Skip to content

Commit cc6ad5b

Browse files
Bart KoelmanBart Koelman
authored andcommitted
AV1532: Switched to syntax-based analysis (10x faster)
1 parent c5dd70b commit cc6ad5b

File tree

1 file changed

+106
-19
lines changed

1 file changed

+106
-19
lines changed

src/CSharpGuidelinesAnalyzer/CSharpGuidelinesAnalyzer/Rules/Maintainability/AvoidNestedLoopsAnalyzer.cs

Lines changed: 106 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
using System;
22
using System.Collections.Immutable;
3-
using CSharpGuidelinesAnalyzer.Extensions;
3+
using System.Threading;
44
using JetBrains.Annotations;
55
using Microsoft.CodeAnalysis;
6+
using Microsoft.CodeAnalysis.CSharp;
7+
using Microsoft.CodeAnalysis.CSharp.Syntax;
68
using Microsoft.CodeAnalysis.Diagnostics;
7-
using Microsoft.CodeAnalysis.Operations;
89

910
namespace CSharpGuidelinesAnalyzer.Rules.Maintainability
1011
{
@@ -28,53 +29,139 @@ public sealed class AvoidNestedLoopsAnalyzer : DiagnosticAnalyzer
2829
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
2930

3031
[NotNull]
31-
private static readonly Action<OperationAnalysisContext> AnalyzeLoopStatementAction =
32-
context => context.SkipInvalid(AnalyzeLoopStatement);
32+
private static readonly SyntaxKind[] LoopStatementKinds =
33+
{
34+
SyntaxKind.WhileStatement,
35+
SyntaxKind.DoStatement,
36+
SyntaxKind.ForStatement,
37+
SyntaxKind.ForEachStatement,
38+
SyntaxKind.ForEachVariableStatement
39+
};
40+
41+
[NotNull]
42+
private static readonly Action<SyntaxNodeAnalysisContext> AnalyzeLoopStatementAction = AnalyzeLoopStatement;
3343

3444
public override void Initialize([NotNull] AnalysisContext context)
3545
{
3646
context.EnableConcurrentExecution();
3747
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
3848

39-
context.RegisterOperationAction(AnalyzeLoopStatementAction, OperationKind.Loop);
49+
context.RegisterSyntaxNodeAction(AnalyzeLoopStatementAction, LoopStatementKinds);
4050
}
4151

42-
private static void AnalyzeLoopStatement(OperationAnalysisContext context)
43-
{
44-
var loopStatement = (ILoopOperation)context.Operation;
52+
[NotNull]
53+
private static readonly LoopBodyLocator BodyLocator = new LoopBodyLocator();
4554

46-
var walker = new LoopBodyWalker();
47-
walker.Visit(loopStatement.Body);
55+
private static void AnalyzeLoopStatement(SyntaxNodeAnalysisContext context)
56+
{
57+
StatementSyntax loopBody = BodyLocator.Visit(context.Node);
58+
if (loopBody != null)
59+
{
60+
AnalyzeLoopBody(loopBody, context);
61+
}
62+
}
4863

49-
context.CancellationToken.ThrowIfCancellationRequested();
64+
private static void AnalyzeLoopBody([NotNull] StatementSyntax loopBody, SyntaxNodeAnalysisContext context)
65+
{
66+
var walker = new LoopLocationWalker(context.CancellationToken);
67+
walker.Visit(loopBody);
5068

5169
if (walker.LoopStatementLocation != null)
5270
{
5371
context.ReportDiagnostic(Diagnostic.Create(Rule, walker.LoopStatementLocation));
5472
}
5573
}
5674

57-
private sealed class LoopBodyWalker : ExplicitOperationWalker
75+
private sealed class LoopBodyLocator : CSharpSyntaxVisitor<StatementSyntax>
5876
{
77+
[NotNull]
78+
public override StatementSyntax VisitWhileStatement([NotNull] WhileStatementSyntax node)
79+
{
80+
return node.Statement;
81+
}
82+
83+
[NotNull]
84+
public override StatementSyntax VisitDoStatement([NotNull] DoStatementSyntax node)
85+
{
86+
return node.Statement;
87+
}
88+
89+
[CanBeNull]
90+
public override StatementSyntax VisitForStatement([NotNull] ForStatementSyntax node)
91+
{
92+
return node.Statement;
93+
}
94+
95+
[CanBeNull]
96+
public override StatementSyntax VisitForEachStatement([NotNull] ForEachStatementSyntax node)
97+
{
98+
return node.Statement;
99+
}
100+
101+
[CanBeNull]
102+
public override StatementSyntax VisitForEachVariableStatement([NotNull] ForEachVariableStatementSyntax node)
103+
{
104+
return node.Statement;
105+
}
106+
}
107+
108+
private sealed class LoopLocationWalker : CSharpSyntaxWalker
109+
{
110+
private CancellationToken cancellationToken;
111+
59112
[CanBeNull]
60113
public Location LoopStatementLocation { get; private set; }
61114

62-
public override void VisitWhileLoop([NotNull] IWhileLoopOperation operation)
115+
public LoopLocationWalker(CancellationToken cancellationToken)
116+
{
117+
this.cancellationToken = cancellationToken;
118+
}
119+
120+
public override void Visit([NotNull] SyntaxNode node)
121+
{
122+
cancellationToken.ThrowIfCancellationRequested();
123+
124+
base.Visit(node);
125+
}
126+
127+
public override void VisitWhileStatement([NotNull] WhileStatementSyntax node)
128+
{
129+
LoopStatementLocation = node.WhileKeyword.GetLocation();
130+
}
131+
132+
public override void VisitDoStatement([NotNull] DoStatementSyntax node)
133+
{
134+
LoopStatementLocation = node.DoKeyword.GetLocation();
135+
}
136+
137+
public override void VisitForStatement([NotNull] ForStatementSyntax node)
138+
{
139+
LoopStatementLocation = node.ForKeyword.GetLocation();
140+
}
141+
142+
public override void VisitForEachStatement([NotNull] ForEachStatementSyntax node)
143+
{
144+
LoopStatementLocation = node.ForEachKeyword.GetLocation();
145+
}
146+
147+
public override void VisitForEachVariableStatement([NotNull] ForEachVariableStatementSyntax node)
148+
{
149+
LoopStatementLocation = node.ForEachKeyword.GetLocation();
150+
}
151+
152+
public override void VisitLocalFunctionStatement([NotNull] LocalFunctionStatementSyntax node)
63153
{
64-
LoopStatementLocation = operation.TryGetLocationForKeyword();
65154
}
66155

67-
public override void VisitForLoop([NotNull] IForLoopOperation operation)
156+
public override void VisitSimpleLambdaExpression([NotNull] SimpleLambdaExpressionSyntax node)
68157
{
69-
LoopStatementLocation = operation.TryGetLocationForKeyword();
70158
}
71159

72-
public override void VisitForEachLoop([NotNull] IForEachLoopOperation operation)
160+
public override void VisitParenthesizedLambdaExpression([NotNull] ParenthesizedLambdaExpressionSyntax node)
73161
{
74-
LoopStatementLocation = operation.TryGetLocationForKeyword();
75162
}
76163

77-
public override void VisitLocalFunction([NotNull] ILocalFunctionOperation operation)
164+
public override void VisitAnonymousMethodExpression([NotNull] AnonymousMethodExpressionSyntax node)
78165
{
79166
}
80167
}

0 commit comments

Comments
 (0)