Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly update return type when updating method. #76157

Merged
merged 5 commits into from
Nov 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Completion.Providers;
Expand All @@ -14,32 +15,33 @@
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations;

/// <summary>
/// The <see cref="AwaitCompletionProvider"/> adds async modifier if the return type is Task or ValueTask.
/// The tests here are only checking whether the completion item is provided or not.
/// Tests for checking adding async modifier are in:
/// src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_AwaitCompletion.vb
/// The <see cref="AwaitCompletionProvider"/> adds async modifier if the return type is Task or ValueTask. The tests
/// here are only checking whether the completion item is provided or not. Tests for checking adding async modifier are
/// in: src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_AwaitCompletion.vb
/// </summary>
[Trait(Traits.Feature, Traits.Features.Completion)]
public class AwaitCompletionProviderTests : AbstractCSharpCompletionProviderTests
public sealed class AwaitCompletionProviderTests : AbstractCSharpCompletionProviderTests
{
internal override Type GetCompletionProviderType() => typeof(AwaitCompletionProvider);

private const string CompletionDisplayTextAwait = "await";
private const string CompletionDisplayTextAwaitAndConfigureAwait = "awaitf";

private async Task VerifyAbsenceAsync(string code)
private async Task VerifyAbsenceAsync([StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string code)
{
await VerifyItemIsAbsentAsync(code, CompletionDisplayTextAwait);
await VerifyItemIsAbsentAsync(code, CompletionDisplayTextAwaitAndConfigureAwait);
}

private async Task VerifyAbsenceAsync(string code, LanguageVersion languageVersion = LanguageVersion.Default)
private async Task VerifyAbsenceAsync(
[StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string code, LanguageVersion languageVersion = LanguageVersion.Default)
{
await VerifyItemIsAbsentAsync(GetMarkup(code, languageVersion), CompletionDisplayTextAwait);
await VerifyItemIsAbsentAsync(GetMarkup(code, languageVersion), CompletionDisplayTextAwaitAndConfigureAwait);
}

private async Task VerifyKeywordAsync(string code, LanguageVersion languageVersion = LanguageVersion.Default, string? inlineDescription = null, bool dotAwait = false, bool dotAwaitf = false)
private async Task VerifyKeywordAsync(
[StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string code, LanguageVersion languageVersion = LanguageVersion.Default, string? inlineDescription = null, bool dotAwait = false, bool dotAwaitf = false)
{
var expectedDescription = dotAwait
? GetDescription(CompletionDisplayTextAwait, FeaturesResources.Await_the_preceding_expression)
Expand Down Expand Up @@ -351,24 +353,24 @@ async Task F(Task<int> someTask)
public async Task TestDotAwaitSuggestAfterDotOnValueTask()
{
var valueTaskAssembly = typeof(ValueTask).Assembly.Location;
var markup = @$"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<MetadataReference>{valueTaskAssembly}</MetadataReference>
<Document FilePath=""Test2.cs"">
using System.Threading.Tasks;
var markup = $$"""
<Workspace>
<Project Language="C#" AssemblyName="Assembly1" CommonReferences="true">
<MetadataReference>{{valueTaskAssembly}}</MetadataReference>
<Document FilePath="Test2.cs">
using System.Threading.Tasks;

class C
{{
async Task F(ValueTask someTask)
{{
someTask.$$
}}
}}
</Document>
</Project>
</Workspace>
";
class C
{
async Task F(ValueTask someTask)
{
someTask.$$
}
}
</Document>
</Project>
</Workspace>
""";
await VerifyItemExistsAsync(markup, "await");
await VerifyItemExistsAsync(markup, "awaitf");
}
Expand Down Expand Up @@ -499,18 +501,19 @@ async Task Test() { }
[InlineData("async Task<System.Int32> Test() => await Task.FromResult(1);")]
public async Task TestDotAwaitSuggestAfterDotBeforeDifferentStatements(string statement)
{
await VerifyKeywordAsync($@"
using System;
using System.Threading.Tasks;
await VerifyKeywordAsync($$"""
using System;
using System.Threading.Tasks;

static class Program
{{
static async Task Main(Task someTask)
{{
someTask.$$
{statement}
}}
}}", dotAwait: true, dotAwaitf: true);
static class Program
{
static async Task Main(Task someTask)
{
someTask.$$
{{statement}}
}
}
""", dotAwait: true, dotAwaitf: true);
}

[Theory]
Expand Down Expand Up @@ -546,38 +549,39 @@ static async Task Main(Task someTask)
[InlineData("(null ?? Task.CompletedTask).$$")]
public async Task TestDotAwaitSuggestAfterDifferentExpressions(string expression)
{
await VerifyKeywordAsync($@"
using System;
using System.Threading.Tasks;
await VerifyKeywordAsync($$"""
using System;
using System.Threading.Tasks;

class C
{{
public C Self => this;
public Task Field = Task.CompletedTask;
public Task Method() => Task.CompletedTask;
public Task Property => Task.CompletedTask;
public Task this[int i] => Task.CompletedTask;
public Func<Task> Function() => () => Task.CompletedTask;
public static Task operator +(C left, C right) => Task.CompletedTask;
public static explicit operator Task(C c) => Task.CompletedTask;
}}
class C
{
public C Self => this;
public Task Field = Task.CompletedTask;
public Task Method() => Task.CompletedTask;
public Task Property => Task.CompletedTask;
public Task this[int i] => Task.CompletedTask;
public Func<Task> Function() => () => Task.CompletedTask;
public static Task operator +(C left, C right) => Task.CompletedTask;
public static explicit operator Task(C c) => Task.CompletedTask;
}

static class Program
{{
static Task StaticField = Task.CompletedTask;
static Task StaticProperty => Task.CompletedTask;
static Task StaticMethod() => Task.CompletedTask;
static class Program
{
static Task StaticField = Task.CompletedTask;
static Task StaticProperty => Task.CompletedTask;
static Task StaticMethod() => Task.CompletedTask;

static async Task Main(Task parameter)
{{
Task local = Task.CompletedTask;
var c = new C();
static async Task Main(Task parameter)
{
Task local = Task.CompletedTask;
var c = new C();

{expression}
{{expression}}

Task LocalFunction() => Task.CompletedTask;
}}
}}", dotAwait: true, dotAwaitf: true);
Task LocalFunction() => Task.CompletedTask;
}
}
""", dotAwait: true, dotAwaitf: true);
}

[Fact(Skip = "Fails because speculative binding can't figure out that local is a Task.")]
Expand Down Expand Up @@ -618,17 +622,18 @@ static async Task Main()
[InlineData("await Task.Run(() => someTask.$$")]
public async Task TestDotAwaitSuggestInLambdas(string lambda)
{
await VerifyKeywordAsync($@"
using System.Threading.Tasks;
await VerifyKeywordAsync($$"""
using System.Threading.Tasks;

static class Program
{{
static async Task Main()
{{
var someTask = Task.CompletedTask;
{lambda}
}}
}}", dotAwait: true, dotAwaitf: true);
static class Program
{
static async Task Main()
{
var someTask = Task.CompletedTask;
{{lambda}}
}
}
""", dotAwait: true, dotAwaitf: true);
}

[Fact]
Expand Down Expand Up @@ -854,25 +859,25 @@ async Task F(Task someTask)
[InlineData("new C().M()?.Pro.M()?.M().SomeTask.$$")]
public async Task TestDotAwaitNotAfterDotInConditionalAccessChain(string conditionalAccess)
{
await VerifyAbsenceAsync($@"
using System.Threading.Tasks;
public class C
{{
public Task SomeTask => Task.CompletedTask;
public C Pro => this;
public C M() => this;
}}

static class Program
{{
public static async Task Main()
{{
var c = new C();
{conditionalAccess}
}}
}}
");
await VerifyAbsenceAsync($$"""
using System.Threading.Tasks;
public class C
{
public Task SomeTask => Task.CompletedTask;

public C Pro => this;
public C M() => this;
}

static class Program
{
public static async Task Main()
{
var c = new C();
{{conditionalAccess}}
}
}
""");
}

[Theory]
Expand All @@ -895,41 +900,42 @@ public static async Task Main()
[InlineData("new C().M()!.Pro.M()!.M().SomeTask.$$")]
public async Task TestDotAwaitAfterNullForgivingOperatorAccessChain(string nullForgivingAccess)
{
await VerifyKeywordAsync($@"
#nullable enable
await VerifyKeywordAsync($$"""
#nullable enable

using System.Threading.Tasks;
public class C
{{
public Task? SomeTask => Task.CompletedTask;
public C? Pro => this;
public C? M() => this;
}}

static class Program
{{
public static async Task Main(params string[] args)
{{
var c = args[1] == string.Empty ? new C() : null;
{nullForgivingAccess}
}}
}}
", dotAwait: true, dotAwaitf: true);
using System.Threading.Tasks;
public class C
{
public Task? SomeTask => Task.CompletedTask;

public C? Pro => this;
public C? M() => this;
}

static class Program
{
public static async Task Main(params string[] args)
{
var c = args[1] == string.Empty ? new C() : null;
{{nullForgivingAccess}}
}
}
""", dotAwait: true, dotAwaitf: true);
}

[Theory, CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/58921")]
public async Task TestInCastExpressionThatMightBeParenthesizedExpression(bool hasNewline)
{
var code = $@"
class C
{{
void M()
{{
var data = (n$$) {(hasNewline ? Environment.NewLine : string.Empty)} M();
}}
}}";
var code = $$"""
class C
{
void M()
{
var data = (n$$) {{(hasNewline ? Environment.NewLine : string.Empty)}} M();
}
}
""";
if (hasNewline)
await VerifyKeywordAsync(code);
else
Expand Down
Loading
Loading