增量生成器产生重复的文件名

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

我正在尝试构建一个增量源生成器,它生成用于依赖注入的 C# 构造函数。然而,我的生成器似乎生成了 2 个同名源

Classes.g.cs

该项目托管于此处。为了尝试一下:

git clone https://github.com/MintPlayer/MintPlayer.Dotnet.Tools

启动 Visual Studio,并将 SourceGenerators 项目(不是 SourceGenerators.Debug)设置为启动项目,然后按 F5。如果您通过 Visual Studio 安装程序安装了 .NET 编译器平台 SDK,这将在调试项目中的文件上运行生成器。

您会看到我在代码中添加的断点语句被击中两次,因此生成器生成此文件的次数过于频繁。

为什么该发电机运行两次?

背景

XxxSourceGenerator 类是实际的生成器。当您键入代码时,它们将由 Visual Studio 运行。

[Generator(LanguageNames.CSharp)]
public class ClassNamesSourceGenerator : IIncrementalGenerator {
    public void Initialize(IncrementalGeneratorInitializationContext context) {
        var classDeclarationsProvider = context.SyntaxProvider
            .CreateSyntaxProvider(
                static (node, ct) => node is ClassDeclarationSyntax { } classDeclaration,
                static (context, ct) => {
                    if (context.Node is ClassDeclarationSyntax classDeclaration &&
                        context.SemanticModel.GetDeclaredSymbol(classDeclaration, ct) is INamedTypeSymbol symbol) {
                        return new Models.ClassDeclaration { Name = symbol.Name };
                    } else {
                        return default;
                    }
                }
            )
            .WithComparer(ValueComparers.ClassDeclarationValueComparer.Instance)
            .Collect();

        var fieldDeclarationsProvider = context.SyntaxProvider
            .CreateSyntaxProvider(
                static (node, ct) => node is FieldDeclarationSyntax { AttributeLists.Count: > 0 } fieldDeclaration
                    && fieldDeclaration.Modifiers.Any(Microsoft.CodeAnalysis.CSharp.SyntaxKind.ReadOnlyKeyword),
                static (context2, ct) => {
                    ...
                    return new Models.FieldDeclaration {
                        Namespace = namespaceDeclaration.Name.ToString(),
                        FullyQualifiedClassName = classSymbol.ToDisplayString(),
                        ClassName = classSymbol.Name,
                        Name = symbol.Name,
                        FullyQualifiedTypeName = symbol.Type.ToDisplayString(),
                        Type = symbol.Type.Name,
                    };
                }
            )
            .Collect();

他们首先转换来自提供者的数据,然后调用多个生产者,将符号单独转换为 C# 代码。

var classNamesSourceProvider = classDeclarationsProvider
    .Combine(config)
    .Select(static (p, ct) => new Producers.ClassNamesProducer(declarations: p.Left, rootNamespace: p.Right.RootNamespace!));

var classNameListSourceProvider = classDeclarationsProvider
    .Combine(config)
    .Select(static (p, ct) => new Producers.ClassNameListProducer(declarations: p.Left, rootNamespace: p.Right.RootNamespace!));

var fieldDeclarationSourceProvider = fieldDeclarationsProvider
    .Combine(config)
    .Select(static (p, ct) => new Producers.FieldNameListProducer(declarations: p.Left, rootNamespace: p.Right.RootNamespace!));

然后您需要合并所有这些生成器的输出并将您的源提供程序传递给.NET/VS

// Combine all Source Providers
var sourceProvider = classNamesSourceProvider
    .Combine(classNameListSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right })
    .Combine(fieldDeclarationSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right });

// Generate Code
context.RegisterSourceOutput(sourceProvider, static (c, g) => g?.Produce(c));

编辑

值得注意的是,如果我使用此代码片段:

var sourceProvider = fieldDeclarationSourceProvider
    .Combine(classNamesSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right })
    .Combine(classNameListSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right });

fieldDeclarations 文件仅生成一次,但 classNameList 文件生成两次,因此显然底部提供程序被触发两次

// Generates ClassNameList.g.cs twice
var sourceProvider = classNamesSourceProvider
    .Combine(fieldDeclarationSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right })
    .Combine(classNameListSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right });
// Generates FieldNameList.g.cs twice
var sourceProvider = classNamesSourceProvider
    .Combine(classNameListSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right })
    .Combine(fieldDeclarationSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right });
// Generates ClassNameList.g.cs twice
var sourceProvider = fieldDeclarationSourceProvider
    .Combine(classNamesSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right })
    .Combine(classNameListSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right });
// Generates ClassNames.g.cs twice
var sourceProvider = fieldDeclarationSourceProvider
    .Combine(classNameListSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right })
    .Combine(classNamesSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right });
// Generates ClassNames.g.cs twice
var sourceProvider = classNameListSourceProvider
    .Combine(fieldDeclarationSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right })
    .Combine(classNamesSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right });
// Generates FieldNameList.g.cs twice
var sourceProvider = classNameListSourceProvider
    .Combine(classNamesSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right })
    .Combine(fieldDeclarationSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right });
c# .net roslyn sourcegenerators incremental-generator
1个回答
0
投票

结果我不得不使用以下内容来代替

// Combine all Source Providers
var sourceProvider = classNamesSourceProvider
    .Combine(fieldDeclarationSourceProvider)
    .SelectMany(static (p, _) => new Producer[] { p.Left, p.Right })
    .Collect()
    .Combine(classNameListSourceProvider)
    .SelectMany(static (p, _) => p.Left.Concat(new Producer[] { p.Right }));
© www.soinside.com 2019 - 2024. All rights reserved.