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

问题描述 投票:0回答:1

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


c# .net roslyn sourcegenerators
1个回答
0
投票

我找到了适合我的情况的解决方案,但我不知道它是否完全防弹。可能有些东西(比如构造函数,也许?)不会成立,但这适用于成员访问。

我很感激有关如何使其更加健壮的建议/编辑。

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;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.