Skip to content

Commit cd2d837

Browse files
Port stackoverflow fix from Roslyn to SourceGenerator PolyFill (#76954)
Revert Remove
1 parent 075e74e commit cd2d837

File tree

1 file changed

+96
-52
lines changed

1 file changed

+96
-52
lines changed

src/libraries/Common/src/Roslyn/SyntaxValueProvider_ForAttributeWithSimpleName.cs

Lines changed: 96 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ private static ImmutableArray<SyntaxNode> GetMatchingNodes(
215215

216216
try
217217
{
218-
recurse(compilationUnit, ref localAliases, ref seenNames, ref results, ref attributeTargets);
218+
processCompilationUnit(compilationUnit, ref localAliases, ref seenNames, ref results, ref attributeTargets);
219219

220220
if (results.Length == 0)
221221
return ImmutableArray<SyntaxNode>.Empty;
@@ -229,7 +229,21 @@ private static ImmutableArray<SyntaxNode> GetMatchingNodes(
229229
seenNames.Dispose();
230230
}
231231

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(
233247
SyntaxNode node,
234248
ref Aliases localAliases,
235249
ref ValueListBuilder<string> seenNames,
@@ -238,70 +252,100 @@ void recurse(
238252
{
239253
cancellationToken.ThrowIfCancellationRequested();
240254

241-
if (node is ICompilationUnitSyntax)
255+
foreach (var child in node.ChildNodesAndTokens())
242256
{
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+
}
246265
}
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();
251276

252-
recurseChildren(node, ref localAliases, ref seenNames, ref results, ref attributeTargets);
277+
var localAliasCount = localAliases.Length;
278+
syntaxHelper.AddAliases(namespaceBlock, ref localAliases, global: false);
253279

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
258301
{
259-
foreach (var attribute in syntaxHelper.GetAttributesOfAttributeList(node))
302+
while (nodeStack.Length > 0)
260303
{
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();
270305

271-
foreach (var target in attributeTargets.AsSpan())
306+
if (syntaxHelper.IsAttributeList(node))
307+
{
308+
foreach (var attribute in syntaxHelper.GetAttributesOfAttributeList(node))
272309
{
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+
}
275328
}
276329

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+
}
278342
}
279-
}
280343

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+
}
289345
}
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
299347
{
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();
305349
}
306350
}
307351

0 commit comments

Comments
 (0)