Background and Motivation
We've had a number of requests over the years for a GetTypeByMetadataName that works differently. The IDE has a GetBestTypeByMetadataName extension method that works well for their purposes, and #52399 requested making it a public API on Compilation itself. However, this API is just a different heuristic for picking the "best" among all potential types exposed for a given metadata name in a compilation; it's one that works well for the IDE, but it's not a one-size-fits all algorithm. Rather than making another heuristic API and trying to determine if we made it extensible enough for any given use case, we instead propose an API that will get all types that match a given metadata name in a Compilation and all referenced assemblies: the caller can than look through those instances and decide which one it would like to use.
Proposed API
namespace Microsoft.CodeAnalysis
{
public class Compilation
{
+ public ImmutableArray<INamedTypeSymbol> GetTypesByMetadataName(string fullyQualifiedMetadataName);
}
}
Usage Examples
// Fetching `System.Runtime.CompilerServices.IsExternalInit` from the current project, if it exists
var isExternalInit = compilation.GetTypesByMetadataName("System.Runtime.CompilerServices.IsExternalInit").FirstOrDefault();
if (ReferenceEquals(isExternalInit?.ContainingAssembly, compilation.Assembly) { ... }
// Reimplementing the IDE's GetBestTypeByMetadataName on top of this API
public static INamedTypeSymbol? GetBestTypeByMetadataName(this Compilation compilation, string fullyQualifiedMetadataName)
{
INamedTypeSymbol type = null;
foreach (var currentType in compilation.GetTypesByMetadataName(fullyQualifiedMetadataName));
{
switch (currentType.GetResultantVisibility())
{
case Utilities.SymbolVisibility.Public:
case Utilities.SymbolVisibility.Internal when currentType.ContainingAssembly.GivesAccessTo(compilation.Assembly):
break;
default:
continue;
}
if (ReferenceEquals(currentType.ContainingAssembly, compilation.ContainingAssembly))
{
return currentType;
}
if (type is not null)
{
// Multiple visible types with the same metadata name are present
return null;
}
type = currentType;
}
return type;
}
Alternative Designs
I've opted to add a few extensions to try address what I view as the common use case for this API, but it's possible we don't really see those extensions being useful/needed, so we can choose to omit them if needed.
We could soften our "no heuristics" stance slightly by adding a default bool parameter includeInaccessibleTypes that would control whether the returned enumerable includes types that the match the requested FQN, but are not visible to the current Compilation (because they are internal without IVT/private). This could be a common enough heuristic that it would make sense to include in the API, but there's definitely a slippery slope of how far down this rabbit hole do we want to go.
Risks
Any new API adds risks, of course, but I believe the approach here leads to less risk for both the compiler and consumers, and the heuristics expected are clearly defined by the consumer's code.
Background and Motivation
We've had a number of requests over the years for a
GetTypeByMetadataNamethat works differently. The IDE has aGetBestTypeByMetadataNameextension method that works well for their purposes, and #52399 requested making it a public API onCompilationitself. However, this API is just a different heuristic for picking the "best" among all potential types exposed for a given metadata name in a compilation; it's one that works well for the IDE, but it's not a one-size-fits all algorithm. Rather than making another heuristic API and trying to determine if we made it extensible enough for any given use case, we instead propose an API that will get all types that match a given metadata name in a Compilation and all referenced assemblies: the caller can than look through those instances and decide which one it would like to use.Proposed API
namespace Microsoft.CodeAnalysis { public class Compilation { + public ImmutableArray<INamedTypeSymbol> GetTypesByMetadataName(string fullyQualifiedMetadataName); } }Usage Examples
Alternative Designs
I've opted to add a few extensions to try address what I view as the common use case for this API, but it's possible we don't really see those extensions being useful/needed, so we can choose to omit them if needed.
We could soften our "no heuristics" stance slightly by adding a default bool parameter
includeInaccessibleTypesthat would control whether the returned enumerable includes types that the match the requested FQN, but are not visible to the current Compilation (because they are internal without IVT/private). This could be a common enough heuristic that it would make sense to include in the API, but there's definitely a slippery slope of how far down this rabbit hole do we want to go.Risks
Any new API adds risks, of course, but I believe the approach here leads to less risk for both the compiler and consumers, and the heuristics expected are clearly defined by the consumer's code.