source: misc/tools/PersistenceCodeFix/PersistenceCodeFix/PersistenceCodeFix/Analyzers/MissingStorableConstructor/MissingStorableConstructorFix.cs @ 16473

Last change on this file since 16473 was 16473, checked in by gkronber, 7 months ago

#2520 changed name and type of parameter for StorableConstructor

File size: 5.6 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
14
15namespace PersistenceCodeFix {
16  [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(MissingStorableConstructorFix)), Shared]
17  public sealed class MissingStorableConstructorFix : CodeFixProvider, IDocumentCodeFixProvider {
18    private const string title = "Add storable constructor";
19
20    public sealed override ImmutableArray<string> FixableDiagnosticIds {
21      get { return ImmutableArray.Create(MissingStorableConstructorAnalyzer.DiagnosticId); }
22    }
23
24    public sealed override FixAllProvider GetFixAllProvider() {
25      return SequentialFixAllProvider.Instance;
26    }
27
28    public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) {
29      var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
30
31      var diagnostic = context.Diagnostics.First();
32      var diagnosticSpan = diagnostic.Location.SourceSpan;
33      var baseTypeDecl = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType<BaseTypeDeclarationSyntax>().First();
34
35      context.RegisterCodeFix(
36        CodeAction.Create(
37          title: title,
38          createChangedDocument: c => AddStorableConstructor(context.Document, baseTypeDecl, c),
39          equivalenceKey: title),
40        diagnostic);
41    }
42
43    private static async Task<Document> AddStorableConstructor(Document document, BaseTypeDeclarationSyntax baseTypeDecl, CancellationToken cancellationToken) {
44      // create new attribute list
45      var attrList = SyntaxFactory.AttributeList();
46      attrList = attrList.AddAttributes(
47        SyntaxFactory.Attribute(
48          SyntaxFactory.IdentifierName("StorableConstructor")));
49
50      // check ctor visibility
51      var visibility = SyntaxFactory.Token(
52        baseTypeDecl.Modifiers.Any(x => x.IsKind(SyntaxKind.SealedKeyword))
53          ? SyntaxKind.PrivateKeyword
54          : SyntaxKind.ProtectedKeyword);
55
56      // create ctor parameters
57      var paramList = SyntaxFactory.ParameterList();
58      paramList = paramList.AddParameters(
59        SyntaxFactory.Parameter(
60          SyntaxFactory.Identifier("_"))
61            .WithType(SyntaxFactory.ParseTypeName("StorableConstructorFlag")));
62
63      // create initializer args
64      var initArgs = SyntaxFactory.ArgumentList();
65      initArgs = initArgs.AddArguments(
66        SyntaxFactory.Argument(
67          SyntaxFactory.IdentifierName("_")));
68
69      // create initializer
70      var initializer = SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, initArgs);
71
72      // create new ctor
73      var storableCtor = SyntaxFactory.ConstructorDeclaration(baseTypeDecl.Identifier)
74        .WithAttributeLists(SyntaxFactory.List(new[] { attrList }))
75        .WithModifiers(SyntaxFactory.TokenList(visibility))
76        .WithParameterList(paramList)
77        .WithInitializer(initializer)
78        .WithBody(SyntaxFactory.Block());
79
80      BaseTypeDeclarationSyntax newBaseTypeDecl;
81
82      // add new ctor
83      switch (baseTypeDecl.Kind()) {
84        case SyntaxKind.ClassDeclaration:
85          var classDecl = (ClassDeclarationSyntax)baseTypeDecl;
86          var classMembers = classDecl.Members;
87          classMembers = InsertIntoMembers(classMembers, storableCtor);
88          newBaseTypeDecl = classDecl.WithMembers(classMembers);
89          break;
90        case SyntaxKind.StructDeclaration:
91          var structDecl = (StructDeclarationSyntax)baseTypeDecl;
92          var structMembers = structDecl.Members;
93          structMembers = InsertIntoMembers(structMembers, storableCtor);
94          newBaseTypeDecl = structDecl.WithMembers(structMembers);
95          break;
96        default: throw new NotSupportedException();
97      }
98
99      var root = await document.GetSyntaxRootAsync(cancellationToken);
100      var newRoot = root.ReplaceNode(baseTypeDecl, newBaseTypeDecl.WithAdditionalAnnotations(Formatter.Annotation));
101
102      return document.WithSyntaxRoot(newRoot);
103    }
104
105    private static SyntaxList<MemberDeclarationSyntax> InsertIntoMembers(SyntaxList<MemberDeclarationSyntax> members, MemberDeclarationSyntax storableCtor) {
106      if (!members.Any()) return members.Add(storableCtor);
107
108      var firstCtor = members.FirstOrDefault(x => x.IsKind(SyntaxKind.ConstructorDeclaration));
109      if (firstCtor != null) {
110        storableCtor = storableCtor.WithLeadingTrivia(firstCtor.GetLeadingTrivia());
111        int index = members.IndexOf(firstCtor);
112        members = members.Insert(index++, storableCtor);
113        members = members.RemoveAt(index);
114        members = members.Insert(index, firstCtor.WithoutLeadingTrivia());
115      } else {
116        var lastMember = members.Last();
117        storableCtor = storableCtor.WithTrailingTrivia(lastMember.GetTrailingTrivia());
118        int index = members.IndexOf(lastMember);
119        members = members.Insert(index, storableCtor);
120        members = members.RemoveAt(index + 1);
121        members = members.Insert(index, lastMember.WithoutTrailingTrivia());
122      }
123
124      return members;
125    }
126
127    public Task<Document> FixDocumentAsync(Document document, SyntaxNode node, CancellationToken cancellationToken) {
128      return AddStorableConstructor(document, (BaseTypeDeclarationSyntax)node, cancellationToken);
129    }
130  }
131}
Note: See TracBrowser for help on using the repository browser.