From 721c56fc6040e0ac88694cb619f519639f6b9d6c Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 11 Jul 2023 17:10:00 +0200 Subject: [PATCH 1/2] Fix forwarded attributes with negative values --- .../ComponentModel/Models/TypedConstantInfo.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/Models/TypedConstantInfo.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/Models/TypedConstantInfo.cs index da13fd429..0f9f10915 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/Models/TypedConstantInfo.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/Models/TypedConstantInfo.cs @@ -135,8 +135,17 @@ public sealed record Enum(string TypeName, object Value) : TypedConstantInfo public override ExpressionSyntax GetSyntax() { // We let Roslyn parse the value expression, so that it can automatically handle both positive and negative values. This - // is needed because negative values have a different syntax tree (UnaryMinuxExpression holding the numeric expression). - return CastExpression(IdentifierName(TypeName), ParseExpression(Value.ToString())); + // is needed because negative values have a different syntax tree (UnaryMinusExpression holding the numeric expression). + ExpressionSyntax valueExpression = ParseExpression(Value.ToString()); + + // If the value is negative, we have to put parentheses around them (to avoid CS0075 errors) + if (valueExpression is PrefixUnaryExpressionSyntax unaryExpression && unaryExpression.IsKind(SyntaxKind.UnaryMinusExpression)) + { + valueExpression = ParenthesizedExpression(valueExpression); + } + + // Now we can safely return the cast expression for the target enum type (with optional parentheses if needed) + return CastExpression(IdentifierName(TypeName), valueExpression); } } From 9f7d0bd34c5650c3b8d1b6eee28effa9390075de Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 11 Jul 2023 17:10:10 +0200 Subject: [PATCH 2/2] Update unit tests for negative forwarded enum values --- .../Test_SourceGeneratorsCodegen.cs | 34 ++++++------ .../Test_ObservablePropertyAttribute.cs | 52 +++++++++++++++++++ 2 files changed, 69 insertions(+), 17 deletions(-) diff --git a/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsCodegen.cs b/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsCodegen.cs index 236d68332..d19c52247 100644 --- a/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsCodegen.cs +++ b/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsCodegen.cs @@ -1349,10 +1349,10 @@ partial class MyViewModel /// [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )] [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)-1073741824)] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)-9223372036854775808)] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)-1234)] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)-1)] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)(-1073741824))] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)(-9223372036854775808))] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)(-1234))] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)(-1))] public object? A { get => a; @@ -1489,24 +1489,24 @@ partial class MyViewModel [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", )] [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)0)] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)-1073741824)] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)(-1073741824))] [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)42)] [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)456)] [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)123)] [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)0)] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)-9223372036854775808)] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)(-9223372036854775808))] [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)42)] [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)456)] [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)123)] [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)1)] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)-1234)] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)(-1234))] [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)42)] [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)456)] [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)123)] [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)1)] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)-1)] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)(-1))] [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)42)] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)-56)] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)(-56))] [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)123)] public object? A { @@ -1792,18 +1792,18 @@ partial class MyViewModel { /// The backing field for . [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", )] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)-1073741824)] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)-9223372036854775808)] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)-1234)] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)-1)] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)(-1073741824))] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)(-9223372036854775808))] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)(-1234))] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)(-1))] private global::CommunityToolkit.Mvvm.Input.RelayCommand? testCommand; /// Gets an instance wrapping . [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", )] [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)-1073741824)] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)-9223372036854775808)] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)-1234)] - [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)-1)] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)(-1073741824))] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)(-9223372036854775808))] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)(-1234))] + [global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)(-1))] public global::CommunityToolkit.Mvvm.Input.IRelayCommand TestCommand => testCommand ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand(new global::System.Action(Test)); } } diff --git a/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservablePropertyAttribute.cs b/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservablePropertyAttribute.cs index c6404a87f..5a557206f 100644 --- a/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservablePropertyAttribute.cs +++ b/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservablePropertyAttribute.cs @@ -1051,6 +1051,23 @@ public void Test_ObservableProperty_MemberNotNullAttributeIsPresent() } #endif + // See https://github.com/CommunityToolkit/dotnet/issues/731 + [TestMethod] + public void Test_ObservableProperty_ForwardedAttributesWithNegativeValues() + { + Assert.AreEqual(PositiveEnum.Something, + typeof(ModelWithForwardedAttributesWithNegativeValues) + .GetProperty(nameof(ModelWithForwardedAttributesWithNegativeValues.Test2))! + .GetCustomAttribute()! + .Value); + + Assert.AreEqual(NegativeEnum.Problem, + typeof(ModelWithForwardedAttributesWithNegativeValues) + .GetProperty(nameof(ModelWithForwardedAttributesWithNegativeValues.Test3))! + .GetCustomAttribute()! + .Value); + } + public abstract partial class BaseViewModel : ObservableObject { public string? Content { get; set; } @@ -1693,4 +1710,39 @@ public ModelWithNonNullableObservableProperty() internal string name; } #endif + + private partial class ModelWithForwardedAttributesWithNegativeValues : ObservableObject + { + [ObservableProperty] + private bool _test1; + + [ObservableProperty] + [property: DefaultValue(PositiveEnum.Something)] + private PositiveEnum _test2; + + [ObservableProperty] + [property: DefaultValue(NegativeEnum.Problem)] + private NegativeEnum _test3; + + [ObservableProperty] + private int _test4; + + public ModelWithForwardedAttributesWithNegativeValues() + { + Test1 = true; + Test2 = PositiveEnum.Else; + } + } + + public enum PositiveEnum + { + Something = 0, + Else = 1 + } + + public enum NegativeEnum + { + Problem = -1, + OK = 0 + } }