Skip to content

Commit

Permalink
Add CA1514: Remove redundant length argument (#6814)
Browse files Browse the repository at this point in the history
* Add CA1514: Remove redundant length argument

This analyzer detects uses of Substring/Slice(start, length) that slice
to the end of the buffer. In these cases, the length check is redundant,
can be omitted, and the call can be replaced with
Substring/Slice(start). The analyzer also detects if the evaluation of
the instance or arguments can lead to side effects and does not flag
these cases.

* Rename LengthCheck to LengthCalculation
  • Loading branch information
mpidash authored Sep 22, 2023
1 parent 4aefee9 commit 39ccb5b
Show file tree
Hide file tree
Showing 22 changed files with 2,325 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
CA1514 | Maintainability | Info | AvoidLengthCheckWhenSlicingToEndAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1514)
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System.Collections.Immutable;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Operations;

namespace Microsoft.CodeQuality.Analyzers.Maintainability
{
/// <summary>
/// CA1514: <inheritdoc cref="MicrosoftCodeQualityAnalyzersResources.AvoidLengthCalculationWhenSlicingToEndTitle"/>
/// </summary>
[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared]
public sealed class AvoidLengthCalculationWhenSlicingToEndFixer : CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(AvoidLengthCalculationWhenSlicingToEndAnalyzer.RuleId);

public sealed override FixAllProvider GetFixAllProvider()
{
return WellKnownFixAllProviders.BatchFixer;
}

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var node = root.FindNode(context.Span, getInnermostNodeForTie: true);

if (node is null)
{
return;
}

var semanticModel = await context.Document.GetRequiredSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
var operation = semanticModel.GetOperation(node, context.CancellationToken);

if (operation is not IInvocationOperation invocationOperation ||
invocationOperation.Instance is null ||
invocationOperation.Arguments.Length != 2)
{
return;
}

var codeAction = CodeAction.Create(
MicrosoftCodeQualityAnalyzersResources.AvoidLengthCalculationWhenSlicingToEndCodeFixTitle,
ct => ReplaceWithStartOnlyCall(
context.Document,
invocationOperation.Instance.Syntax,
invocationOperation.TargetMethod.Name,
invocationOperation.Arguments.GetArgumentsInParameterOrder()[0],
ct),
nameof(MicrosoftCodeQualityAnalyzersResources.AvoidLengthCalculationWhenSlicingToEndCodeFixTitle));

context.RegisterCodeFix(codeAction, context.Diagnostics);

async Task<Document> ReplaceWithStartOnlyCall(
Document document,
SyntaxNode instance,
string methodName,
IArgumentOperation argument,
CancellationToken cancellationToken)
{
var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
var generator = editor.Generator;
var methodExpression = generator.MemberAccessExpression(instance, methodName);
var methodInvocation = generator.InvocationExpression(methodExpression, argument.Syntax);

editor.ReplaceNode(invocationOperation.Syntax, methodInvocation.WithTriviaFrom(invocationOperation.Syntax));

return document.WithSyntaxRoot(editor.GetChangedRoot());
}
}
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,18 @@
<data name="DoNotIgnoreMethodResultsMessageTryParse" xml:space="preserve">
<value>{0} calls {1} but does not explicitly check whether the conversion succeeded. Either use the return value in a conditional statement or verify that the call site expects that the out argument will be set to the default value when the conversion fails.</value>
</data>
<data name="AvoidLengthCalculationWhenSlicingToEndTitle" xml:space="preserve">
<value>Avoid redundant length argument</value>
</data>
<data name="AvoidLengthCalculationWhenSlicingToEndCodeFixTitle" xml:space="preserve">
<value>Remove redundant length argument</value>
</data>
<data name="AvoidLengthCalculationWhenSlicingToEndDescription" xml:space="preserve">
<value>An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</value>
</data>
<data name="AvoidLengthCalculationWhenSlicingToEndMessage" xml:space="preserve">
<value>'{0}' uses a redundant length calculation that can be removed</value>
</data>
<data name="AvoidUninstantiatedInternalClassesTitle" xml:space="preserve">
<value>Avoid uninstantiated internal classes</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@
<target state="translated">Zabránit nekonečné rekurzi</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndCodeFixTitle">
<source>Remove redundant length argument</source>
<target state="new">Remove redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndDescription">
<source>An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</source>
<target state="new">An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndMessage">
<source>'{0}' uses a redundant length calculation that can be removed</source>
<target state="new">'{0}' uses a redundant length calculation that can be removed</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndTitle">
<source>Avoid redundant length argument</source>
<target state="new">Avoid redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidMultipleEnumerationsMessage">
<source>Possible multiple enumerations of 'IEnumerable' collection. Consider using an implementation that avoids multiple enumerations.</source>
<target state="translated">Je možných více výčtů kolekce IEnumerable. Zvažte použití implementace, která zabraňuje více výčtům.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@
<target state="translated">Endlosrekursion vermeiden</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndCodeFixTitle">
<source>Remove redundant length argument</source>
<target state="new">Remove redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndDescription">
<source>An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</source>
<target state="new">An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndMessage">
<source>'{0}' uses a redundant length calculation that can be removed</source>
<target state="new">'{0}' uses a redundant length calculation that can be removed</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndTitle">
<source>Avoid redundant length argument</source>
<target state="new">Avoid redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidMultipleEnumerationsMessage">
<source>Possible multiple enumerations of 'IEnumerable' collection. Consider using an implementation that avoids multiple enumerations.</source>
<target state="translated">Mögliche mehrere Enumerationen der IEnumerable-Auflistung. Erwägen Sie die Verwendung einer Implementierung, die mehrere Enumerationen vermeidet.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@
<target state="translated">Evitar la recursividad infinita</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndCodeFixTitle">
<source>Remove redundant length argument</source>
<target state="new">Remove redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndDescription">
<source>An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</source>
<target state="new">An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndMessage">
<source>'{0}' uses a redundant length calculation that can be removed</source>
<target state="new">'{0}' uses a redundant length calculation that can be removed</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndTitle">
<source>Avoid redundant length argument</source>
<target state="new">Avoid redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidMultipleEnumerationsMessage">
<source>Possible multiple enumerations of 'IEnumerable' collection. Consider using an implementation that avoids multiple enumerations.</source>
<target state="translated">Posibles enumeraciones múltiples de la colección “IEnumerable”. Considere la posibilidad de usar una implementación que evite varias enumeraciones.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@
<target state="translated">Éviter la récursivité infinie</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndCodeFixTitle">
<source>Remove redundant length argument</source>
<target state="new">Remove redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndDescription">
<source>An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</source>
<target state="new">An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndMessage">
<source>'{0}' uses a redundant length calculation that can be removed</source>
<target state="new">'{0}' uses a redundant length calculation that can be removed</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndTitle">
<source>Avoid redundant length argument</source>
<target state="new">Avoid redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidMultipleEnumerationsMessage">
<source>Possible multiple enumerations of 'IEnumerable' collection. Consider using an implementation that avoids multiple enumerations.</source>
<target state="translated">Possibilité d'énumérations multiples de la collection 'IEnumerable'. Envisagez d'utiliser une mise en œuvre qui évite les énumérations multiples.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@
<target state="translated">Evitare la ricorsione infinita</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndCodeFixTitle">
<source>Remove redundant length argument</source>
<target state="new">Remove redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndDescription">
<source>An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</source>
<target state="new">An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndMessage">
<source>'{0}' uses a redundant length calculation that can be removed</source>
<target state="new">'{0}' uses a redundant length calculation that can be removed</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndTitle">
<source>Avoid redundant length argument</source>
<target state="new">Avoid redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidMultipleEnumerationsMessage">
<source>Possible multiple enumerations of 'IEnumerable' collection. Consider using an implementation that avoids multiple enumerations.</source>
<target state="translated">Possibili enumerazioni multiple della raccolta 'IEnumerable'. Considerare l'utilizzo di un'implementazione che eviti enumerazioni multiple.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@
<target state="translated">無限再帰の回避</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndCodeFixTitle">
<source>Remove redundant length argument</source>
<target state="new">Remove redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndDescription">
<source>An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</source>
<target state="new">An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndMessage">
<source>'{0}' uses a redundant length calculation that can be removed</source>
<target state="new">'{0}' uses a redundant length calculation that can be removed</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndTitle">
<source>Avoid redundant length argument</source>
<target state="new">Avoid redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidMultipleEnumerationsMessage">
<source>Possible multiple enumerations of 'IEnumerable' collection. Consider using an implementation that avoids multiple enumerations.</source>
<target state="translated">'IEnumerable' コレクションを複数列挙している可能性があります。複数列挙を回避する実装を検討してください。</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@
<target state="translated">무한 재귀 방지</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndCodeFixTitle">
<source>Remove redundant length argument</source>
<target state="new">Remove redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndDescription">
<source>An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</source>
<target state="new">An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndMessage">
<source>'{0}' uses a redundant length calculation that can be removed</source>
<target state="new">'{0}' uses a redundant length calculation that can be removed</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndTitle">
<source>Avoid redundant length argument</source>
<target state="new">Avoid redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidMultipleEnumerationsMessage">
<source>Possible multiple enumerations of 'IEnumerable' collection. Consider using an implementation that avoids multiple enumerations.</source>
<target state="translated">'IEnumerable' 컬렉션에 대해 여러 개의 열거형이 가능합니다. 여러 열거형을 방지하는 구현을 사용하는 것이 좋습니다.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@
<target state="translated">Unikaj nieskończonej rekursji</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndCodeFixTitle">
<source>Remove redundant length argument</source>
<target state="new">Remove redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndDescription">
<source>An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</source>
<target state="new">An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndMessage">
<source>'{0}' uses a redundant length calculation that can be removed</source>
<target state="new">'{0}' uses a redundant length calculation that can be removed</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndTitle">
<source>Avoid redundant length argument</source>
<target state="new">Avoid redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidMultipleEnumerationsMessage">
<source>Possible multiple enumerations of 'IEnumerable' collection. Consider using an implementation that avoids multiple enumerations.</source>
<target state="translated">Możliwe wielokrotne wyliczenia kolekcji „IEnumerable”. Rozważ użycie implementacji, która pozwala uniknąć wielu wyliczeń.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@
<target state="translated">Evitar a recursão infinita</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndCodeFixTitle">
<source>Remove redundant length argument</source>
<target state="new">Remove redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndDescription">
<source>An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</source>
<target state="new">An explicit length calculation can be error-prone and can be avoided when slicing to end of the buffer.</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndMessage">
<source>'{0}' uses a redundant length calculation that can be removed</source>
<target state="new">'{0}' uses a redundant length calculation that can be removed</target>
<note />
</trans-unit>
<trans-unit id="AvoidLengthCalculationWhenSlicingToEndTitle">
<source>Avoid redundant length argument</source>
<target state="new">Avoid redundant length argument</target>
<note />
</trans-unit>
<trans-unit id="AvoidMultipleEnumerationsMessage">
<source>Possible multiple enumerations of 'IEnumerable' collection. Consider using an implementation that avoids multiple enumerations.</source>
<target state="translated">Possíveis várias enumerações da coleção 'IEnumerable'. Considere usar uma implementação que evite várias enumerações.</target>
Expand Down
Loading

0 comments on commit 39ccb5b

Please sign in to comment.