File: Lowering\DiagnosticsPass_ExpressionTrees.cs
Web Access
Project: src\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.
 
#nullable disable
 
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    /// <summary>
    /// This pass detects and reports diagnostics that do not affect lambda convertibility.
    /// This part of the partial class focuses on features that cannot be used in expression trees.
    /// CAVEAT: Errors may be produced for ObsoleteAttribute, but such errors don't affect lambda convertibility.
    /// </summary>
    internal sealed partial class DiagnosticsPass
    {
        private readonly BindingDiagnosticBag _diagnostics;
        private readonly CSharpCompilation _compilation;
        private bool _inExpressionLambda;
        private bool _reportedUnsafe;
        private readonly MethodSymbol _containingSymbol;
 
        // Containing static local function, static anonymous function, or static lambda.
        private SourceMethodSymbol _staticLocalOrAnonymousFunction;
 
        public static void IssueDiagnostics(CSharpCompilation compilation, BoundNode node, BindingDiagnosticBag diagnostics, MethodSymbol containingSymbol)
        {
            Debug.Assert(node != null);
            Debug.Assert((object)containingSymbol != null);
 
            ExecutableCodeBinder.ValidateIteratorMethod(compilation, containingSymbol, diagnostics);
 
            try
            {
                var diagnosticPass = new DiagnosticsPass(compilation, diagnostics, containingSymbol);
                diagnosticPass.Visit(node);
            }
            catch (CancelledByStackGuardException ex)
            {
                ex.AddAnError(diagnostics);
            }
        }
 
        private DiagnosticsPass(CSharpCompilation compilation, BindingDiagnosticBag diagnostics, MethodSymbol containingSymbol)
        {
            Debug.Assert(diagnostics != null);
            Debug.Assert((object)containingSymbol != null);
 
            _compilation = compilation;
            _diagnostics = diagnostics;
            _containingSymbol = containingSymbol;
        }
 
        private void Error(ErrorCode code, BoundNode node, params object[] args)
        {
            _diagnostics.Add(code, node.Syntax.Location, args);
        }
 
        private void CheckUnsafeType(BoundExpression e)
        {
            if (e != null && (object)e.Type != null && e.Type.IsPointerOrFunctionPointer()) NoteUnsafe(e);
        }
 
        private void NoteUnsafe(BoundNode node)
        {
            if (_inExpressionLambda && !_reportedUnsafe)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsPointerOp, node);
                _reportedUnsafe = true;
            }
        }
 
        public override BoundNode VisitArrayCreation(BoundArrayCreation node)
        {
            var arrayType = (ArrayTypeSymbol)node.Type;
            if (_inExpressionLambda && node.InitializerOpt != null && !arrayType.IsSZArray)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsMultiDimensionalArrayInitializer, node);
            }
 
            return base.VisitArrayCreation(node);
        }
 
        public override BoundNode VisitArrayAccess(BoundArrayAccess node)
        {
            if (_inExpressionLambda &&
                node.Indices.Length == 1 &&
                !node.Indices[0].Type!.SpecialType.CanOptimizeBehavior())
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsPatternImplicitIndexer, node);
            }
 
            return base.VisitArrayAccess(node);
        }
 
        public override BoundNode VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsPatternImplicitIndexer, node);
            }
 
            return base.VisitImplicitIndexerAccess(node);
        }
 
        public override BoundNode VisitInlineArrayAccess(BoundInlineArrayAccess node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsInlineArrayOperation, node);
            }
 
            return base.VisitInlineArrayAccess(node);
        }
 
        public override BoundNode VisitFromEndIndexExpression(BoundFromEndIndexExpression node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsFromEndIndexExpression, node);
            }
 
            return base.VisitFromEndIndexExpression(node);
        }
 
        public override BoundNode VisitRangeExpression(BoundRangeExpression node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsRangeExpression, node);
            }
 
            return base.VisitRangeExpression(node);
        }
 
        public override BoundNode VisitSizeOfOperator(BoundSizeOfOperator node)
        {
            if (_inExpressionLambda && node.ConstantValueOpt == null)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsPointerOp, node);
            }
 
            return base.VisitSizeOfOperator(node);
        }
 
        public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node)
        {
            ExecutableCodeBinder.ValidateIteratorMethod(_compilation, node.Symbol, _diagnostics);
 
            var outerLocalFunction = _staticLocalOrAnonymousFunction;
            if (node.Symbol.IsStatic)
            {
                _staticLocalOrAnonymousFunction = node.Symbol;
            }
            var result = base.VisitLocalFunctionStatement(node);
            _staticLocalOrAnonymousFunction = outerLocalFunction;
            return result;
        }
 
        public override BoundNode VisitThisReference(BoundThisReference node)
        {
            CheckReferenceToThisOrBase(node);
            return base.VisitThisReference(node);
        }
 
        public override BoundNode VisitBaseReference(BoundBaseReference node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsBaseAccess, node);
            }
            CheckReferenceToThisOrBase(node);
            return base.VisitBaseReference(node);
        }
 
        public override BoundNode VisitLocal(BoundLocal node)
        {
            CheckReferenceToVariable(node, node.LocalSymbol);
            return base.VisitLocal(node);
        }
 
        public override BoundNode VisitParameter(BoundParameter node)
        {
            CheckReferenceToVariable(node, node.ParameterSymbol);
            return base.VisitParameter(node);
        }
 
        private void CheckReferenceToThisOrBase(BoundExpression node)
        {
            if (_staticLocalOrAnonymousFunction is object)
            {
                var diagnostic = _staticLocalOrAnonymousFunction.MethodKind == MethodKind.LocalFunction
                    ? ErrorCode.ERR_StaticLocalFunctionCannotCaptureThis
                    : ErrorCode.ERR_StaticAnonymousFunctionCannotCaptureThis;
 
                Error(diagnostic, node);
            }
        }
 
#nullable enable
        private void CheckReferenceToVariable(BoundExpression node, Symbol symbol)
        {
            Debug.Assert(symbol.Kind == SymbolKind.Local || symbol.Kind == SymbolKind.Parameter || symbol is LocalFunctionSymbol);
 
            if (_staticLocalOrAnonymousFunction is object && Symbol.IsCaptured(symbol, _staticLocalOrAnonymousFunction))
            {
                var diagnostic = _staticLocalOrAnonymousFunction.MethodKind == MethodKind.LocalFunction
                    ? ErrorCode.ERR_StaticLocalFunctionCannotCaptureVariable
                    : ErrorCode.ERR_StaticAnonymousFunctionCannotCaptureVariable;
 
                Error(diagnostic, node, new FormattedSymbol(symbol, SymbolDisplayFormat.ShortFormat));
            }
        }
#nullable disable
 
        private void CheckReferenceToMethodIfLocalFunction(BoundExpression node, MethodSymbol method)
        {
            if (method?.OriginalDefinition is LocalFunctionSymbol localFunction)
            {
                CheckReferenceToVariable(node, localFunction);
            }
        }
 
        public override BoundNode VisitConvertedSwitchExpression(BoundConvertedSwitchExpression node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsSwitchExpression, node);
            }
 
            return base.VisitConvertedSwitchExpression(node);
        }
 
        public override BoundNode VisitDeconstructionAssignmentOperator(BoundDeconstructionAssignmentOperator node)
        {
            if (!node.HasAnyErrors)
            {
                CheckForDeconstructionAssignmentToSelf((BoundTupleExpression)node.Left, node.Right);
            }
 
            return base.VisitDeconstructionAssignmentOperator(node);
        }
 
        public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
        {
            CheckForAssignmentToSelf(node);
 
            if (_inExpressionLambda && node.Left.Kind != BoundKind.ObjectInitializerMember && node.Left.Kind != BoundKind.DynamicObjectInitializerMember)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsAssignment, node);
            }
 
            return base.VisitAssignmentOperator(node);
        }
 
        public override BoundNode VisitDynamicObjectInitializerMember(BoundDynamicObjectInitializerMember node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsDynamicOperation, node);
            }
 
            return base.VisitDynamicObjectInitializerMember(node);
        }
 
        public override BoundNode VisitEventAccess(BoundEventAccess node)
        {
            // Don't bother reporting an obsolete diagnostic if the access is already wrong for other reasons
            // (specifically, we can't use it as a field here).
            if (node.IsUsableAsField)
            {
                bool hasBaseReceiver = node.ReceiverOpt != null && node.ReceiverOpt.Kind == BoundKind.BaseReference;
                Binder.ReportDiagnosticsIfObsolete(_diagnostics, node.EventSymbol.AssociatedField, node.Syntax, hasBaseReceiver, _containingSymbol, _containingSymbol.ContainingType, BinderFlags.None);
            }
            CheckReceiverIfField(node.ReceiverOpt);
            return base.VisitEventAccess(node);
        }
 
        public override BoundNode VisitEventAssignmentOperator(BoundEventAssignmentOperator node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsAssignment, node);
            }
 
            bool hasBaseReceiver = node.ReceiverOpt != null && node.ReceiverOpt.Kind == BoundKind.BaseReference;
            Binder.ReportDiagnosticsIfObsolete(_diagnostics, node.Event, ((AssignmentExpressionSyntax)node.Syntax).Left, hasBaseReceiver, _containingSymbol, _containingSymbol.ContainingType, BinderFlags.None);
            CheckReceiverIfField(node.ReceiverOpt);
            return base.VisitEventAssignmentOperator(node);
        }
 
        public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            CheckCompoundAssignmentOperator(node);
 
            return base.VisitCompoundAssignmentOperator(node);
        }
 
        private void VisitCall(
            MethodSymbol method,
            PropertySymbol propertyAccess,
            ImmutableArray<BoundExpression> arguments,
            ImmutableArray<RefKind> argumentRefKindsOpt,
            ImmutableArray<string> argumentNamesOpt,
            BitVector defaultArguments,
            BoundNode node)
        {
            Debug.Assert((object)method != null);
            Debug.Assert(((object)propertyAccess == null) ||
                (method == propertyAccess.GetOwnOrInheritedGetMethod()) ||
                (method == propertyAccess.GetOwnOrInheritedSetMethod()) ||
                propertyAccess.MustCallMethodsDirectly);
 
            CheckArguments(argumentRefKindsOpt, arguments, method);
 
            if (_inExpressionLambda)
            {
                if (method.CallsAreOmitted(node.SyntaxTree))
                {
                    Error(ErrorCode.ERR_PartialMethodInExpressionTree, node);
                }
                else if ((object)propertyAccess != null && propertyAccess.IsIndexedProperty() && !propertyAccess.IsIndexer)
                {
                    Error(ErrorCode.ERR_ExpressionTreeContainsIndexedProperty, node);
                }
                else if (hasDefaultArgument(arguments, defaultArguments))
                {
                    Error(ErrorCode.ERR_ExpressionTreeContainsOptionalArgument, node);
                }
                else if (!argumentNamesOpt.IsDefaultOrEmpty)
                {
                    Error(ErrorCode.ERR_ExpressionTreeContainsNamedArgument, node);
                }
                else if (IsComCallWithRefOmitted(method, arguments, argumentRefKindsOpt))
                {
                    Error(ErrorCode.ERR_ComRefCallInExpressionTree, node);
                }
                else if (method.MethodKind == MethodKind.LocalFunction)
                {
                    Error(ErrorCode.ERR_ExpressionTreeContainsLocalFunction, node);
                }
                else if (method.RefKind != RefKind.None)
                {
                    Error(ErrorCode.ERR_RefReturningCallInExpressionTree, node);
                }
                else if ((method.IsAbstract || method.IsVirtual) && method.IsStatic)
                {
                    Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node);
                }
            }
 
            static bool hasDefaultArgument(ImmutableArray<BoundExpression> arguments, BitVector defaultArguments)
            {
                for (int i = 0; i < arguments.Length; i++)
                {
                    if (defaultArguments[i])
                    {
                        return true;
                    }
                }
 
                return false;
            }
        }
 
        public override BoundNode Visit(BoundNode node)
        {
            if (_inExpressionLambda &&
                // Ignoring BoundConversion nodes prevents redundant diagnostics
                !(node is BoundConversion) &&
                node is BoundExpression expr &&
                expr.Type is TypeSymbol type &&
                type.IsRestrictedType())
            {
                Error(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, node, type.Name);
            }
            return base.Visit(node);
        }
 
        public override BoundNode VisitRefTypeOperator(BoundRefTypeOperator node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_FeatureNotValidInExpressionTree, node, "__reftype");
            }
 
            return base.VisitRefTypeOperator(node);
        }
 
        public override BoundNode VisitRefValueOperator(BoundRefValueOperator node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_FeatureNotValidInExpressionTree, node, "__refvalue");
            }
 
            return base.VisitRefValueOperator(node);
        }
 
        public override BoundNode VisitMakeRefOperator(BoundMakeRefOperator node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_FeatureNotValidInExpressionTree, node, "__makeref");
            }
 
            return base.VisitMakeRefOperator(node);
        }
 
        public override BoundNode VisitArgListOperator(BoundArgListOperator node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_VarArgsInExpressionTree, node);
            }
 
            return base.VisitArgListOperator(node);
        }
 
        public override BoundNode VisitConditionalAccess(BoundConditionalAccess node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_NullPropagatingOpInExpressionTree, node);
            }
 
            return base.VisitConditionalAccess(node);
        }
 
        public override BoundNode VisitObjectInitializerMember(BoundObjectInitializerMember node)
        {
            if (_inExpressionLambda && !node.Arguments.IsDefaultOrEmpty)
            {
                Error(ErrorCode.ERR_DictionaryInitializerInExpressionTree, node);
            }
 
            if (node.MemberSymbol is PropertySymbol property)
            {
                CheckRefReturningPropertyAccess(node, property);
            }
 
            return base.VisitObjectInitializerMember(node);
        }
 
        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)
                {
                    calls.Push(node);
                    node = receiver2;
                }
 
                CheckReceiverIfField(node.ReceiverOpt);
                this.Visit(node.ReceiverOpt);
 
                do
                {
                    VisitCall(node.Method, null, node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt, node.DefaultArguments, node);
                    CheckReferenceToMethodIfLocalFunction(node, node.Method);
                    this.VisitList(node.Arguments);
                }
                while (calls.TryPop(out node));
 
                calls.Free();
            }
            else
            {
                VisitCall(node.Method, null, node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt, node.DefaultArguments, node);
                CheckReceiverIfField(node.ReceiverOpt);
                CheckReferenceToMethodIfLocalFunction(node, node.Method);
                this.Visit(node.ReceiverOpt);
                this.VisitList(node.Arguments);
            }
 
            return null;
        }
 
        /// <summary>
        /// Called when a local represents an out variable declaration. Its syntax is of type DeclarationExpressionSyntax.
        /// </summary>
        private void CheckOutDeclaration(BoundLocal local)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsOutVariable, local);
            }
        }
 
        private void CheckDiscard(BoundDiscardExpression argument)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsDiscard, argument);
            }
        }
 
        public override BoundNode VisitCollectionElementInitializer(BoundCollectionElementInitializer node)
        {
            if (_inExpressionLambda && node.AddMethod.IsStatic)
            {
                Error(ErrorCode.ERR_ExtensionCollectionElementInitializerInExpressionTree, node);
            }
 
            VisitCall(node.AddMethod, null, node.Arguments, default(ImmutableArray<RefKind>), default(ImmutableArray<string>), node.DefaultArguments, node);
            return base.VisitCollectionElementInitializer(node);
        }
 
        public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node)
        {
            VisitCall(node.Constructor, null, node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt, node.DefaultArguments, node);
            return base.VisitObjectCreationExpression(node);
        }
 
        public override BoundNode VisitIndexerAccess(BoundIndexerAccess node)
        {
            var indexer = node.Indexer;
            var method = indexer.GetOwnOrInheritedGetMethod() ?? indexer.GetOwnOrInheritedSetMethod();
            if ((object)method != null)
            {
                VisitCall(method, indexer, node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt, node.DefaultArguments, node);
            }
            CheckReceiverIfField(node.ReceiverOpt);
            return base.VisitIndexerAccess(node);
        }
 
        private void CheckRefReturningPropertyAccess(BoundNode node, PropertySymbol property)
        {
            if (_inExpressionLambda && property.RefKind != RefKind.None)
            {
                Error(ErrorCode.ERR_RefReturningCallInExpressionTree, node);
            }
        }
 
        public override BoundNode VisitPropertyAccess(BoundPropertyAccess node)
        {
            var property = node.PropertySymbol;
            CheckRefReturningPropertyAccess(node, property);
            CheckReceiverIfField(node.ReceiverOpt);
 
            if (_inExpressionLambda && (property.IsAbstract || property.IsVirtual) && property.IsStatic)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node);
            }
 
            return base.VisitPropertyAccess(node);
        }
 
        public override BoundNode VisitLambda(BoundLambda node)
        {
            if (_inExpressionLambda)
            {
                var lambda = node.Symbol;
                bool reportedAttributes = false;
 
                if (!lambda.GetAttributes().IsEmpty || !lambda.GetReturnTypeAttributes().IsEmpty)
                {
                    Error(ErrorCode.ERR_LambdaWithAttributesToExpressionTree, node);
                    reportedAttributes = true;
                }
 
                foreach (var p in lambda.Parameters)
                {
                    if (p.RefKind != RefKind.None && p.TryGetFirstLocation() is Location location)
                    {
                        _diagnostics.Add(ErrorCode.ERR_ByRefParameterInExpressionTree, location);
                    }
                    if (p.TypeWithAnnotations.IsRestrictedType())
                    {
                        _diagnostics.Add(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, p.GetFirstLocation(), p.Type.Name);
                    }
 
                    if (!reportedAttributes && !p.GetAttributes().IsEmpty)
                    {
                        _diagnostics.Add(ErrorCode.ERR_LambdaWithAttributesToExpressionTree, p.GetFirstLocation());
                        reportedAttributes = true;
                    }
                }
 
                switch (node.Syntax.Kind())
                {
                    case SyntaxKind.ParenthesizedLambdaExpression:
                        {
                            var lambdaSyntax = (ParenthesizedLambdaExpressionSyntax)node.Syntax;
                            if (lambdaSyntax.AsyncKeyword.Kind() == SyntaxKind.AsyncKeyword)
                            {
                                Error(ErrorCode.ERR_BadAsyncExpressionTree, node);
                            }
                            else if (lambdaSyntax.Body.Kind() == SyntaxKind.Block)
                            {
                                Error(ErrorCode.ERR_StatementLambdaToExpressionTree, node);
                            }
                            else if (lambdaSyntax.Body.Kind() == SyntaxKind.RefExpression)
                            {
                                Error(ErrorCode.ERR_BadRefReturnExpressionTree, node);
                            }
                        }
                        break;
 
                    case SyntaxKind.SimpleLambdaExpression:
                        {
                            var lambdaSyntax = (SimpleLambdaExpressionSyntax)node.Syntax;
                            if (lambdaSyntax.AsyncKeyword.Kind() == SyntaxKind.AsyncKeyword)
                            {
                                Error(ErrorCode.ERR_BadAsyncExpressionTree, node);
                            }
                            else if (lambdaSyntax.Body.Kind() == SyntaxKind.Block)
                            {
                                Error(ErrorCode.ERR_StatementLambdaToExpressionTree, node);
                            }
                            else if (lambdaSyntax.Body.Kind() == SyntaxKind.RefExpression)
                            {
                                Error(ErrorCode.ERR_BadRefReturnExpressionTree, node);
                            }
                        }
                        break;
 
                    case SyntaxKind.AnonymousMethodExpression:
                        Error(ErrorCode.ERR_ExpressionTreeContainsAnonymousMethod, node);
                        break;
 
                    default:
                        // other syntax forms arise from query expressions, and always result from implied expression-lambda-like forms
                        break;
                }
            }
 
            var outerLocalFunction = _staticLocalOrAnonymousFunction;
            if (node.Symbol.IsStatic)
            {
                _staticLocalOrAnonymousFunction = node.Symbol;
            }
            var result = base.VisitLambda(node);
            _staticLocalOrAnonymousFunction = outerLocalFunction;
            return result;
        }
 
        public override BoundNode VisitBinaryOperator(BoundBinaryOperator node)
        {
            // It is very common for bound trees to be left-heavy binary operators, eg,
            // a + b + c + d + ...
            // To avoid blowing the stack, do not recurse down the left hand side.
 
            // In order to avoid blowing the stack, we end up visiting right children
            // before left children; this should not be a problem in the diagnostics 
            // pass.
 
            BoundBinaryOperator current = node;
            while (true)
            {
                CheckBinaryOperator(current);
 
                Visit(current.Right);
                if (current.Left.Kind == BoundKind.BinaryOperator)
                {
                    current = (BoundBinaryOperator)current.Left;
                }
                else
                {
                    Visit(current.Left);
                    break;
                }
            }
 
            return null;
        }
 
        public override BoundNode VisitBinaryPattern(BoundBinaryPattern node)
        {
            // Do not use left recursion because we can have many nested binary patterns.
 
            BoundBinaryPattern current = node;
            while (true)
            {
                Visit(current.Right);
                if (current.Left is BoundBinaryPattern left)
                {
                    current = left;
                }
                else
                {
                    Visit(current.Left);
                    break;
                }
            }
 
            return null;
        }
 
        public override BoundNode VisitUserDefinedConditionalLogicalOperator(BoundUserDefinedConditionalLogicalOperator node)
        {
            CheckLiftedUserDefinedConditionalLogicalOperator(node);
 
            if (_inExpressionLambda)
            {
                var binary = node.LogicalOperator;
                var unary = node.OperatorKind.Operator() == BinaryOperatorKind.And ? node.FalseOperator : node.TrueOperator;
 
                if (((binary.IsAbstract || binary.IsVirtual) && binary.IsStatic) || ((unary.IsAbstract || unary.IsVirtual) && unary.IsStatic))
                {
                    Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node);
                }
            }
 
            return base.VisitUserDefinedConditionalLogicalOperator(node);
        }
 
        private void CheckDynamic(BoundUnaryOperator node)
        {
            if (_inExpressionLambda && node.OperatorKind.IsDynamic())
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsDynamicOperation, node);
            }
        }
 
        private void CheckDynamic(BoundBinaryOperator node)
        {
            if (_inExpressionLambda && node.OperatorKind.IsDynamic())
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsDynamicOperation, node);
            }
        }
 
        public override BoundNode VisitUnaryOperator(BoundUnaryOperator node)
        {
            CheckUnsafeType(node);
            CheckLiftedUnaryOp(node);
            CheckDynamic(node);
 
            if (_inExpressionLambda && node.MethodOpt is MethodSymbol method && (method.IsAbstract || method.IsVirtual) && method.IsStatic)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node);
            }
 
            return base.VisitUnaryOperator(node);
        }
 
        public override BoundNode VisitAddressOfOperator(BoundAddressOfOperator node)
        {
            CheckUnsafeType(node);
            BoundExpression operand = node.Operand;
            if (operand.Kind == BoundKind.FieldAccess)
            {
                CheckFieldAddress((BoundFieldAccess)operand, consumerOpt: null);
            }
            return base.VisitAddressOfOperator(node);
        }
 
        public override BoundNode VisitIncrementOperator(BoundIncrementOperator node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsAssignment, node);
            }
 
            return base.VisitIncrementOperator(node);
        }
 
        public override BoundNode VisitPointerElementAccess(BoundPointerElementAccess node)
        {
            NoteUnsafe(node);
            return base.VisitPointerElementAccess(node);
        }
 
        public override BoundNode VisitPointerIndirectionOperator(BoundPointerIndirectionOperator node)
        {
            NoteUnsafe(node);
            return base.VisitPointerIndirectionOperator(node);
        }
 
        public override BoundNode VisitConversion(BoundConversion node)
        {
            CheckUnsafeType(node.Operand);
            CheckUnsafeType(node);
            bool wasInExpressionLambda = _inExpressionLambda;
            bool oldReportedUnsafe = _reportedUnsafe;
            switch (node.ConversionKind)
            {
                case ConversionKind.MethodGroup:
                    CheckMethodGroup((BoundMethodGroup)node.Operand, node.Conversion.Method, node.IsExtensionMethod, parentIsConversion: true, node.Type);
 
                    return node;
 
                case ConversionKind.AnonymousFunction:
                    if (!wasInExpressionLambda && node.Type.IsExpressionTree())
                    {
                        _inExpressionLambda = true;
                        // we report "unsafe in expression tree" at most once for each expression tree
                        _reportedUnsafe = false;
                    }
                    break;
 
                case ConversionKind.ImplicitDynamic:
                case ConversionKind.ExplicitDynamic:
                    if (_inExpressionLambda)
                    {
                        Error(ErrorCode.ERR_ExpressionTreeContainsDynamicOperation, node);
                    }
                    break;
 
                case ConversionKind.ExplicitTuple:
                case ConversionKind.ExplicitTupleLiteral:
                case ConversionKind.ImplicitTuple:
                case ConversionKind.ImplicitTupleLiteral:
                    if (_inExpressionLambda)
                    {
                        Error(ErrorCode.ERR_ExpressionTreeContainsTupleConversion, node);
                    }
                    break;
 
                case ConversionKind.InlineArray:
                    if (_inExpressionLambda)
                    {
                        Error(ErrorCode.ERR_ExpressionTreeContainsInlineArrayOperation, node);
                    }
                    break;
 
                case ConversionKind.InterpolatedStringHandler:
                    if (_inExpressionLambda)
                    {
                        Error(ErrorCode.ERR_ExpressionTreeContainsInterpolatedStringHandlerConversion, node);
                    }
                    break;
 
                default:
 
                    if (_inExpressionLambda && node.Conversion.Method is MethodSymbol method && (method.IsAbstract || method.IsVirtual) && method.IsStatic)
                    {
                        Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node);
                    }
                    break;
            }
 
            var result = base.VisitConversion(node);
            _inExpressionLambda = wasInExpressionLambda;
            _reportedUnsafe = oldReportedUnsafe;
            return result;
        }
 
        public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationExpression node)
        {
            if (node.Argument.Kind != BoundKind.MethodGroup)
            {
                this.Visit(node.Argument);
            }
            else
            {
                CheckMethodGroup((BoundMethodGroup)node.Argument, node.MethodOpt, node.IsExtensionMethod, parentIsConversion: true, convertedToType: node.Type);
            }
 
            return null;
        }
 
        public override BoundNode VisitMethodGroup(BoundMethodGroup node)
        {
            CheckMethodGroup(node, method: null, isExtensionMethod: false, parentIsConversion: false, convertedToType: null);
            return null;
        }
 
        private void CheckMethodGroup(BoundMethodGroup node, MethodSymbol method, bool isExtensionMethod, bool parentIsConversion, TypeSymbol convertedToType)
        {
            // Formerly reported ERR_MemGroupInExpressionTree when this occurred, but the expanded 
            // ERR_LambdaInIsAs makes this impossible (since the node will always be wrapped in
            // a failed conversion).
            Debug.Assert(!(!parentIsConversion && _inExpressionLambda));
 
            if (_inExpressionLambda)
            {
                if ((node.LookupSymbolOpt as MethodSymbol)?.MethodKind == MethodKind.LocalFunction)
                {
                    Error(ErrorCode.ERR_ExpressionTreeContainsLocalFunction, node);
                }
                else if (parentIsConversion && convertedToType.IsFunctionPointer())
                {
                    Error(ErrorCode.ERR_AddressOfMethodGroupInExpressionTree, node);
                }
                else if (method is not null && (method.IsAbstract || method.IsVirtual) && method.IsStatic)
                {
                    Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node);
                }
            }
 
            CheckReceiverIfField(node.ReceiverOpt);
            CheckReferenceToMethodIfLocalFunction(node, method);
 
            if (method is null || method.RequiresInstanceReceiver || isExtensionMethod)
            {
                Visit(node.ReceiverOpt);
            }
        }
 
        public override BoundNode VisitNameOfOperator(BoundNameOfOperator node)
        {
            // The nameof(...) operator collapses to a constant in an expression tree,
            // so it does not matter what is recursively within it.
            return node;
        }
 
        public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperator node)
        {
            if (_inExpressionLambda && (node.LeftOperand.IsLiteralNull() || node.LeftOperand.IsLiteralDefault()))
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsBadCoalesce, node.LeftOperand);
            }
 
            return base.VisitNullCoalescingOperator(node);
        }
 
        public override BoundNode VisitNullCoalescingAssignmentOperator(BoundNullCoalescingAssignmentOperator node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeCantContainNullCoalescingAssignment, node);
            }
 
            return base.VisitNullCoalescingAssignmentOperator(node);
        }
 
        public override BoundNode VisitDynamicInvocation(BoundDynamicInvocation node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsDynamicOperation, node);
 
                // avoid reporting errors for the method group:
                if (node.Expression.Kind == BoundKind.MethodGroup)
                {
                    return base.VisitMethodGroup((BoundMethodGroup)node.Expression);
                }
            }
 
            return base.VisitDynamicInvocation(node);
        }
 
        public override BoundNode VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsDynamicOperation, node);
            }
 
            CheckReceiverIfField(node.Receiver);
            return base.VisitDynamicIndexerAccess(node);
        }
 
        public override BoundNode VisitDynamicMemberAccess(BoundDynamicMemberAccess node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsDynamicOperation, node);
            }
 
            return base.VisitDynamicMemberAccess(node);
        }
 
        public override BoundNode VisitDynamicCollectionElementInitializer(BoundDynamicCollectionElementInitializer node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsDynamicOperation, node);
            }
 
            return base.VisitDynamicCollectionElementInitializer(node);
        }
 
        public override BoundNode VisitDynamicObjectCreationExpression(BoundDynamicObjectCreationExpression node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsDynamicOperation, node);
            }
 
            return base.VisitDynamicObjectCreationExpression(node);
        }
 
        public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsIsMatch, node);
            }
 
            return base.VisitIsPatternExpression(node);
        }
 
        public override BoundNode VisitConvertedTupleLiteral(BoundConvertedTupleLiteral node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsTupleLiteral, node);
            }
 
            return base.VisitConvertedTupleLiteral(node);
        }
 
        public override BoundNode VisitTupleLiteral(BoundTupleLiteral node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsTupleLiteral, node);
            }
 
            return base.VisitTupleLiteral(node);
        }
 
        public override BoundNode VisitTupleBinaryOperator(BoundTupleBinaryOperator node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsTupleBinOp, node);
            }
 
            return base.VisitTupleBinaryOperator(node);
        }
 
        public override BoundNode VisitThrowExpression(BoundThrowExpression node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsThrowExpression, node);
            }
 
            return base.VisitThrowExpression(node);
        }
 
        public override BoundNode VisitWithExpression(BoundWithExpression node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsWithExpression, node);
            }
 
            return base.VisitWithExpression(node);
        }
 
        public override BoundNode VisitFunctionPointerInvocation(BoundFunctionPointerInvocation node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsPointerOp, node);
            }
 
            return base.VisitFunctionPointerInvocation(node);
        }
 
        public override BoundNode VisitCollectionExpression(BoundCollectionExpression node)
        {
            if (_inExpressionLambda)
            {
                Error(
                    node.IsParamsArrayOrCollection ?
                        ErrorCode.ERR_ParamsCollectionExpressionTree :
                        ErrorCode.ERR_ExpressionTreeContainsCollectionExpression,
                    node);
            }
 
            return base.VisitCollectionExpression(node);
        }
 
        public override BoundNode VisitIfStatement(BoundIfStatement node)
        {
            while (true)
            {
                this.Visit(node.Condition);
                this.Visit(node.Consequence);
 
                var alternative = node.AlternativeOpt;
                if (alternative is null)
                {
                    break;
                }
 
                if (alternative is BoundIfStatement elseIfStatement)
                {
                    node = elseIfStatement;
                }
                else
                {
                    this.Visit(alternative);
                    break;
                }
            }
 
            return null;
        }
 
        public override BoundNode VisitInterpolatedString(BoundInterpolatedString node)
        {
            Visit(node.InterpolationData?.Construction);
            return base.VisitInterpolatedString(node);
        }
    }
}