| File: BoundTree\BoundTreeWalker.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp { internal abstract partial class BoundTreeWalker : BoundTreeVisitor { protected BoundTreeWalker() { } public void VisitList<T>(ImmutableArray<T> list) where T : BoundNode { if (!list.IsDefault) { for (int i = 0; i < list.Length; i++) { this.Visit(list[i]); } } } protected void VisitUnoptimizedForm(BoundQueryClause queryClause) { BoundExpression? unoptimizedForm = queryClause.UnoptimizedForm; // The unoptimized form of a query has an additional argument in the call, // which is typically the "trivial" expression x where x is the query // variable. So that we can make sense of x in this // context, we store the unoptimized form and visit this extra argument. var qc = unoptimizedForm as BoundQueryClause; if (qc != null) unoptimizedForm = qc.Value; var call = unoptimizedForm as BoundCall; if (call != null && (object)call.Method != null) { var arguments = call.Arguments; if (call.Method.Name == "Select") { this.Visit(arguments[arguments.Length - 1]); } else if (call.Method.Name == "GroupBy") { this.Visit(arguments[arguments.Length - 2]); } } } } /// <summary> /// Note: do not use a static/singleton instance of this type, as it holds state. /// </summary> internal abstract class BoundTreeWalkerWithStackGuard : BoundTreeWalker { private int _recursionDepth; protected BoundTreeWalkerWithStackGuard() { } protected BoundTreeWalkerWithStackGuard(int recursionDepth) { _recursionDepth = recursionDepth; } protected int RecursionDepth => _recursionDepth; public override BoundNode? Visit(BoundNode? node) { if (node is BoundExpression or BoundPattern) { return VisitExpressionOrPatternWithStackGuard(ref _recursionDepth, node); } return base.Visit(node); } protected BoundNode VisitExpressionOrPatternWithStackGuard(BoundNode node) { return VisitExpressionOrPatternWithStackGuard(ref _recursionDepth, node); } protected sealed override BoundNode VisitExpressionOrPatternWithoutStackGuard(BoundNode node) { Debug.Assert(node is BoundExpression or BoundPattern); return base.Visit(node); } } /// <summary> /// Note: do not use a static/singleton instance of this type, as it holds state. /// </summary> internal abstract class BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator : BoundTreeWalkerWithStackGuard { protected BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator() { } protected BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator(int recursionDepth) : base(recursionDepth) { } public sealed override BoundNode? VisitBinaryOperator(BoundBinaryOperator node) { if (node.Left is not BoundBinaryOperator binary) { return base.VisitBinaryOperator(node); } var rightOperands = ArrayBuilder<BoundExpression>.GetInstance(); rightOperands.Push(node.Right); BeforeVisitingSkippedBoundBinaryOperatorChildren(binary); rightOperands.Push(binary.Right); BoundExpression? current = binary.Left; while (current.Kind == BoundKind.BinaryOperator) { binary = (BoundBinaryOperator)current; BeforeVisitingSkippedBoundBinaryOperatorChildren(binary); rightOperands.Push(binary.Right); current = binary.Left; } this.Visit(current); current = rightOperands.Pop(); do { this.Visit(current); } while (rightOperands.TryPop(out current)); rightOperands.Free(); return null; } protected virtual void BeforeVisitingSkippedBoundBinaryOperatorChildren(BoundBinaryOperator node) { } public sealed override BoundNode? VisitBinaryPattern(BoundBinaryPattern node) { if (node.Left is not BoundBinaryPattern binary) { return base.VisitBinaryPattern(node); } var rightOperands = ArrayBuilder<BoundPattern>.GetInstance(); rightOperands.Push(node.Right); rightOperands.Push(binary.Right); BoundPattern? current = binary.Left; while (current.Kind == BoundKind.BinaryPattern) { binary = (BoundBinaryPattern)current; rightOperands.Push(binary.Right); current = binary.Left; } Visit(current); current = rightOperands.Pop(); do { Visit(current); } while (rightOperands.TryPop(out current)); rightOperands.Free(); return null; } public override BoundNode? VisitCall(BoundCall node) { if (node.ReceiverOpt is BoundCall receiver1) { var calls = ArrayBuilder<BoundCall>.GetInstance(); calls.Push(node); node = receiver1; while (node.ReceiverOpt is BoundCall receiver2) { BeforeVisitingSkippedBoundCallChildren(node); calls.Push(node); node = receiver2; } BeforeVisitingSkippedBoundCallChildren(node); VisitReceiver(node); do { VisitArguments(node); } while (calls.TryPop(out node!)); calls.Free(); } else { VisitReceiver(node); VisitArguments(node); } return null; } protected virtual void BeforeVisitingSkippedBoundCallChildren(BoundCall node) { } /// <summary> /// Called only for the first (in evaluation order) <see cref="BoundCall"/> in the chain. /// </summary> protected virtual void VisitReceiver(BoundCall node) { this.Visit(node.ReceiverOpt); } protected virtual void VisitArguments(BoundCall node) { this.VisitList(node.Arguments); } public sealed override BoundNode? VisitIfStatement(BoundIfStatement node) { while (true) { Visit(node.Condition); Visit(node.Consequence); var alternative = node.AlternativeOpt; if (alternative is null) { break; } if (alternative is BoundIfStatement elseIfStatement) { node = elseIfStatement; } else { Visit(alternative); break; } } return null; } } }