File: Symbols\Source\SourceNamespaceSymbol.AliasesAndUsings.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.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    partial class SourceNamespaceSymbol
    {
        public Imports GetImports(CSharpSyntaxNode declarationSyntax, ConsList<TypeSymbol>? basesBeingResolved)
        {
            switch (declarationSyntax)
            {
                case CompilationUnitSyntax compilationUnit:
                    if (!compilationUnit.Externs.Any() && !compilationUnit.Usings.Any())
                    {
                        var result = GetGlobalUsingImports(basesBeingResolved);
#if DEBUG
                        var calculated = GetAliasesAndUsingsForAsserts(declarationSyntax).GetImports(this, declarationSyntax, basesBeingResolved);
                        if (result == Imports.Empty || calculated == Imports.Empty)
                        {
                            Debug.Assert((object)result == calculated);
                        }
                        else
                        {
                            Debug.Assert(result.ExternAliases.SequenceEqual(calculated.ExternAliases));
                            Debug.Assert(result.UsingAliases.SetEquals(calculated.UsingAliases));
                            Debug.Assert(result.Usings.SequenceEqual(calculated.Usings));
                        }
#endif
 
                        return result;
                    }
                    break;
 
                case BaseNamespaceDeclarationSyntax namespaceDecl:
                    if (!namespaceDecl.Externs.Any() && !namespaceDecl.Usings.Any())
                    {
#if DEBUG
                        Debug.Assert(GetAliasesAndUsingsForAsserts(declarationSyntax).GetImports(this, declarationSyntax, basesBeingResolved) == Imports.Empty);
#endif
                        return Imports.Empty;
                    }
                    break;
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(declarationSyntax);
            }
 
            return GetAliasesAndUsings(declarationSyntax).GetImports(this, declarationSyntax, basesBeingResolved);
        }
 
        private AliasesAndUsings GetAliasesAndUsings(CSharpSyntaxNode declarationSyntax)
        {
            return GetAliasesAndUsings(GetMatchingNamespaceDeclaration(declarationSyntax));
        }
 
        private SingleNamespaceDeclaration GetMatchingNamespaceDeclaration(CSharpSyntaxNode declarationSyntax)
        {
            foreach (var declaration in _mergedDeclaration.Declarations)
            {
                var declarationSyntaxRef = declaration.SyntaxReference;
                if (declarationSyntaxRef.SyntaxTree != declarationSyntax.SyntaxTree)
                {
                    continue;
                }
 
                if (declarationSyntaxRef.GetSyntax() == declarationSyntax)
                {
                    return declaration;
                }
            }
 
            throw ExceptionUtilities.Unreachable();
        }
 
        private static AliasesAndUsings GetOrCreateAliasAndUsings(
            ref ImmutableDictionary<SingleNamespaceDeclaration, AliasesAndUsings> dictionary,
            SingleNamespaceDeclaration declaration)
        {
            return ImmutableInterlocked.GetOrAdd(
                ref dictionary,
                declaration,
                static _ => new AliasesAndUsings());
        }
 
        private AliasesAndUsings GetAliasesAndUsings(SingleNamespaceDeclaration declaration)
            => GetOrCreateAliasAndUsings(ref _aliasesAndUsings_doNotAccessDirectly, declaration);
 
#if DEBUG
        private AliasesAndUsings GetAliasesAndUsingsForAsserts(CSharpSyntaxNode declarationSyntax)
        {
            var singleDeclaration = GetMatchingNamespaceDeclaration(declarationSyntax);
 
            return singleDeclaration.HasExternAliases || singleDeclaration.HasGlobalUsings || singleDeclaration.HasUsings
                ? GetAliasesAndUsings(singleDeclaration)
                : GetOrCreateAliasAndUsings(ref _aliasesAndUsingsForAsserts_doNotAccessDirectly, singleDeclaration);
        }
#endif
 
        public ImmutableArray<AliasAndExternAliasDirective> GetExternAliases(CSharpSyntaxNode declarationSyntax)
        {
            switch (declarationSyntax)
            {
                case CompilationUnitSyntax compilationUnit:
                    if (!compilationUnit.Externs.Any())
                    {
#if DEBUG
                        Debug.Assert(GetAliasesAndUsingsForAsserts(declarationSyntax).GetExternAliases(this, declarationSyntax).IsEmpty);
#endif
                        return ImmutableArray<AliasAndExternAliasDirective>.Empty;
                    }
                    break;
 
                case BaseNamespaceDeclarationSyntax namespaceDecl:
                    if (!namespaceDecl.Externs.Any())
                    {
#if DEBUG
                        Debug.Assert(GetAliasesAndUsingsForAsserts(declarationSyntax).GetExternAliases(this, declarationSyntax).IsEmpty);
#endif
                        return ImmutableArray<AliasAndExternAliasDirective>.Empty;
                    }
                    break;
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(declarationSyntax);
            }
 
            return GetAliasesAndUsings(declarationSyntax).GetExternAliases(this, declarationSyntax);
        }
 
        public ImmutableArray<AliasAndUsingDirective> GetUsingAliases(CSharpSyntaxNode declarationSyntax, ConsList<TypeSymbol>? basesBeingResolved)
        {
            switch (declarationSyntax)
            {
                case CompilationUnitSyntax compilationUnit:
                    if (!compilationUnit.Usings.Any())
                    {
#if DEBUG
                        Debug.Assert(GetAliasesAndUsingsForAsserts(declarationSyntax).GetUsingAliases(this, declarationSyntax, basesBeingResolved).IsEmpty);
#endif
                        return ImmutableArray<AliasAndUsingDirective>.Empty;
                    }
                    break;
 
                case BaseNamespaceDeclarationSyntax namespaceDecl:
                    if (!namespaceDecl.Usings.Any())
                    {
#if DEBUG
                        Debug.Assert(GetAliasesAndUsingsForAsserts(declarationSyntax).GetUsingAliases(this, declarationSyntax, basesBeingResolved).IsEmpty);
#endif
                        return ImmutableArray<AliasAndUsingDirective>.Empty;
                    }
                    break;
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(declarationSyntax);
            }
 
            return GetAliasesAndUsings(declarationSyntax).GetUsingAliases(this, declarationSyntax, basesBeingResolved);
        }
 
        public ImmutableDictionary<string, AliasAndUsingDirective> GetUsingAliasesMap(CSharpSyntaxNode declarationSyntax, ConsList<TypeSymbol>? basesBeingResolved)
        {
            switch (declarationSyntax)
            {
                case CompilationUnitSyntax compilationUnit:
                    if (!compilationUnit.Usings.Any())
                    {
                        var result = GetGlobalUsingAliasesMap(basesBeingResolved);
#if DEBUG
                        Debug.Assert(result.SetEquals(GetAliasesAndUsingsForAsserts(declarationSyntax).GetUsingAliasesMap(this, declarationSyntax, basesBeingResolved)));
#endif
                        return result;
                    }
                    break;
 
                case BaseNamespaceDeclarationSyntax namespaceDecl:
                    if (!namespaceDecl.Usings.Any())
                    {
#if DEBUG
                        Debug.Assert(GetAliasesAndUsingsForAsserts(declarationSyntax).GetUsingAliasesMap(this, declarationSyntax, basesBeingResolved).IsEmpty);
#endif
                        return ImmutableDictionary<string, AliasAndUsingDirective>.Empty;
                    }
                    break;
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(declarationSyntax);
            }
 
            return GetAliasesAndUsings(declarationSyntax).GetUsingAliasesMap(this, declarationSyntax, basesBeingResolved);
        }
 
        public ImmutableArray<NamespaceOrTypeAndUsingDirective> GetUsingNamespacesOrTypes(CSharpSyntaxNode declarationSyntax, ConsList<TypeSymbol>? basesBeingResolved)
        {
            switch (declarationSyntax)
            {
                case CompilationUnitSyntax compilationUnit:
                    if (!compilationUnit.Usings.Any())
                    {
                        var result = GetGlobalUsingNamespacesOrTypes(basesBeingResolved);
#if DEBUG
                        Debug.Assert(result.SequenceEqual(GetAliasesAndUsingsForAsserts(declarationSyntax).GetUsingNamespacesOrTypes(this, declarationSyntax, basesBeingResolved)));
#endif
                        return result;
                    }
                    break;
 
                case BaseNamespaceDeclarationSyntax namespaceDecl:
                    if (!namespaceDecl.Usings.Any())
                    {
#if DEBUG
                        Debug.Assert(GetAliasesAndUsingsForAsserts(declarationSyntax).GetUsingNamespacesOrTypes(this, declarationSyntax, basesBeingResolved).IsEmpty);
#endif
                        return ImmutableArray<NamespaceOrTypeAndUsingDirective>.Empty;
                    }
                    break;
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(declarationSyntax);
            }
 
            return GetAliasesAndUsings(declarationSyntax).GetUsingNamespacesOrTypes(this, declarationSyntax, basesBeingResolved);
        }
 
        private Imports GetGlobalUsingImports(ConsList<TypeSymbol>? basesBeingResolved)
        {
            return GetMergedGlobalAliasesAndUsings(basesBeingResolved).Imports;
        }
 
        private ImmutableDictionary<string, AliasAndUsingDirective> GetGlobalUsingAliasesMap(ConsList<TypeSymbol>? basesBeingResolved)
        {
            return GetMergedGlobalAliasesAndUsings(basesBeingResolved).UsingAliasesMap!;
        }
 
        private ImmutableArray<NamespaceOrTypeAndUsingDirective> GetGlobalUsingNamespacesOrTypes(ConsList<TypeSymbol>? basesBeingResolved)
        {
            return GetMergedGlobalAliasesAndUsings(basesBeingResolved).UsingNamespacesOrTypes;
        }
 
        private MergedGlobalAliasesAndUsings GetMergedGlobalAliasesAndUsings(ConsList<TypeSymbol>? basesBeingResolved, CancellationToken cancellationToken = default)
        {
            if (_lazyMergedGlobalAliasesAndUsings is null)
            {
                if (!this.IsGlobalNamespace)
                {
                    _lazyMergedGlobalAliasesAndUsings = MergedGlobalAliasesAndUsings.Empty;
                }
                else
                {
                    ImmutableDictionary<string, AliasAndUsingDirective>? mergedAliases = null;
                    var mergedNamespacesOrTypes = ArrayBuilder<NamespaceOrTypeAndUsingDirective>.GetInstance();
                    var uniqueUsings = SpecializedSymbolCollections.GetPooledSymbolHashSetInstance<NamespaceOrTypeSymbol>();
                    var diagnostics = DiagnosticBag.GetInstance();
 
                    try
                    {
                        bool haveExternAliases = false;
 
                        foreach (var singleDeclaration in _mergedDeclaration.Declarations)
                        {
                            if (singleDeclaration.HasExternAliases)
                            {
                                haveExternAliases = true;
                            }
 
                            if (singleDeclaration.HasGlobalUsings)
                            {
                                var aliases = GetAliasesAndUsings(singleDeclaration).GetGlobalUsingAliasesMap(this, singleDeclaration.SyntaxReference, basesBeingResolved);
 
                                cancellationToken.ThrowIfCancellationRequested();
 
                                if (!aliases.IsEmpty)
                                {
                                    if (mergedAliases is null)
                                    {
                                        mergedAliases = aliases;
                                    }
                                    else
                                    {
                                        var builder = mergedAliases.ToBuilder();
                                        bool added = false;
 
                                        foreach (var pair in aliases)
                                        {
                                            if (builder.ContainsKey(pair.Key))
                                            {
                                                // The using alias '{0}' appeared previously in this namespace
                                                diagnostics.Add(ErrorCode.ERR_DuplicateAlias, pair.Value.Alias.GetFirstLocation(), pair.Key);
                                            }
                                            else
                                            {
                                                builder.Add(pair);
                                                added = true;
                                            }
                                        }
 
                                        if (added)
                                        {
                                            mergedAliases = builder.ToImmutable();
                                        }
 
                                        cancellationToken.ThrowIfCancellationRequested();
                                    }
                                }
 
                                var namespacesOrTypes = GetAliasesAndUsings(singleDeclaration).GetGlobalUsingNamespacesOrTypes(this, singleDeclaration.SyntaxReference, basesBeingResolved);
 
                                if (!namespacesOrTypes.IsEmpty)
                                {
                                    if (mergedNamespacesOrTypes.Count == 0)
                                    {
                                        mergedNamespacesOrTypes.AddRange(namespacesOrTypes);
                                        uniqueUsings.AddAll(namespacesOrTypes.Select(n => n.NamespaceOrType));
                                    }
                                    else
                                    {
                                        foreach (var namespaceOrType in namespacesOrTypes)
                                        {
                                            if (!uniqueUsings.Add(namespaceOrType.NamespaceOrType))
                                            {
                                                diagnostics.Add(ErrorCode.HDN_DuplicateWithGlobalUsing, namespaceOrType.UsingDirective!.NamespaceOrType.Location, namespaceOrType.NamespaceOrType);
                                            }
                                            else
                                            {
                                                mergedNamespacesOrTypes.Add(namespaceOrType);
                                            }
                                        }
                                    }
                                }
 
                                cancellationToken.ThrowIfCancellationRequested();
                            }
                        }
 
                        // Report a conflict between global using aliases and extern aliases from other compilation units
                        if (haveExternAliases && mergedAliases is object)
                        {
                            foreach (var singleDeclaration in _mergedDeclaration.Declarations)
                            {
                                if (singleDeclaration.HasExternAliases)
                                {
                                    var externAliases = GetAliasesAndUsings(singleDeclaration).GetExternAliases(this, singleDeclaration.SyntaxReference);
                                    var globalAliasesMap = ImmutableDictionary<string, AliasAndUsingDirective>.Empty;
 
                                    if (singleDeclaration.HasGlobalUsings)
                                    {
                                        globalAliasesMap = GetAliasesAndUsings(singleDeclaration).GetGlobalUsingAliasesMap(this, singleDeclaration.SyntaxReference, basesBeingResolved);
                                    }
 
                                    foreach (var externAlias in externAliases)
                                    {
                                        if (!externAlias.SkipInLookup &&
                                            !globalAliasesMap.ContainsKey(externAlias.Alias.Name) && // If we have a global alias with the same name declared in the same compilation unit, we already reported the conflict on the global alias.
                                            mergedAliases.ContainsKey(externAlias.Alias.Name))
                                        {
                                            // The using alias '{0}' appeared previously in this namespace
                                            diagnostics.Add(ErrorCode.ERR_DuplicateAlias, externAlias.Alias.GetFirstLocation(), externAlias.Alias.Name);
                                        }
                                    }
                                }
                            }
                        }
 
                        Interlocked.CompareExchange(ref _lazyMergedGlobalAliasesAndUsings,
                            new MergedGlobalAliasesAndUsings()
                            {
                                UsingAliasesMap = mergedAliases ?? ImmutableDictionary<string, AliasAndUsingDirective>.Empty,
                                UsingNamespacesOrTypes = mergedNamespacesOrTypes.ToImmutableAndFree(),
                                Diagnostics = diagnostics.ToReadOnlyAndFree()
                            },
                            null);
 
                        mergedNamespacesOrTypes = null;
                        diagnostics = null;
                    }
                    finally
                    {
                        uniqueUsings.Free();
                        mergedNamespacesOrTypes?.Free();
                        diagnostics?.Free();
                    }
                }
            }
 
            return _lazyMergedGlobalAliasesAndUsings;
        }
 
        private sealed class AliasesAndUsings
        {
            private ExternAliasesAndDiagnostics? _lazyExternAliases;
            private UsingsAndDiagnostics? _lazyGlobalUsings;
            private UsingsAndDiagnostics? _lazyUsings;
            private Imports? _lazyImports;
 
            /// <summary>
            /// Completion state that tracks whether validation was done/not done/currently in process. 
            /// </summary>
            private SymbolCompletionState _state;
 
            internal ImmutableArray<AliasAndExternAliasDirective> GetExternAliases(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax)
            {
                return GetExternAliasesAndDiagnostics(declaringSymbol, declarationSyntax).ExternAliases;
            }
 
            internal ImmutableArray<AliasAndExternAliasDirective> GetExternAliases(SourceNamespaceSymbol declaringSymbol, SyntaxReference declarationSyntax)
            {
                return (_lazyExternAliases ?? GetExternAliasesAndDiagnostics(declaringSymbol, (CSharpSyntaxNode)declarationSyntax.GetSyntax())).ExternAliases;
            }
 
            private ExternAliasesAndDiagnostics GetExternAliasesAndDiagnostics(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax)
            {
                if (_lazyExternAliases is null)
                {
                    SyntaxList<ExternAliasDirectiveSyntax> externAliasDirectives;
                    switch (declarationSyntax)
                    {
                        case CompilationUnitSyntax compilationUnit:
                            externAliasDirectives = compilationUnit.Externs;
                            break;
 
                        case BaseNamespaceDeclarationSyntax namespaceDecl:
                            externAliasDirectives = namespaceDecl.Externs;
                            break;
 
                        default:
                            throw ExceptionUtilities.UnexpectedValue(declarationSyntax);
                    }
 
                    if (!externAliasDirectives.Any())
                    {
#if DEBUG
                        var diagnostics = DiagnosticBag.GetInstance();
                        var result = buildExternAliases(externAliasDirectives, declaringSymbol, diagnostics);
                        Debug.Assert(result.IsEmpty);
                        Debug.Assert(diagnostics.IsEmptyWithoutResolution);
                        diagnostics.Free();
#endif
                        _lazyExternAliases = ExternAliasesAndDiagnostics.Empty;
                    }
                    else
                    {
                        var diagnostics = DiagnosticBag.GetInstance();
                        Interlocked.CompareExchange(
                            ref _lazyExternAliases,
                            new ExternAliasesAndDiagnostics() { ExternAliases = buildExternAliases(externAliasDirectives, declaringSymbol, diagnostics), Diagnostics = diagnostics.ToReadOnlyAndFree() },
                            null);
                    }
                }
 
                return _lazyExternAliases;
 
                static ImmutableArray<AliasAndExternAliasDirective> buildExternAliases(
                    SyntaxList<ExternAliasDirectiveSyntax> syntaxList,
                    SourceNamespaceSymbol declaringSymbol,
                    DiagnosticBag diagnostics)
                {
                    CSharpCompilation compilation = declaringSymbol.DeclaringCompilation;
 
                    var builder = ArrayBuilder<AliasAndExternAliasDirective>.GetInstance();
 
                    foreach (ExternAliasDirectiveSyntax aliasSyntax in syntaxList)
                    {
                        compilation.RecordImport(aliasSyntax);
                        bool skipInLookup = false;
 
                        // Extern aliases not allowed in interactive submissions:
                        if (compilation.IsSubmission)
                        {
                            diagnostics.Add(ErrorCode.ERR_ExternAliasNotAllowed, aliasSyntax.Location);
                            skipInLookup = true;
                        }
                        else
                        {
                            // some n^2 action, but n should be very small.
                            foreach (var existingAlias in builder)
                            {
                                if (existingAlias.Alias.Name == aliasSyntax.Identifier.ValueText)
                                {
                                    diagnostics.Add(ErrorCode.ERR_DuplicateAlias, existingAlias.Alias.GetFirstLocation(), existingAlias.Alias.Name);
                                    break;
                                }
                            }
 
                            if (aliasSyntax.Identifier.ContextualKind() == SyntaxKind.GlobalKeyword)
                            {
                                diagnostics.Add(ErrorCode.ERR_GlobalExternAlias, aliasSyntax.Identifier.GetLocation());
                            }
                        }
 
                        builder.Add(new AliasAndExternAliasDirective(new AliasSymbolFromSyntax(declaringSymbol, aliasSyntax), aliasSyntax, skipInLookup));
                    }
 
                    return builder.ToImmutableAndFree();
                }
            }
 
            internal ImmutableArray<AliasAndUsingDirective> GetUsingAliases(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, ConsList<TypeSymbol>? basesBeingResolved)
            {
                return GetUsingsAndDiagnostics(declaringSymbol, declarationSyntax, basesBeingResolved).UsingAliases;
            }
 
            internal ImmutableArray<AliasAndUsingDirective> GetGlobalUsingAliases(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, ConsList<TypeSymbol>? basesBeingResolved)
            {
                return GetGlobalUsingsAndDiagnostics(declaringSymbol, declarationSyntax, basesBeingResolved).UsingAliases;
            }
 
            internal ImmutableDictionary<string, AliasAndUsingDirective> GetUsingAliasesMap(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, ConsList<TypeSymbol>? basesBeingResolved)
            {
                return GetUsingsAndDiagnostics(declaringSymbol, declarationSyntax, basesBeingResolved).UsingAliasesMap ?? ImmutableDictionary<string, AliasAndUsingDirective>.Empty;
            }
 
            internal ImmutableDictionary<string, AliasAndUsingDirective> GetGlobalUsingAliasesMap(SourceNamespaceSymbol declaringSymbol, SyntaxReference declarationSyntax, ConsList<TypeSymbol>? basesBeingResolved)
            {
                return (_lazyGlobalUsings ?? GetGlobalUsingsAndDiagnostics(declaringSymbol, (CSharpSyntaxNode)declarationSyntax.GetSyntax(), basesBeingResolved)).UsingAliasesMap ?? ImmutableDictionary<string, AliasAndUsingDirective>.Empty;
            }
 
            internal ImmutableArray<NamespaceOrTypeAndUsingDirective> GetUsingNamespacesOrTypes(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, ConsList<TypeSymbol>? basesBeingResolved)
            {
                return GetUsingsAndDiagnostics(declaringSymbol, declarationSyntax, basesBeingResolved).UsingNamespacesOrTypes;
            }
 
            private UsingsAndDiagnostics GetUsingsAndDiagnostics(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, ConsList<TypeSymbol>? basesBeingResolved)
            {
                return GetUsingsAndDiagnostics(ref _lazyUsings, declaringSymbol, declarationSyntax, basesBeingResolved, onlyGlobal: false);
            }
 
            internal ImmutableArray<NamespaceOrTypeAndUsingDirective> GetGlobalUsingNamespacesOrTypes(SourceNamespaceSymbol declaringSymbol, SyntaxReference declarationSyntax, ConsList<TypeSymbol>? basesBeingResolved)
            {
                return (_lazyGlobalUsings ?? GetGlobalUsingsAndDiagnostics(declaringSymbol, (CSharpSyntaxNode)declarationSyntax.GetSyntax(), basesBeingResolved)).UsingNamespacesOrTypes;
            }
 
            private UsingsAndDiagnostics GetGlobalUsingsAndDiagnostics(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, ConsList<TypeSymbol>? basesBeingResolved)
            {
                return GetUsingsAndDiagnostics(ref _lazyGlobalUsings, declaringSymbol, declarationSyntax, basesBeingResolved, onlyGlobal: true);
            }
 
            private UsingsAndDiagnostics GetUsingsAndDiagnostics(ref UsingsAndDiagnostics? usings, SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, ConsList<TypeSymbol>? basesBeingResolved, bool onlyGlobal)
            {
                if (usings is null)
                {
                    SyntaxList<UsingDirectiveSyntax> usingDirectives;
                    bool? applyIsGlobalFilter;
                    switch (declarationSyntax)
                    {
                        case CompilationUnitSyntax compilationUnit:
                            applyIsGlobalFilter = onlyGlobal;
                            usingDirectives = compilationUnit.Usings;
                            break;
 
                        case BaseNamespaceDeclarationSyntax namespaceDecl:
                            Debug.Assert(!onlyGlobal);
                            applyIsGlobalFilter = null; // Global Using directives are not allowed in namespaces, treat them as regular, an error is reported elsewhere.
                            usingDirectives = namespaceDecl.Usings;
                            break;
 
                        default:
                            throw ExceptionUtilities.UnexpectedValue(declarationSyntax);
                    }
 
                    UsingsAndDiagnostics result;
                    if (!usingDirectives.Any())
                    {
                        if (applyIsGlobalFilter != false)
                        {
#if DEBUG
                            var calculated = buildUsings(usingDirectives, declaringSymbol, declarationSyntax, applyIsGlobalFilter, basesBeingResolved);
                            Debug.Assert(calculated.UsingAliases.IsEmpty);
                            Debug.Assert(calculated.UsingAliasesMap?.IsEmpty ?? true);
                            Debug.Assert(calculated.UsingNamespacesOrTypes.IsEmpty);
                            Debug.Assert(calculated.Diagnostics?.IsEmptyWithoutResolution ?? true);
#endif
                            result = UsingsAndDiagnostics.Empty;
                        }
                        else
                        {
                            result = new UsingsAndDiagnostics()
                            {
                                UsingAliases = GetGlobalUsingAliases(declaringSymbol, declarationSyntax, basesBeingResolved),
                                UsingAliasesMap = declaringSymbol.GetGlobalUsingAliasesMap(basesBeingResolved),
                                UsingNamespacesOrTypes = declaringSymbol.GetGlobalUsingNamespacesOrTypes(basesBeingResolved),
                                Diagnostics = null
                            };
#if DEBUG
                            var calculated = buildUsings(usingDirectives, declaringSymbol, declarationSyntax, applyIsGlobalFilter, basesBeingResolved);
                            Debug.Assert(calculated.UsingAliases.SequenceEqual(result.UsingAliases));
                            Debug.Assert((calculated.UsingAliasesMap ?? ImmutableDictionary<string, AliasAndUsingDirective>.Empty).SetEquals(result.UsingAliasesMap ?? ImmutableDictionary<string, AliasAndUsingDirective>.Empty));
                            Debug.Assert(calculated.UsingNamespacesOrTypes.SequenceEqual(result.UsingNamespacesOrTypes));
                            Debug.Assert(calculated.Diagnostics?.IsEmptyWithoutResolution ?? true);
#endif
                        }
                    }
                    else
                    {
                        result = buildUsings(usingDirectives, declaringSymbol, declarationSyntax, applyIsGlobalFilter, basesBeingResolved);
                    }
 
                    Interlocked.CompareExchange(ref usings, result, null);
                }
 
                return usings;
 
                UsingsAndDiagnostics buildUsings(
                    SyntaxList<UsingDirectiveSyntax> usingDirectives,
                    SourceNamespaceSymbol declaringSymbol,
                    CSharpSyntaxNode declarationSyntax,
                    bool? applyIsGlobalFilter,
                    ConsList<TypeSymbol>? basesBeingResolved)
                {
                    // define all of the extern aliases first. They may be used by the target of a using
                    var externAliases = GetExternAliases(declaringSymbol, declarationSyntax);
                    var globalUsingAliasesMap = ImmutableDictionary<string, AliasAndUsingDirective>.Empty;
                    var globalUsingNamespacesOrTypes = ImmutableArray<NamespaceOrTypeAndUsingDirective>.Empty;
                    var globalUsingAliases = ImmutableArray<AliasAndUsingDirective>.Empty;
 
                    if (applyIsGlobalFilter == false)
                    {
                        // Define all of the global usings. They may cause conflicts, etc.
                        globalUsingAliasesMap = declaringSymbol.GetGlobalUsingAliasesMap(basesBeingResolved);
                        globalUsingNamespacesOrTypes = declaringSymbol.GetGlobalUsingNamespacesOrTypes(basesBeingResolved);
                        globalUsingAliases = GetGlobalUsingAliases(declaringSymbol, declarationSyntax, basesBeingResolved);
                    }
 
                    var diagnostics = new DiagnosticBag();
 
                    var compilation = declaringSymbol.DeclaringCompilation;
 
                    ArrayBuilder<NamespaceOrTypeAndUsingDirective>? usings = null;
                    ImmutableDictionary<string, AliasAndUsingDirective>.Builder? usingAliasesMap = null;
                    ArrayBuilder<AliasAndUsingDirective>? usingAliases = null;
 
                    // A binder that contains the extern aliases but not the usings. The resolution of the target of a using directive or alias 
                    // should not make use of other peer usings.
                    Binder? declarationBinder = null;
 
                    PooledHashSet<NamespaceOrTypeSymbol>? uniqueUsings = null;
                    PooledHashSet<NamespaceOrTypeSymbol>? uniqueGlobalUsings = null;
 
                    foreach (var usingDirective in usingDirectives)
                    {
                        if (applyIsGlobalFilter.HasValue && usingDirective.GlobalKeyword.IsKind(SyntaxKind.GlobalKeyword) != applyIsGlobalFilter.GetValueOrDefault())
                        {
                            continue;
                        }
 
                        compilation.RecordImport(usingDirective);
 
                        if (usingDirective.Alias != null)
                        {
                            SyntaxToken identifier = usingDirective.Alias.Name.Identifier;
                            Location location = usingDirective.Alias.Name.Location;
 
                            if (identifier.ContextualKind() == SyntaxKind.GlobalKeyword)
                            {
                                diagnostics.Add(ErrorCode.WRN_GlobalAliasDefn, location);
                            }
 
                            if (usingDirective.StaticKeyword != default(SyntaxToken))
                            {
                                diagnostics.Add(ErrorCode.ERR_NoAliasHere, location);
                            }
 
                            SourceMemberContainerTypeSymbol.ReportReservedTypeName(identifier.Text, compilation, diagnostics, location);
 
                            string identifierValueText = identifier.ValueText;
                            bool skipInLookup = false;
 
                            if (usingAliasesMap?.ContainsKey(identifierValueText) ?? globalUsingAliasesMap.ContainsKey(identifierValueText))
                            {
                                skipInLookup = true;
 
                                // Suppress diagnostics if we're already broken.
                                if (!usingDirective.NamespaceOrType.IsMissing)
                                {
                                    // The using alias '{0}' appeared previously in this namespace
                                    diagnostics.Add(ErrorCode.ERR_DuplicateAlias, location, identifierValueText);
                                }
                            }
                            else
                            {
                                // an O(m*n) algorithm here but n (number of extern aliases) will likely be very small.
                                foreach (var externAlias in externAliases)
                                {
                                    if (externAlias.Alias.Name == identifierValueText)
                                    {
                                        // The using alias '{0}' appeared previously in this namespace
                                        diagnostics.Add(ErrorCode.ERR_DuplicateAlias, usingDirective.Location, identifierValueText);
                                        break;
                                    }
                                }
                            }
 
                            // construct the alias sym with the binder for which we are building imports. That
                            // way the alias target can make use of extern alias definitions.
                            var aliasAndDirective = new AliasAndUsingDirective(new AliasSymbolFromSyntax(declaringSymbol, usingDirective), usingDirective);
 
                            if (usingAliases is null)
                            {
                                usingAliases = ArrayBuilder<AliasAndUsingDirective>.GetInstance();
                                usingAliases.AddRange(globalUsingAliases);
                            }
 
                            usingAliases.Add(aliasAndDirective);
 
                            if (!skipInLookup)
                            {
                                if (usingAliasesMap == null)
                                {
                                    usingAliasesMap = globalUsingAliasesMap.ToBuilder();
                                }
 
                                usingAliasesMap.Add(identifierValueText, aliasAndDirective);
                            }
                        }
                        else
                        {
                            if (usingDirective.NamespaceOrType.IsMissing)
                            {
                                //don't try to lookup namespaces inserted by parser error recovery
                                continue;
                            }
 
                            var flags = BinderFlags.SuppressConstraintChecks;
                            if (usingDirective.UnsafeKeyword != default)
                            {
                                var unsafeKeywordLocation = usingDirective.UnsafeKeyword.GetLocation();
                                if (usingDirective.StaticKeyword == default)
                                {
                                    diagnostics.Add(ErrorCode.ERR_BadUnsafeInUsingDirective, unsafeKeywordLocation);
                                }
                                else
                                {
                                    MessageID.IDS_FeatureUsingTypeAlias.CheckFeatureAvailability(diagnostics, usingDirective, unsafeKeywordLocation);
                                    declaringSymbol.CheckUnsafeModifier(DeclarationModifiers.Unsafe, unsafeKeywordLocation, diagnostics);
                                }
 
                                flags |= BinderFlags.UnsafeRegion;
                            }
                            else
                            {
                                // Prior to C#12, allow the using static type to be an unsafe region.  This allows us to
                                // maintain compat with prior versions of the compiler that allowed `using static
                                // List<int*[]>;` to be written.  In 12.0 and onwards though, we require the code to
                                // explicitly contain the `unsafe` keyword.
                                if (!compilation.IsFeatureEnabled(MessageID.IDS_FeatureUsingTypeAlias))
                                    flags |= BinderFlags.UnsafeRegion;
                            }
 
                            var directiveDiagnostics = BindingDiagnosticBag.GetInstance();
                            Debug.Assert(directiveDiagnostics.DiagnosticBag is object);
                            Debug.Assert(directiveDiagnostics.DependenciesBag is object);
 
                            declarationBinder ??= compilation.GetBinderFactory(declarationSyntax.SyntaxTree).GetBinder(usingDirective.NamespaceOrType).WithAdditionalFlags(flags);
                            var imported = declarationBinder.BindNamespaceOrTypeSymbol(usingDirective.NamespaceOrType, directiveDiagnostics, basesBeingResolved).NamespaceOrTypeSymbol;
                            bool addDirectiveDiagnostics = true;
 
                            if (imported.Kind == SymbolKind.Namespace)
                            {
                                Debug.Assert(directiveDiagnostics.DependenciesBag.IsEmpty());
 
                                if (usingDirective.StaticKeyword != default(SyntaxToken))
                                {
                                    diagnostics.Add(ErrorCode.ERR_BadUsingType, usingDirective.NamespaceOrType.Location, imported);
                                }
                                else if (!getOrCreateUniqueUsings(ref uniqueUsings, globalUsingNamespacesOrTypes).Add(imported))
                                {
                                    diagnostics.Add(!globalUsingNamespacesOrTypes.IsEmpty && getOrCreateUniqueGlobalUsingsNotInTree(ref uniqueGlobalUsings, globalUsingNamespacesOrTypes, declarationSyntax.SyntaxTree).Contains(imported) ?
                                                            ErrorCode.HDN_DuplicateWithGlobalUsing :
                                                            ErrorCode.WRN_DuplicateUsing,
                                                    usingDirective.NamespaceOrType.Location, imported);
                                }
                                else
                                {
                                    getOrCreateUsingsBuilder(ref usings, globalUsingNamespacesOrTypes).Add(new NamespaceOrTypeAndUsingDirective(imported, usingDirective, dependencies: default));
                                }
                            }
                            else if (imported.Kind == SymbolKind.NamedType)
                            {
                                if (usingDirective.StaticKeyword == default(SyntaxToken))
                                {
                                    diagnostics.Add(ErrorCode.ERR_BadUsingNamespace, usingDirective.NamespaceOrType.Location, imported);
                                }
                                else
                                {
                                    var importedType = (NamedTypeSymbol)imported;
                                    if (usingDirective.GlobalKeyword != default(SyntaxToken) && importedType.HasFileLocalTypes())
                                    {
                                        diagnostics.Add(ErrorCode.ERR_GlobalUsingStaticFileType, usingDirective.NamespaceOrType.Location, imported);
                                    }
 
                                    if (!getOrCreateUniqueUsings(ref uniqueUsings, globalUsingNamespacesOrTypes).Add(importedType))
                                    {
                                        diagnostics.Add(!globalUsingNamespacesOrTypes.IsEmpty && getOrCreateUniqueGlobalUsingsNotInTree(ref uniqueGlobalUsings, globalUsingNamespacesOrTypes, declarationSyntax.SyntaxTree).Contains(imported) ?
                                                            ErrorCode.HDN_DuplicateWithGlobalUsing :
                                                            ErrorCode.WRN_DuplicateUsing,
                                                        usingDirective.NamespaceOrType.Location, importedType);
                                    }
                                    else
                                    {
                                        declarationBinder.ReportDiagnosticsIfObsolete(diagnostics, importedType, usingDirective.NamespaceOrType, hasBaseReceiver: false);
 
                                        getOrCreateUsingsBuilder(ref usings, globalUsingNamespacesOrTypes).Add(new NamespaceOrTypeAndUsingDirective(importedType, usingDirective, directiveDiagnostics.DependenciesBag.ToImmutableArray()));
                                    }
                                }
                            }
                            else if (imported.Kind is SymbolKind.ArrayType or SymbolKind.PointerType or SymbolKind.FunctionPointerType or SymbolKind.DynamicType)
                            {
                                diagnostics.Add(ErrorCode.ERR_BadUsingStaticType, usingDirective.NamespaceOrType.Location, imported.GetKindText());
 
                                // Don't bother adding sub diagnostics (like that an unsafe type was referenced).  The
                                // primary thing we want to report is simply that the using-static points to something
                                // entirely invalid.
                                addDirectiveDiagnostics = false;
                            }
                            else if (imported.Kind != SymbolKind.ErrorType)
                            {
                                // Do not report additional error if the symbol itself is erroneous.
 
                                // error: '<symbol>' is a '<symbol kind>' but is used as 'type or namespace'
                                diagnostics.Add(ErrorCode.ERR_BadSKknown, usingDirective.NamespaceOrType.Location,
                                    usingDirective.NamespaceOrType,
                                    imported.GetKindText(),
                                    MessageID.IDS_SK_TYPE_OR_NAMESPACE.Localize());
                            }
 
                            if (addDirectiveDiagnostics)
                            {
                                diagnostics.AddRange(directiveDiagnostics.DiagnosticBag);
                            }
 
                            directiveDiagnostics.Free();
                        }
                    }
 
                    uniqueUsings?.Free();
                    uniqueGlobalUsings?.Free();
 
                    if (diagnostics.IsEmptyWithoutResolution)
                    {
                        diagnostics = null;
                    }
 
                    return new UsingsAndDiagnostics()
                    {
                        UsingAliases = usingAliases?.ToImmutableAndFree() ?? globalUsingAliases,
                        UsingAliasesMap = usingAliasesMap?.ToImmutable() ?? globalUsingAliasesMap,
                        UsingNamespacesOrTypes = usings?.ToImmutableAndFree() ?? globalUsingNamespacesOrTypes,
                        Diagnostics = diagnostics
                    };
 
                    static PooledHashSet<NamespaceOrTypeSymbol> getOrCreateUniqueUsings(ref PooledHashSet<NamespaceOrTypeSymbol>? uniqueUsings, ImmutableArray<NamespaceOrTypeAndUsingDirective> globalUsingNamespacesOrTypes)
                    {
                        if (uniqueUsings is null)
                        {
                            uniqueUsings = SpecializedSymbolCollections.GetPooledSymbolHashSetInstance<NamespaceOrTypeSymbol>();
                            uniqueUsings.AddAll(globalUsingNamespacesOrTypes.Select(n => n.NamespaceOrType));
                        }
 
                        return uniqueUsings;
                    }
 
                    static PooledHashSet<NamespaceOrTypeSymbol> getOrCreateUniqueGlobalUsingsNotInTree(ref PooledHashSet<NamespaceOrTypeSymbol>? uniqueUsings, ImmutableArray<NamespaceOrTypeAndUsingDirective> globalUsingNamespacesOrTypes, SyntaxTree tree)
                    {
                        if (uniqueUsings is null)
                        {
                            uniqueUsings = SpecializedSymbolCollections.GetPooledSymbolHashSetInstance<NamespaceOrTypeSymbol>();
                            uniqueUsings.AddAll(globalUsingNamespacesOrTypes.Where(n => n.UsingDirectiveReference?.SyntaxTree != tree).Select(n => n.NamespaceOrType));
                        }
 
                        return uniqueUsings;
                    }
 
                    static ArrayBuilder<NamespaceOrTypeAndUsingDirective> getOrCreateUsingsBuilder(ref ArrayBuilder<NamespaceOrTypeAndUsingDirective>? usings, ImmutableArray<NamespaceOrTypeAndUsingDirective> globalUsingNamespacesOrTypes)
                    {
                        if (usings is null)
                        {
                            usings = ArrayBuilder<NamespaceOrTypeAndUsingDirective>.GetInstance();
                            usings.AddRange(globalUsingNamespacesOrTypes);
                        }
 
                        return usings;
                    }
                }
            }
 
            internal Imports GetImports(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, ConsList<TypeSymbol>? basesBeingResolved)
            {
                if (_lazyImports is null)
                {
                    Interlocked.CompareExchange(ref _lazyImports,
                                                Imports.Create(GetUsingAliasesMap(declaringSymbol, declarationSyntax, basesBeingResolved),
                                                               GetUsingNamespacesOrTypes(declaringSymbol, declarationSyntax, basesBeingResolved),
                                                               GetExternAliases(declaringSymbol, declarationSyntax)),
                                                null);
                }
 
                return _lazyImports;
            }
 
            internal void Complete(SourceNamespaceSymbol declaringSymbol, SyntaxReference declarationSyntax, CancellationToken cancellationToken)
            {
                var externAliasesAndDiagnostics = _lazyExternAliases ?? GetExternAliasesAndDiagnostics(declaringSymbol, (CSharpSyntaxNode)declarationSyntax.GetSyntax(cancellationToken));
                cancellationToken.ThrowIfCancellationRequested();
 
                var globalUsingsAndDiagnostics = _lazyGlobalUsings ??
                                                (declaringSymbol.IsGlobalNamespace ?
                                                     GetGlobalUsingsAndDiagnostics(declaringSymbol, (CSharpSyntaxNode)declarationSyntax.GetSyntax(cancellationToken), basesBeingResolved: null) :
                                                     UsingsAndDiagnostics.Empty);
                cancellationToken.ThrowIfCancellationRequested();
 
                var usingsAndDiagnostics = _lazyUsings ?? GetUsingsAndDiagnostics(declaringSymbol, (CSharpSyntaxNode)declarationSyntax.GetSyntax(cancellationToken), basesBeingResolved: null);
                cancellationToken.ThrowIfCancellationRequested();
 
                while (true)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    var incompletePart = _state.NextIncompletePart;
                    switch (incompletePart)
                    {
                        case CompletionPart.StartValidatingImports:
                            {
                                if (_state.NotePartComplete(CompletionPart.StartValidatingImports))
                                {
                                    Validate(declaringSymbol, declarationSyntax, externAliasesAndDiagnostics, usingsAndDiagnostics, globalUsingsAndDiagnostics.Diagnostics);
                                    _state.NotePartComplete(CompletionPart.FinishValidatingImports);
                                }
                            }
                            break;
 
                        case CompletionPart.FinishValidatingImports:
                            // some other thread has started validating imports (otherwise we would be in the case above) so
                            // we just wait for it to both finish and report the diagnostics.
                            Debug.Assert(_state.HasComplete(CompletionPart.StartValidatingImports));
                            _state.SpinWaitComplete(CompletionPart.FinishValidatingImports, cancellationToken);
                            break;
 
                        case CompletionPart.None:
                            return;
 
                        default:
                            // any other values are completion parts intended for other kinds of symbols
                            _state.NotePartComplete(CompletionPart.All & ~CompletionPart.ImportsAll);
                            break;
                    }
 
                    _state.SpinWaitComplete(incompletePart, cancellationToken);
                }
            }
 
            private static void Validate(SourceNamespaceSymbol declaringSymbol, SyntaxReference declarationSyntax, ExternAliasesAndDiagnostics externAliasesAndDiagnostics, UsingsAndDiagnostics usingsAndDiagnostics, DiagnosticBag? globalUsingDiagnostics)
            {
                var compilation = declaringSymbol.DeclaringCompilation;
                DiagnosticBag semanticDiagnostics = compilation.DeclarationDiagnostics;
 
                // Check constraints within named aliases.
                var diagnostics = BindingDiagnosticBag.GetInstance();
                Debug.Assert(diagnostics.DiagnosticBag is object);
                Debug.Assert(diagnostics.DependenciesBag is object);
 
                if (usingsAndDiagnostics.UsingAliasesMap is object)
                {
                    // Force resolution of named aliases.
                    foreach (var (_, alias) in usingsAndDiagnostics.UsingAliasesMap)
                    {
                        if (alias.UsingDirectiveReference!.SyntaxTree != declarationSyntax.SyntaxTree)
                        {
                            // Must be a global alias from a different compilation unit
                            Debug.Assert(declaringSymbol.IsGlobalNamespace);
                            continue;
                        }
 
                        NamespaceOrTypeSymbol target = alias.Alias.GetAliasTarget(basesBeingResolved: null);
 
                        diagnostics.Clear();
                        if (alias.Alias is AliasSymbolFromSyntax aliasFromSyntax)
                        {
                            diagnostics.AddRange(aliasFromSyntax.AliasTargetDiagnostics);
                        }
 
                        alias.Alias.CheckConstraints(diagnostics);
 
                        semanticDiagnostics.AddRange(diagnostics.DiagnosticBag);
                        recordImportDependencies(alias.UsingDirective!, target);
                    }
                }
 
                var corLibrary = compilation.SourceAssembly.CorLibrary;
                var conversions = corLibrary.TypeConversions;
                foreach (var @using in usingsAndDiagnostics.UsingNamespacesOrTypes)
                {
                    if (@using.UsingDirectiveReference!.SyntaxTree != declarationSyntax.SyntaxTree)
                    {
                        // Must be a global using directive from a different compilation unit
                        Debug.Assert(declaringSymbol.IsGlobalNamespace);
                        continue;
                    }
 
                    diagnostics.Clear();
                    diagnostics.AddDependencies(@using.Dependencies);
 
                    NamespaceOrTypeSymbol target = @using.NamespaceOrType;
 
                    // Check if `using static` directives meet constraints.
                    UsingDirectiveSyntax usingDirective = @using.UsingDirective!;
                    if (target.IsType)
                    {
                        var typeSymbol = (TypeSymbol)target;
                        var location = usingDirective.NamespaceOrType.Location;
                        typeSymbol.CheckAllConstraints(compilation, conversions, location, diagnostics);
                    }
 
                    semanticDiagnostics.AddRange(diagnostics.DiagnosticBag);
                    recordImportDependencies(usingDirective, target);
                }
 
                // Force resolution of extern aliases.
                foreach (var alias in externAliasesAndDiagnostics.ExternAliases)
                {
                    if (alias.SkipInLookup)
                    {
                        continue;
                    }
 
                    var target = (NamespaceSymbol)alias.Alias.GetAliasTarget(null);
                    Debug.Assert(target.IsGlobalNamespace);
 
                    if (alias.Alias is AliasSymbolFromSyntax aliasFromSyntax)
                    {
                        semanticDiagnostics.AddRange(aliasFromSyntax.AliasTargetDiagnostics.DiagnosticBag!);
                    }
 
                    if (!Compilation.ReportUnusedImportsInTree(alias.ExternAliasDirective!.SyntaxTree))
                    {
                        diagnostics.Clear();
                        diagnostics.AddAssembliesUsedByNamespaceReference(target);
                        compilation.AddUsedAssemblies(diagnostics.DependenciesBag);
                    }
                }
 
                semanticDiagnostics.AddRange(externAliasesAndDiagnostics.Diagnostics);
 
                if (usingsAndDiagnostics.Diagnostics?.IsEmptyWithoutResolution == false)
                {
                    semanticDiagnostics.AddRange(usingsAndDiagnostics.Diagnostics.AsEnumerable());
                }
 
                if (globalUsingDiagnostics?.IsEmptyWithoutResolution == false)
                {
                    semanticDiagnostics.AddRange(globalUsingDiagnostics.AsEnumerable());
                }
 
                diagnostics.Free();
 
                void recordImportDependencies(UsingDirectiveSyntax usingDirective, NamespaceOrTypeSymbol target)
                {
                    if (Compilation.ReportUnusedImportsInTree(usingDirective.SyntaxTree))
                    {
                        compilation.RecordImportDependencies(usingDirective, diagnostics.DependenciesBag.ToImmutableArray());
                    }
                    else
                    {
                        if (target.IsNamespace)
                        {
                            diagnostics.AddAssembliesUsedByNamespaceReference((NamespaceSymbol)target);
                        }
 
                        compilation.AddUsedAssemblies(diagnostics.DependenciesBag);
                    }
                }
            }
 
            private class ExternAliasesAndDiagnostics
            {
                public static readonly ExternAliasesAndDiagnostics Empty = new ExternAliasesAndDiagnostics() { ExternAliases = ImmutableArray<AliasAndExternAliasDirective>.Empty, Diagnostics = ImmutableArray<Diagnostic>.Empty };
 
                public ImmutableArray<AliasAndExternAliasDirective> ExternAliases { get; init; }
                public ImmutableArray<Diagnostic> Diagnostics { get; init; }
            }
 
            private class UsingsAndDiagnostics
            {
                public static readonly UsingsAndDiagnostics Empty =
                    new UsingsAndDiagnostics()
                    {
                        UsingAliases = ImmutableArray<AliasAndUsingDirective>.Empty,
                        UsingAliasesMap = null,
                        UsingNamespacesOrTypes = ImmutableArray<NamespaceOrTypeAndUsingDirective>.Empty,
                        Diagnostics = null
                    };
 
                public ImmutableArray<AliasAndUsingDirective> UsingAliases { get; init; }
                public ImmutableDictionary<string, AliasAndUsingDirective>? UsingAliasesMap { get; init; }
                public ImmutableArray<NamespaceOrTypeAndUsingDirective> UsingNamespacesOrTypes { get; init; }
                public DiagnosticBag? Diagnostics { get; init; }
            }
        }
 
        private class MergedGlobalAliasesAndUsings
        {
            private Imports? _lazyImports;
 
            /// <summary>
            /// Completion state that tracks whether validation was done/not done/currently in process. 
            /// </summary>
            private SymbolCompletionState _state;
 
            public static readonly MergedGlobalAliasesAndUsings Empty =
                new MergedGlobalAliasesAndUsings()
                {
                    UsingAliasesMap = ImmutableDictionary<string, AliasAndUsingDirective>.Empty,
                    UsingNamespacesOrTypes = ImmutableArray<NamespaceOrTypeAndUsingDirective>.Empty,
                    Diagnostics = ImmutableArray<Diagnostic>.Empty,
                    _lazyImports = Imports.Empty
                };
 
            public ImmutableDictionary<string, AliasAndUsingDirective>? UsingAliasesMap { get; init; }
            public ImmutableArray<NamespaceOrTypeAndUsingDirective> UsingNamespacesOrTypes { get; init; }
            public ImmutableArray<Diagnostic> Diagnostics { get; init; }
 
            public Imports Imports
            {
                get
                {
                    if (_lazyImports is null)
                    {
                        Interlocked.CompareExchange(ref _lazyImports,
                                                    Imports.Create(UsingAliasesMap ?? ImmutableDictionary<string, AliasAndUsingDirective>.Empty,
                                                                   UsingNamespacesOrTypes,
                                                                   ImmutableArray<AliasAndExternAliasDirective>.Empty),
                                                    null);
                    }
 
                    return _lazyImports;
                }
            }
 
            internal void Complete(SourceNamespaceSymbol declaringSymbol, CancellationToken cancellationToken)
            {
                while (true)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    var incompletePart = _state.NextIncompletePart;
                    switch (incompletePart)
                    {
                        case CompletionPart.StartValidatingImports:
                            {
                                if (_state.NotePartComplete(CompletionPart.StartValidatingImports))
                                {
                                    if (!Diagnostics.IsDefaultOrEmpty)
                                    {
                                        var compilation = declaringSymbol.DeclaringCompilation;
                                        DiagnosticBag semanticDiagnostics = compilation.DeclarationDiagnostics;
                                        semanticDiagnostics.AddRange(Diagnostics);
                                    }
 
                                    _state.NotePartComplete(CompletionPart.FinishValidatingImports);
                                }
                            }
                            break;
 
                        case CompletionPart.FinishValidatingImports:
                            // some other thread has started validating imports (otherwise we would be in the case above) so
                            // we just wait for it to both finish and report the diagnostics.
                            Debug.Assert(_state.HasComplete(CompletionPart.StartValidatingImports));
                            _state.SpinWaitComplete(CompletionPart.FinishValidatingImports, cancellationToken);
                            break;
 
                        case CompletionPart.None:
                            return;
 
                        default:
                            // any other values are completion parts intended for other kinds of symbols
                            _state.NotePartComplete(CompletionPart.All & ~CompletionPart.ImportsAll);
                            break;
                    }
 
                    _state.SpinWaitComplete(incompletePart, cancellationToken);
                }
            }
        }
    }
}