docs: @JsonExportFields 支持自动补 open(已实测)——非 open 类标注时宏自动加 open 修饰符

- 实测验证:宏展开的 open 在语义检查前生效,子类可正常继承
- 提供两种模式:默认自动补 open(推荐);requireOpen 严格模式非 open 报错
- 非 class 声明标注 → ASTException 编译期报错(已实测,错误定位到标注处)
- 测试计划更新为对应三条用例
This commit is contained in:
2026-08-17 13:13:59 +08:00
parent 7faea12a1e
commit fe1daec4f9
+28 -8
View File
@@ -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 ```cangjie
/// 遍历类声明的修饰符,检查是否含 open(参考 soulsoft Extensions.cj isOpen /// 遍历类声明的修饰符,检查是否含 open(参考 soulsoft Extensions.cj isOpen
@@ -203,17 +203,36 @@ func isOpenClass(decl: ClassDecl): Bool {
} }
false false
} }
```
**模式一(推荐):自动补 open**
```cangjie
// 宏展开入口内: // 宏展开入口内:
if (!isOpenClass(decl)) { 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 类不能被继承,不存在子类场景。") + "因为非 open 类不能被继承,不存在子类场景。")
} }
``` ```
-`MacroException` → 编译期直接报错,错误信息定位到标注处,用户立即发现 -`ASTException`(宏内实际可用异常类,soulsoft 同款)→ 编译期直接报错,错误定位到标注处`main.cj:5:1` 形式)
- 顺带校验:标注在 **struct / interface / enum / func** 等非 class 声明上也抛错(`MacroCallExpr<ClassDecl>.parse` 失败即报); - 顺带校验:标注在 **struct / interface / enum / func** 等非 class 声明上也抛错(`parseDecl` 结果 `as ClassDecl` 失败即报,已实测)。
- 参考先例:soulsoft `Extensions.cj:75-82` 就是遍历 `modifiers``TokenKind.OPEN`;宏内报错用 `std.ast``MacroException``assertion_macro.cj` 同款用法)。
**取舍**:自动补 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 生成的目标代码 ### 3.2 生成的目标代码
@@ -441,8 +460,9 @@ main() {
| 父类字段 null | 导入时保持默认值 | | 父类字段 null | 导入时保持默认值 |
| 与 @JsonPropertyName 组合 | 父类字段用注解名输出/匹配 | | 与 @JsonPropertyName 组合 | 父类字段用注解名输出/匹配 |
| 顶层序列化父类类型实例 | `Serialize(BaseUser 实例)` 直接可用 | | 顶层序列化父类类型实例 | `Serialize(BaseUser 实例)` 直接可用 |
| **宏校验:非 open 类标注报错** | 编译期报 `MacroException`(非 open class 标注 `@JsonExportFields` 编译失败 | | **宏自动补 open** | `@JsonExportFields class Base`(无 open)展开后子类可继承(编译通过 |
| **宏校验:非 class 声明标注报错** | 对 struct/interface/enum 标注 `@JsonExportFields` 编译失败 | | **宏严格模式报错** | `@JsonExportFields[requireOpen: true]` 标注非 open class 编译失败 |
| **宏非 class 标注报错** | 对 struct/interface/enum 标注 `@JsonExportFields` 编译失败 |
--- ---