.net 如何使用CSharpSyntaxRewriter限定Roslyn语法中的符号?

hwamh0ep  于 2023-10-21  发布在  .NET
关注(0)|答案(1)|浏览(138)

我正在编写一个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;
}

我可以通过IFieldSymbol轻松获得完全限定的字段Type。但是,我不知道如何获得初始化器的完全限定版本。
这个值可以是任何值。它不必是静态的;它可以是自定义类的嵌套构造函数;我需要完全限定所有的符号,并将结果打印为一个字符串,我可以将其插入到生成的代码中,而无需使用。
我想我需要做一个自定义的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
            });
    }
}
ujv3wf0j

ujv3wf0j1#

我找到了一个解决我案子的办法,但我不知道它是否无懈可击。可能有一些东西(比如构造函数,也许?),这将不会举行了,但这工程的成员访问。
我很感激关于如何使它更强大的建议/编辑。
从我原来的例子中,得到正确的初始化器:

var newInitializer = new (QualifiedWriter(ctx.SemanticModel))
    .Visit(syntax.Initializer) as EqualsValueClauseSyntax;
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;
    }
}

相关问题