Skip to content

术语分册 2:语法与语义

这一册负责解释“怎么从大量源码里筛到目标”,以及“语法对象和语义对象到底怎么分工”。

SyntaxNode

  • 一句话说明:所有语法节点的基础类型
  • 什么时候会用到:做语法筛选、判断节点形状时
  • 对应教程:第 7 章

关键理解:

你通常拿它做什么说明
判断节点是哪种语法例如是否为 ClassDeclarationSyntax
作为更具体节点的父类型先拿到 SyntaxNode,再转成具体语法类型

ClassDeclarationSyntax

  • 一句话说明:表示类声明的语法节点
  • 什么时候会用到:确认目标是不是类,并准备进入语义分析
  • 对应教程:第 6 章第 7 章

关键点:

你能拿到什么说明
类声明的语法结构类名、修饰符、特性列表等
走向语义的入口通过 SemanticModel.GetDeclaredSymbol(...) 转成 INamedTypeSymbol

SyntaxProvider

  • 一句话说明:从语法树里高效筛选目标的入口
  • 什么时候会用到:你不想全量扫描所有代码时
  • 对应教程:第 6 章

最常见的两条路:

入口适合什么场景
ForAttributeWithMetadataName(...)特性驱动筛选
CreateSyntaxProvider(...)语法形状驱动筛选

ForAttributeWithMetadataName(...)

  • 一句话说明:按特性元数据名筛选目标节点和目标符号
  • 什么时候会用到:你的生成器天然由特性触发时
  • 对应教程:第 6 章

关键参数:

参数能拿到什么
fullyQualifiedMetadataName要匹配的特性全名
predicate语法级快速过滤
transform把上下文转成你真正需要的数据

当前仓库示例:

csharp
context.SyntaxProvider.ForAttributeWithMetadataName(
    fullyQualifiedMetadataName: "ToStringGenerator.GenerateToStringAttribute",
    predicate: IsClassDeclaration,
    transform: TransformClass)

CreateSyntaxProvider(...)

  • 一句话说明:自己定义“按什么语法形状筛目标”和“如何转换结果”的通用入口
  • 什么时候会用到:目标不是靠特性发现,而是靠语法结构发现
  • 对应教程:第 12 章

关键参数:

参数能拿到什么
predicate语法级快速筛选
transform把匹配到的语法节点转成业务数据

源码入口 1:

  • sample/03-enum-extensions-generator/EnumExtensionsGenerator/EnumExtensionsGenerator.cs

关键片段:

csharp
var enumDeclarations = context.SyntaxProvider.CreateSyntaxProvider(
    predicate: IsEnumDeclaration,
    transform: TransformEnum
);

为什么看它:

  • 这是最简单的 CreateSyntaxProvider(...) 写法
  • predicate 只负责判断“是不是枚举声明”
  • 很适合第一次建立“语法形状驱动”的直觉

源码入口 2:

  • sample/07-mvvm-observable-generator/MvvmObservableGenerator/ObservablePropertyGenerator.cs

关键片段:

csharp
var fieldDeclarations = context.SyntaxProvider
    .CreateSyntaxProvider(
        predicate: static (node, _) => IsFieldWithAttribute(node),
        transform: static (ctx, _) => GetFieldSymbol(ctx))
    .Where(static field => field is not null);

为什么看它:

  • 这个项目展示了更真实的两段式处理
  • 先按语法筛“像目标的字段”
  • 再在 transform 里用语义信息确认它是否真带目标特性

源码入口 3:

  • sample/08-validator-generator/ValidatorGenerator/ValidatorGenerator.cs

关键片段:

csharp
var classDeclarations = context.SyntaxProvider
    .CreateSyntaxProvider(
        predicate: static (node, _) => IsCandidateClass(node),
        transform: static (ctx, _) => GetClassInfoWithDiagnostics(ctx))
    .Where(static result => result.ClassInfo != null || result.Diagnostics.Count > 0);

为什么看它:

  • 这个项目说明 CreateSyntaxProvider(...) 不只是筛目标
  • 还可以在 transform 阶段顺手收集诊断信息
  • 它适合帮助你理解“语法筛选、语义确认、诊断准备”可以放进同一条增量链路

GeneratorAttributeSyntaxContext

  • 一句话说明:按特性筛选时拿到的上下文对象
  • 什么时候会用到:进入 transform 回调后
  • 对应教程:第 6 章

关键成员:

成员能拿到什么
TargetNode被特性标记的语法节点
TargetSymbol被特性标记的语义符号
Attributes匹配到的特性数据集合
SemanticModel继续做语义分析的入口

GeneratorSyntaxContext

  • 一句话说明:CreateSyntaxProvider(...)transform 回调里拿到的上下文对象
  • 什么时候会用到:你按语法形状筛目标时
  • 对应教程:第 12 章

关键成员:

成员能拿到什么
Node当前命中的语法节点
SemanticModel从当前节点继续走向语义分析

SemanticModel

  • 一句话说明:把语法节点解释成“它真实代表什么”的入口
  • 什么时候会用到:你已经拿到了语法节点,准备拿类、属性、类型的真实信息
  • 对应教程:第 7 章第 8 章

关键成员:

成员能拿到什么
GetDeclaredSymbol(...)某个声明节点对应的语义符号
GetTypeInfo(...)某段语法表达式的类型信息
GetSymbolInfo(...)某个语法位置对应的符号信息

最常见的桥接动作:

csharp
var classSymbol = context.SemanticModel.GetDeclaredSymbol(classDeclaration, token);

这一册最重要的分层结论

你现在想知道什么优先看哪个对象
代码长什么样SyntaxNode / ClassDeclarationSyntax
被标记的是谁GeneratorAttributeSyntaxContext
语法形状命中了什么GeneratorSyntaxContext
代码真实代表什么类型或成员SemanticModel

下一步去哪里

基于当前仓库文档副本构建的 VitePress 站点