File: src\Analyzers\CSharp\Tests\MakeMethodSynchronous\MakeMethodSynchronousTests.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.MakeMethodSynchronous;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Testing;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.MakeMethodSynchronous;
 
using VerifyCS = CSharpCodeFixVerifier<
    EmptyDiagnosticAnalyzer,
    CSharpMakeMethodSynchronousCodeFixProvider>;
 
[Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)]
public sealed class MakeMethodSynchronousTests
{
    [Fact]
    public async Task TestTaskReturnType()
    {
        await VerifyCS.VerifyCodeFixAsync(
            """
            using System.Threading.Tasks;
 
            class C
            {
                async Task {|CS1998:Goo|}()
                {
                }
            }
            """,
            """
            using System.Threading.Tasks;
 
            class C
            {
                void Goo()
                {
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestTaskOfTReturnType()
    {
        await VerifyCS.VerifyCodeFixAsync(
            """
            using System.Threading.Tasks;
 
            class C
            {
                async Task<int> {|CS1998:Goo|}()
                {
                    return 1;
                }
            }
            """,
            """
            using System.Threading.Tasks;
 
            class C
            {
                int {|#0:Goo|}()
                {
                    return 1;
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestSecondModifier()
    {
        await VerifyCS.VerifyCodeFixAsync(
            """
            using System.Threading.Tasks;
 
            class C
            {
                public async Task {|CS1998:Goo|}()
                {
                }
            }
            """,
            """
            using System.Threading.Tasks;
 
            class C
            {
                public void Goo()
                {
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestFirstModifier()
    {
        await VerifyCS.VerifyCodeFixAsync(
            """
            using System.Threading.Tasks;
 
            class C
            {
                async public Task {|CS1998:Goo|}()
                {
                }
            }
            """,
            """
            using System.Threading.Tasks;
 
            class C
            {
                public void Goo()
                {
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestTrailingTrivia()
    {
        await VerifyCS.VerifyCodeFixAsync(
            """
            using System.Threading.Tasks;
 
            class C
            {
                async // comment
                Task {|CS1998:Goo|}()
                {
                }
            }
            """,
            """
            using System.Threading.Tasks;
 
            class C
            {
                void Goo()
                {
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestRenameMethod()
    {
        await VerifyCS.VerifyCodeFixAsync(
            """
            using System.Threading.Tasks;
 
            class C
            {
                async Task {|CS1998:GooAsync|}()
                {
                }
            }
            """,
            """
            using System.Threading.Tasks;
 
            class C
            {
                void Goo()
                {
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestRenameMethod1()
    {
        await VerifyCS.VerifyCodeFixAsync(
            """
            using System.Threading.Tasks;
 
            class C
            {
                async Task {|CS1998:GooAsync|}()
                {
                }
 
                void Bar()
                {
                    GooAsync();
                }
            }
            """,
            """
            using System.Threading.Tasks;
 
            class C
            {
                void Goo()
                {
                }
 
                void Bar()
                {
                    Goo();
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestParenthesizedLambda()
    {
        var source =
            """
            using System;
            using System.Threading.Tasks;
 
            class C
            {
                void Goo()
                {
                    Func<Task> f =
                        async () {|CS1998:=>|} { };
                }
            }
            """;
        var expected =
            """
            using System;
            using System.Threading.Tasks;
 
            class C
            {
                void Goo()
                {
                    Func<Task> f =
                        () {|#0:=>|} { };
                }
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = source,
            FixedState =
            {
                Sources = { expected },
                ExpectedDiagnostics =
                {
                    // /0/Test0.cs(10,16): error CS1643: Not all code paths return a value in lambda expression of type 'Func<Task>'
                    DiagnosticResult.CompilerError("CS1643").WithLocation(0),
                },
            },
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestSimpleLambda()
    {
        var source =
            """
            using System;
            using System.Threading.Tasks;
 
            class C
            {
                void Goo()
                {
                    Func<string, Task> f =
                        async a {|CS1998:=>|} { };
                }
            }
            """;
        var expected =
            """
            using System;
            using System.Threading.Tasks;
 
            class C
            {
                void Goo()
                {
                    Func<string, Task> f =
                        a {|#0:=>|} { };
                }
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = source,
            FixedState =
            {
                Sources = { expected },
                ExpectedDiagnostics =
                {
                    // /0/Test0.cs(10,15): error CS1643: Not all code paths return a value in lambda expression of type 'Func<Task>'
                    DiagnosticResult.CompilerError("CS1643").WithLocation(0),
                },
            },
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestLambdaWithExpressionBody()
    {
        var source =
            """
            using System;
            using System.Threading.Tasks;
 
            class C
            {
                void Goo()
                {
                    Func<string, Task<int>> f =
                        async a {|CS1998:=>|} 1;
                }
            }
            """;
        var expected =
            """
            using System;
            using System.Threading.Tasks;
 
            class C
            {
                void Goo()
                {
                    Func<string, Task<int>> f =
                        a => {|#0:1|};
                }
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = source,
            FixedState =
            {
                Sources = { expected },
                ExpectedDiagnostics =
                {
                    // /0/Test0.cs(10,18): error CS0029: Cannot implicitly convert type 'int' to 'System.Threading.Tasks.Task<int>'
                    DiagnosticResult.CompilerError("CS0029").WithLocation(0).WithArguments("int", "System.Threading.Tasks.Task<int>"),
                    // /0/Test0.cs(10,18): error CS1662: Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type
                    DiagnosticResult.CompilerError("CS1662").WithLocation(0),
                },
            },
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestAnonymousMethod()
    {
        var source =
            """
            using System;
            using System.Threading.Tasks;
 
            class C
            {
                void Goo()
                {
                    Func<Task> f =
                        async {|CS1998:delegate|} { };
                }
            }
            """;
        var expected =
            """
            using System;
            using System.Threading.Tasks;
 
            class C
            {
                void Goo()
                {
                    Func<Task> f =
                        {|#0:delegate|} { };
                }
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = source,
            FixedState =
            {
                Sources = { expected },
                ExpectedDiagnostics =
                {
                    // /0/Test0.cs(10,13): error CS1643: Not all code paths return a value in anonymous method of type 'Func<Task>'
                    DiagnosticResult.CompilerError("CS1643").WithLocation(0),
                },
            },
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestFixAll()
    {
        await VerifyCS.VerifyCodeFixAsync(
            """
            using System.Threading.Tasks;
 
            public class Class1
            {
                async Task {|CS1998:GooAsync|}()
                {
                    BarAsync();
                }
 
                async Task<int> {|#0:{|CS1998:BarAsync|}|}()
                {
                    GooAsync();
                    return 1;
                }
            }
            """,
            """
            using System.Threading.Tasks;
 
            public class Class1
            {
                void Goo()
                {
                    Bar();
                }
 
                int {|#0:Bar|}()
                {
                    Goo();
                    return 1;
                }
            }
            """);
    }
 
    [Fact]
    [WorkItem("https://github.com/dotnet/roslyn/issues/13961")]
    public async Task TestRemoveAwaitFromCaller1()
    {
        var source =
            """
            using System.Threading.Tasks;
 
            public class Class1
            {
                async Task {|CS1998:GooAsync|}()
                {
                }
 
                async void BarAsync()
                {
                    await GooAsync();
                }
            }
            """;
        var expected =
            """
            using System.Threading.Tasks;
 
            public class Class1
            {
                void Goo()
                {
                }
 
                async void {|CS1998:BarAsync|}()
                {
                    Goo();
                }
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = source,
            FixedState =
            {
                Sources = { expected },
                MarkupHandling = MarkupMode.Allow,
            },
            CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne,
        }.RunAsync();
    }
 
    [Fact]
    [WorkItem("https://github.com/dotnet/roslyn/issues/13961")]
    public async Task TestRemoveAwaitFromCaller2()
    {
        var source =
            """
            using System.Threading.Tasks;
 
            public class Class1
            {
                async Task {|CS1998:GooAsync|}()
                {
                }
 
                async void BarAsync()
                {
                    await GooAsync().ConfigureAwait(false);
                }
            }
            """;
        var expected =
            """
            using System.Threading.Tasks;
 
            public class Class1
            {
                void Goo()
                {
                }
 
                async void {|CS1998:BarAsync|}()
                {
                    Goo();
                }
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = source,
            FixedState =
            {
                Sources = { expected },
                MarkupHandling = MarkupMode.Allow,
            },
            CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne,
        }.RunAsync();
    }
 
    [Fact]
    [WorkItem("https://github.com/dotnet/roslyn/issues/13961")]
    public async Task TestRemoveAwaitFromCaller3()
    {
        var source =
            """
            using System.Threading.Tasks;
 
            public class Class1
            {
                async Task {|CS1998:GooAsync|}()
                {
                }
 
                async void BarAsync()
                {
                    await this.GooAsync();
                }
            }
            """;
        var expected =
            """
            using System.Threading.Tasks;
 
            public class Class1
            {
                void Goo()
                {
                }
 
                async void {|CS1998:BarAsync|}()
                {
                    this.Goo();
                }
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = source,
            FixedState =
            {
                Sources = { expected },
                MarkupHandling = MarkupMode.Allow,
            },
            CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne,
        }.RunAsync();
    }
 
    [Fact]
    [WorkItem("https://github.com/dotnet/roslyn/issues/13961")]
    public async Task TestRemoveAwaitFromCaller4()
    {
        var source =
            """
            using System.Threading.Tasks;
 
            public class Class1
            {
                async Task {|CS1998:GooAsync|}()
                {
                }
 
                async void BarAsync()
                {
                    await this.GooAsync().ConfigureAwait(false);
                }
            }
            """;
        var expected =
            """
            using System.Threading.Tasks;
 
            public class Class1
            {
                void Goo()
                {
                }
 
                async void {|CS1998:BarAsync|}()
                {
                    this.Goo();
                }
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = source,
            FixedState =
            {
                Sources = { expected },
                MarkupHandling = MarkupMode.Allow,
            },
            CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne,
        }.RunAsync();
    }
 
    [Fact]
    [WorkItem("https://github.com/dotnet/roslyn/issues/13961")]
    public async Task TestRemoveAwaitFromCallerNested1()
    {
        var source =
            """
            using System.Threading.Tasks;
 
            public class Class1
            {
                async Task<int> {|CS1998:GooAsync|}(int i)
                {
                    return 1;
                }
 
                async void BarAsync()
                {
                    await this.GooAsync(await this.GooAsync(0));
                }
            }
            """;
        var expected =
            """
            using System.Threading.Tasks;
 
            public class Class1
            {
                int Goo(int i)
                {
                    return 1;
                }
 
                async void {|CS1998:BarAsync|}()
                {
                    this.Goo(this.Goo(0));
                }
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = source,
            FixedState =
            {
                Sources = { expected },
                MarkupHandling = MarkupMode.Allow,
            },
            CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne,
        }.RunAsync();
    }
 
    [Fact]
    [WorkItem("https://github.com/dotnet/roslyn/issues/13961")]
    public async Task TestRemoveAwaitFromCallerNested()
    {
        var source =
            """
            using System.Threading.Tasks;
 
            public class Class1
            {
                async Task<int> {|CS1998:GooAsync|}(int i)
                {
                    return 1;
                }
 
                async void BarAsync()
                {
                    await this.GooAsync(await this.GooAsync(0).ConfigureAwait(false)).ConfigureAwait(false);
                }
            }
            """;
        var expected =
            """
            using System.Threading.Tasks;
 
            public class Class1
            {
                int Goo(int i)
                {
                    return 1;
                }
 
                async void {|CS1998:BarAsync|}()
                {
                    this.Goo(this.Goo(0));
                }
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = source,
            FixedState =
            {
                Sources = { expected },
                MarkupHandling = MarkupMode.Allow,
            },
            CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne,
        }.RunAsync();
    }
 
    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)]
    [WorkItem("https://github.com/dotnet/roslyn/issues/14133")]
    public async Task RemoveAsyncInLocalFunction()
    {
        await VerifyCS.VerifyCodeFixAsync(
            """
            using System.Threading.Tasks;
 
            class C
            {
                public void M1()
                {
                    async Task {|CS1998:M2Async|}()
                    {
                    }
                }
            }
            """,
            """
            using System.Threading.Tasks;
 
            class C
            {
                public void M1()
                {
                    void M2()
                    {
                    }
                }
            }
            """);
    }
 
    [Theory]
    [InlineData("Task<C>", "C")]
    [InlineData("Task<int>", "int")]
    [InlineData("Task", "void")]
    [InlineData("void", "void")]
    [Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)]
    [WorkItem("https://github.com/dotnet/roslyn/issues/18307")]
    public async Task RemoveAsyncInLocalFunctionKeepsTrivia(string asyncReturn, string expectedReturn)
    {
        await VerifyCS.VerifyCodeFixAsync(
            $$"""
            using System;
            using System.Threading.Tasks;
 
            class C
            {
                public void M1()
                {
                    // Leading trivia
                    /*1*/ async {{asyncReturn}} /*2*/ {|CS1998:M2Async|}/*3*/() /*4*/
                    {
                        throw new NotImplementedException();
                    }
                }
            }
            """,
            $$"""
            using System;
            using System.Threading.Tasks;
 
            class C
            {
                public void M1()
                {
                    // Leading trivia
                    /*1*/
                    {{expectedReturn}} /*2*/ M2/*3*/() /*4*/
                    {
                        throw new NotImplementedException();
                    }
                }
            }
            """);
    }
 
    [Theory]
    [InlineData("", "Task<C>", "\r\n    C")]
    [InlineData("", "Task<int>", "\r\n    int")]
    [InlineData("", "Task", "\r\n    void")]
    [InlineData("", "void", "\r\n    void")]
    [InlineData("public", "Task<C>", " C")]
    [InlineData("public", "Task<int>", " int")]
    [InlineData("public", "Task", " void")]
    [InlineData("public", "void", " void")]
    [Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)]
    [WorkItem("https://github.com/dotnet/roslyn/issues/18307")]
    public async Task RemoveAsyncKeepsTrivia(string modifiers, string asyncReturn, string expectedReturn)
    {
        await VerifyCS.VerifyCodeFixAsync(
            $$"""
            using System;
            using System.Threading.Tasks;
 
            class C
            {
                // Leading trivia
                {{modifiers}}/*1*/ async {{asyncReturn}} /*2*/ {|CS1998:M2Async|}/*3*/() /*4*/
                {
                    throw new NotImplementedException();
                }
            }
            """,
            $$"""
            using System;
            using System.Threading.Tasks;
 
            class C
            {
                // Leading trivia
                {{modifiers}}/*1*/{{expectedReturn}} /*2*/ M2/*3*/() /*4*/
                {
                    throw new NotImplementedException();
                }
            }
            """);
    }
 
    [Fact]
    public async Task MethodWithUsingAwait()
    {
        var source =
            """
            class C
            {
                async System.Threading.Tasks.Task MAsync()
                {
                    await using ({|#0:var x = new object()|})
                    {
                    }
                }
            }
            """;
 
        await new VerifyCS.Test
        {
            ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard21,
            TestCode = source,
            ExpectedDiagnostics =
            {
                // /0/Test0.cs(5,22): error CS8410: 'object': type used in an asynchronous using statement must implement 'System.IAsyncDisposable' or implement a suitable 'DisposeAsync' method.
                DiagnosticResult.CompilerError("CS8410").WithLocation(0).WithArguments("object"),
            },
            FixedCode = source,
        }.RunAsync();
    }
 
    [Fact]
    public async Task MethodWithUsingNoAwait()
    {
        await VerifyCS.VerifyCodeFixAsync(
            """
            class C
            {
                async System.Threading.Tasks.Task {|CS1998:MAsync|}()
                {
                    using ({|#0:var x = new object()|})
                    {
                    }
                }
            }
            """,
            // /0/Test0.cs(5,16): error CS1674: 'object': type used in a using statement must implement 'System.IDisposable'.
            DiagnosticResult.CompilerError("CS1674").WithLocation(0).WithArguments("object"),
            """
            class C
            {
                void M()
                {
                    using ({|#0:var x = new object()|})
                    {
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task MethodWithAwaitForEach()
    {
        var source =
            """
            class C
            {
                async System.Threading.Tasks.Task MAsync()
                {
                    await foreach (var n in {|#0:new int[] { }|})
                    {
                    }
                }
            }
            """;
 
        await VerifyCS.VerifyCodeFixAsync(
            source,
            // /0/Test0.cs(5,33): error CS1061: 'bool' does not contain a definition for 'GetAwaiter' and no accessible extension method 'GetAwaiter' accepting a first argument of type 'bool' could be found (are you missing a using directive or an assembly reference?)
            DiagnosticResult.CompilerError("CS1061").WithLocation(0).WithArguments("bool", "GetAwaiter"),
            source);
    }
 
    [Fact]
    public async Task MethodWithForEachNoAwait()
    {
        await VerifyCS.VerifyCodeFixAsync(
            """
            class C
            {
                async System.Threading.Tasks.Task {|CS1998:MAsync|}()
                {
                    foreach (var n in new int[] { })
                    {
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    foreach (var n in new int[] { })
                    {
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task MethodWithForEachVariableAwait()
    {
        var source =
            """
            class C
            {
                async System.Threading.Tasks.Task MAsync()
                {
                    await foreach (var (a, b) in {|#0:new(int, int)[] { }|})
                    {
                    }
                }
            }
            """;
 
        await VerifyCS.VerifyCodeFixAsync(
            source,
            // /0/Test0.cs(5,38): error CS1061: 'bool' does not contain a definition for 'GetAwaiter' and no accessible extension method 'GetAwaiter' accepting a first argument of type 'bool' could be found (are you missing a using directive or an assembly reference?)
            DiagnosticResult.CompilerError("CS1061").WithLocation(0).WithArguments("bool", "GetAwaiter"),
            source);
    }
 
    [Fact]
    public async Task MethodWithForEachVariableNoAwait()
    {
        await VerifyCS.VerifyCodeFixAsync(
            """
            class C
            {
                async System.Threading.Tasks.Task {|CS1998:MAsync|}()
                {
                    foreach (var (a, b) in new(int, int)[] { })
                    {
                    }
                }
            }
            """,
            """
            class C
            {
                void M()
                {
                    foreach (var (a, b) in new (int, int)[] { })
                    {
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestIAsyncEnumerableReturnType()
    {
        var source =
            """
            using System.Threading.Tasks;
            using System.Collections.Generic;
 
            class C
            {
                async IAsyncEnumerable<int> {|CS1998:MAsync|}()
                {
                    yield return 1;
                }
            }
            """;
        var expected =
            """
            using System.Threading.Tasks;
            using System.Collections.Generic;
 
            class C
            {
                IEnumerable<int> M()
                {
                    yield return 1;
                }
            }
            """;
 
        await new VerifyCS.Test
        {
            ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard21,
            TestCode = source,
            FixedCode = expected,
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestIAsyncEnumeratorReturnTypeOnLocalFunction()
    {
        var source =
            """
            using System.Threading.Tasks;
            using System.Collections.Generic;
 
            class C
            {
                void Method()
                {
                    async IAsyncEnumerator<int> {|CS1998:MAsync|}()
                    {
                        yield return 1;
                    }
                }
            }
            """;
        var expected =
            """
            using System.Threading.Tasks;
            using System.Collections.Generic;
 
            class C
            {
                void Method()
                {
                    IEnumerator<int> M()
                    {
                        yield return 1;
                    }
                }
            }
            """;
 
        await new VerifyCS.Test
        {
            ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard21,
            TestCode = source,
            FixedCode = expected,
        }.RunAsync();
    }
}