File: Symbols\NamespaceSymbol.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 Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    /// <summary>
    /// Represents a namespace.
    /// </summary>
    internal abstract partial class NamespaceSymbol : NamespaceOrTypeSymbol, INamespaceSymbolInternal
    {
        // PERF: initialization of the following fields will allocate, so we make them lazy
        private ImmutableArray<NamedTypeSymbol> _lazyTypesMightContainExtensionMethods;
        private string _lazyQualifiedName;
 
        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        // Changes to the public interface of this class should remain synchronized with the VB version.
        // Do not make any changes to the public interface without making the corresponding change
        // to the VB version.
        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
        /// <summary>
        /// Get all the members of this symbol that are namespaces.
        /// </summary>
        /// <returns>An IEnumerable containing all the namespaces that are members of this symbol.
        /// If this symbol has no namespace members, returns an empty IEnumerable. Never returns
        /// null.</returns>
        public IEnumerable<NamespaceSymbol> GetNamespaceMembers()
        {
            return this.GetMembers().OfType<NamespaceSymbol>();
        }
 
        /// <summary>
        /// Returns whether this namespace is the unnamed, global namespace that is 
        /// at the root of all namespaces.
        /// </summary>
        public virtual bool IsGlobalNamespace
        {
            get
            {
                return (object)ContainingNamespace == null;
            }
        }
 
        internal abstract NamespaceExtent Extent { get; }
 
        /// <summary>
        /// The kind of namespace: Module, Assembly or Compilation.
        /// Module namespaces contain only members from the containing module that share the same namespace name.
        /// Assembly namespaces contain members for all modules in the containing assembly that share the same namespace name.
        /// Compilation namespaces contain all members, from source or referenced metadata (assemblies and modules) that share the same namespace name.
        /// </summary>
        public NamespaceKind NamespaceKind
        {
            get { return this.Extent.Kind; }
        }
 
        /// <summary>
        /// The containing compilation for compilation namespaces.
        /// </summary>
        public CSharpCompilation ContainingCompilation
        {
            get { return this.NamespaceKind == NamespaceKind.Compilation ? this.Extent.Compilation : null; }
        }
 
        /// <summary>
        /// If a namespace has Assembly or Compilation extent, it may be composed of multiple
        /// namespaces that are merged together. If so, ConstituentNamespaces returns
        /// all the namespaces that were merged. If this namespace was not merged, returns
        /// an array containing only this namespace.
        /// </summary>
        public virtual ImmutableArray<NamespaceSymbol> ConstituentNamespaces
        {
            get
            {
                return ImmutableArray.Create(this);
            }
        }
 
        public sealed override NamedTypeSymbol ContainingType
        {
            get
            {
                return null;
            }
        }
 
        /// <summary>
        /// Containing assembly.
        /// </summary>
        public abstract override AssemblySymbol ContainingAssembly { get; }
 
        internal override ModuleSymbol ContainingModule
        {
            get
            {
                var extent = this.Extent;
                if (extent.Kind == NamespaceKind.Module)
                {
                    return extent.Module;
                }
 
                return null;
            }
        }
 
        /// <summary>
        /// Gets the kind of this symbol.
        /// </summary>
        public sealed override SymbolKind Kind
        {
            get
            {
                return SymbolKind.Namespace;
            }
        }
 
        public sealed override bool IsImplicitlyDeclared
        {
            get
            {
                return this.IsGlobalNamespace;
            }
        }
 
        /// <summary>
        /// Implements visitor pattern.
        /// </summary>
        internal override TResult Accept<TArgument, TResult>(CSharpSymbolVisitor<TArgument, TResult> visitor, TArgument argument)
        {
            return visitor.VisitNamespace(this, argument);
        }
 
        public override void Accept(CSharpSymbolVisitor visitor)
        {
            visitor.VisitNamespace(this);
        }
 
        public override TResult Accept<TResult>(CSharpSymbolVisitor<TResult> visitor)
        {
            return visitor.VisitNamespace(this);
        }
 
        // Only the compiler can create namespace symbols.
        internal NamespaceSymbol()
        {
        }
 
        /// <summary>
        /// Get this accessibility that was declared on this symbol. For symbols that do not have
        /// accessibility declared on them, returns NotApplicable.
        /// </summary>
        public sealed override Accessibility DeclaredAccessibility
        {
            // C# spec 3.5.1: Namespaces implicitly have public declared accessibility.
            get
            {
                return Accessibility.Public;
            }
        }
 
        /// <summary>
        /// Returns true if this symbol is "static"; i.e., declared with the "static" modifier or
        /// implicitly static.
        /// </summary>
        public sealed override bool IsStatic
        {
            get
            {
                return true;
            }
        }
 
        /// <summary>
        /// Returns true if this symbol was declared as requiring an override; i.e., declared with
        /// the "abstract" modifier. Also returns true on a type declared as "abstract", all
        /// interface types, and members of interface types.
        /// </summary>
        public sealed override bool IsAbstract
        {
            get
            {
                return false;
            }
        }
 
        /// <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 "sealed" modifier.  Also set for
        /// types that do not allow a derived class (declared with "sealed" or "static" or "struct"
        /// or "enum" or "delegate").
        /// </summary>
        public sealed override bool IsSealed
        {
            get
            {
                return false;
            }
        }
 
        /// <summary>
        /// Returns data decoded from Obsolete attribute or null if there is no Obsolete attribute.
        /// This property returns ObsoleteAttributeData.Uninitialized if attribute arguments haven't been decoded yet.
        /// </summary>
        internal sealed override ObsoleteAttributeData ObsoleteAttributeData
        {
            get { return null; }
        }
 
        /// <summary>
        /// Returns an implicit type symbol for this namespace or null if there is none. This type
        /// wraps misplaced global code.
        /// </summary>
        internal NamedTypeSymbol ImplicitType
        {
            get
            {
                var types = this.GetTypeMembers(TypeSymbol.ImplicitTypeName);
                if (types.Length == 0)
                {
                    return null;
                }
 
                Debug.Assert(types.Length == 1);
                return types[0];
            }
        }
 
        /// <summary>
        /// Lookup a nested namespace.
        /// </summary>
        /// <param name="names">
        /// Sequence of names for nested child namespaces.
        /// </param>
        /// <returns>
        /// Symbol for the most nested namespace, if found. Nothing 
        /// if namespace or any part of it can not be found.
        /// </returns>
        internal NamespaceSymbol LookupNestedNamespace(ImmutableArray<ReadOnlyMemory<char>> names)
        {
            NamespaceSymbol scope = this;
            foreach (ReadOnlyMemory<char> name in names)
            {
                scope = scope.GetNestedNamespace(name);
                if (scope is null)
                    return null;
            }
 
            return scope;
        }
 
        internal NamespaceSymbol GetNestedNamespace(string name)
            => GetNestedNamespace(name.AsMemory());
 
        internal virtual NamespaceSymbol GetNestedNamespace(ReadOnlyMemory<char> name)
        {
            foreach (var sym in this.GetMembers(name))
            {
                if (sym.Kind == SymbolKind.Namespace)
                {
                    return (NamespaceSymbol)sym;
                }
            }
 
            return null;
        }
 
        public abstract ImmutableArray<Symbol> GetMembers(ReadOnlyMemory<char> name);
 
        public sealed override ImmutableArray<Symbol> GetMembers(string name)
            => GetMembers(name.AsMemory());
 
        internal NamespaceSymbol GetNestedNamespace(NameSyntax name)
        {
            switch (name.Kind())
            {
                case SyntaxKind.GenericName: // DeclarationTreeBuilder.VisitNamespace uses the PlainName, even for generic names
                case SyntaxKind.IdentifierName:
                    return this.GetNestedNamespace(((SimpleNameSyntax)name).Identifier.ValueText);
 
                case SyntaxKind.QualifiedName:
                    var qn = (QualifiedNameSyntax)name;
                    var leftNs = this.GetNestedNamespace(qn.Left);
                    if ((object)leftNs != null)
                    {
                        return leftNs.GetNestedNamespace(qn.Right);
                    }
 
                    break;
 
                case SyntaxKind.AliasQualifiedName:
                    // This is an error scenario, but we should still handle it.
                    // We recover in the same way as DeclarationTreeBuilder.VisitNamespaceDeclaration.
                    return this.GetNestedNamespace(name.GetUnqualifiedName().Identifier.ValueText);
            }
 
            return null;
        }
 
        private ImmutableArray<NamedTypeSymbol> TypesMightContainExtensionMethods
        {
            get
            {
                var typesWithExtensionMethods = this._lazyTypesMightContainExtensionMethods;
                if (typesWithExtensionMethods.IsDefault)
                {
                    this._lazyTypesMightContainExtensionMethods = this.GetTypeMembersUnordered().WhereAsArray(t => t.MightContainExtensionMethods);
                    typesWithExtensionMethods = this._lazyTypesMightContainExtensionMethods;
                }
 
                return typesWithExtensionMethods;
            }
        }
 
        /// <summary>
        /// Add all extension methods in this namespace to the given list. If name or arity
        /// or both are provided, only those extension methods that match are included.
        /// </summary>
        /// <param name="methods">Methods list</param>
        /// <param name="nameOpt">Optional method name</param>
        /// <param name="arity">Method arity</param>
        /// <param name="options">Lookup options</param>
        internal virtual void GetExtensionMethods(ArrayBuilder<MethodSymbol> methods, string nameOpt, int arity, LookupOptions options)
        {
            var assembly = this.ContainingAssembly;
 
            // Only MergedAssemblySymbol should have a null ContainingAssembly
            // and MergedAssemblySymbol overrides GetExtensionMethods.
            Debug.Assert((object)assembly != null);
 
            if (!assembly.MightContainExtensionMethods)
            {
                return;
            }
 
            var typesWithExtensionMethods = this.TypesMightContainExtensionMethods;
 
            foreach (var type in typesWithExtensionMethods)
            {
                type.DoGetExtensionMethods(methods, nameOpt, arity, options);
            }
        }
 
        internal string QualifiedName
        {
            get
            {
                return _lazyQualifiedName ??
                    (_lazyQualifiedName = this.ToDisplayString(SymbolDisplayFormat.QualifiedNameOnlyFormat));
            }
        }
 
        protected sealed override ISymbol CreateISymbol()
        {
            return new PublicModel.NamespaceSymbol(this);
        }
 
        bool INamespaceSymbolInternal.IsGlobalNamespace => this.IsGlobalNamespace;
    }
}