Roslyn,在codefix之后无法获得提供程序更新解决方案

时间:2016-04-21 21:14:22

标签: c# compiler-construction roslyn roslyn-code-analysis

我创建了一个分析器,它将检测方法中是否包含xml中的<createddate>标记以及将注入此标记的提供程序。它工作正常并插入标记,但即使规则已修复,它仍会返回生成错误。我认为我可能需要使用语义模型吗?

这就是我所拥有的: 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;
        }
    }
}

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

关于如何在xml中存在标记时让构建通过的任何想法?好像它没有真正更新解决方案?

1 个答案:

答案 0 :(得分:0)

context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method)也将执行属性getter和setter的回调。您的codefix在属性上失败,因为它无法为报告的位置找到MethodDeclarationSyntax

为什么要将此功能作为基于符号的分析器实现?它可以是基于语法节点的语法节点,您可以订阅MethodDeclaration s。

此外,在codefix中,documentationComment可以是null,并且您获得了Content,因此也可能失败。

除此之外,您的codefix会添加当前时间。

当您开始调试扩展程序时,这些都会出现。也许看一下Exception Settings窗口,打破所有CLR异常。