Skip to content

Commit 1d98dba

Browse files
authored
Merge pull request #736 from CommunityToolkit/dev/negative-forwarded-attributes
Fix forwarding enum values with negative values in attributes
2 parents e071ed2 + 9f7d0bd commit 1d98dba

File tree

3 files changed

+80
-19
lines changed

3 files changed

+80
-19
lines changed

src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/Models/TypedConstantInfo.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,17 @@ public sealed record Enum(string TypeName, object Value) : TypedConstantInfo
135135
public override ExpressionSyntax GetSyntax()
136136
{
137137
// We let Roslyn parse the value expression, so that it can automatically handle both positive and negative values. This
138-
// is needed because negative values have a different syntax tree (UnaryMinuxExpression holding the numeric expression).
139-
return CastExpression(IdentifierName(TypeName), ParseExpression(Value.ToString()));
138+
// is needed because negative values have a different syntax tree (UnaryMinusExpression holding the numeric expression).
139+
ExpressionSyntax valueExpression = ParseExpression(Value.ToString());
140+
141+
// If the value is negative, we have to put parentheses around them (to avoid CS0075 errors)
142+
if (valueExpression is PrefixUnaryExpressionSyntax unaryExpression && unaryExpression.IsKind(SyntaxKind.UnaryMinusExpression))
143+
{
144+
valueExpression = ParenthesizedExpression(valueExpression);
145+
}
146+
147+
// Now we can safely return the cast expression for the target enum type (with optional parentheses if needed)
148+
return CastExpression(IdentifierName(TypeName), valueExpression);
140149
}
141150
}
142151

tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsCodegen.cs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,10 +1349,10 @@ partial class MyViewModel
13491349
/// <inheritdoc cref="a"/>
13501350
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
13511351
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
1352-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)-1073741824)]
1353-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)-9223372036854775808)]
1354-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)-1234)]
1355-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)-1)]
1352+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)(-1073741824))]
1353+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)(-9223372036854775808))]
1354+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)(-1234))]
1355+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)(-1))]
13561356
public object? A
13571357
{
13581358
get => a;
@@ -1489,24 +1489,24 @@ partial class MyViewModel
14891489
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
14901490
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
14911491
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)0)]
1492-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)-1073741824)]
1492+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)(-1073741824))]
14931493
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)42)]
14941494
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)456)]
14951495
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)123)]
14961496
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)0)]
1497-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)-9223372036854775808)]
1497+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)(-9223372036854775808))]
14981498
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)42)]
14991499
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)456)]
15001500
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)123)]
15011501
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)1)]
1502-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)-1234)]
1502+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)(-1234))]
15031503
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)42)]
15041504
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)456)]
15051505
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)123)]
15061506
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)1)]
1507-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)-1)]
1507+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)(-1))]
15081508
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)42)]
1509-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)-56)]
1509+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)(-56))]
15101510
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)123)]
15111511
public object? A
15121512
{
@@ -1792,18 +1792,18 @@ partial class MyViewModel
17921792
{
17931793
/// <summary>The backing field for <see cref="TestCommand"/>.</summary>
17941794
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
1795-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)-1073741824)]
1796-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)-9223372036854775808)]
1797-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)-1234)]
1798-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)-1)]
1795+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)(-1073741824))]
1796+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)(-9223372036854775808))]
1797+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)(-1234))]
1798+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)(-1))]
17991799
private global::CommunityToolkit.Mvvm.Input.RelayCommand? testCommand;
18001800
/// <summary>Gets an <see cref="global::CommunityToolkit.Mvvm.Input.IRelayCommand"/> instance wrapping <see cref="Test"/>.</summary>
18011801
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
18021802
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
1803-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)-1073741824)]
1804-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)-9223372036854775808)]
1805-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)-1234)]
1806-
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)-1)]
1803+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)(-1073741824))]
1804+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)(-9223372036854775808))]
1805+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)(-1234))]
1806+
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)(-1))]
18071807
public global::CommunityToolkit.Mvvm.Input.IRelayCommand TestCommand => testCommand ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand(new global::System.Action(Test));
18081808
}
18091809
}

tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservablePropertyAttribute.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,6 +1051,23 @@ public void Test_ObservableProperty_MemberNotNullAttributeIsPresent()
10511051
}
10521052
#endif
10531053

1054+
// See https://github.com/CommunityToolkit/dotnet/issues/731
1055+
[TestMethod]
1056+
public void Test_ObservableProperty_ForwardedAttributesWithNegativeValues()
1057+
{
1058+
Assert.AreEqual(PositiveEnum.Something,
1059+
typeof(ModelWithForwardedAttributesWithNegativeValues)
1060+
.GetProperty(nameof(ModelWithForwardedAttributesWithNegativeValues.Test2))!
1061+
.GetCustomAttribute<DefaultValueAttribute>()!
1062+
.Value);
1063+
1064+
Assert.AreEqual(NegativeEnum.Problem,
1065+
typeof(ModelWithForwardedAttributesWithNegativeValues)
1066+
.GetProperty(nameof(ModelWithForwardedAttributesWithNegativeValues.Test3))!
1067+
.GetCustomAttribute<DefaultValueAttribute>()!
1068+
.Value);
1069+
}
1070+
10541071
public abstract partial class BaseViewModel : ObservableObject
10551072
{
10561073
public string? Content { get; set; }
@@ -1693,4 +1710,39 @@ public ModelWithNonNullableObservableProperty()
16931710
internal string name;
16941711
}
16951712
#endif
1713+
1714+
private partial class ModelWithForwardedAttributesWithNegativeValues : ObservableObject
1715+
{
1716+
[ObservableProperty]
1717+
private bool _test1;
1718+
1719+
[ObservableProperty]
1720+
[property: DefaultValue(PositiveEnum.Something)]
1721+
private PositiveEnum _test2;
1722+
1723+
[ObservableProperty]
1724+
[property: DefaultValue(NegativeEnum.Problem)]
1725+
private NegativeEnum _test3;
1726+
1727+
[ObservableProperty]
1728+
private int _test4;
1729+
1730+
public ModelWithForwardedAttributesWithNegativeValues()
1731+
{
1732+
Test1 = true;
1733+
Test2 = PositiveEnum.Else;
1734+
}
1735+
}
1736+
1737+
public enum PositiveEnum
1738+
{
1739+
Something = 0,
1740+
Else = 1
1741+
}
1742+
1743+
public enum NegativeEnum
1744+
{
1745+
Problem = -1,
1746+
OK = 0
1747+
}
16961748
}

0 commit comments

Comments
 (0)