66using System . Linq ;
77using Analyzer . Utilities ;
88using Analyzer . Utilities . Extensions ;
9- using Analyzer . Utilities . PooledObjects ;
109using Microsoft . CodeAnalysis ;
1110using Microsoft . CodeAnalysis . Diagnostics ;
1211using Microsoft . CodeAnalysis . Operations ;
@@ -32,11 +31,9 @@ public abstract class UseSpanBasedStringConcat : DiagnosticAnalyzer
3231 isDataflowRule : false ) ;
3332
3433 /// <summary>
35- /// If the specified binary operation is a string concatenation operation, we try to walk up to the top-most
36- /// string-concatenation operation that it is part of. If it is not a string-concatenation operation, we simply
37- /// return false.
34+ /// Returns true if the specified binary operation is a top-most string concatenation operation
3835 /// </summary>
39- private protected abstract bool TryGetTopMostConcatOperation ( IBinaryOperation binaryOperation , [ NotNullWhen ( true ) ] out IBinaryOperation ? rootBinaryOperation ) ;
36+ private protected abstract bool IsTopMostConcatOperation ( IBinaryOperation binaryOperation ) ;
4037
4138 /// <summary>
4239 /// Remove the built in implicit conversion on operands to concat.
@@ -59,49 +56,19 @@ private void OnCompilationStart(CompilationStartAnalysisContext context)
5956 if ( ! RequiredSymbols . TryGetSymbols ( context . Compilation , out RequiredSymbols symbols ) )
6057 return ;
6158
62- context . RegisterOperationBlockStartAction ( OnOperationBlockStart ) ;
63- return ;
64-
65- // Local functions
66- void OnOperationBlockStart ( OperationBlockStartAnalysisContext context )
59+ context . RegisterOperationAction ( context =>
6760 {
68- // Maintain set of all top-most concat operations so we don't report sub-expressions of an
69- // already-reported violation.
61+ // Report diagnostic only if the operation is the top-most concat operation so we don't report sub-expressions of an
62+ // already-reported violation.
7063 // We also don't report any diagnostic if the concat operation has too many operands for the span-based
7164 // Concat overloads to handle.
72- var topMostConcatOperations = TemporarySet < IBinaryOperation > . Empty ;
73-
74- context . RegisterOperationAction ( PopulateTopMostConcatOperations , OperationKind . Binary ) ;
75- context . RegisterOperationBlockEndAction ( ReportDiagnosticsOnRootConcatOperationsWithSubstringCalls ) ;
76-
77- void PopulateTopMostConcatOperations ( OperationAnalysisContext context )
78- {
79- // If the current operation is a string-concatenation operation, walk up to the top-most concat
80- // operation and add it to the set.
81- var binary = ( IBinaryOperation ) context . Operation ;
82- if ( ! TryGetTopMostConcatOperation ( binary , out var topMostConcatOperation ) )
83- return ;
84-
85- topMostConcatOperations . Add ( topMostConcatOperation , context . CancellationToken ) ;
86- }
87-
88- void ReportDiagnosticsOnRootConcatOperationsWithSubstringCalls ( OperationBlockAnalysisContext context )
89- {
90- // We report diagnostics for all top-most concat operations that contain
91- // direct or conditional substring invocations when there is an applicable span-based overload of
92- // the string.Concat method.
93- // We don't report when the concatenation contains anything other than strings or character literals.
94- foreach ( var operation in topMostConcatOperations . NonConcurrentEnumerable )
95- {
96- if ( ShouldBeReported ( operation ) )
97- {
98- context . ReportDiagnostic ( operation . CreateDiagnostic ( Rule ) ) ;
99- }
100- }
65+ var binary = ( IBinaryOperation ) context . Operation ;
66+ if ( IsTopMostConcatOperation ( binary ) && ShouldBeReported ( binary ) )
67+ context . ReportDiagnostic ( binary . CreateDiagnostic ( Rule ) ) ;
68+ } , OperationKind . Binary ) ;
69+ return ;
10170
102- topMostConcatOperations . Free ( context . CancellationToken ) ;
103- }
104- }
71+ // Local functions
10572
10673 bool ShouldBeReported ( IBinaryOperation topMostConcatOperation )
10774 {
@@ -133,8 +100,8 @@ bool ShouldBeReported(IBinaryOperation topMostConcatOperation)
133100
134101 bool IsAnyDirectOrConditionalSubstringInvocation ( IOperation operation )
135102 {
136- if ( operation is IConditionalAccessOperation conditionallAccessOperation )
137- operation = conditionallAccessOperation . WhenNotNull ;
103+ if ( operation is IConditionalAccessOperation conditionalAccessOperation )
104+ operation = conditionalAccessOperation . WhenNotNull ;
138105
139106 return operation is IInvocationOperation invocation && symbols . IsAnySubstringMethod ( invocation . TargetMethod ) ;
140107 }
0 commit comments