@@ -215,7 +215,7 @@ private static ImmutableArray<SyntaxNode> GetMatchingNodes(
215
215
216
216
try
217
217
{
218
- recurse ( compilationUnit , ref localAliases , ref seenNames , ref results , ref attributeTargets ) ;
218
+ processCompilationUnit ( compilationUnit , ref localAliases , ref seenNames , ref results , ref attributeTargets ) ;
219
219
220
220
if ( results . Length == 0 )
221
221
return ImmutableArray < SyntaxNode > . Empty ;
@@ -229,7 +229,21 @@ private static ImmutableArray<SyntaxNode> GetMatchingNodes(
229
229
seenNames . Dispose ( ) ;
230
230
}
231
231
232
- void recurse (
232
+ void processCompilationUnit (
233
+ SyntaxNode compilationUnit ,
234
+ ref Aliases localAliases ,
235
+ ref ValueListBuilder < string > seenNames ,
236
+ ref ValueListBuilder < SyntaxNode > results ,
237
+ ref ValueListBuilder < SyntaxNode > attributeTargets )
238
+ {
239
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
240
+
241
+ syntaxHelper . AddAliases ( compilationUnit , ref localAliases , global : false ) ;
242
+
243
+ processCompilationOrNamespaceMembers ( compilationUnit , ref localAliases , ref seenNames , ref results , ref attributeTargets ) ;
244
+ }
245
+
246
+ void processCompilationOrNamespaceMembers (
233
247
SyntaxNode node ,
234
248
ref Aliases localAliases ,
235
249
ref ValueListBuilder < string > seenNames ,
@@ -238,70 +252,100 @@ void recurse(
238
252
{
239
253
cancellationToken . ThrowIfCancellationRequested ( ) ;
240
254
241
- if ( node is ICompilationUnitSyntax )
255
+ foreach ( var child in node . ChildNodesAndTokens ( ) )
242
256
{
243
- syntaxHelper . AddAliases ( node , ref localAliases , global : false ) ;
244
-
245
- recurseChildren ( node , ref localAliases , ref seenNames , ref results , ref attributeTargets ) ;
257
+ if ( child . IsNode )
258
+ {
259
+ var childNode = child . AsNode ( ) ! ;
260
+ if ( syntaxHelper . IsAnyNamespaceBlock ( childNode ) )
261
+ processNamespaceBlock ( childNode , ref localAliases , ref seenNames , ref results , ref attributeTargets ) ;
262
+ else
263
+ processMember ( childNode , ref localAliases , ref seenNames , ref results , ref attributeTargets ) ;
264
+ }
246
265
}
247
- else if ( syntaxHelper . IsAnyNamespaceBlock ( node ) )
248
- {
249
- var localAliasCount = localAliases . Length ;
250
- syntaxHelper . AddAliases ( node , ref localAliases , global : false ) ;
266
+ }
267
+
268
+ void processNamespaceBlock (
269
+ SyntaxNode namespaceBlock ,
270
+ ref Aliases localAliases ,
271
+ ref ValueListBuilder < string > seenNames ,
272
+ ref ValueListBuilder < SyntaxNode > results ,
273
+ ref ValueListBuilder < SyntaxNode > attributeTargets )
274
+ {
275
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
251
276
252
- recurseChildren ( node , ref localAliases , ref seenNames , ref results , ref attributeTargets ) ;
277
+ var localAliasCount = localAliases . Length ;
278
+ syntaxHelper . AddAliases ( namespaceBlock , ref localAliases , global : false ) ;
253
279
254
- // after recursing into this namespace, dump any local aliases we added from this namespace decl itself.
255
- localAliases . Length = localAliasCount ;
256
- }
257
- else if ( syntaxHelper . IsAttributeList ( node ) )
280
+ processCompilationOrNamespaceMembers (
281
+ namespaceBlock , ref localAliases , ref seenNames , ref results , ref attributeTargets ) ;
282
+
283
+ // after recursing into this namespace, dump any local aliases we added from this namespace decl itself.
284
+ localAliases . Length = localAliasCount ;
285
+ }
286
+
287
+ void processMember (
288
+ SyntaxNode member ,
289
+ ref Aliases localAliases ,
290
+ ref ValueListBuilder < string > seenNames ,
291
+ ref ValueListBuilder < SyntaxNode > results ,
292
+ ref ValueListBuilder < SyntaxNode > attributeTargets )
293
+ {
294
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
295
+
296
+ // nodes can be arbitrarily deep. Use an explicit stack over recursion to prevent a stack-overflow.
297
+ var nodeStack = new ValueListBuilder < SyntaxNode > ( Span < SyntaxNode > . Empty ) ;
298
+ nodeStack . Append ( member ) ;
299
+
300
+ try
258
301
{
259
- foreach ( var attribute in syntaxHelper . GetAttributesOfAttributeList ( node ) )
302
+ while ( nodeStack . Length > 0 )
260
303
{
261
- // Have to lookup both with the name in the attribute, as well as adding the 'Attribute' suffix.
262
- // e.g. if there is [X] then we have to lookup with X and with XAttribute.
263
- var simpleAttributeName = syntaxHelper . GetUnqualifiedIdentifierOfName (
264
- syntaxHelper . GetNameOfAttribute ( attribute ) ) . ValueText ;
265
- if ( matchesAttributeName ( ref localAliases , ref seenNames , simpleAttributeName , withAttributeSuffix : false ) ||
266
- matchesAttributeName ( ref localAliases , ref seenNames , simpleAttributeName , withAttributeSuffix : true ) )
267
- {
268
- attributeTargets . Length = 0 ;
269
- syntaxHelper . AddAttributeTargets ( node , ref attributeTargets ) ;
304
+ var node = nodeStack . Pop ( ) ;
270
305
271
- foreach ( var target in attributeTargets . AsSpan ( ) )
306
+ if ( syntaxHelper . IsAttributeList ( node ) )
307
+ {
308
+ foreach ( var attribute in syntaxHelper . GetAttributesOfAttributeList ( node ) )
272
309
{
273
- if ( predicate ( target , cancellationToken ) )
274
- results . Append ( target ) ;
310
+ // Have to lookup both with the name in the attribute, as well as adding the 'Attribute' suffix.
311
+ // e.g. if there is [X] then we have to lookup with X and with XAttribute.
312
+ var simpleAttributeName = syntaxHelper . GetUnqualifiedIdentifierOfName (
313
+ syntaxHelper . GetNameOfAttribute ( attribute ) ) . ValueText ;
314
+ if ( matchesAttributeName ( ref localAliases , ref seenNames , simpleAttributeName , withAttributeSuffix : false ) ||
315
+ matchesAttributeName ( ref localAliases , ref seenNames , simpleAttributeName , withAttributeSuffix : true ) )
316
+ {
317
+ attributeTargets . Length = 0 ;
318
+ syntaxHelper . AddAttributeTargets ( node , ref attributeTargets ) ;
319
+
320
+ foreach ( var target in attributeTargets . AsSpan ( ) )
321
+ {
322
+ if ( predicate ( target , cancellationToken ) )
323
+ results . Append ( target ) ;
324
+ }
325
+
326
+ break ;
327
+ }
275
328
}
276
329
277
- return ;
330
+ // attributes can't have attributes inside of them. so no need to recurse when we're done.
331
+ }
332
+ else
333
+ {
334
+ // For any other node, just keep recursing deeper to see if we can find an attribute. Note: we cannot
335
+ // terminate the search anywhere as attributes may be found on things like local functions, and that
336
+ // means having to dive deep into statements and expressions.
337
+ foreach ( var child in node . ChildNodesAndTokens ( ) . Reverse ( ) )
338
+ {
339
+ if ( child . IsNode )
340
+ nodeStack . Append ( child . AsNode ( ) ! ) ;
341
+ }
278
342
}
279
- }
280
343
281
- // attributes can't have attributes inside of them. so no need to recurse when we're done.
282
- }
283
- else
284
- {
285
- // For any other node, just keep recursing deeper to see if we can find an attribute. Note: we cannot
286
- // terminate the search anywhere as attributes may be found on things like local functions, and that
287
- // means having to dive deep into statements and expressions.
288
- recurseChildren ( node , ref localAliases , ref seenNames , ref results , ref attributeTargets ) ;
344
+ }
289
345
}
290
-
291
- return ;
292
-
293
- void recurseChildren (
294
- SyntaxNode node ,
295
- ref Aliases localAliases ,
296
- ref ValueListBuilder < string > seenNames ,
297
- ref ValueListBuilder < SyntaxNode > results ,
298
- ref ValueListBuilder < SyntaxNode > attributeTargets )
346
+ finally
299
347
{
300
- foreach ( var child in node . ChildNodesAndTokens ( ) )
301
- {
302
- if ( child . IsNode )
303
- recurse ( child . AsNode ( ) ! , ref localAliases , ref seenNames , ref results , ref attributeTargets ) ;
304
- }
348
+ nodeStack . Dispose ( ) ;
305
349
}
306
350
}
307
351
0 commit comments