Skip to main content

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