Skip to content

Commit

Permalink
Check recursion (RCS1235) (dotnet#1054)
Browse files Browse the repository at this point in the history
  • Loading branch information
josefpihrt authored and JochemHarmes committed Oct 30, 2023
1 parent 4b161af commit 56d9df3
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 5 deletions.
1 change: 1 addition & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Do not report ([RCS1170](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1170.md)) when `Microsoft.AspNetCore.Components.InjectAttribute` is used ([#1046](https://github.com/JosefPihrt/Roslynator/pull/1046)).
- Fix ([RCS1235](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1235.md)) ([#1047](https://github.com/JosefPihrt/Roslynator/pull/1047)).
- Fix ([RCS1206](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1206.md)) ([#1049](https://github.com/JosefPihrt/Roslynator/pull/1049)).
- Prevent possible recursion in ([RCS1235](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1235.md)) ([#1054](https://github.com/JosefPihrt/Roslynator/pull/1054)).

## [4.2.0] - 2022-11-27

Expand Down
15 changes: 10 additions & 5 deletions src/Analyzers/CSharp/Analysis/OptimizeMethodCallAnalysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -343,26 +343,31 @@ public static void OptimizeAdd(SyntaxNodeAnalysisContext context, in SimpleMembe
&& invocation.ArgumentList.Arguments[0].Expression is IdentifierNameSyntax identifierName
&& identifierName.Identifier.ValueText == forEachStatement.Identifier.ValueText)
{
ITypeSymbol typeSymbol = context.SemanticModel.GetTypeSymbol(invocationInfo.Expression, context.CancellationToken);
SemanticModel semanticModel = context.SemanticModel;
CancellationToken cancellationToken = context.CancellationToken;
ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(invocationInfo.Expression, cancellationToken);

if (typeSymbol?.IsKind(SymbolKind.ErrorType) == false)
{
foreach (ISymbol member in typeSymbol.GetMembers("AddRange"))
{
if (member is IMethodSymbol methodSymbol
&& methodSymbol.Parameters.Length == 1
&& context.SemanticModel.IsAccessible(invocation.SpanStart, methodSymbol)
&& context.SemanticModel.IsImplicitConversion(forEachStatement.Expression, methodSymbol.Parameters[0].Type)
&& semanticModel.IsAccessible(invocation.SpanStart, methodSymbol)
&& semanticModel.IsImplicitConversion(forEachStatement.Expression, methodSymbol.Parameters[0].Type)
&& !SymbolEqualityComparer.Default.Equals(
methodSymbol,
semanticModel.GetEnclosingSymbol(forEachStatement.SpanStart, cancellationToken))
&& forEachStatement.CloseParenToken.TrailingTrivia.IsEmptyOrWhitespace()
&& invocation.GetLeadingTrivia().IsEmptyOrWhitespace()
&& (block is null
|| SyntaxTriviaAnalysis.IsExteriorTriviaEmptyOrWhitespace(block.OpenBraceToken)))
{
var forEachVariableSymbol = context.SemanticModel.GetDeclaredSymbol(forEachStatement, context.CancellationToken) as ILocalSymbol;
var forEachVariableSymbol = semanticModel.GetDeclaredSymbol(forEachStatement, cancellationToken) as ILocalSymbol;

if (forEachVariableSymbol is not null)
{
ContainsLocalOrParameterReferenceWalker walker = ContainsLocalOrParameterReferenceWalker.GetInstance(forEachVariableSymbol, context.SemanticModel, context.CancellationToken);
ContainsLocalOrParameterReferenceWalker walker = ContainsLocalOrParameterReferenceWalker.GetInstance(forEachVariableSymbol, semanticModel, cancellationToken);

walker.Visit(invocationInfo.Expression);

Expand Down
20 changes: 20 additions & 0 deletions src/Tests/Analyzers.Tests/RCS1235OptimizeMethodCallTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,26 @@ class C
return (positive, negative);
}
}
");
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.OptimizeMethodCall)]
public async Task TestNoDiagnostic_Recursion()
{
await VerifyNoDiagnosticAsync(@"
using System.Collections.Generic;
using System.Collections.ObjectModel;
class MyCollection : Collection<string>
{
public void AddRange(IEnumerable<string> data)
{
foreach (string item in data)
{
this.Add(item);
}
}
}
");
}
}

0 comments on commit 56d9df3

Please sign in to comment.