diff --git a/Cargo.toml b/Cargo.toml index 1a5eaa6..bf861c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,9 +17,10 @@ appveyor = { repository = "mcarton/rust-derivative" } proc-macro = true [dependencies] -itertools = "~0.5" -quote = "^0.3" -syn = { version = "0.10", features = ["aster", "full", "visit"] } +itertools = "~0.7" +quote = "^0.6" +proc-macro2 = "0.4" +syn = { version = "0.15", features = ["full", "visit", "extra-traits"] } compiletest_rs = { version = "^0.3", optional = true } [features] diff --git a/rustfmt.toml b/rustfmt.toml index 34132f0..2c1db53 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,3 +1,3 @@ -reorder_imported_names = true -reorder_imports = true -write_mode = "Overwrite" +# reorder_imported_names = true +# reorder_imports = true +# write_mode = "Overwrite" diff --git a/src/ast.rs b/src/ast.rs index 9b94eed..9a584c0 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -27,7 +27,7 @@ pub struct Variant<'a> { pub struct Field<'a> { pub attrs: attr::Field, pub ident: Option, - pub ty: &'a syn::Ty, + pub ty: &'a syn::Type, } #[derive(Clone, Copy, Debug)] @@ -38,17 +38,18 @@ pub enum Style { } impl<'a> Input<'a> { - pub fn from_ast(item: &'a syn::MacroInput) -> Result, String> { + pub fn from_ast(item: &'a syn::DeriveInput) -> Result, String> { let attrs = try!(attr::Input::from_ast(&item.attrs)); - let body = match item.body { - syn::Body::Enum(ref variants) => { + let body = match item.data { + syn::Data::Enum(syn::DataEnum { ref variants, .. }) => { Body::Enum(try!(enum_from_ast(variants))) } - syn::Body::Struct(ref variant_data) => { - let (style, fields) = try!(struct_from_ast(variant_data)); + syn::Data::Struct(syn::DataStruct { ref fields, .. }) => { + let (style, fields) = try!(struct_from_ast(fields)); Body::Struct(style, fields) } + _ => panic!("Unsupported data type"), }; Ok(Input { @@ -63,22 +64,22 @@ impl<'a> Input<'a> { impl<'a> Body<'a> { pub fn all_fields(&self) -> Vec<&Field> { match *self { - Body::Enum(ref variants) => { - variants - .iter() - .flat_map(|variant| variant.fields.iter()) - .collect() - } + Body::Enum(ref variants) => variants + .iter() + .flat_map(|variant| variant.fields.iter()) + .collect(), Body::Struct(_, ref fields) => fields.iter().collect(), } } } -fn enum_from_ast<'a>(variants: &'a [syn::Variant]) -> Result>, String> { +fn enum_from_ast<'a>( + variants: &'a syn::punctuated::Punctuated, +) -> Result>, String> { variants .iter() .map(|variant| { - let (style, fields) = try!(struct_from_ast(&variant.data)); + let (style, fields) = try!(struct_from_ast(&variant.fields)); Ok(Variant { attrs: try!(attr::Input::from_ast(&variant.attrs)), fields: fields, @@ -89,21 +90,19 @@ fn enum_from_ast<'a>(variants: &'a [syn::Variant]) -> Result>, S .collect() } -fn struct_from_ast<'a>(data: &'a syn::VariantData) -> Result<(Style, Vec>), String> { - match *data { - syn::VariantData::Struct(ref fields) => { - Ok((Style::Struct, try!(fields_from_ast(fields)))) - } - syn::VariantData::Tuple(ref fields) => { - Ok((Style::Tuple, try!(fields_from_ast(fields)))) - } - syn::VariantData::Unit => { - Ok((Style::Unit, Vec::new())) +fn struct_from_ast<'a>(fields: &'a syn::Fields) -> Result<(Style, Vec>), String> { + match *fields { + syn::Fields::Named(ref fields) => Ok((Style::Struct, try!(fields_from_ast(&fields.named)))), + syn::Fields::Unnamed(ref fields) => { + Ok((Style::Tuple, try!(fields_from_ast(&fields.unnamed)))) } + syn::Fields::Unit => Ok((Style::Unit, Vec::new())), } } -fn fields_from_ast<'a>(fields: &'a [syn::Field]) -> Result>, String> { +fn fields_from_ast<'a>( + fields: &'a syn::punctuated::Punctuated, +) -> Result>, String> { fields .iter() .map(|field| { diff --git a/src/attr.rs b/src/attr.rs index da3a9b1..6ca83fb 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use syn; /// Represent the `derivative` attributes on the input type (`struct`/`enum`). @@ -152,10 +154,10 @@ pub struct FieldPartialEq { macro_rules! for_all_attr { (for ($name:ident, $value:ident) in $attrs:expr; $($body:tt)*) => { - for meta_items in $attrs.iter().filter_map(derivative_attribute) { + for meta_items in $attrs.iter().filter_map(|attr| derivative_attribute(attr.parse_meta())) { for metaitem in meta_items.iter().map(read_items) { let MetaItem($name, $value) = try!(metaitem); - match $name { + match $name.to_string().as_ref() { $($body)* _ => return Err(format!("unknown trait `{}`", $name)), } @@ -178,14 +180,31 @@ macro_rules! match_attributes { (for $value:ident in $values:expr; $($body:tt)* ) => { for (name, $value) in $values { + let value = $value.as_ref().map(|v| v.as_ref()); match name { - $($body)* - _ => return Err(format!("unknown attribute `{}`", name)), + Some(ident) => { + match ident.to_string().as_ref() { + $($body)* + _ => return Err(format!("unknown attribute `{}`", ident)), + } + } + None => { + match value.expect("Expected value to be passed") { + $($body)* + _ => return Err("unknown attribute".to_string()), + } + } } } }; } +macro_rules! opt_string_to_str { + ($value:ident) => { + $value.as_ref().map(|v| v.as_ref()) + }; +} + impl Input { /// Parse the `derivative` attributes on a type. pub fn from_ast(attrs: &[syn::Attribute]) -> Result { @@ -198,13 +217,14 @@ impl Input { clone.rustc_copy_clone_marker = attrs .iter() - .any(|attr| attr.value.name() == "rustc_copy_clone_marker"); + .filter_map(|attr| attr.parse_meta().ok()) + .any(|meta| meta.name().to_string() == "rustc_copy_clone_marker"); match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut clone.bounds, value)), + "bound" => try!(parse_bound(&mut clone.bounds, opt_string_to_str!(value))), "clone_from" => { - clone.clone_from = try!(parse_boolean_meta_item(&value, true, "clone_from")); + clone.clone_from = try!(parse_boolean_meta_item(&opt_string_to_str!(value), true, "clone_from")); } } @@ -214,11 +234,15 @@ impl Input { let mut copy = input.copy.take().unwrap_or_default(); for attr in attrs { - if let syn::MetaItem::List(ref name, ref traits) = attr.value { - fn is_clone(elem: &syn::NestedMetaItem) -> bool { + if let Ok(syn::Meta::List(syn::MetaList{ + ident: ref name, + nested: ref traits, + .. + })) = attr.parse_meta() { + fn is_clone(elem: &syn::NestedMeta) -> bool { match *elem { - syn::NestedMetaItem::MetaItem(ref mi) => mi.name() == "Clone", - syn::NestedMetaItem::Literal(..) => false, + syn::NestedMeta::Meta(ref mi) => mi.name() == "Clone", + syn::NestedMeta::Literal(..) => false, } } if name == "derive" && traits.iter().any(is_clone) { @@ -229,7 +253,7 @@ impl Input { match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut copy.bounds, value)), + "bound" => try!(parse_bound(&mut copy.bounds, opt_string_to_str!(value))), } input.copy = Some(copy); @@ -238,9 +262,9 @@ impl Input { match_attributes! { let Some(debug) = input.debug; for value in values; - "bound" => try!(parse_bound(&mut debug.bounds, value)), + "bound" => try!(parse_bound(&mut debug.bounds, opt_string_to_str!(value))), "transparent" => { - debug.transparent = try!(parse_boolean_meta_item(&value, true, "transparent")); + debug.transparent = try!(parse_boolean_meta_item(&opt_string_to_str!(value), true, "transparent")); } } } @@ -248,9 +272,9 @@ impl Input { match_attributes! { let Some(default) = input.default; for value in values; - "bound" => try!(parse_bound(&mut default.bounds, value)), + "bound" => try!(parse_bound(&mut default.bounds, opt_string_to_str!(value))), "new" => { - default.new = try!(parse_boolean_meta_item(&value, true, "new")); + default.new = try!(parse_boolean_meta_item(&opt_string_to_str!(value), true, "new")); } } } @@ -258,23 +282,23 @@ impl Input { match_attributes! { let Some(eq) = input.eq; for value in values; - "bound" => try!(parse_bound(&mut eq.bounds, value)), + "bound" => try!(parse_bound(&mut eq.bounds, opt_string_to_str!(value))), } } "Hash" => { match_attributes! { let Some(hash) = input.hash; for value in values; - "bound" => try!(parse_bound(&mut hash.bounds, value)), + "bound" => try!(parse_bound(&mut hash.bounds, opt_string_to_str!(value))), } } "PartialEq" => { match_attributes! { let Some(partial_eq) = input.partial_eq; for value in values; - "bound" => try!(parse_bound(&mut partial_eq.bounds, value)), + "bound" => try!(parse_bound(&mut partial_eq.bounds, opt_string_to_str!(value))), "feature_allow_slow_enum" => { - partial_eq.on_enum = try!(parse_boolean_meta_item(&value, true, "feature_allow_slow_enum")); + partial_eq.on_enum = try!(parse_boolean_meta_item(&opt_string_to_str!(value), true, "feature_allow_slow_enum")); } } } @@ -284,7 +308,9 @@ impl Input { } pub fn clone_bound(&self) -> Option<&[syn::WherePredicate]> { - self.clone.as_ref().map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice)) + self.clone + .as_ref() + .map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice)) } pub fn clone_from(&self) -> bool { @@ -292,7 +318,9 @@ impl Input { } pub fn copy_bound(&self) -> Option<&[syn::WherePredicate]> { - self.copy.as_ref().map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice)) + self.copy + .as_ref() + .map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice)) } pub fn derives_clone(&self) -> bool { @@ -300,7 +328,9 @@ impl Input { } pub fn debug_bound(&self) -> Option<&[syn::WherePredicate]> { - self.debug.as_ref().map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice)) + self.debug + .as_ref() + .map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice)) } pub fn debug_transparent(&self) -> bool { @@ -308,23 +338,33 @@ impl Input { } pub fn default_bound(&self) -> Option<&[syn::WherePredicate]> { - self.default.as_ref().map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice)) + self.default + .as_ref() + .map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice)) } pub fn eq_bound(&self) -> Option<&[syn::WherePredicate]> { - self.eq.as_ref().map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice)) + self.eq + .as_ref() + .map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice)) } pub fn hash_bound(&self) -> Option<&[syn::WherePredicate]> { - self.hash.as_ref().map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice)) + self.hash + .as_ref() + .map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice)) } pub fn rustc_copy_clone_marker(&self) -> bool { - self.clone.as_ref().map_or(false, |d| d.rustc_copy_clone_marker) + self.clone + .as_ref() + .map_or(false, |d| d.rustc_copy_clone_marker) } pub fn partial_eq_bound(&self) -> Option<&[syn::WherePredicate]> { - self.partial_eq.as_ref().map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice)) + self.partial_eq + .as_ref() + .map_or(None, |d| d.bounds.as_ref().map(Vec::as_slice)) } pub fn partial_eq_on_enum(&self) -> bool { @@ -342,65 +382,65 @@ impl Field { "Clone" => { match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut out.clone.bounds, value)), + "bound" => try!(parse_bound(&mut out.clone.bounds, opt_string_to_str!(value))), "clone_with" => { - let path = try!(value.ok_or_else(|| "`clone_with` needs a value".to_string())); - out.clone.clone_with = Some(try!(syn::parse_path(path))); + let path = try!(opt_string_to_str!(value).ok_or_else(|| "`clone_with` needs a value".to_string())); + out.clone.clone_with = Some(try!(parse_str_to_type::(&path))); } } } "Debug" => { match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut out.debug.bounds, value)), + "bound" => try!(parse_bound(&mut out.debug.bounds, opt_string_to_str!(value))), "format_with" => { - let path = try!(value.ok_or_else(|| "`format_with` needs a value".to_string())); - out.debug.format_with = Some(try!(syn::parse_path(path))); + let path = try!(opt_string_to_str!(value).ok_or_else(|| "`format_with` needs a value".to_string())); + out.debug.format_with = Some(try!(parse_str_to_type::(&path))); } "ignore" => { - out.debug.ignore = try!(parse_boolean_meta_item(&value, true, "ignore")); + out.debug.ignore = try!(parse_boolean_meta_item(&opt_string_to_str!(value), true, "ignore")); } } } "Default" => { match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut out.default.bounds, value)), + "bound" => try!(parse_bound(&mut out.default.bounds, opt_string_to_str!(value))), "value" => { - let value = try!(value.ok_or_else(|| "`value` needs a value".to_string())); - out.default.value = Some(try!(syn::parse_expr(value))); + let value = try!(opt_string_to_str!(value).ok_or_else(|| "`value` needs a value".to_string())); + out.default.value = Some(try!(parse_str_to_type(&value))); } } } "Eq" => { match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut out.eq_bound, value)), + "bound" => try!(parse_bound(&mut out.eq_bound, opt_string_to_str!(value))), } } "Hash" => { match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut out.hash.bounds, value)), + "bound" => try!(parse_bound(&mut out.hash.bounds, opt_string_to_str!(value))), "hash_with" => { - let path = try!(value.ok_or_else(|| "`hash_with` needs a value".to_string())); - out.hash.hash_with = Some(try!(syn::parse_path(path))); + let path = try!(opt_string_to_str!(value).ok_or_else(|| "`hash_with` needs a value".to_string())); + out.hash.hash_with = Some(try!(parse_str_to_type::(&path))); } "ignore" => { - out.hash.ignore = try!(parse_boolean_meta_item(&value, true, "ignore")); + out.hash.ignore = try!(parse_boolean_meta_item(&opt_string_to_str!(value), true, "ignore")); } } } "PartialEq" => { match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut out.partial_eq.bounds, value)), + "bound" => try!(parse_bound(&mut out.partial_eq.bounds, opt_string_to_str!(value))), "compare_with" => { - let path = try!(value.ok_or_else(|| "`compare_with` needs a value".to_string())); - out.partial_eq.compare_with = Some(try!(syn::parse_path(path))); + let path = try!(opt_string_to_str!(value).ok_or_else(|| "`compare_with` needs a value".to_string())); + out.partial_eq.compare_with = Some(try!(parse_str_to_type::(&path))); } "ignore" => { - out.partial_eq.ignore = try!(parse_boolean_meta_item(&value, true, "ignore")); + out.partial_eq.ignore = try!(parse_boolean_meta_item(&opt_string_to_str!(value), true, "ignore")); } } } @@ -474,54 +514,80 @@ impl Field { /// /// We only have a limited set of possible attributes: /// -/// * `#[derivative(Debug)]` is represented as `("Debug", [])`; -/// * `#[derivative(Debug="foo")]` is represented as `("Debug", [("foo", None)])`; -/// * `#[derivative(Debug(foo="bar")]` is represented as `("Debug", [("foo", Some("bar"))])`. -struct MetaItem<'a>(&'a str, Vec<(&'a str, Option<&'a str>)>); +/// * `#[derivative(Debug)]` is represented as `(Debug, [])`; +/// * `#[derivative(Debug="foo")]` is represented as `(Debug, [(None, Some("foo"))])`; +/// * `#[derivative(Debug(foo="bar")]` is represented as `(Debug, [(Some(foo), Some("bar"))])`. +struct MetaItem<'a>( + &'a syn::Ident, + Vec<(Option<&'a syn::Ident>, Option)>, +); /// Parse an arbitrary item for our limited `MetaItem` subset. -fn read_items(item: &syn::NestedMetaItem) -> Result { +fn read_items(item: &syn::NestedMeta) -> Result { let item = match *item { - syn::NestedMetaItem::MetaItem(ref item) => item, - syn::NestedMetaItem::Literal(..) => { + syn::NestedMeta::Meta(ref item) => item, + syn::NestedMeta::Literal(..) => { return Err("Expected meta-item but found literal".to_string()); } }; match *item { - syn::MetaItem::Word(ref name) => Ok(MetaItem(name.as_ref(), Vec::new())), - syn::MetaItem::List(ref name, ref values) => { + syn::Meta::Word(ref name) => Ok(MetaItem(name, Vec::new())), + syn::Meta::List(syn::MetaList { + ident: ref name, + nested: ref values, + .. + }) => { let values = try!( values - .iter() - .map(|value| { - if let syn::NestedMetaItem::MetaItem(syn::MetaItem::NameValue(ref name, ref value)) = *value { - let value = try!(str_or_err(value)); - - Ok((name.as_ref(), Some(value))) - } else { - Err("Expected named value".to_string()) - } - }) - .collect() + .iter() + .map(|value| { + if let syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { + ident: ref name, + lit: ref value, + .. + })) = *value + { + let value = try!(string_or_err(value)); + + Ok((Some(name), Some(value))) + } else { + Err("Expected named value".to_string()) + } + }) + .collect() ); - Ok(MetaItem(name.as_ref(), values)) + Ok(MetaItem(name, values)) } - syn::MetaItem::NameValue(ref name, ref value) => { - let value = try!(str_or_err(value)); - - Ok(MetaItem(name.as_ref(), vec![(value, None)])) + syn::Meta::NameValue(syn::MetaNameValue { + ident: ref name, + lit: ref value, + .. + }) => { + let value = try!(string_or_err(value)); + + Ok(MetaItem(name, vec![(None, Some(value))])) } } } /// Filter the `derivative` items from an attribute. -fn derivative_attribute(attr: &syn::Attribute) -> Option<&[syn::NestedMetaItem]> { - match attr.value { - syn::MetaItem::List(ref name, ref mis) if name == "derivative" => Some(mis), - syn::MetaItem::Word(..) | - syn::MetaItem::NameValue(..) | - syn::MetaItem::List(..) => None, +fn derivative_attribute( + meta: syn::parse::Result, +) -> Option> { + match meta { + Ok(syn::Meta::List(syn::MetaList { + ident: name, + nested: mis, + .. + })) => { + if name.to_string() == "derivative" { + Some(mis) + } else { + None + } + } + _ => None, } } @@ -541,15 +607,15 @@ fn parse_boolean_meta_item(item: &Option<&str>, default: bool, name: &str) -> Re /// Parse a `bound` item. fn parse_bound( opt_bounds: &mut Option>, - value: Option<&str> + value: Option<&str>, ) -> Result<(), String> { let mut bounds = opt_bounds.take().unwrap_or_default(); let bound = try!(value.ok_or_else(|| "`bound` needs a value".to_string())); if !bound.is_empty() { - let where_clause = syn::parse_where_clause(&format!("where {}", bound)); - let mut predicates = try!(where_clause).predicates; - bounds.append(&mut predicates); + let where_clause = + syn::parse2::(quote!(where #bound)).map_err(|e| e.to_string()); + bounds.extend(try!(where_clause).predicates); } *opt_bounds = Some(bounds); @@ -558,10 +624,19 @@ fn parse_bound( } /// Get the string out of a string literal or report an error for other literals. -fn str_or_err(lit: &syn::Lit) -> Result<&str, String> { - if let syn::Lit::Str(ref value, _) = *lit { - Ok(value.as_str()) +fn string_or_err(lit: &syn::Lit) -> Result { + if let syn::Lit::Str(ref lit) = *lit { + Ok(lit.value()) } else { Err("Expected string".to_string()) } } + +fn parse_str_to_type(value: &str) -> Result +where + T: syn::parse::Parse, +{ + proc_macro2::TokenStream::from_str(value) + .map_err(|e| format!("{:?}", e)) + .and_then(|stream| syn::parse2::(stream).map_err(|e| e.to_string())) +} diff --git a/src/bound.rs b/src/bound.rs index 29e2daf..c79e276 100644 --- a/src/bound.rs +++ b/src/bound.rs @@ -13,7 +13,7 @@ use ast; use attr; use std::collections::HashSet; -use syn::{self, aster, visit}; +use syn::{self, visit, GenericParam}; // use internals::ast::Item; // use internals::attr; @@ -23,9 +23,16 @@ use syn::{self, aster, visit}; /// allowed here". pub fn without_defaults(generics: &syn::Generics) -> syn::Generics { syn::Generics { - ty_params: generics.ty_params + params: generics + .params .iter() - .map(|ty_param| syn::TyParam { default: None, ..ty_param.clone() }) + .map(|generic_param| match generic_param { + GenericParam::Type(ty_param) => syn::GenericParam::Type(syn::TypeParam { + default: None, + ..ty_param.clone() + }), + param @ _ => param.clone(), + }) .collect(), ..generics.clone() } @@ -35,9 +42,16 @@ pub fn with_where_predicates( generics: &syn::Generics, predicates: &[syn::WherePredicate], ) -> syn::Generics { - aster::from_generics(generics.clone()) - .with_predicates(predicates.to_vec()) - .build() + let mut cloned = generics.clone(); + + { + let where_clause = cloned.make_where_clause(); + predicates + .iter() + .for_each(|predicate| where_clause.predicates.push(predicate.clone())); + } + + cloned } pub fn with_where_predicates_from_fields( @@ -45,16 +59,23 @@ pub fn with_where_predicates_from_fields( generics: &syn::Generics, from_field: F, ) -> syn::Generics - where F: Fn(&attr::Field) -> Option<&[syn::WherePredicate]>, +where + F: Fn(&attr::Field) -> Option<&[syn::WherePredicate]>, { - aster::from_generics(generics.clone()) - .with_predicates( - item.body - .all_fields() - .iter() - .flat_map(|field| from_field(&field.attrs)) - .flat_map(|predicates| predicates.to_vec())) - .build() + let mut cloned = generics.clone(); + { + let fields = item.body.all_fields(); + let field_where_predicates = fields + .iter() + .flat_map(|field| from_field(&field.attrs)) + .flat_map(|predicates| predicates.to_vec()); + + let where_clause = cloned.make_where_clause(); + field_where_predicates + .into_iter() + .for_each(|predicate| where_clause.predicates.push(predicate.clone())); + } + cloned } /// Puts the given bound on any generic type parameters that are used in fields @@ -76,7 +97,8 @@ pub fn with_bound( filter: F, bound: &syn::Path, ) -> syn::Generics - where F: Fn(&attr::Field) -> bool, +where + F: Fn(&attr::Field) -> bool, { #[derive(Debug)] struct FindTyParams { @@ -88,31 +110,32 @@ pub fn with_bound( /// them. relevant_ty_params: HashSet, } - impl visit::Visitor for FindTyParams { - fn visit_path(&mut self, path: &syn::Path) { - if let Some(seg) = path.segments.last() { + impl<'ast> visit::Visit<'ast> for FindTyParams { + fn visit_path(&mut self, path: &'ast syn::Path) { + if let Some(syn::punctuated::Pair::Punctuated(seg, _)) = path.segments.last() { if seg.ident == "PhantomData" { // Hardcoded exception, because `PhantomData` implements // most traits whether or not `T` implements it. return; } } - if !path.global && path.segments.len() == 1 { + if path.leading_colon.is_none() && path.segments.len() == 1 { let id = path.segments[0].ident.clone(); if self.all_ty_params.contains(&id) { self.relevant_ty_params.insert(id); } } - visit::walk_path(self, path); + visit::visit_path(self, path); } } - let all_ty_params: HashSet<_> = generics.ty_params - .iter() + let all_ty_params: HashSet<_> = generics + .type_params() .map(|ty_param| ty_param.ident.clone()) .collect(); - let relevant_tys = item.body + let relevant_tys = item + .body .all_fields() .into_iter() .filter(|field| filter(&field.attrs)) @@ -123,21 +146,21 @@ pub fn with_bound( relevant_ty_params: HashSet::new(), }; for ty in relevant_tys { - visit::walk_ty(&mut visitor, ty); + visit::visit_type(&mut visitor, ty); } - aster::from_generics(generics.clone()) - .with_predicates(generics.ty_params - .iter() - .map(|ty_param| ty_param.ident.clone()) + let mut cloned = generics.clone(); + { + let relevant_where_predicates = generics + .type_params() + .map(|ty_param| &ty_param.ident) .filter(|id| visitor.relevant_ty_params.contains(id)) - .map(|id| { - aster::where_predicate() - // the type parameter that is being bounded e.g. `T` - .bound().build(aster::ty().id(id)) - // the bound e.g. `Debug` - .bound().trait_(bound.clone()).build() - .build() - })) - .build() + .map(|id| parse_quote!( #id : #bound )); + + let where_clause = cloned.make_where_clause(); + relevant_where_predicates.for_each(|predicate: syn::WherePredicate| { + where_clause.predicates.push(predicate.clone()) + }); + } + cloned } diff --git a/src/clone.rs b/src/clone.rs index b6a47dd..570cd11 100644 --- a/src/clone.rs +++ b/src/clone.rs @@ -1,12 +1,11 @@ use ast; use attr; use matcher; -use quote; -use syn::{self, aster}; +use syn; use utils; /// Derive `Copy` for `input`. -pub fn derive_copy(input: &ast::Input) -> Result { +pub fn derive_copy(input: &ast::Input) -> Result { let name = &input.ident; if input.attrs.derives_clone() { @@ -14,6 +13,7 @@ pub fn derive_copy(input: &ast::Input) -> Result { } let copy_trait_path = copy_trait_path(); + let (_impl_generics, ty_generics, _where_clause) = input.generics.split_for_impl(); let impl_generics = utils::build_impl_generics( input, ©_trait_path, @@ -23,24 +23,18 @@ pub fn derive_copy(input: &ast::Input) -> Result { ); let where_clause = &impl_generics.where_clause; - let ty = syn::aster::ty() - .path() - .segment(name.clone()) - .with_generics(impl_generics.clone()) - .build() - .build(); - Ok(quote! { #[allow(unused_qualifications)] - impl #impl_generics #copy_trait_path for #ty #where_clause {} + impl #impl_generics #copy_trait_path for #name #ty_generics #where_clause {} }) } /// Derive `Clone` for `input`. -pub fn derive_clone(input: &ast::Input) -> quote::Tokens { +pub fn derive_clone(input: &ast::Input) -> proc_macro2::TokenStream { let name = &input.ident; let clone_trait_path = clone_trait_path(); + let (_impl_generics, ty_generics, _where_clause) = input.generics.split_for_impl(); let impl_generics = utils::build_impl_generics( input, &clone_trait_path, @@ -50,26 +44,20 @@ pub fn derive_clone(input: &ast::Input) -> quote::Tokens { ); let where_clause = &impl_generics.where_clause; - let ty = syn::aster::ty() - .path() - .segment(name.clone()) - .with_generics(impl_generics.clone()) - .build() - .build(); - let is_copy = input.attrs.rustc_copy_clone_marker() || input.attrs.copy.is_some(); - if is_copy && input.generics.ty_params.is_empty() { + if is_copy && input.generics.type_params().count() == 0 { quote! { #[allow(unused_qualifications)] - impl #impl_generics #clone_trait_path for #ty #where_clause { + impl #impl_generics #clone_trait_path for #name #ty_generics #where_clause { fn clone(&self) -> Self { *self } } } } else { - let body = matcher::Matcher::new(matcher::BindingStyle::Ref) - .build_arms(input, |arm_path, _, style, _, bis| { + let body = matcher::Matcher::new(matcher::BindingStyle::Ref).build_arms( + input, + |arm_path, _, style, _, bis| { let field_clones = bis.iter().map(|bi| { let arg = &bi.ident; @@ -107,40 +95,43 @@ pub fn derive_clone(input: &ast::Input) -> quote::Tokens { } } } - }); + }, + ); let clone_from = if input.attrs.clone_from() { - Some(matcher::Matcher::new(matcher::BindingStyle::RefMut) - .build_arms(input, |outer_arm_path, _, _, _, outer_bis| { - let body = matcher::Matcher::new(matcher::BindingStyle::Ref) - .with_name("__other".into()) - .build_arms(input, |inner_arm_path, _, _, _, inner_bis| { - if outer_arm_path == inner_arm_path { - let field_clones = outer_bis - .iter() - .zip(inner_bis) - .map(|(outer_bi, inner_bi)| { - let outer = &outer_bi.ident; - let inner = &inner_bi.ident; - - quote!(#outer.clone_from(#inner);) - }); - - quote! { - #(#field_clones)* - return; + Some( + matcher::Matcher::new(matcher::BindingStyle::RefMut).build_arms( + input, + |outer_arm_path, _, _, _, outer_bis| { + let body = matcher::Matcher::new(matcher::BindingStyle::Ref) + .with_name("__other".into()) + .build_arms(input, |inner_arm_path, _, _, _, inner_bis| { + if outer_arm_path == inner_arm_path { + let field_clones = outer_bis.iter().zip(inner_bis).map( + |(outer_bi, inner_bi)| { + let outer = &outer_bi.ident; + let inner = &inner_bi.ident; + + quote!(#outer.clone_from(#inner);) + }, + ); + + quote! { + #(#field_clones)* + return; + } + } else { + quote!() } - } else { - quote!() - } - }); + }); - quote! { - match *other { - #body + quote! { + match *other { + #body + } } - } - }) + }, + ), ) } else { None @@ -168,7 +159,7 @@ pub fn derive_clone(input: &ast::Input) -> quote::Tokens { quote! { #[allow(unused_qualifications)] - impl #impl_generics #clone_trait_path for #ty #where_clause { + impl #impl_generics #clone_trait_path for #name #ty_generics #where_clause { fn clone(&self) -> Self { match *self { #body @@ -187,10 +178,10 @@ fn needs_clone_bound(attrs: &attr::Field) -> bool { /// Return the path of the `Clone` trait, that is `::std::clone::Clone`. fn clone_trait_path() -> syn::Path { - aster::path().global().ids(&["std", "clone", "Clone"]).build() + parse_quote!(::std::clone::Clone) } -/// Return the path of the `Copy` trait, that is `::std::marker::Clone`. +/// Return the path of the `Copy` trait, that is `::std::marker::Copy`. fn copy_trait_path() -> syn::Path { - aster::path().global().ids(&["std", "marker", "Copy"]).build() + parse_quote!(::std::marker::Copy) } diff --git a/src/cmp.rs b/src/cmp.rs index 2fe859b..5fc7fd8 100644 --- a/src/cmp.rs +++ b/src/cmp.rs @@ -2,15 +2,15 @@ use ast; use matcher; -use quote; -use syn::{self, aster}; +use syn; use utils; /// Derive `Eq` for `input`. -pub fn derive_eq(input: &ast::Input) -> quote::Tokens { +pub fn derive_eq(input: &ast::Input) -> proc_macro2::TokenStream { let name = &input.ident; let eq_trait_path = eq_trait_path(); + let (_impl_generics, ty_generics, _where_clause) = input.generics.split_for_impl(); let impl_generics = utils::build_impl_generics( input, &eq_trait_path, @@ -20,26 +20,20 @@ pub fn derive_eq(input: &ast::Input) -> quote::Tokens { ); let where_clause = &impl_generics.where_clause; - let ty = syn::aster::ty() - .path() - .segment(name.clone()) - .with_generics(impl_generics.clone()) - .build() - .build(); - quote! { #[allow(unused_qualifications)] - impl #impl_generics #eq_trait_path for #ty #where_clause {} + impl #impl_generics #eq_trait_path for #name #ty_generics #where_clause {} } } /// Derive `PartialEq` for `input`. -pub fn derive_partial_eq(input: &ast::Input) -> Result { +pub fn derive_partial_eq(input: &ast::Input) -> Result { if let ast::Body::Enum(_) = input.body { if !input.attrs.partial_eq_on_enum() { return Err( "can't use `#[derivative(PartialEq)]` on an enumeration without \ - `feature_allow_slow_enum`; see the documentation for more details".into() + `feature_allow_slow_enum`; see the documentation for more details" + .into(), ); } } @@ -57,7 +51,8 @@ pub fn derive_partial_eq(input: &ast::Input) -> Result { if o.field.attrs.ignore_partial_eq() { None - } else if let Some(compare_fn) = o.field.attrs.partial_eq_compare_with() { + } else if let Some(compare_fn) = o.field.attrs.partial_eq_compare_with() + { Some(quote!(&& #compare_fn(#outer_name, #inner_name))) } else { Some(quote!(&& #outer_name == #inner_name)) @@ -80,6 +75,7 @@ pub fn derive_partial_eq(input: &ast::Input) -> Result { let name = &input.ident; let partial_eq_trait_path = partial_eq_trait_path(); + let (_impl_generics, ty_generics, _where_clause) = input.generics.split_for_impl(); let impl_generics = utils::build_impl_generics( input, &partial_eq_trait_path, @@ -89,16 +85,9 @@ pub fn derive_partial_eq(input: &ast::Input) -> Result { ); let where_clause = &impl_generics.where_clause; - let ty = syn::aster::ty() - .path() - .segment(name.clone()) - .with_generics(impl_generics.clone()) - .build() - .build(); - Ok(quote! { #[allow(unused_qualifications)] - impl #impl_generics #partial_eq_trait_path for #ty #where_clause { + impl #impl_generics #partial_eq_trait_path for #name #ty_generics #where_clause { fn eq(&self, other: &Self) -> bool { match *self { #body @@ -110,10 +99,10 @@ pub fn derive_partial_eq(input: &ast::Input) -> Result { /// Return the path of the `Eq` trait, that is `::std::cmp::Eq`. fn eq_trait_path() -> syn::Path { - aster::path().global().ids(&["std", "cmp", "Eq"]).build() + parse_quote!(::std::cmp::Eq) } /// Return the path of the `PartialEq` trait, that is `::std::cmp::PartialEq`. fn partial_eq_trait_path() -> syn::Path { - aster::path().global().ids(&["std", "cmp", "PartialEq"]).build() + parse_quote!(::std::cmp::PartialEq) } diff --git a/src/debug.rs b/src/debug.rs index 6de01b1..c985741 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -1,13 +1,13 @@ use ast; use attr; use matcher; -use quote; -use syn::{self, aster}; +use syn; use utils; -pub fn derive(input: &ast::Input) -> quote::Tokens { - let body = matcher::Matcher::new(matcher::BindingStyle::Ref) - .build_arms(input, |_, arm_name, style, attrs, bis| { +pub fn derive(input: &ast::Input) -> proc_macro2::TokenStream { + let body = matcher::Matcher::new(matcher::BindingStyle::Ref).build_arms( + input, + |_, arm_name, style, attrs, bis| { let field_prints = bis.iter().filter_map(|bi| { if bi.field.attrs.ignore_debug() { return None; @@ -26,7 +26,6 @@ pub fn derive(input: &ast::Input) -> quote::Tokens { }); let builder = if let Some(ref name) = bi.field.ident { - let name = name.as_ref(); quote! { #dummy_debug let _ = builder.field(#name, &#arg); @@ -45,9 +44,7 @@ pub fn derive(input: &ast::Input) -> quote::Tokens { ast::Style::Struct => "debug_struct", ast::Style::Tuple | ast::Style::Unit => "debug_tuple", }; - let method = syn::Ident::new(method); - - let name = arm_name.as_ref(); + let method = syn::Ident::new(method, proc_macro2::Span::call_site()); if attrs.debug_transparent() { quote! { @@ -55,16 +52,18 @@ pub fn derive(input: &ast::Input) -> quote::Tokens { } } else { quote! { - let mut builder = __f.#method(#name); + let mut builder = __f.#method(#arm_name); #(#field_prints)* builder.finish() } } - }); + }, + ); let name = &input.ident; let debug_trait_path = debug_trait_path(); + let (_impl_generics, ty_generics, _where_clause) = input.generics.split_for_impl(); let impl_generics = utils::build_impl_generics( input, &debug_trait_path, @@ -74,16 +73,9 @@ pub fn derive(input: &ast::Input) -> quote::Tokens { ); let where_clause = &impl_generics.where_clause; - let ty = syn::aster::ty() - .path() - .segment(name.clone()) - .with_generics(impl_generics.clone()) - .build() - .build(); - quote! { #[allow(unused_qualifications)] - impl #impl_generics #debug_trait_path for #ty #where_clause { + impl #impl_generics #debug_trait_path for #name #ty_generics #where_clause { fn fmt(&self, __f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match *self { #body @@ -99,7 +91,7 @@ fn needs_debug_bound(attrs: &attr::Field) -> bool { /// Return the path of the `Debug` trait, that is `::std::fmt::Debug`. fn debug_trait_path() -> syn::Path { - aster::path().global().ids(&["std", "fmt", "Debug"]).build() + parse_quote!(::std::fmt::Debug) } fn format_with( @@ -107,31 +99,52 @@ fn format_with( arg_n: &syn::Ident, format_fn: &syn::Path, mut generics: syn::Generics, -) -> quote::Tokens { +) -> proc_macro2::TokenStream { let debug_trait_path = debug_trait_path(); let ctor_generics = generics.clone(); let (_, ctor_ty_generics, _) = ctor_generics.split_for_impl(); - generics.where_clause.predicates.extend(f.attrs.debug_bound().unwrap_or(&[]).iter().cloned()); - - generics.lifetimes.push(syn::LifetimeDef::new("'_derivative")); - for ty in &generics.ty_params { - let path = aster::path::PathBuilder::new().id(&ty.ident).build(); - generics.where_clause.predicates - .push(syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate { - bound_lifetimes: vec![], - bounded_ty: syn::Ty::Path(None, path), - bounds: vec![syn::TyParamBound::Region(syn::Lifetime::new("'_derivative"))], - })); - } + generics + .make_where_clause() + .predicates + .extend(f.attrs.debug_bound().unwrap_or(&[]).iter().cloned()); + + generics + .params + .push(syn::GenericParam::Lifetime(syn::LifetimeDef::new( + parse_quote!('_derivative), + ))); + let where_predicates = generics + .type_params() + .map(|ty| { + let mut bounds = syn::punctuated::Punctuated::new(); + bounds.push(syn::TypeParamBound::Lifetime(syn::Lifetime::new( + "'_derivative", + proc_macro2::Span::call_site(), + ))); + + let path = parse_quote!(#ty.ident); + + syn::WherePredicate::Type(syn::PredicateType { + lifetimes: None, + bounded_ty: syn::Type::Path(syn::TypePath { qself: None, path }), + colon_token: Default::default(), + bounds, + }) + }) + .collect::>(); + generics + .make_where_clause() + .predicates + .extend(where_predicates); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let ty = f.ty; // Leave off the type parameter bounds, defaults, and attributes - let phantom = generics.ty_params.iter().map(|tp| &tp.ident); + let phantom = generics.type_params().map(|tp| &tp.ident); quote!( let #arg_n = { diff --git a/src/default.rs b/src/default.rs index f31a117..af1b7da 100644 --- a/src/default.rs +++ b/src/default.rs @@ -1,22 +1,24 @@ use ast; use attr; -use quote; -use syn::{self, aster}; +use syn; use utils; /// Derive `Default` for `input`. -pub fn derive(input: &ast::Input, default: &attr::InputDefault) -> quote::Tokens { +pub fn derive(input: &ast::Input, default: &attr::InputDefault) -> proc_macro2::TokenStream { fn make_variant_data( - variant_name: quote::Tokens, + variant_name: proc_macro2::TokenStream, style: ast::Style, fields: &[ast::Field], - ) -> quote::Tokens { + ) -> proc_macro2::TokenStream { match style { ast::Style::Struct => { let mut defaults = Vec::new(); for f in fields { - let name = f.ident.as_ref().expect("A structure field must have a name"); + let name = f + .ident + .as_ref() + .expect("A structure field must have a name"); let default = f.attrs.default_value().map_or_else( || quote!(::std::default::Default::default()), |v| quote!(#v), @@ -47,6 +49,7 @@ pub fn derive(input: &ast::Input, default: &attr::InputDefault) -> quote::Tokens let name = &input.ident; let default_trait_path = default_trait_path(); + let (_impl_generics, ty_generics, _where_clause) = input.generics.split_for_impl(); let impl_generics = utils::build_impl_generics( input, &default_trait_path, @@ -56,20 +59,17 @@ pub fn derive(input: &ast::Input, default: &attr::InputDefault) -> quote::Tokens ); let where_clause = &impl_generics.where_clause; - let ty = syn::aster::ty() - .path() - .segment(name.clone()) - .with_generics(impl_generics.clone()) - .build() - .build(); - let body = match input.body { ast::Body::Enum(ref data) => { let arms = data.iter().filter_map(|variant| { if variant.attrs.default.is_some() { let vname = &variant.ident; - Some(make_variant_data(quote!(#name::#vname), variant.style, &variant.fields)) + Some(make_variant_data( + quote!(#name::#vname), + variant.style, + &variant.fields, + )) } else { None } @@ -77,15 +77,13 @@ pub fn derive(input: &ast::Input, default: &attr::InputDefault) -> quote::Tokens quote!(#(#arms),*) } - ast::Body::Struct(style, ref vd) => { - make_variant_data(quote!(#name), style, vd) - } + ast::Body::Struct(style, ref vd) => make_variant_data(quote!(#name), style, vd), }; let new_fn = if default.new { Some(quote!( #[allow(unused_qualifications)] - impl #impl_generics #ty #where_clause { + impl #impl_generics #name #ty_generics #where_clause { /// Creates a default value for this type. #[inline] pub fn new() -> Self { @@ -101,7 +99,7 @@ pub fn derive(input: &ast::Input, default: &attr::InputDefault) -> quote::Tokens #new_fn #[allow(unused_qualifications)] - impl #impl_generics #default_trait_path for #ty #where_clause { + impl #impl_generics #default_trait_path for #name #ty_generics #where_clause { fn default() -> Self { #body } @@ -111,5 +109,5 @@ pub fn derive(input: &ast::Input, default: &attr::InputDefault) -> quote::Tokens /// Return the path of the `Default` trait, that is `::std::default::Default`. fn default_trait_path() -> syn::Path { - aster::path().global().ids(&["std", "default", "Default"]).build() + parse_quote!(::std::default::Default) } diff --git a/src/hash.rs b/src/hash.rs index 6db6589..d4f9f0a 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -1,16 +1,16 @@ use ast; use attr; use matcher; -use quote; -use syn::{self, aster}; +use syn; use utils; -pub fn derive(input: &ast::Input) -> quote::Tokens { +pub fn derive(input: &ast::Input) -> proc_macro2::TokenStream { let hasher_trait_path = hasher_trait_path(); let hash_trait_path = hash_trait_path(); - let body = matcher::Matcher::new(matcher::BindingStyle::Ref) - .build_arms(input, |arm_path, _, _, _, bis| { + let body = matcher::Matcher::new(matcher::BindingStyle::Ref).build_arms( + input, + |arm_path, _, _, _, bis| { let field_prints = bis.iter().filter_map(|bi| { if bi.field.attrs.ignore_hash() { return None; @@ -41,10 +41,11 @@ pub fn derive(input: &ast::Input) -> quote::Tokens { #variant #(#field_prints)* } - }); + }, + ); let name = &input.ident; - + let (_impl_generics, ty_generics, _where_clause) = input.generics.split_for_impl(); let impl_generics = utils::build_impl_generics( input, &hash_trait_path, @@ -54,17 +55,10 @@ pub fn derive(input: &ast::Input) -> quote::Tokens { ); let where_clause = &impl_generics.where_clause; - let ty = syn::aster::ty() - .path() - .segment(name.clone()) - .with_generics(impl_generics.clone()) - .build() - .build(); - let hasher_ty_parameter = utils::hygienic_type_parameter(input, "__H"); quote! { #[allow(unused_qualifications)] - impl #impl_generics #hash_trait_path for #ty #where_clause { + impl #impl_generics #hash_trait_path for #name #ty_generics #where_clause { fn hash<#hasher_ty_parameter>(&self, __state: &mut #hasher_ty_parameter) where #hasher_ty_parameter: #hasher_trait_path { @@ -82,10 +76,10 @@ fn needs_hash_bound(attrs: &attr::Field) -> bool { /// Return the path of the `Hash` trait, that is `::std::hash::Hash`. fn hash_trait_path() -> syn::Path { - aster::path().global().ids(&["std", "hash", "Hash"]).build() + parse_quote!(::std::hash::Hash) } /// Return the path of the `Hasher` trait, that is `::std::hash::Hasher`. fn hasher_trait_path() -> syn::Path { - aster::path().global().ids(&["std", "hash", "Hasher"]).build() + parse_quote!(::std::hash::Hasher) } diff --git a/src/lib.rs b/src/lib.rs index f6fc20b..5dfe20e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ extern crate proc_macro; +extern crate proc_macro2; +#[macro_use] extern crate syn; #[macro_use] @@ -17,43 +19,45 @@ mod utils; use proc_macro::TokenStream; -fn derive_impls(input: &ast::Input) -> Result { - let mut tokens = quote::Tokens::new(); +fn derive_impls(input: &ast::Input) -> Result { + let mut tokens = proc_macro2::TokenStream::new(); if input.attrs.clone.is_some() { - tokens.append(&clone::derive_clone(input).to_string()); + tokens.extend(clone::derive_clone(input)); } if input.attrs.copy.is_some() { - tokens.append(&try!(clone::derive_copy(input)).to_string()); + tokens.extend(clone::derive_copy(input)?); } if input.attrs.debug.is_some() { - tokens.append(&debug::derive(input).to_string()); + tokens.extend(debug::derive(input)); } if let Some(ref default) = input.attrs.default { - tokens.append(&default::derive(input, default).to_string()); + tokens.extend(default::derive(input, default)); } if input.attrs.eq.is_some() { - tokens.append(&cmp::derive_eq(input).to_string()); + tokens.extend(cmp::derive_eq(input)); } if input.attrs.hash.is_some() { - tokens.append(&hash::derive(input).to_string()); + tokens.extend(hash::derive(input)); } if input.attrs.partial_eq.is_some() { - tokens.append(&try!(cmp::derive_partial_eq(input)).to_string()); + tokens.extend(cmp::derive_partial_eq(input)?); } Ok(tokens) } -#[cfg_attr(not(test), proc_macro_derive(Derivative, attributes(derivative)))] -pub fn derivative(input: TokenStream) -> TokenStream { - fn detail(input: TokenStream) -> Result { - let input = try!(syn::parse_macro_input(&input.to_string())); - let parsed = try!(ast::Input::from_ast(&input)); - let output = try!(derive_impls(&parsed)); - Ok(output.to_string().parse().unwrap()) - } +fn detail(input: TokenStream) -> Result { + let parsed = syn::parse::(input).map_err(|e| e.to_string())?; + let output = derive_impls(&ast::Input::from_ast(&parsed)?)?; + Ok(output.into()) +} +#[cfg_attr( + not(test), + proc_macro_derive(Derivative, attributes(derivative)) +)] +pub fn derivative(input: TokenStream) -> TokenStream { match detail(input) { Ok(output) => output, Err(e) => panic!(e), diff --git a/src/matcher.rs b/src/matcher.rs index 727a7e8..75c4b54 100644 --- a/src/matcher.rs +++ b/src/matcher.rs @@ -27,14 +27,13 @@ pub enum BindingStyle { } impl quote::ToTokens for BindingStyle { - fn to_tokens(&self, tokens: &mut quote::Tokens) { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { match *self { BindingStyle::Move => (), - BindingStyle::MoveMut => tokens.append("mut"), - BindingStyle::Ref => tokens.append("ref"), + BindingStyle::MoveMut => tokens.extend(quote!(mut)), + BindingStyle::Ref => tokens.extend(quote!(ref)), BindingStyle::RefMut => { - tokens.append("ref"); - tokens.append("mut"); + tokens.extend(quote!(ref mut)); } } } @@ -60,40 +59,53 @@ impl Matcher { } pub fn with_name(self, name: String) -> Self { - Matcher { binding_name: name, ..self } + Matcher { + binding_name: name, + ..self + } } - pub fn build_arms(self, input: &ast::Input, f: F) -> quote::Tokens - where F: Fn(syn::Path, &syn::Ident, ast::Style, &attr::Input, Vec) -> quote::Tokens + pub fn build_arms(self, input: &ast::Input, f: F) -> proc_macro2::TokenStream + where + F: Fn(syn::Path, &syn::Ident, ast::Style, &attr::Input, Vec) + -> proc_macro2::TokenStream, { let ident = &input.ident; // Generate patterns for matching against all of the variants let variants = match input.body { - ast::Body::Enum(ref variants) => { - variants.iter() - .map(|variant| { - let variant_ident = &variant.ident; - let variant_path = syn::aster::path().ids(&[ident, variant_ident]).build(); - - let pat = self.build_match_pattern( - &variant_path, - variant.style, - &variant.fields - ); - - (variant_path, variant_ident, variant.style, &variant.attrs, pat) - }) - .collect() - } + ast::Body::Enum(ref variants) => variants + .iter() + .map(|variant| { + let variant_ident = &variant.ident; + let variant_path = parse_quote!(#ident::#variant_ident); + + let pat = + self.build_match_pattern(&variant_path, variant.style, &variant.fields); + + ( + variant_path, + variant_ident, + variant.style, + &variant.attrs, + pat, + ) + }) + .collect(), ast::Body::Struct(style, ref vd) => { - let path = syn::aster::path().id(ident).build(); - vec![(path, ident, style, &input.attrs, self.build_match_pattern(ident, style, vd))] + let path = parse_quote!(#ident); + vec![( + path, + ident, + style, + &input.attrs, + self.build_match_pattern(ident, style, vd), + )] } }; // Now that we have the patterns, generate the actual branches of the match // expression - let mut t = quote::Tokens::new(); + let mut t = proc_macro2::TokenStream::new(); for (path, name, style, attrs, (pat, bindings)) in variants { let body = f(path, name, style, attrs, bindings); quote!(#pat => { #body }).to_tokens(&mut t); @@ -106,12 +118,12 @@ impl Matcher { &self, name: &N, style: ast::Style, - fields: &'a [ast::Field<'a>] - ) - -> (quote::Tokens, Vec>) - where N: quote::ToTokens, + fields: &'a [ast::Field<'a>], + ) -> (proc_macro2::TokenStream, Vec>) + where + N: quote::ToTokens, { - let mut t = quote::Tokens::new(); + let mut t = proc_macro2::TokenStream::new(); let mut matches = Vec::new(); let binding = self.binding_style; @@ -119,21 +131,27 @@ impl Matcher { match style { ast::Style::Unit => {} ast::Style::Tuple => { - t.append("("); + t.extend(quote!("(")); for (i, field) in fields.iter().enumerate() { - let ident: syn::Ident = format!("{}_{}", self.binding_name, i).into(); + let ident: syn::Ident = syn::Ident::new( + &format!("{}_{}", self.binding_name, i), + proc_macro2::Span::call_site(), + ); quote!(#binding #ident ,).to_tokens(&mut t); matches.push(BindingInfo { ident: ident, field: field, }); } - t.append(")"); + t.extend(quote!(")")); } ast::Style::Struct => { - t.append("{"); + t.extend(quote!("{")); for (i, field) in fields.iter().enumerate() { - let ident: syn::Ident = format!("{}_{}", self.binding_name, i).into(); + let ident: syn::Ident = syn::Ident::new( + &format!("{}_{}", self.binding_name, i), + proc_macro2::Span::call_site(), + ); { let field_name = field.ident.as_ref().unwrap(); quote!(#field_name : #binding #ident ,).to_tokens(&mut t); @@ -143,7 +161,7 @@ impl Matcher { field: field, }); } - t.append("}"); + t.extend(quote!("}")); } } diff --git a/src/utils.rs b/src/utils.rs index cc433dd..f4bda01 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -12,22 +12,17 @@ pub fn build_impl_generics( field_bound: G, input_bound: H, ) -> syn::Generics - where F: Fn(&attr::Field) -> bool, - G: Fn(&attr::Field) -> Option<&[syn::WherePredicate]>, - H: Fn(&attr::Input) -> Option<&[syn::WherePredicate]>, +where + F: Fn(&attr::Field) -> bool, + G: Fn(&attr::Field) -> Option<&[syn::WherePredicate]>, + H: Fn(&attr::Input) -> Option<&[syn::WherePredicate]>, { let generics = bound::without_defaults(item.generics); - let generics = bound::with_where_predicates_from_fields( - item, &generics, field_bound - ); + let generics = bound::with_where_predicates_from_fields(item, &generics, field_bound); match input_bound(&item.attrs) { - Some(predicates) => { - bound::with_where_predicates(&generics, predicates) - } - None => { - bound::with_bound(item, &generics, needs_debug_bound, trait_path) - } + Some(predicates) => bound::with_where_predicates(&generics, predicates), + None => bound::with_bound(item, &generics, needs_debug_bound, trait_path), } } @@ -35,11 +30,12 @@ pub fn build_impl_generics( /// type parameters of the item. This is achieved by starting with a base and /// then concatenating the names of all other type parameters. pub fn hygienic_type_parameter(item: &ast::Input, base: &str) -> syn::Ident { - let mut typaram = String::from(base); + let mut typaram = String::with_capacity(150); + typaram.push_str(base); + let typaram = item.generics.type_params().fold(typaram, |mut acc, ty| { + acc.push_str(&format!("{}", &ty.ident)); + acc + }); - for ty in &item.generics.ty_params { - typaram.push_str(&ty.ident.as_ref()); - } - - syn::Ident::new(typaram) + syn::Ident::new(&typaram, proc_macro2::Span::call_site()) } diff --git a/tests/rustc-deriving-clone-enum.rs b/tests/rustc-deriving-clone-enum.rs index 40caa7a..2633cd3 100644 --- a/tests/rustc-deriving-clone-enum.rs +++ b/tests/rustc-deriving-clone-enum.rs @@ -18,7 +18,7 @@ extern crate derivative; enum E { A, B(()), - C + C, } #[test]