分析器不支持带 ID 的诊断,但它在 SupportedDiagnostics 属性中

问题描述 投票:0回答:0

我有一个 C# 代码分析器基类:

using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using CodeQuality.Shared;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;

namespace CodeQuality
{
    internal abstract class CodeAnalyzerBase<TSyntaxNode> : DiagnosticAnalyzer
        where TSyntaxNode : SyntaxNode
    {
        private readonly object contextLock = new object();
        private SyntaxNodeAnalysisContext context;

        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(this.Rule, DiagnosticDescriptors.AnalyzerExceptionRule);

        /// <summary>The syntax kind to which you want to register the AnalyzeNode method</summary>
        /// <value>The kind of the syntax.</value>
        internal abstract SyntaxKind SyntaxKind { get; }

        protected abstract DiagnosticDescriptor Rule { get; }

        protected SyntaxNodeAnalysisContext Context
        {
            get
            {
                lock (this.contextLock)
                {
                    return this.context;
                }
            }

            set
            {
                lock (this.contextLock)
                {
                    this.context = value;
                }
            }
        }

        public override void Initialize(AnalysisContext context)
        {
            if (!Debugger.IsAttached)
            {
                // we want concurrent execution, but it's annoying while actively debugging
                context.EnableConcurrentExecution(); 
            }

            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);

            context.RegisterSyntaxNodeAction(this.AnalyzeNode, this.SyntaxKind);
        }

        internal void AnalyzeNode(SyntaxNodeAnalysisContext context)
        {
            try
            {
                this.Context = context;

                if (this.Context.Node.SyntaxTree == null)
                {
                    return;
                }

                if (!(this.Context.Node is TSyntaxNode node))
                {
                    return;
                }

                this.AnalyzeNode(node);
            }
            catch (Exception e)
            {
                var location = this.Context.Node.GetLocation();
                var lineSpan = location.GetLineSpan();

                DebugHelper.LogException(
                    e,
                    lineSpan.Path,
                    lineSpan.StartLinePosition.Line,
                    $"Supported Rules: {string.Join(Environment.NewLine, this.SupportedDiagnostics.Select(r => r.Id))}");

                var diagnostic = Diagnostic.Create(
                        DiagnosticDescriptors.AnalyzerExceptionRule,
                        location,
                        $"File: {lineSpan.Path}",
                        $"Line: {lineSpan.StartLinePosition.Line}",
                        $"Exception message: {e.Message}",
                        $"Inner Exception: {e.InnerException}",
                        $"Stack Trace: {e.StackTrace}");

                this.Context.ReportDiagnostic(diagnostic);
            }
        }

        internal abstract void AnalyzeNode(TSyntaxNode node);
    }
}

我有这个基类的几个实现。本来我的父类AnalyzeNode方法中没有try catch,所以当分析器抛出异常时,就会导致被分析项目编译失败。这些分析器对我们的构建过程并非绝对重要,所以我不希望分析器错误阻止整个构建过程继续进行。考虑到这一点,我添加了 try/catch,这意味着创建一个编译器警告,其中包含引发错误的文件、行、消息等数据。如果我看到警告,我知道我需要查看在那个位置/用例并在分析器中处理它。

当我将 DLL 安装到我的解决方案的项目中时,具体的实现确实捕获了它们应该捕获的东西。但是有时会报如下错误:

AD0001 分析器“{此处的具体实现}”引发了类型为“System.ArgumentException”的异常,消息为“分析器不支持 ID 为‘{DiagnosticDescriptors.AnalyzerExceptionRule.Id}’的报告诊断”。

我试过调试,但我似乎从来没有在调试时碰到过 catch 块。此外,我在尝试报告警告诊断之前添加了一行来记录 SupportedDiagnostics,而 Supported 诊断正是我所期望的(该规则定义了每个具体类型和 DiagnosticDescriptors.AnalyzerExceptionRule)。但是,它在尝试报告时仍然会抛出错误。

这里有一个具体实现的简单例子:

// analyzes to ensure there are no empty catch blocks unless they contain comments
[DiagnosticAnalyzer(LanguageNames.CSharp)]
    internal class EmptyCatchBlockAnalyzer : CodeAnalyzerBase<CatchClauseSyntax>
    {
        internal override SyntaxKind SyntaxKind => SyntaxKind.CatchClause;

        protected override DiagnosticDescriptor Rule => DiagnosticDescriptors.EmptyCatchBlockRule;

        internal override void AnalyzeNode(CatchClauseSyntax node)
        {
            if (node.Block == null)
            {
                return;
            }

            if (node.Block.Statements == null)
            {
                return;
            }

            if (!node.Block.Statements.Any() && !node.Block.DescendantTrivia().Any(trivia => trivia.IsKind(SyntaxKind.SingleLineCommentTrivia) || trivia.IsKind(SyntaxKind.MultiLineCommentTrivia)))
            {
                var diagnostic = Diagnostic.Create(this.Rule, node.CatchKeyword.GetLocation());
                this.Context.ReportDiagnostic(diagnostic);
            }
        }
    }

我在这些文件的各个区域做了很多记录,试图了解事情发生的时间。分析器都已正确构造和初始化(我认为),因为当时记录 SupportedDiagnostics 也显示了正确的代码。

基于基类中的代码,我预计具体类中

AnalyzeNoe
执行中的任何错误都会导致警告诊断。这是 catch 块中使用的诊断:

internal static DiagnosticDescriptor AnalyzerExceptionRule => new DiagnosticDescriptor(
          "ID0000",
          "Code analyzer exception",
          $"An error occurred while attempting to run code analysis.",
          DiagnosticCategories.Usage, // "Usage"
          DiagnosticSeverity.Warning,
          isEnabledByDefault: true);

这里有一个诊断来匹配上面的示例具体实现:

internal static DiagnosticDescriptor EmptyCatchBlockRule => new DiagnosticDescriptor(
           "ID0050",
           "Empty Catch Block",
           "Code contains an empty catch block. Add code to handle the error or add a comment justifying the existence of this empty catch block.",
           DiagnosticCategories.Syntax, // "Syntax"
           DiagnosticSeverity.Error,
           isEnabledByDefault: true);
c# visual-studio roslyn .net-standard
© www.soinside.com 2019 - 2024. All rights reserved.