编译器流水线如何对应Incremental Source Generation?

How does the compiler pipeline correspond to Incremental Source Generation?

我无法理解 Roslyn 文档并将其与我在使用增量源代码生成器时看到的内容相匹配。

https://docs.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/compiler-api-model

它的图片信息量很大。

据我了解,语法树是 Parser 工作的结果(正确吗?)。我可以通过 IncrementalGeneratorInitializationContext.SyntaxProvider 访问语法树。让我失望的是 GeneratorSyntaxContext 有一个名为 SemanticModel 的 属性,它似乎允许访问符号。我认为符号是编译的产物,在处理语法时应该不可用。

这引出了另一个问题。 Compiler Pipeline 的哪一部分对应于编译?是活页夹吗?

增量生成器和管道没有直接关联。老实说,该图应该更表述为:

  1. 解析器生成 SyntaxTree 对象。 (那是深绿色框。)
  2. 可以将多个 SyntaxTree 对象组合在一起以创建一个 Compilation 对象,该对象可以访问符号(灰色框)。
  3. 给定一个 Compilation 和 SyntaxTree,您可以获得一个 SemanticModel 以在那里提出更多问题(浅绿色框)。
  4. Compilation 有一个 emit API 你可以使用(就是浅绿色框。)

与其将其视为管道,不如将其视为指向其他事物的数据结构,并且您在生成器中说明了您所依赖的数据结构。这个想法是你可以越精确,它让我们在 IDE 中更快,这样我们就可以在下次击键时减少运行。

在 IDE 中实际发生的是击键发生,我们为您编辑的文件生成一个新的语法树,但我们拥有您未更改的所有现有树。我们产生一个新的编译器,并且可能重新运行 生成器。我们将首先 运行 增量语法提供程序的“谓词”部分来查找已编辑树中的节点,但我们仍然知道来自其他树的生成器的先前 运行 的节点。这样你就不必重新分析没有改变的部分。然后我们给你机会用语义再看一遍节点,这更昂贵。

真正的目标是“遍历每棵语法树并找到所有看起来像特定模式的节点”是昂贵的,所以这让我们把缓存放在中间,使它比一个更便宜运行下一个。