File: src\Analyzers\CSharp\Tests\UseConditionalExpression\UseConditionalExpressionForAssignmentTests.cs
Web Access
Project: src\src\Features\CSharpTest\Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Features.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.UseConditionalExpression;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseConditionalExpression;
 
using VerifyCS = CSharpCodeFixVerifier<
    CSharpUseConditionalExpressionForAssignmentDiagnosticAnalyzer,
    CSharpUseConditionalExpressionForAssignmentCodeFixProvider>;
 
[Trait(Traits.Feature, Traits.Features.CodeActionsUseConditionalExpression)]
public sealed partial class UseConditionalExpressionForAssignmentTests
{
    private static async Task TestMissingAsync(
        string testCode,
        LanguageVersion languageVersion = LanguageVersion.CSharp8,
        OptionsCollection? options = null)
    {
        var test = new VerifyCS.Test
        {
            TestCode = testCode,
            LanguageVersion = languageVersion,
            Options = { options },
        };
 
        await test.RunAsync();
    }
 
    private static async Task TestInRegularAndScript1Async(
        string testCode,
        string fixedCode,
        LanguageVersion languageVersion = LanguageVersion.CSharp8,
        OptionsCollection? options = null,
        string? equivalenceKey = null)
    {
        await new VerifyCS.Test
        {
            TestCode = testCode,
            FixedCode = fixedCode,
            LanguageVersion = languageVersion,
            CodeActionEquivalenceKey = equivalenceKey,
            Options = { options },
        }.RunAsync();
    }
 
    private static readonly OptionsCollection PreferImplicitTypeAlways = new(LanguageNames.CSharp)
    {
        { CSharpCodeStyleOptions.VarWhenTypeIsApparent, CodeStyleOption2.TrueWithSilentEnforcement },
        { CSharpCodeStyleOptions.VarElsewhere, CodeStyleOption2.TrueWithSilentEnforcement },
        { CSharpCodeStyleOptions.VarForBuiltInTypes, CodeStyleOption2.TrueWithSilentEnforcement },
    };
 
    [Fact]
    public async Task TestOnSimpleAssignment()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(int i)
                {
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        i = 1;
                    }
                }
            }
            """,
            """
            class C
            {
                void M(int i)
                {
                    i = true ? 0 : 1;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestOnSimpleAssignment_Throw1()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(int i)
                {
                    [|if|] (true)
                    {
                        throw new System.Exception();
                    }
                    else
                    {
                        i = 1;
                    }
                }
            }
            """,
            """
            class C
            {
                void M(int i)
                {
                    i = true ? throw new System.Exception() : 1;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestOnSimpleAssignment_Throw2()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(int i)
                {
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        throw new System.Exception();
                    }
                }
            }
            """,
            """
            class C
            {
                void M(int i)
                {
                    i = true ? 0 : throw new System.Exception();
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestNotWithTwoThrows()
    {
        await TestMissingAsync(
            """
            class C
            {
                void M(int i)
                {
                    if (true)
                    {
                        throw new System.Exception();
                    }
                    else
                    {
                        throw new System.Exception();
                    }
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestNotOnSimpleAssignment_Throw1_CSharp6()
    {
        await TestMissingAsync(
            """
            class C
            {
                void M(int i)
                {
                    if (true)
                    {
                        throw new System.Exception();
                    }
                    else
                    {
                        i = 1;
                    }
                }
            }
            """, LanguageVersion.CSharp6);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestWithSimpleThrow()
    {
        await TestMissingAsync(
            """
            class C
            {
                void M(int i)
                {
                    if (true)
                    {
                        {|CS0156:throw|};
                    }
                    else
                    {
                        i = 1;
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestOnSimpleAssignmentNoBlocks()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(int i)
                {
                    [|if|] (true)
                        i = 0;
                    else
                        i = 1;
                }
            }
            """,
            """
            class C
            {
                void M(int i)
                {
                    i = true ? 0 : 1;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestOnSimpleAssignmentNoBlocks_NotInBlock()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(int i)
                {
                    if (true)
                        [|if|] (true)
                            i = 0;
                        else
                            i = 1;
                }
            }
            """,
            """
            class C
            {
                void M(int i)
                {
                    if (true)
                        i = true ? 0 : 1;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestNotOnSimpleAssignmentToDifferentTargets()
    {
        await TestMissingAsync(
            """
            class C
            {
                void M(int i, int j)
                {
                    if (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        j = 1;
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestOnAssignmentToUndefinedField()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    [|if|] (true)
                    {
                        this.{|CS1061:i|} = 0;
                    }
                    else
                    {
                        this.{|CS1061:i|} = 1;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    this.{|CS1061:i|} = true ? 0 : 1;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestOnAssignmentToUndefinedField_Throw()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    [|if|] (true)
                    {
                        this.{|CS1061:i|} = 0;
                    }
                    else
                    {
                        throw new System.Exception();
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    this.{|CS1061:i|} = true ? 0 : throw new System.Exception();
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestOnNonUniformTargetSyntax()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                private int i;
 
                void M()
                {
                    [|if|] (true)
                    {
                        this.i = 0;
                    }
                    else
                    {
                        this . i = 1;
                    }
                }
            }
            """,
            """
            class C
            {
                private int i;
 
                void M()
                {
                    this.i = true ? 0 : 1;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestOnAssignmentToDefinedField()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                int i;
 
                void M()
                {
                    [|if|] (true)
                    {
                        this.i = 0;
                    }
                    else
                    {
                        this.i = 1;
                    }
                }
            }
            """,
            """
            class C
            {
                int i;
 
                void M()
                {
                    this.i = true ? 0 : 1;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestOnAssignmentToAboveLocalNoInitializer()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    int i;
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        i = 1;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    int i = true ? 0 : 1;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestOnAssignmentToAboveLocalNoInitializer_Throw1()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    int i;
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        throw new System.Exception();
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    int i = true ? 0 : throw new System.Exception();
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestOnAssignmentToAboveLocalNoInitializer_Throw2()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    int i;
                    [|if|] (true)
                    {
                        throw new System.Exception();
                    }
                    else
                    {
                        i = 1;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    int i = true ? throw new System.Exception() : 1;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestOnAssignmentToAboveLocalLiteralInitializer()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    int i = 0;
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        i = 1;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    int i = true ? 0 : 1;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestOnAssignmentToAboveLocalDefaultLiteralInitializer()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    int i = default;
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        i = 1;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    int i = true ? 0 : 1;
                }
            }
            """, LanguageVersion.Latest);
    }
 
    [Fact]
    public async Task TestOnAssignmentToAboveLocalDefaultExpressionInitializer()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    int i = default(int);
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        i = 1;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    int i = true ? 0 : 1;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestDoNotMergeAssignmentToAboveLocalWithComplexInitializer()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    int i = Foo();
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        i = 1;
                    }
                }
 
                int Foo() => 0;
            }
            """,
            """
            class C
            {
                void M()
                {
                    int i = Foo();
                    i = true ? 0 : 1;
                }
 
                int Foo() => 0;
            }
            """);
    }
 
    [Fact]
    public async Task TestDoNotMergeAssignmentToAboveLocalIfIntermediaryStatement()
    {
        await TestInRegularAndScript1Async(
            """
            using System;
 
            class C
            {
                void M()
                {
                    int i = 0;
                    Console.WriteLine();
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        i = 1;
                    }
                }
            }
            """,
            """
            using System;
 
            class C
            {
                void M()
                {
                    int i = 0;
                    Console.WriteLine();
                    i = true ? 0 : 1;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestDoNotMergeAssignmentToAboveIfLocalUsedInIfCondition()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    int i = 0;
                    [|if|] (Bar(i))
                    {
                        i = 0;
                    }
                    else
                    {
                        i = 1;
                    }
                }
 
                bool Bar(int i) => true;
            }
            """,
            """
            class C
            {
                void M()
                {
                    int i = 0;
                    i = Bar(i) ? 0 : 1;
                }
 
                bool Bar(int i) => true;
            }
            """);
    }
 
    [Fact]
    public async Task TestDoNotMergeAssignmentToAboveIfMultiDecl()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    int i = 0, j = 0;
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        i = 1;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    int i = 0, j = 0;
                    i = true ? 0 : 1;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestUseImplicitTypeForIntrinsicTypes()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    int i = 0;
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        i = 1;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    var i = true ? 0 : 1;
                }
            }
            """, options: new OptionsCollection(LanguageNames.CSharp) { { CSharpCodeStyleOptions.VarForBuiltInTypes, CodeStyleOption2.TrueWithSilentEnforcement } });
    }
 
    [Fact]
    public async Task TestUseImplicitTypeWhereApparent()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    int i = 0;
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        i = 1;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    int i = true ? 0 : 1;
                }
            }
            """, options: new OptionsCollection(LanguageNames.CSharp) { { CSharpCodeStyleOptions.VarWhenTypeIsApparent, CodeStyleOption2.TrueWithSilentEnforcement } });
    }
 
    [Fact]
    public async Task TestUseImplicitTypeWherePossible()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    int i = 0;
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        i = 1;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    int i = true ? 0 : 1;
                }
            }
            """, options: new OptionsCollection(LanguageNames.CSharp) { { CSharpCodeStyleOptions.VarElsewhere, CodeStyleOption2.TrueWithSilentEnforcement } });
    }
 
    [Fact]
    public async Task TestMissingWithoutElse()
    {
        await TestMissingAsync(
            """
            class C
            {
                void M(int i)
                {
                    if (true)
                    {
                        i = 0;
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMissingWithoutElseWithStatementAfterwards()
    {
        await TestMissingAsync(
            """
            class C
            {
                void M(int i)
                {
                    if (true)
                    {
                        i = 0;
                    }
 
                    i = 1;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestMissingWithoutElseWithThrowStatementAfterwards()
    {
        await TestMissingAsync(
            """
            class C
            {
                void M(int i)
                {
                    if (true)
                    {
                        i = 0;
                    }
 
                    throw new System.Exception();
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestConversionWithUseVarForAll_CastInsertedToKeepTypeSame()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    // cast will be necessary, otherwise 'var' would get the type 'string'.
                    object o;
                    [|if|] (true)
                    {
                        o = "a";
                    }
                    else
                    {
                        o = "b";
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    // cast will be necessary, otherwise 'var' would get the type 'string'.
                    var o = true ? "a" : (object)"b";
                }
            }
            """, options: PreferImplicitTypeAlways);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestConversionWithUseVarForAll_CastInsertedToKeepTypeSame_Throw1_CSharp8()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    object o;
                    [|if|] (true)
                    {
                        throw new System.Exception();
                    }
                    else
                    {
                        o = "b";
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    var o = true ? throw new System.Exception() : (object)"b";
                }
            }
            """, LanguageVersion.CSharp8, PreferImplicitTypeAlways);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestConversionWithUseVarForAll_CastInsertedToKeepTypeSame_Throw1_CSharp9()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    object o;
                    [|if|] (true)
                    {
                        throw new System.Exception();
                    }
                    else
                    {
                        o = "b";
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    var o = true ? throw new System.Exception() : (object)"b";
                }
            }
            """, LanguageVersion.CSharp9, options: PreferImplicitTypeAlways);
    }
 
    [Fact]
    public async Task TestConversionWithUseVarForAll_CastInsertedToKeepTypeSame_Throw2()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    object o;
                    [|if|] (true)
                    {
                        o = "a";
                    }
                    else
                    {
                        throw new System.Exception();
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    var o = true ? (object)"a" : throw new System.Exception();
                }
            }
            """, options: PreferImplicitTypeAlways);
    }
 
    [Fact]
    public async Task TestConversionWithUseVarForAll_CanUseVarBecauseConditionalTypeMatches()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    string s;
                    [|if|] (true)
                    {
                        s = "a";
                    }
                    else
                    {
                        s = null;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    var s = true ? "a" : null;
                }
            }
            """, options: PreferImplicitTypeAlways);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestConversionWithUseVarForAll_CanUseVarBecauseConditionalTypeMatches_Throw1()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    string s;
                    [|if|] (true)
                    {
                        throw new System.Exception();
                    }
                    else
                    {
                        s = null;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    var s = true ? throw new System.Exception() : (string)null;
                }
            }
            """, options: PreferImplicitTypeAlways);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestConversionWithUseVarForAll_CanUseVarBecauseConditionalTypeMatches_Throw2()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    string s;
                    [|if|] (true)
                    {
                        s = "a";
                    }
                    else
                    {
                        throw new System.Exception();
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    var s = true ? "a" : throw new System.Exception();
                }
            }
            """, options: PreferImplicitTypeAlways);
    }
 
    [Fact]
    public async Task TestConversionWithUseVarForAll_CanUseVarButRequiresCastOfConditionalBranch_CSharp8()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    string s;
                    [|if|] (true)
                    {
                        s = null;
                    }
                    else
                    {
                        s = null;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    var s = true ? null : (string)null;
                }
            }
            """, LanguageVersion.CSharp8, PreferImplicitTypeAlways);
    }
 
    [Fact]
    public async Task TestConversionWithUseVarForAll_CanUseVarButRequiresCastOfConditionalBranch_CSharp9()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M()
                {
                    string s;
                    [|if|] (true)
                    {
                        s = null;
                    }
                    else
                    {
                        s = null;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    var s = true ? null : (string)null;
                }
            }
            """, LanguageVersion.CSharp9, options: PreferImplicitTypeAlways);
    }
 
    [Fact]
    public async Task TestKeepTriviaAroundIf()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(int i)
                {
                    // leading
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        i = 1;
                    } // trailing
                }
            }
            """,
            """
            class C
            {
                void M(int i)
                {
                    // leading
                    i = true ? 0 : 1; // trailing
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestFixAll1()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(int i)
                {
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        i = 1;
                    }
 
                    string s;
                    [|if|] (true)
                    {
                        s = "a";
                    }
                    else
                    {
                        s = "b";
                    }
                }
            }
            """,
            """
            class C
            {
                void M(int i)
                {
                    i = true ? 0 : 1;
 
                    string s = true ? "a" : "b";
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMultiLine1()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(int i)
                {
                    [|if|] (true)
                    {
                        i = Foo(
                            1, 2, 3);
                    }
                    else
                    {
                        i = 1;
                    }
                }
 
                int Foo(int x, int y, int z) => 0;
            }
            """,
            """
            class C
            {
                void M(int i)
                {
                    i = true
                        ? Foo(
                            1, 2, 3)
                        : 1;
                }
 
                int Foo(int x, int y, int z) => 0;
            }
            """);
    }
 
    [Fact]
    public async Task TestMultiLine2()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(int i)
                {
                    [|if|] (true)
                    {
                        i = 0;
                    }
                    else
                    {
                        i = Foo(
                            1, 2, 3);
                    }
                }
 
                int Foo(int x, int y, int z) => 0;
            }
            """,
            """
            class C
            {
                void M(int i)
                {
                    i = true
                        ? 0
                        : Foo(
                            1, 2, 3);
                }
 
                int Foo(int x, int y, int z) => 0;
            }
            """);
    }
 
    [Fact]
    public async Task TestMultiLine3()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(int i)
                {
                    [|if|] (true)
                    {
                        i = Foo(
                            1, 2, 3);
                    }
                    else
                    {
                        i = Foo(
                            4, 5, 6);
                    }
                }
 
                int Foo(int x, int y, int z) => 0;
            }
            """,
            """
            class C
            {
                void M(int i)
                {
                    i = true
                        ? Foo(
                            1, 2, 3)
                        : Foo(
                            4, 5, 6);
                }
 
                int Foo(int x, int y, int z) => 0;
            }
            """);
    }
 
    [Fact]
    public async Task TestElseIfWithBlock()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(int i)
                {
                    if (true)
                    {
                    }
                    else [|if|] (false)
                    {
                        i = 1;
                    }
                    else
                    {
                        i = 0;
                    }
                }
            }
            """,
            """
            class C
            {
                void M(int i)
                {
                    if (true)
                    {
                    }
                    else
                    {
                        i = false ? 1 : 0;
                    }
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestElseIfWithBlock_Throw1()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(int i)
                {
                    if (true)
                    {
                    }
                    else [|if|] (false)
                    {
                        throw new System.Exception();
                    }
                    else
                    {
                        i = 0;
                    }
                }
            }
            """,
            """
            class C
            {
                void M(int i)
                {
                    if (true)
                    {
                    }
                    else
                    {
                        i = false ? throw new System.Exception() : 0;
                    }
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestElseIfWithBlock_Throw2()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(int i)
                {
                    if (true)
                    {
                    }
                    else [|if|] (false)
                    {
                        i = 1;
                    }
                    else
                    {
                        throw new System.Exception();
                    }
                }
            }
            """,
            """
            class C
            {
                void M(int i)
                {
                    if (true)
                    {
                    }
                    else
                    {
                        i = false ? 1 : throw new System.Exception();
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestElseIfWithoutBlock()
    {
        await new VerifyCS.Test
        {
            TestCode = """
            class C
            {
                void M(int i)
                {
                    if (true) i = 2;
                    else [|if|] (false) i = 1;
                    else i = 0;
                }
            }
            """,
            FixedCode = """
            class C
            {
                void M(int i)
                {
                    if (true) i = 2;
                    else i = false ? 1 : 0;
                }
            }
            """,
            CodeFixTestBehaviors = Testing.CodeFixTestBehaviors.FixOne,
            FixedState =
            {
                ExpectedDiagnostics =
                {
                    // /0/Test0.cs(6,9): hidden IDE0045: 'if' statement can be simplified
                    VerifyCS.Diagnostic().WithSpan(5, 9, 5, 11).WithSpan(5, 9, 6, 32),
                }
            }
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestRefAssignment1()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(ref int i, ref int j)
                {
                    ref int x = ref i;
                    [|if|] (true)
                    {
                        x = ref i;
                    }
                    else
                    {
                        x = ref j;
                    }
                }
            }
            """,
            """
            class C
            {
                void M(ref int i, ref int j)
                {
                    ref int x = ref i;
                    x = ref true ? ref i : ref j;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestRefAssignment1_Throw1()
    {
        await TestMissingAsync(
            """
            class C
            {
                void M(ref int i, ref int j)
                {
                    ref int x = ref i;
                    if (true)
                    {
                        throw new System.Exception();
                    }
                    else
                    {
                        x = ref j;
                    }
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestRefAssignment1_Throw2()
    {
        await TestMissingAsync(
            """
            class C
            {
                void M(ref int i, ref int j)
                {
                    ref int x = ref i;
                    if (true)
                    {
                        x = ref i;
                    }
                    else
                    {
                        throw new System.Exception();
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestTrueFalse1()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(bool i, int j)
                {
                    [|if|] (j == 0)
                    {
                        i = true;
                    }
                    else
                    {
                        i = false;
                    }
                }
            }
            """,
            """
            class C
            {
                void M(bool i, int j)
                {
                    i = j == 0;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestTrueFalse_Throw1()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(bool i, int j)
                {
                    [|if|] (j == 0)
                    {
                        i = true;
                    }
                    else
                    {
                        throw new System.Exception();
                    }
                }
            }
            """,
            """
            class C
            {
                void M(bool i, int j)
                {
                    i = j == 0 ? true : throw new System.Exception();
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestTrueFalse_Throw2()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(bool i, int j)
                {
                    [|if|] (j == 0)
                    {
                        throw new System.Exception();
                    }
                    else
                    {
                        i = false;
                    }
                }
            }
            """,
            """
            class C
            {
                void M(bool i, int j)
                {
                    i = j == 0 ? throw new System.Exception() : false;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestTrueFalse2()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(bool i, int j)
                {
                    [|if|] (j == 0)
                    {
                        i = false;
                    }
                    else
                    {
                        i = true;
                    }
                }
            }
            """,
            """
            class C
            {
                void M(bool i, int j)
                {
                    i = j != 0;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestFalseTrue_Throw1()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(bool i, int j)
                {
                    [|if|] (j == 0)
                    {
                        throw new System.Exception();
                    }
                    else
                    {
                        i = true;
                    }
                }
            }
            """,
            """
            class C
            {
                void M(bool i, int j)
                {
                    i = j == 0 ? throw new System.Exception() : true;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43291")]
    public async Task TestFalseTrue_Throw2()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                void M(bool i, int j)
                {
                    [|if|] (j == 0)
                    {
                        i = false;
                    }
                    else
                    {
                        throw new System.Exception();
                    }
                }
            }
            """,
            """
            class C
            {
                void M(bool i, int j)
                {
                    i = j == 0 ? false : throw new System.Exception();
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58898")]
    public async Task TestRemoveRedundantCast()
    {
        await TestInRegularAndScript1Async(
            """
            using System;
 
            class C
            {
                void M()
                {
                    Guid? id;
 
                    [|if|] (true)
                    {
                        id = Guid.NewGuid();
                    }
                    else
                    {
                        id = Guid.Empty;
                    }
                }
            }
            """,
            """
            using System;
 
            class C
            {
                void M()
                {
                    var id = true ? Guid.NewGuid() : (Guid?)Guid.Empty;
                }
            }
            """, options: PreferImplicitTypeAlways);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33284")]
    public async Task TestConditionalWithLambdas()
    {
        await TestInRegularAndScript1Async(
            """
            using System;
 
            class C
            {
                void M(bool containsHighBits)
                {
                    Action<char> write;
 
                    [|if|] (containsHighBits)
                    {
                        write = (char character) => Console.WriteLine(1);
                    }
                    else
                    {
                        write = (char character) => Console.WriteLine(2);
                    }
                }
            }
            """,
            """
            using System;
 
            class C
            {
                void M(bool containsHighBits)
                {
                    Action<char> write = containsHighBits ? ((char character) => Console.WriteLine(1)) : ((char character) => Console.WriteLine(2));
                }
            }
            """, LanguageVersion.CSharp9);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/39260")]
    public async Task TestTitleWhenSimplifying()
    {
        await TestInRegularAndScript1Async(
            """
            using System;
 
            class C
            {
                void M(string node1, string node2)
                {
                    bool b;
                    [|if|] (AreSimilarCore(node1, node2))
                    {
                        b = true;
                    }
                    else
                    {
                        b = false;
                    }
                }
 
                private bool AreSimilarCore(string node1, string node2)
                {
                    throw new NotImplementedException();
                }
            }
            """,
            """
            using System;
 
            class C
            {
                void M(string node1, string node2)
                {
                    bool b = AreSimilarCore(node1, node2);
                }
 
                private bool AreSimilarCore(string node1, string node2)
                {
                    throw new NotImplementedException();
                }
            }
            """, LanguageVersion.CSharp9, equivalenceKey: nameof(AnalyzersResources.Simplify_check));
    }
 
    [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/67649")]
    [InlineData("int", "int")]
    [InlineData("string", "string")]
    [InlineData("string", "object")]
    [InlineData("object", "string")]
    [InlineData("int", "long")]
    [InlineData("long", "int")]
    public async Task TestForDiscardsWithMatchingOrConvertibleExpressionTypes(string originalFirstType, string originalSecondType)
    {
        await TestInRegularAndScript1Async($$"""
            class MyClass
            {
                void M(bool flag)
                {
                    [|if|] (flag)
                    {
                        _ = A();
                    }
                    else
                    {
                        _ = B();
                    }
                }
 
                {{originalFirstType}} A() => default;
                {{originalSecondType}} B() => default;
            }
            """, $$"""
            class MyClass
            {
                void M(bool flag)
                {
                    _ = flag ? A() : B();
                }
            
                {{originalFirstType}} A() => default;
                {{originalSecondType}} B() => default;
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67649")]
    public async Task TestMissingForDiscardsWithDifferentTypes()
    {
        await TestMissingAsync("""
            class MyClass
            {
                void M(bool flag)
                {
                    if (flag)
                    {
                        _ = A();
                    }
                    else
                    {
                        _ = B();
                    }
                }
            
                int A() => default;
                string B() => default;
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67649")]
    public async Task TestMissingForDiscardsWithBothImplicitConversions()
    {
        await TestMissingAsync("""
            class MyClass
            {
                void M(bool flag)
                {
                    if (flag)
                    {
                        _ = GetC();
                    }
                    else
                    {
                        _ = GetString();
                    }
                }
 
                C GetC() => new C();
                string GetString() => "";
            }
 
            class C
            {
                public static implicit operator C(string c) => new C();
                public static implicit operator string(C c) => "";
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68578")]
    public async Task TestMissingWhenAssignmentReferencesPatternVariable()
    {
        await TestMissingAsync("""
            using System;
 
            public class Class1
            {
                public int i;
            }
 
            public class Program
            {
                public static void Test(object obj)
                {
                    if (obj is Class1 c)
                    {
                        c.i = 1;
                    }
                    else
                    {
                        throw new Exception();
                    }
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68578")]
    public async Task TestMissingWhenAssignmentReferencesOutVariable()
    {
        await TestMissingAsync("""
            using System;
 
            public class Class1
            {
                public int i;
            }
 
            public class Program
            {
                public static void Test(object obj)
                {
                    if (TryGetValue(out var c))
                    {
                        c.i = 1;
                    }
                    else    
                    {
                        throw new Exception();
                    }
                }
 
                private static bool TryGetValue(out Class1 c) => throw new NotImplementedException();
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71403")]
    public async Task TestGlobalStatements()
    {
        await new VerifyCS.Test
        {
            TestCode = """
                #nullable enable
 
                using System;
 
                object? x = null;
                object? y = null;
                object? z;
 
                [|if|] (x != null)
                {
                    z = x;
                }
                else
                {
                    z = y;
                }
 
                Console.WriteLine($"{x}{y}{z}");
                """,
            FixedCode = """
                #nullable enable
 
                using System;
 
                object? x = null;
                object? y = null;
                object? z = x != null ? x : y;
            
                Console.WriteLine($"{x}{y}{z}");
                """,
            LanguageVersion = LanguageVersion.CSharp9,
            TestState = {
                OutputKind = OutputKind.ConsoleApplication,
            }
        }.RunAsync();
    }
}