From b4b2227bd92425eff6cbf8f29ef53efa74e21335 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 10 Feb 2023 19:01:23 +0100 Subject: [PATCH] Fix forwarded double attribute parameters --- .../Models/TypedConstantInfo.cs | 9 +- .../Test_SourceGeneratorsCodegen.cs | 99 +++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/Models/TypedConstantInfo.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/Models/TypedConstantInfo.cs index e1bd70750..4538afafa 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/Models/TypedConstantInfo.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/Models/TypedConstantInfo.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Globalization; using System.Linq; using CommunityToolkit.Mvvm.SourceGenerators.Helpers; using Microsoft.CodeAnalysis; @@ -89,7 +90,13 @@ public override ExpressionSyntax GetSyntax() { byte b => Literal(b), char c => Literal(c), - double d => Literal(d), + + // For doubles, we need to manually format it and always add the trailing "D" suffix. + // This ensures that the correct type is produced if the expression was assigned to + // an object (eg. the literal was used in an attribute object parameter/property). + double d => Literal(d.ToString("R", CultureInfo.InvariantCulture) + "D", d), + + // For floats, Roslyn will automatically add the "F" suffix, so no extra work is needed float f => Literal(f), int i => Literal(i), long l => Literal(l), diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsCodegen.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsCodegen.cs index 8cf4b9daa..0a357ffac 100644 --- a/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsCodegen.cs +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsCodegen.cs @@ -94,6 +94,105 @@ public string Name VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result)); } + // See https://github.com/CommunityToolkit/dotnet/issues/601 + [TestMethod] + public void ObservablePropertyWithForwardedAttributesWithNumberLiterals_PreservesType() + { + string source = """ + using System.ComponentModel; + using CommunityToolkit.Mvvm.ComponentModel; + + #nullable enable + + namespace MyApp; + + partial class MyViewModel : ObservableObject + { + const double MyDouble = 3.14; + const float MyFloat = 3.14f; + + [ObservableProperty] + [property: DefaultValue(0.0)] + [property: DefaultValue(1.24)] + [property: DefaultValue(0.0f)] + [property: DefaultValue(0.0f)] + [property: DefaultValue(MyDouble)] + [property: DefaultValue(MyFloat)] + private object? a; + } + + public class DefaultValueAttribute : Attribute + { + public DefaultValueAttribute(object value) + { + } + } + """; + + string result = """ + // + #pragma warning disable + #nullable enable + namespace MyApp + { + partial class MyViewModel + { + /// + [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.1.0.0")] + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::MyApp.DefaultValueAttribute(0D)] + [global::MyApp.DefaultValueAttribute(1.24D)] + [global::MyApp.DefaultValueAttribute(0F)] + [global::MyApp.DefaultValueAttribute(0F)] + [global::MyApp.DefaultValueAttribute(3.14D)] + [global::MyApp.DefaultValueAttribute(3.14F)] + public object? A + { + get => a; + set + { + if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(a, value)) + { + OnAChanging(value); + OnAChanging(default, value); + OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.A); + a = value; + OnAChanged(value); + OnAChanged(default, value); + OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.A); + } + } + } + + /// Executes the logic for when is changing. + /// The new property value being set. + /// This method is invoked right before the value of is changed. + [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.1.0.0")] + partial void OnAChanging(object? value); + /// Executes the logic for when is changing. + /// The previous property value that is being replaced. + /// The new property value being set. + /// This method is invoked right before the value of is changed. + [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.1.0.0")] + partial void OnAChanging(object? oldValue, object? newValue); + /// Executes the logic for when just changed. + /// The new property value that was set. + /// This method is invoked right after the value of is changed. + [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.1.0.0")] + partial void OnAChanged(object? value); + /// Executes the logic for when just changed. + /// The previous property value that was replaced. + /// The new property value that was set. + /// This method is invoked right after the value of is changed. + [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.1.0.0")] + partial void OnAChanged(object? oldValue, object? newValue); + } + } + """; + + VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result)); + } + /// /// Generates the requested sources ///