File: Diagnostics\Suppression\RemoveUnnecessaryPragmaSuppressionsTests.cs
Web Access
Project: src\src\Features\CSharpTest\Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Features.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.RemoveUnnecessarySuppressions;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.CSharp.UseAutoProperty;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics.CSharp;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.RemoveUnnecessarySuppressions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
using Xunit.Abstractions;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveUnnecessarySuppressions;
 
[Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessarySuppressions)]
[WorkItem("https://github.com/dotnet/roslyn/issues/44177")]
public abstract class RemoveUnnecessaryInlineSuppressionsTests(ITestOutputHelper logger) : AbstractUnnecessarySuppressionDiagnosticTest(logger)
{
    #region Helpers
 
    internal sealed override CodeFixProvider CodeFixProvider
        => new RemoveUnnecessaryInlineSuppressionsCodeFixProvider();
    internal sealed override AbstractRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer SuppressionAnalyzer
        => new CSharpRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer();
 
    protected sealed override ParseOptions GetScriptOptions() => Options.Script;
    protected internal sealed override string GetLanguage() => LanguageNames.CSharp;
 
    protected override TestParameters SetParameterDefaults(TestParameters parameters)
        => parameters.WithCompilationOptions((parameters.compilationOptions ?? TestOptions.DebugDll).WithReportSuppressedDiagnostics(true));
 
    protected sealed class UserDiagnosticAnalyzer : DiagnosticAnalyzer
    {
        public static readonly DiagnosticDescriptor Descriptor0168 =
            new DiagnosticDescriptor("Analyzer0168", "Variable is declared but never used", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true);
        public static readonly DiagnosticDescriptor Descriptor0219 =
            new DiagnosticDescriptor("Analyzer0219", "Variable is assigned but its value is never used", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true);
 
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Descriptor0168, Descriptor0219];
 
        public override void Initialize(AnalysisContext context)
        {
            context.RegisterOperationBlockStartAction(
                context =>
                {
                    var localsToIsAssignedMap = new ConcurrentDictionary<ILocalSymbol, bool>();
                    var usedLocals = new HashSet<ILocalSymbol>();
                    context.RegisterOperationAction(
                        context =>
                        {
                            var declarator = (IVariableDeclaratorOperation)context.Operation;
                            var hasInitializer = declarator.GetVariableInitializer() != null;
                            localsToIsAssignedMap.GetOrAdd(declarator.Symbol, hasInitializer);
                        }, OperationKind.VariableDeclarator);
 
                    context.RegisterOperationAction(
                        context =>
                        {
                            var localReference = (ILocalReferenceOperation)context.Operation;
                            if (localReference.Parent is ISimpleAssignmentOperation simpleAssignment &&
                                simpleAssignment.Target == localReference)
                            {
                                localsToIsAssignedMap.AddOrUpdate(localReference.Local, true, (_1, _2) => true);
                            }
                            else
                            {
                                usedLocals.Add(localReference.Local);
                            }
                        }, OperationKind.LocalReference);
 
                    context.RegisterOperationBlockEndAction(
                        context =>
                        {
                            foreach (var (local, isAssigned) in localsToIsAssignedMap)
                            {
                                if (usedLocals.Contains(local))
                                {
                                    continue;
                                }
 
                                var rule = !isAssigned ? Descriptor0168 : Descriptor0219;
                                context.ReportDiagnostic(Diagnostic.Create(rule, local.Locations[0]));
                            }
                        });
                });
        }
    }
 
    protected sealed class CompilationEndDiagnosticAnalyzer : DiagnosticAnalyzer
    {
        public static readonly DiagnosticDescriptor Descriptor =
            new DiagnosticDescriptor("CompilationEndId", "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true,
                customTags: [WellKnownDiagnosticTags.CompilationEnd]);
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Descriptor];
        public override void Initialize(AnalysisContext context)
            => context.RegisterCompilationStartAction(context => context.RegisterCompilationEndAction(_ => { }));
    }
 
    #endregion
 
    #region Single analyzer tests (Compiler OR Analyzer)
 
    public abstract class CompilerOrAnalyzerTests(ITestOutputHelper logger) : RemoveUnnecessaryInlineSuppressionsTests(logger)
    {
        protected abstract bool IsCompilerDiagnosticsTest { get; }
        protected abstract string VariableDeclaredButNotUsedDiagnosticId { get; }
        protected abstract string VariableAssignedButNotUsedDiagnosticId { get; }
        protected abstract ImmutableArray<string> UnsupportedDiagnosticIds { get; }
 
        public sealed class CompilerTests(ITestOutputHelper logger) : CompilerOrAnalyzerTests(logger)
        {
            internal override ImmutableArray<DiagnosticAnalyzer> OtherAnalyzers
                => [new CSharpCompilerDiagnosticAnalyzer()];
 
            protected override bool IsCompilerDiagnosticsTest => true;
            protected override string VariableDeclaredButNotUsedDiagnosticId => "CS0168";
            protected override string VariableAssignedButNotUsedDiagnosticId => "CS0219";
            protected override ImmutableArray<string> UnsupportedDiagnosticIds
            {
                get
                {
                    var errorCodes = Enum.GetValues(typeof(ErrorCode));
                    var supported = ((CSharpCompilerDiagnosticAnalyzer)OtherAnalyzers[0]).GetSupportedErrorCodes();
                    using var _ = ArrayBuilder<string>.GetInstance(out var builder);
                    foreach (int errorCode in errorCodes)
                    {
                        if (!supported.Contains(errorCode) && errorCode > 0)
                        {
                            // Add all 3 supported formats for suppressions: integer, integer with leading zeros, "CS" prefix
                            var errorCodeString = errorCode.ToString();
                            var errorCodeD4String = errorCode.ToString("D4");
                            builder.Add(errorCodeString);
                            if (errorCodeD4String != errorCodeString)
                                builder.Add(errorCodeD4String);
                            builder.Add("CS" + errorCodeD4String);
                        }
                    }
 
                    return builder.ToImmutableAndClear();
                }
            }
        }
 
        public sealed class AnalyzerTests(ITestOutputHelper logger) : CompilerOrAnalyzerTests(logger)
        {
            internal override ImmutableArray<DiagnosticAnalyzer> OtherAnalyzers
                => [new UserDiagnosticAnalyzer(), new CompilationEndDiagnosticAnalyzer()];
            protected override bool IsCompilerDiagnosticsTest => false;
            protected override string VariableDeclaredButNotUsedDiagnosticId => UserDiagnosticAnalyzer.Descriptor0168.Id;
            protected override string VariableAssignedButNotUsedDiagnosticId => UserDiagnosticAnalyzer.Descriptor0219.Id;
            protected override ImmutableArray<string> UnsupportedDiagnosticIds
                => [
                    CompilationEndDiagnosticAnalyzer.Descriptor.Id,
                    IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId,
                    IDEDiagnosticIds.FormattingDiagnosticId,
                    "format",
                ];
        }
 
        [Fact]
        public Task TestDoNotRemoveRequiredDiagnosticSuppression_Pragma()
            => TestMissingInRegularAndScriptAsync(
    $$"""

    class Class
    {
        void M()
        {
    [|#pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Necessary
            int y;
    #pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Necessary|]
        }
    }
    """);
 
        [Fact]
        public Task TestDoNotRemoveRequiredDiagnosticSuppression_Pragma_02()
            => TestMissingInRegularAndScriptAsync(
    $$"""

    [|#pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Necessary|]
    class Class
    {
        void M()
        {
            int y;
        }
    }
    """);
 
        [Fact]
        public async Task TestDoNotRemoveRequiredDiagnosticSuppression_Attribute_Method()
        {
            var code = $$"""

                class Class
                {
                    [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")]|]
                    void M()
                    {
                        int y;
                    }
                }
                """;
            // Compiler diagnostics cannot be suppressed with SuppressMessageAttribute.
            // Hence, attribute suppressions for compiler diagnostics are always unnecessary.
            if (!IsCompilerDiagnosticsTest)
            {
                await TestMissingInRegularAndScriptAsync(code);
            }
            else
            {
                await TestInRegularAndScript1Async(code, """
 
                    class Class
                    {
                        void M()
                        {
                            int y;
                        }
                    }
                    """);
            }
        }
 
        [Fact]
        public async Task TestDoNotRemoveRequiredDiagnosticSuppression_Attribute_02()
        {
            var code = $$"""

                [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")]|]
                class Class
                {
                    void M()
                    {
                        int y;
                    }
                }
                """;
            // Compiler diagnostics cannot be suppressed with SuppressMessageAttribute.
            // Hence, attribute suppressions for compiler diagnostics are always unnecessary.
            if (!IsCompilerDiagnosticsTest)
            {
                await TestMissingInRegularAndScriptAsync(code);
            }
            else
            {
                await TestInRegularAndScript1Async(code, """
 
                    class Class
                    {
                        void M()
                        {
                            int y;
                        }
                    }
                    """);
            }
        }
 
        public enum TestKind
        {
            Pragmas,
            SuppressMessageAttributes,
            PragmasAndSuppressMessageAttributes
        }
 
        [Theory, CombinatorialData]
        [WorkItem("https://github.com/dotnet/roslyn/issues/46047")]
        public async Task TestDoNotRemoveUnsupportedDiagnosticSuppression(bool disable, TestKind testKind)
        {
            var disableOrRestore = disable ? "disable" : "restore";
            var pragmas = new StringBuilder();
            var suppressMessageAttribtes = new StringBuilder();
            foreach (var id in UnsupportedDiagnosticIds)
            {
                if (testKind is TestKind.Pragmas or TestKind.PragmasAndSuppressMessageAttributes)
                    pragmas.AppendLine($@"#pragma warning {disableOrRestore} {id}");
 
                if (testKind is TestKind.SuppressMessageAttributes or TestKind.PragmasAndSuppressMessageAttributes)
                    suppressMessageAttribtes.AppendLine($@"[System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""{id}"")]");
            }
 
            var source = $@"{{|FixAllInDocument:{pragmas}{suppressMessageAttribtes}|}}class Class {{ }}";
 
            // Compiler diagnostics cannot be suppressed with SuppressMessageAttribute.
            // Hence, attribute suppressions for compiler diagnostics are always unnecessary.
            if (!IsCompilerDiagnosticsTest || testKind == TestKind.Pragmas)
            {
                await TestMissingInRegularAndScriptAsync(source);
            }
            else
            {
                await TestInRegularAndScriptAsync(source, $@"{pragmas}class Class {{ }}");
            }
        }
 
        [Fact]
        public Task TestDoNotRemoveInactiveDiagnosticSuppression()
            => TestMissingInRegularAndScriptAsync(
    $$"""

    #if false
    [|
    class Class
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")]
        void M()
        {
    #pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Inactive
            int y;
    #pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Inactive
            y = 1;
        }
    }
    |]
    #endif
    """);
 
        [Fact]
        public Task TestDoNotRemoveDiagnosticSuppressionsInCodeWithSyntaxErrors()
            => TestMissingInRegularAndScriptAsync(
    $$"""

    [|
    class Class
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")]
        void M()
        {
    #pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used
            int y   // CS1002: ; expected
    #pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used
            y = 1;
        }
    }
    |]
    """);
 
        [Fact]
        public Task TestDoNotRemoveDiagnosticSuppressionWhenAnalyzerSuppressed()
            => TestMissingInRegularAndScriptAsync(
    $$"""

    #pragma warning disable {{IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId}}
    [|
    class Class
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")]
        void M()
        {
    #pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary, but suppressed
            int y;
    #pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary, but suppressed
            y = 1;
        }
    }
    |]
    """);
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46075")]
        public Task TestDoNotRemoveDiagnosticSuppressionInGeneratedCode()
            => TestMissingInRegularAndScriptAsync(
    $$"""

    // <autogenerated>
    [|
    class Class
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")] // Variable is declared but never used - Unnecessary, but not reported in generated code
        void M()
        {
    #pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary, but not reported in generated code
            int y;
    #pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary, but not reported in generated code
            y = 1;
        }
    }
    |]
    """);
 
        [Theory, CombinatorialData]
        public async Task TestDoNotRemoveExcludedDiagnosticSuppression(bool excludeAll)
        {
            var options = new OptionsCollection(LanguageNames.CSharp)
            {
                { CodeStyleOptions2.RemoveUnnecessarySuppressionExclusions, excludeAll ? "all" : VariableDeclaredButNotUsedDiagnosticId }
            };
 
            await TestMissingInRegularAndScriptAsync(
    $$"""

    [|
    class Class
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")]
        void M()
        {
    #pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary, but suppressed
            int y;
    #pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary, but suppressed
            y = 1;
        }
    }
    |]
    """, new TestParameters(options: options));
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47288")]
        public async Task TestDoNotRemoveExcludedDiagnosticCategorySuppression()
        {
            var options = new OptionsCollection(LanguageNames.CSharp)
            {
                { CodeStyleOptions2.RemoveUnnecessarySuppressionExclusions, "category: ExcludedCategory" }
            };
 
            await TestMissingInRegularAndScriptAsync(
    $$"""

    [|
    class Class
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("ExcludedCategory", "{{VariableDeclaredButNotUsedDiagnosticId}}")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("ExcludedCategory", "{{VariableAssignedButNotUsedDiagnosticId}}")]
        void M()
        {
            int y;
            y = 1;
 
            int z = 1;
            z++;
        }
    }
    |]
    """, new TestParameters(options: options));
        }
 
        [Theory]
        [InlineData("event", "EventHandler")]
        [InlineData("static", "int")]
        [WorkItem("https://github.com/dotnet/roslyn/issues/78786")]
        public Task TestRemoveDiagnosticSuppression_Attribute_MultiVariableDeclaration(string keyword, string type)
            => TestInRegularAndScript1Async(
                $$"""
                public class C
                {
                    [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1720:Identifier contains type name", Justification = "<Pending>")]|]
                    public {{keyword}} {{type}} A, B;
                }
                """,
                $$"""
                public class C
                {
                    public {{keyword}} {{type}} A, B;
                }
                """);
 
        [Fact]
        [WorkItem("https://github.com/dotnet/roslyn/issues/78786")]
        public Task TestRemoveDiagnosticSuppression_Attribute_PartialMethodDefinition()
            => TestInRegularAndScript1Async(
                """
                public partial class C
                {
                    [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1720:Identifier contains type name", Justification = "<Pending>")]|]
                    partial void M();
                }
 
                public partial class C
                {
                    partial void M()
                    {
                    }
                }
                """,
                """
                public partial class C
                {
                    partial void M();
                }
                
                public partial class C
                {
                    partial void M()
                    {
                    }
                }
                """);
 
        [Fact]
        [WorkItem("https://github.com/dotnet/roslyn/issues/78786")]
        public Task TestRemoveDiagnosticSuppression_Attribute_PartialMethodImplementation()
            => TestInRegularAndScript1Async(
                """
                public partial class C
                {
                    partial void M();
                }
 
                public partial class C
                {
                    [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1720:Identifier contains type name", Justification = "<Pending>")]|]
                    partial void M()
                    {
                    }
                }
                """,
                """
                public partial class C
                {
                    partial void M();
                }
                
                public partial class C
                {
                    partial void M()
                    {
                    }
                }
                """);
 
        [Fact]
        [WorkItem("https://github.com/dotnet/roslyn/issues/78786")]
        public Task TestRemoveDiagnosticSuppression_Attribute_PartialPropertyDefinition()
            => TestInRegularAndScript1Async(
                """
                public partial class C
                {
                    [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1720:Identifier contains type name", Justification = "<Pending>")]|]
                    partial int P { get; }
                }
                
                public partial class C
                {
                    partial int P => 5230;
                }
                """,
                """
                public partial class C
                {
                    partial int P { get; }
                }
                
                public partial class C
                {
                    partial int P => 5230;
                }
                """);
 
        [Fact]
        [WorkItem("https://github.com/dotnet/roslyn/issues/78786")]
        public Task TestRemoveDiagnosticSuppression_Attribute_PartialPropertyImplementation()
            => TestInRegularAndScript1Async(
                """
                public partial class C
                {
                    partial int P { get; }
                }
 
                public partial class C
                {
                    [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1720:Identifier contains type name", Justification = "<Pending>")]|]
                    partial int P => 5230;
                }
                """,
                """
                public partial class C
                {
                    partial int P { get; }
                }
                
                public partial class C
                {
                    partial int P => 5230;
                }
                """);
 
        [Fact]
        public Task TestDoNotRemoveDiagnosticSuppression_Attribute_OnPartialDeclarations()
            => TestMissingInRegularAndScriptAsync(
    $$"""

    [|
    // Unnecessary, but we do not perform analysis for SuppressMessageAttributes on partial declarations.
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")]
    partial class Class
    {
    }
 
    partial class Class
    {
        void M()
        {
            int y;
            y = 1;
        }
    }
    |]
    """);
 
        [Theory, CombinatorialData]
        public async Task TestRemoveDiagnosticSuppression_Pragma(bool testFixFromDisable)
        {
            var (disablePrefix, disableSuffix, restorePrefix, restoreSuffix) = testFixFromDisable
                ? ("[|", "|]", "", "")
                : ("", "", "[|", "|]");
 
            await TestInRegularAndScript1Async(
    $$"""

    class Class
    {
        void M()
        {
    {{disablePrefix}}#pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary{{disableSuffix}}
            int y;
    {{restorePrefix}}#pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary{{restoreSuffix}}
            y = 1;
        }
    }
    """,
    """
 
    class Class
    {
        void M()
        {
            int y;
            y = 1;
        }
    }
    """);
        }
 
        [Fact]
        public Task TestRemoveDiagnosticSuppression_Attribute()
            => TestInRegularAndScript1Async(
    $$"""

    class Class
    {
        [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")]|] // Variable is declared but never used - Unnecessary
        void M()
        {
            int y;
            y = 1;
        }
    }
    """,
    """
 
    class Class
    {
        void M()
        {
            int y;
            y = 1;
        }
    }
    """);
 
        [Fact]
        public Task TestRemoveDiagnosticSuppression_Attribute_Trivia()
            => TestInRegularAndScript1Async(
    $$"""

    class Class
    {
        // Comment1
        /// <summary>
        /// DocComment
        /// </summary>
        // Comment2
        [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")]|] // Comment3
        // Comment4
        void M()
        {
            int y;
            y = 1;
        }
    }
    """,
    """
 
    class Class
    {
        // Comment1
        /// <summary>
        /// DocComment
        /// </summary>
        // Comment2
        // Comment4
        void M()
        {
            int y;
            y = 1;
        }
    }
    """);
 
        [Fact]
        public Task TestRemoveDiagnosticSuppression_OnlyDisableDirective()
            => TestInRegularAndScript1Async(
    $$"""

    class Class
    {
        void M()
        {
    [|#pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary|]
            int y;
            y = 1;
        }
    }
    """,
    """
 
    class Class
    {
        void M()
        {
            int y;
            y = 1;
        }
    }
    """);
 
        [Fact]
        public Task TestRemoveDiagnosticSuppression_OnlyRestoreDirective()
            => TestInRegularAndScript1Async(
    $$"""

    class Class
    {
        void M()
        {
            int y;
    [|#pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary|]
            y = 1;
        }
    }
    """,
    """
 
    class Class
    {
        void M()
        {
            int y;
            y = 1;
        }
    }
    """);
 
        [Theory, CombinatorialData]
        public async Task TestRemoveDiagnosticSuppression_DuplicatePragmaSuppression(bool testFixFromDisable)
        {
            var (disablePrefix, disableSuffix, restorePrefix, restoreSuffix) = testFixFromDisable
                ? ("[|", "|]", "", "")
                : ("", "", "[|", "|]");
 
            await TestInRegularAndScript1Async(
    $$"""

    class Class
    {
    {{disablePrefix}}#pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary{{disableSuffix}}
        void M()
        {
    #pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Necessary
            int y;
    #pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Necessary
        }
    {{restorePrefix}}#pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary{{restoreSuffix}}
    }
    """,
    $$"""

    class Class
    {
        void M()
        {
    #pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Necessary
            int y;
    #pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Necessary
        }
    }
    """);
        }
 
        [Fact]
        public async Task TestRemoveDiagnosticSuppression_DuplicateAttributeSuppression()
        {
            // Compiler diagnostics cannot be suppressed with SuppressMessageAttribute.
            // Hence, attribute suppressions for compiler diagnostics are always unnecessary.
            var retainedAttributesInFixCode = IsCompilerDiagnosticsTest
                ? string.Empty
                : $"""
                [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{VariableDeclaredButNotUsedDiagnosticId}")] // Variable is declared but never used - Necessary
                    
                """;
 
            await TestInRegularAndScript1Async(
                $$"""

                class Class
                {
                    [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")] // Variable is declared but never used - Necessary
                    {|FixAllInDocument:[System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")] // Variable is declared but never used - Unnecessary|}
                    void M()
                    {
                        int y;
                    }
                }
                """,
                $$"""

                class Class
                {
                    {{retainedAttributesInFixCode}}void M()
                    {
                        int y;
                    }
                }
                """);
        }
 
        [Fact]
        public async Task TestRemoveDiagnosticSuppression_DuplicateAttributeSuppression_OnContainingSymbol()
        {
            // Compiler diagnostics cannot be suppressed with SuppressMessageAttribute.
            // Hence, attribute suppressions for compiler diagnostics are always unnecessary.
            var retainedAttributesInFixCode = IsCompilerDiagnosticsTest
                ? string.Empty
                : $"""
                [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{VariableDeclaredButNotUsedDiagnosticId}")] // Variable is declared but never used - Necessary
                    
                """;
 
            await TestInRegularAndScript1Async(
                $$"""

                {|FixAllInDocument:[System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")] // Variable is declared but never used - Unnecessary|}
                class Class
                {
                    [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")] // Variable is declared but never used - Necessary
                    void M()
                    {
                        int y;
                    }
                }
                """,
                $$"""

                class Class
                {
                    {{retainedAttributesInFixCode}}void M()
                    {
                        int y;
                    }
                }
                """);
        }
 
        [Fact]
        public async Task TestRemoveDiagnosticSuppression_DuplicatePragmaAndAttributeSuppression()
        {
            string fixedSource;
            if (IsCompilerDiagnosticsTest)
            {
                // Compiler diagnostics cannot be suppressed with SuppressMessageAttribute.
                // Hence, attribute suppressions for compiler diagnostics are always unnecessary.
                fixedSource = $$"""

                    class Class
                    {
                        void M()
                        {
                    #pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}}
                            int y;
                    #pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}}
                        }
                    }
                    """;
            }
            else
            {
                // Analyzer diagnostics can be suppressed with both SuppressMessageAttribute and pragmas.
                // SuppressMessageAttribute takes precedence over pragmas for duplicate suppressions,
                // hence duplicate pragmas are considered unnecessary.
                fixedSource = $$"""

                    class Class
                    {
                        [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")]
                        void M()
                        {
                            int y;
                        }
                    }
                    """;
            }
 
            await TestInRegularAndScript1Async($$"""

                class Class
                {
                    [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")]
                    void M()
                    {
                #pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}}|]
                        int y;
                #pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}}
                    }
                }
                """, fixedSource);
        }
 
        [Theory, CombinatorialData]
        public async Task TestRemoveDiagnosticSuppression_Pragma_InnerValidSuppression(bool testFixFromDisable)
        {
            var (disablePrefix, disableSuffix, restorePrefix, restoreSuffix) = testFixFromDisable
                ? ("[|", "|]", "", "")
                : ("", "", "[|", "|]");
 
            await TestInRegularAndScript1Async(
    $$"""

    class Class
    {
        void M()
        {
    {{disablePrefix}}#pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary{{disableSuffix}}
    #pragma warning disable {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
            int y = 0;
    #pragma warning restore {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
    {{restorePrefix}}#pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary{{restoreSuffix}}
        }
    }
    """,
    $$"""

    class Class
    {
        void M()
        {
    #pragma warning disable {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
            int y = 0;
    #pragma warning restore {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
        }
    }
    """);
        }
 
        [Fact]
        public Task TestRemoveDiagnosticSuppression_Attribute_InnerValidSuppression()
            => TestInRegularAndScript1Async(
    $$"""

    class Class
    {
        [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")]|] // Variable is declared but never used - Unnecessary
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableAssignedButNotUsedDiagnosticId}}")] // Variable is assigned but its value is never used - Necessary
        void M()
        {
            int y = 0;
        }
    }
    """,
    $$"""

    class Class
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableAssignedButNotUsedDiagnosticId}}")] // Variable is assigned but its value is never used - Necessary
        void M()
        {
            int y = 0;
        }
    }
    """);
 
        [Theory, CombinatorialData]
        public async Task TestRemoveDiagnosticSuppression_Pragma_OuterValidSuppression(bool testFixFromDisable)
        {
            var (disablePrefix, disableSuffix, restorePrefix, restoreSuffix) = testFixFromDisable
                ? ("[|", "|]", "", "")
                : ("", "", "[|", "|]");
 
            await TestInRegularAndScript1Async(
    $$"""

    class Class
    {
        void M()
        {
    #pragma warning disable {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
    {{disablePrefix}}#pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary{{disableSuffix}}
            int y = 0;
    {{restorePrefix}}#pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary{{restoreSuffix}}
    #pragma warning restore {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
        }
    }
    """,
    $$"""

    class Class
    {
        void M()
        {
    #pragma warning disable {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
            int y = 0;
    #pragma warning restore {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
        }
    }
    """);
        }
 
        [Fact]
        public Task TestRemoveDiagnosticSuppression_Attribute_OuterValidSuppression()
            => TestInRegularAndScript1Async(
    $$"""

    class Class
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableAssignedButNotUsedDiagnosticId}}")] // Variable is assigned but its value is never used - Necessary
        [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableDeclaredButNotUsedDiagnosticId}}")]|] // Variable is declared but never used - Unnecessary
        void M()
        {
            int y = 0;
        }
    }
    """,
    $$"""

    class Class
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{VariableAssignedButNotUsedDiagnosticId}}")] // Variable is assigned but its value is never used - Necessary
        void M()
        {
            int y = 0;
        }
    }
    """);
 
        [Theory, CombinatorialData]
        public async Task TestRemoveDiagnosticSuppression_OverlappingDirectives(bool testFixFromDisable)
        {
            var (disablePrefix, disableSuffix, restorePrefix, restoreSuffix) = testFixFromDisable
                ? ("[|", "|]", "", "")
                : ("", "", "[|", "|]");
 
            await TestInRegularAndScript1Async(
    $$"""

    class Class
    {
        void M()
        {
    {{disablePrefix}}#pragma warning disable {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary{{disableSuffix}}
    #pragma warning disable {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
            int y = 0;
    {{restorePrefix}}#pragma warning restore {{VariableDeclaredButNotUsedDiagnosticId}} // Variable is declared but never used - Unnecessary{{restoreSuffix}}
    #pragma warning restore {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
        }
    }
    """,
    $$"""

    class Class
    {
        void M()
        {
    #pragma warning disable {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
            int y = 0;
    #pragma warning restore {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
        }
    }
    """);
        }
 
        [Fact]
        public Task TestRemoveDiagnosticSuppression_DuplicateDisableWithoutMatchingRestoreDirective()
            => TestInRegularAndScript1Async(
    $$"""

    class Class
    {
        void M()
        {
    [|#pragma warning disable {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Unnecessary|]
    #pragma warning disable {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
            int y = 0;
    #pragma warning restore {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
        }
    }
    """,
    $$"""

    class Class
    {
        void M()
        {
    #pragma warning disable {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
            int y = 0;
    #pragma warning restore {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
        }
    }
    """);
 
        [Fact]
        public Task TestRemoveDiagnosticSuppression_DuplicateRestoreWithoutMatchingDisableDirective()
            => TestInRegularAndScript1Async(
    $$"""

    class Class
    {
        void M()
        {
    #pragma warning disable {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
            int y = 0;
    #pragma warning restore {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
    [|#pragma warning restore {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Unnecessary|]
        }
    }
    """,
    $$"""

    class Class
    {
        void M()
        {
    #pragma warning disable {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
            int y = 0;
    #pragma warning restore {{VariableAssignedButNotUsedDiagnosticId}} // Variable is assigned but its value is never used - Necessary
        }
    }
    """);
 
        [Theory, CombinatorialData]
        public async Task TestRemoveUnknownDiagnosticSuppression_Pragma(bool testFixFromDisable)
        {
            var (disablePrefix, disableSuffix, restorePrefix, restoreSuffix) = testFixFromDisable
                ? ("[|", "|]", "", "")
                : ("", "", "[|", "|]");
 
            await TestInRegularAndScript1Async(
    $$"""

    {{disablePrefix}}#pragma warning disable UnknownId{{disableSuffix}}
    class Class
    {{restorePrefix}}#pragma warning restore UnknownId{{restoreSuffix}}
    {
    }
    """,
    """
 
    class Class
    {
    }
    """);
        }
 
        [Fact]
        public Task TestRemoveUnknownDiagnosticSuppression_Attribute()
            => TestInRegularAndScript1Async(
    """
 
    [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "UnknownId")]|]
    class Class
    {
    }
    """,
    """
 
    class Class
    {
    }
    """);
    }
 
    #endregion
 
    #region Multiple analyzer tests (Compiler AND Analyzer)
 
    public sealed class CompilerAndAnalyzerTests(ITestOutputHelper logger) : RemoveUnnecessaryInlineSuppressionsTests(logger)
    {
        internal override ImmutableArray<DiagnosticAnalyzer> OtherAnalyzers
            => [new CSharpCompilerDiagnosticAnalyzer(), new UserDiagnosticAnalyzer()];
 
        [Fact]
        public Task TestDoNotRemoveInvalidDiagnosticSuppression()
            => TestMissingInRegularAndScriptAsync(
    $$"""

    class Class
    {
        void M()
        {
    [|#pragma warning disable
            int y;
    #pragma warning restore |]
            y = 1;
        }
    }

    """);
 
        [Fact]
        public async Task TestDoNotRemoveDiagnosticSuppressionsForSuppressedAnalyzer()
        {
            var source = $$"""

                [|class Class
                {
                    [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "CS0168")] // Variable is declared but never used - Unnecessary, but suppressed
                    [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{UserDiagnosticAnalyzer.Descriptor0168.Id}}")] // Variable is declared but never used - Unnecessary, but suppressed
                    void M()
                    {
                #pragma warning disable CS0168 // Variable is declared but never used - Unnecessary, but suppressed
                #pragma warning disable {{UserDiagnosticAnalyzer.Descriptor0168.Id}} // Variable is declared but never used - Unnecessary, but suppressed
                        int y;
                #pragma warning restore {{UserDiagnosticAnalyzer.Descriptor0168.Id}} // Variable is declared but never used - Unnecessary, but suppressed
                #pragma warning restore CS0168 // Variable is declared but never used - Unnecessary, but suppressed
                        y = 1;
                    }
                }|]
                """;
            var parameters = new TestParameters();
            using var workspace = CreateWorkspaceFromOptions(source, parameters);
 
            // Suppress the diagnostic in options.
            var projectId = workspace.Projects[0].Id;
            var compilationOptions = TestOptions.DebugDll.WithSpecificDiagnosticOptions(
            ImmutableDictionary<string, ReportDiagnostic>.Empty
                .Add(IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId, ReportDiagnostic.Suppress));
            workspace.SetCurrentSolution(s => s.WithProjectCompilationOptions(projectId, compilationOptions), WorkspaceChangeKind.ProjectChanged, projectId);
 
            var (actions, _) = await GetCodeActionsAsync(workspace, parameters);
            Assert.True(actions.Length == 0, "An action was offered when none was expected");
        }
 
        [Theory, CombinatorialData]
        public async Task TestDoNotRemoveCompilerDiagnosticSuppression_IntegerId(bool leadingZero)
        {
            var id = leadingZero ? "0168" : "168";
            await TestMissingInRegularAndScriptAsync(
    $$"""

    class Class
    {
        void M()
        {
    [|#pragma warning disable {{id}} // Variable is declared but never used - Necessary
            int y;
    #pragma warning restore {{id}} // Variable is declared but never used - Necessary|]
        }
    }
    """);
        }
 
        [Theory, CombinatorialData]
        public async Task TestRemoveCompilerDiagnosticSuppression_IntegerId(bool leadingZero)
        {
            var id = leadingZero ? "0168" : "168";
            await TestInRegularAndScript1Async(
    $$"""

    class Class
    {
        void M()
        {
    [|#pragma warning disable {{id}} // Variable is declared but never used - Unnecessary|]
            int y;
    #pragma warning restore {{id}} // Variable is declared but never used - Unnecessary
            y = 1;
        }
    }
    """,
    """
 
    class Class
    {
        void M()
        {
            int y;
            y = 1;
        }
    }
    """);
        }
 
        [Theory, CombinatorialData]
        public async Task TestDoNotRemoveExcludedDiagnosticSuppression_Multiple(bool excludeAll)
        {
            var options = new OptionsCollection(LanguageNames.CSharp)
            {
                { CodeStyleOptions2.RemoveUnnecessarySuppressionExclusions, excludeAll ? "all" : $"CS0168, {UserDiagnosticAnalyzer.Descriptor0168.Id}" }
            };
 
            await TestMissingInRegularAndScriptAsync(
    $$"""

    [|class Class
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "CS0168")] // Variable is declared but never used - Unnecessary, but suppressed
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{UserDiagnosticAnalyzer.Descriptor0168.Id}}")] // Variable is declared but never used - Unnecessary, but suppressed
        void M()
        {
    #pragma warning disable CS0168 // Variable is declared but never used - Unnecessary, but suppressed
    #pragma warning disable {{UserDiagnosticAnalyzer.Descriptor0168.Id}} // Variable is declared but never used - Unnecessary, but suppressed
            int y;
    #pragma warning restore {{UserDiagnosticAnalyzer.Descriptor0168.Id}} // Variable is declared but never used - Unnecessary, but suppressed
    #pragma warning restore CS0168 // Variable is declared but never used - Unnecessary, but suppressed
            y = 1;
        }
    }|]
    """, new TestParameters(options: options));
        }
 
        [Theory, CombinatorialData]
        public async Task TestDoNotRemoveExcludedDiagnosticSuppression_Subset(bool suppressCompilerDiagnostic, bool testDisableDirective)
        {
            var (disabledId, enabledId) = suppressCompilerDiagnostic
                ? ("CS0168", UserDiagnosticAnalyzer.Descriptor0168.Id)
                : (UserDiagnosticAnalyzer.Descriptor0168.Id, "CS0168");
 
            var options = new OptionsCollection(LanguageNames.CSharp)
            {
                { CodeStyleOptions2.RemoveUnnecessarySuppressionExclusions, disabledId }
            };
 
            var (disablePrefix, disableSuffix, restorePrefix, restoreSuffix) = testDisableDirective
                ? ("[|", "|]", "", "")
                : ("", "", "[|", "|]");
 
            // Verify disabled ID is not marked unnecessary.
            await TestMissingInRegularAndScriptAsync(
    $$"""

    class Class
    {
        void M()
        {
    {{disablePrefix}}#pragma warning disable {{disabledId}} // Variable is declared but never used - Unnecessary, but suppressed{{disableSuffix}}
    #pragma warning disable {{enabledId}} // Variable is declared but never used - Unnecessary, not suppressed
            int y;
    #pragma warning restore {{enabledId}} // Variable is declared but never used - Unnecessary, not suppressed
    {{restorePrefix}}#pragma warning restore {{disabledId}} // Variable is declared but never used - Unnecessary, but suppressed{{restoreSuffix}}
            y = 1;
        }
    }
    """, new TestParameters(options: options));
 
            // Verify enabled ID is marked unnecessary and removed with code fix.
            await TestInRegularAndScriptAsync(
    $$"""

    class Class
    {
        void M()
        {
    #pragma warning disable {{disabledId}} // Variable is declared but never used - Unnecessary, but suppressed
    {{disablePrefix}}#pragma warning disable {{enabledId}} // Variable is declared but never used - Unnecessary, not suppressed{{disableSuffix}}
            int y;
    {{restorePrefix}}#pragma warning restore {{enabledId}} // Variable is declared but never used - Unnecessary, not suppressed{{restoreSuffix}}
    #pragma warning restore {{disabledId}} // Variable is declared but never used - Unnecessary, but suppressed
            y = 1;
        }
    }
    """, $$"""

    class Class
    {
        void M()
        {
    #pragma warning disable {{disabledId}} // Variable is declared but never used - Unnecessary, but suppressed
            int y;
    #pragma warning restore {{disabledId}} // Variable is declared but never used - Unnecessary, but suppressed
            y = 1;
        }
    }
    """, options: options);
        }
 
        [Theory, CombinatorialData]
        public async Task TestRemoveDiagnosticSuppression_FixAll(bool testFixFromDisable)
        {
            var (disablePrefix, disableSuffix, restorePrefix, restoreSuffix) = testFixFromDisable
                ? ("{|FixAllInDocument:", "|}", "", "")
                : ("", "", "{|FixAllInDocument:", "|}");
 
            await TestInRegularAndScript1Async(
    $$"""

    #pragma warning disable CS0168 // Variable is declared but never used - Unnecessary
    #pragma warning disable {{UserDiagnosticAnalyzer.Descriptor0168.Id}}
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "CS0168")]
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{UserDiagnosticAnalyzer.Descriptor0168.Id}}")]
    class Class
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "CS0168")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "{{UserDiagnosticAnalyzer.Descriptor0168.Id}}")]
        void M()
        {
    {{disablePrefix}}#pragma warning disable {{UserDiagnosticAnalyzer.Descriptor0168.Id}} // Variable is declared but never used - Unnecessary{{disableSuffix}}
    #pragma warning disable CS0168 // Variable is declared but never used - Unnecessary
            int y;
    {{restorePrefix}}#pragma warning restore {{UserDiagnosticAnalyzer.Descriptor0168.Id}} // Variable is declared but never used - Unnecessary{{restoreSuffix}}
    #pragma warning restore CS0168 // Variable is declared but never used
            y = 1;
        }
    }
    """,
    """
 
    class Class
    {
        void M()
        {
            int y;
            y = 1;
        }
    }
    """);
        }
    }
 
    [Fact]
    public Task TestRemoveDiagnosticSuppression_Attribute_Field()
        => TestInRegularAndScript1Async(
            $$"""

            class Class
            {
                [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "UnknownId")]|]
                private int f;
            }
            """, """
 
            class Class
            {
                private int f;
            }
            """);
 
    [Fact]
    public Task TestRemoveDiagnosticSuppression_Attribute_Property()
        => TestInRegularAndScript1Async(
            $$"""

            class Class
            {
                [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "UnknownId")]|]
                public int P { get; }
            }
            """, """
 
            class Class
            {
                public int P { get; }
            }
            """);
 
    [Fact]
    public Task TestRemoveDiagnosticSuppression_Attribute_Event()
        => TestInRegularAndScript1Async(
            $$"""

            class Class
            {
                [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Category", "UnknownId")]|]
                private event System.EventHandler SampleEvent;
            }
            """, """
 
            class Class
            {
                private event System.EventHandler SampleEvent;
            }
            """);
 
    public sealed class NonLocalDiagnosticsAnalyzerTests(ITestOutputHelper logger) : RemoveUnnecessaryInlineSuppressionsTests(logger)
    {
        private sealed class NonLocalDiagnosticsAnalyzer : DiagnosticAnalyzer
        {
            public const string DiagnosticId = "NonLocalDiagnosticId";
            public static readonly DiagnosticDescriptor Descriptor =
                new(DiagnosticId, "NonLocalDiagnosticTitle", "NonLocalDiagnosticMessage", "NonLocalDiagnosticCategory", DiagnosticSeverity.Warning, isEnabledByDefault: true);
 
            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Descriptor];
 
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterSymbolAction(context =>
                {
                    if (!context.Symbol.ContainingNamespace.IsGlobalNamespace)
                    {
                        var diagnostic = Diagnostic.Create(Descriptor, context.Symbol.ContainingNamespace.Locations[0]);
                        context.ReportDiagnostic(diagnostic);
                    }
                }, SymbolKind.NamedType);
            }
        }
 
        internal override ImmutableArray<DiagnosticAnalyzer> OtherAnalyzers
            => [new NonLocalDiagnosticsAnalyzer()];
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/50203")]
        public Task TestDoNotRemoveInvalidDiagnosticSuppression()
            => TestMissingInRegularAndScriptAsync(
    $$"""

    [|#pragma warning disable {{NonLocalDiagnosticsAnalyzer.DiagnosticId}}
    namespace N
    #pragma warning restore {{NonLocalDiagnosticsAnalyzer.DiagnosticId}}|]
    {
        class Class
        {
        }
    }
    """);
    }
 
    public sealed class UseAutoPropertyAnalyzerTests(ITestOutputHelper logger) : RemoveUnnecessaryInlineSuppressionsTests(logger)
    {
        internal override ImmutableArray<DiagnosticAnalyzer> OtherAnalyzers
            => [new CSharpUseAutoPropertyAnalyzer()];
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/55529")]
        public Task TestDoNotRemoveAutoPropertySuppression()
            => TestMissingInRegularAndScriptAsync(
                $$"""

                public class Test2
                {
                        // Message IDE0079 Remove unnecessary suppression
                        [|[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0032: Use auto property", Justification = "<Pending >")]|]
                        private readonly int i;
                            public int I => i;
                }

                """, new TestParameters(options: Option(CodeStyleOptions2.PreferAutoProperties, true, NotificationOption2.Warning)));
    }
 
    #endregion
}