From 5447dc575a494653b82d2a8231ca71766d8734b8 Mon Sep 17 00:00:00 2001 From: NewellClark Date: Tue, 16 Feb 2021 12:34:47 -0500 Subject: [PATCH 01/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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 9338ad452f3ef543f82048debe70130bdd96cba5 Mon Sep 17 00:00:00 2001 From: randomnoise Date: Sat, 15 May 2021 18:01:09 +0300 Subject: [PATCH 13/23] Add .NET Core taint tracking --- ...ForCommandExecutionVulnerabilitiesTests.cs | 20 ++++++++++++++++ src/Utilities/Compiler/WellKnownTypeNames.cs | 1 + .../TaintedDataAnalysis/WebInputSources.cs | 23 +++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForCommandExecutionVulnerabilitiesTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForCommandExecutionVulnerabilitiesTests.cs index b41a0c6c05..3d11ed3bfc 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForCommandExecutionVulnerabilitiesTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForCommandExecutionVulnerabilitiesTests.cs @@ -148,5 +148,25 @@ protected void Page_Load(object sender, EventArgs e) }", GetCSharpResultAt(14, 13, 11, 24, "string ProcessStartInfo.Arguments", "void WebForm.Page_Load(object sender, EventArgs e)", "NameValueCollection HttpRequest.Form", "void WebForm.Page_Load(object sender, EventArgs e)")); } + + [Fact] + public async Task AspNetCoreHttpRequest_Process_Start_fileName_Diagnostic() + { + await VerifyCSharpWithDependenciesAsync(@" +using System.Diagnostics; +using Microsoft.AspNetCore.Mvc; + +public class HomeController : Controller +{ + public IActionResult Index() + { + string input = Request.Form[""in""]; + Process p = Process.Start(input); + + return View(); + } +}", + GetCSharpResultAt(10, 21, 9, 24, "Process Process.Start(string fileName)", "IActionResult HomeController.Index()", "IFormCollection HttpRequest.Form", "IActionResult HomeController.Index()")); + } } } diff --git a/src/Utilities/Compiler/WellKnownTypeNames.cs b/src/Utilities/Compiler/WellKnownTypeNames.cs index 87b4ad4082..8f035cc203 100644 --- a/src/Utilities/Compiler/WellKnownTypeNames.cs +++ b/src/Utilities/Compiler/WellKnownTypeNames.cs @@ -6,6 +6,7 @@ internal static class WellKnownTypeNames { public const string MicrosoftAspNetCoreAntiforgeryIAntiforgery = "Microsoft.AspNetCore.Antiforgery.IAntiforgery"; public const string MicrosoftAspNetCoreHttpCookieOptions = "Microsoft.AspNetCore.Http.CookieOptions"; + public const string MicrosoftAspNetCoreHttpHttpRequest = "Microsoft.AspNetCore.Http.HttpRequest"; public const string MicrosoftAspNetCoreHttpIResponseCookies = "Microsoft.AspNetCore.Http.IResponseCookies"; public const string MicrosoftAspNetCoreHttpInternalResponseCookies = "Microsoft.AspNetCore.Http.Internal.ResponseCookies"; public const string MicrosoftAspNetCoreMvcController = "Microsoft.AspNetCore.Mvc.Controller"; diff --git a/src/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/WebInputSources.cs b/src/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/WebInputSources.cs index 34765462ab..e151798586 100644 --- a/src/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/WebInputSources.cs +++ b/src/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/WebInputSources.cs @@ -34,6 +34,29 @@ static WebInputSources() var sourceInfosBuilder = PooledHashSet.GetInstance(); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.MicrosoftAspNetCoreHttpHttpRequest, + isInterface: false, + taintedProperties: new string[] { + "Body", + "ContentType", + "Cookies", + "Form", + "Headers", + "Host", + "Method", + "Path", + "PathBase", + "Protocol", + "Query", + "QueryString", + "RouteValues", + "Scheme", + }, + taintedMethods: new string[] { + "ReadFormAsync", + }); + sourceInfosBuilder.AddSourceInfoSpecifyingTaintedTargets( WellKnownTypeNames.SystemWebHttpServerUtility, isInterface: false, From eef2c6fb2dd30c5efd1d4f00eda806944ade18fc Mon Sep 17 00:00:00 2001 From: randomnoise Date: Sat, 15 May 2021 21:11:38 +0300 Subject: [PATCH 14/23] Add unit tests for different sink kinds Additional unit tests are for CA3001, CA3003, CA3005, CA3009, CA3011 and CA3012 security rules --- ...CodeForDllInjectionVulnerabilitiesTests.cs | 20 +++++++++++++++ ...ileCanonicalizationVulnerabilitiesTests.cs | 20 +++++++++++++++ ...odeForLdapInjectionVulnerabilitiesTests.cs | 20 +++++++++++++++ ...deForRegexInjectionVulnerabilitiesTests.cs | 20 +++++++++++++++ ...CodeForSqlInjectionVulnerabilitiesTests.cs | 25 +++++++++++++++++++ ...CodeForXmlInjectionVulnerabilitiesTests.cs | 23 +++++++++++++++++ 6 files changed, 128 insertions(+) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForDllInjectionVulnerabilitiesTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForDllInjectionVulnerabilitiesTests.cs index aecd732199..4f76bb7f24 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForDllInjectionVulnerabilitiesTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForDllInjectionVulnerabilitiesTests.cs @@ -130,5 +130,25 @@ protected void Page_Load(object sender, EventArgs e) }", GetCSharpResultAt(15, 9, 14, 24, "int AppDomain.ExecuteAssembly(string assemblyFile)", "void WebForm.Page_Load(object sender, EventArgs e)", "NameValueCollection HttpRequest.Form", "void WebForm.Page_Load(object sender, EventArgs e)")); } + + [Fact] + public async Task AspNetCoreHttpRequest_AppDomain_ExecuteAssembly_Diagnostic() + { + await VerifyCSharpWithDependenciesAsync(@" +using System; +using Microsoft.AspNetCore.Mvc; + +public class HomeController : Controller +{ + public IActionResult Index() + { + string input = Request.Form[""in""]; + AppDomain.CurrentDomain.ExecuteAssembly(input); + + return View(); + } +}", + GetCSharpResultAt(10, 9, 9, 24, "int AppDomain.ExecuteAssembly(string assemblyFile)", "IActionResult HomeController.Index()", "IFormCollection HttpRequest.Form", "IActionResult HomeController.Index()")); + } } } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForFileCanonicalizationVulnerabilitiesTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForFileCanonicalizationVulnerabilitiesTests.cs index 4cc8aa997d..1be4b67305 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForFileCanonicalizationVulnerabilitiesTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForFileCanonicalizationVulnerabilitiesTests.cs @@ -135,5 +135,25 @@ protected void Page_Load(object sender, EventArgs e) } }"); } + + [Fact] + public async Task AspNetCoreHttpRequest_FileInfo_Constructor_Diagnostic() + { + await VerifyCSharpWithDependenciesAsync(@" +using System.IO; +using Microsoft.AspNetCore.Mvc; + +public class HomeController : Controller +{ + public IActionResult Index() + { + string input = Request.Form[""in""]; + new FileInfo(input); + + return View(); + } +}", + GetCSharpResultAt(10, 9, 9, 24, "FileInfo.FileInfo(string fileName)", "IActionResult HomeController.Index()", "IFormCollection HttpRequest.Form", "IActionResult HomeController.Index()")); + } } } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForLdapInjectionVulnerabilitiesTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForLdapInjectionVulnerabilitiesTests.cs index e101c500db..81c1506b4b 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForLdapInjectionVulnerabilitiesTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForLdapInjectionVulnerabilitiesTests.cs @@ -184,5 +184,25 @@ protected void Page_Load(object sender, EventArgs e) } }"); } + + [Fact] + public async Task AspNetCoreHttpRequest_DirectoryEntry_Path_Diagnostic() + { + await VerifyCSharpWithDependenciesAsync(@" +using System.DirectoryServices; +using Microsoft.AspNetCore.Mvc; + +public class HomeController : Controller +{ + public IActionResult Index() + { + string input = Request.Form[""in""]; + new DirectoryEntry(input); + + return View(); + } +}", + GetCSharpResultAt(10, 9, 9, 24, "DirectoryEntry.DirectoryEntry(string path)", "IActionResult HomeController.Index()", "IFormCollection HttpRequest.Form", "IActionResult HomeController.Index()")); + } } } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForRegexInjectionVulnerabilitiesTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForRegexInjectionVulnerabilitiesTests.cs index 0e88a950c9..ad5d4c2e56 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForRegexInjectionVulnerabilitiesTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForRegexInjectionVulnerabilitiesTests.cs @@ -171,5 +171,25 @@ protected void Page_Load(object sender, EventArgs e) } }"); } + + [Fact] + public async Task AspNetCoreHttpRequest_Process_Start_fileName_Diagnostic() + { + await VerifyCSharpWithDependenciesAsync(@" +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Mvc; + +public class HomeController : Controller +{ + public IActionResult Index() + { + string input = Request.Form[""in""]; + new Regex(input); + + return View(); + } +}", + GetCSharpResultAt(10, 9, 9, 24, "Regex.Regex(string pattern)", "IActionResult HomeController.Index()", "IFormCollection HttpRequest.Form", "IActionResult HomeController.Index()")); + } } } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForSqlInjectionVulnerabilitiesTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForSqlInjectionVulnerabilitiesTests.cs index 9be69a909c..3184874935 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForSqlInjectionVulnerabilitiesTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForSqlInjectionVulnerabilitiesTests.cs @@ -3849,5 +3849,30 @@ public async Task HttpServerUtility_HtmlEncode_StringWriterOverload_WrongSanitiz }, }.RunAsync(); } + + [Fact] + public async Task AspNetCoreHttpRequest_Form_Direct_Diagnostic() + { + await VerifyCSharpWithDependenciesAsync(@" +using System.Data; +using System.Data.SqlClient; +using Microsoft.AspNetCore.Mvc; + +public class HomeController : Controller +{ + public IActionResult Index() + { + string input = Request.Form[""in""]; + var sqlCommand = new SqlCommand() + { + CommandText = input, + CommandType = CommandType.Text, + }; + + return View(); + } +}", + GetCSharpResultAt(13, 13, 10, 24, "string SqlCommand.CommandText", "IActionResult HomeController.Index()", "IFormCollection HttpRequest.Form", "IActionResult HomeController.Index()")); + } } } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForXmlInjectionVulnerabilitiesTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForXmlInjectionVulnerabilitiesTests.cs index 435a8e3aac..9c34abc91c 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForXmlInjectionVulnerabilitiesTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Security/ReviewCodeForXmlInjectionVulnerabilitiesTests.cs @@ -312,5 +312,28 @@ protected void Page_Load(object sender, EventArgs e) } }"); } + + [Fact] + public async Task AspNetCoreHttpRequest_XmlTextWriter_WriteRaw_Diagnostic() + { + await VerifyCSharpWithDependenciesAsync(@" +using System.IO; +using System.Text; +using System.Xml; +using Microsoft.AspNetCore.Mvc; + +public class HomeController : Controller +{ + public IActionResult Index() + { + string input = Request.Form[""in""]; + var xtw = new XmlTextWriter(new MemoryStream(), Encoding.UTF8); + xtw.WriteRaw(input); + + return View(); + } +}", + GetCSharpResultAt(13, 9, 11, 24, "void XmlTextWriter.WriteRaw(string data)", "IActionResult HomeController.Index()", "IFormCollection HttpRequest.Form", "IActionResult HomeController.Index()")); + } } } \ No newline at end of file From a0b60a3242a4e691ffa4d1f2fb3d7460933bb76a Mon Sep 17 00:00:00 2001 From: Youssef Victor Date: Mon, 17 May 2021 15:41:13 +0200 Subject: [PATCH 15/23] Fix links --- docs/NetCore_GettingStarted.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/NetCore_GettingStarted.md b/docs/NetCore_GettingStarted.md index 8c2abbc556..5d68ec08bc 100644 --- a/docs/NetCore_GettingStarted.md +++ b/docs/NetCore_GettingStarted.md @@ -60,7 +60,7 @@ 8. In case no any failure introduce an error somewhere to prove that the rule ran. - Be careful about in which project you are producing an error, choose an API not having reference from other APIs, else all dependent API's will fail. 9. If failures found, repeat step 4-5 to evaluate and address all warnings. - - In case you want to [debug some failures](#Debugging-analyzer-with-runtiem-repo-projects). + - In case you want to [debug some failures](#debugging-analyzer-with-runtime-repo-projects). ## Testing against the Roslyn repo @@ -75,7 +75,7 @@ The diagnostics reported by the analyzer will be listed in Output.txt. ## Debugging analyzer with runtime repo projects -1. Copy over debug build of analyzer assemblies on top of NetAnalyzers nuget package in your packages folder. (Instructions are same as the step 1 and 2 of [Testing against the Runtime repo step](#Testing-against-the-Runtime-and-Roslyn-analyzers-repo)) +1. Copy over debug build of analyzer assemblies on top of NetAnalyzers nuget package in your packages folder. (Instructions are same as the step 1 and 2 of [Testing against the Runtime repo step](#testing-against-the-runtime-and-roslyn-analyzers-repo)) 2. Start VS and open a project you want to debug 3. Note the process ID for `ServiceHub.RoslynCodeAnalysisService.exe` corresponding to that VS instance - If you are using `Visual Studio` older than version `16.8 Preview2` then analyzers run in `devenv.exe`, you will need to attach that process instead From a4a206ab66a005e7c319546459ab9b5076fd70a0 Mon Sep 17 00:00:00 2001 From: NewellClark Date: Mon, 10 May 2021 13:12:29 -0400 Subject: [PATCH 16/23] Fix #5025 - Add unit tests for C# - Add unit tests for 'protected internal' and 'private protected' - Fix analyzer to also flag 'protected internal' and 'private protected' members --- ...NotDeclareProtectedMembersInSealedTypes.cs | 9 ++- ...clareProtectedMembersInSealedTypesTests.cs | 72 ++++++++++++++++--- 2 files changed, 71 insertions(+), 10 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDeclareProtectedMembersInSealedTypes.cs b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDeclareProtectedMembersInSealedTypes.cs index 254c50d8f8..a5520cb472 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDeclareProtectedMembersInSealedTypes.cs +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDeclareProtectedMembersInSealedTypes.cs @@ -49,7 +49,7 @@ public override void Initialize(AnalysisContext context) return; } - if (!symbol.IsProtected() || + if (!IsAnyProtectedVariant(symbol) || symbol.IsOverride || !symbol.ContainingType.IsSealed) { @@ -64,5 +64,12 @@ public override void Initialize(AnalysisContext context) context.ReportDiagnostic(symbol.CreateDiagnostic(Rule, symbol.Name, symbol.ContainingType.Name)); }, SymbolKind.Method, SymbolKind.Property, SymbolKind.Event, SymbolKind.Field); } + + private static bool IsAnyProtectedVariant(ISymbol symbol) + { + return symbol.DeclaredAccessibility == Accessibility.Protected || + symbol.DeclaredAccessibility == Accessibility.ProtectedOrInternal || + symbol.DeclaredAccessibility == Accessibility.ProtectedAndInternal; + } } } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDeclareProtectedMembersInSealedTypesTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDeclareProtectedMembersInSealedTypesTests.cs index 4efe901774..2fa71f1b38 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDeclareProtectedMembersInSealedTypesTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotDeclareProtectedMembersInSealedTypesTests.cs @@ -2,6 +2,9 @@ using System.Threading.Tasks; using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines.DoNotDeclareProtectedMembersInSealedTypes, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines.DoNotDeclareProtectedMembersInSealedTypes, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; @@ -10,6 +13,7 @@ namespace Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines.UnitTests { public class DoNotDeclareProtectedMembersInSealedTypesTests { + [Fact] public async Task ProtectedSubInNotInheritable_Diagnostic() { @@ -21,25 +25,75 @@ End Sub VerifyVB.Diagnostic().WithSpan(3, 19, 3, 20).WithArguments("M", "C")); } - [Fact] - public async Task ProtectedMemberInNotInheritable_Diagnostic() + [Theory] + [InlineData("protected")] + [InlineData("protected internal")] + [InlineData("private protected")] + public Task AnyProtectedVariantMembersInSealed_Diagnostic(string accessModifier) { - await VerifyVB.VerifyAnalyzerAsync(@" + return new VerifyCS.Test + { + TestState = + { + Sources = + { + $@" +using System; + +public sealed class C +{{ + {accessModifier} int [|SomeField|]; + + {accessModifier} int [|SomeProperty|] {{ [|get|]; [|set|]; }} + + {accessModifier} event EventHandler [|SomeEvent|]; + + {accessModifier} void [|SomeMethod|]() {{ }} +}}" + }, + AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true + +[*] +dotnet_code_quality.CA1047.api_surface = All +") } + } + }.RunAsync(); + } + + [Theory] + [InlineData("Protected")] + [InlineData("Protected Friend")] + [InlineData("Private Protected")] + public Task AnyProtectedVariantMemberInNotInheritable_Diagnostic(string accessModifier) + { + return new VerifyVB.Test + { + TestState = + { + Sources = { $@" Imports System Public NotInheritable Class C - Protected [|SomeField|] As Integer + {accessModifier} [|SomeField|] As Integer - Protected Property [|SomeProperty|] As Integer + {accessModifier} Property [|SomeProperty|] As Integer - Protected Event [|SomeEvent|] As EventHandler + {accessModifier} Event [|SomeEvent|] As EventHandler - Protected Sub [|SomeSub|]() + {accessModifier} Sub [|SomeSub|]() End Sub - Protected Function [|SomeFunction|]() As Integer + {accessModifier} Function [|SomeFunction|]() As Integer End Function -End Class"); +End Class" + }, + AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true + +[*] +dotnet_code_quality.CA1047.api_surface = All +") } + } + }.RunAsync(); } [Fact] From c8d91859654e827475fd4be66be8f727669b2a2e Mon Sep 17 00:00:00 2001 From: NewellClark Date: Fri, 14 May 2021 14:15:16 -0400 Subject: [PATCH 17/23] Add tests --- ...iseExceptionsInUnexpectedLocationsTests.cs | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocationsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocationsTests.cs index 5532ed4a16..c8ca5bc745 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocationsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocationsTests.cs @@ -621,6 +621,67 @@ await VerifyVB.VerifyAnalyzerAsync(code, GetBasicNoExceptionsResultAt(9, 9, "op_Inequality", "Exception")); } + [Fact] + public async Task CSharpComparisonOperatorWithExceptions() + { + var code = @" +using System; + +public class C +{ + public static bool operator <=(C left, C right) + { + throw new {|#0:Exception|}(); + } + public static bool operator >=(C left, C right) + { + throw new {|#1:Exception|}(); + } + public static bool operator <(C left, C right) + { + throw new {|#2:Exception|}(); + } + public static bool operator >(C left, C right) + { + throw new {|#3:Exception|}(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync( + code, + GetCSharpNoExceptionsResultAt(0, "op_LessThanOrEqual", "Exception"), + GetCSharpNoExceptionsResultAt(1, "op_GreaterThanOrEqual", "Exception"), + GetCSharpNoExceptionsResultAt(2, "op_LessThan", "Exception"), + GetCSharpNoExceptionsResultAt(3, "op_GreaterThan", "Exception")); + } + + [Fact] + public async Task BasicComparisonOperatorWithExceptions() + { + var code = @" +Imports System + +Public Class C + Public Shared Operator <=(left As C, right As C) As Boolean + Throw New {|#0:Exception|}() + End Operator + Public Shared Operator >=(left As C, right As C) As Boolean + Throw New {|#1:Exception|}() + End Operator + Public Shared Operator <(left As C, right As C) As Boolean + Throw New {|#2:Exception|}() + End Operator + Public Shared Operator >(left As C, right As C) As Boolean + Throw New {|#3:Exception|}() + End Operator +End Class"; + await VerifyVB.VerifyAnalyzerAsync( + code, + GetBasicNoExceptionsResultAt(0, "op_LessThanOrEqual", "Exception"), + GetBasicNoExceptionsResultAt(1, "op_GreaterThanOrEqual", "Exception"), + GetBasicNoExceptionsResultAt(2, "op_LessThan", "Exception"), + GetBasicNoExceptionsResultAt(3, "op_GreaterThan", "Exception")); + } + [Fact] public async Task CSharpImplicitOperatorWithExceptions() { @@ -677,6 +738,12 @@ private static DiagnosticResult GetCSharpNoExceptionsResultAt(int line, int colu { return GetCSharpResultAt(line, column, DoNotRaiseExceptionsInUnexpectedLocationsAnalyzer.NoAllowedExceptionsRule, methodName, exceptionName); } + private static DiagnosticResult GetCSharpNoExceptionsResultAt(int markupKey, string methodName, string exceptionName) + { + return VerifyCS.Diagnostic(DoNotRaiseExceptionsInUnexpectedLocationsAnalyzer.NoAllowedExceptionsRule) + .WithLocation(markupKey) + .WithArguments(methodName, exceptionName); + } private static DiagnosticResult GetBasicPropertyResultAt(int line, int column, string methodName, string exceptionName) { @@ -690,6 +757,12 @@ private static DiagnosticResult GetBasicNoExceptionsResultAt(int line, int colum { return GetBasicResultAt(line, column, DoNotRaiseExceptionsInUnexpectedLocationsAnalyzer.NoAllowedExceptionsRule, methodName, exceptionName); } + private static DiagnosticResult GetBasicNoExceptionsResultAt(int markupKey, string methodName, string exceptionName) + { + return VerifyVB.Diagnostic(DoNotRaiseExceptionsInUnexpectedLocationsAnalyzer.NoAllowedExceptionsRule) + .WithLocation(markupKey) + .WithArguments(methodName, exceptionName); + } private static DiagnosticResult GetCSharpResultAt(int line, int column, DiagnosticDescriptor rule, params string[] arguments) #pragma warning disable RS0030 // Do not used banned APIs From da1a0d8a31b3bf0edd2ce26807e5dc3ad3658fc0 Mon Sep 17 00:00:00 2001 From: NewellClark Date: Fri, 14 May 2021 14:56:15 -0400 Subject: [PATCH 18/23] Add checks for comparison operators --- ...oNotRaiseExceptionsInUnexpectedLocations.cs | 18 ++++++++++++++++++ ...aiseExceptionsInUnexpectedLocationsTests.cs | 16 ++++++++-------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocations.cs b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocations.cs index 992356d7d9..449b327ab5 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocations.cs +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocations.cs @@ -187,6 +187,9 @@ private static List GetMethodCategories(Compilation compilation) new MethodCategory(IsEqualityOperator, true, NoAllowedExceptionsRule), + new MethodCategory(IsComparisonOperator, true, + NoAllowedExceptionsRule), + new MethodCategory(IsGetHashCodeOverride, true, NoAllowedExceptionsRule), @@ -329,6 +332,21 @@ private static bool IsEqualityOperator(IMethodSymbol method, Compilation compila }; } + private static bool IsComparisonOperator(IMethodSymbol method, Compilation compilation) + { + if (!method.IsStatic || !method.IsPublic()) + return false; + + return method.Name switch + { + WellKnownMemberNames.LessThanOperatorName + or WellKnownMemberNames.GreaterThanOperatorName + or WellKnownMemberNames.LessThanOrEqualOperatorName + or WellKnownMemberNames.GreaterThanOrEqualOperatorName => true, + _ => false, + }; + } + private static bool IsImplicitCastOperator(IMethodSymbol method, Compilation compilation) { if (!method.IsStatic || !method.IsPublic()) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocationsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocationsTests.cs index c8ca5bc745..92d10b09de 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocationsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocationsTests.cs @@ -631,19 +631,19 @@ public class C { public static bool operator <=(C left, C right) { - throw new {|#0:Exception|}(); + {|#0:throw new Exception();|} } public static bool operator >=(C left, C right) { - throw new {|#1:Exception|}(); + {|#1:throw new Exception();|} } public static bool operator <(C left, C right) { - throw new {|#2:Exception|}(); + {|#2:throw new Exception();|} } public static bool operator >(C left, C right) { - throw new {|#3:Exception|}(); + {|#3:throw new Exception();|} } }"; await VerifyCS.VerifyAnalyzerAsync( @@ -662,16 +662,16 @@ Imports System Public Class C Public Shared Operator <=(left As C, right As C) As Boolean - Throw New {|#0:Exception|}() + {|#0:Throw New Exception()|} End Operator Public Shared Operator >=(left As C, right As C) As Boolean - Throw New {|#1:Exception|}() + {|#1:Throw New Exception()|} End Operator Public Shared Operator <(left As C, right As C) As Boolean - Throw New {|#2:Exception|}() + {|#2:Throw New Exception()|} End Operator Public Shared Operator >(left As C, right As C) As Boolean - Throw New {|#3:Exception|}() + {|#3:Throw New Exception()|} End Operator End Class"; await VerifyVB.VerifyAnalyzerAsync( From a32e91a25171f0b61629a6a9289731532369c767 Mon Sep 17 00:00:00 2001 From: NewellClark Date: Mon, 17 May 2021 12:57:41 -0400 Subject: [PATCH 19/23] Apply suggested changes --- ...NotRaiseExceptionsInUnexpectedLocations.cs | 20 +++---------------- ...iseExceptionsInUnexpectedLocationsTests.cs | 1 + 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocations.cs b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocations.cs index 449b327ab5..8d1f1b3086 100644 --- a/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocations.cs +++ b/src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocations.cs @@ -184,9 +184,6 @@ private static List GetMethodCategories(Compilation compilation) new MethodCategory(IsEqualsOverrideOrInterfaceImplementation, true, NoAllowedExceptionsRule), - new MethodCategory(IsEqualityOperator, true, - NoAllowedExceptionsRule), - new MethodCategory(IsComparisonOperator, true, NoAllowedExceptionsRule), @@ -319,19 +316,6 @@ private static bool IsFinalizer(IMethodSymbol method, Compilation compilation) return method.IsFinalizer(); } - private static bool IsEqualityOperator(IMethodSymbol method, Compilation compilation) - { - if (!method.IsStatic || !method.IsPublic()) - return false; - - return method.Name switch - { - WellKnownMemberNames.EqualityOperatorName - or WellKnownMemberNames.InequalityOperatorName => true, - _ => false, - }; - } - private static bool IsComparisonOperator(IMethodSymbol method, Compilation compilation) { if (!method.IsStatic || !method.IsPublic()) @@ -339,7 +323,9 @@ private static bool IsComparisonOperator(IMethodSymbol method, Compilation compi return method.Name switch { - WellKnownMemberNames.LessThanOperatorName + WellKnownMemberNames.EqualityOperatorName + or WellKnownMemberNames.InequalityOperatorName + or WellKnownMemberNames.LessThanOperatorName or WellKnownMemberNames.GreaterThanOperatorName or WellKnownMemberNames.LessThanOrEqualOperatorName or WellKnownMemberNames.GreaterThanOrEqualOperatorName => true, diff --git a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocationsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocationsTests.cs index 92d10b09de..871789bfc9 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocationsTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/DoNotRaiseExceptionsInUnexpectedLocationsTests.cs @@ -622,6 +622,7 @@ await VerifyVB.VerifyAnalyzerAsync(code, } [Fact] + [WorkItem(5021, "https://github.com/dotnet/roslyn-analyzers/issues/5021")] public async Task CSharpComparisonOperatorWithExceptions() { var code = @" From 24f0017764851915f9a8b9d653a625086536c0c8 Mon Sep 17 00:00:00 2001 From: Newell Clark Date: Mon, 17 May 2021 13:02:10 -0400 Subject: [PATCH 20/23] 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 21/23] 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 22/23] 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 From f0558eb4d594daf86d48cff76691e9b942b6306e Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Wed, 19 May 2021 19:10:00 -0700 Subject: [PATCH 23/23] Update Versions.props move release/6.0.1xx to preview 6 --- eng/Versions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Versions.props b/eng/Versions.props index 1bb2b1edb1..63e69f23bf 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -3,7 +3,7 @@ 3.3.3 beta1 6.0.0 - preview5 + preview6 $(VersionPrefix)