Skip to content

Commit 5e3d65f

Browse files
committed
Improve handling of attribute lists, fix tests
1 parent 46cd488 commit 5e3d65f

File tree

2 files changed

+55
-12
lines changed

2 files changed

+55
-12
lines changed

src/CommunityToolkit.Mvvm.CodeFixers/UsePartialPropertyForSemiAutoPropertyCodeFixer.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,40 @@ private static async Task<Document> ConvertToPartialProperty(Document document,
6969
{
7070
await Task.CompletedTask;
7171

72+
// Prepare the [ObservableProperty] attribute, which is always inserted first
73+
AttributeListSyntax observablePropertyAttributeList = AttributeList(SingletonSeparatedList(Attribute(IdentifierName("ObservableProperty"))));
74+
75+
// Start setting up the updated attribute lists
76+
SyntaxList<AttributeListSyntax> attributeLists = propertyDeclaration.AttributeLists;
77+
78+
if (attributeLists is [AttributeListSyntax firstAttributeListSyntax, ..])
79+
{
80+
// Remove the trivia from the original first attribute
81+
attributeLists = attributeLists.Replace(
82+
nodeInList: firstAttributeListSyntax,
83+
newNode: firstAttributeListSyntax.WithoutTrivia());
84+
85+
// If the property has at least an attribute list, move the trivia from it to the new attribute
86+
observablePropertyAttributeList = observablePropertyAttributeList.WithTriviaFrom(firstAttributeListSyntax);
87+
88+
// Insert the new attribute
89+
attributeLists = attributeLists.Insert(0, observablePropertyAttributeList);
90+
}
91+
else
92+
{
93+
// Otherwise (there are no attribute lists), transfer the trivia to the new (only) attribute list
94+
observablePropertyAttributeList = observablePropertyAttributeList.WithTriviaFrom(propertyDeclaration);
95+
96+
// Save the new attribute list
97+
attributeLists = attributeLists.Add(observablePropertyAttributeList);
98+
}
99+
72100
// Get a new property that is partial and with semicolon token accessors
73101
PropertyDeclarationSyntax updatedPropertyDeclaration =
74102
propertyDeclaration
75103
.AddModifiers(Token(SyntaxKind.PartialKeyword))
76-
.AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("ObservableProperty")))))
104+
.WithoutTrivia()
105+
.WithAttributeLists(attributeLists)
77106
.WithAdditionalAnnotations(Formatter.Annotation)
78107
.WithAccessorList(AccessorList(List(
79108
[

tests/CommunityToolkit.Mvvm.SourceGenerators.Roslyn4120.UnitTests/Test_UseObservablePropertyOnSemiAutoPropertyCodeFixer.cs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ public partial class SampleViewModel : ObservableObject
126126

127127
test.FixedState.ExpectedDiagnostics.AddRange(new[]
128128
{
129-
// /0/Test0.cs(8,27): error CS9248: Partial property 'SampleViewModel.Name' must have an implementation part.
130-
DiagnosticResult.CompilerError("CS9248").WithSpan(8, 27, 8, 31).WithArguments("MyApp.SampleViewModel.Name"),
129+
// /0/Test0.cs(11,27): error CS9248: Partial property 'SampleViewModel.Name' must have an implementation part.
130+
DiagnosticResult.CompilerError("CS9248").WithSpan(11, 27, 11, 31).WithArguments("MyApp.SampleViewModel.Name"),
131131
});
132132

133133
await test.RunAsync();
@@ -175,6 +175,8 @@ public partial class SampleViewModel : ObservableObject
175175
[field: Test("Targeting field")]
176176
public partial string Name { get; set; }
177177
}
178+
179+
public class TestAttribute(string text) : Attribute;
178180
""";
179181

180182
CSharpCodeFixTest test = new(LanguageVersion.Preview)
@@ -193,8 +195,8 @@ public partial class SampleViewModel : ObservableObject
193195

194196
test.FixedState.ExpectedDiagnostics.AddRange(new[]
195197
{
196-
// /0/Test0.cs(8,27): error CS9248: Partial property 'SampleViewModel.Name' must have an implementation part.
197-
DiagnosticResult.CompilerError("CS9248").WithSpan(8, 27, 8, 31).WithArguments("MyApp.SampleViewModel.Name"),
198+
// /0/Test0.cs(14,27): error CS9248: Partial property 'SampleViewModel.Name' must have an implementation part.
199+
DiagnosticResult.CompilerError("CS9248").WithSpan(14, 27, 14, 31).WithArguments("MyApp.SampleViewModel.Name"),
198200
});
199201

200202
await test.RunAsync();
@@ -447,29 +449,29 @@ namespace MyApp;
447449
448450
public partial class SampleViewModel : ObservableObject
449451
{
450-
[ObservableAttribute]
452+
[ObservableProperty]
451453
[Test("This is an attribute")]
452454
public partial string Prop1 { get; set; }
453455
454456
// Single comment
455-
[ObservableAttribute]
457+
[ObservableProperty]
456458
public partial string Prop2 { get; set; }
457459
458460
/// <summary>
459461
/// This is a property.
460462
/// </summary>
461-
[ObservableAttribute]
463+
[ObservableProperty]
462464
public partial string Prop3 { get; set; }
463465
464466
/// <summary>
465467
/// This is another property.
466468
/// </summary>
467-
[ObservableAttribute]
469+
[ObservableProperty]
468470
[Test("Another attribute")]
469471
public partial string Prop4 { get; set; }
470472
471473
// Some other single comment
472-
[ObservableAttribute]
474+
[ObservableProperty]
473475
[Test("Yet another attribute")]
474476
public partial string Prop5 { get; set; }
475477
}
@@ -505,8 +507,20 @@ public class TestAttribute(string text) : Attribute;
505507

506508
test.FixedState.ExpectedDiagnostics.AddRange(new[]
507509
{
508-
// /0/Test0.cs(8,27): error CS9248: Partial property 'SampleViewModel.Name' must have an implementation part.
509-
DiagnosticResult.CompilerError("CS9248").WithSpan(8, 27, 8, 31).WithArguments("MyApp.SampleViewModel.Name"),
510+
// /0/Test0.cs(10,27): error CS9248: Partial property 'SampleViewModel.Prop1' must have an implementation part.
511+
DiagnosticResult.CompilerError("CS9248").WithSpan(10, 27, 10, 32).WithArguments("MyApp.SampleViewModel.Prop1"),
512+
513+
// /0/Test0.cs(14,27): error CS9248: Partial property 'SampleViewModel.Prop2' must have an implementation part.
514+
DiagnosticResult.CompilerError("CS9248").WithSpan(14, 27, 14, 32).WithArguments("MyApp.SampleViewModel.Prop2"),
515+
516+
// /0/Test0.cs(20,27): error CS9248: Partial property 'SampleViewModel.Prop3' must have an implementation part.
517+
DiagnosticResult.CompilerError("CS9248").WithSpan(20, 27, 20, 32).WithArguments("MyApp.SampleViewModel.Prop3"),
518+
519+
// /0/Test0.cs(27,27): error CS9248: Partial property 'SampleViewModel.Prop4' must have an implementation part.
520+
DiagnosticResult.CompilerError("CS9248").WithSpan(27, 27, 27, 32).WithArguments("MyApp.SampleViewModel.Prop4"),
521+
522+
// /0/Test0.cs(32,27): error CS9248: Partial property 'SampleViewModel.Prop5' must have an implementation part.
523+
DiagnosticResult.CompilerError("CS9248").WithSpan(32, 27, 32, 32).WithArguments("MyApp.SampleViewModel.Prop5"),
510524
});
511525

512526
await test.RunAsync();

0 commit comments

Comments
 (0)