2016-04-21 1 views
0

J'ai créé un analyseur qui détectera si une méthode ne contient pas une étiquette <createddate> dans le XML et un fournisseur qui injectera cette balise. Cela fonctionne bien et insère le tag mais il renvoie toujours avec l'erreur de construction même si la règle a été corrigée. Je pense que je dois utiliser des modèles sémantiques?Roslyn, ne peut pas obtenir le fournisseur pour mettre à jour la solution après le codefix

C'est ce que j'ai: 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; 
     } 
    } 
} 

Et 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); 
       } 
      } 
     } 
    } 
} 

Des idées quant à la façon de laisser passer de construction lorsque l'étiquette est présente dans le fichier XML ? On dirait que ce n'est pas vraiment la mise à jour de la solution?

Répondre

0

context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method) exécutera également le callback pour les getters et les setters de propriété. Votre code de code échoue sur les propriétés, car il ne peut pas trouver un MethodDeclarationSyntax pour l'emplacement signalé. Pourquoi avez-vous implémenté cette fonctionnalité en tant qu'analyseur basé sur un symbole? Il peut s'agir d'un nœud de syntaxe basé sur un, et vous pouvez vous abonner à MethodDeclaration s.

En outre, dans le codefix, le documentationComment peut être null, et vous obtenez le Content, donc cela peut échouer aussi.

A part cela, votre codefixe ajoute l'heure actuelle.

Tous ces éléments apparaissent lorsque vous commencez à déboguer votre extension. Peut-être jeter un coup d'œil à la fenêtre des paramètres d'exception pour casser toutes les exceptions CLR.

+0

cela ne fonctionne pas. L'avoir en tant que déclaration de méthode ne la renvoie que comme une erreur IDE et non comme une erreur de construction. Est-ce que cela devrait être un contexte semanticModelAnalysis? –

+0

Désolé, je ne comprends pas ce qui ne fonctionne pas. Pourriez-vous expliquer le problème un peu plus? – Tamas

+0

si je devais vous envoyer le code, using methoddeclarationsyntax ne jette pas une erreur de construction. Y a-t-il un moyen de partager le code? –