File: UsePatternMatching\CSharpIsAndCastCheckWithoutNameDiagnosticAnalyzerTests.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.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.UsePatternMatching;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UsePatternMatching;
 
[Trait(Traits.Feature, Traits.Features.CodeActionsInlineTypeCheck)]
public sealed class CSharpIsAndCastCheckWithoutNameDiagnosticAnalyzerTests(ITestOutputHelper logger)
    : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor(logger)
{
    internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
        => (new CSharpIsAndCastCheckWithoutNameDiagnosticAnalyzer(), new CSharpIsAndCastCheckWithoutNameCodeFixProvider());
 
    [Fact]
    public async Task TestBinaryExpression()
    {
        await TestInRegularAndScript1Async(
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    return [||]obj is TestFile && ((TestFile)obj).i > 0;
                }
            }
            """,
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    return obj is TestFile {|Rename:file|} && file.i > 0;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestNotInCSharp6()
    {
        await TestMissingAsync(
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    return [||]obj is TestFile && ((TestFile)obj).i > 0;
                }
            }
            """, parameters: new TestParameters(parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp6)));
    }
 
    [Fact]
    public async Task TestExpressionBody()
    {
        await TestInRegularAndScript1Async(
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                    => [||]obj is TestFile && ((TestFile)obj).i > 0;
            }
            """,
 
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                    => obj is TestFile {|Rename:file|} && file.i > 0;
            }
            """);
    }
 
    [Fact]
    public async Task TestField()
    {
        await TestInRegularAndScript1Async(
            """
            class TestFile
            {
                int i;
                static object obj;
 
                bool M = [||]obj is TestFile && ((TestFile)obj).i > 0;
            }
            """,
 
            """
            class TestFile
            {
                int i;
                static object obj;
 
                bool M = obj is TestFile {|Rename:file|} && file.i > 0;
            }
            """);
    }
 
    [Fact]
    public async Task TestLambdaBody()
    {
        await TestInRegularAndScript1Async(
            """
            using System;
 
            class TestFile
            {
                int i;
 
                void Goo(Func<bool> f) { }
 
                bool M(object obj)
                    => Goo(() => [||]obj is TestFile && ((TestFile)obj).i > 0, () => obj is TestFile && ((TestFile)obj).i > 0);
            }
            """,
            """
            using System;
 
            class TestFile
            {
                int i;
 
                void Goo(Func<bool> f) { }
 
                bool M(object obj)
                    => Goo(() => obj is TestFile {|Rename:file|} && file.i > 0, () => obj is TestFile && ((TestFile)obj).i > 0);
            }
            """);
    }
 
    [Fact]
    public async Task TestDefiniteAssignment1()
    {
        await TestInRegularAndScript1Async(
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    if ([||]obj is TestFile)
                    {
                        M(((TestFile)obj).i);
                        M(((TestFile)obj).i);
                    }
                    else
                    {
                        M(((TestFile)obj).i);
                        M(((TestFile)obj).i);
                    }
                }
            }
            """,
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    if (obj is TestFile {|Rename:file|})
                    {
                        M(file.i);
                        M(file.i);
                    }
                    else
                    {
                        M(((TestFile)obj).i);
                        M(((TestFile)obj).i);
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestDefiniteAssignment2()
    {
        await TestInRegularAndScript1Async(
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    if (!([||]obj is TestFile))
                    {
                        M(((TestFile)obj).i);
                        M(((TestFile)obj).i);
                    }
                    else
                    {
                        M(((TestFile)obj).i);
                        M(((TestFile)obj).i);
                    }
                }
            }
            """,
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    if (!(obj is TestFile {|Rename:file|}))
                    {
                        M(((TestFile)obj).i);
                        M(((TestFile)obj).i);
                    }
                    else
                    {
                        M(file.i);
                        M(file.i);
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestNotOnAnalyzerMatch()
    {
        await TestMissingAsync(
            """
            class TestFile
            {
                bool M(object obj)
                {
                    if ([||]obj is TestFile)
                    {
                        var file = (TestFile)obj;
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestNotOnNullable()
    {
        await TestMissingAsync(
            """
            struct TestFile
            {
                bool M(object obj)
                {
                    if ([||]obj is TestFile?)
                    {
                        var i = ((TestFile?)obj).Value;
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestComplexMatch()
    {
        await TestInRegularAndScript1Async(
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    return [||]M(null) is TestFile && ((TestFile)M(null)).i > 0;
                }
            }
            """,
 
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    return M(null) is TestFile {|Rename:file|} && file.i > 0;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestTrivia()
    {
        await TestInRegularAndScript1Async(
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    return [||]obj is TestFile && /*before*/ ((TestFile)obj) /*after*/.i > 0;
                }
            }
            """,
 
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    return obj is TestFile {|Rename:file|} && /*before*/ file /*after*/.i > 0;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestFixOnlyAfterIsCheck()
    {
        await TestInRegularAndScript1Async(
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    return ((TestFile)obj).i > 0 && [||]obj is TestFile && ((TestFile)obj).i > 0;
                }
            }
            """,
 
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    return ((TestFile)obj).i > 0 && obj is TestFile {|Rename:file|} && file.i > 0;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestArrayNaming()
    {
        await TestInRegularAndScript1Async(
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    return [||]obj is int[] && ((int[])obj) > 0;
                }
            }
            """,
 
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    return obj is int[] {|Rename:v|} && v > 0;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestNamingConflict1()
    {
        await TestInRegularAndScript1Async(
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    TestFile file = null;
                    return [||]obj is TestFile && ((TestFile)obj).i > 0;
                }
            }
            """,
 
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    TestFile file = null;
                    return obj is TestFile {|Rename:file1|} && file1.i > 0;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestNamingConflict2()
    {
        await TestInRegularAndScript1Async(
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    if ([||]obj is TestFile)
                    {
                        TestFile file = null;
                        M(((TestFile)obj).i);
                    }
                }
            }
            """,
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    if (obj is TestFile {|Rename:file1|})
                    {
                        TestFile file = null;
                        M(file1.i);
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestNamingNoConflict1()
    {
        await TestInRegularAndScript1Async(
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    if ([||]obj is TestFile)
                    {
                        var v = new { file = 0 };
                        M(((TestFile)obj).i);
                    }
                }
            }
            """,
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    if (obj is TestFile {|Rename:file|})
                    {
                        var v = new { file = 0 };
                        M(file.i);
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestNamingNoConflict2()
    {
        await TestInRegularAndScript1Async(
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    if ([||]obj is TestFile)
                    {
                        var v = (file: 0, x: 1);
                        M(((TestFile)obj).i);
                    }
                }
            }
            """,
            """
            class TestFile
            {
                int i;
                bool M(object obj)
                {
                    if (obj is TestFile {|Rename:file|})
                    {
                        var v = (file: 0, x: 1);
                        M(file.i);
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestNamingNoConflict3()
    {
        await TestInRegularAndScript1Async(
            """
            class X { public int file; }
 
            class TestFile
            {
                int i;
                bool M(object obj, X x)
                {
                    if ([||]obj is TestFile)
                    {
                        var v = new { x.file };
                        M(((TestFile)obj).i);
                    }
                }
            }
            """,
            """
            class X { public int file; }
 
            class TestFile
            {
                int i;
                bool M(object obj, X x)
                {
                    if (obj is TestFile {|Rename:file|})
                    {
                        var v = new { x.file };
                        M(file.i);
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestNamingNoConflict4()
    {
        await TestInRegularAndScript1Async(
            """
            class X { public int file; }
 
            class TestFile
            {
                int i;
                bool M(object obj, X x)
                {
                    if ([||]obj is TestFile)
                    {
                        var v = (x.file, 0);
                        M(((TestFile)obj).i);
                    }
                }
            }
            """,
            """
            class X { public int file; }
 
            class TestFile
            {
                int i;
                bool M(object obj, X x)
                {
                    if (obj is TestFile {|Rename:file|})
                    {
                        var v = (x.file, 0);
                        M(file.i);
                    }
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51340")]
    public async Task TestNoDiagnosticWhenCS0103Happens()
    {
        await TestDiagnosticMissingAsync(
            """
            using System.Linq;
            class Bar
            {
                private void Foo()
                {
                    var objects = new SpecificThingType[100];
                    var d = from obj in objects
                            let aGenericThing = obj.Prop
                            where aGenericTh[||]ing is SpecificThingType
                            let specificThing = (SpecificThingType)aGenericThing
                            select (obj, specificThing);
                }
            }
            class SpecificThingType
            {
                public SpecificThingType Prop { get; }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58558")]
    public async Task TestInExpressionTree1()
    {
        await TestMissingAsync(
            """
            using System.Linq.Expressions;
 
            object? o = null;
            Expression<Func<bool>> test = () => [||]o is int && (int)o > 5;
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58558")]
    public async Task TestInExpressionTree2()
    {
        await TestMissingAsync(
            """
            using System.Linq.Expressions;
 
            class C
            {
                void M()
                {
                    object? o = null;
                    Expression<Func<bool>> test = () => [||]o is int && (int)o > 5;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68051")]
    public async Task TestNotWhenCrossingStaticLambda()
    {
        await TestMissingAsync(
            """
            using System;
 
            class C
            {
                void Main(object o)
                {
                    if ([||]o is string)
                    {
                        M(static (object o) =>
                        {
                            var s = (string)o;
                        });
                    }
                }
                private void M(Action<object> value)
                {
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68051")]
    public async Task TestNotWhenCrossingInstanceLambdaThatReferencesDifferentVariable()
    {
        await TestMissingAsync(
            """
            using System;
 
            class C
            {
                void Main(object o)
                {
                    if ([||]o is string)
                    {
                        M((object o) =>
                        {
                            var s = (string)o;
                        });
                    }
                }
                private void M(Action<object> value)
                {
                }
            }
            """);
    }
}