Skip to content

Commit e4b8a68

Browse files
Merge pull request #4219 from Sergio0694/bugfix/value-named-generated-property
Fixed [ObservableProperty] for fields named "value"
2 parents 166e101 + 8d0d473 commit e4b8a68

File tree

2 files changed

+64
-6
lines changed

2 files changed

+64
-6
lines changed

Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,14 @@ private static PropertyDeclarationSyntax CreatePropertyDeclaration(
240240
}
241241
}
242242

243+
// In case the backing field is exactly named "value", we need to add the "this." prefix to ensure that comparisons and assignments
244+
// with it in the generated setter body are executed correctly and without conflicts with the implicit value parameter.
245+
ExpressionSyntax fieldExpression = fieldSymbol.Name switch
246+
{
247+
"value" => MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName("value")),
248+
string name => IdentifierName(name)
249+
};
250+
243251
BlockSyntax setterBlock;
244252

245253
if (validationAttributes.Count > 0)
@@ -263,10 +271,10 @@ private static PropertyDeclarationSyntax CreatePropertyDeclaration(
263271

264272
// Generate the inner setter block as follows:
265273
//
266-
// if (!global::System.Collections.Generic.EqualityComparer<<FIELD_TYPE>>.Default.Equals(<FIELD_NAME>, value))
274+
// if (!global::System.Collections.Generic.EqualityComparer<<FIELD_TYPE>>.Default.Equals(this.<FIELD_NAME>, value))
267275
// {
268276
// OnPropertyChanging(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.PropertyNamePropertyChangingEventArgs); // Optional
269-
// <FIELD_NAME> = value;
277+
// this.<FIELD_NAME> = value;
270278
// OnPropertyChanged(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.PropertyNamePropertyChangedEventArgs);
271279
// ValidateProperty(value, <PROPERTY_NAME>);
272280
// OnPropertyChanged(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.Property1PropertyChangedEventArgs); // Optional
@@ -291,7 +299,7 @@ private static PropertyDeclarationSyntax CreatePropertyDeclaration(
291299
IdentifierName("Default")),
292300
IdentifierName("Equals")))
293301
.AddArgumentListArguments(
294-
Argument(IdentifierName(fieldSymbol.Name)),
302+
Argument(fieldExpression),
295303
Argument(IdentifierName("value")))),
296304
Block(
297305
ExpressionStatement(
@@ -303,7 +311,7 @@ private static PropertyDeclarationSyntax CreatePropertyDeclaration(
303311
ExpressionStatement(
304312
AssignmentExpression(
305313
SyntaxKind.SimpleAssignmentExpression,
306-
IdentifierName(fieldSymbol.Name),
314+
fieldExpression,
307315
IdentifierName("value"))),
308316
ExpressionStatement(
309317
InvocationExpression(IdentifierName("OnPropertyChanged"))
@@ -346,7 +354,7 @@ private static PropertyDeclarationSyntax CreatePropertyDeclaration(
346354
ExpressionStatement(
347355
AssignmentExpression(
348356
SyntaxKind.SimpleAssignmentExpression,
349-
IdentifierName(fieldSymbol.Name),
357+
fieldExpression,
350358
IdentifierName("value"))),
351359
ExpressionStatement(
352360
InvocationExpression(IdentifierName("OnPropertyChanged"))
@@ -384,7 +392,7 @@ private static PropertyDeclarationSyntax CreatePropertyDeclaration(
384392
IdentifierName("Default")),
385393
IdentifierName("Equals")))
386394
.AddArgumentListArguments(
387-
Argument(IdentifierName(fieldSymbol.Name)),
395+
Argument(fieldExpression),
388396
Argument(IdentifierName("value")))),
389397
updateAndNotificationBlock));
390398
}

UnitTests/UnitTests.NetCore/Mvvm/Test_ObservablePropertyAttribute.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,42 @@ public void Test_ValidationAttributes()
119119
Assert.AreEqual(testAttribute.Animal, Animal.Llama);
120120
}
121121

122+
// See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/4216
123+
[TestCategory("Mvvm")]
124+
[TestMethod]
125+
public void Test_ObservablePropertyWithValueNamedField()
126+
{
127+
var model = new ModelWithValueProperty();
128+
129+
List<string?> propertyNames = new();
130+
131+
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
132+
133+
model.Value = "Hello world";
134+
135+
Assert.AreEqual(model.Value, "Hello world");
136+
137+
CollectionAssert.AreEqual(new[] { nameof(model.Value) }, propertyNames);
138+
}
139+
140+
// See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/4216
141+
[TestCategory("Mvvm")]
142+
[TestMethod]
143+
public void Test_ObservablePropertyWithValueNamedField_WithValidationAttributes()
144+
{
145+
var model = new ModelWithValuePropertyWithValidation();
146+
147+
List<string?> propertyNames = new();
148+
149+
model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
150+
151+
model.Value = "Hello world";
152+
153+
Assert.AreEqual(model.Value, "Hello world");
154+
155+
CollectionAssert.AreEqual(new[] { nameof(model.Value) }, propertyNames);
156+
}
157+
122158
public partial class SampleModel : ObservableObject
123159
{
124160
/// <summary>
@@ -195,5 +231,19 @@ public enum Animal
195231
Dog,
196232
Llama
197233
}
234+
235+
public partial class ModelWithValueProperty : ObservableObject
236+
{
237+
[ObservableProperty]
238+
private string value;
239+
}
240+
241+
public partial class ModelWithValuePropertyWithValidation : ObservableValidator
242+
{
243+
[ObservableProperty]
244+
[Required]
245+
[MinLength(5)]
246+
private string value;
247+
}
198248
}
199249
}

0 commit comments

Comments
 (0)