diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
index 4cb7d58e49..39da4a4813 100644
--- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
+++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
@@ -4,5 +4,9 @@
Rule ID | Category | Severity | Notes
--------|----------|----------|-------
+CA1510 | Maintainability | Info | UseExceptionThrowHelpers, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1510)
+CA1511 | Maintainability | Info | UseExceptionThrowHelpers, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1511)
+CA1512 | Maintainability | Info | UseExceptionThrowHelpers, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1512)
+CA1513 | Maintainability | Info | UseExceptionThrowHelpers, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1513)
CA1856 | Performance | Error | ConstantExpectedAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1856)
CA1857 | Performance | Warning | ConstantExpectedAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1857)
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx
index bd0da36e43..b262fb070b 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx
@@ -2031,4 +2031,26 @@
Starting with .NET 7 the explicit conversion '{0}' will throw when overflowing in a checked context. Wrap the expression with an 'unchecked' statement to restore the .NET 6 behavior.
+
+ Use ArgumentNullException throw helper
+
+
+ Use ArgumentException throw helper
+
+
+ Use ArgumentOutOfRangeException throw helper
+
+
+ Use ObjectDisposedException throw helper
+
+
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+
+
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+
+ Use '{0}.{1}'
+
+
\ No newline at end of file
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseExceptionThrowHelpers.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseExceptionThrowHelpers.cs
new file mode 100644
index 0000000000..0ff65a9265
--- /dev/null
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseExceptionThrowHelpers.cs
@@ -0,0 +1,534 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using Analyzer.Utilities;
+using Analyzer.Utilities.Extensions;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Operations;
+
+namespace Microsoft.NetCore.Analyzers.Runtime
+{
+ using static MicrosoftNetCoreAnalyzersResources;
+
+ /// Analyzer that recommends using exception Throw helpers on built-in exception types.
+ [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
+ public sealed class UseExceptionThrowHelpers : DiagnosticAnalyzer
+ {
+ internal const string UseArgumentNullExceptionThrowIfNullRuleId = "CA1510";
+ internal const string UseArgumentExceptionThrowIfNullOrEmptyRuleId = "CA1511";
+ internal const string UseArgumentOutOfRangeExceptionThrowIfRuleId = "CA1512";
+ internal const string UseObjectDisposedExceptionThrowIfRuleId = "CA1513";
+
+ /// Name of the key into the properties dictionary where an optional throw helper method name is stored for the fixer.
+ internal const string MethodNamePropertyKey = "MethodNameProperty";
+
+ // if (arg is null) throw new ArgumentNullException(nameof(arg)); => ArgumentNullException.ThrowIfNull(arg);
+ // if (arg == null) throw new ArgumentNullException(nameof(arg)); => ArgumentNullException.ThrowIfNull(arg);
+ internal static readonly DiagnosticDescriptor UseArgumentNullExceptionThrowIfNullRule = DiagnosticDescriptorHelper.Create(UseArgumentNullExceptionThrowIfNullRuleId,
+ CreateLocalizableResourceString(nameof(UseArgumentNullExceptionThrowHelperTitle)),
+ CreateLocalizableResourceString(nameof(UseThrowHelperMessage)),
+ DiagnosticCategory.Maintainability,
+ RuleLevel.IdeSuggestion,
+ CreateLocalizableResourceString(nameof(UseThrowHelperDescription)),
+ isPortedFxCopRule: false,
+ isDataflowRule: false);
+
+ // if (string.IsNullOrEmpty(arg)) throw new ArgumentException(...); => ArgumentException.ThrowIfNullOrEmpty(arg);
+ // if (arg is null || arg.Length == 0) throw new ArgumentException(...); => ArgumentException.ThrowIfNullOrEmpty(arg);
+ // if (arg == null || arg == string.Empty) throw new ArgumentException(...); => ArgumentException.ThrowIfNullOrEmpty(arg);
+ internal static readonly DiagnosticDescriptor UseArgumentExceptionThrowIfNullOrEmptyRule = DiagnosticDescriptorHelper.Create(UseArgumentExceptionThrowIfNullOrEmptyRuleId,
+ CreateLocalizableResourceString(nameof(UseArgumentExceptionThrowHelperTitle)),
+ CreateLocalizableResourceString(nameof(UseThrowHelperMessage)),
+ DiagnosticCategory.Maintainability,
+ RuleLevel.IdeSuggestion,
+ CreateLocalizableResourceString(nameof(UseThrowHelperDescription)),
+ isPortedFxCopRule: false,
+ isDataflowRule: false);
+
+ // if (arg == 0) throw new ArgumentOutOfRangeException(...); => ArgumentOutOfRangeException.ThrowIfZero(arg);
+ // if (arg is 0) throw new ArgumentOutOfRangeException(...); => ArgumentOutOfRangeException.ThrowIfZero(arg);
+ // if (arg < 0) throw new ArgumentOutOfRangeException(...); => ArgumentOutOfRangeException.ThrowIfNegative(arg);
+ // if (arg <= 0) throw new ArgumentOutOfRangeException(...); => ArgumentOutOfRangeException.ThrowIfNegativeOrZero(arg);
+ // if (arg > 42) throw new ArgumentOutOfRangeException(...); => ArgumentOutOfRangeException.ThrowIfGreaterThan(arg, 42);
+ // if (arg >= 42) throw new ArgumentOutOfRangeException(...); => ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(arg, 42);
+ // if (arg < 42) throw new ArgumentOutOfRangeException(...); => ArgumentOutOfRangeException.ThrowIfLessThan(arg, 42);
+ // if (arg <= 42) throw new ArgumentOutOfRangeException(...); => ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(arg, 42);
+ internal static readonly DiagnosticDescriptor UseArgumentOutOfRangeExceptionThrowIfRule = DiagnosticDescriptorHelper.Create(UseArgumentOutOfRangeExceptionThrowIfRuleId,
+ CreateLocalizableResourceString(nameof(UseArgumentOutOfRangeExceptionThrowHelperTitle)),
+ CreateLocalizableResourceString(nameof(UseThrowHelperMessage)),
+ DiagnosticCategory.Maintainability,
+ RuleLevel.IdeSuggestion,
+ CreateLocalizableResourceString(nameof(UseThrowHelperDescription)),
+ isPortedFxCopRule: false,
+ isDataflowRule: false);
+
+ // if (condition) throw new ObjectDisposedException(...)
+ internal static readonly DiagnosticDescriptor UseObjectDisposedExceptionThrowIfRule = DiagnosticDescriptorHelper.Create(UseObjectDisposedExceptionThrowIfRuleId,
+ CreateLocalizableResourceString(nameof(UseObjectDisposedExceptionThrowHelperTitle)),
+ CreateLocalizableResourceString(nameof(UseThrowHelperMessage)),
+ DiagnosticCategory.Maintainability,
+ RuleLevel.IdeSuggestion,
+ CreateLocalizableResourceString(nameof(UseThrowHelperDescription)),
+ isPortedFxCopRule: false,
+ isDataflowRule: false);
+
+ public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(
+ UseArgumentNullExceptionThrowIfNullRule,
+ UseArgumentExceptionThrowIfNullOrEmptyRule,
+ UseArgumentOutOfRangeExceptionThrowIfRule,
+ UseObjectDisposedExceptionThrowIfRule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.EnableConcurrentExecution();
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.RegisterCompilationStartAction(context =>
+ {
+ // Get the relevant exception types.
+ INamedTypeSymbol? ane = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemArgumentNullException);
+ INamedTypeSymbol? aoore = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemArgumentOutOfRangeException);
+ INamedTypeSymbol? ae = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemArgumentException);
+ INamedTypeSymbol? ode = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemObjectDisposedException);
+ if (ane is null || aoore is null || ae is null || ode is null)
+ {
+ return;
+ }
+
+ // Get other required helper methods. Some diagnostics don't require these, but we also don't care
+ // about reporting those on targets that don't have these, as they've been around forever.
+ INamedTypeSymbol stringType = context.Compilation.GetSpecialType(SpecialType.System_String);
+ INamedTypeSymbol objectType = context.Compilation.GetSpecialType(SpecialType.System_Object);
+ ISymbol? stringIsNullOrEmpty = stringType.GetMembers("IsNullOrEmpty").FirstOrDefault();
+ ISymbol? stringLength = stringType.GetMembers("Length").FirstOrDefault();
+ ISymbol? stringEmpty = stringType.GetMembers("Empty").FirstOrDefault();
+ ISymbol? getType = objectType.GetMembers("GetType").FirstOrDefault();
+ if (stringIsNullOrEmpty is null || stringLength is null || stringEmpty is null || getType is null)
+ {
+ return;
+ }
+
+ // Get the ThrowXx helpers on those types. Some of these may not exist.
+ // If we don't have any such helpers available, there's nothing to do.
+ ISymbol? aneThrowIfNull = ane.GetMembers("ThrowIfNull").FirstOrDefault();
+ ISymbol? aeThrowIfNullOrEmpty = ae.GetMembers("ThrowIfNullOrEmpty").FirstOrDefault();
+ ISymbol? odeThrowIf = ode.GetMembers("ThrowIf").FirstOrDefault();
+ ISymbol? aooreThrowIfZero = aoore.GetMembers("ThrowIfZero").FirstOrDefault();
+ ISymbol? aooreThrowIfNegative = aoore.GetMembers("ThrowIfNegative").FirstOrDefault();
+ ISymbol? aooreThrowIfNegativeOrZero = aoore.GetMembers("ThrowIfNegativeOrZero").FirstOrDefault();
+ ISymbol? aooreThrowIfGreaterThan = aoore.GetMembers("ThrowIfGreaterThan").FirstOrDefault();
+ ISymbol? aooreThrowIfGreaterThanOrEqual = aoore.GetMembers("aooreThrowIfGreaterThanOrEqual").FirstOrDefault();
+ ISymbol? aooreThrowIfLessThan = aoore.GetMembers("ThrowIfLessThan").FirstOrDefault();
+ ISymbol? aooreThrowIfLessThanOrEqual = aoore.GetMembers("ThrowIfLessThanOrEqual").FirstOrDefault();
+ if (aneThrowIfNull is null && aeThrowIfNullOrEmpty is null && odeThrowIf is null &&
+ aooreThrowIfZero is null && aooreThrowIfNegative is null && aooreThrowIfNegativeOrZero is null &&
+ aooreThrowIfGreaterThan is null && aooreThrowIfGreaterThanOrEqual is null &&
+ aooreThrowIfLessThan is null && aooreThrowIfLessThanOrEqual is null)
+ {
+ return;
+ }
+
+ // If we have any of the ArgumentOutOfRangeException.Throw methods, we're likely to have all of them.
+ bool hasAnyAooreThrow =
+ aooreThrowIfZero is not null || aooreThrowIfNegative is not null || aooreThrowIfNegativeOrZero is not null ||
+ aooreThrowIfGreaterThan is not null || aooreThrowIfGreaterThanOrEqual is not null ||
+ aooreThrowIfLessThan is not null || aooreThrowIfLessThanOrEqual is not null;
+
+ // Look for throw operations.
+ context.RegisterOperationAction(context =>
+ {
+ var throwOperation = (IThrowOperation)context.Operation;
+
+ // Try to get the exception object creation operation. As a heuristic, avoid recommending replacing
+ // any exceptions where a meaningful message may have been provided. This is an attempt to reduce
+ // false positives, at the expense of potentially more false negatives in cases where a non-valuable
+ // error message was used.
+ if (throwOperation.Exception.WalkDownConversion() is not IObjectCreationOperation objectCreationOperation ||
+ HasPossiblyMeaningfulAdditionalArguments(objectCreationOperation))
+ {
+ return;
+ }
+
+ // Make sure the throw's parent is a binary expression (or a block that contains only the throw
+ // and whose parent is that binary expression).
+ IConditionalOperation? condition = throwOperation.Parent as IConditionalOperation;
+ if (condition is null)
+ {
+ if (throwOperation.Parent is IBlockOperation parentBlock && parentBlock.Children.Count() == 1)
+ {
+ condition = parentBlock.Parent as IConditionalOperation;
+ }
+
+ if (condition is null)
+ {
+ return;
+ }
+ }
+
+ // If the condition has an else block, give up. These are rare.
+ if (condition.WhenFalse is not null)
+ {
+ return;
+ }
+
+ // Now match the exception type against one of our known types.
+
+ // Handle ArgumentNullException.ThrowIfNull.
+ if (SymbolEqualityComparer.Default.Equals(objectCreationOperation.Type, ane))
+ {
+ if (aneThrowIfNull is not null &&
+ IsParameterNullCheck(condition.Condition, out IParameterReferenceOperation? nullCheckParameter))
+ {
+ context.ReportDiagnostic(condition.CreateDiagnostic(
+ UseArgumentNullExceptionThrowIfNullRule,
+ additionalLocations: ImmutableArray.Create(nullCheckParameter.Syntax.GetLocation()),
+ properties: null,
+ args: new object[] { nameof(ArgumentNullException), "ThrowIfNull" }));
+ }
+ return;
+ }
+
+ // Handle ArgumentException.ThrowIfNullOrEmpty
+ if (SymbolEqualityComparer.Default.Equals(objectCreationOperation.Type, ae))
+ {
+ if (aeThrowIfNullOrEmpty is not null &&
+ IsNullOrEmptyCheck(stringIsNullOrEmpty, stringLength, stringEmpty, condition.Condition, out IParameterReferenceOperation? nullOrEmptyCheckParameter))
+ {
+ context.ReportDiagnostic(condition.CreateDiagnostic(
+ UseArgumentExceptionThrowIfNullOrEmptyRule,
+ additionalLocations: ImmutableArray.Create(nullOrEmptyCheckParameter.Syntax.GetLocation()),
+ properties: null,
+ args: new object[] { nameof(ArgumentException), "ThrowIfNullOrEmpty" }));
+ }
+ return;
+ }
+
+ // Handle ArgumentOutOfRangeException.ThrowIfZero
+ // Handle ArgumentOutOfRangeException.ThrowIfNegative
+ // Handle ArgumentOutOfRangeException.ThrowIfNegativeOrZero
+ // Handle ArgumentOutOfRangeException.ThrowIfGreaterThan
+ // Handle ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual
+ // Handle ArgumentOutOfRangeException.ThrowIfLessThan
+ // Handle ArgumentOutOfRangeException.ThrowIfLessThanOrEqual
+ if (SymbolEqualityComparer.Default.Equals(objectCreationOperation.Type, aoore))
+ {
+ if (hasAnyAooreThrow)
+ {
+ ImmutableArray additionalLocations = ImmutableArray.Empty;
+
+ if (IsNegativeAndOrZeroComparison(condition.Condition, out IParameterReferenceOperation? aooreParameter, out string? methodName))
+ {
+ additionalLocations = ImmutableArray.Create(aooreParameter.Syntax.GetLocation());
+ }
+ else if (IsGreaterLessThanComparison(condition.Condition, out aooreParameter, out methodName, out SyntaxNode? other))
+ {
+ additionalLocations = ImmutableArray.Create(aooreParameter.Syntax.GetLocation(), other.GetLocation());
+ }
+
+ if (additionalLocations.Length != 0 && !AvoidComparing(aooreParameter!))
+ {
+ context.ReportDiagnostic(condition.CreateDiagnostic(
+ UseArgumentOutOfRangeExceptionThrowIfRule,
+ additionalLocations,
+ properties: ImmutableDictionary.Empty.Add(MethodNamePropertyKey, methodName),
+ args: new object[] { nameof(ArgumentOutOfRangeException), methodName! }));
+ }
+
+ static bool AvoidComparing(IParameterReferenceOperation p) =>
+ p.Type.IsNullableValueType() ||
+ p.Type.TypeKind == TypeKind.Enum;
+ }
+ return;
+ }
+
+ // Handle ObjectDisposedException.ThrowIf.
+ if (SymbolEqualityComparer.Default.Equals(objectCreationOperation.Type, ode))
+ {
+ if (odeThrowIf is not null)
+ {
+ // We always report a diagnostic. However, the fixer is only currently provided in the case
+ // of the argument to the ObjectDisposedException constructor containing a call to {something.}GetType().{Full}Name,
+ // in which case we can use the "something" as the argument to ThrowIf.
+
+ ImmutableArray additionalLocations = ImmutableArray.Create(condition.Condition.Syntax.GetLocation());
+
+ if (objectCreationOperation.Arguments is [IArgumentOperation arg, ..] &&
+ arg.Value is IPropertyReferenceOperation nameReference &&
+ nameReference.Member.Name is "Name" or "FullName" &&
+ nameReference.Instance is IInvocationOperation getTypeCall &&
+ SymbolEqualityComparer.Default.Equals(getType, getTypeCall.TargetMethod))
+ {
+ additionalLocations = additionalLocations.Add(
+ getTypeCall.Instance is IInstanceReferenceOperation { IsImplicit: true } ?
+ Location.None :
+ getTypeCall.Instance.Syntax.GetLocation());
+ }
+
+ context.ReportDiagnostic(condition.CreateDiagnostic(
+ UseObjectDisposedExceptionThrowIfRule,
+ additionalLocations,
+ properties: null,
+ args: new object[] { nameof(ObjectDisposedException), "ThrowIf" }));
+ }
+ return;
+ }
+ }, OperationKind.Throw);
+
+ // As a heuristic, we avoid issuing diagnostics if there are additional arguments (e.g. message)
+ // to the exception that could be useful.
+ bool HasPossiblyMeaningfulAdditionalArguments(IObjectCreationOperation objectCreationOperation)
+ {
+ ImmutableArray args = objectCreationOperation.Arguments;
+
+ if (args.IsEmpty)
+ {
+ // No arguments, so nothing meaningful.
+ return false;
+ }
+
+ if (args.Length >= 3)
+ {
+ // More than just parameter name and message/inner exception.
+ // It'll be rare for those to be default values, so just assume
+ // it's meaningful at this point.
+ return true;
+ }
+
+ if (SymbolEqualityComparer.Default.Equals(objectCreationOperation.Type, ae))
+ {
+ // ArgumentException's message is first. If it's not null nor "", it's "meaningful".
+ return !IsNullOrEmptyMessage(args[0]);
+ }
+
+ // The other exceptions all have the message second. If they only have 0 or 1 arguments,
+ // there's no message, and if they have more than one, it's again "meaningful" if it's not null nor "".
+ return args.Length >= 2 && !IsNullOrEmptyMessage(args[1]);
+
+ static bool IsNullOrEmptyMessage(IArgumentOperation arg) =>
+ arg.Value.WalkDownConversion() is ILiteralOperation { ConstantValue.HasValue: true } literal &&
+ literal.ConstantValue.Value is null or "";
+ }
+ });
+ }
+
+ /// Gets the being null checked by the condition, or else null.
+ private static bool IsParameterNullCheck(IOperation condition, [NotNullWhen(true)] out IParameterReferenceOperation? parameterReference)
+ {
+ parameterReference = null;
+
+ if (condition is IIsPatternOperation isPattern &&
+ isPattern.Pattern is IConstantPatternOperation { Value.ConstantValue: { HasValue: true, Value: null } })
+ {
+ // arg is null
+ parameterReference = isPattern.Value as IParameterReferenceOperation;
+ }
+ else if (condition is IBinaryOperation { OperatorKind: BinaryOperatorKind.Equals } equalsOp)
+ {
+ if (equalsOp.RightOperand.HasNullConstantValue())
+ {
+ // arg == null
+ parameterReference = equalsOp.LeftOperand.WalkDownConversion() as IParameterReferenceOperation;
+ }
+ else if (equalsOp.LeftOperand.HasNullConstantValue())
+ {
+ // null == arg
+ parameterReference = equalsOp.RightOperand.WalkDownConversion() as IParameterReferenceOperation;
+ }
+ }
+
+ return parameterReference is not null;
+ }
+
+ /// Gets the string being checked by the condition for being null or empty, or else null.
+ private static bool IsNullOrEmptyCheck(ISymbol stringIsNullOrEmpty, ISymbol stringLength, ISymbol stringEmpty, IOperation condition, [NotNullWhen(true)] out IParameterReferenceOperation? parameterReference)
+ {
+ if (condition is IInvocationOperation invocationOperation)
+ {
+ // (string.IsNullOrEmpty(arg))
+ if (SymbolEqualityComparer.Default.Equals(invocationOperation.TargetMethod, stringIsNullOrEmpty) &&
+ invocationOperation.Arguments is [IArgumentOperation arg] &&
+ arg.Value is IParameterReferenceOperation parameterReferenceOperation)
+ {
+ parameterReference = parameterReferenceOperation;
+ return true;
+ }
+ }
+ else if (condition is IBinaryOperation { OperatorKind: BinaryOperatorKind.ConditionalOr } orOp &&
+ IsParameterNullCheck(orOp.LeftOperand, out IParameterReferenceOperation? nullCheckParameter) &&
+ orOp.RightOperand is IBinaryOperation { OperatorKind: BinaryOperatorKind.Equals } lengthCheckOperation)
+ {
+ // arg is null ||
+
+ // arg.Length == 0
+ if (IsArgLengthEqual0(stringLength, nullCheckParameter.Parameter, lengthCheckOperation.LeftOperand, lengthCheckOperation.RightOperand) ||
+ IsArgLengthEqual0(stringLength, nullCheckParameter.Parameter, lengthCheckOperation.RightOperand, lengthCheckOperation.LeftOperand))
+ {
+ parameterReference = nullCheckParameter;
+ return true;
+ }
+
+ // arg == string.Empty
+ if (IsArgEqualStringEmpty(stringEmpty, nullCheckParameter.Parameter, lengthCheckOperation.LeftOperand, lengthCheckOperation.RightOperand) ||
+ IsArgEqualStringEmpty(stringEmpty, nullCheckParameter.Parameter, lengthCheckOperation.RightOperand, lengthCheckOperation.LeftOperand))
+ {
+ parameterReference = nullCheckParameter;
+ return true;
+ }
+ }
+
+ parameterReference = null;
+ return false;
+ }
+
+ /// Determines whether the left and right operations are performing a comparison of the specified argument against string.Empty.
+ private static bool IsArgEqualStringEmpty(ISymbol stringEmpty, IParameterSymbol arg, IOperation left, IOperation right) =>
+ left is IParameterReferenceOperation parameterReferenceOperation &&
+ SymbolEqualityComparer.Default.Equals(parameterReferenceOperation.Parameter, arg) &&
+ parameterReferenceOperation.Type.SpecialType == SpecialType.System_String &&
+ right is IFieldReferenceOperation fieldReferenceOperation &&
+ SymbolEqualityComparer.Default.Equals(stringEmpty, fieldReferenceOperation.Member);
+
+ /// Determines whether the left and right operations are performing a comparison of the specified argument's Length against 0.
+ private static bool IsArgLengthEqual0(ISymbol stringLength, IParameterSymbol arg, IOperation left, IOperation right) =>
+ right.WalkDownConversion() is ILiteralOperation literalOperation &&
+ literalOperation.ConstantValue is { HasValue: true, Value: 0 } &&
+ left is IPropertyReferenceOperation propertyReferenceOperation &&
+ propertyReferenceOperation.Instance is IParameterReferenceOperation referencedParameter &&
+ SymbolEqualityComparer.Default.Equals(referencedParameter.Parameter, arg) &&
+ SymbolEqualityComparer.Default.Equals(stringLength, propertyReferenceOperation.Member);
+
+ /// Gets the being compared for being negative and/or zero.
+ private static bool IsNegativeAndOrZeroComparison(IOperation condition, [NotNullWhen(true)] out IParameterReferenceOperation? parameterReferenceOperation, [NotNullWhen(true)] out string? methodName)
+ {
+ const string ThrowIfZero = nameof(ThrowIfZero);
+ const string ThrowIfNegative = nameof(ThrowIfNegative);
+ const string ThrowIfNegativeOrZero = nameof(ThrowIfNegativeOrZero);
+
+ if (condition is IIsPatternOperation patternOperation &&
+ patternOperation.Pattern is IConstantPatternOperation { Value.ConstantValue: { HasValue: true, Value: 0 } })
+ {
+ // arg is 0
+ methodName = ThrowIfZero;
+ parameterReferenceOperation = patternOperation.Value as IParameterReferenceOperation;
+ return parameterReferenceOperation is not null;
+ }
+
+ // TODO: Update to include IRelationalPatternOperation when it's available:
+ // arg is < 0
+ // arg is <= 0
+
+ if (condition is IBinaryOperation binaryOperation)
+ {
+ switch (binaryOperation.OperatorKind)
+ {
+ case BinaryOperatorKind.Equals:
+ if (binaryOperation.LeftOperand.WalkDownConversion() is ILiteralOperation { ConstantValue: { HasValue: true, Value: 0 } })
+ {
+ // arg == 0
+ methodName = ThrowIfZero;
+ parameterReferenceOperation = binaryOperation.RightOperand as IParameterReferenceOperation;
+ return parameterReferenceOperation is not null;
+ }
+
+ if (binaryOperation.RightOperand.WalkDownConversion() is ILiteralOperation { ConstantValue: { HasValue: true, Value: 0 } })
+ {
+ // 0 == arg
+ methodName = ThrowIfZero;
+ parameterReferenceOperation = binaryOperation.LeftOperand as IParameterReferenceOperation;
+ return parameterReferenceOperation is not null;
+ }
+ break;
+
+ case BinaryOperatorKind.LessThanOrEqual or BinaryOperatorKind.LessThan:
+ if (binaryOperation.LeftOperand is IParameterReferenceOperation leftOperandParameterReference &&
+ binaryOperation.RightOperand.WalkDownConversion() is ILiteralOperation { ConstantValue: { HasValue: true, Value: 0 } })
+ {
+ // arg < 0
+ // arg <= 0
+ methodName = binaryOperation.OperatorKind == BinaryOperatorKind.LessThanOrEqual ? ThrowIfNegativeOrZero : ThrowIfNegative;
+ parameterReferenceOperation = leftOperandParameterReference;
+ return true;
+ }
+ break;
+
+ case BinaryOperatorKind.GreaterThanOrEqual or BinaryOperatorKind.GreaterThan:
+ if (binaryOperation.RightOperand is IParameterReferenceOperation rightOperationParameterReference &&
+ binaryOperation.LeftOperand.WalkDownConversion() is ILiteralOperation { ConstantValue: { HasValue: true, Value: 0 } })
+ {
+ // 0 > arg
+ // 0 >= arg
+ methodName = binaryOperation.OperatorKind == BinaryOperatorKind.GreaterThanOrEqual ? ThrowIfNegativeOrZero : ThrowIfNegative;
+ parameterReferenceOperation = rightOperationParameterReference;
+ return true;
+ }
+ break;
+ }
+ }
+
+ methodName = null;
+ parameterReferenceOperation = null;
+ return false;
+ }
+
+ /// Gets the being compared to another expression.
+ private static bool IsGreaterLessThanComparison(IOperation condition, [NotNullWhen(true)] out IParameterReferenceOperation? parameterReferenceOperation, [NotNullWhen(true)] out string? methodName, [NotNullWhen(true)] out SyntaxNode? other)
+ {
+ const string ThrowIfGreaterThan = nameof(ThrowIfGreaterThan);
+ const string ThrowIfGreaterThanOrEqual = nameof(ThrowIfGreaterThanOrEqual);
+ const string ThrowIfLessThan = nameof(ThrowIfLessThan);
+ const string ThrowIfLessThanOrEqual = nameof(ThrowIfLessThanOrEqual);
+
+ if (condition is IBinaryOperation binaryOperation)
+ {
+ switch (binaryOperation.OperatorKind)
+ {
+ case BinaryOperatorKind.GreaterThan or BinaryOperatorKind.GreaterThanOrEqual or BinaryOperatorKind.LessThan or BinaryOperatorKind.LessThanOrEqual:
+ if (binaryOperation.LeftOperand is IParameterReferenceOperation leftParameter)
+ {
+ // arg > other
+ // arg >= other
+ // arg < other
+ // arg <= other
+ methodName = binaryOperation.OperatorKind switch
+ {
+ BinaryOperatorKind.GreaterThan => ThrowIfGreaterThan,
+ BinaryOperatorKind.GreaterThanOrEqual => ThrowIfGreaterThanOrEqual,
+ BinaryOperatorKind.LessThan => ThrowIfLessThan,
+ _ => ThrowIfLessThanOrEqual,
+ };
+ other = binaryOperation.RightOperand.Syntax;
+ parameterReferenceOperation = leftParameter;
+ return true;
+ }
+
+ if (binaryOperation.RightOperand is IParameterReferenceOperation rightParameter)
+ {
+ // other > arg
+ // other >= arg
+ // other < arg
+ // other <= arg
+ methodName = binaryOperation.OperatorKind switch
+ {
+ BinaryOperatorKind.GreaterThan => ThrowIfLessThan,
+ BinaryOperatorKind.GreaterThanOrEqual => ThrowIfLessThanOrEqual,
+ BinaryOperatorKind.LessThan => ThrowIfGreaterThan,
+ _ => ThrowIfGreaterThanOrEqual,
+ };
+ other = binaryOperation.LeftOperand.Syntax;
+ parameterReferenceOperation = rightParameter;
+ return true;
+ }
+ break;
+ }
+ }
+
+ methodName = null;
+ parameterReferenceOperation = null;
+ other = null;
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseExceptionThrowHelpersFixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseExceptionThrowHelpersFixer.cs
new file mode 100644
index 0000000000..82a3e986c0
--- /dev/null
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseExceptionThrowHelpersFixer.cs
@@ -0,0 +1,148 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System.Collections.Immutable;
+using System.Composition;
+using System.Diagnostics.CodeAnalysis;
+using System.Threading.Tasks;
+using Analyzer.Utilities;
+using Analyzer.Utilities.Extensions;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.Editing;
+
+namespace Microsoft.NetCore.Analyzers.Runtime
+{
+ /// Fixer for .
+ [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared]
+ public sealed class UseExceptionThrowHelpersFixer : CodeFixProvider
+ {
+ public sealed override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(
+ UseExceptionThrowHelpers.UseArgumentNullExceptionThrowIfNullRuleId,
+ UseExceptionThrowHelpers.UseArgumentExceptionThrowIfNullOrEmptyRuleId,
+ UseExceptionThrowHelpers.UseArgumentOutOfRangeExceptionThrowIfRuleId,
+ UseExceptionThrowHelpers.UseObjectDisposedExceptionThrowIfRuleId);
+
+ public sealed override FixAllProvider GetFixAllProvider() => CustomFixAllProvider.Instance;
+
+ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
+ {
+ Document doc = context.Document;
+ SemanticModel model = await doc.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
+ SyntaxNode root = await doc.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
+
+ if (TryGetFixInfo(doc, root, model, context.Diagnostics[0], out INamedTypeSymbol? typeSymbol, out string? methodName, out SyntaxNode? node, out SyntaxNode? arg, out SyntaxNode? other))
+ {
+ string title = string.Format(MicrosoftNetCoreAnalyzersResources.UseThrowHelperFix, typeSymbol.Name, methodName);
+ context.RegisterCodeFix(
+ CodeAction.Create(title, equivalenceKey: title, createChangedDocument: async cancellationToken =>
+ {
+ DocumentEditor editor = await DocumentEditor.CreateAsync(doc, cancellationToken).ConfigureAwait(false);
+ ApplyFix(typeSymbol, methodName, node, arg, other, editor);
+ return editor.GetChangedDocument();
+ }),
+ context.Diagnostics);
+ }
+ }
+
+ private static bool TryGetFixInfo(
+ Document doc,
+ SyntaxNode root,
+ SemanticModel model,
+ Diagnostic diagnostic,
+ [NotNullWhen(true)] out INamedTypeSymbol? typeSymbol,
+ [NotNullWhen(true)] out string? methodName,
+ [NotNullWhen(true)] out SyntaxNode? node,
+ [NotNullWhen(true)] out SyntaxNode? arg,
+ [NotNullWhen(true)] out SyntaxNode? other)
+ {
+ typeSymbol = null;
+ methodName = null;
+ arg = null;
+ other = null;
+
+ node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
+ if (node != null &&
+ diagnostic.AdditionalLocations.Count != 0 &&
+ diagnostic.AdditionalLocations[0] is Location argLocation)
+ {
+ arg = root.FindNode(argLocation.SourceSpan, getInnermostNodeForTie: true);
+ string id = diagnostic.Id;
+
+ if (diagnostic.AdditionalLocations.Count == 2)
+ {
+ Location otherLocation = diagnostic.AdditionalLocations[1];
+ other = otherLocation == Location.None ? // None is special-cased by the analyzer to mean "this"
+ SyntaxGenerator.GetGenerator(doc).ThisExpression() :
+ root.FindNode(otherLocation.SourceSpan, getInnermostNodeForTie: true);
+ }
+
+ switch (id)
+ {
+ case UseExceptionThrowHelpers.UseArgumentNullExceptionThrowIfNullRuleId:
+ typeSymbol = model.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemArgumentNullException);
+ methodName = "ThrowIfNull";
+ break;
+
+ case UseExceptionThrowHelpers.UseArgumentExceptionThrowIfNullOrEmptyRuleId:
+ typeSymbol = model.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemArgumentException);
+ methodName = "ThrowIfNullOrEmpty";
+ break;
+
+ case UseExceptionThrowHelpers.UseArgumentOutOfRangeExceptionThrowIfRuleId:
+ typeSymbol = model.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemArgumentOutOfRangeException);
+ diagnostic.Properties.TryGetValue(UseExceptionThrowHelpers.MethodNamePropertyKey, out methodName);
+ break;
+
+ case UseExceptionThrowHelpers.UseObjectDisposedExceptionThrowIfRuleId when other is not null:
+ typeSymbol = model.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemObjectDisposedException);
+ methodName = "ThrowIf";
+ break;
+ }
+ }
+
+ return typeSymbol != null && methodName != null && arg != null;
+ }
+
+ private static void ApplyFix(
+ INamedTypeSymbol typeSymbol,
+ string methodName,
+ SyntaxNode node,
+ SyntaxNode arg,
+ SyntaxNode other,
+ SyntaxEditor editor)
+ {
+ editor.ReplaceNode(
+ node,
+ editor.Generator.ExpressionStatement(
+ editor.Generator.InvocationExpression(
+ editor.Generator.MemberAccessExpression(
+ editor.Generator.TypeExpressionForStaticMemberAccess(typeSymbol), methodName),
+ other is not null ? new SyntaxNode[] { arg, other } : new SyntaxNode[] { arg })).WithTriviaFrom(node));
+ }
+
+ private sealed class CustomFixAllProvider : DocumentBasedFixAllProvider
+ {
+ public static readonly CustomFixAllProvider Instance = new();
+
+ protected override string CodeActionTitle => MicrosoftNetCoreAnalyzersResources.UseThrowHelperFix;
+
+ protected override async Task FixAllInDocumentAsync(FixAllContext fixAllContext, Document document, ImmutableArray diagnostics)
+ {
+ DocumentEditor editor = await DocumentEditor.CreateAsync(document, fixAllContext.CancellationToken).ConfigureAwait(false);
+ SyntaxNode root = editor.OriginalRoot;
+ SemanticModel model = editor.SemanticModel;
+
+ foreach (Diagnostic diagnostic in diagnostics)
+ {
+ if (TryGetFixInfo(document, root, model, diagnostic, out INamedTypeSymbol? typeSymbol, out string? methodName, out SyntaxNode? node, out SyntaxNode? arg, out SyntaxNode? other))
+ {
+ ApplyFix(typeSymbol, methodName, node, arg, other, editor);
+ }
+ }
+
+ return editor.GetChangedRoot();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf
index aa68b85a71..1633c40464 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf
@@ -2682,6 +2682,21 @@
ThreadStatic má vliv jenom na statická pole.
+
+ Use ArgumentException throw helper
+ Use ArgumentException throw helper
+
+
+
+ Use ArgumentNullException throw helper
+ Use ArgumentNullException throw helper
+
+
+
+ Use ArgumentOutOfRangeException throw helper
+ Use ArgumentOutOfRangeException throw helper
+
+ Use Array.EmptyPoužijte Array.Empty
@@ -2897,6 +2912,11 @@
Použití spravovaných ekvivalentů rozhraní Win32 API
+
+ Use ObjectDisposedException throw helper
+ Use ObjectDisposedException throw helper
+
+ A string comparison operation that is nonlinguistic does not set the StringComparison parameter to either Ordinal or OrdinalIgnoreCase. By explicitly setting the parameter to either StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase, your code often gains speed, becomes more correct, and becomes more reliable.Operace porovnání řetězců, která není jazyková, nenastavuje parametr StringComparison na hodnotu Ordinal nebo OrdinalIgnoreCase. Explicitním nastavením parametru na hodnotu StringComparison.Ordinal nebo StringComparison.OrdinalIgnoreCase se kód často urychlí a bývá správnější a spolehlivější.
@@ -3042,6 +3062,21 @@
Použít znakový literál pro vyhledávání s jedním znakem
+
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+
+
+ Use '{0}.{1}'
+ Use '{0}.{1}'
+
+
+
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+
+ Platform compatibility analyzer requires a valid platform name and version.Analyzátor kompatibility platformy vyžaduje platný název a verzi platformy.
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 0a819c538d..27b0826b5c 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf
@@ -2682,6 +2682,21 @@
\"ThreadStatic\" wirkt sich nur auf statische Felder aus
+
+ Use ArgumentException throw helper
+ Use ArgumentException throw helper
+
+
+
+ Use ArgumentNullException throw helper
+ Use ArgumentNullException throw helper
+
+
+
+ Use ArgumentOutOfRangeException throw helper
+ Use ArgumentOutOfRangeException throw helper
+
+ Use Array.EmptyArray.Empty verwenden
@@ -2897,6 +2912,11 @@
Verwaltete Entsprechungen der Win32-API verwenden
+
+ Use ObjectDisposedException throw helper
+ Use ObjectDisposedException throw helper
+
+ A string comparison operation that is nonlinguistic does not set the StringComparison parameter to either Ordinal or OrdinalIgnoreCase. By explicitly setting the parameter to either StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase, your code often gains speed, becomes more correct, and becomes more reliable.Bei einem nicht linguistischen Vorgang zum Zeichenfolgenvergleich wird der StringComparison-Parameter nicht auf "Ordinal" oder "OrdinalIgnoreCase" festgelegt. Indem der Parameter explizit auf "StringComparison.Ordinal" oder "StringComparison.OrdinalIgnoreCase" festgelegt wird, gewinnt Ihr Code häufig an Geschwindigkeit und ist zudem korrekter und zuverlässiger.
@@ -3042,6 +3062,21 @@
Zeichenliteral für die Suche nach einem einzelnen Zeichen verwenden
+
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+
+
+ Use '{0}.{1}'
+ Use '{0}.{1}'
+
+
+
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+
+ Platform compatibility analyzer requires a valid platform name and version.Das Analysetool für Plattformkompatibilität erfordert einen gültigen Plattformnamen und eine gültige Version.
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 477bef6339..8dc738448d 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf
@@ -2682,6 +2682,21 @@
“ThreadStatic” solo afecta a campos estáticos
+
+ Use ArgumentException throw helper
+ Use ArgumentException throw helper
+
+
+
+ Use ArgumentNullException throw helper
+ Use ArgumentNullException throw helper
+
+
+
+ Use ArgumentOutOfRangeException throw helper
+ Use ArgumentOutOfRangeException throw helper
+
+ Use Array.EmptyUsar Array.Empty
@@ -2897,6 +2912,11 @@
Utilizar equivalentes administrados de la API Win32
+
+ Use ObjectDisposedException throw helper
+ Use ObjectDisposedException throw helper
+
+ A string comparison operation that is nonlinguistic does not set the StringComparison parameter to either Ordinal or OrdinalIgnoreCase. By explicitly setting the parameter to either StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase, your code often gains speed, becomes more correct, and becomes more reliable.Una operación no lingüística de comparación de cadenas no establece el parámetro StringComparison en Ordinal ni en OrdinalIgnoreCase. Si se establece explícitamente el parámetro en StringComparison.Ordinal o StringComparison.OrdinalIgnoreCase, el código será más rápido y ganará en precisión y confiabilidad.
@@ -3042,6 +3062,21 @@
Usar literal de carácter para una búsqueda de caracteres individuales
+
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+
+
+ Use '{0}.{1}'
+ Use '{0}.{1}'
+
+
+
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+
+ Platform compatibility analyzer requires a valid platform name and version.El analizador de compatibilidad de plataforma requiere un nombre de plataforma y una versión válidos.
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 0e5479e550..703c49d7de 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf
@@ -2682,6 +2682,21 @@
« ThreadStatic » affecte uniquement les champs statiques
+
+ Use ArgumentException throw helper
+ Use ArgumentException throw helper
+
+
+
+ Use ArgumentNullException throw helper
+ Use ArgumentNullException throw helper
+
+
+
+ Use ArgumentOutOfRangeException throw helper
+ Use ArgumentOutOfRangeException throw helper
+
+ Use Array.EmptyUtiliser Array.Empty
@@ -2897,6 +2912,11 @@
Utiliser les équivalents managés de l'API Win32
+
+ Use ObjectDisposedException throw helper
+ Use ObjectDisposedException throw helper
+
+ A string comparison operation that is nonlinguistic does not set the StringComparison parameter to either Ordinal or OrdinalIgnoreCase. By explicitly setting the parameter to either StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase, your code often gains speed, becomes more correct, and becomes more reliable.Une opération de comparaison de chaînes non linguistique n'affecte pas au paramètre StringComparison la valeur Ordinal ou OrdinalIgnoreCase. En affectant explicitement au paramètre la valeur StringComparison.Ordinal ou StringComparison.OrdinalIgnoreCase, votre code gagne souvent en rapidité, tout en devenant plus correct et plus fiable.
@@ -3042,6 +3062,21 @@
Utiliser le littéral char pour une recherche à caractère unique
+
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+
+
+ Use '{0}.{1}'
+ Use '{0}.{1}'
+
+
+
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+
+ Platform compatibility analyzer requires a valid platform name and version.Platform Analyzer requiert un nom de plateforme et une version valides.
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 08f171eebc..4495c77b8d 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf
@@ -2682,6 +2682,21 @@
'ThreadStatic' influisce solo sui campi statici
+
+ Use ArgumentException throw helper
+ Use ArgumentException throw helper
+
+
+
+ Use ArgumentNullException throw helper
+ Use ArgumentNullException throw helper
+
+
+
+ Use ArgumentOutOfRangeException throw helper
+ Use ArgumentOutOfRangeException throw helper
+
+ Use Array.EmptyUsare Array.Empty
@@ -2897,6 +2912,11 @@
Usare equivalenti gestiti dell'API Win32
+
+ Use ObjectDisposedException throw helper
+ Use ObjectDisposedException throw helper
+
+ A string comparison operation that is nonlinguistic does not set the StringComparison parameter to either Ordinal or OrdinalIgnoreCase. By explicitly setting the parameter to either StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase, your code often gains speed, becomes more correct, and becomes more reliable.In un'operazione di confronto tra stringhe di tipo non linguistico il parametro StringComparison non viene impostato su Ordinal o OrdinalIgnoreCase. L'impostazione esplicita del parametro su StringComparison.Ordinal o StringComparison.OrdinalIgnoreCase consente spesso di rendere il codice più veloce, corretto e affidabile.
@@ -3042,6 +3062,21 @@
Usare il valore letterale char per la ricerca di un singolo carattere
+
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+
+
+ Use '{0}.{1}'
+ Use '{0}.{1}'
+
+
+
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+
+ Platform compatibility analyzer requires a valid platform name and version.Con l'analizzatore della compatibilità della piattaforma sono richiesti un nome e una versione di piattaforma validi.
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 0a30fd0f8a..5c56df14cb 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf
@@ -2682,6 +2682,21 @@
'ThreadStatic' は静的フィールドにのみ影響します
+
+ Use ArgumentException throw helper
+ Use ArgumentException throw helper
+
+
+
+ Use ArgumentNullException throw helper
+ Use ArgumentNullException throw helper
+
+
+
+ Use ArgumentOutOfRangeException throw helper
+ Use ArgumentOutOfRangeException throw helper
+
+ Use Array.EmptyArray.Empty を使用します
@@ -2897,6 +2912,11 @@
Win32 API に相当するマネージ API を使用します
+
+ Use ObjectDisposedException throw helper
+ Use ObjectDisposedException throw helper
+
+ A string comparison operation that is nonlinguistic does not set the StringComparison parameter to either Ordinal or OrdinalIgnoreCase. By explicitly setting the parameter to either StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase, your code often gains speed, becomes more correct, and becomes more reliable.非言語的な文字列比較操作では、StringComparison パラメーターが Ordinal にも OrdinalIgnoreCase にも設定されません。パラメーターを StringComparison.Ordinal または StringComparison.OrdinalIgnoreCase に明示的に設定することによって、コードは多くの場合、高速で正確になり、信頼性が向上します。
@@ -3042,6 +3062,21 @@
1 つの文字参照に単一文字検索を使用する
+
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+
+
+ Use '{0}.{1}'
+ Use '{0}.{1}'
+
+
+
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+
+ Platform compatibility analyzer requires a valid platform name and version.プラットフォーム互換性アナライザーには、有効なプラットフォーム名とバージョンが必要です。
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 efcbbdfc51..dbdb2685b9 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf
@@ -2682,6 +2682,21 @@
'ThreadStatic'은(는) 정적 필드에만 영향을 줍니다.
+
+ Use ArgumentException throw helper
+ Use ArgumentException throw helper
+
+
+
+ Use ArgumentNullException throw helper
+ Use ArgumentNullException throw helper
+
+
+
+ Use ArgumentOutOfRangeException throw helper
+ Use ArgumentOutOfRangeException throw helper
+
+ Use Array.EmptyArray.Empty를 사용하세요.
@@ -2897,6 +2912,11 @@
Win32 API에 있는 동일한 기능의 관리되는 항목을 사용하세요.
+
+ Use ObjectDisposedException throw helper
+ Use ObjectDisposedException throw helper
+
+ A string comparison operation that is nonlinguistic does not set the StringComparison parameter to either Ordinal or OrdinalIgnoreCase. By explicitly setting the parameter to either StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase, your code often gains speed, becomes more correct, and becomes more reliable.비언어적 문자열 비교 작업에서는 StringComparison 매개 변수를 서수 또는 OrdinalIgnoreCase로 설정하지 않습니다. 매개 변수를 명시적으로 StringComparison.Ordinal 또는 StringComparison.OrdinalIgnoreCase로 설정하면 코드의 속도가 빨라지고, 정확도와 신뢰도가 더 높아집니다.
@@ -3042,6 +3062,21 @@
단일 문자 조회에 char 리터럴 사용
+
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+
+
+ Use '{0}.{1}'
+ Use '{0}.{1}'
+
+
+
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+
+ Platform compatibility analyzer requires a valid platform name and version.플랫폼 호환성 분석기에는 유효한 플랫폼 이름과 버전이 필요합니다.
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 bbf0b0223b..03e5a6fbf6 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf
@@ -2682,6 +2682,21 @@
Element „ThreadStatic“ ma wpływ tylko na pola statyczne
+
+ Use ArgumentException throw helper
+ Use ArgumentException throw helper
+
+
+
+ Use ArgumentNullException throw helper
+ Use ArgumentNullException throw helper
+
+
+
+ Use ArgumentOutOfRangeException throw helper
+ Use ArgumentOutOfRangeException throw helper
+
+ Use Array.EmptyUżyj metody Array.Empty
@@ -2897,6 +2912,11 @@
Używaj zarządzanych odpowiedników funkcji win32 api
+
+ Use ObjectDisposedException throw helper
+ Use ObjectDisposedException throw helper
+
+ A string comparison operation that is nonlinguistic does not set the StringComparison parameter to either Ordinal or OrdinalIgnoreCase. By explicitly setting the parameter to either StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase, your code often gains speed, becomes more correct, and becomes more reliable.Operacja porównywania ciągów nieuwzględniająca zasad języka nie ustawia parametru StringComparison na wartość Ordinal ani OrdinalIgnoreCase. Jawne ustawienie parametru na wartość StringComparison.Ordinal lub StringComparison.OrdinalIgnoreCase umożliwia często przyspieszenie kodu oraz zwiększenie jego poprawności i niezawodności.
@@ -3042,6 +3062,21 @@
Użyj literału char do wyszukiwania pojedynczego znaku
+
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+
+
+ Use '{0}.{1}'
+ Use '{0}.{1}'
+
+
+
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+
+ Platform compatibility analyzer requires a valid platform name and version.Analizator zgodności platformy wymaga prawidłowej nazwy i wersji platformy.
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 5799395acb..13bca16fa8 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
@@ -2682,6 +2682,21 @@
“ThreadStatic” afeta somente campos estáticos
+
+ Use ArgumentException throw helper
+ Use ArgumentException throw helper
+
+
+
+ Use ArgumentNullException throw helper
+ Use ArgumentNullException throw helper
+
+
+
+ Use ArgumentOutOfRangeException throw helper
+ Use ArgumentOutOfRangeException throw helper
+
+ Use Array.EmptyUsar Array.Empty
@@ -2897,6 +2912,11 @@
Usar equivalentes gerenciados da API win32
+
+ Use ObjectDisposedException throw helper
+ Use ObjectDisposedException throw helper
+
+ A string comparison operation that is nonlinguistic does not set the StringComparison parameter to either Ordinal or OrdinalIgnoreCase. By explicitly setting the parameter to either StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase, your code often gains speed, becomes more correct, and becomes more reliable.Uma comparação de cadeia de caracteres não linguística não define o parâmetro StringComparison como Ordinal ou OrdinalIgnoreCase. Ao definir explicitamente o parâmetro como StringComparison.Ordinal ou StringComparison.OrdinalIgnoreCase, seu código geralmente ganha velocidade, torna-se mais correto e mais confiável.
@@ -3042,6 +3062,21 @@
Usar o literal char para uma pesquisa de caractere único
+
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+
+
+ Use '{0}.{1}'
+ Use '{0}.{1}'
+
+
+
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+
+ Platform compatibility analyzer requires a valid platform name and version.O analisador de compatibilidade de plataforma requer um nome de plataforma e uma versão válidos.
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 ab9b30cf4c..09c7bc24df 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf
@@ -2682,6 +2682,21 @@
ThreadStatic влияет только на статические поля
+
+ Use ArgumentException throw helper
+ Use ArgumentException throw helper
+
+
+
+ Use ArgumentNullException throw helper
+ Use ArgumentNullException throw helper
+
+
+
+ Use ArgumentOutOfRangeException throw helper
+ Use ArgumentOutOfRangeException throw helper
+
+ Use Array.EmptyИспользуйте Array.Empty
@@ -2897,6 +2912,11 @@
Используйте управляемые эквиваленты API Win32
+
+ Use ObjectDisposedException throw helper
+ Use ObjectDisposedException throw helper
+
+ A string comparison operation that is nonlinguistic does not set the StringComparison parameter to either Ordinal or OrdinalIgnoreCase. By explicitly setting the parameter to either StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase, your code often gains speed, becomes more correct, and becomes more reliable.Нелингвистическая операция сравнения строк не задает для параметра StringComparison значение Ordinal или OrdinalIgnoreCase. Задав явным образом значение StringComparison.Ordinal или StringComparison.OrdinalIgnoreCase для параметра, можно сделать код более быстродействующим, корректным и надежным.
@@ -3042,6 +3062,21 @@
Использовать знаковый литерал для поиска одного знака
+
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+
+
+ Use '{0}.{1}'
+ Use '{0}.{1}'
+
+
+
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+
+ Platform compatibility analyzer requires a valid platform name and version.Анализатору совместимости платформы требуется допустимое имя и версия платформы.
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 f5bd911e1e..85e5e28259 100644
--- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf
+++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf
@@ -2682,6 +2682,21 @@
'ThreadStatic' yalnızca statik alanları etkiler
+
+ Use ArgumentException throw helper
+ Use ArgumentException throw helper
+
+
+
+ Use ArgumentNullException throw helper
+ Use ArgumentNullException throw helper
+
+
+
+ Use ArgumentOutOfRangeException throw helper
+ Use ArgumentOutOfRangeException throw helper
+
+ Use Array.EmptyArray.Empty kullanın
@@ -2897,6 +2912,11 @@
Win32 API’sinin yönetilen eşdeğerlerini kullan
+
+ Use ObjectDisposedException throw helper
+ Use ObjectDisposedException throw helper
+
+ A string comparison operation that is nonlinguistic does not set the StringComparison parameter to either Ordinal or OrdinalIgnoreCase. By explicitly setting the parameter to either StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase, your code often gains speed, becomes more correct, and becomes more reliable.Dille ilgili olmayan bir dize karşılaştırma işlemi, StringComparison parametresini Ordinal veya OrdinalIgnoreCase olarak ayarlamaz. Parametreyi açıkça StringComparison.Ordinal veya StringComparison.OrdinalIgnoreCase olarak ayarladığınızda kodunuz genellikle hızlanır, daha doğru ve daha güvenilir hale gelir.
@@ -3042,6 +3062,21 @@
Tek bir karakter araması için sabit değerli karakter kullanın
+
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+
+
+ Use '{0}.{1}'
+ Use '{0}.{1}'
+
+
+
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+
+ Platform compatibility analyzer requires a valid platform name and version.Platform uyumluluğu çözümleyicisi geçerli bir platform adı ve sürümü gerektiriyor.
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 888dec83b1..be8f02e91b 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
@@ -2682,6 +2682,21 @@
“ThreadStatic” 仅影响静态字段
+
+ Use ArgumentException throw helper
+ Use ArgumentException throw helper
+
+
+
+ Use ArgumentNullException throw helper
+ Use ArgumentNullException throw helper
+
+
+
+ Use ArgumentOutOfRangeException throw helper
+ Use ArgumentOutOfRangeException throw helper
+
+ Use Array.Empty使用 Array.Empty
@@ -2897,6 +2912,11 @@
使用 Win32 API 的托管等效项
+
+ Use ObjectDisposedException throw helper
+ Use ObjectDisposedException throw helper
+
+ A string comparison operation that is nonlinguistic does not set the StringComparison parameter to either Ordinal or OrdinalIgnoreCase. By explicitly setting the parameter to either StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase, your code often gains speed, becomes more correct, and becomes more reliable.非语义的字符串比较运算没有将 StringComparison 参数设置为 Ordinal 或 OrdinalIgnoreCase。通过将参数显式设置为 StringComparison.Ordinal 或 StringComparison.OrdinalIgnoreCase,通常可提高代码的速度、准确率和可靠性。
@@ -3042,6 +3062,21 @@
将字符型文本用于单个字符查找
+
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+
+
+ Use '{0}.{1}'
+ Use '{0}.{1}'
+
+
+
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+
+ Platform compatibility analyzer requires a valid platform name and version.平台兼容性分析器需要有效的平台名称和版本。
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 1977b97705..e072978125 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
@@ -2682,6 +2682,21 @@
'ThreadStatic' 只會影響靜態欄位
+
+ Use ArgumentException throw helper
+ Use ArgumentException throw helper
+
+
+
+ Use ArgumentNullException throw helper
+ Use ArgumentNullException throw helper
+
+
+
+ Use ArgumentOutOfRangeException throw helper
+ Use ArgumentOutOfRangeException throw helper
+
+ Use Array.Empty使用 Array.Empty
@@ -2897,6 +2912,11 @@
使用 Win32 API 的受控對等項
+
+ Use ObjectDisposedException throw helper
+ Use ObjectDisposedException throw helper
+
+ A string comparison operation that is nonlinguistic does not set the StringComparison parameter to either Ordinal or OrdinalIgnoreCase. By explicitly setting the parameter to either StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase, your code often gains speed, becomes more correct, and becomes more reliable.非語言的字串比較作業不會將 StringComparison 參數設為 Ordinal 或 OrdinalIgnoreCase。將參數明確設定為 StringComparison.Ordinal 或 StringComparison.OrdinalIgnoreCase 時,程式碼通常會更快速、精確且更可靠。
@@ -3042,6 +3062,21 @@
請為單一字元查閱使用字元常值
+
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+ Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+
+
+ Use '{0}.{1}'
+ Use '{0}.{1}'
+
+
+
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+ Use '{0}.{1}' instead of explicitly throwing a new exception instance
+
+ Platform compatibility analyzer requires a valid platform name and version.平台相容性分析器需要有效的平台名稱和版本。
diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md
index 4a50157bff..e9c2621b11 100644
--- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md
+++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md
@@ -864,6 +864,54 @@ Invalid entry in code metrics rule specification file.
|CodeFix|False|
---
+## [CA1510](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1510): Use ArgumentNullException throw helper
+
+Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+|Item|Value|
+|-|-|
+|Category|Maintainability|
+|Enabled|True|
+|Severity|Info|
+|CodeFix|True|
+---
+
+## [CA1511](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1511): Use ArgumentException throw helper
+
+Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+|Item|Value|
+|-|-|
+|Category|Maintainability|
+|Enabled|True|
+|Severity|Info|
+|CodeFix|True|
+---
+
+## [CA1512](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1512): Use ArgumentOutOfRangeException throw helper
+
+Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+|Item|Value|
+|-|-|
+|Category|Maintainability|
+|Enabled|True|
+|Severity|Info|
+|CodeFix|True|
+---
+
+## [CA1513](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1513): Use ObjectDisposedException throw helper
+
+Throw helpers are simpler and more efficient than an if block constructing a new exception instance.
+
+|Item|Value|
+|-|-|
+|Category|Maintainability|
+|Enabled|True|
+|Severity|Info|
+|CodeFix|True|
+---
+
## [CA1700](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1700): Do not name enum values 'Reserved'
This rule assumes that an enumeration member that has a name that contains "reserved" is not currently used but is a placeholder to be renamed or removed in a future version. Renaming or removing a member is a breaking change.
diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif
index 576fc974e9..543238f773 100644
--- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif
+++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif
@@ -1950,6 +1950,86 @@
]
}
},
+ "CA1510": {
+ "id": "CA1510",
+ "shortDescription": "Use ArgumentNullException throw helper",
+ "fullDescription": "Throw helpers are simpler and more efficient than an if block constructing a new exception instance.",
+ "defaultLevel": "note",
+ "helpUri": "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1510",
+ "properties": {
+ "category": "Maintainability",
+ "isEnabledByDefault": true,
+ "typeName": "UseExceptionThrowHelpers",
+ "languages": [
+ "C#",
+ "Visual Basic"
+ ],
+ "tags": [
+ "Telemetry",
+ "EnabledRuleInAggressiveMode"
+ ]
+ }
+ },
+ "CA1511": {
+ "id": "CA1511",
+ "shortDescription": "Use ArgumentException throw helper",
+ "fullDescription": "Throw helpers are simpler and more efficient than an if block constructing a new exception instance.",
+ "defaultLevel": "note",
+ "helpUri": "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1511",
+ "properties": {
+ "category": "Maintainability",
+ "isEnabledByDefault": true,
+ "typeName": "UseExceptionThrowHelpers",
+ "languages": [
+ "C#",
+ "Visual Basic"
+ ],
+ "tags": [
+ "Telemetry",
+ "EnabledRuleInAggressiveMode"
+ ]
+ }
+ },
+ "CA1512": {
+ "id": "CA1512",
+ "shortDescription": "Use ArgumentOutOfRangeException throw helper",
+ "fullDescription": "Throw helpers are simpler and more efficient than an if block constructing a new exception instance.",
+ "defaultLevel": "note",
+ "helpUri": "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1512",
+ "properties": {
+ "category": "Maintainability",
+ "isEnabledByDefault": true,
+ "typeName": "UseExceptionThrowHelpers",
+ "languages": [
+ "C#",
+ "Visual Basic"
+ ],
+ "tags": [
+ "Telemetry",
+ "EnabledRuleInAggressiveMode"
+ ]
+ }
+ },
+ "CA1513": {
+ "id": "CA1513",
+ "shortDescription": "Use ObjectDisposedException throw helper",
+ "fullDescription": "Throw helpers are simpler and more efficient than an if block constructing a new exception instance.",
+ "defaultLevel": "note",
+ "helpUri": "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1513",
+ "properties": {
+ "category": "Maintainability",
+ "isEnabledByDefault": true,
+ "typeName": "UseExceptionThrowHelpers",
+ "languages": [
+ "C#",
+ "Visual Basic"
+ ],
+ "tags": [
+ "Telemetry",
+ "EnabledRuleInAggressiveMode"
+ ]
+ }
+ },
"CA1700": {
"id": "CA1700",
"shortDescription": "Do not name enum values 'Reserved'",
diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md
index acfaceed87..339438ea6d 100644
--- a/src/NetAnalyzers/RulesMissingDocumentation.md
+++ b/src/NetAnalyzers/RulesMissingDocumentation.md
@@ -4,6 +4,10 @@ Rule ID | Missing Help Link | Title |
--------|-------------------|-------|
CA1311 | | Specify a culture or use an invariant version |
CA1421 | | This method uses runtime marshalling even when the 'DisableRuntimeMarshallingAttribute' is applied |
+CA1510 | | Use ArgumentNullException throw helper |
+CA1511 | | Use ArgumentException throw helper |
+CA1512 | | Use ArgumentOutOfRangeException throw helper |
+CA1513 | | Use ObjectDisposedException throw helper |
CA1852 | | Seal internal types |
CA1853 | | Unnecessary call to 'Dictionary.ContainsKey(key)' |
CA1855 | | Prefer 'Clear' over 'Fill' |
diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseExceptionThrowHelpersTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseExceptionThrowHelpersTests.cs
new file mode 100644
index 0000000000..ee37f3d144
--- /dev/null
+++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseExceptionThrowHelpersTests.cs
@@ -0,0 +1,1171 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Testing;
+using Xunit;
+using VerifyCS = Test.Utilities.CSharpCodeFixVerifier<
+ Microsoft.NetCore.Analyzers.Runtime.UseExceptionThrowHelpers,
+ Microsoft.NetCore.Analyzers.Runtime.UseExceptionThrowHelpersFixer>;
+using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier<
+ Microsoft.NetCore.Analyzers.Runtime.UseExceptionThrowHelpers,
+ Microsoft.NetCore.Analyzers.Runtime.UseExceptionThrowHelpersFixer>;
+
+namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests
+{
+ public class UseExceptionThrowHelpersTests
+ {
+ [Fact]
+ public async Task ArgumentNullExceptionThrowIfNull_DoesntExist_NoDiagnostics()
+ {
+ await VerifyCS.VerifyAnalyzerAsync(@"
+using System;
+
+namespace System
+{
+ public class ArgumentNullException : Exception
+ {
+ public ArgumentNullException(string paramName) { }
+ public ArgumentNullException(string paramName, string message) { }
+ }
+}
+
+class C
+{
+ void M(string arg)
+ {
+ if (arg is null)
+ throw new ArgumentNullException(nameof(arg));
+
+ if (arg == null)
+ throw new ArgumentNullException(nameof(arg));
+
+ if (null == arg)
+ throw new ArgumentNullException(nameof(arg));
+
+ if (arg is null)
+ {
+ throw new ArgumentNullException(nameof(arg));
+ }
+
+ if (arg == null)
+ {
+ throw new ArgumentNullException(nameof(arg));
+ }
+
+ if (null == arg)
+ {
+ throw new ArgumentNullException(nameof(arg));
+ }
+ }
+
+ string this[string name]
+ {
+ get
+ {
+ if (name is null)
+ throw new ArgumentNullException(nameof(name));
+ return name;
+ }
+ }
+}
+");
+ }
+
+ [Fact]
+ public async Task ArgumentNullExceptionThrowIfNull_FixAppliesAsAppropriate()
+ {
+ await new VerifyCS.Test()
+ {
+ LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp9,
+ TestCode =
+@"
+using System;
+
+namespace System
+{
+ public class ArgumentNullException : Exception
+ {
+ public ArgumentNullException() { }
+ public ArgumentNullException(string paramName) { }
+ public ArgumentNullException(string paramName, string message) { }
+ public ArgumentNullException(string paramName, Exception innerException) { }
+ public static void ThrowIfNull(object argument, string name = null) { }
+ }
+}
+
+class C
+{
+ void M(string arg)
+ {
+ {|CA1510:if (arg is null)
+ throw new ArgumentNullException(nameof(arg));|}
+ {|CA1510:if (arg == null)
+ throw new ArgumentNullException(nameof(arg), (string)null);|}
+ {|CA1510:if (null == arg)
+ throw new ArgumentNullException(nameof(arg));|}
+ {|CA1510:if (arg is null)
+ {
+ throw new ArgumentNullException(nameof(arg));
+ }|}
+ {|CA1510:if (arg == null)
+ {
+ throw new ArgumentNullException(nameof(arg), """");
+ }|}
+ {|CA1510:if (null == arg)
+ {
+ throw new ArgumentNullException(nameof(arg));
+ }|}
+ {|CA1510:if (null == arg)
+ {
+ throw new ArgumentNullException(""something else"");
+ }|}
+
+ if (arg == ""test"")
+ {
+ Console.WriteLine(arg);
+ }
+ else {|CA1510:if (arg is null)
+ {
+ throw new ArgumentNullException(nameof(arg));
+ }|}
+
+ if (arg is null)
+ throw new ArgumentNullException(nameof(arg), ""possibly meaningful message"");
+
+ if (arg is null)
+ throw new ArgumentNullException(nameof(arg), new Exception());
+
+ if (arg is null)
+ {
+ Console.WriteLine(); // another operation in the block
+ throw new ArgumentNullException(nameof(arg));
+ }
+
+ if (arg is not null) // inverted condition
+ {
+ throw new ArgumentNullException(nameof(arg));
+ }
+
+ if (arg != null) // inverted condition
+ {
+ throw new ArgumentNullException(nameof(arg));
+ }
+
+ if (null != arg) // inverted condition
+ {
+ throw new ArgumentNullException(nameof(arg));
+ }
+
+ if (arg is null)
+ {
+ throw new ArgumentNullException(nameof(arg));
+ }
+ else if (arg == ""test"")
+ {
+ Console.WriteLine(arg);
+ }
+
+ throw new ArgumentNullException(nameof(arg)); // no guard
+ }
+
+ string this[string name]
+ {
+ get
+ {
+ {|CA1510:if (name is null)
+ throw new ArgumentNullException(nameof(name));|}
+ return name;
+ }
+ }
+}
+",
+ FixedCode =
+@"
+using System;
+
+namespace System
+{
+ public class ArgumentNullException : Exception
+ {
+ public ArgumentNullException() { }
+ public ArgumentNullException(string paramName) { }
+ public ArgumentNullException(string paramName, string message) { }
+ public ArgumentNullException(string paramName, Exception innerException) { }
+ public static void ThrowIfNull(object argument, string name = null) { }
+ }
+}
+
+class C
+{
+ void M(string arg)
+ {
+ ArgumentNullException.ThrowIfNull(arg);
+ ArgumentNullException.ThrowIfNull(arg);
+ ArgumentNullException.ThrowIfNull(arg);
+ ArgumentNullException.ThrowIfNull(arg);
+ ArgumentNullException.ThrowIfNull(arg);
+ ArgumentNullException.ThrowIfNull(arg);
+ ArgumentNullException.ThrowIfNull(arg);
+
+ if (arg == ""test"")
+ {
+ Console.WriteLine(arg);
+ }
+ else ArgumentNullException.ThrowIfNull(arg);
+
+ if (arg is null)
+ throw new ArgumentNullException(nameof(arg), ""possibly meaningful message"");
+
+ if (arg is null)
+ throw new ArgumentNullException(nameof(arg), new Exception());
+
+ if (arg is null)
+ {
+ Console.WriteLine(); // another operation in the block
+ throw new ArgumentNullException(nameof(arg));
+ }
+
+ if (arg is not null) // inverted condition
+ {
+ throw new ArgumentNullException(nameof(arg));
+ }
+
+ if (arg != null) // inverted condition
+ {
+ throw new ArgumentNullException(nameof(arg));
+ }
+
+ if (null != arg) // inverted condition
+ {
+ throw new ArgumentNullException(nameof(arg));
+ }
+
+ if (arg is null)
+ {
+ throw new ArgumentNullException(nameof(arg));
+ }
+ else if (arg == ""test"")
+ {
+ Console.WriteLine(arg);
+ }
+
+ throw new ArgumentNullException(nameof(arg)); // no guard
+ }
+
+ string this[string name]
+ {
+ get
+ {
+ ArgumentNullException.ThrowIfNull(name);
+ return name;
+ }
+ }
+}
+"
+ }.RunAsync();
+ }
+
+
+ [Fact]
+ public async Task ArgumentNullExceptionThrowIfNull_EnsureSystemIsUsed()
+ {
+ await new VerifyCS.Test()
+ {
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
+ TestCode =
+@"
+class C
+{
+ void M(string arg)
+ {
+ {|CA1510:if (arg is null) throw new System.ArgumentNullException(nameof(arg));|}
+ }
+}
+",
+ FixedCode =
+@"
+class C
+{
+ void M(string arg)
+ {
+ System.ArgumentNullException.ThrowIfNull(arg);
+ }
+}
+"
+ }.RunAsync();
+ }
+
+ [Fact]
+ public async Task ArgumentExceptionThrowIfNullOrEmpty_DoesntExist_NoDiagnostics()
+ {
+ await VerifyCS.VerifyAnalyzerAsync(@"
+using System;
+
+namespace System
+{
+ public class ArgumentException : Exception
+ {
+ public ArgumentException() { }
+ public ArgumentException(string message) { }
+ public ArgumentException(string message, string paramName) { }
+ public ArgumentException(string message, string paramName, Exception innerException) { }
+ }
+}
+
+class C
+{
+ void M(string arg)
+ {
+ if (string.IsNullOrEmpty(arg))
+ throw new ArgumentException("""", ""arg"");
+
+ if (arg is null || arg.Length == 0)
+ throw new ArgumentException("""", ""arg"");
+
+ if (arg == null || 0 == arg.Length)
+ throw new ArgumentException("""", ""arg"");
+
+ if (arg is null || arg == string.Empty)
+ {
+ throw new ArgumentException("""", ""arg"");
+ }
+ }
+}
+");
+ }
+
+ [Fact]
+ public async Task ArgumentExceptionThrowIfNullOrEmpty_FixAppliesAsAppropriate()
+ {
+ await new VerifyCS.Test()
+ {
+ LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp9,
+ TestCode =
+@"
+using System;
+
+namespace System
+{
+ public class ArgumentException : Exception
+ {
+ public ArgumentException() { }
+ public ArgumentException(string message) { }
+ public ArgumentException(string message, string paramName) { }
+ public ArgumentException(string message, string paramName, Exception innerException) { }
+ public static void ThrowIfNullOrEmpty(string arg) { }
+ }
+}
+
+class C
+{
+ void M0(string arg)
+ {
+ {|CA1511:if (string.IsNullOrEmpty(arg))
+ throw new ArgumentException("""", ""arg"");|}
+
+ {|CA1511:if (arg is null || arg.Length == 0)
+ throw new ArgumentException("""", ""arg"");|}
+
+ {|CA1511:if (arg == null || 0 == arg.Length)
+ throw new ArgumentException("""", ""arg"");|}
+
+ {|CA1511:if (arg == null || arg == string.Empty)
+ throw new ArgumentException("""", ""arg"");|}
+
+ {|CA1511:if (string.IsNullOrEmpty(arg))
+ {
+ throw new ArgumentException();
+ }|}
+
+ {|CA1511:if (arg is null || arg.Length == 0)
+ {
+ throw new ArgumentException("""");
+ }|}
+
+ {|CA1511:if (arg == null || 0 == arg.Length)
+ {
+ throw new ArgumentException("""", ""arg"");
+ }|}
+
+ {|CA1511:if (arg == null || arg == string.Empty)
+ {
+ throw new ArgumentException();
+ }|}
+
+ if (arg is null)
+ throw new ArgumentException("""", ""arg"");
+ }
+
+ void M1(string arg1, string arg2)
+ {
+ if (!string.IsNullOrEmpty(arg1))
+ throw new ArgumentException("""", ""arg1"");
+
+ if (string.IsNullOrEmpty(arg1))
+ throw new ArgumentException(""something"", ""arg1"");
+
+ if (string.IsNullOrEmpty(arg1))
+ throw new ArgumentException("""", ""arg1"", new Exception());
+
+ if (arg1 is not null && arg1.Length != 0)
+ throw new ArgumentException("""", ""arg1"");
+
+ if (arg1 is null)
+ throw new ArgumentException("""", ""arg1"");
+
+ if (arg1.Length == 0) // this case combined with the previous one could be handled in the future
+ throw new ArgumentException("""", ""arg1"");
+
+ if (arg1 == string.Empty) // here as well
+ {
+ throw new ArgumentException("""", ""arg1"");
+ }
+
+ if (string.IsNullOrEmpty(arg1))
+ {
+ Console.WriteLine();
+ throw new ArgumentException();
+ }
+
+ if (arg1 is null || arg2.Length == 0)
+ throw new ArgumentException();
+
+ if (arg2 == null || arg1 == string.Empty)
+ throw new ArgumentException();
+
+ string nonArg = ""test"";
+
+ if (string.IsNullOrEmpty(nonArg))
+ {
+ Console.WriteLine();
+ throw new ArgumentException();
+ }
+
+ if (nonArg is null || nonArg.Length == 0)
+ {
+ Console.WriteLine();
+ throw new ArgumentException();
+ }
+ }
+}
+",
+ FixedCode =
+@"
+using System;
+
+namespace System
+{
+ public class ArgumentException : Exception
+ {
+ public ArgumentException() { }
+ public ArgumentException(string message) { }
+ public ArgumentException(string message, string paramName) { }
+ public ArgumentException(string message, string paramName, Exception innerException) { }
+ public static void ThrowIfNullOrEmpty(string arg) { }
+ }
+}
+
+class C
+{
+ void M0(string arg)
+ {
+ ArgumentException.ThrowIfNullOrEmpty(arg);
+
+ ArgumentException.ThrowIfNullOrEmpty(arg);
+
+ ArgumentException.ThrowIfNullOrEmpty(arg);
+
+ ArgumentException.ThrowIfNullOrEmpty(arg);
+
+ ArgumentException.ThrowIfNullOrEmpty(arg);
+
+ ArgumentException.ThrowIfNullOrEmpty(arg);
+
+ ArgumentException.ThrowIfNullOrEmpty(arg);
+
+ ArgumentException.ThrowIfNullOrEmpty(arg);
+
+ if (arg is null)
+ throw new ArgumentException("""", ""arg"");
+ }
+
+ void M1(string arg1, string arg2)
+ {
+ if (!string.IsNullOrEmpty(arg1))
+ throw new ArgumentException("""", ""arg1"");
+
+ if (string.IsNullOrEmpty(arg1))
+ throw new ArgumentException(""something"", ""arg1"");
+
+ if (string.IsNullOrEmpty(arg1))
+ throw new ArgumentException("""", ""arg1"", new Exception());
+
+ if (arg1 is not null && arg1.Length != 0)
+ throw new ArgumentException("""", ""arg1"");
+
+ if (arg1 is null)
+ throw new ArgumentException("""", ""arg1"");
+
+ if (arg1.Length == 0) // this case combined with the previous one could be handled in the future
+ throw new ArgumentException("""", ""arg1"");
+
+ if (arg1 == string.Empty) // here as well
+ {
+ throw new ArgumentException("""", ""arg1"");
+ }
+
+ if (string.IsNullOrEmpty(arg1))
+ {
+ Console.WriteLine();
+ throw new ArgumentException();
+ }
+
+ if (arg1 is null || arg2.Length == 0)
+ throw new ArgumentException();
+
+ if (arg2 == null || arg1 == string.Empty)
+ throw new ArgumentException();
+
+ string nonArg = ""test"";
+
+ if (string.IsNullOrEmpty(nonArg))
+ {
+ Console.WriteLine();
+ throw new ArgumentException();
+ }
+
+ if (nonArg is null || nonArg.Length == 0)
+ {
+ Console.WriteLine();
+ throw new ArgumentException();
+ }
+ }
+}
+"
+ }.RunAsync();
+ }
+
+ [Fact]
+ public async Task ArgumentOutOfRangeExceptionThrowIf_DoesntExist_NoDiagnostics()
+ {
+ await VerifyCS.VerifyAnalyzerAsync(@"
+using System;
+
+namespace System
+{
+ public class ArgumentOutOfRangeException : Exception
+ {
+ public ArgumentOutOfRangeException(string paramName) { }
+ public ArgumentOutOfRangeException(string paramName, string message) { }
+ }
+}
+
+class C
+{
+ void M(int arg)
+ {
+ if (arg is 0)
+ throw new ArgumentOutOfRangeException(nameof(arg));
+
+ if (arg == 0)
+ throw new ArgumentOutOfRangeException(nameof(arg));
+
+ if (arg < 0)
+ throw new ArgumentOutOfRangeException(nameof(arg));
+
+ if (arg <= 0)
+ throw new ArgumentOutOfRangeException(nameof(arg));
+
+ if (arg <= 42)
+ throw new ArgumentOutOfRangeException(nameof(arg));
+
+ if (arg < 42)
+ throw new ArgumentOutOfRangeException(nameof(arg));
+
+ if (arg > 42)
+ {
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }
+
+ if (arg >= 42)
+ {
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }
+
+ if (arg > TimeSpan.FromSeconds(42).TotalSeconds)
+ {
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }
+ }
+}
+");
+ }
+
+ [Fact]
+ public async Task ArgumentOutOfRangeExceptionThrowIf_FixAppliesAsAppropriate()
+ {
+ await new VerifyCS.Test()
+ {
+ LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp9,
+ TestCode =
+@"
+using System;
+
+namespace System
+{
+ public class ArgumentOutOfRangeException : Exception
+ {
+ public ArgumentOutOfRangeException(string paramName) { }
+ public ArgumentOutOfRangeException(string paramName, string message) { }
+ public static void ThrowIfZero(T arg) { }
+ public static void ThrowIfNegative(T arg) { }
+ public static void ThrowIfNegativeOrZero(T arg) { }
+ public static void ThrowIfGreaterThan(T arg, T other) { }
+ public static void ThrowIfGreaterThanOrEqual(T arg, T other) { }
+ public static void ThrowIfLessThan(T arg, T other) { }
+ public static void ThrowIfLessThanOrEqual(T arg, T other) { }
+ }
+}
+
+class C
+{
+ void M(int arg)
+ {
+ {|CA1512:if (arg is 0)
+ throw new ArgumentOutOfRangeException(nameof(arg));|}
+ {|CA1512:if (arg == 0)
+ throw new ArgumentOutOfRangeException(nameof(arg));|}
+ {|CA1512:if (0 == arg)
+ throw new ArgumentOutOfRangeException(nameof(arg));|}
+
+ {|CA1512:if (arg < 0)
+ throw new ArgumentOutOfRangeException(nameof(arg));|}
+ {|CA1512:if (0 > arg)
+ throw new ArgumentOutOfRangeException(nameof(arg));|}
+
+ {|CA1512:if (arg <= 0)
+ throw new ArgumentOutOfRangeException(nameof(arg));|}
+ {|CA1512:if (0 >= arg)
+ throw new ArgumentOutOfRangeException(nameof(arg));|}
+
+ {|CA1512:if (arg <= 42)
+ throw new ArgumentOutOfRangeException(nameof(arg));|}
+ {|CA1512:if (42 >= arg)
+ throw new ArgumentOutOfRangeException(nameof(arg));|}
+
+ {|CA1512:if (arg < 42)
+ throw new ArgumentOutOfRangeException(nameof(arg));|}
+ {|CA1512:if (42 > arg)
+ throw new ArgumentOutOfRangeException(nameof(arg));|}
+
+ {|CA1512:if (arg > 42)
+ {
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }|}
+ {|CA1512:if (42 < arg)
+ {
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }|}
+
+ {|CA1512:if (arg >= 42)
+ {
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }|}
+ {|CA1512:if (42 <= arg)
+ {
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }|}
+
+ {|CA1512:if (arg > (int)TimeSpan.FromSeconds(42).TotalSeconds)
+ {
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }|}
+ {|CA1512:if ((int)TimeSpan.FromSeconds(42).TotalSeconds < arg)
+ {
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }|}
+
+ if (arg is 42)
+ throw new ArgumentOutOfRangeException(nameof(arg));
+
+ if (arg == 42)
+ throw new ArgumentOutOfRangeException(nameof(arg));
+
+ if (arg is 0)
+ {
+ Console.WriteLine();
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }
+
+ if (arg is < 0) // we could augment the analyzer in the future to support this
+ {
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }
+
+ if (arg > 42 && arg < 84) // we could augment the analyzer in the future to support this
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }
+
+ void Enums(DayOfWeek dow)
+ {
+ if (dow > DayOfWeek.Sunday)
+ throw new ArgumentOutOfRangeException(nameof(dow));
+ }
+
+ void Nullables(int? arg)
+ {
+ if (arg < 0)
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }
+}
+",
+ FixedCode =
+@"
+using System;
+
+namespace System
+{
+ public class ArgumentOutOfRangeException : Exception
+ {
+ public ArgumentOutOfRangeException(string paramName) { }
+ public ArgumentOutOfRangeException(string paramName, string message) { }
+ public static void ThrowIfZero(T arg) { }
+ public static void ThrowIfNegative(T arg) { }
+ public static void ThrowIfNegativeOrZero(T arg) { }
+ public static void ThrowIfGreaterThan(T arg, T other) { }
+ public static void ThrowIfGreaterThanOrEqual(T arg, T other) { }
+ public static void ThrowIfLessThan(T arg, T other) { }
+ public static void ThrowIfLessThanOrEqual(T arg, T other) { }
+ }
+}
+
+class C
+{
+ void M(int arg)
+ {
+ ArgumentOutOfRangeException.ThrowIfZero(arg);
+ ArgumentOutOfRangeException.ThrowIfZero(arg);
+ ArgumentOutOfRangeException.ThrowIfZero(arg);
+
+ ArgumentOutOfRangeException.ThrowIfNegative(arg);
+ ArgumentOutOfRangeException.ThrowIfNegative(arg);
+
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(arg);
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(arg);
+
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(arg, 42);
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(arg, 42);
+
+ ArgumentOutOfRangeException.ThrowIfLessThan(arg, 42);
+ ArgumentOutOfRangeException.ThrowIfLessThan(arg, 42);
+
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(arg, 42);
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(arg, 42);
+
+ ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(arg, 42);
+ ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(arg, 42);
+
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(arg, (int)TimeSpan.FromSeconds(42).TotalSeconds);
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(arg, (int)TimeSpan.FromSeconds(42).TotalSeconds);
+
+ if (arg is 42)
+ throw new ArgumentOutOfRangeException(nameof(arg));
+
+ if (arg == 42)
+ throw new ArgumentOutOfRangeException(nameof(arg));
+
+ if (arg is 0)
+ {
+ Console.WriteLine();
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }
+
+ if (arg is < 0) // we could augment the analyzer in the future to support this
+ {
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }
+
+ if (arg > 42 && arg < 84) // we could augment the analyzer in the future to support this
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }
+
+ void Enums(DayOfWeek dow)
+ {
+ if (dow > DayOfWeek.Sunday)
+ throw new ArgumentOutOfRangeException(nameof(dow));
+ }
+
+ void Nullables(int? arg)
+ {
+ if (arg < 0)
+ throw new ArgumentOutOfRangeException(nameof(arg));
+ }
+}
+"
+ }.RunAsync();
+ }
+
+ [Fact]
+ public async Task ObjectDisposedExceptionThrowIf_DoesntExist_NoDiagnostics()
+ {
+ await VerifyCS.VerifyAnalyzerAsync(@"
+using System;
+
+namespace System
+{
+ public class ObjectDisposedException : Exception
+ {
+ public ObjectDisposedException(string type) { }
+ public ObjectDisposedException(string type, string message) { }
+ }
+}
+
+class C
+{
+ private bool IsDisposed { get; set; }
+
+ void M()
+ {
+ if (IsDisposed) throw new ObjectDisposedException(null);
+
+ if (IsDisposed)
+ throw new ObjectDisposedException(GetType().Name);
+
+ if (IsDisposed)
+ {
+ throw new ObjectDisposedException(GetType().FullName);
+ }
+
+ if (DateTime.UtcNow.Hour == 0)
+ {
+ throw new ObjectDisposedException(nameof(DateTime));
+ }
+ }
+
+ string Prop
+ {
+ get
+ {
+ if (IsDisposed) throw new ObjectDisposedException(null);
+ return ""test"";
+ }
+ }
+}
+");
+ }
+
+ [Fact]
+ public async Task ObjectDisposedExceptionThrowIf_FixesAppliedAsAppropriate()
+ {
+ var test = new VerifyCS.Test()
+ {
+ TestCode = @"
+using System;
+
+namespace System
+{
+ public class ObjectDisposedException : Exception
+ {
+ public ObjectDisposedException(string type) { }
+ public ObjectDisposedException(string type, string message) { }
+ public static void ThrowIf(bool condition, object instance) { }
+ public static void ThrowIf(bool condition, Type type) { }
+ }
+}
+
+class C
+{
+ private bool IsDisposed { get; set; }
+
+ private C _state;
+
+ void M(object something)
+ {
+ {|CA1513:if (IsDisposed) throw new ObjectDisposedException(null);|}
+
+ {|CA1513:if (IsDisposed)
+ {
+ throw new ObjectDisposedException(this.GetType().FullName);
+ }|}
+
+ {|CA1513:if (IsDisposed)
+ throw new ObjectDisposedException(something.GetType().Name);|}
+
+ {|CA1513:if (_state.IsDisposed)
+ throw new ObjectDisposedException(_state.GetType().Name);|}
+
+ {|CA1513:if (DateTime.UtcNow.Hour == 0)
+ {
+ throw new ObjectDisposedException(nameof(DateTime));
+ }|}
+
+ if (IsDisposed)
+ throw new ObjectDisposedException(GetType().Name, ""something"");
+
+ throw new ObjectDisposedException(null);
+ }
+
+ string Prop
+ {
+ get
+ {
+ {|CA1513:if (IsDisposed)
+ throw new ObjectDisposedException(null);|}
+ return ""test"";
+ }
+ }
+}
+",
+ FixedCode =
+@"
+using System;
+
+namespace System
+{
+ public class ObjectDisposedException : Exception
+ {
+ public ObjectDisposedException(string type) { }
+ public ObjectDisposedException(string type, string message) { }
+ public static void ThrowIf(bool condition, object instance) { }
+ public static void ThrowIf(bool condition, Type type) { }
+ }
+}
+
+class C
+{
+ private bool IsDisposed { get; set; }
+
+ private C _state;
+
+ void M(object something)
+ {
+ {|CA1513:if (IsDisposed) throw new ObjectDisposedException(null);|}
+
+ ObjectDisposedException.ThrowIf(IsDisposed, this);
+
+ ObjectDisposedException.ThrowIf(IsDisposed, something);
+
+ ObjectDisposedException.ThrowIf(_state.IsDisposed, _state);
+
+ {|CA1513:if (DateTime.UtcNow.Hour == 0)
+ {
+ throw new ObjectDisposedException(nameof(DateTime));
+ }|}
+
+ if (IsDisposed)
+ throw new ObjectDisposedException(GetType().Name, ""something"");
+
+ throw new ObjectDisposedException(null);
+ }
+
+ string Prop
+ {
+ get
+ {
+ {|CA1513:if (IsDisposed)
+ throw new ObjectDisposedException(null);|}
+ return ""test"";
+ }
+ }
+}
+"
+ };
+ test.FixedState.MarkupHandling = CodeAnalysis.Testing.MarkupMode.Allow;
+ await test.RunAsync();
+ }
+
+ [Fact]
+ public async Task VisualBasic_ValidateAllThrowHelpers()
+ {
+ await VerifyVB.VerifyCodeFixAsync(
+@"
+Imports System
+
+Class C
+ Public Sub M(ByVal arg As String, ByVal value As Integer)
+ {|CA1510:If arg Is Nothing Then
+ Throw New ArgumentNullException(nameof(arg))
+ End If|}
+
+ {|CA1511:If String.IsNullOrEmpty(arg) Then
+ Throw New ArgumentException("""", nameof(arg))
+ End If|}
+
+ {|CA1513:If arg Is Nothing Then
+ Throw New ObjectDisposedException(Me.GetType().Name)
+ End If|}
+
+ {|CA1512:If value < 42 Then
+ Throw New ArgumentOutOfRangeException(nameof(value))
+ End If|}
+ End Sub
+End Class
+
+Namespace System
+ Public Class ArgumentNullException
+ Inherits Exception
+
+ Public Sub New()
+ End Sub
+
+ Public Sub New(ByVal paramName As String)
+ End Sub
+
+ Public Shared Sub ThrowIfNull(ByVal argument As Object, ByVal Optional name As String = Nothing)
+ End Sub
+ End Class
+
+ Public Class ArgumentException
+ Inherits Exception
+
+ Public Sub New()
+ End Sub
+
+ Public Sub New(ByVal message As String)
+ End Sub
+
+ Public Sub New(ByVal message As String, ByVal paramName As String)
+ End Sub
+
+ Public Shared Sub ThrowIfNullOrEmpty(ByVal argument As String, ByVal Optional name As String = Nothing)
+ End Sub
+ End Class
+
+ Public Class ArgumentOutOfRangeException
+ Inherits Exception
+
+ Public Sub New(ByVal paramName As String)
+ End Sub
+
+ Public Shared Sub ThrowIfZero(Of T)(ByVal arg As T)
+ End Sub
+
+ Public Shared Sub ThrowIfNegative(Of T)(ByVal arg As T)
+ End Sub
+
+ Public Shared Sub ThrowIfNegativeOrZero(Of T)(ByVal arg As T)
+ End Sub
+
+ Public Shared Sub ThrowIfGreaterThan(Of T)(ByVal arg As T, ByVal other As T)
+ End Sub
+
+ Public Shared Sub ThrowIfGreaterThanOrEqual(Of T)(ByVal arg As T, ByVal other As T)
+ End Sub
+
+ Public Shared Sub ThrowIfLessThan(Of T)(ByVal arg As T, ByVal other As T)
+ End Sub
+
+ Public Shared Sub ThrowIfLessThanOrEqual(Of T)(ByVal arg As T, ByVal other As T)
+ End Sub
+ End Class
+
+ Public Class ObjectDisposedException
+ Inherits Exception
+
+ Public Sub New(ByVal type As String)
+ End Sub
+
+ Public Sub New(ByVal type As String, ByVal message As String)
+ End Sub
+
+ Public Shared Sub ThrowIf(ByVal condition As Boolean, ByVal instance As Object)
+ End Sub
+
+ Public Shared Sub ThrowIf(ByVal condition As Boolean, ByVal type As Type)
+ End Sub
+ End Class
+End Namespace
+",
+@"
+Imports System
+
+Class C
+ Public Sub M(ByVal arg As String, ByVal value As Integer)
+ ArgumentNullException.ThrowIfNull(arg)
+
+ ArgumentException.ThrowIfNullOrEmpty(arg)
+
+ ObjectDisposedException.ThrowIf(arg Is Nothing, Me)
+
+ ArgumentOutOfRangeException.ThrowIfLessThan(value, 42)
+ End Sub
+End Class
+
+Namespace System
+ Public Class ArgumentNullException
+ Inherits Exception
+
+ Public Sub New()
+ End Sub
+
+ Public Sub New(ByVal paramName As String)
+ End Sub
+
+ Public Shared Sub ThrowIfNull(ByVal argument As Object, ByVal Optional name As String = Nothing)
+ End Sub
+ End Class
+
+ Public Class ArgumentException
+ Inherits Exception
+
+ Public Sub New()
+ End Sub
+
+ Public Sub New(ByVal message As String)
+ End Sub
+
+ Public Sub New(ByVal message As String, ByVal paramName As String)
+ End Sub
+
+ Public Shared Sub ThrowIfNullOrEmpty(ByVal argument As String, ByVal Optional name As String = Nothing)
+ End Sub
+ End Class
+
+ Public Class ArgumentOutOfRangeException
+ Inherits Exception
+
+ Public Sub New(ByVal paramName As String)
+ End Sub
+
+ Public Shared Sub ThrowIfZero(Of T)(ByVal arg As T)
+ End Sub
+
+ Public Shared Sub ThrowIfNegative(Of T)(ByVal arg As T)
+ End Sub
+
+ Public Shared Sub ThrowIfNegativeOrZero(Of T)(ByVal arg As T)
+ End Sub
+
+ Public Shared Sub ThrowIfGreaterThan(Of T)(ByVal arg As T, ByVal other As T)
+ End Sub
+
+ Public Shared Sub ThrowIfGreaterThanOrEqual(Of T)(ByVal arg As T, ByVal other As T)
+ End Sub
+
+ Public Shared Sub ThrowIfLessThan(Of T)(ByVal arg As T, ByVal other As T)
+ End Sub
+
+ Public Shared Sub ThrowIfLessThanOrEqual(Of T)(ByVal arg As T, ByVal other As T)
+ End Sub
+ End Class
+
+ Public Class ObjectDisposedException
+ Inherits Exception
+
+ Public Sub New(ByVal type As String)
+ End Sub
+
+ Public Sub New(ByVal type As String, ByVal message As String)
+ End Sub
+
+ Public Shared Sub ThrowIf(ByVal condition As Boolean, ByVal instance As Object)
+ End Sub
+
+ Public Shared Sub ThrowIf(ByVal condition As Boolean, ByVal type As Type)
+ End Sub
+ End Class
+End Namespace
+");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt
index 00c64a2dd3..57d0e801ad 100644
--- a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt
+++ b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt
@@ -17,7 +17,7 @@ Security: CA2100-CA2153, CA2300-CA2330, CA3000-CA3147, CA5300-CA5405
Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2260
Naming: CA1700-CA1727
Interoperability: CA1400-CA1422
-Maintainability: CA1500-CA1509
+Maintainability: CA1500-CA1513
Reliability: CA9998-CA9999, CA2000-CA2020
Documentation: CA1200-CA1200
diff --git a/src/Utilities/Compiler/WellKnownTypeNames.cs b/src/Utilities/Compiler/WellKnownTypeNames.cs
index fdbfca0d8d..95930586d0 100644
--- a/src/Utilities/Compiler/WellKnownTypeNames.cs
+++ b/src/Utilities/Compiler/WellKnownTypeNames.cs
@@ -103,6 +103,8 @@ internal static class WellKnownTypeNames
public const string SystemAppContext = "System.AppContext";
public const string SystemAppDomain = "System.AppDomain";
public const string SystemArgumentException = "System.ArgumentException";
+ public const string SystemArgumentNullException = "System.ArgumentNullException";
+ public const string SystemArgumentOutOfRangeException = "System.ArgumentOutOfRangeException";
public const string SystemAttribute = "System.Attribute";
public const string SystemAttributeTargets = "System.AttributeTargets";
public const string SystemAttributeUsageAttribute = "System.AttributeUsageAttribute";
@@ -270,6 +272,7 @@ internal static class WellKnownTypeNames
public const string SystemNumber = "System.Number";
public const string SystemNumericsINumber1 = "System.Numerics.INumber`1";
public const string SystemObject = "System.Object";
+ public const string SystemObjectDisposedException = "System.ObjectDisposedException";
public const string SystemObsoleteAttribute = "System.ObsoleteAttribute";
public const string SystemOperatingSystem = "System.OperatingSystem";
public const string SystemOperationCanceledException = "System.OperationCanceledException";