docs: 宏增加 open 校验——@JsonExportFields 仅允许标注 open class,否则编译期报错

- 遍历 ClassDecl.modifiers 检查 TokenKind.OPEN(参考 soulsoft Extensions.cj isOpen)
- 非 open class / 非 class 声明 → throw MacroException 编译期报错
- 测试计划补充两条校验用例
This commit is contained in:
2026-08-17 13:11:19 +08:00
parent a7ed2902f5
commit 7faea12a1e
+33 -4
View File
@@ -181,13 +181,40 @@ import std.collection.*
/// 标注在父类上:自动生成父类字段的 JSON 导出/导入静态方法
public macro JsonExportFields(input: Tokens): Tokens {
// 1. 解析输入的 ClassDecl
// 2. 收集 public var 字段(跳过 @JsonIgnore
// 3. 生成 exportJsonFields / importJsonFields 两个静态方法
// 4. 拼回原类声明 + 新方法,返回 Tokens
// 1. 解析输入的 ClassDecl(非类声明 → 抛 MacroException
// 2. 校验 open:非 open class → 抛 MacroException(见 3.1.1
// 3. 收集 public var 字段(跳过 @JsonIgnore
// 4. 生成 exportJsonFields / importJsonFields 两个静态方法
// 5. 拼回原类声明 + 新方法,返回 Tokens
}
```
#### 3.1.1 open 校验(编译期报错)
`@JsonExportFields` 的语义是「暴露**父类**字段给子类继承链」——标注在**非 open** 类上没有意义(非 open 类不能被继承,不存在子类实例传入的场景)。因此宏在展开时校验:
```cangjie
/// 遍历类声明的修饰符,检查是否含 open(参考 soulsoft Extensions.cj isOpen
func isOpenClass(decl: ClassDecl): Bool {
for (m in decl.modifiers) {
if (m.keyword.kind == TokenKind.OPEN) {
return true
}
}
false
}
// 宏展开入口内:
if (!isOpenClass(decl)) {
throw MacroException("@JsonExportFields 只能标注在 open class 上,"
+ "因为非 open 类不能被继承,不存在子类场景。")
}
```
-`MacroException` → 编译期直接报错,错误信息定位到标注处,用户立即发现;
- 顺带校验:标注在 **struct / interface / enum / func** 等非 class 声明上也抛错(`MacroCallExpr<ClassDecl>.parse` 失败即报);
- 参考先例:soulsoft `Extensions.cj:75-82` 就是遍历 `modifiers``TokenKind.OPEN`;宏内报错用 `std.ast``MacroException``assertion_macro.cj` 同款用法)。
### 3.2 生成的目标代码
对如下父类:
@@ -414,6 +441,8 @@ main() {
| 父类字段 null | 导入时保持默认值 |
| 与 @JsonPropertyName 组合 | 父类字段用注解名输出/匹配 |
| 顶层序列化父类类型实例 | `Serialize(BaseUser 实例)` 直接可用 |
| **宏校验:非 open 类标注报错** | 编译期报 `MacroException`(非 open class 标注 `@JsonExportFields` 编译失败) |
| **宏校验:非 class 声明标注报错** | 对 struct/interface/enum 标注 `@JsonExportFields` 编译失败 |
---