Appearance
术语分册 2:语法与语义
这一册负责解释“怎么从大量源码里筛到目标”,以及“语法对象和语义对象到底怎么分工”。
SyntaxNode
- 一句话说明:所有语法节点的基础类型
- 什么时候会用到:做语法筛选、判断节点形状时
- 对应教程:第 7 章
关键理解:
| 你通常拿它做什么 | 说明 |
|---|---|
| 判断节点是哪种语法 | 例如是否为 ClassDeclarationSyntax |
| 作为更具体节点的父类型 | 先拿到 SyntaxNode,再转成具体语法类型 |
ClassDeclarationSyntax
关键点:
| 你能拿到什么 | 说明 |
|---|---|
| 类声明的语法结构 | 类名、修饰符、特性列表等 |
| 走向语义的入口 | 通过 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
关键成员:
| 成员 | 能拿到什么 |
|---|---|
GetDeclaredSymbol(...) | 某个声明节点对应的语义符号 |
GetTypeInfo(...) | 某段语法表达式的类型信息 |
GetSymbolInfo(...) | 某个语法位置对应的符号信息 |
最常见的桥接动作:
csharp
var classSymbol = context.SemanticModel.GetDeclaredSymbol(classDeclaration, token);这一册最重要的分层结论
| 你现在想知道什么 | 优先看哪个对象 |
|---|---|
| 代码长什么样 | SyntaxNode / ClassDeclarationSyntax |
| 被标记的是谁 | GeneratorAttributeSyntaxContext |
| 语法形状命中了什么 | GeneratorSyntaxContext |
| 代码真实代表什么类型或成员 | SemanticModel |
下一步去哪里
- 想读类、属性、类型对象:看 术语分册 3:符号与类型
- 想读特性参数与泛型约束:看 术语分册 4:特性与泛型
- 返回索引:看 术语与 API 手册