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}