diff --git a/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Runtime/CSharpInstantiateArgumentExceptionsCorrectly.Fixer.cs b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Runtime/CSharpInstantiateArgumentExceptionsCorrectly.Fixer.cs deleted file mode 100644 index 17e6d2a96e..0000000000 --- a/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Runtime/CSharpInstantiateArgumentExceptionsCorrectly.Fixer.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Composition; -using Microsoft.NetCore.Analyzers.Runtime; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; - -namespace Microsoft.NetCore.CSharp.Analyzers.Runtime -{ - /// - /// CA2208: Instantiate argument exceptions correctly - /// - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - public sealed class CSharpInstantiateArgumentExceptionsCorrectlyFixer : InstantiateArgumentExceptionsCorrectlyFixer - { - } -} \ No newline at end of file diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index b86e12e278..72eb7b2d13 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -19,4 +19,9 @@ CA2012 | Reliability | Hidden | UseValueTasksCorrectlyAnalyzer, [Documentation]( CA2013 | Reliability | Warning | DoNotUseReferenceEqualsWithValueTypesAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca2013) CA2014 | Reliability | Warning | DoNotUseStackallocInLoopsAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca2014) CA2015 | Reliability | Warning | DoNotDefineFinalizersForTypesDerivedFromMemoryManager, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca2015) -CA2247 | Usage | Warning | DoNotCreateTaskCompletionSourceWithWrongArguments, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca2247) \ No newline at end of file +CA2247 | Usage | Warning | DoNotCreateTaskCompletionSourceWithWrongArguments, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca2247) + +### Changed Rules +Rule ID | New Category | New Severity | Old Category | Old Severity | Notes +--------|--------------|--------------|--------------|--------------|------- +CA2208 | Usage | Info | Usage | Hidden | InstantiateArgumentExceptionsCorrectlyAnalyzer, [Documentation](https://docs.microsoft.com/visualstudio/code-quality/ca2208) \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 7822eafe1c..1cd61817a4 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1287,6 +1287,12 @@ Potential stack overflow. Move the stackalloc out of the loop. + + Change to call the two argument constructor, pass null for the message. + + + Swap the arguments order + Prefer strongly-typed Append and Insert method overloads on StringBuilder. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/InstantiateArgumentExceptionsCorrectly.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/InstantiateArgumentExceptionsCorrectly.Fixer.cs index e9e983ae71..544a6a51e7 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/InstantiateArgumentExceptionsCorrectly.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/InstantiateArgumentExceptionsCorrectly.Fixer.cs @@ -1,29 +1,122 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; using System.Threading.Tasks; +using System.Diagnostics; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Operations; namespace Microsoft.NetCore.Analyzers.Runtime { /// /// CA2208: Instantiate argument exceptions correctly /// - public abstract class InstantiateArgumentExceptionsCorrectlyFixer : CodeFixProvider + [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] + public sealed class InstantiateArgumentExceptionsCorrectlyFixer : CodeFixProvider { - public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Empty; + public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(InstantiateArgumentExceptionsCorrectlyAnalyzer.RuleId); - public sealed override FixAllProvider GetFixAllProvider() + public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { - // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers - return WellKnownFixAllProviders.BatchFixer; + var diagnostic = context.Diagnostics.First(); + string paramPositionString = diagnostic.Properties.GetValueOrDefault(InstantiateArgumentExceptionsCorrectlyAnalyzer.MessagePosition); + if (paramPositionString != null) + { + SyntaxNode root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode node = root.FindNode(context.Span, getInnermostNodeForTie: true); + if (node != null) + { + await PopulateCodeFixAsync(context, diagnostic, paramPositionString, node).ConfigureAwait(false); + } + } } - public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) + private static async Task PopulateCodeFixAsync(CodeFixContext context, Diagnostic diagnostic, string paramPositionString, SyntaxNode node) { - // Fixer not yet implemented. - return Task.CompletedTask; + SemanticModel model = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + var operation = model.GetOperation(node, context.CancellationToken); + if (operation is IObjectCreationOperation creation) + { + if (int.TryParse(paramPositionString, out int paramPosition)) + { + CodeAction? codeAction = null; + if (creation.Arguments.Length == 1) + { + // Add null message + codeAction = CodeAction.Create( + title: MicrosoftNetCoreAnalyzersResources.InstantiateArgumentExceptionsCorrectlyChangeToTwoArgumentCodeFixTitle, + createChangedDocument: c => AddNullMessageToArgumentListAsync(context.Document, creation, c), + equivalenceKey: MicrosoftNetCoreAnalyzersResources.InstantiateArgumentExceptionsCorrectlyChangeToTwoArgumentCodeFixTitle); + } + else + { + // Swap message and paramete name + codeAction = CodeAction.Create( + title: MicrosoftNetCoreAnalyzersResources.InstantiateArgumentExceptionsCorrectlyFlipArgumentOrderCodeFixTitle, + createChangedDocument: c => SwapArgumentsOrderAsync(context.Document, creation, paramPosition, creation.Arguments.Length, c), + equivalenceKey: MicrosoftNetCoreAnalyzersResources.InstantiateArgumentExceptionsCorrectlyFlipArgumentOrderCodeFixTitle); + } + context.RegisterCodeFix(codeAction, diagnostic); + } + } + } + + private static async Task SwapArgumentsOrderAsync(Document document, IObjectCreationOperation creation, int paramPosition, int argumentCount, CancellationToken token) + { + DocumentEditor editor = await DocumentEditor.CreateAsync(document, token).ConfigureAwait(false); + SyntaxNode parameter = AddNameOfIfLiteral(creation.Arguments[paramPosition].Value, editor.Generator); + SyntaxNode newCreation; + if (argumentCount == 2) + { + if (paramPosition == 0) + { + newCreation = editor.Generator.ObjectCreationExpression(creation.Type, creation.Arguments[1].Syntax, parameter); + } + else + { + newCreation = editor.Generator.ObjectCreationExpression(creation.Type, parameter, creation.Arguments[0].Syntax); + } + } + else + { + Debug.Assert(argumentCount == 3); + if (paramPosition == 0) + { + newCreation = editor.Generator.ObjectCreationExpression(creation.Type, creation.Arguments[1].Syntax, parameter, creation.Arguments[2].Syntax); + } + else + { + newCreation = editor.Generator.ObjectCreationExpression(creation.Type, parameter, creation.Arguments[1].Syntax, creation.Arguments[0].Syntax); + } + } + editor.ReplaceNode(creation.Syntax, newCreation); + return editor.GetChangedDocument(); + } + private static async Task AddNullMessageToArgumentListAsync(Document document, IObjectCreationOperation creation, CancellationToken token) + { + DocumentEditor editor = await DocumentEditor.CreateAsync(document, token).ConfigureAwait(false); + SyntaxNode argument = AddNameOfIfLiteral(creation.Arguments[0].Value, editor.Generator); + SyntaxNode newCreation = editor.Generator.ObjectCreationExpression(creation.Type, editor.Generator.Argument(editor.Generator.NullLiteralExpression()), argument); + editor.ReplaceNode(creation.Syntax, newCreation); + return editor.GetChangedDocument(); + } + + private static SyntaxNode AddNameOfIfLiteral(IOperation expression, SyntaxGenerator generator) + { + if (expression is ILiteralOperation literal) + { + return generator.NameOfExpression(generator.IdentifierName(literal.ConstantValue.Value.ToString())); + } + return expression.Syntax; } } } \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/InstantiateArgumentExceptionsCorrectly.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/InstantiateArgumentExceptionsCorrectly.cs index 4ee5aba4d2..67491def79 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/InstantiateArgumentExceptionsCorrectly.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/InstantiateArgumentExceptionsCorrectly.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Immutable; +using System.Globalization; using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis; @@ -16,6 +17,7 @@ namespace Microsoft.NetCore.Analyzers.Runtime public sealed class InstantiateArgumentExceptionsCorrectlyAnalyzer : DiagnosticAnalyzer { internal const string RuleId = "CA2208"; + internal const string MessagePosition = nameof(MessagePosition); private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.InstantiateArgumentExceptionsCorrectlyTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); @@ -28,7 +30,7 @@ public sealed class InstantiateArgumentExceptionsCorrectlyAnalyzer : DiagnosticA s_localizableTitle, s_localizableMessageNoArguments, DiagnosticCategory.Usage, - RuleLevel.IdeHidden_BulkConfigurable, + RuleLevel.IdeSuggestion, description: s_localizableDescription, isPortedFxCopRule: true, isDataflowRule: false); @@ -37,7 +39,7 @@ public sealed class InstantiateArgumentExceptionsCorrectlyAnalyzer : DiagnosticA s_localizableTitle, s_localizableMessageIncorrectMessage, DiagnosticCategory.Usage, - RuleLevel.IdeHidden_BulkConfigurable, + RuleLevel.IdeSuggestion, description: s_localizableDescription, isPortedFxCopRule: true, isDataflowRule: false); @@ -46,7 +48,7 @@ public sealed class InstantiateArgumentExceptionsCorrectlyAnalyzer : DiagnosticA s_localizableTitle, s_localizableMessageIncorrectParameterName, DiagnosticCategory.Usage, - RuleLevel.IdeHidden_BulkConfigurable, + RuleLevel.IdeSuggestion, description: s_localizableDescription, isPortedFxCopRule: true, isDataflowRule: false); @@ -84,14 +86,14 @@ private static void AnalyzeObjectCreation( ITypeSymbol argumentExceptionType) { var creation = (IObjectCreationOperation)context.Operation; - if (!creation.Type.Inherits(argumentExceptionType)) + if (!creation.Type.Inherits(argumentExceptionType) || !MatchesConfiguredVisibility(owningSymbol, context)) { return; } if (creation.Arguments.Length == 0) { - if (HasMessageOrParameterNameConstructor(creation.Type)) + if (HasParameters(owningSymbol) && HasMessageOrParameterNameConstructor(creation.Type)) { // Call the {0} constructor that contains a message and/ or paramName parameter context.ReportDiagnostic(context.Operation.Syntax.CreateDiagnostic(RuleNoArguments, creation.Type.Name)); @@ -99,6 +101,7 @@ private static void AnalyzeObjectCreation( } else { + Diagnostic? diagnostic = null; foreach (IArgumentOperation argument in creation.Arguments) { if (argument.Parameter.Type.SpecialType != SpecialType.System_String) @@ -112,11 +115,29 @@ private static void AnalyzeObjectCreation( continue; } - CheckArgument(owningSymbol, creation, argument.Parameter, value, context); + diagnostic = CheckArgument(owningSymbol, creation, argument.Parameter, value, context); + + // RuleIncorrectMessage is the highest priority rule, no need to check other rules + if (diagnostic != null && diagnostic.Descriptor.Equals(RuleIncorrectMessage)) + { + break; + } + } + + if (diagnostic != null) + { + context.ReportDiagnostic(diagnostic); } } } - private static void CheckArgument( + + private static bool MatchesConfiguredVisibility(ISymbol owningSymbol, OperationAnalysisContext context) => + owningSymbol.MatchesConfiguredVisibility(context.Options, RuleIncorrectParameterName, context.Compilation, + context.CancellationToken, defaultRequiredVisibility: SymbolVisibilityGroup.All); + + private static bool HasParameters(ISymbol owningSymbol) => owningSymbol.GetParameters().Length > 0; + + private static Diagnostic? CheckArgument( ISymbol targetSymbol, IObjectCreationOperation creation, IParameterSymbol parameter, @@ -124,27 +145,23 @@ private static void CheckArgument( OperationAnalysisContext context) { bool matchesParameter = MatchesParameter(targetSymbol, creation, stringArgument); - DiagnosticDescriptor? rule = null; if (IsMessage(parameter) && matchesParameter) { - rule = RuleIncorrectMessage; + var dictBuilder = ImmutableDictionary.CreateBuilder(); + dictBuilder.Add(MessagePosition, parameter.Ordinal.ToString(CultureInfo.InvariantCulture)); + return context.Operation.CreateDiagnostic(RuleIncorrectMessage, dictBuilder.ToImmutable(), targetSymbol.Name, stringArgument, parameter.Name, creation.Type.Name); } - else if (IsParameterName(parameter) && !matchesParameter) + else if (HasParameters(targetSymbol) && IsParameterName(parameter) && !matchesParameter) { // Allow argument exceptions in accessors to use the associated property symbol name. - if (MatchesAssociatedSymbol(targetSymbol, stringArgument)) + if (!MatchesAssociatedSymbol(targetSymbol, stringArgument)) { - return; + return context.Operation.CreateDiagnostic(RuleIncorrectParameterName, targetSymbol.Name, stringArgument, parameter.Name, creation.Type.Name); } - - rule = RuleIncorrectParameterName; } - if (rule != null) - { - context.ReportDiagnostic(context.Operation.Syntax.CreateDiagnostic(rule, targetSymbol.Name, stringArgument, parameter.Name, creation.Type.Name)); - } + return null; } private static bool IsMessage(IParameterSymbol parameter) @@ -222,6 +239,20 @@ private static bool MatchesParameterCore(ISymbol? symbol, string stringArgumentV } } + if (symbol is IMethodSymbol method) + { + if (method.IsGenericMethod) + { + foreach (ITypeParameterSymbol parameter in method.TypeParameters) + { + if (parameter.Name == stringArgumentValue) + { + return true; + } + } + } + + } return false; } 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 285f2a67f3..5098ef5a5d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1047,11 +1047,21 @@ Inicializujte statická pole typu hodnot jako vložená + + Change to call the two argument constructor, pass null for the message. + Change to call the two argument constructor, pass null for the message. + + A call is made to the default (parameterless) constructor of an exception type that is or derives from ArgumentException, or an incorrect string argument is passed to a parameterized constructor of an exception type that is or derives from ArgumentException. Zavolal se výchozí konstruktor (bez parametrů) typu výjimky, který je třídou ArgumentException nebo je z ní odvozený, nebo se do jeho konstruktoru s parametry předal nesprávný argument řetězce. + + Swap the arguments order + Swap the arguments order + + Method {0} passes parameter name '{1}' as the {2} argument to a {3} constructor. Replace this argument with a descriptive message and pass the parameter name in the correct position. Metoda {0} předává název parametru {1} jako argument {2} konstruktoru {3}. Nahraďte tento argument popisnou zprávou a předejte název parametru na správné pozici. @@ -1064,7 +1074,7 @@ Call the {0} constructor that contains a message and/or paramName parameter. - Zavolejte konstruktor {0}, který obsahuje zprávu a/nebo parametr paramName. + Zavolejte konstruktor {0}, který obsahuje zprávu a/nebo parametr paramName. 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 03862772ec..11b364460f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1047,11 +1047,21 @@ Statische Felder für Werttyp inline initialisieren + + Change to call the two argument constructor, pass null for the message. + Change to call the two argument constructor, pass null for the message. + + A call is made to the default (parameterless) constructor of an exception type that is or derives from ArgumentException, or an incorrect string argument is passed to a parameterized constructor of an exception type that is or derives from ArgumentException. Ein Aufruf erfolgt an den (parameterlosen) Standardkonstruktur eines Ausnahmetyps, der einer ArgumentException entspricht oder von dieser ableitet, oder ein falsches Zeichenfolgenargument wurde an einen parametrisierten Konstruktor eines Ausnahmetyps übergeben, der einer ArgumentException entspricht oder von dieser ableitet. + + Swap the arguments order + Swap the arguments order + + Method {0} passes parameter name '{1}' as the {2} argument to a {3} constructor. Replace this argument with a descriptive message and pass the parameter name in the correct position. Die Methode "{0}" übergibt den Parameternamen "{1}" als {2}-Argument an einen {3}-Konstruktor. Ersetzen Sie dieses Argument durch eine aussagekräftige Nachricht, und übergeben Sie den Parameternamen an der richtigen Position. @@ -1064,7 +1074,7 @@ Call the {0} constructor that contains a message and/or paramName parameter. - Rufen Sie den Konstruktur "{0}" auf, der eine Nachricht und/oder einen paramName-Parameter enthält. + Rufen Sie den Konstruktur "{0}" auf, der eine Nachricht und/oder einen paramName-Parameter enthält. 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 1d5ff256ae..bb65260eea 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1047,11 +1047,21 @@ Inicializar campos estáticos de tipo de valor insertados + + Change to call the two argument constructor, pass null for the message. + Change to call the two argument constructor, pass null for the message. + + A call is made to the default (parameterless) constructor of an exception type that is or derives from ArgumentException, or an incorrect string argument is passed to a parameterized constructor of an exception type that is or derives from ArgumentException. Se realiza una llamada al constructor predeterminado (sin parámetros) de un tipo de excepción que es o se deriva de ArgumentException, o se pasa un argumento de cadena incorrecto a un constructor con parámetros de un tipo de excepción que es o se deriva de ArgumentException. + + Swap the arguments order + Swap the arguments order + + Method {0} passes parameter name '{1}' as the {2} argument to a {3} constructor. Replace this argument with a descriptive message and pass the parameter name in the correct position. El método {0} pasa un nombre de parámetro "{1}" como argumento {2} a un constructor {3}. Reemplace este argumento por un mensaje descriptivo y pase el nombre del parámetro en la posición correcta. @@ -1064,7 +1074,7 @@ Call the {0} constructor that contains a message and/or paramName parameter. - Llame al constructor {0} que contiene un mensaje o un parámetro paramName. + Llame al constructor {0} que contiene un mensaje o un parámetro paramName. 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 8cfc22654f..3e6e5d183f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1047,11 +1047,21 @@ Initialiser les champs static de type valeur inline + + Change to call the two argument constructor, pass null for the message. + Change to call the two argument constructor, pass null for the message. + + A call is made to the default (parameterless) constructor of an exception type that is or derives from ArgumentException, or an incorrect string argument is passed to a parameterized constructor of an exception type that is or derives from ArgumentException. Le constructeur par défaut (sans paramètre) d'un type d'exception qui correspond à ou dérive de ArgumentException est appelé. Ou bien, un argument de chaîne incorrect est passé à un constructeur paramétré d'un type d'exception qui correspond à ou dérive de ArgumentException. + + Swap the arguments order + Swap the arguments order + + Method {0} passes parameter name '{1}' as the {2} argument to a {3} constructor. Replace this argument with a descriptive message and pass the parameter name in the correct position. La méthode {0} passe le nom de paramètre '{1}' en tant qu'argument {2} à un constructeur {3}. Remplacez cet argument par un message descriptif, et passez le nom de paramètre à l'emplacement approprié. @@ -1064,7 +1074,7 @@ Call the {0} constructor that contains a message and/or paramName parameter. - Appelez le constructeur {0} qui contient un message et/ou un paramètre paramName. + Appelez le constructeur {0} qui contient un message et/ou un paramètre paramName. 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 e19b6dc2ae..654ab3c6e1 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1047,11 +1047,21 @@ Inizializzare inline i campi statici di tipo valore + + Change to call the two argument constructor, pass null for the message. + Change to call the two argument constructor, pass null for the message. + + A call is made to the default (parameterless) constructor of an exception type that is or derives from ArgumentException, or an incorrect string argument is passed to a parameterized constructor of an exception type that is or derives from ArgumentException. Viene effettuata una chiamata al costruttore predefinito (senza parametri) di un tipo di eccezione che corrisponde a o deriva da ArgumentException oppure viene passato un argomento stringa non corretto a un costruttore con parametri di un tipo di eccezione che corrisponde a o deriva da ArgumentException. + + Swap the arguments order + Swap the arguments order + + Method {0} passes parameter name '{1}' as the {2} argument to a {3} constructor. Replace this argument with a descriptive message and pass the parameter name in the correct position. Il metodo {0} passa il nome di parametro '{1}' come argomento di {2} a un costruttore {3}. Sostituire questo argomento con un messaggio descrittivo e passare il nome di parametro nella posizione corretta. @@ -1064,7 +1074,7 @@ Call the {0} constructor that contains a message and/or paramName parameter. - Chiamare il costruttore {0} che contiene un messaggio e/o il parametro paramName. + Chiamare il costruttore {0} che contiene un messaggio e/o il parametro paramName. 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 c14b939af1..e93039a576 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1047,11 +1047,21 @@ 値型の静的フィールドをインラインで初期化します + + Change to call the two argument constructor, pass null for the message. + Change to call the two argument constructor, pass null for the message. + + A call is made to the default (parameterless) constructor of an exception type that is or derives from ArgumentException, or an incorrect string argument is passed to a parameterized constructor of an exception type that is or derives from ArgumentException. ArgumentException またはそれから派生した例外の種類の既定の (パラメーターのない) コンストラクターに対して呼び出しが行われます。または、正しくない文字列引数が、ArgumentException またはそれから派生した例外の種類のパラメーター化されたコンストラクターに渡されます。 + + Swap the arguments order + Swap the arguments order + + Method {0} passes parameter name '{1}' as the {2} argument to a {3} constructor. Replace this argument with a descriptive message and pass the parameter name in the correct position. メソッド {0} は、パラメーター名 '{1}' を {2} 引数として {3} コンストラクターに渡します。この引数を説明メッセージに置き換え、正しい位置にパラメーター名を渡してください。 @@ -1064,7 +1074,7 @@ Call the {0} constructor that contains a message and/or paramName parameter. - メッセージおよび paramName パラメーターまたはそのいずれかを含む {0} コンストラクターを呼び出します。 + メッセージおよび paramName パラメーターまたはそのいずれかを含む {0} コンストラクターを呼び出します。 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 51838c0025..da037c87b3 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1047,11 +1047,21 @@ 값 형식 정적 필드 인라인을 초기화하세요. + + Change to call the two argument constructor, pass null for the message. + Change to call the two argument constructor, pass null for the message. + + A call is made to the default (parameterless) constructor of an exception type that is or derives from ArgumentException, or an incorrect string argument is passed to a parameterized constructor of an exception type that is or derives from ArgumentException. 예외 형식의 매개 변수가 없는 기본 생성자에 발생하는 호출은 ArgumentException이거나 ArgumentException에서 파생됩니다. 즉, 올바르지 않은 문자열 인수는 ArgumentException이거나 ArgumentException에서 파생된 예외 형식의 매개 변수가 있는 생성자로 전달됩니다. + + Swap the arguments order + Swap the arguments order + + Method {0} passes parameter name '{1}' as the {2} argument to a {3} constructor. Replace this argument with a descriptive message and pass the parameter name in the correct position. {0} 메서드가 매개 변수 이름 '{1}'을(를) {3} 생성자에 {2} 인수로 전달합니다. 이 인수를 자세한 설명이 있는 메시지로 바꾸고 매개 변수 이름을 올바른 위치에 전달하세요. @@ -1064,7 +1074,7 @@ Call the {0} constructor that contains a message and/or paramName parameter. - 메시지 및/또는 paramName 매개 변수를 포함하는 {0} 생성자를 호출하세요. + 메시지 및/또는 paramName 매개 변수를 포함하는 {0} 생성자를 호출하세요. 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 7d8b098e41..5b17627c68 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1048,11 +1048,21 @@ Zainicjuj pola statyczne typu wartości w deklaracji pól + + Change to call the two argument constructor, pass null for the message. + Change to call the two argument constructor, pass null for the message. + + A call is made to the default (parameterless) constructor of an exception type that is or derives from ArgumentException, or an incorrect string argument is passed to a parameterized constructor of an exception type that is or derives from ArgumentException. Wywołano domyślny (bezparametrowy) konstruktor typu wyjątku ArgumentException lub typu pochodzącego od niego albo przekazano niepoprawny argument ciągu do konstruktora parametryzowanego typu wyjątku ArgumentException lub typu pochodzącego od niego. + + Swap the arguments order + Swap the arguments order + + Method {0} passes parameter name '{1}' as the {2} argument to a {3} constructor. Replace this argument with a descriptive message and pass the parameter name in the correct position. Metoda {0} przekazuje nazwę parametru „{1}” jako argument {2} do konstruktora {3}. Zastąp ten argument opisowym komunikatem i przekaż nazwę parametru na poprawnej pozycji. @@ -1065,7 +1075,7 @@ Call the {0} constructor that contains a message and/or paramName parameter. - Wywołaj konstruktor {0}, który zawiera komunikat i/lub parametr paramName. + Wywołaj konstruktor {0}, który zawiera komunikat i/lub parametr paramName. 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 9ef96e46f2..4ec6a51da3 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 @@ -1047,11 +1047,21 @@ Inicializar campos estáticos de tipo de valor embutido + + Change to call the two argument constructor, pass null for the message. + Change to call the two argument constructor, pass null for the message. + + A call is made to the default (parameterless) constructor of an exception type that is or derives from ArgumentException, or an incorrect string argument is passed to a parameterized constructor of an exception type that is or derives from ArgumentException. É feita uma chamada para o construtor padrão (sem parâmetro) de um tipo de exceção que é ArgumentException ou derivado dele, ou então um argumento de cadeia de caracteres incorreto é passado para um construtor parametrizado de um tipo de exceção que é ArgumentException ou derivado dele. + + Swap the arguments order + Swap the arguments order + + Method {0} passes parameter name '{1}' as the {2} argument to a {3} constructor. Replace this argument with a descriptive message and pass the parameter name in the correct position. O método {0} passa o nome de parâmetro '{1}' como o argumento {2} para um construtor {3}. Substitua este argumento por uma mensagem descritiva e passe o nome do parâmetro na posição correta. @@ -1064,7 +1074,7 @@ Call the {0} constructor that contains a message and/or paramName parameter. - Chame o construtor {0} que contém uma mensagem e/ou um parâmetro paramName. + Chame o construtor {0} que contém uma mensagem e/ou um parâmetro paramName. 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 3a22a42788..8caa492a1e 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1047,11 +1047,21 @@ Используйте встроенную инициализацию статических полей типов значений + + Change to call the two argument constructor, pass null for the message. + Change to call the two argument constructor, pass null for the message. + + A call is made to the default (parameterless) constructor of an exception type that is or derives from ArgumentException, or an incorrect string argument is passed to a parameterized constructor of an exception type that is or derives from ArgumentException. Выполняется вызов конструктора по умолчанию (без параметров) для типа исключения, который является ArgumentException или производным от него, либо в параметризованный конструктор для типа исключения, который является ArgumentException или производным от него, передается неправильный строковый аргумент. + + Swap the arguments order + Swap the arguments order + + Method {0} passes parameter name '{1}' as the {2} argument to a {3} constructor. Replace this argument with a descriptive message and pass the parameter name in the correct position. Метод {0} передает имя параметра "{1}" в качестве аргумента {2} в конструктор {3}. Замените этот аргумент описательным сообщением и передайте имя этого параметра в правильном положении. @@ -1064,7 +1074,7 @@ Call the {0} constructor that contains a message and/or paramName parameter. - Вызывайте конструктор {0}, который содержит сообщение и (или) параметр paramName. + Вызывайте конструктор {0}, который содержит сообщение и (или) параметр paramName. 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 baf5b1844a..b55a11dcb1 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1047,11 +1047,21 @@ Değer türünde statik alanları satır içi olarak başlatın + + Change to call the two argument constructor, pass null for the message. + Change to call the two argument constructor, pass null for the message. + + A call is made to the default (parameterless) constructor of an exception type that is or derives from ArgumentException, or an incorrect string argument is passed to a parameterized constructor of an exception type that is or derives from ArgumentException. ArgumentException olan veya bundan türetilen bir özel durum türünün varsayılan (parametresiz) oluşturucusuna bir çağrı yapıldı veya ArgumentException olan veya bundan türetilen bir özel durum türünün parametreli oluşturucusuna yanlış bir dize bağımsız değişkeni geçirildi. + + Swap the arguments order + Swap the arguments order + + Method {0} passes parameter name '{1}' as the {2} argument to a {3} constructor. Replace this argument with a descriptive message and pass the parameter name in the correct position. {0} yöntemi, bir {3} oluşturucusuna {2} bağımsız değişkeni olarak '{1}' parametre adını geçiriyor. Bu bağımsız değişkeni açıklayıcı bir iletiyle değiştirin ve parametre adını doğru konumda geçirin. @@ -1064,7 +1074,7 @@ Call the {0} constructor that contains a message and/or paramName parameter. - Bir iletiyi ve/veya paramName parametresini içeren {0} oluşturucusunu çağırın. + Bir iletiyi ve/veya paramName parametresini içeren {0} oluşturucusunu çağırın. 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 d664d53384..114f9e20dd 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 @@ -1047,11 +1047,21 @@ 以内联方式初始化值类型的静态字段 + + Change to call the two argument constructor, pass null for the message. + Change to call the two argument constructor, pass null for the message. + + A call is made to the default (parameterless) constructor of an exception type that is or derives from ArgumentException, or an incorrect string argument is passed to a parameterized constructor of an exception type that is or derives from ArgumentException. 调用了 ArgumentException 异常类型或其派生异常类型的默认(无参数)构造函数,或将不正确的字符串参数传递给 ArgumentException. 异常类型或其派生异常类型的参数化构造函数。 + + Swap the arguments order + Swap the arguments order + + Method {0} passes parameter name '{1}' as the {2} argument to a {3} constructor. Replace this argument with a descriptive message and pass the parameter name in the correct position. 方法 {0} 将参数名“{1}”作为变量 {2} 传递给构造函数 {3}。请将此参数替换为一则说明性消息并在正确的位置传递参数名。 @@ -1064,7 +1074,7 @@ Call the {0} constructor that contains a message and/or paramName parameter. - 调用 {0} 构造函数,该函数包含 message 和/或 paramName 参数。 + 调用 {0} 构造函数,该函数包含 message 和/或 paramName 参数。 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 4ddef294c4..0f3a3cb92f 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 @@ -1047,11 +1047,21 @@ 初始化實值型別靜態欄位內嵌 + + Change to call the two argument constructor, pass null for the message. + Change to call the two argument constructor, pass null for the message. + + A call is made to the default (parameterless) constructor of an exception type that is or derives from ArgumentException, or an incorrect string argument is passed to a parameterized constructor of an exception type that is or derives from ArgumentException. 已呼叫例外狀況類型為 ArgumentException 或由其衍生的預設 (無參數) 建構函式; 或將不正確的字串引數傳遞到例外狀況類型為 ArgumentException 或由其衍生的參數化建構函式。 + + Swap the arguments order + Swap the arguments order + + Method {0} passes parameter name '{1}' as the {2} argument to a {3} constructor. Replace this argument with a descriptive message and pass the parameter name in the correct position. 方法 {0} 會將參數名稱 '{1}' 以 {2} 引數傳遞到 {3} 建構函式。請以描述性訊息取代此引數,並將該參數名稱傳遞到正確的位置。 @@ -1064,7 +1074,7 @@ Call the {0} constructor that contains a message and/or paramName parameter. - 請呼叫包含 message 及 (或) paramName 參數的 {0} 建構函式。 + 請呼叫包含 message 及 (或) paramName 參數的 {0} 建構函式。 diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/InstantiateArgumentExceptionsCorrectlyTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/InstantiateArgumentExceptionsCorrectlyTests.cs index f9cf7f5768..919263e8f3 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/InstantiateArgumentExceptionsCorrectlyTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/InstantiateArgumentExceptionsCorrectlyTests.cs @@ -6,10 +6,10 @@ using Xunit; using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< Microsoft.NetCore.Analyzers.Runtime.InstantiateArgumentExceptionsCorrectlyAnalyzer, - Microsoft.NetCore.CSharp.Analyzers.Runtime.CSharpInstantiateArgumentExceptionsCorrectlyFixer>; + Microsoft.NetCore.Analyzers.Runtime.InstantiateArgumentExceptionsCorrectlyFixer>; using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< Microsoft.NetCore.Analyzers.Runtime.InstantiateArgumentExceptionsCorrectlyAnalyzer, - Microsoft.NetCore.VisualBasic.Analyzers.Runtime.BasicInstantiateArgumentExceptionsCorrectlyFixer>; + Microsoft.NetCore.Analyzers.Runtime.InstantiateArgumentExceptionsCorrectlyFixer>; namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests { @@ -34,7 +34,7 @@ Public Sub Test(first As String) Throw New System.ArgumentException() End Sub End Class", - GetBasicNoArgumentsExpectedResult(4, 31, "ArgumentException")); + GetBasicNoArgumentsExpectedResult(4, 31, "ArgumentException")); } [Fact] @@ -106,9 +106,9 @@ End Sub } [Fact] - public async Task ArgumentException_ParameterNameAsMessage_Warns() + public async Task ArgumentException_ParameterNameAsMessage_WarnsAndCodeFixesWithNameOf() { - await VerifyCS.VerifyAnalyzerAsync(@" + await VerifyCS.VerifyCodeFixAsync(@" public class Class { public void Test(string first) @@ -116,21 +116,33 @@ public void Test(string first) throw new System.ArgumentException(""first""); } }", - GetCSharpIncorrectMessageExpectedResult(6, 31, "Test", "first", "message", "ArgumentException")); + GetCSharpIncorrectMessageExpectedResult(6, 31, "Test", "first", "message", "ArgumentException"), @" + public class Class + { + public void Test(string first) + { + throw new System.ArgumentException(null, nameof(first)); + } + }"); - await VerifyVB.VerifyAnalyzerAsync(@" - Public Class [MyClass] - Public Sub Test(first As String) - Throw New System.ArgumentException(""first"") - End Sub - End Class", - GetBasicIncorrectMessageExpectedResult(4, 31, "Test", "first", "message", "ArgumentException")); + await VerifyVB.VerifyCodeFixAsync(@" +Public Class [MyClass] + Public Sub Test(first As String) + Throw New System.ArgumentException(""first"") + End Sub +End Class", + GetBasicIncorrectMessageExpectedResult(4, 15, "Test", "first", "message", "ArgumentException"), @" +Public Class [MyClass] + Public Sub Test(first As String) + Throw New System.ArgumentException(Nothing, NameOf(first)) + End Sub +End Class"); } [Fact] - public async Task ArgumentException_ReversedArguments_Warns() + public async Task ArgumentException_ReversedArguments_WarnsAndCodeFixesWithNameOf() { - await VerifyCS.VerifyAnalyzerAsync(@" + await VerifyCS.VerifyCodeFixAsync(@" public class Class { public void Test(string first) @@ -138,17 +150,104 @@ public void Test(string first) throw new System.ArgumentException(""first"", ""first is incorrect""); } }", - GetCSharpIncorrectMessageExpectedResult(6, 31, "Test", "first", "message", "ArgumentException"), - GetCSharpIncorrectParameterNameExpectedResult(6, 31, "Test", "first is incorrect", "paramName", "ArgumentException")); + GetCSharpIncorrectMessageExpectedResult(6, 31, "Test", "first", "message", "ArgumentException"), @" + public class Class + { + public void Test(string first) + { + throw new System.ArgumentException(""first is incorrect"", nameof(first)); + } + }"); - await VerifyVB.VerifyAnalyzerAsync(@" - Public Class [MyClass] - Public Sub Test(first As String) - Throw New System.ArgumentException(""first"", ""first is incorrect"") - End Sub - End Class", - GetBasicIncorrectMessageExpectedResult(4, 31, "Test", "first", "message", "ArgumentException"), - GetBasicIncorrectParameterNameExpectedResult(4, 31, "Test", "first is incorrect", "paramName", "ArgumentException")); + await VerifyVB.VerifyCodeFixAsync(@" +Public Class [MyClass] + Public Sub Test(first As String) + Throw New System.ArgumentException(""first"", ""first is incorrect"") + End Sub +End Class", + GetBasicIncorrectMessageExpectedResult(4, 15, "Test", "first", "message", "ArgumentException"), @" +Public Class [MyClass] + Public Sub Test(first As String) + Throw New System.ArgumentException(""first is incorrect"", NameOf(first)) + End Sub +End Class"); + } + + [Fact] + public async Task ArgumentException_ParameterWithNameofAsMessage_WarnsAndCodeFixes() + { + await VerifyCS.VerifyCodeFixAsync(@" + public class Class + { + public void Test(string first) + { + throw new System.ArgumentException(nameof(first)); + } + }", + GetCSharpIncorrectMessageExpectedResult(6, 31, "Test", "first", "message", "ArgumentException") + , @" + public class Class + { + public void Test(string first) + { + throw new System.ArgumentException(null, nameof(first)); + } + }"); + } + + [Fact] + public async Task ArgumentException_ReversedArgumentsWithNameof_WarnsAndCodeFixes() + { + await VerifyCS.VerifyCodeFixAsync(@" + public class Class + { + public void Test(string first) + { + throw new System.ArgumentException(nameof(first), ""first is incorrect""); + } + }", + GetCSharpIncorrectMessageExpectedResult(6, 31, "Test", "first", "message", "ArgumentException"), @" + public class Class + { + public void Test(string first) + { + throw new System.ArgumentException(""first is incorrect"", nameof(first)); + } + }"); + } + + [Fact] + public async Task ArgumentException_Reversed3Arguments_WarnsAndCodeFixes() + { + await VerifyCS.VerifyCodeFixAsync(@" + public class Class + { + public void Test(string first) + { + throw new System.ArgumentException(""first"", ""first is incorrect"", null); + } + }", + GetCSharpIncorrectMessageExpectedResult(6, 31, "Test", "first", "message", "ArgumentException"), @" + public class Class + { + public void Test(string first) + { + throw new System.ArgumentException(""first is incorrect"", nameof(first), null); + } + }"); + + await VerifyVB.VerifyCodeFixAsync(@" +Public Class [MyClass] + Public Sub Test(first As String) + Throw New System.ArgumentException(""first"", ""first is incorrect"", Nothing) + End Sub +End Class", + GetBasicIncorrectMessageExpectedResult(4, 15, "Test", "first", "message", "ArgumentException"), @" +Public Class [MyClass] + Public Sub Test(first As String) + Throw New System.ArgumentException(""first is incorrect"", NameOf(first), Nothing) + End Sub +End Class"); } [Fact] @@ -170,7 +269,7 @@ Public Sub Test(first As String) Throw New System.ArgumentNullException() End Sub End Class", - GetBasicNoArgumentsExpectedResult(4, 31, "ArgumentNullException")); + GetBasicNoArgumentsExpectedResult(4, 31, "ArgumentNullException")); } [Fact] @@ -188,9 +287,9 @@ public void Test(string first) } [Fact] - public async Task ArgumentNullException_ReversedArguments_Warns() + public async Task ArgumentNullException_ReversedArguments_WarnsAndCodeFixes() { - await VerifyCS.VerifyAnalyzerAsync(@" + await VerifyCS.VerifyCodeFixAsync(@" public class Class { public void Test(string first) @@ -198,17 +297,27 @@ public void Test(string first) throw new System.ArgumentNullException(""first is null"", ""first""); } }", - GetCSharpIncorrectParameterNameExpectedResult(6, 31, "Test", "first is null", "paramName", "ArgumentNullException"), - GetCSharpIncorrectMessageExpectedResult(6, 31, "Test", "first", "message", "ArgumentNullException")); + GetCSharpIncorrectMessageExpectedResult(6, 31, "Test", "first", "message", "ArgumentNullException"), @" + public class Class + { + public void Test(string first) + { + throw new System.ArgumentNullException(nameof(first), ""first is null""); + } + }"); - await VerifyVB.VerifyAnalyzerAsync(@" - Public Class [MyClass] - Public Sub Test(first As String) - Throw New System.ArgumentNullException(""first is null"", ""first"") - End Sub - End Class", - GetBasicIncorrectParameterNameExpectedResult(4, 31, "Test", "first is null", "paramName", "ArgumentNullException"), - GetBasicIncorrectMessageExpectedResult(4, 31, "Test", "first", "message", "ArgumentNullException")); + await VerifyVB.VerifyCodeFixAsync(@" +Public Class [MyClass] + Public Sub Test(first As String) + Throw New System.ArgumentNullException(""first is null"", ""first"") + End Sub +End Class", + GetBasicIncorrectMessageExpectedResult(4, 15, "Test", "first", "message", "ArgumentNullException"), @" +Public Class [MyClass] + Public Sub Test(first As String) + Throw New System.ArgumentNullException(NameOf(first), ""first is null"") + End Sub +End Class"); } [Fact] @@ -256,9 +365,9 @@ End Sub } [Fact] - public async Task ArgumentOutOfRangeException_ReversedArguments_Warns() + public async Task ArgumentOutOfRangeException_ReversedArguments_WarnsAndCodeFixes() { - await VerifyCS.VerifyAnalyzerAsync(@" + await VerifyCS.VerifyCodeFixAsync(@" public class Class { public void Test(string first) @@ -266,17 +375,65 @@ public void Test(string first) throw new System.ArgumentOutOfRangeException(""first is out of range"", ""first""); } }", - GetCSharpIncorrectParameterNameExpectedResult(6, 31, "Test", "first is out of range", "paramName", "ArgumentOutOfRangeException"), - GetCSharpIncorrectMessageExpectedResult(6, 31, "Test", "first", "message", "ArgumentOutOfRangeException")); + GetCSharpIncorrectMessageExpectedResult(6, 31, "Test", "first", "message", "ArgumentOutOfRangeException"), @" + public class Class + { + public void Test(string first) + { + throw new System.ArgumentOutOfRangeException(nameof(first), ""first is out of range""); + } + }"); + + await VerifyVB.VerifyCodeFixAsync(@" +Public Class [MyClass] + Public Sub Test(first As String) + Throw New System.ArgumentOutOfRangeException(""first is out of range"", ""first"") + End Sub +End Class", + GetBasicIncorrectMessageExpectedResult(4, 15, "Test", "first", "message", "ArgumentOutOfRangeException"), @" +Public Class [MyClass] + Public Sub Test(first As String) + Throw New System.ArgumentOutOfRangeException(NameOf(first), ""first is out of range"") + End Sub +End Class"); + } + + [Fact] + public async Task ArgumentOutOfRangeException_Reversed3Arguments_WarnsAndCodeFixes() + { + await VerifyCS.VerifyCodeFixAsync(@" + public class Class + { + public void Test(string first) + { + var val = new object(); + throw new System.ArgumentOutOfRangeException(""first is out of range"", val, ""first""); + } + }", + GetCSharpIncorrectMessageExpectedResult(7, 31, "Test", "first", "message", "ArgumentOutOfRangeException"), @" + public class Class + { + public void Test(string first) + { + var val = new object(); + throw new System.ArgumentOutOfRangeException(nameof(first), val, ""first is out of range""); + } + }"); - await VerifyVB.VerifyAnalyzerAsync(@" - Public Class [MyClass] - Public Sub Test(first As String) - Throw New System.ArgumentOutOfRangeException(""first is out of range"", ""first"") - End Sub - End Class", - GetBasicIncorrectParameterNameExpectedResult(4, 31, "Test", "first is out of range", "paramName", "ArgumentOutOfRangeException"), - GetBasicIncorrectMessageExpectedResult(4, 31, "Test", "first", "message", "ArgumentOutOfRangeException")); + await VerifyVB.VerifyCodeFixAsync(@" +Public Class [MyClass] + Public Sub Test(first As String) + Dim val = New Object() + Throw New System.ArgumentOutOfRangeException(""first is out of range"", val, ""first"") + End Sub +End Class", + GetBasicIncorrectMessageExpectedResult(5, 15, "Test", "first", "message", "ArgumentOutOfRangeException"), @" +Public Class [MyClass] + Public Sub Test(first As String) + Dim val = New Object() + Throw New System.ArgumentOutOfRangeException(NameOf(first), val, ""first is out of range"") + End Sub +End Class"); } [Fact] @@ -324,9 +481,9 @@ End Sub } [Fact] - public async Task DuplicateWaitObjectException_ReversedArguments_Warns() + public async Task DuplicateWaitObjectException_ReversedArguments_WarnsAndCodeFixes() { - await VerifyCS.VerifyAnalyzerAsync(@" + await VerifyCS.VerifyCodeFixAsync(@" public class Class { public void Test(string first) @@ -334,19 +491,87 @@ public void Test(string first) throw new System.DuplicateWaitObjectException(""first is duplicate"", ""first""); } }", - GetCSharpIncorrectParameterNameExpectedResult(6, 31, "Test", "first is duplicate", "parameterName", "DuplicateWaitObjectException"), - GetCSharpIncorrectMessageExpectedResult(6, 31, "Test", "first", "message", "DuplicateWaitObjectException")); + GetCSharpIncorrectMessageExpectedResult(6, 31, "Test", "first", "message", "DuplicateWaitObjectException"), @" + public class Class + { + public void Test(string first) + { + throw new System.DuplicateWaitObjectException(nameof(first), ""first is duplicate""); + } + }"); - await VerifyVB.VerifyAnalyzerAsync(@" - Public Class [MyClass] - Public Sub Test(first As String) - Throw New System.DuplicateWaitObjectException(""first is duplicate"", ""first"") - End Sub - End Class", - GetBasicIncorrectParameterNameExpectedResult(4, 31, "Test", "first is duplicate", "parameterName", "DuplicateWaitObjectException"), - GetBasicIncorrectMessageExpectedResult(4, 31, "Test", "first", "message", "DuplicateWaitObjectException")); + await VerifyVB.VerifyCodeFixAsync(@" +Public Class [MyClass] + Public Sub Test(first As String) + Throw New System.DuplicateWaitObjectException(""first is duplicate"", ""first"") + End Sub +End Class", + GetBasicIncorrectMessageExpectedResult(4, 15, "Test", "first", "message", "DuplicateWaitObjectException"), @" +Public Class [MyClass] + Public Sub Test(first As String) + Throw New System.DuplicateWaitObjectException(NameOf(first), ""first is duplicate"") + End Sub +End Class"); + } + + [Fact] + public async Task ArgumentNullException_ParentHasNoParameter_DoesNotWarn() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class Class + { + public void Test() + { + throw new System.ArgumentNullException(""Invalid argument""); + } + }"); + } + + [Fact] + public async Task ArgumentException_ParentHasNoParameter_DoesNotWarn() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class Class + { + public void Test() + { + throw new System.ArgumentException(""Invalid argument"", ""test""); + } + }"); } + [Fact] + public async Task ArgumentException_VariableUsed_DoesNotWarn() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class Class + { + public void Test(string paramName, string message) + { + throw new System.ArgumentException(paramName, message); + } + }"); + } + + [Fact] + public async Task ArgumentException_NoArguments_ParentMethod_HasNoParameter_DoesNotWarn() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class Class + { + public void Test() + { + throw new System.ArgumentException(); + } + }"); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class [MyClass] + Public Sub Test() + Throw New System.ArgumentException() + End Sub + End Class"); + } [Fact] public async Task ArgumentException_CorrectMessage_DoesNotWarn() @@ -368,6 +593,153 @@ End Sub End Class"); } + [Fact] + public async Task ArgumentException_GenericParameterName_DoesNotWarn() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class Class + { + public void Test(TEnum first) + { + throw new System.ArgumentException(""first is incorrect"", nameof(TEnum)); + } + }"); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class [MyClass] + Public Sub Test(Of TEnum)(ByVal first As TEnum) + Throw New System.ArgumentException(""first is incorrect"", NameOf(TEnum)) + End Sub + End Class"); + } + + [Fact] + public async Task ArgumentException_GenericParameterName_WrongPosition_WarnsAndCodeFixes() + { + await VerifyCS.VerifyCodeFixAsync(@" + public class Class + { + public void Test(TEnum first) + { + throw new System.ArgumentException(""TEnum""); + } + }", + GetCSharpIncorrectMessageExpectedResult(6, 31, "Test", "TEnum", "message", "ArgumentException"), @" + public class Class + { + public void Test(TEnum first) + { + throw new System.ArgumentException(null, nameof(TEnum)); + } + }"); + + await VerifyVB.VerifyCodeFixAsync(@" +Public Class [MyClass] + Public Sub Test(Of TEnum)(ByVal first As TEnum) + Throw New System.ArgumentException(""TEnum"") + End Sub +End Class", + GetBasicIncorrectMessageExpectedResult(4, 15, "Test", "TEnum", "message", "ArgumentException"), @" +Public Class [MyClass] + Public Sub Test(Of TEnum)(ByVal first As TEnum) + Throw New System.ArgumentException(Nothing, NameOf(TEnum)) + End Sub +End Class"); + } + + [Theory] + [InlineData("public", "dotnet_code_quality.api_surface = private", false)] + [InlineData("private", "dotnet_code_quality.api_surface = internal, public", false)] + [InlineData("public", "dotnet_code_quality.CA2208.api_surface = private, public", true)] + [InlineData("public", "dotnet_code_quality.CA2208.api_surface = internal, private", false)] + [InlineData("public", "dotnet_code_quality.CA2208.api_surface = Friend, Private", false)] + [InlineData("public", @"dotnet_code_quality.api_surface = all + dotnet_code_quality.CA2208.api_surface = private", false)] + [InlineData("public", "dotnet_code_quality.api_surface = public", true)] + [InlineData("public", "dotnet_code_quality.api_surface = internal, public", true)] + [InlineData("public", "dotnet_code_quality.CA2208.api_surface = public", true)] + [InlineData("public", "dotnet_code_quality.CA2208.api_surface = all", true)] + [InlineData("public", "dotnet_code_quality.CA2208.api_surface = public, private", true)] + [InlineData("public", @"dotnet_code_quality.api_surface = internal + dotnet_code_quality.CA2208.api_surface = public", true)] + [InlineData("public", "", true)] + [InlineData("protected", "", true)] + [InlineData("private", "", true)] + [InlineData("protected", "dotnet_code_quality.CA2208.api_surface = public", true)] + public async Task EditorConfigConfiguration_ApiSurfaceOption_Test(string accessibility, string editorConfigText, bool expectDiagnostic) + { + var exception = expectDiagnostic ? @"[|new System.ArgumentNullException(""first is null"")|]" : @"new System.ArgumentNullException(""first is null"")"; + + await new VerifyCS.Test + { + TestState = + { + Sources = + { + $@" +public class C +{{ + {accessibility} void Test(string first) + {{ + throw {exception}; + }} +}}" + }, + AdditionalFiles = { (".editorconfig", editorConfigText) } + }, + MarkupOptions = MarkupOptions.UseFirstDescriptor + }.RunAsync(); + + exception = expectDiagnostic ? @"[|New System.ArgumentNullException(""first is null"")|]" : @"New System.ArgumentNullException(""first is null"")"; + + await new VerifyVB.Test + { + TestState = + { + Sources = + { + $@" + Public Class C + {accessibility} Sub Test(first As String) + Throw {exception} + End Sub + End Class" + }, + AdditionalFiles = { (".editorconfig", editorConfigText) } + }, + MarkupOptions = MarkupOptions.UseFirstDescriptor + }.RunAsync(); + } + + [Fact] + public async Task EditorConfigConfiguredPublic_PrivateMethods_TriggeringOtherRules_DoesNotWarn() + { + await new VerifyCS.Test + { + TestState = + { + Sources = + { + $@" + public class C + {{ + private void Test(string first) + {{ + throw new System.ArgumentNullException(); + }} + + private void TestFlipped(string first) + {{ + throw new System.ArgumentException(nameof(first), ""message""); + }} + }}" + }, + AdditionalFiles = { (".editorconfig", "dotnet_code_quality.CA2208.api_surface = public") } + }, + MarkupOptions = MarkupOptions.UseFirstDescriptor + }.RunAsync(); + } + [Fact] public async Task ArgumentException_CorrectMessageAndParameterName_DoesNotWarn() { @@ -408,6 +780,19 @@ End Sub End Class"); } + [Fact] + public async Task ArgumentNullException_VariableUsed_DoesNotWarn() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class Class + { + string str = ""Hi there""; + public void Test(string first) + { + throw new System.ArgumentNullException(str); + } + }"); + } [Fact] diff --git a/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Runtime/BasicInstantiateArgumentExceptionsCorrectly.Fixer.vb b/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Runtime/BasicInstantiateArgumentExceptionsCorrectly.Fixer.vb deleted file mode 100644 index e86b1eb327..0000000000 --- a/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Runtime/BasicInstantiateArgumentExceptionsCorrectly.Fixer.vb +++ /dev/null @@ -1,17 +0,0 @@ -' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -Imports System.Composition -Imports Microsoft.NetCore.Analyzers.Runtime -Imports Microsoft.CodeAnalysis -Imports Microsoft.CodeAnalysis.CodeFixes - -Namespace Microsoft.NetCore.VisualBasic.Analyzers.Runtime - ''' - ''' CA2208: Instantiate argument exceptions correctly - ''' - - Public NotInheritable Class BasicInstantiateArgumentExceptionsCorrectlyFixer - Inherits InstantiateArgumentExceptionsCorrectlyFixer - - End Class -End Namespace