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);
}
}
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
+ }
}