|
// 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.Diagnostics;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis;
internal partial struct SymbolKey
{
private sealed class NamespaceSymbolKey : AbstractSymbolKey<INamespaceSymbol>
{
public static readonly NamespaceSymbolKey Instance = new();
// The containing symbol can be one of many things.
// 1) Null when this is the global namespace for a compilation.
// 2) The SymbolId for an assembly symbol if this is the global namespace for an
// assembly.
// 3) The SymbolId for a module symbol if this is the global namespace for a module.
// 4) The SymbolId for the containing namespace symbol if this is not a global
// namespace.
public sealed override void Create(INamespaceSymbol symbol, SymbolKeyWriter visitor)
{
visitor.WriteString(symbol.MetadataName);
if (symbol.ContainingNamespace != null)
{
visitor.WriteInteger(0);
visitor.WriteSymbolKey(symbol.ContainingNamespace);
}
else
{
// A global namespace can either belong to a module or to a compilation.
Debug.Assert(symbol.IsGlobalNamespace);
switch (symbol.NamespaceKind)
{
case NamespaceKind.Module:
visitor.WriteInteger(1);
visitor.WriteSymbolKey(symbol.ContainingModule);
break;
case NamespaceKind.Assembly:
visitor.WriteInteger(2);
visitor.WriteSymbolKey(symbol.ContainingAssembly);
break;
case NamespaceKind.Compilation:
visitor.WriteInteger(3);
visitor.WriteSymbolKey(null);
break;
default:
throw new NotImplementedException();
}
}
}
protected sealed override SymbolKeyResolution Resolve(
SymbolKeyReader reader, INamespaceSymbol? contextualSymbol, out string? failureReason)
{
var metadataName = reader.ReadRequiredString();
var containerKind = reader.ReadInteger();
var containingContextualSymbol = containerKind switch
{
0 => contextualSymbol?.ContainingNamespace,
1 => contextualSymbol?.ContainingModule,
2 => contextualSymbol?.ContainingAssembly,
3 => (ISymbol?)null,
_ => throw ExceptionUtilities.UnexpectedValue(containerKind),
};
// Namespaces are never parented by types, so there can be no contextual type to resolve our container.
var containingSymbolResolution = reader.ReadSymbolKey(
containingContextualSymbol, out var containingSymbolFailureReason);
if (containingSymbolFailureReason != null)
{
failureReason = $"({nameof(NamespaceSymbolKey)} {nameof(containingSymbolResolution)} failed -> {containingSymbolFailureReason})";
return default;
}
if (containerKind == 3)
{
failureReason = null;
return new SymbolKeyResolution(reader.Compilation.GlobalNamespace);
}
using var result = PooledArrayBuilder<INamespaceSymbol>.GetInstance();
foreach (var container in containingSymbolResolution)
{
switch (container)
{
case IAssemblySymbol assembly:
Debug.Assert(metadataName == string.Empty);
result.AddIfNotNull(assembly.GlobalNamespace);
break;
case IModuleSymbol module:
Debug.Assert(metadataName == string.Empty);
result.AddIfNotNull(module.GlobalNamespace);
break;
case INamespaceSymbol namespaceSymbol:
foreach (var member in namespaceSymbol.GetMembers(metadataName))
{
if (member is INamespaceSymbol childNamespace)
{
result.AddIfNotNull(childNamespace);
}
}
break;
}
}
return CreateResolution(result, $"({nameof(NamespaceSymbolKey)} '{metadataName}' not found)", out failureReason);
}
}
}
|