|
// 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.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Shared.Extensions;
internal static partial class INamespaceSymbolExtensions
{
private static readonly ConditionalWeakTable<INamespaceSymbol, List<string>> s_namespaceToNameMap = new();
public static readonly Comparison<INamespaceSymbol> CompareNamespaces = CompareTo;
public static readonly IEqualityComparer<INamespaceSymbol> EqualityComparer = new Comparer();
private static List<string> GetNameParts(INamespaceSymbol? namespaceSymbol)
{
var result = new List<string>();
GetNameParts(namespaceSymbol, result);
return result;
}
private static void GetNameParts(INamespaceSymbol? namespaceSymbol, List<string> result)
{
if (namespaceSymbol == null || namespaceSymbol.IsGlobalNamespace)
{
return;
}
GetNameParts(namespaceSymbol.ContainingNamespace, result);
result.Add(namespaceSymbol.Name);
}
public static int CompareTo(this INamespaceSymbol n1, INamespaceSymbol n2)
{
var names1 = s_namespaceToNameMap.GetValue(n1, GetNameParts);
var names2 = s_namespaceToNameMap.GetValue(n2, GetNameParts);
for (var i = 0; i < Math.Min(names1.Count, names2.Count); i++)
{
var comp = names1[i].CompareTo(names2[i]);
if (comp != 0)
{
return comp;
}
}
return names1.Count - names2.Count;
}
public static IEnumerable<INamespaceOrTypeSymbol> GetAllNamespacesAndTypes(
this INamespaceSymbol namespaceSymbol,
CancellationToken cancellationToken)
{
using var _ = ArrayBuilder<INamespaceOrTypeSymbol>.GetInstance(out var stack);
stack.Push(namespaceSymbol);
while (stack.TryPop(out var current))
{
cancellationToken.ThrowIfCancellationRequested();
if (current is INamespaceSymbol childNamespace)
{
stack.AddRange(childNamespace.GetMembers());
yield return childNamespace;
}
else
{
var child = (INamedTypeSymbol)current;
stack.AddRange(child.GetTypeMembers());
yield return child;
}
}
}
public static IEnumerable<INamespaceSymbol> GetAllNamespaces(
this INamespaceSymbol namespaceSymbol,
CancellationToken cancellationToken)
{
using var _ = ArrayBuilder<INamespaceSymbol>.GetInstance(out var stack);
stack.Push(namespaceSymbol);
while (stack.TryPop(out var childNamespace))
{
cancellationToken.ThrowIfCancellationRequested();
stack.AddRange(childNamespace.GetNamespaceMembers());
yield return childNamespace;
}
}
public static IEnumerable<INamedTypeSymbol> GetAllTypes(
this IEnumerable<INamespaceSymbol> namespaceSymbols,
CancellationToken cancellationToken)
{
return namespaceSymbols.SelectMany(n => n.GetAllTypes(cancellationToken));
}
/// <summary>
/// Searches the namespace for namespaces with the provided name.
/// </summary>
public static IEnumerable<INamespaceSymbol> FindNamespaces(
this INamespaceSymbol namespaceSymbol,
string namespaceName,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using var _ = ArrayBuilder<INamespaceSymbol>.GetInstance(out var stack);
stack.Push(namespaceSymbol);
while (stack.TryPop(out var current))
{
cancellationToken.ThrowIfCancellationRequested();
var matchingChildren = current.GetMembers(namespaceName).OfType<INamespaceSymbol>();
foreach (var child in matchingChildren)
yield return child;
stack.AddRange(current.GetNamespaceMembers());
}
}
public static bool ContainsAccessibleTypesOrNamespaces(
this INamespaceSymbol namespaceSymbol,
IAssemblySymbol assembly)
{
using var namespaceQueue = SharedPools.Default<Queue<INamespaceOrTypeSymbol>>().GetPooledObject();
return ContainsAccessibleTypesOrNamespacesWorker(namespaceSymbol, assembly, namespaceQueue.Object);
}
public static INamespaceSymbol? GetQualifiedNamespace(
this INamespaceSymbol globalNamespace,
string namespaceName)
{
var namespaceSymbol = globalNamespace;
foreach (var name in namespaceName.Split('.'))
{
var members = namespaceSymbol.GetMembers(name);
namespaceSymbol = members.Count() == 1
? members.First() as INamespaceSymbol
: null;
if (namespaceSymbol is null)
{
break;
}
}
return namespaceSymbol;
}
private static bool ContainsAccessibleTypesOrNamespacesWorker(
this INamespaceSymbol namespaceSymbol,
IAssemblySymbol assembly,
Queue<INamespaceOrTypeSymbol> namespaceQueue)
{
// Note: we only store INamespaceSymbols in here, even though we type it as
// INamespaceOrTypeSymbol. This is because when we call GetMembers below we
// want it to return an ImmutableArray so we don't incur any costs to iterate
// over it.
foreach (var constituent in namespaceSymbol.ConstituentNamespaces)
{
// Assume that any namespace in our own assembly is accessible to us. This saves a
// lot of cpu time checking namespaces.
if (Equals(constituent.ContainingAssembly, assembly))
{
return true;
}
namespaceQueue.Enqueue(constituent);
}
while (namespaceQueue.Count > 0)
{
var ns = namespaceQueue.Dequeue();
// Upcast so we call the 'GetMembers' method that returns an ImmutableArray.
var members = ns.GetMembers();
foreach (var namespaceOrType in members)
{
if (namespaceOrType.Kind == SymbolKind.NamedType)
{
if (namespaceOrType.IsAccessibleWithin(assembly))
{
return true;
}
}
else
{
namespaceQueue.Enqueue((INamespaceSymbol)namespaceOrType);
}
}
}
return false;
}
}
|