Skip to content
This repository was archived by the owner on Nov 8, 2018. It is now read-only.

Commit 86945af

Browse files
committed
Treat ValueTask<TResult> as an asynchronous return type
Fixes #68
1 parent 68e64c0 commit 86945af

File tree

6 files changed

+84
-1
lines changed

6 files changed

+84
-1
lines changed

AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/AsyncUsageAnalyzers.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
</PropertyGroup>
1616

1717
<ItemGroup>
18+
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.0" />
1819
<PackageReference Include="Microsoft.CodeAnalysis" Version="1.2.1" />
1920
<PackageReference Include="xunit" Version="2.3.0-beta3-build3705" />
2021
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta3-build3705" PrivateAssets="all" />

AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Helpers/DiagnosticVerifier.Helper.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,16 @@ protected virtual Solution CreateSolution(ProjectId projectId, string language)
122122
.AddMetadataReference(projectId, MetadataReferences.CSharpSymbolsReference)
123123
.AddMetadataReference(projectId, MetadataReferences.CodeAnalysisReference);
124124

125+
if (MetadataReferences.SystemThreadingTasksReference != null)
126+
{
127+
solution = solution.AddMetadataReference(projectId, MetadataReferences.SystemThreadingTasksReference);
128+
}
129+
130+
if (MetadataReferences.SystemThreadingTasksExtensionsReference != null)
131+
{
132+
solution = solution.AddMetadataReference(projectId, MetadataReferences.SystemThreadingTasksExtensionsReference);
133+
}
134+
125135
var settings = this.GetSettings();
126136
if (!string.IsNullOrEmpty(settings))
127137
{

AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Helpers/MetadataReferences.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33

44
namespace AsyncUsageAnalyzers.Test.Helpers
55
{
6+
using System;
67
using System.Collections.Immutable;
78
using System.Linq;
9+
using System.Reflection;
10+
using System.Threading.Tasks;
811
using Microsoft.CodeAnalysis;
912
using Microsoft.CodeAnalysis.CSharp;
1013

@@ -18,5 +21,32 @@ internal static class MetadataReferences
1821
internal static readonly MetadataReference SystemCoreReference = MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location);
1922
internal static readonly MetadataReference CSharpSymbolsReference = MetadataReference.CreateFromFile(typeof(CSharpCompilation).Assembly.Location);
2023
internal static readonly MetadataReference CodeAnalysisReference = MetadataReference.CreateFromFile(typeof(Compilation).Assembly.Location);
24+
25+
internal static readonly MetadataReference SystemThreadingTasksReference;
26+
internal static readonly MetadataReference SystemThreadingTasksExtensionsReference;
27+
28+
static MetadataReferences()
29+
{
30+
if (typeof(ValueTask<>).Assembly == typeof(string).Assembly)
31+
{
32+
// mscorlib contains ValueTask<TResult>, so no need to add a separate reference
33+
SystemThreadingTasksReference = null;
34+
SystemThreadingTasksExtensionsReference = null;
35+
}
36+
else
37+
{
38+
Assembly systemThreadingTasks = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(x => x.GetName().Name == "System.Threading.Tasks");
39+
if (systemThreadingTasks != null)
40+
{
41+
SystemThreadingTasksReference = MetadataReference.CreateFromFile(systemThreadingTasks.Location);
42+
}
43+
44+
Assembly systemThreadingTasksExtensions = typeof(ValueTask<>).Assembly;
45+
if (systemThreadingTasksExtensions != null)
46+
{
47+
SystemThreadingTasksExtensionsReference = MetadataReference.CreateFromFile(systemThreadingTasksExtensions.Location);
48+
}
49+
}
50+
}
2151
}
2252
}

AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Naming/AvoidAsyncSuffixUnitTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,21 @@ class ClassName
340340
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
341341
}
342342

343+
[Fact]
344+
public async Task TestReturnValueTaskAsync()
345+
{
346+
string testCode = @"
347+
using System.Threading.Tasks;
348+
class ClassName
349+
{
350+
ValueTask<int> FirstMethod() { return new ValueTask<int>(3); }
351+
ValueTask<int> SecondMethodAsync() { return new ValueTask<int>(3); }
352+
}
353+
";
354+
355+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
356+
}
357+
343358
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
344359
{
345360
yield return new AvoidAsyncSuffixAnalyzer();

AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Naming/UseAsyncSuffixUnitTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,32 @@ class ClassName
346346
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
347347
}
348348

349+
[Fact]
350+
public async Task TestReturnGenericValueTaskAsync()
351+
{
352+
string testCode = @"
353+
using System.Threading.Tasks;
354+
class ClassName
355+
{
356+
ValueTask<int> FirstMethod() { return new ValueTask<int>(3); }
357+
ValueTask<int> SecondMethodAsync() { return new ValueTask<int>(3); }
358+
}
359+
";
360+
string fixedCode = @"
361+
using System.Threading.Tasks;
362+
class ClassName
363+
{
364+
ValueTask<int> FirstMethodAsync() { return new ValueTask<int>(3); }
365+
ValueTask<int> SecondMethodAsync() { return new ValueTask<int>(3); }
366+
}
367+
";
368+
369+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("FirstMethod").WithLocation(5, 20);
370+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
371+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
372+
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
373+
}
374+
349375
[Fact]
350376
public async Task TestPropertyGetterAndSetterTaskAsync()
351377
{

AsyncUsageAnalyzers/AsyncUsageAnalyzers/Helpers/MethodSymbolExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ public static bool HasAsyncSignature(this IMethodSymbol symbol, bool treatAsyncV
2525
if (!symbol.IsAsync)
2626
{
2727
// This check conveniently covers Task and Task<T> by ignoring the `1 in Task<T>.
28-
if (!string.Equals(nameof(Task), symbol.ReturnType?.Name, StringComparison.Ordinal))
28+
if (!string.Equals(nameof(Task), symbol.ReturnType?.Name, StringComparison.Ordinal)
29+
&& !string.Equals("ValueTask", symbol.ReturnType?.Name, StringComparison.Ordinal))
2930
{
3031
return false;
3132
}

0 commit comments

Comments
 (0)