Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Walk green tree in SyntaxFacts.HasYieldOperations to avoid excessive allocations in reported trace #67221

Merged
merged 5 commits into from
Mar 7, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 44 additions & 18 deletions src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
using static Microsoft.CodeAnalysis.CSharp.SyntaxKind;

Expand Down Expand Up @@ -543,29 +544,54 @@ internal static bool HasAwaitOperations(SyntaxNode node)
});
}

internal static bool IsNestedFunction(SyntaxNode child)
private static bool IsNestedFunction(SyntaxNode child)
=> IsNestedFunction(child.Kind());

private static bool IsNestedFunction(SyntaxKind kind)
=> kind is SyntaxKind.LocalFunctionStatement
or SyntaxKind.AnonymousMethodExpression
or SyntaxKind.SimpleLambdaExpression
or SyntaxKind.ParenthesizedLambdaExpression;

[PerformanceSensitive("https://github.com/dotnet/roslyn/pull/66970", Constraint = "Use Green nodes for walking to avoid heavy allocations.")]
internal static bool HasYieldOperations(SyntaxNode? node)
{
switch (child.Kind())
if (node is null)
return false;

var stack = ArrayBuilder<GreenNode>.GetInstance();
stack.Push(node.Green);

while (stack.Count > 0)
{
case SyntaxKind.LocalFunctionStatement:
case SyntaxKind.AnonymousMethodExpression:
case SyntaxKind.SimpleLambdaExpression:
case SyntaxKind.ParenthesizedLambdaExpression:
var current = stack.Pop();
Debug.Assert(node.Green == current || current is not Syntax.InternalSyntax.MemberDeclarationSyntax and not Syntax.InternalSyntax.TypeDeclarationSyntax);

if (current is null)
continue;

// Do not descend into functions and expressions
if (IsNestedFunction((SyntaxKind)current.RawKind) ||
current is Syntax.InternalSyntax.ExpressionSyntax)
{
continue;
}

if (current is Syntax.InternalSyntax.YieldStatementSyntax)
{
stack.Free();
return true;
default:
return false;
}

foreach (var child in current.ChildNodesAndTokens())
{
if (!child.IsToken)
stack.Push(child);
}
}
}

internal static bool HasYieldOperations(SyntaxNode? node)
{
// Do not descend into functions and expressions
return node is object &&
node.DescendantNodesAndSelf(child =>
{
Debug.Assert(ReferenceEquals(node, child) || child is not (MemberDeclarationSyntax or TypeDeclarationSyntax));
return !IsNestedFunction(child) && !(node is ExpressionSyntax);
}).Any(n => n is YieldStatementSyntax);
stack.Free();
return false;
}

internal static bool HasReturnWithExpression(SyntaxNode? node)
Expand Down