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

Commit 88e1d22

Browse files
authored
Merge pull request #70 from sharwell/support-valuetask
Treat ValueTask<TResult> as an asynchronous return type
2 parents b5fb2d7 + ca7bcad commit 88e1d22

File tree

6 files changed

+118
-1
lines changed

6 files changed

+118
-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: 23 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,25 @@ 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().Single(x => x.GetName().Name == "System.Threading.Tasks");
39+
SystemThreadingTasksReference = MetadataReference.CreateFromFile(systemThreadingTasks.Location);
40+
41+
SystemThreadingTasksExtensionsReference = MetadataReference.CreateFromFile(typeof(ValueTask<>).Assembly.Location);
42+
}
43+
}
2144
}
2245
}

AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Naming/AvoidAsyncSuffixUnitTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,36 @@ 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 FirstMethod() { return default(ValueTask); }
351+
ValueTask SecondMethodAsync() { return default(ValueTask); }
352+
}
353+
";
354+
355+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
356+
}
357+
358+
[Fact]
359+
public async Task TestReturnGenericValueTaskAsync()
360+
{
361+
string testCode = @"
362+
using System.Threading.Tasks;
363+
class ClassName
364+
{
365+
ValueTask<int> FirstMethod() { return new ValueTask<int>(3); }
366+
ValueTask<int> SecondMethodAsync() { return new ValueTask<int>(3); }
367+
}
368+
";
369+
370+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
371+
}
372+
343373
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
344374
{
345375
yield return new AvoidAsyncSuffixAnalyzer();

AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Naming/UseAsyncSuffixUnitTests.cs

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

349+
[Fact]
350+
public async Task TestReturnValueTaskAsync()
351+
{
352+
string testCode = @"
353+
using System.Threading.Tasks;
354+
class ClassName
355+
{
356+
ValueTask FirstMethod() { return default(ValueTask); }
357+
ValueTask SecondMethodAsync() { return default(ValueTask); }
358+
}
359+
";
360+
string fixedCode = @"
361+
using System.Threading.Tasks;
362+
class ClassName
363+
{
364+
ValueTask FirstMethodAsync() { return default(ValueTask); }
365+
ValueTask SecondMethodAsync() { return default(ValueTask); }
366+
}
367+
";
368+
369+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("FirstMethod").WithLocation(5, 15);
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+
375+
[Fact]
376+
public async Task TestReturnGenericValueTaskAsync()
377+
{
378+
string testCode = @"
379+
using System.Threading.Tasks;
380+
class ClassName
381+
{
382+
ValueTask<int> FirstMethod() { return new ValueTask<int>(3); }
383+
ValueTask<int> SecondMethodAsync() { return new ValueTask<int>(3); }
384+
}
385+
";
386+
string fixedCode = @"
387+
using System.Threading.Tasks;
388+
class ClassName
389+
{
390+
ValueTask<int> FirstMethodAsync() { return new ValueTask<int>(3); }
391+
ValueTask<int> SecondMethodAsync() { return new ValueTask<int>(3); }
392+
}
393+
";
394+
395+
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("FirstMethod").WithLocation(5, 20);
396+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
397+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
398+
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
399+
}
400+
349401
[Fact]
350402
public async Task TestPropertyGetterAndSetterTaskAsync()
351403
{

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)