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

How can I qualify symbols within a Roslyn syntax with a CSharpSyntaxRewriter?

提问人:Aaa 提问时间:10/9/2023 最后编辑:Aaa 更新时间:10/15/2023 访问量:44

问:

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

我可以通过以下方式轻松获得完全合格的字段。但是,我不知道如何获取初始值设定项的完全限定版本。TypeIFieldSymbol

此值可以是任何值。它不一定是静态的;它可以是自定义类的嵌套构造函数;等。我需要完全限定所有符号,并将结果打印为字符串,我可以将其插入到生成的代码中,而无需费心使用。

我想我需要做一个自定义,访问正确的节点,并用 .但是互联网上没有太多关于如何做到这一点的信息。CSharpSyntaxRewriterSemanticModel

这是我到目前为止所得到的:

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.BazFoo.MyNamespace.SomeStuff.Foo.BazWithExpressionWithName

有什么想法吗?有没有更好的方法?

编辑:根据要求,这是完整的上下文:

[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 源生成器

评论

0赞 nalka 10/13/2023
SemanticModel 从何而来?更一般地说,你能提供你的源生成器和语法重写器的MRE吗?var symbol = SemanticModel.GetSymbolInfo(node);
0赞 Aaa 10/15/2023
我在答案中提供了更多的上下文,并添加了一个到目前为止对我有用的解决方案。不过,我很欣赏另一双眼睛,因为我认为它不是特别强大。

答:

0赞 Aaa 10/15/2023 #1

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

我将不胜感激有关如何使其更强大的建议/编辑。

从我的原始示例中,使用以下命令获取更正后的初始值设定项:

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;
    }
}