File: src\Analyzers\CSharp\Tests\UseCoalesceExpression\UseCoalesceExpressionForIfNullStatementCheckTests.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.CSharp.Analyzers.UseCoalesceExpression;
using Microsoft.CodeAnalysis.CSharp.UseCoalesceExpression;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.Analyzers.UnitTests.UseCoalesceExpression;
 
using VerifyCS = CSharpCodeFixVerifier<
    CSharpUseCoalesceExpressionForIfNullStatementCheckDiagnosticAnalyzer,
    CSharpUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider>;
 
[Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public sealed class UseCoalesceExpressionForIfNullStatementCheckTests
{
    [Fact]
    public async Task TestLocalDeclaration_ThrowStatement()
    {
        await new VerifyCS.Test
        {
            TestCode = """
            class C
            {
                void M()
                {
                    var item = FindItem() as C;
                    [|if|] (item == null)
                        throw new System.InvalidOperationException();
                }
 
                object FindItem() => null;
            }
            """,
            FixedCode = """
            class C
            {
                void M()
                {
                    var item = FindItem() as C ?? throw new System.InvalidOperationException();
                }
            
                object FindItem() => null;
            }
            """
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLocalDeclaration_Block()
    {
        await new VerifyCS.Test
        {
            TestCode = """
            class C
            {
                void M()
                {
                    var item = FindItem() as C;
                    [|if|] (item == null)
                    {
                        throw new System.InvalidOperationException();
                    }
                }
 
                object FindItem() => null;
            }
            """,
            FixedCode = """
            class C
            {
                void M()
                {
                    var item = FindItem() as C ?? throw new System.InvalidOperationException();
                }
            
                object FindItem() => null;
            }
            """
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLocalDeclaration_IsPattern()
    {
        await new VerifyCS.Test
        {
            TestCode = """
            class C
            {
                void M()
                {
                    var item = FindItem() as C;
                    [|if|] (item is null)
                        throw new System.InvalidOperationException();
                }
 
                object FindItem() => null;
            }
            """,
            FixedCode = """
            class C
            {
                void M()
                {
                    var item = FindItem() as C ?? throw new System.InvalidOperationException();
                }
            
                object FindItem() => null;
            }
            """
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLocalDeclaration_Assignment1()
    {
        await new VerifyCS.Test
        {
            TestCode = """
            class C
            {
                void M()
                {
                    var item = FindItem() as C;
                    [|if|] (item == null)
                        item = new C();
                }
 
                object FindItem() => null;
            }
            """,
            FixedCode = """
            class C
            {
                void M()
                {
                    var item = FindItem() as C ?? new C();
                }
            
                object FindItem() => null;
            }
            """
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLocalDeclaration_Assignment2()
    {
        await new VerifyCS.Test
        {
            TestCode = """
            class C
            {
                void M()
                {
                    var item = FindItem() as C;
                    [|if|] (item == null)
                        item = new();
                }
 
                object FindItem() => null;
            }
            """,
            FixedCode = """
            class C
            {
                void M()
                {
                    var item = FindItem() as C ?? new();
                }
            
                object FindItem() => null;
            }
            """,
            LanguageVersion = LanguageVersion.CSharp9,
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLocalDeclaration_NotWithWrongItemChecked()
    {
        var text = """
            class C
            {
                void M(C item1)
                {
                    var item = FindItem() as C;
                    if (item1 == null)
                        throw new System.InvalidOperationException();
                }
 
                object FindItem() => null;
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = text,
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLocalDeclaration_NotWithWrongCondition()
    {
        var text = """
            class C
            {
                void M()
                {
                    var item = FindItem() as C;
                    if (item != null)
                        throw new System.InvalidOperationException();
                }
 
                object FindItem() => null;
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = text,
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLocalDeclaration_NotWithWrongPattern()
    {
        var text = """
            class C
            {
                void M()
                {
                    var item = FindItem() as C;
                    if (item is not null)
                        throw new System.InvalidOperationException();
                }
 
                object FindItem() => null;
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = text,
            LanguageVersion = LanguageVersion.CSharp9,
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLocalDeclaration_NotWithWrongAssignment()
    {
        var text = """
            class C
            {
                void M(C item1)
                {
                    var item = FindItem() as C;
                    if (item == null)
                        item1 = new C();
                }
 
                object FindItem() => null;
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = text,
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLocalDeclaration_NotWithElseBlock()
    {
        var text = """
            class C
            {
                void M(C item1)
                {
                    var item = FindItem() as C;
                    if (item == null)
                        item = new C();
                    else
                        item = null;
                }
 
                object FindItem() => null;
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = text,
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLocalDeclaration_NotWithMultipleWhenTrueStatements()
    {
        var text = """
            class C
            {
                void M(C item1)
                {
                    var item = FindItem() as C;
                    if (item == null)
                    {
                        item = new C();
                        item = null;
                    }
                }
 
                object FindItem() => null;
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = text,
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLocalDeclaration_NotWithNoWhenTrueStatements()
    {
        var text = """
            class C
            {
                void M(C item1)
                {
                    var item = FindItem() as C;
                    if (item == null)
                    {
                    }
                }
 
                object FindItem() => null;
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = text,
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLocalDeclaration_NotWithThrowWithoutExpression()
    {
        var text = """
            class C
            {
                void M()
                {
                    try
                    {
                    }
                    catch
                    {
                        var item = FindItem() as C;
                        if (item == null)
                            throw;
                    }
                }
 
                object FindItem() => null;
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = text,
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLocalDeclaration_NotWithLocalWithoutInitializer()
    {
        var text = """
            class C
            {
                void M()
                {
                    C item;
                    if ({|CS0165:item|} == null)
                        throw new System.InvalidOperationException();
                }
 
                object FindItem() => null;
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = text,
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLocalDeclaration_NotWithValueTypeInitializer()
    {
        var text = """
            class C
            {
                void M()
                {
                    object item = 0;
                    if (item == null)
                        item = null;
                }
 
                object FindItem() => null;
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = text,
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLocalDeclaration_NotWithReferenceToVariableInThrow()
    {
        var text = """
            class C
            {
                void M()
                {
                    var item = FindItem() as C;
                    if (item is null)
                        throw new System.InvalidOperationException(nameof(item));
                }
 
                object FindItem() => null;
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = text,
            LanguageVersion = LanguageVersion.CSharp9,
        }.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74460")]
    public async Task TestLocalDeclaration_CastWithParenthesizedExpression()
    {
        await new VerifyCS.Test
        {
            TestCode =
            """
            interface I
            {
            }
 
            class C : I
            {
                void M(object o)
                {
                    I item = o as C;
                    [|if|] (item == null)
                    {
                        item = o as D;
                    }
                }
            }
 
            class D : I
            {
            }
            """,
            FixedCode =
            """
            interface I
            {
            }
 
            class C : I
            {
                void M(object o)
                {
                    I item = (I)(o as C) ?? o as D;
                }
            }
 
            class D : I
            {
            }
            """
        }.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74460")]
    public async Task TestLocalDeclaration_CastWithoutParenthesizedExpression()
    {
        await new VerifyCS.Test
        {
            TestCode =
            """
            interface I
            {
            }
 
            class C : I
            {
                void M(C c, D d)
                {
                    I item = c;
                    [|if|] (item == null)
                    {
                        item = d;
                    }
                }
            }
 
            class D : I
            {
            }
            """,
            FixedCode =
            """
            interface I
            {
            }
 
            class C : I
            {
                void M(C c, D d)
                {
                    I item = (I)c ?? d;
                }
            }
 
            class D : I
            {
            }
            """
        }.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74460")]
    public async Task TestLocalDeclaration_NoCastWhenEqualSymbol()
    {
        await new VerifyCS.Test
        {
            TestCode =
            """
            interface I
            {
            }
 
            class C : I
            {
                void M(C c1, C c2)
                {
                    I item = c1;
                    [|if|] (item == null)
                    {
                        item = c2;
                    }
                }
            }
            """,
            FixedCode =
            """
            interface I
            {
            }
 
            class C : I
            {
                void M(C c1, C c2)
                {
                    I item = c1 ?? c2;
                }
            }
            """
        }.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74460")]
    public async Task TestLocalDeclaration_NoCastWhenDerivedClass()
    {
        await new VerifyCS.Test
        {
            TestCode =
            """
            interface I
            {
            }
 
            class C : I
            {
                void M(C c, D d)
                {
                    I item = c;
                    [|if|] (item == null)
                    {
                        item = d;
                    }
                }
            }
 
            class D : C
            {
            }
            """,
            FixedCode =
            """
            interface I
            {
            }
 
            class C : I
            {
                void M(C c, D d)
                {
                    I item = c ?? d;
                }
            }
 
            class D : C
            {
            }
            """
        }.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74460")]
    public async Task TestLocalDeclaration_NoCastWhenDerivedClassReversed()
    {
        await new VerifyCS.Test
        {
            TestCode =
            """
            interface I
            {
            }
 
            class C : D
            {
                void M(C c, D d)
                {
                    I item = c;
                    [|if|] (item == null)
                    {
                        item = d;
                    }
                }
            }
 
            class D : I
            {
            }
            """,
            FixedCode =
            """
            interface I
            {
            }
 
            class C : D
            {
                void M(C c, D d)
                {
                    I item = c ?? d;
                }
            }
 
            class D : I
            {
            }
            """
        }.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/70514")]
    public async Task TestNotAcrossPreprocessorRegion()
    {
        await new VerifyCS.Test
        {
            TestCode = """
                #define DEBUG
 
                class C
                {
                    void M()
                    {
                        var item = FindItem() as C;
                #if DEBUG
                        if (item == null)
                            throw new System.InvalidOperationException();
                #endif
                    }
 
                    object FindItem() => null;
                }
                """,
        }.RunAsync();
    }
}