2016-06-11 13 views
1

Ich baue einen Analysator für C# -Code, der Fehler erzeugt, wenn ein String-Literal anstelle einer Const-Zeichenfolge für bestimmte Argumente für bestimmte Funktionen verwendet wird. Ie.String-Literale durch private const-Anweisungen ersetzen

class MyClass 
{ 
    private void MyMethod(IWriter writer) 
    { 
    writer.WriteInteger("NamedValue", 4); 
    } 
} 

Sollte sich:

class MyClass 
{ 
    private const string IoNamedValueKey = "NamedValue"; 
    private void MyMethod(IWriter writer) 
    { 
    writer.WriteInteger(IoNamedValueKey , 4); 
    } 
} 

ich das bisschen habe zu arbeiten, wo er den Fehler anzeigt, aber ich möchte auch eine CodeFixProvider bereitzustellen. Ich habe in zwei Probleme gerannt:

  1. Ich muss die private const string IoNamedValueKey = "NamedValue"; Anweisung hinzufügen, idealerweise nur über die beleidigende Methode.
  2. Aber nur wenn es nicht schon existiert.

Ich bin nicht ganz sicher, dass die Vorlage Ansatz für die CodeFixProvider die entsprechenden Überlastungen für meine Zwecke verwendet (es ersetzt nur Namen geben mit Großbuchstaben-Varianten), so würde, was der beste Weg vorwärts aus dem RegisterCodeFixesAsync Verfahren ?

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) 
{ 
    // ... now what? 
} 

Nach roslynquoter der erforderliche Knoten, wie unten aufgebaut werden kann, aber ich bin an einem Verlust noch etwas darüber, wie es in den Kontext zu injizieren.

CompilationUnit() 
.WithMembers(
    SingletonList<MemberDeclarationSyntax>(
     FieldDeclaration(
      VariableDeclaration(
       PredefinedType(
        Token(SyntaxKind.StringKeyword))) 
      .WithVariables(
       SingletonSeparatedList<VariableDeclaratorSyntax>(
        VariableDeclarator(
         Identifier("IoNamedValueKey")) 
        .WithInitializer(
         EqualsValueClause(
          LiteralExpression(
           SyntaxKind.StringLiteralExpression, 
           Literal("NamedValue"))))))) 
     .WithModifiers(
      TokenList(
       new []{ 
        Token(SyntaxKind.PrivateKeyword), 
        Token(SyntaxKind.ConstKeyword)})))) 
.NormalizeWhitespace() 

Antwort

2

Sie sollten eine CodeAction registrieren, die das geänderte Dokument durch die context einführt. Für

  • die SyntaxNodes generieren - siehe Roslyn der UniqueNameGenerator und NameGenerator, werden sie nicht ausgesetzt durch die API, aber es wäre sehr leicht zu re - Sie verwenden CSharp SyntaxFactory
  • Erste eindeutigen Namen für Ihren consant verwenden können -Ermöglicht eine vereinfachte Version von ihnen. Hier

ist ein Beispiel Kratzer, was Ihr Code aussehen könnte ( aktualisiert):

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) 
    { 
     var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); 

     var diagnostic = context.Diagnostics.First(); 
     var diagnosticSpan = diagnostic.Location.SourceSpan; 

     var argument = root.FindNode(diagnosticSpan); 
     if (!IsBadStringLiteralArgument(argument)) 
     { 
      return; 
     } 

     // Register a code action that will invoke the fix. 
     context.RegisterCodeFix(
      CodeAction.Create(
       title: title, 
       createChangedDocument: (ct) => InlineConstField(context.Document, root, argument, ct), 
       equivalenceKey: title), 
      diagnostic); 
    } 

    private async Task<Document> InlineConstField(Document document, SyntaxNode root, SyntaxNode argument, CancellationToken cancellationToken) 
    { 
     var stringLiteral = (argument as ArgumentSyntax).Expression as LiteralExpressionSyntax; 
     string suggestdName = this.GetSuggestedName(stringLiteral); 
     var containingMember = argument.FirstAncestorOrSelf<MemberDeclarationSyntax>(); 
     var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); 
     var containingMemberSymbol = semanticModel.GetDeclaredSymbol(containingMember); 


     var takenNames = containingMemberSymbol.ContainingType.MemberNames; 
     string uniqueName = this.GetUniqueName(suggestdName, takenNames); 
     FieldDeclarationSyntax constField = CreateConstFieldDeclaration(uniqueName, stringLiteral).WithAdditionalAnnotations(Formatter.Annotation); 

     var newRoot = root.ReplaceNode(containingMember, new[] { constField, containingMember }); 
     newRoot = Formatter.Format(newRoot, Formatter.Annotation, document.Project.Solution.Workspace); 
     return document.WithSyntaxRoot(newRoot); 
    } 

    private FieldDeclarationSyntax CreateConstFieldDeclaration(string uniqueName, LiteralExpressionSyntax stringLiteral) 
    { 
     return SyntaxFactory.FieldDeclaration(
      SyntaxFactory.List<AttributeListSyntax>(), 
      SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.ConstKeyword)), 
      SyntaxFactory.VariableDeclaration(
       SyntaxFactory.ParseTypeName("string"), 
       SyntaxFactory.SingletonSeparatedList(
        SyntaxFactory.VariableDeclarator(
         SyntaxFactory.Identifier(uniqueName), 
         argumentList: null, 
         initializer: SyntaxFactory.EqualsValueClause(stringLiteral))))); 

    } 
+0

nicht bekommen kann, dass ich zu arbeiten habe Angst, ich bin immer ein 'Können cast-Objekt vom Typ 'Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax' zum Eingeben von 'Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax'-Fehler, wenn ich versuche, entweder 'root.ReplaceNode()' oder 'root.InsertNodesBefore () '. Ich verwende diesen Code, um die const-Deklaration zu erstellen: 'ParseCompilationUnit (string.Format (" private const string {0} = \ "{1} \"; ", Name, Wert));' –

+0

Es ist nicht möglich, CompilationUnitSyntax einzufügen in TypeDeclarationyntax, weil es keine gültige Syntax ist, versuche, eine FieldDeclarationSyntax zu erstellen und einzufügen, schau dir die SyntaxFactory Klassenreferenz an, die ich dir in der Antwort geschickt habe – NValchev

+0

Ich habe es geschafft, den Text einzufügen, aber dieser _cannot_ kann nicht der richtige Weg sein um es zu tun: VariableDeclarationSyntax Variable = VariableDeclaration (ParseTypeName ("private const String")); variable = variable.AddVariables (VariableDeclarator ("+ name +" = "+ wert +" "")); Rückgabe FieldDeclaration (Variable) .NormalizeWhitespace(); –