【问题标题】:Roslyn, can not get provider to update solution after codefixRoslyn,在代码修复后无法让提供商更新解决方案
【发布时间】:2016-05-04 01:50:13
【问题描述】:

我创建了一个分析器,它将检测一个方法是否在 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 中时如何让构建通过的任何想法?似乎并没有真正更新解决方案?

【问题讨论】:

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


    【解决方案1】:

    context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method) 也会执行属性 getter 和 setter 的回调。您的代码修复在属性上失败,因为它找不到报告位置的 MethodDeclarationSyntax。

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

    另外,在 codefix 中,documentationComment 可以是 null,而你会得到它的 Content,这样也可能会失败。

    除了这些之外,您的代码修复会添加当前时间。

    这些都是在您开始调试扩展程序时出现的。也许看看 Exception Settings 窗口以打破所有 CLR 异常。

    【讨论】:

    • 这不起作用。将其作为方法声明仅将其作为 IDE 错误而不是构建错误返回。这应该是语义模型分析上下文吗?
    • 对不起,我不明白什么不起作用。你能再解释一下这个问题吗?
    • 如果我应该向您发送代码,使用 methoddeclarationsyntax 不会引发构建错误。有什么办法可以分享代码吗?
    • 我继续更新了上面的代码,这样你就可以明白我的意思了
    猜你喜欢
    • 2021-01-04
    • 1970-01-01
    • 1970-01-01
    • 2010-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多