Substituting (Dictionary<string, ITypeSymbol> typeParameterValues) into ITypeSymbol in all cases

Viewed 167

I have a dictionary of type parameter values:

ImmutableDictionary<string, ITypeSymbol> typeParameterValues;

How can with an ITypeSymbol I substitute those type parameters in:

Eg. List<T> -> List<string> (if T is string)

Eg. Dictionary<K, V>.ValueCollection -> Dictionary<string, int>.ValueCollection (if K is string and V is int)

So far I have been able to make the first example work with the following code:

static ITypeSymbol SubstituteGenericArgs (ITypeSymbol type) => type switch {
    ITypeParameterSymbol tps => typeParameterValues[tps.Name]
    INamedTypeSymbol nts when nts.IsGeneric => nts.ConstructedFrom.Construct(
        nts.TypeParameters.Select(SubstituteGenericArgs).ToArray()
    ),
    INamedTypeSymbol ngts when !ngts.IsGeneric => ngts
};

I know I can do nts.ContainingType but the problem is once I have substituted the correct type arguments into the containing type, there is no way to get the original type using the new containing type that I have found.

1 Answers

This is what I came up:

public record GenericContext (Dictionary<string, ITypeSymbol> typeParameterValues)
{
    public ITypeSymbol SubstituteGenericArgs (ITypeSymbol type) => type switch
    {
        INamedTypeSymbol nts when nts.ContainingType is {} ct && ct.IsGenericType => SimpleSubstituteGenericArgs(
            SubstituteGenericArgs(ct).GetMembers().OfType<INamedTypeSymbol>().Single(
                gv => gv.OriginalDefinition == nts.OriginalDefinition
            )
        ),
        _ => SimpleSubstituteGenericArgs(type)
    };

    public ITypeSymbol SimpleSubstituteGenericArgs(ITypeSymbol type) => type switch
    {
        ITypeParameterSymbol tps => typeParameterValues[tps.Name],
        INamedTypeSymbol nts when nts.TypeArguments.Length == 0 => nts,
        INamedTypeSymbol nts when nts.TypeArguments.Length >  0 => nts.ConstructedFrom.Construct(
            nts.TypeArguments.Select(SubstituteGenericArgs).ToArray()
        )
    };
}

Thanks @Jeremy Lakeman.

See here: https://dotnetfiddle.net/AIlKBw

Let me know if you think of any improvements. I thought this would be a built-in feature.

Related