Skip to content

Commit 5fe5e5e

Browse files
authored
Merge pull request #590 from Youssef1313/fix-attribute-inherit-fixer
Use SyntaxGenerator and leave trivia handling up to it
2 parents 3b2226b + e1c9bcf commit 5fe5e5e

File tree

1 file changed

+8
-34
lines changed

1 file changed

+8
-34
lines changed

src/CommunityToolkit.Mvvm.CodeFixers/ClassUsingAttributeInsteadOfInheritanceCodeFixer.cs

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using Microsoft.CodeAnalysis.CodeActions;
1414
using Microsoft.CodeAnalysis.CodeFixes;
1515
using Microsoft.CodeAnalysis.CSharp.Syntax;
16+
using Microsoft.CodeAnalysis.Editing;
1617
using Microsoft.CodeAnalysis.Text;
1718
using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors;
1819
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
@@ -63,7 +64,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
6364
context.RegisterCodeFix(
6465
CodeAction.Create(
6566
title: "Inherit from ObservableObject",
66-
createChangedDocument: token => UpdateReference(context.Document, classDeclaration, attributeTypeName, token),
67+
createChangedDocument: token => UpdateReference(context.Document, root, classDeclaration, attributeTypeName),
6768
equivalenceKey: "Inherit from ObservableObject"),
6869
diagnostic);
6970

@@ -76,21 +77,16 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
7677
/// Applies the code fix to a target class declaration and returns an updated document.
7778
/// </summary>
7879
/// <param name="document">The original document being fixed.</param>
80+
/// <param name="root">The original tree root belonging to the current document.</param>
7981
/// <param name="classDeclaration">The <see cref="ClassDeclarationSyntax"/> to update.</param>
8082
/// <param name="attributeTypeName">The name of the attribute that should be removed.</param>
81-
/// <param name="cancellationToken">The cancellation token for the operation.</param>
8283
/// <returns>An updated document with the applied code fix, and <paramref name="classDeclaration"/> inheriting from <c>ObservableObject</c>.</returns>
83-
private static async Task<Document> UpdateReference(Document document, ClassDeclarationSyntax classDeclaration, string attributeTypeName, CancellationToken cancellationToken)
84+
private static Task<Document> UpdateReference(Document document, SyntaxNode root, ClassDeclarationSyntax classDeclaration, string attributeTypeName)
8485
{
8586
// Insert ObservableObject always in first position in the base list. The type might have
8687
// some interfaces in the base list, so we just copy them back after ObservableObject.
87-
ClassDeclarationSyntax updatedClassDeclaration =
88-
classDeclaration.WithBaseList(BaseList(SingletonSeparatedList(
89-
(BaseTypeSyntax)SimpleBaseType(IdentifierName("ObservableObject"))))
90-
.AddTypes(classDeclaration.BaseList?.Types.ToArray() ?? Array.Empty<BaseTypeSyntax>()));
91-
92-
AttributeListSyntax? targetAttributeList = null;
93-
AttributeSyntax? targetAttribute = null;
88+
SyntaxGenerator generator = SyntaxGenerator.GetGenerator(document);
89+
ClassDeclarationSyntax updatedClassDeclaration = (ClassDeclarationSyntax)generator.AddBaseType(classDeclaration, IdentifierName("ObservableObject"));
9490

9591
// Find the attribute list and attribute to remove
9692
foreach (AttributeListSyntax attributeList in updatedClassDeclaration.AttributeLists)
@@ -101,35 +97,13 @@ private static async Task<Document> UpdateReference(Document document, ClassDecl
10197
(identifierName == attributeTypeName || (identifierName + "Attribute") == attributeTypeName))
10298
{
10399
// We found the attribute to remove and the list to update
104-
targetAttributeList = attributeList;
105-
targetAttribute = attribute;
100+
updatedClassDeclaration = (ClassDeclarationSyntax)generator.RemoveNode(updatedClassDeclaration, attribute);
106101

107102
break;
108103
}
109104
}
110105
}
111106

112-
// If we found an attribute to remove, do that
113-
if (targetAttribute is not null)
114-
{
115-
// If the target list has more than one attribute, keep it and just remove the target one
116-
if (targetAttributeList!.Attributes.Count > 1)
117-
{
118-
updatedClassDeclaration =
119-
updatedClassDeclaration.ReplaceNode(
120-
targetAttributeList,
121-
targetAttributeList.RemoveNode(targetAttribute, SyntaxRemoveOptions.KeepNoTrivia)!);
122-
}
123-
else
124-
{
125-
// Otherwise, remove the entire attribute list
126-
updatedClassDeclaration = updatedClassDeclaration.RemoveNode(targetAttributeList, SyntaxRemoveOptions.KeepExteriorTrivia)!;
127-
}
128-
}
129-
130-
SyntaxNode originalRoot = await classDeclaration.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
131-
SyntaxTree updatedTree = originalRoot.ReplaceNode(classDeclaration, updatedClassDeclaration).SyntaxTree;
132-
133-
return document.WithSyntaxRoot(await updatedTree.GetRootAsync(cancellationToken).ConfigureAwait(false));
107+
return Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode(classDeclaration, updatedClassDeclaration)));
134108
}
135109
}

0 commit comments

Comments
 (0)