源代码生成:如何从 InvocationExpressionSyntax 获取涉及的类型
Source generation: How to get involved types from InvocationExpressionSyntax
我正在尝试编写源代码生成器并需要涉及扩展方法调用的类型。
问题是,这个扩展方法是由源代码生成器自己生成的。因此,如果我尝试获取 ISymbol
,我会在生成器 class.
中获取 null
是否可以通过其他方式获得我需要的信息?
示例如下
var result = someObject.ConvertTo<OtherType>();
ConvertTo<T>()
扩展方法是从源代码生成器生成的。我可以找到正确的 InvocationExpressionSyntax
,但是如何获得 someObject
和 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>
方法部分化,并使用此方法引用单独的项目。我现在得到 IMethodSymbol
并且 ITypeSymbol
用于 OtherType
,但是 ITypeSymbol
用于 someObject
是 object
类型,因为扩展方法签名。但是我需要 someObject
的具体类型符号
我找到了解决方案。
首先,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;
}
}
我正在尝试编写源代码生成器并需要涉及扩展方法调用的类型。
问题是,这个扩展方法是由源代码生成器自己生成的。因此,如果我尝试获取 ISymbol
,我会在生成器 class.
是否可以通过其他方式获得我需要的信息?
示例如下
var result = someObject.ConvertTo<OtherType>();
ConvertTo<T>()
扩展方法是从源代码生成器生成的。我可以找到正确的 InvocationExpressionSyntax
,但是如何获得 someObject
和 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>
方法部分化,并使用此方法引用单独的项目。我现在得到 IMethodSymbol
并且 ITypeSymbol
用于 OtherType
,但是 ITypeSymbol
用于 someObject
是 object
类型,因为扩展方法签名。但是我需要 someObject
我找到了解决方案。
首先,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;
}
}