Skip to content

[Proposal] GetAllTypesByMetadataName #57802

@333fred

Description

@333fred

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.

Metadata

Metadata

Assignees

Labels

Area-CompilersConcept-APIThis issue involves adding, removing, clarification, or modification of an API.Feature Requestapi-approvedAPI was approved in API review, it can be implemented

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions