From 7faea12a1e42093df01855c5228ff006556a6906 Mon Sep 17 00:00:00 2001 From: xRain Date: Mon, 17 Aug 2026 13:11:19 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=AE=8F=E5=A2=9E=E5=8A=A0=20open=20?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E2=80=94=E2=80=94@JsonExportFields=20?= =?UTF-8?q?=E4=BB=85=E5=85=81=E8=AE=B8=E6=A0=87=E6=B3=A8=20open=20class?= =?UTF-8?q?=EF=BC=8C=E5=90=A6=E5=88=99=E7=BC=96=E8=AF=91=E6=9C=9F=E6=8A=A5?= =?UTF-8?q?=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 遍历 ClassDecl.modifiers 检查 TokenKind.OPEN(参考 soulsoft Extensions.cj isOpen) - 非 open class / 非 class 声明 → throw MacroException 编译期报错 - 测试计划补充两条校验用例 --- docs/父类字段序列化方案.md | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/docs/父类字段序列化方案.md b/docs/父类字段序列化方案.md index 17e357c..242b0ad 100644 --- a/docs/父类字段序列化方案.md +++ b/docs/父类字段序列化方案.md @@ -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.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` 编译失败 | ---