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// The library uses no unsafe; missing docs are rejected as errors (only for pub items;
4// internal pub(crate) is exempt).
5#![forbid(unsafe_code)]
6#![deny(missing_docs)]
7// The MSVC linker prints "creating library ... and object ..." to stdout, which rustc
8// treats as linker_messages warnings; these are harmless Windows link-product notices,
9// suppressed globally.
10#![allow(linker_messages)]
11// The `delimiter!` macro is defined at the top of preprocess and imported into the crate
12// root via `#[macro_use]`; textual scope requires its declaration to precede all users
13// (fuzz / parse / this module).
14#[macro_use]
15pub(crate) mod preprocess;
16#[cfg(test)]
17mod testing;
18use proc_macro2::{TokenStream, TokenTree};
19use quote::quote;
20use syn::{ItemTrait, parse_macro_input};
21
22mod analyze;
23mod apply;
24mod ast;
25mod codegen;
26mod entry;
27mod parse;
28mod util;
29
30pub(crate) use analyze::TraitBounds;
31pub(crate) use entry::{expand_attr_macro, expand_batch_trait};
32
33use preprocess::{build_from_item, get_trait_item, parse_names_from_tokens};
34use util::compile_error_str;
35
36/// Attribute macro that generates `impl` blocks for a trait in batch.
37///
38/// Annotate a trait definition with `#[batch_impl(...)]`; every impl-spec in the macro
39/// arguments generates a corresponding `impl` block for that trait.
40///
41/// ## Syntax
42///
43/// ```text
44/// #[batch_impl( impl-spec [, impl-spec]* [{ body }]? )]
45/// ```
46///
47/// An impl-spec has three parts (the tail of each part may be omitted):
48/// - `<impl generics>` — generic params of the `impl` block
49/// - `Trait name<trait generics>` — the trait's generic args and associated type bindings
50/// - target type — wrapped in `[]` for a parallel list, `^`/`-` for generic application
51///
52/// ## Examples
53///
54/// ```
55/// # use batch_impl::batch_impl;
56/// #[batch_impl(usize, isize)]
57/// trait Numeric {}
58///
59/// #[batch_impl(<T> Vec<T>)]
60/// trait Collection {}
61///
62/// #[batch_impl(<T> FromValue<T> [i32 { fn wrap(_: T) -> Self { 0 }}, u32 #wrap{0}] )]
63/// trait FromValue<T> { fn wrap(val: T) -> Self; }
64///
65/// // #name{body} also supports const and type items
66/// #[batch_impl(usize #MY_CONST{42})]
67/// trait HasConst { const MY_CONST: usize; }
68///
69/// ```
70#[proc_macro_attribute]
71pub fn batch_impl(
72 attr: proc_macro::TokenStream, item: proc_macro::TokenStream,
73) -> proc_macro::TokenStream {
74 let trait_item = parse_macro_input!(item as ItemTrait);
75 expand_attr_macro(attr.into(), trait_item, true)
76 .map(proc_macro::TokenStream::from)
77 .unwrap_or_else(Into::into)
78}
79
80/// Same as `#[batch_impl]`, but discards the annotated trait definition and only emits
81/// `impl` blocks.
82///
83/// For traits already defined elsewhere where only batched impl generation is needed. The
84/// annotated trait merely serves as the "signature source of truth" for the directive system:
85/// `#name`/`#fill`/`#delegate` read item signatures from it, and the open extension
86/// `#name(args){body}` hands (method name list, body, the whole trait) to the user's
87/// same-named function-like macro (see README "Directive System"). The syntax is identical
88/// to `#[batch_impl]`.
89///
90/// ## Examples
91///
92/// ```
93/// # use batch_impl::batch_impl_only;
94/// trait Greet { fn hello(&self) -> &str; }
95///
96/// #[batch_impl_only(usize #hello{"hi"})]
97/// trait Greet { fn hello(&self) -> &str; } // this trait definition is dropped, existing definitions are unaffected
98/// // Written with batch_impl_only instead of batch_trait to use the directive system; write it verbatim at the trait definition site
99/// ```
100#[proc_macro_attribute]
101pub fn batch_impl_only(
102 attr: proc_macro::TokenStream, item: proc_macro::TokenStream,
103) -> proc_macro::TokenStream {
104 let trait_item = parse_macro_input!(item as ItemTrait);
105 expand_attr_macro(attr.into(), trait_item, false)
106 .map(proc_macro::TokenStream::from)
107 .unwrap_or_else(Into::into)
108}
109
110/// Function-like macro that generates `impl` blocks for a declared trait in batch.
111///
112/// Syntax: `unsafe? Trait path: impl-specs;`, with `;` separating multiple trait segments.
113/// After each segment's `:` comes a DSL expression (type DSL + `@` constants, same as
114/// `#[batch_impl]`).
115///
116/// **`#` directives are not supported** (`#fill`/`#delegate`/`#blanket`/open extension):
117/// directives need the trait definition as the signature source of truth, which `batch_trait!`
118/// as a function-like macro cannot access; use `#[batch_impl]` / `#[batch_impl_only]` when
119/// you need directives.
120///
121/// ## Examples
122///
123/// ```
124/// # use batch_impl::batch_trait;
125/// trait A {}
126/// trait B<T> {}
127/// unsafe trait UnsafeTrait{}
128///
129/// batch_trait!(
130/// A: usize, isize;
131/// B: <T> B<T> Vec<T>;
132/// unsafe UnsafeTrait: usize
133/// );
134/// ```
135///
136/// Path traits (such as `foo::C`) are supported too; see tests/regression.rs.
137#[proc_macro]
138pub fn batch_trait(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
139 expand_batch_trait(input).unwrap_or_else(Into::into)
140}
141
142/// Test-only open-extension macro (function-like): `name!{ {spec}(method name list){body} trait T {...} }`.
143///
144/// Parses the spec body (first Brace group — the target type), the method name list,
145/// the body, and the trait definition from the macro input. In the **top-level form**
146/// (4 segments) it emits a full `impl Trait for {spec}`; in the legacy in-impl form
147/// (3 segments, no spec group) it emits `fn signature { body }` per method (reusing
148/// the trait signature) — equivalent to handing the `#fill` implementation to the user.
149///
150/// Used to verify open instruction extension: `#name(args){body}` expands to
151/// `{ ! name!{(args){body} trait ...} }`, the `!` marking top-level emission —
152/// codegen prepends the spec body and emits the call at top level, where the user
153/// macro generates arbitrary items (typically its own impl)
154/// (see section 28 of `tests/dsl.rs`).
155///
156/// Design point: this must be a **function-like macro call** `name!{...}`, not an
157/// `#[name[...]] trait ...` attribute — a trait is not a valid item inside an impl block
158/// (`#[attr] trait` cannot appear in an impl), whereas a function-like macro in an impl
159/// body position is expanded by rustc into associated items.
160#[doc(hidden)]
161#[proc_macro]
162pub fn batch_preprocess_test(
163 input: proc_macro::TokenStream,
164) -> proc_macro::TokenStream {
165 let tokens = TokenStream::from(input).into_iter().collect::<Vec<_>>();
166 let tokens = match preprocess::angle_collect(&tokens) {
167 Ok(v) => v,
168 Err(e) => return e.into(),
169 };
170 // Shape: `{spec}(method name list){body} trait ...` (top-level form —
171 // the first Brace group is the spec body; the macro emits a full impl
172 // for it) or the legacy `(method name list){body} trait ...` (in-impl
173 // form — emits associated fn definitions for the enclosing impl).
174 let spec = match tokens.first() {
175 Some(TokenTree::Group(g))
176 if g.delimiter() == delimiter![{}]
177 && matches!(
178 tokens.get(1),
179 Some(TokenTree::Group(p)) if p.delimiter() == delimiter![()]
180 ) =>
181 {
182 Some(g.stream())
183 }
184 _ => None,
185 };
186 let idx = if spec.is_some() { 1 } else { 0 };
187 let Some(TokenTree::Group(names_group)) = tokens.get(idx) else {
188 return compile_error_str(
189 "batch-impl: batch_preprocess_test expects `(method name list){body} trait ...`",
190 tokens
191 .first()
192 .map(|t| t.span())
193 .unwrap_or_else(proc_macro2::Span::call_site),
194 )
195 .into();
196 };
197 if names_group.delimiter() != delimiter![()] {
198 return compile_error_str(
199 "batch-impl: batch_preprocess_test expects `(method name list){body} trait ...`",
200 tokens
201 .first()
202 .map(|t| t.span())
203 .unwrap_or_else(proc_macro2::Span::call_site),
204 )
205 .into();
206 }
207 let Some(TokenTree::Group(body_group)) = tokens.get(idx + 1) else {
208 return compile_error_str(
209 "batch-impl: batch_preprocess_test expects `(method name list){body} trait ...`",
210 tokens
211 .get(1)
212 .map(|t| t.span())
213 .unwrap_or_else(proc_macro2::Span::call_site),
214 )
215 .into();
216 };
217 if body_group.delimiter() != delimiter![{}] {
218 return compile_error_str(
219 "batch-impl: batch_preprocess_test expects `(method name list){body} trait ...`",
220 tokens
221 .get(1)
222 .map(|t| t.span())
223 .unwrap_or_else(proc_macro2::Span::call_site),
224 )
225 .into();
226 }
227 let trait_ts = tokens[idx + 2..].iter().cloned().collect();
228 let trait_item = match syn::parse2(trait_ts) {
229 Ok(t) => t,
230 Err(_) => {
231 return compile_error_str(
232 "batch-impl: batch_preprocess_test cannot parse the trait definition",
233 proc_macro2::Span::call_site(),
234 )
235 .into();
236 }
237 };
238 let names = match parse_names_from_tokens(
239 &names_group.stream().into_iter().collect::<Vec<_>>(),
240 &trait_item,
241 ) {
242 Ok(names) => names,
243 Err(e) => return e.into(),
244 };
245 let body = body_group.stream();
246 let mut methods = TokenStream::new();
247 for name in &names {
248 let item = match get_trait_item(&trait_item, name) {
249 Ok(item) => item,
250 Err(e) => return e.into(),
251 };
252 methods.extend(build_from_item(item, &body));
253 }
254 match spec {
255 // Top-level form: emit a full impl for the spec body (`{spec}` first
256 // segment) — the batch_impl crate emits no impl in this mode.
257 Some(spec_ts) => {
258 let ident = &trait_item.ident;
259 preprocess::render_angles(quote!(impl #ident for #spec_ts { #methods }))
260 .into()
261 }
262 None => preprocess::render_angles(methods).into(),
263 }
264}
265
266// ============================================================
267// Documentation placeholders for the DSL directive / macro-meta layers.
268//
269// The `#` directives and `@` constants live inside macro arguments, so IDE
270// hover and docs.rs cannot reach them. Each placeholder below is a public
271// no-op function whose doc block documents one directive — a hoverable,
272// searchable rustdoc entry. Never call these functions.
273// ============================================================
274
275/// Documentation placeholder for the `#delegate` directive.
276///
277/// `#delegate(args){target}` generates one delegation call per selected
278/// method: each becomes `fn m(&self, ...) -> R { (target).m(...) }`. The
279/// `self` argument is skipped; the remaining arguments are forwarded (named
280/// params as-is, non-identifier patterns renamed to `arg{i}` when they
281/// cannot be used as an expression).
282///
283/// ```
284/// # use batch_impl::batch_impl;
285/// #[batch_impl(
286/// Vec<u32> #d_len{self.len()},
287/// Box<Vec<u32>> #delegate(d_len){**self}
288/// )]
289/// trait MyLen { fn d_len(&self) -> usize; }
290/// # fn main() {}
291/// ```
292///
293/// **Documentation marker only — never call this function.**
294#[proc_macro]
295pub fn batch_impl_delegate(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
296 proc_macro::TokenStream::new()
297}
298
299/// Documentation placeholder for the `#fill` directive.
300///
301/// `#fill(args){body}` copies each selected trait item's signature and
302/// substitutes `body` as its implementation. Selection supports the `@all`
303/// families (`@all_methods`, `@all_ref_methods`, `@all_default_methods`,
304/// ...), individual names, and `-` subtraction (`#fill(@all_methods, -foo)`).
305///
306/// ```
307/// # use batch_impl::batch_impl;
308/// #[batch_impl(Vec<u32> #fill(@all_methods){0})]
309/// trait F { fn zero(&self) -> u32; }
310/// # fn main() {}
311/// ```
312///
313/// **Documentation marker only — never call this function.**
314#[proc_macro]
315pub fn batch_impl_fill(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
316 proc_macro::TokenStream::new()
317}
318
319/// Documentation placeholder for the `#blanket` directive.
320///
321/// `#blanket(args){wrapper list}` implements the trait for every wrapper
322/// around a fresh generic `T`, delegating each method by deref. Wrappers may
323/// carry a `:N` deref-depth annotation and a `where{...}` predicate; a
324/// wrapper whose main part contains `@0` treats `@0` as T's position
325/// (`(u32, @0)` → `(u32, T)`), otherwise it is applied as `wrapper^T`.
326///
327/// ```
328/// # use batch_impl::batch_impl;
329/// #[batch_impl(#blanket(@all_methods){Box})]
330/// trait B { fn tag(&self) -> u32; }
331/// # fn main() {}
332/// ```
333///
334/// **Documentation marker only — never call this function.**
335#[proc_macro]
336pub fn batch_impl_blanket(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
337 proc_macro::TokenStream::new()
338}
339
340/// Documentation placeholder for the `#name{body}` fill-by-name directive.
341///
342/// `#name{body}` looks up the single trait item named `name` — a method, an
343/// associated const, or an associated type — and fills it with `body` (the
344/// body must match that item's shape).
345///
346/// ```
347/// # use batch_impl::batch_impl;
348/// #[batch_impl(Box<Vec<u32>> #count{self.len()})]
349/// trait L { fn count(&self) -> usize; }
350/// # fn main() {}
351/// ```
352///
353/// **Documentation marker only — never call this function.**
354#[proc_macro]
355pub fn batch_impl_name(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
356 proc_macro::TokenStream::new()
357}
358
359/// Documentation placeholder for the open-extension protocol.
360///
361/// A `#name(args){body}` whose `name` is not a built-in directive expands to
362/// a call of a user-defined function-like macro of the same name, handed the
363/// args, body and trait definition:
364/// `#my_ext(x){y}` → `{ my_ext!{ (x) {y} trait_def } }`.
365///
366/// ```
367/// # use batch_impl::batch_impl;
368/// macro_rules! my_ext { ($($rest:tt)*) => {}; }
369/// #[batch_impl(Box<u32> #my_ext(x){y})]
370/// trait O {}
371/// # fn main() {}
372/// ```
373///
374/// **Documentation marker only — never call this function.**
375#[proc_macro]
376pub fn batch_impl_open(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
377 proc_macro::TokenStream::new()
378}
379
380/// Documentation placeholder for the `@` macro-meta constant system.
381///
382/// `@` names expand before all other DSL processing (`@ <> # where` order):
383/// - built-in name families: `@uint` / `@int` / `@float` / `@num` /
384/// `@scalar` and wildcards `@u*` / `@i*` / `@f*`;
385/// - range families: `@u8..u128` / `@i8..i128` / `@f32..f64` (inclusive);
386/// - `batch_trait!` user constants: a leading `@name = value;` segment
387/// (lazy expansion, reference checks);
388/// - `@N` position references (resolved by codegen) and `@trait`
389/// (segment-level trait path).
390///
391/// ```
392/// # use batch_impl::batch_impl;
393/// #[batch_impl(Box^@u*)]
394/// trait C {}
395/// # fn main() {}
396/// ```
397///
398/// **Documentation marker only — never call this function.**
399#[proc_macro]
400pub fn batch_impl_consts(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
401 proc_macro::TokenStream::new()
402}