File: Compilation\CSharpSemanticModel.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.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    /// <summary>
    /// Allows asking semantic questions about a tree of syntax nodes in a Compilation. Typically,
    /// an instance is obtained by a call to <see cref="Compilation"/>.<see
    /// cref="Compilation.GetSemanticModel(SyntaxTree, bool)"/>. 
    /// </summary>
    /// <remarks>
    /// <para>An instance of <see cref="CSharpSemanticModel"/> caches local symbols and semantic
    /// information. Thus, it is much more efficient to use a single instance of <see
    /// cref="CSharpSemanticModel"/> when asking multiple questions about a syntax tree, because
    /// information from the first question may be reused. This also means that holding onto an
    /// instance of SemanticModel for a long time may keep a significant amount of memory from being
    /// garbage collected.
    /// </para>
    /// <para>
    /// When an answer is a named symbol that is reachable by traversing from the root of the symbol
    /// table, (that is, from an <see cref="AssemblySymbol"/> of the <see cref="Compilation"/>),
    /// that symbol will be returned (i.e. the returned value will be reference-equal to one
    /// reachable from the root of the symbol table). Symbols representing entities without names
    /// (e.g. array-of-int) may or may not exhibit reference equality. However, some named symbols
    /// (such as local variables) are not reachable from the root. These symbols are visible as
    /// answers to semantic questions. When the same SemanticModel object is used, the answers
    /// exhibit reference-equality.  
    /// </para>
    /// </remarks>
    internal abstract class CSharpSemanticModel : SemanticModel
    {
        /// <summary>
        /// The compilation this object was obtained from.
        /// </summary>
        public new abstract CSharpCompilation Compilation { get; }
 
        /// <summary>
        /// The root node of the syntax tree that this binding is based on.
        /// </summary>
        internal new abstract CSharpSyntaxNode Root { get; }
 
        // Is this node one that could be successfully interrogated by GetSymbolInfo/GetTypeInfo/GetMemberGroup/GetConstantValue?
        // WARN: If isSpeculative is true, then don't look at .Parent - there might not be one.
        internal static bool CanGetSemanticInfo(CSharpSyntaxNode node, bool allowNamedArgumentName = false, bool isSpeculative = false)
        {
            Debug.Assert(node != null);
 
            if (!isSpeculative && IsInStructuredTriviaOtherThanCrefOrNameAttribute(node))
            {
                return false;
            }
 
            switch (node.Kind())
            {
                case SyntaxKind.CollectionInitializerExpression:
                case SyntaxKind.ObjectInitializerExpression:
                    //  new CollectionClass() { 1, 2, 3 }
                    //                        ~~~~~~~~~~~
                    //  OR
                    //
                    //  new ObjectClass() { field = 1, prop = 2 }
                    //                    ~~~~~~~~~~~~~~~~~~~~~~~
                    // CollectionInitializerExpression and ObjectInitializerExpression are not really expressions in the language sense.
                    // We do not allow getting the semantic info for these syntax nodes. However, we do allow getting semantic info
                    // for each of the individual initializer elements or member assignments.
                    return false;
 
                case SyntaxKind.ComplexElementInitializerExpression:
                    //  new Collection { 1, {2, 3} }
                    //                      ~~~~~~
                    // ComplexElementInitializerExpression are also not true expressions in the language sense, so we disallow getting the
                    // semantic info for it. However, we may be interested in getting the semantic info for the compiler generated Add
                    // method invoked with initializer expressions as arguments. Roslyn bug 11987 tracks this work item.
                    return false;
 
                case SyntaxKind.IdentifierName:
                    // The alias of a using directive is a declaration, so there is no semantic info - use GetDeclaredSymbol instead.
                    if (!isSpeculative && node.Parent != null && node.Parent.Kind() == SyntaxKind.NameEquals && node.Parent.Parent.Kind() == SyntaxKind.UsingDirective)
                    {
                        return false;
                    }
 
                    goto default;
 
                case SyntaxKind.OmittedTypeArgument:
                case SyntaxKind.RefExpression:
                case SyntaxKind.RefType:
                case SyntaxKind.ScopedType:
                    // These are just placeholders and are not separately meaningful.
                    return false;
 
                default:
                    // If we are being asked for binding info on a "missing" syntax node
                    // then there's no point in doing any work at all. For example, the user might
                    // have something like "class C { [] void M() {} }". The caller might obtain 
                    // the attribute declaration syntax and then attempt to ask for type information
                    // about the contents of the attribute. But the parser has recovered from the 
                    // missing attribute type and filled in a "missing" node in its place. There's
                    // nothing we can do with that, so let's not allow it.
                    if (node.IsMissing)
                    {
                        return false;
                    }
 
                    return
                        (node is ExpressionSyntax && (isSpeculative || allowNamedArgumentName || !SyntaxFacts.IsNamedArgumentName(node))) ||
                        (node is ConstructorInitializerSyntax) ||
                        (node is PrimaryConstructorBaseTypeSyntax) ||
                        (node is AttributeSyntax) ||
                        (node is CrefSyntax);
            }
        }
 
        #region Abstract worker methods
 
        /// <summary>
        /// Gets symbol information about a syntax node. This is overridden by various specializations of SemanticModel.
        /// It can assume that CheckSyntaxNode and CanGetSemanticInfo have already been called, as well as that named
        /// argument nodes have been handled.
        /// </summary>
        /// <param name="node">The syntax node to get semantic information for.</param>
        /// <param name="options">Options to control behavior.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        internal abstract SymbolInfo GetSymbolInfoWorker(CSharpSyntaxNode node, SymbolInfoOptions options, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Gets symbol information about the 'Add' method corresponding to an expression syntax <paramref name="node"/> within collection initializer.
        /// This is the worker function that is overridden in various derived kinds of Semantic Models. It can assume that 
        /// CheckSyntaxNode has already been called and the <paramref name="node"/> is in the right place in the syntax tree.
        /// </summary>
        internal abstract SymbolInfo GetCollectionInitializerSymbolInfoWorker(InitializerExpressionSyntax collectionInitializer, ExpressionSyntax node, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Gets type information about a syntax node. This is overridden by various specializations of SemanticModel.
        /// It can assume that CheckSyntaxNode and CanGetSemanticInfo have already been called, as well as that named
        /// argument nodes have been handled.
        /// </summary>
        /// <param name="node">The syntax node to get semantic information for.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        internal abstract CSharpTypeInfo GetTypeInfoWorker(CSharpSyntaxNode node, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Binds the provided expression in the given context.
        /// </summary>
        /// <param name="position">The position to bind at.</param>
        /// <param name="expression">The expression to bind</param>
        /// <param name="bindingOption">How to speculatively bind the given expression. If this is <see cref="SpeculativeBindingOption.BindAsTypeOrNamespace"/>
        /// then the provided expression should be a <see cref="TypeSyntax"/>.</param>
        /// <param name="binder">The binder that was used to bind the given syntax.</param>
        /// <param name="crefSymbols">The symbols used in a cref. If this is not default, then the return is null.</param>
        /// <returns>The expression that was bound. If <paramref name="crefSymbols"/> is not default, this is null.</returns>
        internal abstract BoundExpression GetSpeculativelyBoundExpression(int position, ExpressionSyntax expression, SpeculativeBindingOption bindingOption, out Binder binder, out ImmutableArray<Symbol> crefSymbols);
 
        /// <summary>
        /// Gets a list of method or indexed property symbols for a syntax node. This is overridden by various specializations of SemanticModel.
        /// It can assume that CheckSyntaxNode and CanGetSemanticInfo have already been called, as well as that named
        /// argument nodes have been handled.
        /// </summary>
        /// <param name="node">The syntax node to get semantic information for.</param>
        /// <param name="options"></param>
        /// <param name="cancellationToken">The cancellation token.</param>
        internal abstract ImmutableArray<Symbol> GetMemberGroupWorker(CSharpSyntaxNode node, SymbolInfoOptions options, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Gets a list of indexer symbols for a syntax node. This is overridden by various specializations of SemanticModel.
        /// It can assume that CheckSyntaxNode and CanGetSemanticInfo have already been called, as well as that named
        /// argument nodes have been handled.
        /// </summary>
        /// <param name="node">The syntax node to get semantic information for.</param>
        /// <param name="options"></param>
        /// <param name="cancellationToken">The cancellation token.</param>
        internal abstract ImmutableArray<IPropertySymbol> GetIndexerGroupWorker(CSharpSyntaxNode node, SymbolInfoOptions options, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Gets the constant value for a syntax node. This is overridden by various specializations of SemanticModel.
        /// It can assume that CheckSyntaxNode and CanGetSemanticInfo have already been called, as well as that named
        /// argument nodes have been handled.
        /// </summary>
        /// <param name="node">The syntax node to get semantic information for.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        internal abstract Optional<object> GetConstantValueWorker(CSharpSyntaxNode node, CancellationToken cancellationToken = default(CancellationToken));
 
        #endregion Abstract worker methods
 
        #region Helpers for speculative binding
 
        internal Binder GetSpeculativeBinder(int position, ExpressionSyntax expression, SpeculativeBindingOption bindingOption)
        {
            Debug.Assert(expression != null);
 
            position = CheckAndAdjustPosition(position);
 
            if (bindingOption == SpeculativeBindingOption.BindAsTypeOrNamespace)
            {
                if (!(expression is TypeSyntax))
                {
                    return null;
                }
            }
 
            Binder binder = this.GetEnclosingBinder(position);
            if (binder == null)
            {
                return null;
            }
 
            if (bindingOption == SpeculativeBindingOption.BindAsTypeOrNamespace && IsInTypeofExpression(position))
            {
                // If position is within a typeof expression, GetEnclosingBinder may return a
                // TypeofBinder.  However, this TypeofBinder will have been constructed with the
                // actual syntax of the typeof argument and we want to use the given syntax.
                // Wrap the binder in another TypeofBinder to overrule its description of where
                // unbound generic types are allowed.
                //Debug.Assert(binder is TypeofBinder); // Expectation, not requirement.
                binder = new TypeofBinder(expression, binder);
            }
 
            binder = new WithNullableContextBinder(SyntaxTree, position, binder);
 
            return new ExecutableCodeBinder(expression, binder.ContainingMemberOrLambda, binder).GetBinder(expression);
        }
 
        private Binder GetSpeculativeBinderForAttribute(int position, AttributeSyntax attribute)
        {
            position = CheckAndAdjustPositionForSpeculativeAttribute(position);
 
            var binder = this.GetEnclosingBinder(position);
            if (binder == null)
            {
                return null;
            }
 
            return new ExecutableCodeBinder(attribute, binder.ContainingMemberOrLambda, binder).GetBinder(attribute);
        }
 
        private static BoundExpression GetSpeculativelyBoundExpressionHelper(Binder binder, ExpressionSyntax expression, SpeculativeBindingOption bindingOption)
        {
            Debug.Assert(binder != null);
            Debug.Assert(binder.IsSemanticModelBinder);
            Debug.Assert(expression != null);
            Debug.Assert(bindingOption != SpeculativeBindingOption.BindAsTypeOrNamespace || expression is TypeSyntax);
 
            BoundExpression boundNode;
            if (bindingOption == SpeculativeBindingOption.BindAsTypeOrNamespace || binder.Flags.Includes(BinderFlags.CrefParameterOrReturnType))
            {
                boundNode = binder.BindNamespaceOrType(expression, BindingDiagnosticBag.Discarded);
            }
            else
            {
                Debug.Assert(bindingOption == SpeculativeBindingOption.BindAsExpression);
                boundNode = binder.BindExpression(expression, BindingDiagnosticBag.Discarded);
            }
 
            return boundNode;
        }
 
        /// <summary>
        /// Bind the given expression speculatively at the given position, and return back
        /// the resulting bound node. May return null in some error cases.
        /// </summary>
        /// <remarks>
        /// Keep in sync with Binder.BindCrefParameterOrReturnType.
        /// </remarks>
        protected BoundExpression GetSpeculativelyBoundExpressionWithoutNullability(int position, ExpressionSyntax expression, SpeculativeBindingOption bindingOption, out Binder binder, out ImmutableArray<Symbol> crefSymbols)
        {
            if (expression == null)
            {
                throw new ArgumentNullException(nameof(expression));
            }
 
            crefSymbols = default(ImmutableArray<Symbol>);
 
            expression = SyntaxFactory.GetStandaloneExpression(expression);
 
            binder = this.GetSpeculativeBinder(position, expression, bindingOption);
            if (binder == null)
            {
                return null;
            }
 
            if (binder.Flags.Includes(BinderFlags.CrefParameterOrReturnType))
            {
                crefSymbols = ImmutableArray.Create<Symbol>(binder.BindType(expression, BindingDiagnosticBag.Discarded).Type);
                return null;
            }
            else if (binder.InCref)
            {
                if (expression.IsKind(SyntaxKind.QualifiedName))
                {
                    var qualified = (QualifiedNameSyntax)expression;
                    var crefWrapper = SyntaxFactory.QualifiedCref(qualified.Left, SyntaxFactory.NameMemberCref(qualified.Right));
                    crefSymbols = BindCref(crefWrapper, binder);
                }
                else if (expression is TypeSyntax typeSyntax)
                {
                    var crefWrapper = typeSyntax is PredefinedTypeSyntax ?
                        (CrefSyntax)SyntaxFactory.TypeCref(typeSyntax) :
                        SyntaxFactory.NameMemberCref(typeSyntax);
                    crefSymbols = BindCref(crefWrapper, binder);
                }
 
                return null;
            }
 
            var boundNode = GetSpeculativelyBoundExpressionHelper(binder, expression, bindingOption);
            return boundNode;
        }
 
        internal static ImmutableArray<Symbol> BindCref(CrefSyntax crefSyntax, Binder binder)
        {
            Symbol unusedAmbiguityWinner;
            var symbols = binder.BindCref(crefSyntax, out unusedAmbiguityWinner, BindingDiagnosticBag.Discarded);
            return symbols;
        }
 
        internal SymbolInfo GetCrefSymbolInfo(int position, CrefSyntax crefSyntax, SymbolInfoOptions options, bool hasParameterList)
        {
            var binder = this.GetEnclosingBinder(position);
            if (binder?.InCref == true)
            {
                ImmutableArray<Symbol> symbols = BindCref(crefSyntax, binder);
                return GetCrefSymbolInfo(OneOrMany.Create(symbols), options, hasParameterList);
            }
 
            return SymbolInfo.None;
        }
 
        internal static bool HasParameterList(CrefSyntax crefSyntax)
        {
            while (crefSyntax.Kind() == SyntaxKind.QualifiedCref)
            {
                crefSyntax = ((QualifiedCrefSyntax)crefSyntax).Member;
            }
 
            switch (crefSyntax.Kind())
            {
                case SyntaxKind.NameMemberCref:
                    return ((NameMemberCrefSyntax)crefSyntax).Parameters != null;
                case SyntaxKind.IndexerMemberCref:
                    return ((IndexerMemberCrefSyntax)crefSyntax).Parameters != null;
                case SyntaxKind.OperatorMemberCref:
                    return ((OperatorMemberCrefSyntax)crefSyntax).Parameters != null;
                case SyntaxKind.ConversionOperatorMemberCref:
                    return ((ConversionOperatorMemberCrefSyntax)crefSyntax).Parameters != null;
            }
 
            return false;
        }
 
        private static SymbolInfo GetCrefSymbolInfo(OneOrMany<Symbol> symbols, SymbolInfoOptions options, bool hasParameterList)
        {
            switch (symbols.Count)
            {
                case 0:
                    return SymbolInfo.None;
                case 1:
                    // Might have to expand an ExtendedErrorTypeSymbol into multiple candidates.
                    return GetSymbolInfoForSymbol(symbols[0], options);
                default:
                    if ((options & SymbolInfoOptions.ResolveAliases) == SymbolInfoOptions.ResolveAliases)
                    {
                        symbols = UnwrapAliases(symbols);
                    }
 
                    LookupResultKind resultKind = LookupResultKind.Ambiguous;
 
                    // The boundary between Ambiguous and OverloadResolutionFailure is let clear-cut for crefs.
                    // We'll say that overload resolution failed if the syntax has a parameter list and if
                    // all of the candidates have the same kind.
                    SymbolKind firstCandidateKind = symbols[0].Kind;
                    if (hasParameterList && symbols.All(s => s.Kind == firstCandidateKind))
                    {
                        resultKind = LookupResultKind.OverloadResolutionFailure;
                    }
 
                    return SymbolInfoFactory.Create(symbols, resultKind, isDynamic: false);
            }
        }
 
        /// <summary>
        /// Bind the given attribute speculatively at the given position, and return back
        /// the resulting bound node. May return null in some error cases.
        /// </summary>
        private BoundAttribute GetSpeculativelyBoundAttribute(int position, AttributeSyntax attribute, out Binder binder)
        {
            if (attribute == null)
            {
                throw new ArgumentNullException(nameof(attribute));
            }
 
            binder = this.GetSpeculativeBinderForAttribute(position, attribute);
            if (binder == null)
            {
                return null;
            }
 
            AliasSymbol aliasOpt; // not needed.
            NamedTypeSymbol attributeType = (NamedTypeSymbol)binder.BindType(attribute.Name, BindingDiagnosticBag.Discarded, out aliasOpt).Type;
            // note: we don't need to pass an 'attributedMember' here because we only need symbolInfo from this node
            var boundNode = new ExecutableCodeBinder(attribute, binder.ContainingMemberOrLambda, binder).BindAttribute(attribute, attributeType, attributedMember: null, BindingDiagnosticBag.Discarded);
 
            return boundNode;
        }
 
        // When speculatively binding an attribute, we have to use the name lookup rules for an attribute,
        // even if the position isn't within an attribute. For example:
        //   class C {
        //      class DAttribute: Attribute {}
        //   }
        //
        // If we speculatively bind the attribute "D" with position at the beginning of "class C", it should
        // bind to DAttribute. 
        //
        // But GetBinderForPosition won't do that; it only handles the case where position is inside an attribute.
        // This function adds a special case: if the position (after first adjustment) is at the exact beginning
        // of a type or method, the position is adjusted so the right binder is chosen to get the right things
        // in scope.
        private int CheckAndAdjustPositionForSpeculativeAttribute(int position)
        {
            position = CheckAndAdjustPosition(position);
 
            SyntaxToken token = Root.FindToken(position);
            if (position == 0 && position != token.SpanStart)
                return position;
 
            CSharpSyntaxNode node = (CSharpSyntaxNode)token.Parent;
            if (position == node.SpanStart)
            {
                // There are two cases where the binder chosen for a position at the beginning of a symbol
                // is incorrect for binding an attribute:
                //
                //   For a type, the binder should be the one that is used for the interior of the type, where
                //   the types members (and type parameters) are in scope. We adjust the position to the "{" to get
                //   that binder.
                //
                //   For a generic method, the binder should not include the type parameters. We adjust the position to
                //   the method name to get that binder.
 
                if (node is BaseTypeDeclarationSyntax typeDecl)
                {
                    // We're at the beginning of a type declaration. We want the members to be in scope for attributes,
                    // so use the open brace token.
                    position = typeDecl.OpenBraceToken.SpanStart;
                }
 
                var methodDecl = node.FirstAncestorOrSelf<MethodDeclarationSyntax>();
                if (methodDecl?.SpanStart == position)
                {
                    // We're at the beginning of a method declaration. We want the type parameters to NOT be in scope.
                    position = methodDecl.Identifier.SpanStart;
                }
            }
 
            return position;
        }
 
        #endregion Helpers for speculative binding
 
        protected override IOperation GetOperationCore(SyntaxNode node, CancellationToken cancellationToken)
        {
            var csnode = (CSharpSyntaxNode)node;
            CheckSyntaxNode(csnode);
 
            return this.GetOperationWorker(csnode, cancellationToken);
        }
 
        internal virtual IOperation GetOperationWorker(CSharpSyntaxNode node, CancellationToken cancellationToken)
        {
            return null;
        }
 
        #region GetSymbolInfo
 
        /// <summary>
        /// Gets the semantic information for an ordering clause in an orderby query clause.
        /// </summary>
        public abstract SymbolInfo GetSymbolInfo(OrderingSyntax node, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Gets the semantic information associated with a select or group clause.
        /// </summary>
        public abstract SymbolInfo GetSymbolInfo(SelectOrGroupClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Gets the SymbolInfo for the Deconstruct method used for a deconstruction pattern clause, if any.
        /// </summary>
        public SymbolInfo GetSymbolInfo(PositionalPatternClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(node);
            return this.GetSymbolInfoWorker(node, SymbolInfoOptions.DefaultOptions, cancellationToken);
        }
 
        /// <summary>
        /// Returns what symbol(s), if any, the given expression syntax bound to in the program.
        /// 
        /// An AliasSymbol will never be returned by this method. What the alias refers to will be
        /// returned instead. To get information about aliases, call GetAliasInfo.
        /// 
        /// If binding the type name C in the expression "new C(...)" the actual constructor bound to
        /// will be returned (or all constructor if overload resolution failed). This occurs as long as C
        /// unambiguously binds to a single type that has a constructor. If C ambiguously binds to multiple
        /// types, or C binds to a static class, then type(s) are returned.
        /// </summary>
        public SymbolInfo GetSymbolInfo(ExpressionSyntax expression, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(expression);
 
            if (!CanGetSemanticInfo(expression, allowNamedArgumentName: true))
            {
                return SymbolInfo.None;
            }
            else if (SyntaxFacts.IsNamedArgumentName(expression))
            {
                // Named arguments handled in special way.
                return this.GetNamedArgumentSymbolInfo((IdentifierNameSyntax)expression, cancellationToken);
            }
            else if (SyntaxFacts.IsDeclarationExpressionType(expression, out DeclarationExpressionSyntax parent))
            {
                switch (parent.Designation.Kind())
                {
                    case SyntaxKind.SingleVariableDesignation:
                        return GetSymbolInfoFromSymbolOrNone(TypeFromVariable((SingleVariableDesignationSyntax)parent.Designation, cancellationToken).Type);
 
                    case SyntaxKind.DiscardDesignation:
                        return GetSymbolInfoFromSymbolOrNone(GetTypeInfoWorker(parent, cancellationToken).Type.GetPublicSymbol());
 
                    case SyntaxKind.ParenthesizedVariableDesignation:
                        if (((TypeSyntax)expression).IsVar)
                        {
                            var varTypeInfo = GetTypeInfoWorker(expression, cancellationToken);
                            if (varTypeInfo.Type is { TypeKind: not TypeKind.Error })
                            {
                                return GetSymbolInfoFromSymbolOrNone(varTypeInfo.Type.GetPublicSymbol());
                            }
 
                            return GetSymbolInfoFromSymbolOrNone(GetTypeInfoWorker(parent, cancellationToken).Type.GetPublicSymbol());
                        }
 
                        break;
                }
            }
            else if (expression is DeclarationExpressionSyntax declaration)
            {
                if (declaration.Designation.Kind() != SyntaxKind.SingleVariableDesignation)
                {
                    return SymbolInfo.None;
                }
 
                var symbol = GetDeclaredSymbol((SingleVariableDesignationSyntax)declaration.Designation, cancellationToken);
                if ((object)symbol == null)
                {
                    return SymbolInfo.None;
                }
                return new SymbolInfo(symbol);
            }
 
            return this.GetSymbolInfoWorker(expression, SymbolInfoOptions.DefaultOptions, cancellationToken);
        }
 
        private static SymbolInfo GetSymbolInfoFromSymbolOrNone(ITypeSymbol type)
        {
            if (type?.Kind != SymbolKind.ErrorType)
            {
                return new SymbolInfo(type);
            }
 
            return SymbolInfo.None;
        }
 
        /// <summary>
        /// Given a variable designation (typically in the left-hand-side of a deconstruction declaration statement),
        /// figure out its type by looking at the declared symbol of the corresponding variable.
        /// </summary>
        private (ITypeSymbol Type, CodeAnalysis.NullableAnnotation Annotation) TypeFromVariable(SingleVariableDesignationSyntax variableDesignation, CancellationToken cancellationToken)
        {
            var variable = GetDeclaredSymbol(variableDesignation, cancellationToken);
 
            switch (variable)
            {
                case ILocalSymbol local:
                    return (local.Type, local.NullableAnnotation);
                case IFieldSymbol field:
                    return (field.Type, field.NullableAnnotation);
            }
 
            return default;
        }
 
        /// <summary>
        /// Returns what 'Add' method symbol(s), if any, corresponds to the given expression syntax 
        /// within <see cref="BaseObjectCreationExpressionSyntax.Initializer"/>.
        /// </summary>
        public SymbolInfo GetCollectionInitializerSymbolInfo(ExpressionSyntax expression, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(expression);
 
            if (expression.Parent != null && expression.Parent.Kind() == SyntaxKind.CollectionInitializerExpression)
            {
                // Find containing object creation expression
 
                InitializerExpressionSyntax initializer = (InitializerExpressionSyntax)expression.Parent;
 
                // Skip containing object initializers
                while (initializer.Parent != null &&
                       initializer.Parent.Kind() == SyntaxKind.SimpleAssignmentExpression &&
                       ((AssignmentExpressionSyntax)initializer.Parent).Right == initializer &&
                       initializer.Parent.Parent != null &&
                       initializer.Parent.Parent.Kind() == SyntaxKind.ObjectInitializerExpression)
                {
                    initializer = (InitializerExpressionSyntax)initializer.Parent.Parent;
                }
 
                if (initializer.Parent is BaseObjectCreationExpressionSyntax objectCreation &&
                    objectCreation.Initializer == initializer &&
                    CanGetSemanticInfo(objectCreation, allowNamedArgumentName: false))
                {
                    return GetCollectionInitializerSymbolInfoWorker((InitializerExpressionSyntax)expression.Parent, expression, cancellationToken);
                }
            }
 
            return SymbolInfo.None;
        }
 
        /// <summary>
        /// Returns what symbol(s), if any, the given constructor initializer syntax bound to in the program.
        /// </summary>
        /// <param name="constructorInitializer">The syntax node to get semantic information for.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        public SymbolInfo GetSymbolInfo(ConstructorInitializerSyntax constructorInitializer, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(constructorInitializer);
 
            return CanGetSemanticInfo(constructorInitializer)
                ? GetSymbolInfoWorker(constructorInitializer, SymbolInfoOptions.DefaultOptions, cancellationToken)
                : SymbolInfo.None;
        }
 
        /// <summary>
        /// Returns what symbol(s), if any, the given constructor initializer syntax bound to in the program.
        /// </summary>
        /// <param name="constructorInitializer">The syntax node to get semantic information for.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        public SymbolInfo GetSymbolInfo(PrimaryConstructorBaseTypeSyntax constructorInitializer, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(constructorInitializer);
 
            return CanGetSemanticInfo(constructorInitializer)
                ? GetSymbolInfoWorker(constructorInitializer, SymbolInfoOptions.DefaultOptions, cancellationToken)
                : SymbolInfo.None;
        }
 
        /// <summary>
        /// Returns what symbol(s), if any, the given attribute syntax bound to in the program.
        /// </summary>
        /// <param name="attributeSyntax">The syntax node to get semantic information for.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        public SymbolInfo GetSymbolInfo(AttributeSyntax attributeSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(attributeSyntax);
 
            return CanGetSemanticInfo(attributeSyntax)
                ? GetSymbolInfoWorker(attributeSyntax, SymbolInfoOptions.DefaultOptions, cancellationToken)
                : SymbolInfo.None;
        }
 
        /// <summary>
        /// Gets the semantic information associated with a documentation comment cref.
        /// </summary>
        public SymbolInfo GetSymbolInfo(CrefSyntax crefSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(crefSyntax);
 
            return CanGetSemanticInfo(crefSyntax)
                ? GetSymbolInfoWorker(crefSyntax, SymbolInfoOptions.DefaultOptions, cancellationToken)
                : SymbolInfo.None;
        }
 
        /// <summary>
        /// Binds the expression in the context of the specified location and gets symbol information.
        /// This method is used to get symbol information about an expression that did not actually
        /// appear in the source code.
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and
        /// accessibility. This character position must be within the FullSpan of the Root syntax
        /// node in this SemanticModel.
        /// </param>
        /// <param name="expression">A syntax node that represents a parsed expression. This syntax
        /// node need not and typically does not appear in the source code referred to by the
        /// SemanticModel instance.</param>
        /// <param name="bindingOption">Indicates whether to binding the expression as a full expressions,
        /// or as a type or namespace. If SpeculativeBindingOption.BindAsTypeOrNamespace is supplied, then
        /// expression should derive from TypeSyntax.</param>
        /// <returns>The symbol information for the topmost node of the expression.</returns>
        /// <remarks>
        /// The passed in expression is interpreted as a stand-alone expression, as if it
        /// appeared by itself somewhere within the scope that encloses "position".
        /// 
        /// <paramref name="bindingOption"/> is ignored if <paramref name="position"/> is within a documentation
        /// comment cref attribute value.
        /// </remarks>
        public SymbolInfo GetSpeculativeSymbolInfo(int position, ExpressionSyntax expression, SpeculativeBindingOption bindingOption)
        {
            if (!CanGetSemanticInfo(expression, isSpeculative: true)) return SymbolInfo.None;
 
            Binder binder;
            ImmutableArray<Symbol> crefSymbols;
            BoundNode boundNode = GetSpeculativelyBoundExpression(position, expression, bindingOption, out binder, out crefSymbols); //calls CheckAndAdjustPosition
            Debug.Assert(boundNode == null || crefSymbols.IsDefault);
            if (boundNode == null)
            {
                return crefSymbols.IsDefault ? SymbolInfo.None : GetCrefSymbolInfo(OneOrMany.Create(crefSymbols), SymbolInfoOptions.DefaultOptions, hasParameterList: false);
            }
 
            var symbolInfo = this.GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, boundNode, boundNode, boundNodeForSyntacticParent: null, binderOpt: binder);
 
            return symbolInfo;
        }
 
        /// <summary>
        /// Bind the attribute in the context of the specified location and get semantic information
        /// such as type, symbols and diagnostics. This method is used to get semantic information about an attribute
        /// that did not actually appear in the source code.
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and accessibility. This
        /// character position must be within the FullSpan of the Root syntax node in this SemanticModel. In order to obtain
        /// the correct scoping rules for the attribute, position should be the Start position of the Span of the symbol that
        /// the attribute is being applied to.
        /// </param>
        /// <param name="attribute">A syntax node that represents a parsed attribute. This syntax node
        /// need not and typically does not appear in the source code referred to SemanticModel instance.</param>
        /// <returns>The semantic information for the topmost node of the attribute.</returns>
        public SymbolInfo GetSpeculativeSymbolInfo(int position, AttributeSyntax attribute)
        {
            Debug.Assert(CanGetSemanticInfo(attribute, isSpeculative: true));
 
            Binder binder;
            BoundNode boundNode = GetSpeculativelyBoundAttribute(position, attribute, out binder); //calls CheckAndAdjustPosition
            if (boundNode == null)
                return SymbolInfo.None;
 
            var symbolInfo = this.GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, boundNode, boundNode, boundNodeForSyntacticParent: null, binderOpt: binder);
 
            return symbolInfo;
        }
 
        /// <summary>
        /// Bind the constructor initializer in the context of the specified location and get semantic information
        /// such as type, symbols and diagnostics. This method is used to get semantic information about a constructor
        /// initializer that did not actually appear in the source code.
        /// 
        /// NOTE: This will only work in locations where there is already a constructor initializer.
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and accessibility. This
        /// character position must be within the FullSpan of the Root syntax node in this SemanticModel.
        /// Furthermore, it must be within the span of an existing constructor initializer.
        /// </param>
        /// <param name="constructorInitializer">A syntax node that represents a parsed constructor initializer. This syntax node
        /// need not and typically does not appear in the source code referred to SemanticModel instance.</param>
        /// <returns>The semantic information for the topmost node of the constructor initializer.</returns>
        public SymbolInfo GetSpeculativeSymbolInfo(int position, ConstructorInitializerSyntax constructorInitializer)
        {
            Debug.Assert(CanGetSemanticInfo(constructorInitializer, isSpeculative: true));
 
            position = CheckAndAdjustPosition(position);
 
            if (constructorInitializer == null)
            {
                throw new ArgumentNullException(nameof(constructorInitializer));
            }
 
            // NOTE: since we're going to be depending on a MemberModel to do the binding for us,
            // we need to find a constructor initializer in the tree of this semantic model.
            // NOTE: This approach will not allow speculative binding of a constructor initializer
            // on a constructor that didn't formerly have one.
            // TODO: Should we support positions that are not in existing constructor initializers?
            // If so, we will need to build up the context that would otherwise be built up by
            // InitializerMemberModel.
            var existingConstructorInitializer = this.Root.FindToken(position).Parent.AncestorsAndSelf().OfType<ConstructorInitializerSyntax>().FirstOrDefault();
 
            if (existingConstructorInitializer == null)
            {
                return SymbolInfo.None;
            }
 
            MemberSemanticModel memberModel = GetMemberModel(existingConstructorInitializer);
 
            if (memberModel == null)
            {
                return SymbolInfo.None;
            }
 
            var binder = memberModel.GetEnclosingBinder(position);
            if (binder != null)
            {
                binder = new ExecutableCodeBinder(constructorInitializer, binder.ContainingMemberOrLambda, binder);
 
                BoundExpressionStatement bnode = binder.BindConstructorInitializer(constructorInitializer, BindingDiagnosticBag.Discarded);
                var binfo = GetSymbolInfoFromBoundConstructorInitializer(memberModel, binder, bnode);
                return binfo;
            }
            else
            {
                return SymbolInfo.None;
            }
        }
 
        private static SymbolInfo GetSymbolInfoFromBoundConstructorInitializer(MemberSemanticModel memberModel, Binder binder, BoundExpressionStatement bnode)
        {
            BoundExpression expression = bnode.Expression;
 
            while (expression is BoundSequence sequence)
            {
                expression = sequence.Value;
            }
 
            return memberModel.GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, expression, expression, boundNodeForSyntacticParent: null, binderOpt: binder);
        }
 
        /// <summary>
        /// Bind the constructor initializer in the context of the specified location and get semantic information
        /// about symbols. This method is used to get semantic information about a constructor
        /// initializer that did not actually appear in the source code.
        /// 
        /// NOTE: This will only work in locations where there is already a constructor initializer.
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and accessibility. This
        /// character position must be within the span of an existing constructor initializer.
        /// </param>
        /// <param name="constructorInitializer">A syntax node that represents a parsed constructor initializer. This syntax node
        /// need not and typically does not appear in the source code referred to SemanticModel instance.</param>
        /// <returns>The semantic information for the topmost node of the constructor initializer.</returns>
        public SymbolInfo GetSpeculativeSymbolInfo(int position, PrimaryConstructorBaseTypeSyntax constructorInitializer)
        {
            Debug.Assert(CanGetSemanticInfo(constructorInitializer, isSpeculative: true));
 
            position = CheckAndAdjustPosition(position);
 
            if (constructorInitializer == null)
            {
                throw new ArgumentNullException(nameof(constructorInitializer));
            }
 
            // NOTE: since we're going to be depending on a MemberModel to do the binding for us,
            // we need to find a constructor initializer in the tree of this semantic model.
            // NOTE: This approach will not allow speculative binding of a constructor initializer
            // on a constructor that didn't formerly have one.
            // TODO: Should we support positions that are not in existing constructor initializers?
            // If so, we will need to build up the context that would otherwise be built up by
            // InitializerMemberModel.
            var existingConstructorInitializer = this.Root.FindToken(position).Parent.AncestorsAndSelf().OfType<PrimaryConstructorBaseTypeSyntax>().FirstOrDefault();
 
            if (existingConstructorInitializer == null)
            {
                return SymbolInfo.None;
            }
 
            MemberSemanticModel memberModel = GetMemberModel(existingConstructorInitializer);
 
            if (memberModel == null)
            {
                return SymbolInfo.None;
            }
 
            var argumentList = existingConstructorInitializer.ArgumentList;
            var binder = memberModel.GetEnclosingBinder(LookupPosition.IsBetweenTokens(position, argumentList.OpenParenToken, argumentList.CloseParenToken) ? position : argumentList.OpenParenToken.SpanStart);
            if (binder != null)
            {
                binder = new ExecutableCodeBinder(constructorInitializer, binder.ContainingMemberOrLambda, binder);
 
                BoundExpressionStatement bnode = binder.BindConstructorInitializer(constructorInitializer, BindingDiagnosticBag.Discarded);
                SymbolInfo binfo = GetSymbolInfoFromBoundConstructorInitializer(memberModel, binder, bnode);
                return binfo;
            }
            else
            {
                return SymbolInfo.None;
            }
        }
 
        /// <summary>
        /// Bind the cref in the context of the specified location and get semantic information
        /// such as type, symbols and diagnostics. This method is used to get semantic information about a cref
        /// that did not actually appear in the source code.
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and accessibility. This
        /// character position must be within the FullSpan of the Root syntax node in this SemanticModel. In order to obtain
        /// the correct scoping rules for the cref, position should be the Start position of the Span of the original cref.
        /// </param>
        /// <param name="cref">A syntax node that represents a parsed cref. This syntax node
        /// need not and typically does not appear in the source code referred to SemanticModel instance.</param>
        /// <param name="options">SymbolInfo options.</param>
        /// <returns>The semantic information for the topmost node of the cref.</returns>
        public SymbolInfo GetSpeculativeSymbolInfo(int position, CrefSyntax cref, SymbolInfoOptions options = SymbolInfoOptions.DefaultOptions)
        {
            Debug.Assert(CanGetSemanticInfo(cref, isSpeculative: true));
 
            position = CheckAndAdjustPosition(position);
            return this.GetCrefSymbolInfo(position, cref, options, HasParameterList(cref));
        }
 
        #endregion GetSymbolInfo
 
        #region GetTypeInfo
 
        /// <summary>
        /// Gets type information about a constructor initializer.
        /// </summary>
        /// <param name="constructorInitializer">The syntax node to get semantic information for.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        public TypeInfo GetTypeInfo(ConstructorInitializerSyntax constructorInitializer, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(constructorInitializer);
 
            return CanGetSemanticInfo(constructorInitializer)
                ? GetTypeInfoWorker(constructorInitializer, cancellationToken)
                : CSharpTypeInfo.None;
        }
 
        public abstract TypeInfo GetTypeInfo(SelectOrGroupClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken));
 
        public TypeInfo GetTypeInfo(PatternSyntax pattern, CancellationToken cancellationToken = default(CancellationToken))
        {
            while (pattern is ParenthesizedPatternSyntax pp)
                pattern = pp.Pattern;
 
            CheckSyntaxNode(pattern);
            return GetTypeInfoWorker(pattern, cancellationToken);
        }
 
        /// <summary>
        /// Gets type information about an expression.
        /// </summary>
        /// <param name="expression">The syntax node to get semantic information for.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        public TypeInfo GetTypeInfo(ExpressionSyntax expression, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(expression);
 
            if (!CanGetSemanticInfo(expression))
            {
                return CSharpTypeInfo.None;
            }
            else if (SyntaxFacts.IsDeclarationExpressionType(expression, out DeclarationExpressionSyntax parent))
            {
                switch (parent.Designation.Kind())
                {
                    case SyntaxKind.SingleVariableDesignation:
                        var (declarationType, annotation) = ((ITypeSymbol, CodeAnalysis.NullableAnnotation))TypeFromVariable((SingleVariableDesignationSyntax)parent.Designation, cancellationToken);
                        var declarationTypeSymbol = declarationType.GetSymbol();
                        var nullabilityInfo = annotation.ToNullabilityInfo(declarationTypeSymbol);
                        return new CSharpTypeInfo(declarationTypeSymbol, declarationTypeSymbol, nullabilityInfo, nullabilityInfo, Conversion.Identity);
 
                    case SyntaxKind.DiscardDesignation:
                        var declarationInfo = GetTypeInfoWorker(parent, cancellationToken);
                        return new CSharpTypeInfo(declarationInfo.Type, declarationInfo.Type, declarationInfo.Nullability, declarationInfo.Nullability, Conversion.Identity);
 
                    case SyntaxKind.ParenthesizedVariableDesignation:
                        if (((TypeSyntax)expression).IsVar)
                        {
                            var varTypeInfo = GetTypeInfoWorker(expression, cancellationToken);
                            if (varTypeInfo.Type is { TypeKind: not TypeKind.Error })
                            {
                                return varTypeInfo;
                            }
 
                            return GetTypeInfoWorker(parent, cancellationToken);
                        }
 
                        break;
                }
            }
 
            return GetTypeInfoWorker(expression, cancellationToken);
        }
 
        /// <summary>
        /// Gets type information about an attribute.
        /// </summary>
        /// <param name="attributeSyntax">The syntax node to get semantic information for.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        public TypeInfo GetTypeInfo(AttributeSyntax attributeSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(attributeSyntax);
 
            return CanGetSemanticInfo(attributeSyntax)
                ? GetTypeInfoWorker(attributeSyntax, cancellationToken)
                : CSharpTypeInfo.None;
        }
 
        /// <summary>
        /// Gets the conversion that occurred between the expression's type and type implied by the expression's context.
        /// </summary>
        public Conversion GetConversion(SyntaxNode expression, CancellationToken cancellationToken = default(CancellationToken))
        {
            var csnode = (CSharpSyntaxNode)expression;
 
            CheckSyntaxNode(csnode);
 
            var info = CanGetSemanticInfo(csnode)
                ? GetTypeInfoWorker(csnode, cancellationToken)
                : CSharpTypeInfo.None;
 
            return info.ImplicitConversion;
        }
 
        /// <summary>
        /// Binds the expression in the context of the specified location and gets type information.
        /// This method is used to get type information about an expression that did not actually
        /// appear in the source code.
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and
        /// accessibility. This character position must be within the FullSpan of the Root syntax
        /// node in this SemanticModel.
        /// </param>
        /// <param name="expression">A syntax node that represents a parsed expression. This syntax
        /// node need not and typically does not appear in the source code referred to by the
        /// SemanticModel instance.</param>
        /// <param name="bindingOption">Indicates whether to binding the expression as a full expressions,
        /// or as a type or namespace. If SpeculativeBindingOption.BindAsTypeOrNamespace is supplied, then
        /// expression should derive from TypeSyntax.</param>
        /// <returns>The type information for the topmost node of the expression.</returns>
        /// <remarks>The passed in expression is interpreted as a stand-alone expression, as if it
        /// appeared by itself somewhere within the scope that encloses "position".</remarks>
        public TypeInfo GetSpeculativeTypeInfo(int position, ExpressionSyntax expression, SpeculativeBindingOption bindingOption)
        {
            return GetSpeculativeTypeInfoWorker(position, expression, bindingOption);
        }
 
        internal CSharpTypeInfo GetSpeculativeTypeInfoWorker(int position, ExpressionSyntax expression, SpeculativeBindingOption bindingOption)
        {
            if (!CanGetSemanticInfo(expression, isSpeculative: true))
            {
                return CSharpTypeInfo.None;
            }
 
            ImmutableArray<Symbol> crefSymbols;
            BoundNode boundNode = GetSpeculativelyBoundExpression(position, expression, bindingOption, out _, out crefSymbols); //calls CheckAndAdjustPosition
            Debug.Assert(boundNode == null || crefSymbols.IsDefault);
            if (boundNode == null)
            {
                return !crefSymbols.IsDefault && crefSymbols.Length == 1
                    ? GetTypeInfoForSymbol(crefSymbols[0])
                    : CSharpTypeInfo.None;
            }
 
            var typeInfo = GetTypeInfoForNode(boundNode, boundNode, boundNodeForSyntacticParent: null);
 
            return typeInfo;
        }
 
        /// <summary>
        /// Gets the conversion that occurred between the expression's type and type implied by the expression's context.
        /// </summary>
        public Conversion GetSpeculativeConversion(int position, ExpressionSyntax expression, SpeculativeBindingOption bindingOption)
        {
            var info = this.GetSpeculativeTypeInfoWorker(position, expression, bindingOption);
            return info.ImplicitConversion;
        }
 
        #endregion GetTypeInfo
 
        #region GetMemberGroup
 
        /// <summary>
        /// Gets a list of method or indexed property symbols for a syntax node.
        /// </summary>
        /// <param name="expression">The syntax node to get semantic information for.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        public ImmutableArray<ISymbol> GetMemberGroup(ExpressionSyntax expression, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(expression);
 
            return CanGetSemanticInfo(expression)
                ? this.GetMemberGroupWorker(expression, SymbolInfoOptions.DefaultOptions, cancellationToken).GetPublicSymbols()
                : ImmutableArray<ISymbol>.Empty;
        }
 
        /// <summary>
        /// Gets a list of method or indexed property symbols for a syntax node.
        /// </summary>
        /// <param name="attribute">The syntax node to get semantic information for.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        public ImmutableArray<ISymbol> GetMemberGroup(AttributeSyntax attribute, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(attribute);
 
            return CanGetSemanticInfo(attribute)
                ? this.GetMemberGroupWorker(attribute, SymbolInfoOptions.DefaultOptions, cancellationToken).GetPublicSymbols()
                : ImmutableArray<ISymbol>.Empty;
        }
 
        /// <summary>
        /// Gets a list of method symbols for a syntax node.
        /// </summary>
        /// <param name="initializer">The syntax node to get semantic information for.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        public ImmutableArray<ISymbol> GetMemberGroup(ConstructorInitializerSyntax initializer, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(initializer);
 
            return CanGetSemanticInfo(initializer)
                ? this.GetMemberGroupWorker(initializer, SymbolInfoOptions.DefaultOptions, cancellationToken).GetPublicSymbols()
                : ImmutableArray<ISymbol>.Empty;
        }
 
        #endregion GetMemberGroup
 
        #region GetIndexerGroup
 
        /// <summary>
        /// Returns the list of accessible, non-hidden indexers that could be invoked with the given expression as receiver.
        /// </summary>
        /// <param name="expression">Potential indexer receiver.</param>
        /// <param name="cancellationToken">To cancel the computation.</param>
        /// <returns>Accessible, non-hidden indexers.</returns>
        /// <remarks>
        /// If the receiver is an indexer expression, the list will contain the indexers that could be applied to the result
        /// of accessing the indexer, not the set of candidates that were considered during construction of the indexer expression.
        /// </remarks>
        public ImmutableArray<IPropertySymbol> GetIndexerGroup(ExpressionSyntax expression, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(expression);
 
            return CanGetSemanticInfo(expression)
                ? this.GetIndexerGroupWorker(expression, SymbolInfoOptions.DefaultOptions, cancellationToken)
                : ImmutableArray<IPropertySymbol>.Empty;
        }
 
        #endregion GetIndexerGroup
 
        #region GetConstantValue
 
        public Optional<object> GetConstantValue(ExpressionSyntax expression, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(expression);
 
            return CanGetSemanticInfo(expression)
                ? this.GetConstantValueWorker(expression, cancellationToken)
                : default(Optional<object>);
        }
 
        #endregion GetConstantValue
 
        /// <summary>
        /// Gets the semantic information associated with a query clause.
        /// </summary>
        public abstract QueryClauseInfo GetQueryClauseInfo(QueryClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// If <paramref name="nameSyntax"/> resolves to an alias name, return the AliasSymbol corresponding
        /// to A. Otherwise return null.
        /// </summary>
        public IAliasSymbol GetAliasInfo(IdentifierNameSyntax nameSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(nameSyntax);
 
            if (!CanGetSemanticInfo(nameSyntax))
                return null;
 
            SymbolInfo info = GetSymbolInfoWorker(nameSyntax, SymbolInfoOptions.PreferTypeToConstructors | SymbolInfoOptions.PreserveAliases, cancellationToken);
            return info.Symbol as IAliasSymbol;
        }
 
        /// <summary>
        /// Binds the name in the context of the specified location and sees if it resolves to an
        /// alias name. If it does, return the AliasSymbol corresponding to it. Otherwise, return null.
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and
        /// accessibility. This character position must be within the FullSpan of the Root syntax
        /// node in this SemanticModel.
        /// </param>
        /// <param name="nameSyntax">A syntax node that represents a name. This syntax
        /// node need not and typically does not appear in the source code referred to by the
        /// SemanticModel instance.</param>
        /// <param name="bindingOption">Indicates whether to binding the name as a full expression,
        /// or as a type or namespace. If SpeculativeBindingOption.BindAsTypeOrNamespace is supplied, then
        /// expression should derive from TypeSyntax.</param>
        /// <remarks>The passed in name is interpreted as a stand-alone name, as if it
        /// appeared by itself somewhere within the scope that encloses "position".</remarks>
        public IAliasSymbol GetSpeculativeAliasInfo(int position, IdentifierNameSyntax nameSyntax, SpeculativeBindingOption bindingOption)
        {
            Binder binder;
            ImmutableArray<Symbol> crefSymbols;
            BoundNode boundNode = GetSpeculativelyBoundExpression(position, nameSyntax, bindingOption, out binder, out crefSymbols); //calls CheckAndAdjustPosition
            Debug.Assert(boundNode == null || crefSymbols.IsDefault);
            if (boundNode == null)
            {
                return !crefSymbols.IsDefault && crefSymbols.Length == 1
                    ? (crefSymbols[0] as AliasSymbol).GetPublicSymbol()
                    : null;
            }
 
            var symbolInfo = this.GetSymbolInfoForNode(SymbolInfoOptions.PreferTypeToConstructors | SymbolInfoOptions.PreserveAliases,
                boundNode, boundNode, boundNodeForSyntacticParent: null, binderOpt: binder);
 
            return symbolInfo.Symbol as IAliasSymbol;
        }
 
        /// <summary>
        /// Gets the binder that encloses the position.
        /// </summary>
        internal Binder GetEnclosingBinder(int position)
        {
            Binder result = GetEnclosingBinderInternal(position);
            Debug.Assert(result == null || result.IsSemanticModelBinder);
            return result;
        }
 
        internal abstract Binder GetEnclosingBinderInternal(int position);
 
        /// <summary>
        /// Gets the MemberSemanticModel that contains the node.
        /// </summary>
        internal abstract MemberSemanticModel GetMemberModel(SyntaxNode node);
 
        internal bool IsInTree(SyntaxNode node)
        {
            return node.SyntaxTree == this.SyntaxTree;
        }
 
        private static bool IsInStructuredTriviaOtherThanCrefOrNameAttribute(CSharpSyntaxNode node)
        {
            while (node != null)
            {
                if (node.Kind() == SyntaxKind.XmlCrefAttribute || node.Kind() == SyntaxKind.XmlNameAttribute)
                {
                    return false;
                }
                else if (node.IsStructuredTrivia)
                {
                    return true;
                }
                else
                {
                    node = node.ParentOrStructuredTriviaParent;
                }
            }
            return false;
        }
 
        /// <summary>
        /// Given a position, locates the containing token.  If the position is actually within the
        /// leading trivia of the containing token or if that token is EOF, moves one token to the
        /// left.  Returns the start position of the resulting token.
        /// 
        /// This has the effect of moving the position left until it hits the beginning of a non-EOF
        /// token.
        /// 
        /// Throws an ArgumentOutOfRangeException if position is not within the root of this model.
        /// </summary>
        protected int CheckAndAdjustPosition(int position)
        {
            SyntaxToken unused;
            return CheckAndAdjustPosition(position, out unused);
        }
 
        protected int CheckAndAdjustPosition(int position, out SyntaxToken token)
        {
            int fullStart = this.Root.Position;
            int fullEnd = this.Root.FullSpan.End;
            bool atEOF = position == fullEnd && position == this.SyntaxTree.GetRoot().FullSpan.End;
 
            if ((fullStart <= position && position < fullEnd) || atEOF) // allow for EOF
            {
                token = (atEOF ? (CSharpSyntaxNode)this.SyntaxTree.GetRoot() : Root).FindTokenIncludingCrefAndNameAttributes(position);
 
                if (position < token.SpanStart) // NB: Span, not FullSpan
                {
                    // If this is already the first token, then the result will be default(SyntaxToken)
                    token = token.GetPreviousToken();
                }
 
                // If the first token in the root is missing, it's possible to step backwards
                // past the start of the root.  All sorts of bad things will happen in that case,
                // so just use the start of the root span.
                // CONSIDER: this should only happen when we step past the first token found, so
                // the start of that token would be another possible return value.
                return Math.Max(token.SpanStart, fullStart);
            }
            else if (fullStart == fullEnd && position == fullEnd)
            {
                // The root is an empty span and isn't the full compilation unit. No other choice here.
                token = default(SyntaxToken);
                return fullStart;
            }
 
            throw new ArgumentOutOfRangeException(nameof(position), position,
                string.Format(CSharpResources.PositionIsNotWithinSyntax, Root.FullSpan));
        }
 
        /// <summary>
        /// A convenience method that determines a position from a node.  If the node is missing,
        /// then its position will be adjusted using CheckAndAdjustPosition.
        /// </summary>
        protected int GetAdjustedNodePosition(SyntaxNode node)
        {
            Debug.Assert(IsInTree(node));
 
            var fullSpan = this.Root.FullSpan;
            var position = node.SpanStart;
 
            // skip zero-width tokens to get the position, but never get past the end of the node
            SyntaxToken firstToken = node.GetFirstToken(includeZeroWidth: false);
            if (firstToken.Node is object)
            {
                int betterPosition = firstToken.SpanStart;
                if (betterPosition < node.Span.End)
                {
                    position = betterPosition;
                }
            }
 
            if (fullSpan.IsEmpty)
            {
                Debug.Assert(position == fullSpan.Start);
                // At end of zero-width full span. No need to call
                // CheckAndAdjustPosition since that will simply 
                // return the original position.
                return position;
            }
            else if (position == fullSpan.End)
            {
                Debug.Assert(node.Width == 0);
                // For zero-width node at the end of the full span,
                // check and adjust the preceding position.
                return CheckAndAdjustPosition(position - 1);
            }
            else if (node.IsMissing || node.HasErrors || node.Width == 0 || node.IsPartOfStructuredTrivia())
            {
                return CheckAndAdjustPosition(position);
            }
            else
            {
                // No need to adjust position.
                return position;
            }
        }
 
        [Conditional("DEBUG")]
        protected void AssertPositionAdjusted(int position)
        {
            Debug.Assert(position == CheckAndAdjustPosition(position), "Expected adjusted position");
        }
 
        protected void CheckSyntaxNode(CSharpSyntaxNode syntax)
        {
            if (syntax == null)
            {
                throw new ArgumentNullException(nameof(syntax));
            }
 
            if (!IsInTree(syntax))
            {
                throw new ArgumentException(CSharpResources.SyntaxNodeIsNotWithinSynt);
            }
        }
 
        // This method ensures that the given syntax node to speculate is non-null and doesn't belong to a SyntaxTree of any model in the chain.
        private void CheckModelAndSyntaxNodeToSpeculate(CSharpSyntaxNode syntax)
        {
            if (syntax == null)
            {
                throw new ArgumentNullException(nameof(syntax));
            }
 
            if (this.IsSpeculativeSemanticModel)
            {
                throw new InvalidOperationException(CSharpResources.ChainingSpeculativeModelIsNotSupported);
            }
 
            if (this.Compilation.ContainsSyntaxTree(syntax.SyntaxTree))
            {
                throw new ArgumentException(CSharpResources.SpeculatedSyntaxNodeCannotBelongToCurrentCompilation);
            }
        }
 
        /// <summary>
        /// Gets the available named symbols in the context of the specified location and optional container. Only
        /// symbols that are accessible and visible from the given location are returned.
        /// </summary>
        /// <param name="position">The character position for determining the enclosing declaration scope and
        /// accessibility.</param>
        /// <param name="container">The container to search for symbols within. If null then the enclosing declaration
        /// scope around position is used.</param>
        /// <param name="name">The name of the symbol to find. If null is specified then symbols
        /// with any names are returned.</param>
        /// <param name="includeReducedExtensionMethods">Consider (reduced) extension methods.</param>
        /// <returns>A list of symbols that were found. If no symbols were found, an empty list is returned.</returns>
        /// <remarks>
        /// The "position" is used to determine what variables are visible and accessible. Even if "container" is
        /// specified, the "position" location is significant for determining which members of "containing" are
        /// accessible. 
        /// 
        /// Labels are not considered (see <see cref="LookupLabels"/>).
        /// 
        /// Non-reduced extension methods are considered regardless of the value of <paramref name="includeReducedExtensionMethods"/>.
        /// </remarks>
        public ImmutableArray<ISymbol> LookupSymbols(
            int position,
            NamespaceOrTypeSymbol container = null,
            string name = null,
            bool includeReducedExtensionMethods = false)
        {
            var options = includeReducedExtensionMethods ? LookupOptions.IncludeExtensionMethods : LookupOptions.Default;
            return LookupSymbolsInternal(position, container, name, options, useBaseReferenceAccessibility: false);
        }
 
        /// <summary>
        /// Gets the available base type members in the context of the specified location.  Akin to
        /// calling <see cref="LookupSymbols"/> with the container set to the immediate base type of
        /// the type in which <paramref name="position"/> occurs.  However, the accessibility rules
        /// are different: protected members of the base type will be visible.
        /// 
        /// Consider the following example:
        /// 
        ///   public class Base
        ///   {
        ///       protected void M() { }
        ///   }
        ///   
        ///   public class Derived : Base
        ///   {
        ///       void Test(Base b)
        ///       {
        ///           b.M(); // Error - cannot access protected member.
        ///           base.M();
        ///       }
        ///   }
        /// 
        /// Protected members of an instance of another type are only accessible if the instance is known
        /// to be "this" instance (as indicated by the "base" keyword).
        /// </summary>
        /// <param name="position">The character position for determining the enclosing declaration scope and
        /// accessibility.</param>
        /// <param name="name">The name of the symbol to find. If null is specified then symbols
        /// with any names are returned.</param>
        /// <returns>A list of symbols that were found. If no symbols were found, an empty list is returned.</returns>
        /// <remarks>
        /// The "position" is used to determine what variables are visible and accessible.
        /// 
        /// Non-reduced extension methods are considered, but reduced extension methods are not.
        /// </remarks>
        public new ImmutableArray<ISymbol> LookupBaseMembers(
            int position,
            string name = null)
        {
            return LookupSymbolsInternal(position, container: null, name: name, options: LookupOptions.Default, useBaseReferenceAccessibility: true);
        }
 
        /// <summary>
        /// Gets the available named static member symbols in the context of the specified location and optional container.
        /// Only members that are accessible and visible from the given location are returned.
        /// 
        /// Non-reduced extension methods are considered, since they are static methods.
        /// </summary>
        /// <param name="position">The character position for determining the enclosing declaration scope and
        /// accessibility.</param>
        /// <param name="container">The container to search for symbols within. If null then the enclosing declaration
        /// scope around position is used.</param>
        /// <param name="name">The name of the symbol to find. If null is specified then symbols
        /// with any names are returned.</param>
        /// <returns>A list of symbols that were found. If no symbols were found, an empty list is returned.</returns>
        /// <remarks>
        /// The "position" is used to determine what variables are visible and accessible. Even if "container" is
        /// specified, the "position" location is significant for determining which members of "containing" are
        /// accessible. 
        /// </remarks>
        public ImmutableArray<ISymbol> LookupStaticMembers(
            int position,
            NamespaceOrTypeSymbol container = null,
            string name = null)
        {
            return LookupSymbolsInternal(position, container, name, LookupOptions.MustNotBeInstance, useBaseReferenceAccessibility: false);
        }
 
        /// <summary>
        /// Gets the available named namespace and type symbols in the context of the specified location and optional container.
        /// Only members that are accessible and visible from the given location are returned.
        /// </summary>
        /// <param name="position">The character position for determining the enclosing declaration scope and
        /// accessibility.</param>
        /// <param name="container">The container to search for symbols within. If null then the enclosing declaration
        /// scope around position is used.</param>
        /// <param name="name">The name of the symbol to find. If null is specified then symbols
        /// with any names are returned.</param>
        /// <returns>A list of symbols that were found. If no symbols were found, an empty list is returned.</returns>
        /// <remarks>
        /// The "position" is used to determine what variables are visible and accessible. Even if "container" is
        /// specified, the "position" location is significant for determining which members of "containing" are
        /// accessible. 
        /// 
        /// Does not return NamespaceOrTypeSymbol, because there could be aliases.
        /// </remarks>
        public ImmutableArray<ISymbol> LookupNamespacesAndTypes(
            int position,
            NamespaceOrTypeSymbol container = null,
            string name = null)
        {
            return LookupSymbolsInternal(position, container, name, LookupOptions.NamespacesOrTypesOnly, useBaseReferenceAccessibility: false);
        }
 
        /// <summary>
        /// Gets the available named label symbols in the context of the specified location and optional container.
        /// Only members that are accessible and visible from the given location are returned.
        /// </summary>
        /// <param name="position">The character position for determining the enclosing declaration scope and
        /// accessibility.</param>
        /// <param name="name">The name of the symbol to find. If null is specified then symbols
        /// with any names are returned.</param>
        /// <returns>A list of symbols that were found. If no symbols were found, an empty list is returned.</returns>
        /// <remarks>
        /// The "position" is used to determine what variables are visible and accessible. Even if "container" is
        /// specified, the "position" location is significant for determining which members of "containing" are
        /// accessible. 
        /// </remarks>
        public new ImmutableArray<ISymbol> LookupLabels(
            int position,
            string name = null)
        {
            return LookupSymbolsInternal(position, container: null, name: name, options: LookupOptions.LabelsOnly, useBaseReferenceAccessibility: false);
        }
 
        /// <summary>
        /// Gets the available named symbols in the context of the specified location and optional
        /// container. Only symbols that are accessible and visible from the given location are
        /// returned.
        /// </summary>
        /// <param name="position">The character position for determining the enclosing declaration
        /// scope and accessibility.</param>
        /// <param name="container">The container to search for symbols within. If null then the
        /// enclosing declaration scope around position is used.</param>
        /// <param name="name">The name of the symbol to find. If null is specified then symbols
        /// with any names are returned.</param>
        /// <param name="options">Additional options that affect the lookup process.</param>
        /// <param name="useBaseReferenceAccessibility">Ignore 'throughType' in accessibility checking. 
        /// Used in checking accessibility of symbols accessed via 'MyBase' or 'base'.</param>
        /// <remarks>
        /// The "position" is used to determine what variables are visible and accessible. Even if
        /// "container" is specified, the "position" location is significant for determining which
        /// members of "containing" are accessible. 
        /// </remarks>
        /// <exception cref="ArgumentException">Throws an argument exception if the passed lookup options are invalid.</exception>
        private ImmutableArray<ISymbol> LookupSymbolsInternal(
            int position,
            NamespaceOrTypeSymbol container,
            string name,
            LookupOptions options,
            bool useBaseReferenceAccessibility)
        {
            Debug.Assert((options & LookupOptions.UseBaseReferenceAccessibility) == 0, "Use the useBaseReferenceAccessibility parameter.");
            if (useBaseReferenceAccessibility)
            {
                options |= LookupOptions.UseBaseReferenceAccessibility;
            }
            Debug.Assert(!options.IsAttributeTypeLookup()); // Not exposed publicly.
 
            options.ThrowIfInvalid();
 
            SyntaxToken token;
            position = CheckAndAdjustPosition(position, out token);
 
            if ((object)container == null || container.Kind == SymbolKind.Namespace)
            {
                options &= ~LookupOptions.IncludeExtensionMethods;
            }
 
            var binder = GetEnclosingBinder(position);
            if (binder == null)
            {
                return ImmutableArray<ISymbol>.Empty;
            }
 
            if (useBaseReferenceAccessibility)
            {
                Debug.Assert((object)container == null);
                TypeSymbol containingType = binder.ContainingType;
                TypeSymbol baseType = null;
 
                // For a script class or a submission class base should have no members.
                if ((object)containingType != null && containingType.Kind == SymbolKind.NamedType && ((NamedTypeSymbol)containingType).IsScriptClass)
                {
                    return ImmutableArray<ISymbol>.Empty;
                }
 
                if ((object)containingType == null || (object)(baseType = containingType.BaseTypeNoUseSiteDiagnostics) == null)
                {
                    throw new ArgumentException(
                        "Not a valid position for a call to LookupBaseMembers (must be in a type with a base type)",
                        nameof(position));
                }
                container = baseType;
            }
 
            if (!binder.IsInMethodBody &&
                (options & (LookupOptions.NamespaceAliasesOnly | LookupOptions.NamespacesOrTypesOnly | LookupOptions.LabelsOnly)) == 0)
            {
                // Method type parameters are not in scope outside a method
                // body unless the position is either:
                // a) in a type-only context inside an expression, or
                // b) inside of an XML name attribute in an XML doc comment,
                // c) inside a nameof context.
                var parentExpr = token.Parent as ExpressionSyntax;
                if (parentExpr != null && !(parentExpr.Parent is XmlNameAttributeSyntax) && !SyntaxFacts.IsInTypeOnlyContext(parentExpr) && !binder.IsInsideNameof)
                {
                    options |= LookupOptions.MustNotBeMethodTypeParameter;
                }
            }
 
            var info = LookupSymbolsInfo.GetInstance();
            info.FilterName = name;
 
            if ((object)container == null)
            {
                binder.AddLookupSymbolsInfo(info, options);
            }
            else
            {
                binder.AddMemberLookupSymbolsInfo(info, container, options, binder);
            }
 
            var results = ArrayBuilder<ISymbol>.GetInstance(info.Count);
 
            if (name == null)
            {
                // If they didn't provide a name, then look up all names and associated arities 
                // and find all the corresponding symbols.
                foreach (string foundName in info.Names)
                {
                    AppendSymbolsWithName(results, foundName, binder, container, options, info);
                }
            }
            else
            {
                // They provided a name.  Find all the arities for that name, and then look all of those up.
                AppendSymbolsWithName(results, name, binder, container, options, info);
            }
 
            info.Free();
 
            if ((options & LookupOptions.IncludeExtensionMethods) != 0)
            {
                var lookupResult = LookupResult.GetInstance();
 
                options |= LookupOptions.AllMethodsOnArityZero;
                options &= ~LookupOptions.MustBeInstance;
 
                var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
                binder.LookupExtensionMethods(lookupResult, name, 0, options, ref discardedUseSiteInfo);
 
                if (lookupResult.IsMultiViable)
                {
                    TypeSymbol containingType = (TypeSymbol)container;
                    foreach (MethodSymbol extensionMethod in lookupResult.Symbols)
                    {
                        var reduced = extensionMethod.ReduceExtensionMethod(containingType, Compilation);
                        if ((object)reduced != null)
                        {
                            results.Add(reduced.GetPublicSymbol());
                        }
                    }
                }
 
                lookupResult.Free();
            }
 
            if (name == null)
                results.RemoveWhere(static (symbol, _, _) => !symbol.CanBeReferencedByName, arg: default(VoidResult));
 
            return results.ToImmutableAndFree();
        }
 
        private void AppendSymbolsWithName(ArrayBuilder<ISymbol> results, string name, Binder binder, NamespaceOrTypeSymbol container, LookupOptions options, LookupSymbolsInfo info)
        {
            LookupSymbolsInfo.IArityEnumerable arities;
            Symbol uniqueSymbol;
 
            if (info.TryGetAritiesAndUniqueSymbol(name, out arities, out uniqueSymbol))
            {
                if ((object)uniqueSymbol != null)
                {
                    // This name mapped to something unique.  We don't need to proceed
                    // with a costly lookup.  Just add it straight to the results.
                    results.Add(RemapSymbolIfNecessary(uniqueSymbol).GetPublicSymbol());
                }
                else
                {
                    // The name maps to multiple symbols. Actually do a real lookup so 
                    // that we will properly figure out hiding and whatnot.
                    if (arities != null)
                    {
                        foreach (var arity in arities)
                        {
                            this.AppendSymbolsWithNameAndArity(results, name, arity, binder, container, options);
                        }
                    }
                    else
                    {
                        //non-unique symbol with non-zero arity doesn't seem possible.
                        this.AppendSymbolsWithNameAndArity(results, name, 0, binder, container, options);
                    }
                }
            }
        }
 
        private void AppendSymbolsWithNameAndArity(
            ArrayBuilder<ISymbol> results,
            string name,
            int arity,
            Binder binder,
            NamespaceOrTypeSymbol container,
            LookupOptions options)
        {
            Debug.Assert(results != null);
 
            // Don't need to de-dup since AllMethodsOnArityZero can't be set at this point (not exposed in CommonLookupOptions).
            Debug.Assert((options & LookupOptions.AllMethodsOnArityZero) == 0);
 
            var lookupResult = LookupResult.GetInstance();
 
            var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
            binder.LookupSymbolsSimpleName(
                lookupResult,
                container,
                name,
                arity,
                basesBeingResolved: null,
                options: options & ~LookupOptions.IncludeExtensionMethods,
                diagnose: false,
                useSiteInfo: ref discardedUseSiteInfo);
 
            if (lookupResult.IsMultiViable)
            {
                if (lookupResult.Symbols.Any(t => t.Kind == SymbolKind.NamedType || t.Kind == SymbolKind.Namespace || t.Kind == SymbolKind.ErrorType))
                {
                    // binder.ResultSymbol is defined only for type/namespace lookups
                    bool wasError;
                    Symbol singleSymbol = binder.ResultSymbol(lookupResult, name, arity, this.Root, BindingDiagnosticBag.Discarded, true, out wasError, container, options);
 
                    if (!wasError)
                    {
                        results.Add(RemapSymbolIfNecessary(singleSymbol).GetPublicSymbol());
                    }
                    else
                    {
                        foreach (var symbol in lookupResult.Symbols)
                        {
                            results.Add(RemapSymbolIfNecessary(symbol).GetPublicSymbol());
                        }
                    }
                }
                else
                {
                    foreach (var symbol in lookupResult.Symbols)
                    {
                        results.Add(RemapSymbolIfNecessary(symbol).GetPublicSymbol());
                    }
                }
            }
 
            lookupResult.Free();
        }
 
        private Symbol RemapSymbolIfNecessary(Symbol symbol)
        {
            switch (symbol)
            {
                case LocalSymbol _:
                case ParameterSymbol _:
                case MethodSymbol { MethodKind: MethodKind.LambdaMethod }:
                    return RemapSymbolIfNecessaryCore(symbol);
 
                default:
                    return symbol;
            }
        }
 
        /// <summary>
        /// Remaps a local, parameter, localfunction, or lambda symbol, if that symbol or its containing
        /// symbols were reinferred. This should only be called when nullable semantic analysis is enabled.
        /// </summary>
        internal abstract Symbol RemapSymbolIfNecessaryCore(Symbol symbol);
 
        /// <summary>
        /// Determines if the symbol is accessible from the specified location. 
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and
        /// accessibility. This character position must be within the FullSpan of the Root syntax
        /// node in this SemanticModel.
        /// </param>
        /// <param name="symbol">The symbol that we are checking to see if it accessible.</param>
        /// <returns>
        /// True if "symbol is accessible, false otherwise.</returns>
        /// <remarks>
        /// This method only checks accessibility from the point of view of the accessibility
        /// modifiers on symbol and its containing types. Even if true is returned, the given symbol
        /// may not be able to be referenced for other reasons, such as name hiding.
        /// </remarks>
        public bool IsAccessible(int position, Symbol symbol)
        {
            position = CheckAndAdjustPosition(position);
 
            if ((object)symbol == null)
            {
                throw new ArgumentNullException(nameof(symbol));
            }
 
            var binder = this.GetEnclosingBinder(position);
            if (binder != null)
            {
                var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
                return binder.IsAccessible(symbol, ref discardedUseSiteInfo, null);
            }
 
            return false;
        }
 
        /// <summary>
        /// Field-like events can be used as fields in types that can access private
        /// members of the declaring type of the event.
        /// </summary>
        public bool IsEventUsableAsField(int position, EventSymbol symbol)
        {
            return symbol is object && symbol.HasAssociatedField && this.IsAccessible(position, symbol.AssociatedField); //calls CheckAndAdjustPosition
        }
 
        private bool IsInTypeofExpression(int position)
        {
            var token = this.Root.FindToken(position);
            var curr = token.Parent;
            while (curr != this.Root)
            {
                if (curr.IsKind(SyntaxKind.TypeOfExpression))
                {
                    return true;
                }
 
                curr = curr.ParentOrStructuredTriviaParent;
            }
 
            return false;
        }
 
        // Gets the semantic info from a specific bound node and a set of diagnostics
        // lowestBoundNode: The lowest node in the bound tree associated with node
        // highestBoundNode: The highest node in the bound tree associated with node
        // boundNodeForSyntacticParent: The lowest node in the bound tree associated with node.Parent.
        // binderOpt: If this is null, then the one enclosing the bound node's syntax will be used (unsafe during speculative binding).
        [PerformanceSensitive(
            "https://github.com/dotnet/roslyn/issues/23582",
            Constraint = "Provide " + nameof(ArrayBuilder<Symbol>) + " capacity to reduce number of allocations.")]
        internal SymbolInfo GetSymbolInfoForNode(
            SymbolInfoOptions options,
            BoundNode lowestBoundNode,
            BoundNode highestBoundNode,
            BoundNode boundNodeForSyntacticParent,
            Binder binderOpt)
        {
            BoundExpression boundExpr;
 
            switch (highestBoundNode)
            {
                case BoundRecursivePattern pat:
                    return GetSymbolInfoForDeconstruction(pat);
            }
            switch (lowestBoundNode)
            {
                case BoundPositionalSubpattern subpattern:
                    return GetSymbolInfoForSubpattern(subpattern.Symbol);
                case BoundPropertySubpattern subpattern:
                    return GetSymbolInfoForSubpattern(subpattern.Member?.Symbol);
                case BoundPropertySubpatternMember subpatternMember:
                    return GetSymbolInfoForSubpattern(subpatternMember.Symbol);
                case BoundExpression boundExpr2:
                    boundExpr = boundExpr2;
                    break;
                default:
                    return SymbolInfo.None;
            }
 
            // TODO: Should parenthesized expression really not have symbols? At least for C#, I'm not sure that
            // is right. For example, C# allows the assignment statement:
            //    (i) = 9;
            // So we don't think this code should special case parenthesized expressions.
 
            // Get symbols and result kind from the lowest and highest nodes associated with the
            // syntax node.
            OneOrMany<Symbol> symbols = GetSemanticSymbols(
                boundExpr, boundNodeForSyntacticParent, binderOpt, options, out bool isDynamic, out LookupResultKind resultKind, out ImmutableArray<Symbol> unusedMemberGroup);
 
            if (highestBoundNode is BoundExpression highestBoundExpr)
            {
                LookupResultKind highestResultKind;
                bool highestIsDynamic;
                ImmutableArray<Symbol> unusedHighestMemberGroup;
                OneOrMany<Symbol> highestSymbols = GetSemanticSymbols(
                    highestBoundExpr, boundNodeForSyntacticParent, binderOpt, options, out highestIsDynamic, out highestResultKind, out unusedHighestMemberGroup);
 
                if ((symbols.Count != 1 || resultKind == LookupResultKind.OverloadResolutionFailure) && highestSymbols.Count > 0)
                {
                    symbols = highestSymbols;
                    resultKind = highestResultKind;
                    isDynamic = highestIsDynamic;
                }
                else if (highestResultKind != LookupResultKind.Empty && highestResultKind < resultKind)
                {
                    resultKind = highestResultKind;
                    isDynamic = highestIsDynamic;
                }
                else if (highestBoundExpr.Kind == BoundKind.TypeOrValueExpression)
                {
                    symbols = highestSymbols;
                    resultKind = highestResultKind;
                    isDynamic = highestIsDynamic;
                }
                else if (highestBoundExpr.Kind == BoundKind.UnaryOperator)
                {
                    if (IsUserDefinedTrueOrFalse((BoundUnaryOperator)highestBoundExpr))
                    {
                        symbols = highestSymbols;
                        resultKind = highestResultKind;
                        isDynamic = highestIsDynamic;
                    }
                    else
                    {
                        Debug.Assert(ReferenceEquals(lowestBoundNode, highestBoundNode), "How is it that this operator has the same syntax node as its operand?");
                    }
                }
            }
 
            if (resultKind == LookupResultKind.Empty)
            {
                // Empty typically indicates an error symbol that was created because no real
                // symbol actually existed.
                return SymbolInfoFactory.Create(ImmutableArray<Symbol>.Empty, LookupResultKind.Empty, isDynamic);
            }
            else
            {
                // Caas clients don't want ErrorTypeSymbol in the symbols, but the best guess
                // instead. If no best guess, then nothing is returned.
                var builder = ArrayBuilder<Symbol>.GetInstance(symbols.Count);
                foreach (Symbol symbol in symbols)
                {
                    AddUnwrappingErrorTypes(builder, symbol);
                }
 
                symbols = builder.ToOneOrManyAndFree();
            }
 
            if ((options & SymbolInfoOptions.ResolveAliases) != 0)
            {
                symbols = UnwrapAliases(symbols);
            }
 
            if (resultKind == LookupResultKind.Viable && symbols.Count > 1)
            {
                resultKind = LookupResultKind.OverloadResolutionFailure;
            }
 
            return SymbolInfoFactory.Create(symbols, resultKind, isDynamic);
        }
 
        private static SymbolInfo GetSymbolInfoForSubpattern(Symbol subpatternSymbol)
        {
            if (subpatternSymbol?.OriginalDefinition is ErrorTypeSymbol originalErrorType)
            {
                return new SymbolInfo(originalErrorType.CandidateSymbols.GetPublicSymbols(), originalErrorType.ResultKind.ToCandidateReason());
            }
 
            return new SymbolInfo(subpatternSymbol.GetPublicSymbol());
        }
 
        private SymbolInfo GetSymbolInfoForDeconstruction(BoundRecursivePattern pat)
        {
            return new SymbolInfo(pat.DeconstructMethod.GetPublicSymbol());
        }
 
        private static void AddUnwrappingErrorTypes(ArrayBuilder<Symbol> builder, Symbol s)
        {
            var originalErrorSymbol = s.OriginalDefinition as ErrorTypeSymbol;
            if ((object)originalErrorSymbol != null)
            {
                builder.AddRange(originalErrorSymbol.CandidateSymbols);
            }
            else
            {
                builder.Add(s);
            }
        }
 
        private static bool IsUserDefinedTrueOrFalse(BoundUnaryOperator @operator)
        {
            UnaryOperatorKind operatorKind = @operator.OperatorKind;
            return operatorKind == UnaryOperatorKind.UserDefinedTrue || operatorKind == UnaryOperatorKind.UserDefinedFalse;
        }
 
        // Gets the semantic info from a specific bound node and a set of diagnostics
        // lowestBoundNode: The lowest node in the bound tree associated with node
        // highestBoundNode: The highest node in the bound tree associated with node
        // boundNodeForSyntacticParent: The lowest node in the bound tree associated with node.Parent.
        internal CSharpTypeInfo GetTypeInfoForNode(
            BoundNode lowestBoundNode,
            BoundNode highestBoundNode,
            BoundNode boundNodeForSyntacticParent)
        {
            BoundPattern pattern = lowestBoundNode as BoundPattern ?? highestBoundNode as BoundPattern ?? (highestBoundNode is BoundSubpattern sp ? sp.Pattern : null);
            if (pattern != null)
            {
                var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
                // https://github.com/dotnet/roslyn/issues/35032: support patterns
                return new CSharpTypeInfo(
                    pattern.InputType, pattern.NarrowedType, nullability: default, convertedNullability: default,
                    Compilation.Conversions.ClassifyBuiltInConversion(pattern.InputType, pattern.NarrowedType, isChecked: false, ref discardedUseSiteInfo));
            }
            if (lowestBoundNode is BoundPropertySubpatternMember member)
            {
                return new CSharpTypeInfo(member.Type, member.Type, nullability: default, convertedNullability: default, Conversion.Identity);
            }
 
            var boundExpr = lowestBoundNode as BoundExpression;
            var highestBoundExpr = highestBoundNode as BoundExpression;
 
            if (boundExpr != null &&
                !(boundNodeForSyntacticParent != null &&
                  boundNodeForSyntacticParent.Syntax.Kind() == SyntaxKind.ObjectCreationExpression &&
                  ((ObjectCreationExpressionSyntax)boundNodeForSyntacticParent.Syntax).Type == boundExpr.Syntax)) // Do not return any type information for a ObjectCreationExpressionSyntax.Type node.
            {
                // TODO: Should parenthesized expression really not have symbols? At least for C#, I'm not sure that 
                // is right. For example, C# allows the assignment statement:
                //    (i) = 9;  
                // So I don't assume this code should special case parenthesized expressions.
                TypeSymbol type = null;
                NullabilityInfo nullability = boundExpr.TopLevelNullability;
 
                if (boundExpr.HasExpressionType())
                {
                    type = boundExpr.Type;
 
                    switch (boundExpr)
                    {
                        case BoundLocal local:
                            {
                                // Use of local before declaration requires some additional fixup.
                                // Due to complications around implicit locals and type inference, we do not
                                // try to obtain a type of a local when it is used before declaration, we use
                                // a special error type symbol. However, semantic model should return the same
                                // type information for usage of a local before and after its declaration.
                                // We will detect the use before declaration cases and replace the error type
                                // symbol with the one obtained from the local. It should be safe to get the type
                                // from the local at this point.
                                if (type is ExtendedErrorTypeSymbol extended && extended.VariableUsedBeforeDeclaration)
                                {
                                    type = local.LocalSymbol.Type;
                                    nullability = local.LocalSymbol.TypeWithAnnotations.NullableAnnotation.ToNullabilityInfo(type);
                                }
                                break;
                            }
                        case BoundConvertedTupleLiteral { SourceTuple: BoundTupleLiteral original }:
                            {
                                // The bound tree fully binds tuple literals. From the language point of
                                // view, however, converted tuple literals represent tuple conversions
                                // from tuple literal expressions which may or may not have types
                                type = original.Type;
                                break;
                            }
                    }
                }
 
                // we match highestBoundExpr.Kind to various kind frequently, so cache it here.
                // use NoOp kind for the case when highestBoundExpr == null - NoOp will not match anything below.
                var highestBoundExprKind = highestBoundExpr?.Kind ?? BoundKind.NoOpStatement;
                TypeSymbol convertedType;
                NullabilityInfo convertedNullability;
                Conversion conversion;
 
                if (highestBoundExprKind == BoundKind.Lambda) // the enclosing conversion is explicit
                {
                    var lambda = (BoundLambda)highestBoundExpr;
                    convertedType = lambda.Type;
                    // The bound tree always fully binds lambda and anonymous functions. From the language point of
                    // view, however, anonymous functions converted to a real delegate type should only have a 
                    // ConvertedType, not a Type. So set Type to null here. Otherwise you get the edge case where both
                    // Type and ConvertedType are the same, but the conversion isn't Identity.
                    type = null;
                    nullability = default;
                    convertedNullability = new NullabilityInfo(CodeAnalysis.NullableAnnotation.NotAnnotated, CodeAnalysis.NullableFlowState.NotNull);
                    conversion = new Conversion(ConversionKind.AnonymousFunction, lambda.Symbol, false);
                }
                else if ((highestBoundExpr as BoundConversion)?.Conversion.IsTupleLiteralConversion == true)
                {
                    var tupleLiteralConversion = (BoundConversion)highestBoundExpr;
                    if (tupleLiteralConversion.Operand.Kind == BoundKind.ConvertedTupleLiteral)
                    {
                        var convertedTuple = (BoundConvertedTupleLiteral)tupleLiteralConversion.Operand;
                        type = convertedTuple.SourceTuple.Type;
                        nullability = convertedTuple.TopLevelNullability;
                    }
                    else
                    {
                        (type, nullability) = getTypeAndNullability(tupleLiteralConversion.Operand);
                    }
 
                    (convertedType, convertedNullability) = getTypeAndNullability(tupleLiteralConversion);
                    conversion = tupleLiteralConversion.Conversion;
                }
                else if (highestBoundExprKind == BoundKind.FixedLocalCollectionInitializer)
                {
                    var initializer = (BoundFixedLocalCollectionInitializer)highestBoundExpr;
                    (convertedType, convertedNullability) = getTypeAndNullability(initializer);
                    (type, nullability) = getTypeAndNullability(initializer.Expression);
 
                    // the most pertinent conversion is the pointer conversion 
                    conversion = BoundNode.GetConversion(initializer.ElementPointerConversion, initializer.ElementPointerPlaceholder);
                }
                else if (boundExpr is BoundConvertedSwitchExpression { WasTargetTyped: true } convertedSwitch)
                {
                    if (highestBoundExpr is BoundConversion { ConversionKind: ConversionKind.SwitchExpression, Conversion: var convertedSwitchConversion })
                    {
                        // There was an implicit cast.
                        type = convertedSwitch.NaturalTypeOpt;
                        convertedType = convertedSwitch.Type;
                        convertedNullability = convertedSwitch.TopLevelNullability;
                        conversion = convertedSwitchConversion.IsValid ? convertedSwitchConversion : Conversion.NoConversion;
                    }
                    else
                    {
                        // There was an explicit cast on top of this
                        type = convertedSwitch.NaturalTypeOpt;
                        (convertedType, convertedNullability) = (type, nullability);
                        conversion = Conversion.Identity;
                    }
                }
                else if (boundExpr is BoundConditionalOperator { WasTargetTyped: true } cond)
                {
                    if (highestBoundExpr is BoundConversion { ConversionKind: ConversionKind.ConditionalExpression })
                    {
                        // There was an implicit cast.
                        type = cond.NaturalTypeOpt;
                        convertedType = cond.Type;
                        convertedNullability = nullability;
                        conversion = Conversion.MakeConditionalExpression(ImmutableArray<Conversion>.Empty);
                    }
                    else
                    {
                        // There was an explicit cast on top of this.
                        type = cond.NaturalTypeOpt;
                        (convertedType, convertedNullability) = (type, nullability);
                        conversion = Conversion.Identity;
                    }
                }
                else if (boundExpr is BoundCollectionExpression convertedCollection)
                {
                    type = null;
                    if (highestBoundExpr is BoundConversion { ConversionKind: ConversionKind.CollectionExpression or ConversionKind.NoConversion, Conversion: var convertedCollectionConversion })
                    {
                        convertedType = highestBoundExpr.Type;
                        convertedNullability = convertedCollection.TopLevelNullability;
                        conversion = convertedCollectionConversion;
                    }
                    else if (highestBoundExpr is BoundConversion { ConversionKind: ConversionKind.ImplicitNullable, Conversion.UnderlyingConversions: [{ Kind: ConversionKind.CollectionExpression }] } boundConversion)
                    {
                        convertedType = highestBoundExpr.Type;
                        convertedNullability = convertedCollection.TopLevelNullability;
                        conversion = boundConversion.Conversion;
                    }
                    else
                    {
                        // Explicit cast or error scenario like `object x = [];`
                        convertedNullability = nullability;
                        convertedType = null;
                        conversion = Conversion.Identity;
                    }
                }
                else if (highestBoundExpr != null && highestBoundExpr != boundExpr && highestBoundExpr.HasExpressionType())
                {
                    (convertedType, convertedNullability) = getTypeAndNullability(highestBoundExpr);
                    if (highestBoundExprKind != BoundKind.Conversion)
                    {
                        conversion = Conversion.Identity;
                    }
                    else if (((BoundConversion)highestBoundExpr).Operand.Kind != BoundKind.Conversion)
                    {
                        conversion = highestBoundExpr.GetConversion();
                        if (conversion.Kind == ConversionKind.AnonymousFunction)
                        {
                            // See comment above: anonymous functions do not have a type
                            type = null;
                            nullability = default;
                        }
                    }
                    else
                    {
                        // There is a sequence of conversions; we use ClassifyConversionFromExpression to report the most pertinent.
                        var binder = this.GetEnclosingBinder(boundExpr.Syntax.Span.Start);
                        var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
                        conversion = binder.Conversions.ClassifyConversionFromExpression(boundExpr, convertedType, isChecked: ((BoundConversion)highestBoundExpr).Checked, ref discardedUseSiteInfo);
                    }
                }
                else if (boundNodeForSyntacticParent?.Kind == BoundKind.DelegateCreationExpression)
                {
                    // A delegate creation expression takes the place of a method group or anonymous function conversion.
                    var delegateCreation = (BoundDelegateCreationExpression)boundNodeForSyntacticParent;
                    (convertedType, convertedNullability) = getTypeAndNullability(delegateCreation);
                    switch (boundExpr.Kind)
                    {
                        case BoundKind.MethodGroup:
                            {
                                conversion = new Conversion(ConversionKind.MethodGroup, delegateCreation.MethodOpt, delegateCreation.IsExtensionMethod);
                                break;
                            }
                        case BoundKind.Lambda:
                            {
                                var lambda = (BoundLambda)boundExpr;
                                conversion = new Conversion(ConversionKind.AnonymousFunction, lambda.Symbol, delegateCreation.IsExtensionMethod);
                                break;
                            }
                        case BoundKind.UnboundLambda:
                            {
                                var lambda = ((UnboundLambda)boundExpr).BindForErrorRecovery();
                                conversion = new Conversion(ConversionKind.AnonymousFunction, lambda.Symbol, delegateCreation.IsExtensionMethod);
                                break;
                            }
                        default:
                            conversion = Conversion.Identity;
                            break;
                    }
                }
                else if (boundExpr is BoundConversion { ConversionKind: ConversionKind.MethodGroup, Conversion: var exprConversion, Type: { TypeKind: TypeKind.FunctionPointer }, SymbolOpt: var symbol })
                {
                    // Because the method group is a separate syntax node from the &, the lowest bound node here is the BoundConversion. However,
                    // the conversion represents an implicit method group conversion from a typeless method group to a function pointer type, so
                    // we should reflect that in the types and conversion we return.
                    convertedType = type;
                    convertedNullability = nullability;
                    conversion = exprConversion;
                    type = null;
                    nullability = new NullabilityInfo(CodeAnalysis.NullableAnnotation.NotAnnotated, CodeAnalysis.NullableFlowState.NotNull);
                }
                else
                {
                    convertedType = type;
                    convertedNullability = nullability;
                    conversion = Conversion.Identity;
                }
 
                return new CSharpTypeInfo(type, convertedType, nullability, convertedNullability, conversion);
            }
 
            return CSharpTypeInfo.None;
 
            static (TypeSymbol, NullabilityInfo) getTypeAndNullability(BoundExpression expr) => (expr.Type, expr.TopLevelNullability);
        }
 
        // Gets the method or property group from a specific bound node.
        // lowestBoundNode: The lowest node in the bound tree associated with node
        // highestBoundNode: The highest node in the bound tree associated with node
        // boundNodeForSyntacticParent: The lowest node in the bound tree associated with node.Parent.
        internal ImmutableArray<Symbol> GetMemberGroupForNode(
            SymbolInfoOptions options,
            BoundNode lowestBoundNode,
            BoundNode boundNodeForSyntacticParent,
            Binder binderOpt)
        {
            if (lowestBoundNode is BoundExpression boundExpr)
            {
                LookupResultKind resultKind;
                ImmutableArray<Symbol> memberGroup;
                bool isDynamic;
                GetSemanticSymbols(boundExpr, boundNodeForSyntacticParent, binderOpt, options, out isDynamic, out resultKind, out memberGroup);
 
                return memberGroup;
            }
 
            return ImmutableArray<Symbol>.Empty;
        }
 
        // Gets the indexer group from a specific bound node.
        // lowestBoundNode: The lowest node in the bound tree associated with node
        // highestBoundNode: The highest node in the bound tree associated with node
        // boundNodeForSyntacticParent: The lowest node in the bound tree associated with node.Parent.
        internal ImmutableArray<IPropertySymbol> GetIndexerGroupForNode(
            BoundNode lowestBoundNode,
            Binder binderOpt)
        {
            var boundExpr = lowestBoundNode as BoundExpression;
            if (boundExpr != null && boundExpr.Kind != BoundKind.TypeExpression)
            {
                return GetIndexerGroupSemanticSymbols(boundExpr, binderOpt);
            }
 
            return ImmutableArray<IPropertySymbol>.Empty;
        }
 
        // Gets symbol info for a type or namespace or alias reference. It is assumed that any error cases will come in
        // as a type whose OriginalDefinition is an error symbol from which the ResultKind can be retrieved.
        internal static SymbolInfo GetSymbolInfoForSymbol(Symbol symbol, SymbolInfoOptions options)
        {
            Debug.Assert((object)symbol != null);
 
            // Determine type. Dig through aliases if necessary.
            Symbol unwrapped = UnwrapAlias(symbol);
            TypeSymbol type = unwrapped as TypeSymbol;
 
            // Determine symbols and resultKind.
            var originalErrorSymbol = (object)type != null ? type.OriginalDefinition as ErrorTypeSymbol : null;
 
            if ((object)originalErrorSymbol != null)
            {
                // Error case.
                var symbols = OneOrMany<Symbol>.Empty;
 
                LookupResultKind resultKind = originalErrorSymbol.ResultKind;
                if (resultKind != LookupResultKind.Empty)
                {
                    symbols = OneOrMany.Create(originalErrorSymbol.CandidateSymbols);
                }
 
                if ((options & SymbolInfoOptions.ResolveAliases) != 0)
                {
                    symbols = UnwrapAliases(symbols);
                }
 
                return SymbolInfoFactory.Create(symbols, resultKind, isDynamic: false);
            }
            else
            {
                // Non-error case. Use constructor that doesn't require creation of a Symbol array.
                var symbolToReturn = ((options & SymbolInfoOptions.ResolveAliases) != 0) ? unwrapped : symbol;
                return new SymbolInfo(symbolToReturn.GetPublicSymbol());
            }
        }
 
        // Gets TypeInfo for a type or namespace or alias reference.
        internal static CSharpTypeInfo GetTypeInfoForSymbol(Symbol symbol)
        {
            Debug.Assert((object)symbol != null);
 
            // Determine type. Dig through aliases if necessary.
            TypeSymbol type = UnwrapAlias(symbol) as TypeSymbol;
            // https://github.com/dotnet/roslyn/issues/35033: Examine this and make sure that we're using the correct nullabilities
            return new CSharpTypeInfo(type, type, default, default, Conversion.Identity);
        }
 
        protected static Symbol UnwrapAlias(Symbol symbol)
        {
            return symbol is AliasSymbol aliasSym ? aliasSym.Target : symbol;
        }
 
        protected static OneOrMany<Symbol> UnwrapAliases(OneOrMany<Symbol> symbols)
        {
            bool anyAliases = false;
 
            foreach (Symbol symbol in symbols)
            {
                if (symbol.Kind == SymbolKind.Alias)
                    anyAliases = true;
            }
 
            if (!anyAliases)
                return symbols;
 
            ArrayBuilder<Symbol> builder = ArrayBuilder<Symbol>.GetInstance();
            foreach (Symbol symbol in symbols)
            {
                // Caas clients don't want ErrorTypeSymbol in the symbols, but the best guess
                // instead. If no best guess, then nothing is returned.
                AddUnwrappingErrorTypes(builder, UnwrapAlias(symbol));
            }
 
            return builder.ToOneOrManyAndFree();
        }
 
        // This is used by other binding APIs to invoke the right binder API
        internal virtual BoundNode Bind(Binder binder, CSharpSyntaxNode node, BindingDiagnosticBag diagnostics)
        {
            if (Compilation.TestOnlyCompilationData is MemberSemanticModel.MemberSemanticBindingCounter counter)
            {
                counter.BindCount++;
            }
 
            switch (node)
            {
                case ExpressionSyntax expression:
                    var parent = expression.Parent;
                    return parent.IsKind(SyntaxKind.GotoStatement)
                        ? binder.BindLabel(expression, diagnostics)
                        : binder.BindNamespaceOrTypeOrExpression(expression, diagnostics);
                case StatementSyntax statement:
                    return binder.BindStatement(statement, diagnostics);
                case GlobalStatementSyntax globalStatement:
                    BoundStatement bound = binder.BindStatement(globalStatement.Statement, diagnostics);
                    return new BoundGlobalStatementInitializer(node, bound);
            }
 
            return null;
        }
 
        /// <summary>
        /// Analyze control-flow within a part of a method body. 
        /// </summary>
        /// <param name="firstStatement">The first statement to be included in the analysis.</param>
        /// <param name="lastStatement">The last statement to be included in the analysis.</param>
        /// <returns>An object that can be used to obtain the result of the control flow analysis.</returns>
        /// <exception cref="ArgumentException">The two statements are not contained within the same statement list.</exception>
        public virtual ControlFlowAnalysis AnalyzeControlFlow(StatementSyntax firstStatement, StatementSyntax lastStatement)
        {
            // Only supported on a SyntaxTreeSemanticModel.
            throw new NotSupportedException();
        }
 
        /// <summary>
        /// Analyze control-flow within a part of a method body. 
        /// </summary>
        /// <param name="statement">The statement to be included in the analysis.</param>
        /// <returns>An object that can be used to obtain the result of the control flow analysis.</returns>
        public virtual ControlFlowAnalysis AnalyzeControlFlow(StatementSyntax statement)
        {
            return AnalyzeControlFlow(statement, statement);
        }
 
        /// <summary>
        /// Analyze data-flow within an <see cref="ConstructorInitializerSyntax"/>. 
        /// </summary>
        /// <param name="constructorInitializer">The ctor-init within the associated SyntaxTree to analyze.</param>
        /// <returns>An object that can be used to obtain the result of the data flow analysis.</returns>
        public virtual DataFlowAnalysis AnalyzeDataFlow(ConstructorInitializerSyntax constructorInitializer)
        {
            // Only supported on a SyntaxTreeSemanticModel.
            throw new NotSupportedException();
        }
 
        /// <summary>
        /// Analyze data-flow within an <see cref="PrimaryConstructorBaseTypeSyntax.ArgumentList"/>. 
        /// </summary>
        /// <param name="primaryConstructorBaseType">The node within the associated SyntaxTree to analyze.</param>
        /// <returns>An object that can be used to obtain the result of the data flow analysis.</returns>
        public virtual DataFlowAnalysis AnalyzeDataFlow(PrimaryConstructorBaseTypeSyntax primaryConstructorBaseType)
        {
            // Only supported on a SyntaxTreeSemanticModel.
            throw new NotSupportedException();
        }
 
        /// <summary>
        /// Analyze data-flow within an <see cref="ExpressionSyntax"/>. 
        /// </summary>
        /// <param name="expression">The expression within the associated SyntaxTree to analyze.</param>
        /// <returns>An object that can be used to obtain the result of the data flow analysis.</returns>
        public virtual DataFlowAnalysis AnalyzeDataFlow(ExpressionSyntax expression)
        {
            // Only supported on a SyntaxTreeSemanticModel.
            throw new NotSupportedException();
        }
 
        /// <summary>
        /// Analyze data-flow within a part of a method body. 
        /// </summary>
        /// <param name="firstStatement">The first statement to be included in the analysis.</param>
        /// <param name="lastStatement">The last statement to be included in the analysis.</param>
        /// <returns>An object that can be used to obtain the result of the data flow analysis.</returns>
        /// <exception cref="ArgumentException">The two statements are not contained within the same statement list.</exception>
        public virtual DataFlowAnalysis AnalyzeDataFlow(StatementSyntax firstStatement, StatementSyntax lastStatement)
        {
            // Only supported on a SyntaxTreeSemanticModel.
            throw new NotSupportedException();
        }
 
        /// <summary>
        /// Analyze data-flow within a part of a method body. 
        /// </summary>
        /// <param name="statement">The statement to be included in the analysis.</param>
        /// <returns>An object that can be used to obtain the result of the data flow analysis.</returns>
        public virtual DataFlowAnalysis AnalyzeDataFlow(StatementSyntax statement)
        {
            return AnalyzeDataFlow(statement, statement);
        }
 
        /// <summary>
        /// Get a SemanticModel object that is associated with a method body that did not appear in this source code.
        /// Given <paramref name="position"/> must lie within an existing method body of the Root syntax node for this SemanticModel.
        /// Locals and labels declared within this existing method body are not considered to be in scope of the speculated method body.
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and accessibility. This
        /// character position must be within the FullSpan of the Root syntax node in this SemanticModel and must be
        /// within the FullSpan of a Method body within the Root syntax node.</param>
        /// <param name="method">A syntax node that represents a parsed method declaration. This method should not be
        /// present in the syntax tree associated with this object, but must have identical signature to the method containing
        /// the given <paramref name="position"/> in this SemanticModel.</param>
        /// <param name="speculativeModel">A SemanticModel object that can be used to inquire about the semantic
        /// information associated with syntax nodes within <paramref name="method"/>.</param>
        /// <returns>Flag indicating whether a speculative semantic model was created.</returns>
        /// <exception cref="ArgumentException">Throws this exception if the <paramref name="method"/> node is contained any SyntaxTree in the current Compilation</exception>
        /// <exception cref="ArgumentNullException">Throws this exception if <paramref name="method"/> is null.</exception>
        /// <exception cref="InvalidOperationException">Throws this exception if this model is a speculative semantic model, i.e. <see cref="SemanticModel.IsSpeculativeSemanticModel"/> is true.
        /// Chaining of speculative semantic model is not supported.</exception>
        public bool TryGetSpeculativeSemanticModelForMethodBody(int position, BaseMethodDeclarationSyntax method, out SemanticModel speculativeModel)
        {
            CheckModelAndSyntaxNodeToSpeculate(method);
            var result = TryGetSpeculativeSemanticModelForMethodBodyCore((SyntaxTreeSemanticModel)this, position, method, out PublicSemanticModel speculativeSyntaxTreeModel);
            speculativeModel = speculativeSyntaxTreeModel;
            return result;
        }
 
        internal abstract bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out PublicSemanticModel speculativeModel);
 
        /// <summary>
        /// Get a SemanticModel object that is associated with a method body that did not appear in this source code.
        /// Given <paramref name="position"/> must lie within an existing method body of the Root syntax node for this SemanticModel.
        /// Locals and labels declared within this existing method body are not considered to be in scope of the speculated method body.
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and accessibility. This
        /// character position must be within the FullSpan of the Root syntax node in this SemanticModel and must be
        /// within the FullSpan of a Method body within the Root syntax node.</param>
        /// <param name="accessor">A syntax node that represents a parsed accessor declaration. This accessor should not be
        /// present in the syntax tree associated with this object.</param>
        /// <param name="speculativeModel">A SemanticModel object that can be used to inquire about the semantic
        /// information associated with syntax nodes within <paramref name="accessor"/>.</param>
        /// <returns>Flag indicating whether a speculative semantic model was created.</returns>
        /// <exception cref="ArgumentException">Throws this exception if the <paramref name="accessor"/> node is contained any SyntaxTree in the current Compilation</exception>
        /// <exception cref="ArgumentNullException">Throws this exception if <paramref name="accessor"/> is null.</exception>
        /// <exception cref="InvalidOperationException">Throws this exception if this model is a speculative semantic model, i.e. <see cref="SemanticModel.IsSpeculativeSemanticModel"/> is true.
        /// Chaining of speculative semantic model is not supported.</exception>
        public bool TryGetSpeculativeSemanticModelForMethodBody(int position, AccessorDeclarationSyntax accessor, out SemanticModel speculativeModel)
        {
            CheckModelAndSyntaxNodeToSpeculate(accessor);
            var result = TryGetSpeculativeSemanticModelForMethodBodyCore((SyntaxTreeSemanticModel)this, position, accessor, out PublicSemanticModel speculativeSyntaxTreeModel);
            speculativeModel = speculativeSyntaxTreeModel;
            return result;
        }
 
        internal abstract bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, AccessorDeclarationSyntax accessor, out PublicSemanticModel speculativeModel);
 
        /// <summary>
        /// Get a SemanticModel object that is associated with a type syntax node that did not appear in
        /// this source code. This can be used to get detailed semantic information about sub-parts
        /// of a type syntax that did not appear in source code. 
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and accessibility. This
        /// character position must be within the FullSpan of the Root syntax node in this SemanticModel.
        /// </param>
        /// <param name="type">A syntax node that represents a parsed expression. This expression should not be
        /// present in the syntax tree associated with this object.</param>
        /// <param name="bindingOption">Indicates whether to bind the expression as a full expression,
        /// or as a type or namespace.</param>
        /// <param name="speculativeModel">A SemanticModel object that can be used to inquire about the semantic
        /// information associated with syntax nodes within <paramref name="type"/>.</param>
        /// <returns>Flag indicating whether a speculative semantic model was created.</returns>
        /// <exception cref="ArgumentException">Throws this exception if the <paramref name="type"/> node is contained any SyntaxTree in the current Compilation</exception>
        /// <exception cref="ArgumentNullException">Throws this exception if <paramref name="type"/> is null.</exception>
        /// <exception cref="InvalidOperationException">Throws this exception if this model is a speculative semantic model, i.e. <see cref="SemanticModel.IsSpeculativeSemanticModel"/> is true.
        /// Chaining of speculative semantic model is not supported.</exception>
        public bool TryGetSpeculativeSemanticModel(int position, TypeSyntax type, out SemanticModel speculativeModel, SpeculativeBindingOption bindingOption = SpeculativeBindingOption.BindAsExpression)
        {
            CheckModelAndSyntaxNodeToSpeculate(type);
            var result = TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, type, bindingOption, out PublicSemanticModel speculativeSyntaxTreeModel);
            speculativeModel = speculativeSyntaxTreeModel;
            return result;
        }
 
        internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, TypeSyntax type, SpeculativeBindingOption bindingOption, out PublicSemanticModel speculativeModel);
 
        /// <summary>
        /// Get a SemanticModel object that is associated with a statement that did not appear in
        /// this source code. This can be used to get detailed semantic information about sub-parts
        /// of a statement that did not appear in source code. 
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and accessibility. This
        /// character position must be within the FullSpan of the Root syntax node in this SemanticModel.</param>
        /// <param name="statement">A syntax node that represents a parsed statement. This statement should not be
        /// present in the syntax tree associated with this object.</param>
        /// <param name="speculativeModel">A SemanticModel object that can be used to inquire about the semantic
        /// information associated with syntax nodes within <paramref name="statement"/>.</param>
        /// <returns>Flag indicating whether a speculative semantic model was created.</returns>
        /// <exception cref="ArgumentException">Throws this exception if the <paramref name="statement"/> node is contained any SyntaxTree in the current Compilation</exception>
        /// <exception cref="ArgumentNullException">Throws this exception if <paramref name="statement"/> is null.</exception>
        /// <exception cref="InvalidOperationException">Throws this exception if this model is a speculative semantic model, i.e. <see cref="SemanticModel.IsSpeculativeSemanticModel"/> is true.
        /// Chaining of speculative semantic model is not supported.</exception>
        public bool TryGetSpeculativeSemanticModel(int position, StatementSyntax statement, out SemanticModel speculativeModel)
        {
            CheckModelAndSyntaxNodeToSpeculate(statement);
            var result = TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, statement, out PublicSemanticModel speculativeSyntaxTreeModel);
            speculativeModel = speculativeSyntaxTreeModel;
            return result;
        }
 
        internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, StatementSyntax statement, out PublicSemanticModel speculativeModel);
 
        /// <summary>
        /// Get a SemanticModel object that is associated with an initializer that did not appear in
        /// this source code. This can be used to get detailed semantic information about sub-parts
        /// of a field initializer or default parameter value that did not appear in source code.
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and accessibility. This
        /// character position must be within the FullSpan of the Root syntax node in this SemanticModel.
        /// </param>
        /// <param name="initializer">A syntax node that represents a parsed initializer. This initializer should not be
        /// present in the syntax tree associated with this object.</param>
        /// <param name="speculativeModel">A SemanticModel object that can be used to inquire about the semantic
        /// information associated with syntax nodes within <paramref name="initializer"/>.</param>
        /// <returns>Flag indicating whether a speculative semantic model was created.</returns>
        /// <exception cref="ArgumentException">Throws this exception if the <paramref name="initializer"/> node is contained any SyntaxTree in the current Compilation.</exception>
        /// <exception cref="ArgumentNullException">Throws this exception if <paramref name="initializer"/> is null.</exception>
        /// <exception cref="InvalidOperationException">Throws this exception if this model is a speculative semantic model, i.e. <see cref="SemanticModel.IsSpeculativeSemanticModel"/> is true.
        /// Chaining of speculative semantic model is not supported.</exception>
        public bool TryGetSpeculativeSemanticModel(int position, EqualsValueClauseSyntax initializer, out SemanticModel speculativeModel)
        {
            CheckModelAndSyntaxNodeToSpeculate(initializer);
            var result = TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, initializer, out PublicSemanticModel speculativeSyntaxTreeModel);
            speculativeModel = speculativeSyntaxTreeModel;
            return result;
        }
 
        internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out PublicSemanticModel speculativeModel);
 
        /// <summary>
        /// Get a SemanticModel object that is associated with an expression body that did not appear in
        /// this source code. This can be used to get detailed semantic information about sub-parts
        /// of an expression body that did not appear in source code.
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and accessibility. This
        /// character position must be within the FullSpan of the Root syntax node in this SemanticModel.
        /// </param>
        /// <param name="expressionBody">A syntax node that represents a parsed expression body. This node should not be
        /// present in the syntax tree associated with this object.</param>
        /// <param name="speculativeModel">A SemanticModel object that can be used to inquire about the semantic
        /// information associated with syntax nodes within <paramref name="expressionBody"/>.</param>
        /// <returns>Flag indicating whether a speculative semantic model was created.</returns>
        /// <exception cref="ArgumentException">Throws this exception if the <paramref name="expressionBody"/> node is contained any SyntaxTree in the current Compilation.</exception>
        /// <exception cref="ArgumentNullException">Throws this exception if <paramref name="expressionBody"/> is null.</exception>
        /// <exception cref="InvalidOperationException">Throws this exception if this model is a speculative semantic model, i.e. <see cref="SemanticModel.IsSpeculativeSemanticModel"/> is true.
        /// Chaining of speculative semantic model is not supported.</exception>
        public bool TryGetSpeculativeSemanticModel(int position, ArrowExpressionClauseSyntax expressionBody, out SemanticModel speculativeModel)
        {
            CheckModelAndSyntaxNodeToSpeculate(expressionBody);
            var result = TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, expressionBody, out PublicSemanticModel speculativeSyntaxTreeModel);
            speculativeModel = speculativeSyntaxTreeModel;
            return result;
        }
 
        internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out PublicSemanticModel speculativeModel);
 
        /// <summary>
        /// Get a SemanticModel object that is associated with a constructor initializer that did not appear in
        /// this source code. This can be used to get detailed semantic information about sub-parts
        /// of a constructor initializer that did not appear in source code. 
        /// 
        /// NOTE: This will only work in locations where there is already a constructor initializer.
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and accessibility. This
        /// character position must be within the FullSpan of the Root syntax node in this SemanticModel.
        /// Furthermore, it must be within the span of an existing constructor initializer.
        /// </param>
        /// <param name="constructorInitializer">A syntax node that represents a parsed constructor initializer.
        /// This node should not be present in the syntax tree associated with this object.</param>
        /// <param name="speculativeModel">A SemanticModel object that can be used to inquire about the semantic
        /// information associated with syntax nodes within <paramref name="constructorInitializer"/>.</param>
        /// <returns>Flag indicating whether a speculative semantic model was created.</returns>
        /// <exception cref="ArgumentException">Throws this exception if the <paramref name="constructorInitializer"/> node is contained any SyntaxTree in the current Compilation.</exception>
        /// <exception cref="ArgumentNullException">Throws this exception if <paramref name="constructorInitializer"/> is null.</exception>
        /// <exception cref="InvalidOperationException">Throws this exception if this model is a speculative semantic model, i.e. <see cref="SemanticModel.IsSpeculativeSemanticModel"/> is true.
        /// Chaining of speculative semantic model is not supported.</exception>
        public bool TryGetSpeculativeSemanticModel(int position, ConstructorInitializerSyntax constructorInitializer, out SemanticModel speculativeModel)
        {
            CheckModelAndSyntaxNodeToSpeculate(constructorInitializer);
            var result = TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, constructorInitializer, out PublicSemanticModel speculativeSyntaxTreeModel);
            speculativeModel = speculativeSyntaxTreeModel;
            return result;
        }
 
        internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out PublicSemanticModel speculativeModel);
 
        /// <summary>
        /// Get a SemanticModel object that is associated with a constructor initializer that did not appear in
        /// this source code. This can be used to get detailed semantic information about sub-parts
        /// of a constructor initializer that did not appear in source code. 
        /// 
        /// NOTE: This will only work in locations where there is already a constructor initializer.
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and accessibility. This
        /// character position must be within the span of an existing constructor initializer.
        /// </param>
        /// <param name="constructorInitializer">A syntax node that represents a parsed constructor initializer.
        /// This node should not be present in the syntax tree associated with this object.</param>
        /// <param name="speculativeModel">A SemanticModel object that can be used to inquire about the semantic
        /// information associated with syntax nodes within <paramref name="constructorInitializer"/>.</param>
        /// <returns>Flag indicating whether a speculative semantic model was created.</returns>
        /// <exception cref="ArgumentException">Throws this exception if the <paramref name="constructorInitializer"/> node is contained any SyntaxTree in the current Compilation.</exception>
        /// <exception cref="ArgumentNullException">Throws this exception if <paramref name="constructorInitializer"/> is null.</exception>
        /// <exception cref="InvalidOperationException">Throws this exception if this model is a speculative semantic model, i.e. <see cref="SemanticModel.IsSpeculativeSemanticModel"/> is true.
        /// Chaining of speculative semantic model is not supported.</exception>
        public bool TryGetSpeculativeSemanticModel(int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel speculativeModel)
        {
            CheckModelAndSyntaxNodeToSpeculate(constructorInitializer);
            var result = TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, constructorInitializer, out PublicSemanticModel speculativeSyntaxTreeModel);
            speculativeModel = speculativeSyntaxTreeModel;
            return result;
        }
 
        internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out PublicSemanticModel speculativeModel);
 
        /// <summary>
        /// Get a SemanticModel object that is associated with a cref that did not appear in
        /// this source code. This can be used to get detailed semantic information about sub-parts
        /// of a cref that did not appear in source code. 
        /// 
        /// NOTE: This will only work in locations where there is already a cref.
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and accessibility. This
        /// character position must be within the FullSpan of the Root syntax node in this SemanticModel.
        /// Furthermore, it must be within the span of an existing cref.
        /// </param>
        /// <param name="crefSyntax">A syntax node that represents a parsed cref syntax.
        /// This node should not be present in the syntax tree associated with this object.</param>
        /// <param name="speculativeModel">A SemanticModel object that can be used to inquire about the semantic
        /// information associated with syntax nodes within <paramref name="crefSyntax"/>.</param>
        /// <returns>Flag indicating whether a speculative semantic model was created.</returns>
        /// <exception cref="ArgumentException">Throws this exception if the <paramref name="crefSyntax"/> node is contained any SyntaxTree in the current Compilation.</exception>
        /// <exception cref="ArgumentNullException">Throws this exception if <paramref name="crefSyntax"/> is null.</exception>
        /// <exception cref="InvalidOperationException">Throws this exception if this model is a speculative semantic model, i.e. <see cref="SemanticModel.IsSpeculativeSemanticModel"/> is true.
        /// Chaining of speculative semantic model is not supported.</exception>
        public bool TryGetSpeculativeSemanticModel(int position, CrefSyntax crefSyntax, out SemanticModel speculativeModel)
        {
            CheckModelAndSyntaxNodeToSpeculate(crefSyntax);
            var result = TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, crefSyntax, out PublicSemanticModel speculativeSyntaxTreeModel);
            speculativeModel = speculativeSyntaxTreeModel;
            return result;
        }
 
        internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, CrefSyntax crefSyntax, out PublicSemanticModel speculativeModel);
 
        /// <summary>
        /// Get a SemanticModel object that is associated with an attribute that did not appear in
        /// this source code. This can be used to get detailed semantic information about sub-parts
        /// of an attribute that did not appear in source code. 
        /// </summary>
        /// <param name="position">A character position used to identify a declaration scope and accessibility. This
        /// character position must be within the FullSpan of the Root syntax node in this SemanticModel.</param>
        /// <param name="attribute">A syntax node that represents a parsed attribute. This attribute should not be
        /// present in the syntax tree associated with this object.</param>
        /// <param name="speculativeModel">A SemanticModel object that can be used to inquire about the semantic
        /// information associated with syntax nodes within <paramref name="attribute"/>.</param>
        /// <returns>Flag indicating whether a speculative semantic model was created.</returns>
        /// <exception cref="ArgumentException">Throws this exception if the <paramref name="attribute"/> node is contained any SyntaxTree in the current Compilation.</exception>
        /// <exception cref="ArgumentNullException">Throws this exception if <paramref name="attribute"/> is null.</exception>
        /// <exception cref="InvalidOperationException">Throws this exception if this model is a speculative semantic model, i.e. <see cref="SemanticModel.IsSpeculativeSemanticModel"/> is true.
        /// Chaining of speculative semantic model is not supported.</exception>
        public bool TryGetSpeculativeSemanticModel(int position, AttributeSyntax attribute, out SemanticModel speculativeModel)
        {
            CheckModelAndSyntaxNodeToSpeculate(attribute);
 
            var binder = GetSpeculativeBinderForAttribute(position, attribute);
            if (binder == null)
            {
                speculativeModel = null;
                return false;
            }
 
            AliasSymbol aliasOpt;
            var attributeType = (NamedTypeSymbol)binder.BindType(attribute.Name, BindingDiagnosticBag.Discarded, out aliasOpt).Type;
            speculativeModel = ((SyntaxTreeSemanticModel)this).CreateSpeculativeAttributeSemanticModel(position, attribute, binder, aliasOpt, attributeType);
            return true;
        }
 
        /// <summary>
        /// If this is a speculative semantic model, then returns its parent semantic model.
        /// Otherwise, returns null.
        /// </summary>
        public new abstract CSharpSemanticModel ParentModel
        {
            get;
        }
 
        /// <summary>
        /// The SyntaxTree that this object is associated with.
        /// </summary>
        public new abstract SyntaxTree SyntaxTree
        {
            get;
        }
 
        /// <summary>
        /// Determines what type of conversion, if any, would be used if a given expression was
        /// converted to a given type.  If isExplicitInSource is true, the conversion produced is
        /// that which would be used if the conversion were done for a cast expression.
        /// </summary>
        /// <param name="expression">An expression which much occur within the syntax tree
        /// associated with this object.</param>
        /// <param name="destination">The type to attempt conversion to.</param>
        /// <param name="isExplicitInSource">True if the conversion should be determined as for a cast expression.</param>
        /// <returns>Returns a Conversion object that summarizes whether the conversion was
        /// possible, and if so, what kind of conversion it was. If no conversion was possible, a
        /// Conversion object with a false "Exists" property is returned.</returns>
        /// <remarks>To determine the conversion between two types (instead of an expression and a
        /// type), use Compilation.ClassifyConversion.</remarks>
        public abstract Conversion ClassifyConversion(ExpressionSyntax expression, ITypeSymbol destination, bool isExplicitInSource = false);
 
        /// <summary>
        /// Determines what type of conversion, if any, would be used if a given expression was
        /// converted to a given type.  If isExplicitInSource is true, the conversion produced is
        /// that which would be used if the conversion were done for a cast expression.
        /// </summary>
        /// <param name="position">The character position for determining the enclosing declaration
        /// scope and accessibility.</param>
        /// <param name="expression">The expression to classify. This expression does not need to be
        /// present in the syntax tree associated with this object.</param>
        /// <param name="destination">The type to attempt conversion to.</param>
        /// <param name="isExplicitInSource">True if the conversion should be determined as for a cast expression.</param>
        /// <returns>Returns a Conversion object that summarizes whether the conversion was
        /// possible, and if so, what kind of conversion it was. If no conversion was possible, a
        /// Conversion object with a false "Exists" property is returned.</returns>
        /// <remarks>To determine the conversion between two types (instead of an expression and a
        /// type), use Compilation.ClassifyConversion.</remarks>
        public Conversion ClassifyConversion(int position, ExpressionSyntax expression, ITypeSymbol destination, bool isExplicitInSource = false)
        {
            if ((object)destination == null)
            {
                throw new ArgumentNullException(nameof(destination));
            }
 
            TypeSymbol cdestination = destination.EnsureCSharpSymbolOrNull(nameof(destination));
 
            if (expression.Kind() == SyntaxKind.DeclarationExpression)
            {
                // Conversion from a declaration is unspecified.
                return Conversion.NoConversion;
            }
 
            if (isExplicitInSource)
            {
                return ClassifyConversionForCast(position, expression, cdestination);
            }
 
            // 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.
 
            position = CheckAndAdjustPosition(position);
            var binder = this.GetEnclosingBinder(position);
            if (binder != null)
            {
                var bnode = binder.BindExpression(expression, BindingDiagnosticBag.Discarded);
 
                if (bnode != null && !cdestination.IsErrorType())
                {
                    var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
 
                    return binder.Conversions.ClassifyConversionFromExpression(bnode, cdestination, isChecked: binder.CheckOverflowAtRuntime, ref discardedUseSiteInfo);
                }
            }
 
            return Conversion.NoConversion;
        }
 
        /// <summary>
        /// Determines what type of conversion, if any, would be used if a given expression was
        /// converted to a given type using an explicit cast.
        /// </summary>
        /// <param name="expression">An expression which much occur within the syntax tree
        /// associated with this object.</param>
        /// <param name="destination">The type to attempt conversion to.</param>
        /// <returns>Returns a Conversion object that summarizes whether the conversion was
        /// possible, and if so, what kind of conversion it was. If no conversion was possible, a
        /// Conversion object with a false "Exists" property is returned.</returns>
        /// <remarks>To determine the conversion between two types (instead of an expression and a
        /// type), use Compilation.ClassifyConversion.</remarks>
        internal abstract Conversion ClassifyConversionForCast(ExpressionSyntax expression, TypeSymbol destination);
 
        /// <summary>
        /// Determines what type of conversion, if any, would be used if a given expression was
        /// converted to a given type using an explicit cast.
        /// </summary>
        /// <param name="position">The character position for determining the enclosing declaration
        /// scope and accessibility.</param>
        /// <param name="expression">The expression to classify. This expression does not need to be
        /// present in the syntax tree associated with this object.</param>
        /// <param name="destination">The type to attempt conversion to.</param>
        /// <returns>Returns a Conversion object that summarizes whether the conversion was
        /// possible, and if so, what kind of conversion it was. If no conversion was possible, a
        /// Conversion object with a false "Exists" property is returned.</returns>
        /// <remarks>To determine the conversion between two types (instead of an expression and a
        /// type), use Compilation.ClassifyConversion.</remarks>
        internal Conversion ClassifyConversionForCast(int position, ExpressionSyntax expression, TypeSymbol destination)
        {
            if ((object)destination == null)
            {
                throw new ArgumentNullException(nameof(destination));
            }
 
            position = CheckAndAdjustPosition(position);
            var binder = this.GetEnclosingBinder(position);
            if (binder != null)
            {
                var bnode = binder.BindExpression(expression, BindingDiagnosticBag.Discarded);
 
                if (bnode != null && !destination.IsErrorType())
                {
                    var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
 
                    return binder.Conversions.ClassifyConversionFromExpression(bnode, destination, isChecked: binder.CheckOverflowAtRuntime, ref discardedUseSiteInfo, forCast: true);
                }
            }
 
            return Conversion.NoConversion;
        }
 
        #region "GetDeclaredSymbol overloads for MemberDeclarationSyntax and its subtypes"
 
        /// <summary>
        /// Given a member declaration syntax, get the corresponding symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares a member.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        /// <remarks>
        /// NOTE:   We have no GetDeclaredSymbol overloads for following subtypes of MemberDeclarationSyntax:
        /// NOTE:   (1) GlobalStatementSyntax as they don't declare any symbols.
        /// NOTE:   (2) IncompleteMemberSyntax as there are no symbols for incomplete members.
        /// NOTE:   (3) BaseFieldDeclarationSyntax or its subtypes as these declarations can contain multiple variable declarators.
        /// NOTE:       GetDeclaredSymbol should be called on the variable declarators directly.
        /// </remarks>
        public abstract ISymbol GetDeclaredSymbol(MemberDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a local function declaration syntax, get the corresponding symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares a member.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        public abstract IMethodSymbol GetDeclaredSymbol(LocalFunctionStatementSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a compilation unit syntax, get the corresponding Simple Program entry point symbol.
        /// </summary>
        /// <param name="declarationSyntax">The compilation unit that declares the entry point member.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        public abstract IMethodSymbol GetDeclaredSymbol(CompilationUnitSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a namespace declaration syntax node, get the corresponding namespace symbol for
        /// the declaration assembly.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares a namespace.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The namespace symbol that was declared by the namespace declaration.</returns>
        public abstract INamespaceSymbol GetDeclaredSymbol(NamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a namespace declaration syntax node, get the corresponding namespace symbol for
        /// the declaration assembly.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares a namespace.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The namespace symbol that was declared by the namespace declaration.</returns>
        public abstract INamespaceSymbol GetDeclaredSymbol(FileScopedNamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a type declaration, get the corresponding type symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares a type.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The type symbol that was declared.</returns>
        /// <remarks>
        /// NOTE:   We have no GetDeclaredSymbol overloads for subtypes of BaseTypeDeclarationSyntax as all of them return a NamedTypeSymbol.
        /// </remarks>
        public abstract INamedTypeSymbol GetDeclaredSymbol(BaseTypeDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a delegate declaration, get the corresponding type symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares a delegate.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The type symbol that was declared.</returns>
        public abstract INamedTypeSymbol GetDeclaredSymbol(DelegateDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a enum member declaration, get the corresponding field symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares an enum member.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        public abstract IFieldSymbol GetDeclaredSymbol(EnumMemberDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a base method declaration syntax, get the corresponding method symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares a method.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        /// <remarks>
        /// NOTE:   We have no GetDeclaredSymbol overloads for subtypes of BaseMethodDeclarationSyntax as all of them return a MethodSymbol.
        /// </remarks>
        public abstract IMethodSymbol GetDeclaredSymbol(BaseMethodDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        #region GetDeclaredSymbol overloads for BasePropertyDeclarationSyntax and its subtypes
 
        /// <summary>
        /// Given a syntax node that declares a property, indexer or an event, get the corresponding declared symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares a property, indexer or an event.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        public abstract ISymbol GetDeclaredSymbol(BasePropertyDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a syntax node that declares a property, get the corresponding declared symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares a property.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        public abstract IPropertySymbol GetDeclaredSymbol(PropertyDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a syntax node that declares an indexer, get the corresponding declared symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares an indexer.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        public abstract IPropertySymbol GetDeclaredSymbol(IndexerDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a syntax node that declares a (custom) event, get the corresponding event symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares a event.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        public abstract IEventSymbol GetDeclaredSymbol(EventDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        #endregion
 
        #endregion
 
        // Anonymous types and Tuple expressions are an interesting case here because they declare their own types
        //
        // In both cases there is no distinct syntax that creates the type and the syntax that describes the type is the literal itself.
        // Surely - if you need to modify the anonymous type or a type of a tuple literal, you would be modifying these expressions.
        //
        // As a result we support GetDeclaredSymbol on the whole AnonymousObjectCreationExpressionSyntax/TupleExpressionSyntax.
        // The implementation returns the type of the expression.
        //
        // In addition to that GetDeclaredSymbol works on the AnonymousObjectMemberDeclaratorSyntax/ArgumentSyntax
        // The implementation returns the property/field symbol that is declared by the corresponding syntax.
        //
        // Example:
        //              GetDeclaredSymbol => Type: (int Alice, int Bob) 
        //             _____ |__________
        //            [                 ]
        // var tuple = (Alice: 1, Bob: 2);
        //                        [     ]
        //                           \GetDeclaredSymbol => Field: (int Alice, int Bob).Bob
        //
        // A special note must be made about the locations of the corresponding symbols - they refer to the actual syntax
        // of the literal or the anonymous type creation expression
        // 
        // This way IDEs can unambiguously implement such services as "Go to definition"
        //
        // I.E. GetSymbolInfo for "Bob" in "tuple.Bob" should point to the same field as returned by GetDeclaredSymbol when applied to 
        // the ArgumentSyntax "Bob: 2", since that is where the field was declared, where renames should be applied and so on.
        //                 
        //
        // In comparison to anonymous types, tuples have one special behavior. 
        // It is permitted for tuple literals to not have a natural type as long as there is a target type which determines the types of the fields.
        // As, such for the purpose of GetDeclaredSymbol, the type symbol that is returned for tuple literals has target-typed fields, 
        // but yet with the original names.
        //
        //                               GetDeclaredSymbol => Type: (string Alice, short Bob) 
        //                         ________ |__________
        //                         [                   ]
        // (string, short) tuple = (Alice: null, Bob: 2);
        //                         [           ]
        //                              \GetDeclaredSymbol => Field: (string Alice, short Bob).Alice
        //
        // In particular, the location of the field declaration is "Alice: null" and not the "string"
        //                 the location of the type is "(Alice: null, Bob: 2)" and not the "(string, short)"
        //
        // The reason for this behavior is that, even though there might not be other references to "Alice" field in the code, 
        // the name "Alice" itself evidently refers to something named "Alice" and should still work with
        // all the related APIs and services such as "Find all References", "Go to definition", "symbolic rename" etc... 
        // 
        //                         GetSymbolInfo => Field: (string Alice, short Bob).Alice 
        //                         __ |__
        //                         [     ]
        // (string, short) tuple = (Alice: null, Bob: 2);
        //
 
        /// <summary>
        /// Given a syntax node of anonymous object creation initializer, get the anonymous object property symbol.
        /// </summary>
        /// <param name="declaratorSyntax">The syntax node that declares a property.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        public abstract IPropertySymbol GetDeclaredSymbol(AnonymousObjectMemberDeclaratorSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a syntax node of anonymous object creation expression, get the anonymous object type symbol.
        /// </summary>
        /// <param name="declaratorSyntax">The syntax node that declares an anonymous object.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        public abstract INamedTypeSymbol GetDeclaredSymbol(AnonymousObjectCreationExpressionSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a syntax node of a tuple expression, get the tuple type symbol.
        /// </summary>
        /// <param name="declaratorSyntax">The tuple expression node.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        public abstract INamedTypeSymbol GetDeclaredSymbol(TupleExpressionSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a syntax node of an argument expression, get the declared symbol.
        /// </summary>
        /// <param name="declaratorSyntax">The argument syntax node.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        /// <remarks>
        /// Generally ArgumentSyntax nodes do not declare symbols, except when used as arguments of a tuple literal.
        /// Example:  var x = (Alice: 1, Bob: 2);
        ///           ArgumentSyntax "Alice: 1" declares a tuple element field "(int Alice, int Bob).Alice"
        /// </remarks>
        public abstract ISymbol GetDeclaredSymbol(ArgumentSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a syntax node that declares a property or member accessor, get the corresponding
        /// symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares an accessor.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        public abstract IMethodSymbol GetDeclaredSymbol(AccessorDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a syntax node that declares an expression body, get the corresponding symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares an expression body.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        public abstract IMethodSymbol GetDeclaredSymbol(ArrowExpressionClauseSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a variable declarator syntax, get the corresponding symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares a variable.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        public abstract ISymbol GetDeclaredSymbol(VariableDeclaratorSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a variable designation syntax, get the corresponding symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares a variable.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        public abstract ISymbol GetDeclaredSymbol(SingleVariableDesignationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a labeled statement syntax, get the corresponding label symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node of the labeled statement.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The label symbol for that label.</returns>
        public abstract ILabelSymbol GetDeclaredSymbol(LabeledStatementSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a switch label syntax, get the corresponding label symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node of the switch label.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The label symbol for that label.</returns>
        public abstract ILabelSymbol GetDeclaredSymbol(SwitchLabelSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a using declaration get the corresponding symbol for the using alias that was
        /// introduced.
        /// </summary>
        /// <param name="declarationSyntax"></param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The alias symbol that was declared.</returns>
        /// <remarks>
        /// If the using directive is an error because it attempts to introduce an alias for which an existing alias was
        /// previously declared in the same scope, the result is a newly-constructed AliasSymbol (i.e. not one from the
        /// symbol table).
        /// </remarks>
        public abstract IAliasSymbol GetDeclaredSymbol(UsingDirectiveSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given an extern alias declaration get the corresponding symbol for the alias that was introduced.
        /// </summary>
        /// <param name="declarationSyntax"></param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The alias symbol that was declared, or null if a duplicate alias symbol was declared.</returns>
        public abstract IAliasSymbol GetDeclaredSymbol(ExternAliasDirectiveSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a parameter declaration syntax node, get the corresponding symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares a parameter.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The parameter that was declared.</returns>
        public abstract IParameterSymbol GetDeclaredSymbol(ParameterSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Given a base field declaration syntax, get the corresponding symbols.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares one or more fields or events.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbols that were declared.</returns>
        internal abstract ImmutableArray<ISymbol> GetDeclaredSymbols(BaseFieldDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken));
 
        protected ParameterSymbol GetParameterSymbol(
            ImmutableArray<ParameterSymbol> parameters,
            ParameterSyntax parameter,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            foreach (var symbol in parameters)
            {
                cancellationToken.ThrowIfCancellationRequested();
 
                foreach (var location in symbol.Locations)
                {
                    cancellationToken.ThrowIfCancellationRequested();
 
                    if (location.SourceTree == this.SyntaxTree && parameter.Span.Contains(location.SourceSpan))
                    {
                        return symbol;
                    }
                }
            }
 
            return null;
        }
 
        /// <summary>
        /// Given a type parameter declaration (field or method), get the corresponding symbol
        /// </summary>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <param name="typeParameter"></param>
        public abstract ITypeParameterSymbol GetDeclaredSymbol(TypeParameterSyntax typeParameter, CancellationToken cancellationToken = default(CancellationToken));
 
        internal BinderFlags GetSemanticModelBinderFlags()
        {
            return this.IgnoresAccessibility
                ? BinderFlags.SemanticModel | BinderFlags.IgnoreAccessibility
                : BinderFlags.SemanticModel;
        }
 
        /// <summary>
        /// Given a foreach statement, get the symbol for the iteration variable
        /// </summary>
        /// <param name="forEachStatement"></param>
        public ILocalSymbol GetDeclaredSymbol(ForEachStatementSyntax forEachStatement)
        {
            Binder enclosingBinder = this.GetEnclosingBinder(GetAdjustedNodePosition(forEachStatement));
 
            if (enclosingBinder == null)
            {
                return null;
            }
 
            Binder foreachBinder = enclosingBinder.GetBinder(forEachStatement);
 
            // Binder.GetBinder can fail in presence of syntax errors. 
            if (foreachBinder == null)
            {
                return null;
            }
 
            LocalSymbol local = foreachBinder.GetDeclaredLocalsForScope(forEachStatement).FirstOrDefault();
            return (local is SourceLocalSymbol { DeclarationKind: LocalDeclarationKind.ForEachIterationVariable } sourceLocal
                ? GetAdjustedLocalSymbol(sourceLocal)
                : local).GetPublicSymbol();
        }
 
        /// <summary>
        /// Given a local symbol, gets an updated version of that local symbol adjusted for nullability analysis
        /// if the analysis affects the local.
        /// </summary>
        /// <param name="originalSymbol">The original symbol from initial binding.</param>
        /// 
        /// <returns>The nullability-adjusted local, or the original symbol if the nullability analysis made no adjustments or was not run.</returns>
        internal abstract LocalSymbol GetAdjustedLocalSymbol(SourceLocalSymbol originalSymbol);
 
        /// <summary>
        /// Given a catch declaration, get the symbol for the exception variable
        /// </summary>
        /// <param name="catchDeclaration"></param>
        public ILocalSymbol GetDeclaredSymbol(CatchDeclarationSyntax catchDeclaration)
        {
            CSharpSyntaxNode catchClause = catchDeclaration.Parent; //Syntax->Binder map is keyed on clause, not decl
            Debug.Assert(catchClause.Kind() == SyntaxKind.CatchClause);
            Binder enclosingBinder = this.GetEnclosingBinder(GetAdjustedNodePosition(catchClause));
 
            if (enclosingBinder == null)
            {
                return null;
            }
 
            Binder catchBinder = enclosingBinder.GetBinder(catchClause);
 
            // Binder.GetBinder can fail in presence of syntax errors. 
            if (catchBinder == null)
            {
                return null;
            }
 
            catchBinder = enclosingBinder.GetBinder(catchClause);
            LocalSymbol local = catchBinder.GetDeclaredLocalsForScope(catchClause).FirstOrDefault();
            return ((object)local != null && local.DeclarationKind == LocalDeclarationKind.CatchVariable)
                ? local.GetPublicSymbol()
                : null;
        }
 
        public abstract IRangeVariableSymbol GetDeclaredSymbol(QueryClauseSyntax queryClause, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Get the query range variable declared in a join into clause.
        /// </summary>
        public abstract IRangeVariableSymbol GetDeclaredSymbol(JoinIntoClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken));
 
        /// <summary>
        /// Get the query range variable declared in a query continuation clause.
        /// </summary>
        public abstract IRangeVariableSymbol GetDeclaredSymbol(QueryContinuationSyntax node, CancellationToken cancellationToken = default(CancellationToken));
 
        // Get the symbols and possible method or property group associated with a bound node, as
        // they should be exposed through GetSemanticInfo.
        // NB: It is not safe to pass a null binderOpt during speculative binding.
        private OneOrMany<Symbol> GetSemanticSymbols(
            BoundExpression boundNode,
            BoundNode boundNodeForSyntacticParent,
            Binder binderOpt,
            SymbolInfoOptions options,
            out bool isDynamic,
            out LookupResultKind resultKind,
            out ImmutableArray<Symbol> memberGroup)
        {
            memberGroup = ImmutableArray<Symbol>.Empty;
            OneOrMany<Symbol> symbols = OneOrMany<Symbol>.Empty;
            resultKind = LookupResultKind.Viable;
            isDynamic = false;
 
            switch (boundNode.Kind)
            {
                case BoundKind.MethodGroup:
                    symbols = GetMethodGroupSemanticSymbols((BoundMethodGroup)boundNode, boundNodeForSyntacticParent, binderOpt, out resultKind, out isDynamic, out memberGroup);
                    break;
 
                case BoundKind.PropertyGroup:
                    symbols = GetPropertyGroupSemanticSymbols((BoundPropertyGroup)boundNode, boundNodeForSyntacticParent, binderOpt, out resultKind, out memberGroup);
                    break;
 
                case BoundKind.BadExpression:
                    {
                        var expr = (BoundBadExpression)boundNode;
                        resultKind = expr.ResultKind;
 
                        if (expr.Syntax.Kind() is SyntaxKind.ObjectCreationExpression or SyntaxKind.ImplicitObjectCreationExpression)
                        {
                            if (resultKind == LookupResultKind.NotCreatable)
                            {
                                return OneOrMany.Create(expr.Symbols);
                            }
                            else if (expr.Type.IsDelegateType())
                            {
                                resultKind = LookupResultKind.Empty;
                                return symbols;
                            }
 
                            memberGroup = expr.Symbols;
                        }
 
                        return OneOrMany.Create(expr.Symbols);
                    }
 
                case BoundKind.DelegateCreationExpression:
                    break;
 
                case BoundKind.TypeExpression:
                    {
                        var boundType = (BoundTypeExpression)boundNode;
 
                        // Watch out for not creatable types within object creation syntax
                        if (boundNodeForSyntacticParent != null &&
                           boundNodeForSyntacticParent.Syntax.Kind() == SyntaxKind.ObjectCreationExpression &&
                           ((ObjectCreationExpressionSyntax)boundNodeForSyntacticParent.Syntax).Type == boundType.Syntax &&
                           boundNodeForSyntacticParent.Kind == BoundKind.BadExpression &&
                           ((BoundBadExpression)boundNodeForSyntacticParent).ResultKind == LookupResultKind.NotCreatable)
                        {
                            resultKind = LookupResultKind.NotCreatable;
                        }
 
                        // could be a type or alias.
                        var typeSymbol = boundType.AliasOpt ?? (Symbol)boundType.Type;
 
                        var originalErrorType = typeSymbol.OriginalDefinition as ErrorTypeSymbol;
                        if ((object)originalErrorType != null)
                        {
                            resultKind = originalErrorType.ResultKind;
                            symbols = OneOrMany.Create(originalErrorType.CandidateSymbols);
                        }
                        else
                        {
                            symbols = OneOrMany.Create(typeSymbol);
                        }
                    }
                    break;
 
                case BoundKind.TypeOrValueExpression:
                    {
                        // If we're seeing a node of this kind, then we failed to resolve the member access
                        // as either a type or a property/field/event/local/parameter.  In such cases,
                        // the second interpretation applies so just visit the node for that.
                        BoundExpression valueExpression = ((BoundTypeOrValueExpression)boundNode).Data.ValueExpression;
                        return GetSemanticSymbols(valueExpression, boundNodeForSyntacticParent, binderOpt, options, out isDynamic, out resultKind, out memberGroup);
                    }
 
                case BoundKind.Call:
                    {
                        // Either overload resolution succeeded for this call or it did not. If it
                        // did not succeed then we've stashed the original method symbols from the
                        // method group, and we should use those as the symbols displayed for the
                        // call. If it did succeed then we did not stash any symbols; just fall
                        // through to the default case.
 
                        var call = (BoundCall)boundNode;
                        if (call.OriginalMethodsOpt.IsDefault)
                        {
                            if ((object)call.Method != null)
                            {
                                symbols = CreateReducedExtensionMethodIfPossible(call);
                                resultKind = call.ResultKind;
                            }
                        }
                        else
                        {
                            symbols = StaticCast<Symbol>.From(CreateReducedExtensionMethodsFromOriginalsIfNecessary(call, Compilation));
                            resultKind = call.ResultKind;
                        }
                    }
                    break;
 
                case BoundKind.FunctionPointerInvocation:
                    {
                        var invocation = (BoundFunctionPointerInvocation)boundNode;
                        symbols = OneOrMany.Create<Symbol>(invocation.FunctionPointer);
                        resultKind = invocation.ResultKind;
                        break;
                    }
 
                case BoundKind.UnconvertedAddressOfOperator:
                    {
                        // We try to match the results given for a similar piece of syntax here: bad invocations.
                        // A BoundUnconvertedAddressOfOperator represents this syntax: &M
                        // Similarly, a BoundCall for a bad invocation represents this syntax: M(args)
                        // Calling GetSymbolInfo on the syntax will return an array of candidate symbols that were
                        // looked up, but calling GetMemberGroup will return an empty array. So, we ignore the member
                        // group result in the call below.
                        symbols = GetMethodGroupSemanticSymbols(
                            ((BoundUnconvertedAddressOfOperator)boundNode).Operand,
                            boundNodeForSyntacticParent, binderOpt, out resultKind, out isDynamic, methodGroup: out _);
                        break;
                    }
 
                case BoundKind.IndexerAccess:
                    {
                        // As for BoundCall, pull out stashed candidates if overload resolution failed.
 
                        BoundIndexerAccess indexerAccess = (BoundIndexerAccess)boundNode;
                        Debug.Assert((object)indexerAccess.Indexer != null);
 
                        resultKind = indexerAccess.ResultKind;
 
                        ImmutableArray<PropertySymbol> originalIndexersOpt = indexerAccess.OriginalIndexersOpt;
                        symbols = originalIndexersOpt.IsDefault ? OneOrMany.Create<Symbol>(indexerAccess.Indexer) : StaticCast<Symbol>.From(OneOrMany.Create(originalIndexersOpt));
                    }
                    break;
 
                case BoundKind.ImplicitIndexerAccess:
                    return GetSemanticSymbols(((BoundImplicitIndexerAccess)boundNode).IndexerOrSliceAccess,
                        boundNodeForSyntacticParent, binderOpt, options, out isDynamic, out resultKind, out memberGroup);
 
                case BoundKind.EventAssignmentOperator:
                    var eventAssignment = (BoundEventAssignmentOperator)boundNode;
                    isDynamic = eventAssignment.IsDynamic;
                    var eventSymbol = eventAssignment.Event;
                    var methodSymbol = eventAssignment.IsAddition ? eventSymbol.AddMethod : eventSymbol.RemoveMethod;
                    if ((object)methodSymbol == null)
                    {
                        symbols = OneOrMany<Symbol>.Empty;
                        resultKind = LookupResultKind.Empty;
                    }
                    else
                    {
                        symbols = OneOrMany.Create<Symbol>(methodSymbol);
                        resultKind = eventAssignment.ResultKind;
                    }
                    break;
 
                case BoundKind.EventAccess when boundNodeForSyntacticParent is BoundEventAssignmentOperator { ResultKind: LookupResultKind.Viable } parentOperator &&
                                                boundNode.ExpressionSymbol is Symbol accessSymbol &&
                                                boundNode != parentOperator.Argument &&
                                                parentOperator.Event.Equals(accessSymbol, TypeCompareKind.AllNullableIgnoreOptions):
                    // When we're looking at the left-hand side of an event assignment, we synthesize a BoundEventAccess node. This node does not have
                    // nullability information, however, so if we're in that case then we need to grab the event symbol from the parent event assignment
                    // which does have the nullability-reinferred symbol
                    symbols = OneOrMany.Create<Symbol>(parentOperator.Event);
                    resultKind = parentOperator.ResultKind;
                    break;
 
                case BoundKind.Conversion:
                    var conversion = (BoundConversion)boundNode;
                    isDynamic = conversion.ConversionKind.IsDynamic();
                    if (!isDynamic)
                    {
                        if ((conversion.ConversionKind == ConversionKind.MethodGroup) && conversion.IsExtensionMethod)
                        {
                            var symbol = conversion.SymbolOpt;
                            Debug.Assert((object)symbol != null);
                            symbols = OneOrMany.Create<Symbol>(ReducedExtensionMethodSymbol.Create(symbol));
                            resultKind = conversion.ResultKind;
                        }
                        else if (conversion.ConversionKind.IsUserDefinedConversion())
                        {
                            GetSymbolsAndResultKind(conversion, conversion.SymbolOpt, conversion.OriginalUserDefinedConversionsOpt, out symbols, out resultKind);
                        }
                        else
                        {
                            goto default;
                        }
                    }
                    break;
 
                case BoundKind.BinaryOperator:
                    GetSymbolsAndResultKind((BoundBinaryOperator)boundNode, out isDynamic, ref resultKind, ref symbols);
                    break;
 
                case BoundKind.UnaryOperator:
                    GetSymbolsAndResultKind((BoundUnaryOperator)boundNode, out isDynamic, ref resultKind, ref symbols);
                    break;
 
                case BoundKind.UserDefinedConditionalLogicalOperator:
                    var @operator = (BoundUserDefinedConditionalLogicalOperator)boundNode;
                    isDynamic = false;
                    GetSymbolsAndResultKind(@operator, @operator.LogicalOperator, @operator.OriginalUserDefinedOperatorsOpt, out symbols, out resultKind);
                    break;
 
                case BoundKind.CompoundAssignmentOperator:
                    GetSymbolsAndResultKind((BoundCompoundAssignmentOperator)boundNode, out isDynamic, ref resultKind, ref symbols);
                    break;
 
                case BoundKind.IncrementOperator:
                    GetSymbolsAndResultKind((BoundIncrementOperator)boundNode, out isDynamic, ref resultKind, ref symbols);
                    break;
 
                case BoundKind.AwaitExpression:
                    var await = (BoundAwaitExpression)boundNode;
                    isDynamic = await.AwaitableInfo.IsDynamic;
                    goto default;
 
                case BoundKind.ConditionalOperator:
                    var conditional = (BoundConditionalOperator)boundNode;
                    Debug.Assert(conditional.ExpressionSymbol is null);
                    isDynamic = conditional.IsDynamic;
                    goto default;
 
                case BoundKind.Attribute:
                    {
                        Debug.Assert(boundNodeForSyntacticParent == null);
                        var attribute = (BoundAttribute)boundNode;
                        resultKind = attribute.ResultKind;
 
                        // If attribute name bound to a single named type or an error type
                        // with a single named type candidate symbol, we will return constructors
                        // of the named type in the semantic info.
                        // Otherwise, we will return the error type candidate symbols.
 
                        var namedType = (NamedTypeSymbol)attribute.Type;
                        if (namedType.IsErrorType())
                        {
                            Debug.Assert(resultKind != LookupResultKind.Viable);
                            var errorType = (ErrorTypeSymbol)namedType;
                            var candidateSymbols = errorType.CandidateSymbols;
 
                            // If error type has a single named type candidate symbol, we want to 
                            // use that type for symbol info. 
                            if (candidateSymbols.Length == 1 && candidateSymbols[0] is NamedTypeSymbol)
                            {
                                namedType = (NamedTypeSymbol)candidateSymbols[0];
                            }
                            else
                            {
                                symbols = OneOrMany.Create(candidateSymbols);
                                break;
                            }
                        }
 
                        AdjustSymbolsForObjectCreation(attribute, namedType, attribute.Constructor, binderOpt, ref resultKind, ref symbols, ref memberGroup);
                    }
                    break;
 
                case BoundKind.QueryClause:
                    {
                        var query = (BoundQueryClause)boundNode;
                        var builder = ArrayBuilder<Symbol>.GetInstance();
                        if (query.Operation != null && (object)query.Operation.ExpressionSymbol != null) builder.Add(query.Operation.ExpressionSymbol);
                        if ((object)query.DefinedSymbol != null) builder.Add(query.DefinedSymbol);
                        if (query.Cast != null && (object)query.Cast.ExpressionSymbol != null) builder.Add(query.Cast.ExpressionSymbol);
                        symbols = builder.ToOneOrManyAndFree();
                    }
                    break;
 
                case BoundKind.DynamicInvocation:
                    var dynamicInvocation = (BoundDynamicInvocation)boundNode;
                    Debug.Assert(dynamicInvocation.ExpressionSymbol is null);
                    memberGroup = dynamicInvocation.ApplicableMethods.Cast<MethodSymbol, Symbol>();
                    symbols = OneOrMany.Create(memberGroup);
                    isDynamic = true;
                    break;
 
                case BoundKind.DynamicCollectionElementInitializer:
                    var collectionInit = (BoundDynamicCollectionElementInitializer)boundNode;
                    Debug.Assert(collectionInit.ExpressionSymbol is null);
                    memberGroup = collectionInit.ApplicableMethods.Cast<MethodSymbol, Symbol>();
                    symbols = OneOrMany.Create(memberGroup);
                    isDynamic = true;
                    break;
 
                case BoundKind.DynamicIndexerAccess:
                    var dynamicIndexer = (BoundDynamicIndexerAccess)boundNode;
                    Debug.Assert(dynamicIndexer.ExpressionSymbol is null);
                    memberGroup = dynamicIndexer.ApplicableIndexers.Cast<PropertySymbol, Symbol>();
                    symbols = OneOrMany.Create(memberGroup);
                    isDynamic = true;
                    break;
 
                case BoundKind.DynamicMemberAccess:
                    Debug.Assert((object)boundNode.ExpressionSymbol == null);
                    isDynamic = true;
                    break;
 
                case BoundKind.DynamicObjectCreationExpression:
                    var objectCreation = (BoundDynamicObjectCreationExpression)boundNode;
                    memberGroup = objectCreation.ApplicableMethods.Cast<MethodSymbol, Symbol>();
                    symbols = OneOrMany.Create(memberGroup);
                    isDynamic = true;
                    break;
 
                case BoundKind.ObjectCreationExpression:
                    var boundObjectCreation = (BoundObjectCreationExpression)boundNode;
 
                    if ((object)boundObjectCreation.Constructor != null)
                    {
                        Debug.Assert(boundObjectCreation.ConstructorsGroup.Contains(boundObjectCreation.Constructor));
                        symbols = OneOrMany.Create<Symbol>(boundObjectCreation.Constructor);
                    }
                    else if (boundObjectCreation.ConstructorsGroup.Length > 0)
                    {
                        symbols = StaticCast<Symbol>.From(OneOrMany.Create(boundObjectCreation.ConstructorsGroup));
                        resultKind = resultKind.WorseResultKind(LookupResultKind.OverloadResolutionFailure);
                    }
 
                    memberGroup = boundObjectCreation.ConstructorsGroup.Cast<MethodSymbol, Symbol>();
                    break;
 
                case BoundKind.ThisReference:
                case BoundKind.BaseReference:
                    {
                        Binder binder = binderOpt ?? GetEnclosingBinder(GetAdjustedNodePosition(boundNode.Syntax));
                        NamedTypeSymbol containingType = binder.ContainingType;
                        var containingMember = binder.ContainingMember();
 
                        var thisParam = GetThisParameter(boundNode.Type, containingType, containingMember, out resultKind);
                        symbols = thisParam != null ? OneOrMany.Create<Symbol>(thisParam) : OneOrMany<Symbol>.Empty;
                    }
                    break;
 
                case BoundKind.FromEndIndexExpression:
                    {
                        var fromEndIndexExpression = (BoundFromEndIndexExpression)boundNode;
                        if ((object)fromEndIndexExpression.MethodOpt != null)
                        {
                            symbols = OneOrMany.Create<Symbol>(fromEndIndexExpression.MethodOpt);
                        }
                        break;
                    }
 
                case BoundKind.RangeExpression:
                    {
                        var rangeExpression = (BoundRangeExpression)boundNode;
                        if ((object)rangeExpression.MethodOpt != null)
                        {
                            symbols = OneOrMany.Create<Symbol>(rangeExpression.MethodOpt);
                        }
                        break;
                    }
 
                default:
                    {
                        if (boundNode.ExpressionSymbol is Symbol symbol)
                        {
                            symbols = OneOrMany.Create(symbol);
                            resultKind = boundNode.ResultKind;
                        }
                    }
                    break;
            }
 
            if (boundNodeForSyntacticParent != null && (options & SymbolInfoOptions.PreferConstructorsToType) != 0)
            {
                // Adjust symbols to get the constructors if we're T in a "new T(...)".
                AdjustSymbolsForObjectCreation(boundNode, boundNodeForSyntacticParent, binderOpt, ref resultKind, ref symbols, ref memberGroup);
            }
 
            return symbols;
        }
 
        private static ParameterSymbol GetThisParameter(TypeSymbol typeOfThis, NamedTypeSymbol containingType, Symbol containingMember, out LookupResultKind resultKind)
        {
            if ((object)containingMember == null || (object)containingType == null)
            {
                // not in a member of a type (can happen when speculating)
                resultKind = LookupResultKind.NotReferencable;
                return new ThisParameterSymbol(containingMember as MethodSymbol, typeOfThis);
            }
 
            ParameterSymbol thisParam;
 
            switch (containingMember.Kind)
            {
                case SymbolKind.Method:
                case SymbolKind.Field:
                case SymbolKind.Property:
                    if (containingMember.IsStatic)
                    {
                        // in a static member
                        resultKind = LookupResultKind.StaticInstanceMismatch;
                        thisParam = new ThisParameterSymbol(containingMember as MethodSymbol, containingType);
                    }
                    else
                    {
                        if ((object)typeOfThis == ErrorTypeSymbol.UnknownResultType)
                        {
                            // in an instance member, but binder considered this/base unreferenceable
                            thisParam = new ThisParameterSymbol(containingMember as MethodSymbol, containingType);
                            resultKind = LookupResultKind.NotReferencable;
                        }
                        else
                        {
                            switch (containingMember.Kind)
                            {
                                case SymbolKind.Method:
                                    resultKind = LookupResultKind.Viable;
                                    thisParam = containingMember.EnclosingThisSymbol();
                                    break;
 
                                // Fields and properties can't access 'this' since
                                // initializers are run in the constructor    
                                case SymbolKind.Field:
                                case SymbolKind.Property:
                                    resultKind = LookupResultKind.NotReferencable;
                                    thisParam = containingMember.EnclosingThisSymbol() ?? new ThisParameterSymbol(null, containingType);
                                    break;
 
                                default:
                                    throw ExceptionUtilities.UnexpectedValue(containingMember.Kind);
                            }
                        }
                    }
                    break;
 
                default:
                    thisParam = new ThisParameterSymbol(containingMember as MethodSymbol, typeOfThis);
                    resultKind = LookupResultKind.NotReferencable;
                    break;
            }
 
            return thisParam;
        }
 
        private static void GetSymbolsAndResultKind(BoundUnaryOperator unaryOperator, out bool isDynamic, ref LookupResultKind resultKind, ref OneOrMany<Symbol> symbols)
        {
            UnaryOperatorKind operandType = unaryOperator.OperatorKind.OperandTypes();
            isDynamic = unaryOperator.OperatorKind.IsDynamic();
 
            if (operandType == 0 || operandType == UnaryOperatorKind.UserDefined || unaryOperator.ResultKind != LookupResultKind.Viable)
            {
                if (!isDynamic)
                {
                    GetSymbolsAndResultKind(unaryOperator, unaryOperator.MethodOpt, unaryOperator.OriginalUserDefinedOperatorsOpt, out symbols, out resultKind);
                }
            }
            else
            {
                Debug.Assert((object)unaryOperator.MethodOpt == null && unaryOperator.OriginalUserDefinedOperatorsOpt.IsDefaultOrEmpty);
                UnaryOperatorKind op = unaryOperator.OperatorKind.Operator();
                symbols = OneOrMany.Create<Symbol>(new SynthesizedIntrinsicOperatorSymbol(unaryOperator.Operand.Type.StrippedType(),
                                                                                          OperatorFacts.UnaryOperatorNameFromOperatorKind(op, isChecked: unaryOperator.OperatorKind.IsChecked()),
                                                                                          unaryOperator.Type.StrippedType()));
                resultKind = unaryOperator.ResultKind;
            }
        }
 
        private static void GetSymbolsAndResultKind(BoundIncrementOperator increment, out bool isDynamic, ref LookupResultKind resultKind, ref OneOrMany<Symbol> symbols)
        {
            UnaryOperatorKind operandType = increment.OperatorKind.OperandTypes();
            isDynamic = increment.OperatorKind.IsDynamic();
 
            if (operandType == 0 || operandType == UnaryOperatorKind.UserDefined || increment.ResultKind != LookupResultKind.Viable)
            {
                if (!isDynamic)
                {
                    GetSymbolsAndResultKind(increment, increment.MethodOpt, increment.OriginalUserDefinedOperatorsOpt, out symbols, out resultKind);
                }
            }
            else
            {
                Debug.Assert((object)increment.MethodOpt == null && increment.OriginalUserDefinedOperatorsOpt.IsDefaultOrEmpty);
                UnaryOperatorKind op = increment.OperatorKind.Operator();
                TypeSymbol opType = increment.Operand.Type.StrippedType();
                symbols = OneOrMany.Create<Symbol>(new SynthesizedIntrinsicOperatorSymbol(opType,
                                                                                          OperatorFacts.UnaryOperatorNameFromOperatorKind(op, isChecked: increment.OperatorKind.IsChecked()),
                                                                                          opType));
                resultKind = increment.ResultKind;
            }
        }
 
        private static void GetSymbolsAndResultKind(BoundBinaryOperator binaryOperator, out bool isDynamic, ref LookupResultKind resultKind, ref OneOrMany<Symbol> symbols)
        {
            BinaryOperatorKind operandType = binaryOperator.OperatorKind.OperandTypes();
            BinaryOperatorKind op = binaryOperator.OperatorKind.Operator();
            isDynamic = binaryOperator.OperatorKind.IsDynamic();
 
            if (operandType == 0 || operandType == BinaryOperatorKind.UserDefined || binaryOperator.ResultKind != LookupResultKind.Viable || binaryOperator.OperatorKind.IsLogical())
            {
                if (!isDynamic)
                {
                    GetSymbolsAndResultKind(binaryOperator, binaryOperator.Method, binaryOperator.OriginalUserDefinedOperatorsOpt, out symbols, out resultKind);
                }
            }
            else
            {
                Debug.Assert((object)binaryOperator.Method == null && binaryOperator.OriginalUserDefinedOperatorsOpt.IsDefaultOrEmpty);
 
                if (!isDynamic &&
                    (op == BinaryOperatorKind.Equal || op == BinaryOperatorKind.NotEqual) &&
                    ((binaryOperator.Left.IsLiteralNull() && binaryOperator.Right.Type.IsNullableType()) ||
                     (binaryOperator.Right.IsLiteralNull() && binaryOperator.Left.Type.IsNullableType())) &&
                    binaryOperator.Type.SpecialType == SpecialType.System_Boolean)
                {
                    // Comparison of a nullable type with null, return corresponding operator for Object.
                    var objectType = binaryOperator.Type.ContainingAssembly.GetSpecialType(SpecialType.System_Object);
 
                    symbols = OneOrMany.Create<Symbol>(new SynthesizedIntrinsicOperatorSymbol(objectType,
                                                                                              OperatorFacts.BinaryOperatorNameFromOperatorKind(op, isChecked: binaryOperator.OperatorKind.IsChecked()),
                                                                                              objectType,
                                                                                              binaryOperator.Type));
                }
                else
                {
                    symbols = OneOrMany.Create(GetIntrinsicOperatorSymbol(op, isDynamic,
                                                                          binaryOperator.Left.Type,
                                                                          binaryOperator.Right.Type,
                                                                          binaryOperator.Type,
                                                                          binaryOperator.OperatorKind.IsChecked()));
                }
 
                resultKind = binaryOperator.ResultKind;
            }
        }
 
        private static Symbol GetIntrinsicOperatorSymbol(BinaryOperatorKind op, bool isDynamic, TypeSymbol leftType, TypeSymbol rightType, TypeSymbol returnType, bool isChecked)
        {
            if (!isDynamic)
            {
                leftType = leftType.StrippedType();
                rightType = rightType.StrippedType();
                returnType = returnType.StrippedType();
            }
            else
            {
                Debug.Assert(returnType.IsDynamic());
 
                if ((object)leftType == null)
                {
                    Debug.Assert(rightType.IsDynamic());
                    leftType = rightType;
                }
                else if ((object)rightType == null)
                {
                    Debug.Assert(leftType.IsDynamic());
                    rightType = leftType;
                }
            }
            return new SynthesizedIntrinsicOperatorSymbol(leftType,
                                                          OperatorFacts.BinaryOperatorNameFromOperatorKind(op, isChecked),
                                                          rightType,
                                                          returnType);
        }
 
        private static void GetSymbolsAndResultKind(BoundCompoundAssignmentOperator compoundAssignment, out bool isDynamic, ref LookupResultKind resultKind, ref OneOrMany<Symbol> symbols)
        {
            BinaryOperatorKind operandType = compoundAssignment.Operator.Kind.OperandTypes();
            BinaryOperatorKind op = compoundAssignment.Operator.Kind.Operator();
            isDynamic = compoundAssignment.Operator.Kind.IsDynamic();
 
            if (operandType == 0 || operandType == BinaryOperatorKind.UserDefined || compoundAssignment.ResultKind != LookupResultKind.Viable)
            {
                if (!isDynamic)
                {
                    GetSymbolsAndResultKind(compoundAssignment, compoundAssignment.Operator.Method, compoundAssignment.OriginalUserDefinedOperatorsOpt, out symbols, out resultKind);
                }
            }
            else
            {
                Debug.Assert((object)compoundAssignment.Operator.Method == null && compoundAssignment.OriginalUserDefinedOperatorsOpt.IsDefaultOrEmpty);
 
                symbols = OneOrMany.Create(GetIntrinsicOperatorSymbol(op, isDynamic,
                                                                      compoundAssignment.Operator.LeftType,
                                                                      compoundAssignment.Operator.RightType,
                                                                      compoundAssignment.Operator.ReturnType,
                                                                      compoundAssignment.Operator.Kind.IsChecked()));
                resultKind = compoundAssignment.ResultKind;
            }
        }
 
        private static void GetSymbolsAndResultKind(BoundExpression node, Symbol symbolOpt, ImmutableArray<MethodSymbol> originalCandidates, out OneOrMany<Symbol> symbols, out LookupResultKind resultKind)
        {
            if (!ReferenceEquals(symbolOpt, null))
            {
                symbols = OneOrMany.Create(symbolOpt);
                resultKind = node.ResultKind;
            }
            else if (!originalCandidates.IsDefault)
            {
                symbols = StaticCast<Symbol>.From(OneOrMany.Create(originalCandidates));
                resultKind = node.ResultKind;
            }
            else
            {
                symbols = OneOrMany<Symbol>.Empty;
                resultKind = LookupResultKind.Empty;
            }
        }
 
        // In cases where we are binding C in "[C(...)]", the bound nodes return the symbol for the type. However, we've
        // decided that we want this case to return the constructor of the type instead. This affects attributes. 
        // This method checks for this situation and adjusts the syntax and method or property group.
        private void AdjustSymbolsForObjectCreation(
            BoundExpression boundNode,
            BoundNode boundNodeForSyntacticParent,
            Binder binderOpt,
            ref LookupResultKind resultKind,
            ref OneOrMany<Symbol> symbols,
            ref ImmutableArray<Symbol> memberGroup)
        {
            NamedTypeSymbol typeSymbol = null;
            MethodSymbol constructor = null;
 
            // Check if boundNode.Syntax is the type-name child of an Attribute.
            SyntaxNode parentSyntax = boundNodeForSyntacticParent.Syntax;
            if (parentSyntax != null &&
                parentSyntax == boundNode.Syntax.Parent &&
                parentSyntax.Kind() == SyntaxKind.Attribute && ((AttributeSyntax)parentSyntax).Name == boundNode.Syntax)
            {
                var unwrappedSymbols = UnwrapAliases(symbols);
 
                switch (boundNodeForSyntacticParent.Kind)
                {
                    case BoundKind.Attribute:
                        BoundAttribute boundAttribute = (BoundAttribute)boundNodeForSyntacticParent;
 
                        if (unwrappedSymbols.Count == 1 && unwrappedSymbols[0].Kind == SymbolKind.NamedType)
                        {
                            Debug.Assert(resultKind != LookupResultKind.Viable ||
                                TypeSymbol.Equals((TypeSymbol)unwrappedSymbols[0], boundAttribute.Type.GetNonErrorGuess(), TypeCompareKind.ConsiderEverything2));
 
                            typeSymbol = (NamedTypeSymbol)unwrappedSymbols[0];
                            constructor = boundAttribute.Constructor;
                            resultKind = resultKind.WorseResultKind(boundAttribute.ResultKind);
                        }
                        break;
 
                    case BoundKind.BadExpression:
                        BoundBadExpression boundBadExpression = (BoundBadExpression)boundNodeForSyntacticParent;
                        if (unwrappedSymbols.Count == 1)
                        {
                            resultKind = resultKind.WorseResultKind(boundBadExpression.ResultKind);
                            typeSymbol = unwrappedSymbols[0] as NamedTypeSymbol;
                        }
                        break;
 
                    default:
                        throw ExceptionUtilities.UnexpectedValue(boundNodeForSyntacticParent.Kind);
                }
 
                AdjustSymbolsForObjectCreation(boundNode, typeSymbol, constructor, binderOpt, ref resultKind, ref symbols, ref memberGroup);
            }
        }
 
        private void AdjustSymbolsForObjectCreation(
            BoundNode lowestBoundNode,
            NamedTypeSymbol typeSymbolOpt,
            MethodSymbol constructorOpt,
            Binder binderOpt,
            ref LookupResultKind resultKind,
            ref OneOrMany<Symbol> symbols,
            ref ImmutableArray<Symbol> memberGroup)
        {
            Debug.Assert(lowestBoundNode != null);
            Debug.Assert(binderOpt != null || IsInTree(lowestBoundNode.Syntax));
 
            if ((object)typeSymbolOpt != null)
            {
                Debug.Assert(lowestBoundNode.Syntax != null);
 
                // Filter typeSymbol's instance constructors by accessibility.
                // If all the instance constructors are inaccessible, we retain
                // all of them for correct semantic info.
                Binder binder = binderOpt ?? GetEnclosingBinder(GetAdjustedNodePosition(lowestBoundNode.Syntax));
                ImmutableArray<MethodSymbol> candidateConstructors;
 
                if (binder != null)
                {
                    var instanceConstructors = typeSymbolOpt.IsInterfaceType() && (object)typeSymbolOpt.ComImportCoClass != null ?
                        typeSymbolOpt.ComImportCoClass.InstanceConstructors :
                        typeSymbolOpt.InstanceConstructors;
 
                    var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
                    candidateConstructors = binder.FilterInaccessibleConstructors(instanceConstructors, allowProtectedConstructorsOfBaseType: false, useSiteInfo: ref discardedUseSiteInfo);
 
                    if ((object)constructorOpt == null ? !candidateConstructors.Any() : !candidateConstructors.Contains(constructorOpt))
                    {
                        // All instance constructors are inaccessible or if the specified constructor
                        // isn't a candidate, then we retain all of them for correct semantic info.
                        Debug.Assert(resultKind != LookupResultKind.Viable);
                        candidateConstructors = instanceConstructors;
                    }
                }
                else
                {
                    candidateConstructors = ImmutableArray<MethodSymbol>.Empty;
                }
 
                if ((object)constructorOpt != null)
                {
                    Debug.Assert(candidateConstructors.Contains(constructorOpt));
                    symbols = OneOrMany.Create<Symbol>(constructorOpt);
                }
                else if (candidateConstructors.Length > 0)
                {
                    symbols = StaticCast<Symbol>.From(OneOrMany.Create(candidateConstructors));
                    Debug.Assert(resultKind != LookupResultKind.Viable);
                    resultKind = resultKind.WorseResultKind(LookupResultKind.OverloadResolutionFailure);
                }
 
                memberGroup = candidateConstructors.Cast<MethodSymbol, Symbol>();
            }
        }
 
        /// <summary>
        /// Returns a list of accessible, non-hidden indexers that could be invoked with the given expression
        /// as a receiver.
        /// </summary>
        /// <remarks>
        /// If the given expression is an indexer access, then this method will return the list of indexers
        /// that could be invoked on the result, not the list of indexers that were considered.
        /// </remarks>
        private ImmutableArray<IPropertySymbol> GetIndexerGroupSemanticSymbols(BoundExpression boundNode, Binder binderOpt)
        {
            Debug.Assert(binderOpt != null || IsInTree(boundNode.Syntax));
 
            TypeSymbol type = boundNode.Type;
 
            if (ReferenceEquals(type, null) || type.IsStatic)
            {
                return ImmutableArray<IPropertySymbol>.Empty;
            }
 
            Binder binder = binderOpt ?? GetEnclosingBinder(GetAdjustedNodePosition(boundNode.Syntax));
            var symbols = ArrayBuilder<ISymbol>.GetInstance();
            AppendSymbolsWithNameAndArity(symbols, WellKnownMemberNames.Indexer, 0, binder, type, LookupOptions.MustBeInstance);
 
            if (symbols.Count == 0)
            {
                symbols.Free();
                return ImmutableArray<IPropertySymbol>.Empty;
            }
 
            var result = FilterOverriddenOrHiddenIndexers(symbols);
            symbols.Free();
 
            return result;
        }
 
        private static ImmutableArray<IPropertySymbol> FilterOverriddenOrHiddenIndexers(ArrayBuilder<ISymbol> symbols)
        {
            PooledHashSet<Symbol> hiddenSymbols = null;
            foreach (ISymbol iSymbol in symbols)
            {
                Symbol symbol = iSymbol.GetSymbol();
                Debug.Assert(symbol.IsIndexer(), "Only indexers can have name " + WellKnownMemberNames.Indexer);
 
                PropertySymbol indexer = (PropertySymbol)symbol;
                OverriddenOrHiddenMembersResult overriddenOrHiddenMembers = indexer.OverriddenOrHiddenMembers;
 
                foreach (Symbol overridden in overriddenOrHiddenMembers.OverriddenMembers)
                {
                    if (hiddenSymbols == null)
                    {
                        hiddenSymbols = PooledHashSet<Symbol>.GetInstance();
                    }
                    hiddenSymbols.Add(overridden);
                }
 
                // Don't worry about RuntimeOverriddenMembers - this check is for the API, which
                // should reflect the C# semantics.
 
                foreach (Symbol hidden in overriddenOrHiddenMembers.HiddenMembers)
                {
                    if (hiddenSymbols == null)
                    {
                        hiddenSymbols = PooledHashSet<Symbol>.GetInstance();
                    }
                    hiddenSymbols.Add(hidden);
                }
            }
 
            var builder = ArrayBuilder<IPropertySymbol>.GetInstance();
 
            foreach (IPropertySymbol indexer in symbols)
            {
                if (hiddenSymbols == null || !hiddenSymbols.Contains(indexer.GetSymbol()))
                {
                    builder.Add(indexer);
                }
            }
 
            hiddenSymbols?.Free();
            return builder.ToImmutableAndFree();
        }
 
        /// <remarks>
        /// The method group can contain "duplicate" symbols that we do not want to display in the IDE analysis.
        ///
        /// For example, there could be an overriding virtual method and the method it overrides both in
        /// the method group. This, strictly speaking, is a violation of the C# specification because we are
        /// supposed to strip out overriding methods from the method group before overload resolution; overload
        /// resolution is supposed to treat overridden methods as being methods of the less derived type. However,
        /// in the IDE we want to display information about the overriding method, not the overridden method, and
        /// therefore we leave both in the method group. The overload resolution algorithm has been written
        /// to handle this departure from the specification.
        ///
        /// Similarly, we might have two methods in the method group where one is a "new" method that hides 
        /// another. Again, in overload resolution this would be handled by the rule that says that methods
        /// declared on more derived types take priority over methods declared on less derived types. Both
        /// will be in the method group, but in the IDE we want to only display information about the 
        /// hiding method, not the hidden method.
        ///
        /// We can also have "diamond" inheritance of interfaces leading to multiple copies of the same
        /// method ending up in the method group:
        /// 
        /// interface IB { void M(); }
        /// interface IL : IB {}
        /// interface IR : IB {}
        /// interface ID : IL, IR {}
        /// ...
        /// id.M();
        ///
        /// We only want to display one symbol in the IDE, even if the member lookup algorithm is unsophisticated
        /// and puts IB.M in the member group twice. (Again, this is a mild spec violation since a method group
        /// is supposed to be a set, without duplicates.)
        ///
        /// Finally, the interaction of multiple inheritance of interfaces and hiding can lead to some subtle
        /// situations. Suppose we make a slight modification to the scenario above:
        ///
        /// interface IL : IB { new void M(); } 
        ///
        /// Again, we only want to display one symbol in the method group. The fact that there is a "path"
        /// to IB.M from ID via IR is irrelevant; if the symbol IB.M is hidden by IL.M then it is hidden
        /// in ID, period.
        /// </remarks>
        private static ImmutableArray<MethodSymbol> FilterOverriddenOrHiddenMethods(ImmutableArray<MethodSymbol> methods)
        {
            // Optimization, not required for correctness.
            if (methods.Length <= 1)
            {
                return methods;
            }
 
            HashSet<Symbol> hiddenSymbols = new HashSet<Symbol>();
            foreach (MethodSymbol method in methods)
            {
                OverriddenOrHiddenMembersResult overriddenOrHiddenMembers = method.OverriddenOrHiddenMembers;
 
                foreach (Symbol overridden in overriddenOrHiddenMembers.OverriddenMembers)
                {
                    hiddenSymbols.Add(overridden);
                }
 
                // Don't worry about RuntimeOverriddenMembers - this check is for the API, which
                // should reflect the C# semantics.
 
                foreach (Symbol hidden in overriddenOrHiddenMembers.HiddenMembers)
                {
                    hiddenSymbols.Add(hidden);
                }
            }
 
            return methods.WhereAsArray((m, hiddenSymbols) => !hiddenSymbols.Contains(m), hiddenSymbols);
        }
 
        // Get the symbols and possible method group associated with a method group bound node, as
        // they should be exposed through GetSemanticInfo.
        // NB: It is not safe to pass a null binderOpt during speculative binding.
        // 
        // If the parent node of the method group syntax node provides information (such as arguments) 
        // that allows us to return more specific symbols (a specific overload or applicable candidates)
        // we return these. The complete set of symbols of the method group is then returned in methodGroup parameter.
        private OneOrMany<Symbol> GetMethodGroupSemanticSymbols(
            BoundMethodGroup boundNode,
            BoundNode boundNodeForSyntacticParent,
            Binder binderOpt,
            out LookupResultKind resultKind,
            out bool isDynamic,
            out ImmutableArray<Symbol> methodGroup)
        {
            Debug.Assert(binderOpt != null || IsInTree(boundNode.Syntax));
 
            OneOrMany<Symbol> symbols = OneOrMany<Symbol>.Empty;
 
            resultKind = boundNode.ResultKind;
            if (resultKind == LookupResultKind.Empty)
            {
                resultKind = LookupResultKind.Viable;
            }
 
            isDynamic = false;
 
            // The method group needs filtering.
            Binder binder = binderOpt ?? GetEnclosingBinder(GetAdjustedNodePosition(boundNode.Syntax));
            methodGroup = GetReducedAndFilteredMethodGroupSymbols(binder, boundNode).Cast<MethodSymbol, Symbol>();
 
            // We want to get the actual node chosen by overload resolution, if possible. 
            if (boundNodeForSyntacticParent != null)
            {
                switch (boundNodeForSyntacticParent.Kind)
                {
                    case BoundKind.Call:
                        // If we are looking for info on M in M(args), we want the symbol that overload resolution
                        // chose for M.
                        var call = (BoundCall)boundNodeForSyntacticParent;
                        InvocationExpressionSyntax invocation = call.Syntax as InvocationExpressionSyntax;
                        if (invocation != null && invocation.Expression.SkipParens() == ((ExpressionSyntax)boundNode.Syntax).SkipParens() && (object)call.Method != null)
                        {
                            if (call.OriginalMethodsOpt.IsDefault)
                            {
                                // Overload resolution succeeded.
                                symbols = CreateReducedExtensionMethodIfPossible(call);
                                resultKind = LookupResultKind.Viable;
                            }
                            else
                            {
                                resultKind = call.ResultKind.WorseResultKind(LookupResultKind.OverloadResolutionFailure);
                                symbols = StaticCast<Symbol>.From(CreateReducedExtensionMethodsFromOriginalsIfNecessary(call, Compilation));
                            }
                        }
                        break;
 
                    case BoundKind.DelegateCreationExpression:
                        // If we are looking for info on "M" in "new Action(M)" 
                        // we want to get the symbol that overload resolution chose for M, not the whole method group M.
                        var delegateCreation = (BoundDelegateCreationExpression)boundNodeForSyntacticParent;
                        if (delegateCreation.Argument == boundNode && (object)delegateCreation.MethodOpt != null)
                        {
                            symbols = CreateReducedExtensionMethodIfPossible(delegateCreation, boundNode.ReceiverOpt);
                        }
                        break;
 
                    case BoundKind.Conversion:
                        // If we are looking for info on "M" in "(Action)M" 
                        // we want to get the symbol that overload resolution chose for M, not the whole method group M.
                        var conversion = (BoundConversion)boundNodeForSyntacticParent;
 
                        MethodSymbol method = null;
                        if (conversion.ConversionKind == ConversionKind.MethodGroup)
                        {
                            method = conversion.SymbolOpt;
                        }
                        else if (conversion.Operand is BoundConversion { ConversionKind: ConversionKind.MethodGroup } nestedMethodGroupConversion)
                        {
                            method = nestedMethodGroupConversion.SymbolOpt;
                        }
 
                        if ((object)method != null)
                        {
                            if (conversion.IsExtensionMethod)
                            {
                                method = ReducedExtensionMethodSymbol.Create(method);
                            }
 
                            symbols = OneOrMany.Create((Symbol)method);
                            resultKind = conversion.ResultKind;
                        }
                        else
                        {
                            goto default;
                        }
 
                        break;
 
                    case BoundKind.DynamicInvocation:
                        var dynamicInvocation = (BoundDynamicInvocation)boundNodeForSyntacticParent;
                        symbols = OneOrMany.Create(dynamicInvocation.ApplicableMethods.Cast<MethodSymbol, Symbol>());
                        isDynamic = true;
                        break;
 
                    case BoundKind.BadExpression:
                        // If the bad expression has symbol(s) from this method group, it better indicates any problems.
                        ImmutableArray<Symbol> myMethodGroup = methodGroup;
 
                        symbols = OneOrMany.Create(((BoundBadExpression)boundNodeForSyntacticParent).Symbols.WhereAsArray((sym, myMethodGroup) => myMethodGroup.Contains(sym), myMethodGroup));
                        if (symbols.Any())
                        {
                            resultKind = ((BoundBadExpression)boundNodeForSyntacticParent).ResultKind;
                        }
                        break;
 
                    case BoundKind.NameOfOperator:
                        symbols = OneOrMany.Create(methodGroup);
                        resultKind = resultKind.WorseResultKind(LookupResultKind.MemberGroup);
                        break;
 
                    default:
                        symbols = OneOrMany.Create(methodGroup);
                        if (symbols.Count > 0)
                        {
                            resultKind = resultKind.WorseResultKind(LookupResultKind.OverloadResolutionFailure);
                        }
                        break;
                }
            }
            else if (methodGroup.Length == 1 && !boundNode.HasAnyErrors)
            {
                // During speculative binding, there won't be a parent bound node. The parent bound
                // node may also be absent if the syntactic parent has errors or if one is simply
                // not specified (see SemanticModel.GetSymbolInfoForNode). However, if there's exactly
                // one candidate, then we should probably succeed.
 
                symbols = OneOrMany.Create(methodGroup);
                if (symbols.Count > 0)
                {
                    resultKind = resultKind.WorseResultKind(LookupResultKind.OverloadResolutionFailure);
                }
            }
 
            if (!symbols.Any())
            {
                // If we didn't find a better set of symbols, then assume this is a method group that didn't
                // get resolved. Return all members of the method group, with a resultKind of OverloadResolutionFailure
                // (unless the method group already has a worse result kind).
                symbols = OneOrMany.Create(methodGroup);
                if (!isDynamic && resultKind > LookupResultKind.OverloadResolutionFailure)
                {
                    resultKind = LookupResultKind.OverloadResolutionFailure;
                }
            }
 
            return symbols;
        }
 
        // NB: It is not safe to pass a null binderOpt during speculative binding.
        private OneOrMany<Symbol> GetPropertyGroupSemanticSymbols(
            BoundPropertyGroup boundNode,
            BoundNode boundNodeForSyntacticParent,
            Binder binderOpt,
            out LookupResultKind resultKind,
            out ImmutableArray<Symbol> propertyGroup)
        {
            Debug.Assert(binderOpt != null || IsInTree(boundNode.Syntax));
 
            OneOrMany<Symbol> symbols = OneOrMany<Symbol>.Empty;
 
            resultKind = boundNode.ResultKind;
            if (resultKind == LookupResultKind.Empty)
            {
                resultKind = LookupResultKind.Viable;
            }
 
            // The property group needs filtering.
            propertyGroup = boundNode.Properties.Cast<PropertySymbol, Symbol>();
 
            // We want to get the actual node chosen by overload resolution, if possible. 
            if (boundNodeForSyntacticParent != null)
            {
                switch (boundNodeForSyntacticParent.Kind)
                {
                    case BoundKind.IndexerAccess:
                        // If we are looking for info on P in P[args], we want the symbol that overload resolution
                        // chose for P.
                        var indexer = (BoundIndexerAccess)boundNodeForSyntacticParent;
                        var elementAccess = indexer.Syntax as ElementAccessExpressionSyntax;
                        if (elementAccess != null && elementAccess.Expression == boundNode.Syntax && (object)indexer.Indexer != null)
                        {
                            if (indexer.OriginalIndexersOpt.IsDefault)
                            {
                                // Overload resolution succeeded.
                                symbols = OneOrMany.Create<Symbol>(indexer.Indexer);
                                resultKind = LookupResultKind.Viable;
                            }
                            else
                            {
                                resultKind = indexer.ResultKind.WorseResultKind(LookupResultKind.OverloadResolutionFailure);
                                symbols = StaticCast<Symbol>.From(OneOrMany.Create(indexer.OriginalIndexersOpt));
                            }
                        }
                        break;
 
                    case BoundKind.BadExpression:
                        // If the bad expression has symbol(s) from this property group, it better indicates any problems.
                        ImmutableArray<Symbol> myPropertyGroup = propertyGroup;
 
                        symbols = OneOrMany.Create(((BoundBadExpression)boundNodeForSyntacticParent).Symbols.WhereAsArray((sym, myPropertyGroup) => myPropertyGroup.Contains(sym), myPropertyGroup));
                        if (symbols.Any())
                        {
                            resultKind = ((BoundBadExpression)boundNodeForSyntacticParent).ResultKind;
                        }
                        break;
                }
            }
            else if (propertyGroup.Length == 1 && !boundNode.HasAnyErrors)
            {
                // During speculative binding, there won't be a parent bound node. The parent bound
                // node may also be absent if the syntactic parent has errors or if one is simply
                // not specified (see SemanticModel.GetSymbolInfoForNode). However, if there's exactly
                // one candidate, then we should probably succeed.
 
                // If we're speculatively binding and there's exactly one candidate, then we should probably succeed.
                symbols = OneOrMany.Create(propertyGroup);
            }
 
            if (!symbols.Any())
            {
                // If we didn't find a better set of symbols, then assume this is a property group that didn't
                // get resolved. Return all members of the property group, with a resultKind of OverloadResolutionFailure
                // (unless the property group already has a worse result kind).
                symbols = OneOrMany.Create(propertyGroup);
                if (resultKind > LookupResultKind.OverloadResolutionFailure)
                {
                    resultKind = LookupResultKind.OverloadResolutionFailure;
                }
            }
 
            return symbols;
        }
 
        /// <summary>
        /// Get the semantic info of a named argument in an invocation-like expression (e.g. `x` in `M(x: 3)`)
        /// or the name in a Subpattern (e.g. either `Name` in `e is (Name: 3){Name: 3}`).
        /// </summary>
        private SymbolInfo GetNamedArgumentSymbolInfo(IdentifierNameSyntax identifierNameSyntax, CancellationToken cancellationToken)
        {
            Debug.Assert(SyntaxFacts.IsNamedArgumentName(identifierNameSyntax));
 
            // Argument names do not have bound nodes associated with them, so we cannot use the usual
            // GetSymbolInfo mechanism. Instead, we just do the following:
            //   1. Find the containing invocation.
            //   2. Call GetSymbolInfo on that.
            //   3. For each method or indexer in the return semantic info, find the argument
            //      with the given name (if any).
            //   4. Use the ResultKind in that semantic info and any symbols to create the semantic info
            //      for the named argument.
            //   5. Type is always null, as is constant value.
 
            string argumentName = identifierNameSyntax.Identifier.ValueText;
            if (argumentName.Length == 0)
                return SymbolInfo.None;    // missing name.
 
            // argument could be an argument of a tuple expression
            // var x = (Identifier: 1, AnotherIdentifier: 2);
            var parent3 = identifierNameSyntax.Parent.Parent.Parent;
            if (parent3.IsKind(SyntaxKind.TupleExpression))
            {
                var tupleArgument = (ArgumentSyntax)identifierNameSyntax.Parent.Parent;
                var tupleElement = GetDeclaredSymbol(tupleArgument, cancellationToken);
                return (object)tupleElement == null ? SymbolInfo.None : new SymbolInfo(tupleElement);
            }
 
            if (parent3.IsKind(SyntaxKind.PropertyPatternClause) || parent3.IsKind(SyntaxKind.PositionalPatternClause))
            {
                return GetSymbolInfoWorker(identifierNameSyntax, SymbolInfoOptions.DefaultOptions, cancellationToken);
            }
 
            CSharpSyntaxNode containingInvocation = parent3.Parent;
            SymbolInfo containingInvocationInfo = GetSymbolInfoWorker(containingInvocation, SymbolInfoOptions.PreferConstructorsToType | SymbolInfoOptions.ResolveAliases, cancellationToken);
 
            if ((object)containingInvocationInfo.Symbol != null)
            {
                ParameterSymbol param = FindNamedParameter(containingInvocationInfo.Symbol.GetSymbol().GetParameters(), argumentName);
                return (object)param == null ? SymbolInfo.None : new SymbolInfo(param.GetPublicSymbol());
            }
            else
            {
                var symbols = ArrayBuilder<ISymbol>.GetInstance();
 
                foreach (ISymbol invocationSym in containingInvocationInfo.CandidateSymbols)
                {
                    switch (invocationSym.Kind)
                    {
                        case SymbolKind.Method:
                        case SymbolKind.Property:
                            break; // Could have parameters.
                        default:
                            continue; // Definitely doesn't have parameters.
                    }
                    ParameterSymbol param = FindNamedParameter(invocationSym.GetSymbol().GetParameters(), argumentName);
                    if ((object)param != null)
                    {
                        symbols.Add(param.GetPublicSymbol());
                    }
                }
 
                if (symbols.Count == 0)
                {
                    symbols.Free();
                    return SymbolInfo.None;
                }
                else
                {
                    return new SymbolInfo(symbols.ToImmutableAndFree(), containingInvocationInfo.CandidateReason);
                }
            }
        }
 
        /// <summary>
        /// Find the first parameter named "argumentName".
        /// </summary>
        private static ParameterSymbol FindNamedParameter(ImmutableArray<ParameterSymbol> parameters, string argumentName)
        {
            foreach (ParameterSymbol param in parameters)
            {
                if (param.Name == argumentName)
                    return param;
            }
 
            return null;
        }
 
        internal static ImmutableArray<MethodSymbol> GetReducedAndFilteredMethodGroupSymbols(Binder binder, BoundMethodGroup node)
        {
            var methods = ArrayBuilder<MethodSymbol>.GetInstance();
            var filteredMethods = ArrayBuilder<MethodSymbol>.GetInstance();
            var resultKind = LookupResultKind.Empty;
            var typeArguments = node.TypeArgumentsOpt;
 
            // Non-extension methods.
            if (node.Methods.Any())
            {
                // This is the only place we care about overridden/hidden methods.  If there aren't methods
                // in the method group, there's only one fallback candidate and extension methods never override
                // or hide instance methods or other extension methods.
                ImmutableArray<MethodSymbol> nonHiddenMethods = FilterOverriddenOrHiddenMethods(node.Methods);
                Debug.Assert(nonHiddenMethods.Any()); // Something must be hiding, so can't all be hidden.
 
                foreach (var method in nonHiddenMethods)
                {
                    MergeReducedAndFilteredMethodGroupSymbol(
                        methods,
                        filteredMethods,
                        new SingleLookupResult(node.ResultKind, method, node.LookupError),
                        typeArguments,
                        null,
                        ref resultKind,
                        binder.Compilation);
                }
            }
            else
            {
                var otherSymbol = node.LookupSymbolOpt;
                if (((object)otherSymbol != null) && (otherSymbol.Kind == SymbolKind.Method))
                {
                    MergeReducedAndFilteredMethodGroupSymbol(
                        methods,
                        filteredMethods,
                        new SingleLookupResult(node.ResultKind, otherSymbol, node.LookupError),
                        typeArguments,
                        null,
                        ref resultKind,
                        binder.Compilation);
                }
            }
 
            var receiver = node.ReceiverOpt;
            var name = node.Name;
 
            // Extension methods, all scopes.
            if (node.SearchExtensionMethods)
            {
                Debug.Assert(receiver != null);
                int arity;
                LookupOptions options;
                if (typeArguments.IsDefault)
                {
                    arity = 0;
                    options = LookupOptions.AllMethodsOnArityZero;
                }
                else
                {
                    arity = typeArguments.Length;
                    options = LookupOptions.Default;
                }
 
                binder = binder.WithAdditionalFlags(BinderFlags.SemanticModel);
                foreach (var scope in new ExtensionMethodScopes(binder))
                {
                    var extensionMethods = ArrayBuilder<MethodSymbol>.GetInstance();
                    var otherBinder = scope.Binder;
                    otherBinder.GetCandidateExtensionMethods(extensionMethods,
                                                             name,
                                                             arity,
                                                             options,
                                                             originalBinder: binder);
 
                    foreach (var method in extensionMethods)
                    {
                        var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
                        MergeReducedAndFilteredMethodGroupSymbol(
                            methods,
                            filteredMethods,
                            binder.CheckViability(method, arity, options, accessThroughType: null, diagnose: false, useSiteInfo: ref discardedUseSiteInfo),
                            typeArguments,
                            receiver.Type,
                            ref resultKind,
                            binder.Compilation);
                    }
 
                    extensionMethods.Free();
                }
            }
 
            methods.Free();
            return filteredMethods.ToImmutableAndFree();
        }
 
        // Reduce extension methods to their reduced form, and remove:
        //   a) Extension methods are aren't applicable to receiverType
        //   including constraint checking.
        //   b) Duplicate methods
        //   c) Methods that are hidden or overridden by another method in the group.
        private static bool AddReducedAndFilteredMethodGroupSymbol(
            ArrayBuilder<MethodSymbol> methods,
            ArrayBuilder<MethodSymbol> filteredMethods,
            MethodSymbol method,
            ImmutableArray<TypeWithAnnotations> typeArguments,
            TypeSymbol receiverType,
            CSharpCompilation compilation)
        {
            MethodSymbol constructedMethod;
            if (!typeArguments.IsDefaultOrEmpty && method.Arity == typeArguments.Length)
            {
                constructedMethod = method.Construct(typeArguments);
                Debug.Assert((object)constructedMethod != null);
            }
            else
            {
                constructedMethod = method;
            }
 
            if ((object)receiverType != null)
            {
                constructedMethod = constructedMethod.ReduceExtensionMethod(receiverType, compilation);
                if ((object)constructedMethod == null)
                {
                    return false;
                }
            }
 
            // Don't add exact duplicates.
            if (filteredMethods.Contains(constructedMethod))
            {
                return false;
            }
 
            methods.Add(method);
            filteredMethods.Add(constructedMethod);
            return true;
        }
 
        private static void MergeReducedAndFilteredMethodGroupSymbol(
            ArrayBuilder<MethodSymbol> methods,
            ArrayBuilder<MethodSymbol> filteredMethods,
            SingleLookupResult singleResult,
            ImmutableArray<TypeWithAnnotations> typeArguments,
            TypeSymbol receiverType,
            ref LookupResultKind resultKind,
            CSharpCompilation compilation)
        {
            if (singleResult.Symbol is null)
            {
                return;
            }
 
            Debug.Assert(singleResult.Symbol.Kind == SymbolKind.Method);
 
            var singleKind = singleResult.Kind;
            if (resultKind > singleKind)
            {
                return;
            }
            else if (resultKind < singleKind)
            {
                methods.Clear();
                filteredMethods.Clear();
                resultKind = LookupResultKind.Empty;
            }
 
            var method = (MethodSymbol)singleResult.Symbol;
            if (AddReducedAndFilteredMethodGroupSymbol(methods, filteredMethods, method, typeArguments, receiverType, compilation))
            {
                Debug.Assert(methods.Count > 0);
                if (resultKind < singleKind)
                {
                    resultKind = singleKind;
                }
            }
 
            Debug.Assert((methods.Count == 0) == (resultKind == LookupResultKind.Empty));
            Debug.Assert(methods.Count == filteredMethods.Count);
        }
 
        /// <summary>
        /// If the call represents an extension method invocation with an explicit receiver, return the original
        /// methods as ReducedExtensionMethodSymbols. Otherwise, return the original methods unchanged.
        /// </summary>
        private static OneOrMany<MethodSymbol> CreateReducedExtensionMethodsFromOriginalsIfNecessary(BoundCall call, CSharpCompilation compilation)
        {
            var methods = call.OriginalMethodsOpt;
            TypeSymbol extensionThisType = null;
            Debug.Assert(!methods.IsDefault);
 
            if (call.InvokedAsExtensionMethod)
            {
                // If the call was invoked as an extension method, the receiver
                // should be non-null and all methods should be extension methods.
                if (call.ReceiverOpt != null)
                {
                    extensionThisType = call.ReceiverOpt.Type;
                }
                else
                {
                    extensionThisType = call.Arguments[0].Type;
                }
 
                Debug.Assert((object)extensionThisType != null);
            }
 
            var methodBuilder = ArrayBuilder<MethodSymbol>.GetInstance();
            var filteredMethodBuilder = ArrayBuilder<MethodSymbol>.GetInstance();
            foreach (var method in FilterOverriddenOrHiddenMethods(methods))
            {
                AddReducedAndFilteredMethodGroupSymbol(methodBuilder, filteredMethodBuilder, method, default(ImmutableArray<TypeWithAnnotations>), extensionThisType, compilation);
            }
            methodBuilder.Free();
            return filteredMethodBuilder.ToOneOrManyAndFree();
        }
 
        /// <summary>
        /// If the call represents an extension method with an explicit receiver, return a
        /// ReducedExtensionMethodSymbol if it can be constructed. Otherwise, return the 
        /// original call method.
        /// </summary>
        private OneOrMany<Symbol> CreateReducedExtensionMethodIfPossible(BoundCall call)
        {
            var method = call.Method;
            Debug.Assert((object)method != null);
 
            if (call.InvokedAsExtensionMethod && method.IsExtensionMethod && method.MethodKind != MethodKind.ReducedExtension)
            {
                Debug.Assert(call.Arguments.Length > 0);
                BoundExpression receiver = call.Arguments[0];
                MethodSymbol reduced = method.ReduceExtensionMethod(receiver.Type, Compilation);
                // If the extension method can't be applied to the receiver of the given
                // type, we should also return the original call method.
                method = reduced ?? method;
            }
            return OneOrMany.Create<Symbol>(method);
        }
 
        private OneOrMany<Symbol> CreateReducedExtensionMethodIfPossible(BoundDelegateCreationExpression delegateCreation, BoundExpression receiverOpt)
        {
            var method = delegateCreation.MethodOpt;
            Debug.Assert((object)method != null);
 
            if (delegateCreation.IsExtensionMethod && method.IsExtensionMethod && (receiverOpt != null))
            {
                MethodSymbol reduced = method.ReduceExtensionMethod(receiverOpt.Type, Compilation);
                method = reduced ?? method;
            }
            return OneOrMany.Create<Symbol>(method);
        }
 
        /// <summary>
        /// Gets for each statement info.
        /// </summary>
        /// <param name="node">The node.</param>
        public abstract ForEachStatementInfo GetForEachStatementInfo(ForEachStatementSyntax node);
 
        /// <summary>
        /// Gets for each statement info.
        /// </summary>
        /// <param name="node">The node.</param>
        public abstract ForEachStatementInfo GetForEachStatementInfo(CommonForEachStatementSyntax node);
 
        /// <summary>
        /// Gets deconstruction assignment info.
        /// </summary>
        /// <param name="node">The node.</param>
        public abstract DeconstructionInfo GetDeconstructionInfo(AssignmentExpressionSyntax node);
 
        /// <summary>
        /// Gets deconstruction foreach info.
        /// </summary>
        /// <param name="node">The node.</param>
        public abstract DeconstructionInfo GetDeconstructionInfo(ForEachVariableStatementSyntax node);
 
        /// <summary>
        /// Gets await expression info.
        /// </summary>
        /// <param name="node">The node.</param>
        public abstract AwaitExpressionInfo GetAwaitExpressionInfo(AwaitExpressionSyntax node);
 
        /// <summary>
        /// If the given node is within a preprocessing directive, gets the preprocessing symbol info for it.
        /// </summary>
        /// <param name="node">Preprocessing symbol identifier node.</param>
        public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(IdentifierNameSyntax node)
        {
            CheckSyntaxNode(node);
 
            if (node.Ancestors().Any(n => SyntaxFacts.IsPreprocessorDirective(n.Kind())))
            {
                bool isDefined = this.SyntaxTree.IsPreprocessorSymbolDefined(node.Identifier.ValueText, node.Identifier.SpanStart);
                return new PreprocessingSymbolInfo(new Symbols.PublicModel.PreprocessingSymbol(node.Identifier.ValueText), isDefined);
            }
 
            return PreprocessingSymbolInfo.None;
        }
 
        /// <summary>
        /// Options to control the internal working of GetSymbolInfoWorker. Not currently exposed
        /// to public clients, but could be if desired.
        /// </summary>
        [Flags]
        internal enum SymbolInfoOptions
        {
            /// <summary>
            /// When binding "C" new C(...), return the type C and do not return information about
            /// which constructor was bound to. Bind "new C(...)" to get information about which constructor
            /// was chosen.
            /// </summary>
            PreferTypeToConstructors = 0x1,
 
            /// <summary>
            /// When binding "C" new C(...), return the constructor of C that was bound to, if C unambiguously
            /// binds to a single type with at least one constructor. 
            /// </summary>
            PreferConstructorsToType = 0x2,
 
            /// <summary>
            /// When binding a name X that was declared with a "using X=OtherTypeOrNamespace", return OtherTypeOrNamespace.
            /// </summary>
            ResolveAliases = 0x4,
 
            /// <summary>
            /// When binding a name X that was declared with a "using X=OtherTypeOrNamespace", return the alias symbol X.
            /// </summary>
            PreserveAliases = 0x8,
 
            // Default Options.
            DefaultOptions = PreferConstructorsToType | ResolveAliases
        }
 
        internal static void ValidateSymbolInfoOptions(SymbolInfoOptions options)
        {
            Debug.Assert(((options & SymbolInfoOptions.PreferConstructorsToType) != 0) !=
                         ((options & SymbolInfoOptions.PreferTypeToConstructors) != 0), "Options are mutually exclusive");
            Debug.Assert(((options & SymbolInfoOptions.ResolveAliases) != 0) !=
                         ((options & SymbolInfoOptions.PreserveAliases) != 0), "Options are mutually exclusive");
        }
 
        /// <summary>
        /// Given a position in the SyntaxTree for this SemanticModel returns the innermost
        /// NamedType that the position is considered inside of.
        /// </summary>
        public ISymbol GetEnclosingSymbol(int position)
        {
            position = CheckAndAdjustPosition(position);
            var binder = GetEnclosingBinder(position);
            return binder == null ? null : binder.ContainingMemberOrLambda.GetPublicSymbol();
        }
 
        #region SemanticModel Members
 
        public sealed override string Language
        {
            get
            {
                return LanguageNames.CSharp;
            }
        }
 
        protected sealed override Compilation CompilationCore
        {
            get
            {
                return this.Compilation;
            }
        }
 
        protected sealed override SemanticModel ParentModelCore
        {
            get
            {
                return this.ParentModel;
            }
        }
 
        protected sealed override SyntaxTree SyntaxTreeCore
        {
            get
            {
                return this.SyntaxTree;
            }
        }
 
        protected sealed override SyntaxNode RootCore => this.Root;
 
        private SymbolInfo GetSymbolInfoFromNode(SyntaxNode node, CancellationToken cancellationToken)
        {
            switch (node)
            {
                case null:
                    throw new ArgumentNullException(nameof(node));
                case ExpressionSyntax expression:
                    return this.GetSymbolInfo(expression, cancellationToken);
                case ConstructorInitializerSyntax initializer:
                    return this.GetSymbolInfo(initializer, cancellationToken);
                case PrimaryConstructorBaseTypeSyntax initializer:
                    return this.GetSymbolInfo(initializer, cancellationToken);
                case AttributeSyntax attribute:
                    return this.GetSymbolInfo(attribute, cancellationToken);
                case CrefSyntax cref:
                    return this.GetSymbolInfo(cref, cancellationToken);
                case SelectOrGroupClauseSyntax selectOrGroupClause:
                    return this.GetSymbolInfo(selectOrGroupClause, cancellationToken);
                case OrderingSyntax orderingSyntax:
                    return this.GetSymbolInfo(orderingSyntax, cancellationToken);
                case PositionalPatternClauseSyntax ppcSyntax:
                    return this.GetSymbolInfo(ppcSyntax, cancellationToken);
            }
 
            return SymbolInfo.None;
        }
 
        private TypeInfo GetTypeInfoFromNode(SyntaxNode node, CancellationToken cancellationToken)
        {
            switch (node)
            {
                case null:
                    throw new ArgumentNullException(nameof(node));
                case ExpressionSyntax expression:
                    return this.GetTypeInfo(expression, cancellationToken);
                case ConstructorInitializerSyntax initializer:
                    return this.GetTypeInfo(initializer, cancellationToken);
                case AttributeSyntax attribute:
                    return this.GetTypeInfo(attribute, cancellationToken);
                case SelectOrGroupClauseSyntax selectOrGroupClause:
                    return this.GetTypeInfo(selectOrGroupClause, cancellationToken);
                case PatternSyntax pattern:
                    return this.GetTypeInfo(pattern, cancellationToken);
            }
 
            return CSharpTypeInfo.None;
        }
 
        private ImmutableArray<ISymbol> GetMemberGroupFromNode(SyntaxNode node, CancellationToken cancellationToken)
        {
            switch (node)
            {
                case null:
                    throw new ArgumentNullException(nameof(node));
                case ExpressionSyntax expression:
                    return this.GetMemberGroup(expression, cancellationToken);
                case ConstructorInitializerSyntax initializer:
                    return this.GetMemberGroup(initializer, cancellationToken);
                case AttributeSyntax attribute:
                    return this.GetMemberGroup(attribute, cancellationToken);
            }
 
            return ImmutableArray<ISymbol>.Empty;
        }
 
        protected sealed override ImmutableArray<ISymbol> GetMemberGroupCore(SyntaxNode node, CancellationToken cancellationToken)
        {
            var methodGroup = this.GetMemberGroupFromNode(node, cancellationToken);
            return StaticCast<ISymbol>.From(methodGroup);
        }
 
        protected sealed override SymbolInfo GetSpeculativeSymbolInfoCore(int position, SyntaxNode node, SpeculativeBindingOption bindingOption)
        {
            switch (node)
            {
                case ExpressionSyntax expression:
                    return GetSpeculativeSymbolInfo(position, expression, bindingOption);
                case ConstructorInitializerSyntax initializer:
                    return GetSpeculativeSymbolInfo(position, initializer);
                case PrimaryConstructorBaseTypeSyntax initializer:
                    return GetSpeculativeSymbolInfo(position, initializer);
                case AttributeSyntax attribute:
                    return GetSpeculativeSymbolInfo(position, attribute);
                case CrefSyntax cref:
                    return GetSpeculativeSymbolInfo(position, cref);
            }
 
            return SymbolInfo.None;
        }
 
        protected sealed override TypeInfo GetSpeculativeTypeInfoCore(int position, SyntaxNode node, SpeculativeBindingOption bindingOption)
        {
            return node is ExpressionSyntax expression
                ? GetSpeculativeTypeInfo(position, expression, bindingOption)
                : CSharpTypeInfo.None;
        }
 
        protected sealed override IAliasSymbol GetSpeculativeAliasInfoCore(int position, SyntaxNode nameSyntax, SpeculativeBindingOption bindingOption)
        {
            return nameSyntax is IdentifierNameSyntax identifier
                ? GetSpeculativeAliasInfo(position, identifier, bindingOption)
                : null;
        }
 
        protected sealed override SymbolInfo GetSymbolInfoCore(SyntaxNode node, CancellationToken cancellationToken)
        {
            return this.GetSymbolInfoFromNode(node, cancellationToken);
        }
 
        protected sealed override TypeInfo GetTypeInfoCore(SyntaxNode node, CancellationToken cancellationToken)
        {
            return this.GetTypeInfoFromNode(node, cancellationToken);
        }
 
        protected sealed override IAliasSymbol GetAliasInfoCore(SyntaxNode node, CancellationToken cancellationToken)
        {
            return node is IdentifierNameSyntax nameSyntax ? GetAliasInfo(nameSyntax, cancellationToken) : null;
        }
 
        protected sealed override PreprocessingSymbolInfo GetPreprocessingSymbolInfoCore(SyntaxNode node)
        {
            return node is IdentifierNameSyntax nameSyntax
                ? GetPreprocessingSymbolInfo(nameSyntax)
                : PreprocessingSymbolInfo.None;
        }
 
        protected sealed override ISymbol GetDeclaredSymbolCore(SyntaxNode node, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
 
            switch (node)
            {
                case AccessorDeclarationSyntax accessor:
                    return this.GetDeclaredSymbol(accessor, cancellationToken);
                case BaseTypeDeclarationSyntax type:
                    return this.GetDeclaredSymbol(type, cancellationToken);
                case QueryClauseSyntax clause:
                    return this.GetDeclaredSymbol(clause, cancellationToken);
                case MemberDeclarationSyntax member:
                    return this.GetDeclaredSymbol(member, cancellationToken);
            }
 
            switch (node.Kind())
            {
                case SyntaxKind.LocalFunctionStatement:
                    return this.GetDeclaredSymbol((LocalFunctionStatementSyntax)node, cancellationToken);
                case SyntaxKind.LabeledStatement:
                    return this.GetDeclaredSymbol((LabeledStatementSyntax)node, cancellationToken);
                case SyntaxKind.CaseSwitchLabel:
                case SyntaxKind.DefaultSwitchLabel:
                    return this.GetDeclaredSymbol((SwitchLabelSyntax)node, cancellationToken);
                case SyntaxKind.AnonymousObjectCreationExpression:
                    return this.GetDeclaredSymbol((AnonymousObjectCreationExpressionSyntax)node, cancellationToken);
                case SyntaxKind.AnonymousObjectMemberDeclarator:
                    return this.GetDeclaredSymbol((AnonymousObjectMemberDeclaratorSyntax)node, cancellationToken);
                case SyntaxKind.TupleExpression:
                    return this.GetDeclaredSymbol((TupleExpressionSyntax)node, cancellationToken);
                case SyntaxKind.Argument:
                    return this.GetDeclaredSymbol((ArgumentSyntax)node, cancellationToken);
                case SyntaxKind.VariableDeclarator:
                    return this.GetDeclaredSymbol((VariableDeclaratorSyntax)node, cancellationToken);
                case SyntaxKind.SingleVariableDesignation:
                    return this.GetDeclaredSymbol((SingleVariableDesignationSyntax)node, cancellationToken);
                case SyntaxKind.TupleElement:
                    return this.GetDeclaredSymbol((TupleElementSyntax)node, cancellationToken);
                case SyntaxKind.NamespaceDeclaration:
                    return this.GetDeclaredSymbol((NamespaceDeclarationSyntax)node, cancellationToken);
                case SyntaxKind.FileScopedNamespaceDeclaration:
                    return this.GetDeclaredSymbol((FileScopedNamespaceDeclarationSyntax)node, cancellationToken);
                case SyntaxKind.Parameter:
                    return this.GetDeclaredSymbol((ParameterSyntax)node, cancellationToken);
                case SyntaxKind.TypeParameter:
                    return this.GetDeclaredSymbol((TypeParameterSyntax)node, cancellationToken);
                case SyntaxKind.UsingDirective:
                    var usingDirective = (UsingDirectiveSyntax)node;
                    if (usingDirective.Alias == null)
                    {
                        break;
                    }
 
                    return this.GetDeclaredSymbol(usingDirective, cancellationToken);
                case SyntaxKind.ForEachStatement:
                    return this.GetDeclaredSymbol((ForEachStatementSyntax)node);
                case SyntaxKind.CatchDeclaration:
                    return this.GetDeclaredSymbol((CatchDeclarationSyntax)node);
                case SyntaxKind.JoinIntoClause:
                    return this.GetDeclaredSymbol((JoinIntoClauseSyntax)node, cancellationToken);
                case SyntaxKind.QueryContinuation:
                    return this.GetDeclaredSymbol((QueryContinuationSyntax)node, cancellationToken);
                case SyntaxKind.CompilationUnit:
                    return this.GetDeclaredSymbol((CompilationUnitSyntax)node, cancellationToken);
            }
 
            return null;
        }
 
        /// <summary>
        /// Given a tuple element syntax, get the corresponding symbol.
        /// </summary>
        /// <param name="declarationSyntax">The syntax node that declares a tuple element.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The symbol that was declared.</returns>
        public ISymbol GetDeclaredSymbol(TupleElementSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckSyntaxNode(declarationSyntax);
 
            if (declarationSyntax.Parent is TupleTypeSyntax tupleTypeSyntax)
            {
                return (GetSymbolInfo(tupleTypeSyntax, cancellationToken).Symbol.GetSymbol() as NamedTypeSymbol)?.TupleElements.ElementAtOrDefault(tupleTypeSyntax.Elements.IndexOf(declarationSyntax)).GetPublicSymbol();
            }
 
            return null;
        }
 
        protected sealed override ImmutableArray<ISymbol> GetDeclaredSymbolsCore(SyntaxNode declaration, CancellationToken cancellationToken = default(CancellationToken))
        {
            cancellationToken.ThrowIfCancellationRequested();
 
            if (declaration is BaseFieldDeclarationSyntax field)
            {
                return this.GetDeclaredSymbols(field, cancellationToken);
            }
 
            // If the type decl has a primary constructor, return that symbol as well.  This is needed so that if the
            // 'suppression' or 'generated code' attribute is on the primary constructor (i.e. by using `[method:
            // SuppressMessage(...)]`, it will be found when walking up to the type declaration.
            if (declaration is TypeDeclarationSyntax typeDeclaration)
            {
                var namedType = GetDeclaredSymbol(typeDeclaration, cancellationToken);
                var primaryConstructor = TryGetSynthesizedPrimaryConstructor(
                    typeDeclaration, namedType.GetSymbol<NamedTypeSymbol>());
 
                return primaryConstructor is null
                    ? ImmutableArray.Create<ISymbol>(namedType)
                    : ImmutableArray.Create<ISymbol>(namedType, primaryConstructor.GetPublicSymbol());
            }
 
            var symbol = GetDeclaredSymbolCore(declaration, cancellationToken);
            return symbol != null
                ? ImmutableArray.Create(symbol)
                : ImmutableArray<ISymbol>.Empty;
        }
 
#nullable enable
        public IMethodSymbol? GetInterceptorMethod(InvocationExpressionSyntax node, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
 
            CheckSyntaxNode(node);
 
            if (node.GetInterceptableNameSyntax() is { } nameSyntax && Compilation.TryGetInterceptor(nameSyntax) is (_, MethodSymbol interceptor))
            {
                return interceptor.GetPublicSymbol();
            }
 
            return null;
        }
 
#pragma warning disable RSEXPERIMENTAL002 // Internal usage of experimental API
        public InterceptableLocation? GetInterceptableLocation(InvocationExpressionSyntax node, CancellationToken cancellationToken)
        {
            CheckSyntaxNode(node);
            if (node.GetInterceptableNameSyntax() is not { } nameSyntax)
            {
                return null;
            }
 
            return GetInterceptableLocationInternal(nameSyntax, cancellationToken);
        }
 
        // Factored out for ease of test authoring, especially for scenarios involving unsupported syntax.
        internal InterceptableLocation GetInterceptableLocationInternal(SyntaxNode nameSyntax, CancellationToken cancellationToken)
        {
            var tree = nameSyntax.SyntaxTree;
            var text = tree.GetText(cancellationToken);
            var path = tree.FilePath;
            var checksum = text.GetContentHash();
 
            var lineSpan = nameSyntax.Location.GetLineSpan().Span.Start;
            var lineNumberOneIndexed = lineSpan.Line + 1;
            var characterNumberOneIndexed = lineSpan.Character + 1;
 
            return new InterceptableLocation1(checksum, path, Compilation.Options.SourceReferenceResolver, nameSyntax.Position, lineNumberOneIndexed, characterNumberOneIndexed);
        }
#nullable disable
 
        protected static SynthesizedPrimaryConstructor TryGetSynthesizedPrimaryConstructor(TypeDeclarationSyntax node, NamedTypeSymbol type)
        {
            if (type is SourceMemberContainerTypeSymbol { PrimaryConstructor: { } primaryConstructor }
                && primaryConstructor.SyntaxRef.SyntaxTree == node.SyntaxTree
                && primaryConstructor.GetSyntax() == node)
            {
                return primaryConstructor;
            }
 
            return null;
        }
 
        internal override void ComputeDeclarationsInSpan(TextSpan span, bool getSymbol, ArrayBuilder<DeclarationInfo> builder, CancellationToken cancellationToken)
        {
            CSharpDeclarationComputer.ComputeDeclarationsInSpan(this, span, getSymbol, builder, cancellationToken);
        }
 
        internal override void ComputeDeclarationsInNode(SyntaxNode node, ISymbol associatedSymbol, bool getSymbol, ArrayBuilder<DeclarationInfo> builder, CancellationToken cancellationToken, int? levelsToCompute = null)
        {
            CSharpDeclarationComputer.ComputeDeclarationsInNode(this, associatedSymbol, node, getSymbol, builder, cancellationToken, levelsToCompute);
        }
 
        internal abstract override Func<SyntaxNode, bool> GetSyntaxNodesToAnalyzeFilter(SyntaxNode declaredNode, ISymbol declaredSymbol);
        internal abstract override bool ShouldSkipSyntaxNodeAnalysis(SyntaxNode node, ISymbol containingSymbol);
 
        protected internal override SyntaxNode GetTopmostNodeForDiagnosticAnalysis(ISymbol symbol, SyntaxNode declaringSyntax)
        {
            switch (symbol.Kind)
            {
                case SymbolKind.Event:  // for field-like events
                case SymbolKind.Field:
                    var fieldDecl = declaringSyntax.FirstAncestorOrSelf<BaseFieldDeclarationSyntax>();
                    if (fieldDecl != null)
                    {
                        return fieldDecl;
                    }
 
                    break;
            }
 
            return declaringSyntax;
        }
 
        protected sealed override ImmutableArray<ISymbol> LookupSymbolsCore(int position, INamespaceOrTypeSymbol container, string name, bool includeReducedExtensionMethods)
        {
            return LookupSymbols(position, container.EnsureCSharpSymbolOrNull(nameof(container)), name, includeReducedExtensionMethods);
        }
 
        protected sealed override ImmutableArray<ISymbol> LookupBaseMembersCore(int position, string name)
        {
            return LookupBaseMembers(position, name);
        }
 
        protected sealed override ImmutableArray<ISymbol> LookupStaticMembersCore(int position, INamespaceOrTypeSymbol container, string name)
        {
            return LookupStaticMembers(position, container.EnsureCSharpSymbolOrNull(nameof(container)), name);
        }
 
        protected sealed override ImmutableArray<ISymbol> LookupNamespacesAndTypesCore(int position, INamespaceOrTypeSymbol container, string name)
        {
            return LookupNamespacesAndTypes(position, container.EnsureCSharpSymbolOrNull(nameof(container)), name);
        }
 
        protected sealed override ImmutableArray<ISymbol> LookupLabelsCore(int position, string name)
        {
            return LookupLabels(position, name);
        }
 
        protected sealed override ControlFlowAnalysis AnalyzeControlFlowCore(SyntaxNode firstStatement, SyntaxNode lastStatement)
        {
            if (firstStatement == null)
            {
                throw new ArgumentNullException(nameof(firstStatement));
            }
 
            if (lastStatement == null)
            {
                throw new ArgumentNullException(nameof(lastStatement));
            }
 
            if (!(firstStatement is StatementSyntax firstStatementSyntax))
            {
                throw new ArgumentException("firstStatement is not a StatementSyntax.");
            }
 
            if (!(lastStatement is StatementSyntax lastStatementSyntax))
            {
                throw new ArgumentException("firstStatement is a StatementSyntax but lastStatement isn't.");
            }
 
            return this.AnalyzeControlFlow(firstStatementSyntax, lastStatementSyntax);
        }
 
        protected sealed override ControlFlowAnalysis AnalyzeControlFlowCore(SyntaxNode statement)
        {
            if (statement == null)
            {
                throw new ArgumentNullException(nameof(statement));
            }
 
            if (!(statement is StatementSyntax statementSyntax))
            {
                throw new ArgumentException("statement is not a StatementSyntax.");
            }
 
            return this.AnalyzeControlFlow(statementSyntax);
        }
 
        protected sealed override DataFlowAnalysis AnalyzeDataFlowCore(SyntaxNode firstStatement, SyntaxNode lastStatement)
        {
            if (firstStatement == null)
            {
                throw new ArgumentNullException(nameof(firstStatement));
            }
 
            if (lastStatement == null)
            {
                throw new ArgumentNullException(nameof(lastStatement));
            }
 
            if (!(firstStatement is StatementSyntax firstStatementSyntax))
            {
                throw new ArgumentException("firstStatement is not a StatementSyntax.");
            }
 
            if (!(lastStatement is StatementSyntax lastStatementSyntax))
            {
                throw new ArgumentException("lastStatement is not a StatementSyntax.");
            }
 
            return this.AnalyzeDataFlow(firstStatementSyntax, lastStatementSyntax);
        }
 
        protected sealed override DataFlowAnalysis AnalyzeDataFlowCore(SyntaxNode statementOrExpression)
        {
            switch (statementOrExpression)
            {
                case null:
                    throw new ArgumentNullException(nameof(statementOrExpression));
                case StatementSyntax statementSyntax:
                    return this.AnalyzeDataFlow(statementSyntax);
                case ExpressionSyntax expressionSyntax:
                    return this.AnalyzeDataFlow(expressionSyntax);
                case ConstructorInitializerSyntax constructorInitializer:
                    return this.AnalyzeDataFlow(constructorInitializer);
                case PrimaryConstructorBaseTypeSyntax primaryConstructorBaseType:
                    return this.AnalyzeDataFlow(primaryConstructorBaseType);
                default:
                    throw new ArgumentException("statementOrExpression is not a StatementSyntax or an ExpressionSyntax or a ConstructorInitializerSyntax or a PrimaryConstructorBaseTypeSyntax.");
            }
        }
 
        protected sealed override Optional<object> GetConstantValueCore(SyntaxNode node, CancellationToken cancellationToken)
        {
            if (node == null)
            {
                throw new ArgumentNullException(nameof(node));
            }
 
            return node is ExpressionSyntax expression
                ? GetConstantValue(expression, cancellationToken)
                : default(Optional<object>);
        }
 
        protected sealed override ISymbol GetEnclosingSymbolCore(int position, CancellationToken cancellationToken)
        {
            return this.GetEnclosingSymbol(position);
        }
 
        private protected sealed override ImmutableArray<IImportScope> GetImportScopesCore(int position, CancellationToken cancellationToken)
        {
            position = CheckAndAdjustPosition(position);
            var binder = GetEnclosingBinder(position);
            var builder = ArrayBuilder<IImportScope>.GetInstance();
 
            for (var chain = binder?.ImportChain; chain != null; chain = chain.ParentOpt)
            {
                var imports = chain.Imports;
                if (imports.IsEmpty)
                    continue;
 
                // Try to create a node corresponding to the imports of the next higher binder scope. Then create the
                // node corresponding to this set of imports and chain it to that.
                builder.Add(new SimpleImportScope(
                    imports.UsingAliases.SelectAsArray(static kvp => kvp.Value.Alias.GetPublicSymbol()),
                    imports.ExternAliases.SelectAsArray(static e => e.Alias.GetPublicSymbol()),
                    imports.Usings.SelectAsArray(static n => new ImportedNamespaceOrType(n.NamespaceOrType.GetPublicSymbol(), n.UsingDirectiveReference)),
                    xmlNamespaces: ImmutableArray<ImportedXmlNamespace>.Empty));
            }
 
            return builder.ToImmutableAndFree();
        }
 
        protected sealed override bool IsAccessibleCore(int position, ISymbol symbol)
        {
            return this.IsAccessible(position, symbol.EnsureCSharpSymbolOrNull(nameof(symbol)));
        }
 
        protected sealed override bool IsEventUsableAsFieldCore(int position, IEventSymbol symbol)
        {
            return this.IsEventUsableAsField(position, symbol.EnsureCSharpSymbolOrNull(nameof(symbol)));
        }
 
        public sealed override NullableContext GetNullableContext(int position)
        {
            var syntaxTree = (CSharpSyntaxTree)Root.SyntaxTree;
 
            NullableContextOptions? lazyDefaultState = null;
            NullableContextState contextState = syntaxTree.GetNullableContextState(position);
 
            return contextState.AnnotationsState switch
            {
                NullableContextState.State.Enabled => NullableContext.AnnotationsEnabled,
                NullableContextState.State.Disabled => NullableContext.Disabled,
                _ when getDefaultState().AnnotationsEnabled() => NullableContext.AnnotationsContextInherited | NullableContext.AnnotationsEnabled,
                _ => NullableContext.AnnotationsContextInherited,
            }
            | contextState.WarningsState switch
            {
                NullableContextState.State.Enabled => NullableContext.WarningsEnabled,
                NullableContextState.State.Disabled => NullableContext.Disabled,
                _ when getDefaultState().WarningsEnabled() => NullableContext.WarningsContextInherited | NullableContext.WarningsEnabled,
                _ => NullableContext.WarningsContextInherited,
            };
 
            // IsGeneratedCode might be slow, only call it when needed:
            NullableContextOptions getDefaultState()
                => lazyDefaultState ??= syntaxTree.IsGeneratedCode(Compilation.Options.SyntaxTreeOptionsProvider, CancellationToken.None)
                    ? NullableContextOptions.Disable
                    : Compilation.Options.NullableContextOptions;
        }
 
        #endregion
    }
}