File: src\Analyzers\CSharp\Tests\InvokeDelegateWithConditionalAccess\InvokeDelegateWithConditionalAccessTests.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.InvokeDelegateWithConditionalAccess;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.InvokeDelegateWithConditionalAccess;
 
[Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
public partial class InvokeDelegateWithConditionalAccessTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor
{
    public InvokeDelegateWithConditionalAccessTests(ITestOutputHelper logger)
       : base(logger)
    {
    }
 
    internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
        => (new InvokeDelegateWithConditionalAccessAnalyzer(), new InvokeDelegateWithConditionalAccessCodeFixProvider());
 
    [Fact]
    public async Task Test1()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    [||]var v = a;
                    if (v != null)
                    {
                        v();
                    }
                }
            }
            """,
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    a?.Invoke();
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestOnIf()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    var v = a;
                    [||]if (v != null)
                    {
                        v();
                    }
                }
            }
            """,
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    a?.Invoke();
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestOnInvoke()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    var v = a;
                    if (v != null)
                    {
                        [||]v();
                    }
                }
            }
            """,
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    a?.Invoke();
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/13226")]
    public async Task TestMissingBeforeCSharp6()
    {
        await TestMissingAsync(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    [||]var v = a;
                    if (v != null)
                    {
                        v();
                    }
                }
            }
            """, new TestParameters(CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5)));
    }
 
    [Fact]
    public async Task TestInvertedIf()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    [||]var v = a;
                    if (null != v)
                    {
                        v();
                    }
                }
            }
            """,
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    a?.Invoke();
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestIfWithNoBraces()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    [||]var v = a;
                    if (null != v)
                        v();
                }
            }
            """,
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    a?.Invoke();
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestWithComplexExpression()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    bool b = true;
                    [||]var v = b ? a : null;
                    if (v != null)
                    {
                        v();
                    }
                }
            }
            """,
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    bool b = true;
                    (b ? a : null)?.Invoke();
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMissingWithElseClause()
    {
        await TestMissingInRegularAndScriptAsync(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    [||]var v = a;
                    if (v != null)
                    {
                        v();
                    }
                    else
                    {
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMissingOnDeclarationWithMultipleVariables()
    {
        await TestMissingInRegularAndScriptAsync(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    [||]var v = a, x = a;
                    if (v != null)
                    {
                        v();
                    }
                }
            }
            """);
    }
 
    /// <remarks>
    /// With multiple variables in the same declaration, the fix _is not_ offered on the declaration
    /// itself, but _is_ offered on the invocation pattern.
    /// </remarks>
    [Fact]
    public async Task TestLocationWhereOfferedWithMultipleVariables()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    var v = a, x = a;
                    [||]if (v != null)
                    {
                        v();
                    }
                }
            }
            """,
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    var v = a, x = a;
                    v?.Invoke();
                }
            }
            """);
    }
 
    /// <remarks>
    /// If we have a variable declaration and if it is read/written outside the delegate 
    /// invocation pattern, the fix is not offered on the declaration.
    /// </remarks>
    [Fact]
    public async Task TestMissingOnDeclarationIfUsedOutside()
    {
        await TestMissingInRegularAndScriptAsync(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    [||]var v = a;
                    if (v != null)
                    {
                        v();
                    }
 
                    v = null;
                }
            }
            """);
    }
 
    /// <remarks>
    /// If we have a variable declaration and if it is read/written outside the delegate 
    /// invocation pattern, the fix is not offered on the declaration but is offered on
    /// the invocation pattern itself.
    /// </remarks>
    [Fact]
    public async Task TestLocationWhereOfferedIfUsedOutside()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    var v = a;
                    [||]if (v != null)
                    {
                        v();
                    }
 
                    v = null;
                }
            }
            """,
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    var v = a;
                    v?.Invoke();
 
                    v = null;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestSimpleForm1()
    {
        await TestInRegularAndScript1Async(
            """
            using System;
 
            class C
            {
                public event EventHandler E;
 
                void M()
                {
                    [||]if (this.E != null)
                    {
                        this.E(this, EventArgs.Empty);
                    }
                }
            }
            """,
            """
            using System;
 
            class C
            {
                public event EventHandler E;
 
                void M()
                {
                    this.E?.Invoke(this, EventArgs.Empty);
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestSimpleForm2()
    {
        await TestInRegularAndScript1Async(
            """
            using System;
 
            class C
            {
                public event EventHandler E;
 
                void M()
                {
                    if (this.E != null)
                    {
                        [||]this.E(this, EventArgs.Empty);
                    }
                }
            }
            """,
            """
            using System;
 
            class C
            {
                public event EventHandler E;
 
                void M()
                {
                    this.E?.Invoke(this, EventArgs.Empty);
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestInElseClause1()
    {
        await TestInRegularAndScript1Async(
            """
            using System;
 
            class C
            {
                public event EventHandler E;
 
                void M()
                {
                    if (true != true)
                    {
                    }
                    else [||]if (this.E != null)
                    {
                        this.E(this, EventArgs.Empty);
                    }
                }
            }
            """,
            """
            using System;
 
            class C
            {
                public event EventHandler E;
 
                void M()
                {
                    if (true != true)
                    {
                    }
                    else
                    {
                        this.E?.Invoke(this, EventArgs.Empty);
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestInElseClause2()
    {
        await TestInRegularAndScript1Async(
            """
            using System;
 
            class C
            {
                public event EventHandler E;
 
                void M()
                {
                    if (true != true)
                    {
                    }
                    else [||]if (this.E != null)
                        this.E(this, EventArgs.Empty);
                }
            }
            """,
            """
            using System;
 
            class C
            {
                public event EventHandler E;
 
                void M()
                {
                    if (true != true)
                    {
                    }
                    else this.E?.Invoke(this, EventArgs.Empty);
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestTrivia1()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
                void Goo()
                {
                    // Comment
                    [||]var v = a;
                    if (v != null)
                    {
                        v(); // Comment2
                    }
                }
            }
            """,
            """
            class C
            {
                System.Action a;
                void Goo()
                {
                    // Comment
                    a?.Invoke(); // Comment2
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestTrivia2()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
                void Goo()
                {
                    // Comment
                    [||]if (a != null)
                    {
                        a(); // Comment2
                    }
                }
            }
            """,
            """
            class C
            {
                System.Action a;
                void Goo()
                {
                    // Comment
                    a?.Invoke(); // Comment2
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51563")]
    public async Task TestTrivia3()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
                void Goo()
                {
                    // Comment
                    [||]var v = a;
                    if (v != null) { v(); /* 123 */ } // trails
                    System.Console.WriteLine();
                }
            }
            """,
            """
            class C
            {
                System.Action a;
                void Goo()
                {
                    // Comment
                    a?.Invoke(); /* 123 */  // trails
                    System.Console.WriteLine();
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51563")]
    public async Task TestTrivia4()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
                void Goo()
                {
                    [||]if (a != null) { a(); /* 123 */ } // trails
                    System.Console.WriteLine();
                }
            }
            """,
            """
            class C
            {
                System.Action a;
                void Goo()
                {
                    a?.Invoke(); /* 123 */  // trails
                    System.Console.WriteLine();
                }
            }
            """);
    }
 
    /// <remarks>
    /// tests locations where the fix is offered.
    /// </remarks>
    [Fact]
    public async Task TestFixOfferedOnIf()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    var v = a;
                    [||]if (v != null)
                    {
                        v();
                    }
                }
            }
            """,
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    a?.Invoke();
                }
            }
            """);
    }
 
    /// <remarks>
    /// tests locations where the fix is offered.
    /// </remarks>
    [Fact]
    public async Task TestFixOfferedInsideIf()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    var v = a;
                    if (v != null)
                    {
                        [||]v();
                    }
                }
            }
            """,
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    a?.Invoke();
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMissingOnConditionalInvocation()
    {
        await TestMissingInRegularAndScriptAsync(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    [||]var v = a;
                    v?.Invoke();
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMissingOnConditionalInvocation2()
    {
        await TestMissingInRegularAndScriptAsync(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    var v = a;
                    [||]v?.Invoke();
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMissingOnConditionalInvocation3()
    {
        await TestMissingInRegularAndScriptAsync(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    [||]a?.Invoke();
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMissingOnNonNullCheckExpressions()
    {
        await TestMissingInRegularAndScriptAsync(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    var v = a;
                    if (v == a)
                    {
                        [||]v();
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMissingOnNonNullCheckExpressions2()
    {
        await TestMissingInRegularAndScriptAsync(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    var v = a;
                    if (v == null)
                    {
                        [||]v();
                    }
                }
            }
            """);
    }
 
    /// <remarks>
    /// if local declaration is not immediately preceding the invocation pattern, 
    /// the fix is not offered on the declaration.
    /// </remarks>
    [Fact]
    public async Task TestLocalNotImmediatelyPrecedingNullCheckAndInvokePattern()
    {
        await TestMissingInRegularAndScriptAsync(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    [||]var v = a;
                    int x;
                    if (v != null)
                    {
                        v();
                    }
                }
            }
            """);
    }
 
    /// <remarks>
    /// if local declaration is not immediately preceding the invocation pattern, 
    /// the fix is not offered on the declaration but is offered on the invocation pattern itself.
    /// </remarks>
    [Fact]
    public async Task TestLocalDNotImmediatelyPrecedingNullCheckAndInvokePattern2()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    var v = a;
                    int x;
                    [||]if (v != null)
                    {
                        v();
                    }
                }
            }
            """,
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    var v = a;
                    int x;
                    v?.Invoke();
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestMissingOnFunc()
    {
        await TestMissingInRegularAndScriptAsync(
            """
            class C
            {
                System.Func<int> a;
 
                int Goo()
                {
                    var v = a;
                    [||]if (v != null)
                    {
                        return v();
                    }
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/13226")]
    public async Task TestWithLambdaInitializer()
    {
        await TestInRegularAndScript1Async(
            """
            using System;
 
            class C
            {
                void Goo()
                {
                    Action v = () => {};
                    [||]if (v != null)
                    {
                        v();
                    }
                }
            }
            """,
            """
            using System;
 
            class C
            {
                void Goo()
                {
                    Action v = () => {};
                    v?.Invoke();
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/13226")]
    public async Task TestWithLambdaInitializer2()
    {
        await TestInRegularAndScript1Async(
            """
            using System;
 
            class C
            {
                void Goo()
                {
                    Action v = (() => {});
                    [||]if (v != null)
                    {
                        v();
                    }
                }
            }
            """,
            """
            using System;
 
            class C
            {
                void Goo()
                {
                    Action v = (() => {});
                    v?.Invoke();
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/13226")]
    public async Task TestForWithAnonymousMethod()
    {
        await TestInRegularAndScript1Async(
            """
            using System;
 
            class C
            {
                void Goo()
                {
                    Action v = delegate {};
                    [||]if (v != null)
                    {
                        v();
                    }
                }
            }
            """,
            """
            using System;
 
            class C
            {
                void Goo()
                {
                    Action v = delegate {};
                    v?.Invoke();
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/13226")]
    public async Task TestWithMethodReference()
    {
        await TestInRegularAndScript1Async(
            """
            using System;
 
            class C
            {
                void Goo()
                {
                    Action v = Console.WriteLine;
                    [||]if (v != null)
                    {
                        v();
                    }
                }
            }
            """,
            """
            using System;
 
            class C
            {
                void Goo()
                {
                    Action v = Console.WriteLine;
                    v?.Invoke();
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31827")]
    public async Task TestWithExplicitInvokeCall1()
    {
        await TestInRegularAndScript1Async(
            """
            using System;
 
            class C
            {
                void M()
                {
                    [||]if (Event != null)
                        Event.Invoke(this, EventArgs.Empty);
                }
 
                event EventHandler Event;
            }
            """,
            """
            using System;
 
            class C
            {
                void M()
                {
                    Event?.Invoke(this, EventArgs.Empty);
                }
 
                event EventHandler Event;
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31827")]
    public async Task TestWithExplicitInvokeCall2()
    {
        await TestInRegularAndScript1Async(
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    [||]var v = a;
                    if (v != null)
                    {
                        v.Invoke();
                    }
                }
            }
            """,
            """
            class C
            {
                System.Action a;
 
                void Goo()
                {
                    a?.Invoke();
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/50976")]
    public async Task TestMissingOnFunctionPointer()
    {
        await TestMissingInRegularAndScriptAsync(
            """
            class C
            {
                unsafe void M(delegate* managed<void> func)
                {
                    if (func != null)
                    {
                        [||]func();
                    }
                }
            }
            """);
    }
}