From 6adb8a10cb742cd948abe6b54a3288058caae164 Mon Sep 17 00:00:00 2001 From: saresend Date: Mon, 1 Oct 2018 20:43:57 -0700 Subject: [PATCH 1/8] adding const ref generic test --- tests/compile-fail.rs | 6 +++--- tests/compile-pass/const_ref_generic_types.rs | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 tests/compile-pass/const_ref_generic_types.rs diff --git a/tests/compile-fail.rs b/tests/compile-fail.rs index 98d14ff..4e6ab5d 100644 --- a/tests/compile-fail.rs +++ b/tests/compile-fail.rs @@ -3,20 +3,20 @@ //! This file only contains the logic of compile-fail tests. The actual tests //! are in the directory `compile-fail/`. -extern crate libtest_mimic; extern crate build_plan; +extern crate libtest_mimic; use libtest_mimic::{run_tests, Arguments, Outcome}; mod util; - fn main() { // Parse CLI args let args = Arguments::from_args(); // Get the path of the `auto_impl` manifest let dep_path = util::get_dep_path(); + println!("Dep Path: {:?}", dep_path); // Run all tests and exit the application appropriately let tests = util::collect_tests("compile-fail"); @@ -25,7 +25,7 @@ fn main() { if output.status.success() { Outcome::Failed { - msg: Some("Expected compiler error, but file got compiled without error!".into()) + msg: Some("Expected compiler error, but file got compiled without error!".into()), } } else { Outcome::Passed diff --git a/tests/compile-pass/const_ref_generic_types.rs b/tests/compile-pass/const_ref_generic_types.rs new file mode 100644 index 0000000..0dcc0cc --- /dev/null +++ b/tests/compile-pass/const_ref_generic_types.rs @@ -0,0 +1,7 @@ +use auto_impl::auto_impl; + +#[auto_impl(&)] +trait Foo { + fn foo(); + fn bar(&self); +} From 58feffbbf8eaae612c876549d4ddadc49f7c27c9 Mon Sep 17 00:00:00 2001 From: saresend Date: Mon, 1 Oct 2018 22:39:10 -0700 Subject: [PATCH 2/8] fixing T19, and adding generic types on method delegation --- .idea/auto_impl.iml | 8 ++ .idea/codeStyles/Project.xml | 29 ++++ .idea/misc.xml | 12 ++ .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 + .idea/workspace.xml | 263 +++++++++++++++++++++++++++++++++++ .vscode/launch.json | 27 ++++ src/gen.rs | 17 ++- 8 files changed, 368 insertions(+), 2 deletions(-) create mode 100644 .idea/auto_impl.iml create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml create mode 100644 .vscode/launch.json diff --git a/.idea/auto_impl.iml b/.idea/auto_impl.iml new file mode 100644 index 0000000..bc2cd87 --- /dev/null +++ b/.idea/auto_impl.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..30aa626 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..5346672 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..f1e72d0 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..a577132 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,263 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + DEFINITION_ORDER + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - 1537925997955 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - file://$PROJECT_DIR$/src/gen.rs - 456 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index f448bc6..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "(gdb) Launch", - "type": "cppdbg", - "request": "launch", - "program": "enter program name, for example ${workspaceFolder}/a.out", - "args": [], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": true, - "MIMode": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - } - ] -} \ No newline at end of file From ae8708f7c2f72c923ff56a248b43d23dfc8ee5dc Mon Sep 17 00:00:00 2001 From: saresend Date: Mon, 1 Oct 2018 22:45:22 -0700 Subject: [PATCH 4/8] cleaning up comments and printlns --- src/gen.rs | 2 -- tests/compile-fail.rs | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/gen.rs b/src/gen.rs index 175f289..4c5f8d2 100644 --- a/src/gen.rs +++ b/src/gen.rs @@ -451,7 +451,6 @@ fn gen_method_item( 0 => quote! { #proxy_ty_param::#name(#args) }, _ => quote! { #proxy_ty_param::#name::#generic_types(#args) } } - // quote! { #proxy_ty_param::#name(#args) } } // Receiver `self` (by value) @@ -468,7 +467,6 @@ fn gen_method_item( 0 => quote! { (*self).#name(#args) }, _ => quote! { (*self).#name::#generic_types(#args) } } - // quote! { (**self).#name(#args) } } }; diff --git a/tests/compile-fail.rs b/tests/compile-fail.rs index 4e6ab5d..a31d23f 100644 --- a/tests/compile-fail.rs +++ b/tests/compile-fail.rs @@ -3,8 +3,8 @@ //! This file only contains the logic of compile-fail tests. The actual tests //! are in the directory `compile-fail/`. -extern crate build_plan; extern crate libtest_mimic; +extern crate build_plan; use libtest_mimic::{run_tests, Arguments, Outcome}; @@ -16,7 +16,6 @@ fn main() { // Get the path of the `auto_impl` manifest let dep_path = util::get_dep_path(); - println!("Dep Path: {:?}", dep_path); // Run all tests and exit the application appropriately let tests = util::collect_tests("compile-fail"); From 144f32b8ab976428b2a8bfecb1ff009f1c65cb28 Mon Sep 17 00:00:00 2001 From: saresend Date: Mon, 1 Oct 2018 22:47:31 -0700 Subject: [PATCH 5/8] whitespace --- src/gen.rs | 1 - tests/compile-fail.rs | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gen.rs b/src/gen.rs index 4c5f8d2..d743b3a 100644 --- a/src/gen.rs +++ b/src/gen.rs @@ -446,7 +446,6 @@ fn gen_method_item( // No receiver SelfType::None => { // The proxy type is a reference, smartpointer or Box. - match type_param_count { 0 => quote! { #proxy_ty_param::#name(#args) }, _ => quote! { #proxy_ty_param::#name::#generic_types(#args) } diff --git a/tests/compile-fail.rs b/tests/compile-fail.rs index a31d23f..98d14ff 100644 --- a/tests/compile-fail.rs +++ b/tests/compile-fail.rs @@ -10,6 +10,7 @@ use libtest_mimic::{run_tests, Arguments, Outcome}; mod util; + fn main() { // Parse CLI args let args = Arguments::from_args(); @@ -24,7 +25,7 @@ fn main() { if output.status.success() { Outcome::Failed { - msg: Some("Expected compiler error, but file got compiled without error!".into()), + msg: Some("Expected compiler error, but file got compiled without error!".into()) } } else { Outcome::Passed From cfa0d99a934fb2770b76a0647270ffece774ba61 Mon Sep 17 00:00:00 2001 From: saresend Date: Tue, 2 Oct 2018 10:01:46 -0700 Subject: [PATCH 6/8] addressing issues and adding tests --- src/gen.rs | 7 +++++-- ...pes.rs => non_inferred_generic_types_for_all_refs.rs} | 0 tests/compile-pass/non_inferred_generic_types_for_box.rs | 9 +++++++++ ...inferred_generic_types_with_lifetimes_for_all_refs.rs | 9 +++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) rename tests/compile-pass/{const_ref_generic_types.rs => non_inferred_generic_types_for_all_refs.rs} (100%) create mode 100644 tests/compile-pass/non_inferred_generic_types_for_box.rs create mode 100644 tests/compile-pass/non_inferred_generic_types_with_lifetimes_for_all_refs.rs diff --git a/src/gen.rs b/src/gen.rs index d743b3a..6a08635 100644 --- a/src/gen.rs +++ b/src/gen.rs @@ -432,7 +432,7 @@ fn gen_method_item( // Determines the number of generic parameters, and what they are let type_param_count = sig.decl.generics.type_params().count(); - let (_, generic_types, _ ) = &sig.decl.generics.split_for_impl(); + let (_, generic_types, _) = sig.decl.generics.split_for_impl(); // Generate the body of the function. This mainly depends on the self type, // but also on the proxy type. @@ -455,7 +455,10 @@ fn gen_method_item( // Receiver `self` (by value) SelfType::Value => { // The proxy type is a Box. - quote! { (*self).#name(#args) } + match type_param_count { + 0 => quote! { (*self).#name(#args) }, + _ => quote! { (*self).#name::#generic_types(#args) } + } } // `&self` or `&mut self` receiver diff --git a/tests/compile-pass/const_ref_generic_types.rs b/tests/compile-pass/non_inferred_generic_types_for_all_refs.rs similarity index 100% rename from tests/compile-pass/const_ref_generic_types.rs rename to tests/compile-pass/non_inferred_generic_types_for_all_refs.rs diff --git a/tests/compile-pass/non_inferred_generic_types_for_box.rs b/tests/compile-pass/non_inferred_generic_types_for_box.rs new file mode 100644 index 0000000..4f83f24 --- /dev/null +++ b/tests/compile-pass/non_inferred_generic_types_for_box.rs @@ -0,0 +1,9 @@ +use auto_impl::auto_impl; + +#[auto_impl(Box)] +trait Foo { + fn foo(); + fn bar(&self); + fn baz(&mut self); + fn qux(self); +} \ No newline at end of file diff --git a/tests/compile-pass/non_inferred_generic_types_with_lifetimes_for_all_refs.rs b/tests/compile-pass/non_inferred_generic_types_with_lifetimes_for_all_refs.rs new file mode 100644 index 0000000..5066e97 --- /dev/null +++ b/tests/compile-pass/non_inferred_generic_types_with_lifetimes_for_all_refs.rs @@ -0,0 +1,9 @@ +use auto_impl::auto_impl; + +#[auto_impl(&)] +trait Foo { + fn foo(); + fn bar(&self); + fn baz<'a, U>() -> &'a str; + fn qux<'a, 'b, 'c, U, V, T>(&self) -> (&'a str, &'b str, &'c str); +} From ba3af03ba72dfa67544c5a108a986a4cfd173201 Mon Sep 17 00:00:00 2001 From: saresend Date: Wed, 3 Oct 2018 11:33:54 -0700 Subject: [PATCH 7/8] adding turbofish conversion, and fixing some whitespace issues --- examples/names.rs | 3 - examples/refs.rs | 12 +- src/gen.rs | 226 +++++++++--------- .../non_inferred_generic_types_for_box.rs | 2 +- 4 files changed, 114 insertions(+), 129 deletions(-) diff --git a/examples/names.rs b/examples/names.rs index a39da3b..14be78e 100644 --- a/examples/names.rs +++ b/examples/names.rs @@ -26,14 +26,11 @@ //! Thus, the names `H` and `'d` are used. Run `cargo expand --example names` //! to see the output. - // This code is really ugly on purpose... #![allow(non_snake_case, dead_code, unused_variables)] use auto_impl::auto_impl; - - struct X {} trait Z {} diff --git a/examples/refs.rs b/examples/refs.rs index bcd810e..50bf26b 100644 --- a/examples/refs.rs +++ b/examples/refs.rs @@ -2,7 +2,6 @@ use std::fmt::Display; use auto_impl::auto_impl; - /// This trait can be implemented for all reference or pointer types: &, &mut, /// Box, Rc and Arc. /// @@ -47,14 +46,13 @@ fn show_first(c: impl DisplayCollection) { } } - fn main() { let v = vec!["dog", "cat"]; let boxed = Box::new(v.clone()); - show_first(v.clone()); // Vec<&str> (our manual impl) - show_first(&v); // &Vec<&str> (auto-impl) - show_first(&&v); // &&Vec<&str> (works too, of course) - show_first(boxed.clone()); // Box> (auto-impl) - show_first(&boxed); // &Box> + show_first(v.clone()); // Vec<&str> (our manual impl) + show_first(&v); // &Vec<&str> (auto-impl) + show_first(&&v); // &&Vec<&str> (works too, of course) + show_first(boxed.clone()); // Box> (auto-impl) + show_first(&boxed); // &Box> } diff --git a/src/gen.rs b/src/gen.rs index 6a08635..7c21f1d 100644 --- a/src/gen.rs +++ b/src/gen.rs @@ -2,20 +2,18 @@ use crate::proc_macro::Span; use proc_macro2::TokenStream as TokenStream2; use quote::{ToTokens, TokenStreamExt}; use syn::{ - FnArg, Ident, ItemTrait, Lifetime, MethodSig, Pat, PatIdent, TraitItem, TraitItemMethod, - TraitItemType, TraitItemConst, + FnArg, Ident, ItemTrait, Lifetime, MethodSig, Pat, PatIdent, TraitItem, TraitItemConst, + TraitItemMethod, TraitItemType, }; use crate::{ analyze::find_suitable_param_names, - attr::{OurAttr, is_our_attr, parse_our_attr}, + attr::{is_our_attr, parse_our_attr, OurAttr}, diag::{DiagnosticExt, SpanExt}, proxy::ProxyType, - spanned::Spanned + spanned::Spanned, }; - - /// Generates one complete impl of the given trait for each of the given proxy /// types. All impls are returned as token stream. pub(crate) fn gen_impls( @@ -54,7 +52,6 @@ fn header( let trait_ident = &trait_def.ident; let trait_path = quote! { #trait_ident #trait_generics }; - // Here we assemble the parameter list of the impl (the thing in // `impl< ... >`). This is simply the parameter list of the trait with // one or two parameters added. For a trait `trait Foo<'x, 'y, A, B>`, @@ -67,9 +64,10 @@ fn header( let impl_generics = { // Determine if our proxy type needs a lifetime parameter let (mut params, ty_bounds) = match proxy_type { - ProxyType::Ref | ProxyType::RefMut => { - (quote! { #proxy_lt_param, }, quote! { : #proxy_lt_param + #trait_path }) - } + ProxyType::Ref | ProxyType::RefMut => ( + quote! { #proxy_lt_param, }, + quote! { : #proxy_lt_param + #trait_path }, + ), ProxyType::Box | ProxyType::Rc | ProxyType::Arc => (quote!{}, quote! { : #trait_path }), ProxyType::Fn | ProxyType::FnMut | ProxyType::FnOnce => { let fn_bound = gen_fn_type_for_trait(proxy_type, trait_def)?; @@ -80,9 +78,10 @@ fn header( // Append all parameters from the trait. Sadly, `impl_generics` // includes the angle brackets `< >` so we have to remove them like // this. - let mut tts = impl_generics.into_token_stream() + let mut tts = impl_generics + .into_token_stream() .into_iter() - .skip(1) // the opening `<` + .skip(1) // the opening `<` .collect::>(); tts.pop(); // the closing `>` params.append_all(&tts); @@ -99,21 +98,19 @@ fn header( params }; - // The tokens after `for` in the impl header (the type the trait is // implemented for). let self_ty = match *proxy_type { - ProxyType::Ref => quote! { & #proxy_lt_param #proxy_ty_param }, - ProxyType::RefMut => quote! { & #proxy_lt_param mut #proxy_ty_param }, - ProxyType::Arc => quote! { ::std::sync::Arc<#proxy_ty_param> }, - ProxyType::Rc => quote! { ::std::rc::Rc<#proxy_ty_param> }, - ProxyType::Box => quote! { ::std::boxed::Box<#proxy_ty_param> }, - ProxyType::Fn => quote! { #proxy_ty_param }, - ProxyType::FnMut => quote! { #proxy_ty_param }, - ProxyType::FnOnce => quote! { #proxy_ty_param }, + ProxyType::Ref => quote! { & #proxy_lt_param #proxy_ty_param }, + ProxyType::RefMut => quote! { & #proxy_lt_param mut #proxy_ty_param }, + ProxyType::Arc => quote! { ::std::sync::Arc<#proxy_ty_param> }, + ProxyType::Rc => quote! { ::std::rc::Rc<#proxy_ty_param> }, + ProxyType::Box => quote! { ::std::boxed::Box<#proxy_ty_param> }, + ProxyType::Fn => quote! { #proxy_ty_param }, + ProxyType::FnMut => quote! { #proxy_ty_param }, + ProxyType::FnOnce => quote! { #proxy_ty_param }, }; - // Combine everything Ok(quote! { impl<#impl_generics> #trait_path for #self_ty #where_clause @@ -141,10 +138,11 @@ fn gen_fn_type_for_trait( // If this requirement is not satisfied, we emit an error. if method.is_none() || trait_def.items.len() > 1 { - return trait_def.span() + return trait_def + .span() .err( "this trait cannot be auto-implemented for Fn-traits (only traits with exactly \ - one method and no other items are allowed)" + one method and no other items are allowed)", ) .emit_with_attr_note(); } @@ -153,30 +151,32 @@ fn gen_fn_type_for_trait( let method = method.unwrap(); let sig = &method.sig; - // Check for forbidden modifier of the method if let Some(const_token) = sig.constness { - return const_token.span() + return const_token + .span() .err(format!( "the trait '{}' cannot be auto-implemented for Fn-traits: const methods are not \ - allowed", + allowed", trait_def.ident, )) .emit_with_attr_note(); } if let Some(unsafe_token) = &sig.unsafety { - return unsafe_token.span() + return unsafe_token + .span() .err(format!( "the trait '{}' cannot be auto-implemented for Fn-traits: unsafe methods are not \ - allowed", + allowed", trait_def.ident, )) .emit_with_attr_note(); } if let Some(abi_token) = &sig.abi { - return abi_token.span() + return abi_token + .span() .err(format!( "the trait '{}' cannot be implemented for Fn-traits: custom ABIs are not allowed", trait_def.ident, @@ -184,7 +184,6 @@ fn gen_fn_type_for_trait( .emit_with_attr_note(); } - // ======================================================================= // Check if the trait can be implemented for the given proxy type let self_type = SelfType::from_sig(&method.sig); @@ -215,11 +214,8 @@ fn gen_fn_type_for_trait( if let Some((fn_traits, receiver, allowed)) = err { let msg = format!( "the trait '{}' cannot be auto-implemented for {}, because this method has \ - {} receiver{}", - trait_def.ident, - fn_traits, - receiver, - allowed, + {} receiver{}", + trait_def.ident, fn_traits, receiver, allowed, ); return method.sig.span().err(msg).emit_with_attr_note(); @@ -283,7 +279,6 @@ fn gen_fn_type_for_trait( } } - Ok(quote! { for< #(#local_lifetimes),* > #fn_name (#arg_types) #ret }) @@ -296,37 +291,37 @@ fn gen_items( trait_def: &ItemTrait, proxy_ty_param: &Ident, ) -> Result, ()> { - trait_def.items.iter().map(|item| { - match item { - TraitItem::Const(c) => { - gen_const_item(proxy_type, c, trait_def, proxy_ty_param) - } - TraitItem::Method(method) => { - gen_method_item(proxy_type, method, trait_def, proxy_ty_param) - } - TraitItem::Type(ty) => { - gen_type_item(proxy_type, ty, trait_def, proxy_ty_param) - } - TraitItem::Macro(mac) => { - // We cannot resolve the macro invocation and thus cannot know - // if it adds additional items to the trait. Thus, we have to - // give up. - mac.span() - .err( - "traits with macro invocations in their bodies are not \ - supported by auto_impl" - ) - .emit_with_attr_note() - }, - TraitItem::Verbatim(v) => { - // I don't quite know when this happens, but it's better to - // notify the user with a nice error instead of panicking. - v.span() - .err("unexpected 'verbatim'-item (auto-impl doesn't know how to handle it)") - .emit_with_attr_note() + trait_def + .items + .iter() + .map(|item| { + match item { + TraitItem::Const(c) => gen_const_item(proxy_type, c, trait_def, proxy_ty_param), + TraitItem::Method(method) => { + gen_method_item(proxy_type, method, trait_def, proxy_ty_param) + } + TraitItem::Type(ty) => gen_type_item(proxy_type, ty, trait_def, proxy_ty_param), + TraitItem::Macro(mac) => { + // We cannot resolve the macro invocation and thus cannot know + // if it adds additional items to the trait. Thus, we have to + // give up. + mac.span() + .err( + "traits with macro invocations in their bodies are not \ + supported by auto_impl", + ) + .emit_with_attr_note() + } + TraitItem::Verbatim(v) => { + // I don't quite know when this happens, but it's better to + // notify the user with a nice error instead of panicking. + v.span() + .err("unexpected 'verbatim'-item (auto-impl doesn't know how to handle it)") + .emit_with_attr_note() + } } - } - }).collect() + }) + .collect() } /// Generates the implementation of an associated const item described by @@ -342,11 +337,12 @@ fn gen_const_item( ) -> Result { // A trait with associated consts cannot be implemented for Fn* types. if proxy_type.is_fn() { - return item.span() + return item + .span() .err(format!( "the trait `{}` cannot be auto-implemented for Fn-traits, because it has \ - associated consts (only traits with a single method can be implemented \ - for Fn-traits)", + associated consts (only traits with a single method can be implemented \ + for Fn-traits)", trait_def.ident, )) .emit_with_attr_note(); @@ -374,11 +370,12 @@ fn gen_type_item( ) -> Result { // A trait with associated types cannot be implemented for Fn* types. if proxy_type.is_fn() { - return item.span() + return item + .span() .err(format!( "the trait `{}` cannot be auto-implemented for Fn-traits, because it has \ - associated types (only traits with a single method can be implemented \ - for Fn-traits)", + associated types (only traits with a single method can be implemented \ + for Fn-traits)", trait_def.ident, )) .emit_with_attr_note(); @@ -410,10 +407,12 @@ fn gen_method_item( if item.default.is_some() { return Ok(TokenStream2::new()); } else { - return item.sig.span() + return item + .sig + .span() .err(format!( "the method `{}` has the attribute `keep_default_for` but is not a default \ - method (no body is provided)", + method (no body is provided)", item.sig.ident, )) .emit_with_attr_note(); @@ -430,9 +429,9 @@ fn gen_method_item( // Generate the list of argument used to call the method. let args = get_arg_list(sig.decl.inputs.iter())?; - // Determines the number of generic parameters, and what they are - let type_param_count = sig.decl.generics.type_params().count(); + // Builds turbofish with generic types let (_, generic_types, _) = sig.decl.generics.split_for_impl(); + let generic_types = generic_types.as_turbofish(); // Generate the body of the function. This mainly depends on the self type, // but also on the proxy type. @@ -446,29 +445,20 @@ fn gen_method_item( // No receiver SelfType::None => { // The proxy type is a reference, smartpointer or Box. - match type_param_count { - 0 => quote! { #proxy_ty_param::#name(#args) }, - _ => quote! { #proxy_ty_param::#name::#generic_types(#args) } - } + quote! { #proxy_ty_param::#name #generic_types(#args) } } // Receiver `self` (by value) SelfType::Value => { // The proxy type is a Box. - match type_param_count { - 0 => quote! { (*self).#name(#args) }, - _ => quote! { (*self).#name::#generic_types(#args) } - } + quote! { (*self).#name#generic_types(#args) } } // `&self` or `&mut self` receiver SelfType::Ref | SelfType::Mut => { // The proxy type could be anything in the `Ref` case, and `&mut` // or Box in the `Mut` case. - match type_param_count { - 0 => quote! { (*self).#name(#args) }, - _ => quote! { (*self).#name::#generic_types(#args) } - } + quote! { (*self).#name#generic_types(#args) } } }; @@ -477,7 +467,6 @@ fn gen_method_item( Ok(quote! { #sig { #body }}) } - #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum SelfType { None, @@ -515,29 +504,24 @@ fn check_receiver_compatible( sig_span: Span, ) -> Result<(), ()> { match (proxy_type, self_arg) { - (ProxyType::Ref, SelfType::Mut) - | (ProxyType::Ref, SelfType::Value) => { - sig_span - .err(format!( - "the trait `{}` cannot be auto-implemented for immutable references, because \ - this method has a `{}` receiver (only `&self` and no receiver are \ - allowed)", - trait_name, - self_arg.as_str().unwrap(), - )) - .emit_with_attr_note() - } + (ProxyType::Ref, SelfType::Mut) | (ProxyType::Ref, SelfType::Value) => sig_span + .err(format!( + "the trait `{}` cannot be auto-implemented for immutable references, because \ + this method has a `{}` receiver (only `&self` and no receiver are \ + allowed)", + trait_name, + self_arg.as_str().unwrap(), + )) + .emit_with_attr_note(), - (ProxyType::RefMut, SelfType::Value) => { - sig_span - .err(format!( - "the trait `{}` cannot be auto-implemented for mutable references, because \ - this method has a `self` receiver (only `&self`, `&mut self` and no \ - receiver are allowed)", - trait_name, - )) - .emit_with_attr_note() - } + (ProxyType::RefMut, SelfType::Value) => sig_span + .err(format!( + "the trait `{}` cannot be auto-implemented for mutable references, because \ + this method has a `self` receiver (only `&self`, `&mut self` and no \ + receiver are allowed)", + trait_name, + )) + .emit_with_attr_note(), (ProxyType::Rc, SelfType::Mut) | (ProxyType::Rc, SelfType::Value) @@ -552,8 +536,8 @@ fn check_receiver_compatible( sig_span .err(format!( "the trait `{}` cannot be auto-implemented for {}-smartpointer, because \ - this method has a `{}` receiver (only `&self` and no receiver are \ - allowed)", + this method has a `{}` receiver (only `&self` and no receiver are \ + allowed)", trait_name, ptr_name, self_arg.as_str().unwrap(), @@ -593,10 +577,12 @@ fn get_arg_list<'a>(inputs: impl Iterator) -> Result(inputs: impl Iterator) -> Result Result { // Get an iterator of just the attribute we are interested in. - let mut it = m.attrs.iter() + let mut it = m + .attrs + .iter() .filter(|attr| is_our_attr(attr)) .map(|attr| parse_our_attr(&attr)); @@ -644,7 +632,9 @@ fn should_keep_default_for(m: &TraitItemMethod, proxy_type: &ProxyType) -> Resul // Check if there is another such attribute (which we disallow) if it.next().is_some() { - return m.sig.span() + return m + .sig + .span() .err("found two `keep_default_for` attributes on one method") .emit_with_attr_note(); } diff --git a/tests/compile-pass/non_inferred_generic_types_for_box.rs b/tests/compile-pass/non_inferred_generic_types_for_box.rs index 4f83f24..00be69a 100644 --- a/tests/compile-pass/non_inferred_generic_types_for_box.rs +++ b/tests/compile-pass/non_inferred_generic_types_for_box.rs @@ -6,4 +6,4 @@ trait Foo { fn bar(&self); fn baz(&mut self); fn qux(self); -} \ No newline at end of file +} From 5cc2b42d09ebdbfccc00f50e4b35d18d604c42e2 Mon Sep 17 00:00:00 2001 From: Lukas Kalbertodt Date: Thu, 4 Oct 2018 15:26:14 +0200 Subject: [PATCH 8/8] Revert most rustfmt formatting from previous commit --- examples/names.rs | 3 + examples/refs.rs | 12 +-- src/gen.rs | 199 +++++++++++++++++++++++----------------------- 3 files changed, 110 insertions(+), 104 deletions(-) diff --git a/examples/names.rs b/examples/names.rs index 14be78e..a39da3b 100644 --- a/examples/names.rs +++ b/examples/names.rs @@ -26,11 +26,14 @@ //! Thus, the names `H` and `'d` are used. Run `cargo expand --example names` //! to see the output. + // This code is really ugly on purpose... #![allow(non_snake_case, dead_code, unused_variables)] use auto_impl::auto_impl; + + struct X {} trait Z {} diff --git a/examples/refs.rs b/examples/refs.rs index 50bf26b..bcd810e 100644 --- a/examples/refs.rs +++ b/examples/refs.rs @@ -2,6 +2,7 @@ use std::fmt::Display; use auto_impl::auto_impl; + /// This trait can be implemented for all reference or pointer types: &, &mut, /// Box, Rc and Arc. /// @@ -46,13 +47,14 @@ fn show_first(c: impl DisplayCollection) { } } + fn main() { let v = vec!["dog", "cat"]; let boxed = Box::new(v.clone()); - show_first(v.clone()); // Vec<&str> (our manual impl) - show_first(&v); // &Vec<&str> (auto-impl) - show_first(&&v); // &&Vec<&str> (works too, of course) - show_first(boxed.clone()); // Box> (auto-impl) - show_first(&boxed); // &Box> + show_first(v.clone()); // Vec<&str> (our manual impl) + show_first(&v); // &Vec<&str> (auto-impl) + show_first(&&v); // &&Vec<&str> (works too, of course) + show_first(boxed.clone()); // Box> (auto-impl) + show_first(&boxed); // &Box> } diff --git a/src/gen.rs b/src/gen.rs index 7c21f1d..f725b4e 100644 --- a/src/gen.rs +++ b/src/gen.rs @@ -14,6 +14,8 @@ use crate::{ spanned::Spanned, }; + + /// Generates one complete impl of the given trait for each of the given proxy /// types. All impls are returned as token stream. pub(crate) fn gen_impls( @@ -52,6 +54,7 @@ fn header( let trait_ident = &trait_def.ident; let trait_path = quote! { #trait_ident #trait_generics }; + // Here we assemble the parameter list of the impl (the thing in // `impl< ... >`). This is simply the parameter list of the trait with // one or two parameters added. For a trait `trait Foo<'x, 'y, A, B>`, @@ -64,10 +67,9 @@ fn header( let impl_generics = { // Determine if our proxy type needs a lifetime parameter let (mut params, ty_bounds) = match proxy_type { - ProxyType::Ref | ProxyType::RefMut => ( - quote! { #proxy_lt_param, }, - quote! { : #proxy_lt_param + #trait_path }, - ), + ProxyType::Ref | ProxyType::RefMut => { + (quote! { #proxy_lt_param, }, quote! { : #proxy_lt_param + #trait_path }) + } ProxyType::Box | ProxyType::Rc | ProxyType::Arc => (quote!{}, quote! { : #trait_path }), ProxyType::Fn | ProxyType::FnMut | ProxyType::FnOnce => { let fn_bound = gen_fn_type_for_trait(proxy_type, trait_def)?; @@ -78,10 +80,9 @@ fn header( // Append all parameters from the trait. Sadly, `impl_generics` // includes the angle brackets `< >` so we have to remove them like // this. - let mut tts = impl_generics - .into_token_stream() + let mut tts = impl_generics.into_token_stream() .into_iter() - .skip(1) // the opening `<` + .skip(1) // the opening `<` .collect::>(); tts.pop(); // the closing `>` params.append_all(&tts); @@ -98,19 +99,21 @@ fn header( params }; + // The tokens after `for` in the impl header (the type the trait is // implemented for). let self_ty = match *proxy_type { - ProxyType::Ref => quote! { & #proxy_lt_param #proxy_ty_param }, - ProxyType::RefMut => quote! { & #proxy_lt_param mut #proxy_ty_param }, - ProxyType::Arc => quote! { ::std::sync::Arc<#proxy_ty_param> }, - ProxyType::Rc => quote! { ::std::rc::Rc<#proxy_ty_param> }, - ProxyType::Box => quote! { ::std::boxed::Box<#proxy_ty_param> }, - ProxyType::Fn => quote! { #proxy_ty_param }, - ProxyType::FnMut => quote! { #proxy_ty_param }, - ProxyType::FnOnce => quote! { #proxy_ty_param }, + ProxyType::Ref => quote! { & #proxy_lt_param #proxy_ty_param }, + ProxyType::RefMut => quote! { & #proxy_lt_param mut #proxy_ty_param }, + ProxyType::Arc => quote! { ::std::sync::Arc<#proxy_ty_param> }, + ProxyType::Rc => quote! { ::std::rc::Rc<#proxy_ty_param> }, + ProxyType::Box => quote! { ::std::boxed::Box<#proxy_ty_param> }, + ProxyType::Fn => quote! { #proxy_ty_param }, + ProxyType::FnMut => quote! { #proxy_ty_param }, + ProxyType::FnOnce => quote! { #proxy_ty_param }, }; + // Combine everything Ok(quote! { impl<#impl_generics> #trait_path for #self_ty #where_clause @@ -138,11 +141,10 @@ fn gen_fn_type_for_trait( // If this requirement is not satisfied, we emit an error. if method.is_none() || trait_def.items.len() > 1 { - return trait_def - .span() + return trait_def.span() .err( "this trait cannot be auto-implemented for Fn-traits (only traits with exactly \ - one method and no other items are allowed)", + one method and no other items are allowed)" ) .emit_with_attr_note(); } @@ -151,32 +153,30 @@ fn gen_fn_type_for_trait( let method = method.unwrap(); let sig = &method.sig; + // Check for forbidden modifier of the method if let Some(const_token) = sig.constness { - return const_token - .span() + return const_token.span() .err(format!( "the trait '{}' cannot be auto-implemented for Fn-traits: const methods are not \ - allowed", + allowed", trait_def.ident, )) .emit_with_attr_note(); } if let Some(unsafe_token) = &sig.unsafety { - return unsafe_token - .span() + return unsafe_token.span() .err(format!( "the trait '{}' cannot be auto-implemented for Fn-traits: unsafe methods are not \ - allowed", + allowed", trait_def.ident, )) .emit_with_attr_note(); } if let Some(abi_token) = &sig.abi { - return abi_token - .span() + return abi_token.span() .err(format!( "the trait '{}' cannot be implemented for Fn-traits: custom ABIs are not allowed", trait_def.ident, @@ -184,6 +184,7 @@ fn gen_fn_type_for_trait( .emit_with_attr_note(); } + // ======================================================================= // Check if the trait can be implemented for the given proxy type let self_type = SelfType::from_sig(&method.sig); @@ -214,8 +215,11 @@ fn gen_fn_type_for_trait( if let Some((fn_traits, receiver, allowed)) = err { let msg = format!( "the trait '{}' cannot be auto-implemented for {}, because this method has \ - {} receiver{}", - trait_def.ident, fn_traits, receiver, allowed, + {} receiver{}", + trait_def.ident, + fn_traits, + receiver, + allowed, ); return method.sig.span().err(msg).emit_with_attr_note(); @@ -279,6 +283,7 @@ fn gen_fn_type_for_trait( } } + Ok(quote! { for< #(#local_lifetimes),* > #fn_name (#arg_types) #ret }) @@ -291,37 +296,37 @@ fn gen_items( trait_def: &ItemTrait, proxy_ty_param: &Ident, ) -> Result, ()> { - trait_def - .items - .iter() - .map(|item| { - match item { - TraitItem::Const(c) => gen_const_item(proxy_type, c, trait_def, proxy_ty_param), - TraitItem::Method(method) => { - gen_method_item(proxy_type, method, trait_def, proxy_ty_param) - } - TraitItem::Type(ty) => gen_type_item(proxy_type, ty, trait_def, proxy_ty_param), - TraitItem::Macro(mac) => { - // We cannot resolve the macro invocation and thus cannot know - // if it adds additional items to the trait. Thus, we have to - // give up. - mac.span() - .err( - "traits with macro invocations in their bodies are not \ - supported by auto_impl", - ) - .emit_with_attr_note() - } - TraitItem::Verbatim(v) => { - // I don't quite know when this happens, but it's better to - // notify the user with a nice error instead of panicking. - v.span() - .err("unexpected 'verbatim'-item (auto-impl doesn't know how to handle it)") - .emit_with_attr_note() - } + trait_def.items.iter().map(|item| { + match item { + TraitItem::Const(c) => { + gen_const_item(proxy_type, c, trait_def, proxy_ty_param) } - }) - .collect() + TraitItem::Method(method) => { + gen_method_item(proxy_type, method, trait_def, proxy_ty_param) + } + TraitItem::Type(ty) => { + gen_type_item(proxy_type, ty, trait_def, proxy_ty_param) + } + TraitItem::Macro(mac) => { + // We cannot resolve the macro invocation and thus cannot know + // if it adds additional items to the trait. Thus, we have to + // give up. + mac.span() + .err( + "traits with macro invocations in their bodies are not \ + supported by auto_impl" + ) + .emit_with_attr_note() + }, + TraitItem::Verbatim(v) => { + // I don't quite know when this happens, but it's better to + // notify the user with a nice error instead of panicking. + v.span() + .err("unexpected 'verbatim'-item (auto-impl doesn't know how to handle it)") + .emit_with_attr_note() + } + } + }).collect() } /// Generates the implementation of an associated const item described by @@ -337,12 +342,11 @@ fn gen_const_item( ) -> Result { // A trait with associated consts cannot be implemented for Fn* types. if proxy_type.is_fn() { - return item - .span() + return item.span() .err(format!( "the trait `{}` cannot be auto-implemented for Fn-traits, because it has \ - associated consts (only traits with a single method can be implemented \ - for Fn-traits)", + associated consts (only traits with a single method can be implemented \ + for Fn-traits)", trait_def.ident, )) .emit_with_attr_note(); @@ -370,12 +374,11 @@ fn gen_type_item( ) -> Result { // A trait with associated types cannot be implemented for Fn* types. if proxy_type.is_fn() { - return item - .span() + return item.span() .err(format!( "the trait `{}` cannot be auto-implemented for Fn-traits, because it has \ - associated types (only traits with a single method can be implemented \ - for Fn-traits)", + associated types (only traits with a single method can be implemented \ + for Fn-traits)", trait_def.ident, )) .emit_with_attr_note(); @@ -407,12 +410,10 @@ fn gen_method_item( if item.default.is_some() { return Ok(TokenStream2::new()); } else { - return item - .sig - .span() + return item.sig.span() .err(format!( "the method `{}` has the attribute `keep_default_for` but is not a default \ - method (no body is provided)", + method (no body is provided)", item.sig.ident, )) .emit_with_attr_note(); @@ -467,6 +468,7 @@ fn gen_method_item( Ok(quote! { #sig { #body }}) } + #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum SelfType { None, @@ -504,24 +506,29 @@ fn check_receiver_compatible( sig_span: Span, ) -> Result<(), ()> { match (proxy_type, self_arg) { - (ProxyType::Ref, SelfType::Mut) | (ProxyType::Ref, SelfType::Value) => sig_span - .err(format!( - "the trait `{}` cannot be auto-implemented for immutable references, because \ - this method has a `{}` receiver (only `&self` and no receiver are \ - allowed)", - trait_name, - self_arg.as_str().unwrap(), - )) - .emit_with_attr_note(), + (ProxyType::Ref, SelfType::Mut) + | (ProxyType::Ref, SelfType::Value) => { + sig_span + .err(format!( + "the trait `{}` cannot be auto-implemented for immutable references, because \ + this method has a `{}` receiver (only `&self` and no receiver are \ + allowed)", + trait_name, + self_arg.as_str().unwrap(), + )) + .emit_with_attr_note() + } - (ProxyType::RefMut, SelfType::Value) => sig_span - .err(format!( - "the trait `{}` cannot be auto-implemented for mutable references, because \ - this method has a `self` receiver (only `&self`, `&mut self` and no \ - receiver are allowed)", - trait_name, - )) - .emit_with_attr_note(), + (ProxyType::RefMut, SelfType::Value) => { + sig_span + .err(format!( + "the trait `{}` cannot be auto-implemented for mutable references, because \ + this method has a `self` receiver (only `&self`, `&mut self` and no \ + receiver are allowed)", + trait_name, + )) + .emit_with_attr_note() + } (ProxyType::Rc, SelfType::Mut) | (ProxyType::Rc, SelfType::Value) @@ -536,8 +543,8 @@ fn check_receiver_compatible( sig_span .err(format!( "the trait `{}` cannot be auto-implemented for {}-smartpointer, because \ - this method has a `{}` receiver (only `&self` and no receiver are \ - allowed)", + this method has a `{}` receiver (only `&self` and no receiver are \ + allowed)", trait_name, ptr_name, self_arg.as_str().unwrap(), @@ -577,12 +584,10 @@ fn get_arg_list<'a>(inputs: impl Iterator) -> Result(inputs: impl Iterator) -> Result Result { // Get an iterator of just the attribute we are interested in. - let mut it = m - .attrs - .iter() + let mut it = m.attrs.iter() .filter(|attr| is_our_attr(attr)) .map(|attr| parse_our_attr(&attr)); @@ -632,9 +635,7 @@ fn should_keep_default_for(m: &TraitItemMethod, proxy_type: &ProxyType) -> Resul // Check if there is another such attribute (which we disallow) if it.next().is_some() { - return m - .sig - .span() + return m.sig.span() .err("found two `keep_default_for` attributes on one method") .emit_with_attr_note(); }