Skip to main content

batch_impl/
lib.rs

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