File: Binder\WithExternAndUsingAliasesBinder.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.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    /// <summary>
    /// A binder that brings both extern and using aliases into the scope and deals with looking up names in them.
    /// </summary>
    internal abstract class WithExternAndUsingAliasesBinder : WithExternAliasesBinder
    {
        private ImportChain? _lazyImportChain;
 
        protected WithExternAndUsingAliasesBinder(WithUsingNamespacesAndTypesBinder next)
            : base(next)
        {
#if DEBUG
            Debug.Assert(!next.WithImportChainEntry);
#endif 
        }
 
        internal abstract override ImmutableArray<AliasAndUsingDirective> UsingAliases { get; }
 
        protected abstract ImmutableDictionary<string, AliasAndUsingDirective> GetUsingAliasesMap(ConsList<TypeSymbol>? basesBeingResolved);
 
        internal override void LookupSymbolsInSingleBinder(
            LookupResult result, string name, int arity, ConsList<TypeSymbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
        {
            Debug.Assert(result.IsClear);
 
            LookupSymbolInAliases(
                this.GetUsingAliasesMap(basesBeingResolved),
                this.ExternAliases,
                originalBinder,
                result,
                name,
                arity,
                basesBeingResolved,
                options,
                diagnose,
                ref useSiteInfo);
        }
 
        internal override void AddLookupSymbolsInfoInSingleBinder(LookupSymbolsInfo result, LookupOptions options, Binder originalBinder)
        {
            // If we are looking only for labels we do not need to search through the imports.
            if ((options & LookupOptions.LabelsOnly) == 0)
            {
                AddLookupSymbolsInfoInAliases(
                    this.GetUsingAliasesMap(basesBeingResolved: null),
                    this.ExternAliases,
                    result, options, originalBinder);
            }
        }
 
        internal override ImportChain ImportChain
        {
            get
            {
                if (_lazyImportChain == null)
                {
                    Debug.Assert(this.Next is WithUsingNamespacesAndTypesBinder);
                    Interlocked.CompareExchange(ref _lazyImportChain, BuildImportChain(), null);
                }
 
                Debug.Assert(_lazyImportChain != null);
 
                return _lazyImportChain;
            }
        }
 
        protected abstract ImportChain BuildImportChain();
 
        internal bool IsUsingAlias(string name, bool callerIsSemanticModel, ConsList<TypeSymbol>? basesBeingResolved)
        {
            return IsUsingAlias(this.GetUsingAliasesMap(basesBeingResolved), name, callerIsSemanticModel);
        }
 
        /// <summary>
        /// This overload is added to shadow the one from the base.
        /// </summary>
        [Obsolete("Use other overloads", error: true)]
        internal static new WithExternAndUsingAliasesBinder Create(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, Binder next)
        {
            throw ExceptionUtilities.Unreachable();
        }
 
        internal static WithExternAndUsingAliasesBinder Create(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, WithUsingNamespacesAndTypesBinder next)
        {
            return new FromSyntax(declaringSymbol, declarationSyntax, next);
        }
 
        internal static WithExternAndUsingAliasesBinder Create(ImmutableArray<AliasAndExternAliasDirective> externAliases, ImmutableDictionary<string, AliasAndUsingDirective> usingAliases, WithUsingNamespacesAndTypesBinder next)
        {
            return new FromSymbols(externAliases, usingAliases, next);
        }
 
        private sealed class FromSyntax : WithExternAndUsingAliasesBinder
        {
            private readonly SourceNamespaceSymbol _declaringSymbol;
            private readonly CSharpSyntaxNode _declarationSyntax;
            private ImmutableArray<AliasAndExternAliasDirective> _lazyExternAliases;
            private ImmutableArray<AliasAndUsingDirective> _lazyUsingAliases;
            private ImmutableDictionary<string, AliasAndUsingDirective>? _lazyUsingAliasesMap;
            private QuickAttributeChecker? _lazyQuickAttributeChecker;
 
            internal FromSyntax(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, WithUsingNamespacesAndTypesBinder next)
                : base(next)
            {
                Debug.Assert(declarationSyntax.Kind() is SyntaxKind.CompilationUnit or SyntaxKind.NamespaceDeclaration or SyntaxKind.FileScopedNamespaceDeclaration);
                _declaringSymbol = declaringSymbol;
                _declarationSyntax = declarationSyntax;
            }
 
            internal sealed override ImmutableArray<AliasAndExternAliasDirective> ExternAliases
            {
                get
                {
                    if (_lazyExternAliases.IsDefault)
                    {
                        ImmutableInterlocked.InterlockedInitialize(ref _lazyExternAliases, _declaringSymbol.GetExternAliases(_declarationSyntax));
                    }
 
                    return _lazyExternAliases;
                }
            }
 
            internal override ImmutableArray<AliasAndUsingDirective> UsingAliases
            {
                get
                {
                    if (_lazyUsingAliases.IsDefault)
                    {
                        ImmutableInterlocked.InterlockedInitialize(ref _lazyUsingAliases, _declaringSymbol.GetUsingAliases(_declarationSyntax, basesBeingResolved: null));
                    }
 
                    return _lazyUsingAliases;
                }
            }
 
            protected override ImmutableDictionary<string, AliasAndUsingDirective> GetUsingAliasesMap(ConsList<TypeSymbol>? basesBeingResolved)
            {
                if (_lazyUsingAliasesMap is null)
                {
                    Interlocked.CompareExchange(ref _lazyUsingAliasesMap, _declaringSymbol.GetUsingAliasesMap(_declarationSyntax, basesBeingResolved), null);
                }
 
                return _lazyUsingAliasesMap;
            }
 
            /// <summary>
            /// Get <see cref="QuickAttributeChecker"/> that can be used to quickly
            /// check for certain attribute applications in context of this binder.
            /// </summary>
            internal override QuickAttributeChecker QuickAttributeChecker
            {
                get
                {
                    if (_lazyQuickAttributeChecker == null)
                    {
                        QuickAttributeChecker result = this.Next!.QuickAttributeChecker;
 
                        SyntaxList<UsingDirectiveSyntax> usingDirectives;
                        switch (_declarationSyntax)
                        {
                            case CompilationUnitSyntax compilationUnit:
                                // Take global aliases from other compilation units into account
                                foreach (var declaration in ((SourceNamespaceSymbol)Compilation.SourceModule.GlobalNamespace).MergedDeclaration.Declarations)
                                {
                                    if (declaration.HasGlobalUsings && compilationUnit.SyntaxTree != declaration.SyntaxReference.SyntaxTree)
                                    {
                                        result = result.AddAliasesIfAny(((CompilationUnitSyntax)declaration.SyntaxReference.GetSyntax()).Usings, onlyGlobalAliases: true);
                                    }
                                }
 
                                usingDirectives = compilationUnit.Usings;
                                break;
 
                            case BaseNamespaceDeclarationSyntax namespaceDecl:
                                usingDirectives = namespaceDecl.Usings;
                                break;
 
                            default:
                                throw ExceptionUtilities.UnexpectedValue(_declarationSyntax);
                        }
 
                        result = result.AddAliasesIfAny(usingDirectives);
 
                        _lazyQuickAttributeChecker = result;
                    }
 
                    return _lazyQuickAttributeChecker;
                }
            }
 
            protected override ImportChain BuildImportChain()
            {
                var previous = Next!.ImportChain;
 
                if (_declarationSyntax is BaseNamespaceDeclarationSyntax namespaceDecl)
                {
                    // For each dotted name add an empty entry in the chain
                    var name = namespaceDecl.Name;
 
                    while (name is QualifiedNameSyntax dotted)
                    {
                        previous = new ImportChain(Imports.Empty, previous);
                        name = dotted.Left;
                    }
                }
 
                return new ImportChain(_declaringSymbol.GetImports(_declarationSyntax, basesBeingResolved: null), previous);
            }
        }
 
        private sealed class FromSymbols : WithExternAndUsingAliasesBinder
        {
            private readonly ImmutableArray<AliasAndExternAliasDirective> _externAliases;
            private readonly ImmutableDictionary<string, AliasAndUsingDirective> _usingAliases;
 
            internal FromSymbols(ImmutableArray<AliasAndExternAliasDirective> externAliases, ImmutableDictionary<string, AliasAndUsingDirective> usingAliases, WithUsingNamespacesAndTypesBinder next)
                : base(next)
            {
                Debug.Assert(!externAliases.IsDefault);
                _externAliases = externAliases;
                _usingAliases = usingAliases;
            }
 
            internal override ImmutableArray<AliasAndExternAliasDirective> ExternAliases
            {
                get
                {
                    return _externAliases;
                }
            }
 
            internal override ImmutableArray<AliasAndUsingDirective> UsingAliases
            {
                get
                {
                    return _usingAliases.SelectAsArray(static pair => pair.Value);
                }
            }
 
            protected override ImmutableDictionary<string, AliasAndUsingDirective> GetUsingAliasesMap(ConsList<TypeSymbol>? basesBeingResolved)
            {
                return _usingAliases;
            }
 
            protected override ImportChain BuildImportChain()
            {
                return new ImportChain(Imports.Create(_usingAliases, ((WithUsingNamespacesAndTypesBinder)Next!).GetUsings(basesBeingResolved: null), _externAliases), Next!.ImportChain);
            }
        }
    }
}