batch_impl/lib.rs
1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
2#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/tutorial.md"))]
3// 库不使用任何 unsafe;缺失文档按错误拒绝(仅作用于 pub 项,内部 pub(crate) 不受限)。
4#![forbid(unsafe_code)]
5#![deny(missing_docs)]
6// MSVC 链接器输出"正在创建库…和对象…"到 stdout,被 rustc 当 linker_messages 告警,
7// 属于无害的 Windows 链接产物提示,全局抑制。
8#![allow(linker_messages)]
9// `delimiter!` 宏定义在 preprocess 顶部,经 `#[macro_use]` 导入 crate 根;
10// 文本作用域要求其声明先于所有使用者(fuzz / parse / 本模块)。
11#[macro_use]
12pub(crate) mod preprocess;
13#[cfg(test)]
14mod testing;
15use proc_macro2::{TokenStream, TokenTree};
16use syn::{ItemTrait, parse_macro_input};
17
18mod analyze;
19mod apply;
20mod ast;
21mod codegen;
22mod entry;
23mod parse;
24mod util;
25
26pub(crate) use analyze::TraitBounds;
27pub(crate) use entry::{expand_attr_macro, expand_batch_trait};
28
29use preprocess::{build_from_item, get_trait_item, parse_names_from_tokens};
30use util::compile_error_str;
31
32/// 为 trait 批量生成 `impl` 块的属性宏。
33///
34/// 在 trait 定义上标注 `#[batch_impl(...)]`,宏参数中的每个 impl-spec 都会
35/// 为该 trait 生成一个对应的 `impl` 块。
36///
37/// ## 语法
38///
39/// ```text
40/// #[batch_impl( impl-spec [, impl-spec]* [{ body }]? )]
41/// ```
42///
43/// impl-spec 由三部分组成(均可省略后半部分):
44/// - `<impl-泛型>` — `impl` 块的泛型参数
45/// - `Trait名<trait-泛型>` — trait 的泛型参数与关联类型绑定
46/// - 目标类型 — 用 `[]` 包裹表示并列,用 `^`/`-` 表示泛型应用
47///
48/// ## 示例
49///
50/// ```
51/// # use batch_impl::batch_impl;
52/// #[batch_impl(usize, isize)]
53/// trait Numeric {}
54///
55/// #[batch_impl(<T> Vec<T>)]
56/// trait Collection {}
57///
58/// #[batch_impl(<T> FromValue<T> [i32 { fn wrap(_: T) -> Self { 0 }}, u32 #wrap{0}] )]
59/// trait FromValue<T> { fn wrap(val: T) -> Self; }
60///
61/// // #name{body} 也支持 const 和 type 项
62/// #[batch_impl(usize #MY_CONST{42})]
63/// trait HasConst { const MY_CONST: usize; }
64///
65/// ```
66#[proc_macro_attribute]
67pub fn batch_impl(
68 attr: proc_macro::TokenStream, item: proc_macro::TokenStream,
69) -> proc_macro::TokenStream {
70 let trait_item = parse_macro_input!(item as ItemTrait);
71 expand_attr_macro(attr.into(), trait_item, true)
72 .map(proc_macro::TokenStream::from)
73 .unwrap_or_else(Into::into)
74}
75
76/// 与 `#[batch_impl]` 相同,但丢弃被标注的 trait 定义,只输出 `impl` 块。
77///
78/// 用于 trait 已在别处定义、只需批量生成 impl 的场景。被标注的 trait 仅作为
79/// 指令系统的"签名真相源":`#name`/`#fill`/`#delegate` 从它读取 item 签名,
80/// 开放扩展 `#name(args){body}` 把(方法名列表, body, 整个 trait)一起交给
81/// 用户的同名函数式宏(见 README「指令系统」)。语法与 `#[batch_impl]` 完全一致。
82///
83/// ## 示例
84///
85/// ```
86/// # use batch_impl::batch_impl_only;
87/// trait Greet { fn hello(&self) -> &str; }
88///
89/// #[batch_impl_only(usize #hello{"hi"})]
90/// trait Greet { fn hello(&self) -> &str; } // 此 trait 定义被丢弃,不影响已有的定义
91/// // 这样写而不用batch_trait是为了使用指令系统,建议按trait定义处按原样写
92/// ```
93#[proc_macro_attribute]
94pub fn batch_impl_only(
95 attr: proc_macro::TokenStream, item: proc_macro::TokenStream,
96) -> proc_macro::TokenStream {
97 let trait_item = parse_macro_input!(item as ItemTrait);
98 expand_attr_macro(attr.into(), trait_item, false)
99 .map(proc_macro::TokenStream::from)
100 .unwrap_or_else(Into::into)
101}
102
103/// 对已声明的 trait 批量生成 `impl` 块的函数式宏。
104///
105/// 语法:`unsafe? Trait路径: impl-specs;`,以 `;` 分隔多个 trait 段。
106/// 每段的 `:` 之后是 DSL 表达式(类型 DSL + `@` 常量,与 `#[batch_impl]` 相同)。
107///
108/// **不支持 `#` 指令**(`#fill`/`#delegate`/`#blanket`/开放扩展):指令需要
109/// trait 定义作签名真相源,`batch_trait!` 作为函数式宏拿不到定义;需要指令时
110/// 请用 `#[batch_impl]` / `#[batch_impl_only]`。
111///
112/// ## 示例
113///
114/// ```
115/// # use batch_impl::batch_trait;
116/// trait A {}
117/// trait B<T> {}
118/// unsafe trait UnsafeTrait{}
119///
120/// batch_trait!(
121/// A: usize, isize;
122/// B: <T> B<T> Vec<T>;
123/// unsafe UnsafeTrait: usize
124/// );
125/// ```
126///
127/// 路径 trait(如 `foo::C`)同样支持,见 tests/regression.rs。
128#[proc_macro]
129pub fn batch_trait(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
130 expand_batch_trait(input).unwrap_or_else(Into::into)
131}
132
133/// 测试用开放扩展宏(函数式):`name!{(方法名列表){body} trait T {...}}`。
134///
135/// 从宏输入解析方法名列表、body 与 trait 定义,为每个方法生成
136/// `fn 签名 { body }`(沿用 trait 签名)——等价于把 `#fill` 的实现交给用户。
137///
138/// 用于验证开放指令扩展:`#name(args){body}` 展开为 `{name!{(args){body} trait ...}}`,
139/// 宏调用落在 impl body 中,由用户宏根据 trait 展开为需要的 fn 定义
140/// (见 `tests/dsl.rs` 第 28 节)。
141///
142/// 设计要点:这里必须是**函数式宏调用** `name!{...}`,不能是 `#[name[...]] trait ...`
143/// 属性——trait 不是 impl 块内的合法项(`#[attr] trait` 无法出现在 impl 中),
144/// 而函数式宏在 impl body 位置会被 rustc 展开成关联项。
145#[doc(hidden)]
146#[proc_macro]
147pub fn batch_preprocess_test(
148 input: proc_macro::TokenStream,
149) -> proc_macro::TokenStream {
150 let tokens = TokenStream::from(input).into_iter().collect::<Vec<_>>();
151 let tokens = match preprocess::angle_collect(&tokens) {
152 Ok(v) => v,
153 Err(e) => return e.into(),
154 };
155 // 形如:`(add, inc) {*self+1} trait AddInc {...}`
156 let Some(TokenTree::Group(names_group)) = tokens.first() else {
157 return compile_error_str(
158 "batch-impl: batch_preprocess_test 期望 `(方法名列表){body} trait ...`",
159 )
160 .into();
161 };
162 if names_group.delimiter() != delimiter![()] {
163 return compile_error_str(
164 "batch-impl: batch_preprocess_test 期望 `(方法名列表){body} trait ...`",
165 )
166 .into();
167 }
168 let Some(TokenTree::Group(body_group)) = tokens.get(1) else {
169 return compile_error_str(
170 "batch-impl: batch_preprocess_test 期望 `(方法名列表){body} trait ...`",
171 )
172 .into();
173 };
174 if body_group.delimiter() != delimiter![{}] {
175 return compile_error_str(
176 "batch-impl: batch_preprocess_test 期望 `(方法名列表){body} trait ...`",
177 )
178 .into();
179 }
180 let trait_ts = tokens[2..].iter().cloned().collect();
181 let trait_item = match syn::parse2(trait_ts) {
182 Ok(t) => t,
183 Err(_) => {
184 return compile_error_str(
185 "batch-impl: batch_preprocess_test 无法解析 trait 定义",
186 )
187 .into();
188 }
189 };
190 let names = match parse_names_from_tokens(
191 &names_group.stream().into_iter().collect::<Vec<_>>(),
192 &trait_item,
193 ) {
194 Ok(names) => names,
195 Err(e) => return e.into(),
196 };
197 let body = body_group.stream();
198 let mut methods = TokenStream::new();
199 for name in &names {
200 let item = match get_trait_item(&trait_item, name) {
201 Ok(item) => item,
202 Err(e) => return e.into(),
203 };
204 methods.extend(build_from_item(item, &body));
205 }
206 preprocess::render_angles(methods).into()
207}