From 5d0fd6a64afc4ca7b45b4f2708e2f889fb25b952 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Tue, 17 Aug 2021 17:53:52 +0000 Subject: [PATCH 01/64] AvoidConstArrays analyzer drafted --- .../Core/AnalyzerReleases.Unshipped.md | 6 + .../MicrosoftNetCoreAnalyzersResources.resx | 11 + .../Runtime/AvoidConstArrays.Fixer.cs | 175 +++++++++ .../Runtime/AvoidConstArrays.cs | 94 +++++ .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 15 + .../MicrosoftNetCoreAnalyzersResources.de.xlf | 15 + .../MicrosoftNetCoreAnalyzersResources.es.xlf | 15 + .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 15 + .../MicrosoftNetCoreAnalyzersResources.it.xlf | 15 + .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 15 + .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 15 + .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 15 + ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 15 + .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 15 + .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 15 + ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 15 + ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 15 + .../Runtime/AvoidConstArraysTests.cs | 341 ++++++++++++++++++ 18 files changed, 822 insertions(+) create mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs create mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs create mode 100644 src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index cdf4f1397e..98196ef8dd 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -1 +1,7 @@ ; Please do not edit this file manually, it should only be updated through code fix application. + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +CA1848 | Performance | Info | AvoidConstArrays, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1848) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 82286fc3d8..040fb0d27d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -246,6 +246,17 @@ Avoid unsealed attributes + + Avoid const arrays + + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + {Locked="static", "readonly"} + + + Make any constant arrays passed as arguments into 'static readonly' fields + {Locked="static", "readonly"} + Test for empty strings using string length diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs new file mode 100644 index 0000000000..5ff354c245 --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -0,0 +1,175 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.NetCore.Analyzers.Runtime +{ + /// + /// CA1848: Avoid const arrays. Replace with static readonly arrays. + /// + [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] + public sealed class AvoidConstArraysFixer : CodeFixProvider + { + public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(AvoidConstArraysAnalyzer.RuleId); + + private static readonly ImmutableArray s_collectionMemberEndings = ImmutableArray.Create("array", "collection", "enumerable", "list"); + + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers + public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + Document document = context.Document; + SyntaxNode root = await document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxNode node = root.FindNode(context.Span); + SemanticModel model = await document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + DocumentEditor editor = await DocumentEditor.CreateAsync(document, context.CancellationToken).ConfigureAwait(false); + SyntaxGenerator generator = editor.Generator; + + context.RegisterCodeFix( + new MyCodeAction( + MicrosoftNetCoreAnalyzersResources.AvoidConstArraysTitle, + async c => await ExtractConstArrayAsync(node, model, editor, generator, context.Diagnostics.First().Properties, c).ConfigureAwait(false), + equivalenceKey: nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysTitle)), + context.Diagnostics); + } + + private static async Task ExtractConstArrayAsync(SyntaxNode node, SemanticModel model, DocumentEditor editor, + SyntaxGenerator generator, ImmutableDictionary properties, CancellationToken cancellationToken) + { + IArrayCreationOperation arrayArgument = GetArrayCreationOperation(node, model, cancellationToken, out bool isInvoked); + INamedTypeSymbol containingType = model.GetEnclosingSymbol(node.SpanStart, cancellationToken).ContainingType; + IEnumerable typeFields = containingType.GetMembers().Where(x => x is IFieldSymbol); + + // Get a valid member name for the extracted constant + IEnumerable memberNames = typeFields.Select(x => x.Name); + string newMemberName = GetExtractedMemberName(memberNames, properties["paramName"]); + + // Get method containing the symbol that is being diagnosed. Should always be in a method + IOperation? containingMethodBody = arrayArgument.GetAncestor(OperationKind.MethodBody); + containingMethodBody ??= arrayArgument.GetAncestor(OperationKind.Block); // VB methods have a different structure than CS methods + + RoslynDebug.Assert(containingMethodBody != null); + + // Create the new member + SyntaxNode newMember = generator.FieldDeclaration( + newMemberName, + generator.TypeExpression(arrayArgument.Type), + GetAccessibility(model.GetEnclosingSymbol(containingMethodBody!.Syntax.SpanStart, cancellationToken)), + DeclarationModifiers.Static | DeclarationModifiers.ReadOnly, + arrayArgument.Syntax + ).FormatForExtraction(containingMethodBody.Syntax); + + // Add the new extracted member + ISymbol lastFieldSymbol = typeFields.LastOrDefault(); + if (lastFieldSymbol is not null) + { + // Insert after last field if any fields are present + SyntaxNode lastFieldNode = await lastFieldSymbol.DeclaringSyntaxReferences.First().GetSyntaxAsync(cancellationToken).ConfigureAwait(false); + editor.InsertAfter(lastFieldNode, newMember); + } + else + { + // Insert before first method if no fields are present, as a method already exists + editor.InsertBefore(containingMethodBody.Syntax, newMember); + } + + // Replace argument with a reference to our new member + SyntaxNode identifier = generator.IdentifierName(newMemberName); + if (isInvoked) + { + editor.ReplaceNode(node, generator.WithExpression(identifier, node)); + } + else + { + editor.ReplaceNode(node, generator.Argument(identifier)); + } + + // Return changed document + return await Formatter.FormatAsync(editor.GetChangedDocument(), cancellationToken: cancellationToken).ConfigureAwait(false); + } + + private static IArrayCreationOperation GetArrayCreationOperation(SyntaxNode node, SemanticModel model, CancellationToken cancellationToken, + out bool isInvoked) + { + // The analyzer only passes a diagnostic for two scenarios: + // 1. The node is an IArgumentOperation that is a direct parent of an IArrayCreationOperation + // 2. The node is an IArrayCreationOperation already, as it was pulled from an + // invocation, like with LINQ extension methods + // Therefore, casting to IArrayCreationOperation in this case is safe + + // If this is a LINQ invocation, the node is already an IArrayCreationOperation + if (model.GetOperation(node, cancellationToken) is IArrayCreationOperation arrayCreation) + { + isInvoked = true; + return arrayCreation; + } + isInvoked = false; + return (IArrayCreationOperation)model.GetOperation(node.ChildNodes().First(), cancellationToken); + } + + private static string GetExtractedMemberName(IEnumerable memberNames, string parameterName) + { + bool hasCollectionEnding = s_collectionMemberEndings.Any(x => parameterName.EndsWith(x, true, null)); + + if (parameterName == "source" // for LINQ, "sourceArray" is clearer than "source" + || (memberNames.Contains(parameterName) && !hasCollectionEnding)) + { + parameterName += "Array"; + } + + if (memberNames.Contains(parameterName)) + { + int suffix = 0; + while (memberNames.Contains(parameterName + suffix)) + { + suffix++; + } + return parameterName + suffix; + } + + return parameterName; + } + + private static Accessibility GetAccessibility(ISymbol originMethodSymbol) + { + if (Enum.TryParse(originMethodSymbol.GetResultantVisibility().ToString(), out Accessibility accessibility)) + { + return accessibility == Accessibility.Public + ? Accessibility.Private // public accessibility not wanted for fields + : accessibility; + } + return Accessibility.Private; + } + + // Needed for Telemetry (https://github.com/dotnet/roslyn-analyzers/issues/192) + private sealed class MyCodeAction : DocumentChangeAction + { + public MyCodeAction(string title, Func> createChangedDocument, string equivalenceKey) : + base(title, createChangedDocument, equivalenceKey) + { + } + } + } + + internal static class SyntaxNodeExtensions + { + internal static SyntaxNode FormatForExtraction(this SyntaxNode node, SyntaxNode previouslyContainingNode) + { + return node.HasTrailingTrivia ? node : node.WithTrailingTrivia(previouslyContainingNode.GetTrailingTrivia()); + } + } +} \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs new file mode 100644 index 0000000000..a24b27ec6b --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -0,0 +1,94 @@ +// 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.Linq; +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.NetCore.Analyzers.Runtime +{ + /// + /// CA1848: Avoid const arrays. Replace with static readonly arrays. + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class AvoidConstArraysAnalyzer : DiagnosticAnalyzer + { + internal const string RuleId = "CA1848"; + + private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); + private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); + private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysDescription), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(RuleId, + s_localizableTitle, + s_localizableMessage, + DiagnosticCategory.Performance, + RuleLevel.IdeSuggestion, + s_localizableDescription, + isPortedFxCopRule: false, + isDataflowRule: false); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + // Analyzes an argument operation + context.RegisterOperationAction(operationContext => + { + IArgumentOperation? argumentOperation; + + if (operationContext.Operation is IArrayCreationOperation arrayCreationOperation) // For arrays passed as arguments + { + argumentOperation = arrayCreationOperation.GetAncestor(OperationKind.Argument); + if (argumentOperation is null) + { + return; + } + } + else if (operationContext.Operation is IInvocationOperation invocationOperation) // For arrays passed in extension methods, like in LINQ + { + if (invocationOperation.Descendants().Any(x => x is IArrayCreationOperation)) + { + // This is an invocation that contains an array in it + // This will get caught by the first case in another cycle + return; + } + + argumentOperation = invocationOperation.Arguments.FirstOrDefault(); + if (argumentOperation is null || argumentOperation.Children.First() is not IConversionOperation conversionOperation + || conversionOperation.Operand is not IArrayCreationOperation) + { + return; + } + arrayCreationOperation = (IArrayCreationOperation)conversionOperation.Operand; + } + else + { + return; + } + + // Must be literal array + if (!arrayCreationOperation.Children.First(x => x is IArrayInitializerOperation).Children.All(x => x is ILiteralOperation)) + { + return; + } + + Dictionary properties = new() + { + { "paramName", argumentOperation.Parameter.Name } + }; + + operationContext.ReportDiagnostic(arrayCreationOperation.CreateDiagnostic(Rule, properties.ToImmutableDictionary())); + }, + OperationKind.ArrayCreation, + OperationKind.Invocation); + } + } +} \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf index f6adfac4e3..e0372059a3 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -52,6 +52,21 @@ Literály řetězců atributů by se měly správně parsovat + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + {Locked="static", "readonly"} + + + Make any constant arrays passed as arguments into 'static readonly' fields + Make any constant arrays passed as arguments into 'static readonly' fields + {Locked="static", "readonly"} + + + Avoid const arrays + Avoid const arrays + + Marshalling of 'StringBuilder' always creates a native buffer copy, resulting in multiple allocations for one marshalling operation. Zařazením parametru StringBuilder se vždy vytvoří kopie nativní vyrovnávací paměti, která bude mít za následek vícenásobné přidělení pro jednu operaci zařazování. 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 82a8d1668f..4a1e1cbbc5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -52,6 +52,21 @@ Attributzeichenfolgenliterale müssen richtig analysiert werden + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + {Locked="static", "readonly"} + + + Make any constant arrays passed as arguments into 'static readonly' fields + Make any constant arrays passed as arguments into 'static readonly' fields + {Locked="static", "readonly"} + + + Avoid const arrays + Avoid const arrays + + Marshalling of 'StringBuilder' always creates a native buffer copy, resulting in multiple allocations for one marshalling operation. Beim Marshalling von "StringBuilder" wird immer eine native Pufferkopie erstellt, sodass mehrere Zuordnungen für einen Marshallingvorgang vorhanden sind. 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 8056474ccd..4c0169405d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -52,6 +52,21 @@ Los literales de cadena de atributo se deben analizar correctamente + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + {Locked="static", "readonly"} + + + Make any constant arrays passed as arguments into 'static readonly' fields + Make any constant arrays passed as arguments into 'static readonly' fields + {Locked="static", "readonly"} + + + Avoid const arrays + Avoid const arrays + + Marshalling of 'StringBuilder' always creates a native buffer copy, resulting in multiple allocations for one marshalling operation. Al serializar "StringBuilder" siempre se crea una copia del búfer nativo, lo que da lugar a varias asignaciones para una operación de serialización. 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 0daa69ef32..6e8fd40308 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -52,6 +52,21 @@ Les littéraux de chaîne d'attribut doivent être analysés correctement + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + {Locked="static", "readonly"} + + + Make any constant arrays passed as arguments into 'static readonly' fields + Make any constant arrays passed as arguments into 'static readonly' fields + {Locked="static", "readonly"} + + + Avoid const arrays + Avoid const arrays + + Marshalling of 'StringBuilder' always creates a native buffer copy, resulting in multiple allocations for one marshalling operation. Le marshaling de 'StringBuilder' crée toujours une copie de la mémoire tampon native, ce qui entraîne plusieurs allocations pour une seule opération de marshaling. 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 15e55847b7..35d15c0545 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -52,6 +52,21 @@ I valori letterali stringa dell'attributo devono essere analizzati correttamente + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + {Locked="static", "readonly"} + + + Make any constant arrays passed as arguments into 'static readonly' fields + Make any constant arrays passed as arguments into 'static readonly' fields + {Locked="static", "readonly"} + + + Avoid const arrays + Avoid const arrays + + Marshalling of 'StringBuilder' always creates a native buffer copy, resulting in multiple allocations for one marshalling operation. Il marshalling di 'StringBuilder' crea sempre una copia del buffer nativo, di conseguenza vengono generate più allocazioni per una singola operazione di marshalling. 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 1cf2f0b9a4..d834f2fc24 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -52,6 +52,21 @@ 属性文字列リテラルは、正しく解析する必要があります + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + {Locked="static", "readonly"} + + + Make any constant arrays passed as arguments into 'static readonly' fields + Make any constant arrays passed as arguments into 'static readonly' fields + {Locked="static", "readonly"} + + + Avoid const arrays + Avoid const arrays + + Marshalling of 'StringBuilder' always creates a native buffer copy, resulting in multiple allocations for one marshalling operation. 'StringBuilder' をマーシャリングすると、ネイティブ バッファーのコピーが常に作成され、1 回のマーシャリング操作に対して複数の割り当てが発生します。 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 bfcf20a775..b3c7ac7e95 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -52,6 +52,21 @@ 특성 문자열 리터럴이 올바르게 구문 분석되어야 합니다. + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + {Locked="static", "readonly"} + + + Make any constant arrays passed as arguments into 'static readonly' fields + Make any constant arrays passed as arguments into 'static readonly' fields + {Locked="static", "readonly"} + + + Avoid const arrays + Avoid const arrays + + Marshalling of 'StringBuilder' always creates a native buffer copy, resulting in multiple allocations for one marshalling operation. 'StringBuilder'를 마샬링하는 경우 항상 네이티브 버퍼 복사본이 만들어지므로 하나의 마샬링 작업에 대해 할당이 여러 번 이루어집니다. 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 060a1a3e0c..aba46edf63 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -52,6 +52,21 @@ Analiza literałów ciągu atrybutu powinna kończyć się powodzeniem + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + {Locked="static", "readonly"} + + + Make any constant arrays passed as arguments into 'static readonly' fields + Make any constant arrays passed as arguments into 'static readonly' fields + {Locked="static", "readonly"} + + + Avoid const arrays + Avoid const arrays + + Marshalling of 'StringBuilder' always creates a native buffer copy, resulting in multiple allocations for one marshalling operation. Marshalling elementu „StringBuilder” zawsze tworzy natywną kopię buforu, co powoduje powstanie wielu alokacji dla jednej operacji marshallingu. 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 e0fade9ce4..89cd9686ed 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 @@ -52,6 +52,21 @@ Literais de cadeias de caracteres de atributos devem ser analisados corretamente + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + {Locked="static", "readonly"} + + + Make any constant arrays passed as arguments into 'static readonly' fields + Make any constant arrays passed as arguments into 'static readonly' fields + {Locked="static", "readonly"} + + + Avoid const arrays + Avoid const arrays + + Marshalling of 'StringBuilder' always creates a native buffer copy, resulting in multiple allocations for one marshalling operation. O marshaling de 'StringBuilder' sempre cria uma cópia de buffer nativo, resultando em várias alocações para uma operação de marshalling. 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 2395ad3f6c..f33254e7fb 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -52,6 +52,21 @@ Синтаксический анализ строковых литералов атрибута должен осуществляться правильно + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + {Locked="static", "readonly"} + + + Make any constant arrays passed as arguments into 'static readonly' fields + Make any constant arrays passed as arguments into 'static readonly' fields + {Locked="static", "readonly"} + + + Avoid const arrays + Avoid const arrays + + Marshalling of 'StringBuilder' always creates a native buffer copy, resulting in multiple allocations for one marshalling operation. При маршалировании "StringBuilder" всегда создается собственная копия буфера, что приводит к множественным выделениям для одной операции маршалирования. 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 751442aaa0..25b11531fe 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -52,6 +52,21 @@ Öznitelik dizesinin sabit değerleri doğru ayrıştırılmalıdır + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + {Locked="static", "readonly"} + + + Make any constant arrays passed as arguments into 'static readonly' fields + Make any constant arrays passed as arguments into 'static readonly' fields + {Locked="static", "readonly"} + + + Avoid const arrays + Avoid const arrays + + Marshalling of 'StringBuilder' always creates a native buffer copy, resulting in multiple allocations for one marshalling operation. 'StringBuilder' öğesinin hazırlanması her zaman, bir hazırlama işlemi için birden çok ayırmaya neden olan yerel arabellek kopyası oluşturur. 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 58cca3bf82..a5c2e8bd6a 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 @@ -52,6 +52,21 @@ 特性字符串文本应正确分析 + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + {Locked="static", "readonly"} + + + Make any constant arrays passed as arguments into 'static readonly' fields + Make any constant arrays passed as arguments into 'static readonly' fields + {Locked="static", "readonly"} + + + Avoid const arrays + Avoid const arrays + + Marshalling of 'StringBuilder' always creates a native buffer copy, resulting in multiple allocations for one marshalling operation. "StringBuilder" 的封送处理总是会创建一个本机缓冲区副本,这导致一个封送处理操作出现多次分配。 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 c91a4fdd16..80aabf1ca6 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 @@ -52,6 +52,21 @@ 屬性字串常值應正確剖析 + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + {Locked="static", "readonly"} + + + Make any constant arrays passed as arguments into 'static readonly' fields + Make any constant arrays passed as arguments into 'static readonly' fields + {Locked="static", "readonly"} + + + Avoid const arrays + Avoid const arrays + + Marshalling of 'StringBuilder' always creates a native buffer copy, resulting in multiple allocations for one marshalling operation. 封送處理 'StringBuilder' 一律都會建立原生緩衝區複本,因而導致單一封送處理作業出現多重配置。 diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs new file mode 100644 index 0000000000..e7895965d5 --- /dev/null +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -0,0 +1,341 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.NetCore.Analyzers.Runtime.AvoidConstArraysAnalyzer, + Microsoft.NetCore.Analyzers.Runtime.AvoidConstArraysFixer>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.NetCore.Analyzers.Runtime.AvoidConstArraysAnalyzer, + Microsoft.NetCore.Analyzers.Runtime.AvoidConstArraysFixer>; + +namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests +{ + // Analyzer and code fix tests are both in this file + // "Do not separate analyzer tests from code fix tests" + // https://github.com/dotnet/roslyn-analyzers/blob/main/docs/NetCore_GettingStarted.md#definition-of-done + public class AvoidConstArraysTests + { + #region C# Tests + + [Fact] + public async Task CA1848_CSharp_IdentifyConstArrays() + { + // Implicit initialization check + await VerifyCS.VerifyCodeFixAsync(@" +using System; + +public class A +{ + public void B() + { + Console.WriteLine({|CA1848:new[]{ 1, 2, 3 }|}); + } +} +", @" +using System; + +public class A +{ + private static readonly int[] value = new[] { 1, 2, 3 }; + + public void B() + { + Console.WriteLine(value); + } +} +"); + + // Explicit initialization check + await VerifyCS.VerifyCodeFixAsync(@" +using System; + +public class A +{ + public void B() + { + Console.WriteLine({|CA1848:new int[]{ 1, 2, 3 }|}); + } +} +", @" +using System; + +public class A +{ + private static readonly int[] value = new int[] { 1, 2, 3 }; + + public void B() + { + Console.WriteLine(value); + } +} +"); + + // Nested arguments + await VerifyCS.VerifyCodeFixAsync(@" +using System; + +public class A +{ + public void B() + { + Console.WriteLine(string.Join("" "", {|CA1848:new[] { ""Cake"", ""is"", ""good"" }|})); + } +} +", @" +using System; + +public class A +{ + private static readonly string[] value = new[] { ""Cake"", ""is"", ""good"" }; + + public void B() + { + Console.WriteLine(string.Join("" "", value)); + } +} +"); + + // Extension method usage + await VerifyCS.VerifyCodeFixAsync(@" +using System; +using System.Linq; + +public class A +{ + public void B() + { + string y = {|CA1848:new[] { ""a"", ""b"", ""c"" }|}.First(); + Console.WriteLine(y); + } +} +", @" +using System; +using System.Linq; + +public class A +{ + private static readonly string[] sourceArray = new[] { ""a"", ""b"", ""c"" }; + + public void B() + { + string y = sourceArray.First(); + Console.WriteLine(y); + } +} +"); + } + + [Fact] + public async Task CA1848_CSharp_NoDiagnostic_IgnoreOtherArgs() + { + // All code fix tests in this method result in no changes, as the analyzer will ignore these + + // A string + await VerifyCS.VerifyCodeFixAsync(@" +using System; + +public class A +{ + public void B() + { + Console.WriteLine(""Lorem ipsum""); + } +} +", @" +using System; + +public class A +{ + public void B() + { + Console.WriteLine(""Lorem ipsum""); + } +} +"); + + // Test another type to be extra safe + await VerifyCS.VerifyCodeFixAsync(@" +using System; + +public class A +{ + public void B() + { + Console.WriteLine(123); + } +} +", @" +using System; + +public class A +{ + public void B() + { + Console.WriteLine(123); + } +} +"); + + // Non-literal array + await VerifyCS.VerifyCodeFixAsync(@" +using System; + +public class A +{ + public void B() + { + string str = ""Lorem ipsum""; + Console.WriteLine(new[] { str }); + } +} +", @" +using System; + +public class A +{ + public void B() + { + string str = ""Lorem ipsum""; + Console.WriteLine(new[] { str }); + } +} +"); + } + + #endregion + + #region Visual Basic Tests + + [Fact] + public async Task CA1848_VisualBasic_IdentifyConstArrays() + { + // Implicit initialization check + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine({|CA1848:{1, 2, 3}|}) + End Sub +End Class +", @" +Imports System + +Public Class A + Private Shared ReadOnly value As Integer() = {1, 2, 3} + Public Sub B() + Console.WriteLine(value) + End Sub +End Class +"); + + // Explicit initialization check + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine({|CA1848:New Integer() {1, 2, 3}|}) + End Sub +End Class +", @" +Imports System + +Public Class A + Private Shared ReadOnly value As Integer() = New Integer() {1, 2, 3} + Public Sub B() + Console.WriteLine(value) + End Sub +End Class +"); + + // Nested arguments + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(String.Join("" ""c, {|CA1848:{""Cake"", ""is"", ""good""}|})) + End Sub +End Class +", @" +Imports System + +Public Class A + Private Shared ReadOnly value As String() = {""Cake"", ""is"", ""good""} + Public Sub B() + Console.WriteLine(String.Join("" ""c, value)) + End Sub +End Class +"); + } + + [Fact] + public async Task CA1848_VisualBasic_NoDiagnostic_IgnoreOtherArgs() + { + // All code fix tests in this method result in no changes, as the analyzer will ignore these + + // A string + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(""Lorem ipsum"") + End Sub +End Class +", @" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(""Lorem ipsum"") + End Sub +End Class +"); + + // Test another type to be extra safe + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(123) + End Sub +End Class +", @" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(123) + End Sub +End Class +"); + + // Non-literal array + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Dim str As String = ""Lorem ipsum"" + Console.WriteLine({ str }) + End Sub +End Class +", @" +Imports System + +Public Class A + Public Sub B() + Dim str As String = ""Lorem ipsum"" + Console.WriteLine({ str }) + End Sub +End Class +"); + } + + #endregion + } +} \ No newline at end of file From 8ea2352da68fd04e8334d4fdccd916984a4555d0 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Tue, 17 Aug 2021 18:04:18 +0000 Subject: [PATCH 02/64] Renamed rule id --- .../Runtime/AvoidConstArrays.Fixer.cs | 2 +- .../Runtime/AvoidConstArrays.cs | 4 ++-- .../Runtime/AvoidConstArraysTests.cs | 22 +++++++++---------- .../DiagnosticCategoryAndIdRanges.txt | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 5ff354c245..6b746ebd50 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -18,7 +18,7 @@ namespace Microsoft.NetCore.Analyzers.Runtime { /// - /// CA1848: Avoid const arrays. Replace with static readonly arrays. + /// CA1849: Avoid const arrays. Replace with static readonly arrays. /// [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] public sealed class AvoidConstArraysFixer : CodeFixProvider diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index a24b27ec6b..213e766a40 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -12,12 +12,12 @@ namespace Microsoft.NetCore.Analyzers.Runtime { /// - /// CA1848: Avoid const arrays. Replace with static readonly arrays. + /// CA1849: Avoid const arrays. Replace with static readonly arrays. /// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class AvoidConstArraysAnalyzer : DiagnosticAnalyzer { - internal const string RuleId = "CA1848"; + internal const string RuleId = "CA1849"; private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index e7895965d5..0d66cef0be 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -19,7 +19,7 @@ public class AvoidConstArraysTests #region C# Tests [Fact] - public async Task CA1848_CSharp_IdentifyConstArrays() + public async Task CA1849_CSharp_IdentifyConstArrays() { // Implicit initialization check await VerifyCS.VerifyCodeFixAsync(@" @@ -29,7 +29,7 @@ public class A { public void B() { - Console.WriteLine({|CA1848:new[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1849:new[]{ 1, 2, 3 }|}); } } ", @" @@ -54,7 +54,7 @@ public class A { public void B() { - Console.WriteLine({|CA1848:new int[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1849:new int[]{ 1, 2, 3 }|}); } } ", @" @@ -79,7 +79,7 @@ public class A { public void B() { - Console.WriteLine(string.Join("" "", {|CA1848:new[] { ""Cake"", ""is"", ""good"" }|})); + Console.WriteLine(string.Join("" "", {|CA1849:new[] { ""Cake"", ""is"", ""good"" }|})); } } ", @" @@ -105,7 +105,7 @@ public class A { public void B() { - string y = {|CA1848:new[] { ""a"", ""b"", ""c"" }|}.First(); + string y = {|CA1849:new[] { ""a"", ""b"", ""c"" }|}.First(); Console.WriteLine(y); } } @@ -127,7 +127,7 @@ public void B() } [Fact] - public async Task CA1848_CSharp_NoDiagnostic_IgnoreOtherArgs() + public async Task CA1849_CSharp_NoDiagnostic_IgnoreOtherArgs() { // All code fix tests in this method result in no changes, as the analyzer will ignore these @@ -208,7 +208,7 @@ public void B() #region Visual Basic Tests [Fact] - public async Task CA1848_VisualBasic_IdentifyConstArrays() + public async Task CA1849_VisualBasic_IdentifyConstArrays() { // Implicit initialization check await VerifyVB.VerifyCodeFixAsync(@" @@ -216,7 +216,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine({|CA1848:{1, 2, 3}|}) + Console.WriteLine({|CA1849:{1, 2, 3}|}) End Sub End Class ", @" @@ -236,7 +236,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine({|CA1848:New Integer() {1, 2, 3}|}) + Console.WriteLine({|CA1849:New Integer() {1, 2, 3}|}) End Sub End Class ", @" @@ -256,7 +256,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine(String.Join("" ""c, {|CA1848:{""Cake"", ""is"", ""good""}|})) + Console.WriteLine(String.Join("" ""c, {|CA1849:{""Cake"", ""is"", ""good""}|})) End Sub End Class ", @" @@ -272,7 +272,7 @@ End Class } [Fact] - public async Task CA1848_VisualBasic_NoDiagnostic_IgnoreOtherArgs() + public async Task CA1849_VisualBasic_NoDiagnostic_IgnoreOtherArgs() { // All code fix tests in this method result in no changes, as the analyzer will ignore these diff --git a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt index 7e8db860d8..a7756c6170 100644 --- a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt +++ b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt @@ -12,7 +12,7 @@ Design: CA2210, CA1000-CA1070 Globalization: CA2101, CA1300-CA1310 Mobility: CA1600-CA1601 -Performance: HA, CA1800-CA1848 +Performance: HA, CA1800-CA1849 Security: CA2100-CA2153, CA2300-CA2330, CA3000-CA3147, CA5300-CA5403 Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2258 Naming: CA1700-CA1727 From 6d63e0b7ca98329b5cf25a297b91d3a35b82355b Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Tue, 17 Aug 2021 18:15:59 +0000 Subject: [PATCH 03/64] Edited comments in resources --- .../MicrosoftNetCoreAnalyzersResources.resx | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.de.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.es.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.it.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf | 4 ++-- 14 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 040fb0d27d..693ad93d88 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -251,11 +251,11 @@ Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - {Locked="static", "readonly"} + {Locked="static readonly"} Make any constant arrays passed as arguments into 'static readonly' fields - {Locked="static", "readonly"} + {Locked="static readonly"} Test for empty strings using string length 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 e0372059a3..7066fa0cdc 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -55,12 +55,12 @@ Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - {Locked="static", "readonly"} + {Locked="static readonly"} Make any constant arrays passed as arguments into 'static readonly' fields Make any constant arrays passed as arguments into 'static readonly' fields - {Locked="static", "readonly"} + {Locked="static readonly"} Avoid const arrays 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 4a1e1cbbc5..aa4771ab90 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -55,12 +55,12 @@ Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - {Locked="static", "readonly"} + {Locked="static readonly"} Make any constant arrays passed as arguments into 'static readonly' fields Make any constant arrays passed as arguments into 'static readonly' fields - {Locked="static", "readonly"} + {Locked="static readonly"} Avoid const arrays 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 4c0169405d..9d3f29479b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -55,12 +55,12 @@ Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - {Locked="static", "readonly"} + {Locked="static readonly"} Make any constant arrays passed as arguments into 'static readonly' fields Make any constant arrays passed as arguments into 'static readonly' fields - {Locked="static", "readonly"} + {Locked="static readonly"} Avoid const arrays 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 6e8fd40308..66aba87c15 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -55,12 +55,12 @@ Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - {Locked="static", "readonly"} + {Locked="static readonly"} Make any constant arrays passed as arguments into 'static readonly' fields Make any constant arrays passed as arguments into 'static readonly' fields - {Locked="static", "readonly"} + {Locked="static readonly"} Avoid const arrays 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 35d15c0545..f494cffdc0 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -55,12 +55,12 @@ Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - {Locked="static", "readonly"} + {Locked="static readonly"} Make any constant arrays passed as arguments into 'static readonly' fields Make any constant arrays passed as arguments into 'static readonly' fields - {Locked="static", "readonly"} + {Locked="static readonly"} Avoid const arrays 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 d834f2fc24..393d6177e3 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -55,12 +55,12 @@ Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - {Locked="static", "readonly"} + {Locked="static readonly"} Make any constant arrays passed as arguments into 'static readonly' fields Make any constant arrays passed as arguments into 'static readonly' fields - {Locked="static", "readonly"} + {Locked="static readonly"} Avoid const arrays 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 b3c7ac7e95..f0cffb10ee 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -55,12 +55,12 @@ Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - {Locked="static", "readonly"} + {Locked="static readonly"} Make any constant arrays passed as arguments into 'static readonly' fields Make any constant arrays passed as arguments into 'static readonly' fields - {Locked="static", "readonly"} + {Locked="static readonly"} Avoid const arrays 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 aba46edf63..1c5e3c367f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -55,12 +55,12 @@ Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - {Locked="static", "readonly"} + {Locked="static readonly"} Make any constant arrays passed as arguments into 'static readonly' fields Make any constant arrays passed as arguments into 'static readonly' fields - {Locked="static", "readonly"} + {Locked="static readonly"} Avoid const arrays 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 89cd9686ed..35ebaaf560 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 @@ -55,12 +55,12 @@ Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - {Locked="static", "readonly"} + {Locked="static readonly"} Make any constant arrays passed as arguments into 'static readonly' fields Make any constant arrays passed as arguments into 'static readonly' fields - {Locked="static", "readonly"} + {Locked="static readonly"} Avoid const arrays 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 f33254e7fb..f85d64df4e 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -55,12 +55,12 @@ Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - {Locked="static", "readonly"} + {Locked="static readonly"} Make any constant arrays passed as arguments into 'static readonly' fields Make any constant arrays passed as arguments into 'static readonly' fields - {Locked="static", "readonly"} + {Locked="static readonly"} Avoid const arrays 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 25b11531fe..96a02ae5bc 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -55,12 +55,12 @@ Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - {Locked="static", "readonly"} + {Locked="static readonly"} Make any constant arrays passed as arguments into 'static readonly' fields Make any constant arrays passed as arguments into 'static readonly' fields - {Locked="static", "readonly"} + {Locked="static readonly"} Avoid const arrays 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 a5c2e8bd6a..7920969b2a 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 @@ -55,12 +55,12 @@ Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - {Locked="static", "readonly"} + {Locked="static readonly"} Make any constant arrays passed as arguments into 'static readonly' fields Make any constant arrays passed as arguments into 'static readonly' fields - {Locked="static", "readonly"} + {Locked="static readonly"} Avoid const arrays 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 80aabf1ca6..43a3729b10 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 @@ -55,12 +55,12 @@ Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - {Locked="static", "readonly"} + {Locked="static readonly"} Make any constant arrays passed as arguments into 'static readonly' fields Make any constant arrays passed as arguments into 'static readonly' fields - {Locked="static", "readonly"} + {Locked="static readonly"} Avoid const arrays From 86c559edf8d8c8f046608bf7d103a98eeb1251d0 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Tue, 17 Aug 2021 18:40:12 +0000 Subject: [PATCH 04/64] Remove formatting of full document --- .../Runtime/AvoidConstArrays.Fixer.cs | 2 +- .../Runtime/AvoidConstArraysTests.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 6b746ebd50..d25a6d3af6 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -99,7 +99,7 @@ private static async Task ExtractConstArrayAsync(SyntaxNode node, Sema } // Return changed document - return await Formatter.FormatAsync(editor.GetChangedDocument(), cancellationToken: cancellationToken).ConfigureAwait(false); + return editor.GetChangedDocument(); } private static IArrayCreationOperation GetArrayCreationOperation(SyntaxNode node, SemanticModel model, CancellationToken cancellationToken, diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 0d66cef0be..d2b83371a4 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -37,7 +37,7 @@ public void B() public class A { - private static readonly int[] value = new[] { 1, 2, 3 }; + private static readonly int[] value = new[]{ 1, 2, 3 }; public void B() { @@ -62,7 +62,7 @@ public void B() public class A { - private static readonly int[] value = new int[] { 1, 2, 3 }; + private static readonly int[] value = new int[]{ 1, 2, 3 }; public void B() { From aded7e0c60ef234cfb02a55d9c446f676d5eff22 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Tue, 17 Aug 2021 19:23:13 +0000 Subject: [PATCH 05/64] Renamed analyzer --- .../MicrosoftNetCoreAnalyzersResources.resx | 2 +- .../Runtime/AvoidConstArrays.Fixer.cs | 10 ++++------ .../Runtime/AvoidConstArrays.cs | 2 +- .../xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.de.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.es.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.it.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf | 4 ++-- .../xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf | 4 ++-- .../Runtime/AvoidConstArraysTests.cs | 3 --- 17 files changed, 32 insertions(+), 37 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 693ad93d88..fe7c082ebd 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -247,7 +247,7 @@ Avoid unsealed attributes - Avoid const arrays + Avoid constant arrays as arguments Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index d25a6d3af6..112d333c8b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -12,13 +12,12 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Operations; namespace Microsoft.NetCore.Analyzers.Runtime { /// - /// CA1849: Avoid const arrays. Replace with static readonly arrays. + /// CA1849: Avoid constant arrays as arguments. Replace with static readonly arrays. /// [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] public sealed class AvoidConstArraysFixer : CodeFixProvider @@ -51,12 +50,11 @@ private static async Task ExtractConstArrayAsync(SyntaxNode node, Sema SyntaxGenerator generator, ImmutableDictionary properties, CancellationToken cancellationToken) { IArrayCreationOperation arrayArgument = GetArrayCreationOperation(node, model, cancellationToken, out bool isInvoked); - INamedTypeSymbol containingType = model.GetEnclosingSymbol(node.SpanStart, cancellationToken).ContainingType; - IEnumerable typeFields = containingType.GetMembers().Where(x => x is IFieldSymbol); + IEnumerable typeFields = model.GetEnclosingSymbol(node.SpanStart, cancellationToken).ContainingType + .GetMembers().Where(x => x is IFieldSymbol); // Get a valid member name for the extracted constant - IEnumerable memberNames = typeFields.Select(x => x.Name); - string newMemberName = GetExtractedMemberName(memberNames, properties["paramName"]); + string newMemberName = GetExtractedMemberName(typeFields.Select(x => x.Name), properties["paramName"]); // Get method containing the symbol that is being diagnosed. Should always be in a method IOperation? containingMethodBody = arrayArgument.GetAncestor(OperationKind.MethodBody); diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index 213e766a40..de195524a6 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -12,7 +12,7 @@ namespace Microsoft.NetCore.Analyzers.Runtime { /// - /// CA1849: Avoid const arrays. Replace with static readonly arrays. + /// CA1849: Avoid constant arrays as arguments. Replace with static readonly arrays. /// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class AvoidConstArraysAnalyzer : DiagnosticAnalyzer 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 7066fa0cdc..fec0d64608 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -63,8 +63,8 @@ {Locked="static readonly"} - Avoid const arrays - Avoid const arrays + Avoid constant arrays as arguments + Avoid constant arrays as arguments 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 aa4771ab90..71515442cb 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -63,8 +63,8 @@ {Locked="static readonly"} - Avoid const arrays - Avoid const arrays + Avoid constant arrays as arguments + Avoid constant arrays as arguments 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 9d3f29479b..1f9b9ae529 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -63,8 +63,8 @@ {Locked="static readonly"} - Avoid const arrays - Avoid const arrays + Avoid constant arrays as arguments + Avoid constant arrays as arguments 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 66aba87c15..fa91e0b18a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -63,8 +63,8 @@ {Locked="static readonly"} - Avoid const arrays - Avoid const arrays + Avoid constant arrays as arguments + Avoid constant arrays as arguments 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 f494cffdc0..5dabb57038 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -63,8 +63,8 @@ {Locked="static readonly"} - Avoid const arrays - Avoid const arrays + Avoid constant arrays as arguments + Avoid constant arrays as arguments 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 393d6177e3..09f4376360 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -63,8 +63,8 @@ {Locked="static readonly"} - Avoid const arrays - Avoid const arrays + Avoid constant arrays as arguments + Avoid constant arrays as arguments 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 f0cffb10ee..525f0da22e 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -63,8 +63,8 @@ {Locked="static readonly"} - Avoid const arrays - Avoid const arrays + Avoid constant arrays as arguments + Avoid constant arrays as arguments 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 1c5e3c367f..e06ad2331f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -63,8 +63,8 @@ {Locked="static readonly"} - Avoid const arrays - Avoid const arrays + Avoid constant arrays as arguments + Avoid constant arrays as arguments 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 35ebaaf560..8ac9f76366 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 @@ -63,8 +63,8 @@ {Locked="static readonly"} - Avoid const arrays - Avoid const arrays + Avoid constant arrays as arguments + Avoid constant arrays as arguments 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 f85d64df4e..60ce9b961a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -63,8 +63,8 @@ {Locked="static readonly"} - Avoid const arrays - Avoid const arrays + Avoid constant arrays as arguments + Avoid constant arrays as arguments 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 96a02ae5bc..4cc09c6365 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -63,8 +63,8 @@ {Locked="static readonly"} - Avoid const arrays - Avoid const arrays + Avoid constant arrays as arguments + Avoid constant arrays as arguments 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 7920969b2a..eebd63d4c2 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 @@ -63,8 +63,8 @@ {Locked="static readonly"} - Avoid const arrays - Avoid const arrays + Avoid constant arrays as arguments + Avoid constant arrays as arguments 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 43a3729b10..18eb8ae7f4 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 @@ -63,8 +63,8 @@ {Locked="static readonly"} - Avoid const arrays - Avoid const arrays + Avoid constant arrays as arguments + Avoid constant arrays as arguments diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index d2b83371a4..5dbef34d69 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -11,9 +11,6 @@ namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests { - // Analyzer and code fix tests are both in this file - // "Do not separate analyzer tests from code fix tests" - // https://github.com/dotnet/roslyn-analyzers/blob/main/docs/NetCore_GettingStarted.md#definition-of-done public class AvoidConstArraysTests { #region C# Tests From 59e968adc58d60e279aece8749f5abdadf27753e Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Tue, 17 Aug 2021 18:05:43 -0400 Subject: [PATCH 06/64] Combined unit tests --- .../Runtime/AvoidConstArraysTests.cs | 216 ++++++++---------- 1 file changed, 95 insertions(+), 121 deletions(-) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 5dbef34d69..3176c6f8c0 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -13,10 +13,8 @@ namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests { public class AvoidConstArraysTests { - #region C# Tests - [Fact] - public async Task CA1849_CSharp_IdentifyConstArrays() + public async Task IdentifyConstArrays() { // Implicit initialization check await VerifyCS.VerifyCodeFixAsync(@" @@ -41,6 +39,25 @@ public void B() Console.WriteLine(value); } } +"); + + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine({|CA1849:{1, 2, 3}|}) + End Sub +End Class +", @" +Imports System + +Public Class A + Private Shared ReadOnly value As Integer() = {1, 2, 3} + Public Sub B() + Console.WriteLine(value) + End Sub +End Class "); // Explicit initialization check @@ -66,6 +83,25 @@ public void B() Console.WriteLine(value); } } +"); + + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine({|CA1849:New Integer() {1, 2, 3}|}) + End Sub +End Class +", @" +Imports System + +Public Class A + Private Shared ReadOnly value As Integer() = New Integer() {1, 2, 3} + Public Sub B() + Console.WriteLine(value) + End Sub +End Class "); // Nested arguments @@ -91,6 +127,25 @@ public void B() Console.WriteLine(string.Join("" "", value)); } } +"); + + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(String.Join("" ""c, {|CA1849:{""Cake"", ""is"", ""good""}|})) + End Sub +End Class +", @" +Imports System + +Public Class A + Private Shared ReadOnly value As String() = {""Cake"", ""is"", ""good""} + Public Sub B() + Console.WriteLine(String.Join("" ""c, value)) + End Sub +End Class "); // Extension method usage @@ -124,10 +179,8 @@ public void B() } [Fact] - public async Task CA1849_CSharp_NoDiagnostic_IgnoreOtherArgs() + public async Task IgnoreOtherArgs_NoDiagnostic() { - // All code fix tests in this method result in no changes, as the analyzer will ignore these - // A string await VerifyCS.VerifyCodeFixAsync(@" using System; @@ -149,6 +202,24 @@ public void B() Console.WriteLine(""Lorem ipsum""); } } +"); + + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(""Lorem ipsum"") + End Sub +End Class +", @" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(""Lorem ipsum"") + End Sub +End Class "); // Test another type to be extra safe @@ -172,6 +243,24 @@ public void B() Console.WriteLine(123); } } +"); + + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(123) + End Sub +End Class +", @" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(123) + End Sub +End Class "); // Non-literal array @@ -198,120 +287,7 @@ public void B() } } "); - } - - #endregion - - #region Visual Basic Tests - - [Fact] - public async Task CA1849_VisualBasic_IdentifyConstArrays() - { - // Implicit initialization check - await VerifyVB.VerifyCodeFixAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine({|CA1849:{1, 2, 3}|}) - End Sub -End Class -", @" -Imports System - -Public Class A - Private Shared ReadOnly value As Integer() = {1, 2, 3} - Public Sub B() - Console.WriteLine(value) - End Sub -End Class -"); - - // Explicit initialization check - await VerifyVB.VerifyCodeFixAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine({|CA1849:New Integer() {1, 2, 3}|}) - End Sub -End Class -", @" -Imports System - -Public Class A - Private Shared ReadOnly value As Integer() = New Integer() {1, 2, 3} - Public Sub B() - Console.WriteLine(value) - End Sub -End Class -"); - - // Nested arguments - await VerifyVB.VerifyCodeFixAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine(String.Join("" ""c, {|CA1849:{""Cake"", ""is"", ""good""}|})) - End Sub -End Class -", @" -Imports System - -Public Class A - Private Shared ReadOnly value As String() = {""Cake"", ""is"", ""good""} - Public Sub B() - Console.WriteLine(String.Join("" ""c, value)) - End Sub -End Class -"); - } - - [Fact] - public async Task CA1849_VisualBasic_NoDiagnostic_IgnoreOtherArgs() - { - // All code fix tests in this method result in no changes, as the analyzer will ignore these - // A string - await VerifyVB.VerifyCodeFixAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine(""Lorem ipsum"") - End Sub -End Class -", @" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine(""Lorem ipsum"") - End Sub -End Class -"); - - // Test another type to be extra safe - await VerifyVB.VerifyCodeFixAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine(123) - End Sub -End Class -", @" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine(123) - End Sub -End Class -"); - - // Non-literal array await VerifyVB.VerifyCodeFixAsync(@" Imports System @@ -332,7 +308,5 @@ End Sub End Class "); } - - #endregion } } \ No newline at end of file From 39e25264d0480c792a150d22e0d1ce21448ac454 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Tue, 17 Aug 2021 18:29:59 -0400 Subject: [PATCH 07/64] Clean up analyzer literal array evaluation --- .../Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index de195524a6..27f4864bd2 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -75,7 +75,7 @@ public override void Initialize(AnalysisContext context) } // Must be literal array - if (!arrayCreationOperation.Children.First(x => x is IArrayInitializerOperation).Children.All(x => x is ILiteralOperation)) + if (!arrayCreationOperation.Initializer.ElementValues.All(x => x is ILiteralOperation)) { return; } From e416a189b5e2916c2fef48056b8d2c441b71eb9b Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Wed, 18 Aug 2021 10:46:35 -0400 Subject: [PATCH 08/64] Added tests for trivia --- .../Runtime/AvoidConstArraysTests.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 3176c6f8c0..380a7f9f4e 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -146,6 +146,31 @@ Public Sub B() Console.WriteLine(String.Join("" ""c, value)) End Sub End Class +"); + + // Trivia tests + await VerifyCS.VerifyCodeFixAsync(@" +using System; + +public class A +{ + public void B() + { + Console.WriteLine(string.Join("" "", {|CA1849:new[] { ""a"", ""b"" } /* test comment */|})) + } +} +", @" +using System; + +public class A +{ + private static readonly string[] value = new[] { ""a"", ""b"" }; /* test comment */ + + public void B() + { + Console.WriteLine(string.Join("" "", value)); + } +} "); // Extension method usage From fe7abab13367f26422f1925aea09be41bf4aa3df Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Wed, 18 Aug 2021 21:11:27 +0000 Subject: [PATCH 09/64] Tests pass for argument trailing trivia --- .../Runtime/AvoidConstArrays.Fixer.cs | 7 ++++--- .../Runtime/AvoidConstArraysTests.cs | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 112d333c8b..2f811e3c6f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -68,8 +68,8 @@ private static async Task ExtractConstArrayAsync(SyntaxNode node, Sema generator.TypeExpression(arrayArgument.Type), GetAccessibility(model.GetEnclosingSymbol(containingMethodBody!.Syntax.SpanStart, cancellationToken)), DeclarationModifiers.Static | DeclarationModifiers.ReadOnly, - arrayArgument.Syntax - ).FormatForExtraction(containingMethodBody.Syntax); + arrayArgument.Syntax.WithoutTrailingTrivia() // don't include extra trivia before the end of the declaration + ).FormatForExtraction(containingMethodBody.Syntax); // any additional formatting // Add the new extracted member ISymbol lastFieldSymbol = typeFields.LastOrDefault(); @@ -93,7 +93,8 @@ private static async Task ExtractConstArrayAsync(SyntaxNode node, Sema } else { - editor.ReplaceNode(node, generator.Argument(identifier)); + editor.ReplaceNode(node, generator.Argument(identifier) + .WithTrailingTrivia(arrayArgument.Syntax.GetTrailingTrivia())); // add any extra trivia that was after the original argument } // Return changed document diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 380a7f9f4e..f8080b30e2 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -148,7 +148,7 @@ End Sub End Class "); - // Trivia tests + // Trivia tests, CS only await VerifyCS.VerifyCodeFixAsync(@" using System; @@ -156,7 +156,7 @@ public class A { public void B() { - Console.WriteLine(string.Join("" "", {|CA1849:new[] { ""a"", ""b"" } /* test comment */|})) + Console.WriteLine(string.Join("" "", {|CA1849:new[] { ""a"", ""b"" }|} /* test comment */)); } } ", @" @@ -164,11 +164,11 @@ public void B() public class A { - private static readonly string[] value = new[] { ""a"", ""b"" }; /* test comment */ + private static readonly string[] value = new[] { ""a"", ""b"" }; public void B() { - Console.WriteLine(string.Join("" "", value)); + Console.WriteLine(string.Join("" "", value /* test comment */)); } } "); From 038c55e10349b7d0c6fa531a997144f235a3aa7f Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Wed, 18 Aug 2021 21:56:23 +0000 Subject: [PATCH 10/64] Edited headers for analyzer files --- .../Runtime/AvoidConstArrays.Fixer.cs | 2 +- .../Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs | 2 +- .../Runtime/AvoidConstArraysTests.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 2f811e3c6f..5196687851 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. using System; using System.Linq; diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index 27f4864bd2..b725c18ba1 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. using System.Linq; using System.Collections.Generic; diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index f8080b30e2..c2f81a06f2 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. using System.Threading.Tasks; using Xunit; From da49630e9d62809f0ccdd389eeb6dfec661dcbf4 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Thu, 19 Aug 2021 02:17:24 +0000 Subject: [PATCH 11/64] VB extension method tests added, not passing yet --- .../Runtime/AvoidConstArraysTests.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index c2f81a06f2..e8bc0656d2 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -200,6 +200,18 @@ public void B() Console.WriteLine(y); } } +"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System +Imports System.Linq + +Public Class A + Public Sub B() + Dim y As String = {|CA1849:{""a"", ""b"", ""c""}|}.First() + Console.WriteLine(y) + End Sub +End Class "); } From ac3658438e15563cc8b4230260069d3f9d13036c Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Fri, 20 Aug 2021 14:58:32 +0000 Subject: [PATCH 12/64] Tests pass for VB extension method usage --- .../Runtime/AvoidConstArrays.Fixer.cs | 9 +++++- .../Runtime/AvoidConstArrays.cs | 28 ++++++++++++++----- .../Runtime/AvoidConstArraysTests.cs | 15 ++++++++-- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 5196687851..d6739f33f7 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -54,7 +54,7 @@ private static async Task ExtractConstArrayAsync(SyntaxNode node, Sema .GetMembers().Where(x => x is IFieldSymbol); // Get a valid member name for the extracted constant - string newMemberName = GetExtractedMemberName(typeFields.Select(x => x.Name), properties["paramName"]); + string newMemberName = GetExtractedMemberName(typeFields.Select(x => x.Name), properties["paramName"] ?? GetMemberNameFromType(arrayArgument)); // Get method containing the symbol that is being diagnosed. Should always be in a method IOperation? containingMethodBody = arrayArgument.GetAncestor(OperationKind.MethodBody); @@ -143,6 +143,13 @@ private static string GetExtractedMemberName(IEnumerable memberNames, st return parameterName; } + private static string GetMemberNameFromType(IArrayCreationOperation arrayCreationOperation) + { +#pragma warning disable CA1308 + return ((IArrayTypeSymbol)arrayCreationOperation.Type).ElementType.Name.ToLowerInvariant() + "Array"; +#pragma warning restore CA1308 + } + private static Accessibility GetAccessibility(ISymbol originMethodSymbol) { if (Enum.TryParse(originMethodSymbol.GetResultantVisibility().ToString(), out Accessibility accessibility)) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index b725c18ba1..a1978206ec 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -54,20 +54,34 @@ public override void Initialize(AnalysisContext context) } else if (operationContext.Operation is IInvocationOperation invocationOperation) // For arrays passed in extension methods, like in LINQ { - if (invocationOperation.Descendants().Any(x => x is IArrayCreationOperation)) + if (invocationOperation.Descendants().Any(x => x is IArrayCreationOperation) + && invocationOperation.Descendants().Any(x => x is IArgumentOperation)) { - // This is an invocation that contains an array in it + // This is an invocation that contains an array as an argument // This will get caught by the first case in another cycle return; } argumentOperation = invocationOperation.Arguments.FirstOrDefault(); - if (argumentOperation is null || argumentOperation.Children.First() is not IConversionOperation conversionOperation - || conversionOperation.Operand is not IArrayCreationOperation) + if (argumentOperation is not null) { - return; + if (argumentOperation.Children.First() is not IConversionOperation conversionOperation + || conversionOperation.Operand is not IArrayCreationOperation arrayCreation) + { + return; + } + arrayCreationOperation = arrayCreation; + } + else // An invocation, extension or regular, has an argument, unless it's a VB extension method call + { + // For VB extension method invocations, find a matching child + arrayCreationOperation = (IArrayCreationOperation)invocationOperation.Descendants() + .FirstOrDefault(x => x is IArrayCreationOperation); + if (arrayCreationOperation is null) + { + return; + } } - arrayCreationOperation = (IArrayCreationOperation)conversionOperation.Operand; } else { @@ -82,7 +96,7 @@ public override void Initialize(AnalysisContext context) Dictionary properties = new() { - { "paramName", argumentOperation.Parameter.Name } + { "paramName", argumentOperation is not null ? argumentOperation.Parameter.Name : null } }; operationContext.ReportDiagnostic(arrayCreationOperation.CreateDiagnostic(Rule, properties.ToImmutableDictionary())); diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index e8bc0656d2..0278bfd608 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -148,7 +148,7 @@ End Sub End Class "); - // Trivia tests, CS only + // Trivia test, CS only await VerifyCS.VerifyCodeFixAsync(@" using System; @@ -202,7 +202,7 @@ public void B() } "); - await VerifyVB.VerifyAnalyzerAsync(@" + await VerifyVB.VerifyCodeFixAsync(@" Imports System Imports System.Linq @@ -212,6 +212,17 @@ Public Sub B() Console.WriteLine(y) End Sub End Class +", @" +Imports System +Imports System.Linq + +Public Class A + Private Shared ReadOnly stringArray As String() = {""a"", ""b"", ""c""} + Public Sub B() + Dim y As String = stringArray.First() + Console.WriteLine(y) + End Sub +End Class "); } From c03ce9a116c140520453d3c83e8afd493f8a718d Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Fri, 20 Aug 2021 16:07:51 +0000 Subject: [PATCH 13/64] Added tests for extractions --- .../Runtime/AvoidConstArrays.Fixer.cs | 2 +- .../Runtime/AvoidConstArraysTests.cs | 65 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index d6739f33f7..d62a2f3078 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -77,7 +77,7 @@ private static async Task ExtractConstArrayAsync(SyntaxNode node, Sema { // Insert after last field if any fields are present SyntaxNode lastFieldNode = await lastFieldSymbol.DeclaringSyntaxReferences.First().GetSyntaxAsync(cancellationToken).ConfigureAwait(false); - editor.InsertAfter(lastFieldNode, newMember); + editor.InsertAfter(generator.GetDeclaration(lastFieldNode), newMember); } else { diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 0278bfd608..5b1dc676c0 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -223,6 +223,71 @@ Public Sub B() Console.WriteLine(y) End Sub End Class +"); + + // Member extraction tests + await VerifyCS.VerifyCodeFixAsync(@" +using System; + +public class A +{ + private static readonly string value = ""hello""; + private static readonly int[] valueArray = new[]{ -2, -1, 0 }; + private static readonly bool[] valueArray1 = new[]{ true, false, true }; + + private static readonly int x = 1; + + public void B() + { + Console.WriteLine({|CA1849:new[]{ 1, 2, 3 }|}); + } +} +", @" +using System; + +public class A +{ + private static readonly string value = ""hello""; + private static readonly int[] valueArray = new[]{ -2, -1, 0 }; + private static readonly bool[] valueArray1 = new[]{ true, false, true }; + + private static readonly int x = 1; + private static readonly int[] valueArray0 = new[]{ 1, 2, 3 }; + + public void B() + { + Console.WriteLine(valueArray0); + } +} +"); + + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Private Shared ReadOnly value As String = ""hello"" + Private Shared ReadOnly valueArray As Integer() = {-2, -1, 0} + Private Shared ReadOnly valueArray1 As Boolean() = {True, False, True} + Private Shared ReadOnly x As Integer = 1 + + Public Sub B() + Console.WriteLine({|CA1849:{1, 2, 3}|}) + End Sub +End Class +", @" +Imports System + +Public Class A + Private Shared ReadOnly value As String = ""hello"" + Private Shared ReadOnly valueArray As Integer() = {-2, -1, 0} + Private Shared ReadOnly valueArray1 As Boolean() = {True, False, True} + Private Shared ReadOnly x As Integer = 1 + Private Shared ReadOnly valueArray0 As Integer() = {1, 2, 3} + + Public Sub B() + Console.WriteLine(valueArray0) + End Sub +End Class "); } From 5b2c843b8c577b1999107f0eae6dff0fd8dc3d5e Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Fri, 20 Aug 2021 12:51:20 -0700 Subject: [PATCH 14/64] Run dotnet msbuild /t:Pack --- .../Microsoft.CodeAnalysis.NetAnalyzers.md | 12 +++++++++++ .../Microsoft.CodeAnalysis.NetAnalyzers.sarif | 20 +++++++++++++++++++ src/NetAnalyzers/RulesMissingDocumentation.md | 1 + 3 files changed, 33 insertions(+) diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md index 4dc610af1a..d4d3e8114e 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md @@ -1452,6 +1452,18 @@ For improved performance, use the LoggerMessage delegates. |CodeFix|False| --- +## [CA1849](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1849): Avoid constant arrays as arguments + +Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Info| +|CodeFix|True| +--- + ## [CA2000](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2000): Dispose objects before losing scope If a disposable object is not explicitly disposed before all references to it are out of scope, the object will be disposed at some indeterminate time when the garbage collector runs the finalizer of the object. Because an exceptional event might occur that will prevent the finalizer of the object from running, the object should be explicitly disposed instead. diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif index ee4caf0c0a..90e5dae750 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif @@ -2701,6 +2701,26 @@ ] } }, + "CA1849": { + "id": "CA1849", + "shortDescription": "Avoid constant arrays as arguments", + "fullDescription": "Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'.", + "defaultLevel": "note", + "helpUri": "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1849", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "AvoidConstArraysAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry", + "EnabledRuleInAggressiveMode" + ] + } + }, "CA2000": { "id": "CA2000", "shortDescription": "Dispose objects before losing scope", diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md index aff82e3e0c..dc46c206a1 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -10,6 +10,7 @@ CA1840 | | Do not use 'WhenAll' with a single task | CA1843 | | Do not use 'WaitAll' with a single task | CA1848 | | Use the LoggerMessage delegates | +CA1849 | | Avoid constant arrays as arguments | CA2017 | | Parameter count mismatch | CA2252 | | This API requires opting into preview features | CA2253 | | Named placeholders should not be numeric values | From cfdd1766f256fb8ab4b1f712860ea4268da2987a Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Sun, 22 Aug 2021 19:54:55 +0000 Subject: [PATCH 15/64] Added checks for ReadOnlySpan types --- .../Runtime/AvoidConstArrays.cs | 10 +++ .../Runtime/AvoidConstArraysTests.cs | 83 +++++-------------- 2 files changed, 32 insertions(+), 61 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index a1978206ec..becca3ee92 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -23,6 +23,8 @@ public sealed class AvoidConstArraysAnalyzer : DiagnosticAnalyzer private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysDescription), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); + private const string s_readonlySpanTypeName = WellKnownTypeNames.SystemReadOnlySpan1; + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, s_localizableMessage, @@ -43,6 +45,8 @@ public override void Initialize(AnalysisContext context) context.RegisterOperationAction(operationContext => { IArgumentOperation? argumentOperation; + string readonlySpanTypeString = WellKnownTypeProvider.GetOrCreate(operationContext.Compilation) + .GetOrCreateTypeByMetadataName(s_readonlySpanTypeName)!.Name; if (operationContext.Operation is IArrayCreationOperation arrayCreationOperation) // For arrays passed as arguments { @@ -88,6 +92,12 @@ public override void Initialize(AnalysisContext context) return; } + // Can't be a ReadOnlySpan, as those are already optimized + if (argumentOperation is not null && argumentOperation.Parameter.Type.Name == readonlySpanTypeString) + { + return; + } + // Must be literal array if (!arrayCreationOperation.Initializer.ElementValues.All(x => x is ILiteralOperation)) { diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 5b1dc676c0..cc5f5781a5 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -295,17 +295,7 @@ End Class public async Task IgnoreOtherArgs_NoDiagnostic() { // A string - await VerifyCS.VerifyCodeFixAsync(@" -using System; - -public class A -{ - public void B() - { - Console.WriteLine(""Lorem ipsum""); - } -} -", @" + await VerifyCS.VerifyAnalyzerAsync(@" using System; public class A @@ -317,15 +307,7 @@ public void B() } "); - await VerifyVB.VerifyCodeFixAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine(""Lorem ipsum"") - End Sub -End Class -", @" + await VerifyVB.VerifyAnalyzerAsync(@" Imports System Public Class A @@ -336,17 +318,7 @@ End Class "); // Test another type to be extra safe - await VerifyCS.VerifyCodeFixAsync(@" -using System; - -public class A -{ - public void B() - { - Console.WriteLine(123); - } -} -", @" + await VerifyCS.VerifyAnalyzerAsync(@" using System; public class A @@ -358,15 +330,7 @@ public void B() } "); - await VerifyVB.VerifyCodeFixAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine(123) - End Sub -End Class -", @" + await VerifyVB.VerifyAnalyzerAsync(@" Imports System Public Class A @@ -377,18 +341,7 @@ End Class "); // Non-literal array - await VerifyCS.VerifyCodeFixAsync(@" -using System; - -public class A -{ - public void B() - { - string str = ""Lorem ipsum""; - Console.WriteLine(new[] { str }); - } -} -", @" + await VerifyCS.VerifyAnalyzerAsync(@" using System; public class A @@ -401,7 +354,7 @@ public void B() } "); - await VerifyVB.VerifyCodeFixAsync(@" + await VerifyVB.VerifyAnalyzerAsync(@" Imports System Public Class A @@ -410,15 +363,23 @@ Public Sub B() Console.WriteLine({ str }) End Sub End Class -", @" -Imports System +"); -Public Class A - Public Sub B() - Dim str As String = ""Lorem ipsum"" - Console.WriteLine({ str }) - End Sub -End Class + // A ReadOnlySpan, which is already optimized + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class A +{ + public void B() + { + C(new bool[] { true, false }); + } + + private void C(ReadOnlySpan span) + { + } +} "); } } From 97e926193308110d109fb6d272ca3f409053a82f Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Wed, 25 Aug 2021 13:08:13 -0400 Subject: [PATCH 16/64] Added comments for warning suppression --- .../Runtime/AvoidConstArrays.Fixer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index d62a2f3078..5bd173a642 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -145,9 +145,9 @@ private static string GetExtractedMemberName(IEnumerable memberNames, st private static string GetMemberNameFromType(IArrayCreationOperation arrayCreationOperation) { -#pragma warning disable CA1308 +#pragma warning disable CA1308 // Normalize strings to uppercase return ((IArrayTypeSymbol)arrayCreationOperation.Type).ElementType.Name.ToLowerInvariant() + "Array"; -#pragma warning restore CA1308 +#pragma warning restore CA1308 // Normalize strings to uppercase } private static Accessibility GetAccessibility(ISymbol originMethodSymbol) From 317ed229be2384bda396f4280c09c9650f033ff5 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Wed, 25 Aug 2021 17:33:49 -0400 Subject: [PATCH 17/64] Clean up parameter name property assignment --- .../Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index becca3ee92..d7f28865ee 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -106,7 +106,7 @@ public override void Initialize(AnalysisContext context) Dictionary properties = new() { - { "paramName", argumentOperation is not null ? argumentOperation.Parameter.Name : null } + { "paramName", argumentOperation?.Parameter?.Name } }; operationContext.ReportDiagnostic(arrayCreationOperation.CreateDiagnostic(Rule, properties.ToImmutableDictionary())); From 124afdcbb69d45e95794c8f289d3fc1f44c26518 Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Thu, 2 Sep 2021 00:15:30 +0000 Subject: [PATCH 18/64] Update analyzer name in shipment doc --- src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index 98196ef8dd..313b1c7e15 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -4,4 +4,4 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- -CA1848 | Performance | Info | AvoidConstArrays, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1848) +CA1849 | Performance | Info | AvoidConstArrays, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1849) From b606194d146b439394c8280950a0cd1892814d96 Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Wed, 8 Sep 2021 21:29:48 +0000 Subject: [PATCH 19/64] Use symbol equality for ReadOnlySpan type checks --- .../Runtime/AvoidConstArrays.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index d7f28865ee..06b8bf99cc 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -23,8 +23,6 @@ public sealed class AvoidConstArraysAnalyzer : DiagnosticAnalyzer private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysDescription), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); - private const string s_readonlySpanTypeName = WellKnownTypeNames.SystemReadOnlySpan1; - internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, s_localizableMessage, @@ -45,8 +43,8 @@ public override void Initialize(AnalysisContext context) context.RegisterOperationAction(operationContext => { IArgumentOperation? argumentOperation; - string readonlySpanTypeString = WellKnownTypeProvider.GetOrCreate(operationContext.Compilation) - .GetOrCreateTypeByMetadataName(s_readonlySpanTypeName)!.Name; + INamedTypeSymbol readonlySpanType = WellKnownTypeProvider.GetOrCreate(operationContext.Compilation) + .GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReadOnlySpan1)!; if (operationContext.Operation is IArrayCreationOperation arrayCreationOperation) // For arrays passed as arguments { @@ -93,7 +91,7 @@ public override void Initialize(AnalysisContext context) } // Can't be a ReadOnlySpan, as those are already optimized - if (argumentOperation is not null && argumentOperation.Parameter.Type.Name == readonlySpanTypeString) + if (argumentOperation is not null && argumentOperation.Parameter.Type.OriginalDefinition.Equals(readonlySpanType)) { return; } From a74af2942db9e90076e6dfbc5d968e847ff2a206 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Fri, 17 Sep 2021 18:33:17 -0400 Subject: [PATCH 20/64] Added comments for better readability --- .../Runtime/AvoidConstArrays.Fixer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 5bd173a642..cdf3343765 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -104,11 +104,10 @@ private static async Task ExtractConstArrayAsync(SyntaxNode node, Sema private static IArrayCreationOperation GetArrayCreationOperation(SyntaxNode node, SemanticModel model, CancellationToken cancellationToken, out bool isInvoked) { - // The analyzer only passes a diagnostic for two scenarios: + // The analyzer only passes a diagnostic for two scenarios, each having an IArrayCreationOperation: // 1. The node is an IArgumentOperation that is a direct parent of an IArrayCreationOperation // 2. The node is an IArrayCreationOperation already, as it was pulled from an // invocation, like with LINQ extension methods - // Therefore, casting to IArrayCreationOperation in this case is safe // If this is a LINQ invocation, the node is already an IArrayCreationOperation if (model.GetOperation(node, cancellationToken) is IArrayCreationOperation arrayCreation) @@ -116,6 +115,7 @@ private static IArrayCreationOperation GetArrayCreationOperation(SyntaxNode node isInvoked = true; return arrayCreation; } + // Otherwise, we'll get the IArrayCreationOperation from the argument node's child isInvoked = false; return (IArrayCreationOperation)model.GetOperation(node.ChildNodes().First(), cancellationToken); } From cd3b32f0ad198ffddc09f885147f762a6e47d913 Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Tue, 21 Sep 2021 21:27:26 +0000 Subject: [PATCH 21/64] Goodbye to the '49er --- .../Runtime/AvoidConstArrays.Fixer.cs | 2 +- .../Runtime/AvoidConstArrays.cs | 4 ++-- .../Runtime/AvoidConstArraysTests.cs | 22 +++++++++---------- .../DiagnosticCategoryAndIdRanges.txt | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index cdf3343765..8903eb206c 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -17,7 +17,7 @@ namespace Microsoft.NetCore.Analyzers.Runtime { /// - /// CA1849: Avoid constant arrays as arguments. Replace with static readonly arrays. + /// CA1850: Avoid constant arrays as arguments. Replace with static readonly arrays. /// [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] public sealed class AvoidConstArraysFixer : CodeFixProvider diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index 06b8bf99cc..d83480ef15 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -12,12 +12,12 @@ namespace Microsoft.NetCore.Analyzers.Runtime { /// - /// CA1849: Avoid constant arrays as arguments. Replace with static readonly arrays. + /// CA1850: Avoid constant arrays as arguments. Replace with static readonly arrays. /// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class AvoidConstArraysAnalyzer : DiagnosticAnalyzer { - internal const string RuleId = "CA1849"; + internal const string RuleId = "CA1850"; private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index cc5f5781a5..66d71f2010 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -24,7 +24,7 @@ public class A { public void B() { - Console.WriteLine({|CA1849:new[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1850:new[]{ 1, 2, 3 }|}); } } ", @" @@ -46,7 +46,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine({|CA1849:{1, 2, 3}|}) + Console.WriteLine({|CA1850:{1, 2, 3}|}) End Sub End Class ", @" @@ -68,7 +68,7 @@ public class A { public void B() { - Console.WriteLine({|CA1849:new int[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1850:new int[]{ 1, 2, 3 }|}); } } ", @" @@ -90,7 +90,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine({|CA1849:New Integer() {1, 2, 3}|}) + Console.WriteLine({|CA1850:New Integer() {1, 2, 3}|}) End Sub End Class ", @" @@ -112,7 +112,7 @@ public class A { public void B() { - Console.WriteLine(string.Join("" "", {|CA1849:new[] { ""Cake"", ""is"", ""good"" }|})); + Console.WriteLine(string.Join("" "", {|CA1850:new[] { ""Cake"", ""is"", ""good"" }|})); } } ", @" @@ -134,7 +134,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine(String.Join("" ""c, {|CA1849:{""Cake"", ""is"", ""good""}|})) + Console.WriteLine(String.Join("" ""c, {|CA1850:{""Cake"", ""is"", ""good""}|})) End Sub End Class ", @" @@ -156,7 +156,7 @@ public class A { public void B() { - Console.WriteLine(string.Join("" "", {|CA1849:new[] { ""a"", ""b"" }|} /* test comment */)); + Console.WriteLine(string.Join("" "", {|CA1850:new[] { ""a"", ""b"" }|} /* test comment */)); } } ", @" @@ -182,7 +182,7 @@ public class A { public void B() { - string y = {|CA1849:new[] { ""a"", ""b"", ""c"" }|}.First(); + string y = {|CA1850:new[] { ""a"", ""b"", ""c"" }|}.First(); Console.WriteLine(y); } } @@ -208,7 +208,7 @@ Imports System.Linq Public Class A Public Sub B() - Dim y As String = {|CA1849:{""a"", ""b"", ""c""}|}.First() + Dim y As String = {|CA1850:{""a"", ""b"", ""c""}|}.First() Console.WriteLine(y) End Sub End Class @@ -239,7 +239,7 @@ public class A public void B() { - Console.WriteLine({|CA1849:new[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1850:new[]{ 1, 2, 3 }|}); } } ", @" @@ -271,7 +271,7 @@ Private Shared ReadOnly valueArray1 As Boolean() = {True, False, True} Private Shared ReadOnly x As Integer = 1 Public Sub B() - Console.WriteLine({|CA1849:{1, 2, 3}|}) + Console.WriteLine({|CA1850:{1, 2, 3}|}) End Sub End Class ", @" diff --git a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt index bc7985a909..a94aba62d9 100644 --- a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt +++ b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt @@ -12,7 +12,7 @@ Design: CA2210, CA1000-CA1070 Globalization: CA2101, CA1300-CA1310 Mobility: CA1600-CA1601 -Performance: HA, CA1800-CA1849 +Performance: HA, CA1800-CA1850 Security: CA2100-CA2153, CA2300-CA2330, CA3000-CA3147, CA5300-CA5405 Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2258 Naming: CA1700-CA1727 From aa345654dce0ca496714a70305cc12090fc1646a Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Tue, 21 Sep 2021 21:57:33 +0000 Subject: [PATCH 22/64] Addressed issues --- .../Runtime/AvoidConstArrays.Fixer.cs | 5 +- .../Runtime/AvoidConstArrays.cs | 121 ++++++++++-------- 2 files changed, 68 insertions(+), 58 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 8903eb206c..675a1155b5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -38,11 +38,12 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) DocumentEditor editor = await DocumentEditor.CreateAsync(document, context.CancellationToken).ConfigureAwait(false); SyntaxGenerator generator = editor.Generator; + string title = MicrosoftNetCoreAnalyzersResources.AvoidConstArraysTitle; context.RegisterCodeFix( new MyCodeAction( - MicrosoftNetCoreAnalyzersResources.AvoidConstArraysTitle, + title, async c => await ExtractConstArrayAsync(node, model, editor, generator, context.Diagnostics.First().Properties, c).ConfigureAwait(false), - equivalenceKey: nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysTitle)), + equivalenceKey: title), context.Diagnostics); } diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index d83480ef15..f4ecd7c796 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -11,6 +11,8 @@ namespace Microsoft.NetCore.Analyzers.Runtime { + using static MicrosoftNetCoreAnalyzersResources; + /// /// CA1850: Avoid constant arrays as arguments. Replace with static readonly arrays. /// @@ -19,9 +21,9 @@ public sealed class AvoidConstArraysAnalyzer : DiagnosticAnalyzer { internal const string RuleId = "CA1850"; - private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); - private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); - private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysDescription), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources)); + private static readonly LocalizableString s_localizableTitle = CreateLocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysTitle)); + private static readonly LocalizableString s_localizableMessage = CreateLocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysMessage)); + private static readonly LocalizableString s_localizableDescription = CreateLocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysDescription)); internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(RuleId, s_localizableTitle, @@ -39,78 +41,85 @@ public override void Initialize(AnalysisContext context) context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - // Analyzes an argument operation - context.RegisterOperationAction(operationContext => + context.RegisterCompilationStartAction(compilationContext => { - IArgumentOperation? argumentOperation; - INamedTypeSymbol readonlySpanType = WellKnownTypeProvider.GetOrCreate(operationContext.Compilation) - .GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReadOnlySpan1)!; - - if (operationContext.Operation is IArrayCreationOperation arrayCreationOperation) // For arrays passed as arguments + INamedTypeSymbol? readonlySpanType = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReadOnlySpan1); + if (readonlySpanType is null) { - argumentOperation = arrayCreationOperation.GetAncestor(OperationKind.Argument); - if (argumentOperation is null) - { - return; - } + return; } - else if (operationContext.Operation is IInvocationOperation invocationOperation) // For arrays passed in extension methods, like in LINQ + + // Analyzes an argument operation + compilationContext.RegisterOperationAction(operationContext => { - if (invocationOperation.Descendants().Any(x => x is IArrayCreationOperation) - && invocationOperation.Descendants().Any(x => x is IArgumentOperation)) - { - // This is an invocation that contains an array as an argument - // This will get caught by the first case in another cycle - return; - } + IArgumentOperation? argumentOperation; - argumentOperation = invocationOperation.Arguments.FirstOrDefault(); - if (argumentOperation is not null) + if (operationContext.Operation is IArrayCreationOperation arrayCreationOperation) // For arrays passed as arguments { - if (argumentOperation.Children.First() is not IConversionOperation conversionOperation - || conversionOperation.Operand is not IArrayCreationOperation arrayCreation) + argumentOperation = arrayCreationOperation.GetAncestor(OperationKind.Argument); + if (argumentOperation is null) { return; } - arrayCreationOperation = arrayCreation; } - else // An invocation, extension or regular, has an argument, unless it's a VB extension method call + else if (operationContext.Operation is IInvocationOperation invocationOperation) // For arrays passed in extension methods, like in LINQ { - // For VB extension method invocations, find a matching child - arrayCreationOperation = (IArrayCreationOperation)invocationOperation.Descendants() - .FirstOrDefault(x => x is IArrayCreationOperation); - if (arrayCreationOperation is null) + if (invocationOperation.Descendants().Any(x => x is IArrayCreationOperation) + && invocationOperation.Descendants().Any(x => x is IArgumentOperation)) { + // This is an invocation that contains an array as an argument + // This will get caught by the first case in another cycle return; } + + argumentOperation = invocationOperation.Arguments.FirstOrDefault(); + if (argumentOperation is not null) + { + if (argumentOperation.Children.First() is not IConversionOperation conversionOperation + || conversionOperation.Operand is not IArrayCreationOperation arrayCreation) + { + return; + } + arrayCreationOperation = arrayCreation; + } + else // An invocation, extension or regular, has an argument, unless it's a VB extension method call + { + // For VB extension method invocations, find a matching child + arrayCreationOperation = (IArrayCreationOperation)invocationOperation.Descendants() + .FirstOrDefault(x => x is IArrayCreationOperation); + if (arrayCreationOperation is null) + { + return; + } + } + } + else + { + return; } - } - else - { - return; - } - // Can't be a ReadOnlySpan, as those are already optimized - if (argumentOperation is not null && argumentOperation.Parameter.Type.OriginalDefinition.Equals(readonlySpanType)) - { - return; - } + // Can't be a ReadOnlySpan, as those are already optimized + if (argumentOperation is not null && argumentOperation.Parameter.Type.OriginalDefinition.Equals(readonlySpanType)) + { + return; + } - // Must be literal array - if (!arrayCreationOperation.Initializer.ElementValues.All(x => x is ILiteralOperation)) - { - return; - } + // Must be literal array + if (!arrayCreationOperation.Initializer.ElementValues.All(x => x is ILiteralOperation)) + { + return; + } - Dictionary properties = new() - { - { "paramName", argumentOperation?.Parameter?.Name } - }; + Dictionary properties = new() + { + { "paramName", argumentOperation?.Parameter?.Name } + }; - operationContext.ReportDiagnostic(arrayCreationOperation.CreateDiagnostic(Rule, properties.ToImmutableDictionary())); - }, - OperationKind.ArrayCreation, - OperationKind.Invocation); + operationContext.ReportDiagnostic(arrayCreationOperation.CreateDiagnostic(Rule, properties.ToImmutableDictionary())); + }, + OperationKind.ArrayCreation, + OperationKind.Invocation); + }); } } } \ No newline at end of file From 89d8e3ddd6d4c534e26dfd2d7dac7ab96be8c0f5 Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Sun, 10 Oct 2021 17:51:01 +0000 Subject: [PATCH 23/64] Addressed style according to #5406 --- .../Runtime/AvoidConstArrays.Fixer.cs | 2 +- .../Runtime/AvoidConstArrays.cs | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 675a1155b5..0049096e42 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -22,7 +22,7 @@ namespace Microsoft.NetCore.Analyzers.Runtime [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] public sealed class AvoidConstArraysFixer : CodeFixProvider { - public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(AvoidConstArraysAnalyzer.RuleId); + public sealed override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(AvoidConstArraysAnalyzer.RuleId); private static readonly ImmutableArray s_collectionMemberEndings = ImmutableArray.Create("array", "collection", "enumerable", "list"); diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index f4ecd7c796..3b6ae239a3 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -21,20 +21,16 @@ public sealed class AvoidConstArraysAnalyzer : DiagnosticAnalyzer { internal const string RuleId = "CA1850"; - private static readonly LocalizableString s_localizableTitle = CreateLocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysTitle)); - private static readonly LocalizableString s_localizableMessage = CreateLocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysMessage)); - private static readonly LocalizableString s_localizableDescription = CreateLocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysDescription)); - internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(RuleId, - s_localizableTitle, - s_localizableMessage, + CreateLocalizableResourceString(AvoidConstArraysTitle), + CreateLocalizableResourceString(AvoidConstArraysMessage), DiagnosticCategory.Performance, RuleLevel.IdeSuggestion, - s_localizableDescription, + CreateLocalizableResourceString(AvoidConstArraysDescription), isPortedFxCopRule: false, isDataflowRule: false); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { From b882ffb5d20381a063d2681ec7e8cda9af848716 Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Sun, 10 Oct 2021 18:12:55 +0000 Subject: [PATCH 24/64] Added test case, fixed formatting --- .../Runtime/AvoidConstArrays.cs | 2 +- .../Runtime/AvoidConstArraysTests.cs | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index 3b6ae239a3..076ef1ad1b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -30,7 +30,7 @@ public sealed class AvoidConstArraysAnalyzer : DiagnosticAnalyzer isPortedFxCopRule: false, isDataflowRule: false); - public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 66d71f2010..598b0a3c8a 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -380,6 +380,21 @@ private void C(ReadOnlySpan span) { } } +"); + + // A lambda with an array creation + await VerifyCS.VerifyAnalyzerAsync(@" +using System; +using System.Linq; + +public class A +{ + public void B() + { + var x = new string[] { ""a"", ""b"" }; + var y = x.Select(z => new[] { z, ""c"" }); // [ac, bc] + } +} "); } } From 92ad53b2635b8a1798c5948e91bc6412c4611c9c Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Mon, 11 Oct 2021 12:09:53 -0400 Subject: [PATCH 25/64] Update test case --- .../Runtime/AvoidConstArraysTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 598b0a3c8a..0a051094dc 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -392,10 +392,10 @@ public class A public void B() { var x = new string[] { ""a"", ""b"" }; - var y = x.Select(z => new[] { z, ""c"" }); // [ac, bc] + var y = x.Select(z => new[] { ""c"" }); } } "); } } -} \ No newline at end of file +} From 1acb9d7748664e0337cdf15f6a661c9d29b09251 Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Tue, 12 Oct 2021 21:51:00 +0000 Subject: [PATCH 26/64] Ran msbuild --- src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md | 4 ++-- src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif | 4 ++-- src/NetAnalyzers/RulesMissingDocumentation.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md index e1f6274d93..3d96f77417 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md @@ -1464,9 +1464,9 @@ When inside a Task-returning method, use the async version of methods, if they e |CodeFix|False| --- -## [CA1850](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1850): Avoid constant arrays as arguments +## [CA1850](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1850): + -Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. |Item|Value| |-|-| diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif index 9c7baf0223..8ee30708f0 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif @@ -2723,8 +2723,8 @@ }, "CA1850": { "id": "CA1850", - "shortDescription": "Avoid constant arrays as arguments", - "fullDescription": "Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'.", + "shortDescription": "", + "fullDescription": "", "defaultLevel": "note", "helpUri": "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1850", "properties": { diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md index 1a6e6bd0da..eae3f34419 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -9,7 +9,7 @@ CA1840 | | Do not use 'WhenAll' with a single task | CA1843 | | Do not use 'WaitAll' with a single task | CA1848 | | Use the LoggerMessage delegates | -CA1850 | | Avoid constant arrays as arguments | +CA1850 | | | CA2017 | | Parameter count mismatch | CA2253 | | Named placeholders should not be numeric values | CA2254 | | Template should be a static expression | From f60659455e638c664e34579da23cc95ba39b570e Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Tue, 12 Oct 2021 22:37:18 +0000 Subject: [PATCH 27/64] Updated absolute .All check with .Any check --- .../Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index 076ef1ad1b..c175015899 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -101,7 +101,7 @@ public override void Initialize(AnalysisContext context) } // Must be literal array - if (!arrayCreationOperation.Initializer.ElementValues.All(x => x is ILiteralOperation)) + if (arrayCreationOperation.Initializer.ElementValues.Any(x => x is not ILiteralOperation)) { return; } From 461e0ef8b4a5c24293acf7426e413c4020f667c8 Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Tue, 12 Oct 2021 22:41:50 +0000 Subject: [PATCH 28/64] Member name from type reflects original definition --- .../Runtime/AvoidConstArrays.Fixer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 0049096e42..1298e5f28c 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -147,7 +147,7 @@ private static string GetExtractedMemberName(IEnumerable memberNames, st private static string GetMemberNameFromType(IArrayCreationOperation arrayCreationOperation) { #pragma warning disable CA1308 // Normalize strings to uppercase - return ((IArrayTypeSymbol)arrayCreationOperation.Type).ElementType.Name.ToLowerInvariant() + "Array"; + return ((IArrayTypeSymbol)arrayCreationOperation.Type).ElementType.OriginalDefinition.Name.ToLowerInvariant() + "Array"; #pragma warning restore CA1308 // Normalize strings to uppercase } From 274733cdbd0a89b261911a9635ed8b0cf7f83d56 Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Wed, 13 Oct 2021 02:02:33 +0000 Subject: [PATCH 29/64] Tests pass for lambda arguments --- .../Runtime/AvoidConstArrays.cs | 15 +++- .../Runtime/AvoidConstArraysTests.cs | 73 +++++++++++++++---- 2 files changed, 71 insertions(+), 17 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index c175015899..ae64803f70 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -40,7 +40,9 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationStartAction(compilationContext => { INamedTypeSymbol? readonlySpanType = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReadOnlySpan1); - if (readonlySpanType is null) + INamedTypeSymbol? functionType = compilationContext.Compilation.GetOrCreateTypeByMetadataName("System.Func`2"); + INamedTypeSymbol? actionType = compilationContext.Compilation.GetOrCreateTypeByMetadataName("System.Action`1"); + if (readonlySpanType is null || functionType is null || actionType is null) { return; } @@ -48,6 +50,7 @@ public override void Initialize(AnalysisContext context) // Analyzes an argument operation compilationContext.RegisterOperationAction(operationContext => { + bool isDirectlyInsideLambda = false; IArgumentOperation? argumentOperation; if (operationContext.Operation is IArrayCreationOperation arrayCreationOperation) // For arrays passed as arguments @@ -94,6 +97,14 @@ public override void Initialize(AnalysisContext context) return; } + // Check if the parameter is a function or action so the name can be set to null + // If the argument found is a function or action, the paramter name doesn't reflect the array creation + if (argumentOperation is not null) + { + ITypeSymbol originalTypeDefinition = argumentOperation.Parameter.Type.OriginalDefinition; + isDirectlyInsideLambda = originalTypeDefinition.Equals(functionType) || originalTypeDefinition.Equals(actionType); + } + // Can't be a ReadOnlySpan, as those are already optimized if (argumentOperation is not null && argumentOperation.Parameter.Type.OriginalDefinition.Equals(readonlySpanType)) { @@ -108,7 +119,7 @@ public override void Initialize(AnalysisContext context) Dictionary properties = new() { - { "paramName", argumentOperation?.Parameter?.Name } + { "paramName", isDirectlyInsideLambda ? null : argumentOperation?.Parameter?.Name } }; operationContext.ReportDiagnostic(arrayCreationOperation.CreateDiagnostic(Rule, properties.ToImmutableDictionary())); diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 0a051094dc..95b74c4463 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -171,6 +171,64 @@ public void B() Console.WriteLine(string.Join("" "", value /* test comment */)); } } +"); + + // A lambda with an array creation + await VerifyCS.VerifyCodeFixAsync(@" +using System; +using System.Linq; + +public class A +{ + public void B() + { + var x = new string[] { ""a"", ""b"" }; + var y = x.Select(z => {|CA1850:new[] { ""c"" }|}); + } +} +", @" +using System; +using System.Linq; + +public class A +{ + private static readonly string[] stringArray = new[] { ""c"" }; + + public void B() + { + var x = new string[] { ""a"", ""b"" }; + var y = x.Select(z => stringArray); + } +} +"); + + // A lambda with an invoked array creation + await VerifyCS.VerifyCodeFixAsync(@" +using System; +using System.Linq; + +public class A +{ + public void B() + { + var x = new string[] { ""a"", ""b"" }; + var y = x.Select(z => {|CA1850:new[] { ""c"" }|}.First()); + } +} +", @" +using System; +using System.Linq; + +public class A +{ + private static readonly string[] sourceArray = new[] { ""c"" }; + + public void B() + { + var x = new string[] { ""a"", ""b"" }; + var y = x.Select(z => sourceArray.First()); + } +} "); // Extension method usage @@ -380,21 +438,6 @@ private void C(ReadOnlySpan span) { } } -"); - - // A lambda with an array creation - await VerifyCS.VerifyAnalyzerAsync(@" -using System; -using System.Linq; - -public class A -{ - public void B() - { - var x = new string[] { ""a"", ""b"" }; - var y = x.Select(z => new[] { ""c"" }); - } -} "); } } From 4a199aebbf7772d6e9cd5eb33a54ecf5f0fbf4e9 Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Wed, 13 Oct 2021 02:15:20 +0000 Subject: [PATCH 30/64] Removed unnecessary check for action type, combined logic, etc --- .../Runtime/AvoidConstArrays.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index ae64803f70..14924a5a73 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -41,8 +41,7 @@ public override void Initialize(AnalysisContext context) { INamedTypeSymbol? readonlySpanType = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReadOnlySpan1); INamedTypeSymbol? functionType = compilationContext.Compilation.GetOrCreateTypeByMetadataName("System.Func`2"); - INamedTypeSymbol? actionType = compilationContext.Compilation.GetOrCreateTypeByMetadataName("System.Action`1"); - if (readonlySpanType is null || functionType is null || actionType is null) + if (readonlySpanType is null || functionType is null) { return; } @@ -97,18 +96,19 @@ public override void Initialize(AnalysisContext context) return; } - // Check if the parameter is a function or action so the name can be set to null - // If the argument found is a function or action, the paramter name doesn't reflect the array creation if (argumentOperation is not null) { - ITypeSymbol originalTypeDefinition = argumentOperation.Parameter.Type.OriginalDefinition; - isDirectlyInsideLambda = originalTypeDefinition.Equals(functionType) || originalTypeDefinition.Equals(actionType); - } + ITypeSymbol originalDefinition = argumentOperation.Parameter.Type.OriginalDefinition; - // Can't be a ReadOnlySpan, as those are already optimized - if (argumentOperation is not null && argumentOperation.Parameter.Type.OriginalDefinition.Equals(readonlySpanType)) - { - return; + // Can't be a ReadOnlySpan, as those are already optimized + if (originalDefinition.Equals(readonlySpanType)) + { + return; + } + + // Check if the parameter is a function so the name can be set to null + // Otherwise, the parameter name doesn't reflect the array creation as well + isDirectlyInsideLambda = originalDefinition.Equals(functionType); } // Must be literal array From 5b8e57f5a9588123baaa0eee7afed196ea4bc47d Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Wed, 13 Oct 2021 02:20:20 +0000 Subject: [PATCH 31/64] Shadowed context --- .../Runtime/AvoidConstArrays.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index 14924a5a73..62d694490c 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -37,22 +37,22 @@ public override void Initialize(AnalysisContext context) context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.RegisterCompilationStartAction(compilationContext => + context.RegisterCompilationStartAction(context => { - INamedTypeSymbol? readonlySpanType = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReadOnlySpan1); - INamedTypeSymbol? functionType = compilationContext.Compilation.GetOrCreateTypeByMetadataName("System.Func`2"); + INamedTypeSymbol? readonlySpanType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReadOnlySpan1); + INamedTypeSymbol? functionType = context.Compilation.GetOrCreateTypeByMetadataName("System.Func`2"); if (readonlySpanType is null || functionType is null) { return; } // Analyzes an argument operation - compilationContext.RegisterOperationAction(operationContext => + context.RegisterOperationAction(context => { bool isDirectlyInsideLambda = false; IArgumentOperation? argumentOperation; - if (operationContext.Operation is IArrayCreationOperation arrayCreationOperation) // For arrays passed as arguments + if (context.Operation is IArrayCreationOperation arrayCreationOperation) // For arrays passed as arguments { argumentOperation = arrayCreationOperation.GetAncestor(OperationKind.Argument); if (argumentOperation is null) @@ -60,7 +60,7 @@ public override void Initialize(AnalysisContext context) return; } } - else if (operationContext.Operation is IInvocationOperation invocationOperation) // For arrays passed in extension methods, like in LINQ + else if (context.Operation is IInvocationOperation invocationOperation) // For arrays passed in extension methods, like in LINQ { if (invocationOperation.Descendants().Any(x => x is IArrayCreationOperation) && invocationOperation.Descendants().Any(x => x is IArgumentOperation)) @@ -122,7 +122,7 @@ public override void Initialize(AnalysisContext context) { "paramName", isDirectlyInsideLambda ? null : argumentOperation?.Parameter?.Name } }; - operationContext.ReportDiagnostic(arrayCreationOperation.CreateDiagnostic(Rule, properties.ToImmutableDictionary())); + context.ReportDiagnostic(arrayCreationOperation.CreateDiagnostic(Rule, properties.ToImmutableDictionary())); }, OperationKind.ArrayCreation, OperationKind.Invocation); From 7176fd5dda2eba7d11b3f38be12a478c5ea9316b Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Wed, 13 Oct 2021 02:24:48 +0000 Subject: [PATCH 32/64] Remove duplicate expensive calls --- .../Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index 62d694490c..c57330f1e3 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -62,8 +62,9 @@ public override void Initialize(AnalysisContext context) } else if (context.Operation is IInvocationOperation invocationOperation) // For arrays passed in extension methods, like in LINQ { - if (invocationOperation.Descendants().Any(x => x is IArrayCreationOperation) - && invocationOperation.Descendants().Any(x => x is IArgumentOperation)) + IEnumerable invocationDescendants = invocationOperation.Descendants(); + if (invocationDescendants.Any(x => x is IArrayCreationOperation) + && invocationDescendants.Any(x => x is IArgumentOperation)) { // This is an invocation that contains an array as an argument // This will get caught by the first case in another cycle From e811a004926f36d4dfe974955ce30489728a25b4 Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Fri, 15 Oct 2021 09:41:43 -0400 Subject: [PATCH 33/64] Update localizable strings --- .../Runtime/AvoidConstArrays.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index c57330f1e3..e20cdf8d3e 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -22,11 +22,11 @@ public sealed class AvoidConstArraysAnalyzer : DiagnosticAnalyzer internal const string RuleId = "CA1850"; internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(RuleId, - CreateLocalizableResourceString(AvoidConstArraysTitle), - CreateLocalizableResourceString(AvoidConstArraysMessage), + CreateLocalizableResourceString(nameof(AvoidConstArraysTitle)), + CreateLocalizableResourceString(nameof(AvoidConstArraysMessage)), DiagnosticCategory.Performance, RuleLevel.IdeSuggestion, - CreateLocalizableResourceString(AvoidConstArraysDescription), + CreateLocalizableResourceString(nameof(AvoidConstArraysDescription)), isPortedFxCopRule: false, isDataflowRule: false); @@ -130,4 +130,4 @@ public override void Initialize(AnalysisContext context) }); } } -} \ No newline at end of file +} From 156c8a8fb911a18d6134590380c4a685ce24961b Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Sat, 16 Oct 2021 15:35:12 +0000 Subject: [PATCH 34/64] Ran msbuild --- src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md | 4 ++-- src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif | 4 ++-- src/NetAnalyzers/RulesMissingDocumentation.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md index 3d96f77417..e1f6274d93 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md @@ -1464,9 +1464,9 @@ When inside a Task-returning method, use the async version of methods, if they e |CodeFix|False| --- -## [CA1850](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1850): - +## [CA1850](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1850): Avoid constant arrays as arguments +Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. |Item|Value| |-|-| diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif index 8ee30708f0..9c7baf0223 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif @@ -2723,8 +2723,8 @@ }, "CA1850": { "id": "CA1850", - "shortDescription": "", - "fullDescription": "", + "shortDescription": "Avoid constant arrays as arguments", + "fullDescription": "Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'.", "defaultLevel": "note", "helpUri": "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1850", "properties": { diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md index eae3f34419..1a6e6bd0da 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -9,7 +9,7 @@ CA1840 | | Do not use 'WhenAll' with a single task | CA1843 | | Do not use 'WaitAll' with a single task | CA1848 | | Use the LoggerMessage delegates | -CA1850 | | | +CA1850 | | Avoid constant arrays as arguments | CA2017 | | Parameter count mismatch | CA2253 | | Named placeholders should not be numeric values | CA2254 | | Template should be a static expression | From 3e4c7d2823f765f8899320c35cdbc62a830df132 Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Wed, 27 Oct 2021 16:43:50 -0400 Subject: [PATCH 35/64] Update MicrosoftNetCoreAnalyzersResources.resx --- .../MicrosoftNetCoreAnalyzersResources.resx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index af7b214cfd..e04fa11ab5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -247,7 +247,7 @@ Avoid unsealed attributes - Avoid constant arrays as arguments + Extract to static readonly field Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. @@ -1833,4 +1833,4 @@ '{0}' uses the preview type '{1}' and needs to opt into preview features. See {2} for more information. - \ No newline at end of file + From 74e6f155eac028624e21e1592b337722107b5a9d Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Thu, 18 Nov 2021 18:42:22 -0500 Subject: [PATCH 36/64] Update analyzer id --- .../Runtime/AvoidConstArrays.Fixer.cs | 2 +- .../Runtime/AvoidConstArrays.cs | 4 +-- .../Runtime/AvoidConstArraysTests.cs | 26 +++++++++---------- .../DiagnosticCategoryAndIdRanges.txt | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 1298e5f28c..ef02c5cefc 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -17,7 +17,7 @@ namespace Microsoft.NetCore.Analyzers.Runtime { /// - /// CA1850: Avoid constant arrays as arguments. Replace with static readonly arrays. + /// CA1851: Avoid constant arrays as arguments. Replace with static readonly arrays. /// [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] public sealed class AvoidConstArraysFixer : CodeFixProvider diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index e20cdf8d3e..f2f53ebdda 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -14,12 +14,12 @@ namespace Microsoft.NetCore.Analyzers.Runtime using static MicrosoftNetCoreAnalyzersResources; /// - /// CA1850: Avoid constant arrays as arguments. Replace with static readonly arrays. + /// CA1851: Avoid constant arrays as arguments. Replace with static readonly arrays. /// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class AvoidConstArraysAnalyzer : DiagnosticAnalyzer { - internal const string RuleId = "CA1850"; + internal const string RuleId = "CA1851"; internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(RuleId, CreateLocalizableResourceString(nameof(AvoidConstArraysTitle)), diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 95b74c4463..8a81898305 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -24,7 +24,7 @@ public class A { public void B() { - Console.WriteLine({|CA1850:new[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1851:new[]{ 1, 2, 3 }|}); } } ", @" @@ -46,7 +46,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine({|CA1850:{1, 2, 3}|}) + Console.WriteLine({|CA1851:{1, 2, 3}|}) End Sub End Class ", @" @@ -68,7 +68,7 @@ public class A { public void B() { - Console.WriteLine({|CA1850:new int[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1851:new int[]{ 1, 2, 3 }|}); } } ", @" @@ -90,7 +90,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine({|CA1850:New Integer() {1, 2, 3}|}) + Console.WriteLine({|CA1851:New Integer() {1, 2, 3}|}) End Sub End Class ", @" @@ -112,7 +112,7 @@ public class A { public void B() { - Console.WriteLine(string.Join("" "", {|CA1850:new[] { ""Cake"", ""is"", ""good"" }|})); + Console.WriteLine(string.Join("" "", {|CA1851:new[] { ""Cake"", ""is"", ""good"" }|})); } } ", @" @@ -134,7 +134,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine(String.Join("" ""c, {|CA1850:{""Cake"", ""is"", ""good""}|})) + Console.WriteLine(String.Join("" ""c, {|CA1851:{""Cake"", ""is"", ""good""}|})) End Sub End Class ", @" @@ -156,7 +156,7 @@ public class A { public void B() { - Console.WriteLine(string.Join("" "", {|CA1850:new[] { ""a"", ""b"" }|} /* test comment */)); + Console.WriteLine(string.Join("" "", {|CA1851:new[] { ""a"", ""b"" }|} /* test comment */)); } } ", @" @@ -183,7 +183,7 @@ public class A public void B() { var x = new string[] { ""a"", ""b"" }; - var y = x.Select(z => {|CA1850:new[] { ""c"" }|}); + var y = x.Select(z => {|CA1851:new[] { ""c"" }|}); } } ", @" @@ -212,7 +212,7 @@ public class A public void B() { var x = new string[] { ""a"", ""b"" }; - var y = x.Select(z => {|CA1850:new[] { ""c"" }|}.First()); + var y = x.Select(z => {|CA1851:new[] { ""c"" }|}.First()); } } ", @" @@ -240,7 +240,7 @@ public class A { public void B() { - string y = {|CA1850:new[] { ""a"", ""b"", ""c"" }|}.First(); + string y = {|CA1851:new[] { ""a"", ""b"", ""c"" }|}.First(); Console.WriteLine(y); } } @@ -266,7 +266,7 @@ Imports System.Linq Public Class A Public Sub B() - Dim y As String = {|CA1850:{""a"", ""b"", ""c""}|}.First() + Dim y As String = {|CA1851:{""a"", ""b"", ""c""}|}.First() Console.WriteLine(y) End Sub End Class @@ -297,7 +297,7 @@ public class A public void B() { - Console.WriteLine({|CA1850:new[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1851:new[]{ 1, 2, 3 }|}); } } ", @" @@ -329,7 +329,7 @@ Private Shared ReadOnly valueArray1 As Boolean() = {True, False, True} Private Shared ReadOnly x As Integer = 1 Public Sub B() - Console.WriteLine({|CA1850:{1, 2, 3}|}) + Console.WriteLine({|CA1851:{1, 2, 3}|}) End Sub End Class ", @" diff --git a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt index a94aba62d9..cc1f82addd 100644 --- a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt +++ b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt @@ -12,7 +12,7 @@ Design: CA2210, CA1000-CA1070 Globalization: CA2101, CA1300-CA1310 Mobility: CA1600-CA1601 -Performance: HA, CA1800-CA1850 +Performance: HA, CA1800-CA1851 Security: CA2100-CA2153, CA2300-CA2330, CA3000-CA3147, CA5300-CA5405 Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2258 Naming: CA1700-CA1727 From b4dc60c1d9e5e4f271050791b0db83014fcf11b0 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Thu, 18 Nov 2021 20:03:27 -0500 Subject: [PATCH 37/64] Updated xlf files --- .../MicrosoftNetCoreAnalyzersResources.resx | 3 +++ .../Runtime/AvoidConstArrays.Fixer.cs | 2 +- .../Runtime/AvoidConstArrays.cs | 11 +++++------ .../xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.de.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.es.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.it.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf | 5 +++++ .../MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf | 5 +++++ .../MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf | 5 +++++ 16 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 5fedcdf6f0..08d6ef22d1 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -247,6 +247,9 @@ Avoid unsealed attributes + Avoid constant arrays as arguments + + Extract to static readonly field diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index ef02c5cefc..deadedc693 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -38,7 +38,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) DocumentEditor editor = await DocumentEditor.CreateAsync(document, context.CancellationToken).ConfigureAwait(false); SyntaxGenerator generator = editor.Generator; - string title = MicrosoftNetCoreAnalyzersResources.AvoidConstArraysTitle; + string title = MicrosoftNetCoreAnalyzersResources.AvoidConstArraysCodeFixTitle; context.RegisterCodeFix( new MyCodeAction( title, diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index f2f53ebdda..de58d77d64 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -41,10 +41,6 @@ public override void Initialize(AnalysisContext context) { INamedTypeSymbol? readonlySpanType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReadOnlySpan1); INamedTypeSymbol? functionType = context.Compilation.GetOrCreateTypeByMetadataName("System.Func`2"); - if (readonlySpanType is null || functionType is null) - { - return; - } // Analyzes an argument operation context.RegisterOperationAction(context => @@ -102,14 +98,17 @@ public override void Initialize(AnalysisContext context) ITypeSymbol originalDefinition = argumentOperation.Parameter.Type.OriginalDefinition; // Can't be a ReadOnlySpan, as those are already optimized - if (originalDefinition.Equals(readonlySpanType)) + if (readonlySpanType is not null && originalDefinition.Equals(readonlySpanType)) { return; } // Check if the parameter is a function so the name can be set to null // Otherwise, the parameter name doesn't reflect the array creation as well - isDirectlyInsideLambda = originalDefinition.Equals(functionType); + if (functionType is not null) + { + isDirectlyInsideLambda = originalDefinition.Equals(functionType); + } } // Must be literal array 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 b720dd0c20..9de51c1766 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -52,6 +52,11 @@ Literály řetězců atributů by se měly správně parsovat + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 5c7d973510..8242f6f570 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -52,6 +52,11 @@ Attributzeichenfolgenliterale müssen richtig analysiert werden + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 e69763720a..24615979f6 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -52,6 +52,11 @@ Los literales de cadena de atributo se deben analizar correctamente + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 4d4167fbf7..05e5c4f9f7 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -52,6 +52,11 @@ Les littéraux de chaîne d'attribut doivent être analysés correctement + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 6e9c8252e1..f7029b01ea 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -52,6 +52,11 @@ I valori letterali stringa dell'attributo devono essere analizzati correttamente + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 c1f315cfb9..2b8e706269 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -52,6 +52,11 @@ 属性文字列リテラルは、正しく解析する必要があります + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 81f62f8538..3f32c2c67f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -52,6 +52,11 @@ 특성 문자열 리터럴이 올바르게 구문 분석되어야 합니다. + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 6f948d6ff8..74a640bf4b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -52,6 +52,11 @@ Analiza literałów ciągu atrybutu powinna kończyć się powodzeniem + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 92f49b6de6..367bbb4374 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 @@ -52,6 +52,11 @@ Literais de cadeias de caracteres de atributos devem ser analisados corretamente + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 5fb73fb6a1..bd71205dbe 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -52,6 +52,11 @@ Синтаксический анализ строковых литералов атрибута должен осуществляться правильно + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 20a0294d9e..f9444d17b4 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -52,6 +52,11 @@ Öznitelik dizesinin sabit değerleri doğru ayrıştırılmalıdır + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 e826b2f139..9321acd003 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 @@ -52,6 +52,11 @@ 特性字符串文本应正确分析 + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 51c738da32..78d5cbec23 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 @@ -52,6 +52,11 @@ 屬性字串常值應正確剖析 + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. From 47a0232a861dbe02243e8c9198dd94f3a0659bff Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Thu, 18 Nov 2021 20:03:27 -0500 Subject: [PATCH 38/64] Applied feedback, updated xlf files --- .../MicrosoftNetCoreAnalyzersResources.resx | 3 +++ .../Runtime/AvoidConstArrays.Fixer.cs | 2 +- .../Runtime/AvoidConstArrays.cs | 11 +++++------ .../xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.de.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.es.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.it.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf | 5 +++++ .../xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf | 5 +++++ .../MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf | 5 +++++ .../MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf | 5 +++++ 16 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 5fedcdf6f0..08d6ef22d1 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -247,6 +247,9 @@ Avoid unsealed attributes + Avoid constant arrays as arguments + + Extract to static readonly field diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index ef02c5cefc..deadedc693 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -38,7 +38,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) DocumentEditor editor = await DocumentEditor.CreateAsync(document, context.CancellationToken).ConfigureAwait(false); SyntaxGenerator generator = editor.Generator; - string title = MicrosoftNetCoreAnalyzersResources.AvoidConstArraysTitle; + string title = MicrosoftNetCoreAnalyzersResources.AvoidConstArraysCodeFixTitle; context.RegisterCodeFix( new MyCodeAction( title, diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index f2f53ebdda..de58d77d64 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -41,10 +41,6 @@ public override void Initialize(AnalysisContext context) { INamedTypeSymbol? readonlySpanType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReadOnlySpan1); INamedTypeSymbol? functionType = context.Compilation.GetOrCreateTypeByMetadataName("System.Func`2"); - if (readonlySpanType is null || functionType is null) - { - return; - } // Analyzes an argument operation context.RegisterOperationAction(context => @@ -102,14 +98,17 @@ public override void Initialize(AnalysisContext context) ITypeSymbol originalDefinition = argumentOperation.Parameter.Type.OriginalDefinition; // Can't be a ReadOnlySpan, as those are already optimized - if (originalDefinition.Equals(readonlySpanType)) + if (readonlySpanType is not null && originalDefinition.Equals(readonlySpanType)) { return; } // Check if the parameter is a function so the name can be set to null // Otherwise, the parameter name doesn't reflect the array creation as well - isDirectlyInsideLambda = originalDefinition.Equals(functionType); + if (functionType is not null) + { + isDirectlyInsideLambda = originalDefinition.Equals(functionType); + } } // Must be literal array 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 b720dd0c20..9de51c1766 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -52,6 +52,11 @@ Literály řetězců atributů by se měly správně parsovat + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 5c7d973510..8242f6f570 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -52,6 +52,11 @@ Attributzeichenfolgenliterale müssen richtig analysiert werden + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 e69763720a..24615979f6 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -52,6 +52,11 @@ Los literales de cadena de atributo se deben analizar correctamente + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 4d4167fbf7..05e5c4f9f7 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -52,6 +52,11 @@ Les littéraux de chaîne d'attribut doivent être analysés correctement + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 6e9c8252e1..f7029b01ea 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -52,6 +52,11 @@ I valori letterali stringa dell'attributo devono essere analizzati correttamente + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 c1f315cfb9..2b8e706269 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -52,6 +52,11 @@ 属性文字列リテラルは、正しく解析する必要があります + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 81f62f8538..3f32c2c67f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -52,6 +52,11 @@ 특성 문자열 리터럴이 올바르게 구문 분석되어야 합니다. + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 6f948d6ff8..74a640bf4b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -52,6 +52,11 @@ Analiza literałów ciągu atrybutu powinna kończyć się powodzeniem + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 92f49b6de6..367bbb4374 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 @@ -52,6 +52,11 @@ Literais de cadeias de caracteres de atributos devem ser analisados corretamente + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 5fb73fb6a1..bd71205dbe 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -52,6 +52,11 @@ Синтаксический анализ строковых литералов атрибута должен осуществляться правильно + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 20a0294d9e..f9444d17b4 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -52,6 +52,11 @@ Öznitelik dizesinin sabit değerleri doğru ayrıştırılmalıdır + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 e826b2f139..9321acd003 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 @@ -52,6 +52,11 @@ 特性字符串文本应正确分析 + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. 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 51c738da32..78d5cbec23 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 @@ -52,6 +52,11 @@ 屬性字串常值應正確剖析 + + Extract to static readonly field + Extract to static readonly field + + Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. From 97667aee4fc0a0791be953bf10cf7982da5424ed Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Thu, 16 Dec 2021 20:35:03 -0500 Subject: [PATCH 39/64] Uh... I forgot what this commit was about --- .../Runtime/AvoidConstArrays.Fixer.cs | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index deadedc693..d3a38dfb25 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -51,41 +51,37 @@ private static async Task ExtractConstArrayAsync(SyntaxNode node, Sema SyntaxGenerator generator, ImmutableDictionary properties, CancellationToken cancellationToken) { IArrayCreationOperation arrayArgument = GetArrayCreationOperation(node, model, cancellationToken, out bool isInvoked); - IEnumerable typeFields = model.GetEnclosingSymbol(node.SpanStart, cancellationToken).ContainingType - .GetMembers().Where(x => x is IFieldSymbol); + IEnumerable members = model.GetEnclosingSymbol(node.SpanStart, cancellationToken).ContainingType.GetMembers(); // Get a valid member name for the extracted constant - string newMemberName = GetExtractedMemberName(typeFields.Select(x => x.Name), properties["paramName"] ?? GetMemberNameFromType(arrayArgument)); + string newMemberName = GetExtractedMemberName( + members.Where(x => x is IFieldSymbol).Select(x => x.Name), + properties["paramName"] ?? GetMemberNameFromType(arrayArgument)); - // Get method containing the symbol that is being diagnosed. Should always be in a method - IOperation? containingMethodBody = arrayArgument.GetAncestor(OperationKind.MethodBody); - containingMethodBody ??= arrayArgument.GetAncestor(OperationKind.Block); // VB methods have a different structure than CS methods - - RoslynDebug.Assert(containingMethodBody != null); + // Get method containing the symbol that is being diagnosed. + IOperation? methodContext = arrayArgument.GetAncestor(OperationKind.MethodBody); + methodContext ??= arrayArgument.GetAncestor(OperationKind.Block); // VB methods have a different structure than CS methods // Create the new member SyntaxNode newMember = generator.FieldDeclaration( newMemberName, generator.TypeExpression(arrayArgument.Type), - GetAccessibility(model.GetEnclosingSymbol(containingMethodBody!.Syntax.SpanStart, cancellationToken)), + GetAccessibility(methodContext is null ? null : model.GetEnclosingSymbol(methodContext.Syntax.SpanStart, cancellationToken)), DeclarationModifiers.Static | DeclarationModifiers.ReadOnly, arrayArgument.Syntax.WithoutTrailingTrivia() // don't include extra trivia before the end of the declaration - ).FormatForExtraction(containingMethodBody.Syntax); // any additional formatting + ); - // Add the new extracted member - ISymbol lastFieldSymbol = typeFields.LastOrDefault(); - if (lastFieldSymbol is not null) + // Add any additional formatting + if (methodContext is not null) { - // Insert after last field if any fields are present - SyntaxNode lastFieldNode = await lastFieldSymbol.DeclaringSyntaxReferences.First().GetSyntaxAsync(cancellationToken).ConfigureAwait(false); - editor.InsertAfter(generator.GetDeclaration(lastFieldNode), newMember); - } - else - { - // Insert before first method if no fields are present, as a method already exists - editor.InsertBefore(containingMethodBody.Syntax, newMember); + newMember = newMember.FormatForExtraction(methodContext.Syntax); } + // Insert the new extracted member before the first member + SyntaxNode firstMemberNode = await members.First().DeclaringSyntaxReferences.First() + .GetSyntaxAsync(cancellationToken).ConfigureAwait(false); + editor.InsertBefore(generator.GetDeclaration(firstMemberNode), newMember); + // Replace argument with a reference to our new member SyntaxNode identifier = generator.IdentifierName(newMemberName); if (isInvoked) @@ -151,9 +147,9 @@ private static string GetMemberNameFromType(IArrayCreationOperation arrayCreatio #pragma warning restore CA1308 // Normalize strings to uppercase } - private static Accessibility GetAccessibility(ISymbol originMethodSymbol) + private static Accessibility GetAccessibility(ISymbol? methodSymbol) { - if (Enum.TryParse(originMethodSymbol.GetResultantVisibility().ToString(), out Accessibility accessibility)) + if (methodSymbol is not null && Enum.TryParse(methodSymbol.GetResultantVisibility().ToString(), out Accessibility accessibility)) { return accessibility == Accessibility.Public ? Accessibility.Private // public accessibility not wanted for fields From ab10a36e5214ab986187d9f62f9eec82742e7f99 Mon Sep 17 00:00:00 2001 From: Steve Berdy <86739818+steveberdy@users.noreply.github.com> Date: Fri, 1 Apr 2022 10:58:38 -0400 Subject: [PATCH 40/64] Update src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs Co-authored-by: Buyaa Namnan --- .../Runtime/AvoidConstArrays.Fixer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index d3a38dfb25..9b3ca1aff5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -119,7 +119,7 @@ private static IArrayCreationOperation GetArrayCreationOperation(SyntaxNode node private static string GetExtractedMemberName(IEnumerable memberNames, string parameterName) { - bool hasCollectionEnding = s_collectionMemberEndings.Any(x => parameterName.EndsWith(x, true, null)); + bool hasCollectionEnding = s_collectionMemberEndings.Any(x => parameterName.EndsWith(x, true, CultureInfo.InvariantCulture)); if (parameterName == "source" // for LINQ, "sourceArray" is clearer than "source" || (memberNames.Contains(parameterName) && !hasCollectionEnding)) From 75a6e2e642605b51fd715c7697636d80f4c3ebb0 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Mon, 25 Apr 2022 21:13:18 -0400 Subject: [PATCH 41/64] Update rule id --- .../Runtime/AvoidConstArrays.Fixer.cs | 2 +- .../Runtime/AvoidConstArrays.cs | 4 +-- .../Microsoft.CodeAnalysis.NetAnalyzers.md | 2 +- .../Runtime/AvoidConstArraysTests.cs | 26 +++++++++---------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 9b3ca1aff5..fe83381a83 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -17,7 +17,7 @@ namespace Microsoft.NetCore.Analyzers.Runtime { /// - /// CA1851: Avoid constant arrays as arguments. Replace with static readonly arrays. + /// CA1853: Avoid constant arrays as arguments. Replace with static readonly arrays. /// [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] public sealed class AvoidConstArraysFixer : CodeFixProvider diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index de58d77d64..4e0b3bfd77 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -14,12 +14,12 @@ namespace Microsoft.NetCore.Analyzers.Runtime using static MicrosoftNetCoreAnalyzersResources; /// - /// CA1851: Avoid constant arrays as arguments. Replace with static readonly arrays. + /// CA1853: Avoid constant arrays as arguments. Replace with static readonly arrays. /// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class AvoidConstArraysAnalyzer : DiagnosticAnalyzer { - internal const string RuleId = "CA1851"; + internal const string RuleId = "CA1853"; internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(RuleId, CreateLocalizableResourceString(nameof(AvoidConstArraysTitle)), diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md index 88336fcb18..6f3b5ee623 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md @@ -1536,7 +1536,7 @@ When a type is not accessible outside its assembly and has no subtypes within it |CodeFix|True| --- -## [CA1853](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1851): Avoid constant arrays as arguments +## [CA1853](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1853): Avoid constant arrays as arguments Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 8a81898305..e2ea2b2461 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -24,7 +24,7 @@ public class A { public void B() { - Console.WriteLine({|CA1851:new[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1853:new[]{ 1, 2, 3 }|}); } } ", @" @@ -46,7 +46,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine({|CA1851:{1, 2, 3}|}) + Console.WriteLine({|CA1853:{1, 2, 3}|}) End Sub End Class ", @" @@ -68,7 +68,7 @@ public class A { public void B() { - Console.WriteLine({|CA1851:new int[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1853:new int[]{ 1, 2, 3 }|}); } } ", @" @@ -90,7 +90,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine({|CA1851:New Integer() {1, 2, 3}|}) + Console.WriteLine({|CA1853:New Integer() {1, 2, 3}|}) End Sub End Class ", @" @@ -112,7 +112,7 @@ public class A { public void B() { - Console.WriteLine(string.Join("" "", {|CA1851:new[] { ""Cake"", ""is"", ""good"" }|})); + Console.WriteLine(string.Join("" "", {|CA1853:new[] { ""Cake"", ""is"", ""good"" }|})); } } ", @" @@ -134,7 +134,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine(String.Join("" ""c, {|CA1851:{""Cake"", ""is"", ""good""}|})) + Console.WriteLine(String.Join("" ""c, {|CA1853:{""Cake"", ""is"", ""good""}|})) End Sub End Class ", @" @@ -156,7 +156,7 @@ public class A { public void B() { - Console.WriteLine(string.Join("" "", {|CA1851:new[] { ""a"", ""b"" }|} /* test comment */)); + Console.WriteLine(string.Join("" "", {|CA1853:new[] { ""a"", ""b"" }|} /* test comment */)); } } ", @" @@ -183,7 +183,7 @@ public class A public void B() { var x = new string[] { ""a"", ""b"" }; - var y = x.Select(z => {|CA1851:new[] { ""c"" }|}); + var y = x.Select(z => {|CA1853:new[] { ""c"" }|}); } } ", @" @@ -212,7 +212,7 @@ public class A public void B() { var x = new string[] { ""a"", ""b"" }; - var y = x.Select(z => {|CA1851:new[] { ""c"" }|}.First()); + var y = x.Select(z => {|CA1853:new[] { ""c"" }|}.First()); } } ", @" @@ -240,7 +240,7 @@ public class A { public void B() { - string y = {|CA1851:new[] { ""a"", ""b"", ""c"" }|}.First(); + string y = {|CA1853:new[] { ""a"", ""b"", ""c"" }|}.First(); Console.WriteLine(y); } } @@ -266,7 +266,7 @@ Imports System.Linq Public Class A Public Sub B() - Dim y As String = {|CA1851:{""a"", ""b"", ""c""}|}.First() + Dim y As String = {|CA1853:{""a"", ""b"", ""c""}|}.First() Console.WriteLine(y) End Sub End Class @@ -297,7 +297,7 @@ public class A public void B() { - Console.WriteLine({|CA1851:new[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1853:new[]{ 1, 2, 3 }|}); } } ", @" @@ -329,7 +329,7 @@ Private Shared ReadOnly valueArray1 As Boolean() = {True, False, True} Private Shared ReadOnly x As Integer = 1 Public Sub B() - Console.WriteLine({|CA1851:{1, 2, 3}|}) + Console.WriteLine({|CA1853:{1, 2, 3}|}) End Sub End Class ", @" From 99b29272adde03eff8361dbccaaa4101f041de75 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Mon, 25 Apr 2022 21:25:55 -0400 Subject: [PATCH 42/64] Remove MyCodeAction --- .../Runtime/AvoidConstArrays.Fixer.cs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index fe83381a83..77659f2269 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -10,6 +10,7 @@ using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Operations; @@ -39,8 +40,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) SyntaxGenerator generator = editor.Generator; string title = MicrosoftNetCoreAnalyzersResources.AvoidConstArraysCodeFixTitle; - context.RegisterCodeFix( - new MyCodeAction( + context.RegisterCodeFix(CodeAction.Create( title, async c => await ExtractConstArrayAsync(node, model, editor, generator, context.Diagnostics.First().Properties, c).ConfigureAwait(false), equivalenceKey: title), @@ -157,15 +157,6 @@ private static Accessibility GetAccessibility(ISymbol? methodSymbol) } return Accessibility.Private; } - - // Needed for Telemetry (https://github.com/dotnet/roslyn-analyzers/issues/192) - private sealed class MyCodeAction : DocumentChangeAction - { - public MyCodeAction(string title, Func> createChangedDocument, string equivalenceKey) : - base(title, createChangedDocument, equivalenceKey) - { - } - } } internal static class SyntaxNodeExtensions From 85ae9610197fcd9ee6a1ed395338e9855cec7c17 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Mon, 25 Apr 2022 21:46:44 -0400 Subject: [PATCH 43/64] Added assembly reference for globalization --- .../Runtime/AvoidConstArrays.Fixer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 77659f2269..4401b1afde 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -2,6 +2,7 @@ using System; using System.Linq; +using System.Globalization; using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; From 7146c43fef2f5a5894f4026029eb9308069b0155 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Wed, 25 May 2022 18:56:09 -0400 Subject: [PATCH 44/64] Update to ignore params arguments --- .../Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs | 4 ++-- .../Runtime/AvoidConstArraysTests.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index 382a385239..2f568b4e36 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -51,7 +51,7 @@ public override void Initialize(AnalysisContext context) if (context.Operation is IArrayCreationOperation arrayCreationOperation) // For arrays passed as arguments { argumentOperation = arrayCreationOperation.GetAncestor(OperationKind.Argument); - if (argumentOperation is null) + if (argumentOperation is null || argumentOperation.Parameter.IsParams) { return; } @@ -129,4 +129,4 @@ public override void Initialize(AnalysisContext context) }); } } -} +} \ No newline at end of file diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 66e1e3b1c5..57596e95e2 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -441,4 +441,4 @@ private void C(ReadOnlySpan span) "); } } -} +} \ No newline at end of file From d0aad930632d197d8cc7faa20f1f1b1b5d60146d Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Wed, 25 May 2022 18:59:06 -0400 Subject: [PATCH 45/64] Added tests for params arguments --- .../Runtime/AvoidConstArraysTests.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 57596e95e2..0d754f5d39 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -438,6 +438,40 @@ private void C(ReadOnlySpan span) { } } +"); + + // Params arguments + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class A +{ + public void B() + { + C(true, false); + } + + private void C(params bool[] booleans) + { + } +} +"); + + // A params argument, even as a literal array + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class A +{ + public void B() + { + C(new bool[] { true, false }); + } + + private void C(params bool[] booleans) + { + } +} "); } } From 95a805f6233317137f23ba6a7e2f920c5e69b4bd Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Wed, 25 May 2022 19:06:49 -0400 Subject: [PATCH 46/64] Split tests between CS and VB --- .../Runtime/AvoidConstArraysTests.cs | 242 ++++++++++-------- 1 file changed, 129 insertions(+), 113 deletions(-) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 0d754f5d39..3724636a87 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -14,7 +14,7 @@ namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests public class AvoidConstArraysTests { [Fact] - public async Task IdentifyConstArrays() + public async Task IdentifyConstArraysCS() { // Implicit initialization check await VerifyCS.VerifyCodeFixAsync(@" @@ -39,25 +39,6 @@ public void B() Console.WriteLine(value); } } -"); - - await VerifyVB.VerifyCodeFixAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine({|CA1854:{1, 2, 3}|}) - End Sub -End Class -", @" -Imports System - -Public Class A - Private Shared ReadOnly value As Integer() = {1, 2, 3} - Public Sub B() - Console.WriteLine(value) - End Sub -End Class "); // Explicit initialization check @@ -83,25 +64,6 @@ public void B() Console.WriteLine(value); } } -"); - - await VerifyVB.VerifyCodeFixAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine({|CA1854:New Integer() {1, 2, 3}|}) - End Sub -End Class -", @" -Imports System - -Public Class A - Private Shared ReadOnly value As Integer() = New Integer() {1, 2, 3} - Public Sub B() - Console.WriteLine(value) - End Sub -End Class "); // Nested arguments @@ -127,25 +89,6 @@ public void B() Console.WriteLine(string.Join("" "", value)); } } -"); - - await VerifyVB.VerifyCodeFixAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine(String.Join("" ""c, {|CA1854:{""Cake"", ""is"", ""good""}|})) - End Sub -End Class -", @" -Imports System - -Public Class A - Private Shared ReadOnly value As String() = {""Cake"", ""is"", ""good""} - Public Sub B() - Console.WriteLine(String.Join("" ""c, value)) - End Sub -End Class "); // Trivia test, CS only @@ -258,29 +201,6 @@ public void B() Console.WriteLine(y); } } -"); - - await VerifyVB.VerifyCodeFixAsync(@" -Imports System -Imports System.Linq - -Public Class A - Public Sub B() - Dim y As String = {|CA1854:{""a"", ""b"", ""c""}|}.First() - Console.WriteLine(y) - End Sub -End Class -", @" -Imports System -Imports System.Linq - -Public Class A - Private Shared ReadOnly stringArray As String() = {""a"", ""b"", ""c""} - Public Sub B() - Dim y As String = stringArray.First() - Console.WriteLine(y) - End Sub -End Class "); // Member extraction tests @@ -317,8 +237,97 @@ public void B() Console.WriteLine(valueArray0); } } +"); + } + + [Fact] + public async Task IdentifyConstArraysVB() + { + // Implicit initialization check + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine({|CA1854:{1, 2, 3}|}) + End Sub +End Class +", @" +Imports System + +Public Class A + Private Shared ReadOnly value As Integer() = {1, 2, 3} + Public Sub B() + Console.WriteLine(value) + End Sub +End Class +"); + + // Explicit initialization check + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine({|CA1854:New Integer() {1, 2, 3}|}) + End Sub +End Class +", @" +Imports System + +Public Class A + Private Shared ReadOnly value As Integer() = New Integer() {1, 2, 3} + Public Sub B() + Console.WriteLine(value) + End Sub +End Class +"); + + // Nested arguments + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(String.Join("" ""c, {|CA1854:{""Cake"", ""is"", ""good""}|})) + End Sub +End Class +", @" +Imports System + +Public Class A + Private Shared ReadOnly value As String() = {""Cake"", ""is"", ""good""} + Public Sub B() + Console.WriteLine(String.Join("" ""c, value)) + End Sub +End Class "); + // Extension method usage + await VerifyVB.VerifyCodeFixAsync(@" +Imports System +Imports System.Linq + +Public Class A + Public Sub B() + Dim y As String = {|CA1854:{""a"", ""b"", ""c""}|}.First() + Console.WriteLine(y) + End Sub +End Class +", @" +Imports System +Imports System.Linq + +Public Class A + Private Shared ReadOnly stringArray As String() = {""a"", ""b"", ""c""} + Public Sub B() + Dim y As String = stringArray.First() + Console.WriteLine(y) + End Sub +End Class +"); + + // Member extraction tests await VerifyVB.VerifyCodeFixAsync(@" Imports System @@ -350,7 +359,7 @@ End Class } [Fact] - public async Task IgnoreOtherArgs_NoDiagnostic() + public async Task IgnoreOtherArgs_NoDiagnosticCS() { // A string await VerifyCS.VerifyAnalyzerAsync(@" @@ -363,16 +372,6 @@ public void B() Console.WriteLine(""Lorem ipsum""); } } -"); - - await VerifyVB.VerifyAnalyzerAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine(""Lorem ipsum"") - End Sub -End Class "); // Test another type to be extra safe @@ -386,16 +385,6 @@ public void B() Console.WriteLine(123); } } -"); - - await VerifyVB.VerifyAnalyzerAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine(123) - End Sub -End Class "); // Non-literal array @@ -410,17 +399,6 @@ public void B() Console.WriteLine(new[] { str }); } } -"); - - await VerifyVB.VerifyAnalyzerAsync(@" -Imports System - -Public Class A - Public Sub B() - Dim str As String = ""Lorem ipsum"" - Console.WriteLine({ str }) - End Sub -End Class "); // A ReadOnlySpan, which is already optimized @@ -472,6 +450,44 @@ private void C(params bool[] booleans) { } } +"); + } + + [Fact] + public async Task IgnoreOtherArgs_NoDiagnosticVB() + { + // A string + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(""Lorem ipsum"") + End Sub +End Class +"); + + // Test another type to be extra safe + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(123) + End Sub +End Class +"); + + // Non-literal array + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class A + Public Sub B() + Dim str As String = ""Lorem ipsum"" + Console.WriteLine({ str }) + End Sub +End Class "); } } From 72c183a15684a4e8f176a2fab6d2fc6fb4d9b897 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Wed, 25 May 2022 21:06:02 -0400 Subject: [PATCH 47/64] Separated tests by topic --- .../Runtime/AvoidConstArraysTests.cs | 286 +++++++++--------- 1 file changed, 150 insertions(+), 136 deletions(-) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 3724636a87..8988da055f 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -14,9 +14,8 @@ namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests public class AvoidConstArraysTests { [Fact] - public async Task IdentifyConstArraysCS() + public async Task IdentifyConstArrays_ImplicitInitialization() { - // Implicit initialization check await VerifyCS.VerifyCodeFixAsync(@" using System; @@ -41,7 +40,29 @@ public void B() } "); - // Explicit initialization check + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine({|CA1854:{1, 2, 3}|}) + End Sub +End Class +", @" +Imports System + +Public Class A + Private Shared ReadOnly value As Integer() = {1, 2, 3} + Public Sub B() + Console.WriteLine(value) + End Sub +End Class +"); + } + + [Fact] + public async Task IdentifyConstArrays_ExplicitInitialization() + { await VerifyCS.VerifyCodeFixAsync(@" using System; @@ -66,7 +87,29 @@ public void B() } "); - // Nested arguments + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine({|CA1854:New Integer() {1, 2, 3}|}) + End Sub +End Class +", @" +Imports System + +Public Class A + Private Shared ReadOnly value As Integer() = New Integer() {1, 2, 3} + Public Sub B() + Console.WriteLine(value) + End Sub +End Class +"); + } + + [Fact] + public async Task IdentifyConstArrays_NestedArgs() + { await VerifyCS.VerifyCodeFixAsync(@" using System; @@ -91,7 +134,29 @@ public void B() } "); - // Trivia test, CS only + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(String.Join("" ""c, {|CA1854:{""Cake"", ""is"", ""good""}|})) + End Sub +End Class +", @" +Imports System + +Public Class A + Private Shared ReadOnly value As String() = {""Cake"", ""is"", ""good""} + Public Sub B() + Console.WriteLine(String.Join("" ""c, value)) + End Sub +End Class +"); + } + + [Fact] + public async Task IdentifyConstArrays_TriviaTest() + { await VerifyCS.VerifyCodeFixAsync(@" using System; @@ -115,8 +180,11 @@ public void B() } } "); + } - // A lambda with an array creation + [Fact] + public async Task IdentifyConstArrays_LambdaArrayCreation() + { await VerifyCS.VerifyCodeFixAsync(@" using System; using System.Linq; @@ -144,8 +212,11 @@ public void B() } } "); + } - // A lambda with an invoked array creation + [Fact] + public async Task IdentifyConstArrays_LambdaInvokedArrayCreation() + { await VerifyCS.VerifyCodeFixAsync(@" using System; using System.Linq; @@ -173,8 +244,11 @@ public void B() } } "); + } - // Extension method usage + [Fact] + public async Task IdentifyConstArrays_ExtensionMethod() + { await VerifyCS.VerifyCodeFixAsync(@" using System; using System.Linq; @@ -203,6 +277,33 @@ public void B() } "); + await VerifyVB.VerifyCodeFixAsync(@" +Imports System +Imports System.Linq + +Public Class A + Public Sub B() + Dim y As String = {|CA1854:{""a"", ""b"", ""c""}|}.First() + Console.WriteLine(y) + End Sub +End Class +", @" +Imports System +Imports System.Linq + +Public Class A + Private Shared ReadOnly stringArray As String() = {""a"", ""b"", ""c""} + Public Sub B() + Dim y As String = stringArray.First() + Console.WriteLine(y) + End Sub +End Class +"); + } + + [Fact] + public async Task IdentifyConstArrays_MemberExtractionTest() + { // Member extraction tests await VerifyCS.VerifyCodeFixAsync(@" using System; @@ -237,97 +338,8 @@ public void B() Console.WriteLine(valueArray0); } } -"); - } - - [Fact] - public async Task IdentifyConstArraysVB() - { - // Implicit initialization check - await VerifyVB.VerifyCodeFixAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine({|CA1854:{1, 2, 3}|}) - End Sub -End Class -", @" -Imports System - -Public Class A - Private Shared ReadOnly value As Integer() = {1, 2, 3} - Public Sub B() - Console.WriteLine(value) - End Sub -End Class -"); - - // Explicit initialization check - await VerifyVB.VerifyCodeFixAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine({|CA1854:New Integer() {1, 2, 3}|}) - End Sub -End Class -", @" -Imports System - -Public Class A - Private Shared ReadOnly value As Integer() = New Integer() {1, 2, 3} - Public Sub B() - Console.WriteLine(value) - End Sub -End Class "); - // Nested arguments - await VerifyVB.VerifyCodeFixAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine(String.Join("" ""c, {|CA1854:{""Cake"", ""is"", ""good""}|})) - End Sub -End Class -", @" -Imports System - -Public Class A - Private Shared ReadOnly value As String() = {""Cake"", ""is"", ""good""} - Public Sub B() - Console.WriteLine(String.Join("" ""c, value)) - End Sub -End Class -"); - - // Extension method usage - await VerifyVB.VerifyCodeFixAsync(@" -Imports System -Imports System.Linq - -Public Class A - Public Sub B() - Dim y As String = {|CA1854:{""a"", ""b"", ""c""}|}.First() - Console.WriteLine(y) - End Sub -End Class -", @" -Imports System -Imports System.Linq - -Public Class A - Private Shared ReadOnly stringArray As String() = {""a"", ""b"", ""c""} - Public Sub B() - Dim y As String = stringArray.First() - Console.WriteLine(y) - End Sub -End Class -"); - - // Member extraction tests await VerifyVB.VerifyCodeFixAsync(@" Imports System @@ -359,7 +371,7 @@ End Class } [Fact] - public async Task IgnoreOtherArgs_NoDiagnosticCS() + public async Task IgnoreOtherArgs_NoDiagnostic() { // A string await VerifyCS.VerifyAnalyzerAsync(@" @@ -372,6 +384,16 @@ public void B() Console.WriteLine(""Lorem ipsum""); } } +"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(""Lorem ipsum"") + End Sub +End Class "); // Test another type to be extra safe @@ -385,6 +407,16 @@ public void B() Console.WriteLine(123); } } +"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class A + Public Sub B() + Console.WriteLine(123) + End Sub +End Class "); // Non-literal array @@ -401,6 +433,22 @@ public void B() } "); + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + +Public Class A + Public Sub B() + Dim str As String = ""Lorem ipsum"" + Console.WriteLine({ str }) + End Sub +End Class +"); + } + + [Fact] + public async Task IgnoreReadonlySpan_NoDiagnostic() + { + // A ReadOnlySpan, which is already optimized await VerifyCS.VerifyAnalyzerAsync(@" using System; @@ -417,7 +465,11 @@ private void C(ReadOnlySpan span) } } "); + } + [Fact] + public async Task IgnoreParams_NoDiagnostic() + { // Params arguments await VerifyCS.VerifyAnalyzerAsync(@" using System; @@ -450,44 +502,6 @@ private void C(params bool[] booleans) { } } -"); - } - - [Fact] - public async Task IgnoreOtherArgs_NoDiagnosticVB() - { - // A string - await VerifyVB.VerifyAnalyzerAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine(""Lorem ipsum"") - End Sub -End Class -"); - - // Test another type to be extra safe - await VerifyVB.VerifyAnalyzerAsync(@" -Imports System - -Public Class A - Public Sub B() - Console.WriteLine(123) - End Sub -End Class -"); - - // Non-literal array - await VerifyVB.VerifyAnalyzerAsync(@" -Imports System - -Public Class A - Public Sub B() - Dim str As String = ""Lorem ipsum"" - Console.WriteLine({ str }) - End Sub -End Class "); } } From 4e61c9d1c2349831818ddb499906c3356a5f004b Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Wed, 25 May 2022 22:45:57 -0400 Subject: [PATCH 48/64] Edited tests, ran dotnet msbuild /t:pack --- .../Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs | 2 +- src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md | 1 - src/NetAnalyzers/RulesMissingDocumentation.md | 2 +- .../Runtime/AvoidConstArraysTests.cs | 6 +++--- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index 2f568b4e36..782b7a7d6f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -129,4 +129,4 @@ public override void Initialize(AnalysisContext context) }); } } -} \ No newline at end of file +} diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md index c3eefa5a7e..3fbdfe5618 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md @@ -1539,7 +1539,6 @@ When a type is not accessible outside its assembly and has no subtypes within it ## [CA1853](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1853): Unnecessary call to 'Dictionary.ContainsKey(key)' Do not guard 'Dictionary.Remove(key)' with 'Dictionary.ContainsKey(key)'. The former already checks whether the key exists, and will not throw if it does not. ->>>>>>> 04edea1ac503805a8d9a6959ec18e20bff506bb5 |Item|Value| |-|-| diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md index f8a6ce90ff..61d359af1c 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -9,4 +9,4 @@ CA1852 | | Unnecessary call to 'Dictionary.ContainsKey(key)' | CA1854 | | Avoid constant arrays as arguments | CA2019 | | Improper 'ThreadStatic' field initialization | -CA2259 | | 'ThreadStatic' only affects static fields | \ No newline at end of file +CA2259 | | 'ThreadStatic' only affects static fields | diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 8988da055f..afc2b66013 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -326,12 +326,12 @@ public void B() public class A { + private static readonly int[] valueArray0 = new[]{ 1, 2, 3 }; private static readonly string value = ""hello""; private static readonly int[] valueArray = new[]{ -2, -1, 0 }; private static readonly bool[] valueArray1 = new[]{ true, false, true }; private static readonly int x = 1; - private static readonly int[] valueArray0 = new[]{ 1, 2, 3 }; public void B() { @@ -357,11 +357,11 @@ End Class Imports System Public Class A + Private Shared ReadOnly valueArray0 As Integer() = {1, 2, 3} Private Shared ReadOnly value As String = ""hello"" Private Shared ReadOnly valueArray As Integer() = {-2, -1, 0} Private Shared ReadOnly valueArray1 As Boolean() = {True, False, True} Private Shared ReadOnly x As Integer = 1 - Private Shared ReadOnly valueArray0 As Integer() = {1, 2, 3} Public Sub B() Console.WriteLine(valueArray0) @@ -505,4 +505,4 @@ private void C(params bool[] booleans) "); } } -} \ No newline at end of file +} From ea6c6207762e805262fcf90a2d4ceac07561c292 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Fri, 27 May 2022 17:00:44 -0400 Subject: [PATCH 49/64] Update extraction, apply feedback --- .../MicrosoftNetCoreAnalyzersResources.resx | 4 +- .../Runtime/AvoidConstArrays.Fixer.cs | 48 +++++++++------ .../Runtime/AvoidConstArrays.cs | 33 +++++++---- .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 8 +-- .../MicrosoftNetCoreAnalyzersResources.de.xlf | 8 +-- .../MicrosoftNetCoreAnalyzersResources.es.xlf | 8 +-- .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 8 +-- .../MicrosoftNetCoreAnalyzersResources.it.xlf | 8 +-- .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 8 +-- .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 8 +-- .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 8 +-- ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 8 +-- .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 8 +-- .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 8 +-- ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 8 +-- ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 8 +-- .../Runtime/AvoidConstArraysTests.cs | 59 ++++++++++++------- src/Utilities/Compiler/WellKnownTypeNames.cs | 1 + 18 files changed, 143 insertions(+), 106 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index fa9d4599ab..57b2e093e5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -253,11 +253,11 @@ Extract to static readonly field - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. {Locked="static readonly"} - Make any constant arrays passed as arguments into 'static readonly' fields + Prefer 'static readonly' fields over local constant array arguments {Locked="static readonly"} diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 6d63b3620e..a9e6959c83 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -43,23 +43,21 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) string title = MicrosoftNetCoreAnalyzersResources.AvoidConstArraysCodeFixTitle; context.RegisterCodeFix(CodeAction.Create( title, - async c => await ExtractConstArrayAsync(node, model, editor, generator, context.Diagnostics.First().Properties, c).ConfigureAwait(false), + async c => await ExtractConstArrayAsync(root, node, model, editor, generator, context.Diagnostics.First().Properties, c).ConfigureAwait(false), equivalenceKey: title), context.Diagnostics); } - private static async Task ExtractConstArrayAsync(SyntaxNode node, SemanticModel model, DocumentEditor editor, - SyntaxGenerator generator, ImmutableDictionary properties, CancellationToken cancellationToken) + private static Task ExtractConstArrayAsync(SyntaxNode root, SyntaxNode node, SemanticModel model, DocumentEditor editor, + SyntaxGenerator generator, ImmutableDictionary properties, CancellationToken cancellationToken) { IArrayCreationOperation arrayArgument = GetArrayCreationOperation(node, model, cancellationToken, out bool isInvoked); - IEnumerable members = model.GetEnclosingSymbol(node.SpanStart, cancellationToken).ContainingType.GetMembers(); + INamedTypeSymbol containingType = model.GetEnclosingSymbol(node.SpanStart, cancellationToken).ContainingType; // Get a valid member name for the extracted constant - string newMemberName = GetExtractedMemberName( - members.Where(x => x is IFieldSymbol).Select(x => x.Name), - properties["paramName"] ?? GetMemberNameFromType(arrayArgument)); + string newMemberName = GetExtractedMemberName(containingType.MemberNames, properties["paramName"] ?? GetMemberNameFromType(arrayArgument)); - // Get method containing the symbol that is being diagnosed. + // Get method containing the symbol that is being diagnosed IOperation? methodContext = arrayArgument.GetAncestor(OperationKind.MethodBody); methodContext ??= arrayArgument.GetAncestor(OperationKind.Block); // VB methods have a different structure than CS methods @@ -78,10 +76,18 @@ private static async Task ExtractConstArrayAsync(SyntaxNode node, Sema newMember = newMember.FormatForExtraction(methodContext.Syntax); } - // Insert the new extracted member before the first member - SyntaxNode firstMemberNode = await members.First().DeclaringSyntaxReferences.First() - .GetSyntaxAsync(cancellationToken).ConfigureAwait(false); - editor.InsertBefore(generator.GetDeclaration(firstMemberNode), newMember); + ISymbol lastFieldOrPropertSymbol = containingType.GetMembers().LastOrDefault(x => x is IFieldSymbol || x is IPropertySymbol); + if (lastFieldOrPropertSymbol is not null) + { + // Insert after fields or properties + SyntaxNode lastFieldOrPropertyNode = root.FindNode(lastFieldOrPropertSymbol.Locations.First().SourceSpan); + editor.InsertAfter(generator.GetDeclaration(lastFieldOrPropertyNode), newMember); + } + else + { + // No fields or properties, insert right before the containing method for simplicity + editor.InsertBefore(methodContext?.Syntax, newMember); + } // Replace argument with a reference to our new member SyntaxNode identifier = generator.IdentifierName(newMemberName); @@ -91,12 +97,12 @@ private static async Task ExtractConstArrayAsync(SyntaxNode node, Sema } else { - editor.ReplaceNode(node, generator.Argument(identifier) - .WithTrailingTrivia(arrayArgument.Syntax.GetTrailingTrivia())); // add any extra trivia that was after the original argument + // add any extra trivia that was after the original argument + editor.ReplaceNode(node, generator.Argument(identifier).WithTrailingTrivia(arrayArgument.Syntax.GetTrailingTrivia())); } // Return changed document - return editor.GetChangedDocument(); + return Task.FromResult(editor.GetChangedDocument()); } private static IArrayCreationOperation GetArrayCreationOperation(SyntaxNode node, SemanticModel model, CancellationToken cancellationToken, @@ -150,11 +156,15 @@ private static string GetMemberNameFromType(IArrayCreationOperation arrayCreatio private static Accessibility GetAccessibility(ISymbol? methodSymbol) { - if (methodSymbol is not null && Enum.TryParse(methodSymbol.GetResultantVisibility().ToString(), out Accessibility accessibility)) + if (methodSymbol is not null) { - return accessibility == Accessibility.Public - ? Accessibility.Private // public accessibility not wanted for fields - : accessibility; + // If private or public, return private since public accessibility not wanted for fields by default + return methodSymbol.GetResultantVisibility() switch + { + // Return internal if internal + SymbolVisibility.Internal => Accessibility.Internal, + _ => Accessibility.Private + }; } return Accessibility.Private; } diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index 782b7a7d6f..9e97d12bed 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -40,18 +40,20 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationStartAction(context => { INamedTypeSymbol? readonlySpanType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReadOnlySpan1); - INamedTypeSymbol? functionType = context.Compilation.GetOrCreateTypeByMetadataName("System.Func`2"); + INamedTypeSymbol? functionType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemFunc2); // Analyzes an argument operation context.RegisterOperationAction(context => { - bool isDirectlyInsideLambda = false; IArgumentOperation? argumentOperation; if (context.Operation is IArrayCreationOperation arrayCreationOperation) // For arrays passed as arguments { argumentOperation = arrayCreationOperation.GetAncestor(OperationKind.Argument); - if (argumentOperation is null || argumentOperation.Parameter.IsParams) + + // If no argument, return + // If argument is passed as a params array but isn't itself an array, return + if (argumentOperation is null || (argumentOperation.Parameter.IsParams && arrayCreationOperation.IsImplicit)) { return; } @@ -93,33 +95,38 @@ public override void Initialize(AnalysisContext context) return; } + // Must be literal array + if (arrayCreationOperation.Initializer.ElementValues.Any(x => x is not ILiteralOperation)) + { + return; + } + + string? paramName = null; if (argumentOperation is not null) { ITypeSymbol originalDefinition = argumentOperation.Parameter.Type.OriginalDefinition; // Can't be a ReadOnlySpan, as those are already optimized - if (readonlySpanType is not null && originalDefinition.Equals(readonlySpanType)) + if (SymbolEqualityComparer.Default.Equals(readonlySpanType, originalDefinition)) { return; } // Check if the parameter is a function so the name can be set to null // Otherwise, the parameter name doesn't reflect the array creation as well - if (functionType is not null) + bool isDirectlyInsideLambda = originalDefinition.Equals(functionType); + + // Parameter shouldn't have same containing type as the context, to prevent naming ambiguity + // Ignore parameter name if we're inside a lambda function + if (!isDirectlyInsideLambda && !argumentOperation.Parameter.ContainingType.Equals(context.ContainingSymbol.ContainingType)) { - isDirectlyInsideLambda = originalDefinition.Equals(functionType); + paramName = argumentOperation.Parameter.Name; } } - // Must be literal array - if (arrayCreationOperation.Initializer.ElementValues.Any(x => x is not ILiteralOperation)) - { - return; - } - Dictionary properties = new() { - { "paramName", isDirectlyInsideLambda ? null : argumentOperation?.Parameter?.Name } + { "paramName", paramName } }; context.ReportDiagnostic(arrayCreationOperation.CreateDiagnostic(Rule, properties.ToImmutableDictionary())); 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 730b42043e..eb75c28d2d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -58,13 +58,13 @@ - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. {Locked="static readonly"} - Make any constant arrays passed as arguments into 'static readonly' fields - Make any constant arrays passed as arguments into 'static readonly' fields + Prefer 'static readonly' fields over local constant array arguments + Prefer 'static readonly' fields over local constant array arguments {Locked="static readonly"} 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 49b31c7368..e430d72afe 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -58,13 +58,13 @@ - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. {Locked="static readonly"} - Make any constant arrays passed as arguments into 'static readonly' fields - Make any constant arrays passed as arguments into 'static readonly' fields + Prefer 'static readonly' fields over local constant array arguments + Prefer 'static readonly' fields over local constant array arguments {Locked="static readonly"} 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 7d3004917a..8fcb7eaa46 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -58,13 +58,13 @@ - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. {Locked="static readonly"} - Make any constant arrays passed as arguments into 'static readonly' fields - Make any constant arrays passed as arguments into 'static readonly' fields + Prefer 'static readonly' fields over local constant array arguments + Prefer 'static readonly' fields over local constant array arguments {Locked="static readonly"} 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 fb77773362..6a07d19000 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -58,13 +58,13 @@ - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. {Locked="static readonly"} - Make any constant arrays passed as arguments into 'static readonly' fields - Make any constant arrays passed as arguments into 'static readonly' fields + Prefer 'static readonly' fields over local constant array arguments + Prefer 'static readonly' fields over local constant array arguments {Locked="static readonly"} 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 a11886f793..752dbeb49c 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -58,13 +58,13 @@ - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. {Locked="static readonly"} - Make any constant arrays passed as arguments into 'static readonly' fields - Make any constant arrays passed as arguments into 'static readonly' fields + Prefer 'static readonly' fields over local constant array arguments + Prefer 'static readonly' fields over local constant array arguments {Locked="static readonly"} 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 23d4ac01b9..9ac7bb2e22 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -58,13 +58,13 @@ - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. {Locked="static readonly"} - Make any constant arrays passed as arguments into 'static readonly' fields - Make any constant arrays passed as arguments into 'static readonly' fields + Prefer 'static readonly' fields over local constant array arguments + Prefer 'static readonly' fields over local constant array arguments {Locked="static readonly"} 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 fc897216e7..45eadd8280 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -58,13 +58,13 @@ - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. {Locked="static readonly"} - Make any constant arrays passed as arguments into 'static readonly' fields - Make any constant arrays passed as arguments into 'static readonly' fields + Prefer 'static readonly' fields over local constant array arguments + Prefer 'static readonly' fields over local constant array arguments {Locked="static readonly"} 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 78d9cb8aab..656a6a7e7b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -58,13 +58,13 @@ - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. {Locked="static readonly"} - Make any constant arrays passed as arguments into 'static readonly' fields - Make any constant arrays passed as arguments into 'static readonly' fields + Prefer 'static readonly' fields over local constant array arguments + Prefer 'static readonly' fields over local constant array arguments {Locked="static readonly"} 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 e0218f479a..e26a23a33a 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 @@ -58,13 +58,13 @@ - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. {Locked="static readonly"} - Make any constant arrays passed as arguments into 'static readonly' fields - Make any constant arrays passed as arguments into 'static readonly' fields + Prefer 'static readonly' fields over local constant array arguments + Prefer 'static readonly' fields over local constant array arguments {Locked="static readonly"} 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 2df0750a3c..5a4ae8a1a2 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -58,13 +58,13 @@ - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. {Locked="static readonly"} - Make any constant arrays passed as arguments into 'static readonly' fields - Make any constant arrays passed as arguments into 'static readonly' fields + Prefer 'static readonly' fields over local constant array arguments + Prefer 'static readonly' fields over local constant array arguments {Locked="static readonly"} 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 708c3ee22d..f1765f660a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -58,13 +58,13 @@ - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. {Locked="static readonly"} - Make any constant arrays passed as arguments into 'static readonly' fields - Make any constant arrays passed as arguments into 'static readonly' fields + Prefer 'static readonly' fields over local constant array arguments + Prefer 'static readonly' fields over local constant array arguments {Locked="static readonly"} 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 0df019b867..aa7ef5ec93 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 @@ -58,13 +58,13 @@ - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. {Locked="static readonly"} - Make any constant arrays passed as arguments into 'static readonly' fields - Make any constant arrays passed as arguments into 'static readonly' fields + Prefer 'static readonly' fields over local constant array arguments + Prefer 'static readonly' fields over local constant array arguments {Locked="static readonly"} 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 46fa3c7528..7deef45936 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 @@ -58,13 +58,13 @@ - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. - Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. + Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. {Locked="static readonly"} - Make any constant arrays passed as arguments into 'static readonly' fields - Make any constant arrays passed as arguments into 'static readonly' fields + Prefer 'static readonly' fields over local constant array arguments + Prefer 'static readonly' fields over local constant array arguments {Locked="static readonly"} diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index afc2b66013..7a3d9a82f7 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -301,6 +301,43 @@ End Class "); } + [Fact] + public async Task IdentifyConstArrays_ParamsArrayOfLiterals() + { + // A params argument passed as an array of literals + await VerifyCS.VerifyCodeFixAsync(@" +using System; + +public class A +{ + public void B() + { + C({|CA1854:new bool[] { true, false }|}); + } + + private void C(params bool[] booleans) + { + } +} +", @" +using System; + +public class A +{ + private static readonly bool[] booleanArray = new bool[] { true, false }; + + public void B() + { + C(booleanArray); + } + + private void C(params bool[] booleans) + { + } +} +"); + } + [Fact] public async Task IdentifyConstArrays_MemberExtractionTest() { @@ -326,12 +363,12 @@ public void B() public class A { - private static readonly int[] valueArray0 = new[]{ 1, 2, 3 }; private static readonly string value = ""hello""; private static readonly int[] valueArray = new[]{ -2, -1, 0 }; private static readonly bool[] valueArray1 = new[]{ true, false, true }; private static readonly int x = 1; + private static readonly int[] valueArray0 = new[]{ 1, 2, 3 }; public void B() { @@ -357,11 +394,11 @@ End Class Imports System Public Class A - Private Shared ReadOnly valueArray0 As Integer() = {1, 2, 3} Private Shared ReadOnly value As String = ""hello"" Private Shared ReadOnly valueArray As Integer() = {-2, -1, 0} Private Shared ReadOnly valueArray1 As Boolean() = {True, False, True} Private Shared ReadOnly x As Integer = 1 + Private Shared ReadOnly valueArray0 As Integer() = {1, 2, 3} Public Sub B() Console.WriteLine(valueArray0) @@ -448,7 +485,6 @@ End Class [Fact] public async Task IgnoreReadonlySpan_NoDiagnostic() { - // A ReadOnlySpan, which is already optimized await VerifyCS.VerifyAnalyzerAsync(@" using System; @@ -485,23 +521,6 @@ private void C(params bool[] booleans) { } } -"); - - // A params argument, even as a literal array - await VerifyCS.VerifyAnalyzerAsync(@" -using System; - -public class A -{ - public void B() - { - C(new bool[] { true, false }); - } - - private void C(params bool[] booleans) - { - } -} "); } } diff --git a/src/Utilities/Compiler/WellKnownTypeNames.cs b/src/Utilities/Compiler/WellKnownTypeNames.cs index 124128a89b..181b121659 100644 --- a/src/Utilities/Compiler/WellKnownTypeNames.cs +++ b/src/Utilities/Compiler/WellKnownTypeNames.cs @@ -211,6 +211,7 @@ internal static class WellKnownTypeNames public const string SystemException = "System.Exception"; public const string SystemExecutionEngineException = "System.ExecutionEngineException"; public const string SystemFlagsAttribute = "System.FlagsAttribute"; + public const string SystemFunc2 = "System.Func`2"; public const string SystemGC = "System.GC"; public const string SystemGlobalizationCultureInfo = "System.Globalization.CultureInfo"; public const string SystemGuid = "System.Guid"; From e8ae4a8cb971656856ab3c9e5eba5c933f4bcf9c Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Fri, 27 May 2022 17:27:16 -0400 Subject: [PATCH 50/64] Ran dotnet msbuild /t:Pack --- src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md | 2 +- src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md index 3fbdfe5618..5c84d2681a 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md @@ -1550,7 +1550,7 @@ Do not guard 'Dictionary.Remove(key)' with 'Dictionary.ContainsKey(key)'. The fo ## [CA1854](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1854): Avoid constant arrays as arguments -Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'. +Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. |Item|Value| |-|-| diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif index bcc5ff1464..a953d31597 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif @@ -2863,7 +2863,7 @@ "CA1854": { "id": "CA1854", "shortDescription": "Avoid constant arrays as arguments", - "fullDescription": "Having a constant array passed as an argument is not ideally performant. Extract constant arrays as 'static readonly'.", + "fullDescription": "Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance.", "defaultLevel": "note", "helpUri": "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1854", "properties": { From 02759e6370fc3438895b23348385157f90e86306 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Sat, 28 May 2022 18:44:12 -0400 Subject: [PATCH 51/64] Added batch fix test --- .../Runtime/AvoidConstArraysTests.cs | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 7a3d9a82f7..4a135ef6fc 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -306,8 +306,6 @@ public async Task IdentifyConstArrays_ParamsArrayOfLiterals() { // A params argument passed as an array of literals await VerifyCS.VerifyCodeFixAsync(@" -using System; - public class A { public void B() @@ -320,8 +318,6 @@ private void C(params bool[] booleans) } } ", @" -using System; - public class A { private static readonly bool[] booleanArray = new bool[] { true, false }; @@ -338,6 +334,46 @@ private void C(params bool[] booleans) "); } + [Fact] + public async Task IdentifyConstArrays_ParamsArrays() + { + // A params array of arrays + // Doubles as test for batch fix and two or more errors on same line + await new VerifyCS.Test() + { + TestCode = @" +public class A +{ + public void B() + { + C({|CA1854:new bool[] { true, false }|}, {|CA1854:new bool[] { false, true }|}); + } + + private void C(params bool[][] booleans) + { + } +} +", + NumberOfFixAllIterations = 2, + FixedCode = @" +public class A +{ + private static readonly bool[] booleanArray = new bool[] { true, false }; + private static readonly bool[] booleanArray0 = new bool[] { false, true }; + + public void B() + { + C(booleanArray, booleanArray0); + } + + private void C(params bool[][] booleans) + { + } +} +" + }.RunAsync(); + } + [Fact] public async Task IdentifyConstArrays_MemberExtractionTest() { @@ -508,8 +544,6 @@ public async Task IgnoreParams_NoDiagnostic() { // Params arguments await VerifyCS.VerifyAnalyzerAsync(@" -using System; - public class A { public void B() From 4174f605a29e96c1651f6138e06c9da78c1322da Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Mon, 30 May 2022 14:38:30 -0400 Subject: [PATCH 52/64] Update GetAccessibility --- .../Runtime/AvoidConstArrays.Fixer.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index a9e6959c83..fe5887e161 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -156,17 +156,13 @@ private static string GetMemberNameFromType(IArrayCreationOperation arrayCreatio private static Accessibility GetAccessibility(ISymbol? methodSymbol) { - if (methodSymbol is not null) + // If private or public, return private since public accessibility not wanted for fields by default + return methodSymbol?.GetResultantVisibility() switch { - // If private or public, return private since public accessibility not wanted for fields by default - return methodSymbol.GetResultantVisibility() switch - { - // Return internal if internal - SymbolVisibility.Internal => Accessibility.Internal, - _ => Accessibility.Private - }; - } - return Accessibility.Private; + // Return internal if internal + SymbolVisibility.Internal => Accessibility.Internal, + _ => Accessibility.Private + }; } } From 06794e9cd0ad45063b949edd3a019cfe9b63cf5c Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Sat, 4 Jun 2022 18:57:45 -0400 Subject: [PATCH 53/64] Updated rule id --- .../Runtime/AvoidConstArrays.Fixer.cs | 2 +- .../Runtime/AvoidConstArrays.cs | 4 +-- .../Runtime/AvoidConstArraysTests.cs | 30 +++++++++---------- .../DiagnosticCategoryAndIdRanges.txt | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index fe5887e161..cab93b6193 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -19,7 +19,7 @@ namespace Microsoft.NetCore.Analyzers.Runtime { /// - /// CA1854: Avoid constant arrays as arguments. Replace with static readonly arrays. + /// CA1855: Avoid constant arrays as arguments. Replace with static readonly arrays. /// [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] public sealed class AvoidConstArraysFixer : CodeFixProvider diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index 9e97d12bed..b1a027bc52 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -14,12 +14,12 @@ namespace Microsoft.NetCore.Analyzers.Runtime using static MicrosoftNetCoreAnalyzersResources; /// - /// CA1854: Avoid constant arrays as arguments. Replace with static readonly arrays. + /// CA1855: Avoid constant arrays as arguments. Replace with static readonly arrays. /// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class AvoidConstArraysAnalyzer : DiagnosticAnalyzer { - internal const string RuleId = "CA1854"; + internal const string RuleId = "CA1855"; internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(RuleId, CreateLocalizableResourceString(nameof(AvoidConstArraysTitle)), diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 4a135ef6fc..f13a6bf492 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -23,7 +23,7 @@ public class A { public void B() { - Console.WriteLine({|CA1854:new[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1855:new[]{ 1, 2, 3 }|}); } } ", @" @@ -45,7 +45,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine({|CA1854:{1, 2, 3}|}) + Console.WriteLine({|CA1855:{1, 2, 3}|}) End Sub End Class ", @" @@ -70,7 +70,7 @@ public class A { public void B() { - Console.WriteLine({|CA1854:new int[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1855:new int[]{ 1, 2, 3 }|}); } } ", @" @@ -92,7 +92,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine({|CA1854:New Integer() {1, 2, 3}|}) + Console.WriteLine({|CA1855:New Integer() {1, 2, 3}|}) End Sub End Class ", @" @@ -117,7 +117,7 @@ public class A { public void B() { - Console.WriteLine(string.Join("" "", {|CA1854:new[] { ""Cake"", ""is"", ""good"" }|})); + Console.WriteLine(string.Join("" "", {|CA1855:new[] { ""Cake"", ""is"", ""good"" }|})); } } ", @" @@ -139,7 +139,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine(String.Join("" ""c, {|CA1854:{""Cake"", ""is"", ""good""}|})) + Console.WriteLine(String.Join("" ""c, {|CA1855:{""Cake"", ""is"", ""good""}|})) End Sub End Class ", @" @@ -164,7 +164,7 @@ public class A { public void B() { - Console.WriteLine(string.Join("" "", {|CA1854:new[] { ""a"", ""b"" }|} /* test comment */)); + Console.WriteLine(string.Join("" "", {|CA1855:new[] { ""a"", ""b"" }|} /* test comment */)); } } ", @" @@ -194,7 +194,7 @@ public class A public void B() { var x = new string[] { ""a"", ""b"" }; - var y = x.Select(z => {|CA1854:new[] { ""c"" }|}); + var y = x.Select(z => {|CA1855:new[] { ""c"" }|}); } } ", @" @@ -226,7 +226,7 @@ public class A public void B() { var x = new string[] { ""a"", ""b"" }; - var y = x.Select(z => {|CA1854:new[] { ""c"" }|}.First()); + var y = x.Select(z => {|CA1855:new[] { ""c"" }|}.First()); } } ", @" @@ -257,7 +257,7 @@ public class A { public void B() { - string y = {|CA1854:new[] { ""a"", ""b"", ""c"" }|}.First(); + string y = {|CA1855:new[] { ""a"", ""b"", ""c"" }|}.First(); Console.WriteLine(y); } } @@ -283,7 +283,7 @@ Imports System.Linq Public Class A Public Sub B() - Dim y As String = {|CA1854:{""a"", ""b"", ""c""}|}.First() + Dim y As String = {|CA1855:{""a"", ""b"", ""c""}|}.First() Console.WriteLine(y) End Sub End Class @@ -310,7 +310,7 @@ public class A { public void B() { - C({|CA1854:new bool[] { true, false }|}); + C({|CA1855:new bool[] { true, false }|}); } private void C(params bool[] booleans) @@ -346,7 +346,7 @@ public class A { public void B() { - C({|CA1854:new bool[] { true, false }|}, {|CA1854:new bool[] { false, true }|}); + C({|CA1855:new bool[] { true, false }|}, {|CA1855:new bool[] { false, true }|}); } private void C(params bool[][] booleans) @@ -391,7 +391,7 @@ public class A public void B() { - Console.WriteLine({|CA1854:new[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1855:new[]{ 1, 2, 3 }|}); } } ", @" @@ -423,7 +423,7 @@ Private Shared ReadOnly valueArray1 As Boolean() = {True, False, True} Private Shared ReadOnly x As Integer = 1 Public Sub B() - Console.WriteLine({|CA1854:{1, 2, 3}|}) + Console.WriteLine({|CA1855:{1, 2, 3}|}) End Sub End Class ", @" diff --git a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt index 428f549aad..6ef35bf04c 100644 --- a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt +++ b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt @@ -12,7 +12,7 @@ Design: CA2210, CA1000-CA1070 Globalization: CA2101, CA1300-CA1311 Mobility: CA1600-CA1601 -Performance: HA, CA1800-CA1854 +Performance: HA, CA1800-CA1855 Security: CA2100-CA2153, CA2300-CA2330, CA3000-CA3147, CA5300-CA5405 Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2259 Naming: CA1700-CA1727 From 41ddd2009477f05c54c7bed6db9f8756fcb2fe62 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Sat, 26 Nov 2022 09:33:25 -0500 Subject: [PATCH 54/64] Add namespace tests --- .../Runtime/AvoidConstArraysTests.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index f13a6bf492..3caa57ac73 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -85,6 +85,36 @@ public void B() Console.WriteLine(value); } } +"); + + await VerifyCS.VerifyCodeFixAsync(@" +using System; + +namespace Z +{ + public class A + { + public void B() + { + Console.WriteLine({|CA1855:new int[]{ 1, 2, 3 }|}); + } + } +} +", @" +using System; + +namespace Z +{ + public class A + { + private static readonly int[] value = new int[]{ 1, 2, 3 }; + + public void B() + { + Console.WriteLine(value); + } + } +} "); await VerifyVB.VerifyCodeFixAsync(@" From de8d4b77daaedff8314b8374450b922a8b112bbe Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Sat, 26 Nov 2022 09:56:47 -0500 Subject: [PATCH 55/64] Linting --- src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index b6b5e4c64b..9072c8ebfd 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -6,4 +6,4 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- CA1856 | Performance | Error | ConstantExpectedAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1856) CA1857 | Performance | Warning | ConstantExpectedAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1857) -CA1858 | Performance | Info | AvoidConstArrays, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1858) \ No newline at end of file +CA1858 | Performance | Info | AvoidConstArrays, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1858) From a2e54cdf2a985c57362b36439b409046795ac06e Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Tue, 14 Mar 2023 22:29:26 -0400 Subject: [PATCH 56/64] updated rule id --- .../MicrosoftNetCoreAnalyzersResources.resx | 4 -- .../Runtime/AvoidConstArrays.Fixer.cs | 2 +- .../Runtime/AvoidConstArrays.cs | 4 +- .../Microsoft.CodeAnalysis.NetAnalyzers.sarif | 40 +++++++++---------- .../Runtime/AvoidConstArraysTests.cs | 32 +++++++-------- 5 files changed, 39 insertions(+), 43 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 38d16d5497..7c2aaced9d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1976,9 +1976,6 @@ Widening and user defined conversions are not supported with generic types. Starting with .NET 7 the explicit conversion '{0}' will throw when overflowing in a checked context. Wrap the expression with an 'unchecked' statement to restore the .NET 6 behavior. -<<<<<<< HEAD - -======= Use 'StartsWith' @@ -2058,4 +2055,3 @@ Widening and user defined conversions are not supported with generic types.Prefer an 'IsEmpty' check rather than using 'Any()', both for clarity and for performance ->>>>>>> cc75004c017e7bd929ab2bb3dee21d1ed45b7753 diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 3d9161a1c3..f303044b42 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -19,7 +19,7 @@ namespace Microsoft.NetCore.Analyzers.Runtime { /// - /// CA1858: Avoid constant arrays as arguments. Replace with static readonly arrays. + /// CA1861: Avoid constant arrays as arguments. Replace with static readonly arrays. /// [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] public sealed class AvoidConstArraysFixer : CodeFixProvider diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index c8a80083ff..816a59d374 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -14,12 +14,12 @@ namespace Microsoft.NetCore.Analyzers.Runtime using static MicrosoftNetCoreAnalyzersResources; /// - /// CA1858: Avoid constant arrays as arguments. Replace with static readonly arrays. + /// CA1861: Avoid constant arrays as arguments. Replace with static readonly arrays. /// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class AvoidConstArraysAnalyzer : DiagnosticAnalyzer { - internal const string RuleId = "CA1858"; + internal const string RuleId = "CA1861"; internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(RuleId, CreateLocalizableResourceString(nameof(AvoidConstArraysTitle)), diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif index ee277066a7..e1c44da50b 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif @@ -342,26 +342,6 @@ ] } }, - "CA1858": { - "id": "CA1858", - "shortDescription": "Avoid constant arrays as arguments", - "fullDescription": "Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance.", - "defaultLevel": "note", - "helpUri": "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1858", - "properties": { - "category": "Performance", - "isEnabledByDefault": true, - "typeName": "AvoidConstArraysAnalyzer", - "languages": [ - "C#", - "Visual Basic" - ], - "tags": [ - "Telemetry", - "EnabledRuleInAggressiveMode" - ] - } - }, "CA2014": { "id": "CA2014", "shortDescription": "Do not use stackalloc in loops", @@ -3155,6 +3135,26 @@ ] } }, + "CA1861": { + "id": "CA1861", + "shortDescription": "Avoid constant arrays as arguments", + "fullDescription": "Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance.", + "defaultLevel": "note", + "helpUri": "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "AvoidConstArraysAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry", + "EnabledRuleInAggressiveMode" + ] + } + }, "CA2000": { "id": "CA2000", "shortDescription": "Dispose objects before losing scope", diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index cf61aba944..60d4bfe65e 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -23,7 +23,7 @@ public class A { public void B() { - Console.WriteLine({|CA1858:new[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1861:new[]{ 1, 2, 3 }|}); } } ", @" @@ -45,7 +45,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine({|CA1858:{1, 2, 3}|}) + Console.WriteLine({|CA1861:{1, 2, 3}|}) End Sub End Class ", @" @@ -70,7 +70,7 @@ public class A { public void B() { - Console.WriteLine({|CA1858:new int[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1861:new int[]{ 1, 2, 3 }|}); } } ", @" @@ -96,7 +96,7 @@ public class A { public void B() { - Console.WriteLine({|CA1858:new int[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1861:new int[]{ 1, 2, 3 }|}); } } } @@ -122,7 +122,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine({|CA1858:New Integer() {1, 2, 3}|}) + Console.WriteLine({|CA1861:New Integer() {1, 2, 3}|}) End Sub End Class ", @" @@ -147,7 +147,7 @@ public class A { public void B() { - Console.WriteLine(string.Join("" "", {|CA1858:new[] { ""Cake"", ""is"", ""good"" }|})); + Console.WriteLine(string.Join("" "", {|CA1861:new[] { ""Cake"", ""is"", ""good"" }|})); } } ", @" @@ -169,7 +169,7 @@ Imports System Public Class A Public Sub B() - Console.WriteLine(String.Join("" ""c, {|CA1858:{""Cake"", ""is"", ""good""}|})) + Console.WriteLine(String.Join("" ""c, {|CA1861:{""Cake"", ""is"", ""good""}|})) End Sub End Class ", @" @@ -194,7 +194,7 @@ public class A { public void B() { - Console.WriteLine(string.Join("" "", {|CA1858:new[] { ""a"", ""b"" }|} /* test comment */)); + Console.WriteLine(string.Join("" "", {|CA1861:new[] { ""a"", ""b"" }|} /* test comment */)); } } ", @" @@ -224,7 +224,7 @@ public class A public void B() { var x = new string[] { ""a"", ""b"" }; - var y = x.Select(z => {|CA1858:new[] { ""c"" }|}); + var y = x.Select(z => {|CA1861:new[] { ""c"" }|}); } } ", @" @@ -256,7 +256,7 @@ public class A public void B() { var x = new string[] { ""a"", ""b"" }; - var y = x.Select(z => {|CA1858:new[] { ""c"" }|}.First()); + var y = x.Select(z => {|CA1861:new[] { ""c"" }|}.First()); } } ", @" @@ -287,7 +287,7 @@ public class A { public void B() { - string y = {|CA1858:new[] { ""a"", ""b"", ""c"" }|}.First(); + string y = {|CA1861:new[] { ""a"", ""b"", ""c"" }|}.First(); Console.WriteLine(y); } } @@ -313,7 +313,7 @@ Imports System.Linq Public Class A Public Sub B() - Dim y As String = {|CA1858:{""a"", ""b"", ""c""}|}.First() + Dim y As String = {|CA1861:{""a"", ""b"", ""c""}|}.First() Console.WriteLine(y) End Sub End Class @@ -340,7 +340,7 @@ public class A { public void B() { - C({|CA1858:new bool[] { true, false }|}); + C({|CA1861:new bool[] { true, false }|}); } private void C(params bool[] booleans) @@ -376,7 +376,7 @@ public class A { public void B() { - C({|CA1858:new bool[] { true, false }|}, {|CA1858:new bool[] { false, true }|}); + C({|CA1861:new bool[] { true, false }|}, {|CA1861:new bool[] { false, true }|}); } private void C(params bool[][] booleans) @@ -421,7 +421,7 @@ public class A public void B() { - Console.WriteLine({|CA1858:new[]{ 1, 2, 3 }|}); + Console.WriteLine({|CA1861:new[]{ 1, 2, 3 }|}); } } ", @" @@ -453,7 +453,7 @@ Private Shared ReadOnly valueArray1 As Boolean() = {True, False, True} Private Shared ReadOnly x As Integer = 1 Public Sub B() - Console.WriteLine({|CA1858:{1, 2, 3}|}) + Console.WriteLine({|CA1861:{1, 2, 3}|}) End Sub End Class ", @" From cca975ddae13e9bb66712c1de668eaee7a78ae34 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Tue, 14 Mar 2023 22:41:11 -0400 Subject: [PATCH 57/64] formatting to fix linting --- .../Runtime/AvoidConstArrays.Fixer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index f303044b42..2df12ab2b9 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -141,6 +141,7 @@ private static string GetExtractedMemberName(IEnumerable memberNames, st { suffix++; } + return parameterName + suffix; } @@ -173,4 +174,4 @@ internal static SyntaxNode FormatForExtraction(this SyntaxNode node, SyntaxNode return node.HasTrailingTrivia ? node : node.WithTrailingTrivia(previouslyContainingNode.GetTrailingTrivia()); } } -} \ No newline at end of file +} From f239689a0661595c1fcd3671ea87bcbefa696932 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Tue, 14 Mar 2023 23:00:18 -0400 Subject: [PATCH 58/64] fix CI errors pt 1 --- .../Runtime/AvoidConstArrays.Fixer.cs | 2 +- .../Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 2df12ab2b9..feccbb01f9 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -76,7 +76,7 @@ private static Task ExtractConstArrayAsync(SyntaxNode root, SyntaxNode newMember = newMember.FormatForExtraction(methodContext.Syntax); } - ISymbol lastFieldOrPropertSymbol = containingType.GetMembers().LastOrDefault(x => x is IFieldSymbol || x is IPropertySymbol); + ISymbol lastFieldOrPropertSymbol = containingType.GetMembers().LastOrDefault(x => x is IFieldSymbol or IPropertySymbol); if (lastFieldOrPropertSymbol is not null) { // Insert after fields or properties diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index 816a59d374..62bc3157e2 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -77,6 +77,7 @@ public override void Initialize(AnalysisContext context) { return; } + arrayCreationOperation = arrayCreation; } else // An invocation, extension or regular, has an argument, unless it's a VB extension method call From ea8dd59b64a936a6a0a244a19a1a9df4b72c631b Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Tue, 14 Mar 2023 23:04:48 -0400 Subject: [PATCH 59/64] fix CI errors pt 2 --- src/NetAnalyzers/RulesMissingDocumentation.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md index 3de930d1a9..7217157c0a 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -9,7 +9,6 @@ CA1513 | | Incorrect usage of ConstantExpected attribute | CA1857 | | A constant is expected for the parameter | CA1859 | | Use concrete types when possible for improved performance | -CA1860 | | Avoid using 'Enumerable.Any()' extension method -| +CA1860 | | Avoid using 'Enumerable.Any()' extension method | CA1861 | | Avoid constant arrays as arguments | CA2021 | | Do not call Enumerable.Cast\ or Enumerable.OfType\ with incompatible types | From f15b53f8286cafdd1b0de985635e28c1b61b5808 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Tue, 14 Mar 2023 23:51:03 -0400 Subject: [PATCH 60/64] fix CI errors pt 3 --- src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md | 2 +- src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md | 3 ++- src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif | 2 +- src/NetAnalyzers/RulesMissingDocumentation.md | 3 +-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index 8ffb3564be..272ba8e499 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -13,7 +13,7 @@ CA1857 | Performance | Warning | ConstantExpectedAnalyzer, [Documentation](https CA1858 | Performance | Info | UseStartsWithInsteadOfIndexOfComparisonWithZero, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1858) CA1859 | Performance | Info | UseConcreteTypeAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1859) CA1860 | Performance | Info | PreferLengthCountIsEmptyOverAnyAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1860) -CA1861 | Performance | Info | AvoidConstArrays, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861) +CA1861 | Performance | Info | AvoidConstArrays, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861) CA2021 | Reliability | Warning | DoNotCallEnumerableCastOrOfTypeWithIncompatibleTypesAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2021) ### Removed Rules diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md index c3967bc202..78af35eede 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md @@ -1692,7 +1692,7 @@ Prefer using 'IsEmpty', 'Count' or 'Length' properties whichever available, rath |CodeFix|True| --- -## [CA1861](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861): Avoid constant arrays as arguments +## [CA1861](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861): Avoid constant arrays as arguments Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance. @@ -1702,6 +1702,7 @@ Constant arrays passed as arguments are not reused which implies a performance o |Enabled|True| |Severity|Info| |CodeFix|True| +--- ## [CA2000](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2000): Dispose objects before losing scope diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif index e1c44da50b..950dfb4a1e 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif @@ -3140,7 +3140,7 @@ "shortDescription": "Avoid constant arrays as arguments", "fullDescription": "Constant arrays passed as arguments are not reused which implies a performance overhead. Consider extracting them to 'static readonly' fields to improve performance.", "defaultLevel": "note", - "helpUri": "https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861", + "helpUri": "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861", "properties": { "category": "Performance", "isEnabledByDefault": true, diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md index 7217157c0a..cb801d4a81 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -9,6 +9,5 @@ CA1513 | | Incorrect usage of ConstantExpected attribute | CA1857 | | A constant is expected for the parameter | CA1859 | | Use concrete types when possible for improved performance | -CA1860 | | Avoid using 'Enumerable.Any()' extension method | -CA1861 | | Avoid constant arrays as arguments | +CA1861 | | Avoid constant arrays as arguments | CA2021 | | Do not call Enumerable.Cast\ or Enumerable.OfType\ with incompatible types | From 042fefeeaff65e6ae486e0b86698f4ebd737ad42 Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Wed, 15 Mar 2023 15:42:07 -0400 Subject: [PATCH 61/64] perf improvement and added namespaces to all tests --- .../Runtime/AvoidConstArrays.cs | 2 +- .../Runtime/AvoidConstArraysTests.cs | 527 +++++++++++------- 2 files changed, 315 insertions(+), 214 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index 62bc3157e2..6bf438793a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -83,7 +83,7 @@ public override void Initialize(AnalysisContext context) else // An invocation, extension or regular, has an argument, unless it's a VB extension method call { // For VB extension method invocations, find a matching child - arrayCreationOperation = (IArrayCreationOperation)invocationOperation.Descendants() + arrayCreationOperation = (IArrayCreationOperation)invocationDescendants .FirstOrDefault(x => x is IArrayCreationOperation); if (arrayCreationOperation is null) { diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 60d4bfe65e..be0f4185c1 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -19,23 +19,29 @@ public async Task IdentifyConstArrays_ImplicitInitialization() await VerifyCS.VerifyCodeFixAsync(@" using System; -public class A +namespace Z { - public void B() + public class A { - Console.WriteLine({|CA1861:new[]{ 1, 2, 3 }|}); + public void B() + { + Console.WriteLine({|CA1861:new[]{ 1, 2, 3 }|}); + } } } ", @" using System; -public class A +namespace Z { - private static readonly int[] value = new[]{ 1, 2, 3 }; - - public void B() + public class A { - Console.WriteLine(value); + private static readonly int[] value = new[]{ 1, 2, 3 }; + + public void B() + { + Console.WriteLine(value); + } } } "); @@ -43,20 +49,24 @@ public void B() await VerifyVB.VerifyCodeFixAsync(@" Imports System -Public Class A - Public Sub B() - Console.WriteLine({|CA1861:{1, 2, 3}|}) - End Sub -End Class +Namespace Z + Public Class A + Public Sub B() + Console.WriteLine({|CA1861:{1, 2, 3}|}) + End Sub + End Class +End Namespace ", @" Imports System -Public Class A - Private Shared ReadOnly value As Integer() = {1, 2, 3} - Public Sub B() - Console.WriteLine(value) - End Sub -End Class +Namespace Z + Public Class A + Private Shared ReadOnly value As Integer() = {1, 2, 3} + Public Sub B() + Console.WriteLine(value) + End Sub + End Class +End Namespace "); } @@ -66,23 +76,29 @@ public async Task IdentifyConstArrays_ExplicitInitialization() await VerifyCS.VerifyCodeFixAsync(@" using System; -public class A +namespace Z { - public void B() + public class A { - Console.WriteLine({|CA1861:new int[]{ 1, 2, 3 }|}); + public void B() + { + Console.WriteLine({|CA1861:new int[]{ 1, 2, 3 }|}); + } } } ", @" using System; -public class A +namespace Z { - private static readonly int[] value = new int[]{ 1, 2, 3 }; - - public void B() + public class A { - Console.WriteLine(value); + private static readonly int[] value = new int[]{ 1, 2, 3 }; + + public void B() + { + Console.WriteLine(value); + } } } "); @@ -120,20 +136,24 @@ public void B() await VerifyVB.VerifyCodeFixAsync(@" Imports System -Public Class A - Public Sub B() - Console.WriteLine({|CA1861:New Integer() {1, 2, 3}|}) - End Sub -End Class +Namespace Z + Public Class A + Public Sub B() + Console.WriteLine({|CA1861:New Integer() {1, 2, 3}|}) + End Sub + End Class +End Namespace ", @" Imports System -Public Class A - Private Shared ReadOnly value As Integer() = New Integer() {1, 2, 3} - Public Sub B() - Console.WriteLine(value) - End Sub -End Class +Namespace Z + Public Class A + Private Shared ReadOnly value As Integer() = New Integer() {1, 2, 3} + Public Sub B() + Console.WriteLine(value) + End Sub + End Class +End Namespace "); } @@ -143,23 +163,29 @@ public async Task IdentifyConstArrays_NestedArgs() await VerifyCS.VerifyCodeFixAsync(@" using System; -public class A +namespace Z { - public void B() + public class A { - Console.WriteLine(string.Join("" "", {|CA1861:new[] { ""Cake"", ""is"", ""good"" }|})); + public void B() + { + Console.WriteLine(string.Join("" "", {|CA1861:new[] { ""Cake"", ""is"", ""good"" }|})); + } } } ", @" using System; -public class A +namespace Z { - private static readonly string[] value = new[] { ""Cake"", ""is"", ""good"" }; - - public void B() + public class A { - Console.WriteLine(string.Join("" "", value)); + private static readonly string[] value = new[] { ""Cake"", ""is"", ""good"" }; + + public void B() + { + Console.WriteLine(string.Join("" "", value)); + } } } "); @@ -167,20 +193,24 @@ public void B() await VerifyVB.VerifyCodeFixAsync(@" Imports System -Public Class A - Public Sub B() - Console.WriteLine(String.Join("" ""c, {|CA1861:{""Cake"", ""is"", ""good""}|})) - End Sub -End Class +Namespace Z + Public Class A + Public Sub B() + Console.WriteLine(String.Join("" ""c, {|CA1861:{""Cake"", ""is"", ""good""}|})) + End Sub + End Class +End Namespace ", @" Imports System -Public Class A - Private Shared ReadOnly value As String() = {""Cake"", ""is"", ""good""} - Public Sub B() - Console.WriteLine(String.Join("" ""c, value)) - End Sub -End Class +Namespace Z + Public Class A + Private Shared ReadOnly value As String() = {""Cake"", ""is"", ""good""} + Public Sub B() + Console.WriteLine(String.Join("" ""c, value)) + End Sub + End Class +End Namespace "); } @@ -190,23 +220,29 @@ public async Task IdentifyConstArrays_TriviaTest() await VerifyCS.VerifyCodeFixAsync(@" using System; -public class A +namespace Z { - public void B() + public class A { - Console.WriteLine(string.Join("" "", {|CA1861:new[] { ""a"", ""b"" }|} /* test comment */)); + public void B() + { + Console.WriteLine(string.Join("" "", {|CA1861:new[] { ""a"", ""b"" }|} /* test comment */)); + } } } ", @" using System; -public class A +namespace Z { - private static readonly string[] value = new[] { ""a"", ""b"" }; - - public void B() + public class A { - Console.WriteLine(string.Join("" "", value /* test comment */)); + private static readonly string[] value = new[] { ""a"", ""b"" }; + + public void B() + { + Console.WriteLine(string.Join("" "", value /* test comment */)); + } } } "); @@ -219,26 +255,32 @@ await VerifyCS.VerifyCodeFixAsync(@" using System; using System.Linq; -public class A +namespace Z { - public void B() + public class A { - var x = new string[] { ""a"", ""b"" }; - var y = x.Select(z => {|CA1861:new[] { ""c"" }|}); + public void B() + { + var x = new string[] { ""a"", ""b"" }; + var y = x.Select(z => {|CA1861:new[] { ""c"" }|}); + } } } ", @" using System; using System.Linq; -public class A +namespace Z { - private static readonly string[] stringArray = new[] { ""c"" }; - - public void B() + public class A { - var x = new string[] { ""a"", ""b"" }; - var y = x.Select(z => stringArray); + private static readonly string[] stringArray = new[] { ""c"" }; + + public void B() + { + var x = new string[] { ""a"", ""b"" }; + var y = x.Select(z => stringArray); + } } } "); @@ -251,26 +293,32 @@ await VerifyCS.VerifyCodeFixAsync(@" using System; using System.Linq; -public class A +namespace Z { - public void B() + public class A { - var x = new string[] { ""a"", ""b"" }; - var y = x.Select(z => {|CA1861:new[] { ""c"" }|}.First()); + public void B() + { + var x = new string[] { ""a"", ""b"" }; + var y = x.Select(z => {|CA1861:new[] { ""c"" }|}.First()); + } } } ", @" using System; using System.Linq; -public class A +namespace Z { - private static readonly string[] sourceArray = new[] { ""c"" }; - - public void B() + public class A { - var x = new string[] { ""a"", ""b"" }; - var y = x.Select(z => sourceArray.First()); + private static readonly string[] sourceArray = new[] { ""c"" }; + + public void B() + { + var x = new string[] { ""a"", ""b"" }; + var y = x.Select(z => sourceArray.First()); + } } } "); @@ -283,26 +331,32 @@ await VerifyCS.VerifyCodeFixAsync(@" using System; using System.Linq; -public class A +namespace Z { - public void B() + public class A { - string y = {|CA1861:new[] { ""a"", ""b"", ""c"" }|}.First(); - Console.WriteLine(y); + public void B() + { + string y = {|CA1861:new[] { ""a"", ""b"", ""c"" }|}.First(); + Console.WriteLine(y); + } } } ", @" using System; using System.Linq; -public class A +namespace Z { - private static readonly string[] sourceArray = new[] { ""a"", ""b"", ""c"" }; - - public void B() + public class A { - string y = sourceArray.First(); - Console.WriteLine(y); + private static readonly string[] sourceArray = new[] { ""a"", ""b"", ""c"" }; + + public void B() + { + string y = sourceArray.First(); + Console.WriteLine(y); + } } } "); @@ -311,23 +365,27 @@ await VerifyVB.VerifyCodeFixAsync(@" Imports System Imports System.Linq -Public Class A - Public Sub B() - Dim y As String = {|CA1861:{""a"", ""b"", ""c""}|}.First() - Console.WriteLine(y) - End Sub -End Class +Namespace Z + Public Class A + Public Sub B() + Dim y As String = {|CA1861:{""a"", ""b"", ""c""}|}.First() + Console.WriteLine(y) + End Sub + End Class +End Namespace ", @" Imports System Imports System.Linq -Public Class A - Private Shared ReadOnly stringArray As String() = {""a"", ""b"", ""c""} - Public Sub B() - Dim y As String = stringArray.First() - Console.WriteLine(y) - End Sub -End Class +Namespace Z + Public Class A + Private Shared ReadOnly stringArray As String() = {""a"", ""b"", ""c""} + Public Sub B() + Dim y As String = stringArray.First() + Console.WriteLine(y) + End Sub + End Class +End Namespace "); } @@ -336,29 +394,35 @@ public async Task IdentifyConstArrays_ParamsArrayOfLiterals() { // A params argument passed as an array of literals await VerifyCS.VerifyCodeFixAsync(@" -public class A +namespace Z { - public void B() + public class A { - C({|CA1861:new bool[] { true, false }|}); - } + public void B() + { + C({|CA1861:new bool[] { true, false }|}); + } - private void C(params bool[] booleans) - { + private void C(params bool[] booleans) + { + } } } ", @" -public class A +namespace Z { - private static readonly bool[] booleanArray = new bool[] { true, false }; - - public void B() + public class A { - C(booleanArray); - } + private static readonly bool[] booleanArray = new bool[] { true, false }; - private void C(params bool[] booleans) - { + public void B() + { + C(booleanArray); + } + + private void C(params bool[] booleans) + { + } } } "); @@ -372,32 +436,38 @@ public async Task IdentifyConstArrays_ParamsArrays() await new VerifyCS.Test() { TestCode = @" -public class A +namespace Z { - public void B() + public class A { - C({|CA1861:new bool[] { true, false }|}, {|CA1861:new bool[] { false, true }|}); - } + public void B() + { + C({|CA1861:new bool[] { true, false }|}, {|CA1861:new bool[] { false, true }|}); + } - private void C(params bool[][] booleans) - { + private void C(params bool[][] booleans) + { + } } } ", NumberOfFixAllIterations = 2, FixedCode = @" -public class A +namespace Z { - private static readonly bool[] booleanArray = new bool[] { true, false }; - private static readonly bool[] booleanArray0 = new bool[] { false, true }; - - public void B() + public class A { - C(booleanArray, booleanArray0); - } + private static readonly bool[] booleanArray = new bool[] { true, false }; + private static readonly bool[] booleanArray0 = new bool[] { false, true }; - private void C(params bool[][] booleans) - { + public void B() + { + C(booleanArray, booleanArray0); + } + + private void C(params bool[][] booleans) + { + } } } " @@ -411,34 +481,40 @@ public async Task IdentifyConstArrays_MemberExtractionTest() await VerifyCS.VerifyCodeFixAsync(@" using System; -public class A +namespace Z { - private static readonly string value = ""hello""; - private static readonly int[] valueArray = new[]{ -2, -1, 0 }; - private static readonly bool[] valueArray1 = new[]{ true, false, true }; + public class A + { + private static readonly string value = ""hello""; + private static readonly int[] valueArray = new[]{ -2, -1, 0 }; + private static readonly bool[] valueArray1 = new[]{ true, false, true }; - private static readonly int x = 1; + private static readonly int x = 1; - public void B() - { - Console.WriteLine({|CA1861:new[]{ 1, 2, 3 }|}); + public void B() + { + Console.WriteLine({|CA1861:new[]{ 1, 2, 3 }|}); + } } } ", @" using System; -public class A +namespace Z { - private static readonly string value = ""hello""; - private static readonly int[] valueArray = new[]{ -2, -1, 0 }; - private static readonly bool[] valueArray1 = new[]{ true, false, true }; + public class A + { + private static readonly string value = ""hello""; + private static readonly int[] valueArray = new[]{ -2, -1, 0 }; + private static readonly bool[] valueArray1 = new[]{ true, false, true }; - private static readonly int x = 1; - private static readonly int[] valueArray0 = new[]{ 1, 2, 3 }; + private static readonly int x = 1; + private static readonly int[] valueArray0 = new[]{ 1, 2, 3 }; - public void B() - { - Console.WriteLine(valueArray0); + public void B() + { + Console.WriteLine(valueArray0); + } } } "); @@ -446,30 +522,34 @@ public void B() await VerifyVB.VerifyCodeFixAsync(@" Imports System -Public Class A - Private Shared ReadOnly value As String = ""hello"" - Private Shared ReadOnly valueArray As Integer() = {-2, -1, 0} - Private Shared ReadOnly valueArray1 As Boolean() = {True, False, True} - Private Shared ReadOnly x As Integer = 1 - - Public Sub B() - Console.WriteLine({|CA1861:{1, 2, 3}|}) - End Sub -End Class +Namespace Z + Public Class A + Private Shared ReadOnly value As String = ""hello"" + Private Shared ReadOnly valueArray As Integer() = {-2, -1, 0} + Private Shared ReadOnly valueArray1 As Boolean() = {True, False, True} + Private Shared ReadOnly x As Integer = 1 + + Public Sub B() + Console.WriteLine({|CA1861:{1, 2, 3}|}) + End Sub + End Class +End Namespace ", @" Imports System -Public Class A - Private Shared ReadOnly value As String = ""hello"" - Private Shared ReadOnly valueArray As Integer() = {-2, -1, 0} - Private Shared ReadOnly valueArray1 As Boolean() = {True, False, True} - Private Shared ReadOnly x As Integer = 1 - Private Shared ReadOnly valueArray0 As Integer() = {1, 2, 3} - - Public Sub B() - Console.WriteLine(valueArray0) - End Sub -End Class +Namespace Z + Public Class A + Private Shared ReadOnly value As String = ""hello"" + Private Shared ReadOnly valueArray As Integer() = {-2, -1, 0} + Private Shared ReadOnly valueArray1 As Boolean() = {True, False, True} + Private Shared ReadOnly x As Integer = 1 + Private Shared ReadOnly valueArray0 As Integer() = {1, 2, 3} + + Public Sub B() + Console.WriteLine(valueArray0) + End Sub + End Class +End Namespace "); } @@ -480,11 +560,14 @@ public async Task IgnoreOtherArgs_NoDiagnostic() await VerifyCS.VerifyAnalyzerAsync(@" using System; -public class A +namespace Z { - public void B() + public class A { - Console.WriteLine(""Lorem ipsum""); + public void B() + { + Console.WriteLine(""Lorem ipsum""); + } } } "); @@ -492,22 +575,27 @@ public void B() await VerifyVB.VerifyAnalyzerAsync(@" Imports System -Public Class A - Public Sub B() - Console.WriteLine(""Lorem ipsum"") - End Sub -End Class +Namespace Z + Public Class A + Public Sub B() + Console.WriteLine(""Lorem ipsum"") + End Sub + End Class +End Namespace "); // Test another type to be extra safe await VerifyCS.VerifyAnalyzerAsync(@" using System; -public class A +namespace Z { - public void B() + public class A { - Console.WriteLine(123); + public void B() + { + Console.WriteLine(123); + } } } "); @@ -515,23 +603,28 @@ public void B() await VerifyVB.VerifyAnalyzerAsync(@" Imports System -Public Class A - Public Sub B() - Console.WriteLine(123) - End Sub -End Class +Namespace Z + Public Class A + Public Sub B() + Console.WriteLine(123) + End Sub + End Class +End Namespace "); // Non-literal array await VerifyCS.VerifyAnalyzerAsync(@" using System; -public class A +namespace Z { - public void B() + public class A { - string str = ""Lorem ipsum""; - Console.WriteLine(new[] { str }); + public void B() + { + string str = ""Lorem ipsum""; + Console.WriteLine(new[] { str }); + } } } "); @@ -539,12 +632,14 @@ public void B() await VerifyVB.VerifyAnalyzerAsync(@" Imports System -Public Class A - Public Sub B() - Dim str As String = ""Lorem ipsum"" - Console.WriteLine({ str }) - End Sub -End Class +Namespace Z + Public Class A + Public Sub B() + Dim str As String = ""Lorem ipsum"" + Console.WriteLine({ str }) + End Sub + End Class +End Namespace "); } @@ -555,15 +650,18 @@ public async Task IgnoreReadonlySpan_NoDiagnostic() await VerifyCS.VerifyAnalyzerAsync(@" using System; -public class A +namespace Z { - public void B() + public class A { - C(new bool[] { true, false }); - } + public void B() + { + C(new bool[] { true, false }); + } - private void C(ReadOnlySpan span) - { + private void C(ReadOnlySpan span) + { + } } } "); @@ -574,15 +672,18 @@ public async Task IgnoreParams_NoDiagnostic() { // Params arguments await VerifyCS.VerifyAnalyzerAsync(@" -public class A +namespace Z { - public void B() + public class A { - C(true, false); - } + public void B() + { + C(true, false); + } - private void C(params bool[] booleans) - { + private void C(params bool[] booleans) + { + } } } "); From fec9a2fba1a98ace148e17e4e09e7295a5e4c38e Mon Sep 17 00:00:00 2001 From: Steve Berdy Date: Fri, 17 Mar 2023 19:05:49 -0400 Subject: [PATCH 62/64] added exclusion for readonly field assignments, more tests --- .../Runtime/AvoidConstArrays.Fixer.cs | 9 +++-- .../Runtime/AvoidConstArrays.cs | 6 ++++ .../Runtime/AvoidConstArraysTests.cs | 36 +++++++++++++++++-- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index feccbb01f9..90d33babf3 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -38,12 +38,12 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) SyntaxNode node = root.FindNode(context.Span); SemanticModel model = await document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); DocumentEditor editor = await DocumentEditor.CreateAsync(document, context.CancellationToken).ConfigureAwait(false); - SyntaxGenerator generator = editor.Generator; string title = MicrosoftNetCoreAnalyzersResources.AvoidConstArraysCodeFixTitle; context.RegisterCodeFix(CodeAction.Create( title, - async c => await ExtractConstArrayAsync(root, node, model, editor, generator, context.Diagnostics.First().Properties, c).ConfigureAwait(false), + async ct => await ExtractConstArrayAsync(root, node, model, editor, editor.Generator, + context.Diagnostics.First().Properties, ct).ConfigureAwait(false), equivalenceKey: title), context.Diagnostics); } @@ -98,15 +98,14 @@ private static Task ExtractConstArrayAsync(SyntaxNode root, SyntaxNode else { // add any extra trivia that was after the original argument - editor.ReplaceNode(node, generator.Argument(identifier).WithTrailingTrivia(arrayArgument.Syntax.GetTrailingTrivia())); + editor.ReplaceNode(node, generator.Argument(identifier).WithTriviaFrom(arrayArgument.Syntax)); } // Return changed document return Task.FromResult(editor.GetChangedDocument()); } - private static IArrayCreationOperation GetArrayCreationOperation(SyntaxNode node, SemanticModel model, CancellationToken cancellationToken, - out bool isInvoked) + private static IArrayCreationOperation GetArrayCreationOperation(SyntaxNode node, SemanticModel model, CancellationToken cancellationToken, out bool isInvoked) { // The analyzer only passes a diagnostic for two scenarios, each having an IArrayCreationOperation: // 1. The node is an IArgumentOperation that is a direct parent of an IArrayCreationOperation diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index 6bf438793a..a121c31fa0 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -105,6 +105,12 @@ public override void Initialize(AnalysisContext context) string? paramName = null; if (argumentOperation is not null) { + IFieldInitializerOperation? fieldInitializer = argumentOperation.GetAncestor(OperationKind.FieldInitializer); + if (fieldInitializer is not null && fieldInitializer.InitializedFields.Any(x => x.IsReadOnly)) + { + return; + } + ITypeSymbol originalDefinition = argumentOperation.Parameter.Type.OriginalDefinition; // Can't be a ReadOnlySpan, as those are already optimized diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index be0f4185c1..72dd289b44 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -226,7 +226,11 @@ public class A { public void B() { - Console.WriteLine(string.Join("" "", {|CA1861:new[] { ""a"", ""b"" }|} /* test comment */)); + Console.WriteLine(string.Join( + ""a"", + {|CA1861:new[] { ""b"", ""c"" }|}, /* test comment */ + ""d"" + )); } } } @@ -237,11 +241,15 @@ namespace Z { public class A { - private static readonly string[] value = new[] { ""a"", ""b"" }; + private static readonly string[] values = new[] { ""b"", ""c"" }; public void B() { - Console.WriteLine(string.Join("" "", value /* test comment */)); + Console.WriteLine(string.Join( + ""a"", + values, /* test comment */ + ""d"" + )); } } } @@ -686,6 +694,28 @@ private void C(params bool[] booleans) } } } +"); + } + + [Fact] + public async Task IgnoreReadonlyFieldAssignment_NoDiagnostic() + { + // Ignore when we're an argument used in a method/constructor that is assigned to a readonly field + await VerifyCS.VerifyAnalyzerAsync(@" +namespace Z +{ + public class A + { + private static readonly B s = new B(new string[] { ""a"" }); + } + + public class B + { + public B(string[] s) + { + } + } +} "); } } From fc8992de1e9f2fae4ce6649b707e4fe35b3a5803 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 5 May 2023 13:11:33 -0700 Subject: [PATCH 63/64] Fix issues in fixer, do not warn for static constructor and readonly properties --- .../Runtime/AvoidConstArrays.Fixer.cs | 31 ++++++++++++------- .../Runtime/AvoidConstArrays.cs | 21 ++++++++----- .../Runtime/AvoidConstArraysTests.cs | 23 ++++++++++++++ 3 files changed, 57 insertions(+), 18 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 90d33babf3..7fc7205cbf 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -37,20 +37,20 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) SyntaxNode root = await document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); SyntaxNode node = root.FindNode(context.Span); SemanticModel model = await document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); - DocumentEditor editor = await DocumentEditor.CreateAsync(document, context.CancellationToken).ConfigureAwait(false); string title = MicrosoftNetCoreAnalyzersResources.AvoidConstArraysCodeFixTitle; context.RegisterCodeFix(CodeAction.Create( title, - async ct => await ExtractConstArrayAsync(root, node, model, editor, editor.Generator, - context.Diagnostics.First().Properties, ct).ConfigureAwait(false), + async ct => await ExtractConstArrayAsync(document, root, node, model, context.Diagnostics.First().Properties, ct).ConfigureAwait(false), equivalenceKey: title), context.Diagnostics); } - private static Task ExtractConstArrayAsync(SyntaxNode root, SyntaxNode node, SemanticModel model, DocumentEditor editor, - SyntaxGenerator generator, ImmutableDictionary properties, CancellationToken cancellationToken) + private static async Task ExtractConstArrayAsync(Document document, SyntaxNode root, SyntaxNode node, + SemanticModel model, ImmutableDictionary properties, CancellationToken cancellationToken) { + DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + SyntaxGenerator generator = editor.Generator; IArrayCreationOperation arrayArgument = GetArrayCreationOperation(node, model, cancellationToken, out bool isInvoked); INamedTypeSymbol containingType = model.GetEnclosingSymbol(node.SpanStart, cancellationToken).ContainingType; @@ -76,12 +76,21 @@ private static Task ExtractConstArrayAsync(SyntaxNode root, SyntaxNode newMember = newMember.FormatForExtraction(methodContext.Syntax); } - ISymbol lastFieldOrPropertSymbol = containingType.GetMembers().LastOrDefault(x => x is IFieldSymbol or IPropertySymbol); - if (lastFieldOrPropertSymbol is not null) + ISymbol lastFieldOrPropertySymbol = containingType.GetMembers().LastOrDefault(x => x is IFieldSymbol or IPropertySymbol); + if (lastFieldOrPropertySymbol is not null) { - // Insert after fields or properties - SyntaxNode lastFieldOrPropertyNode = root.FindNode(lastFieldOrPropertSymbol.Locations.First().SourceSpan); - editor.InsertAfter(generator.GetDeclaration(lastFieldOrPropertyNode), newMember); + var span = lastFieldOrPropertySymbol.Locations.First().SourceSpan; + if (root.FullSpan.Contains(span)) + { + // Insert after fields or properties + SyntaxNode lastFieldOrPropertyNode = root.FindNode(span); + editor.InsertAfter(generator.GetDeclaration(lastFieldOrPropertyNode), newMember); + } + else + { + // Span not found + editor.InsertBefore(methodContext?.Syntax, newMember); + } } else { @@ -102,7 +111,7 @@ private static Task ExtractConstArrayAsync(SyntaxNode root, SyntaxNode } // Return changed document - return Task.FromResult(editor.GetChangedDocument()); + return editor.GetChangedDocument(); } private static IArrayCreationOperation GetArrayCreationOperation(SyntaxNode node, SemanticModel model, CancellationToken cancellationToken, out bool isInvoked) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index a121c31fa0..900892054b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -47,6 +47,11 @@ public override void Initialize(AnalysisContext context) { IArgumentOperation? argumentOperation; + if (context.ContainingSymbol is IMethodSymbol method && method.MethodKind == MethodKind.StaticConstructor) + { + return; + } + if (context.Operation is IArrayCreationOperation arrayCreationOperation) // For arrays passed as arguments { argumentOperation = arrayCreationOperation.GetAncestor(OperationKind.Argument); @@ -105,8 +110,12 @@ public override void Initialize(AnalysisContext context) string? paramName = null; if (argumentOperation is not null) { - IFieldInitializerOperation? fieldInitializer = argumentOperation.GetAncestor(OperationKind.FieldInitializer); - if (fieldInitializer is not null && fieldInitializer.InitializedFields.Any(x => x.IsReadOnly)) + IFieldInitializerOperation? fieldInitializer = argumentOperation.GetAncestor( + OperationKind.FieldInitializer, f => f.InitializedFields.Any(x => x.IsReadOnly)); + IPropertyInitializerOperation? propertyInitializer = argumentOperation.GetAncestor( + OperationKind.PropertyInitializer, p => p.InitializedProperties.Any(x => x.IsReadOnly)); + + if (fieldInitializer is not null || propertyInitializer is not null) { return; } @@ -131,12 +140,10 @@ public override void Initialize(AnalysisContext context) } } - Dictionary properties = new() - { - { "paramName", paramName } - }; + ImmutableDictionary.Builder properties = ImmutableDictionary.CreateBuilder(); + properties.Add("paramName", paramName); - context.ReportDiagnostic(arrayCreationOperation.CreateDiagnostic(Rule, properties.ToImmutableDictionary())); + context.ReportDiagnostic(arrayCreationOperation.CreateDiagnostic(Rule, properties.ToImmutable())); }, OperationKind.ArrayCreation, OperationKind.Invocation); diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 72dd289b44..931b5a3ee7 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -718,5 +718,28 @@ public B(string[] s) } "); } + + [Fact] + public async Task IgnoreReadOnlyProperties_NoDiagnostic() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; +using System.Collections.Generic; + +public class A +{ + public static readonly A Field; + public static List Property { get; } = GetValues(new string[] { ""close"" }); + public static string[] Property2 { get; } = new string[] { ""close"" }; + + static A() // Exclude initialization in static constructors + { + Property = GetValues(new string[] { ""close"" }); + Field = new A(new string[] { ""close"" }); + } + public A(string[] arr) { } + private static List GetValues(string[] arr) => null; +}"); + } } } From bbf1ebe71df3a5aba70b7b5fbdf488a4ed6f953f Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 8 May 2023 09:37:41 -0700 Subject: [PATCH 64/64] Apply feedbacks --- .../Runtime/AvoidConstArrays.Fixer.cs | 11 +++--- .../Runtime/AvoidConstArrays.cs | 2 +- .../Runtime/AvoidConstArraysTests.cs | 38 +++++++++++++++++++ 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs index 7fc7205cbf..8d6b06338c 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.Fixer.cs @@ -36,19 +36,18 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) Document document = context.Document; SyntaxNode root = await document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); SyntaxNode node = root.FindNode(context.Span); - SemanticModel model = await document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); - string title = MicrosoftNetCoreAnalyzersResources.AvoidConstArraysCodeFixTitle; context.RegisterCodeFix(CodeAction.Create( - title, - async ct => await ExtractConstArrayAsync(document, root, node, model, context.Diagnostics.First().Properties, ct).ConfigureAwait(false), - equivalenceKey: title), + MicrosoftNetCoreAnalyzersResources.AvoidConstArraysCodeFixTitle, + async ct => await ExtractConstArrayAsync(document, root, node, context.Diagnostics[0].Properties, ct).ConfigureAwait(false), + equivalenceKey: nameof(MicrosoftNetCoreAnalyzersResources.AvoidConstArraysCodeFixTitle)), context.Diagnostics); } private static async Task ExtractConstArrayAsync(Document document, SyntaxNode root, SyntaxNode node, - SemanticModel model, ImmutableDictionary properties, CancellationToken cancellationToken) + ImmutableDictionary properties, CancellationToken cancellationToken) { + SemanticModel model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); SyntaxGenerator generator = editor.Generator; IArrayCreationOperation arrayArgument = GetArrayCreationOperation(node, model, cancellationToken, out bool isInvoked); diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs index 900892054b..98a7095075 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArrays.cs @@ -77,7 +77,7 @@ public override void Initialize(AnalysisContext context) argumentOperation = invocationOperation.Arguments.FirstOrDefault(); if (argumentOperation is not null) { - if (argumentOperation.Children.First() is not IConversionOperation conversionOperation + if (argumentOperation.Value is not IConversionOperation conversionOperation || conversionOperation.Operand is not IArrayCreationOperation arrayCreation) { return; diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs index 931b5a3ee7..de5a789cd0 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidConstArraysTests.cs @@ -294,6 +294,44 @@ public void B() "); } + [Fact] + public async Task IdentifyConstArrays_LambdaArrayCreationTwoParams() + { + await VerifyCS.VerifyCodeFixAsync(@" +using System; +using System.Linq; + +namespace Z +{ + public class A + { + public void B() + { + var x = new string[] { ""a"", ""b"" }; + var y = x.Select((z1, z2) => {|CA1861:new[] { ""c"" }|}); + } + } +} +", @" +using System; +using System.Linq; + +namespace Z +{ + public class A + { + private static readonly string[] selector = new[] { ""c"" }; + + public void B() + { + var x = new string[] { ""a"", ""b"" }; + var y = x.Select((z1, z2) => selector); + } + } +} +"); + } + [Fact] public async Task IdentifyConstArrays_LambdaInvokedArrayCreation() {