diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Tasks/UseValueTasksCorrectly.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Tasks/UseValueTasksCorrectly.cs index 43b685922e..a4fa716a94 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Tasks/UseValueTasksCorrectly.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Tasks/UseValueTasksCorrectly.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -131,6 +132,16 @@ public sealed override void Initialize(AnalysisContext context) // ConfigureAwait returns another awaitable. Use that one instead for subsequent analysis. operation = invocation = parentIo; break; + default: + var additionalMethods = + operationContext.Options.GetStringOptionValue( + EditorConfigOptionNames.AdditionalValidValueTaskConsumption, GeneralRule, + operation.Syntax.SyntaxTree, operationContext.Compilation) + .Split(['|'], StringSplitOptions.RemoveEmptyEntries) + .ToImmutableArray(); + if (additionalMethods.Contains(parentIo.TargetMethod.Name)) + return; + break; } } else if (invocation.Parent is IPropertyReferenceOperation { Property.Name: nameof(ValueTask.Result) }) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Tasks/UseValueTaskCorrectlyTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Tasks/UseValueTaskCorrectlyTests.cs index 704d9da91c..e5855ea32c 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Tasks/UseValueTaskCorrectlyTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Tasks/UseValueTaskCorrectlyTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. using System.Collections.Generic; using System.Threading.Tasks; @@ -1371,6 +1371,41 @@ public async Task MultipleAwaitsAcrossDifferingConditions(bool condition1, bo ); } + [Theory] + [InlineData("dotnet_code_quality.additional_valid_valuetask_consumption= KeepContext")] + public async Task NoDiagnostics_AdditionalMembers_VBAsync(string editorConfigText) + { + var test = new VerifyVB.Test + { + TestState = + { + Sources = + { + VBBoilerplate(""" + Imports System + Imports System.Threading.Tasks + + Class C + Public Async Sub DontConsume() + Await Helpers.ReturnsValueTask().KeepContext() + End Sub + End Class + Module AsyncHelpers + + Public Function KeepContext(vt As ValueTask) As System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable + Return vt.ConfigureAwait(True) + End Function + End Module + """) + }, + AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true +[*] +{editorConfigText}") } + } + }; + await test.RunAsync(); + } + #endregion private static DiagnosticResult GetCSharpResultAt(int line, int column, DiagnosticDescriptor rule) => diff --git a/src/Utilities/Compiler/Options/EditorConfigOptionNames.cs b/src/Utilities/Compiler/Options/EditorConfigOptionNames.cs index 1128cfe22f..64b5135b89 100644 --- a/src/Utilities/Compiler/Options/EditorConfigOptionNames.cs +++ b/src/Utilities/Compiler/Options/EditorConfigOptionNames.cs @@ -234,5 +234,10 @@ internal static partial class EditorConfigOptionNames /// Boolean option whether to perform the analysis even if the assembly exposes its internals. /// public const string IgnoreInternalsVisibleTo = "ignore_internalsvisibleto"; + + /// + /// String option to configure names of additional safe value task consumptions (separated by '|') for CA2012. + /// + public const string AdditionalValidValueTaskConsumption = "additional_valid_valuetask_consumption"; } }