﻿// 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.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.RemoveUnnecessarySuppressions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.UseAutoProperty;
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 : AbstractUnncessarySuppressionDiagnosticTest
    {
        protected RemoveUnnecessaryInlineSuppressionsTests(ITestOutputHelper logger)
            : base(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 => ImmutableArray.Create(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: new[] { WellKnownDiagnosticTags.CompilationEnd });
            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
            public override void Initialize(AnalysisContext context)
                => context.RegisterCompilationStartAction(context => context.RegisterCompilationEndAction(_ => { }));
        }

        #endregion

        #region Single analyzer tests (Compiler OR Analyzer)

        public abstract class CompilerOrAnalyzerTests : RemoveUnnecessaryInlineSuppressionsTests
        {
            protected CompilerOrAnalyzerTests(ITestOutputHelper logger)
                : base(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 : CompilerOrAnalyzerTests
            {
                public CompilerTests(ITestOutputHelper logger)
                    : base(logger)
                {
                }

                internal override ImmutableArray<DiagnosticAnalyzer> OtherAnalyzers
                    => ImmutableArray.Create<DiagnosticAnalyzer>(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.ToImmutable();
                    }
                }
            }

            public sealed class AnalyzerTests : CompilerOrAnalyzerTests
            {
                public AnalyzerTests(ITestOutputHelper logger)
                    : base(logger)
                {
                }

                internal override ImmutableArray<DiagnosticAnalyzer> OtherAnalyzers
                    => ImmutableArray.Create<DiagnosticAnalyzer>(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
                    => ImmutableArray.Create(
                        CompilationEndDiagnosticAnalyzer.Descriptor.Id,
                        IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId,
                        IDEDiagnosticIds.FormattingDiagnosticId,
                        "format");
            }

            [Fact]
            public async Task TestDoNotRemoveRequiredDiagnosticSuppression_Pragma()
            {
                await 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 async Task TestDoNotRemoveRequiredDiagnosticSuppression_Pragma_02()
            {
                await 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
                {
                    var fixedSource = $@"{pragmas}class Class {{ }}";
                    await TestInRegularAndScriptAsync(source, fixedSource);
                }
            }

            [Fact]
            public async Task TestDoNotRemoveInactiveDiagnosticSuppression()
            {
                await 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 async Task TestDoNotRemoveDiagnosticSuppressionsInCodeWithSyntaxErrors()
            {
                await 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 async Task TestDoNotRemoveDiagnosticSuppressionWhenAnalyzerSuppressed()
            {
                await 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 async Task TestDoNotRemoveDiagnosticSuppressionInGeneratedCode()
            {
                await 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));
            }

            [Fact]
            public async Task TestDoNotRemoveDiagnosticSuppression_Attribute_OnPartialDeclarations()
            {
                await 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 async Task TestRemoveDiagnosticSuppression_Attribute()
            {
                await 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 async Task TestRemoveDiagnosticSuppression_Attribute_Trivia()
            {
                await 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 async Task TestRemoveDiagnosticSuppression_OnlyDisableDirective()
            {
                await 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 async Task TestRemoveDiagnosticSuppression_OnlyRestoreDirective()
            {
                await 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()
            {
                var source = $@"
class Class
{{
    [|[System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""{VariableDeclaredButNotUsedDiagnosticId}"")]
    void M()
    {{
#pragma warning disable {VariableDeclaredButNotUsedDiagnosticId}|]
        int y;
#pragma warning restore {VariableDeclaredButNotUsedDiagnosticId}
    }}
}}";
                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(source, 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 async Task TestRemoveDiagnosticSuppression_Attribute_InnerValidSuppression()
            {
                await 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 async Task TestRemoveDiagnosticSuppression_Attribute_OuterValidSuppression()
            {
                await 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 async Task TestRemoveDiagnosticSuppression_DuplicateDisableWithoutMatchingRestoreDirective()
            {
                await 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 async Task TestRemoveDiagnosticSuppression_DuplicateRestoreWithoutMatchingDisableDirective()
            {
                await 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 async Task TestRemoveUnknownDiagnosticSuppression_Attribute()
            {
                await TestInRegularAndScript1Async(
        @"
[|[System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""UnknownId"")]|]
class Class
{
}",
        @"
class Class
{
}");
            }
        }

        #endregion

        #region Multiple analyzer tests (Compiler AND Analyzer)

        public sealed class CompilerAndAnalyzerTests : RemoveUnnecessaryInlineSuppressionsTests
        {
            public CompilerAndAnalyzerTests(ITestOutputHelper logger)
                : base(logger)
            {
            }

            internal override ImmutableArray<DiagnosticAnalyzer> OtherAnalyzers
                => ImmutableArray.Create<DiagnosticAnalyzer>(new CSharpCompilerDiagnosticAnalyzer(), new UserDiagnosticAnalyzer());

            [Fact]
            public async Task TestDoNotRemoveInvalidDiagnosticSuppression()
            {
                await 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 async Task TestRemoveDiagnosticSuppression_Attribute_Field()
        {
            await TestInRegularAndScript1Async(
    $@"
class Class
{{
    [|[System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""UnknownId"")]|]
    private int f;
}}", @"
class Class
{
    private int f;
}");
        }

        [Fact]
        public async Task TestRemoveDiagnosticSuppression_Attribute_Property()
        {
            await TestInRegularAndScript1Async(
    $@"
class Class
{{
    [|[System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""UnknownId"")]|]
    public int P {{ get; }}
}}", @"
class Class
{
    public int P { get; }
}");
        }

        [Fact]
        public async Task TestRemoveDiagnosticSuppression_Attribute_Event()
        {
            await 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 : RemoveUnnecessaryInlineSuppressionsTests
        {
            public NonLocalDiagnosticsAnalyzerTests(ITestOutputHelper logger)
                : base(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 => ImmutableArray.Create(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
                => ImmutableArray.Create<DiagnosticAnalyzer>(new NonLocalDiagnosticsAnalyzer());

            [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/50203")]
            public async Task TestDoNotRemoveInvalidDiagnosticSuppression()
            {
                await TestMissingInRegularAndScriptAsync(
        $@"
[|#pragma warning disable {NonLocalDiagnosticsAnalyzer.DiagnosticId}
namespace N
#pragma warning restore {NonLocalDiagnosticsAnalyzer.DiagnosticId}|]
{{
    class Class
    {{
    }}
}}");
            }
        }

        public sealed class UseAutoPropertyAnalyzerTests : RemoveUnnecessaryInlineSuppressionsTests
        {
            public UseAutoPropertyAnalyzerTests(ITestOutputHelper logger)
                : base(logger)
            {
            }

            internal override ImmutableArray<DiagnosticAnalyzer> OtherAnalyzers
                => ImmutableArray.Create<DiagnosticAnalyzer>(new CSharpUseAutoPropertyAnalyzer());

            [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/55529")]
            public async Task TestDoNotRemoveAutoPropertySuppression()
            {
                await 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
    }
}
