|
// 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.
#nullable disable
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System;
using Roslyn.Utilities;
using System.Linq;
namespace Microsoft.CodeAnalysis.CSharp
{
internal abstract partial class BoundTreeVisitor<A, R>
{
protected BoundTreeVisitor()
{
}
public virtual R Visit(BoundNode node, A arg)
{
if (node == null)
{
return default(R);
}
// this switch contains fewer than 50 of the most common node kinds
switch (node.Kind)
{
case BoundKind.TypeExpression:
return VisitTypeExpression(node as BoundTypeExpression, arg);
case BoundKind.NamespaceExpression:
return VisitNamespaceExpression(node as BoundNamespaceExpression, arg);
case BoundKind.UnaryOperator:
return VisitUnaryOperator(node as BoundUnaryOperator, arg);
case BoundKind.IncrementOperator:
return VisitIncrementOperator(node as BoundIncrementOperator, arg);
case BoundKind.BinaryOperator:
return VisitBinaryOperator(node as BoundBinaryOperator, arg);
case BoundKind.CompoundAssignmentOperator:
return VisitCompoundAssignmentOperator(node as BoundCompoundAssignmentOperator, arg);
case BoundKind.AssignmentOperator:
return VisitAssignmentOperator(node as BoundAssignmentOperator, arg);
case BoundKind.NullCoalescingOperator:
return VisitNullCoalescingOperator(node as BoundNullCoalescingOperator, arg);
case BoundKind.ConditionalOperator:
return VisitConditionalOperator(node as BoundConditionalOperator, arg);
case BoundKind.ArrayAccess:
return VisitArrayAccess(node as BoundArrayAccess, arg);
case BoundKind.TypeOfOperator:
return VisitTypeOfOperator(node as BoundTypeOfOperator, arg);
case BoundKind.DefaultLiteral:
return VisitDefaultLiteral(node as BoundDefaultLiteral, arg);
case BoundKind.DefaultExpression:
return VisitDefaultExpression(node as BoundDefaultExpression, arg);
case BoundKind.IsOperator:
return VisitIsOperator(node as BoundIsOperator, arg);
case BoundKind.AsOperator:
return VisitAsOperator(node as BoundAsOperator, arg);
case BoundKind.Conversion:
return VisitConversion(node as BoundConversion, arg);
case BoundKind.SequencePointExpression:
return VisitSequencePointExpression(node as BoundSequencePointExpression, arg);
case BoundKind.SequencePoint:
return VisitSequencePoint(node as BoundSequencePoint, arg);
case BoundKind.SequencePointWithSpan:
return VisitSequencePointWithSpan(node as BoundSequencePointWithSpan, arg);
case BoundKind.Block:
return VisitBlock(node as BoundBlock, arg);
case BoundKind.LocalDeclaration:
return VisitLocalDeclaration(node as BoundLocalDeclaration, arg);
case BoundKind.MultipleLocalDeclarations:
return VisitMultipleLocalDeclarations(node as BoundMultipleLocalDeclarations, arg);
case BoundKind.Sequence:
return VisitSequence(node as BoundSequence, arg);
case BoundKind.NoOpStatement:
return VisitNoOpStatement(node as BoundNoOpStatement, arg);
case BoundKind.ReturnStatement:
return VisitReturnStatement(node as BoundReturnStatement, arg);
case BoundKind.ThrowStatement:
return VisitThrowStatement(node as BoundThrowStatement, arg);
case BoundKind.ExpressionStatement:
return VisitExpressionStatement(node as BoundExpressionStatement, arg);
case BoundKind.BreakStatement:
return VisitBreakStatement(node as BoundBreakStatement, arg);
case BoundKind.ContinueStatement:
return VisitContinueStatement(node as BoundContinueStatement, arg);
case BoundKind.IfStatement:
return VisitIfStatement(node as BoundIfStatement, arg);
case BoundKind.ForEachStatement:
return VisitForEachStatement(node as BoundForEachStatement, arg);
case BoundKind.TryStatement:
return VisitTryStatement(node as BoundTryStatement, arg);
case BoundKind.Literal:
return VisitLiteral(node as BoundLiteral, arg);
case BoundKind.ThisReference:
return VisitThisReference(node as BoundThisReference, arg);
case BoundKind.Local:
return VisitLocal(node as BoundLocal, arg);
case BoundKind.Parameter:
return VisitParameter(node as BoundParameter, arg);
case BoundKind.LabelStatement:
return VisitLabelStatement(node as BoundLabelStatement, arg);
case BoundKind.GotoStatement:
return VisitGotoStatement(node as BoundGotoStatement, arg);
case BoundKind.LabeledStatement:
return VisitLabeledStatement(node as BoundLabeledStatement, arg);
case BoundKind.StatementList:
return VisitStatementList(node as BoundStatementList, arg);
case BoundKind.ConditionalGoto:
return VisitConditionalGoto(node as BoundConditionalGoto, arg);
case BoundKind.Call:
return VisitCall(node as BoundCall, arg);
case BoundKind.ObjectCreationExpression:
return VisitObjectCreationExpression(node as BoundObjectCreationExpression, arg);
case BoundKind.DelegateCreationExpression:
return VisitDelegateCreationExpression(node as BoundDelegateCreationExpression, arg);
case BoundKind.FieldAccess:
return VisitFieldAccess(node as BoundFieldAccess, arg);
case BoundKind.PropertyAccess:
return VisitPropertyAccess(node as BoundPropertyAccess, arg);
case BoundKind.Lambda:
return VisitLambda(node as BoundLambda, arg);
case BoundKind.NameOfOperator:
return VisitNameOfOperator(node as BoundNameOfOperator, arg);
}
return VisitInternal(node, arg);
}
public virtual R DefaultVisit(BoundNode node, A arg)
{
return default(R);
}
}
internal abstract partial class BoundTreeVisitor
{
protected BoundTreeVisitor()
{
}
[DebuggerHidden]
public virtual BoundNode Visit(BoundNode node)
{
if (node != null)
{
return node.Accept(this);
}
return null;
}
[DebuggerHidden]
public virtual BoundNode DefaultVisit(BoundNode node)
{
return null;
}
public class CancelledByStackGuardException : Exception
{
public readonly BoundNode Node;
public CancelledByStackGuardException(Exception inner, BoundNode node)
: base(inner.Message, inner)
{
Node = node;
}
public void AddAnError(DiagnosticBag diagnostics)
{
diagnostics.Add(ErrorCode.ERR_InsufficientStack, GetTooLongOrComplexExpressionErrorLocation(Node));
}
public void AddAnError(BindingDiagnosticBag diagnostics)
{
diagnostics.Add(ErrorCode.ERR_InsufficientStack, GetTooLongOrComplexExpressionErrorLocation(Node));
}
public static Location GetTooLongOrComplexExpressionErrorLocation(BoundNode node)
{
SyntaxNode syntax = node.Syntax;
if (syntax is not (ExpressionSyntax or PatternSyntax))
{
syntax = syntax.DescendantNodes(n => n is not (ExpressionSyntax or PatternSyntax)).FirstOrDefault(n => n is ExpressionSyntax or PatternSyntax) ?? syntax;
}
return syntax.GetFirstToken().GetLocation();
}
}
/// <summary>
/// Consumers must provide implementation for <see cref="VisitExpressionOrPatternWithoutStackGuard"/>.
/// </summary>
[DebuggerStepThrough]
protected BoundNode VisitExpressionOrPatternWithStackGuard(ref int recursionDepth, BoundNode node)
{
Debug.Assert(node is BoundExpression or BoundPattern);
BoundNode result;
recursionDepth++;
#if DEBUG
int saveRecursionDepth = recursionDepth;
#endif
if (recursionDepth > 1 || !ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException())
{
EnsureSufficientExecutionStack(recursionDepth);
result = VisitExpressionOrPatternWithoutStackGuard(node);
}
else
{
result = VisitExpressionOrPatternWithStackGuard(node);
}
#if DEBUG
Debug.Assert(saveRecursionDepth == recursionDepth);
#endif
recursionDepth--;
return result;
}
protected virtual void EnsureSufficientExecutionStack(int recursionDepth)
{
StackGuard.EnsureSufficientExecutionStack(recursionDepth);
}
protected virtual bool ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException()
{
return true;
}
#nullable enable
[DebuggerStepThrough]
private BoundNode? VisitExpressionOrPatternWithStackGuard(BoundNode node)
{
try
{
return VisitExpressionOrPatternWithoutStackGuard(node);
}
catch (InsufficientExecutionStackException ex)
{
throw new CancelledByStackGuardException(ex, node);
}
}
/// <summary>
/// We should be intentional about behavior of derived classes regarding guarding against stack overflow.
/// </summary>
protected abstract BoundNode? VisitExpressionOrPatternWithoutStackGuard(BoundNode node);
}
}
|