From fefd0619a17f7ab0bc9e5c4e9b05e08fd03ad6e1 Mon Sep 17 00:00:00 2001 From: hcpl Date: Wed, 4 Apr 2018 03:12:39 +0300 Subject: [PATCH 1/3] Update dependencies --- .gitignore | 4 + Cargo.toml | 7 +- src/ast.rs | 70 ++++---- src/attr.rs | 220 +++++++++++++----------- src/bound.rs | 164 ++++++++++++------ src/clone.rs | 87 +++++----- src/cmp.rs | 66 +++---- src/debug.rs | 126 ++++++++++---- src/default.rs | 100 +++++------ src/hash.rs | 59 ++++--- src/lib.rs | 28 +-- src/matcher.rs | 163 ++++++++++-------- src/utils.rs | 51 +++++- tests/compile-fail/derive-partial-eq.rs | 2 +- tests/compile-test.rs | 3 +- 15 files changed, 674 insertions(+), 476 deletions(-) diff --git a/.gitignore b/.gitignore index b0f775d..35fc156 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ target Cargo.lock +*.rs.bk +[._]*.sw? +[._]sw? +*~ /_book /node_modules diff --git a/Cargo.toml b/Cargo.toml index 689f2df..6c1869d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,10 +17,9 @@ appveyor = { repository = "mcarton/rust-derivative" } proc-macro = true [dependencies] -itertools = "~0.5" -quote = "^0.3" -syn = { version = "0.10", features = ["aster", "full", "visit"] } -compiletest_rs = { version = "^0.2", optional = true } +quote = "0.5" +syn = { version = "0.13", features = ["extra-traits", "full", "visit"] } +compiletest_rs = { version = "0.3", optional = true } [features] nightly = ["compiletest_rs"] diff --git a/src/ast.rs b/src/ast.rs index 9b94eed..95ed9f4 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -4,13 +4,13 @@ use syn; #[derive(Debug)] pub struct Input<'a> { pub attrs: attr::Input, - pub body: Body<'a>, + pub data: Data<'a>, pub generics: &'a syn::Generics, pub ident: syn::Ident, } #[derive(Debug)] -pub enum Body<'a> { +pub enum Data<'a> { Enum(Vec>), Struct(Style, Vec>), } @@ -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 type_: &'a syn::Type, } #[derive(Clone, Copy, Debug)] @@ -38,79 +38,87 @@ pub enum Style { } impl<'a> Input<'a> { - pub fn from_ast(item: &'a syn::MacroInput) -> Result, String> { - let attrs = try!(attr::Input::from_ast(&item.attrs)); + pub fn from_ast(item: &'a syn::DeriveInput) -> Result, String> { + let attrs = attr::Input::from_ast(&item.attrs)?; - let body = match item.body { - syn::Body::Enum(ref variants) => { - Body::Enum(try!(enum_from_ast(variants))) + let data = match item.data { + syn::Data::Enum(ref data_enum) => { + Data::Enum(enum_from_ast(&data_enum.variants)?) } - syn::Body::Struct(ref variant_data) => { - let (style, fields) = try!(struct_from_ast(variant_data)); - Body::Struct(style, fields) + syn::Data::Struct(ref data_struct) => { + let (style, fields) = struct_like_data_from_ast(&data_struct.fields)?; + Data::Struct(style, fields) + } + syn::Data::Union(_) => { + // TODO: support or not support? + return Err("`derivative` doesn't support unions".to_string()); } }; Ok(Input { attrs: attrs, - body: body, + data: data, generics: &item.generics, - ident: item.ident.clone(), + ident: item.ident, }) } } -impl<'a> Body<'a> { +impl<'a> Data<'a> { pub fn all_fields(&self) -> Vec<&Field> { match *self { - Body::Enum(ref variants) => { + Data::Enum(ref variants) => { variants .iter() .flat_map(|variant| variant.fields.iter()) .collect() } - Body::Struct(_, ref fields) => fields.iter().collect(), + Data::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) = struct_like_data_from_ast(&variant.fields)?; Ok(Variant { - attrs: try!(attr::Input::from_ast(&variant.attrs)), + attrs: attr::Input::from_ast(&variant.attrs)?, fields: fields, - ident: variant.ident.clone(), + ident: variant.ident, style: style, }) }) .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)))) +fn struct_like_data_from_ast<'a>(fields: &'a syn::Fields) -> Result<(Style, Vec>), String> { + match *fields { + syn::Fields::Named(syn::FieldsNamed { ref named, .. }) => { + Ok((Style::Struct, fields_from_ast(named)?)) } - syn::VariantData::Tuple(ref fields) => { - Ok((Style::Tuple, try!(fields_from_ast(fields)))) + syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) => { + Ok((Style::Tuple, fields_from_ast(unnamed)?)) } - syn::VariantData::Unit => { + 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| { Ok(Field { - attrs: try!(attr::Field::from_ast(field)), - ident: field.ident.clone(), - ty: &field.ty, + attrs: attr::Field::from_ast(field)?, + ident: field.ident, + type_: &field.ty, }) }) .collect() diff --git a/src/attr.rs b/src/attr.rs index da3a9b1..3ae43f3 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -9,13 +9,13 @@ pub struct Input { pub copy: Option, /// Whether `Debug` is present and its specific attributes. pub debug: Option, - /// Whether `Default` is present and its specitif attributes. + /// Whether `Default` is present and its specific attributes. pub default: Option, - /// Whether `Eq` is present and its specitif attributes. + /// Whether `Eq` is present and its specific attributes. pub eq: Option, /// Whether `Hash` is present and its specific attributes. pub hash: Option, - /// Whether `Eq` is present and its specitif attributes. + /// Whether `Eq` is present and its specific attributes. pub partial_eq: Option, } @@ -152,10 +152,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 metaitem in meta_items.iter().map(read_items) { - let MetaItem($name, $value) = try!(metaitem); - match $name { + for nested_metas in $attrs.into_iter().filter_map(|attr| derivative_attribute(&attr)) { + for meta in nested_metas.into_iter().map(read_meta) { + let Meta($name, $value) = meta?; + match $name.as_ref() { $($body)* _ => return Err(format!("unknown trait `{}`", $name)), } @@ -165,20 +165,20 @@ macro_rules! for_all_attr { } macro_rules! match_attributes { - (let Some($name:ident) = $unwraped:expr; for $value:ident in $values:expr; $($body:tt)* ) => { - let mut $name = $unwraped.take().unwrap_or_default(); + (let Some($name:ident) = $unwrapped:expr; for $value:ident in $values:expr; $($body:tt)* ) => { + let mut $name = $unwrapped.take().unwrap_or_default(); match_attributes! { for $value in $values; $($body)* } - $unwraped = Some($name); + $unwrapped = Some($name); }; (for $value:ident in $values:expr; $($body:tt)* ) => { for (name, $value) in $values { - match name { + match name.as_ref() { $($body)* _ => return Err(format!("unknown attribute `{}`", name)), } @@ -198,13 +198,21 @@ impl Input { clone.rustc_copy_clone_marker = attrs .iter() - .any(|attr| attr.value.name() == "rustc_copy_clone_marker"); + .any(|attr| { + let path = &attr.path; + let segments = &path.segments; + + !path.global() + && segments.len() == 1 + && segments[0].ident == "rustc_copy_clone_marker" + && segments[0].arguments == syn::PathArguments::None + }); match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut clone.bounds, value)), + "bound" => parse_bound(&mut clone.bounds, value)?, "clone_from" => { - clone.clone_from = try!(parse_boolean_meta_item(&value, true, "clone_from")); + clone.clone_from = parse_boolean_meta_item(value, true, "clone_from")?; } } @@ -214,14 +222,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 Some(syn::Meta::List(syn::MetaList { ident, nested, .. })) = attr.interpret_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 meta) => meta.name() == "Clone", + syn::NestedMeta::Literal(_) => false, } } - if name == "derive" && traits.iter().any(is_clone) { + + if ident == "derive" && nested.iter().any(is_clone) { copy.derives_clone = true; } } @@ -229,7 +238,7 @@ impl Input { match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut copy.bounds, value)), + "bound" => parse_bound(&mut copy.bounds, value)?, } input.copy = Some(copy); @@ -238,9 +247,9 @@ impl Input { match_attributes! { let Some(debug) = input.debug; for value in values; - "bound" => try!(parse_bound(&mut debug.bounds, value)), + "bound" => parse_bound(&mut debug.bounds, value)?, "transparent" => { - debug.transparent = try!(parse_boolean_meta_item(&value, true, "transparent")); + debug.transparent = parse_boolean_meta_item(value, true, "transparent")?; } } } @@ -248,9 +257,9 @@ impl Input { match_attributes! { let Some(default) = input.default; for value in values; - "bound" => try!(parse_bound(&mut default.bounds, value)), + "bound" => parse_bound(&mut default.bounds, value)?, "new" => { - default.new = try!(parse_boolean_meta_item(&value, true, "new")); + default.new = parse_boolean_meta_item(value, true, "new")?; } } } @@ -258,23 +267,23 @@ impl Input { match_attributes! { let Some(eq) = input.eq; for value in values; - "bound" => try!(parse_bound(&mut eq.bounds, value)), + "bound" => parse_bound(&mut eq.bounds, value)?, } } "Hash" => { match_attributes! { let Some(hash) = input.hash; for value in values; - "bound" => try!(parse_bound(&mut hash.bounds, value)), + "bound" => parse_bound(&mut hash.bounds, 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" => parse_bound(&mut partial_eq.bounds, value)?, "feature_allow_slow_enum" => { - partial_eq.on_enum = try!(parse_boolean_meta_item(&value, true, "feature_allow_slow_enum")); + partial_eq.on_enum = parse_boolean_meta_item(value, true, "feature_allow_slow_enum")?; } } } @@ -338,69 +347,69 @@ impl Field { let mut out = Field::default(); for_all_attr! { - for (name, values) in field.attrs; + for (name, values) in &field.attrs; "Clone" => { match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut out.clone.bounds, value)), + "bound" => parse_bound(&mut out.clone.bounds, 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 = value.ok_or_else(|| "`clone_with` needs a value".to_string())?; + out.clone.clone_with = Some(syn::parse_str(&path).map_err(|e| e.to_string())?); } } } "Debug" => { match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut out.debug.bounds, value)), + "bound" => parse_bound(&mut out.debug.bounds, 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 = value.ok_or_else(|| "`format_with` needs a value".to_string())?; + out.debug.format_with = Some(syn::parse_str(&path).map_err(|e| e.to_string())?); } "ignore" => { - out.debug.ignore = try!(parse_boolean_meta_item(&value, true, "ignore")); + out.debug.ignore = parse_boolean_meta_item(value, true, "ignore")?; } } } "Default" => { match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut out.default.bounds, value)), + "bound" => parse_bound(&mut out.default.bounds, 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 = value.ok_or_else(|| "`value` needs a value".to_string())?; + out.default.value = Some(syn::parse_str(&value).map_err(|e| e.to_string())?); } } } "Eq" => { match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut out.eq_bound, value)), + "bound" => parse_bound(&mut out.eq_bound, value)?, } } "Hash" => { match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut out.hash.bounds, value)), + "bound" => parse_bound(&mut out.hash.bounds, 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 = value.ok_or_else(|| "`hash_with` needs a value".to_string())?; + out.hash.hash_with = Some(syn::parse_str(&path).map_err(|e| e.to_string())?); } "ignore" => { - out.hash.ignore = try!(parse_boolean_meta_item(&value, true, "ignore")); + out.hash.ignore = parse_boolean_meta_item(value, true, "ignore")?; } } } "PartialEq" => { match_attributes! { for value in values; - "bound" => try!(parse_bound(&mut out.partial_eq.bounds, value)), + "bound" => parse_bound(&mut out.partial_eq.bounds, 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 = value.ok_or_else(|| "`compare_with` needs a value".to_string())?; + out.partial_eq.compare_with = Some(syn::parse_str(&path).map_err(|e| e.to_string())?); } "ignore" => { - out.partial_eq.ignore = try!(parse_boolean_meta_item(&value, true, "ignore")); + out.partial_eq.ignore = parse_boolean_meta_item(value, true, "ignore")?; } } } @@ -470,6 +479,25 @@ impl Field { } } +/// Filter the `derivative` items from an attribute. +fn derivative_attribute( + attr: &syn::Attribute, +) -> Option> { + attr.interpret_meta().and_then(|meta| { + match meta { + syn::Meta::List(syn::MetaList { ident, nested, .. }) => { + if ident == "derivative" { + Some(nested) + } else { + None + } + } + syn::Meta::Word(..) | + syn::Meta::NameValue(..) => None, + } + }) +} + /// Represent an attribute. /// /// We only have a limited set of possible attributes: @@ -477,79 +505,54 @@ impl Field { /// * `#[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>)>); - -/// Parse an arbitrary item for our limited `MetaItem` subset. -fn read_items(item: &syn::NestedMetaItem) -> Result { - let item = match *item { - syn::NestedMetaItem::MetaItem(ref item) => item, - syn::NestedMetaItem::Literal(..) => { - return Err("Expected meta-item but found literal".to_string()); +struct Meta(syn::Ident, Vec<(syn::Ident, Option)>); + +/// Parse an arbitrary meta for our limited `Meta` subset. +fn read_meta(meta: syn::NestedMeta) -> Result { + let meta = match meta { + syn::NestedMeta::Meta(meta) => meta, + syn::NestedMeta::Literal(_) => { + return Err("Expected meta 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) => { - let values = try!( - values - .iter() + + match meta { + syn::Meta::Word(name) => Ok(Meta(name, Vec::new())), + syn::Meta::List(syn::MetaList { ident, nested, .. }) => { + let values = nested + .into_iter() .map(|value| { - if let syn::NestedMetaItem::MetaItem(syn::MetaItem::NameValue(ref name, ref value)) = *value { - let value = try!(str_or_err(value)); + if let syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { ident, lit, .. })) = value { + let string_lit = string_or_err(&lit)?; - Ok((name.as_ref(), Some(value))) + Ok((ident, Some(string_lit))) } else { Err("Expected named value".to_string()) } }) - .collect() - ); + .collect::>()?; - Ok(MetaItem(name.as_ref(), values)) + Ok(Meta(ident, values)) } - syn::MetaItem::NameValue(ref name, ref value) => { - let value = try!(str_or_err(value)); + syn::Meta::NameValue(syn::MetaNameValue { ident, lit, .. }) => { + let string_lit = string_or_err(&lit)?; - Ok(MetaItem(name.as_ref(), vec![(value, None)])) + Ok(Meta(ident, vec![(syn::Ident::from(string_lit), None)])) } } } -/// 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, - } -} - -/// Parse an item value as a boolean. Accepted values are the string literal `"true"` and -/// `"false"`. The `default` parameter specifies what the value of the boolean is when only its -/// name is specified (eg. `Debug="ignore"` is equivalent to `Debug(ignore="true")`). The `name` -/// parameter is used for error reporting. -fn parse_boolean_meta_item(item: &Option<&str>, default: bool, name: &str) -> Result { - match *item { - Some("true") => Ok(true), - Some("false") => Ok(false), - Some(_) => Err(format!("Invalid value for `{}`", name)), - None => Ok(default), - } -} - /// Parse a `bound` item. fn parse_bound( opt_bounds: &mut Option>, - value: Option<&str> + value: Option, ) -> Result<(), String> { let mut bounds = opt_bounds.take().unwrap_or_default(); - let bound = try!(value.ok_or_else(|| "`bound` needs a value".to_string())); + let bound = 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::WhereClause = syn::parse_str(&format!("where {}", bound)).map_err(|e| e.to_string())?; + bounds.extend(where_clause.predicates); } *opt_bounds = Some(bounds); @@ -557,10 +560,23 @@ fn parse_bound( Ok(()) } +/// Parse an item value as a boolean. Accepted values are the string literal `"true"` and +/// `"false"`. The `default` parameter specifies what the value of the boolean is when only its +/// name is specified (eg. `Debug="ignore"` is equivalent to `Debug(ignore="true")`). The `name` +/// parameter is used for error reporting. +fn parse_boolean_meta_item(item: Option, default: bool, name: &str) -> Result { + match item.as_ref().map(String::as_str) { + Some("true") => Ok(true), + Some("false") => Ok(false), + Some(_) => Err(format!("Invalid value for `{}`", name)), + None => Ok(default), + } +} + /// 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_str) = *lit { + Ok(lit_str.value()) } else { Err("Expected string".to_string()) } diff --git a/src/bound.rs b/src/bound.rs index 29e2daf..2e590c0 100644 --- a/src/bound.rs +++ b/src/bound.rs @@ -13,19 +13,26 @@ use ast; use attr; use std::collections::HashSet; -use syn::{self, aster, visit}; - -// use internals::ast::Item; -// use internals::attr; +use syn; /// Remove the default from every type parameter because in the generated `impl`s /// they look like associated types: "error: associated type bindings are not /// 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(|param| { + match *param { + syn::GenericParam::Type(ref type_param) => { + syn::GenericParam::Type(syn::TypeParam { + default: None, + ..type_param.clone() + }) + } + ref other => other.clone(), + } + }) .collect(), ..generics.clone() } @@ -35,9 +42,27 @@ 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 predicates = predicates.iter().cloned(); + + syn::Generics { + where_clause: Some( + match generics.where_clause { + Some(ref where_clause) => { + syn::WhereClause { + where_token: where_clause.where_token, + predicates: where_clause.predicates.iter().cloned().chain(predicates).collect(), + } + } + None => { + syn::WhereClause { + where_token: ::default(), + predicates: predicates.collect(), + } + } + } + ), + ..generics.clone() + } } pub fn with_where_predicates_from_fields( @@ -47,14 +72,32 @@ pub fn with_where_predicates_from_fields( ) -> syn::Generics 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 all_fields = item.data.all_fields(); + let predicates = all_fields + .iter() + .flat_map(|field| from_field(&field.attrs)) + .flat_map(|predicates| predicates.iter()) + .cloned(); + + syn::Generics { + where_clause: Some( + match generics.where_clause { + Some(ref where_clause) => { + syn::WhereClause { + where_token: where_clause.where_token, + predicates: where_clause.predicates.iter().cloned().chain(predicates).collect(), + } + } + None => { + syn::WhereClause { + where_token: ::default(), + predicates: predicates.collect(), + } + } + } + ), + ..generics.clone() + } } /// Puts the given bound on any generic type parameters that are used in fields @@ -79,65 +122,82 @@ pub fn with_bound( where F: Fn(&attr::Field) -> bool, { #[derive(Debug)] - struct FindTyParams { + struct FindTypeParams { /// Set of all generic type parameters on the current struct (A, B, C in /// the example). Initialized up front. - all_ty_params: HashSet, + all_type_params: HashSet, /// Set of generic type parameters used in fields for which filter /// returns true (A and B in the example). Filled in as the visitor sees /// them. - relevant_ty_params: HashSet, + relevant_type_params: HashSet, } - impl visit::Visitor for FindTyParams { - fn visit_path(&mut self, path: &syn::Path) { - if let Some(seg) = path.segments.last() { + + impl<'ast> syn::visit::Visit<'ast> for FindTypeParams { + fn visit_path(&mut self, path: &'ast syn::Path) { + if let Some(seg) = path.segments.last().map(|pair| pair.into_value()) { 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 { - let id = path.segments[0].ident.clone(); - if self.all_ty_params.contains(&id) { - self.relevant_ty_params.insert(id); + + if !path.global() && path.segments.len() == 1 { + let id = path.segments[0].ident; + if self.all_type_params.contains(&id) { + self.relevant_type_params.insert(id); } } - visit::walk_path(self, path); + + syn::visit::visit_path(self, path); } } - let all_ty_params: HashSet<_> = generics.ty_params - .iter() - .map(|ty_param| ty_param.ident.clone()) + let all_type_params: HashSet<_> = generics.type_params() + .map(|type_param| type_param.ident) .collect(); - let relevant_tys = item.body + let relevant_types = item.data .all_fields() .into_iter() .filter(|field| filter(&field.attrs)) - .map(|field| &field.ty); + .map(|field| &field.type_); - let mut visitor = FindTyParams { - all_ty_params: all_ty_params, - relevant_ty_params: HashSet::new(), + let mut visitor = FindTypeParams { + all_type_params: all_type_params, + relevant_type_params: HashSet::new(), }; - for ty in relevant_tys { - visit::walk_ty(&mut visitor, ty); + + for type_ in relevant_types { + syn::visit::visit_type(&mut visitor, type_); } - aster::from_generics(generics.clone()) - .with_predicates(generics.ty_params - .iter() - .map(|ty_param| ty_param.ident.clone()) - .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() + let predicates = generics.type_params() + .map(|type_param| type_param.ident) + .filter(|id| visitor.relevant_type_params.contains(id)) + .map(|id| { + parse_quote! { + #id: #bound + } + }); + + syn::Generics { + where_clause: Some( + match generics.where_clause { + Some(ref where_clause) => { + syn::WhereClause { + where_token: where_clause.where_token, + predicates: where_clause.predicates.iter().cloned().chain(predicates).collect(), + } + } + None => { + syn::WhereClause { + where_token: ::default(), + predicates: predicates.collect(), + } + } + } + ), + ..generics.clone() + } } diff --git a/src/clone.rs b/src/clone.rs index b6a47dd..aefc144 100644 --- a/src/clone.rs +++ b/src/clone.rs @@ -2,19 +2,18 @@ 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 { - let name = &input.ident; - if input.attrs.derives_clone() { - return Err("`#[derivative(Copy)]` can't be used with `#[derive(Clone)]`".into()); + return Err("`#[derivative(Copy)]` can't be used with `#[derive(Clone)]`".to_string()); } + let name = input.ident; let copy_trait_path = copy_trait_path(); - let impl_generics = utils::build_impl_generics( + + let (impl_generics, path_args) = utils::build_generics( input, ©_trait_path, |attrs| attrs.copy_bound().is_none(), @@ -23,25 +22,25 @@ 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(); + let type_ = syn::Type::Path(syn::TypePath { + qself: None, + path: syn::PathSegment { + ident: name, + arguments: path_args, + }.into(), + }); Ok(quote! { - #[allow(unused_qualifications)] - impl #impl_generics #copy_trait_path for #ty #where_clause {} + impl #impl_generics #copy_trait_path for #type_ #where_clause {} }) } /// Derive `Clone` for `input`. pub fn derive_clone(input: &ast::Input) -> quote::Tokens { - let name = &input.ident; - + let name = input.ident; let clone_trait_path = clone_trait_path(); - let impl_generics = utils::build_impl_generics( + + let (impl_generics, path_args) = utils::build_generics( input, &clone_trait_path, needs_clone_bound, @@ -50,18 +49,18 @@ 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 type_ = syn::Type::Path(syn::TypePath { + qself: None, + path: syn::PathSegment { + ident: name, + arguments: path_args, + }.into(), + }); 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 #type_ #where_clause { fn clone(&self) -> Self { *self } @@ -71,7 +70,7 @@ pub fn derive_clone(input: &ast::Input) -> quote::Tokens { 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; + let arg = bi.ident; let clone = if let Some(clone_with) = bi.field.attrs.clone_with() { quote!(#clone_with(#arg)) @@ -109,8 +108,8 @@ pub fn derive_clone(input: &ast::Input) -> quote::Tokens { } }); - let clone_from = if input.attrs.clone_from() { - Some(matcher::Matcher::new(matcher::BindingStyle::RefMut) + let clone_from_body = if input.attrs.clone_from() { + let clone_from_def = 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()) @@ -120,8 +119,8 @@ pub fn derive_clone(input: &ast::Input) -> quote::Tokens { .iter() .zip(inner_bis) .map(|(outer_bi, inner_bi)| { - let outer = &outer_bi.ident; - let inner = &inner_bi.ident; + let outer = outer_bi.ident; + let inner = inner_bi.ident; quote!(#outer.clone_from(#inner);) }); @@ -140,16 +139,17 @@ pub fn derive_clone(input: &ast::Input) -> quote::Tokens { #body } } - }) - ) + }); + + Some(clone_from_def) } else { None }; - let clone_from = clone_from.map(|body| { + let clone_from_item = clone_from_body.map(|body| { // Enumerations are only cloned-from if both variants are the same. // If they are different, fallback to normal cloning. - let fallback = if let ast::Body::Enum(_) = input.body { + let fallback = if let ast::Data::Enum(_) = input.data { Some(quote!(*self = other.clone();)) } else { None @@ -167,15 +167,14 @@ 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 #type_ #where_clause { fn clone(&self) -> Self { match *self { #body } } - #clone_from + #clone_from_item } } } @@ -185,12 +184,12 @@ fn needs_clone_bound(attrs: &attr::Field) -> bool { attrs.clone_bound().is_none() } -/// 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() +/// Return the path of the `Copy` trait, that is `::std::marker::Copy`. +fn copy_trait_path() -> syn::Path { + parse_quote! { ::std::marker::Copy } } -/// Return the path of the `Copy` trait, that is `::std::marker::Clone`. -fn copy_trait_path() -> syn::Path { - aster::path().global().ids(&["std", "marker", "Copy"]).build() +/// Return the path of the `Clone` trait, that is `::std::clone::Clone`. +fn clone_trait_path() -> syn::Path { + parse_quote! { ::std::clone::Clone } } diff --git a/src/cmp.rs b/src/cmp.rs index 2fe859b..826d55c 100644 --- a/src/cmp.rs +++ b/src/cmp.rs @@ -3,15 +3,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 { - let name = &input.ident; - + let name = input.ident; let eq_trait_path = eq_trait_path(); - let impl_generics = utils::build_impl_generics( + + let (impl_generics, path_args) = utils::build_generics( input, &eq_trait_path, |attrs| attrs.eq_bound().is_none(), @@ -20,26 +20,26 @@ 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(); + let type_ = syn::Type::Path(syn::TypePath { + qself: None, + path: syn::PathSegment { + ident: name, + arguments: path_args, + }.into(), + }); quote! { - #[allow(unused_qualifications)] - impl #impl_generics #eq_trait_path for #ty #where_clause {} + impl #impl_generics #eq_trait_path for #type_ #where_clause {} } } /// Derive `PartialEq` for `input`. pub fn derive_partial_eq(input: &ast::Input) -> Result { - if let ast::Body::Enum(_) = input.body { + if let ast::Data::Enum(_) = input.data { 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".to_string() ); } } @@ -51,20 +51,20 @@ pub fn derive_partial_eq(input: &ast::Input) -> Result { .with_name("__other".into()) .build_arms(input, |_, inner_arm_name, _, _, inner_bis| { if outer_arm_name == inner_arm_name { - let cmp = outer_bis.iter().zip(inner_bis).map(|(o, i)| { - let outer_name = &o.ident; - let inner_name = &i.ident; + let cmp = outer_bis.iter().zip(inner_bis).filter_map(|(o, i)| { + let outer_name = o.ident; + let inner_name = i.ident; if o.field.attrs.ignore_partial_eq() { None } else if let Some(compare_fn) = o.field.attrs.partial_eq_compare_with() { - Some(quote!(&& #compare_fn(#outer_name, #inner_name))) + Some(quote!(#compare_fn(#outer_name, #inner_name))) } else { - Some(quote!(&& #outer_name == #inner_name)) + Some(quote!(#outer_name == #inner_name)) } }); - quote!(true #(#cmp)*) + quote!(true #(&& #cmp)*) } else { quote!(false) } @@ -77,10 +77,10 @@ pub fn derive_partial_eq(input: &ast::Input) -> Result { } }); - let name = &input.ident; - + let name = input.ident; let partial_eq_trait_path = partial_eq_trait_path(); - let impl_generics = utils::build_impl_generics( + + let (impl_generics, path_args) = utils::build_generics( input, &partial_eq_trait_path, |attrs| attrs.partial_eq_bound().is_none(), @@ -89,16 +89,16 @@ 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(); + let type_ = syn::Type::Path(syn::TypePath { + qself: None, + path: syn::PathSegment { + ident: name, + arguments: path_args, + }.into(), + }); Ok(quote! { - #[allow(unused_qualifications)] - impl #impl_generics #partial_eq_trait_path for #ty #where_clause { + impl #impl_generics #partial_eq_trait_path for #type_ #where_clause { fn eq(&self, other: &Self) -> bool { match *self { #body @@ -110,10 +110,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..a62658e 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -2,7 +2,7 @@ use ast; use attr; use matcher; use quote; -use syn::{self, aster}; +use syn; use utils; pub fn derive(input: &ast::Input) -> quote::Tokens { @@ -19,7 +19,7 @@ pub fn derive(input: &ast::Input) -> quote::Tokens { }); } - let arg = &bi.ident; + let arg = bi.ident; let dummy_debug = bi.field.attrs.debug_format_with().map(|format_fn| { format_with(bi.field, &arg, format_fn, input.generics.clone()) @@ -45,7 +45,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 method = syn::Ident::from(method); let name = arm_name.as_ref(); @@ -62,10 +62,10 @@ pub fn derive(input: &ast::Input) -> quote::Tokens { } }); - let name = &input.ident; - + let name = input.ident; let debug_trait_path = debug_trait_path(); - let impl_generics = utils::build_impl_generics( + + let (impl_generics, path_args) = utils::build_generics( input, &debug_trait_path, needs_debug_bound, @@ -74,16 +74,16 @@ 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 type_ = syn::Type::Path(syn::TypePath { + qself: None, + path: syn::PathSegment { + ident: name, + arguments: path_args, + }.into(), + }); quote! { - #[allow(unused_qualifications)] - impl #impl_generics #debug_trait_path for #ty #where_clause { + impl #impl_generics #debug_trait_path for #type_ #where_clause { fn fmt(&self, __f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match *self { #body @@ -99,7 +99,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( @@ -111,39 +111,95 @@ fn format_with( 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"))], + let (_, ctor_type_generics, _) = ctor_generics.split_for_impl(); + + fn new_where_clause() -> syn::WhereClause { + syn::WhereClause { + where_token: ::default(), + predicates: syn::punctuated::Punctuated::new(), + } + } + + fn get_or_insert_with(option: &mut Option, f: F) -> &mut T + where F: FnOnce() -> T, + { + match *option { + None => *option = Some(f()), + _ => (), + } + + match *option { + Some(ref mut v) => v, + _ => unreachable!(), + } + } + + generics + .make_where_clause() + .predicates + .extend(f.attrs + .debug_bound() + .unwrap_or(&[]) + .iter() + .cloned()); + generics.params.push(parse_quote!('_derivative)); + + for type_param in generics.params.iter().filter_map(|param| { + match *param { + syn::GenericParam::Type(ref type_param) => Some(type_param), + _ => None, + } + }) { + let path = type_param.ident.into(); + let bound: syn::TypeParamBound = parse_quote!('_derivative); + + get_or_insert_with(&mut generics.where_clause, new_where_clause) + .predicates + .push(syn::WherePredicate::Type(syn::PredicateType { + lifetimes: None, + bounded_ty: syn::Type::Path(syn::TypePath { + qself: None, + path: path, + }), + colon_token: ::default(), + bounds: vec![bound].into_iter().collect(), })); } - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); - let ty = f.ty; + let type_ = f.type_; // 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!( + quote! { let #arg_n = { - struct Dummy #ty_generics (&'_derivative #ty, ::std::marker::PhantomData <(#(#phantom),*)>) #where_clause; + struct Dummy #type_generics (&'_derivative #type_, ::std::marker::PhantomData <(#(#phantom),*)>) #where_clause; - impl #impl_generics #debug_trait_path for Dummy #ty_generics #where_clause { + impl #impl_generics #debug_trait_path for Dummy #type_generics #where_clause { fn fmt(&self, __f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { #format_fn(&self.0, __f) } } - Dummy:: #ctor_ty_generics (#arg_n, ::std::marker::PhantomData) + Dummy:: #ctor_type_generics (#arg_n, ::std::marker::PhantomData) }; - ) + } } + + + + + + + + + + + + + + + + diff --git a/src/default.rs b/src/default.rs index f31a117..e09eb6c 100644 --- a/src/default.rs +++ b/src/default.rs @@ -1,7 +1,7 @@ use ast; use attr; use quote; -use syn::{self, aster}; +use syn; use utils; /// Derive `Default` for `input`. @@ -13,41 +13,46 @@ pub fn derive(input: &ast::Input, default: &attr::InputDefault) -> quote::Tokens ) -> quote::Tokens { 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 default = f.attrs.default_value().map_or_else( - || quote!(::std::default::Default::default()), - |v| quote!(#v), - ); - - defaults.push(quote!(#name: #default)); + let defaults = fields + .iter() + .map(|f| { + 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), + ); + + quote!(#name: #default) + }); + + quote! { + #variant_name { + #(#defaults,)* + } } - - quote!(#variant_name { #(#defaults),* }) } ast::Style::Tuple => { - let mut defaults = Vec::new(); - - for f in fields { - let default = f.attrs.default_value().map_or_else( - || quote!(::std::default::Default::default()), - |v| quote!(#v), - ); - - defaults.push(default); + let defaults = fields + .iter() + .map(|f| { + f.attrs.default_value().map_or_else( + || quote!(::std::default::Default::default()), + |v| quote!(#v), + ) + }); + + quote! { + #variant_name ( #(#defaults,)* ) } - - quote!(#variant_name ( #(#defaults),* )) } ast::Style::Unit => quote!(#variant_name), } } - let name = &input.ident; + let name = input.ident; let default_trait_path = default_trait_path(); - let impl_generics = utils::build_impl_generics( + + let (impl_generics, path_args) = utils::build_generics( input, &default_trait_path, |attrs| attrs.default_bound().is_none(), @@ -56,18 +61,19 @@ 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| { + let type_ = syn::Type::Path(syn::TypePath { + qself: None, + path: syn::PathSegment { + ident: name, + arguments: path_args, + }.into(), + }); + + let body = match input.data { + ast::Data::Enum(ref variants) => { + let arms = variants.iter().filter_map(|variant| { if variant.attrs.default.is_some() { - let vname = &variant.ident; + let vname = variant.ident; Some(make_variant_data(quote!(#name::#vname), variant.style, &variant.fields)) } else { @@ -77,39 +83,37 @@ 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::Data::Struct(style, ref fields) => { + make_variant_data(quote!(#name), style, fields) } }; let new_fn = if default.new { - Some(quote!( - #[allow(unused_qualifications)] - impl #impl_generics #ty #where_clause { + Some(quote! { + impl #impl_generics #type_ #where_clause { /// Creates a default value for this type. #[inline] pub fn new() -> Self { #default_trait_path::default() } } - )) + }) } else { None }; - quote!( + quote! { #new_fn - #[allow(unused_qualifications)] - impl #impl_generics #default_trait_path for #ty #where_clause { + impl #impl_generics #default_trait_path for #type_ #where_clause { fn default() -> Self { #body } } - ) + } } /// 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..2b65004 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -2,21 +2,28 @@ 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 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 variant = if let ast::Data::Enum(_) = input.data { + Some(quote! { + #hash_trait_path::hash(&(#arm_path as u64), __state); + }) + } else { + None + }; + let field_prints = bis.iter().filter_map(|bi| { if bi.field.attrs.ignore_hash() { return None; } - let arg = &bi.ident; + let arg = bi.ident; if let Some(hash_with) = bi.field.attrs.hash_with() { Some(quote! { @@ -29,23 +36,16 @@ pub fn derive(input: &ast::Input) -> quote::Tokens { } }); - let variant = if let ast::Body::Enum(_) = input.body { - Some(quote!( - #hash_trait_path::hash(&(#arm_path as u64), __state); - )) - } else { - None - }; - quote! { #variant #(#field_prints)* } }); - let name = &input.ident; + let name = input.ident; + let hasher_trait_path = hasher_trait_path(); - let impl_generics = utils::build_impl_generics( + let (impl_generics, path_args) = utils::build_generics( input, &hash_trait_path, needs_hash_bound, @@ -54,19 +54,20 @@ 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 type_ = syn::Type::Path(syn::TypePath { + qself: None, + path: syn::PathSegment { + ident: name, + arguments: path_args, + }.into(), + }); + + let hasher_type_parameter = utils::hygienic_type_parameter(input, "__H"); - let hasher_ty_parameter = utils::hygienic_type_parameter(input, "__H"); quote! { - #[allow(unused_qualifications)] - impl #impl_generics #hash_trait_path for #ty #where_clause { - fn hash<#hasher_ty_parameter>(&self, __state: &mut #hasher_ty_parameter) - where #hasher_ty_parameter: #hasher_trait_path + impl #impl_generics #hash_trait_path for #type_ #where_clause { + fn hash<#hasher_type_parameter>(&self, __state: &mut #hasher_type_parameter) + where #hasher_type_parameter: #hasher_trait_path { match *self { #body @@ -80,12 +81,10 @@ fn needs_hash_bound(attrs: &attr::Field) -> bool { !attrs.ignore_hash() && attrs.hash_bound().is_none() } -/// 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() +fn hasher_trait_path() -> syn::Path { + parse_quote! { ::std::hash::Hasher } } -/// 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() +fn hash_trait_path() -> syn::Path { + parse_quote! { ::std::hash::Hash } } diff --git a/src/lib.rs b/src/lib.rs index f6fc20b..396e940 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ extern crate proc_macro; -extern crate syn; - #[macro_use] extern crate quote; +#[macro_use] +extern crate syn; mod ast; mod attr; @@ -17,29 +17,29 @@ mod utils; use proc_macro::TokenStream; -fn derive_impls(input: &ast::Input) -> Result { +fn derive_impls(input: ast::Input) -> Result { let mut tokens = quote::Tokens::new(); if input.attrs.clone.is_some() { - tokens.append(&clone::derive_clone(input).to_string()); + tokens.append_all(clone::derive_clone(&input)); } if input.attrs.copy.is_some() { - tokens.append(&try!(clone::derive_copy(input)).to_string()); + tokens.append_all(clone::derive_copy(&input)?); } if input.attrs.debug.is_some() { - tokens.append(&debug::derive(input).to_string()); + tokens.append_all(debug::derive(&input)); } if let Some(ref default) = input.attrs.default { - tokens.append(&default::derive(input, default).to_string()); + tokens.append_all(default::derive(&input, default)); } if input.attrs.eq.is_some() { - tokens.append(&cmp::derive_eq(input).to_string()); + tokens.append_all(cmp::derive_eq(&input)); } if input.attrs.hash.is_some() { - tokens.append(&hash::derive(input).to_string()); + tokens.append_all(hash::derive(&input)); } if input.attrs.partial_eq.is_some() { - tokens.append(&try!(cmp::derive_partial_eq(input)).to_string()); + tokens.append_all(cmp::derive_partial_eq(&input)?); } Ok(tokens) @@ -48,10 +48,10 @@ fn derive_impls(input: &ast::Input) -> Result { #[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()) + let input = syn::parse(input).map_err(|e| e.to_string())?; + let parsed = ast::Input::from_ast(&input)?; + let output = derive_impls(parsed)?; + Ok(output.into()) } match detail(input) { diff --git a/src/matcher.rs b/src/matcher.rs index 727a7e8..7268acd 100644 --- a/src/matcher.rs +++ b/src/matcher.rs @@ -8,9 +8,7 @@ use ast; use attr; -use quote; - -use quote::ToTokens; +use quote::{self, ToTokens}; use syn; /// The type of binding to use when generating a pattern. @@ -26,17 +24,19 @@ pub enum BindingStyle { RefMut, } -impl quote::ToTokens for BindingStyle { - fn to_tokens(&self, tokens: &mut quote::Tokens) { - match *self { - BindingStyle::Move => (), - BindingStyle::MoveMut => tokens.append("mut"), - BindingStyle::Ref => tokens.append("ref"), - BindingStyle::RefMut => { - tokens.append("ref"); - tokens.append("mut"); - } - } +fn to_pat_ident(binding_style: BindingStyle, ident: syn::Ident) -> syn::PatIdent { + let (by_ref, mutability) = match binding_style { + BindingStyle::Move => (None, None), + BindingStyle::MoveMut => (None, Some(::default())), + BindingStyle::Ref => (Some(::default()), None), + BindingStyle::RefMut => (Some(::default()), Some(::default())), + }; + + syn::PatIdent { + by_ref: by_ref, + mutability: mutability, + ident: ident, + subpat: None, } } @@ -64,89 +64,106 @@ impl Matcher { } 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 + where F: Fn(syn::Path, &syn::Ident, ast::Style, &attr::Input, Vec) -> quote::Tokens { - let ident = &input.ident; + 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() + let variants = match input.data { + ast::Data::Enum(ref variants) => { + variants + .iter() .map(|variant| { - let variant_ident = &variant.ident; - let variant_path = syn::aster::path().ids(&[ident, variant_ident]).build(); + let variant_ident = variant.ident; + let variant_path: syn::Path = parse_quote! { #ident::#variant_ident }; - let pat = self.build_match_pattern( - &variant_path, + let pattern_info = self.build_match_pattern( + variant_path.clone(), variant.style, - &variant.fields + &variant.fields, ); - (variant_path, variant_ident, variant.style, &variant.attrs, pat) + (variant_path, variant_ident, variant.style, &variant.attrs, pattern_info) }) .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))] + ast::Data::Struct(style, ref fields) => { + let path = ident.into(); + vec![(path, ident, style, &input.attrs, self.build_match_pattern(ident, style, fields))] } }; // Now that we have the patterns, generate the actual branches of the match // expression - let mut t = quote::Tokens::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); + let processed = variants + .into_iter() + .map(|(path, name, style, attrs, (pattern, bindings))| { + let body = f(path, &name, style, attrs, bindings); + quote! { #pattern => { #body } } + }); + + quote! { + #(#processed)* } - - t } pub fn build_match_pattern<'a, N>( &self, - name: &N, + name: N, style: ast::Style, fields: &'a [ast::Field<'a>] - ) - -> (quote::Tokens, Vec>) - where N: quote::ToTokens, + ) -> (quote::Tokens, Vec>) + where N: Into, { - let mut t = quote::Tokens::new(); - let mut matches = Vec::new(); - - let binding = self.binding_style; - name.to_tokens(&mut t); - match style { - ast::Style::Unit => {} - ast::Style::Tuple => { - t.append("("); - for (i, field) in fields.iter().enumerate() { - let ident: syn::Ident = format!("{}_{}", self.binding_name, i).into(); - quote!(#binding #ident ,).to_tokens(&mut t); - matches.push(BindingInfo { - ident: ident, - field: field, - }); + let matches: Vec<_> = fields + .iter() + .enumerate() + .map(|(i, field)| { + BindingInfo { + ident: format!("{}_{}", self.binding_name, i).into(), + field: field, } - t.append(")"); - } - ast::Style::Struct => { - t.append("{"); - for (i, field) in fields.iter().enumerate() { - let ident: syn::Ident = format!("{}_{}", self.binding_name, i).into(); - { - let field_name = field.ident.as_ref().unwrap(); - quote!(#field_name : #binding #ident ,).to_tokens(&mut t); - } - matches.push(BindingInfo { - ident: ident, - field: field, - }); - } - t.append("}"); - } - } + }) + .collect(); + + let match_expr = match style { + ast::Style::Unit => syn::Pat::Path(syn::PatPath { + qself: None, + path: name.into(), + }), + ast::Style::Tuple => syn::Pat::TupleStruct(syn::PatTupleStruct { + path: name.into(), + pat: syn::PatTuple { + paren_token: syn::token::Paren::default(), + front: matches + .iter() + .map(|&BindingInfo { ref ident, .. }| { + syn::Pat::Ident(to_pat_ident(self.binding_style, *ident)) + }) + .collect(), + dot2_token: None, + comma_token: None, + back: syn::punctuated::Punctuated::new(), + }, + }), + ast::Style::Struct => syn::Pat::Struct(syn::PatStruct { + path: name.into(), + brace_token: syn::token::Brace::default(), + fields: matches + .iter() + .map(|&BindingInfo { ref ident, ref field }| { + syn::FieldPat { + attrs: vec![], + member: syn::Member::Named(field.ident.unwrap()), + colon_token: Some(::default()), + pat: Box::new(syn::Pat::Ident(to_pat_ident(self.binding_style, *ident))), + } + }) + .collect(), + dot2_token: None, + }), + }; - (t, matches) + (match_expr.into_tokens(), matches) } } diff --git a/src/utils.rs b/src/utils.rs index cc433dd..883a5f0 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -5,13 +5,13 @@ use syn; /// Make generic with all the generics in the input, plus a bound `T: ` for each /// generic field type that will be shown. -pub fn build_impl_generics( +pub fn build_generics( item: &ast::Input, trait_path: &syn::Path, needs_debug_bound: F, field_bound: G, input_bound: H, -) -> syn::Generics +) -> (syn::Generics, syn::PathArguments) where F: Fn(&attr::Field) -> bool, G: Fn(&attr::Field) -> Option<&[syn::WherePredicate]>, H: Fn(&attr::Input) -> Option<&[syn::WherePredicate]>, @@ -21,25 +21,60 @@ pub fn build_impl_generics( item, &generics, field_bound ); - match input_bound(&item.attrs) { + let impl_generics = match input_bound(&item.attrs) { Some(predicates) => { bound::with_where_predicates(&generics, predicates) } None => { bound::with_bound(item, &generics, needs_debug_bound, trait_path) } - } + }; + + let path_args = if impl_generics.params.is_empty() { + syn::PathArguments::None + } else { + let generic_args = impl_generics.params + .iter() + .map(|param| { + match *param { + syn::GenericParam::Type(syn::TypeParam { ident, .. }) => { + syn::GenericArgument::Type(syn::Type::Path(syn::TypePath { + qself: None, + path: ident.into(), + })) + } + syn::GenericParam::Lifetime(syn::LifetimeDef { lifetime, .. }) => { + syn::GenericArgument::Lifetime(lifetime) + } + syn::GenericParam::Const(_) => { + // TODO: yet? + unimplemented!(); + } + } + }) + .collect(); + + syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + colon2_token: None, + lt_token: ::default(), + args: generic_args, + gt_token: ]>::default(), + }) + }; + + (impl_generics, path_args) } +// TODO: this function should be obsolete with `Span::def_site()` when it stabilizes /// Construct a name for the inner type parameter that can't collide with any /// 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 string_type_param = base.to_string(); - for ty in &item.generics.ty_params { - typaram.push_str(&ty.ident.as_ref()); + for param in item.generics.type_params() { + string_type_param.push_str(param.ident.as_ref()); } - syn::Ident::new(typaram) + syn::Ident::from(string_type_param) } diff --git a/tests/compile-fail/derive-partial-eq.rs b/tests/compile-fail/derive-partial-eq.rs index 2ba02da..781b1c8 100644 --- a/tests/compile-fail/derive-partial-eq.rs +++ b/tests/compile-fail/derive-partial-eq.rs @@ -2,7 +2,7 @@ extern crate derivative; #[derive(Derivative)] -//~^ ERROR custom derive attribute panicked +//~^ ERROR proc-macro derive panicked //~| HELP can't use `#[derivative(PartialEq)]` on an enumeration without `feature_allow_slow_enum` #[derivative(PartialEq)] enum Option { diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 84d9243..f015916 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -6,7 +6,8 @@ fn run_mode(dir: &'static str, mode: &'static str) { use std::path::PathBuf; use std::env::var; - let mut config = compiletest::default_config(); + //let mut config = compiletest::default_config(); + let mut config = compiletest::Config::default(); let cfg_mode = mode.parse().expect("Invalid mode"); config.target_rustcflags = Some("-L target/debug/ -L target/debug/deps".to_owned()); From f596f276cdf864481127181eec4436cfaa83a933 Mon Sep 17 00:00:00 2001 From: hcpl Date: Wed, 4 Apr 2018 11:48:06 +0300 Subject: [PATCH 2/3] Explain why we need such a workaround for `Debug` --- src/debug.rs | 50 +++++++++++++++++++++++++++++--------------------- src/hash.rs | 8 ++++---- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/debug.rs b/src/debug.rs index a62658e..28c35a9 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -113,27 +113,6 @@ fn format_with( let ctor_generics = generics.clone(); let (_, ctor_type_generics, _) = ctor_generics.split_for_impl(); - fn new_where_clause() -> syn::WhereClause { - syn::WhereClause { - where_token: ::default(), - predicates: syn::punctuated::Punctuated::new(), - } - } - - fn get_or_insert_with(option: &mut Option, f: F) -> &mut T - where F: FnOnce() -> T, - { - match *option { - None => *option = Some(f()), - _ => (), - } - - match *option { - Some(ref mut v) => v, - _ => unreachable!(), - } - } - generics .make_where_clause() .predicates @@ -144,6 +123,11 @@ fn format_with( .cloned()); generics.params.push(parse_quote!('_derivative)); + // The ergonomic-looking code has a problem with overlapping borrows: + // - immutable borrow when iterating over `TypeParam` via `Generics::type_params()` + // - mutable borrow when calling `Generics::make_where_clause()` + // + // The solution is to make borrows disjoint at the cost of readability. for type_param in generics.params.iter().filter_map(|param| { match *param { syn::GenericParam::Type(ref type_param) => Some(type_param), @@ -153,6 +137,30 @@ fn format_with( let path = type_param.ident.into(); let bound: syn::TypeParamBound = parse_quote!('_derivative); + // This is a workaround so that the code would work on Rust 1.15 + // NOTE: Implementation copied from the actual `Option::get_or_insert_with` (which is + // stable since Rust 1.20) + fn get_or_insert_with(option: &mut Option, f: F) -> &mut T + where F: FnOnce() -> T, + { + match *option { + None => *option = Some(f()), + _ => (), + } + + match *option { + Some(ref mut v) => v, + _ => unreachable!(), + } + } + + fn new_where_clause() -> syn::WhereClause { + syn::WhereClause { + where_token: ::default(), + predicates: syn::punctuated::Punctuated::new(), + } + } + get_or_insert_with(&mut generics.where_clause, new_where_clause) .predicates .push(syn::WherePredicate::Type(syn::PredicateType { diff --git a/src/hash.rs b/src/hash.rs index 2b65004..ad006d4 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -81,10 +81,10 @@ fn needs_hash_bound(attrs: &attr::Field) -> bool { !attrs.ignore_hash() && attrs.hash_bound().is_none() } -fn hasher_trait_path() -> syn::Path { - parse_quote! { ::std::hash::Hasher } -} - fn hash_trait_path() -> syn::Path { parse_quote! { ::std::hash::Hash } } + +fn hasher_trait_path() -> syn::Path { + parse_quote! { ::std::hash::Hasher } +} From 96b7942e44475172ff8a296f5242a129ea62bb3d Mon Sep 17 00:00:00 2001 From: hcpl Date: Wed, 4 Apr 2018 13:23:57 +0300 Subject: [PATCH 3/3] Declare `syn` as >=0.13.1 `Generics::make_where_clause()` appeared in 0.13.1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6c1869d..b242ccf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ proc-macro = true [dependencies] quote = "0.5" -syn = { version = "0.13", features = ["extra-traits", "full", "visit"] } +syn = { version = "0.13.1", features = ["extra-traits", "full", "visit"] } compiletest_rs = { version = "0.3", optional = true } [features]