From 5447dc575a494653b82d2a8231ca71766d8734b8 Mon Sep 17 00:00:00 2001 From: NewellClark Date: Tue, 16 Feb 2021 12:34:47 -0500 Subject: [PATCH 01/15] Add files --- .../Core/AnalyzerReleases.Unshipped.md | 5 +++ .../MicrosoftNetCoreAnalyzersResources.resx | 10 +++++ ...TokenThrowIfCancellationRequested.Fixer.cs | 23 ++++++++++ ...lationTokenThrowIfCancellationRequested.cs | 44 +++++++++++++++++++ ...nTokenThrowIfCancellationRequestedTests.cs | 18 ++++++++ .../DiagnosticCategoryAndIdRanges.txt | 2 +- 6 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs create mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs create mode 100644 src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index 64372afc0b..408b980b1b 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -1,5 +1,10 @@ ; Please do not edit this file manually, it should only be updated through code fix application. +### New Rules +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +CA2250 | Usage | Info | UseCancellationTokenThrowIfCancellationRequested, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2250) + ### Removed Rules Rule ID | Category | Severity | Notes diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 0b8e62a9ba..97d797dc63 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1510,4 +1510,14 @@ and all other platforms This call site is reachable on: 'windows' 10.0.2000 and later, and all other platforms + + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + + + Consider calling '{0}' instead of checking '{1}' + 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + + + Use '{0}' + \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs new file mode 100644 index 0000000000..0c37b2a586 --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; + +namespace Microsoft.NetCore.Analyzers.Runtime +{ + [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class UseCancellationTokenThrowIfCancellationRequestedFixer : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(UseCancellationTokenThrowIfCancellationRequested.RuleId); + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + return Task.CompletedTask; + } + } +} diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs new file mode 100644 index 0000000000..5073ac0659 --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Text; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Resx = Microsoft.NetCore.Analyzers.MicrosoftNetCoreAnalyzersResources; + +namespace Microsoft.NetCore.Analyzers.Runtime +{ + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class UseCancellationTokenThrowIfCancellationRequested : DiagnosticAnalyzer + { + internal const string RuleId = "CA2250"; + + private static readonly LocalizableString s_localizableTitle = CreateResource(nameof(Resx.UseCancellationTokenThrowIfCancellationRequestedTitle)); + private static readonly LocalizableString s_localizableMessage = CreateResource(nameof(Resx.UseCancellationTokenThrowIfCancellationRequestedMessage)); + private static readonly LocalizableString s_localizableDescription = CreateResource(nameof(Resx.UseCancellationTokenThrowIfCancellationRequestedDescription)); + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + s_localizableMessage, + DiagnosticCategory.Usage, + RuleLevel.IdeSuggestion, + s_localizableDescription, + isPortedFxCopRule: false, + isDataflowRule: false); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + } + + private static LocalizableString CreateResource(string resourceName) + { + return new LocalizableResourceString(resourceName, Resx.ResourceManager, typeof(Resx)); + } + } +} diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs new file mode 100644 index 0000000000..6899c1b664 --- /dev/null +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using Xunit; + +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.NetCore.Analyzers.Runtime.UseCancellationTokenThrowIfCancellationRequested, + Microsoft.NetCore.Analyzers.Runtime.UseCancellationTokenThrowIfCancellationRequestedFixer>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.NetCore.Analyzers.Runtime.UseCancellationTokenThrowIfCancellationRequested, + Microsoft.NetCore.Analyzers.Runtime.UseCancellationTokenThrowIfCancellationRequestedFixer>; + +namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests +{ + public class UseCancellationTokenThrowIfCancellationRequestedTests + { + } +} diff --git a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt index 94d4940230..b4b0ae1713 100644 --- a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt +++ b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt @@ -14,7 +14,7 @@ Globalization: CA2101, CA1300-CA1310 Mobility: CA1600-CA1601 Performance: HA, CA1800-CA1838 Security: CA2100-CA2153, CA2300-CA2330, CA3000-CA3147, CA5300-CA5403 -Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2249 +Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2250 Naming: CA1700-CA1726 Interoperability: CA1400-CA1417 Maintainability: CA1500-CA1509 From 122ebad45920e7b9dcc750fead31448f20a32b3d Mon Sep 17 00:00:00 2001 From: NewellClark Date: Tue, 16 Feb 2021 14:59:13 -0500 Subject: [PATCH 02/15] Add affirmative tests --- ...lationTokenThrowIfCancellationRequested.cs | 58 ++++++ .../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 ++ ...nTokenThrowIfCancellationRequestedTests.cs | 175 ++++++++++++++++++ src/Utilities/Compiler/WellKnownTypeNames.cs | 1 + 16 files changed, 429 insertions(+) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs index 5073ac0659..f0f3d909a7 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs @@ -3,8 +3,12 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text; +using System.Threading; using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Resx = Microsoft.NetCore.Analyzers.MicrosoftNetCoreAnalyzersResources; @@ -36,6 +40,60 @@ public override void Initialize(AnalysisContext context) { } + // Use readonly struct to avoid allocations. +#pragma warning disable CA1815 // Override equals and operator equals on value types + internal readonly struct RequiredSymbols +#pragma warning restore CA1815 // Override equals and operator equals on value types + { + public static bool TryGetSymbols(Compilation compilation, out RequiredSymbols symbols) + { + symbols = default; + INamedTypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); + if (boolType is null) + return false; + + if (!compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingCancellationToken, out INamedTypeSymbol? cancellationTokenType)) + return false; + IMethodSymbol? throwIfCancellationRequestedMethod = cancellationTokenType.GetMembers(nameof(CancellationToken.ThrowIfCancellationRequested)) + .OfType() + .GetFirstOrDefaultMemberWithParameterInfos(); + IPropertySymbol? isCancellationRequestedProperty = cancellationTokenType.GetMembers(nameof(CancellationToken.IsCancellationRequested)) + .OfType() + .FirstOrDefault(); + if (throwIfCancellationRequestedMethod is null || isCancellationRequestedProperty is null) + return false; + + if (!compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemOperationCanceledException, out INamedTypeSymbol? operationCanceledExceptionType)) + return false; + IMethodSymbol? operationCanceledExceptionDefaultCtor = operationCanceledExceptionType.InstanceConstructors + .GetFirstOrDefaultMemberWithParameterInfos(); + IMethodSymbol? operationCanceledExceptionTokenCtor = operationCanceledExceptionType.InstanceConstructors + .GetFirstOrDefaultMemberWithParameterInfos(ParameterInfo.GetParameterInfo(cancellationTokenType)); + if (operationCanceledExceptionDefaultCtor is null || operationCanceledExceptionTokenCtor is null) + return false; + + symbols = new RequiredSymbols + { + BoolType = boolType, + CancellationTokenType = cancellationTokenType, + OperationCanceledExceptionType = operationCanceledExceptionType, + ThrowIfCancellationRequestedMethod = throwIfCancellationRequestedMethod, + IsCancellationRequestedProperty = isCancellationRequestedProperty, + OperationCanceledExceptionDefaultCtor = operationCanceledExceptionDefaultCtor, + OperationCanceledExceptionTokenCtor = operationCanceledExceptionTokenCtor + }; + return true; + } + + public INamedTypeSymbol BoolType { get; init; } + public INamedTypeSymbol CancellationTokenType { get; init; } + public INamedTypeSymbol OperationCanceledExceptionType { get; init; } + public IMethodSymbol ThrowIfCancellationRequestedMethod { get; init; } + public IPropertySymbol IsCancellationRequestedProperty { get; init; } + public IMethodSymbol OperationCanceledExceptionDefaultCtor { get; init; } + public IMethodSymbol OperationCanceledExceptionTokenCtor { get; init; } + } + private static LocalizableString CreateResource(string resourceName) { return new LocalizableResourceString(resourceName, Resx.ResourceManager, typeof(Resx)); 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 714e9e49b4..e075efc004 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -2097,6 +2097,21 @@ Metoda {0} zpracovává požadavek {1} bez ověřování tokenu proti padělkům. Je potřeba také zajistit, aby váš formulář HTML odesílal tokeny proti padělkům. + + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + + + + Consider calling '{0}' instead of checking '{1}' + Consider calling '{0}' instead of checking '{1}' + 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + + + Use '{0}' + Use '{0}' + + Use Container Level Access Policy Použít zásady přístupu na úrovni kontejneru 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 6e0f153a85..82c9dd0a71 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -2097,6 +2097,21 @@ Die Methode "{0}" verarbeitet eine {1}-Anforderung ohne Überprüfung eines Fälschungssicherheitstokens. Sie müssen außerdem sicherstellen, dass Ihr HTML-Formular ein Fälschungssicherheitstoken sendet. + + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + + + + Consider calling '{0}' instead of checking '{1}' + Consider calling '{0}' instead of checking '{1}' + 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + + + Use '{0}' + Use '{0}' + + Use Container Level Access Policy Zugriffsrichtlinie auf Containerebene verwenden 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 8c66938819..d18f3829e5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -2097,6 +2097,21 @@ El método {0} controla una solicitud de {1} sin validar un token antifalsificación. También debe asegurarse de que el formulario HTML envíe un token antifalsificación. + + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + + + + Consider calling '{0}' instead of checking '{1}' + Consider calling '{0}' instead of checking '{1}' + 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + + + Use '{0}' + Use '{0}' + + Use Container Level Access Policy Usar una directiva de acceso de nivel de contenedor 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 71a0c15261..52b86d7898 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -2097,6 +2097,21 @@ La méthode {0} traite une requête {1} sans validation de jeton antifalsification. Vous devez également vérifier que votre formulaire HTML envoie un jeton antifalsification. + + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + + + + Consider calling '{0}' instead of checking '{1}' + Consider calling '{0}' instead of checking '{1}' + 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + + + Use '{0}' + Use '{0}' + + Use Container Level Access Policy Utiliser une stratégie d'accès au niveau du conteneur 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 61adbbacb6..5750506997 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -2097,6 +2097,21 @@ Il metodo {0} gestisce una richiesta {1} senza eseguire la convalida del token antifalsificazione. È necessario assicurarsi anche che il modulo HTML invii un token antifalsificazione. + + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + + + + Consider calling '{0}' instead of checking '{1}' + Consider calling '{0}' instead of checking '{1}' + 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + + + Use '{0}' + Use '{0}' + + Use Container Level Access Policy Usa criteri di accesso a livello di contenitore 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 4faf2ebe51..dce26e13b5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -2097,6 +2097,21 @@ メソッド {0} では、偽造防止トークンの検証を実行せずに {1} 要求が処理されます。また、HTML フォームで偽造防止トークンが送信されるようにする必要もあります。 + + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + + + + Consider calling '{0}' instead of checking '{1}' + Consider calling '{0}' instead of checking '{1}' + 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + + + Use '{0}' + Use '{0}' + + Use Container Level Access Policy コンテナー レベルのアクセス ポリシーを使用する 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 42e160e005..999c3da269 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -2097,6 +2097,21 @@ {0} 메서드는 위조 방지 토큰 유효성 검사를 수행하지 않고 {1} 요청을 처리합니다. 또한 HTML 양식이 위조 방지 토큰을 보내는지 확인해야 합니다. + + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + + + + Consider calling '{0}' instead of checking '{1}' + Consider calling '{0}' instead of checking '{1}' + 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + + + Use '{0}' + Use '{0}' + + Use Container Level Access Policy 컨테이너 수준 액세스 정책 사용 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 dd9ad6db14..97f3a92adc 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -2097,6 +2097,21 @@ Metoda {0} obsługuje żądanie {1} bez przeprowadzania weryfikacji tokenu zabezpieczającego przed fałszerstwem. Należy również upewnić się, że formularz HTML wysyła token zabezpieczający przed fałszerstwem. + + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + + + + Consider calling '{0}' instead of checking '{1}' + Consider calling '{0}' instead of checking '{1}' + 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + + + Use '{0}' + Use '{0}' + + Use Container Level Access Policy Użyj zasad dostępu na poziomie kontenera 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 f73645c162..2dea7a9b65 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 @@ -2097,6 +2097,21 @@ O método {0} lida com uma solicitação de {1} sem executar a validação de token antifalsificação. Também é necessário garantir que o seu formulário em HTML envie um token antifalsificação. + + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + + + + Consider calling '{0}' instead of checking '{1}' + Consider calling '{0}' instead of checking '{1}' + 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + + + Use '{0}' + Use '{0}' + + Use Container Level Access Policy Usar Política de Acesso no Nível de Contêiner 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 3b853eaf7e..140836b284 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -2097,6 +2097,21 @@ Метод {0} обрабатывает запрос {1} без проверки маркера для защиты от подделки. Также убедитесь в том, что HTML-форма отправляет маркер для защиты от подделки. + + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + + + + Consider calling '{0}' instead of checking '{1}' + Consider calling '{0}' instead of checking '{1}' + 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + + + Use '{0}' + Use '{0}' + + Use Container Level Access Policy Использовать политику доступа на уровне контейнера 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 e85849b540..2133ba38b5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -2097,6 +2097,21 @@ {0} metodu, {1} isteğini sahtecilik önleme belirtecini doğrulamadan işler. Ayrıca HTML formunuzun sahtecilik önleme belirteci gönderdiğinden emin olmanız gerekir. + + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + + + + Consider calling '{0}' instead of checking '{1}' + Consider calling '{0}' instead of checking '{1}' + 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + + + Use '{0}' + Use '{0}' + + Use Container Level Access Policy Kapsayıcı Düzeyinde Erişim İlkesi Kullan 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 2548f9f6f3..0a8cb37102 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 @@ -2097,6 +2097,21 @@ 方法 {0} 在不执行防伪造令牌验证的情况下处理 {1} 请求。你还需要确保 HTML 窗体发送防伪造令牌。 + + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + + + + Consider calling '{0}' instead of checking '{1}' + Consider calling '{0}' instead of checking '{1}' + 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + + + Use '{0}' + Use '{0}' + + Use Container Level Access Policy 使用容器级别访问策略 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 1aebe6df07..d8c337abe6 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 @@ -2097,6 +2097,21 @@ 方法 {0} 會在不驗證 antiforgery 權杖的情況下處理 {1} 要求。您也必須確保 HTML 表單傳送 antiforgery 權杖。 + + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + + + + Consider calling '{0}' instead of checking '{1}' + Consider calling '{0}' instead of checking '{1}' + 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + + + Use '{0}' + Use '{0}' + + Use Container Level Access Policy 使用容器層級存取原則 diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs index 6899c1b664..fc5ce4c89f 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs @@ -1,6 +1,10 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Testing; using Xunit; using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< @@ -14,5 +18,176 @@ namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests { public class UseCancellationTokenThrowIfCancellationRequestedTests { + public static IEnumerable Data_OperationCanceledExceptionCtors + { + get + { + yield return new[] { "OperationCanceledException()" }; + yield return new[] { "OperationCanceledException(token)" }; + } + } + + #region Reports Diagnostics + [Theory] + [MemberData(nameof(Data_OperationCanceledExceptionCtors))] + public Task SimpleAffirmativeCheck_ReportedAndFixed_CS(string operationCanceledExceptionCtor) + { + string testStatements = Markup($@" +if (token.IsCancellationRequested) + throw new {operationCanceledExceptionCtor};", 0); + string fixedStatements = @" +token.ThrowIfCancellationRequested();"; + + var test = new VerifyCS.Test + { + TestCode = CS.CreateBlock(testStatements), + FixedCode = CS.CreateBlock(fixedStatements), + ExpectedDiagnostics = { CS.DiagnosticAt(0) }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [MemberData(nameof(Data_OperationCanceledExceptionCtors))] + public Task SimpleAffirmativeCheck_ReportedAndFixed_VB(string operationCanceledExceptionCtor) + { + string testStatements = Markup($@" +If token.IsCancellationRequested Then + Throw New {operationCanceledExceptionCtor} +End If", 0); + string fixedStatements = @" +token.ThrowIfCancellationRequested()"; + + var test = new VerifyVB.Test + { + TestCode = VB.CreateBlock(testStatements), + FixedCode = VB.CreateBlock(fixedStatements), + ExpectedDiagnostics = { VB.DiagnosticAt(0) }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [MemberData(nameof(Data_OperationCanceledExceptionCtors))] + public Task NegatedCheckWithElse_ReportedAndFixed_CS(string operationCanceledExceptionCtor) + { + const string members = @" +private CancellationToken token; +private void DoSomething() { }"; + string testStatements = Markup($@" +if (!token.IsCancellationRequested) + DoSomething(); +else + throw new {operationCanceledExceptionCtor};", 0); + string fixedStatements = @" +token.ThrowIfCancellationRequested(); +DoSomething();"; + + var test = new VerifyCS.Test + { + TestCode = CS.CreateBlock(testStatements, members), + FixedCode = CS.CreateBlock(fixedStatements, members), + ExpectedDiagnostics = { CS.DiagnosticAt(0) }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [MemberData(nameof(Data_OperationCanceledExceptionCtors))] + public Task NegatedCheckWithElse_ReportedAndFixed_VB(string operationCanceledExceptionCtor) + { + const string members = @" +Private token As CancellationToken +Private Sub DoSomething() +End Sub"; + string testStatements = Markup($@" +If Not token.IsCancellationRequested Then + DoSomething() +Else + Throw New {operationCanceledExceptionCtor} +End If", 0); + string fixedStatements = @" +token.ThrowIfCancellationRequested() +DoSomething()"; + + var test = new VerifyVB.Test + { + TestCode = VB.CreateBlock(testStatements, members), + FixedCode = VB.CreateBlock(fixedStatements, members), + ExpectedDiagnostics = { VB.DiagnosticAt(0) }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + #endregion + + #region Helpers + private static class CS + { + public const string Usings = @" +using System; +using System.Threading;"; + + public static string CreateBlock(string statements, string members) + { + return Usings + @" +public partial class Body +{ +" + IndentLines(members, " ") + @" + public void Run() + { +" + IndentLines(statements, " ") + @" + } +}"; + } + + /// + /// Creates a test class with a single private CancellationToken member called 'token'. + /// + public static string CreateBlock(string statements) => CreateBlock(statements, @"private CancellationToken token;"); + + public static DiagnosticResult DiagnosticAt(int markupKey) => VerifyCS.Diagnostic(Rule).WithLocation(markupKey); + } + + private static class VB + { + public const string Usings = @" +Imports System +Imports System.Threading"; + + public static string CreateBlock(string statements, string members) + { + return Usings + @" +Partial Public Class Body + +" + IndentLines(members, " ") + @" + Public Sub Run() + +" + IndentLines(statements, " ") + @" + End Sub +End Class"; + } + + public static string CreateBlock(string statements) => CreateBlock(statements, @"Private token As CancellationToken"); + + public static DiagnosticResult DiagnosticAt(int markupKey) => VerifyVB.Diagnostic(Rule).WithLocation(markupKey); + } + + private static string IndentLines(string lines, string indent) + { + return indent + lines.TrimStart().Replace(Environment.NewLine, indent + Environment.NewLine, StringComparison.Ordinal); + } + + private static string Markup(string text, int markupKey, bool removeLeadingWhitespace = true) + { + text = removeLeadingWhitespace ? text.TrimStart() : text; + return $"{{|#{markupKey}:{text}|}}"; + } + + private static DiagnosticDescriptor Rule => UseCancellationTokenThrowIfCancellationRequested.Rule; + #endregion } } diff --git a/src/Utilities/Compiler/WellKnownTypeNames.cs b/src/Utilities/Compiler/WellKnownTypeNames.cs index ca67f5eec2..5aab1c9d91 100644 --- a/src/Utilities/Compiler/WellKnownTypeNames.cs +++ b/src/Utilities/Compiler/WellKnownTypeNames.cs @@ -237,6 +237,7 @@ internal static class WellKnownTypeNames public const string SystemObject = "System.Object"; public const string SystemObsoleteAttribute = "System.ObsoleteAttribute"; public const string SystemOperatingSystem = "System.OperatingSystem"; + public const string SystemOperationCanceledException = "System.OperationCanceledException"; public const string SystemOutOfMemoryException = "System.OutOfMemoryException"; public const string SystemPlatformNotSupportedException = "System.PlatformNotSupportedException"; public const string SystemRandom = "System.Random"; From 214adef5c892d9dc99f052232ea9ed888b025113 Mon Sep 17 00:00:00 2001 From: NewellClark Date: Tue, 16 Feb 2021 16:14:57 -0500 Subject: [PATCH 03/15] More affirmative tests --- ...lationTokenThrowIfCancellationRequested.cs | 6 + ...nTokenThrowIfCancellationRequestedTests.cs | 219 +++++++++++++++--- 2 files changed, 192 insertions(+), 33 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs index f0f3d909a7..a2830e1df5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs @@ -11,6 +11,7 @@ using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; using Resx = Microsoft.NetCore.Analyzers.MicrosoftNetCoreAnalyzersResources; namespace Microsoft.NetCore.Analyzers.Runtime @@ -40,6 +41,11 @@ public override void Initialize(AnalysisContext context) { } + private static void OnCompilationStart(CompilationStartAnalysisContext context) + { + + } + // Use readonly struct to avoid allocations. #pragma warning disable CA1815 // Override equals and operator equals on value types internal readonly struct RequiredSymbols diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs index fc5ce4c89f..87ee2dd7aa 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Testing; @@ -18,25 +19,47 @@ namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests { public class UseCancellationTokenThrowIfCancellationRequestedTests { - public static IEnumerable Data_OperationCanceledExceptionCtors + private static IEnumerable OperationCanceledExceptionCtors { get { - yield return new[] { "OperationCanceledException()" }; - yield return new[] { "OperationCanceledException(token)" }; + yield return "OperationCanceledException()"; + yield return "OperationCanceledException(token)"; } } #region Reports Diagnostics + public static IEnumerable Data_SimpleAffirmativeCheck_ReportedAndFixed_CS + { + get + { + static IEnumerable ConditionalFormatStrings() + { + yield return @"if ({0}) {1}"; + yield return @" +if ({0}) + {1}"; + yield return @" +if ({0}) +{{ + {1} +}}"; + } + + return CartesianProduct(OperationCanceledExceptionCtors, ConditionalFormatStrings()); + } + } + [Theory] - [MemberData(nameof(Data_OperationCanceledExceptionCtors))] - public Task SimpleAffirmativeCheck_ReportedAndFixed_CS(string operationCanceledExceptionCtor) + [MemberData(nameof(Data_SimpleAffirmativeCheck_ReportedAndFixed_CS))] + public Task SimpleAffirmativeCheck_ReportedAndFixed_CS(string operationCanceledExceptionCtor, string simpleConditionalFormatString) { - string testStatements = Markup($@" -if (token.IsCancellationRequested) - throw new {operationCanceledExceptionCtor};", 0); - string fixedStatements = @" -token.ThrowIfCancellationRequested();"; + string testStatements = Markup( + FormatInvariant( + simpleConditionalFormatString, + @"token.IsCancellationRequested", + $@"throw new {operationCanceledExceptionCtor};"), 0); + string fixedStatements = @"token.ThrowIfCancellationRequested();"; var test = new VerifyCS.Test { @@ -48,16 +71,34 @@ public Task SimpleAffirmativeCheck_ReportedAndFixed_CS(string operationCanceledE return test.RunAsync(); } + public static IEnumerable Data_SimpleAffirmativeCheck_ReportedAndFixed_VB + { + get + { + static IEnumerable ConditionalFormatStrings() + { + yield return @"If {0} Then {1}"; + yield return @" +If {0} Then + {1} +End If"; + } + + return CartesianProduct(OperationCanceledExceptionCtors, ConditionalFormatStrings()); + } + } + [Theory] - [MemberData(nameof(Data_OperationCanceledExceptionCtors))] - public Task SimpleAffirmativeCheck_ReportedAndFixed_VB(string operationCanceledExceptionCtor) + [MemberData(nameof(Data_SimpleAffirmativeCheck_ReportedAndFixed_VB))] + public Task SimpleAffirmativeCheck_ReportedAndFixed_VB(string operationCanceledExceptionCtor, string conditionalFormatString) { - string testStatements = Markup($@" -If token.IsCancellationRequested Then - Throw New {operationCanceledExceptionCtor} -End If", 0); - string fixedStatements = @" -token.ThrowIfCancellationRequested()"; + string testStatements = Markup( + FormatInvariant( + conditionalFormatString, + "token.IsCancellationRequested", + $"Throw New {operationCanceledExceptionCtor}"), + 0); + string fixedStatements = @"token.ThrowIfCancellationRequested()"; var test = new VerifyVB.Test { @@ -69,18 +110,63 @@ Throw New {operationCanceledExceptionCtor} return test.RunAsync(); } + public static IEnumerable Data_NegatedCheckWithElse_ReportedAndFixed_CS + { + get + { + static IEnumerable ConditionalFormatStrings() + { + yield return @" +if ({0}) {1} +else {2}"; + yield return @" +if ({0}) + {1} +else + {2}"; + yield return @" +if ({0}) +{{ + {1} +}} +else +{{ + {2} +}}"; + yield return @" +if ({0}) + {1} +else +{{ + {2} +}}"; + yield return @" +if ({0}) +{{ + {1} +}} +else + {2}"; + } + + return CartesianProduct(OperationCanceledExceptionCtors, ConditionalFormatStrings()); + } + } + [Theory] - [MemberData(nameof(Data_OperationCanceledExceptionCtors))] - public Task NegatedCheckWithElse_ReportedAndFixed_CS(string operationCanceledExceptionCtor) + [MemberData(nameof(Data_NegatedCheckWithElse_ReportedAndFixed_CS))] + public Task NegatedCheckWithElse_ReportedAndFixed_CS(string operationCanceledExceptionCtor, string conditionalFormatString) { const string members = @" private CancellationToken token; private void DoSomething() { }"; - string testStatements = Markup($@" -if (!token.IsCancellationRequested) - DoSomething(); -else - throw new {operationCanceledExceptionCtor};", 0); + string testStatements = Markup( + FormatInvariant( + conditionalFormatString, + "!token.IsCancellationRequested", + "DoSomething();", + $"throw new {operationCanceledExceptionCtor};"), + 0); string fixedStatements = @" token.ThrowIfCancellationRequested(); DoSomething();"; @@ -95,20 +181,39 @@ public Task NegatedCheckWithElse_ReportedAndFixed_CS(string operationCanceledExc return test.RunAsync(); } + public static IEnumerable Data_NegatedCheckWithElse_ReportedAndFixed_VB + { + get + { + static IEnumerable ConditionalFormatStrings() + { + return Enumerable.Repeat(@" +If {0} Then + {1} +Else + {2} +End If", 1); + } + + return CartesianProduct(OperationCanceledExceptionCtors, ConditionalFormatStrings()); + } + } + [Theory] - [MemberData(nameof(Data_OperationCanceledExceptionCtors))] - public Task NegatedCheckWithElse_ReportedAndFixed_VB(string operationCanceledExceptionCtor) + [MemberData(nameof(Data_NegatedCheckWithElse_ReportedAndFixed_VB))] + public Task NegatedCheckWithElse_ReportedAndFixed_VB(string operationCanceledExceptionCtor, string conditionalFormatString) { const string members = @" Private token As CancellationToken Private Sub DoSomething() End Sub"; - string testStatements = Markup($@" -If Not token.IsCancellationRequested Then - DoSomething() -Else - Throw New {operationCanceledExceptionCtor} -End If", 0); + string testStatements = Markup( + FormatInvariant( + conditionalFormatString, + "Not token.IsCancellationRequested", + "DoSomething()", + $"Throw New {operationCanceledExceptionCtor}"), + 0); string fixedStatements = @" token.ThrowIfCancellationRequested() DoSomething()"; @@ -188,6 +293,54 @@ private static string Markup(string text, int markupKey, bool removeLeadingWhite } private static DiagnosticDescriptor Rule => UseCancellationTokenThrowIfCancellationRequested.Rule; + + private static IEnumerable CartesianProduct(IEnumerable left, IEnumerable right) + { + return left.SelectMany(x => + { + return right.Select(y => + { + var result = new object[x.Length + y.Length]; + x.CopyTo(result.AsSpan()); + y.CopyTo(result.AsSpan(x.Length)); + return result; + }); + }); + } + private static IEnumerable CartesianProduct(IEnumerable left, IEnumerable right) + { + return left.SelectMany(x => + { + return right.Select(y => + { + var result = new object[y.Length + 1]; + result[0] = x; + y.CopyTo(result.AsSpan(1)); + return result; + }); + }); + } + + private static IEnumerable CartesianProduct(IEnumerable left, IEnumerable right) + { + return left.SelectMany(x => + { + return right.Select(y => + { + var result = new object[x.Length + 1]; + x.CopyTo(result.AsSpan()); + result[x.Length] = y; + return result; + }); + }); + } + + private static IEnumerable CartesianProduct(IEnumerable left, IEnumerable right) + { + return left.SelectMany(x => right.Select(y => new[] { x, y })); + } + + private static string FormatInvariant(string format, params object[] args) => string.Format(System.Globalization.CultureInfo.InvariantCulture, format, args); #endregion } } From 7b0522df287dfcc6d66db43adcbaf43b6837b37a Mon Sep 17 00:00:00 2001 From: NewellClark Date: Wed, 17 Feb 2021 14:35:01 -0500 Subject: [PATCH 04/15] Add no diagnostic tests -Add no-diagnostic tests -Fix incorrect formatting by code fixer --- .../MicrosoftNetCoreAnalyzersResources.resx | 4 +- ...TokenThrowIfCancellationRequested.Fixer.cs | 93 +++++- ...lationTokenThrowIfCancellationRequested.cs | 109 ++++++- .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.de.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.es.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.it.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 8 +- ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 8 +- .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 8 +- ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 8 +- ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 8 +- .../Microsoft.CodeAnalysis.NetAnalyzers.md | 12 + .../Microsoft.CodeAnalysis.NetAnalyzers.sarif | 20 ++ src/NetAnalyzers/RulesMissingDocumentation.md | 1 + ...nTokenThrowIfCancellationRequestedTests.cs | 303 +++++++++++++++--- 20 files changed, 539 insertions(+), 107 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 97d797dc63..83f0ddc853 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1511,10 +1511,10 @@ This call site is reachable on: 'windows' 10.0.2000 and later, and all other platforms - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - Consider calling '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs index 0c37b2a586..3e21cd61e7 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs @@ -1,23 +1,108 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Text; +using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Operations; +using RequiredSymbols = Microsoft.NetCore.Analyzers.Runtime.UseCancellationTokenThrowIfCancellationRequested.RequiredSymbols; +using Resx = Microsoft.NetCore.Analyzers.MicrosoftNetCoreAnalyzersResources; namespace Microsoft.NetCore.Analyzers.Runtime { + /// + /// Use instead of checking and + /// throwing . + /// [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class UseCancellationTokenThrowIfCancellationRequestedFixer : CodeFixProvider { public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(UseCancellationTokenThrowIfCancellationRequested.RuleId); - public override Task RegisterCodeFixesAsync(CodeFixContext context) + public override async Task RegisterCodeFixesAsync(CodeFixContext context) { - return Task.CompletedTask; + SemanticModel model = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + if (!RequiredSymbols.TryGetSymbols(model.Compilation, out RequiredSymbols symbols)) + return; + SyntaxNode root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode node = root.FindNode(context.Span); + if (model.GetOperation(node, context.CancellationToken) is not IConditionalOperation conditional) + return; + IOperation? whenTrue = UseCancellationTokenThrowIfCancellationRequested.GetSingleStatementOrDefault(conditional.WhenTrue); + IOperation? whenFalse = UseCancellationTokenThrowIfCancellationRequested.GetSingleStatementOrDefault(conditional.WhenFalse); + + Func> createChangedDocument; + if (symbols.IsSimpleAffirmativeCheck(conditional, out IPropertyReferenceOperation? propertyReference)) + { + // For simple checks of the form: + // if (token.IsCancellationRequested) + // throw new OperationCanceledException(); + // Replace with: + // token.ThrowIfCancellationRequested(); + createChangedDocument = async token => + { + var editor = await DocumentEditor.CreateAsync(context.Document, token).ConfigureAwait(false); + SyntaxNode expressionStatement = CreateThrowIfCancellationRequestedExpressionStatement(editor, conditional, propertyReference); + editor.ReplaceNode(conditional.Syntax, expressionStatement); + return editor.GetChangedDocument(); + }; + } + else if (symbols.IsNegatedCheckWithThrowingElseClause(conditional, out propertyReference)) + { + // For negated checks of the form: + // if (!token.IsCancellationRequested) { DoStatements(); } + // else { throw new OperationCanceledException(); } + // Replace with: + // token.ThrowIfCancellationRequested(); + // DoStatements(); + createChangedDocument = async token => + { + var editor = await DocumentEditor.CreateAsync(context.Document, token).ConfigureAwait(false); + SyntaxNode expressionStatement = CreateThrowIfCancellationRequestedExpressionStatement(editor, conditional, propertyReference); + editor.ReplaceNode(conditional.Syntax, expressionStatement); + if (conditional.WhenTrue is IBlockOperation block) + { + editor.InsertAfter(expressionStatement, block.Operations.Select(x => x.Syntax.WithAdditionalAnnotations(Formatter.Annotation))); + } + else + { + editor.InsertAfter(expressionStatement, conditional.WhenTrue.Syntax); + } + + // Ensure if-blocks with multiple statements maintain correct indentation. + return await Formatter.FormatAsync(editor.GetChangedDocument(), Formatter.Annotation, cancellationToken: token).ConfigureAwait(false); + }; + } + else + { + return; + } + + var codeAction = CodeAction.Create( + Resx.UseCancellationTokenThrowIfCancellationRequestedTitle, + createChangedDocument, + Resx.UseCancellationTokenThrowIfCancellationRequestedTitle); + context.RegisterCodeFix(codeAction, context.Diagnostics); + } + + public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + + private static SyntaxNode CreateThrowIfCancellationRequestedExpressionStatement( + DocumentEditor editor, + IConditionalOperation conditional, + IPropertyReferenceOperation isCancellationRequestedPropertyReference) + { + SyntaxNode memberAccess = editor.Generator.MemberAccessExpression( + isCancellationRequestedPropertyReference.Instance.Syntax, + nameof(CancellationToken.ThrowIfCancellationRequested)); + SyntaxNode invocation = editor.Generator.InvocationExpression(memberAccess, Array.Empty()); + return editor.Generator.ExpressionStatement(invocation).WithTriviaFrom(conditional.Syntax); } } } diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs index a2830e1df5..0b3af361a0 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs @@ -1,11 +1,8 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Text; using System.Threading; using Analyzer.Utilities; using Analyzer.Utilities.Extensions; @@ -39,11 +36,51 @@ public sealed class UseCancellationTokenThrowIfCancellationRequested : Diagnosti public override void Initialize(AnalysisContext context) { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + context.EnableConcurrentExecution(); + context.RegisterCompilationStartAction(OnCompilationStart); } private static void OnCompilationStart(CompilationStartAnalysisContext context) { - + if (!RequiredSymbols.TryGetSymbols(context.Compilation, out var symbols)) + return; + + context.RegisterOperationAction(AnalyzeOperation, OperationKind.Conditional); + + void AnalyzeOperation(OperationAnalysisContext context) + { + var conditional = (IConditionalOperation)context.Operation; + IOperation? whenTrue = GetSingleStatementOrDefault(conditional.WhenTrue); + IOperation? whenFalse = GetSingleStatementOrDefault(conditional.WhenFalse); + + if (symbols.IsSimpleAffirmativeCheck(conditional, out _) || symbols.IsNegatedCheckWithThrowingElseClause(conditional, out _)) + { + var model = conditional.SemanticModel; + int position = conditional.Syntax.SpanStart; + Diagnostic diagnostic = conditional.CreateDiagnostic( + Rule, + symbols.ThrowIfCancellationRequestedMethod.ToMinimalDisplayString(model, position, SymbolDisplayFormat.MinimallyQualifiedFormat), + symbols.IsCancellationRequestedProperty.ToMinimalDisplayString(model, position, SymbolDisplayFormat.MinimallyQualifiedFormat), + symbols.OperationCanceledExceptionType.ToMinimalDisplayString(model, position, SymbolDisplayFormat.MinimallyQualifiedFormat)); + context.ReportDiagnostic(diagnostic); + } + } + } + + /// + /// If is a block operation with one child, returns that child. + /// If is a block operation with more than one child, returns . + /// If is not a block operation, returns . + /// + /// The operation to unwrap. + internal static IOperation? GetSingleStatementOrDefault(IOperation? singleOrBlock) + { + if (singleOrBlock is IBlockOperation blockOperation) + { + return blockOperation.Operations.Length is 1 ? blockOperation.Operations[0] : default; + } + return singleOrBlock; } // Use readonly struct to avoid allocations. @@ -98,6 +135,70 @@ public static bool TryGetSymbols(Compilation compilation, out RequiredSymbols sy public IPropertySymbol IsCancellationRequestedProperty { get; init; } public IMethodSymbol OperationCanceledExceptionDefaultCtor { get; init; } public IMethodSymbol OperationCanceledExceptionTokenCtor { get; init; } + + /// + /// Indicates whether the specified operation is a conditional statement of the form + /// + /// if (token.IsCancellationRequested) + /// throw new OperationCanceledException(); + /// + /// + public bool IsSimpleAffirmativeCheck(IConditionalOperation conditional, [NotNullWhen(true)] out IPropertyReferenceOperation? isCancellationRequestedPropertyReference) + { + IOperation? whenTrueUnwrapped = GetSingleStatementOrDefault(conditional.WhenTrue); + + if (conditional.Condition is IPropertyReferenceOperation propertyReference && + SymbolEqualityComparer.Default.Equals(propertyReference.Property, IsCancellationRequestedProperty) && + whenTrueUnwrapped is IThrowOperation @throw && + @throw.Exception is IObjectCreationOperation objectCreation && + IsDefaultOrTokenOperationCanceledExceptionCtor(objectCreation.Constructor) && + conditional.WhenFalse is null) + { + isCancellationRequestedPropertyReference = propertyReference; + return true; + } + + isCancellationRequestedPropertyReference = default; + return false; + } + + /// + /// Indicates whether the specified operation is a conditional statement of the form + /// + /// if (!token.IsCancellationRequested) + /// { + /// // statements + /// } + /// else + /// { + /// throw new OperationCanceledException(); + /// } + /// + /// + public bool IsNegatedCheckWithThrowingElseClause(IConditionalOperation conditional, [NotNullWhen(true)] out IPropertyReferenceOperation? isCancellationRequestedPropertyReference) + { + IOperation? whenFalseUnwrapped = GetSingleStatementOrDefault(conditional.WhenFalse); + + if (conditional.Condition is IUnaryOperation { OperatorKind: UnaryOperatorKind.Not } unary && + unary.Operand is IPropertyReferenceOperation propertyReference && + SymbolEqualityComparer.Default.Equals(propertyReference.Property, IsCancellationRequestedProperty) && + whenFalseUnwrapped is IThrowOperation @throw && + @throw.Exception is IObjectCreationOperation objectCreation && + IsDefaultOrTokenOperationCanceledExceptionCtor(objectCreation.Constructor)) + { + isCancellationRequestedPropertyReference = propertyReference; + return true; + } + + isCancellationRequestedPropertyReference = default; + return false; + } + + private bool IsDefaultOrTokenOperationCanceledExceptionCtor(IMethodSymbol method) + { + return SymbolEqualityComparer.Default.Equals(method, OperationCanceledExceptionDefaultCtor) || + SymbolEqualityComparer.Default.Equals(method, OperationCanceledExceptionTokenCtor); + } } private static LocalizableString CreateResource(string resourceName) 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 e075efc004..3471901bde 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -2098,13 +2098,13 @@ - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - Consider calling '{0}' instead of checking '{1}' - Consider calling '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type 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 82c9dd0a71..9843dfbfa1 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -2098,13 +2098,13 @@ - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - Consider calling '{0}' instead of checking '{1}' - Consider calling '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type 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 d18f3829e5..5ebe41bb76 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -2098,13 +2098,13 @@ - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - Consider calling '{0}' instead of checking '{1}' - Consider calling '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type 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 52b86d7898..ad08607c04 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -2098,13 +2098,13 @@ - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - Consider calling '{0}' instead of checking '{1}' - Consider calling '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type 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 5750506997..b35ad8d932 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -2098,13 +2098,13 @@ - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - Consider calling '{0}' instead of checking '{1}' - Consider calling '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type 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 dce26e13b5..54b7e86d12 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -2098,13 +2098,13 @@ - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - Consider calling '{0}' instead of checking '{1}' - Consider calling '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type 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 999c3da269..cba2f09037 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -2098,13 +2098,13 @@ - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - Consider calling '{0}' instead of checking '{1}' - Consider calling '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type 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 97f3a92adc..05e68df924 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -2098,13 +2098,13 @@ - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - Consider calling '{0}' instead of checking '{1}' - Consider calling '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type 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 2dea7a9b65..06ea69e187 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 @@ -2098,13 +2098,13 @@ - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - Consider calling '{0}' instead of checking '{1}' - Consider calling '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type 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 140836b284..2ff0ecfe0a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -2098,13 +2098,13 @@ - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - Consider calling '{0}' instead of checking '{1}' - Consider calling '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type 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 2133ba38b5..341b3737f8 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -2098,13 +2098,13 @@ - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - Consider calling '{0}' instead of checking '{1}' - Consider calling '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type 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 0a8cb37102..da6e3c2bf5 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 @@ -2098,13 +2098,13 @@ - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - Consider calling '{0}' instead of checking '{1}' - Consider calling '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type 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 d8c337abe6..522b46cf0a 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 @@ -2098,13 +2098,13 @@ - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws a new '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - Consider calling '{0}' instead of checking '{1}' - Consider calling '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' + Use '{0}' instead of checking '{1}' 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md index fa5d976400..7a2db196b3 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md @@ -1848,6 +1848,18 @@ Calls to 'string.IndexOf' where the result is used to check for the presence/abs |CodeFix|True| --- +## [CA2250](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2250): Use '{0}' + +'{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + +|Item|Value| +|-|-| +|Category|Usage| +|Enabled|True| +|Severity|Info| +|CodeFix|True| +--- + ## [CA2300](https://docs.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 82ce598feb..13997406b0 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif @@ -3271,6 +3271,26 @@ ] } }, + "CA2250": { + "id": "CA2250", + "shortDescription": "Use '{0}'", + "fullDescription": "'{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has.", + "defaultLevel": "note", + "helpUri": "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2250", + "properties": { + "category": "Usage", + "isEnabledByDefault": true, + "typeName": "UseCancellationTokenThrowIfCancellationRequested", + "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 75e0f77588..865a67bfbd 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -2,3 +2,4 @@ Rule ID | Missing Help Link | Title | --------|-------------------|-------| +CA2250 | | Use '{0}' | diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs index 87ee2dd7aa..47f57d4f3c 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Testing; @@ -28,7 +29,7 @@ private static IEnumerable OperationCanceledExceptionCtors } } - #region Reports Diagnostics + #region Reports Diagnostic public static IEnumerable Data_SimpleAffirmativeCheck_ReportedAndFixed_CS { get @@ -227,6 +228,250 @@ Private Sub DoSomething() }; return test.RunAsync(); } + + [Fact] + public Task NegatedCheckWithElse_MultipleOperationsInTrueBranch_ReportedAndFixed_CS() + { + const string members = @" +private CancellationToken token; +private void Fooble() { } +private void Barble() { }"; + string testStatements = Markup(@" +if (!token.IsCancellationRequested) +{ + Fooble(); + Barble(); +} +else +{ + throw new OperationCanceledException(); +}", 0); + string fixedStatements = @" +token.ThrowIfCancellationRequested(); +Fooble(); +Barble();"; + + var test = new VerifyCS.Test + { + TestCode = CS.CreateBlock(testStatements, members), + FixedCode = CS.CreateBlock(fixedStatements, members), + ExpectedDiagnostics = { CS.DiagnosticAt(0) }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Fact] + public Task NegatedCheckWithElse_MultpleOperationsInTrueBranch_ReportedAndFixed_VB() + { + const string members = @" +Private token As CancellationToken +Private Sub Fooble() +End Sub +Private Sub Barble() +End Sub"; + string testStatements = Markup(@" +If Not token.IsCancellationRequested Then + Fooble() + Barble() +Else + Throw New OperationCanceledException() +End If", 0); + string fixedStatements = @" +token.ThrowIfCancellationRequested() +Fooble() +Barble()"; + + var test = new VerifyVB.Test + { + TestCode = VB.CreateBlock(testStatements, members), + FixedCode = VB.CreateBlock(fixedStatements, members), + ExpectedDiagnostics = { VB.DiagnosticAt(0) }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + #endregion + + #region No Diagnostic + [Fact] + public Task MultipleConditions_NoDiagnostic_CS() + { + const string members = @" +private CancellationToken token; +private bool otherCondition;"; + const string testStatements = @" +if (token.IsCancellationRequested && otherCondition) + throw new OperationCanceledException();"; + + var test = new VerifyCS.Test + { + TestCode = CS.CreateBlock(testStatements, members), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Fact] + public Task MultipleConditions_NoDiagnostic_VB() + { + const string members = @" +Private token As CancellationToken +Private otherCondition As Boolean"; + const string testStatements = @" +If token.IsCancellationRequested AndAlso otherCondition Then + Throw New OperationCanceledException() +End If"; + + var test = new VerifyVB.Test + { + TestCode = VB.CreateBlock(testStatements, members), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Fact] + public Task OtherStatementsInSimpleAffirmativeCheck_NoDiagnostic_CS() + { + const string members = @" +private CancellationToken token; +private void SomeOtherAction() { }"; + const string testStatements = @" +if (token.IsCancellationRequested) +{ + SomeOtherAction(); + throw new OperationCanceledException(); +}"; + + var test = new VerifyCS.Test + { + TestCode = CS.CreateBlock(testStatements, members), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Fact] + public Task OtherStatementsInSimpleAffirmativeCheck_NoDiagnostic_VB() + { + const string members = @" +Private token As CancellationToken +Private Sub SomeOtherAction() +End Sub"; + const string testStatements = @" +If token.IsCancellationRequested Then + SomeOtherAction() + Throw New OperationCanceledException() +End If"; + + var test = new VerifyVB.Test + { + TestCode = VB.CreateBlock(testStatements, members), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + public static IEnumerable Data_OperationCanceledExceptionCtorArguments + { + get + { + yield return new[] { "text" }; + yield return new[] { "text, token" }; + yield return new[] { "text, exception" }; + yield return new[] { "text, exception, token" }; + } + } + + [Theory] + [MemberData(nameof(Data_OperationCanceledExceptionCtorArguments))] + public Task OtherExceptionCtorOverloads_SimpleAffirmativeCheck_NoDiagnostic_CS(string ctorArguments) + { + const string members = @" +private CancellationToken token; +private string text; +private Exception exception;"; + string testStatements = @" +if (token.IsCancellationRequested) + throw new OperationCanceledException(" + ctorArguments + @");"; + + var test = new VerifyCS.Test + { + TestCode = CS.CreateBlock(testStatements, members), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [MemberData(nameof(Data_OperationCanceledExceptionCtorArguments))] + public Task OtherExceptionCtorOverloads_SimpleAffirmativeCheck_NoDiagnostic_VB(string ctorArguments) + { + const string members = @" +Private token As CancellationToken +Private text As String +private exception As Exception"; + string testStatements = @" +If token.IsCancellationRequested Then + Throw New OperationCanceledException(" + ctorArguments + @") +End If"; + + var test = new VerifyVB.Test + { + TestCode = VB.CreateBlock(testStatements, members), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [MemberData(nameof(Data_OperationCanceledExceptionCtorArguments))] + public Task OtherExceptionCtorOverloads_NegatedCheckWithElse_NoDiagnostic_CS(string ctorArguments) + { + const string members = @" +private CancellationToken token; +private string text; +private Exception exception; +private void DoSomething() { }"; + string testStatements = @" +if (!token.IsCancellationRequested) + DoSomething(); +else + throw new OperationCanceledException(" + ctorArguments + @");"; + + var test = new VerifyCS.Test + { + TestCode = CS.CreateBlock(testStatements, members), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + + [Theory] + [MemberData(nameof(Data_OperationCanceledExceptionCtorArguments))] + public Task OtherExceptionCtorOverloads_NegatedCheckWithElse_NoDiagnostic_VB(string ctorArguments) + { + const string members = @" +Private token As CancellationToken +Private text As String +Private exception As Exception +Private Sub DoSomething() +End Sub"; + string testStatements = @" +If Not token.IsCancellationRequested Then + DoSomething() +Else + Throw New OperationCanceledException(" + ctorArguments + @") +End If"; + + var test = new VerifyVB.Test + { + TestCode = VB.CreateBlock(testStatements, members), + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } #endregion #region Helpers @@ -254,7 +499,11 @@ public void Run() /// public static string CreateBlock(string statements) => CreateBlock(statements, @"private CancellationToken token;"); - public static DiagnosticResult DiagnosticAt(int markupKey) => VerifyCS.Diagnostic(Rule).WithLocation(markupKey); + public static DiagnosticResult DiagnosticAt(int markupKey) => VerifyCS.Diagnostic(Rule) + .WithLocation(markupKey) + .WithArguments($"void {nameof(CancellationToken)}.{nameof(CancellationToken.ThrowIfCancellationRequested)}()", + $"bool {nameof(CancellationToken)}.{nameof(CancellationToken.IsCancellationRequested)}", + nameof(OperationCanceledException)); } private static class VB @@ -278,12 +527,17 @@ End Sub public static string CreateBlock(string statements) => CreateBlock(statements, @"Private token As CancellationToken"); - public static DiagnosticResult DiagnosticAt(int markupKey) => VerifyVB.Diagnostic(Rule).WithLocation(markupKey); + public static DiagnosticResult DiagnosticAt(int markupKey) => VerifyVB.Diagnostic(Rule) + .WithLocation(markupKey) + .WithArguments( + $"Sub {nameof(CancellationToken)}.{nameof(CancellationToken.ThrowIfCancellationRequested)}()", + $"Property {nameof(CancellationToken)}.{nameof(CancellationToken.IsCancellationRequested)} As Boolean", + nameof(OperationCanceledException)); } private static string IndentLines(string lines, string indent) { - return indent + lines.TrimStart().Replace(Environment.NewLine, indent + Environment.NewLine, StringComparison.Ordinal); + return indent + lines.TrimStart().Replace(Environment.NewLine, Environment.NewLine + indent, StringComparison.Ordinal); } private static string Markup(string text, int markupKey, bool removeLeadingWhitespace = true) @@ -294,47 +548,6 @@ private static string Markup(string text, int markupKey, bool removeLeadingWhite private static DiagnosticDescriptor Rule => UseCancellationTokenThrowIfCancellationRequested.Rule; - private static IEnumerable CartesianProduct(IEnumerable left, IEnumerable right) - { - return left.SelectMany(x => - { - return right.Select(y => - { - var result = new object[x.Length + y.Length]; - x.CopyTo(result.AsSpan()); - y.CopyTo(result.AsSpan(x.Length)); - return result; - }); - }); - } - private static IEnumerable CartesianProduct(IEnumerable left, IEnumerable right) - { - return left.SelectMany(x => - { - return right.Select(y => - { - var result = new object[y.Length + 1]; - result[0] = x; - y.CopyTo(result.AsSpan(1)); - return result; - }); - }); - } - - private static IEnumerable CartesianProduct(IEnumerable left, IEnumerable right) - { - return left.SelectMany(x => - { - return right.Select(y => - { - var result = new object[x.Length + 1]; - x.CopyTo(result.AsSpan()); - result[x.Length] = y; - return result; - }); - }); - } - private static IEnumerable CartesianProduct(IEnumerable left, IEnumerable right) { return left.SelectMany(x => right.Select(y => new[] { x, y })); From 090bde5b0554cfd2b32f5c2a5f3a9986dcf0495d Mon Sep 17 00:00:00 2001 From: NewellClark Date: Mon, 22 Feb 2021 11:45:23 -0500 Subject: [PATCH 05/15] Apply suggested changes --- .../MicrosoftNetCoreAnalyzersResources.resx | 10 ++++--- ...TokenThrowIfCancellationRequested.Fixer.cs | 4 +-- ...lationTokenThrowIfCancellationRequested.cs | 28 ++++++++----------- .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 19 ++++++++----- .../MicrosoftNetCoreAnalyzersResources.de.xlf | 19 ++++++++----- .../MicrosoftNetCoreAnalyzersResources.es.xlf | 19 ++++++++----- .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 19 ++++++++----- .../MicrosoftNetCoreAnalyzersResources.it.xlf | 19 ++++++++----- .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 19 ++++++++----- .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 19 ++++++++----- .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 19 ++++++++----- ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 19 ++++++++----- .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 19 ++++++++----- .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 19 ++++++++----- ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 19 ++++++++----- ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 19 ++++++++----- .../Microsoft.CodeAnalysis.NetAnalyzers.md | 4 +-- .../Microsoft.CodeAnalysis.NetAnalyzers.sarif | 4 +-- src/NetAnalyzers/RulesMissingDocumentation.md | 2 +- ...nTokenThrowIfCancellationRequestedTests.cs | 13 ++------- 20 files changed, 182 insertions(+), 130 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 83f0ddc853..051f3f6dbd 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1511,13 +1511,15 @@ This call site is reachable on: 'windows' 10.0.2000 and later, and all other platforms - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. - Use '{0}' instead of checking '{1}' - 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' - Use '{0}' + Use 'ThrowIfCancellationRequested' + + + Use 'ThrowIfCancellationRequested' \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs index 3e21cd61e7..de0f2deb97 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs @@ -85,9 +85,9 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) } var codeAction = CodeAction.Create( - Resx.UseCancellationTokenThrowIfCancellationRequestedTitle, + Resx.UseCancellationTokenThrowIfCancellationRequestedCodeFixTitle, createChangedDocument, - Resx.UseCancellationTokenThrowIfCancellationRequestedTitle); + Resx.UseCancellationTokenThrowIfCancellationRequestedCodeFixTitle); context.RegisterCodeFix(codeAction, context.Diagnostics); } diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs index 0b3af361a0..4786b7cb6c 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs @@ -18,9 +18,9 @@ public sealed class UseCancellationTokenThrowIfCancellationRequested : Diagnosti { internal const string RuleId = "CA2250"; - private static readonly LocalizableString s_localizableTitle = CreateResource(nameof(Resx.UseCancellationTokenThrowIfCancellationRequestedTitle)); - private static readonly LocalizableString s_localizableMessage = CreateResource(nameof(Resx.UseCancellationTokenThrowIfCancellationRequestedMessage)); - private static readonly LocalizableString s_localizableDescription = CreateResource(nameof(Resx.UseCancellationTokenThrowIfCancellationRequestedDescription)); + private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(Resx.UseCancellationTokenThrowIfCancellationRequestedTitle), Resx.ResourceManager, typeof(Resx)); + private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(Resx.UseCancellationTokenThrowIfCancellationRequestedMessage), Resx.ResourceManager, typeof(Resx)); + private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(Resx.UseCancellationTokenThrowIfCancellationRequestedDescription), Resx.ResourceManager, typeof(Resx)); internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( RuleId, @@ -36,7 +36,7 @@ public sealed class UseCancellationTokenThrowIfCancellationRequested : Diagnosti public override void Initialize(AnalysisContext context) { - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.EnableConcurrentExecution(); context.RegisterCompilationStartAction(OnCompilationStart); } @@ -56,14 +56,7 @@ void AnalyzeOperation(OperationAnalysisContext context) if (symbols.IsSimpleAffirmativeCheck(conditional, out _) || symbols.IsNegatedCheckWithThrowingElseClause(conditional, out _)) { - var model = conditional.SemanticModel; - int position = conditional.Syntax.SpanStart; - Diagnostic diagnostic = conditional.CreateDiagnostic( - Rule, - symbols.ThrowIfCancellationRequestedMethod.ToMinimalDisplayString(model, position, SymbolDisplayFormat.MinimallyQualifiedFormat), - symbols.IsCancellationRequestedProperty.ToMinimalDisplayString(model, position, SymbolDisplayFormat.MinimallyQualifiedFormat), - symbols.OperationCanceledExceptionType.ToMinimalDisplayString(model, position, SymbolDisplayFormat.MinimallyQualifiedFormat)); - context.ReportDiagnostic(diagnostic); + context.ReportDiagnostic(conditional.CreateDiagnostic(Rule)); } } } @@ -80,6 +73,7 @@ void AnalyzeOperation(OperationAnalysisContext context) { return blockOperation.Operations.Length is 1 ? blockOperation.Operations[0] : default; } + return singleOrBlock; } @@ -97,21 +91,25 @@ public static bool TryGetSymbols(Compilation compilation, out RequiredSymbols sy if (!compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingCancellationToken, out INamedTypeSymbol? cancellationTokenType)) return false; + IMethodSymbol? throwIfCancellationRequestedMethod = cancellationTokenType.GetMembers(nameof(CancellationToken.ThrowIfCancellationRequested)) .OfType() .GetFirstOrDefaultMemberWithParameterInfos(); IPropertySymbol? isCancellationRequestedProperty = cancellationTokenType.GetMembers(nameof(CancellationToken.IsCancellationRequested)) .OfType() .FirstOrDefault(); + if (throwIfCancellationRequestedMethod is null || isCancellationRequestedProperty is null) return false; if (!compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemOperationCanceledException, out INamedTypeSymbol? operationCanceledExceptionType)) return false; + IMethodSymbol? operationCanceledExceptionDefaultCtor = operationCanceledExceptionType.InstanceConstructors .GetFirstOrDefaultMemberWithParameterInfos(); IMethodSymbol? operationCanceledExceptionTokenCtor = operationCanceledExceptionType.InstanceConstructors .GetFirstOrDefaultMemberWithParameterInfos(ParameterInfo.GetParameterInfo(cancellationTokenType)); + if (operationCanceledExceptionDefaultCtor is null || operationCanceledExceptionTokenCtor is null) return false; @@ -125,6 +123,7 @@ public static bool TryGetSymbols(Compilation compilation, out RequiredSymbols sy OperationCanceledExceptionDefaultCtor = operationCanceledExceptionDefaultCtor, OperationCanceledExceptionTokenCtor = operationCanceledExceptionTokenCtor }; + return true; } @@ -200,10 +199,5 @@ private bool IsDefaultOrTokenOperationCanceledExceptionCtor(IMethodSymbol method SymbolEqualityComparer.Default.Equals(method, OperationCanceledExceptionTokenCtor); } } - - private static LocalizableString CreateResource(string resourceName) - { - return new LocalizableResourceString(resourceName, Resx.ResourceManager, typeof(Resx)); - } } } 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 3471901bde..70188067f3 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -2097,19 +2097,24 @@ Metoda {0} zpracovává požadavek {1} bez ověřování tokenu proti padělkům. Je potřeba také zajistit, aby váš formulář HTML odesílal tokeny proti padělkům. + + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' + + - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. - Use '{0}' instead of checking '{1}' - Use '{0}' instead of checking '{1}' - 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + - Use '{0}' - Use '{0}' + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' 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 9843dfbfa1..e45e747be6 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -2097,19 +2097,24 @@ Die Methode "{0}" verarbeitet eine {1}-Anforderung ohne Überprüfung eines Fälschungssicherheitstokens. Sie müssen außerdem sicherstellen, dass Ihr HTML-Formular ein Fälschungssicherheitstoken sendet. + + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' + + - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. - Use '{0}' instead of checking '{1}' - Use '{0}' instead of checking '{1}' - 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + - Use '{0}' - Use '{0}' + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' 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 5ebe41bb76..5f2f27bfc0 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -2097,19 +2097,24 @@ El método {0} controla una solicitud de {1} sin validar un token antifalsificación. También debe asegurarse de que el formulario HTML envíe un token antifalsificación. + + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' + + - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. - Use '{0}' instead of checking '{1}' - Use '{0}' instead of checking '{1}' - 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + - Use '{0}' - Use '{0}' + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' 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 ad08607c04..f07dd7b9f1 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -2097,19 +2097,24 @@ La méthode {0} traite une requête {1} sans validation de jeton antifalsification. Vous devez également vérifier que votre formulaire HTML envoie un jeton antifalsification. + + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' + + - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. - Use '{0}' instead of checking '{1}' - Use '{0}' instead of checking '{1}' - 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + - Use '{0}' - Use '{0}' + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' 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 b35ad8d932..5196712781 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -2097,19 +2097,24 @@ Il metodo {0} gestisce una richiesta {1} senza eseguire la convalida del token antifalsificazione. È necessario assicurarsi anche che il modulo HTML invii un token antifalsificazione. + + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' + + - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. - Use '{0}' instead of checking '{1}' - Use '{0}' instead of checking '{1}' - 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + - Use '{0}' - Use '{0}' + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' 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 54b7e86d12..49b3f2cc24 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -2097,19 +2097,24 @@ メソッド {0} では、偽造防止トークンの検証を実行せずに {1} 要求が処理されます。また、HTML フォームで偽造防止トークンが送信されるようにする必要もあります。 + + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' + + - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. - Use '{0}' instead of checking '{1}' - Use '{0}' instead of checking '{1}' - 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + - Use '{0}' - Use '{0}' + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' 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 cba2f09037..2a1223e3dd 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -2097,19 +2097,24 @@ {0} 메서드는 위조 방지 토큰 유효성 검사를 수행하지 않고 {1} 요청을 처리합니다. 또한 HTML 양식이 위조 방지 토큰을 보내는지 확인해야 합니다. + + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' + + - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. - Use '{0}' instead of checking '{1}' - Use '{0}' instead of checking '{1}' - 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + - Use '{0}' - Use '{0}' + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' 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 05e68df924..5dfaaa31a4 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -2097,19 +2097,24 @@ Metoda {0} obsługuje żądanie {1} bez przeprowadzania weryfikacji tokenu zabezpieczającego przed fałszerstwem. Należy również upewnić się, że formularz HTML wysyła token zabezpieczający przed fałszerstwem. + + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' + + - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. - Use '{0}' instead of checking '{1}' - Use '{0}' instead of checking '{1}' - 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + - Use '{0}' - Use '{0}' + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' 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 06ea69e187..1e8b900911 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 @@ -2097,19 +2097,24 @@ O método {0} lida com uma solicitação de {1} sem executar a validação de token antifalsificação. Também é necessário garantir que o seu formulário em HTML envie um token antifalsificação. + + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' + + - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. - Use '{0}' instead of checking '{1}' - Use '{0}' instead of checking '{1}' - 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + - Use '{0}' - Use '{0}' + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' 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 2ff0ecfe0a..00a278cab3 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -2097,19 +2097,24 @@ Метод {0} обрабатывает запрос {1} без проверки маркера для защиты от подделки. Также убедитесь в том, что HTML-форма отправляет маркер для защиты от подделки. + + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' + + - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. - Use '{0}' instead of checking '{1}' - Use '{0}' instead of checking '{1}' - 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + - Use '{0}' - Use '{0}' + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' 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 341b3737f8..cecece059d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -2097,19 +2097,24 @@ {0} metodu, {1} isteğini sahtecilik önleme belirtecini doğrulamadan işler. Ayrıca HTML formunuzun sahtecilik önleme belirteci gönderdiğinden emin olmanız gerekir. + + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' + + - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. - Use '{0}' instead of checking '{1}' - Use '{0}' instead of checking '{1}' - 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + - Use '{0}' - Use '{0}' + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' 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 da6e3c2bf5..9917840e6b 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 @@ -2097,19 +2097,24 @@ 方法 {0} 在不执行防伪造令牌验证的情况下处理 {1} 请求。你还需要确保 HTML 窗体发送防伪造令牌。 + + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' + + - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. - Use '{0}' instead of checking '{1}' - Use '{0}' instead of checking '{1}' - 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + - Use '{0}' - Use '{0}' + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' 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 522b46cf0a..396ec8a0ce 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 @@ -2097,19 +2097,24 @@ 方法 {0} 會在不驗證 antiforgery 權杖的情況下處理 {1} 要求。您也必須確保 HTML 表單傳送 antiforgery 權杖。 + + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' + + - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. - '{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. + 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. - Use '{0}' instead of checking '{1}' - Use '{0}' instead of checking '{1}' - 0=ThrowIfCancellationRequested method, 1=IsCancellationRequested property, 2=OperationCanceledException type + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + - Use '{0}' - Use '{0}' + Use 'ThrowIfCancellationRequested' + Use 'ThrowIfCancellationRequested' diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md index 7a2db196b3..59a31de82f 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md @@ -1848,9 +1848,9 @@ Calls to 'string.IndexOf' where the result is used to check for the presence/abs |CodeFix|True| --- -## [CA2250](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2250): Use '{0}' +## [CA2250](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2250): Use 'ThrowIfCancellationRequested' -'{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has. +'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. |Item|Value| |-|-| diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif index 13997406b0..185ed7bcb7 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif @@ -3273,8 +3273,8 @@ }, "CA2250": { "id": "CA2250", - "shortDescription": "Use '{0}'", - "fullDescription": "'{0}' automatically checks whether the token has been canceled, and throws an '{2}' if it has.", + "shortDescription": "Use 'ThrowIfCancellationRequested'", + "fullDescription": "'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has.", "defaultLevel": "note", "helpUri": "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2250", "properties": { diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md index 865a67bfbd..bf8a3f1e9b 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -2,4 +2,4 @@ Rule ID | Missing Help Link | Title | --------|-------------------|-------| -CA2250 | | Use '{0}' | +CA2250 | | Use 'ThrowIfCancellationRequested' | diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs index 47f57d4f3c..1862e8ce62 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs @@ -499,11 +499,7 @@ public void Run() /// public static string CreateBlock(string statements) => CreateBlock(statements, @"private CancellationToken token;"); - public static DiagnosticResult DiagnosticAt(int markupKey) => VerifyCS.Diagnostic(Rule) - .WithLocation(markupKey) - .WithArguments($"void {nameof(CancellationToken)}.{nameof(CancellationToken.ThrowIfCancellationRequested)}()", - $"bool {nameof(CancellationToken)}.{nameof(CancellationToken.IsCancellationRequested)}", - nameof(OperationCanceledException)); + public static DiagnosticResult DiagnosticAt(int markupKey) => VerifyCS.Diagnostic(Rule).WithLocation(markupKey); } private static class VB @@ -527,12 +523,7 @@ End Sub public static string CreateBlock(string statements) => CreateBlock(statements, @"Private token As CancellationToken"); - public static DiagnosticResult DiagnosticAt(int markupKey) => VerifyVB.Diagnostic(Rule) - .WithLocation(markupKey) - .WithArguments( - $"Sub {nameof(CancellationToken)}.{nameof(CancellationToken.ThrowIfCancellationRequested)}()", - $"Property {nameof(CancellationToken)}.{nameof(CancellationToken.IsCancellationRequested)} As Boolean", - nameof(OperationCanceledException)); + public static DiagnosticResult DiagnosticAt(int markupKey) => VerifyVB.Diagnostic(Rule).WithLocation(markupKey); } private static string IndentLines(string lines, string indent) From 4e6d46719488b236a10c7666f944e4cf351e4ee6 Mon Sep 17 00:00:00 2001 From: Newell Clark Date: Sat, 27 Feb 2021 10:29:04 -0500 Subject: [PATCH 06/15] Apply changes to resources from code review Changes to code will be applied separately. Co-authored-by: Buyaa Namnan --- .../MicrosoftNetCoreAnalyzersResources.resx | 6 +++--- .../UseCancellationTokenThrowIfCancellationRequested.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 051f3f6dbd..2c32220274 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1514,12 +1514,12 @@ 'ThrowIfCancellationRequested' automatically checks whether the token has been canceled, and throws an 'OperationCanceledException' if it has. - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' Use 'ThrowIfCancellationRequested' - Use 'ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' - \ No newline at end of file + diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs index 4786b7cb6c..c0163722c8 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs @@ -51,8 +51,8 @@ private static void OnCompilationStart(CompilationStartAnalysisContext context) void AnalyzeOperation(OperationAnalysisContext context) { var conditional = (IConditionalOperation)context.Operation; - IOperation? whenTrue = GetSingleStatementOrDefault(conditional.WhenTrue); - IOperation? whenFalse = GetSingleStatementOrDefault(conditional.WhenFalse); + GetSingleStatementOrDefault(conditional.WhenTrue); + GetSingleStatementOrDefault(conditional.WhenFalse); if (symbols.IsSimpleAffirmativeCheck(conditional, out _) || symbols.IsNegatedCheckWithThrowingElseClause(conditional, out _)) { From 445c415ee65ba7998b34c8bcc2f2db947ea6e054 Mon Sep 17 00:00:00 2001 From: NewellClark Date: Sat, 27 Feb 2021 10:54:13 -0500 Subject: [PATCH 07/15] Remove dead code found by buyaa-n --- ...cellationTokenThrowIfCancellationRequested.Fixer.cs | 2 -- ...UseCancellationTokenThrowIfCancellationRequested.cs | 10 ---------- .../xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf | 8 ++++---- .../xlf/MicrosoftNetCoreAnalyzersResources.de.xlf | 8 ++++---- .../xlf/MicrosoftNetCoreAnalyzersResources.es.xlf | 8 ++++---- .../xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf | 8 ++++---- .../xlf/MicrosoftNetCoreAnalyzersResources.it.xlf | 8 ++++---- .../xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf | 8 ++++---- .../xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf | 8 ++++---- .../xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf | 8 ++++---- .../xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf | 8 ++++---- .../xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf | 8 ++++---- .../xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf | 8 ++++---- .../xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf | 8 ++++---- .../xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf | 8 ++++---- 15 files changed, 52 insertions(+), 64 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs index de0f2deb97..6b180c75cf 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs @@ -34,8 +34,6 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) SyntaxNode node = root.FindNode(context.Span); if (model.GetOperation(node, context.CancellationToken) is not IConditionalOperation conditional) return; - IOperation? whenTrue = UseCancellationTokenThrowIfCancellationRequested.GetSingleStatementOrDefault(conditional.WhenTrue); - IOperation? whenFalse = UseCancellationTokenThrowIfCancellationRequested.GetSingleStatementOrDefault(conditional.WhenFalse); Func> createChangedDocument; if (symbols.IsSimpleAffirmativeCheck(conditional, out IPropertyReferenceOperation? propertyReference)) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs index c0163722c8..14e465fef5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs @@ -51,8 +51,6 @@ private static void OnCompilationStart(CompilationStartAnalysisContext context) void AnalyzeOperation(OperationAnalysisContext context) { var conditional = (IConditionalOperation)context.Operation; - GetSingleStatementOrDefault(conditional.WhenTrue); - GetSingleStatementOrDefault(conditional.WhenFalse); if (symbols.IsSimpleAffirmativeCheck(conditional, out _) || symbols.IsNegatedCheckWithThrowingElseClause(conditional, out _)) { @@ -115,10 +113,6 @@ public static bool TryGetSymbols(Compilation compilation, out RequiredSymbols sy symbols = new RequiredSymbols { - BoolType = boolType, - CancellationTokenType = cancellationTokenType, - OperationCanceledExceptionType = operationCanceledExceptionType, - ThrowIfCancellationRequestedMethod = throwIfCancellationRequestedMethod, IsCancellationRequestedProperty = isCancellationRequestedProperty, OperationCanceledExceptionDefaultCtor = operationCanceledExceptionDefaultCtor, OperationCanceledExceptionTokenCtor = operationCanceledExceptionTokenCtor @@ -127,10 +121,6 @@ public static bool TryGetSymbols(Compilation compilation, out RequiredSymbols sy return true; } - public INamedTypeSymbol BoolType { get; init; } - public INamedTypeSymbol CancellationTokenType { get; init; } - public INamedTypeSymbol OperationCanceledExceptionType { get; init; } - public IMethodSymbol ThrowIfCancellationRequestedMethod { get; init; } public IPropertySymbol IsCancellationRequestedProperty { get; init; } public IMethodSymbol OperationCanceledExceptionDefaultCtor { get; init; } public IMethodSymbol OperationCanceledExceptionTokenCtor { get; init; } 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 70188067f3..193b7d9a60 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -2098,8 +2098,8 @@ - Use 'ThrowIfCancellationRequested' - Use 'ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' @@ -2108,8 +2108,8 @@ - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' 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 e45e747be6..f0fbec2181 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -2098,8 +2098,8 @@ - Use 'ThrowIfCancellationRequested' - Use 'ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' @@ -2108,8 +2108,8 @@ - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' 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 5f2f27bfc0..1e26f1a3ff 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -2098,8 +2098,8 @@ - Use 'ThrowIfCancellationRequested' - Use 'ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' @@ -2108,8 +2108,8 @@ - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' 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 f07dd7b9f1..9c7760ea08 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -2098,8 +2098,8 @@ - Use 'ThrowIfCancellationRequested' - Use 'ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' @@ -2108,8 +2108,8 @@ - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' 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 5196712781..3af265eae6 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -2098,8 +2098,8 @@ - Use 'ThrowIfCancellationRequested' - Use 'ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' @@ -2108,8 +2108,8 @@ - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' 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 49b3f2cc24..56a52e75a9 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -2098,8 +2098,8 @@ - Use 'ThrowIfCancellationRequested' - Use 'ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' @@ -2108,8 +2108,8 @@ - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' 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 2a1223e3dd..5fceb7e6c6 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -2098,8 +2098,8 @@ - Use 'ThrowIfCancellationRequested' - Use 'ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' @@ -2108,8 +2108,8 @@ - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' 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 5dfaaa31a4..0c02a1b6b7 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -2098,8 +2098,8 @@ - Use 'ThrowIfCancellationRequested' - Use 'ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' @@ -2108,8 +2108,8 @@ - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' 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 1e8b900911..5505d50155 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 @@ -2098,8 +2098,8 @@ - Use 'ThrowIfCancellationRequested' - Use 'ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' @@ -2108,8 +2108,8 @@ - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' 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 00a278cab3..f8e1bc4848 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -2098,8 +2098,8 @@ - Use 'ThrowIfCancellationRequested' - Use 'ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' @@ -2108,8 +2108,8 @@ - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' 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 cecece059d..21b3a432b2 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -2098,8 +2098,8 @@ - Use 'ThrowIfCancellationRequested' - Use 'ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' @@ -2108,8 +2108,8 @@ - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' 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 9917840e6b..ad506fbfb3 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 @@ -2098,8 +2098,8 @@ - Use 'ThrowIfCancellationRequested' - Use 'ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' @@ -2108,8 +2108,8 @@ - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' 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 396ec8a0ce..2788edbbe1 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 @@ -2098,8 +2098,8 @@ - Use 'ThrowIfCancellationRequested' - Use 'ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' + Replace with 'CancellationToken.ThrowIfCancellationRequested' @@ -2108,8 +2108,8 @@ - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' - Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' + Use 'ThrowIfCancellationRequested' instead of checking 'IsCancellationRequested' and throwing 'OperationCanceledException' From f10db940d5b645ce575bad021a8dbaffaaaa3e9c Mon Sep 17 00:00:00 2001 From: NewellClark Date: Mon, 10 May 2021 15:03:56 -0400 Subject: [PATCH 08/15] Allow affirmative checks with else-clause --- ...TokenThrowIfCancellationRequested.Fixer.cs | 20 ++++++++ ...lationTokenThrowIfCancellationRequested.cs | 3 +- ...nTokenThrowIfCancellationRequestedTests.cs | 49 +++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs index 6b180c75cf..c6e0d26865 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs @@ -43,11 +43,30 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) // throw new OperationCanceledException(); // Replace with: // token.ThrowIfCancellationRequested(); + // + // For simple checks of the form: + // if (token.IsCancellationRequested) + // throw new OperationCanceledException(); + // else + // Frob(); + // Replace with: + // token.ThrowIfCancellationRequested(); + // Frob(); createChangedDocument = async token => { var editor = await DocumentEditor.CreateAsync(context.Document, token).ConfigureAwait(false); SyntaxNode expressionStatement = CreateThrowIfCancellationRequestedExpressionStatement(editor, conditional, propertyReference); editor.ReplaceNode(conditional.Syntax, expressionStatement); + + if (conditional.WhenFalse is IBlockOperation block) + { + editor.InsertAfter(expressionStatement, block.Operations.Select(x => x.Syntax.WithAdditionalAnnotations(Formatter.Annotation))); + } + else if (conditional.WhenFalse is not null) + { + editor.InsertAfter(expressionStatement, conditional.WhenFalse.Syntax); + } + return editor.GetChangedDocument(); }; } @@ -64,6 +83,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) var editor = await DocumentEditor.CreateAsync(context.Document, token).ConfigureAwait(false); SyntaxNode expressionStatement = CreateThrowIfCancellationRequestedExpressionStatement(editor, conditional, propertyReference); editor.ReplaceNode(conditional.Syntax, expressionStatement); + if (conditional.WhenTrue is IBlockOperation block) { editor.InsertAfter(expressionStatement, block.Operations.Select(x => x.Syntax.WithAdditionalAnnotations(Formatter.Annotation))); diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs index 14e465fef5..d0f0a71dc9 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs @@ -140,8 +140,7 @@ public bool IsSimpleAffirmativeCheck(IConditionalOperation conditional, [NotNull SymbolEqualityComparer.Default.Equals(propertyReference.Property, IsCancellationRequestedProperty) && whenTrueUnwrapped is IThrowOperation @throw && @throw.Exception is IObjectCreationOperation objectCreation && - IsDefaultOrTokenOperationCanceledExceptionCtor(objectCreation.Constructor) && - conditional.WhenFalse is null) + IsDefaultOrTokenOperationCanceledExceptionCtor(objectCreation.Constructor)) { isCancellationRequestedPropertyReference = propertyReference; return true; diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs index 1862e8ce62..d8d649b532 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs @@ -154,6 +154,55 @@ static IEnumerable ConditionalFormatStrings() } } + [Fact] + public Task SimpleAffirmativeCheckWithElseClause_ReportedAndFixed_CS() + { + var test = new VerifyCS.Test + { + TestCode = @" +using System; +using System.Threading; + +public class C +{ + private CancellationToken token; + + public void M() + { + {|#0:if (token.IsCancellationRequested) + { + throw new OperationCanceledException(); + } + else + { + Frob(); + }|} + } + + private void Frob() { } +}", + FixedCode = @" +using System; +using System.Threading; + +public class C +{ + private CancellationToken token; + + public void M() + { + token.ThrowIfCancellationRequested(); + Frob(); + } + + private void Frob() { } +}", + ExpectedDiagnostics = { CS.DiagnosticAt(0) }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + [Theory] [MemberData(nameof(Data_NegatedCheckWithElse_ReportedAndFixed_CS))] public Task NegatedCheckWithElse_ReportedAndFixed_CS(string operationCanceledExceptionCtor, string conditionalFormatString) From 688388ce7c323a289dd96f53b9cbceb8c003dfb7 Mon Sep 17 00:00:00 2001 From: NewellClark Date: Mon, 10 May 2021 16:43:21 -0400 Subject: [PATCH 09/15] Add VB test --- ...nTokenThrowIfCancellationRequestedTests.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs index d8d649b532..194eb159a7 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs @@ -203,6 +203,50 @@ private void Frob() { } return test.RunAsync(); } + [Fact] + public Task SimpleAffirmativeCheckWithElseClause_ReportedAndFixed_VB() + { + var test = new VerifyVB.Test + { + TestCode = @" +Imports System +Imports System.Threading + +Public Class C + Private token As CancellationToken + + Public Sub M() + {|#0:If token.IsCancellationRequested Then + Throw New OperationCanceledException() + Else + Frob() + End If|} + End Sub + + Private Sub Frob() + End Sub +End Class", + FixedCode = @" +Imports System +Imports System.Threading + +Public Class C + Private token As CancellationToken + + Public Sub M() + token.ThrowIfCancellationRequested() + Frob() + End Sub + + Private Sub Frob() + End Sub +End Class", + ExpectedDiagnostics = { VB.DiagnosticAt(0) }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + [Theory] [MemberData(nameof(Data_NegatedCheckWithElse_ReportedAndFixed_CS))] public Task NegatedCheckWithElse_ReportedAndFixed_CS(string operationCanceledExceptionCtor, string conditionalFormatString) From 369b057db059235d7506544244c67722dac5f924 Mon Sep 17 00:00:00 2001 From: NewellClark Date: Thu, 13 May 2021 12:36:13 -0400 Subject: [PATCH 10/15] Preserve trivia - Add tests ensuring trivia is preserved - Ensure trivia is preserved --- ...TokenThrowIfCancellationRequested.Fixer.cs | 9 +++- ...nTokenThrowIfCancellationRequestedTests.cs | 46 +++++++++++++++++-- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs index c6e0d26865..35f30b2e2d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs @@ -81,7 +81,8 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) createChangedDocument = async token => { var editor = await DocumentEditor.CreateAsync(context.Document, token).ConfigureAwait(false); - SyntaxNode expressionStatement = CreateThrowIfCancellationRequestedExpressionStatement(editor, conditional, propertyReference); + SyntaxNode expressionStatement = CreateThrowIfCancellationRequestedExpressionStatement(editor, conditional, propertyReference) + .WithAdditionalAnnotations(Formatter.Annotation); editor.ReplaceNode(conditional.Syntax, expressionStatement); if (conditional.WhenTrue is IBlockOperation block) @@ -120,7 +121,11 @@ private static SyntaxNode CreateThrowIfCancellationRequestedExpressionStatement( isCancellationRequestedPropertyReference.Instance.Syntax, nameof(CancellationToken.ThrowIfCancellationRequested)); SyntaxNode invocation = editor.Generator.InvocationExpression(memberAccess, Array.Empty()); - return editor.Generator.ExpressionStatement(invocation).WithTriviaFrom(conditional.Syntax); + var firstWhenTrueStatement = conditional.WhenTrue is IBlockOperation block ? block.Operations.FirstOrDefault() : conditional.WhenTrue; + + var result = editor.Generator.ExpressionStatement(invocation); + result = firstWhenTrueStatement is not null ? result.WithTriviaFrom(firstWhenTrueStatement.Syntax) : result; + return result.WithAdditionalAnnotations(Formatter.Annotation); } } } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs index 194eb159a7..0724f6736b 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs @@ -100,7 +100,7 @@ public Task SimpleAffirmativeCheck_ReportedAndFixed_VB(string operationCanceledE $"Throw New {operationCanceledExceptionCtor}"), 0); string fixedStatements = @"token.ThrowIfCancellationRequested()"; - + var testCode = VB.CreateBlock(testStatements); var test = new VerifyVB.Test { TestCode = VB.CreateBlock(testStatements), @@ -247,6 +247,48 @@ End Sub return test.RunAsync(); } + [Fact] + public Task TriviaInIfBlock_IsPreserved_CS() + { + var test = new VerifyCS.Test + { + TestCode = @" +using System; +using System.Threading; + +public class C +{ + private CancellationToken token; + + public void M() + { + {|#0:if (token.IsCancellationRequested) + { + // Comment + throw new OperationCanceledException(); + }|} + } +}", + FixedCode = @" +using System; +using System.Threading; + +public class C +{ + private CancellationToken token; + + public void M() + { + // Comment + token.ThrowIfCancellationRequested(); + } +}", + ExpectedDiagnostics = { CS.DiagnosticAt(0) }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + return test.RunAsync(); + } + [Theory] [MemberData(nameof(Data_NegatedCheckWithElse_ReportedAndFixed_CS))] public Task NegatedCheckWithElse_ReportedAndFixed_CS(string operationCanceledExceptionCtor, string conditionalFormatString) @@ -605,10 +647,8 @@ public static string CreateBlock(string statements, string members) { return Usings + @" Partial Public Class Body - " + IndentLines(members, " ") + @" Public Sub Run() - " + IndentLines(statements, " ") + @" End Sub End Class"; From 4d890bdc2dcd93a18af2c07ee3326e6f6ef6da22 Mon Sep 17 00:00:00 2001 From: NewellClark Date: Fri, 14 May 2021 10:50:59 -0400 Subject: [PATCH 11/15] Fix CI --- .../UseCancellationTokenThrowIfCancellationRequestedTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs index 0724f6736b..58d7e3d486 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequestedTests.cs @@ -100,7 +100,7 @@ public Task SimpleAffirmativeCheck_ReportedAndFixed_VB(string operationCanceledE $"Throw New {operationCanceledExceptionCtor}"), 0); string fixedStatements = @"token.ThrowIfCancellationRequested()"; - var testCode = VB.CreateBlock(testStatements); + var test = new VerifyVB.Test { TestCode = VB.CreateBlock(testStatements), From a55129174c56973ffbaf328f302787e296147858 Mon Sep 17 00:00:00 2001 From: NewellClark Date: Fri, 14 May 2021 11:00:04 -0400 Subject: [PATCH 12/15] Add shared attribute to fixer --- .../UseCancellationTokenThrowIfCancellationRequested.Fixer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs index 35f30b2e2d..42a423e55d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Immutable; +using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -20,7 +21,7 @@ namespace Microsoft.NetCore.Analyzers.Runtime /// Use instead of checking and /// throwing . /// - [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic)] + [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] public sealed class UseCancellationTokenThrowIfCancellationRequestedFixer : CodeFixProvider { public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(UseCancellationTokenThrowIfCancellationRequested.RuleId); From 24f0017764851915f9a8b9d653a625086536c0c8 Mon Sep 17 00:00:00 2001 From: Newell Clark Date: Mon, 17 May 2021 13:02:10 -0400 Subject: [PATCH 13/15] Update src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md Co-authored-by: Manish Vasani --- src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index a1d9d108a1..033aae59c4 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -8,10 +8,6 @@ CA1840 | Performance | Info | UseEnvironmentMembers, [Documentation](https://doc CA1841 | Performance | Info | PreferDictionaryContainsMethods, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1841) CA1842 | Performance | Info | DoNotUseWhenAllOrWaitAllWithSingleArgument, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1842) CA1843 | Performance | Info | DoNotUseWhenAllOrWaitAllWithSingleArgument, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1843) - -### New Rules -Rule ID | Category | Severity | Notes ---------|----------|----------|------- CA2250 | Usage | Info | UseCancellationTokenThrowIfCancellationRequested, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2250) ### Removed Rules From 3a4b0c83aaf89b895cd1358a80464e1dde66a8d3 Mon Sep 17 00:00:00 2001 From: Newell Clark Date: Mon, 17 May 2021 13:02:23 -0400 Subject: [PATCH 14/15] Update src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs Co-authored-by: Manish Vasani --- .../Runtime/UseCancellationTokenThrowIfCancellationRequested.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs index d0f0a71dc9..d59131fcd7 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.cs @@ -47,6 +47,7 @@ private static void OnCompilationStart(CompilationStartAnalysisContext context) return; context.RegisterOperationAction(AnalyzeOperation, OperationKind.Conditional); + return; void AnalyzeOperation(OperationAnalysisContext context) { From ea967fe3736e68ef73bbcf2ccd86ea15f292523c Mon Sep 17 00:00:00 2001 From: NewellClark Date: Mon, 17 May 2021 13:10:46 -0400 Subject: [PATCH 15/15] Simplify fixer --- .../UseCancellationTokenThrowIfCancellationRequested.Fixer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs index 42a423e55d..d6118ceae4 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseCancellationTokenThrowIfCancellationRequested.Fixer.cs @@ -82,6 +82,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) createChangedDocument = async token => { var editor = await DocumentEditor.CreateAsync(context.Document, token).ConfigureAwait(false); + SyntaxNode expressionStatement = CreateThrowIfCancellationRequestedExpressionStatement(editor, conditional, propertyReference) .WithAdditionalAnnotations(Formatter.Annotation); editor.ReplaceNode(conditional.Syntax, expressionStatement); @@ -95,8 +96,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) editor.InsertAfter(expressionStatement, conditional.WhenTrue.Syntax); } - // Ensure if-blocks with multiple statements maintain correct indentation. - return await Formatter.FormatAsync(editor.GetChangedDocument(), Formatter.Annotation, cancellationToken: token).ConfigureAwait(false); + return editor.GetChangedDocument(); }; } else