【问题标题】:Source generation: How to get involved types from InvocationExpressionSyntax源代码生成:如何从 InvocationExpressionSyntax 获取涉及的类型
【发布时间】:2021-04-07 17:51:00
【问题描述】:

我正在尝试编写一个源生成器,并且需要扩展方法调用中涉及的类型。

问题是,这个扩展方法是源码生成器自己生成的。因此,如果我尝试获取 ISymbol,我的 Generator 类中将得到 null。

是否有可能获得我需要的信息?

这是一个例子

var result = someObject.ConvertTo<OtherType>();

ConvertTo&lt;T&gt;() 扩展方法是从源生成器生成的。我可以找到正确的InvocationExpressionSyntax,但是如何获得someObjectOtherType 的完全限定类型?

这里是生成器

[Generator]
public class ConvertGenerator : ISourceGenerator
{
    private const string defaultNamespace = "AutoGenerators";
    private const string extensionsClassName = "ConvertExtensions";
    private static readonly string _classText = @$"
namespace {defaultNamespace}
{{
public static class {extensionsClassName}
{{
    public static TDestination ConvertTo<TDestination>(this object source)
    {{
        /* generated */

        return default;
    }}
}} }}";

    public void Initialize(GeneratorInitializationContext context)
    {
#if DEBUG
        if (!Debugger.IsAttached)
        {
            Debugger.Launch();
        }
#endif
        // Register a syntax receiver that will be created for each generation pass
        context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
    }

    public void Execute(GeneratorExecutionContext context)
    {
        // retrieve the populated receiver 
        if (!(context.SyntaxReceiver is SyntaxReceiver receiver))
            return;

        // for testing
        var invocationSyntax = receiver.Methods.FirstOrDefault();
        if (invocationSyntax != null)
        {
           var semanticModel = context.Compilation.GetSemanticModel(invocationSyntax.SyntaxTree);
           
           // symbol is null here
           var symbol = semanticModel.GetSymbolInfo(invocationSyntax.Expression).Symbol;
           
           // TODO: how to get type description for sourceObjectName and destinationTypeName
           var convertInvocationString = invocationSyntax.ToString();
           var sourceObjectName = convertInvocationString.Substring(0, convertInvocationString.IndexOf('.'));
           var destTypeSubs = convertInvocationString.Substring(convertInvocationString.IndexOf('<') + 1);
           var destinationTypeName = destTypeSubs.Substring(0, destTypeSubs.IndexOf('(') - 1);
           
        }

        var classSource = _classText;
        context.AddSource($"{extensionsClassName}.cs", SourceText.From(classSource, Encoding.UTF8));
    }

    /// <summary>
    /// Created on demand before each generation pass
    /// </summary>
    class SyntaxReceiver : ISyntaxReceiver
    {
        public List<InvocationExpressionSyntax> Methods { get; } = new List<InvocationExpressionSyntax>();

        /// <summary>
        /// Called for every syntax node in the compilation, we can inspect the nodes and save any information useful for generation
        /// </summary>
        public void OnVisitSyntaxNode(SyntaxNode context)
        {
            // any field with at least one attribute is a candidate for property generation
            if (context is InvocationExpressionSyntax invocationExpressionSyntax
                && invocationExpressionSyntax.ToString().Contains("ConvertTo<"))
            {
                Methods.Add(invocationExpressionSyntax);
            }
        }
    }
}

更新:我认为我需要的不仅仅是类型。我需要ISymbol 来获取类型的所有属性

更新 2:我做了一小步,将 ConvertTo&lt;T&gt; 方法设置为部分,并使用此方法引用分离项目。我现在得到IMethodSymbol 并且有OtherTypeITypeSymbol,但是someObjectITypeSymbolobject 类型,因为扩展方法签名。但我需要someObject 的具体类型符号

【问题讨论】:

    标签: c# roslyn sourcegenerators


    【解决方案1】:

    我找到了解决办法。

    首先ConvertTo&lt;T&gt; 方法应该在我的项目中声明为部分,这样我就可以获得ISymbol 进行调用。它给了我ReturnType

    var semanticModel = context.Compilation.GetSemanticModel(invocationSyntax.SyntaxTree);
    var mapToSymbol = semanticModel.GetSymbolInfo(invocationSyntax.Expression).Symbol as IMethodSymbol;
    var convertToType = mapToSymbol.ReturnType;
    

    那么我可以使用invocationSyntax.Expression来获取someObject的类型或者扩展方法的参数

    var convertFromType = TryGetSourceType(invocationSyntax.Expression, semanticModel);
    
    ...
    
    private static ITypeSymbol TryGetSourceType(ExpressionSyntax invocationExpression, SemanticModel semanticModel)
    {
        switch (invocationExpression)
        {
            case MemberAccessExpressionSyntax memberAccessExpressionSyntax:
                var symbol = semanticModel.GetSymbolInfo(memberAccessExpressionSyntax.Expression).Symbol;
                return symbol switch
                {
                    ILocalSymbol local => local.Type,
                    IParameterSymbol param => param.Type,
                    IFieldSymbol field => field.Type,
                    IPropertySymbol prop => prop.Type,
                    IMethodSymbol method => method.MethodKind == MethodKind.Constructor ? method.ReceiverType : method.ReturnType,
                    _ => null
                };
            default:
                return null;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-23
      • 1970-01-01
      • 2022-10-20
      相关资源
      最近更新 更多