2016-04-21 9 views
0

Ich habe einen Analysator erstellt, der erkennt, ob eine Methode kein <createddate>-Tag im XML-Code und einen Provider enthält, der dieses Tag injiziert. Es funktioniert gut und fügt das Tag ein, aber es wird immer noch mit Erstellungsfehler zurückgegeben, obwohl die Regel behoben wurde. Ich denke, ich muss möglicherweise semantische Modelle verwenden?Roslyn, Provider kann keine Lösung nach Codefix aktualisieren

Das ist, was ich habe: CodeFixProvider.cs

using System; 
using System.Collections.Immutable; 
using System.Composition; 
using System.Linq; 
using System.Threading; 
using System.Threading.Tasks; 
using Microsoft.CodeAnalysis; 
using Microsoft.CodeAnalysis.CodeFixes; 
using Microsoft.CodeAnalysis.CodeActions; 
using Microsoft.CodeAnalysis.CSharp; 
using Microsoft.CodeAnalysis.CSharp.Syntax; 
using Microsoft.CodeAnalysis.Formatting; 
using Microsoft.CodeAnalysis.Simplification; 
using Microsoft.CodeAnalysis.Rename; 

namespace NewAnalyzer 
{ 
    [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(NewAnalyzerCodeFixProvider)), Shared] 
    public class NewAnalyzerCodeFixProvider : CodeFixProvider 
    { 
     private const string title = "Add Createddate"; 

     public sealed override ImmutableArray<string> FixableDiagnosticIds 
     { 
      get { return ImmutableArray.Create(NewAnalyzerAnalyzer.DiagnosticId); } 
     } 

     public sealed override FixAllProvider GetFixAllProvider() 
     { 
      // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers 
      return WellKnownFixAllProviders.BatchFixer; 
     } 

     public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) 
     { 
      var diagnostic = context.Diagnostics.First(); 
      var diagnosticSpan = diagnostic.Location.SourceSpan; 
      SyntaxNode root; 
      context.Document.TryGetSyntaxRoot(out root); 

      var syntax = root.FindNode(diagnostic.Location.SourceSpan); 

      var methodDeclarationSyntax = syntax.FirstAncestorOrSelf<MethodDeclarationSyntax>(); 

      var description = "Add created datetime for API endpoint."; 
      var equivalenceKey = "empty string"; 
      context.RegisterCodeFix(CodeAction.Create(description, cancellationToken => CreateChangedDocument(context, methodDeclarationSyntax, cancellationToken), equivalenceKey), diagnostic); 
      return Task.FromResult(0); 
     } 


     /// <summary> 
     /// Create a new method that will contain the changes required to insert a createddate tag into the comments. 
     /// </summary> 
     /// <param name="context">context</param> 
     /// <param name="methodDeclarationSyntax">method declaration syntax</param> 
     /// <param name="cancellationToken">cancellation token</param> 
     /// <returns>new method that contains createddate in the comments</returns> 
     private static async Task<Document> CreateChangedDocument(CodeFixContext context, MethodDeclarationSyntax methodDeclarationSyntax, CancellationToken cancellationToken) 
     { 
      var originalTree = await context.Document.GetSyntaxTreeAsync(cancellationToken); 
      var newTree = await context.Document.GetSyntaxTreeAsync(cancellationToken); 
      var root = await newTree.GetRootAsync(cancellationToken); 

      var documentationComment = methodDeclarationSyntax.GetLeadingTrivia().Select(i => i.GetStructure()).OfType<DocumentationCommentTriviaSyntax>().FirstOrDefault(); 
      var summaryElement = (XmlElementSyntax)documentationComment.Content.FirstOrDefault(x => x is XmlElementSyntax); // works 


      if (documentationComment == null) 
       return context.Document; 

      var newLineText = SyntaxFactory.XmlTextNewLine(SyntaxFactory.TriviaList(), Environment.NewLine, Environment.NewLine, SyntaxFactory.TriviaList()); 

      var createdDateText = 
      SyntaxFactory.XmlText(SyntaxFactory.TokenList(
       SyntaxFactory.XmlTextLiteral(
        SyntaxFactory.TriviaList(), 
        DateTime.UtcNow.ToString("d"), 
        DateTime.UtcNow.ToString("d"), 
        SyntaxFactory.TriviaList()) 
        )); 

      var textList = SyntaxFactory.List<XmlNodeSyntax>(new[] { createdDateText }); 
      var createdDateNode = new XmlNodeSyntax[] 
      { 
       SyntaxFactory.XmlText().AddTextTokens(SyntaxFactory.XmlTextNewLine(SyntaxFactory.TriviaList(), Environment.NewLine, Environment.NewLine, SyntaxFactory.TriviaList())), 
        SyntaxFactory.XmlElement(SyntaxFactory.XmlElementStartTag(SyntaxFactory.XmlName("createddate")).WithLeadingTrivia(SyntaxFactory.DocumentationCommentExterior("/// ")), 
            textList, 
            SyntaxFactory.XmlElementEndTag(SyntaxFactory.XmlName("createddate"))).WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation) 
      }; 

      var list = SyntaxFactory.List<XmlNodeSyntax>(createdDateNode); 
      SyntaxNode tempNode = documentationComment.InsertNodesAfter(summaryElement, list); 

      var newRoot = root.ReplaceNode(documentationComment, tempNode); 
      var semanticModel = await context.Document.GetSemanticModelAsync(cancellationToken); 
      var typeSymbol = semanticModel.GetDeclaredSymbol(methodDeclarationSyntax, cancellationToken); 

      var semModel = await context.Document.GetSemanticModelAsync(); 
      var compilation = semModel.Compilation.ReplaceSyntaxTree(originalTree, newTree); 

      var oldSemModel = await context.Document.GetSemanticModelAsync(); 
      oldSemModel = semanticModel; 
      return context.Document; 
     } 
    } 
} 

Und DiagnosticAnalyzer.cs

using System; 
using System.Collections.Generic; 
using System.Collections.Immutable; 
using System.Linq; 
using System.Threading; 
using Microsoft.CodeAnalysis; 
using Microsoft.CodeAnalysis.CSharp; 
using Microsoft.CodeAnalysis.CSharp.Syntax; 
using Microsoft.CodeAnalysis.Diagnostics; 

namespace NewAnalyzer 
{ 
    [DiagnosticAnalyzer(LanguageNames.CSharp)] 
    public class NewAnalyzerAnalyzer : DiagnosticAnalyzer 
    { 
     public const string DiagnosticId = "NA001"; 

     // You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat. 
     // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Localizing%20Analyzers.md for more on localization 
     private static readonly LocalizableString Title = "Title: createddate is missing"; 
     private static readonly LocalizableString MessageFormat = "Format: createddate is missing"; 
     private static readonly LocalizableString Description = "Desc: createddate is missing"; 
     private const string Category = "Naming"; 

     private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); 

     public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } } 

     public override void Initialize(AnalysisContext context) 
     { 
      // TODO: Consider registering other actions that act on syntax instead of or in addition to symbols 
      // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Analyzer%20Actions%20Semantics.md for more information 
      //context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType); 
      context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method); 
     } 

     /// <summary> 
     /// Analyze method to see if it's a REST endpoint method 
     /// </summary> 
     /// <param name="context">context</param> 
     private static void AnalyzeMethod(SymbolAnalysisContext context) 
     { 
      var methodDeclarationNode = context.Symbol.GetDocumentationCommentXml(); 

      if (methodDeclarationNode != null) 
      { 
       if (!methodDeclarationNode.Contains("createddate")) 
       { 
        var diagnostic = Diagnostic.Create(Rule, context.Symbol.Locations[0], methodDeclarationNode); 
        context.ReportDiagnostic(diagnostic); 
       } 
      } 
     } 
    } 
} 

Irgendwelche Ideen, wie die Build verstreichen zu lassen, wenn der Tag in der XML vorhanden ist ? Scheint es nicht wirklich die Lösung zu aktualisieren?

Antwort

0

context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method) wird den Rückruf auch für Eigenschaften-Getter und Setter ausführen. Ihr Codefix schlägt bei Eigenschaften fehl, weil er keinen MethodDeclarationSyntax für den gemeldeten Standort finden kann.

Warum haben Sie diese Funktionalität als symbolbasierter Analysator implementiert? Es könnte ein auf Syntaxknoten basierender sein, und Sie können MethodDeclaration s abonnieren.

Auch in der Codefix die documentationComment kann null sein, und Sie erhalten die Content davon, so dass auch scheitern kann.

Ansonsten fügt Ihr Codefix die aktuelle Uhrzeit hinzu.

Diese erscheinen alle, wenn Sie mit dem Debuggen Ihrer Erweiterung beginnen. Vielleicht sehen Sie sich das Fenster "Ausnahmeneinstellungen" an, um alle CLR-Ausnahmen zu unterbrechen.

+0

das funktioniert nicht. Wenn Sie es als Methodendeklaration angeben, wird es nur als IDE-Fehler und nicht als Buildfehler zurückgegeben. Sollte dies ein semantischerModelAnalysis-Kontext sein? –

+0

Sorry, ich verstehe nicht, was nicht funktioniert. Könnten Sie das Problem ein wenig mehr erklären? – Tamas

+0

Wenn ich Ihnen den Code senden soll, verursacht die Verwendung von methoddeclarationsyntax keinen Build-Fehler. Gibt es eine Möglichkeit, den Code zu teilen? –