Skip to content

System.InvalidOperationException : Context: Verifying exclusions in '#pragma warning disable' code #1230

Description

@anthonymastreanvae

Version Used: .NET 8.0

Steps to Reproduce:

For regulatory and compliance reasons, we have an analyzer that adds a diagnostic to uses of #pragma warning disable ....

using System.Collections.Immutable;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace Example.Analyzers;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class PragmaWarningAnalyzer : DiagnosticAnalyzer
{
    internal static DiagnosticDescriptor Rule = new(
        id: "EX0001",
        title: "Do not use pragma statements to suppress analyzers",
        messageFormat: "Diagnostic(s) '{0}' are suppressed using a pragma statement. Use a global suppression instead.",
        category: "Reliability",
        defaultSeverity: DiagnosticSeverity.Warning,
        isEnabledByDefault: true
    );

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Rule];

    public override void Initialize(AnalysisContext context)
    {
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.EnableConcurrentExecution();
        context.RegisterSyntaxNodeAction(AnalyzeSyntaxNode, SyntaxKind.PragmaWarningDirectiveTrivia);
    }

    private static void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context)
    {
        var pragmaWarningDirective = (PragmaWarningDirectiveTriviaSyntax)context.Node;
        if (!pragmaWarningDirective.DisableOrRestoreKeyword.IsKind(SyntaxKind.DisableKeyword))
        {
            return;
        }

        foreach (var errorCode in pragmaWarningDirective.ErrorCodes)
        {
            var diagnostic = Diagnostic.Create(Rule, errorCode.GetLocation(), errorCode.ToString());

            context.ReportDiagnostic(diagnostic);
        }
    }
}

I wrote a CSharp Analyzer Verifier test case.

using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Example.Analyzers.PragmaWarningAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;

namespace Example.Analyzers.Tests;

public class PragmaWarningAnalyzerTests
{
    [Fact]
    public async Task ITPIE0001()
    {
        const string source = """
            #pragma warning disable CS0168
            class C
            {
                void M()
                {
                    int unused;
                }
            }
            #pragma warning restore CS0168
            """;

        await Verify.VerifyAnalyzerAsync(source);
    }
}

It correctly identifies the location of an unexpected diagnostic.

System.InvalidOperationException : Mismatch between number of diagnostics returned, expected "0" actual "1"

Diagnostics:
// /0/Test0.cs(1,25): warning EX0001: Diagnostic(s) 'CS0168' are suppressed using a pragma statement. Use a global suppression instead.
VerifyCS.Diagnostic().WithSpan(1, 25, 1, 31).WithArguments("CS0168"),

Expected Behavior:

I expected to add the expected diagnostic:

        // /0/Test0.cs(1,25): warning EX0001: Diagnostic(s) 'CS0168' are suppressed using a pragma statement. Use a global suppression instead.
        var expected = Verify.Diagnostic("EX0001").WithSpan(1, 25, 1, 31).WithArguments("CS0168");

        await Verify.VerifyAnalyzerAsync(source, expected);

Or to use one of the marker syntaxes:

        const string source = """
            #pragma warning disable [|CS0168|]
            ...
            """;
        const string source = """
            #pragma warning disable {|EX0001:CS0168|}
            ...
            """;

Actual Behavior:

No matter how I provided or marked the diagnostic, I got an exception where MY diagnostic ID is now switched into the place of the argument!

System.InvalidOperationException : Context: Verifying exclusions in '#pragma warning disable' code
Mismatch between number of diagnostics returned, expected "0" actual "1"

Diagnostics:
// /0/Test0.cs(1,25): warning EX0001: Diagnostic(s) 'EX0001' are suppressed using a pragma statement. Use a global suppression instead.
VerifyCS.Diagnostic().WithSpan(1, 25, 1, 34).WithArguments("EX0001"),

I think something is amiss with a parser somewhere.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions