diff --git a/docs/父类字段序列化方案.md b/docs/父类字段序列化方案.md index 242b0ad..71d6501 100644 --- a/docs/父类字段序列化方案.md +++ b/docs/父类字段序列化方案.md @@ -189,9 +189,9 @@ public macro JsonExportFields(input: Tokens): Tokens { } ``` -#### 3.1.1 open 校验(编译期报错) +#### 3.1.1 open 处理(两种模式,默认自动补 open) -`@JsonExportFields` 的语义是「暴露**父类**字段给子类继承链」——标注在**非 open** 类上没有意义(非 open 类不能被继承,不存在子类实例传入的场景)。因此宏在展开时校验: +`@JsonExportFields` 的语义是「暴露**父类**字段给子类继承链」——标注的类**必须可被继承**。Cangjie 类默认不可继承(非 open 类被继承会编译报错 `super class is not inheritable`),所以宏展开时处理 `open`: ```cangjie /// 遍历类声明的修饰符,检查是否含 open(参考 soulsoft Extensions.cj isOpen) @@ -203,17 +203,36 @@ func isOpenClass(decl: ClassDecl): Bool { } false } +``` +**模式一(推荐):自动补 open** + +```cangjie // 宏展开入口内: if (!isOpenClass(decl)) { - throw MacroException("@JsonExportFields 只能标注在 open class 上," + decl.modifiers.add(Modifier(Token(TokenKind.OPEN))) // 自动加 open +} +``` + +- **已实测可行**:宏给 `class Base` 自动加 `open` 后,子类 `class Child <: Base` 编译通过、正常运行(`macro_open_probe` 验证); +- 宏展开的 `open` 在语义检查**之前**生效,所以后续继承检查能看到; +- 用户少写一个关键字,标注 `@JsonExportFields` 本身就表达了「我要被继承」的意图。 + +**模式二:严格校验(不自动补,非 open 报错)** + +```cangjie +if (!isOpenClass(decl)) { + throw ASTException("@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` 同款用法)。 +- 抛 `ASTException`(宏内实际可用异常类,soulsoft 同款)→ 编译期直接报错,错误定位到标注处(`main.cj:5:1` 形式); +- 顺带校验:标注在 **struct / interface / enum / func** 等非 class 声明上也抛错(`parseDecl` 结果 `as ClassDecl` 失败即报,已实测)。 + +**取舍**:自动补 open 更省事但「隐式改变类语义」;严格校验更显式但要求用户记得写 `open`。默认建议模式一(自动补),可在宏属性中提供开关,如 `@JsonExportFields[requireOpen: true]` 切到模式二。 + +- 参考先例:soulsoft `Extensions.cj:75-82` 遍历 `modifiers` 判 `TokenKind.OPEN`;`SerializationMacro.cj:27` 用 `throw ASTException(...)` 做宏内校验;`cd.modifiers.add(Modifier(Token(TokenKind.OPEN)))` 与 soulsoft `funcDecl.modifiers.add(Modifier(Token(TokenKind.PUBLIC)))` 同款操作。 ### 3.2 生成的目标代码 @@ -441,8 +460,9 @@ main() { | 父类字段 null | 导入时保持默认值 | | 与 @JsonPropertyName 组合 | 父类字段用注解名输出/匹配 | | 顶层序列化父类类型实例 | `Serialize(BaseUser 实例)` 直接可用 | -| **宏校验:非 open 类标注报错** | 编译期报 `MacroException`(非 open class 标注 `@JsonExportFields` 编译失败) | -| **宏校验:非 class 声明标注报错** | 对 struct/interface/enum 标注 `@JsonExportFields` 编译失败 | +| **宏自动补 open** | `@JsonExportFields class Base`(无 open)展开后子类可继承(编译通过) | +| **宏严格模式报错** | `@JsonExportFields[requireOpen: true]` 标注非 open class 编译失败 | +| **宏非 class 标注报错** | 对 struct/interface/enum 标注 `@JsonExportFields` 编译失败 | ---