File: ConvertLocalFunctionToMethod\ConvertLocalFunctionToMethodTests.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.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.ConvertLocalFunctionToMethod;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeActions.ConvertLocalFunctionToMethod;
 
[Trait(Traits.Feature, Traits.Features.CodeActionsConvertLocalFunctionToMethod)]
public class ConvertLocalFunctionToMethodTests : AbstractCSharpCodeActionTest_NoEditor
{
    protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWorkspace workspace, TestParameters parameters)
        => new CSharpConvertLocalFunctionToMethodCodeRefactoringProvider();
 
    [Fact]
    public async Task TestCaptures1()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                static void Use<T>(T a) {}
                static void Use<T>(ref T a) {}
 
                static void LocalFunction() {} // trigger rename
 
                void M<T1, T2>(T1 param1, T2 param2)
                    where T1 : struct
                    where T2 : struct
                {
                    var local1 = 0;
                    var local2 = 0;
                    void [||]LocalFunction()
                    {
                        Use(param1);
                        Use(ref param2);
                        Use(local1);
                        Use(ref local2);
                        Use(this);
                        LocalFunction();
                    }
                    LocalFunction();
                    System.Action x = LocalFunction;
                }
            }
            """,
            """
            class C
            {
                static void Use<T>(T a) {}
                static void Use<T>(ref T a) {}
 
                static void LocalFunction() {} // trigger rename
 
                void M<T1, T2>(T1 param1, T2 param2)
                    where T1 : struct
                    where T2 : struct
                {
                    var local1 = 0;
                    var local2 = 0;
                    LocalFunction1(param1, ref param2, local1, ref local2);
                    System.Action x = () => LocalFunction1(param1, ref param2, local1, ref local2);
                }
 
                private void LocalFunction1<T1, T2>(T1 param1, ref T2 param2, int local1, ref int local2)
                    where T1 : struct
                    where T2 : struct
                {
                    Use(param1);
                    Use(ref param2);
                    Use(local1);
                    Use(ref local2);
                    Use(this);
                    LocalFunction1(param1, ref param2, local1, ref local2);
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestCaptures2()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                public static void M()
                {
                    var s = default(S);
                    SetValue(3);
                    void [||]SetValue(int value) => s.Value = value;
                }
            }
            struct S
            {
                public int Value;
            }
            """,
            """
            class C
            {
                public static void M()
                {
                    var s = default(S);
                    SetValue(3, ref s);
                }
 
                private static void SetValue(int value, ref S s) => s.Value = value;
            }
            struct S
            {
                public int Value;
            }
            """);
    }
 
    [Fact]
    public async Task TestCaptures3()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                public static void M()
                {
                    LocalFunction(3);
                    void [||]LocalFunction(int value)
                    {
                        System.Func<int> x = () => value;
                    }
                }
            }
            """,
            """
            class C
            {
                public static void M()
                {
                    LocalFunction(3);
                }
 
                private static void LocalFunction(int value)
                {
                    System.Func<int> x = () => value;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestCaptures4()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                public static void M(int i, int j)
                {
                    LocalFunction1();
                    int [||]LocalFunction1() => i;
                    int LocalFunction2() => j;
                }
            }
            """,
            """
            class C
            {
                public static void M(int i, int j)
                {
                    LocalFunction1(i);
                    int LocalFunction2() => j;
                }
 
                private static int LocalFunction1(int i) => i;
            }
            """);
    }
 
    [Fact]
    public async Task TestCaptures5()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                public static void M()
                {
                    LocalFunction();
                    void [||]LocalFunction()
                    {
                        int value = 123;
                        System.Func<int> x = () => value;
                    }
                }
            }
            """,
            """
            class C
            {
                public static void M()
                {
                    LocalFunction();
                }
 
                private static void LocalFunction()
                {
                    int value = 123;
                    System.Func<int> x = () => value;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestTypeParameters1()
    {
        await TestInRegularAndScriptAsync(
            """
            class C<T0>
            {
                static void LocalFunction() {} // trigger rename
 
                void M<T1, T2>(int i)
                    where T1 : struct
                {
                    void Local1<T3, T4>()
                        where T4 : struct
                    {
                        void [||]LocalFunction<T5, T6>(T5 a, T6 b)
                            where T5 : struct
                        {
                            _ = typeof(T2);
                            _ = typeof(T4);
                            LocalFunction(a, b);
                            System.Action<T5, T6> x = LocalFunction;
                        }
                        LocalFunction<byte, int>(5, 6);
                        LocalFunction(5, 6);
                    }
                }
            }
            """,
            """
            class C<T0>
            {
                static void LocalFunction() {} // trigger rename
 
                void M<T1, T2>(int i)
                    where T1 : struct
                {
                    void Local1<T3, T4>()
                        where T4 : struct
                    {
                        LocalFunction1<T2, T4, byte, int>(5, 6);
                        LocalFunction1<T2, T4, int, int>(5, 6);
                    }
                }
 
                private static void LocalFunction1<T2, T4, T5, T6>(T5 a, T6 b)
                    where T4 : struct
                    where T5 : struct
                {
                    _ = typeof(T2);
                    _ = typeof(T4);
                    LocalFunction1<T2, T4, T5, T6>(a, b);
                    System.Action<T5, T6> x = (T5 a1, T6 b1) => LocalFunction1<T2, T4, T5, T6>(a1, b1);
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestTypeParameters2()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                void M(int i)
                {
                    int [||]LocalFunction<T1, T2>(T1 a, T2 b) => i;
                    LocalFunction(2, 3);
                }
            }
            """,
            """
            class C
            {
                void M(int i)
                {
                    LocalFunction(2, 3, i);
                }
 
                private static int LocalFunction<T1, T2>(T1 a, T2 b, int i) => i;
            }
            """);
    }
 
    [Fact]
    public async Task TestNameConflict()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                void LocalFunction() {} // trigger rename
 
                void M()
                {
                    void [||]LocalFunction() => M();
                    LocalFunction();
                    System.Action x = LocalFunction;
                }
            }
            """,
            """
            class C
            {
                void LocalFunction() {} // trigger rename
 
                void M()
                {
                    LocalFunction1();
                    System.Action x = LocalFunction1;
                }
 
                private void LocalFunction1() => M();
            }
            """);
    }
 
    [Fact]
    public async Task TestNamedArguments1()
    {
        await TestAsync(
            """
            class C
            {
                void LocalFunction() {} // trigger rename
 
                void M()
                {
                    int var = 2;
                    int [||]LocalFunction(int i)
                    {
                        return var;
                    }
                    LocalFunction(i: 0);
                }
            }
            """,
            """
            class C
            {
                void LocalFunction() {} // trigger rename
 
                void M()
                {
                    int var = 2;
                    LocalFunction1(i: 0, var);
                }
 
                private static int LocalFunction1(int i, int var)
                {
                    return var;
                }
            }
            """, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7_2));
    }
 
    [Fact]
    public async Task TestNamedArguments2()
    {
        await TestAsync(
            """
            class C
            {
                void LocalFunction() {} // trigger rename
 
                void M()
                {
                    int var = 2;
                    int [||]LocalFunction(int i)
                    {
                        return var;
                    }
                    LocalFunction(i: 0);
                }
            }
            """,
            """
            class C
            {
                void LocalFunction() {} // trigger rename
 
                void M()
                {
                    int var = 2;
                    LocalFunction1(i: 0, var: var);
                }
 
                private static int LocalFunction1(int i, int var)
                {
                    return var;
                }
            }
            """, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7));
    }
 
    [Fact]
    public async Task TestDelegate1()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                void LocalFunction() {} // trigger rename
 
                void M(int i)
                {
                    int [||]LocalFunction() => i;
                    System.Func<int> x = LocalFunction;
                }
            }
            """,
            """
            class C
            {
                void LocalFunction() {} // trigger rename
 
                void M(int i)
                {
                    System.Func<int> x = () => LocalFunction1(i);
                }
 
                private static int LocalFunction1(int i) => i;
            }
            """);
    }
 
    [Fact]
    public async Task TestDelegate2()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                void LocalFunction() {} // trigger rename
                delegate int D(int a, ref string b);
                void M(int i, int j)
                {
                    int [||]LocalFunction(int a, ref string b) => i = j;
                    var x = (D)LocalFunction;
                }
            }
            """,
            """
            class C
            {
                void LocalFunction() {} // trigger rename
                delegate int D(int a, ref string b);
                void M(int i, int j)
                {
                    var x = (D)((int a, ref string b) => LocalFunction1(a, ref b, ref i, j));
                }
 
                private static int LocalFunction1(int a, ref string b, ref int i, int j) => i = j;
            }
            """);
    }
 
    [Fact]
    public async Task TestAsyncFunction1()
    {
        await TestInRegularAndScriptAsync(
            """
            using System;
            using System.Threading;
            using System.Threading.Tasks;
            class C
            {
                void M<T>(Func<CancellationToken, Task<T>> func) {}
                void M<T>(Task<T> task)
                {
                    async Task<T> [||]LocalFunction(CancellationToken c)
                    {
                        return await task;
                    }
                    M(LocalFunction);
                }
            }
            """,
            """
            using System;
            using System.Threading;
            using System.Threading.Tasks;
            class C
            {
                void M<T>(Func<CancellationToken, Task<T>> func) {}
                void M<T>(Task<T> task)
                {
                    M(c => LocalFunction(c, task));
                }
 
                private static async Task<T> LocalFunction<T>(CancellationToken c, Task<T> task)
                {
                    return await task;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestAsyncFunction2()
    {
        await TestInRegularAndScriptAsync(
            """
            using System;
            using System.Threading;
            using System.Threading.Tasks;
            class C
            {
                void M(Action<CancellationToken> func) {}
                void M<T>(Task<T> task)
                {
                    async void [||]LocalFunction(CancellationToken c)
                    {
                        await task;
                    }
                    M(LocalFunction);
                }
            }
            """,
            """
            using System;
            using System.Threading;
            using System.Threading.Tasks;
            class C
            {
                void M(Action<CancellationToken> func) {}
                void M<T>(Task<T> task)
                {
                    M(c => LocalFunction(c, task));
                }
 
                private static async void LocalFunction<T>(CancellationToken c, Task<T> task)
                {
                    await task;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35180")]
    public async Task TestCaretPositon()
    {
        await TestAsync("C [||]LocalFunction(C c)");
        await TestAsync("C Local[||]Function(C c)");
        await TestAsync("C [|LocalFunction|](C c)");
        await TestAsync("C LocalFunction[||](C c)");
        await TestAsync("C Local[|Function|](C c)");
        await TestAsync("[||]C LocalFunction(C c)");
        await TestAsync("C[||] LocalFunction(C c)");
        await TestAsync("C LocalFunction([||]C c)");
        await TestAsync("C LocalFunction(C [||]c)");
        await TestMissingAsync("[|C|] LocalFunction(C c)");
        await TestMissingAsync("C[| |]LocalFunction(C c)");
        await TestMissingAsync("C LocalFunction([|C c|])");
 
        async Task TestAsync(string signature)
        {
            await TestInRegularAndScriptAsync(
$@"class C
{{
    void M()
    {{
        {signature}
        {{
            return null;
        }}
    }}
}}",
"""
class C
{
    void M()
    {
    }
 
    private static C LocalFunction(C c)
    {
        return null;
    }
}
""");
        }
 
        async Task TestMissingAsync(string signature)
        {
            await this.TestMissingAsync(
$@"class C
{{
    void M()
    {{
        {signature}
        {{
            return null;
        }}
    }}
}}");
        }
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35180")]
    public async Task TestMethodBlockSelection1()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                void M()
                {
                    [|C LocalFunction(C c)
                    {
                        return null;
                    }|]
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                }
 
                private static C LocalFunction(C c)
                {
                    return null;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35180")]
    public async Task TestMethodBlockSelection2()
    {
 
        await TestMissingAsync(
            """
            class C
            {
                void M()
                {
                    C LocalFunction(C c)[|
                    {
                        return null;
                    }|]
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35180")]
    public async Task TestMethodBlockSelection3()
    {
        await TestInRegularAndScriptAsync(
 
            """
            class C
            {
                void M()
                {
            [|
                    C LocalFunction(C c)
                    {
                        return null;
                    }
                    |]
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    
                }
 
                private static C LocalFunction(C c)
                {
                    return null;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35180")]
    public async Task TestMethodBlockSelection4()
    {
 
        await this.TestMissingAsync(
"""
class C
{
    void M()
    {
 
        object a = null[|;
        C LocalFunction(C c)
        {      
            return null;
 
        }|]
    }
}
""");
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35180")]
    public async Task TestMethodBlockSelection5()
    {
 
        await this.TestMissingAsync(
"""
class C
{
    void M()
    {
 
        [|
        C LocalFunction(C c)
        {      
            return null;
 
        }
        object|] a = null
    }
}
""");
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35180")]
    public async Task TestMethodBlockSelection6()
    {
 
        await this.TestMissingAsync(
"""
class C
{
    void M()
    {
        C LocalFunction(C c)
        {
            object b = null;
            [|
            object a = null;
            return null;
            |]
 
        }
    }
}
""");
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35180")]
    public async Task TestMethodBlockSelection7()
    {
        await TestMissingAsync(
            """
            class C
            {
                void M()
                {
                    C LocalFunction(C c)
                    {
                        [|return null;|]
                    }
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35180")]
    public async Task TestMethodBlockSelection8()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                void M()
                {
                    [|C LocalFunction(C c)|]
                    {
                        return null;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                }
 
                private static C LocalFunction(C c)
                {
                    return null;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35180")]
    public async Task TestMethodBlockSelection9()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                void M()
                {
                    C LocalFunction(C c)
                    {
                        return null;
                    }[||]
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                }
 
                private static C LocalFunction(C c)
                {
                    return null;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35180")]
    public async Task TestMethodBlockSelection10()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                void M()
                {
                    [||]C LocalFunction(C c)
                    {
                        return null;
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                }
 
                private static C LocalFunction(C c)
                {
                    return null;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35180")]
    public async Task TestMethodBlockSelection11()
    {
        await TestMissingAsync(
            """
            class C
            {
                void M()
                {
                    object a = null;[||]
                    C LocalFunction(C c)
                    {
                        return null;
                    }
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32976")]
    public async Task TestUnsafeLocalFunction()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                public unsafe void UnsafeFunction()
                {
                    byte b = 1;
                    [|unsafe byte* GetPtr(byte* bytePt)
                    {
                        return bytePt;
                    }|]
                    var aReference = GetPtr(&b);
                }
            }
            """,
            """
            class C
            {
                public unsafe void UnsafeFunction()
                {
                    byte b = 1;
                    var aReference = GetPtr(&b);
                }
 
                private static unsafe byte* GetPtr(byte* bytePt)
                {
                    return bytePt;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32976")]
    public async Task TestUnsafeLocalFunctionInUnsafeMethod()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                public unsafe void UnsafeFunction()
                {
                    byte b = 1;
                    [|byte* GetPtr(byte* bytePt)
                    {
                        return bytePt;
                    }|]
                    var aReference = GetPtr(&b);
                }
            }
            """,
            """
            class C
            {
                public unsafe void UnsafeFunction()
                {
                    byte b = 1;
                    var aReference = GetPtr(&b);
                }
 
                private static unsafe byte* GetPtr(byte* bytePt)
                {
                    return bytePt;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32976")]
    public async Task TestLocalFunctionInUnsafeMethod()
    {
        await TestInRegularAndScriptAsync(
            """
            class C
            {
                public unsafe void UnsafeFunction()
                {
                    byte b = 1;
                    [|byte GetPtr(byte bytePt)
                    {
                        return bytePt;
                    }|]
                    var aReference = GetPtr(b);
                }
            }
            """,
            """
            class C
            {
                public unsafe void UnsafeFunction()
                {
                    byte b = 1;
                    var aReference = GetPtr(b);
                }
 
                private static byte GetPtr(byte bytePt)
                {
                    return bytePt;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestTopLevelStatements()
    {
        await TestMissingAsync("""
            Console.WriteLine("Hello");
            {
                public static int [|Add|](int x, int y)
                {
                    return x + y;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32975")]
    public async Task TestRefReturn()
    {
        await TestInRegularAndScript1Async("""
            class ClassA
            {
                class RefClass { }
                RefClass refClass = new RefClass();
                public void RefLocalFunction()
                {
                    ref RefClass [|GetRef|]()
                    {
                        return ref refClass;
                    }
                    ref var aReference = ref GetRef();
                }
            }
            """, """
            class ClassA
            {
                class RefClass { }
                RefClass refClass = new RefClass();
                public void RefLocalFunction()
                {
                    ref var aReference = ref GetRef();
                }
 
                private ref RefClass GetRef()
                {
                    return ref refClass;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32975")]
    public async Task TestAttributes()
    {
        await TestInRegularAndScript1Async("""
            class ClassA
            {
                class RefClass { }
                RefClass refClass = new RefClass();
                public void RefLocalFunction()
                {
                    [System.STAThread]
                    ref RefClass [|GetRef|]()
                    {
                        return ref refClass;
                    }
                    ref var aReference = ref GetRef();
                }
            }
            """, """
            class ClassA
            {
                class RefClass { }
                RefClass refClass = new RefClass();
                public void RefLocalFunction()
                {
                    ref var aReference = ref GetRef();
                }
 
                [System.STAThread]
                private ref RefClass GetRef()
                {
                    return ref refClass;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72024")]
    public async Task TestLocalFunctionInField()
    {
        await TestMissingInRegularAndScriptAsync(
            """
            using System;
 
            class C
            {
                private Action a = () =>
                {
                    var s = default(S);
                    SetValue(3);
                    void [||]SetValue(int value) => s.Value = value;
                }
            }
            struct S
            {
                public int Value;
            }
            """);
    }
}