From 8770fb62a36157ed4ca38a16a0283d27321a01a7 Mon Sep 17 00:00:00 2001 From: Fred Silberberg Date: Tue, 7 Mar 2023 12:33:04 -0800 Subject: [PATCH] Walk green tree in SyntaxFacts.HasYieldOperations to avoid excessive allocations in reported trace (#67221) * Walk green tree to avoid excessive allocations in reported trace * Make private * Remove comment * Free stack on return * Include github link for attribute descriptions. --------- Co-authored-by: Cyrus Najmabadi Co-authored-by: Rikki Gibson --- .../CSharp/Portable/Syntax/SyntaxFacts.cs | 62 +++++++++++++------ 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs index e19e0aa498b65..2723d5a623788 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs @@ -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; @@ -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.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)