Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/tools/PersistenceCodeFix/PersistenceCodeFix/PersistenceCodeFix/CodeFixProvider.cs @ 14933

Last change on this file since 14933 was 14933, checked in by gkronber, 7 years ago

#2520 added a code fix for generating StorableTypeAttributes

File size: 7.3 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Collections.Immutable;
4using System.Composition;
5using System.Linq;
6using System.Threading;
7using System.Threading.Tasks;
8using Microsoft.CodeAnalysis;
9using Microsoft.CodeAnalysis.CodeFixes;
10using Microsoft.CodeAnalysis.CodeActions;
11using Microsoft.CodeAnalysis.CSharp;
12using Microsoft.CodeAnalysis.CSharp.Syntax;
13using Microsoft.CodeAnalysis.Formatting;
14using Microsoft.CodeAnalysis.Rename;
15using Microsoft.CodeAnalysis.Text;
16
17
18namespace PersistenceCodeFix {
19  [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(PersistenceCodeFixProvider)), Shared]
20  public class PersistenceCodeFixProvider : CodeFixProvider {
21    private const string title = "Convert to Persistence-4.0";
22
23    public sealed override ImmutableArray<string> FixableDiagnosticIds {
24      get { return ImmutableArray.Create(MissingStorableTypeAnalyzer.DiagnosticId); }
25    }
26
27    public sealed override FixAllProvider GetFixAllProvider() {
28      // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers
29      return WellKnownFixAllProviders.BatchFixer;
30    }
31
32    public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) {
33      var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
34
35      var diagnostic = context.Diagnostics.First();
36      var diagnosticSpan = diagnostic.Location.SourceSpan;
37
38      // Find the type declaration identified by the diagnostic.
39      var typeDeclaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType<BaseTypeDeclarationSyntax>().First();
40
41      // Register a code action that will invoke the fix.
42      context.RegisterCodeFix(
43        CodeAction.Create(
44          title: title,
45          createChangedSolution: c => AddStorableTypeAttribute(context.Document, typeDeclaration, c),
46          equivalenceKey: title),
47        diagnostic);
48    }
49
50
51    private async Task<Solution> AddStorableTypeAttribute(Document document, BaseTypeDeclarationSyntax typeDecl, CancellationToken cancellationToken) {
52      var newGuid = Guid.NewGuid();
53      var attributeLists = typeDecl.AttributeLists.ToDictionary(l => l, l => (AttributeListSyntax)null);
54      bool renamed = false;
55      AttributeListSyntax newAttrList = null;
56      SyntaxList<AttributeListSyntax> oldAttrList;
57
58      foreach (var kvp in attributeLists.ToArray()) {
59        var newList = SyntaxFactory.AttributeList();
60        foreach (var attr in kvp.Key.Attributes) {
61          if (attr.Name.WithoutTrivia().ToString() == "StorableClass") {
62            // rename
63            var guidArgument = SyntaxFactory.AttributeArgument(
64                SyntaxFactory.LiteralExpression(
65                  SyntaxKind.StringLiteralExpression,
66                  SyntaxFactory.Literal(newGuid.ToString())));
67
68            // collect existing arguments (if any) and append the GUID argument
69            var argumentList = new List<AttributeArgumentSyntax>();
70            if (attr.ArgumentList != null) argumentList.AddRange(attr.ArgumentList.Arguments);
71            argumentList.Add(guidArgument);
72
73            newList = newList.WithAttributes(
74              newList.Attributes.Add(
75                attr.WithName(SyntaxFactory.IdentifierName("StorableType"))
76                .WithLeadingTrivia(attr.GetLeadingTrivia())
77                .WithTrailingTrivia(attr.GetTrailingTrivia())
78                .WithArgumentList(SyntaxFactory.AttributeArgumentList(
79                SyntaxFactory.SeparatedList<AttributeArgumentSyntax>(argumentList)))));
80
81            renamed = true;
82          } else {
83            newList = newList.WithAttributes(newList.Attributes.Add(attr));
84          }
85        }
86
87        attributeLists[kvp.Key] = newList
88          .WithLeadingTrivia(kvp.Key.GetLeadingTrivia())
89          .WithTrailingTrivia(kvp.Key.GetTrailingTrivia());
90      }
91
92      if (!renamed) {
93        oldAttrList = typeDecl.AttributeLists;
94
95
96        newAttrList = SyntaxFactory.AttributeList(
97          SyntaxFactory.SingletonSeparatedList<AttributeSyntax>(
98            SyntaxFactory.Attribute(
99                SyntaxFactory.IdentifierName("StorableType"))
100              .WithArgumentList(
101                SyntaxFactory.AttributeArgumentList(
102                  SyntaxFactory.SingletonSeparatedList<AttributeArgumentSyntax>(
103                    SyntaxFactory.AttributeArgument(
104                        SyntaxFactory.LiteralExpression(
105                          SyntaxKind.StringLiteralExpression,
106                          SyntaxFactory.Literal(newGuid.ToString())))
107                      )))));
108
109      } else {
110        oldAttrList = SyntaxFactory.List(attributeLists.Values);
111      }
112
113      BaseTypeDeclarationSyntax newTypeDecl;
114      var classDecl = typeDecl as ClassDeclarationSyntax;
115      var structDecl = typeDecl as StructDeclarationSyntax;
116      var interfaceDecl = typeDecl as InterfaceDeclarationSyntax;
117      var enumDecl = typeDecl as EnumDeclarationSyntax;
118      if (classDecl != null) {
119        if (newAttrList != null) newTypeDecl = classDecl.WithoutLeadingTrivia().WithoutTrailingTrivia().WithAttributeLists(oldAttrList.Add(newAttrList));
120        else newTypeDecl = classDecl.WithoutLeadingTrivia().WithoutTrailingTrivia().WithAttributeLists(oldAttrList);
121      } else if (structDecl != null) {
122        if (newAttrList != null) newTypeDecl = structDecl.WithoutLeadingTrivia().WithoutTrailingTrivia().WithAttributeLists(oldAttrList.Add(newAttrList));
123        else newTypeDecl = structDecl.WithoutLeadingTrivia().WithoutTrailingTrivia().WithAttributeLists(oldAttrList);
124      } else if (interfaceDecl != null) {
125        if (newAttrList != null) newTypeDecl = interfaceDecl.WithoutLeadingTrivia().WithoutTrailingTrivia().WithAttributeLists(oldAttrList.Add(newAttrList));
126        else newTypeDecl = interfaceDecl.WithoutLeadingTrivia().WithoutTrailingTrivia().WithAttributeLists(oldAttrList);
127      } else if (enumDecl != null) {
128        if (newAttrList != null) newTypeDecl = enumDecl.WithoutLeadingTrivia().WithoutTrailingTrivia().WithAttributeLists(oldAttrList.Add(newAttrList));
129        else newTypeDecl = enumDecl.WithoutLeadingTrivia().WithoutTrailingTrivia().WithAttributeLists(oldAttrList);
130      } else throw new NotSupportedException();
131
132
133      var root = await document.GetSyntaxRootAsync(cancellationToken);
134
135      var oldUsings = (root as CompilationUnitSyntax).Usings;
136
137
138      var newRoot = root
139        .ReplaceNode(typeDecl, newTypeDecl
140          .WithAdditionalAnnotations(Formatter.Annotation)
141          .WithLeadingTrivia(typeDecl.GetLeadingTrivia())
142          .WithTrailingTrivia(typeDecl.GetTrailingTrivia())
143        ) as CompilationUnitSyntax;
144
145
146      // add using HeuristicLab.Persistence if something was changed or added and the document does already contain the using directive
147      if ((newAttrList != null || renamed) &&
148        oldUsings.All(sn => sn.Name.WithoutTrivia().ToString() != "HeuristicLab.Persistence")) {
149        var newUsing = SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName("HeuristicLab.Persistence"));
150        newRoot = newRoot.WithUsings(oldUsings.Add(newUsing));
151      }
152
153      return document
154        .WithSyntaxRoot(newRoot)
155        .Project.Solution;
156    }
157  }
158}
Note: See TracBrowser for help on using the repository browser.