File: Compilation\MemberSemanticModel.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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    /// <summary>
    /// Binding info for expressions and statements that are part of a member declaration.
    /// Instances of this class should not be exposed to external consumers.
    /// </summary>
    internal abstract partial class MemberSemanticModel : CSharpSemanticModel
    {
        private readonly Symbol _memberSymbol;
        private readonly CSharpSyntaxNode _root;
        private readonly ReaderWriterLockSlim _nodeMapLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
        // The bound nodes associated with a syntax node, from highest in the tree to lowest.
        private readonly Dictionary<SyntaxNode, OneOrMany<BoundNode>> _guardedBoundNodeMap = new Dictionary<SyntaxNode, OneOrMany<BoundNode>>();
        private readonly Dictionary<SyntaxNode, IOperation> _guardedIOperationNodeMap = new Dictionary<SyntaxNode, IOperation>();
        private Dictionary<SyntaxNode, BoundStatement> _lazyGuardedSynthesizedStatementsMap;
        private NullableWalker.SnapshotManager _lazySnapshotManager;
        private ImmutableDictionary<Symbol, Symbol> _lazyRemappedSymbols;
        private readonly ImmutableDictionary<Symbol, Symbol> _parentRemappedSymbolsOpt;
 
        internal readonly Binder RootBinder;
 
        private readonly PublicSemanticModel _containingPublicSemanticModel;
 
        private readonly Lazy<CSharpOperationFactory> _operationFactory;
 
        protected MemberSemanticModel(
            CSharpSyntaxNode root,
            Symbol memberSymbol,
            Binder rootBinder,
            PublicSemanticModel containingPublicSemanticModel,
            ImmutableDictionary<Symbol, Symbol> parentRemappedSymbolsOpt)
        {
            Debug.Assert(root != null);
            Debug.Assert((object)memberSymbol != null);
            Debug.Assert(containingPublicSemanticModel.IsSpeculativeSemanticModel == (containingPublicSemanticModel is SpeculativeSemanticModelWithMemberModel));
 
            _root = root;
            _memberSymbol = memberSymbol;
            _containingPublicSemanticModel = containingPublicSemanticModel;
            _parentRemappedSymbolsOpt = parentRemappedSymbolsOpt;
 
            this.RootBinder = rootBinder.WithAdditionalFlags(GetSemanticModelBinderFlags());
            _operationFactory = new Lazy<CSharpOperationFactory>(() => new CSharpOperationFactory(this));
        }
 
        public override CSharpCompilation Compilation
        {
            get
            {
                return _containingPublicSemanticModel.Compilation;
            }
        }
 
        internal override CSharpSyntaxNode Root
        {
            get
            {
                return _root;
            }
        }
 
        /// <summary>
        /// The member symbol 
        /// </summary>
        internal Symbol MemberSymbol
        {
            get
            {
                return _memberSymbol;
            }
        }
 
        public sealed override bool IsSpeculativeSemanticModel
        {
            get
            {
                return _containingPublicSemanticModel.IsSpeculativeSemanticModel;
            }
        }
 
        public sealed override bool IgnoresAccessibility
        {
            get
            {
                return _containingPublicSemanticModel.IgnoresAccessibility;
            }
        }
 
        [Experimental(RoslynExperiments.NullableDisabledSemanticModel, UrlFormat = RoslynExperiments.NullableDisabledSemanticModel_Url)]
        public sealed override bool NullableAnalysisIsDisabled
        {
            get
            {
                return _containingPublicSemanticModel.NullableAnalysisIsDisabled;
            }
        }
 
        public sealed override int OriginalPositionForSpeculation
        {
            get
            {
                // This property is not meaningful for member semantic models.
                // An external consumer should never be able to access them directly.
                throw ExceptionUtilities.Unreachable();
            }
        }
 
        public sealed override CSharpSemanticModel ParentModel
        {
            get
            {
                // This property is not meaningful for member semantic models.
                // An external consumer should never be able to access them directly.
                throw ExceptionUtilities.Unreachable();
            }
        }
 
        internal sealed override SemanticModel ContainingPublicModelOrSelf
        {
            get
            {
                return _containingPublicSemanticModel;
            }
        }
 
        internal override MemberSemanticModel GetMemberModel(SyntaxNode node)
        {
            // We do have to override this method, but should never call it because it might not do the right thing. 
            Debug.Assert(false);
            return IsInTree(node) ? this : null;
        }
 
        /// <remarks>
        /// This will cause the bound node cache to be populated if nullable semantic analysis is enabled.
        /// </remarks>
        protected virtual NullableWalker.SnapshotManager GetSnapshotManager()
        {
            EnsureNullabilityAnalysisPerformedIfNecessary();
            Debug.Assert(_lazySnapshotManager is object || this is AttributeSemanticModel || !IsNullableAnalysisEnabled());
            return _lazySnapshotManager;
        }
 
        internal ImmutableDictionary<Symbol, Symbol> GetRemappedSymbols()
        {
            EnsureNullabilityAnalysisPerformedIfNecessary();
            Debug.Assert(_lazyRemappedSymbols is object || this is AttributeSemanticModel || !IsNullableAnalysisEnabled());
            return _lazyRemappedSymbols;
        }
 
        internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, TypeSyntax type, SpeculativeBindingOption bindingOption, out PublicSemanticModel speculativeModel)
        {
            var expression = SyntaxFactory.GetStandaloneExpression(type);
 
            var binder = this.GetSpeculativeBinder(position, expression, bindingOption);
            if (binder != null)
            {
                speculativeModel = new SpeculativeSemanticModelWithMemberModel(parentModel, position, _memberSymbol, type, binder, GetRemappedSymbols(), GetSnapshotManager());
                return true;
            }
 
            speculativeModel = null;
            return false;
        }
 
        internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, CrefSyntax crefSyntax, out PublicSemanticModel speculativeModel)
        {
            // crefs can never legally appear within members.
            speculativeModel = null;
            return false;
        }
 
        internal override BoundExpression GetSpeculativelyBoundExpression(int position, ExpressionSyntax expression, SpeculativeBindingOption bindingOption, out Binder binder, out ImmutableArray<Symbol> crefSymbols)
        {
            if (expression == null)
            {
                throw new ArgumentNullException(nameof(expression));
            }
 
            if (bindingOption == SpeculativeBindingOption.BindAsExpression && GetSnapshotManager() is { } snapshotManager)
            {
                crefSymbols = default;
                position = CheckAndAdjustPosition(position);
                expression = SyntaxFactory.GetStandaloneExpression(expression);
                binder = GetSpeculativeBinder(position, expression, bindingOption);
                var boundRoot = binder.BindExpression(expression, BindingDiagnosticBag.Discarded);
                ImmutableDictionary<Symbol, Symbol> ignored = null;
                return (BoundExpression)NullableWalker.AnalyzeAndRewriteSpeculation(position, boundRoot, binder, snapshotManager, newSnapshots: out _, remappedSymbols: ref ignored);
            }
            else
            {
                return GetSpeculativelyBoundExpressionWithoutNullability(position, expression, bindingOption, out binder, out crefSymbols);
            }
        }
 
        private Binder GetEnclosingBinderInternalWithinRoot(SyntaxNode node, int position)
        {
            AssertPositionAdjusted(position);
            return GetEnclosingBinderInternalWithinRoot(node, position, RootBinder, _root).WithAdditionalFlags(GetSemanticModelBinderFlags());
        }
 
        private static Binder GetEnclosingBinderInternalWithinRoot(SyntaxNode node, int position, Binder rootBinder, SyntaxNode root)
        {
            if (node == root)
            {
                return rootBinder.GetBinder(node) ?? rootBinder;
            }
 
            Debug.Assert(root.Contains(node));
 
            ExpressionSyntax typeOfArgument = null;
            LocalFunctionStatementSyntax ownerOfTypeParametersInScope = null;
 
            Binder binder = null;
 
            for (var current = node; binder == null; current = current.ParentOrStructuredTriviaParent)
            {
                Debug.Assert(current != null); // Why were we asked for an enclosing binder for a node outside our root?
                StatementSyntax stmt = current as StatementSyntax;
                TypeOfExpressionSyntax typeOfExpression;
                SyntaxKind kind = current.Kind();
 
                if (stmt != null)
                {
                    if (LookupPosition.IsInStatementScope(position, stmt))
                    {
                        binder = rootBinder.GetBinder(current);
 
                        if (binder != null)
                        {
                            binder = AdjustBinderForPositionWithinStatement(position, binder, stmt);
                        }
                        else if (kind == SyntaxKind.LocalFunctionStatement)
                        {
                            Debug.Assert(ownerOfTypeParametersInScope == null);
                            var localFunction = (LocalFunctionStatementSyntax)stmt;
                            if (LookupPosition.IsInLocalFunctionTypeParameterScope(position, localFunction))
                            {
                                ownerOfTypeParametersInScope = localFunction;
                            }
                        }
                    }
                }
                else if (kind == SyntaxKind.CatchClause)
                {
                    if (LookupPosition.IsInCatchBlockScope(position, (CatchClauseSyntax)current))
                    {
                        binder = rootBinder.GetBinder(current);
                    }
                }
                else if (kind == SyntaxKind.CatchFilterClause)
                {
                    if (LookupPosition.IsInCatchFilterScope(position, (CatchFilterClauseSyntax)current))
                    {
                        binder = rootBinder.GetBinder(current);
                    }
                }
                else if (current.IsAnonymousFunction())
                {
                    if (LookupPosition.IsInAnonymousFunctionOrQuery(position, current))
                    {
                        binder = rootBinder.GetBinder(current.AnonymousFunctionBody());
                        Debug.Assert(binder != null);
                    }
                }
                else if (kind == SyntaxKind.TypeOfExpression &&
                    typeOfArgument == null &&
                    LookupPosition.IsBetweenTokens(
                        position,
                        (typeOfExpression = (TypeOfExpressionSyntax)current).OpenParenToken,
                        typeOfExpression.CloseParenToken))
                {
                    typeOfArgument = typeOfExpression.Type;
                }
                else if (kind == SyntaxKind.SwitchSection)
                {
                    if (LookupPosition.IsInSwitchSectionScope(position, (SwitchSectionSyntax)current))
                    {
                        binder = rootBinder.GetBinder(current);
                    }
                }
                else if (kind == SyntaxKind.ArgumentList)
                {
                    var argList = (ArgumentListSyntax)current;
 
                    if (LookupPosition.IsBetweenTokens(position, argList.OpenParenToken, argList.CloseParenToken))
                    {
                        binder = rootBinder.GetBinder(current);
                    }
                }
                else if (kind == SyntaxKind.EqualsValueClause)
                {
                    binder = rootBinder.GetBinder(current);
                }
                else if (kind == SyntaxKind.Attribute)
                {
                    binder = rootBinder.GetBinder(current);
                }
                else if (kind == SyntaxKind.ArrowExpressionClause)
                {
                    binder = rootBinder.GetBinder(current);
                }
                else if (kind == SyntaxKind.ThisConstructorInitializer || kind == SyntaxKind.BaseConstructorInitializer || kind == SyntaxKind.PrimaryConstructorBaseType)
                {
                    binder = rootBinder.GetBinder(current);
                }
                else if (kind == SyntaxKind.ConstructorDeclaration)
                {
                    binder = rootBinder.GetBinder(current);
                }
                else if (kind == SyntaxKind.SwitchExpression)
                {
                    binder = rootBinder.GetBinder(current);
                }
                else if (kind == SyntaxKind.SwitchExpressionArm)
                {
                    binder = rootBinder.GetBinder(current);
                }
                else if ((current as ExpressionSyntax).IsValidScopeDesignator())
                {
                    binder = rootBinder.GetBinder(current);
                }
                else if ((current is InvocationExpressionSyntax invocation) && invocation.MayBeNameofOperator())
                {
                    binder = rootBinder.GetBinder(current);
                }
                else if (current is CheckedExpressionSyntax checkedExpression)
                {
                    if (LookupPosition.IsBetweenTokens(position, checkedExpression.OpenParenToken, checkedExpression.CloseParenToken))
                    {
                        binder = rootBinder.GetBinder(current);
                    }
                }
                else
                {
                    // If this ever breaks, make sure that all callers of
                    // CanHaveAssociatedLocalBinder are in sync.
                    Debug.Assert(!current.CanHaveAssociatedLocalBinder());
                }
 
                if (current == root)
                {
                    break;
                }
            }
 
            binder = binder ?? rootBinder.GetBinder(root) ?? rootBinder;
            Debug.Assert(binder != null);
 
            if (ownerOfTypeParametersInScope != null)
            {
                LocalFunctionSymbol function = GetDeclaredLocalFunction(binder, ownerOfTypeParametersInScope.Identifier);
                if ((object)function != null)
                {
                    binder = function.WithTypeParametersBinder;
                }
            }
 
            if (typeOfArgument != null)
            {
                binder = new TypeofBinder(typeOfArgument, binder);
            }
 
            return binder;
        }
 
        private static Binder AdjustBinderForPositionWithinStatement(int position, Binder binder, StatementSyntax stmt)
        {
            switch (stmt.Kind())
            {
                case SyntaxKind.SwitchStatement:
                    var switchStmt = (SwitchStatementSyntax)stmt;
                    if (LookupPosition.IsBetweenTokens(position, switchStmt.SwitchKeyword, switchStmt.OpenBraceToken))
                    {
                        binder = binder.GetBinder(switchStmt.Expression);
                        Debug.Assert(binder != null);
                    }
                    break;
 
                case SyntaxKind.ForStatement:
                    var forStmt = (ForStatementSyntax)stmt;
                    if (LookupPosition.IsBetweenTokens(position, forStmt.SecondSemicolonToken, forStmt.CloseParenToken) &&
                        forStmt.Incrementors.Count > 0)
                    {
                        binder = binder.GetBinder(forStmt.Incrementors.First());
                        Debug.Assert(binder != null);
                    }
                    else if (LookupPosition.IsBetweenTokens(position, forStmt.FirstSemicolonToken, LookupPosition.GetFirstExcludedToken(forStmt)) &&
                        forStmt.Condition != null)
                    {
                        binder = binder.GetBinder(forStmt.Condition);
                        Debug.Assert(binder != null);
                    }
                    break;
 
                case SyntaxKind.ForEachStatement:
                case SyntaxKind.ForEachVariableStatement:
                    var foreachStmt = (CommonForEachStatementSyntax)stmt;
                    var start = stmt.Kind() == SyntaxKind.ForEachVariableStatement ? foreachStmt.InKeyword : foreachStmt.OpenParenToken;
                    if (LookupPosition.IsBetweenTokens(position, start, foreachStmt.Statement.GetFirstToken()))
                    {
                        binder = binder.GetBinder(foreachStmt.Expression);
                        Debug.Assert(binder != null);
                    }
                    break;
            }
 
            return binder;
        }
 
        public override Conversion ClassifyConversion(
            ExpressionSyntax expression,
            ITypeSymbol destination,
            bool isExplicitInSource = false)
        {
            if ((object)destination == null)
            {
                throw new ArgumentNullException(nameof(destination));
            }
 
            TypeSymbol csdestination = destination.EnsureCSharpSymbolOrNull(nameof(destination));
 
            if (expression.Kind() == SyntaxKind.DeclarationExpression)
            {
                // Conversion from a declaration is unspecified.
                return Conversion.NoConversion;
            }
 
            // Special Case: We have to treat anonymous functions differently, because of the way
            // they are cached in the syntax-to-bound node map.  Specifically, UnboundLambda nodes
            // never appear in the map - they are converted to BoundLambdas, even in error scenarios.
            // Since a BoundLambda has a type, we would end up doing a conversion from the delegate
            // type, rather than from the anonymous function expression.  If we use the overload that
            // takes a position, it considers the request speculative and does not use the map.
            // Bonus: Since the other overload will always bind the anonymous function from scratch,
            // we don't have to worry about it affecting the trial-binding cache in the "real" 
            // UnboundLambda node (DevDiv #854548).
            if (expression.IsAnonymousFunction())
            {
                CheckSyntaxNode(expression);
                return this.ClassifyConversion(expression.SpanStart, expression, destination, isExplicitInSource);
            }
 
            if (isExplicitInSource)
            {
                return ClassifyConversionForCast(expression, csdestination);
            }
 
            // Note that it is possible for an expression to be convertible to a type
            // via both an implicit user-defined conversion and an explicit built-in conversion.
            // In that case, this method chooses the implicit conversion.
 
            CheckSyntaxNode(expression);
 
            var binder = this.GetEnclosingBinderInternal(expression, GetAdjustedNodePosition(expression));
            CSharpSyntaxNode bindableNode = this.GetBindableSyntaxNode(expression);
            var boundExpression = this.GetLowerBoundNode(bindableNode) as BoundExpression;
            if (binder == null || boundExpression == null)
            {
                return Conversion.NoConversion;
            }
 
            var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
 
            return binder.Conversions.ClassifyConversionFromExpression(boundExpression, csdestination, isChecked: binder.CheckOverflowAtRuntime, ref discardedUseSiteInfo);
        }
 
        internal override Conversion ClassifyConversionForCast(
            ExpressionSyntax expression,
            TypeSymbol destination)
        {
            CheckSyntaxNode(expression);
 
            if ((object)destination == null)
            {
                throw new ArgumentNullException(nameof(destination));
            }
 
            var binder = this.GetEnclosingBinderInternal(expression, GetAdjustedNodePosition(expression));
            CSharpSyntaxNode bindableNode = this.GetBindableSyntaxNode(expression);
            var boundExpression = this.GetLowerBoundNode(bindableNode) as BoundExpression;
            if (binder == null || boundExpression == null)
            {
                return Conversion.NoConversion;
            }
 
            var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
 
            return binder.Conversions.ClassifyConversionFromExpression(boundExpression, destination, isChecked: binder.CheckOverflowAtRuntime, ref discardedUseSiteInfo, forCast: true);
        }
 
        /// <summary>
        /// Get the bound node corresponding to the root.
        /// </summary> 
        internal virtual BoundNode GetBoundRoot()
        {
            return GetUpperBoundNode(GetBindableSyntaxNode(this.Root));
        }
 
        /// <summary>
        /// Get the highest bound node in the tree associated with a particular syntax node.
        /// </summary>
        internal BoundNode GetUpperBoundNode(CSharpSyntaxNode node, bool promoteToBindable = false)
        {
            if (promoteToBindable)
            {
                node = GetBindableSyntaxNode(node);
            }
            else
            {
                Debug.Assert(node == GetBindableSyntaxNode(node));
            }
 
            // The bound nodes are stored in the map from highest to lowest, so the first bound node is the highest.
            var boundNodes = GetBoundNodes(node);
 
            if (boundNodes.Count == 0)
            {
                return null;
            }
            else
            {
                return boundNodes[0];
            }
        }
 
        /// <summary>
        /// Get the lowest bound node in the tree associated with a particular syntax node. Lowest is defined as last
        /// in a pre-order traversal of the bound tree.
        /// </summary>
        internal BoundNode GetLowerBoundNode(CSharpSyntaxNode node)
        {
            Debug.Assert(node == GetBindableSyntaxNode(node));
 
            // The bound nodes are stored in the map from highest to lowest, so the last bound node is the lowest.
            var boundNodes = GetBoundNodes(node);
 
            if (boundNodes.Count == 0)
            {
                return null;
            }
            else
            {
                return GetLowerBoundNode(boundNodes);
            }
        }
 
        private static BoundNode GetLowerBoundNode(OneOrMany<BoundNode> boundNodes)
        {
            return boundNodes[boundNodes.Count - 1];
        }
 
        public sealed override ImmutableArray<Diagnostic> GetSyntaxDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            throw new NotSupportedException();
        }
 
        public sealed override ImmutableArray<Diagnostic> GetDeclarationDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            throw new NotSupportedException();
        }
 
        public sealed override ImmutableArray<Diagnostic> GetMethodBodyDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            throw new NotSupportedException();
        }
 
        public sealed override ImmutableArray<Diagnostic> GetDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            throw new NotSupportedException();
        }
 
        public override INamespaceSymbol GetDeclaredSymbol(NamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't defined namespace inside a member.
            return null;
        }
 
        public override INamespaceSymbol GetDeclaredSymbol(FileScopedNamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't defined namespace inside a member.
            return null;
        }
 
        public override INamedTypeSymbol GetDeclaredSymbol(BaseTypeDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't define type inside a member.
            return null;
        }
 
        public override INamedTypeSymbol GetDeclaredSymbol(DelegateDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't define type inside a member.
            return null;
        }
 
        public override IFieldSymbol GetDeclaredSymbol(EnumMemberDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't define member inside member.
            return null;
        }
 
        public override IMethodSymbol GetDeclaredSymbol(LocalFunctionStatementSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(declarationSyntax);
            return GetDeclaredLocalFunction(declarationSyntax).GetPublicSymbol();
        }
 
        public override ISymbol GetDeclaredSymbol(MemberDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't define member inside member.
            return null;
        }
 
        public override IMethodSymbol GetDeclaredSymbol(CompilationUnitSyntax declarationSyntax, CancellationToken cancellationToken = default)
        {
            return null;
        }
 
        public override IMethodSymbol GetDeclaredSymbol(BaseMethodDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't define method inside member.
            return null;
        }
 
        public override ISymbol GetDeclaredSymbol(BasePropertyDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't define property inside member.
            return null;
        }
 
        public override IPropertySymbol GetDeclaredSymbol(PropertyDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't define property inside member.
            return null;
        }
 
        public override IPropertySymbol GetDeclaredSymbol(IndexerDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't define indexer inside member.
            return null;
        }
 
        public override IEventSymbol GetDeclaredSymbol(EventDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't define event inside member.
            return null;
        }
 
        public override IMethodSymbol GetDeclaredSymbol(AccessorDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't define accessor inside member.
            return null;
        }
 
        public override IMethodSymbol GetDeclaredSymbol(ArrowExpressionClauseSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't define another member inside member.
            return null;
        }
 
        public override ISymbol GetDeclaredSymbol(VariableDeclaratorSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(declarationSyntax);
            return GetDeclaredLocal(declarationSyntax, declarationSyntax.Identifier).GetPublicSymbol();
        }
 
        public override ISymbol GetDeclaredSymbol(SingleVariableDesignationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(declarationSyntax);
            return GetDeclaredLocal(declarationSyntax, declarationSyntax.Identifier).GetPublicSymbol();
        }
 
        private LocalSymbol GetDeclaredLocal(CSharpSyntaxNode declarationSyntax, SyntaxToken declaredIdentifier)
        {
            for (var binder = this.GetEnclosingBinder(GetAdjustedNodePosition(declarationSyntax)); binder != null; binder = binder.Next)
            {
                foreach (var local in binder.Locals)
                {
                    if (local.IdentifierToken == declaredIdentifier)
                    {
                        return GetAdjustedLocalSymbol((SourceLocalSymbol)local);
                    }
                }
            }
 
            return null;
        }
 
#nullable enable
        internal override LocalSymbol GetAdjustedLocalSymbol(SourceLocalSymbol local)
        {
            return GetRemappedSymbol<LocalSymbol>(local);
        }
 
        internal LocalFunctionSymbol GetDeclaredLocalFunction(LocalFunctionStatementSyntax declarationSyntax)
        {
            var originalSymbol = GetDeclaredLocalFunction(this.GetEnclosingBinder(GetAdjustedNodePosition(declarationSyntax)), declarationSyntax.Identifier);
            return GetRemappedSymbol(originalSymbol);
        }
 
        private T GetRemappedSymbol<T>(T originalSymbol) where T : Symbol
        {
            EnsureNullabilityAnalysisPerformedIfNecessary();
            if (_lazyRemappedSymbols is null) return originalSymbol;
 
            if (_lazyRemappedSymbols.TryGetValue(originalSymbol, out Symbol? remappedSymbol))
            {
                RoslynDebug.Assert(remappedSymbol is object);
                return (T)remappedSymbol;
            }
 
            return originalSymbol;
        }
#nullable disable
 
        private static LocalFunctionSymbol GetDeclaredLocalFunction(Binder enclosingBinder, SyntaxToken declaredIdentifier)
        {
            for (var binder = enclosingBinder; binder != null; binder = binder.Next)
            {
                foreach (var localFunction in binder.LocalFunctions)
                {
                    if (localFunction.NameToken == declaredIdentifier)
                    {
                        return localFunction;
                    }
                }
            }
 
            return null;
        }
 
        public override ILabelSymbol GetDeclaredSymbol(LabeledStatementSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(declarationSyntax);
 
            var binder = this.GetEnclosingBinder(GetAdjustedNodePosition(declarationSyntax));
 
            while (binder != null && !binder.IsLabelsScopeBinder)
            {
                binder = binder.Next;
            }
 
            if (binder != null)
            {
                foreach (var label in binder.Labels)
                {
                    if (label.IdentifierNodeOrToken.IsToken &&
                        label.IdentifierNodeOrToken.AsToken() == declarationSyntax.Identifier)
                    {
                        return label.GetPublicSymbol();
                    }
                }
            }
 
            return null;
        }
 
        public override ILabelSymbol GetDeclaredSymbol(SwitchLabelSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(declarationSyntax);
 
            var binder = this.GetEnclosingBinder(GetAdjustedNodePosition(declarationSyntax));
            while (binder != null && !(binder is SwitchBinder))
            {
                binder = binder.Next;
            }
 
            if (binder != null)
            {
                foreach (var label in binder.Labels)
                {
                    if (label.IdentifierNodeOrToken.IsNode &&
                        label.IdentifierNodeOrToken.AsNode() == declarationSyntax)
                    {
                        return label.GetPublicSymbol();
                    }
                }
            }
 
            return null;
        }
 
        public override IAliasSymbol GetDeclaredSymbol(UsingDirectiveSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't define alias inside member.
            return null;
        }
 
        public override IAliasSymbol GetDeclaredSymbol(ExternAliasDirectiveSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't define an extern alias inside a member.
            return null;
        }
 
        public override IParameterSymbol GetDeclaredSymbol(ParameterSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Could be parameter of a lambda or a local function.
            CheckSyntaxNode(declarationSyntax);
 
            return GetLambdaOrLocalFunctionParameterSymbol(declarationSyntax, cancellationToken).GetPublicSymbol();
        }
 
        internal override ImmutableArray<ISymbol> GetDeclaredSymbols(BaseFieldDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't define field inside member.
            return ImmutableArray.Create<ISymbol>();
        }
 
        private ParameterSymbol GetLambdaOrLocalFunctionParameterSymbol(
            ParameterSyntax parameter,
            CancellationToken cancellationToken)
        {
            Debug.Assert(parameter != null);
 
            var simpleLambda = parameter.Parent as SimpleLambdaExpressionSyntax;
            if (simpleLambda != null)
            {
                return GetLambdaParameterSymbol(parameter, simpleLambda, cancellationToken);
            }
 
            var paramList = parameter.Parent as ParameterListSyntax;
            if (paramList == null || paramList.Parent == null)
            {
                return null;
            }
 
            if (paramList.Parent.IsAnonymousFunction())
            {
                return GetLambdaParameterSymbol(parameter, (ExpressionSyntax)paramList.Parent, cancellationToken);
            }
            else if (paramList.Parent.Kind() == SyntaxKind.LocalFunctionStatement)
            {
                var localFunction = GetDeclaredSymbol((LocalFunctionStatementSyntax)paramList.Parent, cancellationToken).GetSymbol<MethodSymbol>();
                if ((object)localFunction != null)
                {
                    return GetParameterSymbol(localFunction.Parameters, parameter, cancellationToken);
                }
            }
 
            return null;
        }
 
        private ParameterSymbol GetLambdaParameterSymbol(
            ParameterSyntax parameter,
            ExpressionSyntax lambda,
            CancellationToken cancellationToken)
        {
            Debug.Assert(parameter != null);
            Debug.Assert(lambda != null && lambda.IsAnonymousFunction());
 
            // We should always be able to get at least an error binding for a lambda.
 
            SymbolInfo symbolInfo = this.GetSymbolInfo(lambda, cancellationToken);
 
            LambdaSymbol lambdaSymbol;
            if ((object)symbolInfo.Symbol != null)
            {
                lambdaSymbol = symbolInfo.Symbol.GetSymbol<LambdaSymbol>();
            }
            else if (symbolInfo.CandidateSymbols.Length == 1)
            {
                lambdaSymbol = symbolInfo.CandidateSymbols.Single().GetSymbol<LambdaSymbol>();
            }
            else
            {
                Debug.Assert(this.GetMemberModel(lambda) == null, "Did not find a unique LambdaSymbol for lambda in member.");
                return null;
            }
            return GetParameterSymbol(lambdaSymbol.Parameters, parameter, cancellationToken);
        }
 
        public override ITypeParameterSymbol GetDeclaredSymbol(TypeParameterSyntax typeParameter, CancellationToken cancellationToken = default(CancellationToken))
        {
            // Can't define alias inside member.
            return null;
        }
 
        public override IRangeVariableSymbol GetDeclaredSymbol(JoinIntoClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken))
        {
            var bound = GetBoundQueryClause(node);
            return bound == null ? null : bound.DefinedSymbol.GetPublicSymbol();
        }
 
        public override IRangeVariableSymbol GetDeclaredSymbol(QueryClauseSyntax queryClause, CancellationToken cancellationToken = default(CancellationToken))
        {
            var bound = GetBoundQueryClause(queryClause);
            return bound == null ? null : bound.DefinedSymbol.GetPublicSymbol();
        }
 
        public override IRangeVariableSymbol GetDeclaredSymbol(QueryContinuationSyntax node, CancellationToken cancellationToken = default(CancellationToken))
        {
            var bound = GetBoundQueryClause(node);
            return bound == null ? null : bound.DefinedSymbol.GetPublicSymbol();
        }
 
        public override AwaitExpressionInfo GetAwaitExpressionInfo(AwaitExpressionSyntax node)
        {
            if (node.Kind() != SyntaxKind.AwaitExpression)
            {
                throw new ArgumentException("node.Kind==" + node.Kind());
            }
 
            var bound = GetLowerBoundNode(node);
            BoundAwaitableInfo awaitableInfo = (((bound as BoundExpressionStatement)?.Expression ?? bound) as BoundAwaitExpression)?.AwaitableInfo;
            if (awaitableInfo == null)
            {
                return default(AwaitExpressionInfo);
            }
 
            return new AwaitExpressionInfo(
                getAwaiter: (IMethodSymbol)awaitableInfo.GetAwaiter?.ExpressionSymbol.GetPublicSymbol(),
                isCompleted: awaitableInfo.IsCompleted.GetPublicSymbol(),
                getResult: awaitableInfo.GetResult.GetPublicSymbol(),
                isDynamic: awaitableInfo.IsDynamic);
        }
 
        public override ForEachStatementInfo GetForEachStatementInfo(ForEachStatementSyntax node)
        {
            return GetForEachStatementInfo((CommonForEachStatementSyntax)node);
        }
 
        public override ForEachStatementInfo GetForEachStatementInfo(CommonForEachStatementSyntax node)
        {
            BoundForEachStatement boundForEach = (BoundForEachStatement)GetUpperBoundNode(node);
 
            if (boundForEach == null)
            {
                return default(ForEachStatementInfo);
            }
 
            ForEachEnumeratorInfo enumeratorInfoOpt = boundForEach.EnumeratorInfoOpt;
 
            Debug.Assert(enumeratorInfoOpt != null || boundForEach.HasAnyErrors);
 
            if (enumeratorInfoOpt == null)
            {
                return default(ForEachStatementInfo);
            }
 
            // Even though we usually pretend to be using System.Collection.IEnumerable
            // for arrays, that doesn't make sense for pointer arrays since object
            // (the type of System.Collections.IEnumerator.Current) isn't convertible
            // to pointer types.
            if (enumeratorInfoOpt.CurrentConversion is null && enumeratorInfoOpt.ElementType.IsPointerType())
            {
                return default(ForEachStatementInfo);
            }
 
            // NOTE: we're going to list GetEnumerator, etc for array and string
            // collections, even though we know that's not how the implementation
            // actually enumerates them.
            MethodSymbol disposeMethod = null;
            if (enumeratorInfoOpt.NeedsDisposal)
            {
                if (enumeratorInfoOpt.PatternDisposeInfo is { Method: var method })
                {
                    disposeMethod = method;
                }
                else
                {
                    disposeMethod = enumeratorInfoOpt.IsAsync
                    ? (MethodSymbol)Compilation.GetWellKnownTypeMember(WellKnownMember.System_IAsyncDisposable__DisposeAsync)
                    : (MethodSymbol)Compilation.GetSpecialTypeMember(SpecialMember.System_IDisposable__Dispose);
                }
            }
 
            return new ForEachStatementInfo(
                enumeratorInfoOpt.IsAsync,
                enumeratorInfoOpt.GetEnumeratorInfo.Method.GetPublicSymbol(),
                enumeratorInfoOpt.MoveNextInfo.Method.GetPublicSymbol(),
                currentProperty: ((PropertySymbol)enumeratorInfoOpt.CurrentPropertyGetter?.AssociatedSymbol).GetPublicSymbol(),
                disposeMethod.GetPublicSymbol(),
                enumeratorInfoOpt.ElementTypeWithAnnotations.GetPublicSymbol(),
                BoundNode.GetConversion(boundForEach.ElementConversion, boundForEach.ElementPlaceholder),
                BoundNode.GetConversion(enumeratorInfoOpt.CurrentConversion, enumeratorInfoOpt.CurrentPlaceholder));
        }
 
        public override DeconstructionInfo GetDeconstructionInfo(AssignmentExpressionSyntax node)
        {
            var boundDeconstruction = GetUpperBoundNode(node) as BoundDeconstructionAssignmentOperator;
            if (boundDeconstruction is null)
            {
                return default;
            }
 
            var boundConversion = boundDeconstruction.Right;
            Debug.Assert(boundConversion != null);
            if (boundConversion is null)
            {
                return default;
            }
 
            return new DeconstructionInfo(boundConversion.Conversion);
        }
 
        public override DeconstructionInfo GetDeconstructionInfo(ForEachVariableStatementSyntax node)
        {
            var boundForEach = (BoundForEachStatement)GetUpperBoundNode(node);
            if (boundForEach is null)
            {
                return default;
            }
 
            var boundDeconstruction = boundForEach.DeconstructionOpt;
            Debug.Assert(boundDeconstruction != null || boundForEach.HasAnyErrors);
            if (boundDeconstruction is null)
            {
                return default;
            }
 
            return new DeconstructionInfo(boundDeconstruction.DeconstructionAssignment.Right.Conversion);
        }
 
        private BoundQueryClause GetBoundQueryClause(CSharpSyntaxNode node)
        {
            CheckSyntaxNode(node);
            return this.GetLowerBoundNode(node) as BoundQueryClause;
        }
 
        private QueryClauseInfo GetQueryClauseInfo(BoundQueryClause bound)
        {
            if (bound == null) return default(QueryClauseInfo);
            var castInfo = (bound.Cast == null) ? SymbolInfo.None : GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, bound.Cast, bound.Cast, boundNodeForSyntacticParent: null, binderOpt: null);
            var operationInfo = GetSymbolInfoForQuery(bound);
            return new QueryClauseInfo(castInfo: castInfo, operationInfo: operationInfo);
        }
 
        private SymbolInfo GetSymbolInfoForQuery(BoundQueryClause bound)
        {
            var call = bound?.Operation as BoundCall;
            if (call == null)
            {
                return SymbolInfo.None;
            }
 
            var operation = call.IsDelegateCall ? call.ReceiverOpt : call;
            return GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, operation, operation, boundNodeForSyntacticParent: null, binderOpt: null);
        }
 
        private CSharpTypeInfo GetTypeInfoForQuery(BoundQueryClause bound)
        {
            return bound == null ?
                CSharpTypeInfo.None :
                GetTypeInfoForNode(bound, bound, bound);
        }
 
        public override QueryClauseInfo GetQueryClauseInfo(QueryClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken))
        {
            var bound = GetBoundQueryClause(node);
            return GetQueryClauseInfo(bound);
        }
 
        public override IPropertySymbol GetDeclaredSymbol(AnonymousObjectMemberDeclaratorSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(declaratorSyntax);
            var anonymousObjectCreation = (AnonymousObjectCreationExpressionSyntax)declaratorSyntax.Parent;
            if (anonymousObjectCreation == null)
            {
                return null;
            }
 
            var bound = this.GetLowerBoundNode(anonymousObjectCreation) as BoundAnonymousObjectCreationExpression;
            if (bound == null)
            {
                return null;
            }
 
            var anonymousType = bound.Type as NamedTypeSymbol;
            if ((object)anonymousType == null)
            {
                return null;
            }
 
            int index = anonymousObjectCreation.Initializers.IndexOf(declaratorSyntax);
            Debug.Assert(index >= 0);
            Debug.Assert(index < anonymousObjectCreation.Initializers.Count);
            return AnonymousTypeManager.GetAnonymousTypeProperty(anonymousType, index).GetPublicSymbol();
        }
 
        public override INamedTypeSymbol GetDeclaredSymbol(AnonymousObjectCreationExpressionSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(declaratorSyntax);
            var bound = this.GetLowerBoundNode(declaratorSyntax) as BoundAnonymousObjectCreationExpression;
            return (bound == null) ? null : (bound.Type as NamedTypeSymbol).GetPublicSymbol();
        }
 
        public override INamedTypeSymbol GetDeclaredSymbol(TupleExpressionSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(declaratorSyntax);
            return GetTypeOfTupleLiteral(declaratorSyntax).GetPublicSymbol();
        }
 
        public override ISymbol GetDeclaredSymbol(ArgumentSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(declaratorSyntax);
 
            var tupleLiteral = declaratorSyntax?.Parent as TupleExpressionSyntax;
 
            // for now only arguments of a tuple literal may declare symbols
            if (tupleLiteral == null)
            {
                return null;
            }
 
            var tupleLiteralType = GetTypeOfTupleLiteral(tupleLiteral);
 
            if ((object)tupleLiteralType != null)
            {
                var elements = tupleLiteralType.TupleElements;
 
                if (!elements.IsDefault)
                {
                    var idx = tupleLiteral.Arguments.IndexOf(declaratorSyntax);
                    return elements[idx].GetPublicSymbol();
                }
            }
 
            return null;
        }
 
        private NamedTypeSymbol GetTypeOfTupleLiteral(TupleExpressionSyntax declaratorSyntax)
        {
            var bound = this.GetLowerBoundNode(declaratorSyntax);
 
            return (bound as BoundTupleExpression)?.Type as NamedTypeSymbol;
        }
 
        public override SyntaxTree SyntaxTree
        {
            get
            {
                return _root.SyntaxTree;
            }
        }
 
#nullable enable
        internal override IOperation? GetOperationWorker(CSharpSyntaxNode node, CancellationToken cancellationToken)
        {
            using (_nodeMapLock.DisposableRead())
            {
                if (_guardedIOperationNodeMap.Count != 0)
                {
                    return guardedGetIOperation();
                }
            }
 
            IOperation rootOperation = GetRootOperation();
 
            using var _ = _nodeMapLock.DisposableWrite();
 
            if (_guardedIOperationNodeMap.Count != 0)
            {
                return guardedGetIOperation();
            }
 
            OperationMapBuilder.AddToMap(rootOperation, _guardedIOperationNodeMap);
            return guardedGetIOperation();
 
            IOperation? guardedGetIOperation()
            {
                _nodeMapLock.AssertCanRead();
                return _guardedIOperationNodeMap.TryGetValue(node, out var operation) ? operation : null;
            }
        }
 
        private IOperation GetRootOperation()
        {
            BoundNode highestBoundNode = GetBoundRoot();
            Debug.Assert(highestBoundNode != null);
 
            if (highestBoundNode is BoundGlobalStatementInitializer { Statement: var innerStatement })
            {
                // Script top-level field declarations use a BoundGlobalStatementInitializer to wrap initializers.
                // We don't represent these nodes in IOperation, so skip it.
                highestBoundNode = innerStatement;
            }
 
            // The CSharp operation factory assumes that UnboundLambda will be bound for error recovery and never be passed to the factory
            // as the start of a tree to get operations for. This is guaranteed by the builder that populates the node map, as it will call
            // UnboundLambda.BindForErrorRecovery() when it encounters an UnboundLambda node.
            Debug.Assert(highestBoundNode.Kind != BoundKind.UnboundLambda);
            IOperation operation = _operationFactory.Value.Create(highestBoundNode);
            Operation.SetParentOperation(operation, null);
            return operation;
        }
#nullable disable
 
        internal override SymbolInfo GetSymbolInfoWorker(CSharpSyntaxNode node, SymbolInfoOptions options, CancellationToken cancellationToken = default(CancellationToken))
        {
            ValidateSymbolInfoOptions(options);
 
            CSharpSyntaxNode bindableNode;
            BoundNode lowestBoundNode;
            BoundNode highestBoundNode;
            BoundNode boundParent;
            GetBoundNodes(node, out bindableNode, out lowestBoundNode, out highestBoundNode, out boundParent);
 
            Debug.Assert(IsInTree(node), "Since the node is in the tree, we can always recompute the binder later");
            return base.GetSymbolInfoForNode(options, lowestBoundNode, highestBoundNode, boundParent, binderOpt: null);
        }
 
        internal override CSharpTypeInfo GetTypeInfoWorker(CSharpSyntaxNode node, CancellationToken cancellationToken = default(CancellationToken))
        {
            CSharpSyntaxNode bindableNode;
            BoundNode lowestBoundNode;
            BoundNode highestBoundNode;
            BoundNode boundParent;
            GetBoundNodes(node, out bindableNode, out lowestBoundNode, out highestBoundNode, out boundParent);
 
            return GetTypeInfoForNode(lowestBoundNode, highestBoundNode, boundParent);
        }
 
        internal override ImmutableArray<Symbol> GetMemberGroupWorker(CSharpSyntaxNode node, SymbolInfoOptions options, CancellationToken cancellationToken = default(CancellationToken))
        {
            CSharpSyntaxNode bindableNode;
            BoundNode lowestBoundNode;
            BoundNode highestBoundNode;
            BoundNode boundParent;
            GetBoundNodes(node, out bindableNode, out lowestBoundNode, out highestBoundNode, out boundParent);
 
            Debug.Assert(IsInTree(node), "Since the node is in the tree, we can always recompute the binder later");
            return base.GetMemberGroupForNode(options, lowestBoundNode, boundParent, binderOpt: null);
        }
 
        internal override ImmutableArray<IPropertySymbol> GetIndexerGroupWorker(CSharpSyntaxNode node, SymbolInfoOptions options, CancellationToken cancellationToken = default(CancellationToken))
        {
            CSharpSyntaxNode bindableNode;
            BoundNode lowestBoundNode;
            BoundNode highestBoundNode;
            BoundNode boundParent;
            GetBoundNodes(node, out bindableNode, out lowestBoundNode, out highestBoundNode, out boundParent);
 
            Debug.Assert(IsInTree(node), "Since the node is in the tree, we can always recompute the binder later");
            return base.GetIndexerGroupForNode(lowestBoundNode, binderOpt: null);
        }
 
        internal override Optional<object> GetConstantValueWorker(CSharpSyntaxNode node, CancellationToken cancellationToken)
        {
            CSharpSyntaxNode bindableNode = this.GetBindableSyntaxNode(node);
            BoundExpression boundExpr = this.GetLowerBoundNode(bindableNode) as BoundExpression;
 
            if (boundExpr == null) return default(Optional<object>);
 
            ConstantValue constantValue = boundExpr.ConstantValueOpt;
            return constantValue == null || constantValue.IsBad
                ? default(Optional<object>)
                : new Optional<object>(constantValue.Value);
        }
 
        internal override SymbolInfo GetCollectionInitializerSymbolInfoWorker(InitializerExpressionSyntax collectionInitializer, ExpressionSyntax node, CancellationToken cancellationToken = default(CancellationToken))
        {
            var boundCollectionInitializer = GetLowerBoundNode(collectionInitializer) as BoundCollectionInitializerExpression;
 
            if (boundCollectionInitializer != null)
            {
                var boundAdd = boundCollectionInitializer.Initializers[collectionInitializer.Expressions.IndexOf(node)];
 
                return GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, boundAdd, boundAdd, null, binderOpt: null);
            }
 
            return SymbolInfo.None;
        }
 
        public override SymbolInfo GetSymbolInfo(OrderingSyntax node, CancellationToken cancellationToken = default(CancellationToken))
        {
            var bound = GetBoundQueryClause(node);
            return GetSymbolInfoForQuery(bound);
        }
 
        public override SymbolInfo GetSymbolInfo(SelectOrGroupClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken))
        {
            var bound = GetBoundQueryClause(node);
            return GetSymbolInfoForQuery(bound);
        }
 
        public override TypeInfo GetTypeInfo(SelectOrGroupClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken))
        {
            var bound = GetBoundQueryClause(node);
            return GetTypeInfoForQuery(bound);
        }
 
        private void GetBoundNodes(CSharpSyntaxNode node, out CSharpSyntaxNode bindableNode, out BoundNode lowestBoundNode, out BoundNode highestBoundNode, out BoundNode boundParent)
        {
            bindableNode = this.GetBindableSyntaxNode(node);
 
            CSharpSyntaxNode bindableParent = this.GetBindableParentNode(bindableNode);
 
            // Special handling for the Color Color case.
            //
            // Suppose we have:
            // public class Color {
            //   public void M(int x) {}
            //   public static void M(params int[] x) {}
            // }
            // public class C {
            //   public void Test() {
            //     Color Color = new Color();
            //     System.Action<int> d = Color.M;
            //   }
            // }
            //
            // We actually don't know how to interpret the "Color" in "Color.M" until we
            // perform overload resolution on the method group.  Now, if we were getting
            // the semantic info for the method group, then bindableParent would be the
            // variable declarator "d = Color.M" and so we would be able to pull the result
            // of overload resolution out of the bound (method group) conversion.  However,
            // if we are getting the semantic info for just the "Color" part, then
            // bindableParent will be the member access, which doesn't have enough information
            // to determine which "Color" to use (since no overload resolution has been
            // performed).  We resolve this problem by detecting the case where we're looking
            // up the LHS of a member access and calling GetBindableParentNode one more time.
            // This gets us up to the level where the method group conversion occurs.
            if (bindableParent != null && bindableParent.Kind() == SyntaxKind.SimpleMemberAccessExpression && ((MemberAccessExpressionSyntax)bindableParent).Expression == bindableNode)
            {
                bindableParent = this.GetBindableParentNode(bindableParent);
            }
 
            boundParent = bindableParent == null ? null : this.GetLowerBoundNode(bindableParent);
 
            lowestBoundNode = this.GetLowerBoundNode(bindableNode);
            highestBoundNode = this.GetUpperBoundNode(bindableNode);
        }
 
        // In lambda binding scenarios we need to know two things: First,
        // what is the *innermost* lambda that contains the expression we're
        // interested in?  Second, what is the smallest expression that contains 
        // the *outermost* lambda that we can bind in order to get a sensible
        // lambda binding?
        //
        // For example, suppose we have the statement:
        //
        // A().B(x=>x.C(y=>y.D().E())).F().G();
        //
        // and the user wants binding information about method group "D".  We must know
        // the bindable expression that is outside of every lambda:
        //
        // A().B(x=>x.C(y=>y.D().E()))
        //
        // By binding that we can determine the type of lambda parameters "x" and "y" and
        // put that information in the bound tree. Once we know those facts then
        // we can obtain the binding object associated with the innermost lambda:
        //
        // y=>y.D().E()
        //
        // And use that binding to obtain the analysis of:
        //
        // y.D
        //
        private CSharpSyntaxNode GetInnermostLambdaOrQuery(CSharpSyntaxNode node, int position, bool allowStarting = false)
        {
            Debug.Assert(node != null);
 
            for (var current = node; current != this.Root; current = current.ParentOrStructuredTriviaParent)
            {
                // current can only become null if we somehow got past the root. The only way we
                // could have gotten past the root is to have started outside of it. That's
                // unexpected; the binding should only be asked to provide an opinion on syntax
                // nodes that it knows about.
                Debug.Assert(current != null, "Why are we being asked to find an enclosing lambda outside of our root?");
 
                if (!(current.IsAnonymousFunction() || current.IsQuery()))
                {
                    continue;
                }
 
                // If the position is not actually within the scope of the lambda, then keep
                // looking.
                if (!LookupPosition.IsInAnonymousFunctionOrQuery(position, current))
                {
                    continue;
                }
 
                // If we were asked for the innermost lambda enclosing a lambda then don't return
                // that; it's not enclosing anything. Only return the lambda if it's enclosing the
                // original node.
 
                if (!allowStarting && current == node)
                {
                    continue;
                }
 
                return current;
            }
 
            // If we made it to the root, then we are not "inside" a lambda even if the root is a
            // lambda. Remember, the point of this code is to get the binding that is associated
            // with the innermost lambda; if we are already in a binding associated with the
            // innermost lambda then we're done.
            return null;
        }
 
        private void GuardedAddSynthesizedStatementToMap(StatementSyntax node, BoundStatement statement)
        {
            if (_lazyGuardedSynthesizedStatementsMap == null)
            {
                _lazyGuardedSynthesizedStatementsMap = new Dictionary<SyntaxNode, BoundStatement>();
            }
 
            _lazyGuardedSynthesizedStatementsMap.Add(node, statement);
        }
 
        private BoundStatement GuardedGetSynthesizedStatementFromMap(StatementSyntax node)
        {
            if (_lazyGuardedSynthesizedStatementsMap != null &&
                _lazyGuardedSynthesizedStatementsMap.TryGetValue(node, out BoundStatement result))
            {
                return result;
            }
 
            return null;
        }
 
        private OneOrMany<BoundNode> GuardedGetBoundNodesFromMap(CSharpSyntaxNode node)
        {
            Debug.Assert(_nodeMapLock.IsWriteLockHeld || _nodeMapLock.IsReadLockHeld);
            OneOrMany<BoundNode> result;
            return _guardedBoundNodeMap.TryGetValue(node, out result) ? result : OneOrMany<BoundNode>.Empty;
        }
 
        /// <summary>
        /// Internal for test purposes only
        /// </summary>
        internal OneOrMany<BoundNode> TestOnlyTryGetBoundNodesFromMap(CSharpSyntaxNode node)
        {
            OneOrMany<BoundNode> result;
            return _guardedBoundNodeMap.TryGetValue(node, out result) ? result : OneOrMany<BoundNode>.Empty;
        }
 
        // Adds every syntax/bound pair in a tree rooted at the given bound node to the map, and the
        // performs a lookup of the given syntax node in the map. 
        private OneOrMany<BoundNode> GuardedAddBoundTreeAndGetBoundNodeFromMap(CSharpSyntaxNode syntax, BoundNode bound)
        {
            Debug.Assert(_nodeMapLock.IsWriteLockHeld);
 
            bool alreadyInTree = false;
 
            if (bound != null)
            {
                alreadyInTree = _guardedBoundNodeMap.ContainsKey(bound.Syntax);
            }
 
            // check if we already have node in the cache.
            // this may happen if we have races and in such case we are no longer interested in adding
            if (!alreadyInTree)
            {
                NodeMapBuilder.AddToMap(bound, _guardedBoundNodeMap, SyntaxTree);
                Debug.Assert(syntax != _root || _guardedBoundNodeMap.ContainsKey(bound.Syntax));
            }
 
            OneOrMany<BoundNode> result;
            return _guardedBoundNodeMap.TryGetValue(syntax, out result) ? result : OneOrMany<BoundNode>.Empty;
        }
 
        protected void UnguardedAddBoundTreeForStandaloneSyntax(SyntaxNode syntax, BoundNode bound, NullableWalker.SnapshotManager manager = null, ImmutableDictionary<Symbol, Symbol> remappedSymbols = null)
        {
            using (_nodeMapLock.DisposableWrite())
            {
                GuardedAddBoundTreeForStandaloneSyntax(syntax, bound, manager, remappedSymbols);
            }
        }
 
        protected void GuardedAddBoundTreeForStandaloneSyntax(SyntaxNode syntax, BoundNode bound, NullableWalker.SnapshotManager manager = null, ImmutableDictionary<Symbol, Symbol> remappedSymbols = null)
        {
            Debug.Assert(_nodeMapLock.IsWriteLockHeld);
            bool alreadyInTree = false;
 
            // check if we already have node in the cache.
            // this may happen if we have races and in such case we are no longer interested in adding
            if (bound != null)
            {
                alreadyInTree = _guardedBoundNodeMap.ContainsKey(bound.Syntax);
            }
 
            if (!alreadyInTree)
            {
                if (syntax == _root || syntax is StatementSyntax)
                {
                    // Note: For speculative model we want to always cache the entire bound tree.
                    // If syntax is a statement, we need to add all its children.
                    // Node cache assumes that if statement is cached, then all 
                    // its children are cached too.
                    NodeMapBuilder.AddToMap(bound, _guardedBoundNodeMap, SyntaxTree);
                    Debug.Assert(syntax != _root || _guardedBoundNodeMap.ContainsKey(bound.Syntax));
                }
                else
                {
                    // expressions can be added individually.
                    NodeMapBuilder.AddToMap(bound, _guardedBoundNodeMap, SyntaxTree, syntax);
                }
 
                Debug.Assert((manager is null && (!IsNullableAnalysisEnabled() || syntax != Root || syntax is TypeSyntax ||
                                                  // Supporting attributes is tracked by
                                                  // https://github.com/dotnet/roslyn/issues/36066
                                                  this is AttributeSemanticModel)) ||
                             (manager is object && remappedSymbols is object && syntax == Root && IsNullableAnalysisEnabled() && _lazySnapshotManager is null));
                if (manager is object)
                {
                    _lazySnapshotManager = manager;
                    _lazyRemappedSymbols = remappedSymbols;
                }
            }
        }
 
        // We might not have actually been given a bindable expression or statement; the caller can
        // give us variable declaration nodes, for example. If we're not at an expression or
        // statement, back up until we find one.
        private CSharpSyntaxNode GetBindingRoot(CSharpSyntaxNode node)
        {
            Debug.Assert(node != null);
 
#if DEBUG
            for (CSharpSyntaxNode current = node; current != this.Root; current = current.ParentOrStructuredTriviaParent)
            {
                // make sure we never go out of Root
                Debug.Assert(current != null, "How did we get outside the root?");
            }
#endif
 
            for (CSharpSyntaxNode current = node; current != this.Root; current = current.ParentOrStructuredTriviaParent)
            {
                if (current is StatementSyntax)
                {
                    return current;
                }
 
                switch (current.Kind())
                {
                    case SyntaxKind.ThisConstructorInitializer:
                    case SyntaxKind.BaseConstructorInitializer:
                    case SyntaxKind.PrimaryConstructorBaseType:
                        return current;
                    case SyntaxKind.ArrowExpressionClause:
                        // If this is an arrow expression on a local function statement, then our bindable root is actually our parent syntax as it's
                        // a statement in a function. If this is returned directly in IOperation, we'll end up with a separate tree.
                        if (current.Parent == null || current.Parent.Kind() != SyntaxKind.LocalFunctionStatement)
                        {
                            return current;
                        }
                        break;
                }
            }
 
            return this.Root;
        }
 
        // We want the binder in which this syntax node is going to be bound, NOT the binder which
        // this syntax node *produces*. That is, suppose we have
        //
        // void M() { int x; { int y; { int z; } } } 
        //
        // We want the enclosing binder of the syntax node for { int z; }.  We do not want the binder
        // that has local z, but rather the binder that has local y. The inner block is going to be
        // bound in the context of its enclosing binder; it's contents are going to be bound in the
        // context of its binder.
        internal override Binder GetEnclosingBinderInternal(int position)
        {
            AssertPositionAdjusted(position);
 
            // If we have a root binder with no tokens in it, position can be outside the span event
            // after position is adjusted. If this happens, there can't be any 
            if (!this.Root.FullSpan.Contains(position))
                return this.RootBinder;
 
            SyntaxToken token = this.Root.FindToken(position);
            CSharpSyntaxNode node = (CSharpSyntaxNode)token.Parent;
 
            return GetEnclosingBinderInternal(node, position);
        }
 
        /// <summary>
        /// This overload exists for callers who already have a node in hand 
        /// and don't want to search through the tree.
        /// </summary>
        private Binder GetEnclosingBinderInternal(CSharpSyntaxNode node, int position)
        {
            AssertPositionAdjusted(position);
 
            CSharpSyntaxNode innerLambdaOrQuery = GetInnermostLambdaOrQuery(node, position, allowStarting: true);
 
            // There are three possible scenarios here.
            //
            // 1) the node is outside all lambdas in this context, or
            // 2) The node is an outermost lambda in this context, or
            // 3) the node is inside the outermost lambda in this context.
            //
            // In the first case, no lambdas are involved at all so let's just fall back on the
            // original enclosing binder code.
            //
            // In the second case, we have been asked to bind an entire lambda and we know it to be
            // the outermost lambda in this context. Therefore the enclosing binder is going to be
            // the enclosing binder of this expression. However, we do not simply want to say
            // "here's the enclosing binder":
            // 
            // void M() { Func<int, int> f = x=>x+1; }
            //
            // We should step out to the enclosing statement or expression, if there is one, and
            // bind that.
 
            if (innerLambdaOrQuery == null)
            {
                return GetEnclosingBinderInternalWithinRoot(node, position);
            }
 
            // In the third case, we're in a child lambda. 
            BoundNode boundInnerLambdaOrQuery = GetBoundLambdaOrQuery(innerLambdaOrQuery);
            return GetEnclosingBinderInLambdaOrQuery(position, node, innerLambdaOrQuery, ref boundInnerLambdaOrQuery);
        }
 
        private BoundNode GetBoundLambdaOrQuery(CSharpSyntaxNode lambdaOrQuery)
        {
            // Have we already cached a bound node for it?
            // If not, bind the outermost expression containing the lambda and then fill in the map.
            OneOrMany<BoundNode> nodes;
 
            EnsureNullabilityAnalysisPerformedIfNecessary();
 
            using (_nodeMapLock.DisposableRead())
            {
                nodes = GuardedGetBoundNodesFromMap(lambdaOrQuery);
            }
 
            if (!nodes.IsEmpty)
            {
                return GetLowerBoundNode(nodes);
            }
 
            // We probably never tried to bind an enclosing statement
            // Let's do that
            Binder lambdaRecoveryBinder;
            CSharpSyntaxNode bindingRoot = GetBindingRoot(lambdaOrQuery);
            CSharpSyntaxNode enclosingLambdaOrQuery = GetInnermostLambdaOrQuery(lambdaOrQuery, lambdaOrQuery.SpanStart, allowStarting: false);
            BoundNode boundEnclosingLambdaOrQuery = null;
            CSharpSyntaxNode nodeToBind;
 
            if (enclosingLambdaOrQuery == null)
            {
                nodeToBind = bindingRoot;
                lambdaRecoveryBinder = GetEnclosingBinderInternalWithinRoot(nodeToBind, GetAdjustedNodePosition(nodeToBind));
            }
            else
            {
                if (enclosingLambdaOrQuery == bindingRoot || !enclosingLambdaOrQuery.Contains(bindingRoot))
                {
                    Debug.Assert(bindingRoot.Contains(enclosingLambdaOrQuery));
                    nodeToBind = lambdaOrQuery;
                }
                else
                {
                    nodeToBind = bindingRoot;
                }
 
                boundEnclosingLambdaOrQuery = GetBoundLambdaOrQuery(enclosingLambdaOrQuery);
 
                using (_nodeMapLock.DisposableRead())
                {
                    nodes = GuardedGetBoundNodesFromMap(lambdaOrQuery);
                }
 
                if (!nodes.IsEmpty)
                {
                    // If everything is working as expected we should end up here because binding the enclosing lambda
                    // should also take care of binding and caching this lambda.
                    return GetLowerBoundNode(nodes);
                }
 
                lambdaRecoveryBinder = GetEnclosingBinderInLambdaOrQuery(GetAdjustedNodePosition(nodeToBind), nodeToBind, enclosingLambdaOrQuery, ref boundEnclosingLambdaOrQuery);
            }
 
            Binder incrementalBinder = new IncrementalBinder(this, lambdaRecoveryBinder);
 
            using (_nodeMapLock.DisposableWrite())
            {
                BoundNode boundOuterExpression = this.Bind(incrementalBinder, nodeToBind, BindingDiagnosticBag.Discarded);
 
                // https://github.com/dotnet/roslyn/issues/35038: Rewrite the above node and add a test that hits this path with nullable
                // enabled
 
                nodes = GuardedAddBoundTreeAndGetBoundNodeFromMap(lambdaOrQuery, boundOuterExpression);
            }
 
            if (!nodes.IsEmpty)
            {
                return GetLowerBoundNode(nodes);
            }
 
            Debug.Assert(lambdaOrQuery != nodeToBind);
 
            // If there is a bug in the binder such that we "lose" a sub-expression containing a
            // lambda, and never put bound state for it into the bound tree, then the bound lambda
            // that comes back from the map lookup will be null. This can occur in error recovery
            // situations. Let's bind the node directly.
            if (enclosingLambdaOrQuery == null)
            {
                lambdaRecoveryBinder = GetEnclosingBinderInternalWithinRoot(lambdaOrQuery, GetAdjustedNodePosition(lambdaOrQuery));
            }
            else
            {
                lambdaRecoveryBinder = GetEnclosingBinderInLambdaOrQuery(GetAdjustedNodePosition(lambdaOrQuery), lambdaOrQuery, enclosingLambdaOrQuery, ref boundEnclosingLambdaOrQuery);
            }
 
            incrementalBinder = new IncrementalBinder(this, lambdaRecoveryBinder);
 
            using (_nodeMapLock.DisposableWrite())
            {
                BoundNode boundOuterExpression = this.Bind(incrementalBinder, lambdaOrQuery, BindingDiagnosticBag.Discarded);
 
                // https://github.com/dotnet/roslyn/issues/35038: We need to do a rewrite here, and create a test that can hit this.
                if (!IsNullableAnalysisEnabled() && Compilation.IsNullableAnalysisEnabledAlways)
                {
                    AnalyzeBoundNodeNullability(boundOuterExpression, incrementalBinder, diagnostics: new DiagnosticBag(), createSnapshots: false);
                }
 
                nodes = GuardedAddBoundTreeAndGetBoundNodeFromMap(lambdaOrQuery, boundOuterExpression);
            }
 
            return GetLowerBoundNode(nodes);
        }
 
        private Binder GetEnclosingBinderInLambdaOrQuery(int position, CSharpSyntaxNode node, CSharpSyntaxNode innerLambdaOrQuery, ref BoundNode boundInnerLambdaOrQuery)
        {
            Debug.Assert(boundInnerLambdaOrQuery != null);
            Binder result;
            switch (boundInnerLambdaOrQuery.Kind)
            {
                case BoundKind.UnboundLambda:
                    boundInnerLambdaOrQuery = ((UnboundLambda)boundInnerLambdaOrQuery).BindForErrorRecovery();
                    goto case BoundKind.Lambda;
                case BoundKind.Lambda:
                    AssertPositionAdjusted(position);
                    result = GetLambdaEnclosingBinder(position, node, innerLambdaOrQuery, ((BoundLambda)boundInnerLambdaOrQuery).Binder);
                    break;
                case BoundKind.QueryClause:
                    result = GetQueryEnclosingBinder(position, node, ((BoundQueryClause)boundInnerLambdaOrQuery));
                    break;
                default:
                    return GetEnclosingBinderInternalWithinRoot(node, position); // Known to return non-null with BinderFlags.SemanticModel.
            }
 
            Debug.Assert(result != null);
            return result.WithAdditionalFlags(GetSemanticModelBinderFlags());
        }
 
        /// <remarks>
        /// Returned binder doesn't need to have <see cref="BinderFlags.SemanticModel"/> set - the caller will add it.
        /// </remarks>
        private static Binder GetQueryEnclosingBinder(int position, CSharpSyntaxNode startingNode, BoundQueryClause queryClause)
        {
            BoundExpression node = queryClause;
 
            do
            {
                switch (node.Kind)
                {
                    case BoundKind.QueryClause:
                        queryClause = (BoundQueryClause)node;
                        node = GetQueryClauseValue(queryClause);
                        continue;
                    case BoundKind.Call:
                        var call = (BoundCall)node;
                        node = GetContainingArgument(call.Arguments, position);
                        if (node != null)
                        {
                            continue;
                        }
 
                        BoundExpression receiver = call.ReceiverOpt;
 
                        // In some error scenarios, we end-up with a method group as the receiver,
                        // let's get to real receiver.
                        while (receiver?.Kind == BoundKind.MethodGroup)
                        {
                            receiver = ((BoundMethodGroup)receiver).ReceiverOpt;
                        }
 
                        if (receiver != null)
                        {
                            node = GetContainingExprOrQueryClause(receiver, position);
                            if (node != null)
                            {
                                continue;
                            }
                        }
 
                        // TODO: should we look for the "nearest" argument as a fallback?
                        node = call.Arguments.LastOrDefault();
                        continue;
                    case BoundKind.Conversion:
                        node = ((BoundConversion)node).Operand;
                        continue;
                    case BoundKind.UnboundLambda:
                        var unbound = (UnboundLambda)node;
                        return GetEnclosingBinderInternalWithinRoot(AdjustStartingNodeAccordingToNewRoot(startingNode, unbound.Syntax),
                                                  position, unbound.BindForErrorRecovery().Binder, unbound.Syntax);
                    case BoundKind.Lambda:
                        var lambda = (BoundLambda)node;
                        return GetEnclosingBinderInternalWithinRoot(AdjustStartingNodeAccordingToNewRoot(startingNode, lambda.Body.Syntax),
                                                  position, lambda.Binder, lambda.Body.Syntax);
                    default:
                        goto done;
                }
            }
            while (node != null);
 
done:
            return GetEnclosingBinderInternalWithinRoot(AdjustStartingNodeAccordingToNewRoot(startingNode, queryClause.Syntax),
                                      position, queryClause.Binder, queryClause.Syntax);
        }
 
        // Return the argument containing the position. For query
        // expressions, the span of an argument may include other
        // arguments, so the argument with the smallest span is returned.
        private static BoundExpression GetContainingArgument(ImmutableArray<BoundExpression> arguments, int position)
        {
            BoundExpression result = null;
            TextSpan resultSpan = default(TextSpan);
            foreach (var arg in arguments)
            {
                var expr = GetContainingExprOrQueryClause(arg, position);
                if (expr != null)
                {
                    var span = expr.Syntax.FullSpan;
                    if (result == null || resultSpan.Contains(span))
                    {
                        result = expr;
                        resultSpan = span;
                    }
                }
            }
            return result;
        }
 
        // Returns the expr if the syntax span contains the position;
        // returns the BoundQueryClause value if expr is a BoundQueryClause
        // and the value contains the position; otherwise returns null.
        private static BoundExpression GetContainingExprOrQueryClause(BoundExpression expr, int position)
        {
            if (expr.Kind == BoundKind.QueryClause)
            {
                var value = GetQueryClauseValue((BoundQueryClause)expr);
                if (value.Syntax.FullSpan.Contains(position))
                {
                    return value;
                }
            }
            if (expr.Syntax.FullSpan.Contains(position))
            {
                return expr;
            }
            return null;
        }
 
        private static BoundExpression GetQueryClauseValue(BoundQueryClause queryClause)
        {
            return queryClause.UnoptimizedForm ?? queryClause.Value;
        }
 
        private static SyntaxNode AdjustStartingNodeAccordingToNewRoot(SyntaxNode startingNode, SyntaxNode root)
        {
            SyntaxNode result = startingNode.Contains(root) ? root : startingNode;
            if (result != root && !root.Contains(result))
            {
                result = root;
            }
 
            return result;
        }
 
        /// <summary>
        /// Performs the same function as GetEnclosingBinder, but is known to take place within a
        /// specified lambda.  Walks up the syntax hierarchy until a node with an associated binder
        /// is found.
        /// </summary>
        /// <remarks>
        /// CONSIDER: can this share code with MemberSemanticModel.GetEnclosingBinder?
        /// 
        /// Returned binder doesn't need to have <see cref="BinderFlags.SemanticModel"/> set - the caller will add it.
        /// </remarks>
        private static Binder GetLambdaEnclosingBinder(int position, CSharpSyntaxNode startingNode, CSharpSyntaxNode containingLambda, Binder lambdaBinder)
        {
            Debug.Assert(containingLambda.IsAnonymousFunction());
            Debug.Assert(LookupPosition.IsInAnonymousFunctionOrQuery(position, containingLambda));
 
            return GetEnclosingBinderInternalWithinRoot(startingNode, position, lambdaBinder, containingLambda);
        }
 
        /// <summary>
        /// If we're doing nullable analysis, we need to fully bind this member, and then run
        /// nullable analysis on the resulting nodes before putting them in the map. Nullable
        /// analysis does not run a subset of code, so we need to fully bind the entire member
        /// first
        /// </summary>
        protected void EnsureNullabilityAnalysisPerformedIfNecessary()
        {
#if !DEBUG
            // In release mode, when the semantic model options include DisableNullableAnalysis,
            // we want to completely avoid doing any work for nullable analysis.
#pragma warning disable RSEXPERIMENTAL001 // Internal usage of experimental API
            if (NullableAnalysisIsDisabled)
#pragma warning restore RSEXPERIMENTAL001
            {
                return;
            }
#endif
 
            bool isNullableAnalysisEnabled = IsNullableAnalysisEnabled();
            // When 'isNullableAnalysisEnabled' is false but 'Compilation.IsNullableAnalysisEnabledAlways' is true here,
            // we still need to perform a nullable analysis whose results are discarded for debug verification purposes.
            if (!isNullableAnalysisEnabled && !Compilation.IsNullableAnalysisEnabledAlways)
            {
                return;
            }
 
            // If we have a snapshot manager, then we've already done
            // all the work necessary and we should avoid taking an
            // unnecessary read lock.
            if (_lazySnapshotManager is object)
            {
                return;
            }
 
            var bindableRoot = GetBindableSyntaxNode(Root);
            using var upgradeableLock = _nodeMapLock.DisposableUpgradeableRead();
 
            // If there are already nodes in the map, then we've already done work here. Since
            // EnsureNullabilityAnalysis is guaranteed to have run first, that means we've
            // already bound the root node and we can just exit. We can't just assert that Root
            // is in the map, as there are some models for which there is no BoundNode for the
            // Root elements (such as fields, where the root is a VariableDeclarator but the
            // first BoundNode corresponds to the underlying EqualsValueSyntax of the initializer)
            if (_guardedBoundNodeMap.Count > 0)
            {
                Debug.Assert(!isNullableAnalysisEnabled ||
                             _guardedBoundNodeMap.ContainsKey(bindableRoot) ||
                             _guardedBoundNodeMap.ContainsKey(bind(bindableRoot, out _).Syntax));
                return;
            }
 
            upgradeableLock.EnterWrite();
 
            NullableWalker.SnapshotManager snapshotManager;
            var remappedSymbols = _parentRemappedSymbolsOpt;
            Binder binder;
 
            BoundNode boundRoot = bind(bindableRoot, out binder);
 
            if (IsSpeculativeSemanticModel)
            {
                // Not all speculative models are created with existing snapshots. Attributes,
                // TypeSyntaxes, and MethodBodies do not depend on existing state in a member,
                // and so the SnapshotManager can be null in these cases.
                var parentSnapshotManagerOpt = ((SpeculativeSemanticModelWithMemberModel)_containingPublicSemanticModel).ParentSnapshotManagerOpt;
                if (parentSnapshotManagerOpt is null || !isNullableAnalysisEnabled)
                {
                    rewriteAndCache();
                    return;
                }
 
                boundRoot = NullableWalker.AnalyzeAndRewriteSpeculation(_containingPublicSemanticModel.OriginalPositionForSpeculation, boundRoot, binder, parentSnapshotManagerOpt, out var newSnapshots, ref remappedSymbols);
                GuardedAddBoundTreeForStandaloneSyntax(bindableRoot, boundRoot, newSnapshots, remappedSymbols);
            }
            else
            {
                rewriteAndCache();
            }
 
            BoundNode bind(CSharpSyntaxNode root, out Binder binder)
            {
                binder = GetBinderToBindNode(root);
                return Bind(binder, root, BindingDiagnosticBag.Discarded);
            }
 
            void rewriteAndCache()
            {
                var diagnostics = DiagnosticBag.GetInstance();
#if DEBUG
                if (!isNullableAnalysisEnabled)
                {
                    Debug.Assert(Compilation.IsNullableAnalysisEnabledAlways);
                    AnalyzeBoundNodeNullability(boundRoot, binder, diagnostics, createSnapshots: true);
                    diagnostics.Free();
                    return;
                }
#endif
 
                boundRoot = RewriteNullableBoundNodesWithSnapshots(boundRoot, binder, diagnostics, createSnapshots: true, out snapshotManager, ref remappedSymbols);
                diagnostics.Free();
                GuardedAddBoundTreeForStandaloneSyntax(bindableRoot, boundRoot, snapshotManager, remappedSymbols);
            }
        }
 
        private Binder GetBinderToBindNode(CSharpSyntaxNode nodeToBind)
        {
            Binder binder;
            if (nodeToBind is CompilationUnitSyntax)
            {
                // Top level statements are unique among our nodes: if there are no syntax nodes before local functions,
                // then that means the start of the span of the top-level statement is the same as the start of the local
                // function. Therefore, GetEnclosingBinder can't tell the difference, and it will get the binder for the
                // local function, not for the CompilationUnitSyntax. This is desirable in almost all cases but this one:
                // There are no locals or invocations before this, meaning there's nothing to call GetDeclaredSymbol,
                // GetTypeInfo, or GetSymbolInfo on. GetDeclaredSymbol(CompilationUnitSyntax) goes down another path that
                // does not need to do any binding whatsoever, so it also doesn't care about this behavior. The only place
                // that actually needs to get the enclosing binding for a CompilationUnitSyntax in such a scenario is this
                // method. So, if our root is the CompilationUnitSyntax, directly get the binder for it.
                binder = RootBinder.GetBinder(nodeToBind);
                Debug.Assert(binder is SimpleProgramBinder);
            }
            else
            {
                binder = GetEnclosingBinder(GetAdjustedNodePosition(nodeToBind));
            }
 
            return binder;
        }
 
#nullable enable
        /// <summary>
        /// Rewrites the given bound node with nullability information, and returns snapshots for later speculative analysis at positions inside this member.
        /// </summary>
        protected abstract BoundNode RewriteNullableBoundNodesWithSnapshots(
            BoundNode boundRoot,
            Binder binder,
            DiagnosticBag diagnostics,
            bool createSnapshots,
            out NullableWalker.SnapshotManager? snapshotManager,
            ref ImmutableDictionary<Symbol, Symbol>? remappedSymbols);
 
        /// <summary>
        /// Performs the analysis step of getting nullability information for a semantic model but
        /// does not actually use the results. This gives us extra verification of nullable flow analysis.
        /// It is only used in contexts where nullable analysis is disabled in the compilation but requested
        /// through "run-nullable-analysis=always" or when the compiler is running in DEBUG.
        /// </summary>
        protected abstract void AnalyzeBoundNodeNullability(BoundNode boundRoot, Binder binder, DiagnosticBag diagnostics, bool createSnapshots);
 
        protected abstract bool IsNullableAnalysisEnabledCore();
 
        protected bool IsNullableAnalysisEnabled()
#pragma warning disable RSEXPERIMENTAL001 // internal use of experimental API
            => !NullableAnalysisIsDisabled && IsNullableAnalysisEnabledCore();
#pragma warning restore RSEXPERIMENTAL001
#nullable disable
 
        /// <summary>
        /// Get all bounds nodes associated with a node, ordered from highest to lowest in the bound tree.
        /// Strictly speaking, the order is that of a pre-order traversal of the bound tree.
        /// </summary>
        internal OneOrMany<BoundNode> GetBoundNodes(CSharpSyntaxNode node)
        {
            // If this method is called with a null parameter, that implies that the Root should be
            // bound, but make sure that the Root is bindable.
            if (node == null)
            {
                node = GetBindableSyntaxNode(Root);
            }
            Debug.Assert(node == GetBindableSyntaxNode(node));
 
            // Note: This nullability analysis can be distracting when debugging. This can be disabled with a feature flag in parse options:
            //       `.WithFeature("run-nullable-analysis", "never")` 
            EnsureNullabilityAnalysisPerformedIfNecessary();
 
            // We have one SemanticModel for each method.
            //
            // The SemanticModel contains a lazily-built immutable map from scope-introducing 
            // syntactic statements (such as blocks) to binders, but not from lambdas to binders.
            //
            // The SemanticModel also contains a mutable map from syntax to bound nodes; that is 
            // declared here. Since the map is not thread-safe we ensure that it is guarded with a
            // reader-writer lock.
            //
            // Have we already got the desired bound node in the mutable map? If so, return it.
            OneOrMany<BoundNode> results;
 
            using (_nodeMapLock.DisposableRead())
            {
                results = GuardedGetBoundNodesFromMap(node);
            }
 
            if (!results.IsEmpty)
            {
                return results;
            }
 
            // We might not actually have been given an expression or statement even though we were
            // allegedly given something that is "bindable".
 
            // If we didn't find in the cached bound nodes, find a binding root and bind it.
            // This will cache bound nodes under the binding root.
            CSharpSyntaxNode nodeToBind = GetBindingRoot(node);
            var statementBinder = GetBinderToBindNode(nodeToBind);
            Binder incrementalBinder = new IncrementalBinder(this, statementBinder);
 
            using (_nodeMapLock.DisposableWrite())
            {
                BoundNode boundStatement = this.Bind(incrementalBinder, nodeToBind, BindingDiagnosticBag.Discarded);
                results = GuardedAddBoundTreeAndGetBoundNodeFromMap(node, boundStatement);
            }
 
            if (!results.IsEmpty)
            {
                return results;
            }
 
            // If we still didn't find it, its still possible we could bind it directly.
            // For example, types are usually not represented by bound nodes, and some error conditions and
            // not yet implemented features do not create bound nodes for everything underneath them.
            //
            // In this case, however, we only add the single bound node we found to the map, not any child bound nodes,
            // to avoid duplicates in the map if a parent of this node comes through this code path also.
 
            var binder = GetBinderToBindNode(node);
            incrementalBinder = new IncrementalBinder(this, binder);
 
            using (_nodeMapLock.DisposableRead())
            {
                results = GuardedGetBoundNodesFromMap(node);
            }
 
            if (results.IsEmpty)
            {
                // https://github.com/dotnet/roslyn/issues/35038: We have to run analysis on this node in some manner
                using (_nodeMapLock.DisposableWrite())
                {
                    var boundNode = this.Bind(incrementalBinder, node, BindingDiagnosticBag.Discarded);
                    GuardedAddBoundTreeForStandaloneSyntax(node, boundNode);
                    results = GuardedGetBoundNodesFromMap(node);
                }
 
                if (!results.IsEmpty)
                {
                    return results;
                }
            }
            else
            {
                return results;
            }
 
            return OneOrMany<BoundNode>.Empty;
        }
 
        // some nodes don't have direct semantic meaning by themselves and so we need to bind a different node that does
        protected internal virtual CSharpSyntaxNode GetBindableSyntaxNode(CSharpSyntaxNode node)
        {
            switch (node.Kind())
            {
                case SyntaxKind.GetAccessorDeclaration:
                case SyntaxKind.SetAccessorDeclaration:
                case SyntaxKind.InitAccessorDeclaration:
                case SyntaxKind.AddAccessorDeclaration:
                case SyntaxKind.RemoveAccessorDeclaration:
                case SyntaxKind.MethodDeclaration:
                case SyntaxKind.ConstructorDeclaration:
                case SyntaxKind.DestructorDeclaration:
                case SyntaxKind.OperatorDeclaration:
                case SyntaxKind.ConversionOperatorDeclaration:
                case SyntaxKind.GlobalStatement:
                case SyntaxKind.Subpattern:
                    return node;
                case SyntaxKind.PositionalPatternClause:
                    return node.Parent;
            }
 
            while (true)
            {
                switch (node)
                {
                    case ParenthesizedExpressionSyntax n:
                        node = n.Expression;
                        continue;
 
                    case CheckedExpressionSyntax n:
                        node = n.Expression;
                        continue;
 
                    // Simple mitigation to give a result for suppressions. Public API tracked by https://github.com/dotnet/roslyn/issues/26198
                    case PostfixUnaryExpressionSyntax { RawKind: (int)SyntaxKind.SuppressNullableWarningExpression } n:
                        node = n.Operand;
                        continue;
 
                    case UnsafeStatementSyntax n:
                        node = n.Block;
                        continue;
 
                    case CheckedStatementSyntax n:
                        node = n.Block;
                        continue;
                }
 
                break;
            }
 
            var parent = node.Parent;
            if (parent != null && node != this.Root)
            {
                switch (node.Kind())
                {
                    case SyntaxKind.IdentifierName:
                    case SyntaxKind.GenericName:
                        var tmp = SyntaxFactory.GetStandaloneNode(node);
                        if (tmp != node)
                        {
                            return GetBindableSyntaxNode(tmp);
                        }
 
                        break;
 
                    case SyntaxKind.AnonymousObjectMemberDeclarator:
                        return GetBindableSyntaxNode(parent);
 
                    case SyntaxKind.VariableDeclarator: // declarators are mapped in SyntaxBinder
 
                        // When a local variable declaration contains a single declarator, the bound node
                        // is associated with the declaration, rather than with the declarator.  If we
                        // used the declarator here, we would have enough context to bind it, but we wouldn't
                        // end up with an entry in the syntax-to-bound node map.
                        Debug.Assert(parent.Kind() == SyntaxKind.VariableDeclaration);
                        var grandparent = parent.Parent;
                        if (grandparent != null && grandparent.Kind() == SyntaxKind.LocalDeclarationStatement &&
                            ((VariableDeclarationSyntax)parent).Variables.Count == 1)
                        {
                            return GetBindableSyntaxNode(parent);
                        }
                        break;
 
                    default:
                        if (node is QueryExpressionSyntax && parent is QueryContinuationSyntax ||
                            !(node is ExpressionSyntax) &&
                            !(node is StatementSyntax) &&
                            !(node is SelectOrGroupClauseSyntax) &&
                            !(node is QueryClauseSyntax) &&
                            !(node is OrderingSyntax) &&
                            !(node is JoinIntoClauseSyntax) &&
                            !(node is QueryContinuationSyntax) &&
                            !(node is ConstructorInitializerSyntax) &&
                            !(node is PrimaryConstructorBaseTypeSyntax) &&
                            !(node is ArrowExpressionClauseSyntax) &&
                            !(node is PatternSyntax))
                        {
                            return GetBindableSyntaxNode(parent);
                        }
 
                        break;
                }
            }
 
            return node;
        }
 
#nullable enable
        /// <summary>
        /// If the node is an expression, return the nearest parent node
        /// with semantic meaning. Otherwise return null.
        /// </summary>
        protected CSharpSyntaxNode? GetBindableParentNode(CSharpSyntaxNode node)
        {
            if (!(node is ExpressionSyntax))
            {
                return null;
            }
 
            // The node is an expression, but its parent is null
            CSharpSyntaxNode? parent = node.Parent;
            if (parent == null)
            {
                // For speculative model, expression might be the root of the syntax tree, in which case it can have a null parent.
                if (this.IsSpeculativeSemanticModel && this.Root == node)
                {
                    return null;
                }
 
                throw new ArgumentException($"The parent of {nameof(node)} must not be null unless this is a speculative semantic model.", nameof(node));
            }
 
            // skip up past parens and ref expressions, as we have no bound nodes for them.
            while (true)
            {
                switch (parent.Kind())
                {
                    case SyntaxKind.ParenthesizedExpression:
                    case SyntaxKind.RefExpression:
                    case SyntaxKind.RefType:
                    case SyntaxKind.ScopedType:
                        var pp = parent.Parent;
                        if (pp == null) break;
                        parent = pp;
                        break;
                    default:
                        goto foundParent;
                }
            }
foundParent:;
 
            var bindableParent = this.GetBindableSyntaxNode(parent);
            Debug.Assert(bindableParent != null);
 
            // If the parent is a member used for a method invocation, then
            // the node is the instance associated with the method invocation.
            // In that case, return the invocation expression so that any conversion
            // of the receiver can be included in the resulting SemanticInfo.
            switch (bindableParent)
            {
                case { RawKind: (int)SyntaxKind.SimpleMemberAccessExpression, Parent.RawKind: (int)SyntaxKind.InvocationExpression }:
                    bindableParent = bindableParent.Parent;
                    break;
                case ArrayTypeSyntax arrayType:
                    bindableParent = SyntaxFactory.GetStandaloneExpression(arrayType);
                    break;
                case { RawKind: (int)SyntaxKind.ComplexElementInitializerExpression }:
                    // The { "a", "b" } node in a collection initializer is marked as compiler-generated, and will never
                    // end up in the bound node map. We don't need the parent node for any calculations here, so just return
                    // null to avoid rebinding the initializer (which can cause exponential binding problems, see EndToEndTests.LongInitializerList).
                    bindableParent = null;
                    break;
            }
 
            return bindableParent;
        }
#nullable disable
 
        internal override Symbol RemapSymbolIfNecessaryCore(Symbol symbol)
        {
            Debug.Assert(symbol is LocalSymbol or ParameterSymbol or MethodSymbol { MethodKind: MethodKind.LambdaMethod });
 
            EnsureNullabilityAnalysisPerformedIfNecessary();
 
            if (_lazyRemappedSymbols is null)
            {
                return symbol;
            }
 
            if (_lazyRemappedSymbols.TryGetValue(symbol, out var remappedSymbol))
            {
                return remappedSymbol;
            }
            else
            {
                return symbol;
            }
        }
 
        internal sealed override Func<SyntaxNode, bool> GetSyntaxNodesToAnalyzeFilter(SyntaxNode declaredNode, ISymbol declaredSymbol)
        {
            throw ExceptionUtilities.Unreachable();
        }
 
        internal sealed override bool ShouldSkipSyntaxNodeAnalysis(SyntaxNode node, ISymbol containingSymbol)
        {
            throw ExceptionUtilities.Unreachable();
        }
 
        /// <summary>
        /// The incremental binder is used when binding statements. Whenever a statement
        /// is bound, it checks the bound node cache to see if that statement was bound, 
        /// and returns it instead of rebinding it. 
        /// 
        /// For example, we might have:
        ///    while (x > goo())
        ///    {
        ///      y = y * x;
        ///      z = z + y;
        ///    }
        /// 
        /// We might first get semantic info about "z", and thus bind just the statement
        /// "z = z + y". Later, we might bind the entire While block. While binding the while
        /// block, we can reuse the binding we did of "z = z + y".
        /// </summary>
        /// <remarks>
        /// NOTE: any member overridden by this binder should follow the BuckStopsHereBinder pattern.
        /// Otherwise, a subsequent binder in the chain could suppress the caching behavior.
        /// </remarks>
        internal class IncrementalBinder : Binder
        {
            private readonly MemberSemanticModel _semanticModel;
 
            internal IncrementalBinder(MemberSemanticModel semanticModel, Binder next)
                : base(next)
            {
                _semanticModel = semanticModel;
            }
 
            /// <summary>
            /// We override GetBinder so that the BindStatement override is still
            /// in effect on nested binders.
            /// </summary>
            internal override Binder GetBinder(SyntaxNode node)
            {
                Binder binder = this.Next.GetBinder(node);
 
                if (binder != null)
                {
                    Debug.Assert(!(binder is IncrementalBinder));
                    return new IncrementalBinder(_semanticModel, binder.WithAdditionalFlags(BinderFlags.SemanticModel));
                }
 
                return null;
            }
 
            public override BoundStatement BindStatement(StatementSyntax node, BindingDiagnosticBag diagnostics)
            {
                // Check the bound node cache to see if the statement was already bound.
                if (node.SyntaxTree == _semanticModel.SyntaxTree)
                {
                    BoundStatement synthesizedStatement = _semanticModel.GuardedGetSynthesizedStatementFromMap(node);
 
                    if (synthesizedStatement != null)
                    {
                        return synthesizedStatement;
                    }
 
                    BoundNode boundNode = TryGetBoundNodeFromMap(node);
 
                    if (boundNode != null)
                    {
                        return (BoundStatement)boundNode;
                    }
                }
 
                BoundStatement statement = base.BindStatement(node, diagnostics);
 
                // Synthesized statements are not added to the _guardedNodeMap, we cache them explicitly here in  
                // _lazyGuardedSynthesizedStatementsMap
                if (statement.WasCompilerGenerated && node.SyntaxTree == _semanticModel.SyntaxTree)
                {
                    _semanticModel.GuardedAddSynthesizedStatementToMap(node, statement);
                }
 
                return statement;
            }
 
            internal override BoundBlock BindEmbeddedBlock(BlockSyntax node, BindingDiagnosticBag diagnostics)
            {
                BoundBlock block = (BoundBlock)TryGetBoundNodeFromMap(node);
 
                if (block is object)
                {
                    return block;
                }
 
                block = base.BindEmbeddedBlock(node, diagnostics);
 
                Debug.Assert(!block.WasCompilerGenerated);
                return block;
            }
 
            private BoundNode TryGetBoundNodeFromMap(CSharpSyntaxNode node)
            {
                if (node.SyntaxTree == _semanticModel.SyntaxTree)
                {
                    OneOrMany<BoundNode> boundNodes = _semanticModel.GuardedGetBoundNodesFromMap(node);
 
                    if (!boundNodes.IsEmpty)
                    {
                        // Already bound. Return the top-most bound node associated with the statement. 
                        return boundNodes[0];
                    }
                }
 
                return null;
            }
 
            public override BoundNode BindMethodBody(CSharpSyntaxNode node, BindingDiagnosticBag diagnostics)
            {
                BoundNode boundNode = TryGetBoundNodeFromMap(node);
 
                if (boundNode is object)
                {
                    return boundNode;
                }
 
                boundNode = base.BindMethodBody(node, diagnostics);
 
                return boundNode;
            }
 
            internal override BoundExpressionStatement BindConstructorInitializer(ConstructorInitializerSyntax node, BindingDiagnosticBag diagnostics)
            {
                return (BoundExpressionStatement)TryGetBoundNodeFromMap(node) ?? base.BindConstructorInitializer(node, diagnostics);
            }
 
            internal override BoundExpressionStatement BindConstructorInitializer(PrimaryConstructorBaseTypeSyntax node, BindingDiagnosticBag diagnostics)
            {
                return (BoundExpressionStatement)TryGetBoundNodeFromMap(node) ?? base.BindConstructorInitializer(node, diagnostics);
            }
 
            internal override BoundBlock BindExpressionBodyAsBlock(ArrowExpressionClauseSyntax node, BindingDiagnosticBag diagnostics)
            {
                BoundBlock block = (BoundBlock)TryGetBoundNodeFromMap(node);
 
                if (block is object)
                {
                    return block;
                }
 
                block = base.BindExpressionBodyAsBlock(node, diagnostics);
 
                return block;
            }
        }
 
        internal sealed class MemberSemanticBindingCounter
        {
            internal int BindCount;
        }
    }
}