diff --git a/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/AsyncUsageAnalyzers.Test.csproj b/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/AsyncUsageAnalyzers.Test.csproj index 22f9458..ca10552 100644 --- a/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/AsyncUsageAnalyzers.Test.csproj +++ b/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/AsyncUsageAnalyzers.Test.csproj @@ -15,6 +15,7 @@ + diff --git a/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Helpers/DiagnosticVerifier.Helper.cs b/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Helpers/DiagnosticVerifier.Helper.cs index 11f0f8c..b940a06 100644 --- a/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Helpers/DiagnosticVerifier.Helper.cs +++ b/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Helpers/DiagnosticVerifier.Helper.cs @@ -122,6 +122,16 @@ protected virtual Solution CreateSolution(ProjectId projectId, string language) .AddMetadataReference(projectId, MetadataReferences.CSharpSymbolsReference) .AddMetadataReference(projectId, MetadataReferences.CodeAnalysisReference); + if (MetadataReferences.SystemThreadingTasksReference != null) + { + solution = solution.AddMetadataReference(projectId, MetadataReferences.SystemThreadingTasksReference); + } + + if (MetadataReferences.SystemThreadingTasksExtensionsReference != null) + { + solution = solution.AddMetadataReference(projectId, MetadataReferences.SystemThreadingTasksExtensionsReference); + } + var settings = this.GetSettings(); if (!string.IsNullOrEmpty(settings)) { diff --git a/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Helpers/MetadataReferences.cs b/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Helpers/MetadataReferences.cs index d229114..a3fd60f 100644 --- a/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Helpers/MetadataReferences.cs +++ b/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Helpers/MetadataReferences.cs @@ -3,8 +3,11 @@ namespace AsyncUsageAnalyzers.Test.Helpers { + using System; using System.Collections.Immutable; using System.Linq; + using System.Reflection; + using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -18,5 +21,32 @@ internal static class MetadataReferences internal static readonly MetadataReference SystemCoreReference = MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location); internal static readonly MetadataReference CSharpSymbolsReference = MetadataReference.CreateFromFile(typeof(CSharpCompilation).Assembly.Location); internal static readonly MetadataReference CodeAnalysisReference = MetadataReference.CreateFromFile(typeof(Compilation).Assembly.Location); + + internal static readonly MetadataReference SystemThreadingTasksReference; + internal static readonly MetadataReference SystemThreadingTasksExtensionsReference; + + static MetadataReferences() + { + if (typeof(ValueTask<>).Assembly == typeof(string).Assembly) + { + // mscorlib contains ValueTask, so no need to add a separate reference + SystemThreadingTasksReference = null; + SystemThreadingTasksExtensionsReference = null; + } + else + { + Assembly systemThreadingTasks = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(x => x.GetName().Name == "System.Threading.Tasks"); + if (systemThreadingTasks != null) + { + SystemThreadingTasksReference = MetadataReference.CreateFromFile(systemThreadingTasks.Location); + } + + Assembly systemThreadingTasksExtensions = typeof(ValueTask<>).Assembly; + if (systemThreadingTasksExtensions != null) + { + SystemThreadingTasksExtensionsReference = MetadataReference.CreateFromFile(systemThreadingTasksExtensions.Location); + } + } + } } } diff --git a/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Naming/AvoidAsyncSuffixUnitTests.cs b/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Naming/AvoidAsyncSuffixUnitTests.cs index d84b86e..a5bb962 100644 --- a/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Naming/AvoidAsyncSuffixUnitTests.cs +++ b/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Naming/AvoidAsyncSuffixUnitTests.cs @@ -340,6 +340,21 @@ class ClassName await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); } + [Fact] + public async Task TestReturnValueTaskAsync() + { + string testCode = @" +using System.Threading.Tasks; +class ClassName +{ + ValueTask FirstMethod() { return new ValueTask(3); } + ValueTask SecondMethodAsync() { return new ValueTask(3); } +} +"; + + await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } + protected override IEnumerable GetCSharpDiagnosticAnalyzers() { yield return new AvoidAsyncSuffixAnalyzer(); diff --git a/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Naming/UseAsyncSuffixUnitTests.cs b/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Naming/UseAsyncSuffixUnitTests.cs index 8142b75..c1ee24d 100644 --- a/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Naming/UseAsyncSuffixUnitTests.cs +++ b/AsyncUsageAnalyzers/AsyncUsageAnalyzers.Test/Naming/UseAsyncSuffixUnitTests.cs @@ -346,6 +346,32 @@ class ClassName await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); } + [Fact] + public async Task TestReturnGenericValueTaskAsync() + { + string testCode = @" +using System.Threading.Tasks; +class ClassName +{ + ValueTask FirstMethod() { return new ValueTask(3); } + ValueTask SecondMethodAsync() { return new ValueTask(3); } +} +"; + string fixedCode = @" +using System.Threading.Tasks; +class ClassName +{ + ValueTask FirstMethodAsync() { return new ValueTask(3); } + ValueTask SecondMethodAsync() { return new ValueTask(3); } +} +"; + + DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("FirstMethod").WithLocation(5, 20); + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + [Fact] public async Task TestPropertyGetterAndSetterTaskAsync() { diff --git a/AsyncUsageAnalyzers/AsyncUsageAnalyzers/Helpers/MethodSymbolExtensions.cs b/AsyncUsageAnalyzers/AsyncUsageAnalyzers/Helpers/MethodSymbolExtensions.cs index 3cf4bd4..b3d10f8 100644 --- a/AsyncUsageAnalyzers/AsyncUsageAnalyzers/Helpers/MethodSymbolExtensions.cs +++ b/AsyncUsageAnalyzers/AsyncUsageAnalyzers/Helpers/MethodSymbolExtensions.cs @@ -25,7 +25,8 @@ public static bool HasAsyncSignature(this IMethodSymbol symbol, bool treatAsyncV if (!symbol.IsAsync) { // This check conveniently covers Task and Task by ignoring the `1 in Task. - if (!string.Equals(nameof(Task), symbol.ReturnType?.Name, StringComparison.Ordinal)) + if (!string.Equals(nameof(Task), symbol.ReturnType?.Name, StringComparison.Ordinal) + && !string.Equals("ValueTask", symbol.ReturnType?.Name, StringComparison.Ordinal)) { return false; }