├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── README.md ├── src ├── full_automatic.rs ├── lib.rs ├── semi_automatic.rs └── utils.rs └── tests ├── codegen.rs ├── fail.rs ├── fail ├── custom_trait_bound_invalid.rs ├── custom_trait_bound_invalid.stderr ├── trait_bound_not_added.rs ├── trait_bound_not_added.stderr ├── tuple_impls_less_than_minimum_does_not_exists.rs └── tuple_impls_less_than_minimum_does_not_exists.stderr ├── ui.rs └── ui ├── calling_method_in_static_function.rs ├── calling_method_in_static_function.stderr ├── custom_where_clause_not_allowed.rs ├── custom_where_clause_not_allowed.stderr ├── empty_for_tuples.rs ├── empty_for_tuples.stderr ├── max_before_min_in_arguments.rs ├── max_before_min_in_arguments.stderr ├── missing_arguments_to_macro.rs ├── missing_arguments_to_macro.stderr ├── missing_paren_in_for_tuples.rs ├── missing_paren_in_for_tuples.stderr ├── no_const_value.rs ├── no_const_value.stderr ├── no_return_value.rs ├── no_return_value.stderr ├── no_type_parameter.rs ├── no_type_parameter.stderr ├── too_much_arguments_to_macro.rs ├── too_much_arguments_to_macro.stderr ├── unknown_separator.rs └── unknown_separator.stderr /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | # Run jobs when commits are pushed to 6 | # main: 7 | branches: 8 | - main 9 | pull_request: 10 | # Run jobs for any external PR that wants 11 | # to merge to main, too: 12 | branches: 13 | - main 14 | 15 | env: 16 | CARGO_TERM_COLOR: always 17 | 18 | jobs: 19 | build: 20 | name: Cargo check 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout sources 24 | uses: actions/checkout@v2 25 | 26 | - name: Install Rust stable toolchain 27 | uses: actions-rs/toolchain@v1 28 | with: 29 | profile: minimal 30 | toolchain: stable 31 | override: true 32 | 33 | - name: Rust Cache 34 | uses: Swatinem/rust-cache@v1.3.0 35 | 36 | - name: Check all features 37 | uses: actions-rs/cargo@v1.0.3 38 | with: 39 | command: check 40 | args: --all-targets 41 | 42 | fmt: 43 | name: Cargo fmt 44 | runs-on: ubuntu-latest 45 | steps: 46 | - name: Checkout sources 47 | uses: actions/checkout@v2 48 | 49 | - name: Install Rust stable toolchain 50 | uses: actions-rs/toolchain@v1 51 | with: 52 | profile: minimal 53 | toolchain: stable 54 | override: true 55 | components: rustfmt 56 | 57 | - name: Rust Cache 58 | uses: Swatinem/rust-cache@v1.3.0 59 | 60 | - name: Cargo fmt 61 | uses: actions-rs/cargo@v1.0.3 62 | with: 63 | command: fmt 64 | args: --all -- --check 65 | 66 | tests: 67 | name: Cargo test 68 | runs-on: ubuntu-latest 69 | steps: 70 | - name: Checkout sources 71 | uses: actions/checkout@v2 72 | 73 | - name: Install Rust stable toolchain 74 | uses: actions-rs/toolchain@v1 75 | with: 76 | profile: minimal 77 | toolchain: stable 78 | override: true 79 | 80 | - name: Rust Cache 81 | uses: Swatinem/rust-cache@v1.3.0 82 | 83 | - name: Cargo test 84 | uses: actions-rs/cargo@v1.0.3 85 | with: 86 | command: test 87 | args: --all-targets 88 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "impl-trait-for-tuples" 3 | version = "0.2.3" 4 | authors = ["Bastian Köcher "] 5 | edition = "2021" 6 | categories = [ "development-tools::procedural-macro-helpers" ] 7 | documentation = "https://docs.rs/impl-trait-for-tuples" 8 | repository = "https://github.com/bkchr/impl-trait-for-tuples" 9 | keywords = [ "trait", "tuple", "crate", "macro", "proc-macro" ] 10 | license = "Apache-2.0/MIT" 11 | description = """ 12 | Attribute macro to implement a trait for tuples 13 | """ 14 | readme = "./README.md" 15 | 16 | [lib] 17 | proc-macro = true 18 | 19 | [dependencies] 20 | syn = { version = "2.0.89", features = [ "full", "visit", "fold", "extra-traits" ] } 21 | quote = "1.0.37" 22 | proc-macro2 = "1.0.91" 23 | 24 | [dev-dependencies] 25 | trybuild = "1.0.101" 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # impl-trait-for-tuples 2 | 3 | [![](https://docs.rs/impl-trait-for-tuples/badge.svg)](https://docs.rs/impl-trait-for-tuples/) [![](https://img.shields.io/crates/v/impl-trait-for-tuples.svg)](https://crates.io/crates/impl-trait-for-tuples) [![](https://img.shields.io/crates/d/impl-trait-for-tuples.png)](https://crates.io/crates/impl-trait-for-tuples) 4 | 5 | Attribute macro to implement a trait for tuples 6 | 7 | * [Introduction](#introduction) 8 | * [Syntax](#syntax) 9 | * [Limitations](#limitations) 10 | * [Example](#example) 11 | * [License](#license) 12 | 13 | ## Introduction 14 | 15 | When wanting to implement a trait for combinations of tuples, Rust requires the trait to be implemented 16 | for each combination manually. With this crate you just need to place `#[impl_for_tuples(5)]` above 17 | your trait declaration (in full-automatic mode) to implement the trait for the tuple combinations 18 | `(), (T0), (T0, T1), (T0, T1, T2), (T0, T1, T2, T3), (T0, T1, T2, T3, T4, T5)`. The number of tuples is the 19 | parameter given to the attribute and can be chosen freely. 20 | 21 | This crate provides two modes full-automatic and semi-automatic. The full-automatic mode just requires 22 | the trait definition to implement the trait for the tuple combinations. While being much easier to 23 | use, it also comes with some restrictions like no associated types, no return values or no associated 24 | consts. To support these, the semi-automatic mode is provided. This mode requires a dummy implementation 25 | block of the trait that is expanded to all the tuple combinations implementations. To express the 26 | tuple access in this dummy implementation a special syntax is required `for_tuples!( #( Tuple::function(); )* )`. 27 | This would expand to `Tuple::function();` for each tuple while `Tuple` is chosen by the user and will be 28 | replaced by the corresponding tuple identifier per iteration. 29 | 30 | ## Syntax 31 | 32 | The attribute macro can be called with one `#[impl_for_tuples(5)]` or with two `#[impl_for_tuples(2, 5)]` 33 | parameters. The former instructs the macro to generate up to a tuple of five elements and the later instructs it 34 | to generate from a tuple with two element up to five elements. 35 | 36 | ### Semi-automatic syntax 37 | 38 | ```rust 39 | trait Trait { 40 | type Ret; 41 | type Arg; 42 | type FixedType; 43 | const VALUE: u32; 44 | 45 | fn test(arg: Self::Arg) -> Self::Ret; 46 | 47 | fn test_with_self(&self) -> Result<(), ()>; 48 | } 49 | 50 | #[impl_for_tuples(1, 5)] 51 | impl Trait for Tuple { 52 | // Here we expand the `Ret` and `Arg` associated types. 53 | for_tuples!( type Ret = ( #( Tuple::Ret ),* ); ); 54 | for_tuples!( type Arg = ( #( Tuple::Arg ),* ); ); 55 | for_tuples!( const VALUE: u32 = #( Tuple::VALUE )+*; ); 56 | 57 | // Here we set the `FixedType` to `u32` and add a custom where bound that forces the same 58 | // `FixedType` for all tuple types. 59 | type FixedType = u32; 60 | for_tuples!( where #( Tuple: Trait )* ); 61 | 62 | fn test(arg: Self::Arg) -> Self::Ret { 63 | for_tuples!( ( #( Tuple::test(arg.Tuple) ),* ) ) 64 | } 65 | 66 | fn test_with_self(&self) -> Result<(), ()> { 67 | for_tuples!( #( Tuple.test_with_self()?; )* ); 68 | Ok(()) 69 | } 70 | } 71 | 72 | ``` 73 | 74 | The given example shows all supported combinations of `for_tuples!`. When accessing a method from the 75 | `self` tuple instance, `self.Tuple` is the required syntax and is replaced by `self.0`, `self.1`, etc. 76 | The placeholder tuple identifer is taken from the self type given to the implementation block. So, it 77 | is up to the user to chose any valid identifier. 78 | 79 | The separator given to `#( Tuple::something() )SEPARATOR*` can be chosen from `,`, `+`, `-`, 80 | `*`, `/`, `|`, `&` or nothing for no separator. 81 | 82 | By adding the `#[tuple_types_no_default_trait_bound]` above the impl block, the macro will not add the 83 | automatic bound to the implemented trait for each tuple type. 84 | 85 | The trait bound can be customized using `#[tuple_types_custom_trait_bound(NewBound)]`. 86 | The new bound will be used instead of the impleted trait for each tuple type. 87 | 88 | ## Limitations 89 | 90 | The macro does not supports `for_tuples!` calls in a different macro, so stuff like 91 | `vec![ for_tuples!( bla ) ]` will generate invalid code. 92 | 93 | ## Example 94 | 95 | ### Full-automatic 96 | 97 | ```rust 98 | #[impl_for_tuples(5)] 99 | trait Notify { 100 | fn notify(&self); 101 | } 102 | 103 | ``` 104 | 105 | ### Semi-automatic 106 | 107 | ```rust 108 | trait Notify { 109 | fn notify(&self) -> Result<(), ()>; 110 | } 111 | 112 | #[impl_for_tuples(5)] 113 | impl Notify for TupleIdentifier { 114 | fn notify(&self) -> Result<(), ()> { 115 | for_tuples!( #( TupleIdentifier.notify()?; )* ); 116 | Ok(()) 117 | } 118 | } 119 | 120 | ``` 121 | 122 | ## License 123 | Licensed under either of 124 | * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) 125 | * [MIT license](http://opensource.org/licenses/MIT) 126 | at your option. 127 | 128 | License: Apache-2.0/MIT 129 | -------------------------------------------------------------------------------- /src/full_automatic.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of the full-automatic tuple trait implementation. 2 | //! 3 | //! The full-automatic implementation uses the trait definition to generate the implementations for 4 | //! tuples. This has some limitations, as no support for associated types, consts, return values or 5 | //! functions with a default implementation. 6 | 7 | use crate::utils::add_tuple_element_generics; 8 | 9 | use proc_macro2::{Span, TokenStream}; 10 | 11 | use std::iter::repeat; 12 | 13 | use syn::{ 14 | parse_quote, 15 | spanned::Spanned, 16 | visit::{self, Visit}, 17 | Error, FnArg, Generics, Ident, Index, ItemTrait, Pat, Result, ReturnType, Signature, TraitItem, 18 | TraitItemFn, Type, 19 | }; 20 | 21 | use quote::quote; 22 | 23 | /// Generate the full-automatic tuple implementations for a given trait definition and the given tuples. 24 | pub fn full_automatic_impl( 25 | definition: ItemTrait, 26 | tuple_elements: Vec, 27 | min: Option, 28 | ) -> Result { 29 | check_trait_declaration(&definition)?; 30 | 31 | let impls = (min.unwrap_or(0)..=tuple_elements.len()) 32 | .map(|i| generate_tuple_impl(&definition, &tuple_elements[0..i])); 33 | 34 | Ok(quote!( 35 | #definition 36 | #( #impls )* 37 | )) 38 | } 39 | 40 | fn check_trait_declaration(trait_decl: &ItemTrait) -> Result<()> { 41 | let mut visitor = CheckTraitDeclaration { errors: Vec::new() }; 42 | visit::visit_item_trait(&mut visitor, trait_decl); 43 | 44 | match visitor.errors.pop() { 45 | Some(init) => Err(visitor.errors.into_iter().fold(init, |mut old, new| { 46 | old.combine(new); 47 | old 48 | })), 49 | None => Ok(()), 50 | } 51 | } 52 | 53 | /// Checks that the given trait declaration corresponds to the expected format. 54 | struct CheckTraitDeclaration { 55 | /// Stores all errors that are found. 56 | errors: Vec, 57 | } 58 | 59 | impl CheckTraitDeclaration { 60 | fn add_error(&mut self, span: &T) { 61 | self.errors.push(Error::new(span.span(), CHECK_ERROR_MSG)); 62 | } 63 | } 64 | 65 | const CHECK_ERROR_MSG: &str = "Not supported by full-automatic tuple implementation. \ 66 | Use semi-automatic tuple implementation for more control of the implementation."; 67 | 68 | impl<'ast> Visit<'ast> for CheckTraitDeclaration { 69 | fn visit_trait_item(&mut self, ti: &'ast TraitItem) { 70 | match ti { 71 | TraitItem::Fn(f) => visit::visit_trait_item_fn(self, f), 72 | _ => self.add_error(ti), 73 | } 74 | } 75 | 76 | fn visit_return_type(&mut self, rt: &'ast ReturnType) { 77 | match rt { 78 | ReturnType::Default => {} 79 | ReturnType::Type(_, ty) => match **ty { 80 | Type::Tuple(ref tuple) if tuple.elems.is_empty() => {} 81 | _ => self.add_error(rt), 82 | }, 83 | } 84 | } 85 | } 86 | 87 | fn generate_tuple_impl(definition: &ItemTrait, tuple_elements: &[Ident]) -> TokenStream { 88 | let name = &definition.ident; 89 | let unsafety = &definition.unsafety; 90 | let generics = generate_generics(definition, tuple_elements); 91 | let ty_generics = definition.generics.split_for_impl().1; 92 | let (impl_generics, _, where_clause) = generics.split_for_impl(); 93 | let fns = definition.items.iter().filter_map(|i| match i { 94 | TraitItem::Fn(f) => Some(generate_delegate_method(f, tuple_elements)), 95 | _ => None, 96 | }); 97 | 98 | quote!( 99 | #[allow(unused)] 100 | #unsafety impl #impl_generics #name #ty_generics for ( #( #tuple_elements, )* ) #where_clause { 101 | #( #fns )* 102 | } 103 | ) 104 | } 105 | 106 | /// Collects all non-reference argument types. 107 | struct CollectNonReferenceArgTypes { 108 | result: Vec, 109 | } 110 | 111 | impl<'ast> Visit<'ast> for CollectNonReferenceArgTypes { 112 | fn visit_receiver(&mut self, _: &'ast syn::Receiver) { 113 | // Do nothing: explicitly ignore any receiver type. 114 | } 115 | fn visit_type(&mut self, ty: &'ast Type) { 116 | if !is_reference_type(ty) { 117 | self.result.push(ty.clone()); 118 | } 119 | } 120 | } 121 | 122 | fn generate_generics(definition: &ItemTrait, tuple_elements: &[Ident]) -> Generics { 123 | let mut generics = definition.generics.clone(); 124 | let name = &definition.ident; 125 | let ty_generics = definition.generics.split_for_impl().1; 126 | 127 | // Make sure that all non-reference types implement `Clone`. 128 | let mut visitor = CollectNonReferenceArgTypes { result: Vec::new() }; 129 | definition 130 | .items 131 | .iter() 132 | .for_each(|item| visit::visit_trait_item(&mut visitor, item)); 133 | visitor.result.dedup(); 134 | { 135 | let where_clause = generics.make_where_clause(); 136 | visitor 137 | .result 138 | .into_iter() 139 | .for_each(|ty| where_clause.predicates.push(parse_quote!(#ty: Clone))); 140 | } 141 | 142 | add_tuple_element_generics( 143 | tuple_elements, 144 | Some(quote!(#name #ty_generics)), 145 | &mut generics, 146 | ); 147 | 148 | generics 149 | } 150 | 151 | fn generate_delegate_method(method: &TraitItemFn, tuple_elements: &[Ident]) -> TokenStream { 152 | let name = repeat(&method.sig.ident); 153 | let self_arg = method 154 | .sig 155 | .inputs 156 | .first() 157 | .map(|a| match a { 158 | FnArg::Receiver(_) => true, 159 | _ => false, 160 | }) 161 | .unwrap_or(false); 162 | let (sig, arg_names, arg_clones) = update_signature_and_extract_arg_infos(method.sig.clone()); 163 | let arg_names_per_tuple = repeat(&arg_names); 164 | let arg_clones_per_tuple = repeat(&arg_clones); 165 | let tuple_access = tuple_elements.iter().enumerate().map(|(i, te)| { 166 | if self_arg { 167 | let index = Index::from(i); 168 | quote!(self.#index.) 169 | } else { 170 | quote!(#te::) 171 | } 172 | }); 173 | 174 | let mut res = method.clone(); 175 | res.sig = sig; 176 | res.semi_token = None; 177 | res.default = Some(parse_quote!( 178 | { #( #tuple_access #name ( #( #arg_names_per_tuple #arg_clones_per_tuple ),* ); )* } 179 | )); 180 | 181 | quote!( #res ) 182 | } 183 | 184 | /// Update `Signature` by replacing all wild card argument names with unique identifiers, collect all 185 | /// argument names and if we need to clone an argument. 186 | fn update_signature_and_extract_arg_infos( 187 | mut sig: Signature, 188 | ) -> (Signature, Vec, Vec) { 189 | let mut unique_id = 0; 190 | let mut arg_clone = Vec::new(); 191 | let mut push_arg_clone = |is_ref: bool| { 192 | if is_ref { 193 | arg_clone.push(quote!()) 194 | } else { 195 | arg_clone.push(quote!(.clone())) 196 | } 197 | }; 198 | 199 | let arg_names = sig 200 | .inputs 201 | .iter_mut() 202 | .filter_map(|a| match a { 203 | FnArg::Typed(ref mut arg) => match &mut *arg.pat { 204 | Pat::Ident(p) => { 205 | push_arg_clone(is_reference_type(&arg.ty)); 206 | Some(p.ident.clone()) 207 | } 208 | Pat::Wild(_) => { 209 | push_arg_clone(is_reference_type(&arg.ty)); 210 | let ident = Ident::new( 211 | &format!("tuple_element_unique_ident_{}", unique_id), 212 | Span::call_site(), 213 | ); 214 | unique_id += 1; 215 | let ident2 = ident.clone(); 216 | 217 | arg.pat = parse_quote!(#ident); 218 | Some(ident2) 219 | } 220 | _ => None, 221 | }, 222 | _ => None, 223 | }) 224 | .collect::>(); 225 | 226 | (sig, arg_names, arg_clone) 227 | } 228 | 229 | fn is_reference_type(ty: &Type) -> bool { 230 | match ty { 231 | Type::Reference(_) => true, 232 | _ => false, 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | [![](https://docs.rs/impl-trait-for-tuples/badge.svg)](https://docs.rs/impl-trait-for-tuples/) [![](https://img.shields.io/crates/v/impl-trait-for-tuples.svg)](https://crates.io/crates/impl-trait-for-tuples) [![](https://img.shields.io/crates/d/impl-trait-for-tuples.png)](https://crates.io/crates/impl-trait-for-tuples) 3 | 4 | Attribute macro to implement a trait for tuples 5 | 6 | * [Introduction](#introduction) 7 | * [Syntax](#syntax) 8 | * [Limitations](#limitations) 9 | * [Example](#example) 10 | * [License](#license) 11 | 12 | ## Introduction 13 | 14 | When wanting to implement a trait for combinations of tuples, Rust requires the trait to be implemented 15 | for each combination manually. With this crate you just need to place `#[impl_for_tuples(5)]` above 16 | your trait declaration (in full-automatic mode) to implement the trait for the tuple combinations 17 | `(), (T0), (T0, T1), (T0, T1, T2), (T0, T1, T2, T3), (T0, T1, T2, T3, T4, T5)`. The number of tuples is the 18 | parameter given to the attribute and can be chosen freely. 19 | 20 | This crate provides two modes full-automatic and semi-automatic. The full-automatic mode just requires 21 | the trait definition to implement the trait for the tuple combinations. While being much easier to 22 | use, it also comes with some restrictions like no associated types, no return values or no associated 23 | consts. To support these, the semi-automatic mode is provided. This mode requires a dummy implementation 24 | block of the trait that is expanded to all the tuple combinations implementations. To express the 25 | tuple access in this dummy implementation a special syntax is required `for_tuples!( #( Tuple::function(); )* )`. 26 | This would expand to `Tuple::function();` for each tuple while `Tuple` is chosen by the user and will be 27 | replaced by the corresponding tuple identifier per iteration. 28 | 29 | ## Syntax 30 | 31 | The attribute macro can be called with one `#[impl_for_tuples(5)]` or with two `#[impl_for_tuples(2, 5)]` 32 | parameters. The former instructs the macro to generate up to a tuple of five elements and the later instructs it 33 | to generate from a tuple with two element up to five elements. 34 | 35 | ### Semi-automatic syntax 36 | 37 | ``` 38 | # use impl_trait_for_tuples::impl_for_tuples; 39 | trait Trait { 40 | type Ret; 41 | type Arg; 42 | type FixedType; 43 | const VALUE: u32; 44 | 45 | fn test(arg: Self::Arg) -> Self::Ret; 46 | 47 | fn test_with_self(&self) -> Result<(), ()>; 48 | } 49 | 50 | #[impl_for_tuples(1, 5)] 51 | impl Trait for Tuple { 52 | // Here we expand the `Ret` and `Arg` associated types. 53 | for_tuples!( type Ret = ( #( Tuple::Ret ),* ); ); 54 | for_tuples!( type Arg = ( #( Tuple::Arg ),* ); ); 55 | for_tuples!( const VALUE: u32 = #( Tuple::VALUE )+*; ); 56 | 57 | // Here we set the `FixedType` to `u32` and add a custom where bound that forces the same 58 | // `FixedType` for all tuple types. 59 | type FixedType = u32; 60 | for_tuples!( where #( Tuple: Trait )* ); 61 | 62 | fn test(arg: Self::Arg) -> Self::Ret { 63 | for_tuples!( ( #( Tuple::test(arg.Tuple) ),* ) ) 64 | } 65 | 66 | fn test_with_self(&self) -> Result<(), ()> { 67 | for_tuples!( #( Tuple.test_with_self()?; )* ); 68 | Ok(()) 69 | } 70 | } 71 | 72 | # fn main() {} 73 | ``` 74 | 75 | The given example shows all supported combinations of `for_tuples!`. When accessing a method from the 76 | `self` tuple instance, `self.Tuple` is the required syntax and is replaced by `self.0`, `self.1`, etc. 77 | The placeholder tuple identifer is taken from the self type given to the implementation block. So, it 78 | is up to the user to chose any valid identifier. 79 | 80 | The separator given to `#( Tuple::something() )SEPARATOR*` can be chosen from `,`, `+`, `-`, 81 | `*`, `/`, `|`, `&` or nothing for no separator. 82 | 83 | By adding the `#[tuple_types_no_default_trait_bound]` above the impl block, the macro will not add the 84 | automatic bound to the implemented trait for each tuple type. 85 | 86 | The trait bound can be customized using `#[tuple_types_custom_trait_bound(NewBound)]`. 87 | The new bound will be used instead of the impleted trait for each tuple type. 88 | 89 | ## Limitations 90 | 91 | The macro does not supports `for_tuples!` calls in a different macro, so stuff like 92 | `vec![ for_tuples!( bla ) ]` will generate invalid code. 93 | 94 | ## Example 95 | 96 | ### Full-automatic 97 | 98 | ``` 99 | # use impl_trait_for_tuples::impl_for_tuples; 100 | #[impl_for_tuples(5)] 101 | trait Notify { 102 | fn notify(&self); 103 | } 104 | 105 | # fn main() {} 106 | ``` 107 | 108 | ### Semi-automatic 109 | 110 | ``` 111 | # use impl_trait_for_tuples::impl_for_tuples; 112 | trait Notify { 113 | fn notify(&self) -> Result<(), ()>; 114 | } 115 | 116 | #[impl_for_tuples(5)] 117 | impl Notify for TupleIdentifier { 118 | fn notify(&self) -> Result<(), ()> { 119 | for_tuples!( #( TupleIdentifier.notify()?; )* ); 120 | Ok(()) 121 | } 122 | } 123 | 124 | # fn main() {} 125 | ``` 126 | 127 | ## License 128 | Licensed under either of 129 | * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) 130 | * [MIT license](http://opensource.org/licenses/MIT) 131 | at your option. 132 | */ 133 | 134 | extern crate proc_macro; 135 | 136 | use proc_macro2::{Span, TokenStream}; 137 | 138 | use syn::{ 139 | parse::{Parse, ParseStream}, 140 | parse_macro_input, 141 | punctuated::Punctuated, 142 | token, Attribute, Error, Ident, ItemImpl, ItemTrait, LitInt, Result, 143 | }; 144 | 145 | mod full_automatic; 146 | mod semi_automatic; 147 | mod utils; 148 | 149 | /// Enum to parse the input and to distinguish between full/semi-automatic mode. 150 | enum FullOrSemiAutomatic { 151 | /// Full-automatic trait implementation for tuples uses the trait definition. 152 | Full(ItemTrait), 153 | /// Sem-automatic trait implementation for tuples uses a trait implementation. 154 | Semi(ItemImpl), 155 | } 156 | 157 | impl Parse for FullOrSemiAutomatic { 158 | fn parse(input: ParseStream) -> Result { 159 | // We need to parse any attributes first, before we know what we actually can parse. 160 | let fork = input.fork(); 161 | fork.call(Attribute::parse_outer)?; 162 | 163 | // If there is a `unsafe` in front of, just skip it. 164 | if fork.peek(token::Unsafe) { 165 | fork.parse::()?; 166 | } 167 | 168 | let lookahead1 = fork.lookahead1(); 169 | 170 | if lookahead1.peek(token::Impl) { 171 | Ok(Self::Semi(input.parse()?)) 172 | } else if lookahead1.peek(token::Trait) || lookahead1.peek(token::Pub) { 173 | Ok(Self::Full(input.parse()?)) 174 | } else { 175 | Err(lookahead1.error()) 176 | } 177 | } 178 | } 179 | 180 | /// The minimum and maximum given as two `LitInt`'s to the macro as arguments. 181 | struct MinMax { 182 | min: Option, 183 | max: usize, 184 | } 185 | 186 | impl Parse for MinMax { 187 | fn parse(input: ParseStream) -> Result { 188 | let args = Punctuated::::parse_terminated(input)?; 189 | 190 | if args.is_empty() { 191 | Err(Error::new( 192 | Span::call_site(), 193 | "Expected at least one argument to the macro!", 194 | )) 195 | } else if args.len() == 1 { 196 | Ok(Self { 197 | max: args[0].base10_parse()?, 198 | min: None, 199 | }) 200 | } else if args.len() == 2 { 201 | let min = args[0].base10_parse()?; 202 | let max = args[1].base10_parse()?; 203 | 204 | if min >= max { 205 | Err(Error::new( 206 | Span::call_site(), 207 | "It is expected that `min` comes before `max` and that `max > min` is true!", 208 | )) 209 | } else { 210 | Ok(Self { 211 | min: Some(min), 212 | max, 213 | }) 214 | } 215 | } else { 216 | Err(Error::new( 217 | Span::call_site(), 218 | "Too many arguments given to the macro!", 219 | )) 220 | } 221 | } 222 | } 223 | 224 | /// See [crate](index.html) documentation. 225 | #[proc_macro_attribute] 226 | pub fn impl_for_tuples( 227 | args: proc_macro::TokenStream, 228 | input: proc_macro::TokenStream, 229 | ) -> proc_macro::TokenStream { 230 | let input = parse_macro_input!(input as FullOrSemiAutomatic); 231 | let min_max = parse_macro_input!(args as MinMax); 232 | 233 | impl_for_tuples_impl(input, min_max) 234 | .unwrap_or_else(|e| e.to_compile_error()) 235 | .into() 236 | } 237 | 238 | fn impl_for_tuples_impl(input: FullOrSemiAutomatic, min_max: MinMax) -> Result { 239 | let tuple_elements = (0usize..min_max.max) 240 | .map(|i| generate_tuple_element_ident(i)) 241 | .collect::>(); 242 | 243 | match input { 244 | FullOrSemiAutomatic::Full(definition) => { 245 | full_automatic::full_automatic_impl(definition, tuple_elements, min_max.min) 246 | } 247 | FullOrSemiAutomatic::Semi(trait_impl) => { 248 | semi_automatic::semi_automatic_impl(trait_impl, tuple_elements, min_max.min) 249 | } 250 | } 251 | } 252 | 253 | fn generate_tuple_element_ident(num: usize) -> Ident { 254 | Ident::new(&format!("TupleElement{}", num), Span::call_site()) 255 | } 256 | -------------------------------------------------------------------------------- /src/semi_automatic.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of the semi-automatic tuple trait implementation. 2 | //! 3 | //! The semi-automatic implementation uses an implementation provided by the user to generate the 4 | //! tuple implementations. The user is able to use a special syntax `for_tuples!( #(TUPLE)* );` to 5 | //! express the tuple access while the `TUPLE` ident can be chosen by the user. 6 | 7 | use proc_macro2::TokenStream; 8 | 9 | use syn::{ 10 | bracketed, 11 | fold::{self, Fold}, 12 | parenthesized, 13 | parse::{Parse, ParseStream}, 14 | parse_quote, 15 | spanned::Spanned, 16 | token, Block, Error, Expr, ExprField, FnArg, Ident, ImplItem, ImplItemFn, Index, ItemImpl, 17 | Macro, Member, Meta, Result, Stmt, Type, WhereClause, WherePredicate, 18 | }; 19 | 20 | use quote::{quote, ToTokens}; 21 | 22 | /// By default we add the trait bound for the implemented trait to each tuple type. When this 23 | /// attribute is given we don't add this bound. 24 | const TUPLE_TYPES_NO_DEFAULT_TRAIT_BOUND: &str = "tuple_types_no_default_trait_bound"; 25 | const TUPLE_TYPES_CUSTOM_TRAIT_BOUND: &str = "tuple_types_custom_trait_bound"; 26 | 27 | /// The supported separators in the `#( Tuple::test() )SEPARATOR*` syntax. 28 | enum Separator { 29 | Comma(token::Comma), 30 | Plus(token::Plus), 31 | Minus(token::Minus), 32 | Or(token::Or), 33 | And(token::And), 34 | Star(token::Star), 35 | Slash(token::Slash), 36 | } 37 | 38 | impl Separator { 39 | /// Try to parse the separator before the `*` token. 40 | fn parse_before_star(input: ParseStream) -> Result> { 41 | if input.peek2(token::Star) { 42 | Self::parse(input).map(Some) 43 | } else { 44 | Ok(None) 45 | } 46 | } 47 | 48 | /// Convert into a `TokenStream`. 49 | /// 50 | /// `last` - Is this the last separator to add? Only `,` will be added on `last == true`. 51 | fn to_token_stream(&self, last: bool) -> TokenStream { 52 | let empty_on_last = |token: &dyn ToTokens| { 53 | if last { 54 | TokenStream::default() 55 | } else { 56 | token.to_token_stream() 57 | } 58 | }; 59 | 60 | match self { 61 | Self::Comma(comma) => comma.to_token_stream(), 62 | Self::Plus(add) => empty_on_last(add), 63 | Self::Minus(sub) => empty_on_last(sub), 64 | Self::Or(or) => empty_on_last(or), 65 | Self::And(and) => empty_on_last(and), 66 | Self::Star(star) => empty_on_last(star), 67 | Self::Slash(div) => empty_on_last(div), 68 | } 69 | } 70 | } 71 | 72 | impl Parse for Separator { 73 | fn parse(input: ParseStream) -> Result { 74 | let lookahead1 = input.lookahead1(); 75 | 76 | if lookahead1.peek(token::Comma) { 77 | Ok(Self::Comma(input.parse()?)) 78 | } else if lookahead1.peek(token::Plus) { 79 | Ok(Self::Plus(input.parse()?)) 80 | } else if lookahead1.peek(token::Minus) { 81 | Ok(Self::Minus(input.parse()?)) 82 | } else if lookahead1.peek(token::Or) { 83 | Ok(Self::Or(input.parse()?)) 84 | } else if lookahead1.peek(token::And) { 85 | Ok(Self::And(input.parse()?)) 86 | } else if lookahead1.peek(token::Star) { 87 | Ok(Self::Star(input.parse()?)) 88 | } else if lookahead1.peek(token::Slash) { 89 | Ok(Self::Slash(input.parse()?)) 90 | } else { 91 | Err(lookahead1.error()) 92 | } 93 | } 94 | } 95 | 96 | /// The different kind of repetitions that can be found in a [`TupleRepetition`]. 97 | enum Repetition { 98 | Stmts(Vec), 99 | Type(Type), 100 | Where(WherePredicate), 101 | } 102 | 103 | /// The `#( Tuple::test() )SEPARATOR*` (tuple repetition) syntax. 104 | struct TupleRepetition { 105 | pub pound_token: token::Pound, 106 | pub _paren_token: token::Paren, 107 | pub repetition: Repetition, 108 | pub separator: Option, 109 | pub _star_token: token::Star, 110 | } 111 | 112 | impl TupleRepetition { 113 | /// Parse the inner representation as stmts. 114 | fn parse_as_stmts(input: ParseStream) -> Result { 115 | let content; 116 | Ok(Self { 117 | pound_token: input.parse()?, 118 | _paren_token: parenthesized!(content in input), 119 | repetition: Repetition::Stmts(content.call(Block::parse_within)?), 120 | separator: Separator::parse_before_star(input)?, 121 | _star_token: input.parse()?, 122 | }) 123 | } 124 | 125 | /// Parse the inner representation as a where predicate. 126 | fn parse_as_where_predicate(input: ParseStream) -> Result { 127 | let content; 128 | Ok(Self { 129 | pound_token: input.parse()?, 130 | _paren_token: parenthesized!(content in input), 131 | repetition: Repetition::Where(content.parse()?), 132 | separator: Separator::parse_before_star(input)?, 133 | _star_token: input.parse()?, 134 | }) 135 | } 136 | 137 | /// Parse the inner representation as a type. 138 | fn parse_as_type(input: ParseStream) -> Result { 139 | let content; 140 | Ok(Self { 141 | pound_token: input.parse()?, 142 | _paren_token: parenthesized!(content in input), 143 | repetition: Repetition::Type(content.parse()?), 144 | separator: Separator::parse_before_star(input)?, 145 | _star_token: input.parse()?, 146 | }) 147 | } 148 | 149 | /// Expand this repetition to the actual stmts implementation. 150 | fn expand_as_stmts( 151 | self, 152 | tuple_placeholder_ident: &Ident, 153 | tuples: &[Ident], 154 | use_self: bool, 155 | ) -> Result { 156 | let mut generated = TokenStream::new(); 157 | let span = self.pound_token.span(); 158 | let stmts = match self.repetition { 159 | Repetition::Stmts(stmts) => stmts, 160 | _ => return Err(Error::new( 161 | span, 162 | "Internal error, expected `repetition` to be of type `Stmts`! Please report this issue!", 163 | )), 164 | }; 165 | 166 | for (i, tuple) in tuples.iter().enumerate() { 167 | generated.extend(stmts.iter().cloned().map(|s| { 168 | ReplaceTuplePlaceholder::replace_ident_in_stmt( 169 | tuple_placeholder_ident, 170 | tuple, 171 | use_self, 172 | i, 173 | s, 174 | ) 175 | .map(|s| s.to_token_stream()) 176 | .unwrap_or_else(|e| e.to_compile_error()) 177 | })); 178 | 179 | if let Some(ref sep) = self.separator { 180 | generated.extend(sep.to_token_stream(i + 1 == tuples.len())); 181 | } 182 | } 183 | 184 | Ok(generated) 185 | } 186 | 187 | /// Expand this repetition to the actual type declaration. 188 | fn expand_as_type_declaration( 189 | self, 190 | tuple_placeholder_ident: &Ident, 191 | tuples: &[Ident], 192 | ) -> Result { 193 | let mut generated = TokenStream::new(); 194 | let span = self.pound_token.span(); 195 | let ty = match self.repetition { 196 | Repetition::Type(ty) => ty, 197 | _ => return Err(Error::new( 198 | span, 199 | "Internal error, expected `repetition` to be of type `Type`! Please report this issue!", 200 | )), 201 | }; 202 | 203 | for (i, tuple) in tuples.iter().enumerate() { 204 | generated.extend( 205 | ReplaceTuplePlaceholder::replace_ident_in_type( 206 | tuple_placeholder_ident, 207 | tuple, 208 | ty.clone(), 209 | ) 210 | .map(|s| s.to_token_stream()) 211 | .unwrap_or_else(|e| e.to_compile_error()), 212 | ); 213 | 214 | if let Some(ref sep) = self.separator { 215 | generated.extend(sep.to_token_stream(i + 1 == tuples.len())); 216 | } 217 | } 218 | 219 | Ok(generated) 220 | } 221 | 222 | /// Expand this to the given `where_clause`. 223 | /// It is expected that the instance was created with `parse_as_where_predicate`. 224 | fn expand_to_where_clause( 225 | self, 226 | tuple_placeholder_ident: &Ident, 227 | tuples: &[Ident], 228 | where_clause: &mut WhereClause, 229 | ) -> Result<()> { 230 | let span = self.pound_token.span(); 231 | let predicate = match self.repetition { 232 | Repetition::Where(pred) => pred, 233 | _ => return Err(Error::new( 234 | span, 235 | "Internal error, expected `repetition` to be of type `Where`! Please report this issue!", 236 | )), 237 | }; 238 | 239 | for tuple in tuples.iter() { 240 | where_clause.predicates.push( 241 | ReplaceTuplePlaceholder::replace_ident_in_where_predicate( 242 | tuple_placeholder_ident, 243 | tuple, 244 | predicate.clone(), 245 | )?, 246 | ); 247 | } 248 | 249 | Ok(()) 250 | } 251 | } 252 | 253 | /// Replace the tuple place holder in the ast. 254 | struct ReplaceTuplePlaceholder<'a> { 255 | search: &'a Ident, 256 | replace: &'a Ident, 257 | use_self: bool, 258 | index: Index, 259 | errors: Vec, 260 | } 261 | 262 | impl<'a> ReplaceTuplePlaceholder<'a> { 263 | /// Replace the given `replace` ident in the given `stmt`. 264 | fn replace_ident_in_stmt( 265 | search: &'a Ident, 266 | replace: &'a Ident, 267 | use_self: bool, 268 | index: usize, 269 | stmt: Stmt, 270 | ) -> Result { 271 | let mut folder = Self { 272 | search, 273 | replace, 274 | use_self, 275 | index: index.into(), 276 | errors: Vec::new(), 277 | }; 278 | 279 | let res = fold::fold_stmt(&mut folder, stmt); 280 | 281 | if let Some(first) = folder.errors.pop() { 282 | Err(folder.errors.into_iter().fold(first, |mut e, n| { 283 | e.combine(n); 284 | e 285 | })) 286 | } else { 287 | Ok(res) 288 | } 289 | } 290 | 291 | /// Replace the given `replace` ident in the given `type_`. 292 | fn replace_ident_in_type(search: &'a Ident, replace: &'a Ident, type_: Type) -> Result { 293 | let mut folder = Self { 294 | search, 295 | replace, 296 | use_self: false, 297 | index: 0.into(), 298 | errors: Vec::new(), 299 | }; 300 | 301 | let res = fold::fold_type(&mut folder, type_); 302 | 303 | if let Some(first) = folder.errors.pop() { 304 | Err(folder.errors.into_iter().fold(first, |mut e, n| { 305 | e.combine(n); 306 | e 307 | })) 308 | } else { 309 | Ok(res) 310 | } 311 | } 312 | 313 | /// Replace the given `replace` ident in the given `where_predicate`. 314 | fn replace_ident_in_where_predicate( 315 | search: &'a Ident, 316 | replace: &'a Ident, 317 | where_predicate: WherePredicate, 318 | ) -> Result { 319 | let mut folder = Self { 320 | search, 321 | replace, 322 | use_self: false, 323 | index: 0.into(), 324 | errors: Vec::new(), 325 | }; 326 | 327 | let res = fold::fold_where_predicate(&mut folder, where_predicate); 328 | 329 | if let Some(first) = folder.errors.pop() { 330 | Err(folder.errors.into_iter().fold(first, |mut e, n| { 331 | e.combine(n); 332 | e 333 | })) 334 | } else { 335 | Ok(res) 336 | } 337 | } 338 | } 339 | 340 | impl<'a> Fold for ReplaceTuplePlaceholder<'a> { 341 | fn fold_ident(&mut self, ident: Ident) -> Ident { 342 | if &ident == self.search { 343 | self.replace.clone() 344 | } else { 345 | ident 346 | } 347 | } 348 | 349 | fn fold_expr(&mut self, expr: Expr) -> Expr { 350 | match expr { 351 | Expr::MethodCall(mut call) => match *call.receiver { 352 | Expr::Path(ref path) if path.path.is_ident(self.search) => { 353 | if self.use_self { 354 | let index = &self.index; 355 | call.receiver = parse_quote!( self.#index ); 356 | 357 | call.into() 358 | } else { 359 | self.errors.push(Error::new( 360 | path.span(), 361 | "Can not call non-static method from within a static method.", 362 | )); 363 | Expr::Verbatim(Default::default()) 364 | } 365 | } 366 | _ => fold::fold_expr_method_call(self, call).into(), 367 | }, 368 | _ => fold::fold_expr(self, expr), 369 | } 370 | } 371 | 372 | fn fold_expr_field(&mut self, mut expr: ExprField) -> ExprField { 373 | match expr.member { 374 | Member::Named(ref ident) if ident == self.search => { 375 | // Replace `something.Tuple` with `something.0`, `something.1`, etc. 376 | expr.member = Member::Unnamed(self.index.clone()); 377 | expr 378 | } 379 | _ => expr, 380 | } 381 | } 382 | } 383 | 384 | /// The expression of a const item. 385 | enum ConstExpr { 386 | /// repetition 387 | Simple { tuple_repetition: TupleRepetition }, 388 | /// &[ repetition ] 389 | RefArray { 390 | and_token: token::And, 391 | bracket_token: token::Bracket, 392 | tuple_repetition: TupleRepetition, 393 | }, 394 | } 395 | 396 | impl ConstExpr { 397 | /// Expand `self` into a [`TokenStream`]. 398 | fn expand( 399 | self, 400 | tuple_placeholder_ident: &Ident, 401 | tuples: &[Ident], 402 | use_self: bool, 403 | ) -> Result { 404 | match self { 405 | Self::Simple { tuple_repetition } => { 406 | tuple_repetition.expand_as_stmts(tuple_placeholder_ident, tuples, use_self) 407 | } 408 | Self::RefArray { 409 | and_token, 410 | bracket_token, 411 | tuple_repetition, 412 | } => { 413 | let repetition = 414 | tuple_repetition.expand_as_stmts(tuple_placeholder_ident, tuples, use_self)?; 415 | 416 | let mut token_stream = and_token.to_token_stream(); 417 | bracket_token.surround(&mut token_stream, |tokens| tokens.extend(repetition)); 418 | Ok(token_stream) 419 | } 420 | } 421 | } 422 | } 423 | 424 | impl Parse for ConstExpr { 425 | fn parse(input: ParseStream) -> Result { 426 | let lookahead1 = input.lookahead1(); 427 | 428 | if lookahead1.peek(token::And) { 429 | let content; 430 | Ok(ConstExpr::RefArray { 431 | and_token: input.parse()?, 432 | bracket_token: bracketed!(content in input), 433 | tuple_repetition: content.call(TupleRepetition::parse_as_stmts)?, 434 | }) 435 | } else if lookahead1.peek(token::Pound) { 436 | Ok(ConstExpr::Simple { 437 | tuple_repetition: TupleRepetition::parse_as_stmts(input)?, 438 | }) 439 | } else { 440 | Err(lookahead1.error()) 441 | } 442 | } 443 | } 444 | 445 | /// The `for_tuples!` macro syntax. 446 | enum ForTuplesMacro { 447 | /// The macro at an item type position. 448 | ItemType { 449 | type_token: token::Type, 450 | ident: Ident, 451 | equal_token: token::Eq, 452 | paren_token: token::Paren, 453 | tuple_repetition: TupleRepetition, 454 | semi_token: token::Semi, 455 | }, 456 | /// The macro at an item const position. 457 | ItemConst { 458 | const_token: token::Const, 459 | ident: Ident, 460 | colon_token: token::Colon, 461 | const_type: Type, 462 | equal_token: token::Eq, 463 | expr: ConstExpr, 464 | semi_token: token::Semi, 465 | }, 466 | /// The repetition stmt wrapped in parenthesis. 467 | StmtParenthesized { 468 | paren_token: token::Paren, 469 | tuple_repetition: TupleRepetition, 470 | }, 471 | /// Just the repetition stmt. 472 | Stmt { tuple_repetition: TupleRepetition }, 473 | /// A custom where clause. 474 | Where { 475 | _where_token: token::Where, 476 | tuple_repetition: TupleRepetition, 477 | }, 478 | } 479 | 480 | impl Parse for ForTuplesMacro { 481 | fn parse(input: ParseStream) -> Result { 482 | let lookahead1 = input.lookahead1(); 483 | 484 | if lookahead1.peek(token::Type) { 485 | let content; 486 | Ok(ForTuplesMacro::ItemType { 487 | type_token: input.parse()?, 488 | ident: input.parse()?, 489 | equal_token: input.parse()?, 490 | paren_token: parenthesized!(content in input), 491 | tuple_repetition: content.call(TupleRepetition::parse_as_type)?, 492 | semi_token: input.parse()?, 493 | }) 494 | } else if lookahead1.peek(token::Const) { 495 | Ok(ForTuplesMacro::ItemConst { 496 | const_token: input.parse()?, 497 | ident: input.parse()?, 498 | colon_token: input.parse()?, 499 | const_type: input.parse()?, 500 | equal_token: input.parse()?, 501 | expr: input.parse()?, 502 | semi_token: input.parse()?, 503 | }) 504 | } else if lookahead1.peek(token::Paren) { 505 | let content; 506 | Ok(ForTuplesMacro::StmtParenthesized { 507 | paren_token: parenthesized!(content in input), 508 | tuple_repetition: content.call(TupleRepetition::parse_as_stmts)?, 509 | }) 510 | } else if lookahead1.peek(token::Pound) { 511 | Ok(ForTuplesMacro::Stmt { 512 | tuple_repetition: input.call(TupleRepetition::parse_as_stmts)?, 513 | }) 514 | } else if lookahead1.peek(token::Where) { 515 | Ok(ForTuplesMacro::Where { 516 | _where_token: input.parse()?, 517 | tuple_repetition: input.call(TupleRepetition::parse_as_where_predicate)?, 518 | }) 519 | } else { 520 | Err(lookahead1.error()) 521 | } 522 | } 523 | } 524 | 525 | impl ForTuplesMacro { 526 | /// Try to parse the given macro as `Self`. 527 | /// 528 | /// `allow_where` signals that a custom where clause is allowed at this position. 529 | /// 530 | /// Returns `Ok(None)` if it is not a `for_tuples!` macro. 531 | fn try_from(macro_item: &Macro, allow_where: bool) -> Result> { 532 | // Not the macro we are searching for 533 | if !macro_item.path.is_ident("for_tuples") { 534 | return Ok(None); 535 | } 536 | 537 | let res = macro_item.parse_body::()?; 538 | 539 | if !allow_where && res.is_where() { 540 | Err(Error::new( 541 | macro_item.span(), 542 | "Custom where clause not allowed at this position!", 543 | )) 544 | } else { 545 | Ok(Some(res)) 546 | } 547 | } 548 | 549 | /// Is this a custom where clause? 550 | fn is_where(&self) -> bool { 551 | matches!(self, Self::Where { .. }) 552 | } 553 | 554 | /// Convert this into the where clause tuple repetition. 555 | fn into_where(self) -> Option { 556 | match self { 557 | Self::Where { 558 | tuple_repetition, .. 559 | } => Some(tuple_repetition), 560 | _ => None, 561 | } 562 | } 563 | 564 | /// Expand `self` to the actual implementation without the `for_tuples!` macro. 565 | /// 566 | /// This will unroll the repetition by replacing the placeholder identifier in each iteration 567 | /// with the one given in `tuples`. If `use_self` is `true`, the tuple will be access by using 568 | /// `self.x`. 569 | /// 570 | /// Returns the generated code. 571 | fn expand( 572 | self, 573 | tuple_placeholder_ident: &Ident, 574 | tuples: &[Ident], 575 | use_self: bool, 576 | ) -> TokenStream { 577 | match self { 578 | Self::ItemType { 579 | type_token, 580 | ident, 581 | equal_token, 582 | paren_token, 583 | tuple_repetition, 584 | semi_token, 585 | } => { 586 | let mut token_stream = type_token.to_token_stream(); 587 | let repetition = 588 | tuple_repetition.expand_as_type_declaration(tuple_placeholder_ident, tuples); 589 | 590 | match repetition { 591 | Ok(rep) => { 592 | ident.to_tokens(&mut token_stream); 593 | equal_token.to_tokens(&mut token_stream); 594 | paren_token.surround(&mut token_stream, |tokens| tokens.extend(rep)); 595 | semi_token.to_tokens(&mut token_stream); 596 | } 597 | Err(e) => token_stream.extend(e.to_compile_error()), 598 | } 599 | 600 | token_stream 601 | } 602 | Self::ItemConst { 603 | const_token, 604 | ident, 605 | colon_token, 606 | const_type, 607 | equal_token, 608 | expr, 609 | semi_token, 610 | } => { 611 | let mut token_stream = const_token.to_token_stream(); 612 | 613 | let expr = expr.expand(tuple_placeholder_ident, tuples, use_self); 614 | 615 | match expr { 616 | Ok(expr) => { 617 | ident.to_tokens(&mut token_stream); 618 | colon_token.to_tokens(&mut token_stream); 619 | const_type.to_tokens(&mut token_stream); 620 | equal_token.to_tokens(&mut token_stream); 621 | token_stream.extend(expr); 622 | semi_token.to_tokens(&mut token_stream); 623 | } 624 | Err(e) => token_stream.extend(e.to_compile_error()), 625 | } 626 | 627 | token_stream 628 | } 629 | Self::StmtParenthesized { 630 | paren_token, 631 | tuple_repetition, 632 | } => { 633 | let mut token_stream = TokenStream::new(); 634 | let repetition = 635 | tuple_repetition.expand_as_stmts(tuple_placeholder_ident, tuples, use_self); 636 | 637 | match repetition { 638 | Ok(rep) => paren_token.surround(&mut token_stream, |tokens| tokens.extend(rep)), 639 | Err(e) => token_stream.extend(e.to_compile_error()), 640 | } 641 | 642 | token_stream 643 | } 644 | Self::Stmt { tuple_repetition } => tuple_repetition 645 | .expand_as_stmts(tuple_placeholder_ident, tuples, use_self) 646 | .unwrap_or_else(|e| e.to_compile_error()), 647 | Self::Where { .. } => TokenStream::new(), 648 | } 649 | } 650 | } 651 | 652 | /// Add the tuple elements as generic parameters to the given trait implementation. 653 | fn add_tuple_elements_generics( 654 | tuples: &[Ident], 655 | mut trait_impl: ItemImpl, 656 | bound: Option, 657 | ) -> Result { 658 | crate::utils::add_tuple_element_generics(tuples, bound, &mut trait_impl.generics); 659 | Ok(trait_impl) 660 | } 661 | 662 | /// Fold a given trait implementation into a tuple implementation of the given trait. 663 | struct ToTupleImplementation<'a> { 664 | /// The tuple idents to use while expanding the repetitions. 665 | tuples: &'a [Ident], 666 | /// The placeholder ident given by the user. 667 | /// 668 | /// This placeholder ident while be replaced in the expansion with the correct tuple identifiers. 669 | tuple_placeholder_ident: &'a Ident, 670 | /// Any errors found while doing the conversion. 671 | errors: Vec, 672 | /// This is set to `true`, when folding in a function block that has a `self` parameter. 673 | has_self_parameter: bool, 674 | /// A custom where clause provided by the user. 675 | custom_where_clause: Option, 676 | } 677 | 678 | impl<'a> ToTupleImplementation<'a> { 679 | /// Generate the tuple implementation for the given `tuples`. 680 | fn generate_implementation( 681 | trait_impl: &ItemImpl, 682 | tuple_placeholder_ident: &'a Ident, 683 | tuples: &'a [Ident], 684 | ref_tuples: bool, 685 | ) -> Result { 686 | let mut to_tuple = ToTupleImplementation { 687 | tuples, 688 | errors: Vec::new(), 689 | tuple_placeholder_ident, 690 | has_self_parameter: false, 691 | custom_where_clause: None, 692 | }; 693 | 694 | let mut res = fold::fold_item_impl(&mut to_tuple, trait_impl.clone()); 695 | 696 | let default_trait = trait_impl.trait_.clone().map(|t| t.1).ok_or_else(|| { 697 | Error::new( 698 | trait_impl.span(), 699 | "The semi-automatic implementation is required to implement a trait!", 700 | ) 701 | })?; 702 | 703 | // Check if we should customize the trait bound 704 | let trait_ = if let Some(pos) = res 705 | .attrs 706 | .iter() 707 | .position(|a| a.path().is_ident(TUPLE_TYPES_CUSTOM_TRAIT_BOUND)) 708 | { 709 | // Parse custom trait bound 710 | let attr = &res.attrs[pos]; 711 | let Meta::List(items) = &attr.meta else { 712 | return Err(Error::new( 713 | attr.span(), 714 | "Expected #[tuple_types_custom_trait_bound($trait_bounds)]", 715 | )); 716 | }; 717 | 718 | let input = items.tokens.to_token_stream(); 719 | let result = syn::parse2::(input); 720 | let trait_name = match result { 721 | Ok(bounds) => bounds, 722 | Err(e) => { 723 | return Err(Error::new( 724 | e.span(), 725 | format!("Invalid trait bound: {}", e.to_string()), 726 | )) 727 | } 728 | }; 729 | 730 | res.attrs.remove(pos); 731 | quote! { #trait_name } 732 | } else { 733 | quote! { #default_trait } 734 | }; 735 | 736 | // Check if we should add the bound to the implemented trait for each tuple type. 737 | let add_bound = if let Some(pos) = res 738 | .attrs 739 | .iter() 740 | .position(|a| a.path().is_ident(TUPLE_TYPES_NO_DEFAULT_TRAIT_BOUND)) 741 | { 742 | res.attrs.remove(pos); 743 | None 744 | } else { 745 | Some(trait_) 746 | }; 747 | 748 | // Add the tuple generics 749 | let mut res = add_tuple_elements_generics(tuples, res, add_bound)?; 750 | // Add the correct self type 751 | if ref_tuples { 752 | res.self_ty = parse_quote!( ( #( &#tuples, )* ) ); 753 | } else { 754 | res.self_ty = parse_quote!( ( #( #tuples, )* ) ); 755 | }; 756 | res.attrs.push(parse_quote!(#[allow(unused)])); 757 | 758 | if let Some(where_clause) = to_tuple.custom_where_clause.take() { 759 | where_clause.expand_to_where_clause( 760 | tuple_placeholder_ident, 761 | tuples, 762 | res.generics.make_where_clause(), 763 | )?; 764 | } 765 | 766 | if let Some(first_error) = to_tuple.errors.pop() { 767 | Err(to_tuple.errors.into_iter().fold(first_error, |mut e, n| { 768 | e.combine(n); 769 | e 770 | })) 771 | } else { 772 | Ok(res.to_token_stream()) 773 | } 774 | } 775 | 776 | /// Fold the expr and returns the folded expr and if it was a `for_tuples!`. 777 | fn custom_fold_expr(&mut self, expr: Expr) -> (Expr, bool) { 778 | match expr { 779 | Expr::Macro(expr_macro) => match ForTuplesMacro::try_from(&expr_macro.mac, false) { 780 | Ok(Some(for_tuples)) => ( 781 | Expr::Verbatim(for_tuples.expand( 782 | &self.tuple_placeholder_ident, 783 | self.tuples, 784 | self.has_self_parameter, 785 | )), 786 | true, 787 | ), 788 | Ok(None) => (fold::fold_expr_macro(self, expr_macro).into(), false), 789 | Err(e) => { 790 | self.errors.push(e); 791 | (Expr::Verbatim(Default::default()), false) 792 | } 793 | }, 794 | _ => (fold::fold_expr(self, expr), false), 795 | } 796 | } 797 | } 798 | 799 | impl<'a> Fold for ToTupleImplementation<'a> { 800 | fn fold_impl_item(&mut self, i: ImplItem) -> ImplItem { 801 | match i { 802 | ImplItem::Macro(macro_item) => match ForTuplesMacro::try_from(¯o_item.mac, true) { 803 | Ok(Some(for_tuples)) => { 804 | if for_tuples.is_where() { 805 | if self.custom_where_clause.is_some() { 806 | self.errors.push(Error::new( 807 | macro_item.span(), 808 | "Only one custom where clause is supported!", 809 | )); 810 | } else { 811 | self.custom_where_clause = for_tuples.into_where(); 812 | } 813 | 814 | ImplItem::Verbatim(Default::default()) 815 | } else { 816 | ImplItem::Verbatim(for_tuples.expand( 817 | &self.tuple_placeholder_ident, 818 | self.tuples, 819 | false, 820 | )) 821 | } 822 | } 823 | Ok(None) => fold::fold_impl_item_macro(self, macro_item).into(), 824 | Err(e) => { 825 | self.errors.push(e); 826 | ImplItem::Verbatim(Default::default()) 827 | } 828 | }, 829 | _ => fold::fold_impl_item(self, i), 830 | } 831 | } 832 | 833 | fn fold_expr(&mut self, expr: Expr) -> Expr { 834 | self.custom_fold_expr(expr).0 835 | } 836 | 837 | fn fold_stmt(&mut self, stmt: Stmt) -> Stmt { 838 | match stmt { 839 | Stmt::Expr(expr, semi) => { 840 | let (expr, expanded) = self.custom_fold_expr(expr); 841 | Stmt::Expr(expr, if expanded { None } else { semi }) 842 | } 843 | Stmt::Macro(macro_stmt) => { 844 | let expr = Expr::Macro(syn::ExprMacro { 845 | mac: macro_stmt.mac, 846 | attrs: macro_stmt.attrs, 847 | }); 848 | let (expr, expanded) = self.custom_fold_expr(expr); 849 | Stmt::Expr( 850 | expr, 851 | if expanded { 852 | None 853 | } else { 854 | macro_stmt.semi_token 855 | }, 856 | ) 857 | } 858 | _ => fold::fold_stmt(self, stmt), 859 | } 860 | } 861 | 862 | fn fold_type(&mut self, ty: Type) -> Type { 863 | match ty { 864 | Type::Macro(ty_macro) => match ForTuplesMacro::try_from(&ty_macro.mac, false) { 865 | Ok(Some(for_tuples)) => Type::Verbatim(for_tuples.expand( 866 | &self.tuple_placeholder_ident, 867 | self.tuples, 868 | false, 869 | )), 870 | Ok(None) => fold::fold_type_macro(self, ty_macro).into(), 871 | Err(e) => { 872 | self.errors.push(e); 873 | Type::Verbatim(Default::default()) 874 | } 875 | }, 876 | _ => fold::fold_type(self, ty), 877 | } 878 | } 879 | 880 | fn fold_impl_item_fn(&mut self, mut impl_item_method: ImplItemFn) -> ImplItemFn { 881 | let has_self = impl_item_method 882 | .sig 883 | .inputs 884 | .first() 885 | .map(|a| match a { 886 | FnArg::Receiver(_) => true, 887 | _ => false, 888 | }) 889 | .unwrap_or(false); 890 | 891 | impl_item_method.sig = fold::fold_signature(self, impl_item_method.sig); 892 | 893 | // Store the old value and set the current one 894 | let old_has_self_parameter = self.has_self_parameter; 895 | self.has_self_parameter = has_self; 896 | 897 | impl_item_method.block = fold::fold_block(self, impl_item_method.block); 898 | self.has_self_parameter = old_has_self_parameter; 899 | 900 | impl_item_method 901 | } 902 | } 903 | 904 | /// Extracts the tuple placeholder ident from the given trait implementation. 905 | fn extract_tuple_placeholder_ident(trait_impl: &ItemImpl) -> Result<(bool, Ident)> { 906 | match *trait_impl.self_ty { 907 | Type::Reference(ref type_ref) => { 908 | if let Type::Path(ref type_path) = *type_ref.elem { 909 | if let Some(ident) = type_path.path.get_ident() { 910 | return Ok((true, ident.clone())); 911 | } 912 | } 913 | } 914 | Type::Path(ref type_path) => { 915 | if let Some(ident) = type_path.path.get_ident() { 916 | return Ok((false, ident.clone())); 917 | } 918 | } 919 | _ => {} 920 | } 921 | 922 | Err(Error::new( 923 | trait_impl.self_ty.span(), 924 | "Expected an `Ident` as tuple placeholder.", 925 | )) 926 | } 927 | 928 | /// Generate the semi-automatic tuple implementations for a given trait implementation and the given tuples. 929 | pub fn semi_automatic_impl( 930 | trait_impl: ItemImpl, 931 | tuple_elements: Vec, 932 | min: Option, 933 | ) -> Result { 934 | let placeholder_ident = extract_tuple_placeholder_ident(&trait_impl)?; 935 | 936 | let mut res = TokenStream::new(); 937 | 938 | (min.unwrap_or(0)..=tuple_elements.len()).try_for_each(|i| { 939 | res.extend(ToTupleImplementation::generate_implementation( 940 | &trait_impl, 941 | &placeholder_ident.1, 942 | &tuple_elements[..i], 943 | placeholder_ident.0, 944 | )?); 945 | Ok::<_, Error>(()) 946 | })?; 947 | 948 | Ok(res) 949 | } 950 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | //! Provides common utils function shared between full and semi-automatic. 2 | 3 | use proc_macro2::TokenStream; 4 | 5 | use syn::{parse_quote, Generics, Ident}; 6 | 7 | use quote::quote; 8 | 9 | /// Add the given tuple elements as generics with the given `bounds` to `generics`. 10 | pub fn add_tuple_element_generics( 11 | tuple_elements: &[Ident], 12 | bounds: Option, 13 | generics: &mut Generics, 14 | ) { 15 | let bound = bounds.map(|b| quote!(: #b)).unwrap_or_else(|| quote!()); 16 | 17 | tuple_elements.iter().for_each(|tuple_element| { 18 | generics.params.push(parse_quote!(#tuple_element #bound)); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /tests/codegen.rs: -------------------------------------------------------------------------------- 1 | use impl_trait_for_tuples::impl_for_tuples; 2 | 3 | #[test] 4 | fn is_implemented_for_tuples() { 5 | #[impl_for_tuples(5)] 6 | trait EmptyTrait {} 7 | 8 | struct EmptyTraitImpl; 9 | 10 | impl EmptyTrait for EmptyTraitImpl {} 11 | 12 | fn test() {} 13 | 14 | test::<()>(); 15 | test::(); 16 | test::<(EmptyTraitImpl, EmptyTraitImpl, EmptyTraitImpl)>(); 17 | test::<( 18 | EmptyTraitImpl, 19 | EmptyTraitImpl, 20 | EmptyTraitImpl, 21 | EmptyTraitImpl, 22 | EmptyTraitImpl, 23 | )>(); 24 | test::<( 25 | ( 26 | EmptyTraitImpl, 27 | EmptyTraitImpl, 28 | EmptyTraitImpl, 29 | EmptyTraitImpl, 30 | EmptyTraitImpl, 31 | ), 32 | ( 33 | EmptyTraitImpl, 34 | EmptyTraitImpl, 35 | EmptyTraitImpl, 36 | EmptyTraitImpl, 37 | EmptyTraitImpl, 38 | ), 39 | )>(); 40 | } 41 | 42 | #[test] 43 | fn full_automatic_accept_empty_tuple_return() { 44 | #[impl_for_tuples(5)] 45 | trait EmptyTupleReturn { 46 | fn test() -> (); 47 | } 48 | } 49 | 50 | #[test] 51 | fn full_automatic_unsafe_trait() { 52 | #[impl_for_tuples(5)] 53 | unsafe trait UnsafeTrait { 54 | fn test(); 55 | } 56 | } 57 | 58 | #[test] 59 | fn accept_attributes() { 60 | /// Hey, I'm an attribute! 61 | #[cfg(test)] 62 | #[impl_for_tuples(5)] 63 | trait Attributes {} 64 | 65 | trait Test {} 66 | 67 | /// Hey, I'm an attribute! 68 | #[cfg(test)] 69 | #[impl_for_tuples(5)] 70 | impl Test for Tuple {} 71 | } 72 | 73 | #[test] 74 | fn is_implemented_for_tuples_with_semi() { 75 | trait EmptyTrait {} 76 | 77 | #[impl_for_tuples(5)] 78 | impl EmptyTrait for Tuple {} 79 | 80 | struct EmptyTraitImpl; 81 | 82 | impl EmptyTrait for EmptyTraitImpl {} 83 | 84 | fn test() {} 85 | 86 | test::<()>(); 87 | test::(); 88 | test::<(EmptyTraitImpl, EmptyTraitImpl, EmptyTraitImpl)>(); 89 | test::<( 90 | EmptyTraitImpl, 91 | EmptyTraitImpl, 92 | EmptyTraitImpl, 93 | EmptyTraitImpl, 94 | EmptyTraitImpl, 95 | )>(); 96 | test::<( 97 | ( 98 | EmptyTraitImpl, 99 | EmptyTraitImpl, 100 | EmptyTraitImpl, 101 | EmptyTraitImpl, 102 | EmptyTraitImpl, 103 | ), 104 | ( 105 | EmptyTraitImpl, 106 | EmptyTraitImpl, 107 | EmptyTraitImpl, 108 | EmptyTraitImpl, 109 | EmptyTraitImpl, 110 | ), 111 | )>(); 112 | } 113 | 114 | #[test] 115 | fn trait_with_static_functions() { 116 | #[impl_for_tuples(50)] 117 | trait TraitWithFunctions { 118 | fn function(counter: &mut u32); 119 | fn function_with_args(data: String, l: u32); 120 | fn function_with_args_wild(_: String, _: u32); 121 | } 122 | 123 | struct Impl; 124 | 125 | impl TraitWithFunctions for Impl { 126 | fn function(counter: &mut u32) { 127 | *counter += 1; 128 | } 129 | fn function_with_args(_: String, _: u32) {} 130 | fn function_with_args_wild(_: String, _: u32) {} 131 | } 132 | 133 | fn test(counter: &mut u32) { 134 | T::function(counter); 135 | } 136 | 137 | let mut counter = 0; 138 | test::<(Impl, Impl, Impl)>(&mut counter); 139 | assert_eq!(3, counter); 140 | 141 | let mut counter = 0; 142 | test::<(Impl, Impl, Impl, Impl, Impl)>(&mut counter); 143 | assert_eq!(5, counter); 144 | } 145 | 146 | #[test] 147 | fn trait_with_functions() { 148 | #[impl_for_tuples(50)] 149 | trait TraitWithFunctions { 150 | fn function(&self, counter: &mut u32); 151 | fn function_with_args(&self, data: String, l: u32); 152 | fn function_with_args_wild(self, _: String, _: u32); 153 | } 154 | 155 | struct Impl; 156 | 157 | impl TraitWithFunctions for Impl { 158 | fn function(&self, counter: &mut u32) { 159 | *counter += 1; 160 | } 161 | fn function_with_args(&self, _: String, _: u32) {} 162 | fn function_with_args_wild(self, _: String, _: u32) {} 163 | } 164 | 165 | fn test(data: T, counter: &mut u32) { 166 | data.function(counter); 167 | } 168 | 169 | let mut counter = 0; 170 | test((Impl, Impl, Impl), &mut counter); 171 | assert_eq!(3, counter); 172 | 173 | let mut counter = 0; 174 | test((Impl, Impl, Impl, Impl, Impl), &mut counter); 175 | assert_eq!(5, counter); 176 | } 177 | 178 | #[test] 179 | fn trait_with_static_functions_and_generics() { 180 | #[impl_for_tuples(50)] 181 | trait TraitWithFunctions { 182 | fn function(counter: &mut u32); 183 | fn function_with_args(data: String, l: T); 184 | fn function_with_args_wild(_: String, _: &N); 185 | } 186 | 187 | struct Impl; 188 | 189 | impl TraitWithFunctions for Impl { 190 | fn function(counter: &mut u32) { 191 | *counter += 1; 192 | } 193 | fn function_with_args(_: String, _: T) {} 194 | fn function_with_args_wild(_: String, _: &N) {} 195 | } 196 | 197 | fn test>(counter: &mut u32) { 198 | T::function(counter); 199 | } 200 | 201 | let mut counter = 0; 202 | test::<()>(&mut counter); 203 | assert_eq!(0, counter); 204 | 205 | let mut counter = 0; 206 | test::(&mut counter); 207 | assert_eq!(1, counter); 208 | 209 | let mut counter = 0; 210 | test::<(Impl, Impl, Impl)>(&mut counter); 211 | assert_eq!(3, counter); 212 | 213 | let mut counter = 0; 214 | test::<(Impl, Impl, Impl, Impl, Impl)>(&mut counter); 215 | assert_eq!(5, counter); 216 | } 217 | 218 | #[test] 219 | fn trait_with_return_type() { 220 | trait TraitWithReturnType { 221 | fn function(counter: &mut u32) -> Result<(), ()>; 222 | } 223 | 224 | #[impl_for_tuples(50)] 225 | impl TraitWithReturnType for Tuple { 226 | fn function(counter: &mut u32) -> Result<(), ()> { 227 | for_tuples!( #( Tuple::function(counter)?; )* ); 228 | Ok(()) 229 | } 230 | } 231 | 232 | struct Impl; 233 | 234 | impl TraitWithReturnType for Impl { 235 | fn function(counter: &mut u32) -> Result<(), ()> { 236 | *counter += 1; 237 | Ok(()) 238 | } 239 | } 240 | 241 | fn test(counter: &mut u32) { 242 | T::function(counter).unwrap(); 243 | } 244 | 245 | let mut counter = 0; 246 | test::<()>(&mut counter); 247 | assert_eq!(0, counter); 248 | 249 | let mut counter = 0; 250 | test::(&mut counter); 251 | assert_eq!(1, counter); 252 | 253 | let mut counter = 0; 254 | test::<(Impl, Impl, Impl)>(&mut counter); 255 | assert_eq!(3, counter); 256 | 257 | let mut counter = 0; 258 | test::<(Impl, Impl, Impl, Impl, Impl)>(&mut counter); 259 | assert_eq!(5, counter); 260 | } 261 | 262 | #[test] 263 | fn trait_with_associated_type() { 264 | trait TraitWithAssociatedType { 265 | type Ret; 266 | fn function(counter: &mut u32) -> Self::Ret; 267 | } 268 | 269 | #[impl_for_tuples(50)] 270 | impl TraitWithAssociatedType for Tuple { 271 | for_tuples!( type Ret = ( #( Tuple::Ret ),* ); ); 272 | fn function(counter: &mut u32) -> Self::Ret { 273 | for_tuples!( ( #( Tuple::function(counter) ),* ) ) 274 | } 275 | } 276 | 277 | struct Impl; 278 | 279 | impl TraitWithAssociatedType for Impl { 280 | type Ret = u32; 281 | fn function(counter: &mut u32) -> u32 { 282 | *counter += 1; 283 | *counter 284 | } 285 | } 286 | 287 | fn test(counter: &mut u32) -> T::Ret { 288 | T::function(counter) 289 | } 290 | 291 | let mut counter = 0; 292 | let res = test::<()>(&mut counter); 293 | assert_eq!(0, counter); 294 | assert_eq!((), res); 295 | 296 | let mut counter = 0; 297 | let res = test::(&mut counter); 298 | assert_eq!(1, counter); 299 | assert_eq!((1), res); 300 | 301 | let mut counter = 0; 302 | let res = test::<(Impl, Impl, Impl)>(&mut counter); 303 | assert_eq!(3, counter); 304 | assert_eq!((1, 2, 3), res); 305 | 306 | let mut counter = 0; 307 | let res = test::<(Impl, Impl, Impl, Impl, Impl)>(&mut counter); 308 | assert_eq!(5, counter); 309 | assert_eq!((1, 2, 3, 4, 5), res); 310 | } 311 | 312 | #[test] 313 | fn trait_with_associated_type_and_generics() { 314 | trait TraitWithAssociatedType { 315 | type Ret; 316 | fn function(counter: &mut u32) -> Self::Ret; 317 | } 318 | 319 | #[impl_for_tuples(50)] 320 | impl TraitWithAssociatedType for Tuple { 321 | for_tuples!( type Ret = ( #( Tuple::Ret ),* ); ); 322 | fn function(counter: &mut u32) -> Self::Ret { 323 | for_tuples!( ( #( Tuple::function(counter) ),* ) ) 324 | } 325 | } 326 | 327 | struct Impl; 328 | 329 | impl TraitWithAssociatedType for Impl { 330 | type Ret = u32; 331 | fn function(counter: &mut u32) -> u32 { 332 | *counter += 1; 333 | *counter 334 | } 335 | } 336 | 337 | fn test>(counter: &mut u32) -> T::Ret { 338 | T::function(counter) 339 | } 340 | 341 | let mut counter = 0; 342 | let res = test::<()>(&mut counter); 343 | assert_eq!(0, counter); 344 | assert_eq!((), res); 345 | 346 | let mut counter = 0; 347 | let res = test::(&mut counter); 348 | assert_eq!(1, counter); 349 | assert_eq!((1), res); 350 | 351 | let mut counter = 0; 352 | let res = test::<(Impl, Impl, Impl)>(&mut counter); 353 | assert_eq!(3, counter); 354 | assert_eq!((1, 2, 3), res); 355 | 356 | let mut counter = 0; 357 | let res = test::<(Impl, Impl, Impl, Impl, Impl)>(&mut counter); 358 | assert_eq!(5, counter); 359 | assert_eq!((1, 2, 3, 4, 5), res); 360 | } 361 | 362 | #[test] 363 | fn trait_with_associated_type_and_self() { 364 | trait TraitWithAssociatedTypeAndSelf { 365 | type Ret; 366 | fn function(&self, counter: &mut u32) -> Self::Ret; 367 | } 368 | 369 | #[impl_for_tuples(50)] 370 | impl TraitWithAssociatedTypeAndSelf for Tuple { 371 | for_tuples!( type Ret = ( #( Tuple::Ret ),* ); ); 372 | fn function(&self, counter: &mut u32) -> Self::Ret { 373 | for_tuples!( ( #( Tuple.function(counter) ),* ) ) 374 | } 375 | } 376 | 377 | struct Impl; 378 | 379 | impl TraitWithAssociatedTypeAndSelf for Impl { 380 | type Ret = u32; 381 | fn function(&self, counter: &mut u32) -> u32 { 382 | *counter += 1; 383 | *counter 384 | } 385 | } 386 | 387 | fn test(data: T, counter: &mut u32) -> T::Ret { 388 | data.function(counter) 389 | } 390 | 391 | let mut counter = 0; 392 | let res = test((), &mut counter); 393 | assert_eq!(0, counter); 394 | assert_eq!((), res); 395 | 396 | let mut counter = 0; 397 | let res = test(Impl, &mut counter); 398 | assert_eq!(1, counter); 399 | assert_eq!((1), res); 400 | 401 | let mut counter = 0; 402 | let res = test((Impl, Impl, Impl), &mut counter); 403 | assert_eq!(3, counter); 404 | assert_eq!((1, 2, 3), res); 405 | 406 | let mut counter = 0; 407 | let res = test((Impl, Impl, Impl, Impl, Impl), &mut counter); 408 | assert_eq!(5, counter); 409 | assert_eq!((1, 2, 3, 4, 5), res); 410 | } 411 | 412 | #[test] 413 | fn semi_automatic_tuple_access() { 414 | trait Trait { 415 | type Arg; 416 | 417 | fn test(arg: Self::Arg); 418 | } 419 | 420 | #[impl_for_tuples(5)] 421 | impl Trait for Tuple { 422 | for_tuples!( type Arg = ( #( Tuple::Arg ),* ); ); 423 | fn test(arg: Self::Arg) { 424 | for_tuples!( #( Tuple::test(arg.Tuple); )* ) 425 | } 426 | } 427 | } 428 | 429 | #[test] 430 | fn semi_automatic_with_custom_where_clause() { 431 | trait Trait { 432 | type Arg; 433 | 434 | fn test(arg: Self::Arg); 435 | } 436 | 437 | #[impl_for_tuples(5)] 438 | impl Trait for Tuple { 439 | for_tuples!( where #( Tuple: Trait ),* ); 440 | type Arg = u32; 441 | 442 | fn test(arg: Self::Arg) { 443 | for_tuples!( #( Tuple::test(arg); )* ) 444 | } 445 | } 446 | } 447 | 448 | #[test] 449 | fn for_tuples_in_nested_expr_works() { 450 | trait Trait { 451 | type Arg; 452 | type Res; 453 | 454 | fn test(arg: Self::Arg) -> Result; 455 | } 456 | 457 | #[impl_for_tuples(5)] 458 | impl Trait for Tuple { 459 | for_tuples!( where #( Tuple: Trait ),* ); 460 | type Arg = u32; 461 | for_tuples!( type Res = ( #( Tuple::Res ),* ); ); 462 | 463 | fn test(arg: Self::Arg) -> Result { 464 | Ok(for_tuples!( ( #( Tuple::test(arg)? ),* ) )) 465 | } 466 | } 467 | } 468 | 469 | #[test] 470 | fn test_separators() { 471 | trait Trait { 472 | fn plus() -> u32; 473 | fn minus() -> i32; 474 | fn star() -> u32; 475 | } 476 | 477 | #[impl_for_tuples(1, 5)] 478 | impl Trait for Tuple { 479 | fn plus() -> u32 { 480 | for_tuples!( #( Tuple::plus() )+* ) 481 | } 482 | 483 | fn minus() -> i32 { 484 | for_tuples!( #( Tuple::minus() )-* ) 485 | } 486 | 487 | fn star() -> u32 { 488 | for_tuples!( #( Tuple::star() )** ) 489 | } 490 | } 491 | 492 | struct Impl; 493 | 494 | impl Trait for Impl { 495 | fn plus() -> u32 { 496 | 5 497 | } 498 | 499 | fn minus() -> i32 { 500 | 1000 501 | } 502 | 503 | fn star() -> u32 { 504 | 5 505 | } 506 | } 507 | 508 | assert_eq!(<(Impl, Impl, Impl)>::plus(), 15); 509 | assert_eq!(<(Impl, Impl, Impl)>::star(), 125); 510 | assert_eq!(<(Impl, Impl, Impl)>::minus(), -1000); 511 | } 512 | 513 | #[test] 514 | fn semi_automatic_tuple_with_custom_trait_bound() { 515 | #[allow(dead_code)] 516 | trait Trait { 517 | type Arg; 518 | 519 | fn test(arg: Self::Arg); 520 | } 521 | 522 | trait Custom { 523 | type NewArg; 524 | 525 | fn new_test(arg: Self::NewArg); 526 | } 527 | 528 | #[impl_for_tuples(5)] 529 | #[tuple_types_custom_trait_bound(Custom)] 530 | impl Trait for Tuple { 531 | for_tuples!( type Arg = ( #( Tuple::NewArg ),* ); ); 532 | fn test(arg: Self::Arg) { 533 | for_tuples!( #( Tuple::new_test(arg.Tuple); )* ); 534 | } 535 | } 536 | } 537 | 538 | #[test] 539 | fn semi_automatic_tuple_with_custom_advanced_trait_bound() { 540 | #[allow(dead_code)] 541 | trait Trait { 542 | type Arg; 543 | type Output; 544 | 545 | fn test(arg: Self::Arg); 546 | fn clone_test(&self) -> Self::Output; 547 | } 548 | 549 | trait Custom { 550 | type NewArg; 551 | 552 | fn new_test(arg: Self::NewArg); 553 | } 554 | 555 | #[impl_for_tuples(5)] 556 | #[tuple_types_custom_trait_bound(Custom + Clone)] 557 | impl Trait for Tuple { 558 | for_tuples!( type Arg = ( #( Tuple::NewArg ),* ); ); 559 | for_tuples!( type Output = ( #( Tuple ),* ); ); 560 | 561 | fn test(arg: Self::Arg) { 562 | for_tuples!( #( Tuple::new_test(arg.Tuple); )* ); 563 | } 564 | 565 | fn clone_test(&self) -> Self::Output { 566 | for_tuples!( ( #( Tuple.clone() ),* ) ); 567 | } 568 | } 569 | } 570 | 571 | #[test] 572 | fn semi_automatic_tuple_as_ref() { 573 | trait Trait { 574 | type Arg; 575 | 576 | fn test(arg: Self::Arg); 577 | } 578 | 579 | #[impl_for_tuples(5)] 580 | impl Trait for &Tuple { 581 | for_tuples!( type Arg = ( #( Tuple::Arg ),* ); ); 582 | fn test(arg: Self::Arg) { 583 | for_tuples!( #( Tuple::test(arg.Tuple); )* ) 584 | } 585 | } 586 | } 587 | 588 | #[test] 589 | fn semi_automatic_associated_const() { 590 | trait Trait { 591 | const TYPE: &'static [u32]; 592 | } 593 | 594 | trait Trait2 { 595 | const TYPE: u32; 596 | } 597 | 598 | #[impl_for_tuples(1, 5)] 599 | #[tuple_types_no_default_trait_bound] 600 | impl Trait for Tuple { 601 | for_tuples!( where #( Tuple: Trait2 )* ); 602 | 603 | for_tuples!( const TYPE: &'static [u32] = &[ #( Tuple::TYPE ),* ]; ); 604 | } 605 | } 606 | 607 | #[test] 608 | fn semi_automatic_associated_const_calculation() { 609 | trait Trait { 610 | const TYPE: u32; 611 | } 612 | 613 | #[impl_for_tuples(1, 5)] 614 | impl Trait for Tuple { 615 | for_tuples!( const TYPE: u32 = #( Tuple::TYPE )+*; ); 616 | } 617 | 618 | struct Test; 619 | impl Trait for Test { 620 | const TYPE: u32 = 1; 621 | } 622 | 623 | assert_eq!(3, <(Test, Test, Test)>::TYPE); 624 | assert_eq!(1, Test::TYPE); 625 | } 626 | 627 | #[test] 628 | fn semi_automatic_associated_type() { 629 | trait Trait { 630 | type A; 631 | type B; 632 | } 633 | 634 | #[impl_for_tuples(5)] 635 | impl Trait for Tuple { 636 | for_tuples!( type A = ( #( Option< Tuple::B > ),* ); ); 637 | for_tuples!( type B = ( #( Tuple::B ),* ); ); 638 | } 639 | } 640 | 641 | #[test] 642 | fn semi_automatic_unsafe_trait() { 643 | unsafe trait Trait { 644 | type A; 645 | } 646 | 647 | #[impl_for_tuples(5)] 648 | unsafe impl Trait for Tuple { 649 | for_tuples!( type A = ( #( Tuple::A ),* ); ); 650 | } 651 | } 652 | -------------------------------------------------------------------------------- /tests/fail.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn fail() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/fail/*.rs"); 5 | } 6 | -------------------------------------------------------------------------------- /tests/fail/custom_trait_bound_invalid.rs: -------------------------------------------------------------------------------- 1 | trait Test { 2 | type Arg; 3 | 4 | fn test(arg: Self::Arg); 5 | } 6 | 7 | trait Custom { 8 | type NewArg; 9 | 10 | fn new_test(arg: Self::NewArg); 11 | } 12 | 13 | #[impl_trait_for_tuples::impl_for_tuples(5)] 14 | #[tuple_types_custom_trait_bound(Custom, Clone)] 15 | impl Test for Tuple { 16 | for_tuples!( type Arg = ( #( Tuple::NewArg ),* ); ); 17 | fn test(arg: Self::Arg) { 18 | for_tuples!( #( Tuple::new_test(arg.Tuple); )* ); 19 | } 20 | } 21 | 22 | struct Impl; 23 | 24 | impl Custom for Impl { 25 | type NewArg = (); 26 | 27 | fn new_test(_arg: Self::NewArg) {} 28 | } 29 | 30 | fn test() {} 31 | fn main() { 32 | test::<(Impl, Impl)>(); 33 | test::<(Impl, Impl, Impl)>(); 34 | } 35 | -------------------------------------------------------------------------------- /tests/fail/custom_trait_bound_invalid.stderr: -------------------------------------------------------------------------------- 1 | error: Invalid trait bound: unexpected token 2 | --> tests/fail/custom_trait_bound_invalid.rs:14:40 3 | | 4 | 14 | #[tuple_types_custom_trait_bound(Custom, Clone)] 5 | | ^ 6 | 7 | error[E0277]: the trait bound `(Impl, Impl): Test` is not satisfied 8 | --> tests/fail/custom_trait_bound_invalid.rs:32:12 9 | | 10 | 32 | test::<(Impl, Impl)>(); 11 | | ^^^^^^^^^^^^ the trait `Test` is not implemented for `(Impl, Impl)` 12 | | 13 | help: this trait has no implementations, consider adding one 14 | --> tests/fail/custom_trait_bound_invalid.rs:1:1 15 | | 16 | 1 | trait Test { 17 | | ^^^^^^^^^^ 18 | note: required by a bound in `test` 19 | --> tests/fail/custom_trait_bound_invalid.rs:30:12 20 | | 21 | 30 | fn test() {} 22 | | ^^^^ required by this bound in `test` 23 | 24 | error[E0277]: the trait bound `(Impl, Impl, Impl): Test` is not satisfied 25 | --> tests/fail/custom_trait_bound_invalid.rs:33:12 26 | | 27 | 33 | test::<(Impl, Impl, Impl)>(); 28 | | ^^^^^^^^^^^^^^^^^^ the trait `Test` is not implemented for `(Impl, Impl, Impl)` 29 | | 30 | help: this trait has no implementations, consider adding one 31 | --> tests/fail/custom_trait_bound_invalid.rs:1:1 32 | | 33 | 1 | trait Test { 34 | | ^^^^^^^^^^ 35 | note: required by a bound in `test` 36 | --> tests/fail/custom_trait_bound_invalid.rs:30:12 37 | | 38 | 30 | fn test() {} 39 | | ^^^^ required by this bound in `test` 40 | -------------------------------------------------------------------------------- /tests/fail/trait_bound_not_added.rs: -------------------------------------------------------------------------------- 1 | trait Test { 2 | fn test(); 3 | } 4 | 5 | #[impl_trait_for_tuples::impl_for_tuples(5)] 6 | #[tuple_types_no_default_trait_bound] 7 | impl Test for Tuple { 8 | fn test() { 9 | for_tuples!( #( Tuple::test(); )* ) 10 | } 11 | } 12 | 13 | struct Impl; 14 | 15 | impl Test for Impl {} 16 | 17 | fn test() {} 18 | fn main() { 19 | test::<(Impl, Impl)>(); 20 | test::<(Impl, Impl, Impl)>(); 21 | } 22 | -------------------------------------------------------------------------------- /tests/fail/trait_bound_not_added.stderr: -------------------------------------------------------------------------------- 1 | error[E0046]: not all trait items implemented, missing: `test` 2 | --> tests/fail/trait_bound_not_added.rs:15:1 3 | | 4 | 2 | fn test(); 5 | | ---------- `test` from trait 6 | ... 7 | 15 | impl Test for Impl {} 8 | | ^^^^^^^^^^^^^^^^^^ missing `test` in implementation 9 | 10 | error[E0599]: no function or associated item named `test` found for type parameter `TupleElement0` in the current scope 11 | --> tests/fail/trait_bound_not_added.rs:9:32 12 | | 13 | 5 | #[impl_trait_for_tuples::impl_for_tuples(5)] 14 | | -------------------------------------------- function or associated item `test` not found for this type parameter 15 | ... 16 | 9 | for_tuples!( #( Tuple::test(); )* ) 17 | | ^^^^ function or associated item not found in `TupleElement0` 18 | | 19 | = help: items from traits can only be used if the type parameter is bounded by the trait 20 | help: the following trait defines an item `test`, perhaps you need to restrict type parameter `TupleElement0` with it: 21 | | 22 | 5 | #[impl_trait_for_tuples::impl_for_tuples(5)]: Test 23 | | ++++++ 24 | 25 | error[E0599]: no function or associated item named `test` found for type parameter `TupleElement1` in the current scope 26 | --> tests/fail/trait_bound_not_added.rs:9:32 27 | | 28 | 5 | #[impl_trait_for_tuples::impl_for_tuples(5)] 29 | | -------------------------------------------- function or associated item `test` not found for this type parameter 30 | ... 31 | 9 | for_tuples!( #( Tuple::test(); )* ) 32 | | ^^^^ function or associated item not found in `TupleElement1` 33 | | 34 | = help: items from traits can only be used if the type parameter is bounded by the trait 35 | help: the following trait defines an item `test`, perhaps you need to restrict type parameter `TupleElement1` with it: 36 | | 37 | 5 | #[impl_trait_for_tuples::impl_for_tuples(5)]: Test 38 | | ++++++ 39 | 40 | error[E0599]: no function or associated item named `test` found for type parameter `TupleElement2` in the current scope 41 | --> tests/fail/trait_bound_not_added.rs:9:32 42 | | 43 | 5 | #[impl_trait_for_tuples::impl_for_tuples(5)] 44 | | -------------------------------------------- function or associated item `test` not found for this type parameter 45 | ... 46 | 9 | for_tuples!( #( Tuple::test(); )* ) 47 | | ^^^^ function or associated item not found in `TupleElement2` 48 | | 49 | = help: items from traits can only be used if the type parameter is bounded by the trait 50 | help: the following trait defines an item `test`, perhaps you need to restrict type parameter `TupleElement2` with it: 51 | | 52 | 5 | #[impl_trait_for_tuples::impl_for_tuples(5)]: Test 53 | | ++++++ 54 | 55 | error[E0599]: no function or associated item named `test` found for type parameter `TupleElement3` in the current scope 56 | --> tests/fail/trait_bound_not_added.rs:9:32 57 | | 58 | 5 | #[impl_trait_for_tuples::impl_for_tuples(5)] 59 | | -------------------------------------------- function or associated item `test` not found for this type parameter 60 | ... 61 | 9 | for_tuples!( #( Tuple::test(); )* ) 62 | | ^^^^ function or associated item not found in `TupleElement3` 63 | | 64 | = help: items from traits can only be used if the type parameter is bounded by the trait 65 | help: the following trait defines an item `test`, perhaps you need to restrict type parameter `TupleElement3` with it: 66 | | 67 | 5 | #[impl_trait_for_tuples::impl_for_tuples(5)]: Test 68 | | ++++++ 69 | 70 | error[E0599]: no function or associated item named `test` found for type parameter `TupleElement4` in the current scope 71 | --> tests/fail/trait_bound_not_added.rs:9:32 72 | | 73 | 5 | #[impl_trait_for_tuples::impl_for_tuples(5)] 74 | | -------------------------------------------- function or associated item `test` not found for this type parameter 75 | ... 76 | 9 | for_tuples!( #( Tuple::test(); )* ) 77 | | ^^^^ function or associated item not found in `TupleElement4` 78 | | 79 | = help: items from traits can only be used if the type parameter is bounded by the trait 80 | help: the following trait defines an item `test`, perhaps you need to restrict type parameter `TupleElement4` with it: 81 | | 82 | 5 | #[impl_trait_for_tuples::impl_for_tuples(5)]: Test 83 | | ++++++ 84 | -------------------------------------------------------------------------------- /tests/fail/tuple_impls_less_than_minimum_does_not_exists.rs: -------------------------------------------------------------------------------- 1 | #[impl_trait_for_tuples::impl_for_tuples(3, 5)] 2 | trait Test {} 3 | 4 | struct Impl; 5 | 6 | impl Test for Impl {} 7 | 8 | fn test() {} 9 | fn main() { 10 | test::<(Impl, Impl)>(); 11 | test::<(Impl, Impl, Impl)>(); 12 | } 13 | -------------------------------------------------------------------------------- /tests/fail/tuple_impls_less_than_minimum_does_not_exists.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `(Impl, Impl): Test` is not satisfied 2 | --> tests/fail/tuple_impls_less_than_minimum_does_not_exists.rs:10:12 3 | | 4 | 10 | test::<(Impl, Impl)>(); 5 | | ^^^^^^^^^^^^ the trait `Test` is not implemented for `(Impl, Impl)` 6 | | 7 | = help: the following other types implement trait `Test`: 8 | (TupleElement0, TupleElement1, TupleElement2) 9 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3) 10 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4) 11 | note: required by a bound in `test` 12 | --> tests/fail/tuple_impls_less_than_minimum_does_not_exists.rs:8:12 13 | | 14 | 8 | fn test() {} 15 | | ^^^^ required by this bound in `test` 16 | -------------------------------------------------------------------------------- /tests/ui.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn ui() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/ui/*.rs"); 5 | } 6 | -------------------------------------------------------------------------------- /tests/ui/calling_method_in_static_function.rs: -------------------------------------------------------------------------------- 1 | trait Test { 2 | type Test; 3 | 4 | fn test() -> Self::Test; 5 | } 6 | 7 | #[impl_trait_for_tuples::impl_for_tuples(2)] 8 | impl Test for Tuple { 9 | for_tuples!( type Test = ( #( Tuple::Test ),* ); ); 10 | 11 | fn test() -> Self::Test { 12 | for_tuples!( ( #( Tuple.test() ),* ) ) 13 | } 14 | } 15 | 16 | fn main() {} 17 | -------------------------------------------------------------------------------- /tests/ui/calling_method_in_static_function.stderr: -------------------------------------------------------------------------------- 1 | error: Can not call non-static method from within a static method. 2 | --> $DIR/calling_method_in_static_function.rs:12:27 3 | | 4 | 12 | for_tuples!( ( #( Tuple.test() ),* ) ) 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/custom_where_clause_not_allowed.rs: -------------------------------------------------------------------------------- 1 | trait Test { 2 | fn test(); 3 | } 4 | 5 | #[impl_trait_for_tuples::impl_for_tuples(2)] 6 | impl Test for Tuple { 7 | fn test() { 8 | for_tuples!( where #( Tuple: Test ),* ) 9 | } 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /tests/ui/custom_where_clause_not_allowed.stderr: -------------------------------------------------------------------------------- 1 | error: Custom where clause not allowed at this position! 2 | --> tests/ui/custom_where_clause_not_allowed.rs:8:9 3 | | 4 | 8 | for_tuples!( where #( Tuple: Test ),* ) 5 | | ^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/empty_for_tuples.rs: -------------------------------------------------------------------------------- 1 | trait Test { 2 | type Test; 3 | 4 | fn test() -> Self::Test; 5 | } 6 | 7 | #[impl_trait_for_tuples::impl_for_tuples(1)] 8 | impl Test for Tuple { 9 | for_tuples!( type Test = ( #( Tuple::Test ),* ); ); 10 | 11 | fn test() -> Self::Test { 12 | for_tuples!() 13 | } 14 | } 15 | 16 | fn main() {} 17 | -------------------------------------------------------------------------------- /tests/ui/empty_for_tuples.stderr: -------------------------------------------------------------------------------- 1 | error: unexpected end of input, expected one of: `type`, `const`, parentheses, `#`, `where` 2 | --> tests/ui/empty_for_tuples.rs:12:21 3 | | 4 | 12 | for_tuples!() 5 | | ^ 6 | -------------------------------------------------------------------------------- /tests/ui/max_before_min_in_arguments.rs: -------------------------------------------------------------------------------- 1 | #[impl_trait_for_tuples::impl_for_tuples(5, 2)] 2 | trait Test {} 3 | 4 | fn main() {} 5 | -------------------------------------------------------------------------------- /tests/ui/max_before_min_in_arguments.stderr: -------------------------------------------------------------------------------- 1 | error: It is expected that `min` comes before `max` and that `max > min` is true! 2 | --> tests/ui/max_before_min_in_arguments.rs:1:1 3 | | 4 | 1 | #[impl_trait_for_tuples::impl_for_tuples(5, 2)] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `impl_trait_for_tuples::impl_for_tuples` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /tests/ui/missing_arguments_to_macro.rs: -------------------------------------------------------------------------------- 1 | #[impl_trait_for_tuples::impl_for_tuples()] 2 | trait Test {} 3 | 4 | fn main() {} 5 | -------------------------------------------------------------------------------- /tests/ui/missing_arguments_to_macro.stderr: -------------------------------------------------------------------------------- 1 | error: Expected at least one argument to the macro! 2 | --> tests/ui/missing_arguments_to_macro.rs:1:1 3 | | 4 | 1 | #[impl_trait_for_tuples::impl_for_tuples()] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `impl_trait_for_tuples::impl_for_tuples` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /tests/ui/missing_paren_in_for_tuples.rs: -------------------------------------------------------------------------------- 1 | trait Test { 2 | type Test; 3 | } 4 | 5 | #[impl_trait_for_tuples::impl_for_tuples(1)] 6 | impl Test for Tuple { 7 | for_tuples!( type Test = #( Tuple::Test ),*; ); 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /tests/ui/missing_paren_in_for_tuples.stderr: -------------------------------------------------------------------------------- 1 | error: expected parentheses 2 | --> tests/ui/missing_paren_in_for_tuples.rs:7:30 3 | | 4 | 7 | for_tuples!( type Test = #( Tuple::Test ),*; ); 5 | | ^ 6 | -------------------------------------------------------------------------------- /tests/ui/no_const_value.rs: -------------------------------------------------------------------------------- 1 | #[impl_trait_for_tuples::impl_for_tuples(1)] 2 | trait Test { 3 | const Test: u32; 4 | } 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /tests/ui/no_const_value.stderr: -------------------------------------------------------------------------------- 1 | error: Not supported by full-automatic tuple implementation. Use semi-automatic tuple implementation for more control of the implementation. 2 | --> tests/ui/no_const_value.rs:3:2 3 | | 4 | 3 | const Test: u32; 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/no_return_value.rs: -------------------------------------------------------------------------------- 1 | #[impl_trait_for_tuples::impl_for_tuples(1)] 2 | trait Test { 3 | fn test() -> u32; 4 | } 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /tests/ui/no_return_value.stderr: -------------------------------------------------------------------------------- 1 | error: Not supported by full-automatic tuple implementation. Use semi-automatic tuple implementation for more control of the implementation. 2 | --> tests/ui/no_return_value.rs:3:15 3 | | 4 | 3 | fn test() -> u32; 5 | | ^ 6 | -------------------------------------------------------------------------------- /tests/ui/no_type_parameter.rs: -------------------------------------------------------------------------------- 1 | #[impl_trait_for_tuples::impl_for_tuples(1)] 2 | trait Test { 3 | type Test; 4 | } 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /tests/ui/no_type_parameter.stderr: -------------------------------------------------------------------------------- 1 | error: Not supported by full-automatic tuple implementation. Use semi-automatic tuple implementation for more control of the implementation. 2 | --> tests/ui/no_type_parameter.rs:3:2 3 | | 4 | 3 | type Test; 5 | | ^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/too_much_arguments_to_macro.rs: -------------------------------------------------------------------------------- 1 | #[impl_trait_for_tuples::impl_for_tuples(1, 2, 3)] 2 | trait Test {} 3 | 4 | fn main() {} 5 | -------------------------------------------------------------------------------- /tests/ui/too_much_arguments_to_macro.stderr: -------------------------------------------------------------------------------- 1 | error: Too many arguments given to the macro! 2 | --> tests/ui/too_much_arguments_to_macro.rs:1:1 3 | | 4 | 1 | #[impl_trait_for_tuples::impl_for_tuples(1, 2, 3)] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `impl_trait_for_tuples::impl_for_tuples` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /tests/ui/unknown_separator.rs: -------------------------------------------------------------------------------- 1 | trait Test { 2 | type Test; 3 | 4 | fn test() -> Self::Test; 5 | } 6 | 7 | #[impl_trait_for_tuples::impl_for_tuples(2)] 8 | impl Test for Tuple { 9 | for_tuples!( type Test = ( #( Tuple::Test ),* ); ); 10 | 11 | fn test() -> Self::Test { 12 | for_tuples!( ( #( Tuple.test() )$* ) ) 13 | } 14 | } 15 | 16 | fn main() {} 17 | -------------------------------------------------------------------------------- /tests/ui/unknown_separator.stderr: -------------------------------------------------------------------------------- 1 | error: expected one of: `,`, `+`, `-`, `|`, `&`, `*`, `/` 2 | --> tests/ui/unknown_separator.rs:12:41 3 | | 4 | 12 | for_tuples!( ( #( Tuple.test() )$* ) ) 5 | | ^ 6 | --------------------------------------------------------------------------------