diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/MicrosoftMaintainabilityAnalyzersResources.resx b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/MicrosoftMaintainabilityAnalyzersResources.resx index ebbf3be1c6..8ea78c6468 100644 --- a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/MicrosoftMaintainabilityAnalyzersResources.resx +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/MicrosoftMaintainabilityAnalyzersResources.resx @@ -192,4 +192,13 @@ {0} calls {1} but does not use the value the method returns. Because {1} is marked as a Pure method, it cannot have side effects. Use the result in a conditional statement, assign the result to a variable, or pass it as an argument to another method. + + Using nameof helps keep your code valid when refactoring. + + + Use nameof in place of string literal '{0}' + + + Use nameof to express symbol names + \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/UseNameofInPlaceOfString.Fixer.cs b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/UseNameofInPlaceOfString.Fixer.cs new file mode 100644 index 0000000000..a404df0eca --- /dev/null +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/UseNameofInPlaceOfString.Fixer.cs @@ -0,0 +1,65 @@ +// 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.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; + +namespace Microsoft.CodeQuality.Analyzers.Maintainability +{ + /// + /// CA1507 Use nameof to express symbol names + /// + [ExportCodeFixProvider(LanguageNames.VisualBasic, LanguageNames.CSharp), Shared] + public class UseNameOfInPlaceOfStringFixer : CodeFixProvider + { + public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(UseNameofInPlaceOfStringAnalyzer.RuleId); + + public sealed override FixAllProvider GetFixAllProvider() + { + // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers' + return WellKnownFixAllProviders.BatchFixer; + } + + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + var diagnostics = context.Diagnostics; + var diagnosticSpan = context.Span; + // getInnerModeNodeForTie = true so we are replacing the string literal node and not the whole argument node + var nodeToReplace = root.FindNode(diagnosticSpan, getInnermostNodeForTie: true); + + Debug.Assert(nodeToReplace != null); + var stringText = nodeToReplace.FindToken(diagnosticSpan.Start).ValueText; + context.RegisterCodeFix(CodeAction.Create( + MicrosoftMaintainabilityAnalyzersResources.UseNameOfInPlaceOfStringTitle, + c => ReplaceWithNameOf(context.Document, nodeToReplace, stringText, c), + equivalenceKey: nameof(UseNameOfInPlaceOfStringFixer)), + context.Diagnostics); + } + + private async Task ReplaceWithNameOf(Document document, SyntaxNode nodeToReplace, + string stringText, CancellationToken cancellationToken) + { + var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var generator = editor.Generator; + + var trailingTrivia = nodeToReplace.GetTrailingTrivia(); + var leadingTrivia = nodeToReplace.GetLeadingTrivia(); + var nameOfExpression = generator.NameOfExpression(generator.IdentifierName(stringText)) + .WithTrailingTrivia(trailingTrivia) + .WithLeadingTrivia(leadingTrivia); + + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var newRoot = root.ReplaceNode(nodeToReplace, nameOfExpression); + + return document.WithSyntaxRoot(newRoot); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/UseNameofInPlaceOfString.cs b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/UseNameofInPlaceOfString.cs new file mode 100644 index 0000000000..7bf536062b --- /dev/null +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/UseNameofInPlaceOfString.cs @@ -0,0 +1,151 @@ +// 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.Generic; +using System.Collections.Immutable; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeQuality.Analyzers.Maintainability +{ + /// + /// CA1507 Use nameof to express symbol names + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class UseNameofInPlaceOfStringAnalyzer : DiagnosticAnalyzer + { + internal const string RuleId = "CA1507"; + private const string ParamName = "paramName"; + private const string PropertyName = "propertyName"; + internal const string StringText = "StringText"; + + private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftMaintainabilityAnalyzersResources.UseNameOfInPlaceOfStringTitle), MicrosoftMaintainabilityAnalyzersResources.ResourceManager, typeof(MicrosoftMaintainabilityAnalyzersResources)); + private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(MicrosoftMaintainabilityAnalyzersResources.UseNameOfInPlaceOfStringMessage), MicrosoftMaintainabilityAnalyzersResources.ResourceManager, typeof(MicrosoftMaintainabilityAnalyzersResources)); + private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(MicrosoftMaintainabilityAnalyzersResources.UseNameOfInPlaceOfStringDescription), MicrosoftMaintainabilityAnalyzersResources.ResourceManager, typeof(MicrosoftMaintainabilityAnalyzersResources)); + + internal static DiagnosticDescriptor RuleWithSuggestion = new DiagnosticDescriptor(RuleId, + s_localizableTitle, + s_localizableMessage, + DiagnosticCategory.Maintainability, + DiagnosticHelpers.DefaultDiagnosticSeverity, + isEnabledByDefault: true, + description: s_localizableDescription, + helpLinkUri: "https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeQuality.Analyzers/Microsoft.CodeQuality.Analyzers.md#maintainability", + customTags: WellKnownDiagnosticTags.Telemetry); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(RuleWithSuggestion); + + public override void Initialize(AnalysisContext analysisContext) + { + analysisContext.EnableConcurrentExecution(); + analysisContext.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + analysisContext.RegisterOperationAction(AnalyzeArgument, OperationKind.Argument); + } + + private void AnalyzeArgument(OperationAnalysisContext context) + { + var argument = (IArgumentOperation)context.Operation; + if ((argument.Value.Kind != OperationKind.Literal + || argument.Value.Type.SpecialType != SpecialType.System_String)) + { + return; + } + + if (argument.Parameter == null) + { + return; + } + + var stringText = (string)argument.Value.ConstantValue.Value; + + var matchingParameter = argument.Parameter; + + switch (matchingParameter.Name) + { + case ParamName: + var parametersInScope = GetParametersInScope(context); + if (HasAMatchInScope(stringText, parametersInScope)) + { + context.ReportDiagnostic(Diagnostic.Create( + RuleWithSuggestion, argument.Value.Syntax.GetLocation(), stringText )); + } + return; + case PropertyName: + var propertiesInScope = GetPropertiesInScope(context); + if (HasAMatchInScope(stringText, propertiesInScope)) + { + context.ReportDiagnostic(Diagnostic.Create( + RuleWithSuggestion, argument.Value.Syntax.GetLocation(), stringText)); + } + return; + default: + return; + } + } + + private IEnumerable GetPropertiesInScope(OperationAnalysisContext context) + { + var containingType = context.ContainingSymbol.ContainingType; + // look for all of the properties in the containing type and return the property names + if (containingType != null) + { + foreach (var property in containingType.GetMembers().OfType()) + { + yield return property.Name; + } + } + } + + internal IEnumerable GetParametersInScope(OperationAnalysisContext context) + { + // get the parameters for the containing method + foreach (var parameter in context.ContainingSymbol.GetParameters()) + { + yield return parameter.Name; + } + + // and loop through the ancestors to find parameters of anonymous functions and local functions + var parentOperation = context.Operation.Parent; + while (parentOperation != null) + { + if (parentOperation.Kind == OperationKind.AnonymousFunction) + { + var lambdaSymbol = ((IAnonymousFunctionOperation)parentOperation).Symbol; + if (lambdaSymbol != null) + { + foreach (var lambdaParameter in lambdaSymbol.Parameters) + { + yield return lambdaParameter.Name; + } + } + } + else if (parentOperation.Kind == OperationKind.LocalFunction) + { + var localFunction = ((ILocalFunctionOperation)parentOperation).Symbol; + foreach (var localFunctionParameter in localFunction.Parameters) + { + yield return localFunctionParameter.Name; + } + } + + parentOperation = parentOperation.Parent; + } + } + + private static bool HasAMatchInScope(string stringText, IEnumerable searchCollection) + { + foreach (var name in searchCollection) + { + if (stringText == name) + { + return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.cs.xlf b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.cs.xlf index 11a9c6c7b6..3e79e05724 100644 --- a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.cs.xlf +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.cs.xlf @@ -127,6 +127,21 @@ {0} volá {1}, ale nepoužívá hodnotu, kterou tato metoda vrací. Protože {1} je označená jako metoda Pure, nemůže mít vedlejší účinky. Použijte výsledek v podmíněném příkazu, přiřaďte výsledek proměnné nebo ho předejte jako argument jiné metodě. + + Using nameof helps keep your code valid when refactoring. + Using nameof helps keep your code valid when refactoring. + + + + Use nameof in place of string literal '{0}' + Use nameof in place of string literal '{0}' + + + + Use nameof to express symbol names + Use nameof to express symbol names + + \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.de.xlf b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.de.xlf index 0b12c89a4a..c484eea19b 100644 --- a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.de.xlf +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.de.xlf @@ -127,6 +127,21 @@ "{0}" ruft "{1}" auf, verwendet jedoch nicht den von der Methode zurückgegebenen Wert. Weil "{1}" als Pure-Methode markiert ist, kann sie keine Nebeneffekte haben. Verwenden Sie das Ergebnis in einer Bedingungsanweisung, weisen Sie das Ergebnis einer Variablen zu, oder übergeben Sie es als Argument an eine andere Methode. + + Using nameof helps keep your code valid when refactoring. + Using nameof helps keep your code valid when refactoring. + + + + Use nameof in place of string literal '{0}' + Use nameof in place of string literal '{0}' + + + + Use nameof to express symbol names + Use nameof to express symbol names + + \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.es.xlf b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.es.xlf index d36b51bbe6..500f747936 100644 --- a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.es.xlf +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.es.xlf @@ -127,6 +127,21 @@ {0} llama a {1} pero no usa el valor que el método devuelve. {1} se ha marcado como método Pure, por lo que no puede tener efectos secundarios. Use el resultado en una instrucción condicional, asigne el resultado a una variable o páselo como argumento a otro método. + + Using nameof helps keep your code valid when refactoring. + Using nameof helps keep your code valid when refactoring. + + + + Use nameof in place of string literal '{0}' + Use nameof in place of string literal '{0}' + + + + Use nameof to express symbol names + Use nameof to express symbol names + + \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.fr.xlf b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.fr.xlf index 453810f9b5..cf083755df 100644 --- a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.fr.xlf +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.fr.xlf @@ -127,6 +127,21 @@ {0} appelle {1} mais n'utilise pas la valeur retournée par la méthode. Dans la mesure où {1} est marqué en tant que méthode Pure, il ne peut pas avoir d'effets secondaires. Utilisez le résultat dans une instruction conditionnelle, assignez-le à une variable ou passez-le en tant qu'argument à une autre méthode. + + Using nameof helps keep your code valid when refactoring. + Using nameof helps keep your code valid when refactoring. + + + + Use nameof in place of string literal '{0}' + Use nameof in place of string literal '{0}' + + + + Use nameof to express symbol names + Use nameof to express symbol names + + \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.it.xlf b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.it.xlf index af1cfa4708..19d5624c9e 100644 --- a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.it.xlf +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.it.xlf @@ -127,6 +127,21 @@ {0} chiama {1} ma non usa il valore restituito dal metodo. Dal momento che {1} è contrassegnato come metodo Pure, non può avere effetti collaterali. Usare il risultato in un'istruzione condizionale, assegnare il risultato a una variabile o passarlo come argomento a un altro metodo. + + Using nameof helps keep your code valid when refactoring. + Using nameof helps keep your code valid when refactoring. + + + + Use nameof in place of string literal '{0}' + Use nameof in place of string literal '{0}' + + + + Use nameof to express symbol names + Use nameof to express symbol names + + \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.ja.xlf b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.ja.xlf index 7c7e0b7363..48d0e2e853 100644 --- a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.ja.xlf +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.ja.xlf @@ -127,6 +127,21 @@ {0} は {1} を呼び出しますが、メソッドから返される値は使用されません。{1} は Pure メソッドとしてマークされているため、副作用は発生しません。条件ステートメントで結果を使用するか、結果を変数に割り当てるか、引数として他のメソッドに渡してください。 + + Using nameof helps keep your code valid when refactoring. + Using nameof helps keep your code valid when refactoring. + + + + Use nameof in place of string literal '{0}' + Use nameof in place of string literal '{0}' + + + + Use nameof to express symbol names + Use nameof to express symbol names + + \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.ko.xlf b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.ko.xlf index 73b44825da..2e49f20f38 100644 --- a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.ko.xlf +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.ko.xlf @@ -127,6 +127,21 @@ {0}이(가) {1}을(를) 호출하지만 메서드가 반환하는 값을 사용하지 않습니다. {1}이(가) 순수 메서드로 표시되어 있으므로 의도하지 않은 결과가 발생하지 않습니다. 결과를 조건문에 사용하거나 변수에 할당하거나 다른 메서드에 인수로 전달하세요. + + Using nameof helps keep your code valid when refactoring. + Using nameof helps keep your code valid when refactoring. + + + + Use nameof in place of string literal '{0}' + Use nameof in place of string literal '{0}' + + + + Use nameof to express symbol names + Use nameof to express symbol names + + \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.pl.xlf b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.pl.xlf index 47103fa79a..6b70400dfc 100644 --- a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.pl.xlf +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.pl.xlf @@ -127,6 +127,21 @@ Element {0} wywołuje element {1}, ale nie używa wartości zwracanej przez metodę. Ponieważ metoda {1} jest oznaczona jako metoda Pure, nie może ona mieć efektów ubocznych. Użyj wyniku w instrukcji warunkowej, przypisz wynik do zmiennej lub przekaż go jako argument do innej metody. + + Using nameof helps keep your code valid when refactoring. + Using nameof helps keep your code valid when refactoring. + + + + Use nameof in place of string literal '{0}' + Use nameof in place of string literal '{0}' + + + + Use nameof to express symbol names + Use nameof to express symbol names + + \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.pt-BR.xlf b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.pt-BR.xlf index 865966cc43..e518001bea 100644 --- a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.pt-BR.xlf +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.pt-BR.xlf @@ -127,6 +127,21 @@ {0} chama {1}, mas não usa o valor retornado pelo método. Como {1} é marcado como um método Pure, ele não pode ter efeitos colaterais. Use o resultado em uma instrução condicional, atribua o resultado a uma variável ou passe-o como argumento para outro método. + + Using nameof helps keep your code valid when refactoring. + Using nameof helps keep your code valid when refactoring. + + + + Use nameof in place of string literal '{0}' + Use nameof in place of string literal '{0}' + + + + Use nameof to express symbol names + Use nameof to express symbol names + + \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.ru.xlf b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.ru.xlf index 7c6227a7cc..2cd31088fa 100644 --- a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.ru.xlf +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.ru.xlf @@ -127,6 +127,21 @@ {0} вызывает {1}, но не использует значение, возвращаемое методом. Так как {1} помечен как метод Pure, он не может иметь побочные эффекты. Используйте результат в условном операторе, присвойте его переменной или передайте его в качестве аргумента другому методу. + + Using nameof helps keep your code valid when refactoring. + Using nameof helps keep your code valid when refactoring. + + + + Use nameof in place of string literal '{0}' + Use nameof in place of string literal '{0}' + + + + Use nameof to express symbol names + Use nameof to express symbol names + + \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.tr.xlf b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.tr.xlf index 33cf5d3bc7..bf10e84ff9 100644 --- a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.tr.xlf +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.tr.xlf @@ -127,6 +127,21 @@ {0} {1} çağırıyor ancak yöntemin döndürdüğü değeri kullanmıyor. {1} Saf yöntem olarak işaretlendiğinden yan etkilere sahip olamaz. Bir koşullu ifadedeki sonucu kullanın, sonucu bir değişkene atayın veya bir bağımsız değişken olarak başka bir yönteme geçirin. + + Using nameof helps keep your code valid when refactoring. + Using nameof helps keep your code valid when refactoring. + + + + Use nameof in place of string literal '{0}' + Use nameof in place of string literal '{0}' + + + + Use nameof to express symbol names + Use nameof to express symbol names + + \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.zh-Hans.xlf b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.zh-Hans.xlf index 3d4e8276c9..118c9157e6 100644 --- a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.zh-Hans.xlf +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.zh-Hans.xlf @@ -127,6 +127,21 @@ {0} 调用 {1},但不使用方法返回的值。{1} 被标记为纯方法,所以不会有副作用。请使用条件语句中的结果,并将结果赋给变量或将其作为自变量传递给另一个方法。 + + Using nameof helps keep your code valid when refactoring. + Using nameof helps keep your code valid when refactoring. + + + + Use nameof in place of string literal '{0}' + Use nameof in place of string literal '{0}' + + + + Use nameof to express symbol names + Use nameof to express symbol names + + \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.zh-Hant.xlf b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.zh-Hant.xlf index 4d06e850fc..0c5472615a 100644 --- a/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.zh-Hant.xlf +++ b/src/Microsoft.CodeQuality.Analyzers/Core/Maintainability/xlf/MicrosoftMaintainabilityAnalyzersResources.zh-Hant.xlf @@ -127,6 +127,21 @@ {0} 呼叫了 {1},但未使用方法傳回的值。因為 {1} 標記為 Pure 方法,所以不能有副作用。請使用條件陳述式中的結果、將結果指派到變數,或將其以引數方式傳遞到另一個方法。 + + Using nameof helps keep your code valid when refactoring. + Using nameof helps keep your code valid when refactoring. + + + + Use nameof in place of string literal '{0}' + Use nameof in place of string literal '{0}' + + + + Use nameof to express symbol names + Use nameof to express symbol names + + \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/UnitTests/Maintainability/UseNameOfInPlaceOfStringTests.Fixer.cs b/src/Microsoft.CodeQuality.Analyzers/UnitTests/Maintainability/UseNameOfInPlaceOfStringTests.Fixer.cs new file mode 100644 index 0000000000..31cbaa1cec --- /dev/null +++ b/src/Microsoft.CodeQuality.Analyzers/UnitTests/Maintainability/UseNameOfInPlaceOfStringTests.Fixer.cs @@ -0,0 +1,181 @@ +// 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 Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeQuality.CSharp.Analyzers.Maintainability; +using Microsoft.CodeQuality.VisualBasic.Analyzers.Maintainability; +using Test.Utilities; +using Xunit; + +namespace Microsoft.CodeQuality.Analyzers.Maintainability.UnitTests +{ + public class UseNameOfInPlaceOfStringTests : CodeFixTestBase + { + protected override DiagnosticAnalyzer GetBasicDiagnosticAnalyzer() + { + return new UseNameofInPlaceOfStringAnalyzer(); + } + + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new UseNameofInPlaceOfStringAnalyzer(); + + } + + protected override CodeFixProvider GetBasicCodeFixProvider() + { + return new UseNameOfInPlaceOfStringFixer(); + } + + protected override CodeFixProvider GetCSharpCodeFixProvider() + { + return new UseNameOfInPlaceOfStringFixer(); + } + + [Fact] + public void Fixer_CSharp_ArgumentMatchesAParameterInScope() + { + VerifyCSharpFix(@" +using System; +class C +{ + void M(int x) + { + throw new ArgumentNullException(""x""); + } +}", +@" +using System; +class C +{ + void M(int x) + { + throw new ArgumentNullException(nameof(x)); + } +}", allowNewCompilerDiagnostics: true, validationMode: TestValidationMode.AllowCompileErrors ); + } + + [Fact] + public void Fixer_CSharp_ArgumentWithComments() + { + VerifyCSharpFix(@" +using System; +class C +{ + void M(int x) + { + throw new ArgumentNullException(/*Leading*/""x""/*Trailing*/); + } +}", +@" +using System; +class C +{ + void M(int x) + { + throw new ArgumentNullException(/*Leading*/nameof(x)/*Trailing*/); + } +}", allowNewCompilerDiagnostics: true, validationMode: TestValidationMode.AllowCompileErrors); + } + + [Fact] + public void Fixer_CSharp_ArgumentWithComments2() + { + VerifyCSharpFix(@" +using System; +class C +{ + void M(int x) + { + throw new ArgumentException(""Somemessage"", /*Leading*/""x""/*Trailing*/); + } +}", +@" +using System; +class C +{ + void M(int x) + { + throw new ArgumentException(""Somemessage"", /*Leading*/nameof(x)/*Trailing*/); + } +}", allowNewCompilerDiagnostics: true, validationMode: TestValidationMode.AllowCompileErrors); + } + + [Fact] + public void Fixer_VB_ArgumentMatchesAParameterInScope() + { + VerifyBasicFix(@" +Imports System + +Module Mod1 + Sub f(s As String) + Throw New ArgumentNullException(""s"") + End Sub +End Module", +@" +Imports System + +Module Mod1 + Sub f(s As String) + Throw New ArgumentNullException(NameOf(s)) + End Sub +End Module", allowNewCompilerDiagnostics: true, validationMode: TestValidationMode.AllowCompileErrors); + } + + [Fact] + public void Fixer_CSharp_ArgumentMatchesPropertyInScope() + { + VerifyCSharpFix(@" +using System.ComponentModel; + +public class Person : INotifyPropertyChanged +{ + private string name; + public event PropertyChangedEventHandler PropertyChanged; + + public string PersonName { + get { return name; } + set + { + name = value; + OnPropertyChanged(""PersonName""); + } + } + + protected void OnPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +}", @" +using System.ComponentModel; + +public class Person : INotifyPropertyChanged +{ + private string name; + public event PropertyChangedEventHandler PropertyChanged; + + public string PersonName { + get { return name; } + set + { + name = value; + OnPropertyChanged(nameof(PersonName)); + } + } + + protected void OnPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +}", allowNewCompilerDiagnostics: true, validationMode: TestValidationMode.AllowCompileErrors); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.CodeQuality.Analyzers/UnitTests/Maintainability/UseNameOfInPlaceOfStringTests.cs b/src/Microsoft.CodeQuality.Analyzers/UnitTests/Maintainability/UseNameOfInPlaceOfStringTests.cs new file mode 100644 index 0000000000..2ab1bb640e --- /dev/null +++ b/src/Microsoft.CodeQuality.Analyzers/UnitTests/Maintainability/UseNameOfInPlaceOfStringTests.cs @@ -0,0 +1,528 @@ +// 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 Microsoft.CodeAnalysis.Diagnostics; +using Test.Utilities; +using Xunit; + +namespace Microsoft.CodeQuality.Analyzers.Maintainability.UnitTests +{ + public class UseNameofInPlaceOfStringTests : DiagnosticAnalyzerTestBase + { + #region Unit tests for no analyzer diagnostic + + [Fact] + public void NoDiagnostic_NoArguments() + { + VerifyCSharp(@" +using System; +class C +{ + void M(int x) + { + throw new ArgumentNullException(); + } +}"); + } + + [Fact] + public void NoDiagnostic_NullLiteral() + { + VerifyCSharp(@" +using System; +class C +{ + void M(int x) + { + throw new ArgumentNullException(null); + } +}"); + } + + [Fact] + public void NoDiagnostic_StringIsAReservedWord() + { + VerifyCSharp(@" +using System; +class C +{ + void M(int x) + { + throw new ArgumentNullException(""static""); + } +}"); + } + + [Fact] + public void NoDiagnostic_NoMatchingParametersInScope() + { + VerifyCSharp(@" +using System; +class C +{ + void M(int y) + { + throw new ArgumentNullException(""x""); + } +}"); + } + + [Fact] + public void NoDiagnostic_NameColonOtherParameterName() + { + VerifyCSharp(@" +using System; +class C +{ + void M(int y) + { + Console.WriteLine(format:""x""); + } +}"); + } + + [Fact] + public void NoDiagnostic_NotStringLiteral() + { + VerifyCSharp(@" +using System; +class C +{ + void M(int x) + { + string param = ""x""; + throw new ArgumentNullException(param); + } +}"); + } + + [Fact] + public void NoDiagnostic_NotValidIdentifier() + { + VerifyCSharp(@" +using System; +class C +{ + void M(int x) + { + throw new ArgumentNullException(""9x""); + } +}"); + } + + [Fact] + public void NoDiagnostic_NoArgumentList() + { + VerifyCSharp(@" +using System; +class C +{ + void M(int x) + { + throw new ArgumentNullException( + } +}", TestValidationMode.AllowCompileErrors); + } + + [Fact] + public void NoDiagnostic_NoMatchingParameter() + { + VerifyCSharp(@" +using System; +class C +{ + void M(int x) + { + throw new ArgumentNullException(""test"", ""test2"", ""test3""); + } +}", TestValidationMode.AllowCompileErrors); + } + + [Fact] + public void NoDiagnostic_MatchesParameterButNotCalledParamName() + { + VerifyCSharp(@" +using System; +class C +{ + void M(int x) + { + Console.WriteLine(""x""); + } +}"); + } + + [Fact] + public void NoDiagnostic_MatchesPropertyButNotCalledPropertyName() + { + VerifyCSharp(@" +using System; +using System.ComponentModel; + +public class Person : INotifyPropertyChanged +{ + private string name; + public event PropertyChangedEventHandler PropertyChanged; + + public string PersonName { + get { return name; } + set + { + name = value; + Console.WriteLine(""PersonName""); + } + } + + protected void OnPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +}"); + } + + [Fact] + public void NoDiagnostic_PositionalArgumentOtherParameterName() + { + VerifyCSharp(@" +using System; +class C +{ + void M(int x) + { + Console.WriteLine(""x""); + } +}"); + } + + + #endregion + + + #region Unit tests for analyzer diagnostic(s) + + [Fact] + public void Diagnostic_ArgumentMatchesAParameterInScope() + { + VerifyCSharp(@" +using System; +class C +{ + void M(int x) + { + throw new ArgumentNullException(""x""); + } +}", + GetCSharpNameofResultAt(7, 41, "x")); + } + + [Fact] + public void Diagnostic_VB_ArgumentMatchesAParameterInScope() + { + VerifyBasic(@" +Imports System + +Module Mod1 + Sub f(s As String) + Throw New ArgumentNullException(""s"") + End Sub +End Module", + GetBasicNameofResultAt(6, 41, "s")); + } + + [Fact] + public void Diagnostic_ArgumentMatchesAPropertyInScope() + { + VerifyCSharp(@" +using System.ComponentModel; + +public class Person : INotifyPropertyChanged +{ + private string name; + public event PropertyChangedEventHandler PropertyChanged; + + public string PersonName { + get { return name; } + set + { + name = value; + OnPropertyChanged(""PersonName""); + } + } + + protected void OnPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +}", + GetCSharpNameofResultAt(14, 31, "PersonName")); + } + + [Fact] + public void Diagnostic_ArgumentMatchesAPropertyInScope2() + { + VerifyCSharp(@" +using System.ComponentModel; + +public class Person : INotifyPropertyChanged +{ + private string name; + public event PropertyChangedEventHandler PropertyChanged; + + public string PersonName + { + get { return name; } + set + { + name = value; + OnPropertyChanged(""PersonName""); + } + } + + public string PersonName2 + { + get { return name; } + set + { + name = value; + OnPropertyChanged(nameof(PersonName2)); + } + } + + protected void OnPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +}", + GetCSharpNameofResultAt(15, 31, "PersonName")); + } + + [Fact] + public void Diagnostic_ArgumentNameColonParamName() + { + VerifyCSharp(@" +using System; +class C +{ + void M(int x) + { + throw new ArgumentNullException(paramName:""x""); + } +}", + GetCSharpNameofResultAt(7, 51, "x")); + } + + [Fact] + public void Diagnostic_ArgumentNameColonPropertyName() + { + VerifyCSharp(@" +using System.ComponentModel; + +public class Person : INotifyPropertyChanged +{ + private string name; + public event PropertyChangedEventHandler PropertyChanged; + + public string PersonName { + get { return name; } + set + { + name = value; + OnPropertyChanged(propertyName:""PersonName""); + } + } + + protected void OnPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +}", + GetCSharpNameofResultAt(14, 44, "PersonName")); + } + + + [Fact] + public void Diagnostic_AnonymousFunctionMultiline1() + { + VerifyCSharp(@" +using System; + +class Test +{ + void Method(int x) + { + Action a = (int y) => + { + throw new ArgumentException(""somemessage"", ""x""); + }; + } +}", + GetCSharpNameofResultAt(10, 56, "x")); + } + + [Fact] + public void Diagnostic_AnonymousFunctionMultiLine2() + { + VerifyCSharp(@" +using System; + +class Test +{ + void Method(int x) + { + Action a = (int y) => + { + throw new ArgumentException(""somemessage"", ""y""); + }; + } +}", + GetCSharpNameofResultAt(10, 56, "y")); + } + + [Fact] + public void Diagnostic_AnonymousFunctionSingleLine1() + { + VerifyCSharp(@" +using System; + +class Test +{ + void Method(int x) + { + Action a = (int y) => throw new ArgumentException(""somemessage"", ""y""); + } +}", + GetCSharpNameofResultAt(8, 79, "y")); + } + + [Fact] + public void Diagnostic_AnonymousFunctionSingleLine2() + { + VerifyCSharp(@" +using System; + +class Test +{ + void Method(int x) + { + Action a = (int y) => throw new ArgumentException(""somemessage"", ""x""); + } +}", + GetCSharpNameofResultAt(8, 79, "x")); + } + + [Fact] + public void Diagnostic_AnonymousFunctionMultipleParameters() + { + VerifyCSharp(@" +using System; + +class Test +{ + void Method(int x) + { + Action a = (j, k) => throw new ArgumentException(""somemessage"", ""x""); + } +}", + GetCSharpNameofResultAt(8, 83, "x")); + } + + [Fact] + public void Diagnostic_LocalFunction1() + { + VerifyCSharp(@" +using System; + +class Test +{ + void Method(int x) + { + void AnotherMethod(int y, int z) + { + throw new ArgumentException(""somemessage"", ""x""); + } + } +}", + GetCSharpNameofResultAt(10, 60, "x")); + } + + [Fact] + public void Diagnostic_LocalFunction2() + { + VerifyCSharp(@" +using System; + +class Test +{ + void Method(int x) + { + void AnotherMethod(int y, int z) + { + throw new ArgumentException(""somemessage"", ""y""); + } + } +}", + GetCSharpNameofResultAt(10, 60, "y")); + } + + [Fact] + public void Diagnostic_Delegate() + { + VerifyCSharp(@" +using System; + +namespace ConsoleApp14 +{ + class Program + { + class test + { + Action x2 = delegate (int xyz) + { + throw new ArgumentNullException(""xyz""); + }; + } + } +}", + GetCSharpNameofResultAt(12, 49, "xyz")); + } + + #endregion + + private DiagnosticResult GetBasicNameofResultAt(int line, int column, string name) + { + var message = string.Format(MicrosoftMaintainabilityAnalyzersResources.UseNameOfInPlaceOfStringMessage, name); + return GetBasicResultAt(line, column, UseNameofInPlaceOfStringAnalyzer.RuleId, message); + } + + private DiagnosticResult GetCSharpNameofResultAt(int line, int column, string name) + { + var message = string.Format(MicrosoftMaintainabilityAnalyzersResources.UseNameOfInPlaceOfStringMessage, name); + return GetCSharpResultAt(line, column, UseNameofInPlaceOfStringAnalyzer.RuleId, message); + } + + protected override DiagnosticAnalyzer GetBasicDiagnosticAnalyzer() + { + return new UseNameofInPlaceOfStringAnalyzer(); + } + + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new UseNameofInPlaceOfStringAnalyzer(); + } + } +}