File: RemoveUnusedValueAssignmentTests.cs
Web Access
Project: ..\..\..\src\CodeStyle\CSharp\Tests\Microsoft.CodeAnalysis.CSharp.CodeStyle.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.CodeStyle.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.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.RemoveUnusedParametersAndValues;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Testing;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
using Xunit.Abstractions;
using static Roslyn.Test.Utilities.TestHelpers;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveUnusedParametersAndValues
{
    using VerifyCS = CSharpCodeFixVerifier<
        CSharpRemoveUnusedParametersAndValuesDiagnosticAnalyzer,
        CSharpRemoveUnusedValuesCodeFixProvider>;
 
    [Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedValues)]
    public class RemoveUnusedValueAssignmentTests : RemoveUnusedValuesTestsBase
    {
        public RemoveUnusedValueAssignmentTests(ITestOutputHelper logger)
          : base(logger)
        {
        }
 
        private protected override OptionsCollection PreferNone
            => Option(CSharpCodeStyleOptions.UnusedValueAssignment,
                   new CodeStyleOption2<UnusedValuePreference>(UnusedValuePreference.DiscardVariable, NotificationOption2.None));
 
        private protected override OptionsCollection PreferDiscard
            => Option(CSharpCodeStyleOptions.UnusedValueAssignment,
                   new CodeStyleOption2<UnusedValuePreference>(UnusedValuePreference.DiscardVariable, NotificationOption2.Suggestion));
 
        private protected override OptionsCollection PreferUnusedLocal
            => Option(CSharpCodeStyleOptions.UnusedValueAssignment,
                   new CodeStyleOption2<UnusedValuePreference>(UnusedValuePreference.UnusedLocalVariable, NotificationOption2.Suggestion));
 
        [Theory, CombinatorialData]
        public void TestStandardProperty(AnalyzerProperty property)
            => VerifyCS.VerifyStandardProperty(property);
 
        [Fact]
        public async Task Initialization_Suppressed()
        {
            var source =
@"class C
{
    int M()
    {
        int x = 1;
        x = 2;
        return x;
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = source,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable, NotificationOption2.None },
                },
            }.RunAsync();
        }
 
        [Fact]
        public async Task Assignment_Suppressed()
        {
            var source =
@"class C
{
    int M()
    {
        int x;
        x = 1;
        x = 2;
        return x;
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = source,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable, NotificationOption2.None },
                },
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable)]
        [InlineData(UnusedValuePreference.UnusedLocalVariable)]
        public async Task Initialization_ConstantValue(object option)
        {
            var source =
@"class C
{
    int M()
    {
        int {|IDE0059:x|} = 1;
        x = 2;
        return x;
    }
}";
            var fixedSource =
@"class C
{
    int M()
    {
        int x = 2;
        return x;
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/48070")]
        [InlineData(UnusedValuePreference.DiscardVariable)]
        [InlineData(UnusedValuePreference.UnusedLocalVariable)]
        public async Task Initialization_ConstantValue_DoNotCopyLeadingTriviaDirectives(object option)
        {
            var source =
@"class C {
    void M()
    {
        #region
        int value = 3;
        #endregion
 
        int? {|IDE0059:x|} = null;
        int y = value + value;
        
        x = y;
        System.Console.WriteLine(x);
    }
}";
            var fixedSource =
@"class C {
    void M()
    {
        #region
        int value = 3;
 
        #endregion
        int y = value + value;
 
        int? x = y;
        System.Console.WriteLine(x);
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Fact]
        public async Task Initialization_ConstantValue_RemoveUnusedParametersSuppressed()
        {
            var source =
@"class C
{
    int M()
    {
        int {|IDE0059:x|} = 1;
        x = 2;
        return x;
    }
}";
            var fixedSource =
@"class C
{
    int M()
    {
        int x = 2;
        return x;
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CodeStyleOptions2.UnusedParameters, UnusedParametersPreference.NonPublicMethods, NotificationOption2.None },
                },
            }.RunAsync();
        }
 
        [Fact]
        public async Task Initialization_ConstantValue_RemoveUnusedParametersNotApplicable()
        {
            var source =
@"class C
{
    public int M(int {|IDE0060:z|})
    {
        int {|IDE0059:x|} = 1;
        x = 2;
        return x;
    }
}";
            var fixedSource =
@"class C
{
    public int M(int {|IDE0060:z|})
    {
        int x = 2;
        return x;
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CodeStyleOptions2.UnusedParameters, UnusedParametersPreference.NonPublicMethods, NotificationOption2.Silent },
                },
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable)]
        [InlineData(UnusedValuePreference.UnusedLocalVariable)]
        public async Task Assignment_ConstantValue(object option)
        {
            var source =
@"class C
{
    int M()
    {
        int x;
        {|IDE0059:x|} = 1;
        x = 2;
        return x;
    }
}";
            var fixedSource =
@"class C
{
    int M()
    {
        int x;
        x = 2;
        return x;
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable)]
        [InlineData(UnusedValuePreference.UnusedLocalVariable)]
        public async Task Assignment_ConstantValue_NoReads(object option)
        {
            var source =
@"class C
{
    void M()
    {
        int x;
        {|IDE0059:x|} = 1;
    }
}";
            var fixedSource =
@"class C
{
    void M()
    {
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Fact]
        public async Task Assignment_NonConstantValue_NoReads_PreferDiscard()
        {
            var source =
@"class C
{
    void M()
    {
        int x;
        {|IDE0059:x|} = M2();
    }
 
    int M2() => 0;
}";
            var fixedSource =
@"class C
{
    void M()
    {
        _ = M2();
    }
 
    int M2() => 0;
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable },
                },
            }.RunAsync();
        }
 
        [Fact]
        public async Task Assignment_NonConstantValue_NoReads_PreferUnusedLocal()
        {
            var source =
@"class C
{
    void M()
    {
        int x;
        x = M2();
    }
 
    int M2() => 0;
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = source,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.UnusedLocalVariable },
                },
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable)]
        [InlineData(UnusedValuePreference.UnusedLocalVariable)]
        public async Task Initialization_NonConstantValue_ParameterReference(object option)
        {
            var source =
@"class C
{
    int M(int p)
    {
        int {|IDE0059:x|} = p;
        x = 2;
        return x;
    }
}";
            var fixedSource =
@"class C
{
    int M(int {|IDE0060:p|})
    {
        int x = 2;
        return x;
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable)]
        [InlineData(UnusedValuePreference.UnusedLocalVariable)]
        public async Task Assignment_NonConstantValue_ParameterReference(object option)
        {
            var source =
@"class C
{
    int M(int p)
    {
        int x;
        {|IDE0059:x|} = p;
        x = 2;
        return x;
    }
}";
            var fixedSource =
@"class C
{
    int M(int {|IDE0060:p|})
    {
        int x;
        x = 2;
        return x;
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [CombinatorialData]
        public async Task Initialization_NonConstantValue_LocalReference(
            [CombinatorialValues(UnusedValuePreference.DiscardVariable, UnusedValuePreference.UnusedLocalVariable)] object option,
            [CombinatorialValues(CodeFixTestBehaviors.None, CodeFixTestBehaviors.FixOne)] CodeFixTestBehaviors testBehaviors)
        {
            var source =
@"class C
{
    int M()
    {
        int local = 0;
        int {|IDE0059:x|} = local;
        x = 2;
        return x;
    }
}";
            var (fixedSource, iterations) = testBehaviors switch
            {
                CodeFixTestBehaviors.None =>
(@"class C
{
    int M()
    {
        int x = 2;
        return x;
    }
}", iterations: 2),
                CodeFixTestBehaviors.FixOne =>
(@"class C
{
    int M()
    {
        int {|IDE0059:local|} = 0;
        int x = 2;
        return x;
    }
}", iterations: 1),
                _ => throw ExceptionUtilities.Unreachable(),
            };
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedState = { Sources = { fixedSource }, MarkupHandling = MarkupMode.Allow },
                CodeFixTestBehaviors = testBehaviors,
                NumberOfIncrementalIterations = iterations,
                NumberOfFixAllIterations = iterations,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [CombinatorialData]
        public async Task Assignment_NonConstantValue_LocalReference(
            [CombinatorialValues(UnusedValuePreference.DiscardVariable, UnusedValuePreference.UnusedLocalVariable)] object option,
            [CombinatorialValues(CodeFixTestBehaviors.None, CodeFixTestBehaviors.FixOne)] CodeFixTestBehaviors testBehaviors)
        {
            var source =
@"class C
{
    int M()
    {
        int local = 0;
        int x;
        {|IDE0059:x|} = local;
        x = 2;
        return x;
    }
}";
            var (fixedSource, iterations) = testBehaviors switch
            {
                CodeFixTestBehaviors.None =>
(@"class C
{
    int M()
    {
        int x;
        x = 2;
        return x;
    }
}", iterations: 2),
                CodeFixTestBehaviors.FixOne =>
(@"class C
{
    int M()
    {
        int {|IDE0059:local|} = 0;
        int x;
        x = 2;
        return x;
    }
}", iterations: 1),
                _ => throw ExceptionUtilities.Unreachable(),
            };
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedState = { Sources = { fixedSource }, MarkupHandling = MarkupMode.Allow },
                CodeFixTestBehaviors = testBehaviors,
                NumberOfIncrementalIterations = iterations,
                NumberOfFixAllIterations = iterations,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable)]
        [InlineData(UnusedValuePreference.UnusedLocalVariable)]
        public async Task Initialization_NonConstantValue_DefaultExpression(object option)
        {
            var source =
@"struct C
{
    C M()
    {
        C {|IDE0059:c|} = default(C);
        c = new C();
        return c;
    }
}";
            var fixedSource =
@"struct C
{
    C M()
    {
        C c = new C();
        return c;
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable)]
        [InlineData(UnusedValuePreference.UnusedLocalVariable)]
        public async Task Initialization_NonConstantValue_CastExpression(object option)
        {
            var source =
@"struct C
{
    C M(object obj)
    {
        C {|IDE0059:c|} = (C)obj;
        c = new C();
        return c;
    }
}";
            var fixedSource =
@"struct C
{
    C M(object {|IDE0060:obj|})
    {
        C c = new C();
        return c;
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable)]
        [InlineData(UnusedValuePreference.UnusedLocalVariable)]
        public async Task Initialization_NonConstantValue_FieldReferenceWithThisReceiver(object option)
        {
            var source =
@"class C
{
    private int field;
    int M()
    {
        int {|IDE0059:x|} = field;
        x = 2;
        return x;
    }
}";
            var fixedSource =
@"class C
{
    private int field;
    int M()
    {
        int x = 2;
        return x;
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable)]
        [InlineData(UnusedValuePreference.UnusedLocalVariable)]
        public async Task Assignment_NonConstantValue_FieldReferenceWithNullReceiver(object option)
        {
            var source =
@"class C
{
    private static int field;
    int M()
    {
        int x;
        {|IDE0059:x|} = field;
        x = 2;
        return x;
    }
}";
            var fixedSource =
@"class C
{
    private static int field;
    int M()
    {
        int x;
        x = 2;
        return x;
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable, "_")]
        [InlineData(UnusedValuePreference.UnusedLocalVariable, "int unused")]
        public async Task Assignment_NonConstantValue_FieldReferenceWithReceiver(object option, string fix)
        {
            var source =
@"class C
{
    private int field;
    int M(C c)
    {
        int x;
        {|IDE0059:x|} = c.field;
        x = 2;
        return x;
    }
}";
            var fixedSource =
$@"class C
{{
    private int field;
    int M(C c)
    {{
        int x;
        {fix} = c.field;
        x = 2;
        return x;
    }}
}}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable, "_")]
        [InlineData(UnusedValuePreference.UnusedLocalVariable, "int unused")]
        public async Task Initialization_NonConstantValue_PropertyReference(object option, string fix)
        {
            var source =
@"class C
{
    private int Property { get { throw new System.Exception(); } }
    int M()
    {
        int x;
        {|IDE0059:x|} = Property;
        x = 2;
        return x;
    }
}";
            var fixedSource =
$@"class C
{{
    private int Property {{ get {{ throw new System.Exception(); }} }}
    int M()
    {{
        int x;
        {fix} = Property;
        x = 2;
        return x;
    }}
}}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable, "_")]
        [InlineData(UnusedValuePreference.UnusedLocalVariable, "int unused")]
        public async Task Initialization_NonConstantValue_MethodInvocation(object option, string fix)
        {
            var source =
@"class C
{
    int M()
    {
        int {|IDE0059:x|} = M2();
        x = 2;
        return x;
    }
 
    int M2() => 0;
}";
            var fixedSource =
$@"class C
{{
    int M()
    {{
        {fix} = M2();
        int x = 2;
        return x;
    }}
 
    int M2() => 0;
}}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Fact]
        public async Task Initialization_NonConstantValue_PreferDiscard_CSharp6()
        {
            // Discard not supported in C# 6.0, so we fallback to unused local variable.
            var source =
@"class C
{
    int M()
    {
        int {|IDE0059:x|} = M2();
        x = 2;
        return x;
    }
 
    int M2() => 0;
}";
            var fixedSource =
@"class C
{
    int M()
    {
        int unused = M2();
        int x = 2;
        return x;
    }
 
    int M2() => 0;
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable },
                },
                LanguageVersion = LanguageVersion.CSharp6,
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable, "_")]
        [InlineData(UnusedValuePreference.UnusedLocalVariable, "int unused")]
        public async Task Assignment_NonConstantValue_MethodInvocation(object option, string fix)
        {
            var source =
@"class C
{
    int M()
    {
        int x;
        {|IDE0059:x|} = M2();
        x = 2;
        return x;
    }
 
    int M2() => 0;
}";
            var fixedSource =
$@"class C
{{
    int M()
    {{
        int x;
        {fix} = M2();
        x = 2;
        return x;
    }}
 
    int M2() => 0;
}}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable)]
        [InlineData(UnusedValuePreference.UnusedLocalVariable)]
        public async Task Assignment_NonConstantValue_ImplicitConversion(object option)
        {
            var source =
@"class C
{
    int M(int {|IDE0060:x|}, short s)
    {
        {|IDE0059:x|} = s;
        x = 2;
        return x;
    }
}";
            var fixedSource =
@"class C
{
    int M(int {|IDE0060:x|}, short {|IDE0060:s|})
    {
        x = 2;
        return x;
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable, "_")]
        [InlineData(UnusedValuePreference.UnusedLocalVariable, "int unused")]
        public async Task Assignment_NonConstantValue_UserDefinedConversion(object option, string fix)
        {
            var source =
@"class C
{
    int M(int {|IDE0060:x|}, C c)
    {
        {|IDE0059:x|} = (int)c;
        x = 2;
        return x;
    }
 
    public static explicit operator int(C {|IDE0060:c|})
    {
        return 0;
    }
 
    public static explicit operator C(int {|IDE0060:i|})
    {
        return default(C);
    }
}";
            var fixedSource =
$@"class C
{{
    int M(int {{|IDE0060:x|}}, C c)
    {{
        {fix} = (int)c;
        x = 2;
        return x;
    }}
 
    public static explicit operator int(C {{|IDE0060:c|}})
    {{
        return 0;
    }}
 
    public static explicit operator C(int {{|IDE0060:i|}})
    {{
        return default(C);
    }}
}}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [CombinatorialData]
        public async Task NestedAssignment_ConstantValue(
            [CombinatorialValues(UnusedValuePreference.DiscardVariable, UnusedValuePreference.UnusedLocalVariable)] object option,
            [CombinatorialValues(CodeFixTestBehaviors.None, CodeFixTestBehaviors.FixOne | CodeFixTestBehaviors.SkipFixAllCheck)] CodeFixTestBehaviors testBehaviors)
        {
            var source =
@"class C
{
    int M(int {|IDE0060:x|}, int {|IDE0060:y|})
    {
        {|IDE0059:y|} = {|IDE0059:x|} = 1;
        x = 2;
        return x;
    }
}";
 
            var (fixedSource, iterations) = ((UnusedValuePreference)option, testBehaviors) switch
            {
                (UnusedValuePreference.DiscardVariable, CodeFixTestBehaviors.None) =>
(@"class C
{
    int M(int {|IDE0060:x|}, int {|IDE0060:y|})
    {
        _ = 1;
        x = 2;
        return x;
    }
}", iterations: 2),
                (UnusedValuePreference.UnusedLocalVariable, CodeFixTestBehaviors.None) =>
(@"class C
{
    int M(int {|IDE0060:x|}, int {|IDE0060:y|})
    {
        x = 2;
        return x;
    }
}", iterations: 3),
                (_, CodeFixTestBehaviors.FixOne | CodeFixTestBehaviors.SkipFixAllCheck) =>
(@"class C
{
    int M(int {|IDE0060:x|}, int {|IDE0060:y|})
    {
        {|IDE0059:y|} = 1;
        x = 2;
        return x;
    }
}", iterations: 1),
                _ => throw ExceptionUtilities.Unreachable(),
            };
 
            var test = new VerifyCS.Test
            {
                TestCode = source,
                FixedState = { Sources = { fixedSource }, MarkupHandling = MarkupMode.Allow },
                CodeFixTestBehaviors = testBehaviors,
                NumberOfIncrementalIterations = iterations,
                NumberOfFixAllIterations = iterations,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            };
 
            if (testBehaviors.HasFlag(CodeFixTestBehaviors.FixOne))
            {
                test.DiagnosticSelector = diagnostics => diagnostics[1];
            }
 
            await test.RunAsync();
        }
 
        [Theory]
        [CombinatorialData]
        public async Task NestedAssignment_NonConstantValue(
            [CombinatorialValues(UnusedValuePreference.DiscardVariable, UnusedValuePreference.UnusedLocalVariable)] object option,
            [CombinatorialValues(CodeFixTestBehaviors.None, CodeFixTestBehaviors.FixOne | CodeFixTestBehaviors.SkipFixAllCheck)] CodeFixTestBehaviors testBehaviors)
        {
            var source =
@"class C
{
    int M(int {|IDE0060:x|}, int {|IDE0060:y|})
    {
        {|IDE0059:y|} = {|IDE0059:x|} = M2();
        x = 2;
        return x;
    }
 
    int M2() => 0;
}";
 
            var fixedSource = ((UnusedValuePreference)option, testBehaviors) switch
            {
                (UnusedValuePreference.DiscardVariable, CodeFixTestBehaviors.None) =>
@"class C
{
    int M(int {|IDE0060:x|}, int {|IDE0060:y|})
    {
        _ = _ = M2();
        x = 2;
        return x;
    }
 
    int M2() => 0;
}",
                (UnusedValuePreference.DiscardVariable, CodeFixTestBehaviors.FixOne | CodeFixTestBehaviors.SkipFixAllCheck) =>
@"class C
{
    int M(int {|IDE0060:x|}, int {|IDE0060:y|})
    {
        {|IDE0059:y|} = _ = M2();
        x = 2;
        return x;
    }
 
    int M2() => 0;
}",
                (UnusedValuePreference.UnusedLocalVariable, CodeFixTestBehaviors.None) =>
@"class C
{
    int M(int {|IDE0060:x|}, int {|IDE0060:y|})
    {
        int unused1;
        int unused = unused1 = M2();
        x = 2;
        return x;
    }
 
    int M2() => 0;
}",
                (UnusedValuePreference.UnusedLocalVariable, CodeFixTestBehaviors.FixOne | CodeFixTestBehaviors.SkipFixAllCheck) =>
@"class C
{
    int M(int {|IDE0060:x|}, int {|IDE0060:y|})
    {
        int unused;
        {|IDE0059:y|} = unused = M2();
        x = 2;
        return x;
    }
 
    int M2() => 0;
}",
                _ => throw ExceptionUtilities.Unreachable(),
            };
 
            var test = new VerifyCS.Test
            {
                TestCode = source,
                FixedState = { Sources = { fixedSource }, MarkupHandling = MarkupMode.Allow },
                CodeFixTestBehaviors = testBehaviors,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            };
 
            if (testBehaviors.HasFlag(CodeFixTestBehaviors.FixOne))
            {
                test.DiagnosticSelector = diagnostics => diagnostics[1];
            }
 
            await test.RunAsync();
        }
 
        [Theory]
        [InlineData(UnusedValuePreference.DiscardVariable)]
        [InlineData(UnusedValuePreference.UnusedLocalVariable)]
        public async Task ReadAndWriteInSameExpression_MethodInvocation(object option)
        {
            var source =
@"class C
{
    int M()
    {
        int x = 1;
        x = M2(x);
        return x;
    }
 
    int M2(int x) => x;
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = source,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [CombinatorialData]
        public async Task IncrementOrDecrementOperator_ValueUsed_SameStatement(
            [CombinatorialValues("++", "--")] string @operator,
            bool applyAsPrefix,
            [CombinatorialValues(UnusedValuePreference.DiscardVariable, UnusedValuePreference.UnusedLocalVariable)] object option)
        {
            var (prefix, postfix) = applyAsPrefix ? (@operator, "") : ("", @operator);
            var source =
$@"class C
{{
    void M(int x)
    {{
        var {{|#0:y|}} = {prefix}x{postfix};
    }}
}}";
 
            var fixedSource = (UnusedValuePreference)option switch
            {
                UnusedValuePreference.UnusedLocalVariable => source,
                UnusedValuePreference.DiscardVariable =>
$@"class C
{{
    void M(int x)
    {{
        _ = {prefix}x{postfix};
    }}
}}",
                _ => throw ExceptionUtilities.Unreachable(),
            };
 
            var test = new VerifyCS.Test
            {
                TestState = { Sources = { source } },
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            };
 
            if ((UnusedValuePreference)option == UnusedValuePreference.DiscardVariable)
            {
                test.TestState.ExpectedDiagnostics.Add(
                    // /0/Test0.cs(5,13): info IDE0059: Unnecessary assignment of a value to 'y'
                    VerifyCS.Diagnostic("IDE0059").WithSeverity(DiagnosticSeverity.Info).WithLocation(0).WithArguments("y"));
            }
 
            await test.RunAsync();
        }
 
        [Theory]
        [CombinatorialData]
        public async Task IncrementOrDecrementOperator_ValueUsed_LaterStatement(
            [CombinatorialValues("++", "--")] string @operator,
            bool applyAsPrefix,
            [CombinatorialValues(UnusedValuePreference.DiscardVariable, UnusedValuePreference.UnusedLocalVariable)] object option)
        {
            var (prefix, postfix) = applyAsPrefix ? (@operator, "") : ("", @operator);
            var source =
$@"class C
{{
    int M(int x)
    {{
        {prefix}x{postfix};
        return x;
    }}
}}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = source,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [CombinatorialData]
        public async Task IncrementOrDecrementOperator_ValueUnused(
            [CombinatorialValues("++", "--")] string @operator,
            bool applyAsPrefix,
            [CombinatorialValues(UnusedValuePreference.DiscardVariable, UnusedValuePreference.UnusedLocalVariable)] object option)
        {
            var (prefix, postfix) = applyAsPrefix ? (@operator, "") : ("", @operator);
            var source =
$@"class C
{{
    void M(int x)
    {{
        {prefix}{{|IDE0059:x|}}{postfix};
    }}
}}";
            var fixedSource =
@"class C
{
    void M(int {|IDE0060:x|})
    {
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [CombinatorialData]
        public async Task CompoundAssignmentOperator_ValueUsed_SameStatement(
            [CombinatorialValues("1" /*Constant*/, "M2()" /*Non-constant*/)] string rightHandSide,
            [CombinatorialValues(UnusedValuePreference.DiscardVariable, UnusedValuePreference.UnusedLocalVariable)] object option)
        {
            var source =
$@"class C
{{
    void M(int x)
    {{
        var {{|#0:y|}} = x += {rightHandSide};
    }}
 
    int M2() => 0;
}}";
 
            var fixedSource = (UnusedValuePreference)option switch
            {
                UnusedValuePreference.UnusedLocalVariable => source,
                UnusedValuePreference.DiscardVariable =>
$@"class C
{{
    void M(int x)
    {{
        _ = x += {rightHandSide};
    }}
 
    int M2() => 0;
}}",
                _ => throw ExceptionUtilities.Unreachable(),
            };
 
            var test = new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            };
 
            if ((UnusedValuePreference)option == UnusedValuePreference.DiscardVariable)
            {
                test.TestState.ExpectedDiagnostics.Add(
                    // /0/Test0.cs(5,13): info IDE0059: Unnecessary assignment of a value to 'y'
                    VerifyCS.Diagnostic("IDE0059").WithSeverity(DiagnosticSeverity.Info).WithLocation(0).WithArguments("y"));
            }
 
            await test.RunAsync();
        }
 
        [Theory]
        [CombinatorialData]
        public async Task CompoundAssignmentOperator_ValueUsed_LaterStatement(
            [CombinatorialValues("1" /*Constant*/, "M2()" /*Non-constant*/)] string rightHandSide,
            [CombinatorialValues(UnusedValuePreference.DiscardVariable, UnusedValuePreference.UnusedLocalVariable)] object option)
        {
            var source =
$@"class C
{{
    int M(int x)
    {{
        x += {rightHandSide};
        return x;
    }}
 
    int M2() => 0;
}}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = source,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [CombinatorialData]
        public async Task CompoundLogicalOrOperator_ValueUsed_LaterStatement(
            [CombinatorialValues("true" /*Constant*/, "M2()" /*Non-constant*/)] string rightHandSide,
            [CombinatorialValues(UnusedValuePreference.DiscardVariable, UnusedValuePreference.UnusedLocalVariable)] object option)
        {
            var source =
$@"class C
{{
    bool M(bool x)
    {{
        x |= {rightHandSide} && {rightHandSide};
        return x;
    }}
 
    bool M2() => true;
}}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = source,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [CombinatorialData]
        public async Task CompoundLogicalOrOperator_ValueUsed_LaterStatement_02(
            [CombinatorialValues("true" /*Constant*/, "M2()" /*Non-constant*/)] string rightHandSide,
            [CombinatorialValues(UnusedValuePreference.DiscardVariable, UnusedValuePreference.UnusedLocalVariable)] object option)
        {
            var source =
$@"class C
{{
    bool M()
    {{
        bool x = false;
        x |= {rightHandSide} && {rightHandSide};
        return x;
    }}
 
    bool M2() => true;
}}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = source,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            }.RunAsync();
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task CompoundAssignmentOperator_ValueNotUsed_ConstantValue(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M(int x)
    {
        [|x|] += 1;
    }
}",
@"class C
{
    int M(int x)
    {
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard), "_")]
        [InlineData(nameof(PreferUnusedLocal), "int unused")]
        public async Task CompoundAssignmentOperator_ValueNotUsed_NonConstantValue(string optionName, string fix)
        {
            await TestInRegularAndScriptAsync(
$@"class C
{{
    int M(int x)
    {{
        [|x|] += M2();
    }}
 
    int M2() => 0;
}}",
$@"class C
{{
    int M(int x)
    {{
        {fix} = M2();
    }}
 
    int M2() => 0;
}}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task NullCoalescing_ReadWrite(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    C M()
    {
        C [|x|] = M2();
        x = x ?? new C();
        return x;
    }
 
    C M2() => null;
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task LValueFlowCapture_Assignment_ControlFlowInAssignedTarget(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    C M(C y)
    {
        C [|x|] = M2();
        (x ?? y) = y;
        return x;
    }
 
    C M2() => null;
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard), "_")]
        [InlineData(nameof(PreferUnusedLocal), "var unused")]
        public async Task LValueFlowCapture_Assignment_ControlFlowInAssignedValue_01(string optionName, string fix)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    C M(C y, C z)
    {
        var [|x|] = M2();
        x = y ?? z;
        return x;
    }
 
    C M2() => null;
}",
$@"class C
{{
    C M(C y, C z)
    {{
        {fix} = M2();
        C x = y ?? z;
        return x;
    }}
 
    C M2() => null;
}}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task LValueFlowCapture_Assignment_ControlFlowInAssignedValue_02(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    C M(C y, C z)
    {
        C [|x|] = M2();
        x = y ?? (x ?? z);
        return x;
    }
 
    C M2() => null;
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task LValueFlowCapture_DeconstructionAssignment_ControlFlowInAssignedTarget(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    C M(C y)
    {
        C [|x|] = M2();
        ((x ?? y), _) = (y, y);
        return x;
    }
 
    C M2() => null;
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard), "_")]
        [InlineData(nameof(PreferUnusedLocal), "var unused")]
        public async Task LValueFlowCapture_DeconstructionAssignment_ControlFlowInAssignedValue_01(string optionName, string fix)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    C M(C y, C z)
    {
        var [|x|] = M2();
        (x, y) = (y ?? z, z);
        return x;
    }
 
    C M2() => null;
}",
$@"class C
{{
    C M(C y, C z)
    {{
        {fix} = M2();
        C x;
        (x, y) = (y ?? z, z);
        return x;
    }}
 
    C M2() => null;
}}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task LValueFlowCapture_DeconstructionAssignment_ControlFlowInAssignedValue_02(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    C M(C y, C z)
    {
        C [|x|] = M2();
        (x, y) = (y ?? x, z);
        return x;
    }
 
    C M2() => null;
}", optionName);
        }
 
        [Fact]
        public async Task Initialization_NonConstantValue_NoReferences_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        int [|x|] = M2();
    }
 
    int M2() => 0;
}",
@"class C
{
    void M()
    {
        _ = M2();
    }
 
    int M2() => 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task Initialization_NonConstantValue_NoReferences_PreferUnusedLocal()
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        int [|x|] = M2();
    }
 
    int M2() => 0;
}", new TestParameters(options: PreferUnusedLocal));
        }
 
        [Fact]
        public async Task Initialization_NonConstantValue_NoReadReferences_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        int [|x|] = M2();
        x = 0;
    }
 
    int M2() => 0;
}",
@"class C
{
    void M()
    {
        _ = M2();
        int x = 0;
    }
 
    int M2() => 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task Initialization_NonConstantValue_NoReadReferences_PreferUnusedLocal()
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        int [|x|] = M2();
        x = 0;
    }
 
    int M2() => 0;
}", new TestParameters(options: PreferUnusedLocal));
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task Initialization_ConstantValue_FirstField(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int [|x|] = 1, y = 2;
        x = 2;
        return x;
    }
}",
@"class C
{
    int M()
    {
        int y = 2;
        int x = 2;
        return x;
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task Initialization_ConstantValue_MiddleField(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int i = 0, [|x|] = 1, y = 2;
        x = 2;
        return x;
    }
}",
@"class C
{
    int M()
    {
        int i = 0, y = 2;
        int x = 2;
        return x;
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task Initialization_ConstantValue_LastField(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int i = 0, y = 2, [|x|] = 1;
        x = 2;
        return x;
    }
}",
@"class C
{
    int M()
    {
        int i = 0, y = 2;
        int x = 2;
        return x;
    }
}", optionName);
        }
 
        [Fact]
        public async Task Initialization_NonConstantValue_FirstField_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int [|x|] = M2(), y = 2;
        x = 2;
        return x;
    }
 
    void M2() => 0;
}",
@"class C
{
    int M()
    {
        _ = M2();
        int y = 2;
        int x = 2;
        return x;
    }
 
    void M2() => 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task Initialization_NonConstantValue_FirstField_PreferUnusedLocal()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int [|x|] = M2(), y = 2;
        x = 2;
        return x;
    }
 
    void M2() => 0;
}",
@"class C
{
    int M()
    {
        int unused = M2(), y = 2;
        int x = 2;
        return x;
    }
 
    void M2() => 0;
}", options: PreferUnusedLocal);
        }
 
        [Fact]
        public async Task Initialization_NonConstantValue_MiddleField_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int i = 0, [|x|] = M2(), y = 2;
        x = 2;
        return x;
    }
 
    void M2() => 0;
}",
@"class C
{
    int M()
    {
        int i = 0;
        _ = M2();
        int y = 2;
        int x = 2;
        return x;
    }
 
    void M2() => 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task Initialization_NonConstantValue_MiddleField_PreferUnusedLocal()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int i = 0, [|x|] = M2(), y = 2;
        x = 2;
        return x;
    }
 
    void M2() => 0;
}",
@"class C
{
    int M()
    {
        int i = 0, unused = M2(), y = 2;
        int x = 2;
        return x;
    }
 
    void M2() => 0;
}", options: PreferUnusedLocal);
        }
 
        [Fact]
        public async Task Initialization_NonConstantValue_LastField_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int i = 0, y = 2, [|x|] = M2();
        x = 2;
        return x;
    }
 
    void M2() => 0;
}",
@"class C
{
    int M()
    {
        int i = 0, y = 2;
        _ = M2();
        int x = 2;
        return x;
    }
 
    void M2() => 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task Initialization_NonConstantValue_LastField_PreferUnusedLocal()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int i = 0, y = 2, [|x|] = M2();
        x = 2;
        return x;
    }
 
    void M2() => 0;
}",
@"class C
{
    int M()
    {
        int i = 0, y = 2, unused = M2();
        int x = 2;
        return x;
    }
 
    void M2() => 0;
}", options: PreferUnusedLocal);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task Assignment_BeforeUseAsOutArgument(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int x;
        [|x|] = 1;
        M2(out x);
        return x;
    }
 
    void M2(out int x) => x = 0;
}",
@"class C
{
    int M()
    {
        int x;
        M2(out x);
        return x;
    }
 
    void M2(out int x) => x = 0;
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task NonRedundantAssignment_BeforeUseAsRefArgument(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int x;
        [|x|] = 1;
        M2(ref x);
        return x;
    }
 
    void M2(ref int x) => x = 0;
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/40717")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task NonRedundantAssignment_AfterUseAsRefArgument(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    static int Example()
    {
        int value = 1;
 
        Span<int> valueSpan = M(ref value);
 
        [|value = 2;|]
 
        return valueSpan[0];
    }
 
    static Span<int> M(ref int value)
    {
        return default;
    }
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/40483")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task NonRedundantAssignment_AfterUseAsRefArgument_02(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class P
{
    public ref bool f(ref bool z, ref bool q)
    {
        z = ref q;
        return ref z;
    }
}
 
class Q
{
    static void F()
    {
        bool a = true;
        bool b = false;
        ref var r = ref new P().f(ref a, ref b);
        [|b = true|];
 
        Console.WriteLine(r);
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task NonRedundantAssignment_BeforeUseAsInArgument(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int x;
        [|x|] = 1;
        M2(in x);
        return x;
    }
 
    void M2(in int x) { }
}", optionName);
        }
 
        [Fact]
        public async Task OutArgument_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int x;
        M2(out [|x|]);
        x = 1;
        return x;
    }
 
    void M2(out int x) => x = 0;
}",
@"class C
{
    int M()
    {
        int x;
        M2(out _);
        x = 1;
        return x;
    }
 
    void M2(out int x) => x = 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task OutArgument_PreferUnusedLocal()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int x;
        M2(out [|x|]);
        x = 1;
        return x;
    }
 
    void M2(out int x) => x = 0;
}",
@"class C
{
    int M()
    {
        int x;
        int unused;
        M2(out unused);
        x = 1;
        return x;
    }
 
    void M2(out int x) => x = 0;
}", options: PreferUnusedLocal);
        }
 
        [Fact]
        public async Task OutVarArgument_ExpressionBody_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M() => M2(out var [|x|]);
    void M2(out int x) => x = 0;
}",
@"class C
{
    void M() => M2(out _);
    void M2(out int x) => x = 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task OutArgument_NoReads_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        int x;
        M2(out [|x|]);
 
        // Unrelated, unused local should not be removed.
        int unused;
    }
 
    void M2(out int x) => x = 0;
}",
@"class C
{
    void M()
    {
        M2(out _);
 
        // Unrelated, unused local should not be removed.
        int unused;
    }
 
    void M2(out int x) => x = 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task OutArgument_NoReads_PreferUnusedLocal()
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        int x;
        M2(out [|x|]);
    }
 
    void M2(out int x) => x = 0;
}", options: PreferUnusedLocal);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard), "_")]
        [InlineData(nameof(PreferUnusedLocal), "var unused")]
        public async Task OutDeclarationExpressionArgument(string optionName, string fix)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        M2(out var [|x|]);
        x = 1;
        return x;
    }
 
    void M2(out int x) => x = 0;
}",
$@"class C
{{
    int M()
    {{
        M2(out {fix});
        int x = 1;
        return x;
    }}
 
    void M2(out int x) => x = 0;
}}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task NonRedundantRefArgument(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    int M(int x)
    {
        M2(ref [|x|]);
        x = 1;
        return x;
    }
 
    void M2(ref int x) => x = 0;
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task NonRedundantInArgument(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    int M(int x)
    {
        M2(in [|x|]);
        x = 1;
        return x;
    }
 
    void M2(in int x) { }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard), "_")]
        [InlineData(nameof(PreferUnusedLocal), "unused")]
        public async Task DeconstructionDeclarationExpression(string optionName, string fix)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        var ([|x|], y) = (1, 1);
        x = 1;
        return x;
    }
}",
$@"class C
{{
    int M()
    {{
        var ({fix}, y) = (1, 1);
        int x = 1;
        return x;
    }}
}}", optionName);
        }
 
        [Fact]
        public async Task DeconstructionAssignment_01_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int x, y;
        ([|x|], y) = (1, 1);
        x = 1;
        return x;
    }
}",
@"class C
{
    int M()
    {
        int x, y;
        (_, y) = (1, 1);
        x = 1;
        return x;
    }
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task DeconstructionAssignment_01_PreferUnusedLocal()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int x, y;
        ([|x|], y) = (1, 1);
        x = 1;
        return x;
    }
}",
@"class C
{
    int M()
    {
        int x, y;
        int unused;
        (unused, y) = (1, 1);
        x = 1;
        return x;
    }
}", options: PreferUnusedLocal);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task DeconstructionAssignment_02(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int [|x|] = 0, y = 0;
        (x, y) = (x, y);
        return x;
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard), "_")]
        [InlineData(nameof(PreferUnusedLocal), "var unused")]
        public async Task TupleExpressionWithDeclarationExpressions(string optionName, string fix)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        (var [|x|], var y) = (1, 1);
        x = 1;
        return x;
    }
}",
$@"class C
{{
    int M()
    {{
        ({fix}, var y) = (1, 1);
        int x = 1;
        return x;
    }}
}}", optionName);
        }
 
        [Fact]
        public async Task DeclarationPatternInSwitchCase_WithOnlyWriteReference_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M(object p)
    {
        switch (p)
        {
            case int [|x|]:
                x = 1;
                break;
        };
    }
}",
@"class C
{
    void M(object p)
    {
        switch (p)
        {
            case int _:
                int x = 1;
                break;
        };
    }
}", options: PreferDiscard, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8));
        }
 
        [Fact]
        public async Task DeclarationPatternInSwitchCase_WithOnlyWriteReference_PreferDiscard_CSharp9()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M(object p)
    {
        switch (p)
        {
            case int [|x|]:
                x = 1;
                break;
        };
    }
}",
@"class C
{
    void M(object p)
    {
        switch (p)
        {
            case int:
                int x = 1;
                break;
        };
    }
}", options: PreferDiscard, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp9));
        }
 
        [Theory]
        [CombinatorialData]
        public async Task DeclarationPatternInSwitchCase_WithOnlyWriteReference_PreferUnusedLocal(
            [CombinatorialValues(LanguageVersion.CSharp8, LanguageVersion.CSharp9)] LanguageVersion languageVersion)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(object p)
    {
        switch (p)
        {
            case int [|x|]:
                x = 1;
                break;
        };
    }
}", new TestParameters(options: PreferUnusedLocal, parseOptions: new CSharpParseOptions(languageVersion)));
        }
 
        [Theory]
        [CombinatorialData]
        public async Task DeclarationPatternInSwitchCase_WithOnlyWriteReference_TypePattern(
            [CombinatorialValues(CodeFixTestBehaviors.None, CodeFixTestBehaviors.FixOne)] CodeFixTestBehaviors testBehaviors)
        {
            var source =
@"class C
{
    void M(object p)
    {
        switch (p)
        {
            case int {|IDE0059:x|}:
                {|IDE0059:x|} = 1;
                break;
        };
    }
}";
            var (fixedSource, iterations) = testBehaviors switch
            {
                CodeFixTestBehaviors.None =>
(@"class C
{
    void M(object p)
    {
        switch (p)
        {
            case int:
                break;
        };
    }
}", iterations: 2),
                CodeFixTestBehaviors.FixOne =>
(@"class C
{
    void M(object p)
    {
        switch (p)
        {
            case int:
                int {|IDE0059:x|} = 1;
                break;
        };
    }
}", iterations: 1),
                _ => throw ExceptionUtilities.Unreachable(),
            };
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedState = { Sources = { fixedSource }, MarkupHandling = MarkupMode.Allow },
                LanguageVersion = LanguageVersion.CSharp9,
                CodeFixTestBehaviors = testBehaviors,
                NumberOfIncrementalIterations = iterations,
                NumberOfFixAllIterations = iterations,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable },
                },
            }.RunAsync();
        }
 
        [Fact]
        public async Task DeclarationPatternInIsPattern_WithNoReference_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M(object p)
    {
        if (p is C [|x|])
        {
        }
    }
}",
@"class C
{
    void M(object p)
    {
        if (p is C)
        {
        }
    }
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task DeclarationPatternInIsPattern_WithNoReference_PreferUnusedLocal()
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(object p)
    {
        if (p is C [|x|])
        {
        }
    }
}", options: PreferUnusedLocal);
        }
 
        [Fact]
        public async Task DeclarationPatternInIsPattern_WithOnlyWriteReference_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M(object p)
    {
        if (p is C [|x|])
        {
            x = null;
        }
    }
}",
@"class C
{
    void M(object p)
    {
        if (p is C)
        {
            C x = null;
        }
    }
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task DeclarationPatternInIsPattern_WithOnlyWriteReference_PreferUnusedLocal()
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(object p)
    {
        if (p is C [|x|])
        {
            x = null;
        }
    }
}", options: PreferUnusedLocal);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard), "C")]
        [InlineData(nameof(PreferUnusedLocal), "C unused")]
        public async Task DeclarationPatternInIsPattern_WithReadAndWriteReference(string optionName, string fix)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M(object p)
    {
        if (p is C [|x|])
        {
            x = null;
            p = x;
        }
    }
}",
$@"class C
{{
    void M(object p)
    {{
        if (p is {fix})
        {{
            C x = null;
            p = x;
        }}
    }}
}}", optionName: optionName);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32271")]
        public async Task DeclarationPatternInRecursivePattern_WithNoReference_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M(object p1, object p2)
    {
        var isZero = (p1, p2) switch { (0, 0) => true, (int [|x1|], int x2) => false };
    }
}",
@"class C
{
    void M(object p1, object p2)
    {
        var isZero = (p1, p2) switch { (0, 0) => true, (int _, int x2) => false };
    }
}", options: PreferDiscard, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8));
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/32271")]
        [CombinatorialData]
        public async Task DeclarationPatternInRecursivePattern_WithNoReference_PreferUnusedLocal(
            [CombinatorialValues(LanguageVersion.CSharp8, LanguageVersion.CSharp9)] LanguageVersion languageVersion)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(object p1, object p2)
    {
        var isZero = (p1, p2) switch { (0, 0) => true, (int [|x1|], int x2) => false };
    }
}", options: PreferUnusedLocal, parseOptions: new CSharpParseOptions(languageVersion));
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/32271")]
        [CombinatorialData]
        public async Task DeclarationPatternInRecursivePattern_WithNoReference_TypePattern(
            [CombinatorialValues(CodeFixTestBehaviors.None, CodeFixTestBehaviors.FixOne)] CodeFixTestBehaviors testBehaviors)
        {
            var source =
@"class C
{
    bool M(object p1, object p2)
    {
        var isZero = (p1, p2) switch { (0, 0) => true, (int {|IDE0059:x1|}, int {|IDE0059:x2|}) => false };
        return isZero;
    }
}";
            var batchFixedSource =
@"class C
{
    bool M(object p1, object p2)
    {
        var isZero = (p1, p2) switch { (0, 0) => true, (int, int) => false };
        return isZero;
    }
}";
            var fixedSource = testBehaviors switch
            {
                CodeFixTestBehaviors.None => batchFixedSource,
                CodeFixTestBehaviors.FixOne =>
                @"class C
{
    bool M(object p1, object p2)
    {
        var isZero = (p1, p2) switch { (0, 0) => true, (int, int {|IDE0059:x2|}) => false };
        return isZero;
    }
}",
                _ => throw ExceptionUtilities.Unreachable()
            };
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedState = { Sources = { fixedSource }, MarkupHandling = MarkupMode.Allow },
                BatchFixedCode = batchFixedSource,
                LanguageVersion = LanguageVersion.CSharp9,
                CodeFixTestBehaviors = testBehaviors,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable },
                },
            }.RunAsync();
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32271")]
        public async Task DeclarationPatternInRecursivePattern_WithOnlyWriteReference_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M(object p1, object p2)
    {
        var isZero = (p1, p2) switch { (0, 0) => true, (int [|x1|], int x2) => M2(out x1) };
    }
 
    bool M2(out int x)
    {
        x = 0;
        return false;
    }
}",
@"class C
{
    void M(object p1, object p2)
    {
        int x1;
        var isZero = (p1, p2) switch { (0, 0) => true, (int _, int x2) => M2(out x1) };
    }
 
    bool M2(out int x)
    {
        x = 0;
        return false;
    }
}", options: PreferDiscard, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8));
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/32271")]
        [CombinatorialData]
        public async Task DeclarationPatternInRecursivePattern_WithOnlyWriteReference_PreferUnusedLocal(
            [CombinatorialValues(LanguageVersion.CSharp8, LanguageVersion.CSharp9)] LanguageVersion languageVersion)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(object p1, object p2)
    {
        var isZero = (p1, p2) switch { (0, 0) => true, (int [|x1|], int x2) => M2(out x1) };
    }
 
    bool M2(out int x)
    {
        x = 0;
        return false;
    }
}", options: PreferUnusedLocal, parseOptions: new CSharpParseOptions(languageVersion));
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/32271")]
        [CombinatorialData]
        public async Task DeclarationPatternInRecursivePattern_WithOnlyWriteReference_TypePattern(
            [CombinatorialValues(CodeFixTestBehaviors.None, CodeFixTestBehaviors.FixOne)] CodeFixTestBehaviors testBehaviors)
        {
            var source =
@"class C
{
    bool M(object p1, object p2)
    {
        var isZero = (p1, p2) switch { (0, 0) => true, (int {|IDE0059:x1|}, int {|IDE0059:x2|}) => M2(out {|IDE0059:x1|}) };
        return isZero;
    }
 
    bool M2(out int x)
    {
        x = 0;
        return false;
    }
}";
            var batchFixedSource =
@"class C
{
    bool M(object p1, object p2)
    {
        var isZero = (p1, p2) switch { (0, 0) => true, (int, int) => M2(out _) };
        return isZero;
    }
 
    bool M2(out int x)
    {
        x = 0;
        return false;
    }
}";
            var fixedSource = testBehaviors switch
            {
                CodeFixTestBehaviors.None => batchFixedSource,
                CodeFixTestBehaviors.FixOne =>
@"class C
{
    bool M(object p1, object p2)
    {
        int x1;
        var isZero = (p1, p2) switch { (0, 0) => true, (int, int {|IDE0059:x2|}) => M2(out {|IDE0059:x1|}) };
        return isZero;
    }
 
    bool M2(out int x)
    {
        x = 0;
        return false;
    }
}",
                _ => throw ExceptionUtilities.Unreachable(),
            };
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedState = { Sources = { fixedSource }, MarkupHandling = MarkupMode.Allow },
                BatchFixedCode = batchFixedSource,
                LanguageVersion = LanguageVersion.CSharp9,
                CodeFixTestBehaviors = testBehaviors,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable },
                },
            }.RunAsync();
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/32271")]
        [InlineData(nameof(PreferDiscard), "_")]
        [InlineData(nameof(PreferUnusedLocal), "unused")]
        public async Task DeclarationPatternInRecursivePattern_WithReadAndWriteReference(string optionName, string fix)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M(object p1, object p2)
    {
        var isZero = (p1, p2) switch { (0, 0) => true, (int [|x1|], int x2) => M2(x1 = 0) && M2(x1) };
    }
 
    bool M2(int x)
    {
        return false;
    }
}",
$@"class C
{{
    void M(object p1, object p2)
    {{
        int x1;
        var isZero = (p1, p2) switch {{ (0, 0) => true, (int {fix}, int x2) => M2(x1 = 0) && M2(x1) }};
    }}
 
    bool M2(int x)
    {{
        return false;
    }}
}}", optionName: optionName, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8));
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/32271")]
        [InlineData(nameof(PreferDiscard), "")]
        [InlineData(nameof(PreferUnusedLocal), " unused")]
        public async Task DeclarationPatternInRecursivePattern_WithReadAndWriteReference_TypePatternxxxxxxxxxxxxxxxxxxxxxx(string optionName, string fix)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M(object p1, object p2)
    {
        var isZero = (p1, p2) switch { (0, 0) => true, (int [|x1|], int x2) => M2(x1 = 0) && M2(x1) };
    }
 
    bool M2(int x)
    {
        return false;
    }
}",
$@"class C
{{
    void M(object p1, object p2)
    {{
        int x1;
        var isZero = (p1, p2) switch {{ (0, 0) => true, (int{fix}, int x2) => M2(x1 = 0) && M2(x1) }};
    }}
 
    bool M2(int x)
    {{
        return false;
    }}
}}", optionName: optionName, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp9));
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/32271")]
        [CombinatorialData]
        public async Task DeclarationPatternInRecursivePattern_WithReadAndWriteReference_TypePattern(
            [CombinatorialValues(UnusedValuePreference.DiscardVariable, UnusedValuePreference.UnusedLocalVariable)] object option,
            [CombinatorialValues(CodeFixTestBehaviors.None, CodeFixTestBehaviors.FixOne | CodeFixTestBehaviors.SkipFixAllCheck)] CodeFixTestBehaviors testBehaviors)
        {
            var source =
@"class C
{
    bool M(object p1, object p2)
    {
        var isZero = (p1, p2) switch { (0, 0) => true, (int {|IDE0059:x1|}, int {|#0:x2|}) => M2(x1 = 0) && M2(x1) };
        return isZero;
    }
 
    bool M2(int {|IDE0060:x|})
    {
        return false;
    }
}";
 
            var fixedSource = ((UnusedValuePreference)option, testBehaviors) switch
            {
                (UnusedValuePreference.DiscardVariable, CodeFixTestBehaviors.None) =>
@"class C
{
    bool M(object p1, object p2)
    {
        int x1;
        var isZero = (p1, p2) switch { (0, 0) => true, (int, int) => M2(x1 = 0) && M2(x1) };
        return isZero;
    }
 
    bool M2(int {|IDE0060:x|})
    {
        return false;
    }
}",
                (UnusedValuePreference.DiscardVariable, CodeFixTestBehaviors.FixOne | CodeFixTestBehaviors.SkipFixAllCheck) =>
@"class C
{
    bool M(object p1, object p2)
    {
        int x1;
        var isZero = (p1, p2) switch { (0, 0) => true, (int, int {|IDE0059:x2|}) => M2(x1 = 0) && M2(x1) };
        return isZero;
    }
 
    bool M2(int {|IDE0060:x|})
    {
        return false;
    }
}",
                (UnusedValuePreference.UnusedLocalVariable, _) =>
@"class C
{
    bool M(object p1, object p2)
    {
        int x1;
        var isZero = (p1, p2) switch { (0, 0) => true, (int unused, int x2) => M2(x1 = 0) && M2(x1) };
        return isZero;
    }
 
    bool M2(int {|IDE0060:x|})
    {
        return false;
    }
}",
                _ => throw ExceptionUtilities.Unreachable(),
            };
 
            var test = new VerifyCS.Test
            {
                TestCode = source,
                FixedState = { Sources = { fixedSource }, MarkupHandling = MarkupMode.Allow },
                LanguageVersion = LanguageVersion.CSharp9,
                CodeFixTestBehaviors = testBehaviors,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option },
                },
            };
 
            if ((UnusedValuePreference)option == UnusedValuePreference.DiscardVariable)
            {
                test.TestState.ExpectedDiagnostics.Add(
                    // /0/Test0.cs(5,69): info IDE0059: Unnecessary assignment of a value to 'x2'
                    VerifyCS.Diagnostic("IDE0059").WithSeverity(DiagnosticSeverity.Info).WithLocation(0).WithArguments("x2"));
            }
 
            await test.RunAsync();
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_WithInvocation(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        Action lambda = () =>
        {
            var x = p;
        };
 
        [|p|] = null;
        lambda();
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_WithInvocation_DefinedAtStart(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        void LocalFunction()
        {
            var x = p;
        }
 
        [|p|] = null;
        LocalFunction();
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_WithInvocation_DefinedAtEnd(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        [|p|] = null;
        LocalFunction();
 
        void LocalFunction()
        {
            var x = p;
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_WithoutInvocation(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        [|p|] = null;
        Action lambda = () =>
        {
            var x = p;
        };
    }
}",
@"using System;
 
class C
{
    void M(object p)
    {
        Action lambda = () =>
        {
            var x = p;
        };
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_WithoutInvocation_DefinedAtStart(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        void LocalFunction()
        {
            var x = p;
        }
        [|p|] = null;
    }
}",
@"using System;
 
class C
{
    void M(object p)
    {
        void LocalFunction()
        {
            var x = p;
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_WithoutInvocation_DefinedAtEnd(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        [|p|] = null;
        void LocalFunction()
        {
            var x = p;
        }
    }
}",
@"using System;
 
class C
{
    void M(object p)
    {
        void LocalFunction()
        {
            var x = p;
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task NotUseInLambda_WithInvocation(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        Action lambda = () =>
        {
        };
        [|p|] = null;
        lambda();
    }
}",
@"using System;
 
class C
{
    void M(object p)
    {
        Action lambda = () =>
        {
        };
        lambda();
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task NotUseInLocalFunction_WithInvocation(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        [|p|] = null;
        LocalFunction();
        void LocalFunction()
        {
        }
    }
}",
@"using System;
 
class C
{
    void M(object p)
    {
        LocalFunction();
        void LocalFunction()
        {
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task NotUseInLambda_WithoutInvocation(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        [|p|] = null;
        Action lambda = () =>
        {
        };
    }
}",
@"using System;
 
class C
{
    void M(object p)
    {
        Action lambda = () =>
        {
        };
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task NotUseInLocalFunction_WithoutInvocation(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        [|p|] = null;
        void LocalFunction()
        {
        }
    }
}",
@"using System;
 
class C
{
    void M(object p)
    {
        void LocalFunction()
        {
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task RedundantWriteInLambda_WithInvocation(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        Action lambda = () =>
        {
            [|p|] = null;
        };
        lambda();
    }
}",
@"using System;
 
class C
{
    void M(object p)
    {
        Action lambda = () =>
        {
        };
        lambda();
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task RedundantWriteInLocalFunction_WithInvocation(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        LocalFunction();
        void LocalFunction()
        {
            [|p|] = null;
        }
    }
}",
@"using System;
 
class C
{
    void M(object p)
    {
        LocalFunction();
        void LocalFunction()
        {
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task WriteThenReadInLambda_WithInvocation(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        Action lambda = () =>
        {
            [|p|] = null;
            var x = p;
        };
        lambda();
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task WriteThenReadInLocalFunction_WithInvocation(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        LocalFunction();
        void LocalFunction()
        {
            [|p|] = null;
            var x = p;
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task RedundantWriteInLambda_WithoutInvocation(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        Action lambda = () =>
        {
            [|p|] = null;
        };
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task RedundantWriteInLocalFunction_WithoutInvocation(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        void LocalFunction()
        {
            [|p|] = null;
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_Nested(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        Action outerLambda = () =>
        {
            Action innerLambda = () =>
            {
                var x = p;
            };
 
            innerLambda();
        });
 
        [|p|] = null;
        outerLambda();
    }
 
    void M2(Action a) => a();
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_NestedLocalFunction(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        [|p|] = null;
        OuterLocalFunction();
 
        void OuterLocalFunction()
        {
            InnerLocalFunction();
 
            void InnerLocalFunction()
            {
                var x = p;
            }
        });
    }
 
    void M2(Action a) => a();
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_NestedLocalFunction(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p, Action<Action> outerDelegate)
    {
        [|p|] = null;
        outerDelegate(() =>
        {
            InnerLocalFunction();
            void InnerLocalFunction()
            {
                var x = p;
            }
        });
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_NestedLambda(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p, Action<Action> myDelegate)
    {
        [|p|] = null;
        OuterLocalFunction();
 
        void OuterLocalFunction()
        {
            myDelegate(() =>
            {
                var x = p;
            });
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInNestedLambda_InvokedInOuterFunction(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p, Action myDelegate)
    {
        [|p|] = null;
        OuterLocalFunction();
        myDelegate();
 
        void OuterLocalFunction()
        {
            myDelegate = () =>
            {
                var x = p;
            };
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInNestedLocalFunction_InvokedInOuterFunction(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p, Action myDelegate)
    {
        [|p|] = null;
        OuterLocalFunction();
        myDelegate();
 
        void OuterLocalFunction()
        {
            myDelegate = NestedLocalFunction;
            void NestedLocalFunction()
            {
                var x = p;
            }
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_ArgumentToLambda(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p, Action<object> myDelegate)
    {
        [|p|] = null;
        myDelegate(p);
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_ArgumentToLambda_02(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    Action<int> M(object p, Action<object> myDelegate)
    {
        [|p|] = null;
        return d => { myDelegate(0); };
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_PassedAsArgument(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        [|p|] = null;
        M2(() =>
        {
            var x = p;
        });
    }
 
    void M2(Action a) => a();
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_PassedAsArgument_02(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    public C(bool flag)
    {
        Flag = flag;
    }
 
    public bool Flag { get; }
    public static bool M()
    {
        bool flag = true;
        var c = Create(() => flag);
 
        M2(c);
        [|flag|] = false;
        return M2(c);
    }
 
    private static C Create(Func<bool> isFlagTrue) { return new C(isFlagTrue()); }
    private static bool M2(C c) => c.Flag;
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_PassedAsArgument(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        [|p|] = null;
        M2(LocalFunction);
 
        void LocalFunction()
        {
            var x = p;
        }
    }
 
    void M2(Action a) => a();
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_PassedAsArgument_CustomDelegate(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
public delegate void MyAction();
 
class C
{
    void M(object p)
    {
        [|p|] = null;
        M2(() =>
        {
            var x = p;
        });
    }
 
    void M2(MyAction a) => a();
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_PassedAsArgument_CustomDelegate(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
public delegate void MyAction();
 
class C
{
    void M(object p)
    {
        [|p|] = null;
        M2(LocalFunction);
 
        void LocalFunction()
        {
            var x = p;
        }
    }
 
    void M2(MyAction a) => a();
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/31744")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UnusedInExpressionTree_PassedAsArgument(string optionName)
        {
            // Currently we bail out of analysis in presence of expression trees.
            await TestMissingInRegularAndScriptAsync(
@"using System;
using System.Linq.Expressions;
 
class C
{
    public static void M1()
    {
        object [|p|] = null;
        M2(x => x.M3());
    }
 
    private static C M2(Expression<Func<C, int>> a) { return null; }
    private int M3() { return 0; }
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/31744")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task ReadInExpressionTree_PassedAsArgument(string optionName)
        {
            // Currently we bail out of analysis in presence of expression trees.
            await TestMissingInRegularAndScriptAsync(
@"using System;
using System.Linq.Expressions;
 
class C
{
    public static void M1()
    {
        object [|p|] = null;
        M2(x => x.M3(p));
    }
 
    private static C M2(Expression<Func<C, int>> a) { return null; }
    private int M3(object o) { return 0; }
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/31744")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task OnlyWrittenInExpressionTree_PassedAsArgument(string optionName)
        {
            // Currently we bail out of analysis in presence of expression trees.
            await TestMissingInRegularAndScriptAsync(
@"using System;
using System.Linq.Expressions;
 
class C
{
    public static void M1()
    {
        object [|p|] = null;
        M2(x => x.M3(out p));
    }
 
    private static C M2(Expression<Func<C, int>> a) { return null; }
    private int M3(out object o) { o = null; return 0; }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_PassedAsArgument_CastFromDelegateType(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        Action a = () =>
        {
            var x = p;
        };
 
        object o = a;
        [|p|] = null;
        M2(o);
    }
 
    void M2(object a) => ((Action)a)();
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_PassedAsArgument_CastFromDelegateType(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        object o = (Action)LocalFunction;
        [|p|] = null;
        M2(o);
 
        void LocalFunction()
        {
            var x = p;
        }
    }
 
    void M2(object a) => ((Action)a)();
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_DelegateCreationPassedAsArgument(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        [|p|] = null;
        M2(new Action(() =>
        {
            var x = p;
        }));
    }
 
    void M2(Action a) => a();
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_DelegateCreationPassedAsArgument(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        [|p|] = null;
        M2(new Action(LocalFunction));
 
        void LocalFunction()
        {
            var x = p;
        }
    }
 
    void M2(Action a) => a();
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_DelegatePassedAsArgument(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        Action local = () =>
        {
            var x = p;
        };
 
        [|p|] = null;
        M2(local);
    }
 
    void M2(Action a) => a();
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_DelegatePassedAsArgument(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        Action local = LocalFunction;
        [|p|] = null;
        M2(local);
 
        void LocalFunction()
        {
            var x = p;
        }
    }
 
    void M2(Action a) => a();
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task WrittenInLambda_DelegatePassedAsArgument(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p, object p2)
    {
        Action local = () =>
        {
            p = p2;
        };
 
        [|p|] = null;
        M2(local);
 
        var x = p;
    }
 
    void M2(Action a) => a();
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task WrittenInLocalFunction_DelegatePassedAsArgument(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p, object p2)
    {
        Action local = LocalFunction;
        [|p|] = null;
        M2(local);
        var x = p;
 
        void LocalFunction()
        {
            p = p2;
        }
    }
 
    void M2(Action a) => a();
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task WrittenInLambdaAndLocalFunctionTargets_DelegatePassedAsArgument(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p, object p2)
    {
        Action lambda = () =>
        {
            p = p2;
        };
 
        Action myDelegate;
        if (p2 != null)
        {
            myDelegate = lambda;
        }
        else
        {
            myDelegate = LocalFunction;
        }
 
        [|p|] = null;
        M2(myDelegate);
 
        var x = p;
 
        void LocalFunction()
        {
            p = p2;
        }
    }
 
    void M2(Action a) => a();
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_ReturnedDelegateCreation(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    Action M(object p)
    {
        [|p|] = null;
        return new Action(() =>
        {
            var x = p;
        });
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_ReturnedDelegateCreation(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    Action M(object p)
    {
        [|p|] = null;
        return new Action(LocalFunction);
 
        void LocalFunction()
        {
            var x = p;
        };
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_ReturnedDelegate(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    Action M(object p)
    {
        Action local = () =>
        {
            var x = p;
        };
 
        [|p|] = null;
        return local;
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_ReturnedDelegate(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    Action M(object p)
    {
        [|p|] = null;
        return LocalFunction;
 
        void LocalFunction()
        {
            var x = p;
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_InvokedDelegate_ControlFlow(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p, bool flag)
    {
        Action local1 = () =>
        {
            var x = p;
        };
 
        Action local2 = () => { };
 
        [|p|] = null;
        var y = flag ? local1 : local2;
        y();
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_InvokedDelegate_ControlFlow(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p, bool flag)
    {
        [|p|] = null;
        (flag ? LocalFunction1 : (Action)LocalFunction2)();
 
        void LocalFunction1()
        {
            var x = p;
        }
 
        void LocalFunction2()
        {
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_LambdaAndLocalFunctionTargets(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p, bool flag, bool flag2)
    {
        Action lambda = () =>
        {
            var x = p;
        };
 
        [|p|] = null;
        var y = flag ? lambda : (flag2 ? (Action)LocalFunction : M2);
        y();
 
        void LocalFunction() { }
    }
 
    void M2() { }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task NotUsed_LambdaAndLocalFunctionTargets(string optionName)
        {
            // Below should be changed to verify diagnostic/fix once we
            // perform points-to-analysis for accurate delegate target tracking.
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p, bool flag, bool flag2)
    {
        Action lambda = () =>
        {
        };
 
        [|p|] = null;
        var y = flag ? lambda : (flag2 ? (Action)LocalFunction : M2);
        y();
 
        void LocalFunction() { }
    }
 
    void M2() { }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_LambdaAndLocalFunctionTargets_ThroughLocalsAndParameters(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p, bool flag, bool flag2, Action param)
    {
        Action lambda = () =>
        {
            var x = p;
        };
 
        [|p|] = null;
 
        Action y;
        if (flag)
        {
            if (flag2)
            {
                y = (Action)LocalFunction;
            }
            else
            {
                y = M2;
            }
        }
        else
        {
            y = null;
            if (flag2)
            {
                param = lambda;
            }
            else
            {
                param = M2;
            }
        }
 
        Action z;
        if (y != null)
        {
            z = y;
        }
        else
        {
            z = param;
        }
 
        z();
 
        void LocalFunction() { }
    }
 
    void M2() { }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task NotUsed_LambdaAndLocalFunctionTargets_ThroughLocalsAndParameters(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p, bool flag, bool flag2, Action param)
    {
        Action lambda = () =>
        {
        };
 
        [|p|] = null;
 
        Action y;
        if (flag)
        {
            if (flag2)
            {
                y = (Action)LocalFunction;
            }
            else
            {
                y = M2;
            }
        }
        else
        {
            y = null;
            if (flag2)
            {
                param = lambda;
            }
            else
            {
                param = M2;
            }
        }
 
        Action z;
        if (y != null)
        {
            z = y;
        }
        else
        {
            z = param;
        }
 
        z();
 
        void LocalFunction() { }
    }
 
    void M2() { }
}",
@"using System;
 
class C
{
    void M(object p, bool flag, bool flag2, Action param)
    {
        Action lambda = () =>
        {
        };
        Action y;
        if (flag)
        {
            if (flag2)
            {
                y = (Action)LocalFunction;
            }
            else
            {
                y = M2;
            }
        }
        else
        {
            y = null;
            if (flag2)
            {
                param = lambda;
            }
            else
            {
                param = M2;
            }
        }
 
        Action z;
        if (y != null)
        {
            z = y;
        }
        else
        {
            z = param;
        }
 
        z();
 
        void LocalFunction() { }
    }
 
    void M2() { }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInLambda_UsedAfterInvocation(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    int M(int x)
    {
        Action a = () =>
        {
            [|x|] = 1;
        };
        a();
 
        return x;
    }
}
", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInLocalFunction_UsedAfterInvocation(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    int M(int x)
    {
        a();
 
        return x;
 
        void a()
        {
            [|x|] = 1;
        }
    }
}
", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInLambda_UsedAfterSecondInvocation(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    int M(int x)
    {
        Action a = () =>
        {
            [|x|] = 1;
        };
 
        a();
        a();
 
        return x;
    }
}
", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInLocalFunction_UsedAfterSecondInvocation(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    int M(int x)
    {
        a();
        a();
 
        return x;
 
        void a()
        {
            [|x|] = 1;
        }
    }
}
", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInLambda_MayBeUsedAfterOneOfTheInvocations(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    int M(int x, bool flag, bool flag2)
    {
        Action a = () =>
        {
            [|x|] = 1;
        };
 
        a();
 
        if (flag)
        {
            a();
            if (flag2)
            {
                return x;
            }
        }
 
        return 0;
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInLocalFunction_MayBeUsedAfterOneOfTheInvocations(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    int M(int x, bool flag, bool flag2)
    {
        a();
 
        if (flag)
        {
            a();
            if (flag2)
            {
                return x;
            }
        }
 
        return 0;
 
        void a()
        {
            [|x|] = 1;
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInLambda_NotUsedAfterInvocation(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(int x)
    {
        Action a = () =>
        {
            [|x|] = 1;
        };
        a();
    }
}
",
@"using System;
 
class C
{
    void M(int x)
    {
        Action a = () =>
        {
        };
        a();
    }
}
", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInLocalFunction_NotUsedAfterInvocation(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(int x)
    {
        a();
 
        void a()
        {
            [|x|] = 1;
        }
    }
}",
@"using System;
 
class C
{
    void M(int x)
    {
        a();
 
        void a()
        {
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_WithRecursiveInvocation(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        void LocalFunction()
        {
            var x = p;
            LocalFunction();
        }
 
        [|p|] = null;
        LocalFunction();
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task NotUseInLocalFunction_WithRecursiveInvocation(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        void LocalFunction()
        {
            LocalFunction();
        }
 
        [|p|] = null;
        LocalFunction();
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task Lambda_WithNonReachableExit(string optionName)
        {
            // We bail out from analysis for delegate passed an argument.
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        Action throwEx = () =>
        {
            throw new Exception();
        };
 
        [|p|] = null;
        M2(throwEx);
    }
 
    void M2(Action a) { }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task Lambda_WithMultipleInvocations(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        Action lambda = () =>
        {
            var x = p;
            [|p|] = null;   // This write is read on next invocation of lambda.
        };
 
        M2(lambda);
    }
 
    void M2(Action a)
    {
        a();
        a();
    }
}", optionName);
        }
 
        [Fact]
        public async Task UnusedValue_DelegateTypeOptionalParameter_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M()
    {
        var [|x|] = M2();
    }
 
    C M2(Action c = null) => null;
}",
@"using System;
 
class C
{
    void M()
    {
        _ = M2();
    }
 
    C M2(Action c = null) => null;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task UnusedValue_DelegateTypeOptionalParameter_PreferUnusedLocal()
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M()
    {
        var [|x|] = M2();
    }
 
    C M2(Action c = null) => null;
}", options: PreferUnusedLocal);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLocalFunction_NestedInvocation(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        LocalFunction();
 
        bool LocalFunction2()
        {
            return true;
        }
 
        void LocalFunction()
        {
            object [|p|] = null;
            if (LocalFunction2())
            {
            }
 
            if (p != null)
            {
            }
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard), "_")]
        [InlineData(nameof(PreferUnusedLocal), "unused")]
        public async Task DeclarationPatternInSwitchCase_WithReadAndWriteReferences(string optionName, string fix)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M(object p)
    {
        switch (p)
        {
            case int [|x|]:
                x = 1;
                p = x;
                break;
        }
    }
}",
$@"class C
{{
    void M(object p)
    {{
        switch (p)
        {{
            case int {fix}:
                int x = 1;
                p = x;
                break;
        }}
    }}
}}", optionName, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8));
        }
 
        [Fact]
        public async Task CatchClause_ExceptionVariable_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        try
        {
        }
        catch (Exception [|ex|])
        {
        }
    }
}",
@"using System;
 
class C
{
    void M(object p)
    {
        try
        {
        }
        catch (Exception)
        {
        }
    }
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task CatchClause_ExceptionVariable_PreferUnusedLocal_01()
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        try
        {
        }
        catch (Exception [|ex|])
        {
        }
    }
}", options: PreferUnusedLocal);
        }
 
        [Fact]
        public async Task CatchClause_ExceptionVariable_PreferUnusedLocal_02()
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(object p)
    {
        try
        {
        }
        catch (Exception [|ex|])
        {
            ex = null;
            var x = ex;
        }
    }
}",
@"using System;
 
class C
{
    void M(object p)
    {
        try
        {
        }
        catch (Exception unused)
        {
            Exception ex = null;
            var x = ex;
        }
    }
}", options: PreferUnusedLocal);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedOutsideTry_UsedOnlyInCatchClause(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(int x)
    {
        [|x|] = 0;
        try
        {
        }
        catch (Exception)
        {
            var y = x;
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedOutsideTry_UsedOnlyInCatchFilter(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(int x)
    {
        [|x|] = 0;
        try
        {
        }
        catch (Exception) when (x != 0)
        {
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedOutsideTry_UsedOnlyInFinally(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(int x)
    {
        [|x|] = 0;
        try
        {
        }
        finally
        {
            var y = x;
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInsideTry_UsedOnlyInCatchClause(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(int x)
    {
        try
        {
            [|x|] = 0;
        }
        catch (Exception)
        {
            var y = x;
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInsideNestedBlockInTry_UsedOnlyInCatchClause(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(int x)
    {
        try
        {
            if (x > 0)
            {
                [|x|] = 0;
            }
        }
        catch (Exception)
        {
            var y = x;
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInCatchClause_UsedAfterTryCatch(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(int x)
    {
        try
        {
        }
        catch (Exception)
        {
            [|x|] = 0;
        }
 
        var y = x;
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInNestedCatchClause_UsedInOuterFinally(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(int x)
    {
        try
        {
            try
            {
            }
            catch (Exception)
            {
                [|x|] = 0;
            }
        }
        finally
        {
            var y = x;
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInCatchClause_UsedInFinally(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(int x)
    {
        try
        {
        }
        catch (Exception)
        {
            [|x|] = 0;
        }
        finally
        {
            var y = x;
        }
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInCatchFilter_UsedAfterTryCatch(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(int x)
    {
        try
        {
        }
        catch (Exception) when (M2(out [|x|]))
        {
        }
 
        var y = x;
    }
 
    bool M2(out int x) { x = 0; return true; }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInFinally_UsedAfterTryFinally(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(int x)
    {
        try
        {
        }
        finally
        {
            [|x|] = 0;
        }
 
        var y = x;
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInNestedFinally_UsedInOuterFinally(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(int x)
    {
        try
        {
            try
            {
            }
            finally
            {
                [|x|] = 0;
            }
        }
        finally
        {
            var y = x;
        }
    }
}", optionName);
        }
 
        [Fact]
        public async Task IfElse_AssignedInCondition_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool flag)
    {
        int x;
        if (M2(out [|x|]))
        {
            x = 2;
        }
        else
        {
            x = 3;
        }
    }
 
    bool M2(out int x) => x = 0;
}",
@"class C
{
    void M(bool flag)
    {
        int x;
        if (M2(out _))
        {
            x = 2;
        }
        else
        {
            x = 3;
        }
    }
 
    bool M2(out int x) => x = 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task IfElse_DeclaredInCondition_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool flag)
    {
        if (M2(out var [|x|]))
        {
            x = 2;
        }
        else
        {
            x = 3;
        }
    }
 
    bool M2(out int x) => x = 0;
}",
@"class C
{
    void M(bool flag)
    {
        int x;
        if (M2(out _))
        {
            x = 2;
        }
        else
        {
            x = 3;
        }
    }
 
    bool M2(out int x) => x = 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task IfElseAssignedInCondition_ReadAfter_PreferUnusedLocal()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M(bool flag)
    {
        int x;
        if (M2(out [|x|]))
        {
            x = 2;
        }
        else
        {
            x = 3;
        }
 
        return x;
    }
 
    bool M2(out int x) => x = 0;
}",
@"class C
{
    int M(bool flag)
    {
        int x;
        int unused;
        if (M2(out unused))
        {
            x = 2;
        }
        else
        {
            x = 3;
        }
 
        return x;
    }
 
    bool M2(out int x) => x = 0;
}", options: PreferUnusedLocal);
        }
 
        [Fact]
        public async Task IfElse_AssignedInCondition_NoReads_PreferUnusedLocal()
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(bool flag)
    {
        int x;
        if (M2(out [|x|]))
        {
            x = 2;
        }
        else
        {
            x = 3;
        }
    }
 
    bool M2(out int x) => x = 0;
}", new TestParameters(options: PreferUnusedLocal));
        }
 
        [Fact]
        public async Task IfElse_DeclaredInCondition_ReadAfter_PreferUnusedLocal()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M(bool flag)
    {
        if (M2(out var [|x|]))
        {
            x = 2;
        }
        else
        {
            x = 3;
        }
 
        return x;
    }
 
    bool M2(out int x) => x = 0;
}",
@"class C
{
    int M(bool flag)
    {
        int x;
        if (M2(out var unused))
        {
            x = 2;
        }
        else
        {
            x = 3;
        }
 
        return x;
    }
 
    bool M2(out int x) => x = 0;
}", options: PreferUnusedLocal);
        }
 
        [Fact]
        public async Task IfElse_DeclaredInCondition_NoReads_PreferUnusedLocal()
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(bool flag)
    {
        if (M2(out var [|x|]))
        {
            x = 2;
        }
        else
        {
            x = 3;
        }
    }
 
    bool M2(out int x) => x = 0;
}", new TestParameters(options: PreferUnusedLocal));
        }
 
        [Theory]
        // Simple if-else.
        [InlineData("x = 1;", "x = 2;")]
        // Nested if-else.
        [InlineData("if(flag) { x = 1; } else { x = 2; }",
                    "x = 3;")]
        // Multiple nested paths.
        [InlineData("if(flag) { x = 1; } else { x = 2; }",
                    "if(flag) { x = 3; } else { x = 4; }")]
        // Nested if-elseif-else.
        [InlineData("if(flag) { x = 1; } else if(flag2) { x = 2; } else { x = 3; }",
                    "if(flag) { x = 5; } else { x = 6; }")]
        //Multi-level nesting.
        [InlineData(@"if(flag) { x = 1; } else { if(flag2) { if(flag3) { x = 2; } else { x = 3; } } else { x = 4; } }",
                    @"x = 5;")]
        public async Task IfElse_OverwrittenInAllControlFlowPaths(string ifBranchCode, string elseBranchCode)
        {
            await TestInRegularAndScriptWithAllOptionsAsync(
$@"class C
{{
    int M(bool flag, bool flag2, bool flag3)
    {{
        int [|x|] = 1;
        if (flag4)
        {{
            {ifBranchCode}
        }}
        else
        {{
            {elseBranchCode}
        }}
 
        return x;
    }}
}}",
$@"class C
{{
    int M(bool flag, bool flag2, bool flag3)
    {{
        int x;
        if (flag4)
        {{
            {ifBranchCode}
        }}
        else
        {{
            {elseBranchCode}
        }}
 
        return x;
    }}
}}");
        }
 
        [Theory]
        // Overwrite missing in if path.
        [InlineData(";", "x = 2;")]
        // Overwrite missing in else path.
        [InlineData("x = 2;", "")]
        // Overwrite missing in nested else path.
        [InlineData("if(flag) { x = 1; }",
                    "x = 2;")]
        // Overwrite missing in multiple nested paths.
        [InlineData("if(flag) { x = 1; }",
                    "if(flag) { x = 2; }")]
        // Overwrite missing with nested if-elseif-else.
        [InlineData("if(flag) { x = 1; } else if(flag2) { x = 2; }",
                    "if(flag) { x = 3; } else { x = 4; }")]
        // Overwrite missing in one path with multi-level nesting.
        [InlineData(@"if(flag) { x = 1; } else { if(flag2) { if(flag3) { x = 2; } } else { x = 3; } }",
                    @"x = 4;")]
        public async Task IfElse_OverwrittenInSomeControlFlowPaths(string ifBranchCode, string elseBranchCode)
        {
            await TestMissingInRegularAndScriptWithAllOptionsAsync(
$@"class C
{{
    int M(bool flag, bool flag2, bool flag3)
    {{
        int [|x|] = 1;
        if (flag4)
        {{
            {ifBranchCode}
        }}
        else
        {{
            {elseBranchCode}
        }}
 
        return x;
    }}
}}");
        }
 
        [Theory]
        // Overitten in condition when true, overwritten in else code block when false.
        [InlineData("flag && M2(out x)", ";", "x = 2;")]
        // Overitten in condition when false, overwritten in if code block when true.
        [InlineData("flag || M2(out x)", "x = 2;", ";")]
        public async Task IfElse_Overwritten_CodeInOneBranch_ConditionInOtherBranch(string condition, string ifBranchCode, string elseBranchCode)
        {
            await TestInRegularAndScriptWithAllOptionsAsync(
$@"class C
{{
    int M(bool flag)
    {{
        int [|x|] = 1;
        if ({condition})
        {{
            {ifBranchCode}
        }}
        else
        {{
            {elseBranchCode}
        }}
 
        return x;
    }}
 
    bool M2(out int x) {{ x = 0; return true; }}
    int M3() => 0;
}}",
$@"class C
{{
    int M(bool flag)
    {{
        int x;
        if ({condition})
        {{
            {ifBranchCode}
        }}
        else
        {{
            {elseBranchCode}
        }}
 
        return x;
    }}
 
    bool M2(out int x) {{ x = 0; return true; }}
    int M3() => 0;
}}");
        }
 
        [Theory]
        // Overwrite missing in condition when left of || is true.
        [InlineData("flag || M2(out x)")]
        // Overwrite missing in condition when left of && is true.
        [InlineData("flag && M2(out x)")]
        // Overwrite missing in condition when left of || is true, but both both sides of && have an overwrite.
        [InlineData("flag || M2(out x) && (x = M3()) > 0")]
        public async Task IfElse_MayBeOverwrittenInCondition_LogicalOperators(string condition)
        {
            await TestMissingInRegularAndScriptWithAllOptionsAsync(
$@"class C
{{
    int M(bool flag)
    {{
        int [|x|] = 1;
        if ({condition})
        {{
        }}
        else
        {{
        }}
 
        return x;
    }}
 
    bool M2(out int x) {{ x = 0; return true; }}
    int M3() => 0;
}}");
        }
 
        [Theory]
        [InlineData("M2(out x) || flag")]
        [InlineData("M2(out x) && flag")]
        [InlineData("M2(out x) || M2(out x)")]
        [InlineData("M2(out x) && M2(out x)")]
        [InlineData("flag && M2(out x) || (x = M3()) > 0")]
        [InlineData("(flag || M2(out x)) && (x = M3()) > 0")]
        [InlineData("M2(out x) && flag || (x = M3()) > 0")]
        [InlineData("flag && M2(out x) || (x = M3()) > 0 && flag")]
        public async Task IfElse_OverwrittenInCondition_LogicalOperators(string condition)
        {
            await TestInRegularAndScriptWithAllOptionsAsync(
$@"class C
{{
    int M(bool flag)
    {{
        int [|x|] = 1;
        if ({condition})
        {{
        }}
        else
        {{
        }}
 
        return x;
    }}
 
    bool M2(out int x) {{ x = 0; return true; }}
    int M3() => 0;
}}",
        $@"class C
{{
    int M(bool flag)
    {{
        int x;
        if ({condition})
        {{
        }}
        else
        {{
        }}
 
        return x;
    }}
 
    bool M2(out int x) {{ x = 0; return true; }}
    int M3() => 0;
}}");
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task ElselessIf(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    int M(bool flag)
    {
        int [|x|] = 1;
        if (flag)
        {
            x = 1;
        }
 
        return x;
    }
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UnusedDefinition_NotFlagged_InUnreachableBlock(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        int x;
        if (true)
        {
            x = 0;
        }
        else
        {
            [|x|] = 1;
        }
 
        return x;
    }
}
 
    bool M2(out int x) { x = 0; return true; }
}", optionName);
        }
 
        [Fact]
        public async Task SwitchCase_UnusedValueWithOnlyWrite_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M(int flag)
    {
        switch(flag)
        {
            case 0:
                int [|x|] = M2();
                return 0;
 
            default:
                return flag;
        }
    }
 
    int M2() => 0;
}",
@"class C
{
    int M(int flag)
    {
        switch(flag)
        {
            case 0:
                _ = M2();
                return 0;
 
            default:
                return flag;
        }
    }
 
    int M2() => 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task SwitchCase_UnusedValueWithOnlyWrite_PreferUnusedLocal()
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    int M(int flag)
    {
        switch(flag)
        {
            case 0:
                int [|x|] = M2();
                return 0;
 
            default:
                return flag;
        }
    }
 
    int M2() => 0;
}", options: PreferUnusedLocal);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task SwitchCase_UnusedConstantValue_WithReadsAndWrites(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M(int flag)
    {
        switch(flag)
        {
            case 0:
                int [|x|] = 0;
                x = 1;
                return x;
 
            default:
                return flag;
        }
    }
 
    int M2() => 0;
}",
@"class C
{
    int M(int flag)
    {
        switch(flag)
        {
            case 0:
                int x = 1;
                return x;
 
            default:
                return flag;
        }
    }
 
    int M2() => 0;
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard), "_")]
        [InlineData(nameof(PreferUnusedLocal), "int unused")]
        public async Task SwitchCase_UnusedNonConstantValue_WithReadsAndWrites(string optionName, string fix)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M(int flag)
    {
        switch(flag)
        {
            case 0:
                int [|x|] = M2();
                x = 1;
                return x;
 
            default:
                return flag;
        }
    }
 
    int M2() => 0;
}",
$@"class C
{{
    int M(int flag)
    {{
        switch(flag)
        {{
            case 0:
                {fix} = M2();
                int x = 1;
                return x;
 
            default:
                return flag;
        }}
    }}
 
    int M2() => 0;
}}", optionName);
        }
 
        [Theory]
        // For loop, assignment in body, read on back edge.
        [InlineData("for(i = 1; i < 10; i--)",
                        "M2(x); [|x|] = 1;")]
        // While loop, assignment in body, read on back edge.
        [InlineData("while(i++ < 10)",
                        "M2(x); [|x|] = 1;")]
        // Do loop, assignment in body, read on back edge.
        [InlineData("do",
                        "M2(x); [|x|] = 1;",
                    "while(i++ < 10);")]
        // Continue, read on back edge.
        [InlineData("while(i++ < 10)",
                        "M2(x); [|x|] = 1; if (flag) continue; x = 2;")]
        // Break.
        [InlineData(@"x = 0;
                      while(i++ < 10)",
                         "[|x|] = 1; if (flag) break; x = 2;")]
        // Assignment before loop, no overwrite on path where loop is never entered.
        [InlineData(@"[|x|] = 1;
                      while(i++ < 10)",
                         "x = 2;")]
        public async Task Loops_Overwritten_InSomeControlFlowPaths(
            string loopHeader, string loopBody, string? loopFooter = null)
        {
            await TestMissingInRegularAndScriptWithAllOptionsAsync(
$@"class C
{{
    int M(int i, int x, bool flag)
    {{
        {loopHeader}
        {{
            {loopBody}
        }}
        {loopFooter ?? string.Empty}
 
        return x;
    }}
 
    void M2(int x) {{ }}
}}");
        }
 
        [Theory]
        // For loop, assignment in body, re-assigned on back edge before read in loop and re-assigned at loop exit.
        [InlineData("for(i = 1; i < 10; i--)",
                        "x = 1; M2(x); [|x|] = 2;",
                    "x = 3;",
                    // Fixed code.
                    "for(i = 1; i < 10; i--)",
                        "x = 1; M2(x);",
                    "x = 3;")]
        // While loop, assignment in body, re-assigned on condition before read in loop and re-assigned at loop exit.
        [InlineData("while(i++ < (x = 10))",
                        "M2(x); [|x|] = 2;",
                    "x = 3;",
                    // Fixed code.
                    "while(i++ < (x = 10))",
                        "M2(x);",
                    "x = 3;")]
        // Assigned before loop, Re-assigned in continue, break paths and loop exit.
        [InlineData(@"[|x|] = 1;
                      i = 1;
                      while(i++ < 10)",
                        @"if(flag)
                            { x = 2; continue; }
                          else if(i < 5)
                            { break; }
                          else
                            { x = 3; }
                          M2(x);",
                      "x = 4;",
                    // Fixed code.
                    @"i = 1;
                      while(i++ < 10)",
                        @"if(flag)
                            { x = 2; continue; }
                          else if(i < 5)
                            { break; }
                          else
                            { x = 3; }
                          M2(x);",
                      "x = 4;")]
        public async Task Loops_Overwritten_InAllControlFlowPaths(
            string loopHeader, string loopBody, string loopFooter,
            string fixedLoopHeader, string fixedLoopBody, string fixedLoopFooter)
        {
            await TestInRegularAndScriptWithAllOptionsAsync(
$@"class C
{{
    int M(int i, int x, bool flag)
    {{
        {loopHeader}
        {{
            {loopBody}
        }}
        {loopFooter}
 
        return x;
    }}
 
    void M2(int x) {{ }}
}}",
$@"class C
{{
    int M(int i, int x, bool flag)
    {{
        {fixedLoopHeader}
        {{
            {fixedLoopBody}
        }}
        {fixedLoopFooter}
 
        return x;
    }}
 
    void M2(int x) {{ }}
}}");
        }
 
        [Fact]
        public async Task FixAll_NonConstantValue_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    public C()
    {
        // Different code block
        int x = M2();
    }
 
    int M(bool flag)
    {
        // Trigger diagostic
        {|FixAllInDocument:int x = M2()|};
 
        // Unused out assignment
        M2(out x);
 
        // Used Assignment
        x = 0;
        System.Console.WriteLine(x);
 
        // Unused constant assignment.
        // Not fixed as we have a different code fix 'Remove redundant assignment'
        x = 1;
 
        // Unused initialization with only def/use in nested block.
        // Declaration for 'y' should be moved inside the if block.
        int y = M2();
        if (flag)
        {
            y = 2;
            System.Console.WriteLine(y);
        }
        else
        {
        }
 
        x = M2();
        return x;
    }
 
    bool M2(out int x) { x = 0; return true; }
    int M2() => 0;
}",
@"class C
{
    public C()
    {
        // Different code block
        _ = M2();
    }
 
    int M(bool flag)
    {
        // Trigger diagostic
        _ = M2();
 
        // Unused out assignment
        M2(out _);
 
        // Used Assignment
        int x = 0;
        System.Console.WriteLine(x);
 
        // Unused constant assignment.
        // Not fixed as we have a different code fix 'Remove redundant assignment'
        x = 1;
 
        // Unused initialization with only def/use in nested block.
        // Declaration for 'y' should be moved inside the if block.
        _ = M2();
        if (flag)
        {
            int y = 2;
            System.Console.WriteLine(y);
        }
        else
        {
        }
 
        x = M2();
        return x;
    }
 
    bool M2(out int x) { x = 0; return true; }
    int M2() => 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task FixAll_NonConstantValue_PreferUnusedLocal()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    public C()
    {
        // Different code block
        int x = M2();
        x = 0;
        System.Console.WriteLine(x);
    }
 
    int M(bool flag)
    {
        // Trigger diagostic
        {|FixAllInDocument:int x = M2()|};
 
        // Unused out assignment
        M2(out x);
 
        // Used Assignment, declaration for 'x' should move here
        x = 0;
        System.Console.WriteLine(x);
 
        // Unused constant assignment.
        // Not fixed as we have a different code fix 'Remove redundant assignment'
        x = 1;
 
        // Unused initialization with only def/use in nested block.
        // Declaration for 'y' should be moved inside the if block.
        int y = M2();
        if (flag)
        {
            y = 2;
            System.Console.WriteLine(y);
        }
        else
        {
        }
 
        x = M2();
        return x;
    }
 
    bool M2(out int x) { x = 0; return true; }
    int M2() => 0;
}",
@"class C
{
    public C()
    {
        // Different code block
        int unused = M2();
        int x = 0;
        System.Console.WriteLine(x);
    }
 
    int M(bool flag)
    {
        // Trigger diagostic
        int unused = M2();
        int unused1;
 
        // Unused out assignment
        M2(out unused1);
 
        // Used Assignment, declaration for 'x' should move here
        int x = 0;
        System.Console.WriteLine(x);
 
        // Unused constant assignment.
        // Not fixed as we have a different code fix 'Remove redundant assignment'
        x = 1;
 
        // Unused initialization with only def/use in nested block.
        // Declaration for 'y' should be moved inside the if block.
        int unused2 = M2();
        if (flag)
        {
            int y = 2;
            System.Console.WriteLine(y);
        }
        else
        {
        }
 
        x = M2();
        return x;
    }
 
    bool M2(out int x) { x = 0; return true; }
    int M2() => 0;
}", options: PreferUnusedLocal);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task FixAll_ConstantValue_RemoveRedundantAssignments(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    public C()
    {
        // Different code block
        int x = 1;
    }
 
    int M(bool flag, int p)
    {
        // Trigger diagostic
        {|FixAllInDocument:int x = 0|};
 
        // Unused assignment from parameter, should be removed.
        x = p;
 
        // Unused assignment from local, should be removed.
        int local = 3;
        x = local;
 
        // Used assignment, declaration for 'x' should move here
        x = 0;
        System.Console.WriteLine(x);
 
        // Unused non-constant 'out' assignment
        // Not fixed as we have a different code fix 'Use discard' for it.
        M2(out x);
 
        // Unused initialization with only def/use in nested block.
        // Declaration for 'y' should be moved inside the if block.
        int y = 1;
        if (flag)
        {
            y = 2;
            System.Console.WriteLine(y);
        }
        else
        {
        }
 
        x = M2();
        return x;
    }
 
    bool M2(out int x) { x = 0; return true; }
    int M2() => 0;
}",
@"class C
{
    public C()
    {
        // Different code block
    }
 
    int M(bool flag, int p)
    {
 
        // Unused assignment from parameter, should be removed.
 
        // Unused assignment from local, should be removed.
        int local = 3;
 
        // Trigger diagostic
        // Used assignment, declaration for 'x' should move here
        int x = 0;
        System.Console.WriteLine(x);
 
        // Unused non-constant 'out' assignment
        // Not fixed as we have a different code fix 'Use discard' for it.
        M2(out x);
        if (flag)
        {
            // Unused initialization with only def/use in nested block.
            // Declaration for 'y' should be moved inside the if block.
            int y = 2;
            System.Console.WriteLine(y);
        }
        else
        {
        }
 
        x = M2();
        return x;
    }
 
    bool M2(out int x) { x = 0; return true; }
    int M2() => 0;
}", optionName);
        }
 
        [Fact]
        public async Task FixAll_MoveMultipleVariableDeclarations_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M(bool flag, int p)
    {
        // Multiple unused variable declarations (x and y) moved below to start of if-else block
        // Used declaration (z1) and evaluation (_ = M2()) retained.
        // Completely unused declaration (z2) removed.
        {|FixAllInDocument:int x = 0|};
        int z1 = 1, _ = M2(), y = 0, z2 = 2;
 
        if (flag)
        {
            x = 1;
            y = 1;
        }
        else
        {
            x = 2;
            y = 2;
        }
 
        return x + y + z1;
    }
 
    int M2() => 0;
}",
@"class C
{
    int M(bool flag, int p)
    {
        int z1 = 1;
        _ = M2();
 
        // Multiple unused variable declarations (x and y) moved below to start of if-else block
        // Used declaration (z1) and evaluation (_ = M2()) retained.
        // Completely unused declaration (z2) removed.
        int x;
        int y;
        if (flag)
        {
            x = 1;
            y = 1;
        }
        else
        {
            x = 2;
            y = 2;
        }
 
        return x + y + z1;
    }
 
    int M2() => 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task FixAll_MoveMultipleVariableDeclarations_PreferUnusedLocal()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M(bool flag, int p)
    {
        // Multiple unused variable declarations (x and y) moved below to start of if-else block
        // Used declaration (z1) and evaluation (_ = M2()) retained.
        // Completely unused declaration (z2) removed.
        {|FixAllInDocument:int x = 0|};
        int z1 = 1, _ = M2(), y = 0, z2 = 2;
 
        if (flag)
        {
            x = 1;
            y = 1;
        }
        else
        {
            x = 2;
            y = 2;
        }
 
        return x + y + z1;
    }
 
    int M2() => 0;
}",
@"class C
{
    int M(bool flag, int p)
    {
        int z1 = 1, _ = M2();
 
        // Multiple unused variable declarations (x and y) moved below to start of if-else block
        // Used declaration (z1) and evaluation (_ = M2()) retained.
        // Completely unused declaration (z2) removed.
        int x;
        int y;
        if (flag)
        {
            x = 1;
            y = 1;
        }
        else
        {
            x = 2;
            y = 2;
        }
 
        return x + y + z1;
    }
 
    int M2() => 0;
}", options: PreferUnusedLocal);
        }
 
        [Fact]
        public async Task NonConstantValue_Trivia_PreferDiscard_01()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        // C1
        [|int x = M2()|], y = M2();   // C2
        // C3
 
        return y;
    }
 
    int M2() => 0;
}",
@"class C
{
    int M()
    {
        // C1
        _ = M2();
        // C1
        int y = M2();   // C2
        // C3
 
        return y;
    }
 
    int M2() => 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task NonConstantValue_Trivia_PreferDiscard_02()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        /*C1*/
        /*C2*/[|int/*C3*/ /*C4*/x/*C5*/ = /*C6*/M2()|]/*C7*/, y/*C8*/ = M2()/*C9*/;   // C10
        /*C11*/
 
        return y;
    }
 
    int M2() => 0;
}",
@"class C
{
    int M()
    {
        /*C1*/
        /*C2*//*C3*/ /*C4*/
        _/*C5*/ = /*C6*/M2()/*C7*/;
        /*C1*/
        /*C2*/
        int/*C3*/ /*C4*/y/*C8*/ = M2()/*C9*/;   // C10
        /*C11*/
 
        return y;
    }
 
    int M2() => 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task NonConstantValue_Trivia_PreferUnusedLocal_01()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        // C1
        [|int x = M2()|], y = M2();   // C2
        // C3
 
        // C4
        x = 1;
        return x + y;
    }
 
    int M2() => 0;
}",
@"class C
{
    int M()
    {
        // C1
        int unused = M2(), y = M2();   // C2
        // C3
 
        // C4
        int x = 1;
        return x + y;
    }
 
    int M2() => 0;
}", options: PreferUnusedLocal);
        }
 
        [Fact]
        public async Task NonConstantValue_Trivia_PreferUnusedLocal_02()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        /*C1*/
        /*C2*/[|int/*C3*/ /*C4*/x/*C5*/ = /*C6*/M2()|]/*C7*/, y/*C8*/ = M2()/*C9*/;   // C10
        /*C11*/
 
        // C12
        x = 1;
        return x + y;
    }
 
    int M2() => 0;
}",
@"class C
{
    int M()
    {
        /*C1*/
        /*C2*/
        int/*C3*/ /*C4*/unused/*C5*/ = /*C6*/M2()/*C7*/, y/*C8*/ = M2()/*C9*/;   // C10
        /*C11*/
 
        // C12
        int x = 1;
        return x + y;
    }
 
    int M2() => 0;
}", options: PreferUnusedLocal);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task ConstantValue_Trivia_01(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        // C1
        [|int x = 0|], y = M2();   // C2
        // C3
 
        // C4
        x = 1;
        return x + y;
    }
 
    int M2() => 0;
}",
@"class C
{
    int M()
    {
        // C1
        int y = M2();   // C2
        // C3
 
        // C4
        int x = 1;
        return x + y;
    }
 
    int M2() => 0;
}", optionName);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task ConstantValue_Trivia_02(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        /*C1*/
        /*C2*/[|int/*C3*/ /*C4*/x/*C5*/ = /*C6*/0|]/*C7*/, y/*C8*/ = M2()/*C9*/;   // C10
        /*C11*/
 
        // C12
        x = 1;
        return x + y;
    }
 
    int M2() => 0;
}",
@"class C
{
    int M()
    {
        /*C1*/
        /*C2*/
        int/*C3*/ /*C4*/y/*C8*/ = M2()/*C9*/;   // C10
        /*C11*/
 
        // C12
        int x = 1;
        return x + y;
    }
 
    int M2() => 0;
}", optionName);
        }
 
        [Fact]
        public async Task ExistingDiscardDeclarationInLambda_UseOutsideLambda()
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M()
    {
        int [|x|] = M2();
        Action a = () =>
        {
            var _ = M2();
        };
 
        a();
    }
 
    int M2() => 0;
}",
@"using System;
 
class C
{
    void M()
    {
        _ = M2();
        Action a = () =>
        {
            _ = M2();
        };
 
        a();
    }
 
    int M2() => 0;
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task ExistingDiscardDeclarationInLambda_UseInsideLambda()
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M()
    {
        Action a = () =>
        {
            int [|x|] = M2();
            var _ = M2();
        };
 
        a();
    }
 
    int M2() => 0;
}",
@"using System;
 
class C
{
    void M()
    {
        Action a = () =>
        {
            _ = M2();
            _ = M2();
        };
 
        a();
    }
 
    int M2() => 0;
}", options: PreferDiscard);
        }
 
        [Theory]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task ValueOverwrittenByOutVar_ConditionalAndExpression(string optionName)
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M()
    {
        int {|FixAllInDocument:x1|} = -1, x2 = -1;
        if (M2(x: out x1) &&
            M2(x: out x2))
        {
            x1 = 0;
            x2 = 0;
        }
        else
        {
            Console.WriteLine(x1);
        }
 
        Console.WriteLine(x1 + x2);
    }
 
    bool M2(out int x)
    {
        x = 0;
        return true;
    }
}",
@"using System;
 
class C
{
    void M()
    {
        int x2 = -1;
        int x1;
        if (M2(x: out x1) &&
            M2(x: out x2))
        {
            x1 = 0;
            x2 = 0;
        }
        else
        {
            Console.WriteLine(x1);
        }
 
        Console.WriteLine(x1 + x2);
    }
 
    bool M2(out int x)
    {
        x = 0;
        return true;
    }
}", optionName);
        }
 
        [Theory]
        [InlineData("var")]
        [InlineData("int")]
        public async Task UnusedOutVariableDeclaration_PreferDiscard(string typeName)
        {
            await TestInRegularAndScriptAsync(
$@"class C
{{
    void M()
    {{
        if (M2(out {typeName} [|x|]))
        {{
        }}
    }}
 
    bool M2(out int x)
    {{
        x = 0;
        return true;
    }}
}}",
@"class C
{
    void M()
    {
        if (M2(out _))
        {
        }
    }
 
    bool M2(out int x)
    {
        x = 0;
        return true;
    }
}", options: PreferDiscard);
        }
 
        [Fact]
        public async Task UnusedOutVariableDeclaration_MethodOverloads_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        if (M2(out int [|x|]))
        {
        }
    }
 
    bool M2(out int x)
    {
        x = 0;
        return true;
    }
 
    bool M2(out char x)
    {
        x = 'c';
        return true;
    }
}",
@"class C
{
    void M()
    {
        if (M2(out int _))
        {
        }
    }
 
    bool M2(out int x)
    {
        x = 0;
        return true;
    }
 
    bool M2(out char x)
    {
        x = 'c';
        return true;
    }
}", options: PreferDiscard);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/31583")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task MissingImports(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        List<int> [|x|] = null;
    }
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/31583")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UsedAssignment_ConditionalPreprocessorDirective(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"#define DEBUG
 
class C
{
    int M()
    {
        int [|x|] = 0;
#if DEBUG
        x = 1;
#endif
        return x;
    }
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/32855")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task RefLocalInitialization(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class Test
{
  int[] data = { 0 };
 
  void Method()
  {
    ref int [|target|] = ref data[0];
    target = 1;
  }
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/32855")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task RefLocalAssignment(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class Test
{
  int[] data = { 0 };
 
  int Method()
  {
    ref int target = ref data[0];
    [|target|] = 1;
    return data[0];
  }
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/32903")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task DelegateCreationWrappedInATuple_UsedInReturnedLambda(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
public class C
{
    private (int, int) createTuple() => (1, 1);
 
    public (Func<int>, bool) M()
    {
        var ([|value1, value2|]) = createTuple();
 
        int LocalFunction() => value1 + value2;
 
        return (LocalFunction, true);
    }
}", optionName);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32923")]
        public async Task UnusedLocal_ForEach()
        {
            await TestDiagnosticsAsync(
@"using System;
 
public struct S
{
    public Enumerator GetEnumerator() => throw new NotImplementedException();
 
    public struct Enumerator
    {
        public Enumerator(S sequence) => throw new NotImplementedException();
        public int Current => throw new NotImplementedException();
        public bool MoveNext() => throw new NotImplementedException();
    }
}
 
class C
{
    void M(S s)
    {
        foreach (var [|x|] in s)
        {
        }
    }
}", new TestParameters(options: PreferDiscard, retainNonFixableDiagnostics: true),
    Diagnostic("IDE0059"));
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/60030")]
        public async Task UnusedLocal_ForEach_TopLevelStatement()
        {
            await TestMissingInRegularAndScriptAsync(
@"var items = new[] { new { x = 1 } };
 
foreach (var [|item|] in items)
{
}", PreferDiscard, new CSharpParseOptions(LanguageVersion.CSharp9));
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/32923")]
        [InlineData("_", nameof(PreferDiscard))]
        [InlineData("_", nameof(PreferUnusedLocal))]
        [InlineData("_1", nameof(PreferDiscard))]
        [InlineData("_1", nameof(PreferUnusedLocal))]
        public async Task UnusedLocal_SpecialName_01(string variableName, string optionName)
        {
            await TestDiagnosticMissingAsync(
$@"using System;
 
public struct S
{{
    public Enumerator GetEnumerator() => throw new NotImplementedException();
 
    public struct Enumerator
    {{
        public Enumerator(S sequence) => throw new NotImplementedException();
        public int Current => throw new NotImplementedException();
        public bool MoveNext() => throw new NotImplementedException();
    }}
}}
 
class C
{{
    void M(S s)
    {{
        foreach (var [|{variableName}|] in s)
        {{
        }}
    }}
}}", new TestParameters(options: GetOptions(optionName), retainNonFixableDiagnostics: true));
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/32923")]
        [InlineData("_", nameof(PreferDiscard))]
        [InlineData("_", nameof(PreferUnusedLocal))]
        [InlineData("_3", nameof(PreferDiscard))]
        [InlineData("_3", nameof(PreferUnusedLocal))]
        public async Task UnusedLocal_SpecialName_02(string variableName, string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
$@"using System;
 
public class C
{{
    public void M(int p)
    {{
        var [|{variableName}|] = p;
    }}
}}", optionName);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32959")]
        public async Task UsedVariable_BailOutOnSemanticError()
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        int [|x|] = 1;
        
        // CS1662: Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type.
        Invoke<string>(() => x);
 
        T Invoke<T>(Func<T> a) { return a(); }
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32959")]
        public async Task UnusedVariable_BailOutOnSemanticError()
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        int [|x|] = 1;
        
        // CS1662: Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type.
        Invoke<string>(() => 0);
 
        T Invoke<T>(Func<T> a) { return a(); }
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32946")]
        public async Task DelegateEscape_01()
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    Action[] M()
    {
        var [|j|] = 0;
        return new Action[1] { () => Console.WriteLine(j) };
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32946")]
        public async Task DelegateEscape_02()
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    Action[] M(Action[] actions)
    {
        var [|j|] = 0;
        actions[0] = () => Console.WriteLine(j);
        return actions;
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32946")]
        public async Task DelegateEscape_03()
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    Action[,] M(Action[,] actions)
    {
        var [|j|] = 0;
        actions[0, 0] = () => Console.WriteLine(j);
        return actions;
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32946")]
        public async Task DelegateEscape_04()
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
using System.Collections.Generic;
 
class C
{
    List<Action> M()
    {
        var [|j|] = 0;
        return new List<Action> { () => Console.WriteLine(j) };
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32946")]
        public async Task DelegateEscape_05()
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
using System.Collections.Generic;
 
class C
{
    List<Action> M()
    {
        var [|j|] = 0;
        var list = new List<Action>();
        list.Add(() => Console.WriteLine(j));
        return list;
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32924")]
        public async Task DelegateEscape_06()
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M()
    {
        int [|j|] = 0;
        Console.CancelKeyPress += (s, e) => e.Cancel = j != 0;
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32924")]
        public async Task DelegateEscape_07()
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M()
    {
        int [|j|] = 0;
        Console.CancelKeyPress += LocalFunctionHandler;
        return;
 
        void LocalFunctionHandler(object s, ConsoleCancelEventArgs e) => e.Cancel = j != 0;
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32856")]
        public async Task RedundantAssignment_IfStatementParent()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M(int j)
    {
        if (M2())
            [|j|] = 0;
    }
 
    bool M2() => true;
}",
@"class C
{
    void M(int j)
    {
        if (M2())
            _ = 0;
    }
 
    bool M2() => true;
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32856")]
        public async Task RedundantAssignment_LoopStatementParent()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M(int j, int[] array)
    {
        for (int i = 0; i < array.Length; i++)
            [|j|] = i;
    }
}",
@"class C
{
    void M(int j, int[] array)
    {
        for (int i = 0; i < array.Length; i++)
            _ = i;
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40336")]
        public async Task RedundantAssignment_ForStatementVariableDeclarationConstant()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        for (int [|i|] = 0; ; ) 
        {
        }
    }
}",
@"class C
{
    void M()
    {
        for (; ; ) 
        {
        }
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40336")]
        public async Task RedundantAssignment_ForStatementVariableDeclarationMethod()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int GetValue() => 0;
 
    void M()
    {
        for (int [|i|] = GetValue(); ; )
        {
        }
    }
}",
@"class C
{
    int GetValue() => 0;
 
    void M()
    {
        for (int _ = GetValue(); ; )
        {
        }
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40336")]
        public async Task RedundantAssignment_ForStatementVariableDeclarationStaticMethod()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    static int GetValue() => 0;
 
    void M()
    {
        for (int [|i|] = GetValue(); ; )
        {
        }
    }
}",
@"class C
{
    static int GetValue() => 0;
 
    void M()
    {
        for (int _ = GetValue(); ; )
        {
        }
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40336")]
        public async Task RedundantAssignment_ForStatementVariableDeclarationInsideUsedLambda()
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M()
    {
        Action a = () =>
        {
            for (int [|i|] = 0; ; )
            {
            }
        };
        a();
    }
}",
@"using System;
 
class C
{
    void M()
    {
        Action a = () =>
        {
            for (; ; )
            {
            }
        };
        a();
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40336")]
        public async Task RedundantAssignment_ForStatementVariableDeclarationInsideUnusedLambda()
        {
            //NOTE: Currently the diagnostic is only reported on the outer unused variable a. 
            await TestDiagnosticMissingAsync(
@"using System;
 
class C
{
    void M()
    {
        Action a = () =>
        {
            for (int [|i|] = 0; ; )
            {
            }
        };
    }
}");
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33299")]
        public async Task NullCoalesceAssignment_01()
        {
            await TestMissingInRegularAndScriptWithAllOptionsAsync(
@"class C
{
    public static void M(C x)
    {
        [|x|] = M2();
        x ??= new C();
    }
 
    private static C M2() => null;
}
", parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8));
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33299")]
        public async Task NullCoalesceAssignment_02()
        {
            await TestMissingInRegularAndScriptWithAllOptionsAsync(
@"class C
{
    public static C M(C x)
    {
        [|x|] ??= new C();
        return x;
    }
}
", parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8));
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33299")]
        public async Task NullCoalesceAssignment_03()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    public static void M(C x)
    {
        [|x|] ??= new C();
    }
}
",
@"class C
{
    public static void M(C x)
    {
        _ = x ?? new C();
    }
}
", optionName: nameof(PreferDiscard), parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8));
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33299")]
        public async Task NullCoalesceAssignment_04()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    public static C M(C x)
    {
        return [|x|] ??= new C();
    }
}
",
@"class C
{
    public static C M(C x)
    {
        return x ?? new C();
    }
}
", optionName: nameof(PreferDiscard), parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8));
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33299")]
        public async Task NullCoalesceAssignment_05()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    public static C M(C x)
        => [|x|] ??= new C();
}
",
@"class C
{
    public static C M(C x)
        => x ?? new C();
}
", optionName: nameof(PreferDiscard), parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8));
        }
 
        [Fact, WorkItem(32856, "https://github.com/dotnet/roslyn/issues/33312")]
        public async Task RedundantAssignment_WithLeadingAndTrailingComment()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        // This is a comment before the variable assignment.
        // It has two lines.
        [|int foo = 0;|] // Trailing comment.
        if (true)
        {
            foo = 1;
        }
        System.Console.WriteLine(foo);
    }
}",
@"class C
{
    void M()
    {
        // This is a comment before the variable assignment.
        // It has two lines.
        int foo;
        if (true)
        {
            foo = 1;
        }
        System.Console.WriteLine(foo);
    }
}", options: PreferUnusedLocal);
        }
 
        [Fact, WorkItem(32856, "https://github.com/dotnet/roslyn/issues/33312")]
        public async Task MultipleRedundantAssignment_WithLeadingAndTrailingComment()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        // This is a comment before the variable assignment.
        // It has two lines.
        {|FixAllInDocument:int unused = 0, foo = 0, bar = 0;|} // Trailing comment.
        if (true)
        {
            foo = 1;
            bar = 1;
        }
        System.Console.WriteLine(foo);
        System.Console.WriteLine(bar);
    }
}",
@"class C
{
    void M()
    {
        // This is a comment before the variable assignment.
        // It has two lines.
        int foo;
        int bar;
        if (true)
        {
            foo = 1;
            bar = 1;
        }
        System.Console.WriteLine(foo);
        System.Console.WriteLine(bar);
    }
}", options: PreferUnusedLocal);
        }
 
        [Fact, WorkItem(32856, "https://github.com/dotnet/roslyn/issues/33312")]
        public async Task MultipleRedundantAssignment_WithInnerComment()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M()
    {
        {|FixAllInDocument:int unused = 0, /*Comment*/foo = 0, /*Another comment*/ bar = 0;|}
        if (true)
        {
            foo = 1;
        }
        System.Console.WriteLine(foo);
    }
}",
@"class C
{
    void M()
    {
        int foo;
        if (true)
        {
            foo = 1;
        }
        System.Console.WriteLine(foo);
    }
}", options: PreferUnusedLocal);
        }
 
        [Fact, WorkItem(32856, "https://github.com/dotnet/roslyn/issues/33312")]
        public async Task DeclarationPatternInSwitchCase_WithTrivia_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    void M(object p)
    {
        switch (p)
        {
            case /*Inline trivia*/ int [|x|]:
                // Other trivia
                x = 1;
                break;
        };
    }
}",
@"class C
{
    void M(object p)
    {
        switch (p)
        {
            case /*Inline trivia*/ int _:
                // Other trivia
                int x = 1;
                break;
        };
    }
}", options: PreferDiscard, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8));
        }
 
        [Theory, WorkItem(32856, "https://github.com/dotnet/roslyn/issues/33312")]
        [CombinatorialData]
        public async Task DeclarationPatternInSwitchCase_WithTrivia_PreferUnusedLocal(
            [CombinatorialValues(LanguageVersion.CSharp8, LanguageVersion.CSharp9)] LanguageVersion languageVersion)
        {
            await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(object p)
    {
        switch (p)
        {
            case /*Inline trivia*/ int [|x|]:
                // Other trivia
                x = 1;
                break;
        };
    }
}", PreferUnusedLocal, parseOptions: new CSharpParseOptions(languageVersion));
        }
 
        [Theory, WorkItem(32856, "https://github.com/dotnet/roslyn/issues/33312")]
        [CombinatorialData]
        public async Task DeclarationPatternInSwitchCase_WithTrivia_TypePattern(
            [CombinatorialValues(CodeFixTestBehaviors.None, CodeFixTestBehaviors.FixOne)] CodeFixTestBehaviors testBehaviors)
        {
            var source =
@"class C
{
    void M(object p)
    {
        switch (p)
        {
            case /*Inline trivia*/ int {|IDE0059:x|}:
                // Other trivia
                {|IDE0059:x|} = 1;
                break;
        };
    }
}";
            var (fixedSource, iterations) = testBehaviors switch
            {
                CodeFixTestBehaviors.None =>
(@"class C
{
    void M(object p)
    {
        switch (p)
        {
            case /*Inline trivia*/ int:
                // Other trivia
                break;
        };
    }
}", iterations: 2),
                CodeFixTestBehaviors.FixOne =>
(@"class C
{
    void M(object p)
    {
        switch (p)
        {
            case /*Inline trivia*/ int:
                // Other trivia
                int {|IDE0059:x|} = 1;
                break;
        };
    }
}", iterations: 1),
                _ => throw ExceptionUtilities.Unreachable(),
            };
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedState = { Sources = { fixedSource }, MarkupHandling = MarkupMode.Allow },
                LanguageVersion = LanguageVersion.CSharp9,
                CodeFixTestBehaviors = testBehaviors,
                NumberOfIncrementalIterations = iterations,
                NumberOfFixAllIterations = iterations,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable },
                },
            }.RunAsync();
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/33949")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UsedInArgumentAfterAnArgumentWithControlFlow(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class A
{
    public static void M(int? x)
    {
        A [|a|] = new A();
        a = M2(x ?? 1, a);
    }
 
    private static A M2(int? x, A a)
    {
        return a;
    }
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/33949")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task ConpoundAssignmentWithControlFlowInValue(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"class A
{
    public static void M(int? x)
    {
        int [|a|] = 1;
        a += M2(x ?? 1);
    }
 
    private static int M2(int? x) => 0;
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/33843")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UsedValueWithUsingStatementAndLocalFunction(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    private class Disposable : IDisposable { public void Dispose() { } }
    public int M()
    {
        var result = 0;
        void append() => [|result|] += 1; // IDE0059 for 'result'
        using (var a = new Disposable())
            append();
        return result;
    }
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/33843")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UsedValueWithUsingStatementAndLambda(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    private class Disposable : IDisposable { public void Dispose() { } }
    public int M()
    {
        var result = 0;
        Action append = () => [|result|] += 1; // IDE0059 for 'result'
        using (var a = new Disposable())
            append();
        return result;
    }
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/33843")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UsedValueWithUsingStatementAndLambda_02(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    private class Disposable : IDisposable { public void Dispose() { } }
    public int M()
    {
        var result = 0;
        Action appendLambda = () => [|result|] += 1;
        void appendLocalFunction() => appendLambda();
        Action appendDelegate = appendLocalFunction;
        using (var a = new Disposable())
            appendDelegate();
        return result;
    }
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/33843")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UsedValueWithUsingStatementAndLambda_03(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    private class Disposable : IDisposable { public void Dispose() { } }
    public int M()
    {
        var result = 0;
        void appendLocalFunction() => [|result|] += 1;
        Action appendLambda = () => appendLocalFunction();
        Action appendDelegate = appendLambda;
        using (var a = new Disposable())
            appendDelegate();
        return result;
    }
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/33937")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInCatchUsedInFinally_ThrowInCatch(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
public static class Program
{
    public static void Test()
    {
        var exceptionThrown = false;
        try
        {
            throw new Exception();
        }
        catch
        {
            // The `exceptionThrown` token is incorrectly greyed out in the IDE
            // IDE0059 Value assigned to 'exceptionThrown' is never used
            [|exceptionThrown|] = true;
            throw;
        }
        finally
        {
            // Breakpoint on this line is hit and 'true' is printed
            Console.WriteLine(exceptionThrown);
        }
    }
}", optionName);
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/33937")]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task AssignedInCatchUsedInFinally_NoThrowInCatch(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
public static class Program
{
    public static void Test()
    {
        var exceptionThrown = false;
        try
        {
            throw new Exception();
        }
        catch
        {
            [|exceptionThrown|] = true;
        }
        finally
        {
            Console.WriteLine(exceptionThrown);
        }
    }
}", optionName);
        }
 
        [Fact]
        public async Task DoesNotUseLocalFunctionName_PreferUnused()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int [|x|] = M2();
        x = 2;
        return x;
 
        void unused() { }
    }
 
    int M2() => 0;
}",
@"class C
{
    int M()
    {
        int unused1 = M2();
        int x = 2;
        return x;
 
        void unused() { }
    }
 
    int M2() => 0;
}", options: PreferUnusedLocal);
        }
 
        [Fact]
        public async Task CanUseLocalFunctionParameterName_PreferUnused()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    int M()
    {
        int [|x|] = M2();
        x = 2;
        return x;
 
        void MLocal(int unused) { }
    }
 
    int M2() => 0;
}",
@"class C
{
    int M()
    {
        int unused = M2();
        int x = 2;
        return x;
 
        void MLocal(int unused) { }
    }
 
    int M2() => 0;
}", options: PreferUnusedLocal);
        }
 
        [Fact]
        public async Task DoesNotUseLambdaFunctionParameterNameWithCSharpLessThan8_PreferUnused()
        {
            await TestInRegularAndScriptAsync(
@"
using System;
class C
{
    int M()
    {
        int [|x|] = M2();
        x = 2;
        Action<int> myLambda = unused => { };
 
        return x;
    }
 
    int M2() => 0;
}",
@"
using System;
class C
{
    int M()
    {
        int unused1 = M2();
        int x = 2;
        Action<int> myLambda = unused => { };
 
        return x;
    }
 
    int M2() => 0;
}", options: PreferUnusedLocal, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp7_3));
        }
 
        [Fact]
        public async Task CanUseLambdaFunctionParameterNameWithCSharp8_PreferUnused()
        {
            await TestInRegularAndScriptAsync(
@"
using System;
class C
{
    int M()
    {
        int [|x|] = M2();
        x = 2;
        Action<int> myLambda = unused => { };
 
        return x;
    }
 
    int M2() => 0;
}",
@"
using System;
class C
{
    int M()
    {
        int unused = M2();
        int x = 2;
        Action<int> myLambda = unused => { };
 
        return x;
    }
 
    int M2() => 0;
}", options: PreferUnusedLocal, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8));
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33464")]
        public async Task UsingDeclaration()
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C : IDisposable
{
    public void Dispose()
    {
    }
 
    void M()
    {
        using var [|x|] = new C();
    }
}", options: PreferDiscard,
    parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9));
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33464")]
        public async Task UsingDeclarationWithInitializer()
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C : IDisposable
{
    public int P { get; set; }
    public void Dispose()
    {
    }
 
    void M()
    {
        using var [|x|] = new C() { P = 1 };
    }
}", options: PreferDiscard,
    parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9));
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37709")]
        public async Task RefParameter_WrittenBeforeThrow()
        {
            await TestDiagnosticMissingAsync(
@"using System;
 
class C
{
    public void DoSomething(ref bool p)
    {
        if (p)
        {
            [|p|] = false;
            throw new ArgumentException(string.Empty);
        }
    }
}");
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37709")]
        public async Task OutParameter_WrittenBeforeThrow()
        {
            await TestDiagnosticMissingAsync(
@"using System;
 
class C
{
    public void DoSomething(out bool p, bool x)
    {
        if (x)
        {
            [|p|] = false;
            throw new ArgumentException(string.Empty);
        }
        else
        {
            p = true;
        }
    }
}");
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37871")]
        public async Task RefParameter_RefAssignmentFollowedByAssignment()
        {
            await TestDiagnosticMissingAsync(
@"using System;
 
class C
{
    delegate ref int UnsafeAdd(ref int source, int elementOffset);
    static UnsafeAdd MyUnsafeAdd;
    
    static void T1(ref int param)
    {
        [|param|] = ref MyUnsafeAdd(ref param, 1);
        param = default;
    }
}");
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37871")]
        public async Task RefParameter_RefConditionalAssignment()
        {
            await TestDiagnosticMissingAsync(
@"using System;
 
class C
{
    delegate ref int UnsafeAdd(ref int source, int elementOffset);
    static UnsafeAdd MyUnsafeAdd;
 
    static void T1(ref int param, bool flag)
    {
        [|param|] = flag ? ref MyUnsafeAdd(ref param, 1) : ref MyUnsafeAdd(ref param, 2);
        param = default;
    }
}");
        }
 
        [Fact]
        public async Task LocalFunction_OutParameter_UsedInCaller()
        {
            await TestDiagnosticMissingAsync(
@"
public class C
{
    public void M()
    {
        if (GetVal(out var [|value|]))
        {
            var x = value;
        }
 
        bool GetVal(out string val)
        {
            val = string.Empty;
            return true;
        }
    }
}");
        }
 
        [Fact]
        public async Task TupleMember_UsedAfterContinueBranch()
        {
            await TestDiagnosticMissingAsync(
@"
using System;
using System.Collections.Generic;
 
public class Test
{
    void M(List<(int, int)> list)
    {
        foreach (var (x, [|y|]) in list)
        {
            if (x != 0)
            {
                continue;
            }
 
            Console.Write(y);
        }
    }
}");
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38640")]
        public async Task DeclarationPatternInSwitchExpressionArm_UsedLocal()
        {
            await TestDiagnosticMissingAsync(
@"class C
{
    string M(object obj)
    {
        return obj switch
        {
            int [|p2|] => p2.ToString(),
            _ => ""NoMatch""
        };
    }
}", new TestParameters(options: PreferDiscard, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8)));
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/38640")]
        [CombinatorialData]
        public async Task DeclarationPatternInSwitchExpressionArm_UnusedLocal_PreferUnusedLocal(
            [CombinatorialValues(LanguageVersion.CSharp8, LanguageVersion.CSharp9)] LanguageVersion languageVersion)
        {
            await TestDiagnosticMissingAsync(
@"class C
{
    string M(object obj)
    {
        return obj switch
        {
            int [|p2|] => ""Int"",
            _ => ""NoMatch""
        };
    }
}", new TestParameters(options: PreferUnusedLocal, parseOptions: new CSharpParseOptions(languageVersion)));
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40499")]
        public async Task LocalUsedWithPropertySubPattern()
        {
            await TestDiagnosticMissingAsync(
@"class C
{
    public object P { get; }
    void M()
    {
        C [|c|] = new C();
        var x = c is { P : int i };
    }
}", new TestParameters(options: PreferDiscard, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8)));
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40499")]
        public async Task UnusedLocalDefinedInPropertySubPattern_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    public object P { get; }
    void M(C c)
    {
        var x = c is { P : int [|i|] };
    }
}",
@"class C
{
    public object P { get; }
    void M(C c)
    {
        var x = c is { P : int _ };
    }
}", options: PreferDiscard, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8));
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40499")]
        public async Task UnusedLocalDefinedInPropertySubPattern_TypePattern()
        {
            var source =
@"class C
{
    public object P { get; }
    bool M(C c)
    {
        var x = c is { P : int {|IDE0059:i|} };
        return x;
    }
}";
            var fixedSource =
@"class C
{
    public object P { get; }
    bool M(C c)
    {
        var x = c is { P : int };
        return x;
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                LanguageVersion = LanguageVersion.CSharp9,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable },
                },
            }.RunAsync();
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/40499")]
        [CombinatorialData]
        public async Task UnusedVarLocalDefinedInPropertySubPattern_PreferDiscard(
            [CombinatorialValues(LanguageVersion.CSharp8, LanguageVersion.CSharp9)] LanguageVersion languageVersion)
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    public object P { get; }
    void M(C c)
    {
        var x = c is { P : var [|i|] };
    }
}",
@"class C
{
    public object P { get; }
    void M(C c)
    {
        var x = c is { P : _ };
    }
}", options: PreferDiscard, parseOptions: new CSharpParseOptions(languageVersion));
        }
 
        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/40499")]
        [CombinatorialData]
        public async Task UnusedLocalDefinedInPropertySubPattern_PreferUnusedLocal(
            [CombinatorialValues(LanguageVersion.CSharp8, LanguageVersion.CSharp9)] LanguageVersion languageVersion)
        {
            await TestDiagnosticMissingAsync(
@"class C
{
    public object P { get; }
    void M(C c)
    {
        var x = c is { P : int [|i|] };
    }
}", new TestParameters(options: PreferUnusedLocal, parseOptions: new CSharpParseOptions(languageVersion)));
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38640")]
        public async Task DeclarationPatternInSwitchExpressionArm_UnusedLocal_PreferDiscard()
        {
            await TestInRegularAndScriptAsync(
@"class C
{
    string M(object obj)
    {
        return obj switch
        {
            int [|p2|] => ""Int"",
            _ => ""NoMatch""
        };
    }
}",
@"class C
{
    string M(object obj)
    {
        return obj switch
        {
            int _ => ""Int"",
            _ => ""NoMatch""
        };
    }
}", options: PreferDiscard, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8));
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38640")]
        public async Task DeclarationPatternInSwitchExpressionArm_UnusedLocal_TypePattern()
        {
            var source =
@"class C
{
    string M(object obj)
    {
        return obj switch
        {
            int {|IDE0059:p2|} => ""Int"",
            _ => ""NoMatch""
        };
    }
}";
            var fixedSource =
@"class C
{
    string M(object obj)
    {
        return obj switch
        {
            int => ""Int"",
            _ => ""NoMatch""
        };
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                LanguageVersion = LanguageVersion.CSharp9,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable },
                },
            }.RunAsync();
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/39344")]
        public async Task AssignmentInTry_UsedInFinally_NoDiagnostic()
        {
            await TestDiagnosticMissingAsync(
@"using System;
 
class C
{
    void M(int i)
    {
        bool b = false;
        try
        {
            if (i == 0)
            {
                [|b|] = true;
            }
        }
        finally
        {
            if (!b)
            {
                Console.WriteLine(i);
            }
        }
    }
}");
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/39755")]
        public async Task AssignmentInTry_UsedInFinally_NoDiagnostic_02()
        {
            await TestDiagnosticMissingAsync(
@"using System;
 
class C
{
    void M()
    {
        IntPtr a = (IntPtr)1;
        try
        {
            var b = a;
 
            if (Some(a))
                [|a|] = IntPtr.Zero;
        }
        finally
        {
            if (a != IntPtr.Zero)
            {
 
            }
        }
    }
 
    bool Some(IntPtr a) => true;
}");
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/39755")]
        public async Task AssignmentInTry_NotUsedInFinally_Diagnostic()
        {
            await TestInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M(int i)
    {
        bool b = false;
        try 
        { 
            if (i == 0)
            {
                [|b|] = true;
            }
        }
        finally 
        {
        }
    }
}",
@"using System;
 
class C
{
    void M(int i)
    {
        bool b = false;
        try 
        { 
            if (i == 0)
            {
            }
        }
        finally 
        {
        }
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38507")]
        public async Task TestCodeFixTitleForBlockBodyRedundantCompoundAssignmentReturn()
        {
            var source = @"
class C
{
    C M(C x)
    {
        return [|x ??= M2()|];
    }
 
    C M2() => new C();
}
";
 
            await TestExactActionSetOfferedAsync(source, new[] { CodeFixesResources.Remove_redundant_assignment });
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38507")]
        public async Task TestCodeFixTitleForExpressionBodyRedundantCompoundAssignmentReturn()
        {
            var source = @"
class C
{
    C M(C x) => [|x ??= M2()|];
 
    C M2() => new C();
}
";
            await TestExactActionSetOfferedAsync(source, new[] { CodeFixesResources.Remove_redundant_assignment });
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38507")]
        public async Task TestCodeFixTitleForPatternMatching()
        {
            var source = @"
class C
{
    void M()
    {
        var c = M2();
        if [|(c is object obj)|]
        {
        }
    }
 
    C M2() => new C();
}
";
 
            await TestExactActionSetOfferedAsync(source, new[] { CodeFixesResources.Remove_redundant_assignment });
        }
 
        [Fact, WorkItem(38507, "https://github.com/dotnet/roslyn/issues/46251")]
        public async Task TestCodeFixForAllInDocumentForNestedDiagnostic()
        {
            var source = @"
using System;
namespace ConsoleApp
{
	public static class ConsoleApp
    {
		public static void Main(string[] args)
        {
            {|FixAllInDocument:Foo(() => { Bar(); return true; })|};
        }
 
        public static bool Foo(Func<bool> func)
        {
            return func. Invoke();
        }
 
        public static bool Bar()
        {
            return true;
        }
	}
}";
            var expected = @"
using System;
namespace ConsoleApp
{
	public static class ConsoleApp
    {
		public static void Main(string[] args)
        {
            _ = Foo(() => { _ = Bar(); return true; });
        }
 
        public static bool Foo(Func<bool> func)
        {
            return func. Invoke();
        }
 
        public static bool Bar()
        {
            return true;
        }
	}
}";
            await TestInRegularAndScriptAsync(source, expected, options: PreferDiscard).ConfigureAwait(false);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/45768")]
        public async Task UnusedVarPattern_PartOfCase()
        {
            await TestInRegularAndScriptAsync(
@"static class Program
{
    public static void Main()
    {
        switch (string.Empty.Length)
        {
            case var [|i|] when string.Empty.Length switch { var y => y > 0 }:
            {
                break;
            }
        }
    }
}",
@"static class Program
{
    public static void Main()
    {
        switch (string.Empty.Length)
        {
            case var _ when string.Empty.Length switch { var y => y > 0 }:
            {
                break;
            }
        }
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/45768")]
        public async Task UnusedVarPattern_PartOfIs()
        {
            await TestInRegularAndScriptAsync(
@"static class Program
{
    public static void Main()
    {
        if (string.Empty.Length is var [|x|])
        {
        }
    }
}",
@"static class Program
{
    public static void Main()
    {
        if (string.Empty.Length is var _)
        {
        }
    }
}", options: PreferDiscard);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/45768")]
        public async Task UnusedVarPattern_TestTrivia()
        {
            await TestInRegularAndScriptAsync(
@"static class Program
{
    public static void Main()
    {
        if (string.Empty.Length is var [|/*1*/x/*2*/|])
        {
        }
    }
}",
@"static class Program
{
    public static void Main()
    {
        if (string.Empty.Length is var /*1*/_/*2*/)
        {
        }
    }
}", options: PreferDiscard);
        }
 
        [WorkItem("https://github.com/dotnet/roslyn/issues/57650")]
        [Theory, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedValues)]
        [InlineData(nameof(PreferDiscard))]
        [InlineData(nameof(PreferUnusedLocal))]
        public async Task UseInLambda_WithInvocationOutsideLocalScope(string optionName)
        {
            await TestMissingInRegularAndScriptAsync(
@"using System;
 
class C
{
    void M()
    {
        Action act = null;
        {
            var[| capture |] = new object();
            act = () => capture.ToString();
        }
        act();
    }
}", optionName);
        }
 
        [Fact]
        [WorkItem("https://github.com/dotnet/roslyn/issues/64291")]
        public async Task TestImplicitObjectCreationInInitialization()
        {
            var source =
@"class C
{
    void M()
    {
        C {|IDE0059:c|} = new();
    }
}";
            var fixedSource =
@"class C
{
    void M()
    {
        _ = new C();
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable },
                },
                LanguageVersion = LanguageVersion.CSharp9,
            }.RunAsync();
        }
 
        [Fact]
        [WorkItem("https://github.com/dotnet/roslyn/issues/64291")]
        public async Task TestImplicitObjectCreationInAssignement()
        {
            var source =
@"class C
{
    void M(C c)
    {
        System.Console.WriteLine(c);
        {|IDE0059:c|} = new();
    }
}";
            var fixedSource =
@"class C
{
    void M(C c)
    {
        System.Console.WriteLine(c);
        _ = new C();
    }
}";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                Options =
                {
                    { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable },
                },
                LanguageVersion = LanguageVersion.CSharp9,
            }.RunAsync();
        }
    }
}