conditional_trait_gen/lib.rs
1// Copyright 2023 Redglyph
2//
3// Macros and helpers. Contains procedural macros so nothing else than macros can be exported.
4
5//! # The trait_gen library
6//!
7//! This library provides an attribute macro to generate the trait implementations for several
8//! types without needing custom declarative macros, code repetition, or blanket implementations.
9//! It makes the code easier to read and to maintain.
10//!
11//! Here is a short example:
12//!
13//! ```rust
14//! # use trait_gen::trait_gen;
15//! # trait MyLog { fn my_log2(self) -> u32; }
16//! #[trait_gen(T -> u8, u16, u32, u64, u128)]
17//! impl MyLog for T {
18//! fn my_log2(self) -> u32 {
19//! T::BITS - 1 - self.leading_zeros()
20//! }
21//! }
22//! ```
23//!
24//! The `trait_gen` attribute generates the following code by replacing `T` with the types given as
25//! arguments:
26//!
27//! ```rust
28//! # trait MyLog { fn my_log2(self) -> u32; }
29//! impl MyLog for u8 {
30//! fn my_log2(self) -> u32 {
31//! u8::BITS - 1 - self.leading_zeros()
32//! }
33//! }
34//! impl MyLog for u16 {
35//! fn my_log2(self) -> u32 {
36//! u16::BITS - 1 - self.leading_zeros()
37//! }
38//! }
39//! // and so on for the remaining types
40//! ```
41//!
42//! ## Usage
43//!
44//! The attribute is placed before the pseudo-generic implementation code. The _generic argument_
45//! is given first, followed by a right arrow (`->`) and a list of type arguments.
46//!
47//! ```rust
48//! # use trait_gen::trait_gen;
49//! # struct Type1; struct Type2; struct Type3;
50//! # trait Trait {}
51//! #[trait_gen(T -> Type1, Type2, Type3)]
52//! impl Trait for T {
53//! // ...
54//! }
55//! ```
56//!
57//! The attribute macro successively substitutes the generic argument `T` in the code with
58//! the following types (`Type1`, `Type2`, `Type3`) to generate all the implementations.
59//!
60//! All the [type paths](https://doc.rust-lang.org/reference/paths.html#paths-in-types) beginning with `T`
61//! in the code have this part replaced. For example, `T::default()` generates `Type1::default()`,
62//! `Type2::default()` and so on, but `super::T` is unchanged because it belongs to another scope.
63//!
64//! The code must be compatible with all the types, or the compiler will trigger the relevant
65//! errors. For example `#[trait_gen(T -> u64, f64)]` cannot be applied to `let x: T = 0;` because `0`
66//! is not a valid floating-point literal.
67//!
68//! Finally, the actual type replaces any `${T}` occurrence in doc comments, macros, and string literals.
69//!
70//! _Notes:_
71//! - _Using the letter "T" is not mandatory; any type path will do. For example, `gen::Type` is fine
72//! too. But to make it easy to read and similar to a generic implementation, short upper-case identifiers
73//! are preferred._
74//! - _Two or more attributes can be chained to generate all the combinations._
75//! - _`trait_gen` can be used on type implementations too._
76//!
77//! For more examples, look at the [README.md](https://github.com/blueglyph/trait_gen/blob/v0.2.0/README.md)
78//! or the crate [integration tests](https://github.com/blueglyph/trait_gen/blob/v0.2.0/tests/integration.rs).
79//!
80//! ## Legacy Format
81//!
82//! The attribute used a shorter format in earlier versions, which is still supported even though it
83//! may be more confusing to read:
84//!
85//! ```rust
86//! # use trait_gen::trait_gen;
87//! # struct Type1; struct Type2; struct Type3;
88//! # trait Trait {}
89//! #[trait_gen(Type1, Type2, Type3)]
90//! impl Trait for Type1 {
91//! // ...
92//! }
93//! ```
94//!
95//! is a shortcut for the equivalent attribute with the other format:
96//!
97//! ```rust
98//! # use trait_gen::trait_gen;
99//! # struct Type1; struct Type2; struct Type3;
100//! # trait Trait {}
101//! #[trait_gen(Type1 -> Type1, Type2, Type3)]
102//! impl Trait for Type1 {
103//! // ...
104//! }
105//! ```
106//!
107//! ## Alternative Format
108//!
109//! An alternative format is also supported when the `in_format` feature is enabled:
110//!
111//! ```cargo
112//! trait-gen = { version="0.3", features=["in_format"] }
113//! ```
114//!
115//! **<u>Warning</u>: This feature is temporary, and there is no guarantee that it will be maintained.**
116//!
117//! Here, `in` is used instead of an arrow `->`, and the argument types must be between square brackets:
118//!
119//! ```rust
120//! # use trait_gen::trait_gen;
121//! # trait MyLog { fn my_log2(self) -> u32; }
122//! # #[cfg(feature = "in_format")]
123//! #[trait_gen(T in [u8, u16, u32, u64, u128])]
124//! # #[cfg(not(feature = "in_format"))]
125//! # #[trait_gen(T -> u8, u16, u32, u64, u128)]
126//! impl MyLog for T {
127//! fn my_log2(self) -> u32 {
128//! T::BITS - 1 - self.leading_zeros()
129//! }
130//! }
131//! ```
132//!
133//! Using this format issues 'deprecated' warnings that you can turn off by adding the `#![allow(deprecated)]`
134//! directive at the top of the file or by adding `#[allow(deprecated)]` where the generated code is used.
135//!
136//! ## Limitations
137//!
138//! * The procedural macro of the `trait_gen` attribute can't handle scopes, so it doesn't support any
139//! type declaration with the same literal as the generic argument. For instance, this code fails to compile
140//! because of the generic function:
141//!
142//! ```rust, compile_fail
143//! # use num::Num;
144//! # use trait_gen::trait_gen;
145//! #
146//! # trait AddMod {
147//! # type Output;
148//! # fn add_mod(self, rhs: Self, modulo: Self) -> Self::Output;
149//! # }
150//! #[trait_gen(T -> u64, i64, u32, i32)]
151//! impl AddMod for T {
152//! type Output = T;
153//!
154//! fn add_mod(self, rhs: Self, modulo: Self) -> Self::Output {
155//! fn int_mod<T: Num> (a: T, m: T) -> T { // <== ERROR, conflicting 'T'
156//! a % m
157//! }
158//! int_mod(self + rhs, modulo)
159//! }
160//! }
161//! ```
162//!
163//! * The generic argument must be a [type path](https://doc.rust-lang.org/reference/paths.html#paths-in-types);
164//! it cannot be a more complex type like a reference or a slice. So you can use `gen::T<U> -> ...`
165//! but not `&T -> ...`.
166
167mod tests;
168
169use proc_macro::TokenStream;
170use proc_macro2::Ident;
171use proc_macro_error::{abort, proc_macro_error};
172use quote::__private::ext::RepToTokensExt;
173use quote::{quote, ToTokens};
174use std::fmt::{Display, Formatter};
175use syn::parse::{Parse, ParseStream};
176use syn::punctuated::Punctuated;
177use syn::spanned::Spanned;
178use syn::token::Colon2;
179use syn::visit_mut::VisitMut;
180use syn::{
181 bracketed, parenthesized, parse, parse2, parse_macro_input, parse_str, Attribute, Error, Expr,
182 ExprLit, File, GenericArgument, GenericParam, Generics, ImplItem, ImplItemMethod, ItemImpl,
183 Lit, LitStr, Macro, Path, PathArguments, PathSegment, Token, Type, TypePath,
184};
185
186const VERBOSE: bool = false;
187const VERBOSE_TF: bool = false;
188
189//==============================================================================
190// Main substitution types and their trait implementations
191
192#[derive(Debug, PartialEq, Eq)]
193/// Substitution item, either a Path (`super::Type`) or a Type (`&mut Type`)
194enum SubstType {
195 Path(Path),
196 Type(Type),
197}
198
199impl ToTokens for SubstType {
200 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
201 match self {
202 SubstType::Path(path) => path.to_tokens(tokens),
203 SubstType::Type(ty) => ty.to_tokens(tokens),
204 }
205 }
206}
207
208#[derive(Debug)]
209/// Attribute substitution data used to replace the generic argument in `generic_arg` with the
210/// types in `new_types`.
211struct Subst {
212 /// generic argument to replace
213 generic_arg: Path,
214 /// types that replace the generic argument
215 new_types: Vec<SubstType>,
216 /// legacy format if true
217 legacy: bool,
218 /// format `T in [...]` if true
219 in_format: bool,
220 /// Path substitution items if true, Type items if false
221 is_path: bool,
222 /// Context stack, cannot substitue paths when last is false (can substitute if empty)
223 can_subst_path: Vec<bool>,
224}
225
226#[derive(Debug)]
227/// Attribute data used to substitute arguments in inner `trait_gen` attributes
228struct AttrParams {
229 /// generic argument to replace
230 generic_arg: Path,
231 /// types that replace the generic argument
232 new_types: Vec<Type>,
233 /// legacy format if true
234 legacy: bool,
235}
236
237impl Subst {
238 fn can_subst_path(&self) -> bool {
239 *self.can_subst_path.last().unwrap_or(&true)
240 }
241}
242
243impl Display for Subst {
244 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
245 write!(f, "PathTypes {{\n current_types: {}\n new_types: {}\n current_defined: {}\n enabled: {}\n}}",
246 pathname(&self.generic_arg),
247 self.new_types.iter().map(|t| pathname(t)).collect::<Vec<_>>().join(", "),
248 self.legacy.to_string(),
249 self.can_subst_path.iter().map(|e| e.to_string()).collect::<Vec<_>>().join(", ")
250 )
251 }
252}
253
254//==============================================================================
255// Helper functions and traits
256
257fn pathname<T: ToTokens>(path: &T) -> String {
258 path.to_token_stream()
259 .to_string()
260 .replace(" :: ", "::")
261 .replace(" <", "<")
262 .replace("< ", "<")
263 .replace(" >", ">")
264 .replace("> ", ">")
265 .replace("& ", "&")
266 .replace(", ", ",")
267 .replace(") ", ")")
268 .replace(" ;", ";")
269 .replace("; ", ";")
270}
271
272trait NodeMatch {
273 /// Checks if the `self` node is a prefix of `other`.
274 fn match_prefix(&self, other: &Self) -> bool;
275}
276
277impl NodeMatch for GenericArgument {
278 /// Compares both generic arguments, disregarding lifetime argument names
279 fn match_prefix(&self, other: &Self) -> bool {
280 if let GenericArgument::Lifetime(_) = self {
281 // ignoring the actual lifetime ident
282 matches!(other, GenericArgument::Lifetime(_))
283 } else {
284 self == other
285 }
286 }
287}
288
289impl NodeMatch for PathSegment {
290 /// Compares both segments and returns true if `self` is similar to `seg_pat`, disregarding
291 /// * any "turbofish" difference when there are angle bracket arguments
292 /// * the arguments if `seg_pat` doesn't have any
293 fn match_prefix(&self, seg_pat: &PathSegment) -> bool {
294 self.ident == seg_pat.ident
295 && match &seg_pat.arguments {
296 PathArguments::None => true, //matches!(seg_pat.arguments, PathArguments::None),
297 PathArguments::AngleBracketed(ab_pat) => {
298 if let PathArguments::AngleBracketed(ab) = &self.arguments {
299 // ignoring turbofish in colon2_token
300 ab.args.len() == ab_pat.args.len()
301 && ab
302 .args
303 .iter()
304 .zip(&ab_pat.args)
305 .all(|(a, b)| a.match_prefix(b))
306 } else {
307 false
308 }
309 }
310 PathArguments::Parenthesized(p_pat) => {
311 if let PathArguments::Parenthesized(p) = &self.arguments {
312 p == p_pat
313 } else {
314 false
315 }
316 }
317 }
318 }
319}
320
321/// Compares two type paths and, if `prefix` is a prefix of `full_path`, returns the number of
322/// matching segments.
323fn path_prefix_len(prefix: &Path, full_path: &Path) -> Option<usize> {
324 // if VERBOSE { println!("- path_prefix_len(prefix: {}, full: {})", pathname(prefix), pathname(full_path)); }
325 let prefix_len = prefix.segments.len();
326 if full_path.leading_colon == prefix.leading_colon && full_path.segments.len() >= prefix_len {
327 for (seg_full, seg_prefix) in full_path.segments.iter().zip(&prefix.segments) {
328 if !seg_full.match_prefix(seg_prefix) {
329 // if VERBOSE { print!(" - {:?} != {:?} ", pathname(seg_prefix), pathname(seg_full)); }
330 return None;
331 } else {
332 // if VERBOSE { print!(" - {:?} ~= {:?} ", pathname(seg_prefix), pathname(seg_full)); }
333 }
334 }
335 return Some(prefix_len);
336 }
337 None
338}
339
340/// Replaces the pattern `pat` with `repl` in `string`. Returns `Some(resulting string)` if
341/// the string changed, None if there was no replacement.
342fn replace_str(string: &str, pat: &str, repl: &str) -> Option<String> {
343 if string.contains(pat) {
344 Some(string.replace(pat, repl))
345 } else {
346 None
347 }
348}
349
350#[derive(Debug)]
351struct WhenArgs {
352 for_type: Type,
353 rename: Ident,
354}
355
356impl WhenArgs {
357 fn subst_type(&self) -> SubstType {
358 match &self.for_type {
359 Type::Path(p) => SubstType::Path(p.path.clone()),
360 _ => SubstType::Type(self.for_type.clone()),
361 }
362 }
363}
364
365impl Parse for WhenArgs {
366 fn parse(input: ParseStream) -> syn::Result<Self> {
367 let for_type = input.parse()?;
368 input.parse::<Token![->]>()?;
369 let rename = input.parse()?;
370 Ok(WhenArgs { for_type, rename })
371 }
372}
373
374//==============================================================================
375// Main substitution code
376
377impl VisitMut for Subst {
378 fn visit_attribute_mut(&mut self, node: &mut Attribute) {
379 if let Some(PathSegment { ident, .. }) = node.path.segments.first() {
380 match ident.to_string().as_str() {
381 "doc" => {
382 if let Some(ts_str) = replace_str(
383 &node.tokens.to_string(),
384 &format!("${{{}}}", pathname(&self.generic_arg)),
385 &pathname(self.new_types.first().unwrap()),
386 ) {
387 let new_ts: proc_macro2::TokenStream = ts_str
388 .parse()
389 .expect(&format!("parsing attribute failed: {}", ts_str));
390 node.tokens = new_ts;
391 }
392 return;
393 }
394 "trait_gen" => {
395 if VERBOSE {
396 println!(
397 "#trait_gen: '{}' in {}",
398 pathname(&self.generic_arg),
399 pathname(&node.tokens)
400 );
401 }
402 let new_args = process_attr_args(self, node.tokens.clone());
403 if VERBOSE {
404 println!("=> #trait_gen: {}", pathname(&new_args));
405 }
406 node.tokens = new_args;
407 return;
408 }
409 _ => (),
410 }
411 }
412 syn::visit_mut::visit_attribute_mut(self, node);
413 }
414
415 fn visit_expr_mut(&mut self, node: &mut Expr) {
416 let mut enabled = self.can_subst_path();
417 match node {
418 // allows substitutions for the nodes below, and until a new Expr is met:
419 Expr::Call(_) => enabled = true,
420 Expr::Cast(_) => enabled = true,
421 Expr::Struct(_) => enabled = true,
422 Expr::Type(_) => enabled = true,
423
424 // 'ExprPath' is the node checking for authorization through ExprPath.path,
425 // so the current 'enabled' is preserved: (see also visit_path_mut())
426 Expr::Path(_) => { /* don't change */ }
427
428 // all other expressions in general must disable substitution:
429 _ => enabled = false,
430 };
431 self.can_subst_path.push(enabled);
432 syn::visit_mut::visit_expr_mut(self, node);
433 self.can_subst_path.pop();
434 }
435
436 fn visit_expr_lit_mut(&mut self, node: &mut ExprLit) {
437 if let Lit::Str(lit) = &node.lit {
438 // substitutes "${T}" in expression literals (not used in macros, see visit_macro_mut)
439 if let Some(ts_str) = replace_str(
440 &lit.to_token_stream().to_string(),
441 &format!("${{{}}}", pathname(&self.generic_arg)),
442 &pathname(self.new_types.first().unwrap()),
443 ) {
444 let new_lit: LitStr =
445 parse_str(&ts_str).expect(&format!("parsing LitStr failed: {}", ts_str));
446 node.lit = Lit::Str(new_lit);
447 } else {
448 syn::visit_mut::visit_expr_lit_mut(self, node);
449 }
450 }
451 }
452
453 fn visit_generics_mut(&mut self, i: &mut Generics) {
454 if let Some(segment) = self.generic_arg.segments.first() {
455 let current_ident = &segment.ident;
456 for t in i.params.iter() {
457 match &t {
458 GenericParam::Type(t) => {
459 if &t.ident == current_ident {
460 abort!(t.span(),
461 "Type '{}' is reserved for the substitution.", current_ident.to_string();
462 help = "Use another identifier for this local generic type."
463 );
464
465 // replace the 'abort!' above with this once it is stable:
466 //
467 // t.span().unwrap()
468 // .error(format!("Type '{}' is reserved for the substitution.", self.current_type.to_string()))
469 // .help("Use another identifier for this local generic type.")
470 // .emit();
471 }
472 }
473 _ => {}
474 }
475 }
476 }
477 syn::visit_mut::visit_generics_mut(self, i);
478 }
479
480 fn visit_item_impl_mut(&mut self, i: &mut ItemImpl) {
481 syn::visit_mut::visit_item_impl_mut(self, i);
482 i.items.retain_mut(|item| {
483 if let ImplItem::Method(method) = item {
484 let when = method.attrs.iter().find(|a| a.path.is_ident("when"));
485 if let Some(when) = when {
486 let args: WhenArgs = when.parse_args().unwrap();
487 if args.subst_type() == self.new_types[0] {
488 method.sig.ident = args.rename.clone();
489 true
490 } else {
491 false
492 }
493 } else {
494 true
495 }
496 } else {
497 true
498 }
499 });
500 }
501
502 fn visit_macro_mut(&mut self, node: &mut Macro) {
503 // substitutes "${T}" in macros
504 if let Some(ts_str) = replace_str(
505 &node.tokens.to_string(),
506 &format!("${{{}}}", pathname(&self.generic_arg)),
507 &pathname(self.new_types.first().unwrap()),
508 ) {
509 let new_ts: proc_macro2::TokenStream = ts_str
510 .parse()
511 .expect(&format!("parsing Macro failed: {}", ts_str));
512 node.tokens = new_ts;
513 } else {
514 syn::visit_mut::visit_macro_mut(self, node);
515 }
516 }
517
518 fn visit_path_mut(&mut self, path: &mut Path) {
519 let path_name = pathname(path);
520 let path_length = path.segments.len();
521 if let Some(length) = path_prefix_len(&self.generic_arg, path) {
522 // If U is both a constant and the generic argument, in an expression so when
523 // self.substitution_enabled() == false, we must distinguish two cases:
524 // - U::MAX must be replaced (length < path_length)
525 // - U or U.add(1) must stay
526 if length < path_length || self.can_subst_path() {
527 if VERBOSE {
528 print!("path: {} length = {}", path_name, length);
529 }
530 match self.new_types.first().unwrap() {
531 SubstType::Path(p) => {
532 let mut new_seg = p.segments.clone();
533 for seg in path.segments.iter().skip(length) {
534 new_seg.push(seg.clone());
535 }
536 // check if orphan arguments:
537 // #[trait_gen(gen::T -> mod::Name, ...) { ... gen::T<'_> ... }
538 // path = gen :: T <'_> len = 2, subst enabled
539 // new_path = mod :: Name len = 2
540 // => new_seg = mod :: Name<'_>
541 let mut nth_new_seg = new_seg.last_mut().unwrap();
542 let nth_seg = path.segments.iter().nth(length - 1).unwrap();
543 if nth_new_seg.arguments.is_empty() && !nth_seg.arguments.is_empty() {
544 nth_new_seg.arguments = nth_seg.arguments.clone();
545 }
546 path.segments = new_seg;
547 if VERBOSE {
548 println!(" -> {}", pathname(path));
549 }
550 }
551 SubstType::Type(ty) => {
552 if VERBOSE {
553 println!(
554 " -> Path '{}' cannot be substituted by type '{}'",
555 path_name,
556 pathname(ty)
557 );
558 }
559 // note: emit-warning is unstable...
560 // abort!(ty.span(), "Path '{}' cannot be substituted by type '{}'", path_name, pathname(ty));
561 }
562 }
563 } else {
564 if VERBOSE {
565 println!("disabled path: {}", path_name);
566 }
567 syn::visit_mut::visit_path_mut(self, path);
568 }
569 } else {
570 if VERBOSE {
571 println!("path: {} mismatch", path_name);
572 }
573 syn::visit_mut::visit_path_mut(self, path);
574 }
575 }
576
577 fn visit_type_mut(&mut self, node: &mut Type) {
578 if !self.is_path {
579 match node {
580 Type::Path(TypePath { path, .. }) => {
581 let path_name = pathname(path);
582 let path_length = path.segments.len();
583 if let Some(length) = path_prefix_len(&self.generic_arg, path) {
584 if length < path_length || self.can_subst_path() {
585 if VERBOSE {
586 println!("type path: {} length = {}", path_name, length);
587 }
588 *node = if let SubstType::Type(ty) = self.new_types.first().unwrap() {
589 ty.clone()
590 } else {
591 panic!("found path item instead of type in SubstType")
592 };
593 }
594 } else {
595 syn::visit_mut::visit_type_mut(self, node);
596 }
597 }
598 _ => syn::visit_mut::visit_type_mut(self, node),
599 }
600 } else {
601 syn::visit_mut::visit_type_mut(self, node);
602 }
603 }
604
605 fn visit_type_path_mut(&mut self, typepath: &mut TypePath) {
606 self.can_subst_path.push(true);
607 let TypePath { path, .. } = typepath;
608 if VERBOSE {
609 println!("typepath: {}", pathname(path));
610 }
611 syn::visit_mut::visit_type_path_mut(self, typepath);
612 self.can_subst_path.pop();
613 }
614}
615
616//==============================================================================
617// Attribute argument processing
618
619/// Perform substitutions in the arguments of the inner attribute if necessary.
620///
621/// `
622/// #[trait_gen(U -> i32, u32)] // <== we are processing this attribute
623/// #[trait_gen(T -> &U, &mut U)] // <== change 'U' to 'i32' and 'u32'
624/// impl Neg for T { /* .... */ }
625/// `
626fn process_attr_args(
627 subst: &mut Subst,
628 args: proc_macro2::TokenStream,
629) -> proc_macro2::TokenStream {
630 match parse2::<AttrParams>(args) {
631 Ok(mut types) => {
632 let mut output = proc_macro2::TokenStream::new();
633 if !types.legacy {
634 let gen = types.generic_arg;
635 output.extend(quote!(#gen -> ));
636 }
637 let mut first = true;
638 for ty in &mut types.new_types {
639 if !first {
640 output.extend(quote!(, ));
641 }
642 // checks if substitutions must be made in that argument:
643 subst.visit_type_mut(ty);
644
645 output.extend(quote!(#ty));
646 first = false;
647 }
648
649 // puts the parentheses back and returns the modified token stream
650 proc_macro2::Group::new(proc_macro2::Delimiter::Parenthesis, output).into_token_stream()
651 }
652 Err(err) => err.to_compile_error(),
653 }
654}
655
656/// Parses the attribute arguments, and extracts the generic argument and the types that must substitute it.
657///
658/// There are three syntaxes:
659/// - `T -> Type1, Type2, Type3`
660/// - `T in [Type1, Type2, Type3]` (when "in_format" feature is enabled)
661/// - `Type1, Type2, Type3` (legacy format)
662///
663/// Returns (path, types, legacy), where
664/// - `path` is the generic argument `T` (or `Type1` in legacy format)
665/// - `types` is a vector of parsed `Type` items: `Type1, Type2, Type3` (or `Type2, Type3` in legacy)
666/// - `legacy` is true if the legacy format is used
667/// - `in_format` is true if the `T in [Type1, Type2, Type3]` format is used
668///
669/// Note: we don't include `Type1` in `types` for the legacy format because the original stream will be copied
670/// in the generated code, so only the remaining types are requires for the substitutions.
671fn parse_parameters(input: ParseStream) -> syn::parse::Result<(Path, Vec<Type>, bool, bool)> {
672 let current_type = input.parse::<Path>()?;
673 let types: Vec<Type>;
674 let arrow_format = input.peek(Token![->]); // "T -> Type1, Type2, Type3"
675 let in_format = !arrow_format && input.peek(Token![in]); // "T in [Type1, Type2, Type3]"
676 let legacy = !arrow_format && !in_format; // "Type1, Type2, Type3"
677 if legacy {
678 input.parse::<Token![,]>()?;
679 let vars = Punctuated::<Type, Token![,]>::parse_terminated(input)?;
680 types = vars.into_iter().collect();
681 } else {
682 let vars = if cfg!(feature = "in_format") && in_format {
683 input.parse::<Token![in]>()?;
684 let content;
685 bracketed!(content in input);
686 Punctuated::<Type, Token![,]>::parse_terminated(&content.into())?
687 } else {
688 // removes the "->" and parses the arguments
689 input.parse::<Token![->]>()?;
690 Punctuated::<Type, Token![,]>::parse_terminated(input)?
691 };
692 types = vars.into_iter().collect();
693 if types.is_empty() {
694 return Err(Error::new(input.span(), "expected type"));
695 }
696 }
697 Ok((current_type, types, legacy, in_format))
698}
699
700/// Attribute parser used for inner attributes
701impl Parse for AttrParams {
702 fn parse(input: ParseStream) -> syn::Result<Self> {
703 let content;
704 parenthesized!(content in input);
705 let (current_type, types, legacy, _) = parse_parameters(&content.into())?;
706 Ok(AttrParams {
707 generic_arg: current_type,
708 new_types: types,
709 legacy,
710 })
711 }
712}
713
714/// Attribute argument parser used for the procedural macro being processed
715impl Parse for Subst {
716 fn parse(input: ParseStream) -> syn::parse::Result<Self> {
717 let (current_type, mut types, legacy, in_format) = parse_parameters(input)?;
718 let mut visitor = TurboFish;
719 for ty in types.iter_mut() {
720 visitor.visit_type_mut(ty);
721 }
722 let is_path = types.iter().all(|ty| matches!(ty, Type::Path(_)));
723 let new_types = types
724 .into_iter()
725 .map(|ty| {
726 if is_path {
727 if let Type::Path(p) = ty {
728 SubstType::Path(p.path)
729 } else {
730 panic!("this should match Type::Path: {:?}", ty)
731 }
732 } else {
733 SubstType::Type(ty)
734 }
735 })
736 .collect::<Vec<_>>();
737 Ok(Subst {
738 generic_arg: current_type,
739 new_types: new_types,
740 legacy,
741 in_format,
742 is_path,
743 can_subst_path: Vec::new(),
744 })
745 }
746}
747
748//------------------------------------------------------------------------------
749
750// This type is only used to implement the VisitMut trait.
751struct TurboFish;
752
753/// Adds the turbofish double-colon whenever possible to avoid post-substitution problems.
754///
755/// The replaced part may be an expression requiring it, or a type that doesn't require the
756/// double-colon but accepts it. Handling both cases would be complicated so we always include it.
757impl VisitMut for TurboFish {
758 fn visit_path_mut(&mut self, node: &mut Path) {
759 if VERBOSE_TF {
760 println!("TURBOF: path '{}'", pathname(node));
761 }
762 for segment in &mut node.segments {
763 if let PathArguments::AngleBracketed(generic_args) = &mut segment.arguments {
764 generic_args.colon2_token = Some(Colon2::default());
765 }
766 }
767 }
768}
769
770//==============================================================================
771
772/// Generates the attached trait implementation for all the types given in argument.
773///
774/// The attribute is placed before the pseudo-generic implementation code. The _generic argument_
775/// is given first, followed by a right arrow (`->`) and a list of type arguments.
776///
777/// ```rust
778/// # use trait_gen::trait_gen;
779/// # struct Type1; struct Type2; struct Type3;
780/// # trait Trait {}
781/// #[trait_gen(T -> Type1, Type2, Type3)]
782/// impl Trait for T {
783/// // ...
784/// }
785/// ```
786///
787/// The attribute macro successively substitutes the generic argument `T` in the code with
788/// the following types (`Type1`, `Type2`, `Type3`) to generate all the implementations.
789///
790/// All the [type paths](https://doc.rust-lang.org/reference/paths.html#paths-in-types) beginning with `T`
791/// in the code have this part replaced. For example, `T::default()` generates `Type1::default()`,
792/// `Type2::default()`, and so on, but `super::T` is unchanged because it belongs to another scope.
793///
794/// The code must be compatible with all the types, or the compiler will trigger the relevant
795/// errors. For example, `#[trait_gen(T -> u64, f64)]` cannot be applied to `let x: T = 0;`, because `0`
796/// is not a valid floating-point literal.
797///
798/// Finally, the actual type replaces any `${T}` occurrence in doc comments, macros and string literals.
799///
800/// _Notes:_
801/// - _Using the letter "T" is not mandatory; any type path will do. For example, `gen::Type` is fine
802/// too. But to make it easy to read and similar to a generic implementation, short upper-case identifiers
803/// are preferred._
804/// - _Two or more attributes can be chained to generate all the combinations._
805/// - _`trait_gen` can be used on type implementations too._
806///
807/// ## Examples
808///
809/// ```rust
810/// # use trait_gen::trait_gen;
811/// # trait MyLog { fn my_log2(self) -> u32; }
812/// #[trait_gen(T -> u8, u16, u32, u64, u128)]
813/// impl MyLog for T {
814/// /// Logarithm base 2 for `${T}`
815/// fn my_log2(self) -> u32 {
816/// T::BITS - 1 - self.leading_zeros()
817/// }
818/// }
819///
820/// #[trait_gen(T -> u8, u16, u32, u64, u128)]
821/// #[trait_gen(U -> &T, &mut T, Box<T>)]
822/// impl MyLog for U {
823/// /// Logarithm base 2 for `${U}`
824/// fn my_log2(self) -> u32 {
825/// MyLog::my_log2(*self)
826/// }
827/// }
828/// ```
829#[proc_macro_attribute]
830#[proc_macro_error]
831pub fn trait_gen(args: TokenStream, item: TokenStream) -> TokenStream {
832 let mut types = parse_macro_input!(args as Subst);
833 let warning = if types.in_format {
834 let message = format!(
835 "Use of temporary format '{} in [{}]' in #[trait_gen] macro",
836 pathname(&types.generic_arg),
837 &types
838 .new_types
839 .iter()
840 .map(|t| pathname(t))
841 .collect::<Vec<_>>()
842 .join(", "),
843 );
844 // no way to generate warnings in Rust
845 if VERBOSE || VERBOSE_TF {
846 println!("{}\nWARNING: \n{}", "=".repeat(80), message);
847 }
848 Some(message)
849 } else {
850 None
851 };
852 if VERBOSE || VERBOSE_TF {
853 println!(
854 "{}\ntrait_gen for {} -> {}: {}",
855 "=".repeat(80),
856 pathname(&types.generic_arg),
857 if types.is_path { "PATH" } else { "TYPE" },
858 &types
859 .new_types
860 .iter()
861 .map(|t| pathname(t))
862 .collect::<Vec<_>>()
863 .join(", ")
864 )
865 }
866 if VERBOSE || VERBOSE_TF {
867 println!("\n{}\n{}", item, "-".repeat(80));
868 }
869 let ast: File = syn::parse(item).unwrap();
870 let mut output = TokenStream::new();
871 if let Some(message) = warning {
872 output.extend(TokenStream::from(quote!(
873 #[deprecated = #message]
874 )));
875 }
876 while !types.new_types.is_empty() {
877 let mut modified_ast = ast.clone();
878 types.visit_file_mut(&mut modified_ast);
879 output.extend(TokenStream::from(quote!(#modified_ast)));
880 assert!(
881 types.can_subst_path.is_empty(),
882 "self.enabled has {} entries after type {}",
883 types.can_subst_path.len(),
884 pathname(types.new_types.first().unwrap())
885 );
886 types.new_types.remove(0);
887 }
888 if types.legacy {
889 output.extend(TokenStream::from(quote!(#ast)));
890 }
891 if VERBOSE {
892 println!(
893 "end trait_gen for {}\n{}",
894 pathname(&types.generic_arg),
895 "-".repeat(80)
896 );
897 }
898 if VERBOSE {
899 println!("{}\n{}", output, "=".repeat(80));
900 }
901 output
902}
903
904#[proc_macro_attribute]
905pub fn when(args: TokenStream, item: TokenStream) -> TokenStream {
906 item
907}