我正在编写一个 Roslyn 源生成器,我需要从字段的
VariableDeclaratorSyntax.Initializer
获取初始化程序,并将其回显到生成的文件中。
例如,如果我写了这段代码......
using MyNamespace.SomeStuff;
...
partial class Bar
{
[Convert]
Foo _fooField = Foo.Baz;
}
我的源生成器可能会产生...
partial class Bar
{
MyNamespace.SomeStuff.Foo _fooField = MyNamespace.SomeStuff.Foo.Baz;
}
我可以通过
Type
轻松获得完全合格的字段 IFieldSymbol
。但是,我不知道如何获得初始化程序的完全限定版本。
这个值可以是任何东西。它不必是静态的;它可以是自定义类的嵌套构造函数;等等。我需要完全限定所有符号,并将结果打印为字符串,我可以将其插入到生成的代码中,而无需担心使用。
我认为我需要制作一个自定义的
CSharpSyntaxRewriter
来访问正确的节点,并将名称替换为 SemanticModel
中的解析版本。但互联网上没有太多关于如何做到这一点的信息。
这是我到目前为止所得到的:
public override SyntaxNode? VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
{
var symbol = SemanticModel.GetSymbolInfo(node);
if (symbol.Symbol is not null)
return node.WithName(SyntaxFactory.IdentifierName(symbol.Symbol.ToString()));
else return base.VisitMemberAccessExpression(node);
}
但是它生成的不是
MyNamespace.SomeStuff.Foo.Baz
而是 Foo.MyNamespace.SomeStuff.Foo.Baz
,这显然不起作用。我需要使用 WithExpression
而不是 WithName
,但表达式可以是任何内容,我不知道如何缩小范围。
有什么想法吗?有更好的方法吗?
编辑:根据要求,这是完整的上下文:
[Generator]
public class StyleSourceGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// find any field tagged with [Convert]
var fields = context.SyntaxProvider.ForAttributeWithMetadataName(typeof(ConvertAttribute).FullName,
(node, token) =>
{
return node is VariableDeclaratorSyntax;
},
(ctx, token) =>
{
var syntax = (VariableDeclaratorSyntax)ctx.TargetNode;
syntax.Initializer.Value.ToString(); // this will print Foo.Baz, when I need it to print MyNamespace.SomeStuff.Foo.Baz
});
}
}
我找到了适合我的情况的解决方案,但我不知道它是否完全防弹。可能有些东西(比如构造函数,也许?)不会成立,但这适用于成员访问。
我很感激有关如何使其更加健壮的建议/编辑。
internal class QualifiedWriter : CSharpSyntaxRewriter
{
public QualifiedWriter(SemanticModel semanticModel)
{
SemanticModel = semanticModel;
}
private SemanticModel SemanticModel { get; }
public override SyntaxNode? VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
{
// for example: Microsoft.Xna.Color.Transparent; identifier name is Transparent, expression is Microsoft.Xna.Color
// Then we have a russian nesting doll of SimpleMemberAccessExpression until the expression is just an IdentifierNameSyntax
return ResolveMemberAccessSyntaxTree(node);
}
public MemberAccessExpressionSyntax ResolveMemberAccessSyntaxTree(MemberAccessExpressionSyntax node)
{
if (node.Expression is MemberAccessExpressionSyntax access) return node.WithExpression(ResolveMemberAccessSyntaxTree(access));
if (node.Expression is IdentifierNameSyntax name)
{
var symbol = SemanticModel.GetSymbolInfo(name);
if (symbol.Symbol is null) return node; // give up
return node.WithExpression(name.WithIdentifier(SyntaxFactory.Identifier(symbol.Symbol.ToString())));
}
// we don't know how to nest further!
return node;
}
}