From 804501313b047c79bce27e9d76bb544ba2562f9b Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 5 Jun 2023 16:59:57 -0400 Subject: [PATCH] Add analyzer to detect use of Task.ConfigureAwait with ConfigureAwaitOptions.SuppressThrowing .NET 8 introduces new overloads of ConfigureAwait. One of the options is SuppressThrowing, but it's not supported by `Task`, since that would then lead to returning a possibly invalid T. This rule flags use of SuppressThrowing so that you get a build-time rather than execution-time error. --- .../Core/AnalyzerReleases.Unshipped.md | 1 + .../MicrosoftNetCoreAnalyzersResources.resx | 9 ++ ...DoNotConfigureAwaitWithSuppressThrowing.cs | 72 +++++++++++ .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 15 +++ .../MicrosoftNetCoreAnalyzersResources.de.xlf | 15 +++ .../MicrosoftNetCoreAnalyzersResources.es.xlf | 15 +++ .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 15 +++ .../MicrosoftNetCoreAnalyzersResources.it.xlf | 15 +++ .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 15 +++ .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 15 +++ .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 15 +++ ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 15 +++ .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 15 +++ .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 15 +++ ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 15 +++ ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 15 +++ .../Microsoft.CodeAnalysis.NetAnalyzers.md | 12 ++ .../Microsoft.CodeAnalysis.NetAnalyzers.sarif | 20 +++ src/NetAnalyzers/RulesMissingDocumentation.md | 1 + ...ConfigureAwaitWithSuppressThrowingTests.cs | 119 ++++++++++++++++++ .../DiagnosticCategoryAndIdRanges.txt | 2 +- src/Utilities/Compiler/WellKnownTypeNames.cs | 1 + 22 files changed, 431 insertions(+), 1 deletion(-) create mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Tasks/DoNotConfigureAwaitWithSuppressThrowing.cs create mode 100644 src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Tasks/DoNotUseConfigureAwaitWithSuppressThrowingTests.cs diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index 272ba8e499..0c62bf3ee2 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -4,6 +4,7 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- +CA2261 | Usage | Warning | DoNotUseConfigureAwaitWithSuppressThrowing, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2250) CA1510 | Maintainability | Info | UseExceptionThrowHelpers, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1510) CA1511 | Maintainability | Info | UseExceptionThrowHelpers, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1511) CA1512 | Maintainability | Info | UseExceptionThrowHelpers, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1512) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index ac1d44a9e0..8221a806f4 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1064,6 +1064,15 @@ ValueTask instances should not have their result directly accessed unless the instance has already completed. Unlike Tasks, calling Result or GetAwaiter().GetResult() on a ValueTask is not guaranteed to block until the operation completes. If you can't simply await the instance, consider first checking its IsCompleted property (or asserting it's true if you know that to be the case). + + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + Argument passed to TaskCompletionSource constructor should be TaskCreationOptions enum instead of TaskContinuationOptions enum diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Tasks/DoNotConfigureAwaitWithSuppressThrowing.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Tasks/DoNotConfigureAwaitWithSuppressThrowing.cs new file mode 100644 index 0000000000..af8e736e27 --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Tasks/DoNotConfigureAwaitWithSuppressThrowing.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.NetCore.Analyzers.Tasks +{ + using static MicrosoftNetCoreAnalyzersResources; + + /// + /// CA2261: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class DoNotUseConfigureAwaitWithSuppressThrowing : DiagnosticAnalyzer + { + internal const string RuleId = "CA2261"; + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(RuleId, + CreateLocalizableResourceString(nameof(DoNotUseConfigureAwaitWithSuppressThrowingTitle)), + CreateLocalizableResourceString(nameof(DoNotUseConfigureAwaitWithSuppressThrowingMessage)), + DiagnosticCategory.Usage, + RuleLevel.BuildWarning, + CreateLocalizableResourceString(nameof(DoNotUseConfigureAwaitWithSuppressThrowingDescription)), + isPortedFxCopRule: false, + isDataflowRule: false); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.RegisterCompilationStartAction(compilationContext => + { + // Only proceed if we have the Task.ConfigureAwait(ConfigureAwaitOptions) method and if ConfigureAwaitOptions.SuppressThrowing is defined. + if (!compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask1, out var genericTask) || + !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksConfigureAwaitOptions, out var configureAwaitOptionsType) || + configureAwaitOptionsType.TypeKind != TypeKind.Enum || + genericTask + .GetMembers("ConfigureAwait") + .OfType() + .Where(m => SymbolEqualityComparer.Default.Equals(m.ContainingType, genericTask) && m.Parameters.Length == 1 && SymbolEqualityComparer.Default.Equals(m.Parameters[0].Type, configureAwaitOptionsType)) + .FirstOrDefault() is not IMethodSymbol configureAwait || + configureAwaitOptionsType.GetMembers("SuppressThrowing").FirstOrDefault() is not IFieldSymbol suppressThrowing || + !DiagnosticHelpers.TryConvertToUInt64(suppressThrowing.ConstantValue, configureAwaitOptionsType.EnumUnderlyingType.SpecialType, out ulong suppressThrowingValue)) + { + return; + } + + // Raise a diagnostic if the invocation is to Task.ConfigureAwait with a constant value that includes SuppressThrowing + compilationContext.RegisterOperationAction(operationContext => + { + IInvocationOperation invocation = (IInvocationOperation)operationContext.Operation; + + if (SymbolEqualityComparer.Default.Equals(invocation.TargetMethod.OriginalDefinition, configureAwait) && + invocation.Arguments.Length == 1 && + invocation.Arguments[0].Value is IOperation { ConstantValue.HasValue: true } arg && + DiagnosticHelpers.TryConvertToUInt64(arg.ConstantValue.Value, configureAwaitOptionsType.EnumUnderlyingType.SpecialType, out ulong argValue) && + (argValue & suppressThrowingValue) != 0) + { + operationContext.ReportDiagnostic(arg.CreateDiagnostic(Rule)); + } + }, OperationKind.Invocation); + }); + } + } +} \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf index d7dd5c1b25..acf6084ae2 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -893,6 +893,21 @@ Obecné přetypování (IL unbox.any) používané sekvencí vrácenou metodou E {0} používá prolomený kryptografický algoritmus {1}. + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + + + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + + + + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + + For non-empty collections, CountAsync() and LongCountAsync() enumerate the entire sequence, while AnyAsync() stops at the first item or the first item that satisfies a condition. U neprázdných kolekcí CountAsync() a LongCountAsync() zobrazí výčet celé sekvence, zatímco AnyAsync() se zastaví u první položky nebo u první položky, která splňuje podmínku. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf index 4eb70e2ac9..cc41d843be 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -893,6 +893,21 @@ Erweiterungen und benutzerdefinierte Konvertierungen werden bei generischen Type "{0}" verwendet einen beschädigten kryptografischen Algorithmus: {1} + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + + + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + + + + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + + For non-empty collections, CountAsync() and LongCountAsync() enumerate the entire sequence, while AnyAsync() stops at the first item or the first item that satisfies a condition. Bei nicht leeren Sammlungen listen CountAsync() und LongCountAsync() die gesamte Sequenz auf, während AnyAsync() beim ersten Element oder bei dem ersten Element, das eine Bedingung erfüllt, beendet wird. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf index 69825ae35e..966c49a9d3 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -893,6 +893,21 @@ La ampliación y las conversiones definidas por el usuario no se admiten con tip {0} usa un algoritmo criptográfico dañado {1}. + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + + + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + + + + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + + For non-empty collections, CountAsync() and LongCountAsync() enumerate the entire sequence, while AnyAsync() stops at the first item or the first item that satisfies a condition. Para colecciones no vacías, CountAsync() y LongCountAsync() enumeran la secuencia completa, mientras que AnyAsync() se detiene en el primer elemento o en el primer elemento que satisface una condición. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf index 1c14bb70f8..328acd0091 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -893,6 +893,21 @@ Les conversions étendues et définies par l’utilisateur ne sont pas prises en {0} utilise un algorithme de chiffrement cassé {1} + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + + + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + + + + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + + For non-empty collections, CountAsync() and LongCountAsync() enumerate the entire sequence, while AnyAsync() stops at the first item or the first item that satisfies a condition. Pour les collections non vides, CountAsync() et LongCountAsync() énumèrent la séquence entière, alors que AnyAsync() s'arrête au premier élément ou au premier élément qui satisfait une condition. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf index f8db0d0ed3..6803d69506 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -893,6 +893,21 @@ L'ampliamento e le conversioni definite dall'utente non sono supportate con tipi {0} usa un algoritmo di crittografia violato {1} + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + + + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + + + + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + + For non-empty collections, CountAsync() and LongCountAsync() enumerate the entire sequence, while AnyAsync() stops at the first item or the first item that satisfies a condition. Nel caso di raccolte non vuote CountAsync() e LongCountAsync() enumerano l'intera sequenza, mentre AnyAsync() si arresta in corrispondenza del primo elemento o del primo elemento che soddisfa una condizione. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf index d447a18ca0..1df0c8e4b6 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -893,6 +893,21 @@ Enumerable.OfType<T> で使用されるジェネリック型チェック ( {0} が、破られた暗号アルゴリズム {1} を使用します + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + + + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + + + + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + + For non-empty collections, CountAsync() and LongCountAsync() enumerate the entire sequence, while AnyAsync() stops at the first item or the first item that satisfies a condition. 空ではないコレクションで、CountAsync() と LongCountAsync() ではシーケンス全体が列挙されますが、AnyAsync() では最初の項目または条件を満たす最初の項目で停止します。 diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf index b74bbf2fca..e14b97646e 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -893,6 +893,21 @@ Enumerable.OfType<T>에서 사용하는 제네릭 형식 검사(C# 'is' {0}이(가) 손상된 암호화 알고리즘 {1}을(를) 사용합니다. + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + + + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + + + + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + + For non-empty collections, CountAsync() and LongCountAsync() enumerate the entire sequence, while AnyAsync() stops at the first item or the first item that satisfies a condition. 비어 있지 않은 컬렉션의 경우 CountAsync() 및 LongCountAsync()는 전체 시퀀스를 열거하고, AnyAsync()는 첫 번째 항목 또는 조건을 충족하는 첫 번째 항목에서 중지합니다. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf index bb0b135799..2d799876d4 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -893,6 +893,21 @@ Konwersje poszerzane i zdefiniowane przez użytkownika nie są obsługiwane w pr Element {0} używa złamanych algorytmów kryptograficznych {1} + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + + + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + + + + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + + For non-empty collections, CountAsync() and LongCountAsync() enumerate the entire sequence, while AnyAsync() stops at the first item or the first item that satisfies a condition. W przypadku niepustych kolekcji funkcje CountAsync() i LongCountAsync() wyliczają całą sekwencję, podczas gdy funkcja AnyAsync() zatrzymuje się na pierwszym elemencie lub pierwszym elemencie, który spełnia warunek. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf index 55b8d790a3..3c3d5889fb 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf @@ -893,6 +893,21 @@ Ampliação e conversões definidas pelo usuário não são compatíveis com tip {0} usa um algoritmo de criptografia desfeito {1} + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + + + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + + + + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + + For non-empty collections, CountAsync() and LongCountAsync() enumerate the entire sequence, while AnyAsync() stops at the first item or the first item that satisfies a condition. Para coleções não vazias, CountAsync() e LongCountAsync() enumeram toda a sequência, enquanto AnyAsync() é interrompido no primeiro item ou no primeiro item que satisfaz uma condição. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf index a814efe640..4ecef521c8 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -893,6 +893,21 @@ Widening and user defined conversions are not supported with generic types.{0} использует взломанный алгоритм шифрования {1} + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + + + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + + + + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + + For non-empty collections, CountAsync() and LongCountAsync() enumerate the entire sequence, while AnyAsync() stops at the first item or the first item that satisfies a condition. Для непустых коллекций методы CountAsync() и LongCountAsync() перечисляют всю последовательность, а метод AnyAsync() останавливается на первом элементе или на первом элементе, который соответствует условию. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf index 1cd04abaf6..2a605b30f4 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -893,6 +893,21 @@ Genel türlerde genişletme ve kullanıcı tanımlı dönüştürmeler desteklen {0} bozuk {1} kriptografik algoritmasını kullanıyor + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + + + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + + + + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + + For non-empty collections, CountAsync() and LongCountAsync() enumerate the entire sequence, while AnyAsync() stops at the first item or the first item that satisfies a condition. Boş olmayan koleksiyonlarda CountAsync() ve LongCountAsync() dizinin tamamını numaralandırır. AnyAsync() ise ilk öğede veya koşulu karşılayan ilk öğede durur. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf index 12f87fc5fd..d8ed0db5a1 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf @@ -893,6 +893,21 @@ Enumerable.OfType<T> 使用的泛型类型检查 (C# 'is' operator/IL 'isi {0} 使用损坏的加密算法 {1} + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + + + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + + + + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + + For non-empty collections, CountAsync() and LongCountAsync() enumerate the entire sequence, while AnyAsync() stops at the first item or the first item that satisfies a condition. 对于非空集合,CountAsync() 和 LongCountAsync() 将枚举整个序列,而 AnyAsync() 将在第一项或满足条件的第一项处停止。 diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf index d71c7a0cc6..2f32279a87 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf @@ -893,6 +893,21 @@ Enumerable.OfType<T> 使用的一般型別檢查 (C# 'is' operator/IL 'isi {0} 使用損壞的密碼編譯演算法 {1} + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task<TResult>. To use it with a Task<TResult>, first cast to the base Task. + + + + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task + + + + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult> + + For non-empty collections, CountAsync() and LongCountAsync() enumerate the entire sequence, while AnyAsync() stops at the first item or the first item that satisfies a condition. 對於非空白集合,CountAsync() 和 LongCountAsync() 會列舉整個序列,而 AnyAsync() 則會停止於第一個項目或第一個符合條件的項目。 diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md index 3f0840886b..a56eed0d02 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md @@ -2442,6 +2442,18 @@ Generic math interfaces require the derived type itself to be used for the self |CodeFix|False| --- +## [CA2261](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2261): Do not use ConfigureAwaitOptions.SuppressThrowing with Task\ + +The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task\. To use it with a Task\, first cast to the base Task. + +|Item|Value| +|-|-| +|Category|Usage| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + ## [CA2300](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2300): Do not use insecure deserializer BinaryFormatter The method '{0}' is insecure when deserializing untrusted data. If you need to instead detect BinaryFormatter deserialization without a SerializationBinder set, then disable rule CA2300, and enable rules CA2301 and CA2302. diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif index daf94fe054..ca5ea5c3d6 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif @@ -4241,6 +4241,26 @@ ] } }, + "CA2261": { + "id": "CA2261", + "shortDescription": "Do not use ConfigureAwaitOptions.SuppressThrowing with Task", + "fullDescription": "The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generic Task, not a Task. To use it with a Task, first cast to the base Task.", + "defaultLevel": "warning", + "helpUri": "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2261", + "properties": { + "category": "Usage", + "isEnabledByDefault": true, + "typeName": "DoNotUseConfigureAwaitWithSuppressThrowing", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry", + "EnabledRuleInAggressiveMode" + ] + } + }, "CA2300": { "id": "CA2300", "shortDescription": "Do not use insecure deserializer BinaryFormatter", diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md index 08969ce815..b5055e1207 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -9,3 +9,4 @@ CA1513 | | Incorrect usage of ConstantExpected attribute | CA1857 | | A constant is expected for the parameter | CA2021 | | Do not call Enumerable.Cast\ or Enumerable.OfType\ with incompatible types | +CA2261 | | Do not use ConfigureAwaitOptions.SuppressThrowing with Task\ | diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Tasks/DoNotUseConfigureAwaitWithSuppressThrowingTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Tasks/DoNotUseConfigureAwaitWithSuppressThrowingTests.cs new file mode 100644 index 0000000000..1695217905 --- /dev/null +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Tasks/DoNotUseConfigureAwaitWithSuppressThrowingTests.cs @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.NetCore.Analyzers.Tasks.DoNotUseConfigureAwaitWithSuppressThrowing, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.NetCore.Analyzers.Tasks.UnitTests +{ + public class DoNotUseConfigureAwaitWithSuppressThrowingTests + { + [Fact] + public async Task ValidateCSharp() + { + await VerifyCS.VerifyAnalyzerAsync(""" + using System; + using System.Runtime.CompilerServices; + using System.Threading.Tasks; + + namespace System.Threading.Tasks + { + [Flags] + public enum ConfigureAwaitOptions + { + None = 0, + ContinueOnCapturedContext = 1, + SuppressThrowing = 2, + ForceYielding = 4, + } + + public class Task + { + public ConfiguredTaskAwaitable ConfigureAwait(bool continueOnCapturedContext) => throw null; + public ConfiguredTaskAwaitable ConfigureAwait(ConfigureAwaitOptions options) => throw null; + } + + public class Task : Task + { + public ConfiguredTaskAwaitable ConfigureAwait(bool continueOnCapturedContext) => throw null; + public ConfiguredTaskAwaitable ConfigureAwait(ConfigureAwaitOptions options) => throw null; + } + + public class DerivedGenericTask : Task + { + } + } + + class C + { + public void Test(ConfigureAwaitOptions options) + { + // No diagnostics + Task nonGenericTask = new Task(); + nonGenericTask.ConfigureAwait(false); + nonGenericTask.ConfigureAwait(true); + nonGenericTask.ConfigureAwait(ConfigureAwaitOptions.None); + nonGenericTask.ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + nonGenericTask.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing); + nonGenericTask.ConfigureAwait( ( (( ( ConfigureAwaitOptions.SuppressThrowing )) ) )); + nonGenericTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); + nonGenericTask.ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext | ConfigureAwaitOptions.ForceYielding); + nonGenericTask.ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext | ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.ForceYielding); + nonGenericTask.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.ForceYielding); + nonGenericTask.ConfigureAwait((ConfigureAwaitOptions)0x7); + nonGenericTask.ConfigureAwait(((ConfigureAwaitOptions)0x1) | ((ConfigureAwaitOptions)0x2)); + nonGenericTask.ConfigureAwait(options); + + // Diagnostics when SuppressThrowing is used + Task genericTask = new Task(); + genericTask.ConfigureAwait(false); + genericTask.ConfigureAwait(true); + genericTask.ConfigureAwait(ConfigureAwaitOptions.None); + genericTask.ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + genericTask.ConfigureAwait([|ConfigureAwaitOptions.SuppressThrowing|]); + genericTask.ConfigureAwait( ( (( ( [|ConfigureAwaitOptions.SuppressThrowing|] )) ) )); + genericTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); + genericTask.ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext | ConfigureAwaitOptions.ForceYielding); + genericTask.ConfigureAwait([|ConfigureAwaitOptions.ContinueOnCapturedContext | ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.ForceYielding|]); + genericTask.ConfigureAwait([|ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.ForceYielding|]); + genericTask.ConfigureAwait([|(ConfigureAwaitOptions)0x7|]); + genericTask.ConfigureAwait([|((ConfigureAwaitOptions)0x1) | ((ConfigureAwaitOptions)0x2)|]); + genericTask.ConfigureAwait(options); + + DerivedGenericTask derivedGenericTask = new DerivedGenericTask(); + derivedGenericTask.ConfigureAwait(false); + derivedGenericTask.ConfigureAwait(true); + derivedGenericTask.ConfigureAwait(ConfigureAwaitOptions.None); + derivedGenericTask.ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + derivedGenericTask.ConfigureAwait([|ConfigureAwaitOptions.SuppressThrowing|]); + derivedGenericTask.ConfigureAwait( ( (( ( [|ConfigureAwaitOptions.SuppressThrowing|] )) ) )); + derivedGenericTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); + derivedGenericTask.ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext | ConfigureAwaitOptions.ForceYielding); + derivedGenericTask.ConfigureAwait([|ConfigureAwaitOptions.ContinueOnCapturedContext | ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.ForceYielding|]); + derivedGenericTask.ConfigureAwait([|ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.ForceYielding|]); + derivedGenericTask.ConfigureAwait([|(ConfigureAwaitOptions)0x7|]); + derivedGenericTask.ConfigureAwait([|((ConfigureAwaitOptions)0x1) | ((ConfigureAwaitOptions)0x2)|]); + derivedGenericTask.ConfigureAwait(options); + + // No diagnostics + ((Task)genericTask).ConfigureAwait(false); + ((Task)genericTask).ConfigureAwait(true); + ((Task)genericTask).ConfigureAwait(ConfigureAwaitOptions.None); + ((Task)genericTask).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + ((Task)genericTask).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing); + ((Task)genericTask).ConfigureAwait( ( (( ( ConfigureAwaitOptions.SuppressThrowing )) ) )); + ((Task)genericTask).ConfigureAwait(ConfigureAwaitOptions.ForceYielding); + ((Task)genericTask).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext | ConfigureAwaitOptions.ForceYielding); + ((Task)genericTask).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext | ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.ForceYielding); + ((Task)genericTask).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.ForceYielding); + ((Task)genericTask).ConfigureAwait((ConfigureAwaitOptions)0x7); + ((Task)genericTask).ConfigureAwait(((ConfigureAwaitOptions)0x1) | ((ConfigureAwaitOptions)0x2)); + ((Task)genericTask).ConfigureAwait(options); + } + } + """); + } + } +} diff --git a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt index 43ad39c7a9..13cb1cd3a4 100644 --- a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt +++ b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt @@ -14,7 +14,7 @@ Globalization: CA2101, CA1300-CA1311 Mobility: CA1600-CA1601 Performance: HA, CA1800-CA1861 Security: CA2100-CA2153, CA2300-CA2330, CA3000-CA3147, CA5300-CA5405 -Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2260 +Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2261 Naming: CA1700-CA1727 Interoperability: CA1400-CA1422 Maintainability: CA1500-CA1513 diff --git a/src/Utilities/Compiler/WellKnownTypeNames.cs b/src/Utilities/Compiler/WellKnownTypeNames.cs index bd800d4ee3..6a734c431f 100644 --- a/src/Utilities/Compiler/WellKnownTypeNames.cs +++ b/src/Utilities/Compiler/WellKnownTypeNames.cs @@ -401,6 +401,7 @@ internal static class WellKnownTypeNames public const string SystemThreadingInterlocked = "System.Threading.Interlocked"; public const string SystemThreadingMonitor = "System.Threading.Monitor"; public const string SystemThreadingSpinLock = "System.Threading.SpinLock"; + public const string SystemThreadingTasksConfigureAwaitOptions = "System.Threading.Tasks.ConfigureAwaitOptions"; public const string SystemThreadingTasksTask = "System.Threading.Tasks.Task"; public const string SystemThreadingTasksTask1 = "System.Threading.Tasks.Task`1"; public const string SystemThreadingTasksTaskCompletionSource = "System.Threading.Tasks.TaskCompletionSource";