dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19k stars 4.03k forks source link

Autocompletion for `return new …` statement differs between `Task` and `ValueTask` methods #64862

Closed jhinder closed 1 year ago

jhinder commented 2 years ago

Version Used: VS 2022 17.3.6

Steps to Reproduce:

  1. Paste the code below into the editor.
  2. Invoke the autocompletion list after each of the two new keywords.
class C
{
    async Task<string> M1Async()
    {
        await Task.CompletedTask;
        return new ;
    }

    async ValueTask<string> M2Async()
    {
        await Task.CompletedTask;
        return new ;
    }
}

Expected Behavior: In both cases string is suggested, as the methods are marked as async.

Actual Behavior:

Accepting the autocompletion of ValueTask<string> leads to error CS4016.

MartyIX commented 1 year ago

Does anybody know which source code file is responsible for the autocompletion of return new please?

Youssef1313 commented 1 year ago

@MartyIX Probably ObjectCreationCompletionProvider, if not, it will be SymbolCompletionProvider

Youssef1313 commented 1 year ago

Root cause might be https://github.com/dotnet/roslyn/issues/30559, but can't confirm without debugging through this.

Youssef1313 commented 1 year ago

This should also work for the following:

using System.Threading.Tasks;
using System.Runtime.CompilerServices;

class C
{
    async MyTask<string> M()
    {
        return new $$
    }
}

[AsyncMethodBuilder(typeof(Task<>))]
struct MyTask<T>
{
}
MartyIX commented 1 year ago

Adding the following test

        [Fact]
        public async Task InAsyncMethodReturnValueTask()
        {
            var markup =
@"using System;
using System.Threading.Tasks;

class Program
{
    async ValueTask<string> M2Async()
    {
        return new $$;
    }
}";
            await VerifyItemExistsAsync(markup, "string");
        }

to src\EditorFeatures\CSharpTest\Completion\CompletionProviders\ObjectCreationCompletionProviderTests.cs and modifying https://github.com/dotnet/roslyn/blob/39485811c4d1b869ed1531f50fdbcc43bd3dfa48/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs#L2025-L2042

with

private ITypeSymbol UnwrapTaskLike(ITypeSymbol type, bool isAsync)
{
    if (isAsync)
    {
        if (type.OriginalDefinition.Equals(this.Compilation.TaskOfTType()) || type.OriginalDefinition.Equals(this.Compilation.ValueTaskOfTType()))
        {
            var namedTypeSymbol = (INamedTypeSymbol)type;
            return namedTypeSymbol.TypeArguments[0];
        }

        if (type.OriginalDefinition.Equals(this.Compilation.TaskType()) || type.OriginalDefinition.Equals(this.Compilation.ValueTaskType()))
        {
            return this.Compilation.GetSpecialType(SpecialType.System_Void);
        }
    }

    return type;
}

does not help because this.Compilation.ValueTaskOfTType() is null and I'm not too sure why. Probably because it is available only in

.NET | Core 1.0, Core 1.1, Core 2.0, Core 2.1, Core 2.2, Core 3.0, Core 3.1, 5, 6, 7 -- | -- .NET Standard | 2.1
Youssef1313 commented 1 year ago

@MartyIX You can use .NET 6 in tests by doing something like this:

https://github.com/dotnet/roslyn/blob/39485811c4d1b869ed1531f50fdbcc43bd3dfa48/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs#L12031-L12042

MartyIX commented 1 year ago

Thanks! I tried that and it seems to work... as far as I understand.