ISanych
ISanych

Reputation: 22680

Roslyn generic method specialization body

Is possible to get Operations and/or Syntax Nodes for generic methods specialized with concrete types during invocation? So in sample below it will be string instead of T?

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Semantics;
using System;
using System.Linq;

namespace so
{
    internal class Walker : CSharpSyntaxWalker
    {
        public SemanticModel Model { get; set; }

        public override void VisitAssignmentExpression(AssignmentExpressionSyntax node)
        {
            var operation = (IAssignmentExpression)Model.GetOperation(node);
            if (operation.Value.Kind == OperationKind.InvocationExpression)
            {
                var invocation = (IInvocationExpression)operation.Value;
                foreach (
                    var syntax in
                    invocation.TargetMethod.DeclaringSyntaxReferences.Select(
                        x => (MethodDeclarationSyntax)x.GetSyntax()))
                {
                    var e = (TypeOfExpressionSyntax)syntax.ExpressionBody.Expression;
                    Console.WriteLine($"Generic type {invocation.TargetMethod.TypeParameters.First()} specialized with {invocation.TargetMethod.TypeArguments.First()}");
                    // How to get specialized body here? Currently it is just generic TypeParameters
                    var symbol = Model.GetSymbolInfo(e.Type).Symbol;
                    var typeofOperation = (ITypeOfExpression)Model.GetOperation(e);
                    Console.WriteLine($"{syntax.Identifier.Text} {symbol} {typeofOperation.TypeOperand}");
                }
            }
            base.VisitAssignmentExpression(node);
        }
    }

    internal static class Program
    {
        private static void Main()
        {
            var tree = CSharpSyntaxTree.ParseText(@"
public class Program
{
    static Type TypeOf<T>() => typeof(T);

    public static void Main()
    {
        Type t;
        t = TypeOf<string>();
    }
}");
            var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
            var compilation = CSharpCompilation.Create(null, new[] { tree }, new[] { mscorlib });
            var walker = new Walker { Model = compilation.GetSemanticModel(tree) };
            walker.Visit(tree.GetRoot());
        }
    }
}

Output:

Generic type T specialized with string
TypeOf T T

And I'm trying to get TypeOf string string in output.

Code on github - https://github.com/isanych/so-39447605

Upvotes: 1

Views: 751

Answers (1)

SLaks
SLaks

Reputation: 887509

You can't do that (directly).

Generic type substitution happens at the callsite, not the declaration. There is no syntax tree that has your method body with a substituted T.

If you really want to do that, you'll need to manually replace T with its value from the callsite (and beware of all sorts of corner cases like name hiding, type parameters from base and/or enclosing classes, overload resolution, and worse).

Upvotes: 2

Related Questions