File: Symbols\Symbol.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.Globalization;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    /// <summary>
    /// The base class for all symbols (namespaces, classes, method, parameters, etc.) that are 
    /// exposed by the compiler.
    /// </summary>
    [DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
    internal abstract partial class Symbol : ISymbolInternal, IFormattable
    {
        private ISymbol _lazyISymbol;
 
        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        // Changes to the public interface of this class should remain synchronized with the VB version of Symbol.
        // Do not make any changes to the public interface without making the corresponding change
        // to the VB version.
        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
        /// <summary>
        /// True if this Symbol should be completed by calling ForceComplete.
        /// Intuitively, true for source entities (from any compilation).
        /// </summary>
        internal virtual bool RequiresCompletion
        {
            get { return false; }
        }
 
#nullable enable
        internal virtual void ForceComplete(SourceLocation? locationOpt, Predicate<Symbol>? filter, CancellationToken cancellationToken)
        {
            // must be overridden by source symbols, no-op for other symbols
            Debug.Assert(!this.RequiresCompletion);
        }
#nullable disable
 
        internal virtual bool HasComplete(CompletionPart part)
        {
            // must be overridden by source symbols, no-op for other symbols
            Debug.Assert(!this.RequiresCompletion);
            return true;
        }
 
        /// <summary>
        /// Gets the name of this symbol. Symbols without a name return the empty string; null is
        /// never returned.
        /// </summary>
        public virtual string Name
        {
            get
            {
                return string.Empty;
            }
        }
 
        /// <summary>
        /// Gets the name of a symbol as it appears in metadata. Most of the time, this
        /// is the same as the Name property, with the following exceptions:
        /// 1) The metadata name of generic types includes the "`1", "`2" etc. suffix that
        /// indicates the number of type parameters (it does not include, however, names of
        /// containing types or namespaces).
        /// 2) The metadata name of explicit interface names have spaces removed, compared to
        /// the name property.
        /// </summary>
        public virtual string MetadataName
        {
            get
            {
                return this.Name;
            }
        }
 
        /// <summary>
        /// Gets the token for this symbol as it appears in metadata. Most of the time this is 0,
        /// as it is when the symbol is not loaded from metadata.
        /// </summary>
        public virtual int MetadataToken => 0;
 
        /// <summary>
        /// Gets the kind of this symbol.
        /// </summary>
        public abstract SymbolKind Kind { get; }
 
        /// <summary>
        /// Get the symbol that logically contains this symbol. 
        /// </summary>
        public abstract Symbol ContainingSymbol { get; }
 
        /// <summary>
        /// Returns the nearest lexically enclosing type, or null if there is none.
        /// </summary>
        public virtual NamedTypeSymbol ContainingType
        {
            get
            {
                Symbol container = this.ContainingSymbol;
 
                NamedTypeSymbol containerAsType = container as NamedTypeSymbol;
 
                // NOTE: container could be null, so we do not check 
                //       whether containerAsType is not null, but 
                //       instead check if it did not change after 
                //       the cast.
                if ((object)containerAsType == (object)container)
                {
                    // this should be relatively uncommon
                    // most symbols that may be contained in a type
                    // know their containing type and can override ContainingType
                    // with a more precise implementation
                    return containerAsType;
                }
 
                // this is recursive, but recursion should be very short 
                // before we reach symbol that definitely knows its containing type.
                return container.ContainingType;
            }
        }
 
        /// <summary>
        /// Gets the nearest enclosing namespace for this namespace or type. For a nested type,
        /// returns the namespace that contains its container.
        /// </summary>
        public virtual NamespaceSymbol ContainingNamespace
        {
            get
            {
                for (var container = this.ContainingSymbol; (object)container != null; container = container.ContainingSymbol)
                {
                    var ns = container as NamespaceSymbol;
                    if ((object)ns != null)
                    {
                        return ns;
                    }
                }
 
                return null;
            }
        }
 
        /// <summary>
        /// Returns the assembly containing this symbol. If this symbol is shared across multiple
        /// assemblies, or doesn't belong to an assembly, returns null.
        /// </summary>
        public virtual AssemblySymbol ContainingAssembly
        {
            get
            {
                // Default implementation gets the containers assembly.
 
                var container = this.ContainingSymbol;
                return (object)container != null ? container.ContainingAssembly : null;
            }
        }
 
        /// <summary>
        /// For a source assembly, the associated compilation.
        /// For any other assembly, null.
        /// For a source module, the DeclaringCompilation of the associated source assembly.
        /// For any other module, null.
        /// For any other symbol, the DeclaringCompilation of the associated module.
        /// </summary>
        /// <remarks>
        /// We're going through the containing module, rather than the containing assembly,
        /// because of /addmodule (symbols in such modules should return null).
        /// 
        /// Remarks, not "ContainingCompilation" because it isn't transitive.
        /// </remarks>
        internal virtual CSharpCompilation DeclaringCompilation
        {
            get
            {
                if (!this.IsDefinition)
                {
                    return OriginalDefinition.DeclaringCompilation;
                }
 
                switch (this.Kind)
                {
                    case SymbolKind.ErrorType:
                        return null;
                    case SymbolKind.Assembly:
                        Debug.Assert(!(this is SourceAssemblySymbol), "SourceAssemblySymbol must override DeclaringCompilation");
                        return null;
                    case SymbolKind.NetModule:
                        Debug.Assert(!(this is SourceModuleSymbol), "SourceModuleSymbol must override DeclaringCompilation");
                        return null;
                }
 
                switch (this.ContainingModule)
                {
                    case SourceModuleSymbol sourceModuleSymbol:
                        return sourceModuleSymbol.DeclaringCompilation;
 
                    case PEModuleSymbol:
                        // A special handling for EE.
                        return ContainingSymbol?.DeclaringCompilation;
                }
 
                return null;
            }
        }
 
        Compilation ISymbolInternal.DeclaringCompilation
            => DeclaringCompilation;
 
        string ISymbolInternal.Name => this.Name;
 
        string ISymbolInternal.MetadataName => this.MetadataName;
 
        public Cci.TypeMemberVisibility MetadataVisibility
        {
            get
            {
                //
                // We need to relax visibility of members in interactive submissions since they might be emitted into multiple assemblies.
                //
                // Top-level:
                //   private                       -> public
                //   protected                     -> public (compiles with a warning)
                //   public
                //   internal                      -> public
                //
                // In a nested class:
                //
                //   private
                //   protected
                //   public
                //   internal                      -> public
                //
                switch (DeclaredAccessibility)
                {
                    case Accessibility.Public:
                        return Cci.TypeMemberVisibility.Public;
 
                    case Accessibility.Private:
                        if (ContainingType?.TypeKind == TypeKind.Submission)
                        {
                            // top-level private member:
                            return Cci.TypeMemberVisibility.Public;
                        }
                        else
                        {
                            return Cci.TypeMemberVisibility.Private;
                        }
 
                    case Accessibility.Internal:
                        if (ContainingAssembly.IsInteractive)
                        {
                            // top-level or nested internal member:
                            return Cci.TypeMemberVisibility.Public;
                        }
                        else
                        {
                            return Cci.TypeMemberVisibility.Assembly;
                        }
 
                    case Accessibility.Protected:
                        if (ContainingType.TypeKind == TypeKind.Submission)
                        {
                            // top-level protected member:
                            return Cci.TypeMemberVisibility.Public;
                        }
                        else
                        {
                            return Cci.TypeMemberVisibility.Family;
                        }
 
                    case Accessibility.ProtectedAndInternal:
                        Debug.Assert(ContainingType.TypeKind != TypeKind.Submission);
                        return Cci.TypeMemberVisibility.FamilyAndAssembly;
 
                    case Accessibility.ProtectedOrInternal:
                        if (ContainingAssembly.IsInteractive)
                        {
                            // top-level or nested protected internal member:
                            return Cci.TypeMemberVisibility.Public;
                        }
                        else
                        {
                            return Cci.TypeMemberVisibility.FamilyOrAssembly;
                        }
 
                    default:
                        throw ExceptionUtilities.UnexpectedValue(DeclaredAccessibility);
                }
            }
        }
 
        ISymbolInternal ISymbolInternal.ContainingSymbol => this.ContainingSymbol;
 
        IModuleSymbolInternal ISymbolInternal.ContainingModule => this.ContainingModule;
 
        IAssemblySymbolInternal ISymbolInternal.ContainingAssembly => this.ContainingAssembly;
 
        ImmutableArray<Location> ISymbolInternal.Locations => this.Locations;
 
        INamespaceSymbolInternal ISymbolInternal.ContainingNamespace => this.ContainingNamespace;
 
        bool ISymbolInternal.IsImplicitlyDeclared => this.IsImplicitlyDeclared;
 
        INamedTypeSymbolInternal ISymbolInternal.ContainingType
        {
            get
            {
                return this.ContainingType;
            }
        }
 
        ISymbol ISymbolInternal.GetISymbol() => this.ISymbol;
 
        /// <summary>
        /// Returns the module containing this symbol. If this symbol is shared across multiple
        /// modules, or doesn't belong to a module, returns null.
        /// </summary>
        internal virtual ModuleSymbol ContainingModule
        {
            get
            {
                // Default implementation gets the containers module.
 
                var container = this.ContainingSymbol;
                return (object)container != null ? container.ContainingModule : null;
            }
        }
 
        /// <summary>
        /// The index of this member in the containing symbol. This is an optional
        /// property, implemented by anonymous type properties only, for comparing
        /// symbols in flow analysis.
        /// </summary>
        /// <remarks>
        /// Should this be used for tuple fields as well?
        /// </remarks>
        internal virtual int? MemberIndexOpt => null;
 
        /// <summary>
        /// The original definition of this symbol. If this symbol is constructed from another
        /// symbol by type substitution then OriginalDefinition gets the original symbol as it was defined in
        /// source or metadata.
        /// </summary>
        public Symbol OriginalDefinition
        {
            get
            {
                return OriginalSymbolDefinition;
            }
        }
 
        protected virtual Symbol OriginalSymbolDefinition
        {
            get
            {
                return this;
            }
        }
 
        /// <summary>
        /// Returns true if this is the original definition of this symbol.
        /// </summary>
        public bool IsDefinition
        {
            get
            {
                return (object)this == (object)OriginalDefinition;
            }
        }
 
        /// <summary>
        /// <para>
        /// Get a source location key for sorting. For performance, it's important that this
        /// be able to be returned from a symbol without doing any additional allocations (even
        /// if nothing is cached yet.)
        /// </para>
        /// <para>
        /// Only (original) source symbols and namespaces that can be merged
        /// need implement this function if they want to do so for efficiency.
        /// </para>
        /// </summary>
        internal virtual LexicalSortKey GetLexicalSortKey()
        {
            var firstLocation = this.TryGetFirstLocation();
            if (firstLocation is null)
                return LexicalSortKey.NotInSource;
 
            var declaringCompilation = this.DeclaringCompilation;
            Debug.Assert(declaringCompilation != null); // require that it is a source symbol
            return new LexicalSortKey(firstLocation, declaringCompilation);
        }
 
        /// <summary>
        /// Gets the locations where this symbol was originally defined, either in source or
        /// metadata. Some symbols (for example, partial classes) may be defined in more than one
        /// location.
        /// </summary>
        public abstract ImmutableArray<Location> Locations { get; }
 
#nullable enable
 
        public virtual Location? TryGetFirstLocation()
        {
            // Simple (but allocating) impl that can be overridden in subtypes if they show up in traces.
            var locations = this.Locations;
            return locations.IsEmpty ? null : locations[0];
        }
 
        public Location GetFirstLocation()
            => TryGetFirstLocation() ?? throw new InvalidOperationException("Symbol has no locations");
 
        public Location GetFirstLocationOrNone()
            => TryGetFirstLocation() ?? Location.None;
 
        /// <summary>
        /// Determines if there is a location (see <see cref="Locations"/>) for this symbol whose span is in <paramref
        /// name="tree"/> and is contained within <paramref name="declarationSpan"/>.  Subclasses can override this to
        /// be more efficient if desired (especially if avoiding allocations of the <see cref="Locations"/> array is
        /// desired).
        /// </summary>
        public virtual bool HasLocationContainedWithin(SyntaxTree tree, TextSpan declarationSpan, out bool wasZeroWidthMatch)
        {
            foreach (var loc in this.Locations)
            {
                if (IsLocationContainedWithin(loc, tree, declarationSpan, out wasZeroWidthMatch))
                    return true;
            }
 
            wasZeroWidthMatch = false;
            return false;
        }
 
        protected static bool IsLocationContainedWithin(Location loc, SyntaxTree tree, TextSpan declarationSpan, out bool wasZeroWidthMatch)
        {
            if (loc.IsInSource && loc.SourceTree == tree && declarationSpan.Contains(loc.SourceSpan))
            {
                wasZeroWidthMatch = loc.SourceSpan.IsEmpty && loc.SourceSpan.End == declarationSpan.Start;
                return true;
            }
 
            wasZeroWidthMatch = false;
            return false;
        }
 
#nullable disable
 
        /// <summary>
        /// <para>
        /// Get the syntax node(s) where this symbol was declared in source. Some symbols (for
        /// example, partial classes) may be defined in more than one location. This property should
        /// return one or more syntax nodes only if the symbol was declared in source code and also
        /// was not implicitly declared (see the <see cref="IsImplicitlyDeclared"/> property). 
        /// </para>
        /// <para>
        /// Note that for namespace symbol, the declaring syntax might be declaring a nested
        /// namespace. For example, the declaring syntax node for N1 in "namespace N1.N2 {...}" is
        /// the entire <see cref="BaseNamespaceDeclarationSyntax"/> for N1.N2. For the global namespace, the declaring
        /// syntax will be the <see cref="CompilationUnitSyntax"/>.
        /// </para>
        /// </summary>
        /// <returns>
        /// The syntax node(s) that declared the symbol. If the symbol was declared in metadata or
        /// was implicitly declared, returns an empty read-only array.
        /// </returns>
        /// <remarks>
        /// To go the opposite direction (from syntax node to symbol), see <see
        /// cref="CSharpSemanticModel.GetDeclaredSymbol(MemberDeclarationSyntax, CancellationToken)"/>.
        /// </remarks>
        public abstract ImmutableArray<SyntaxReference> DeclaringSyntaxReferences { get; }
 
        /// <summary>
        /// Helper for implementing <see cref="DeclaringSyntaxReferences"/> for derived classes that store a location but not a 
        /// <see cref="CSharpSyntaxNode"/> or <see cref="SyntaxReference"/>.
        /// </summary>
        internal static ImmutableArray<SyntaxReference> GetDeclaringSyntaxReferenceHelper<TNode>(ImmutableArray<Location> locations)
            where TNode : CSharpSyntaxNode
        {
            if (locations.IsEmpty)
            {
                return ImmutableArray<SyntaxReference>.Empty;
            }
 
            ArrayBuilder<SyntaxReference> builder = ArrayBuilder<SyntaxReference>.GetInstance();
            foreach (Location location in locations)
            {
                // Location may be null. See https://github.com/dotnet/roslyn/issues/28862.
                if (location == null || !location.IsInSource)
                {
                    continue;
                }
 
                if (location.SourceSpan.Length != 0)
                {
                    SyntaxToken token = location.SourceTree.GetRoot().FindToken(location.SourceSpan.Start);
                    if (token.Kind() != SyntaxKind.None)
                    {
                        CSharpSyntaxNode node = token.Parent.FirstAncestorOrSelf<TNode>();
                        if (node != null)
                        {
                            builder.Add(node.GetReference());
                        }
                    }
                }
                else
                {
                    // Since the location we're interested in can't contain a token, we'll inspect the whole tree,
                    // pruning away branches that don't contain that location. We'll pick the narrowest node of the type
                    // we're looking for.
                    // eg: finding the ParameterSyntax from the empty location of a blank identifier
                    SyntaxNode parent = location.SourceTree.GetRoot();
                    SyntaxNode found = null;
                    foreach (var descendant in parent.DescendantNodesAndSelf(c => c.Location.SourceSpan.Contains(location.SourceSpan)))
                    {
                        if (descendant is TNode && descendant.Location.SourceSpan.Contains(location.SourceSpan))
                        {
                            found = descendant;
                        }
                    }
 
                    if (found is object)
                    {
                        builder.Add(found.GetReference());
                    }
                }
            }
 
            return builder.ToImmutableAndFree();
        }
 
        /// <summary>
        /// Get this accessibility that was declared on this symbol. For symbols that do not have
        /// accessibility declared on them, returns <see cref="Accessibility.NotApplicable"/>.
        /// </summary>
        public abstract Accessibility DeclaredAccessibility { get; }
 
        /// <summary>
        /// Returns true if this symbol is "static"; i.e., declared with the <c>static</c> modifier or
        /// implicitly static.
        /// </summary>
        public abstract bool IsStatic { get; }
 
        /// <summary>
        /// Returns true if this symbol is "virtual", has an implementation, and does not override a
        /// base class member; i.e., declared with the <c>virtual</c> modifier. Does not return true for
        /// members declared as abstract or override.
        /// </summary>
        public abstract bool IsVirtual { get; }
 
        /// <summary>
        /// Returns true if this symbol was declared to override a base class member; i.e., declared
        /// with the <c>override</c> modifier. Still returns true if member was declared to override
        /// something, but (erroneously) no member to override exists.
        /// </summary>
        /// <remarks>
        /// Even for metadata symbols, <see cref="IsOverride"/> = true does not imply that <see cref="IMethodSymbol.OverriddenMethod"/> will
        /// be non-null.
        /// </remarks>
        public abstract bool IsOverride { get; }
 
        /// <summary>
        /// Returns true if this symbol was declared as requiring an override; i.e., declared with
        /// the <c>abstract</c> modifier. Also returns true on a type declared as "abstract", all
        /// interface types, and members of interface types.
        /// </summary>
        public abstract bool IsAbstract { get; }
 
        /// <summary>
        /// Returns true if this symbol was declared to override a base class member and was also
        /// sealed from further overriding; i.e., declared with the <c>sealed</c> modifier. Also set for
        /// types that do not allow a derived class (declared with <c>sealed</c> or <c>static</c> or <c>struct</c>
        /// or <c>enum</c> or <c>delegate</c>).
        /// </summary>
        public abstract bool IsSealed { get; }
 
        /// <summary>
        /// Returns true if this symbol has external implementation; i.e., declared with the 
        /// <c>extern</c> modifier. 
        /// </summary>
        public abstract bool IsExtern { get; }
 
        /// <summary>
        /// Returns true if this symbol was automatically created by the compiler, and does not
        /// have an explicit corresponding source code declaration.  
        /// 
        /// This is intended for symbols that are ordinary symbols in the language sense,
        /// and may be used by code, but that are simply declared implicitly rather than
        /// with explicit language syntax.
        /// 
        /// Examples include (this list is not exhaustive):
        ///   the default constructor for a class or struct that is created if one is not provided,
        ///   the BeginInvoke/Invoke/EndInvoke methods for a delegate,
        ///   the generated backing field for an auto property or a field-like event,
        ///   the "this" parameter for non-static methods,
        ///   the "value" parameter for a property setter,
        ///   the parameters on indexer accessor methods (not on the indexer itself),
        ///   methods in anonymous types,
        ///   anonymous functions
        /// </summary>
        public virtual bool IsImplicitlyDeclared
        {
            get { return false; }
        }
 
        /// <summary>
        /// Returns true if this symbol can be referenced by its name in code. Examples of symbols
        /// that cannot be referenced by name are:
        ///    constructors, destructors, operators, explicit interface implementations,
        ///    accessor methods for properties and events, array types.
        /// </summary>
        public bool CanBeReferencedByName
        {
            get
            {
                switch (this.Kind)
                {
                    case SymbolKind.Local:
                    case SymbolKind.Label:
                    case SymbolKind.Alias:
                    case SymbolKind.RangeVariable:
                        // never imported, and always references by name.
                        return true;
 
                    case SymbolKind.Namespace:
                    case SymbolKind.Field:
                    case SymbolKind.ErrorType:
                    case SymbolKind.Parameter:
                    case SymbolKind.TypeParameter:
                    case SymbolKind.Event:
                        break;
 
                    case SymbolKind.NamedType:
                        if (((NamedTypeSymbol)this).IsSubmissionClass)
                        {
                            return false;
                        }
                        break;
 
                    case SymbolKind.Property:
                        var property = (PropertySymbol)this;
                        if (property.IsIndexer || property.MustCallMethodsDirectly)
                        {
                            return false;
                        }
                        break;
 
                    case SymbolKind.Method:
                        var method = (MethodSymbol)this;
                        switch (method.MethodKind)
                        {
                            case MethodKind.Ordinary:
                            case MethodKind.LocalFunction:
                            case MethodKind.ReducedExtension:
                                break;
                            case MethodKind.Destructor:
                                // You wouldn't think that destructors would be referenceable by name, but
                                // dev11 only prevents them from being invoked - they can still be assigned
                                // to delegates.
                                return true;
                            case MethodKind.DelegateInvoke:
                                return true;
                            case MethodKind.PropertyGet:
                            case MethodKind.PropertySet:
                                if (!((PropertySymbol)method.AssociatedSymbol).CanCallMethodsDirectly())
                                {
                                    return false;
                                }
                                break;
                            default:
                                return false;
                        }
                        break;
 
                    case SymbolKind.ArrayType:
                    case SymbolKind.PointerType:
                    case SymbolKind.FunctionPointerType:
                    case SymbolKind.Assembly:
                    case SymbolKind.DynamicType:
                    case SymbolKind.NetModule:
                    case SymbolKind.Discard:
                        return false;
 
                    default:
                        throw ExceptionUtilities.UnexpectedValue(this.Kind);
                }
 
                // This will eliminate backing fields for auto-props, explicit interface implementations,
                // indexers, etc.
                // See the comment on ContainsDroppedIdentifierCharacters for an explanation of why
                // such names are not referenceable (or see DevDiv #14432).
                return SyntaxFacts.IsValidIdentifier(this.Name) &&
                    !SyntaxFacts.ContainsDroppedIdentifierCharacters(this.Name);
            }
        }
 
        /// <summary>
        /// As an optimization, viability checking in the lookup code should use this property instead
        /// of <see cref="CanBeReferencedByName"/>. The full name check will then be performed in the <see cref="CSharpSemanticModel"/>.
        /// </summary>
        /// <remarks>
        /// This property exists purely for performance reasons.
        /// </remarks>
        internal bool CanBeReferencedByNameIgnoringIllegalCharacters
        {
            get
            {
                if (this.Kind == SymbolKind.Method)
                {
                    var method = (MethodSymbol)this;
                    switch (method.MethodKind)
                    {
                        case MethodKind.Ordinary:
                        case MethodKind.LocalFunction:
                        case MethodKind.DelegateInvoke:
                        case MethodKind.Destructor: // See comment in CanBeReferencedByName.
                            return true;
                        case MethodKind.PropertyGet:
                        case MethodKind.PropertySet:
                            return ((PropertySymbol)method.AssociatedSymbol).CanCallMethodsDirectly();
                        default:
                            return false;
                    }
                }
                return true;
            }
        }
 
        /// <summary>
        /// Perform additional checks after the member has been
        /// added to the member list of the containing type.
        /// </summary>
        internal virtual void AfterAddingTypeMembersChecks(ConversionsBase conversions, BindingDiagnosticBag diagnostics)
        {
        }
 
        // Note: This is no public "IsNew". This is intentional, because new has no syntactic meaning.
        // It serves only to remove a warning. Furthermore, it can not be inferred from 
        // metadata. For symbols defined in source, the modifiers in the syntax tree
        // can be examined.
 
        /// <summary>
        /// Compare two symbol objects to see if they refer to the same symbol. You should always
        /// use <see cref="operator =="/> and <see cref="operator !="/>, or the <see cref="Equals(object)"/> method, to compare two symbols for equality.
        /// </summary>
        public static bool operator ==(Symbol left, Symbol right)
        {
            //PERF: this function is often called with
            //      1) left referencing same object as the right 
            //      2) right being null
            //      The code attempts to check for these conditions before 
            //      resorting to .Equals
 
            // the condition is expected to be folded when inlining "someSymbol == null"
            if (right is null)
            {
                return left is null;
            }
 
            // this part is expected to disappear when inlining "someSymbol == null"
            return (object)left == (object)right || right.Equals(left);
        }
 
        /// <summary>
        /// Compare two symbol objects to see if they refer to the same symbol. You should always
        /// use == and !=, or the Equals method, to compare two symbols for equality.
        /// </summary>
        public static bool operator !=(Symbol left, Symbol right)
        {
            //PERF: this function is often called with
            //      1) left referencing same object as the right 
            //      2) right being null
            //      The code attempts to check for these conditions before 
            //      resorting to .Equals
            //
            //NOTE: we do not implement this as !(left == right) 
            //      since that sometimes results in a worse code
 
            // the condition is expected to be folded when inlining "someSymbol != null"
            if (right is null)
            {
                return left is object;
            }
 
            // this part is expected to disappear when inlining "someSymbol != null"
            return (object)left != (object)right && !right.Equals(left);
        }
 
        public sealed override bool Equals(object obj)
        {
            return this.Equals(obj as Symbol, SymbolEqualityComparer.Default.CompareKind);
        }
 
        public bool Equals(Symbol other)
        {
            return this.Equals(other, SymbolEqualityComparer.Default.CompareKind);
        }
 
        bool ISymbolInternal.Equals(ISymbolInternal other, TypeCompareKind compareKind)
        {
            return this.Equals(other as Symbol, compareKind);
        }
 
        // By default we don't consider the compareKind, and do reference equality. This can be overridden.
        public virtual bool Equals(Symbol other, TypeCompareKind compareKind)
        {
            return (object)this == other;
        }
 
        // By default, we do reference equality. This can be overridden.
        public override int GetHashCode()
        {
            return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this);
        }
 
        public static bool Equals(Symbol first, Symbol second, TypeCompareKind compareKind)
        {
            if (first is null)
            {
                return second is null;
            }
 
            return first.Equals(second, compareKind);
        }
 
        /// <summary>
        /// Returns a string representation of this symbol, suitable for debugging purposes, or
        /// for placing in an error message.
        /// </summary>
        /// <remarks>
        /// This will provide a useful representation, but it would be clearer to call <see cref="ToDisplayString"/>
        /// directly and provide an explicit format.
        /// Sealed so that <see cref="ToString"/> and <see cref="ToDisplayString"/> can't get out of sync.
        /// </remarks>
        public sealed override string ToString()
        {
            return this.ToDisplayString();
        }
 
        // ---- End of Public Definition ---
        // Below here can be various useful virtual methods that are useful to the compiler, but we don't
        // want to expose publicly.
        // ---- End of Public Definition ---
 
        // Must override this in derived classes for visitor pattern.
        internal abstract TResult Accept<TArgument, TResult>(CSharpSymbolVisitor<TArgument, TResult> visitor, TArgument a);
 
        // Prevent anyone else from deriving from this class.
        internal Symbol()
        {
        }
 
        /// <summary>
        /// Build and add synthesized attributes for this symbol.
        /// </summary>
        internal virtual void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder<SynthesizedAttributeData> attributes)
        {
        }
 
        /// <summary>
        /// Convenience helper called by subclasses to add a synthesized attribute to a collection of attributes.
        /// </summary>
        internal static void AddSynthesizedAttribute(ref ArrayBuilder<SynthesizedAttributeData> attributes, SynthesizedAttributeData attribute)
        {
            if (attribute != null)
            {
                if (attributes == null)
                {
                    attributes = new ArrayBuilder<SynthesizedAttributeData>(1);
                }
 
                attributes.Add(attribute);
            }
        }
 
        /// <summary>
        /// <see cref="CharSet"/> effective for this symbol (type or DllImport method).
        /// Nothing if <see cref="DefaultCharSetAttribute"/> isn't applied on the containing module or it doesn't apply on this symbol.
        /// </summary>
        /// <remarks>
        /// Determined based upon value specified via <see cref="DefaultCharSetAttribute"/> applied on the containing module.
        /// </remarks>
        internal CharSet? GetEffectiveDefaultMarshallingCharSet()
        {
            Debug.Assert(this.Kind == SymbolKind.NamedType || this.Kind == SymbolKind.Method);
            return this.ContainingModule.DefaultMarshallingCharSet;
        }
 
        internal bool IsFromCompilation(CSharpCompilation compilation)
        {
            Debug.Assert(compilation != null);
            return compilation == this.DeclaringCompilation;
        }
 
        /// <summary>
        /// Always prefer <see cref="IsFromCompilation"/>.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Unfortunately, when determining overriding/hiding/implementation relationships, we don't 
        /// have the "current" compilation available.  We could, but that would clutter up the API 
        /// without providing much benefit.  As a compromise, we consider all compilations "current".
        /// </para>
        /// <para>
        /// Unlike in VB, we are not allowing retargeting symbols.  This method is used as an approximation
        /// for <see cref="IsFromCompilation"/> when a compilation is not available and that method will never return
        /// true for retargeting symbols.
        /// </para>
        /// </remarks>
        internal bool Dangerous_IsFromSomeCompilation
        {
            get { return this.DeclaringCompilation != null; }
        }
 
        public virtual bool IsDefinedInSourceTree(SyntaxTree tree, TextSpan? definedWithinSpan, CancellationToken cancellationToken = default(CancellationToken))
        {
            var declaringReferences = this.DeclaringSyntaxReferences;
            if (this.IsImplicitlyDeclared && declaringReferences.Length == 0)
            {
                return this.ContainingSymbol.IsDefinedInSourceTree(tree, definedWithinSpan, cancellationToken);
            }
 
            foreach (var syntaxRef in declaringReferences)
            {
                cancellationToken.ThrowIfCancellationRequested();
 
                if (IsDefinedInSourceTree(syntaxRef, tree, definedWithinSpan))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        protected static bool IsDefinedInSourceTree(SyntaxReference syntaxRef, SyntaxTree tree, TextSpan? definedWithinSpan)
            => syntaxRef.SyntaxTree == tree &&
                (!definedWithinSpan.HasValue || syntaxRef.Span.IntersectsWith(definedWithinSpan.Value));
 
#nullable enable
        internal static void ForceCompleteMemberConditionally(SourceLocation? locationOpt, Predicate<Symbol>? filter, Symbol member, CancellationToken cancellationToken)
        {
            if ((locationOpt == null || member.IsDefinedInSourceTree(locationOpt.SourceTree, locationOpt.SourceSpan, cancellationToken))
                && (filter == null || filter(member)))
            {
                cancellationToken.ThrowIfCancellationRequested();
                member.ForceComplete(locationOpt, filter, cancellationToken);
            }
        }
#nullable disable
 
        /// <summary>
        /// Returns the Documentation Comment ID for the symbol, or null if the symbol doesn't
        /// support documentation comments.
        /// </summary>
        public virtual string GetDocumentationCommentId()
        {
            // NOTE: we're using a try-finally here because there's a test that specifically
            // triggers an exception here to confirm that some symbols don't have documentation
            // comment IDs.  We don't care about "leaks" in such cases, but we don't want spew
            // in the test output.
            var pool = PooledStringBuilder.GetInstance();
            try
            {
                StringBuilder builder = pool.Builder;
                DocumentationCommentIDVisitor.Instance.Visit(this, builder);
                return builder.Length == 0 ? null : builder.ToString();
            }
            finally
            {
                pool.Free();
            }
        }
 
#nullable enable 
        /// <summary>
        /// Fetches the documentation comment for this element with a cancellation token.
        /// </summary>
        /// <param name="preferredCulture">Optionally, retrieve the comments formatted for a particular culture. No impact on source documentation comments.</param>
        /// <param name="expandIncludes">Optionally, expand <![CDATA[<include>]]> elements. No impact on non-source documentation comments.</param>
        /// <param name="cancellationToken">Optionally, allow cancellation of documentation comment retrieval.</param>
        /// <returns>The XML that would be written to the documentation file for the symbol.</returns>
        public virtual string GetDocumentationCommentXml(
            CultureInfo? preferredCulture = null,
            bool expandIncludes = false,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            return "";
        }
#nullable disable
 
        private static readonly SymbolDisplayFormat s_debuggerDisplayFormat =
            SymbolDisplayFormat.TestFormat
                .AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier
                    | SymbolDisplayMiscellaneousOptions.IncludeNotNullableReferenceTypeModifier)
                .WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes);
 
        internal virtual string GetDebuggerDisplay()
        {
            return $"{this.Kind} {this.ToDisplayString(s_debuggerDisplayFormat)}";
        }
 
        internal virtual void AddDeclarationDiagnostics(BindingDiagnosticBag diagnostics)
        {
#if DEBUG
            if (ContainingSymbol is SourceMemberContainerTypeSymbol container)
            {
                container.AssertMemberExposure(this, forDiagnostics: true);
            }
#endif
            if (diagnostics.DiagnosticBag?.IsEmptyWithoutResolution == false || diagnostics.DependenciesBag?.Count > 0)
            {
                CSharpCompilation compilation = this.DeclaringCompilation;
                Debug.Assert(compilation != null);
 
                compilation.AddUsedAssemblies(diagnostics.DependenciesBag);
 
                if (diagnostics.DiagnosticBag?.IsEmptyWithoutResolution == false)
                {
                    compilation.DeclarationDiagnostics.AddRange(diagnostics.DiagnosticBag);
                }
            }
        }
 
        #region Use-Site Diagnostics
 
        /// <summary>
        /// True if the symbol has a use-site diagnostic with error severity.
        /// </summary>
        internal bool HasUseSiteError
        {
            get
            {
                var info = GetUseSiteInfo();
                return info.DiagnosticInfo?.Severity == DiagnosticSeverity.Error;
            }
        }
 
        /// <summary>
        /// Returns diagnostic info that should be reported at the use site of the symbol, or default if there is none.
        /// </summary>
        internal virtual UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
        {
            return default;
        }
 
        protected AssemblySymbol PrimaryDependency
        {
            get
            {
                AssemblySymbol dependency = this.ContainingAssembly;
                if (dependency is object && dependency.CorLibrary == dependency)
                {
                    return null;
                }
 
                return dependency;
            }
        }
 
        /// <summary>
        /// Returns true if the error code is the highest priority while calculating use site error for this symbol. 
        /// Supposed to be ErrorCode, but it causes inconsistent accessibility error.
        /// </summary>
        protected virtual bool IsHighestPriorityUseSiteErrorCode(int code) => true;
 
        /// <summary>
        /// Indicates that this symbol uses metadata that cannot be supported by the language.
        /// 
        /// Examples include:
        ///    - Pointer types in VB
        ///    - ByRef return type
        ///    - Required custom modifiers
        ///    
        /// This is distinguished from, for example, references to metadata symbols defined in assemblies that weren't referenced.
        /// Symbols where this returns true can never be used successfully, and thus should never appear in any IDE feature.
        /// 
        /// This is set for metadata symbols, as follows:
        /// Type - if a type is unsupported (e.g., a pointer type, etc.)
        /// Method - parameter or return type is unsupported
        /// Field - type is unsupported
        /// Event - type is unsupported
        /// Property - type is unsupported
        /// Parameter - type is unsupported
        /// </summary>
        public virtual bool HasUnsupportedMetadata
        {
            get
            {
                return false;
            }
        }
 
        /// <summary>
        /// Merges given diagnostic to the existing result diagnostic.
        /// </summary>
        internal bool MergeUseSiteDiagnostics(ref DiagnosticInfo result, DiagnosticInfo info)
        {
            if (info == null)
            {
                return false;
            }
 
            if (info.Severity == DiagnosticSeverity.Error && IsHighestPriorityUseSiteErrorCode(info.Code))
            {
                // this error is final, no other error can override it:
                result = info;
                return true;
            }
 
            if (result == null || result.Severity == DiagnosticSeverity.Warning && info.Severity == DiagnosticSeverity.Error)
            {
                // there could be an error of higher-priority
                result = info;
                return false;
            }
 
            // we have a second low-pri error, continue looking for a higher priority one
            return false;
        }
 
        /// <summary>
        /// Merges given diagnostic and dependencies to the existing result.
        /// </summary>
        internal bool MergeUseSiteInfo(ref UseSiteInfo<AssemblySymbol> result, UseSiteInfo<AssemblySymbol> info)
        {
            DiagnosticInfo diagnosticInfo = result.DiagnosticInfo;
 
            bool retVal = MergeUseSiteDiagnostics(ref diagnosticInfo, info.DiagnosticInfo);
 
            if (diagnosticInfo?.Severity == DiagnosticSeverity.Error)
            {
                result = new UseSiteInfo<AssemblySymbol>(diagnosticInfo);
                return retVal;
            }
 
            var secondaryDependencies = result.SecondaryDependencies;
            var primaryDependency = result.PrimaryDependency;
 
            info.MergeDependencies(ref primaryDependency, ref secondaryDependencies);
 
            result = new UseSiteInfo<AssemblySymbol>(diagnosticInfo, primaryDependency, secondaryDependencies);
            Debug.Assert(!retVal);
            return retVal;
        }
 
        /// <summary>
        /// Reports specified use-site diagnostic to given diagnostic bag. 
        /// </summary>
        /// <remarks>
        /// This method should be the only method adding use-site diagnostics to a diagnostic bag. 
        /// It performs additional adjustments of the location for unification related diagnostics and 
        /// may be the place where to add more use-site location post-processing.
        /// </remarks>
        /// <returns>True if the diagnostic has error severity.</returns>
        internal static bool ReportUseSiteDiagnostic(DiagnosticInfo info, DiagnosticBag diagnostics, Location location)
        {
            // Unlike VB the C# Dev11 compiler reports only a single unification error/warning.
            // By dropping the location we effectively merge all unification use-site errors that have the same error code into a single error.
            // The error message clearly explains how to fix the problem and reporting the error for each location wouldn't add much value. 
            if (info.Code == (int)ErrorCode.WRN_UnifyReferenceBldRev ||
                info.Code == (int)ErrorCode.WRN_UnifyReferenceMajMin ||
                info.Code == (int)ErrorCode.ERR_AssemblyMatchBadVersion)
            {
                location = NoLocation.Singleton;
            }
 
            diagnostics.Add(info, location);
            return info.Severity == DiagnosticSeverity.Error;
        }
 
        internal static bool ReportUseSiteDiagnostic(DiagnosticInfo info, BindingDiagnosticBag diagnostics, Location location)
        {
            return diagnostics.ReportUseSiteDiagnostic(info, location);
        }
 
        /// <summary>
        /// Derive use-site info from a type symbol.
        /// </summary>
        internal bool DeriveUseSiteInfoFromType(ref UseSiteInfo<AssemblySymbol> result, TypeSymbol type)
        {
            UseSiteInfo<AssemblySymbol> info = type.GetUseSiteInfo();
            if (info.DiagnosticInfo?.Code == (int)ErrorCode.ERR_BogusType)
            {
                GetSymbolSpecificUnsupportedMetadataUseSiteErrorInfo(ref info);
            }
 
            return MergeUseSiteInfo(ref result, info);
        }
 
        private void GetSymbolSpecificUnsupportedMetadataUseSiteErrorInfo(ref UseSiteInfo<AssemblySymbol> info)
        {
            switch (this.Kind)
            {
                case SymbolKind.Field:
                case SymbolKind.Method:
                case SymbolKind.Property:
                case SymbolKind.Event:
                    info = info.AdjustDiagnosticInfo(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this));
                    break;
            }
        }
 
        private UseSiteInfo<AssemblySymbol> GetSymbolSpecificUnsupportedMetadataUseSiteErrorInfo()
        {
            var useSiteInfo = new UseSiteInfo<AssemblySymbol>(new CSDiagnosticInfo(ErrorCode.ERR_BogusType, string.Empty));
            GetSymbolSpecificUnsupportedMetadataUseSiteErrorInfo(ref useSiteInfo);
            return useSiteInfo;
        }
 
        internal bool DeriveUseSiteInfoFromType(ref UseSiteInfo<AssemblySymbol> result, TypeWithAnnotations type, AllowedRequiredModifierType allowedRequiredModifierType)
        {
            return DeriveUseSiteInfoFromType(ref result, type.Type) ||
                   DeriveUseSiteInfoFromCustomModifiers(ref result, type.CustomModifiers, allowedRequiredModifierType);
        }
 
        internal bool DeriveUseSiteInfoFromParameter(ref UseSiteInfo<AssemblySymbol> result, ParameterSymbol param)
        {
            return DeriveUseSiteInfoFromType(ref result, param.TypeWithAnnotations, AllowedRequiredModifierType.None) ||
                   DeriveUseSiteInfoFromCustomModifiers(ref result, param.RefCustomModifiers,
                                                              this is MethodSymbol method && method.MethodKind == MethodKind.FunctionPointerSignature ?
                                                                  AllowedRequiredModifierType.System_Runtime_InteropServices_InAttribute | AllowedRequiredModifierType.System_Runtime_CompilerServices_OutAttribute :
                                                                  AllowedRequiredModifierType.System_Runtime_InteropServices_InAttribute);
        }
 
        internal bool DeriveUseSiteInfoFromParameters(ref UseSiteInfo<AssemblySymbol> result, ImmutableArray<ParameterSymbol> parameters)
        {
            foreach (ParameterSymbol param in parameters)
            {
                if (DeriveUseSiteInfoFromParameter(ref result, param))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        [Flags]
        internal enum AllowedRequiredModifierType
        {
            None = 0,
            System_Runtime_CompilerServices_Volatile = 1,
            System_Runtime_InteropServices_InAttribute = 1 << 1,
            System_Runtime_CompilerServices_IsExternalInit = 1 << 2,
            System_Runtime_CompilerServices_OutAttribute = 1 << 3,
        }
 
        internal bool DeriveUseSiteInfoFromCustomModifiers(ref UseSiteInfo<AssemblySymbol> result, ImmutableArray<CustomModifier> customModifiers, AllowedRequiredModifierType allowedRequiredModifierType)
        {
            AllowedRequiredModifierType requiredModifiersFound = AllowedRequiredModifierType.None;
            bool checkRequiredModifiers = true;
 
            foreach (CustomModifier modifier in customModifiers)
            {
                NamedTypeSymbol modifierType = ((CSharpCustomModifier)modifier).ModifierSymbol;
 
                if (checkRequiredModifiers && !modifier.IsOptional)
                {
                    AllowedRequiredModifierType current = AllowedRequiredModifierType.None;
 
                    if ((allowedRequiredModifierType & AllowedRequiredModifierType.System_Runtime_InteropServices_InAttribute) != 0 &&
                        modifierType.IsWellKnownTypeInAttribute())
                    {
                        current = AllowedRequiredModifierType.System_Runtime_InteropServices_InAttribute;
                    }
                    else if ((allowedRequiredModifierType & AllowedRequiredModifierType.System_Runtime_CompilerServices_Volatile) != 0 &&
                        modifierType.SpecialType == SpecialType.System_Runtime_CompilerServices_IsVolatile)
                    {
                        current = AllowedRequiredModifierType.System_Runtime_CompilerServices_Volatile;
                    }
                    else if ((allowedRequiredModifierType & AllowedRequiredModifierType.System_Runtime_CompilerServices_IsExternalInit) != 0 &&
                        modifierType.IsWellKnownTypeIsExternalInit())
                    {
                        current = AllowedRequiredModifierType.System_Runtime_CompilerServices_IsExternalInit;
                    }
                    else if ((allowedRequiredModifierType & AllowedRequiredModifierType.System_Runtime_CompilerServices_OutAttribute) != 0 &&
                        modifierType.IsWellKnownTypeOutAttribute())
                    {
                        current = AllowedRequiredModifierType.System_Runtime_CompilerServices_OutAttribute;
                    }
 
                    if (current == AllowedRequiredModifierType.None ||
                        (current != requiredModifiersFound && requiredModifiersFound != AllowedRequiredModifierType.None)) // At the moment we don't support applying different allowed modreqs to the same target.
                    {
                        if (MergeUseSiteInfo(ref result, GetSymbolSpecificUnsupportedMetadataUseSiteErrorInfo()))
                        {
                            return true;
                        }
 
                        checkRequiredModifiers = false;
                    }
 
                    requiredModifiersFound |= current;
                }
 
                // Unbound generic type is valid as a modifier, let's not report any use site diagnostics because of that.
                if (modifierType.IsUnboundGenericType)
                {
                    modifierType = modifierType.OriginalDefinition;
                }
 
                if (DeriveUseSiteInfoFromType(ref result, modifierType))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        internal static bool GetUnificationUseSiteDiagnosticRecursive<T>(ref DiagnosticInfo result, ImmutableArray<T> types, Symbol owner, ref HashSet<TypeSymbol> checkedTypes) where T : TypeSymbol
        {
            foreach (var t in types)
            {
                if (t.GetUnificationUseSiteDiagnosticRecursive(ref result, owner, ref checkedTypes))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        internal static bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo result, ImmutableArray<TypeWithAnnotations> types, Symbol owner, ref HashSet<TypeSymbol> checkedTypes)
        {
            foreach (var t in types)
            {
                if (t.GetUnificationUseSiteDiagnosticRecursive(ref result, owner, ref checkedTypes))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        internal static bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo result, ImmutableArray<CustomModifier> modifiers, Symbol owner, ref HashSet<TypeSymbol> checkedTypes)
        {
            foreach (var modifier in modifiers)
            {
                if (((CSharpCustomModifier)modifier).ModifierSymbol.GetUnificationUseSiteDiagnosticRecursive(ref result, owner, ref checkedTypes))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        internal static bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo result, ImmutableArray<ParameterSymbol> parameters, Symbol owner, ref HashSet<TypeSymbol> checkedTypes)
        {
            foreach (var parameter in parameters)
            {
                if (parameter.TypeWithAnnotations.GetUnificationUseSiteDiagnosticRecursive(ref result, owner, ref checkedTypes) ||
                    GetUnificationUseSiteDiagnosticRecursive(ref result, parameter.RefCustomModifiers, owner, ref checkedTypes))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        internal static bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo result, ImmutableArray<TypeParameterSymbol> typeParameters, Symbol owner, ref HashSet<TypeSymbol> checkedTypes)
        {
            foreach (var typeParameter in typeParameters)
            {
                if (GetUnificationUseSiteDiagnosticRecursive(ref result, typeParameter.ConstraintTypesNoUseSiteDiagnostics, owner, ref checkedTypes))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        #endregion
 
#nullable enable
        /// <summary>
        /// True if this symbol has been marked with the <see cref="ObsoleteAttribute"/> attribute. 
        /// This property returns <see cref="ThreeState.Unknown"/> if the <see cref="ObsoleteAttribute"/> attribute hasn't been cracked yet.
        /// </summary>
        internal ThreeState ObsoleteState
        {
            get
            {
                switch (ObsoleteKind)
                {
                    case ObsoleteAttributeKind.None:
                    case ObsoleteAttributeKind.WindowsExperimental:
                    case ObsoleteAttributeKind.Experimental:
                        return ThreeState.False;
                    case ObsoleteAttributeKind.Uninitialized:
                        return ThreeState.Unknown;
                    default:
                        return ThreeState.True;
                }
            }
        }
 
        /// <summary>
        /// True if this symbol has been marked with the System.Diagnostics.CodeAnalysis.ExperimentalAttribute attribute. 
        /// This property returns <see cref="ThreeState.Unknown"/> if the attribute hasn't been cracked yet.
        /// </summary>
        internal ThreeState ExperimentalState
        {
            get
            {
                switch (ObsoleteKind)
                {
                    case ObsoleteAttributeKind.Experimental:
                        return ThreeState.True;
                    case ObsoleteAttributeKind.Uninitialized:
                        return ThreeState.Unknown;
                    default:
                        return ThreeState.False;
                }
            }
        }
 
        internal ObsoleteAttributeKind ObsoleteKind
        {
            get
            {
                var data = this.ObsoleteAttributeData;
                return (data == null) ? ObsoleteAttributeKind.None : data.Kind;
            }
        }
 
        /// <summary>
        /// Returns data decoded from <see cref="ObsoleteAttribute"/>/Experimental attribute or null if there is no <see cref="ObsoleteAttribute"/>/Experimental attribute.
        /// This property returns <see cref="Microsoft.CodeAnalysis.ObsoleteAttributeData.Uninitialized"/> if attribute arguments haven't been decoded yet.
        /// </summary>
        internal abstract ObsoleteAttributeData? ObsoleteAttributeData { get; }
#nullable disable
 
        public string ToDisplayString(SymbolDisplayFormat format = null)
        {
            return SymbolDisplay.ToDisplayString(ISymbol, format);
        }
 
        public ImmutableArray<SymbolDisplayPart> ToDisplayParts(SymbolDisplayFormat format = null)
        {
            return SymbolDisplay.ToDisplayParts(ISymbol, format);
        }
 
        public string ToMinimalDisplayString(
            SemanticModel semanticModel,
            int position,
            SymbolDisplayFormat format = null)
        {
            return SymbolDisplay.ToMinimalDisplayString(ISymbol, semanticModel, position, format);
        }
 
        public ImmutableArray<SymbolDisplayPart> ToMinimalDisplayParts(
            SemanticModel semanticModel,
            int position,
            SymbolDisplayFormat format = null)
        {
            return SymbolDisplay.ToMinimalDisplayParts(ISymbol, semanticModel, position, format);
        }
 
        internal static void ReportErrorIfHasConstraints(
            SyntaxList<TypeParameterConstraintClauseSyntax> constraintClauses, DiagnosticBag diagnostics)
        {
            if (constraintClauses.Count > 0)
            {
                diagnostics.Add(
                    ErrorCode.ERR_ConstraintOnlyAllowedOnGenericDecl,
                    constraintClauses[0].WhereKeyword.GetLocation());
            }
        }
 
        internal static void CheckForBlockAndExpressionBody(
            CSharpSyntaxNode block,
            CSharpSyntaxNode expression,
            CSharpSyntaxNode syntax,
            BindingDiagnosticBag diagnostics)
        {
            if (block != null && expression != null)
            {
                diagnostics.Add(ErrorCode.ERR_BlockBodyAndExpressionBody, syntax.GetLocation());
            }
        }
 
        [Flags]
        internal enum ReservedAttributes
        {
            DynamicAttribute = 1 << 1,
            IsReadOnlyAttribute = 1 << 2,
            IsUnmanagedAttribute = 1 << 3,
            IsByRefLikeAttribute = 1 << 4,
            TupleElementNamesAttribute = 1 << 5,
            NullableAttribute = 1 << 6,
            NullableContextAttribute = 1 << 7,
            NullablePublicOnlyAttribute = 1 << 8,
            NativeIntegerAttribute = 1 << 9,
            CaseSensitiveExtensionAttribute = 1 << 10,
            RequiredMemberAttribute = 1 << 11,
            ScopedRefAttribute = 1 << 12,
            RefSafetyRulesAttribute = 1 << 13,
            RequiresLocationAttribute = 1 << 14,
        }
 
        internal bool ReportExplicitUseOfReservedAttributes(in DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments, ReservedAttributes reserved)
        {
            var attribute = arguments.Attribute;
            var diagnostics = (BindingDiagnosticBag)arguments.Diagnostics;
 
            Debug.Assert(attribute is SourceAttributeData);
 
            if ((reserved & ReservedAttributes.DynamicAttribute) != 0 &&
                attribute.IsTargetAttribute(AttributeDescription.DynamicAttribute))
            {
                // DynamicAttribute should not be set explicitly.
                diagnostics.Add(ErrorCode.ERR_ExplicitDynamicAttr, arguments.AttributeSyntaxOpt.Location);
            }
            else if ((reserved & ReservedAttributes.IsReadOnlyAttribute) != 0 &&
                reportExplicitUseOfReservedAttribute(attribute, arguments, AttributeDescription.IsReadOnlyAttribute))
            {
            }
            else if ((reserved & ReservedAttributes.RequiresLocationAttribute) != 0 &&
                reportExplicitUseOfReservedAttribute(attribute, arguments, AttributeDescription.RequiresLocationAttribute))
            {
            }
            else if ((reserved & ReservedAttributes.IsUnmanagedAttribute) != 0 &&
                reportExplicitUseOfReservedAttribute(attribute, arguments, AttributeDescription.IsUnmanagedAttribute))
            {
            }
            else if ((reserved & ReservedAttributes.IsByRefLikeAttribute) != 0 &&
                reportExplicitUseOfReservedAttribute(attribute, arguments, AttributeDescription.IsByRefLikeAttribute))
            {
            }
            else if ((reserved & ReservedAttributes.TupleElementNamesAttribute) != 0 &&
                attribute.IsTargetAttribute(AttributeDescription.TupleElementNamesAttribute))
            {
                diagnostics.Add(ErrorCode.ERR_ExplicitTupleElementNamesAttribute, arguments.AttributeSyntaxOpt.Location);
            }
            else if ((reserved & ReservedAttributes.NullableAttribute) != 0 &&
                attribute.IsTargetAttribute(AttributeDescription.NullableAttribute))
            {
                // NullableAttribute should not be set explicitly.
                diagnostics.Add(ErrorCode.ERR_ExplicitNullableAttribute, arguments.AttributeSyntaxOpt.Location);
            }
            else if ((reserved & ReservedAttributes.NullableContextAttribute) != 0 &&
                reportExplicitUseOfReservedAttribute(attribute, arguments, AttributeDescription.NullableContextAttribute))
            {
            }
            else if ((reserved & ReservedAttributes.NullablePublicOnlyAttribute) != 0 &&
                reportExplicitUseOfReservedAttribute(attribute, arguments, AttributeDescription.NullablePublicOnlyAttribute))
            {
            }
            else if ((reserved & ReservedAttributes.NativeIntegerAttribute) != 0 &&
                reportExplicitUseOfReservedAttribute(attribute, arguments, AttributeDescription.NativeIntegerAttribute))
            {
            }
            else if ((reserved & ReservedAttributes.CaseSensitiveExtensionAttribute) != 0 &&
                attribute.IsTargetAttribute(AttributeDescription.CaseSensitiveExtensionAttribute))
            {
                // ExtensionAttribute should not be set explicitly.
                diagnostics.Add(ErrorCode.ERR_ExplicitExtension, arguments.AttributeSyntaxOpt.Location);
            }
            else if ((reserved & ReservedAttributes.RequiredMemberAttribute) != 0 &&
                attribute.IsTargetAttribute(AttributeDescription.RequiredMemberAttribute))
            {
                // Do not use 'System.Runtime.CompilerServices.RequiredMemberAttribute'. Use the 'required' keyword on required fields and properties instead.
                diagnostics.Add(ErrorCode.ERR_ExplicitRequiredMember, arguments.AttributeSyntaxOpt.Location);
            }
            else if ((reserved & ReservedAttributes.ScopedRefAttribute) != 0 &&
                attribute.IsTargetAttribute(AttributeDescription.ScopedRefAttribute))
            {
                // Do not use 'System.Runtime.CompilerServices.ScopedRefAttribute'. Use the 'scoped' keyword instead.
                diagnostics.Add(ErrorCode.ERR_ExplicitScopedRef, arguments.AttributeSyntaxOpt.Location);
            }
            else if ((reserved & ReservedAttributes.RefSafetyRulesAttribute) != 0 &&
                reportExplicitUseOfReservedAttribute(attribute, arguments, AttributeDescription.RefSafetyRulesAttribute))
            {
            }
            else
            {
                return false;
            }
            return true;
 
            bool reportExplicitUseOfReservedAttribute(CSharpAttributeData attribute, in DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments, in AttributeDescription attributeDescription)
            {
                if (attribute.IsTargetAttribute(attributeDescription))
                {
                    // Do not use '{FullName}'. This is reserved for compiler usage.
                    diagnostics.Add(ErrorCode.ERR_ExplicitReservedAttr, arguments.AttributeSyntaxOpt.Location, attributeDescription.FullName);
                    return true;
                }
                return false;
            }
        }
 
        internal virtual byte? GetNullableContextValue()
        {
            return GetLocalNullableContextValue() ?? ContainingSymbol?.GetNullableContextValue();
        }
 
        internal virtual byte? GetLocalNullableContextValue()
        {
            return null;
        }
 
        internal void GetCommonNullableValues(CSharpCompilation compilation, ref MostCommonNullableValueBuilder builder)
        {
            switch (this.Kind)
            {
                case SymbolKind.NamedType:
                    if (compilation.ShouldEmitNullableAttributes(this))
                    {
                        builder.AddValue(this.GetLocalNullableContextValue());
                    }
                    break;
                case SymbolKind.Event:
                    if (compilation.ShouldEmitNullableAttributes(this))
                    {
                        builder.AddValue(((EventSymbol)this).TypeWithAnnotations);
                    }
                    break;
                case SymbolKind.Field:
                    var field = (FieldSymbol)this;
                    if (field is TupleElementFieldSymbol tupleElement)
                    {
                        field = tupleElement.TupleUnderlyingField;
                    }
 
                    if (compilation.ShouldEmitNullableAttributes(field))
                    {
                        builder.AddValue(field.TypeWithAnnotations);
                    }
                    break;
                case SymbolKind.Method:
                    if (compilation.ShouldEmitNullableAttributes(this))
                    {
                        builder.AddValue(this.GetLocalNullableContextValue());
                    }
                    break;
                case SymbolKind.Property:
                    if (compilation.ShouldEmitNullableAttributes(this))
                    {
                        builder.AddValue(((PropertySymbol)this).TypeWithAnnotations);
                        // Attributes are not emitted for property parameters.
                    }
                    break;
                case SymbolKind.Parameter:
                    builder.AddValue(((ParameterSymbol)this).TypeWithAnnotations);
                    break;
                case SymbolKind.TypeParameter:
                    if (this is SourceTypeParameterSymbolBase typeParameter)
                    {
                        builder.AddValue(typeParameter.GetSynthesizedNullableAttributeValue());
                        foreach (var constraintType in typeParameter.ConstraintTypesNoUseSiteDiagnostics)
                        {
                            builder.AddValue(constraintType);
                        }
                    }
                    break;
            }
        }
 
        internal bool ShouldEmitNullableContextValue(out byte value)
        {
            byte? localValue = GetLocalNullableContextValue();
            if (localValue == null)
            {
                value = 0;
                return false;
            }
 
            value = localValue.GetValueOrDefault();
            byte containingValue = ContainingSymbol?.GetNullableContextValue() ?? 0;
            return value != containingValue;
        }
 
#nullable enable
        /// <summary>
        /// True if the symbol is declared outside of the scope of the containing
        /// symbol
        /// </summary>
        internal static bool IsCaptured(Symbol variable, SourceMethodSymbol containingSymbol)
        {
            switch (variable.Kind)
            {
                case SymbolKind.Field:
                case SymbolKind.Property:
                case SymbolKind.Event:
                // Range variables are not captured, but their underlying parameters
                // may be. If this is a range underlying parameter it will be a
                // ParameterSymbol, not a RangeVariableSymbol.
                case SymbolKind.RangeVariable:
                    return false;
 
                case SymbolKind.Local:
                    if (((LocalSymbol)variable).IsConst)
                    {
                        return false;
                    }
                    break;
 
                case SymbolKind.Parameter:
                    break;
 
                case SymbolKind.Method:
                    if (variable is LocalFunctionSymbol localFunction)
                    {
                        // calling a static local function doesn't require capturing state
                        if (localFunction.IsStatic)
                        {
                            return false;
                        }
 
                        break;
                    }
 
                    throw ExceptionUtilities.UnexpectedValue(variable);
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(variable.Kind);
            }
 
            // Walk up the containing symbols until we find the target function, in which
            // case the variable is not captured by the target function, or null, in which
            // case it is.
            for (var currentFunction = variable.ContainingSymbol;
                 (object)currentFunction != null;
                 currentFunction = currentFunction.ContainingSymbol)
            {
                if (ReferenceEquals(currentFunction, containingSymbol))
                {
                    return false;
                }
            }
 
            return true;
        }
#nullable disable
 
        bool ISymbolInternal.IsStatic
        {
            get { return this.IsStatic; }
        }
 
        bool ISymbolInternal.IsVirtual
        {
            get { return this.IsVirtual; }
        }
 
        bool ISymbolInternal.IsOverride
        {
            get { return this.IsOverride; }
        }
 
        bool ISymbolInternal.IsAbstract
        {
            get
            {
                return this.IsAbstract;
            }
        }
 
        bool ISymbolInternal.IsExtern
            => IsExtern;
 
        Accessibility ISymbolInternal.DeclaredAccessibility
        {
            get
            {
                return this.DeclaredAccessibility;
            }
        }
 
        public abstract void Accept(CSharpSymbolVisitor visitor);
 
        public abstract TResult Accept<TResult>(CSharpSymbolVisitor<TResult> visitor);
 
        string IFormattable.ToString(string format, IFormatProvider formatProvider)
        {
            return ToString();
        }
 
        protected abstract ISymbol CreateISymbol();
 
        internal ISymbol ISymbol
        {
            get
            {
                if (_lazyISymbol is null)
                {
                    Interlocked.CompareExchange(ref _lazyISymbol, CreateISymbol(), null);
                }
 
                return _lazyISymbol;
            }
        }
    }
}