batch-impl 0.6.0

A proc-macro library for batch generating trait impls with a powerful DSL
Documentation
# batch-impl 内部架构


面向贡献者:模块组织、解析流程、错误机制、测试矩阵。

## 模块组织


```text
lib.rs              宏入口(#[batch_impl] / #[batch_impl_only] / batch_trait! / 测试宏)
  ├── expand.rs             入口实现:expand_attr_macro / expand_batch_trait + 公共管线 run_pipeline
  ├── batch_trait_entry.rs  共享驱动:BFS 展开并列列表 → 逐叶子 generate_impl
  ├── trait_bounds.rs       TraitBounds / TraitParam + syn AST 引用收集(where 谓词透传槽位)
  ├── empty_generics.rs     `A<>` 照抄展开(形参渲染用合并后的 bound)
  ├── consts.rs             `@` 常量系统:内置类型族(@uint/@scalar/@u8..u128)+ batch_trait! 自定义定义段
  ├── path_prefix.rs        外部 trait 路径前缀:#Path::to::Trait: 状态机解析
  ├── diagnostic.rs         统一 compile_error_str(msg) 用于编译期诊断
  ├── scan.rs               扫描与游标:Cursor<'a> + scan_stop(尖括号已配对,仅剩 -> 守卫)
  ├── parse/                解析层
  │   ├── mod.rs            DSL 解析器:优先级攀爬(Op::Semi/Comma/Dash/Caret/Prim)
  │   ├── parse_atom.rs     原子层解析:属性 / fn / 前缀 / 范围 / 分组 / 列表
  │   └── generic.rs        泛型解析:parse_generic / parse_angle_bracket_contents(尖括号组即 delimiter![<>])
  ├── preprocess/           预处理层
  │   ├── mod.rs            delimiter! 分隔符拼写宏 + 指令预处理:#name 指令展开(内置 + 开放扩展)
  │   ├── preprocess_helpers.rs  预处理辅助:build_from_item / get_trait_item / parse_names_from_tokens(列表减法 `-`)
  │   ├── where_process.rs  裸 where 改写:`where 谓词 {body}` → 旧式 `where{谓词}`
  │   └── angle.rs          尖括号组:入口 None 组扁平化 + `<...>` 配对为组(输出侧还原),parse 层不再管 <> 深度
  ├── ast/                  AST 层
  │   ├── mod.rs            Ty 枚举(18 个变体,含 Error)+ Op 优先级定义
  │   └── types_render.rs   AST 渲染:ToTokens impl for Ty + params_to_tokens 系列
  ├── apply/                运算层
  │   ├── mod.rs            Apply trait + 核心 apply() 两阶段分发(右操作数"结构"优先)
  │   └── apply_tuple.rs    元组与容器运算符 + 元组展开(^N / 笛卡尔积 / 范围 / fresh 泛型)
  └── codegen/
      └── mod.rs            代码生成:extract_impl_parts → hoist_type_params → generate_impl(含 where 谓词附加与引用检查)
```

## 解析流程


**token 流 → angle_collect 配对尖括号组 → const 展开(`@` 常量:内置 +
batch_trait! 自定义表)→ 指令预处理(每条指令展开为 0..n 个 token:既有
指令恰一 `{...}` 组,`#blanket` 多段 spec)→ where 裸写改写 → `A<>` 照抄
→ Cursor 扫描取切片 → parse_item 优先级攀爬(`^`/`-` 经 `Apply` 组合:
右操作数结构优先分发)→ Ty AST → 工作清单摊平并列列表 → 逐叶子 generate_impl**

### 关键设计决策


- **尖括号组**:proc-macro2 只对 `()`/`[]`/`{}` 分组,`<>` 是扁平 Punct。
  `angle_collect` 在入口一趟把 `<...>` 配对为 `delimiter![<>]` 组(`->` 箭头的
  `>` 不参与),下游解析不再跟踪 `<>` 深度;输出侧 `render_angles` 还原为
  扁平 `<...>``angle_collect`**破坏性**的(已配对组再次收集会被当真实
  None 组扁平化),故只做一次。
- **delimiter! 宏**`Delimiter::None` 在本 crate 有两种语义——`delimiter![<>]`
  (尖括号组载体)与 `delimiter![none]`(真实透明组,宏变量展开产物)。二者
  展开值相同,不可在同一条 match 中作两个臂。proc-macro crate 禁止
  `#[macro_export]`,故宏置于 `preprocess` 顶部经 `#[macro_use]` 导入 crate 根
  (文本作用域要求其声明先于所有使用者)。
- **where 谓词继承**:trait 级 where 子句中**单一形参谓词**`T: Clone`)合并进
  `TraitParam.bound`(内联 + where 拼接),**其余谓词原样透传**到 impl 的
  where 子句。引用收集在 **syn AST** 上做(`syn::visit`):单段路径与泛型实参
  是形参引用位置;`::` 后的路径段(关联类型名)、关联类型绑定名、
  HRTB binder(`for<'a>`)天然排除;const 泛型实参 / 数组长度经 `visit_expr`
  收集。`impl_names``const N` 归一如 `N` 以匹配引用检查。

## 语法域隔离


DSL 由两个(未来三个)**互不渗透的语法域**组成,各域记号自洽、语义独立:

|| 记号 | 语义 | 由谁解析 |
|----|------|------|----------|
| **类型域**(spec 表达式) | `^`/`-`(同一 apply 的两种结合性:右嵌套/左累加)、`[...]` 列表、`(...)` 元组、`<...>` 泛型、`where{...}` 后缀、附着 `{body}` | 描述类型矩阵,每个格子生成一个 impl | `parse/` + `apply/` + `codegen/` |
| **指令域**`#name{body}` / `#fill(args)` / `#delegate(args)` / `#blanket(#all){包装}` / 开放扩展) | 参数列表内 `,` 分隔、`-name` 排除项、`#all` 系列标记 | 从 trait 定义抄签名 / 批量填 body / 委托调用 / 覆盖式委托 | `preprocess/``parse_names_from_tokens` 独立解析,DSL 解析不进入) |
| **宏元层**`@` 常量) | `@uint`/`@scalar` 名字族、`@u8..u128` 范围族、`batch_trait!` 前导 `@name=值;` 自定义段 | 类型矩阵命名复用;词法替换为列表后走原管线,不参与任何域内解析 | `consts.rs``angle_collect` 后、指令预处理前) |

### 隔离规则


- **同记号、分域、各义**`-` 在类型域是 apply 链接(`HashMap-K-V` = `HashMap<K, V>`),
  在指令域是排除记号(`#fill(#all,-foo)`)——两域解析互不进入,语义永不冲突;
- **域边界即模块边界**:类型域解析(`parse_item` 优先级攀爬)永远不递归进入
  指令参数;指令预处理(`expand_tokens`)只展开 `#` 指令,不解释 DSL 运算符;
  `@` 常量(`consts.rs`)只做词法替换,不进入任何域;
- **透传守卫统一**`ident![...]` 宏体与 `#[...]` 属性内的内容是任意 Rust,
  三个递归入口(`angle_collect` / `expand_tokens` / `where_process`)一律不进入,
  判定收敛在 `scan::bracket_is_passthrough`(0.5.7 曾因一处守卫缺失误展开
  `#[...]` 内的 `#name` 指令)。

### 附着语义


指令展开产物分两类:**单组产物**(`#name`/`#fill`/`#delegate`/开放扩展的
`{...}` 组)可附着到类型后(`T {body}`)或独立成 spec;**多 token 产物**
(`#blanket` 的完整 spec 段)自含泛型/目标/委托,只能独立成 spec,附着
无意义。

### 扩展准则


新语法只能**在既有域内延伸既有机制**(如 `^`/`-` 系补充差集、指令域补充新
指令、宏元层补充新常量),不得跨域复用记号、不得改变既有记号的域内语义。
`@` 绑定与 `#blanket` 均遵循此准则:前者是宏元层纯词法替换,后者是指令域
内 `#delegate` 的自动化形态。

## 错误机制


所有 DSL 语法错误均通过 `compile_error!()` 输出友好的编译错误,**永不 panic**。
两层分工,不合并:

- **DSL 解析层**(parse/apply/codegen):`Ty::Error` 变体在 AST 链中透传
  (链式组合中途失败需要信号值),最终经 ToTokens 输出 `compile_error!`- **入口层**(preprocess/expand):`Result<_, TokenStream>``?` 传播,
  `diagnostic.rs::compile_error_str` 统一构造。

## 测试矩阵


四层:

| 目录        | 文件            | 用途                                                                                                                                                                         |
|-------------|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `examples/` | `quickstart.rs` | 可运行的 DSL 主特性 demo(`cargo run --example quickstart`),14 段覆盖基础→复杂场景                                                                                         |
| `src/`      | `fuzz.rs`       | proptest 属性测试:随机 token 序列喂 `where_process` / `parse_item`,验证"不因用户输入 panic"(`cargo test --lib`|
| `tests/`    | `dsl.rs`        | 34 个 `#[test]`,覆盖核心特性的语义回归(含 where 子句继承、外部路径前缀、宏调用边界、`unsafe fn` 类型、列表减法 `-``A<>` 与同名继承)                                     |
| `tests/`    | `regression.rs` | 23 个 `#[test]`,覆盖 dsl.rs 未触碰的 corner case:嵌套 `>>`、路径类型、const 泛型、生命周期、dyn + Send、路径前缀、数组/切片 builder、`batch_impl` vs `batch_trait!` 一致性 |
| `tests/`    | `ui.rs`         | `trybuild` UI 测试:23 个 `compile_fail` fixture 锁定诊断措辞 + 1 个 `pass` fixture                                                                                          |

运行:

```bash
cargo run --example quickstart       # 主特性 demo
cargo test --lib                     # 单元测试 + fuzz
cargo test --test dsl --test regression   # 功能与回归测试
cargo test --test ui                  # 诊断 UI 测试
# 重新生成 UI 快照:

TRYBUILD=overwrite cargo test --test ui
```

## 发布流程


1. `CHANGELOG.md`(用户视角)与 `docs/dev-changelog.md`(开发者视角)各记
   一条
2. `cargo package` 验证打包(docs/ 目录随 git 跟踪自动入包)
3. `cargo publish`
4. `git tag vX.Y.Z && git push origin vX.Y.Z`
5. `gh release create vX.Y.Z --notes-file <notes>`