source: misc/tools/PersistenceCodeFix/PersistenceCodeFix/PersistenceCodeFix/Analyzers/MissingStorableType/MissingStorableTypeFix.cs @ 16576

Last change on this file since 16576 was 16576, checked in by gkronber, 2 years ago

#2520: changed Fossil -> Attic in VS Code Fix

File size: 5.3 KB
Line 
1using System;
2using System.Collections.Immutable;
3using System.Composition;
4using System.Linq;
5using System.Threading;
6using System.Threading.Tasks;
7using Microsoft.CodeAnalysis;
8using Microsoft.CodeAnalysis.CodeActions;
9using Microsoft.CodeAnalysis.CodeFixes;
10using Microsoft.CodeAnalysis.CSharp;
11using Microsoft.CodeAnalysis.CSharp.Syntax;
12using Microsoft.CodeAnalysis.Formatting;
13
14namespace PersistenceCodeFix {
15  [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(MissingStorableTypeFix)), Shared]
16  public sealed class MissingStorableTypeFix : CodeFixProvider, IDocumentCodeFixProvider {
17    private const string title = "Add StorableType attribute";
18
19    public sealed override ImmutableArray<string> FixableDiagnosticIds {
20      get { return ImmutableArray.Create(MissingStorableTypeAnalyzer.DiagnosticId); }
21    }
22
23    public sealed override FixAllProvider GetFixAllProvider() {
24      return SequentialFixAllProvider.Instance;
25    }
26
27    public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) {
28      var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
29
30      var diagnostic = context.Diagnostics.First();
31      var diagnosticSpan = diagnostic.Location.SourceSpan;
32      var baseTypeDecl = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType<BaseTypeDeclarationSyntax>().First();
33
34      context.RegisterCodeFix(
35        CodeAction.Create(
36          title: title,
37          createChangedDocument: c => AddStorableTypeAttribute(context.Document, baseTypeDecl, c),
38          equivalenceKey: title),
39        diagnostic);
40    }
41
42    private static async Task<Document> AddStorableTypeAttribute(Document document, BaseTypeDeclarationSyntax baseTypeDecl, CancellationToken cancellationToken) {
43      // create new identifier
44      var name = SyntaxFactory.IdentifierName("StorableType");
45
46      // create guid arg
47      var guid = Guid.NewGuid();
48      var guidArg = SyntaxFactory.AttributeArgument(
49        SyntaxFactory.LiteralExpression(
50          SyntaxKind.StringLiteralExpression,
51          SyntaxFactory.Literal(guid.ToString().ToUpperInvariant())
52        ));
53
54      var attrArgs = SyntaxFactory.AttributeArgumentList();
55      attrArgs = attrArgs.AddArguments(guidArg);
56
57      // create new attribute
58      var attrDecl = SyntaxFactory.Attribute(name)
59        .WithArgumentList(attrArgs)
60        .WithAdditionalAnnotations(Formatter.Annotation);
61
62      // create new attribute list
63      var attrList = SyntaxFactory.AttributeList();
64      attrList = attrList.AddAttributes(attrDecl);
65
66      BaseTypeDeclarationSyntax newBaseTypeDecl;
67
68      // add new attribute list
69      switch (baseTypeDecl.Kind()) {
70        case SyntaxKind.ClassDeclaration:
71          var classDecl = (ClassDeclarationSyntax)baseTypeDecl;
72          var classAttrLists = classDecl.AttributeLists;
73          classAttrLists = AddToAttributeLists(classAttrLists, attrList);
74          newBaseTypeDecl = classDecl.WithAttributeLists(classAttrLists);
75          break;
76        case SyntaxKind.StructDeclaration:
77          var structDecl = (StructDeclarationSyntax)baseTypeDecl;
78          var structAttrLists = structDecl.AttributeLists;
79          structAttrLists = AddToAttributeLists(structAttrLists, attrList);
80          newBaseTypeDecl = structDecl.WithAttributeLists(structAttrLists);
81          break;
82        case SyntaxKind.EnumDeclaration:
83          var enumDecl = (EnumDeclarationSyntax)baseTypeDecl;
84          var enumAttrLists = enumDecl.AttributeLists;
85          enumAttrLists = AddToAttributeLists(enumAttrLists, attrList);
86          newBaseTypeDecl = enumDecl.WithAttributeLists(enumAttrLists);
87          break;
88        case SyntaxKind.InterfaceDeclaration:
89          var interfaceDecl = (InterfaceDeclarationSyntax)baseTypeDecl;
90          var interfaceAttrLists = interfaceDecl.AttributeLists;
91          interfaceAttrLists = AddToAttributeLists(interfaceAttrLists, attrList);
92          newBaseTypeDecl = interfaceDecl.WithAttributeLists(interfaceAttrLists);
93          break;
94        default: throw new NotSupportedException();
95      }
96
97      newBaseTypeDecl = newBaseTypeDecl.WithAdditionalAnnotations(Formatter.Annotation);
98
99      var root = await document.GetSyntaxRootAsync(cancellationToken) as CompilationUnitSyntax;
100      var newRoot = root.ReplaceNode(baseTypeDecl, newBaseTypeDecl);
101
102      // add using of HEAL.Attic if the document does not already have it
103      var oldUsings = root.Usings;
104      if (oldUsings.All(x => x.Name.WithoutTrivia().ToString() != "HEAL.Attic")) {
105        var persistenceUsing = SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName("HEAL.Attic"));
106        newRoot = newRoot.WithUsings(oldUsings.Add(persistenceUsing));
107      }
108
109      return document.WithSyntaxRoot(newRoot);
110    }
111
112    private static SyntaxList<AttributeListSyntax> AddToAttributeLists(SyntaxList<AttributeListSyntax> attrLists, AttributeListSyntax attrList) {
113      return attrLists == null ? SyntaxFactory.List(new[] { attrList }) : attrLists.Add(attrList);
114    }
115
116    public Task<Document> FixDocumentAsync(Document document, SyntaxNode node, CancellationToken cancellationToken) {
117      return AddStorableTypeAttribute(document, (BaseTypeDeclarationSyntax)node, cancellationToken);
118    }
119  }
120}
Note: See TracBrowser for help on using the repository browser.