File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\Utilities\AliasSymbolCache.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
 
namespace Microsoft.CodeAnalysis.Shared.Utilities;
 
using TreeMap = ConcurrentDictionary<(SyntaxTree tree, int namespaceId), ImmutableDictionary<INamespaceOrTypeSymbol, IAliasSymbol>>;
 
internal static class AliasSymbolCache
{
    private static readonly ConditionalWeakTable<Compilation, TreeMap> s_treeAliasMap = new();
 
    /// <summary>
    /// Returns <see langword="true"/> if items were already cached for this <paramref name="semanticModel"/> and
    /// <paramref name="namespaceId"/>, <see langword="false"/> otherwise.  Callers should use this value to
    /// determine if they should call <see cref="AddAliasSymbols"/> or not.  A result of <see langword="true"/> does
    /// *not* mean that <paramref name="aliasSymbol"/> is non-<see langword="null"/>.
    /// </summary>
    public static bool TryGetAliasSymbol(
        SemanticModel semanticModel,
        int namespaceId,
        INamespaceOrTypeSymbol targetSymbol,
        out IAliasSymbol? aliasSymbol)
    {
        semanticModel = semanticModel.GetOriginalSemanticModel();
 
        aliasSymbol = null;
        if (!s_treeAliasMap.TryGetValue(semanticModel.Compilation, out var treeMap) ||
            !treeMap.TryGetValue((semanticModel.SyntaxTree, namespaceId), out var symbolMap))
        {
            // maps aren't available.  Caller needs to call back into us to add aliases for this scope.
            return false;
        }
 
        // map was available.  see if it contains an alias to this target.  This is considered successful regardless
        // of whether we find a mapping or not.
        symbolMap.TryGetValue(targetSymbol, out aliasSymbol);
        return true;
    }
 
    public static void AddAliasSymbols(SemanticModel semanticModel, int namespaceId, IEnumerable<IAliasSymbol> aliasSymbols)
    {
        // given semantic model must be the original semantic model for now
        var treeMap = s_treeAliasMap.GetValue(semanticModel.Compilation, static _ => new TreeMap());
 
        // check again to see whether somebody has beaten us
        var key = (tree: semanticModel.SyntaxTree, namespaceId);
        if (treeMap.ContainsKey(key))
            return;
 
        var builder = ImmutableDictionary.CreateBuilder<INamespaceOrTypeSymbol, IAliasSymbol>();
        foreach (var alias in aliasSymbols)
        {
            if (builder.ContainsKey(alias.Target))
                continue;
 
            // only put the first one.
            builder.Add(alias.Target, alias);
        }
 
        // Use namespace id rather than holding onto namespace node directly, that will keep the tree alive as long
        // as the compilation is alive. In the current design, a node can come and go even if compilation is alive
        // through recoverable tree.
        treeMap.TryAdd(key, builder.ToImmutable());
    }
}