File: SplitOrMergeIfStatements\SplitIntoConsecutiveIfStatementsTests.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.
 
#nullable disable
 
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.SplitOrMergeIfStatements;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SplitOrMergeIfStatements;
 
[Trait(Traits.Feature, Traits.Features.CodeActionsSplitIntoConsecutiveIfStatements)]
public sealed class SplitIntoConsecutiveIfStatementsTests : AbstractCSharpCodeActionTest_NoEditor
{
    protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWorkspace workspace, TestParameters parameters)
        => new CSharpSplitIntoConsecutiveIfStatementsCodeRefactoringProvider();
 
    [Theory]
    [InlineData("a [||]|| b")]
    [InlineData("a |[||]| b")]
    [InlineData("a ||[||] b")]
    [InlineData("a [||||] b")]
    public async Task SplitOnOrOperatorSpans(string condition)
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (" + condition + @")
        {
        }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
        {
        }
        else if (b)
        {
        }
    }
}");
    }
 
    [Theory]
    [InlineData("a [|||]| b")]
    [InlineData("a[| |||] b")]
    [InlineData("a[||] || b")]
    public async Task NotSplitOnOrOperatorSpans(string condition)
    {
        await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (" + condition + @")
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task NotSplitOnIfKeyword()
    {
        await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        [||]if (a || b)
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task NotSplitOnAndOperator()
    {
        await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]&& b)
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task NotSplitOnBitwiseOrOperator()
    {
        await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]| b)
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task NotSplitOnOrOperatorOutsideIfStatement()
    {
        await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        var v = a [||]|| b;
    }
}");
    }
 
    [Fact]
    public async Task NotSplitOnOrOperatorInIfStatementBody()
    {
        await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a || b)
            a [||]|| b;
    }
}");
    }
 
    [Fact]
    public async Task SplitWithChainedOrExpression1()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b, bool c, bool d)
    {
        if (a [||]|| b || c || d)
        {
        }
    }
}",
@"class C
{
    void M(bool a, bool b, bool c, bool d)
    {
        if (a)
        {
        }
        else if (b || c || d)
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithChainedOrExpression2()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b, bool c, bool d)
    {
        if (a || b [||]|| c || d)
        {
        }
    }
}",
@"class C
{
    void M(bool a, bool b, bool c, bool d)
    {
        if (a || b)
        {
        }
        else if (c || d)
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithChainedOrExpression3()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b, bool c, bool d)
    {
        if (a || b || c [||]|| d)
        {
        }
    }
}",
@"class C
{
    void M(bool a, bool b, bool c, bool d)
    {
        if (a || b || c)
        {
        }
        else if (d)
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task NotSplitInsideParentheses1()
    {
        await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b, bool c, bool d)
    {
        if ((a [||]|| b) || c || d)
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task NotSplitInsideParentheses2()
    {
        await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b, bool c, bool d)
    {
        if (a || b || (c [||]|| d))
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task NotSplitInsideParentheses3()
    {
        await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b, bool c, bool d)
    {
        if ((a || b [||]|| c || d))
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithOtherExpressionInsideParentheses1()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b, bool c, bool d)
    {
        if (a [||]|| (b || c) || d)
        {
        }
    }
}",
@"class C
{
    void M(bool a, bool b, bool c, bool d)
    {
        if (a)
        {
        }
        else if ((b || c) || d)
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithOtherExpressionInsideParentheses2()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b, bool c, bool d)
    {
        if (a || (b || c) [||]|| d)
        {
        }
    }
}",
@"class C
{
    void M(bool a, bool b, bool c, bool d)
    {
        if (a || (b || c))
        {
        }
        else if (d)
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithMixedAndExpression1()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b, bool c)
    {
        if (a [||]|| b && c)
        {
        }
    }
}",
@"class C
{
    void M(bool a, bool b, bool c)
    {
        if (a)
        {
        }
        else if (b && c)
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithMixedAndExpression2()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b, bool c)
    {
        if (a && b [||]|| c)
        {
        }
    }
}",
@"class C
{
    void M(bool a, bool b, bool c)
    {
        if (a && b)
        {
        }
        else if (c)
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task NotSplitWithMixedConditionalExpression1()
    {
        await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b, bool c)
    {
        if (a [||]|| b ? c : c)
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task NotSplitWithMixedConditionalExpression2()
    {
        await TestMissingInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b, bool c)
    {
        if (a ? b : b [||]|| c)
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithMixedConditionalExpressionInsideParentheses1()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b, bool c)
    {
        if (a [||]|| (b ? c : c))
        {
        }
    }
}",
@"class C
{
    void M(bool a, bool b, bool c)
    {
        if (a)
        {
        }
        else if ((b ? c : c))
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithMixedConditionalExpressionInsideParentheses2()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b, bool c)
    {
        if ((a ? b : b) [||]|| c)
        {
        }
    }
}",
@"class C
{
    void M(bool a, bool b, bool c)
    {
        if ((a ? b : b))
        {
        }
        else if (c)
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithStatementInsideBlock()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
        {
            System.Console.WriteLine(a || b);
        }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
        {
            System.Console.WriteLine(a || b);
        }
        else if (b)
        {
            System.Console.WriteLine(a || b);
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithStatementWithoutBlock()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
            System.Console.WriteLine(a || b);
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
            System.Console.WriteLine(a || b);
        else if (b)
            System.Console.WriteLine(a || b);
    }
}");
    }
 
    [Fact]
    public async Task SplitWithNestedIfStatement()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
            if (true) { }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
        {
            if (true) { }
        }
        else if (b)
            if (true) { }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithNestedIfStatementInWhileLoop()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
            while (a)
                if (true)
                    using (null) { }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
        {
            while (a)
                if (true)
                    using (null) { }
        }
        else if (b)
            while (a)
                if (true)
                    using (null) { }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithNestedIfStatementInsideBlockInWhileLoop()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
            while (a)
            {
                if (true)
                    using (null) { }
            }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
            while (a)
            {
                if (true)
                    using (null) { }
            }
        else if (b)
            while (a)
            {
                if (true)
                    using (null) { }
            }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithNestedIfStatementInsideBlock()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
        {
            if (true) { }
        }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
        {
            if (true) { }
        }
        else if (b)
        {
            if (true) { }
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithMissingStatement()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
else if (b)
    }
}");
    }
 
    [Fact]
    public async Task SplitWithElseStatementInsideBlock()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
            System.Console.WriteLine();
        else
        {
            System.Console.WriteLine(a || b);
        }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
            System.Console.WriteLine();
        else if (b)
            System.Console.WriteLine();
        else
        {
            System.Console.WriteLine(a || b);
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithElseStatementWithoutBlock()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
            System.Console.WriteLine();
        else
            System.Console.WriteLine(a || b);
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
            System.Console.WriteLine();
        else if (b)
            System.Console.WriteLine();
        else
            System.Console.WriteLine(a || b);
    }
}");
    }
 
    [Fact]
    public async Task SplitWithElseNestedIfStatement()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
            System.Console.WriteLine();
        else if (true) { }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
            System.Console.WriteLine();
        else if (b)
            System.Console.WriteLine();
        else if (true) { }
    }
}");
    }
 
    [Fact]
    public async Task SplitWithElseIfElse()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
            System.Console.WriteLine();
        else if (a)
            System.Console.WriteLine(a);
        else
            System.Console.WriteLine(b);
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
            System.Console.WriteLine();
        else if (b)
            System.Console.WriteLine();
        else if (a)
            System.Console.WriteLine(a);
        else
            System.Console.WriteLine(b);
    }
}");
    }
 
    [Fact]
    public async Task SplitAsPartOfElseIfElse()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (true)
            System.Console.WriteLine();
        else if (a [||]|| b)
            System.Console.WriteLine(a);
        else
            System.Console.WriteLine(b);
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (true)
            System.Console.WriteLine();
        else if (a)
            System.Console.WriteLine(a);
        else if (b)
            System.Console.WriteLine(a);
        else
            System.Console.WriteLine(b);
    }
}");
    }
 
    [Fact]
    public async Task SplitWithMissingElseStatement()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
            System.Console.WriteLine();
        else
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
            System.Console.WriteLine();
        else if (b)
            System.Console.WriteLine();
        else
    }
}");
    }
 
    [Fact]
    public async Task SplitWithPreservedSingleLineFormatting()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b) System.Console.WriteLine();
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a) System.Console.WriteLine();
        else if (b) System.Console.WriteLine();
    }
}");
    }
 
    [Fact]
    public async Task SplitIntoSeparateStatementsIfControlFlowQuits1()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
            return;
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
            return;
        if (b)
            return;
    }
}");
    }
 
    [Fact]
    public async Task SplitIntoSeparateStatementsIfControlFlowQuits2()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
            throw new System.Exception();
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
            throw new System.Exception();
        if (b)
            throw new System.Exception();
    }
}");
    }
 
    [Fact]
    public async Task SplitIntoSeparateStatementsIfControlFlowQuits3()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        while (true)
        {
            if (a [||]|| b)
                continue;
        }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        while (true)
        {
            if (a)
                continue;
            if (b)
                continue;
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitIntoSeparateStatementsIfControlFlowQuits4()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        while (true)
        {
            if (a [||]|| b)
            {
                if (a)
                    continue;
                else
                    break;
            }
        }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        while (true)
        {
            if (a)
            {
                if (a)
                    continue;
                else
                    break;
            }
 
            if (b)
            {
                if (a)
                    continue;
                else
                    break;
            }
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitIntoSeparateStatementsIfControlFlowQuits5()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        while (true)
        {
            if (a [||]|| b)
                switch (a)
                {
                    default:
                        continue;
                }
        }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        while (true)
        {
            if (a)
                switch (a)
                {
                    default:
                        continue;
                }
 
            if (b)
                switch (a)
                {
                    default:
                        continue;
                }
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitIntoSeparateStatementsIfControlFlowQuitsInSwitchSection()
    {
        // Switch sections are interesting in that they are blocks of statements that aren't BlockSyntax.
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        switch (a)
        {
            case true:
                if (a [||]|| b)
                    break;
                break;
        }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        switch (a)
        {
            case true:
                if (a)
                    break;
                if (b)
                    break;
                break;
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitIntoSeparateStatementsIfControlFlowQuitsWithNestedIfStatement()
    {
        // No need to create a block if we're not adding an else clause.
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
            if (true)
                return;
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
            if (true)
                return;
        if (b)
            if (true)
                return;
    }
}");
    }
 
    [Fact]
    public async Task SplitIntoSeparateStatementsIfControlFlowQuitsWithPreservedSingleLineFormatting()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b) return;
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a) return;
        if (b) return;
    }
}");
    }
 
    [Fact]
    public async Task SplitNotIntoSeparateStatementsIfControlFlowContinues1()
    {
        // Even though there are no statements inside, we still can't split this into separate statements
        // because it would change the semantics from short-circuiting to always evaluating the second condition,
        // breaking code like 'if (a == null || a.InstanceMethod())'.
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
        {
        }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
        {
        }
        else if (b)
        {
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitNotIntoSeparateStatementsIfControlFlowContinues2()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
        {
            if (a)
                return;
        }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
        {
            if (a)
                return;
        }
        else if (b)
        {
            if (a)
                return;
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitNotIntoSeparateStatementsIfControlFlowContinues3()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
            while (a)
            {
                break;
            }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
            while (a)
            {
                break;
            }
        else if (b)
            while (a)
            {
                break;
            }
    }
}");
    }
 
    [Fact]
    public async Task SplitNotIntoSeparateStatementsIfControlFlowContinues4()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        while (true)
        {
            if (a [||]|| b)
                switch (a)
                {
                    default:
                        break;
                }
        }
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        while (true)
        {
            if (a)
                switch (a)
                {
                    default:
                        break;
                }
            else if (b)
                switch (a)
                {
                    default:
                        break;
                }
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitNotIntoSeparateStatementsWithElseIfControlFlowQuits()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a [||]|| b)
            return;
        else
            return;
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
            return;
        else if (b)
            return;
        else
            return;
    }
}");
    }
 
    [Fact]
    public async Task SplitIntoSeparateStatementsAsEmbeddedStatementIfControlFlowQuits()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        while (a)
            if (a [||]|| b)
                return;
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        while (a)
        {
            if (a)
                return;
            if (b)
                return;
        }
    }
}");
    }
 
    [Fact]
    public async Task SplitNotIntoSeparateStatementsAsElseIfIfControlFlowQuits()
    {
        await TestInRegularAndScriptAsync(
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
            return;
        else if (a [||]|| b)
            return;
    }
}",
@"class C
{
    void M(bool a, bool b)
    {
        if (a)
            return;
        else if (a)
            return;
        else if (b)
            return;
    }
}");
    }
}