@@ -30,63 +30,62 @@ public override void Initialize(AnalysisContext context)
3030 private static void AnalyzeInvocation ( SyntaxNodeAnalysisContext context )
3131 {
3232 var invocation = ( InvocationExpressionSyntax ) context . Node ;
33+ if ( invocation . Expression is not MemberAccessExpressionSyntax memberAccess )
34+ {
35+ return ;
36+ }
3337
34- if ( invocation . Expression is MemberAccessExpressionSyntax memberAccess )
38+ // Check for .Split().First() or .Split().FirstOrDefault()
39+ if ( memberAccess . Name . Identifier . Text is "First" or "FirstOrDefault"
40+ && memberAccess . Expression is InvocationExpressionSyntax innerInvocation
41+ && innerInvocation . Expression is MemberAccessExpressionSyntax innerMemberAccess
42+ && innerMemberAccess . Name . Identifier . Text == "Split" )
3543 {
36- // Check for .Split().First() or .Split().FirstOrDefault()
37- if ( memberAccess . Name . Identifier . Text is "First" or "FirstOrDefault" )
44+ var symbolInfo = context . SemanticModel . GetSymbolInfo ( innerMemberAccess , context . CancellationToken ) ;
45+ if ( symbolInfo . Symbol is IMethodSymbol innerMethod
46+ && innerMethod . ContainingType ? . SpecialType == SpecialType . System_String )
3847 {
39- if ( memberAccess . Expression is InvocationExpressionSyntax innerInvocation &&
40- innerInvocation . Expression is MemberAccessExpressionSyntax innerMemberAccess &&
41- innerMemberAccess . Name . Identifier . Text == "Split" )
42- {
43- var symbolInfo = context . SemanticModel . GetSymbolInfo ( innerMemberAccess , context . CancellationToken ) ;
44- if ( symbolInfo . Symbol is IMethodSymbol innerMethod &&
45- innerMethod . ContainingType ? . SpecialType == SpecialType . System_String )
46- {
47- var argsString = GetArgumentsString ( innerInvocation . ArgumentList . Arguments ) ;
48- var diagnostic = Diagnostic . Create (
49- DiagnosticDescriptors . UseFirstSplitInsteadOfSplitFirst ,
50- invocation . GetLocation ( ) ,
51- argsString ) ;
52- context . ReportDiagnostic ( diagnostic ) ;
53- return ;
54- }
55- }
48+ var argsString = GetArgumentsString ( innerInvocation . ArgumentList . Arguments ) ;
49+ var diagnostic = Diagnostic . Create (
50+ DiagnosticDescriptors . UseFirstSplitInsteadOfSplitFirst ,
51+ invocation . GetLocation ( ) ,
52+ argsString ) ;
53+ context . ReportDiagnostic ( diagnostic ) ;
54+ return ;
5655 }
56+ }
57+
58+ // Check for .Split() that should use SplitAsSegments or SplitToEnumerable
59+ if ( memberAccess . Name . Identifier . Text == "Split" )
60+ {
61+ var symbolInfo = context . SemanticModel . GetSymbolInfo ( memberAccess , context . CancellationToken ) ;
62+ if ( symbolInfo . Symbol is not IMethodSymbol methodSymbol )
63+ return ;
5764
58- // Check for .Split() that should use SplitAsSegments or SplitToEnumerable
59- if ( memberAccess . Name . Identifier . Text == "Split" )
65+ if ( methodSymbol . ContainingType ? . SpecialType != SpecialType . System_String )
66+ return ;
67+
68+ // Check how the result is used
69+ var isUsedInLoop = IsUsedInForeachLoop ( invocation ) ;
70+ var argsString = GetArgumentsString ( invocation . ArgumentList . Arguments ) ;
71+
72+ if ( isUsedInLoop )
73+ {
74+ // Suggest SplitToEnumerable for foreach loops
75+ var diagnostic = Diagnostic . Create (
76+ DiagnosticDescriptors . UseSplitToEnumerable ,
77+ invocation . GetLocation ( ) ,
78+ argsString ) ;
79+ context . ReportDiagnostic ( diagnostic ) ;
80+ }
81+ else
6082 {
61- var symbolInfo = context . SemanticModel . GetSymbolInfo ( memberAccess , context . CancellationToken ) ;
62- if ( symbolInfo . Symbol is not IMethodSymbol methodSymbol )
63- return ;
64-
65- if ( methodSymbol . ContainingType ? . SpecialType != SpecialType . System_String )
66- return ;
67-
68- // Check how the result is used
69- var isUsedInLoop = IsUsedInForeachLoop ( invocation ) ;
70- var argsString = GetArgumentsString ( invocation . ArgumentList . Arguments ) ;
71-
72- if ( isUsedInLoop )
73- {
74- // Suggest SplitToEnumerable for foreach loops
75- var diagnostic = Diagnostic . Create (
76- DiagnosticDescriptors . UseSplitToEnumerable ,
77- invocation . GetLocation ( ) ,
78- argsString ) ;
79- context . ReportDiagnostic ( diagnostic ) ;
80- }
81- else
82- {
83- // General suggestion for SplitAsSegments
84- var diagnostic = Diagnostic . Create (
85- DiagnosticDescriptors . UseSplitAsSegments ,
86- invocation . GetLocation ( ) ,
87- argsString ) ;
88- context . ReportDiagnostic ( diagnostic ) ;
89- }
83+ // General suggestion for SplitAsSegments
84+ var diagnostic = Diagnostic . Create (
85+ DiagnosticDescriptors . UseSplitAsSegments ,
86+ invocation . GetLocation ( ) ,
87+ argsString ) ;
88+ context . ReportDiagnostic ( diagnostic ) ;
9089 }
9190 }
9291 }
@@ -96,40 +95,48 @@ private static void AnalyzeElementAccess(SyntaxNodeAnalysisContext context)
9695 var elementAccess = ( ElementAccessExpressionSyntax ) context . Node ;
9796
9897 // Check for .Split()[0] or .Split()[index]
99- if ( elementAccess . Expression is InvocationExpressionSyntax invocation &&
100- invocation . Expression is MemberAccessExpressionSyntax memberAccess &&
101- memberAccess . Name . Identifier . Text = = "Split" )
98+ if ( elementAccess . Expression is not InvocationExpressionSyntax invocation
99+ || invocation . Expression is not MemberAccessExpressionSyntax memberAccess
100+ || memberAccess . Name . Identifier . Text ! = "Split" )
102101 {
103- var symbolInfo = context . SemanticModel . GetSymbolInfo ( memberAccess , context . CancellationToken ) ;
104- if ( symbolInfo . Symbol is IMethodSymbol methodSymbol &&
105- methodSymbol . ContainingType ? . SpecialType == SpecialType . System_String )
106- {
107- // Check if accessing index 0
108- if ( elementAccess . ArgumentList . Arguments . Count == 1 )
109- {
110- var argument = elementAccess . ArgumentList . Arguments [ 0 ] ;
111- if ( argument . Expression is LiteralExpressionSyntax literal &&
112- literal . Token . ValueText == "0" )
113- {
114- var argsString = GetArgumentsString ( invocation . ArgumentList . Arguments ) ;
115- var diagnostic = Diagnostic . Create (
116- DiagnosticDescriptors . UseFirstSplitInsteadOfSplitFirst ,
117- elementAccess . GetLocation ( ) ,
118- argsString ) ;
119- context . ReportDiagnostic ( diagnostic ) ;
120- }
121- }
122- }
102+ return ;
103+ }
104+
105+ var symbolInfo = context . SemanticModel . GetSymbolInfo ( memberAccess , context . CancellationToken ) ;
106+ if ( symbolInfo . Symbol is not IMethodSymbol methodSymbol
107+ || methodSymbol . ContainingType ? . SpecialType != SpecialType . System_String )
108+ {
109+ return ;
123110 }
111+
112+ // Check if accessing index 0
113+ if ( elementAccess . ArgumentList . Arguments . Count != 1 )
114+ {
115+ return ;
116+ }
117+
118+ var argument = elementAccess . ArgumentList . Arguments [ 0 ] ;
119+ if ( argument . Expression is not LiteralExpressionSyntax literal
120+ || literal . Token . ValueText != "0" )
121+ {
122+ return ;
123+ }
124+
125+ var argsString = GetArgumentsString ( invocation . ArgumentList . Arguments ) ;
126+ var diagnostic = Diagnostic . Create (
127+ DiagnosticDescriptors . UseFirstSplitInsteadOfSplitFirst ,
128+ elementAccess . GetLocation ( ) ,
129+ argsString ) ;
130+ context . ReportDiagnostic ( diagnostic ) ;
124131 }
125132
126133 private static bool IsUsedInForeachLoop ( SyntaxNode node )
127134 {
128135 var parent = node . Parent ;
129136 while ( parent != null )
130137 {
131- if ( parent is ForEachStatementSyntax foreachStatement &&
132- foreachStatement . Expression . Contains ( node ) )
138+ if ( parent is ForEachStatementSyntax foreachStatement
139+ && foreachStatement . Expression . Contains ( node ) )
133140 {
134141 return true ;
135142 }
0 commit comments