1

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

问题是,这个扩展方法是由源生成器自己生成的。所以如果我尝试 get ISymbol,我会在我的 Generator 类中得到 null 。

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

这是示例

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

ConvertTo<T>()扩展方法是从源生成器生成的。我可以找到正确的InvocationExpressionSyntax,但我怎样才能获得完全限定的someObjectand类型OtherType

这是生成器

[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<T>方法设为局部并使用此方法引用分离项目。我得到了IMethodSymbolnow 并拥有ITypeSymbolfor OtherType,但ITypeSymbolforsomeObjectobject类型,因为扩展方法签名。但我需要具体的类型符号someObject

4

1 回答 1

1

我找到了解决方案。

首先,该ConvertTo<T>方法应在我的项目中声明为部分方法,以便我可以获得 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;
    }
}
于 2021-04-12T08:38:58.023 回答