diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 05:48:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 05:48:48 +0000 |
commit | ef24de24a82fe681581cc130f342363c47c0969a (patch) | |
tree | 0d494f7e1a38b95c92426f58fe6eaa877303a86c /vendor/derivative/src | |
parent | Releasing progress-linux version 1.74.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-ef24de24a82fe681581cc130f342363c47c0969a.tar.xz rustc-ef24de24a82fe681581cc130f342363c47c0969a.zip |
Merging upstream version 1.75.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | vendor/derivative/src/ast.rs | 160 | ||||
-rw-r--r-- | vendor/derivative/src/attr.rs | 887 | ||||
-rw-r--r-- | vendor/derivative/src/bound.rs | 175 | ||||
-rw-r--r-- | vendor/derivative/src/clone.rs | 196 | ||||
-rw-r--r-- | vendor/derivative/src/cmp.rs | 407 | ||||
-rw-r--r-- | vendor/derivative/src/debug.rs | 225 | ||||
-rw-r--r-- | vendor/derivative/src/default.rs | 120 | ||||
-rw-r--r-- | vendor/derivative/src/hash.rs | 97 | ||||
-rw-r--r-- | vendor/derivative/src/lib.rs | 94 | ||||
-rw-r--r-- | vendor/derivative/src/matcher.rs | 336 | ||||
-rw-r--r-- | vendor/derivative/src/paths.rs | 10 | ||||
-rw-r--r-- | vendor/derivative/src/utils.rs | 43 |
12 files changed, 2750 insertions, 0 deletions
diff --git a/vendor/derivative/src/ast.rs b/vendor/derivative/src/ast.rs new file mode 100644 index 000000000..cf06995fa --- /dev/null +++ b/vendor/derivative/src/ast.rs @@ -0,0 +1,160 @@ +use attr; +use proc_macro2; +use syn; +use syn::spanned::Spanned as SynSpanned; + +#[derive(Debug)] +pub struct Input<'a> { + pub attrs: attr::Input, + pub body: Body<'a>, + pub generics: &'a syn::Generics, + pub ident: syn::Ident, + pub span: proc_macro2::Span, +} + +#[derive(Debug)] +pub enum Body<'a> { + Enum(Vec<Variant<'a>>), + Struct(Style, Vec<Field<'a>>), +} + +#[derive(Debug)] +pub struct Variant<'a> { + pub attrs: attr::Input, + pub fields: Vec<Field<'a>>, + pub ident: syn::Ident, + pub style: Style, +} + +#[derive(Debug)] +pub struct Field<'a> { + pub attrs: attr::Field, + pub ident: Option<syn::Ident>, + pub ty: &'a syn::Type, + pub span: proc_macro2::Span, +} + +#[derive(Clone, Copy, Debug)] +pub enum Style { + Struct, + Tuple, + Unit, +} + +impl<'a> Input<'a> { + pub fn from_ast( + item: &'a syn::DeriveInput, + errors: &mut proc_macro2::TokenStream, + ) -> Result<Input<'a>, ()> { + let attrs = attr::Input::from_ast(&item.attrs, errors)?; + + let body = match item.data { + syn::Data::Enum(syn::DataEnum { ref variants, .. }) => { + Body::Enum(enum_from_ast(variants, errors)?) + } + syn::Data::Struct(syn::DataStruct { ref fields, .. }) => { + let (style, fields) = struct_from_ast(fields, errors)?; + Body::Struct(style, fields) + } + syn::Data::Union(..) => { + errors.extend( + syn::Error::new_spanned(item, "derivative does not support unions") + .to_compile_error(), + ); + return Err(()); + } + }; + + Ok(Input { + attrs, + body, + generics: &item.generics, + ident: item.ident.clone(), + span: item.span(), + }) + } + + /// Checks whether this type is an enum with only unit variants. + pub fn is_trivial_enum(&self) -> bool { + match &self.body { + Body::Enum(e) => e.iter().all(|v| v.is_unit()), + Body::Struct(..) => false, + } + } +} + +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::Struct(_, ref fields) => fields.iter().collect(), + } + } + + pub fn is_empty(&self) -> bool { + match *self { + Body::Enum(ref variants) => variants.is_empty(), + Body::Struct(_, ref fields) => fields.is_empty(), + } + } +} + +impl<'a> Variant<'a> { + /// Checks whether this variant is a unit variant. + pub fn is_unit(&self) -> bool { + self.fields.is_empty() + } +} + +fn enum_from_ast<'a>( + variants: &'a syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>, + errors: &mut proc_macro2::TokenStream, +) -> Result<Vec<Variant<'a>>, ()> { + variants + .iter() + .map(|variant| { + let (style, fields) = struct_from_ast(&variant.fields, errors)?; + Ok(Variant { + attrs: attr::Input::from_ast(&variant.attrs, errors)?, + fields, + ident: variant.ident.clone(), + style, + }) + }) + .collect() +} + +fn struct_from_ast<'a>( + fields: &'a syn::Fields, + errors: &mut proc_macro2::TokenStream, +) -> Result<(Style, Vec<Field<'a>>), ()> { + match *fields { + syn::Fields::Named(ref fields) => { + Ok((Style::Struct, fields_from_ast(&fields.named, errors)?)) + } + syn::Fields::Unnamed(ref fields) => { + Ok((Style::Tuple, fields_from_ast(&fields.unnamed, errors)?)) + } + syn::Fields::Unit => Ok((Style::Unit, Vec::new())), + } +} + +fn fields_from_ast<'a>( + fields: &'a syn::punctuated::Punctuated<syn::Field, syn::token::Comma>, + errors: &mut proc_macro2::TokenStream, +) -> Result<Vec<Field<'a>>, ()> { + fields + .iter() + .map(|field| { + Ok(Field { + attrs: attr::Field::from_ast(field, errors)?, + ident: field.ident.clone(), + ty: &field.ty, + span: field.span(), + }) + }) + .collect() +} diff --git a/vendor/derivative/src/attr.rs b/vendor/derivative/src/attr.rs new file mode 100644 index 000000000..01ac740bd --- /dev/null +++ b/vendor/derivative/src/attr.rs @@ -0,0 +1,887 @@ +use proc_macro2; +use syn; +use syn::spanned::Spanned; + +/// Represent the `derivative` attributes on the input type (`struct`/`enum`). +#[derive(Debug, Default)] +pub struct Input { + /// Whether `Clone` is present and its specific attributes. + pub clone: Option<InputClone>, + /// Whether `Copy` is present and its specific attributes. + pub copy: Option<InputCopy>, + /// Whether `Debug` is present and its specific attributes. + pub debug: Option<InputDebug>, + /// Whether `Default` is present and its specific attributes. + pub default: Option<InputDefault>, + /// Whether `Eq` is present and its specific attributes. + pub eq: Option<InputEq>, + /// Whether `Hash` is present and its specific attributes. + pub hash: Option<InputHash>, + /// Whether `PartialEq` is present and its specific attributes. + pub partial_eq: Option<InputPartialEq>, + /// Whether `PartialOrd` is present and its specific attributes. + pub partial_ord: Option<InputPartialOrd>, + /// Whether `Ord` is present and its specific attributes. + pub ord: Option<InputOrd>, + pub is_packed: bool, +} + +#[derive(Debug, Default)] +/// Represent the `derivative` attributes on a field. +pub struct Field { + /// The parameters for `Clone`. + clone: FieldClone, + /// The parameters for `Copy`. + copy_bound: Option<Vec<syn::WherePredicate>>, + /// The parameters for `Debug`. + debug: FieldDebug, + /// The parameters for `Default`. + default: FieldDefault, + /// The parameters for `Eq`. + eq_bound: Option<Vec<syn::WherePredicate>>, + /// The parameters for `Hash`. + hash: FieldHash, + /// The parameters for `PartialEq`. + partial_eq: FieldPartialEq, + /// The parameters for `PartialOrd`. + partial_ord: FieldPartialOrd, + /// The parameters for `Ord`. + ord: FieldOrd, +} + +#[derive(Debug, Default)] +/// Represent the `derivative(Clone(…))` attributes on an input. +pub struct InputClone { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, + /// Whether the implementation should have an explicit `clone_from`. + pub clone_from: bool, +} + +#[derive(Debug, Default)] +/// Represent the `derivative(Clone(…))` attributes on an input. +pub struct InputCopy { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, +} + +#[derive(Debug, Default)] +/// Represent the `derivative(Debug(…))` attributes on an input. +pub struct InputDebug { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, + /// Whether the type is marked `transparent`. + pub transparent: bool, +} + +#[derive(Debug, Default)] +/// Represent the `derivative(Default(…))` attributes on an input. +pub struct InputDefault { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, + /// Whether the type is marked with `new`. + pub new: bool, +} + +#[derive(Debug, Default)] +/// Represent the `derivative(Eq(…))` attributes on an input. +pub struct InputEq { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, +} + +#[derive(Debug, Default)] +/// Represent the `derivative(Hash(…))` attributes on an input. +pub struct InputHash { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, +} + +#[derive(Debug, Default)] +/// Represent the `derivative(PartialEq(…))` attributes on an input. +pub struct InputPartialEq { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, +} + +#[derive(Debug, Default)] +/// Represent the `derivative(PartialOrd(…))` attributes on an input. +pub struct InputPartialOrd { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, + /// Allow `derivative(PartialOrd)` on enums: + on_enum: bool, +} + +#[derive(Debug, Default)] +/// Represent the `derivative(Ord(…))` attributes on an input. +pub struct InputOrd { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, + /// Allow `derivative(Ord)` on enums: + on_enum: bool, +} + +#[derive(Debug, Default)] +/// Represents the `derivative(Clone(…))` attributes on a field. +pub struct FieldClone { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, + /// The `clone_with` attribute if present and the path to the cloning function. + clone_with: Option<syn::Path>, +} + +#[derive(Debug, Default)] +/// Represents the `derivative(Debug(…))` attributes on a field. +pub struct FieldDebug { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, + /// The `format_with` attribute if present and the path to the formatting function. + format_with: Option<syn::Path>, + /// Whether the field is to be ignored from output. + ignore: bool, +} + +#[derive(Debug, Default)] +/// Represent the `derivative(Default(…))` attributes on a field. +pub struct FieldDefault { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, + /// The default value for the field if present. + pub value: Option<proc_macro2::TokenStream>, +} + +#[derive(Debug, Default)] +/// Represents the `derivative(Hash(…))` attributes on a field. +pub struct FieldHash { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, + /// The `hash_with` attribute if present and the path to the hashing function. + hash_with: Option<syn::Path>, + /// Whether the field is to be ignored when hashing. + ignore: bool, +} + +#[derive(Debug, Default)] +/// Represent the `derivative(PartialEq(…))` attributes on a field. +pub struct FieldPartialEq { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, + /// The `compare_with` attribute if present and the path to the comparison function. + compare_with: Option<syn::Path>, + /// Whether the field is to be ignored when comparing. + ignore: bool, +} + +#[derive(Debug, Default)] +/// Represent the `derivative(PartialOrd(…))` attributes on a field. +pub struct FieldPartialOrd { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, + /// The `compare_with` attribute if present and the path to the comparison function. + compare_with: Option<syn::Path>, + /// Whether the field is to be ignored when comparing. + ignore: bool, +} + +#[derive(Debug, Default)] +/// Represent the `derivative(Ord(…))` attributes on a field. +pub struct FieldOrd { + /// The `bound` attribute if present and the corresponding bounds. + bounds: Option<Vec<syn::WherePredicate>>, + /// The `compare_with` attribute if present and the path to the comparison function. + compare_with: Option<syn::Path>, + /// Whether the field is to be ignored when comparing. + ignore: bool, +} + +macro_rules! for_all_attr { + ($errors:ident; for ($name:ident, $value:ident) in $attrs:expr; $($body:tt)*) => { + for meta_items in $attrs.iter() { + let meta_items = derivative_attribute(meta_items, $errors); + if let Some(meta_items) = meta_items { + for meta_item in meta_items.iter() { + let meta_item = read_items(meta_item, $errors); + let MetaItem($name, $value) = try!(meta_item); + match $name.to_string().as_ref() { + $($body)* + } + } + } + } + }; +} + +macro_rules! match_attributes { + ($errors:ident for $trait:expr; let Some($name:ident) = $unwrapped:expr; for $value:ident in $values:expr; $($body:tt)* ) => { + let mut $name = $unwrapped.take().unwrap_or_default(); + + match_attributes! { + $errors for $trait; + for $value in $values; + $($body)* + } + + $unwrapped = Some($name); + }; + + ($errors:ident for $trait:expr; for $value:ident in $values:expr; $($body:tt)* ) => { + for (name, $value) in $values { + match name { + Some(ident) => { + match ident.to_string().as_ref() { + $($body)* + unknown => { + let message = format!("Unknown attribute `{}` for trait `{}`", unknown, $trait); + $errors.extend(quote_spanned! {ident.span()=> + compile_error!(#message); + }); + } + } + } + None => { + let value = $value.expect("Expected value to be passed"); + match value.value().as_ref() { + $($body)* + unknown => { + let message = format!("Unknown attribute `{}` for trait `{}`", unknown, $trait); + let span = value.span(); + $errors.extend(quote_spanned! {span=> + compile_error!(#message); + }); + } + } + } + } + } + }; +} + +impl Input { + /// Parse the `derivative` attributes on a type. + #[allow(clippy::cognitive_complexity)] // mostly macros + pub fn from_ast( + attrs: &[syn::Attribute], + errors: &mut proc_macro2::TokenStream, + ) -> Result<Input, ()> { + let mut input = Input { + is_packed: attrs.iter().any(has_repr_packed_attr), + ..Default::default() + }; + + for_all_attr! { + errors; + for (name, values) in attrs; + "Clone" => { + match_attributes! { + errors for "Clone"; + let Some(clone) = input.clone; + for value in values; + "bound" => parse_bound(&mut clone.bounds, value, errors), + "clone_from" => { + clone.clone_from = parse_boolean_meta_item(value, true, "clone_from", errors); + } + } + } + "Copy" => { + match_attributes! { + errors for "Copy"; + let Some(copy) = input.copy; + for value in values; + "bound" => parse_bound(&mut copy.bounds, value, errors), + } + } + "Debug" => { + match_attributes! { + errors for "Debug"; + let Some(debug) = input.debug; + for value in values; + "bound" => parse_bound(&mut debug.bounds, value, errors), + "transparent" => { + debug.transparent = parse_boolean_meta_item(value, true, "transparent", errors); + } + } + } + "Default" => { + match_attributes! { + errors for "Default"; + let Some(default) = input.default; + for value in values; + "bound" => parse_bound(&mut default.bounds, value, errors), + "new" => { + default.new = parse_boolean_meta_item(value, true, "new", errors); + } + } + } + "Eq" => { + match_attributes! { + errors for "Eq"; + let Some(eq) = input.eq; + for value in values; + "bound" => parse_bound(&mut eq.bounds, value, errors), + } + } + "Hash" => { + match_attributes! { + errors for "Hash"; + let Some(hash) = input.hash; + for value in values; + "bound" => parse_bound(&mut hash.bounds, value, errors), + } + } + "PartialEq" => { + match_attributes! { + errors for "PartialEq"; + let Some(partial_eq) = input.partial_eq; + for value in values; + "bound" => parse_bound(&mut partial_eq.bounds, value, errors), + "feature_allow_slow_enum" => (), // backward compatibility, now unnecessary + } + } + "PartialOrd" => { + match_attributes! { + errors for "PartialOrd"; + let Some(partial_ord) = input.partial_ord; + for value in values; + "bound" => parse_bound(&mut partial_ord.bounds, value, errors), + "feature_allow_slow_enum" => { + partial_ord.on_enum = parse_boolean_meta_item(value, true, "feature_allow_slow_enum", errors); + } + } + } + "Ord" => { + match_attributes! { + errors for "Ord"; + let Some(ord) = input.ord; + for value in values; + "bound" => parse_bound(&mut ord.bounds, value, errors), + "feature_allow_slow_enum" => { + ord.on_enum = parse_boolean_meta_item(value, true, "feature_allow_slow_enum", errors); + } + } + } + unknown => { + let message = format!("deriving `{}` is not supported by derivative", unknown); + errors.extend(quote_spanned! {name.span()=> + compile_error!(#message); + }); + } + } + + Ok(input) + } + + pub fn clone_bound(&self) -> Option<&[syn::WherePredicate]> { + self.clone + .as_ref() + .and_then(|d| d.bounds.as_ref().map(Vec::as_slice)) + } + + pub fn clone_from(&self) -> bool { + self.clone.as_ref().map_or(false, |d| d.clone_from) + } + + pub fn copy_bound(&self) -> Option<&[syn::WherePredicate]> { + self.copy + .as_ref() + .and_then(|d| d.bounds.as_ref().map(Vec::as_slice)) + } + + pub fn debug_bound(&self) -> Option<&[syn::WherePredicate]> { + self.debug + .as_ref() + .and_then(|d| d.bounds.as_ref().map(Vec::as_slice)) + } + + pub fn debug_transparent(&self) -> bool { + self.debug.as_ref().map_or(false, |d| d.transparent) + } + + pub fn default_bound(&self) -> Option<&[syn::WherePredicate]> { + self.default + .as_ref() + .and_then(|d| d.bounds.as_ref().map(Vec::as_slice)) + } + + pub fn eq_bound(&self) -> Option<&[syn::WherePredicate]> { + self.eq + .as_ref() + .and_then(|d| d.bounds.as_ref().map(Vec::as_slice)) + } + + pub fn hash_bound(&self) -> Option<&[syn::WherePredicate]> { + self.hash + .as_ref() + .and_then(|d| d.bounds.as_ref().map(Vec::as_slice)) + } + + pub fn partial_eq_bound(&self) -> Option<&[syn::WherePredicate]> { + self.partial_eq + .as_ref() + .and_then(|d| d.bounds.as_ref().map(Vec::as_slice)) + } + + pub fn partial_ord_bound(&self) -> Option<&[syn::WherePredicate]> { + self.partial_ord + .as_ref() + .and_then(|d| d.bounds.as_ref().map(Vec::as_slice)) + } + + pub fn ord_bound(&self) -> Option<&[syn::WherePredicate]> { + self.ord + .as_ref() + .and_then(|d| d.bounds.as_ref().map(Vec::as_slice)) + } + + pub fn partial_ord_on_enum(&self) -> bool { + self.partial_ord.as_ref().map_or(false, |d| d.on_enum) + } + + pub fn ord_on_enum(&self) -> bool { + self.ord.as_ref().map_or(false, |d| d.on_enum) + } +} + +impl Field { + /// Parse the `derivative` attributes on a type. + #[allow(clippy::cognitive_complexity)] // mostly macros + pub fn from_ast( + field: &syn::Field, + errors: &mut proc_macro2::TokenStream, + ) -> Result<Field, ()> { + let mut out = Field::default(); + + for_all_attr! { + errors; + for (name, values) in field.attrs; + "Clone" => { + match_attributes! { + errors for "Clone"; + for value in values; + "bound" => parse_bound(&mut out.clone.bounds, value, errors), + "clone_with" => { + let path = value.expect("`clone_with` needs a value"); + out.clone.clone_with = parse_str_lit(&path, errors).ok(); + } + } + } + "Debug" => { + match_attributes! { + errors for "Debug"; + for value in values; + "bound" => parse_bound(&mut out.debug.bounds, value, errors), + "format_with" => { + let path = value.expect("`format_with` needs a value"); + out.debug.format_with = parse_str_lit(&path, errors).ok(); + } + "ignore" => { + out.debug.ignore = parse_boolean_meta_item(value, true, "ignore", errors); + } + } + } + "Default" => { + match_attributes! { + errors for "Default"; + for value in values; + "bound" => parse_bound(&mut out.default.bounds, value, errors), + "value" => { + let value = value.expect("`value` needs a value"); + out.default.value = parse_str_lit(&value, errors).ok(); + } + } + } + "Eq" => { + match_attributes! { + errors for "Eq"; + for value in values; + "bound" => parse_bound(&mut out.eq_bound, value, errors), + } + } + "Hash" => { + match_attributes! { + errors for "Hash"; + for value in values; + "bound" => parse_bound(&mut out.hash.bounds, value, errors), + "hash_with" => { + let path = value.expect("`hash_with` needs a value"); + out.hash.hash_with = parse_str_lit(&path, errors).ok(); + } + "ignore" => { + out.hash.ignore = parse_boolean_meta_item(value, true, "ignore", errors); + } + } + } + "PartialEq" => { + match_attributes! { + errors for "PartialEq"; + for value in values; + "bound" => parse_bound(&mut out.partial_eq.bounds, value, errors), + "compare_with" => { + let path = value.expect("`compare_with` needs a value"); + out.partial_eq.compare_with = parse_str_lit(&path, errors).ok(); + } + "ignore" => { + out.partial_eq.ignore = parse_boolean_meta_item(value, true, "ignore", errors); + } + } + } + "PartialOrd" => { + match_attributes! { + errors for "PartialOrd"; + for value in values; + "bound" => parse_bound(&mut out.partial_ord.bounds, value, errors), + "compare_with" => { + let path = value.expect("`compare_with` needs a value"); + out.partial_ord.compare_with = parse_str_lit(&path, errors).ok(); + } + "ignore" => { + out.partial_ord.ignore = parse_boolean_meta_item(value, true, "ignore", errors); + } + } + } + "Ord" => { + match_attributes! { + errors for "Ord"; + for value in values; + "bound" => parse_bound(&mut out.ord.bounds, value, errors), + "compare_with" => { + let path = value.expect("`compare_with` needs a value"); + out.ord.compare_with = parse_str_lit(&path, errors).ok(); + } + "ignore" => { + out.ord.ignore = parse_boolean_meta_item(value, true, "ignore", errors); + } + } + } + unknown => { + let message = format!("deriving `{}` is not supported by derivative", unknown); + errors.extend(quote_spanned! {name.span()=> + compile_error!(#message); + }); + } + } + + Ok(out) + } + + pub fn clone_bound(&self) -> Option<&[syn::WherePredicate]> { + self.clone.bounds.as_ref().map(Vec::as_slice) + } + + pub fn clone_with(&self) -> Option<&syn::Path> { + self.clone.clone_with.as_ref() + } + + pub fn copy_bound(&self) -> Option<&[syn::WherePredicate]> { + self.copy_bound.as_ref().map(Vec::as_slice) + } + + pub fn debug_bound(&self) -> Option<&[syn::WherePredicate]> { + self.debug.bounds.as_ref().map(Vec::as_slice) + } + + pub fn debug_format_with(&self) -> Option<&syn::Path> { + self.debug.format_with.as_ref() + } + + pub fn ignore_debug(&self) -> bool { + self.debug.ignore + } + + pub fn ignore_hash(&self) -> bool { + self.hash.ignore + } + + pub fn default_bound(&self) -> Option<&[syn::WherePredicate]> { + self.default.bounds.as_ref().map(Vec::as_slice) + } + + pub fn default_value(&self) -> Option<&proc_macro2::TokenStream> { + self.default.value.as_ref() + } + + pub fn eq_bound(&self) -> Option<&[syn::WherePredicate]> { + self.eq_bound.as_ref().map(Vec::as_slice) + } + + pub fn hash_bound(&self) -> Option<&[syn::WherePredicate]> { + self.hash.bounds.as_ref().map(Vec::as_slice) + } + + pub fn hash_with(&self) -> Option<&syn::Path> { + self.hash.hash_with.as_ref() + } + + pub fn partial_eq_bound(&self) -> Option<&[syn::WherePredicate]> { + self.partial_eq.bounds.as_ref().map(Vec::as_slice) + } + + pub fn partial_ord_bound(&self) -> Option<&[syn::WherePredicate]> { + self.partial_ord.bounds.as_ref().map(Vec::as_slice) + } + + pub fn ord_bound(&self) -> Option<&[syn::WherePredicate]> { + self.ord.bounds.as_ref().map(Vec::as_slice) + } + + pub fn partial_eq_compare_with(&self) -> Option<&syn::Path> { + self.partial_eq.compare_with.as_ref() + } + + pub fn partial_ord_compare_with(&self) -> Option<&syn::Path> { + self.partial_ord.compare_with.as_ref() + } + + pub fn ord_compare_with(&self) -> Option<&syn::Path> { + self.ord.compare_with.as_ref() + } + + pub fn ignore_partial_eq(&self) -> bool { + self.partial_eq.ignore + } + + pub fn ignore_partial_ord(&self) -> bool { + self.partial_ord.ignore + } + + pub fn ignore_ord(&self) -> bool { + self.ord.ignore + } +} + +/// Represent an attribute. +/// +/// We only have a limited set of possible attributes: +/// +/// * `#[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<&'a syn::LitStr>)>, +); + +/// Parse an arbitrary item for our limited `MetaItem` subset. +fn read_items<'a>(item: &'a syn::NestedMeta, errors: &mut proc_macro2::TokenStream) -> Result<MetaItem<'a>, ()> { + let item = match *item { + syn::NestedMeta::Meta(ref item) => item, + syn::NestedMeta::Lit(ref lit) => { + errors.extend(quote_spanned! {lit.span()=> + compile_error!("expected meta-item but found literal"); + }); + + return Err(()); + } + }; + match *item { + syn::Meta::Path(ref path) => match path.get_ident() { + Some(name) => Ok(MetaItem(name, Vec::new())), + None => { + errors.extend(quote_spanned! {path.span()=> + compile_error!("expected derivative attribute to be a string, but found a path"); + }); + + Err(()) + } + }, + syn::Meta::List(syn::MetaList { + ref path, + nested: ref values, + .. + }) => { + let values = values + .iter() + .map(|value| { + if let syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { + ref path, + lit: ref value, + .. + })) = *value + { + let (name, value) = ensure_str_lit(&path, &value, errors)?; + + Ok((Some(name), Some(value))) + } else { + errors.extend(quote_spanned! {value.span()=> + compile_error!("expected named value"); + }); + + Err(()) + } + }) + .collect::<Result<_, _>>()?; + + let name = match path.get_ident() { + Some(name) => name, + None => { + errors.extend(quote_spanned! {path.span()=> + compile_error!("expected derivative attribute to be a string, but found a path"); + }); + + return Err(()); + } + }; + + Ok(MetaItem(name, values)) + } + syn::Meta::NameValue(syn::MetaNameValue { + ref path, + lit: ref value, + .. + }) => { + let (name, value) = ensure_str_lit(&path, &value, errors)?; + + Ok(MetaItem(name, vec![(None, Some(value))])) + } + } +} + +/// Filter the `derivative` items from an attribute. +fn derivative_attribute( + attribute: &syn::Attribute, + errors: &mut proc_macro2::TokenStream, +) -> Option<syn::punctuated::Punctuated<syn::NestedMeta, syn::token::Comma>> { + if !attribute.path.is_ident("derivative") { + return None; + } + match attribute.parse_meta() { + Ok(syn::Meta::List(meta_list)) => Some(meta_list.nested), + Ok(_) => None, + Err(e) => { + let message = format!("invalid attribute: {}", e); + errors.extend(quote_spanned! {e.span()=> + compile_error!(#message); + }); + + 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<&syn::LitStr>, + default: bool, + name: &str, + errors: &mut proc_macro2::TokenStream, +) -> bool { + if let Some(item) = item.as_ref() { + match item.value().as_ref() { + "true" => true, + "false" => false, + val => { + if val == name { + true + } else { + let message = format!( + r#"expected `"true"` or `"false"` for `{}`, got `{}`"#, + name, val + ); + errors.extend(quote_spanned! {item.span()=> + compile_error!(#message); + }); + + default + } + } + } + } else { + default + } +} + +/// Parse a `bound` item. +fn parse_bound( + opt_bounds: &mut Option<Vec<syn::WherePredicate>>, + value: Option<&syn::LitStr>, + errors: &mut proc_macro2::TokenStream, +) { + let bound = value.expect("`bound` needs a value"); + let bound_value = bound.value(); + + *opt_bounds = if !bound_value.is_empty() { + let where_string = syn::LitStr::new(&format!("where {}", bound_value), bound.span()); + + let bounds = parse_str_lit::<syn::WhereClause>(&where_string, errors) + .map(|wh| wh.predicates.into_iter().collect()); + + match bounds { + Ok(bounds) => Some(bounds), + Err(_) => { + errors.extend(quote_spanned! {where_string.span()=> + compile_error!("could not parse bound"); + }); + + None + } + } + } else { + Some(vec![]) + }; +} + +fn parse_str_lit<T>(value: &syn::LitStr, errors: &mut proc_macro2::TokenStream) -> Result<T, ()> +where + T: syn::parse::Parse, +{ + match value.parse() { + Ok(value) => Ok(value), + Err(e) => { + let message = format!("could not parse string literal: {}", e); + errors.extend(quote_spanned! {value.span()=> + compile_error!(#message); + }); + Err(()) + } + } +} + +fn ensure_str_lit<'a>( + attr_path: &'a syn::Path, + lit: &'a syn::Lit, + errors: &mut proc_macro2::TokenStream, +) -> Result<(&'a syn::Ident, &'a syn::LitStr), ()> { + let attr_name = match attr_path.get_ident() { + Some(attr_name) => attr_name, + None => { + errors.extend(quote_spanned! {attr_path.span()=> + compile_error!("expected derivative attribute to be a string, but found a path"); + }); + return Err(()); + } + }; + + if let syn::Lit::Str(ref lit) = *lit { + Ok((attr_name, lit)) + } else { + let message = format!( + "expected derivative {} attribute to be a string: `{} = \"...\"`", + attr_name, attr_name + ); + errors.extend(quote_spanned! {lit.span()=> + compile_error!(#message); + }); + Err(()) + } +} + +pub fn has_repr_packed_attr(attr: &syn::Attribute) -> bool { + if let Ok(attr) = attr.parse_meta() { + if attr.path().get_ident().map(|i| i == "repr") == Some(true) { + if let syn::Meta::List(items) = attr { + for item in items.nested { + if let syn::NestedMeta::Meta(item) = item { + if item.path().get_ident().map(|i| i == "packed") == Some(true) { + return true; + } + } + } + } + } + } + + false +} diff --git a/vendor/derivative/src/bound.rs b/vendor/derivative/src/bound.rs new file mode 100644 index 000000000..4c1361c0a --- /dev/null +++ b/vendor/derivative/src/bound.rs @@ -0,0 +1,175 @@ +/* This file incorporates work covered by the following copyright and + * permission notice: + * Copyright 2016 The serde Developers. See + * https://github.com/serde-rs/serde/blob/3f28a9324042950afa80354722aeeee1a55cbfa3/README.md#license. + * + * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or + * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license + * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your + * option. This file may not be copied, modified, or distributed + * except according to those terms. + */ + +use ast; +use attr; +use std::collections::HashSet; +use syn::{self, visit, GenericParam}; + +// use internals::ast::Item; +// use internals::attr; + +/// 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 { + params: generics + .params + .iter() + .map(|generic_param| match *generic_param { + GenericParam::Type(ref ty_param) => syn::GenericParam::Type(syn::TypeParam { + default: None, + ..ty_param.clone() + }), + ref param => param.clone(), + }) + .collect(), + ..generics.clone() + } +} + +pub fn with_where_predicates( + generics: &syn::Generics, + predicates: &[syn::WherePredicate], +) -> syn::Generics { + let mut cloned = generics.clone(); + cloned + .make_where_clause() + .predicates + .extend(predicates.iter().cloned()); + cloned +} + +pub fn with_where_predicates_from_fields<F>( + item: &ast::Input, + generics: &syn::Generics, + from_field: F, +) -> syn::Generics +where + F: Fn(&attr::Field) -> Option<&[syn::WherePredicate]>, +{ + 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()); + + cloned + .make_where_clause() + .predicates + .extend(field_where_predicates); + } + cloned +} + +/// Puts the given bound on any generic type parameters that are used in fields +/// for which filter returns true. +/// +/// For example, the following structure needs the bound `A: Debug, B: Debug`. +/// +/// ```ignore +/// struct S<'b, A, B: 'b, C> { +/// a: A, +/// b: Option<&'b B> +/// #[derivative(Debug="ignore")] +/// c: C, +/// } +/// ``` +pub fn with_bound<F>( + item: &ast::Input, + generics: &syn::Generics, + filter: F, + bound: &syn::Path, +) -> syn::Generics +where + F: Fn(&attr::Field) -> bool, +{ + #[derive(Debug)] + struct FindTyParams { + /// Set of all generic type parameters on the current struct (A, B, C in + /// the example). Initialized up front. + all_ty_params: HashSet<syn::Ident>, + /// 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<syn::Ident>, + } + impl<'ast> visit::Visit<'ast> for FindTyParams { + fn visit_path(&mut self, path: &'ast syn::Path) { + if is_phantom_data(path) { + // Hardcoded exception, because `PhantomData<T>` implements + // most traits whether or not `T` implements it. + return; + } + if path.leading_colon.is_none() && path.segments.len() == 1 { + let id = &path.segments[0].ident; + if self.all_ty_params.contains(id) { + self.relevant_ty_params.insert(id.clone()); + } + } + visit::visit_path(self, path); + } + } + + let all_ty_params: HashSet<_> = generics + .type_params() + .map(|ty_param| ty_param.ident.clone()) + .collect(); + + let relevant_tys = item + .body + .all_fields() + .into_iter() + .filter(|field| { + if let syn::Type::Path(syn::TypePath { ref path, .. }) = *field.ty { + !is_phantom_data(path) + } else { + true + } + }) + .filter(|field| filter(&field.attrs)) + .map(|field| &field.ty); + + let mut visitor = FindTyParams { + all_ty_params, + relevant_ty_params: HashSet::new(), + }; + for ty in relevant_tys { + visit::visit_type(&mut visitor, ty); + } + + 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| -> syn::WherePredicate { parse_quote!( #id : #bound ) }); + + cloned + .make_where_clause() + .predicates + .extend(relevant_where_predicates); + } + cloned +} + +#[allow(clippy::match_like_matches_macro)] // needs rustc 1.42 +fn is_phantom_data(path: &syn::Path) -> bool { + match path.segments.last() { + Some(path) if path.ident == "PhantomData" => true, + _ => false, + } +} diff --git a/vendor/derivative/src/clone.rs b/vendor/derivative/src/clone.rs new file mode 100644 index 000000000..3594e9498 --- /dev/null +++ b/vendor/derivative/src/clone.rs @@ -0,0 +1,196 @@ +use proc_macro2; + +use ast; +use attr; +use matcher; +use syn; +use utils; + +/// Derive `Copy` for `input`. +pub fn derive_copy(input: &ast::Input) -> proc_macro2::TokenStream { + let name = &input.ident; + + let copy_trait_path = copy_trait_path(); + let generics = utils::build_impl_generics( + input, + ©_trait_path, + |attrs| attrs.copy_bound().is_none(), + |field| field.copy_bound(), + |input| input.copy_bound(), + ); + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + quote! { + #[allow(unused_qualifications)] + impl #impl_generics #copy_trait_path for #name #ty_generics #where_clause {} + } +} + +/// Derive `Clone` for `input`. +pub fn derive_clone(input: &ast::Input) -> proc_macro2::TokenStream { + let name = &input.ident; + + let clone_trait_path = clone_trait_path(); + let generics = utils::build_impl_generics( + input, + &clone_trait_path, + needs_clone_bound, + |field| field.clone_bound(), + |input| input.clone_bound(), + ); + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let is_copy = input.attrs.copy.is_some(); + if is_copy && input.generics.type_params().count() == 0 { + quote! { + #[allow(unused_qualifications)] + 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, input.attrs.is_packed).build_arms( + input, + "__arg", + |arm_path, _, _, style, _, bis| { + let field_clones = bis.iter().map(|bi| { + let arg = &bi.expr; + + let clone = if let Some(clone_with) = bi.field.attrs.clone_with() { + quote!(#clone_with(&#arg)) + } else { + quote!(#arg.clone()) + }; + + if let Some(ref name) = bi.field.ident { + quote! { + #name: #clone + } + } else { + clone + } + }); + + match style { + ast::Style::Struct => { + quote! { + #arm_path { + #(#field_clones),* + } + } + } + ast::Style::Tuple => { + quote! { + #arm_path (#(#field_clones),*) + } + } + ast::Style::Unit => { + quote! { + #arm_path + } + } + } + }, + ); + + let clone_from = if input.attrs.clone_from() { + Some( + matcher::Matcher::new(matcher::BindingStyle::RefMut, input.attrs.is_packed).build_arms( + input, + "__arg", + |outer_arm_path, _, _, _, _, outer_bis| { + let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed).build_arms( + input, + "__other", + |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.expr; + let inner = &inner_bi.expr; + + quote!(#outer.clone_from(&#inner);) + }, + ); + + quote! { + #(#field_clones)* + return; + } + } else { + quote!() + } + }, + ); + + quote! { + match *other { + #body + } + } + }, + ), + ) + } else { + None + }; + + let clone_from = clone_from.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 { + Some(quote!(*self = other.clone();)) + } else { + None + }; + + quote! { + #[allow(clippy::needless_return)] + fn clone_from(&mut self, other: &Self) { + match *self { + #body + } + + #fallback + } + } + }); + + quote! { + #[allow(unused_qualifications)] + impl #impl_generics #clone_trait_path for #name #ty_generics #where_clause { + fn clone(&self) -> Self { + match *self { + #body + } + } + + #clone_from + } + } + } +} + +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 { + if cfg!(feature = "use_core") { + parse_quote!(::core::clone::Clone) + } else { + parse_quote!(::std::clone::Clone) + } +} + +/// Return the path of the `Copy` trait, that is `::std::marker::Copy`. +fn copy_trait_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::marker::Copy) + } else { + parse_quote!(::std::marker::Copy) + } +} diff --git a/vendor/derivative/src/cmp.rs b/vendor/derivative/src/cmp.rs new file mode 100644 index 000000000..573c5fa79 --- /dev/null +++ b/vendor/derivative/src/cmp.rs @@ -0,0 +1,407 @@ +// https://github.com/rust-lang/rust/issues/13101 + +use ast; +use attr; +use matcher; +use paths; +use proc_macro2; +use syn; +use utils; + +/// Derive `Eq` for `input`. +pub fn derive_eq(input: &ast::Input) -> proc_macro2::TokenStream { + let name = &input.ident; + + let eq_trait_path = eq_trait_path(); + let generics = utils::build_impl_generics( + input, + &eq_trait_path, + needs_eq_bound, + |field| field.eq_bound(), + |input| input.eq_bound(), + ); + let new_where_clause; + let (impl_generics, ty_generics, mut where_clause) = generics.split_for_impl(); + + if let Some(new_where_clause2) = + maybe_add_copy(input, where_clause, |f| !f.attrs.ignore_partial_eq()) + { + new_where_clause = new_where_clause2; + where_clause = Some(&new_where_clause); + } + + quote! { + #[allow(unused_qualifications)] + impl #impl_generics #eq_trait_path for #name #ty_generics #where_clause {} + } +} + +/// Derive `PartialEq` for `input`. +pub fn derive_partial_eq(input: &ast::Input) -> proc_macro2::TokenStream { + let discriminant_cmp = if let ast::Body::Enum(_) = input.body { + let discriminant_path = paths::discriminant_path(); + + quote!((#discriminant_path(&*self) == #discriminant_path(&*other))) + } else { + quote!(true) + }; + + let name = &input.ident; + + let partial_eq_trait_path = partial_eq_trait_path(); + let generics = utils::build_impl_generics( + input, + &partial_eq_trait_path, + needs_partial_eq_bound, + |field| field.partial_eq_bound(), + |input| input.partial_eq_bound(), + ); + let new_where_clause; + let (impl_generics, ty_generics, mut where_clause) = generics.split_for_impl(); + + let match_fields = if input.is_trivial_enum() { + quote!(true) + } else { + matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed) + .with_field_filter(|f: &ast::Field| !f.attrs.ignore_partial_eq()) + .build_2_arms( + (quote!(*self), quote!(*other)), + (input, "__self"), + (input, "__other"), + |_, _, _, (left_variant, right_variant)| { + let cmp = left_variant.iter().zip(&right_variant).map(|(o, i)| { + let outer_name = &o.expr; + let inner_name = &i.expr; + + 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))) + } else { + Some(quote!(&& &#outer_name == &#inner_name)) + } + }); + + quote!(true #(#cmp)*) + }, + ) + }; + + if let Some(new_where_clause2) = + maybe_add_copy(input, where_clause, |f| !f.attrs.ignore_partial_eq()) + { + new_where_clause = new_where_clause2; + where_clause = Some(&new_where_clause); + } + + quote! { + #[allow(unused_qualifications)] + #[allow(clippy::unneeded_field_pattern)] + impl #impl_generics #partial_eq_trait_path for #name #ty_generics #where_clause { + fn eq(&self, other: &Self) -> bool { + #discriminant_cmp && #match_fields + } + } + } +} + +/// Derive `PartialOrd` for `input`. +pub fn derive_partial_ord( + input: &ast::Input, + errors: &mut proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + if let ast::Body::Enum(_) = input.body { + if !input.attrs.partial_ord_on_enum() { + let message = "can't use `#[derivative(PartialOrd)]` on an enumeration without \ + `feature_allow_slow_enum`; see the documentation for more details"; + errors.extend(syn::Error::new(input.span, message).to_compile_error()); + } + } + + let option_path = option_path(); + let ordering_path = ordering_path(); + + let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed) + .with_field_filter(|f: &ast::Field| !f.attrs.ignore_partial_ord()) + .build_arms(input, "__self", |_, n, _, _, _, outer_bis| { + let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed) + .with_field_filter(|f: &ast::Field| !f.attrs.ignore_partial_ord()) + .build_arms(input, "__other", |_, m, _, _, _, inner_bis| { + match n.cmp(&m) { + ::std::cmp::Ordering::Less => { + quote!(#option_path::Some(#ordering_path::Less)) + } + ::std::cmp::Ordering::Greater => { + quote!(#option_path::Some(#ordering_path::Greater)) + } + ::std::cmp::Ordering::Equal => { + let equal_path = quote!(#ordering_path::Equal); + outer_bis + .iter() + .rev() + .zip(inner_bis.into_iter().rev()) + .fold(quote!(#option_path::Some(#equal_path)), |acc, (o, i)| { + let outer_name = &o.expr; + let inner_name = &i.expr; + + if o.field.attrs.ignore_partial_ord() { + acc + } else { + let cmp_fn = o + .field + .attrs + .partial_ord_compare_with() + .map(|f| quote!(#f)) + .unwrap_or_else(|| { + let path = partial_ord_trait_path(); + quote!(#path::partial_cmp) + }); + + quote!(match #cmp_fn(&#outer_name, &#inner_name) { + #option_path::Some(#equal_path) => #acc, + __derive_ordering_other => __derive_ordering_other, + }) + } + }) + } + } + }); + + quote! { + match *other { + #body + } + + } + }); + + let name = &input.ident; + + let partial_ord_trait_path = partial_ord_trait_path(); + let generics = utils::build_impl_generics( + input, + &partial_ord_trait_path, + needs_partial_ord_bound, + |field| field.partial_ord_bound(), + |input| input.partial_ord_bound(), + ); + let new_where_clause; + let (impl_generics, ty_generics, mut where_clause) = generics.split_for_impl(); + + if let Some(new_where_clause2) = + maybe_add_copy(input, where_clause, |f| !f.attrs.ignore_partial_ord()) + { + new_where_clause = new_where_clause2; + where_clause = Some(&new_where_clause); + } + + quote! { + #[allow(unused_qualifications)] + #[allow(clippy::unneeded_field_pattern)] + impl #impl_generics #partial_ord_trait_path for #name #ty_generics #where_clause { + fn partial_cmp(&self, other: &Self) -> #option_path<#ordering_path> { + match *self { + #body + } + } + } + } +} + +/// Derive `Ord` for `input`. +pub fn derive_ord( + input: &ast::Input, + errors: &mut proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + if let ast::Body::Enum(_) = input.body { + if !input.attrs.ord_on_enum() { + let message = "can't use `#[derivative(Ord)]` on an enumeration without \ + `feature_allow_slow_enum`; see the documentation for more details"; + errors.extend(syn::Error::new(input.span, message).to_compile_error()); + } + } + + let ordering_path = ordering_path(); + + let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed) + .with_field_filter(|f: &ast::Field| !f.attrs.ignore_ord()) + .build_arms(input, "__self", |_, n, _, _, _, outer_bis| { + let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed) + .with_field_filter(|f: &ast::Field| !f.attrs.ignore_ord()) + .build_arms(input, "__other", |_, m, _, _, _, inner_bis| { + match n.cmp(&m) { + ::std::cmp::Ordering::Less => quote!(#ordering_path::Less), + ::std::cmp::Ordering::Greater => quote!(#ordering_path::Greater), + ::std::cmp::Ordering::Equal => { + let equal_path = quote!(#ordering_path::Equal); + outer_bis + .iter() + .rev() + .zip(inner_bis.into_iter().rev()) + .fold(quote!(#equal_path), |acc, (o, i)| { + let outer_name = &o.expr; + let inner_name = &i.expr; + + if o.field.attrs.ignore_ord() { + acc + } else { + let cmp_fn = o + .field + .attrs + .ord_compare_with() + .map(|f| quote!(#f)) + .unwrap_or_else(|| { + let path = ord_trait_path(); + quote!(#path::cmp) + }); + + quote!(match #cmp_fn(&#outer_name, &#inner_name) { + #equal_path => #acc, + __derive_ordering_other => __derive_ordering_other, + }) + } + }) + } + } + }); + + quote! { + match *other { + #body + } + + } + }); + + let name = &input.ident; + + let ord_trait_path = ord_trait_path(); + let generics = utils::build_impl_generics( + input, + &ord_trait_path, + needs_ord_bound, + |field| field.ord_bound(), + |input| input.ord_bound(), + ); + let new_where_clause; + let (impl_generics, ty_generics, mut where_clause) = generics.split_for_impl(); + + if let Some(new_where_clause2) = maybe_add_copy(input, where_clause, |f| !f.attrs.ignore_ord()) + { + new_where_clause = new_where_clause2; + where_clause = Some(&new_where_clause); + } + + quote! { + #[allow(unused_qualifications)] + #[allow(clippy::unneeded_field_pattern)] + impl #impl_generics #ord_trait_path for #name #ty_generics #where_clause { + fn cmp(&self, other: &Self) -> #ordering_path { + match *self { + #body + } + } + } + } +} + +fn needs_partial_eq_bound(attrs: &attr::Field) -> bool { + !attrs.ignore_partial_eq() && attrs.partial_eq_bound().is_none() +} + +fn needs_partial_ord_bound(attrs: &attr::Field) -> bool { + !attrs.ignore_partial_ord() && attrs.partial_ord_bound().is_none() +} + +fn needs_ord_bound(attrs: &attr::Field) -> bool { + !attrs.ignore_ord() && attrs.ord_bound().is_none() +} + +fn needs_eq_bound(attrs: &attr::Field) -> bool { + !attrs.ignore_partial_eq() && attrs.eq_bound().is_none() +} + +/// Return the path of the `Eq` trait, that is `::std::cmp::Eq`. +fn eq_trait_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::cmp::Eq) + } else { + parse_quote!(::std::cmp::Eq) + } +} + +/// Return the path of the `PartialEq` trait, that is `::std::cmp::PartialEq`. +fn partial_eq_trait_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::cmp::PartialEq) + } else { + parse_quote!(::std::cmp::PartialEq) + } +} + +/// Return the path of the `PartialOrd` trait, that is `::std::cmp::PartialOrd`. +fn partial_ord_trait_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::cmp::PartialOrd) + } else { + parse_quote!(::std::cmp::PartialOrd) + } +} + +/// Return the path of the `Ord` trait, that is `::std::cmp::Ord`. +fn ord_trait_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::cmp::Ord) + } else { + parse_quote!(::std::cmp::Ord) + } +} + +/// Return the path of the `Option` trait, that is `::std::option::Option`. +fn option_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::option::Option) + } else { + parse_quote!(::std::option::Option) + } +} + +/// Return the path of the `Ordering` trait, that is `::std::cmp::Ordering`. +fn ordering_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::cmp::Ordering) + } else { + parse_quote!(::std::cmp::Ordering) + } +} + +fn maybe_add_copy( + input: &ast::Input, + where_clause: Option<&syn::WhereClause>, + field_filter: impl Fn(&ast::Field) -> bool, +) -> Option<syn::WhereClause> { + if input.attrs.is_packed && !input.body.is_empty() { + let mut new_where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause { + where_token: parse_quote!(where), + predicates: Default::default(), + }); + + new_where_clause.predicates.extend( + input + .body + .all_fields() + .into_iter() + .filter(|f| field_filter(f)) + .map(|f| { + let ty = f.ty; + + let pred: syn::WherePredicate = parse_quote!(#ty: Copy); + pred + }), + ); + + Some(new_where_clause) + } else { + None + } +} diff --git a/vendor/derivative/src/debug.rs b/vendor/derivative/src/debug.rs new file mode 100644 index 000000000..ed667c1d7 --- /dev/null +++ b/vendor/derivative/src/debug.rs @@ -0,0 +1,225 @@ +use proc_macro2; + +use ast; +use attr; +use matcher; +use syn; +use syn::spanned::Spanned; +use utils; + +pub fn derive(input: &ast::Input) -> proc_macro2::TokenStream { + let debug_trait_path = debug_trait_path(); + let fmt_path = fmt_path(); + + let formatter = quote_spanned! {input.span=> __f}; + + let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed) + .with_field_filter(|f: &ast::Field| !f.attrs.ignore_debug()) + .build_arms(input, "__arg", |_, _, arm_name, style, attrs, bis| { + let field_prints = bis.iter().filter_map(|bi| { + if bi.field.attrs.ignore_debug() { + return None; + } + + if attrs.debug_transparent() { + return Some(quote_spanned! {arm_name.span()=> + #debug_trait_path::fmt(__arg_0, #formatter) + }); + } + + let arg_expr = &bi.expr; + let arg_ident = &bi.ident; + + let dummy_debug = bi.field.attrs.debug_format_with().map(|format_fn| { + format_with( + bi.field, + &input.attrs.debug_bound(), + &arg_expr, + &arg_ident, + format_fn, + input.generics.clone(), + ) + }); + let expr = if bi.field.attrs.debug_format_with().is_some() { + quote_spanned! {arm_name.span()=> + &#arg_ident + } + } else { + quote_spanned! {arm_name.span()=> + &&#arg_expr + } + }; + + let builder = if let Some(ref name) = bi.field.ident { + let name = name.to_string(); + quote_spanned! {arm_name.span()=> + #dummy_debug + let _ = __debug_trait_builder.field(#name, #expr); + } + } else { + quote_spanned! {arm_name.span()=> + #dummy_debug + let _ = __debug_trait_builder.field(#expr); + } + }; + + Some(builder) + }); + + let method = match style { + ast::Style::Struct => "debug_struct", + ast::Style::Tuple | ast::Style::Unit => "debug_tuple", + }; + let method = syn::Ident::new(method, proc_macro2::Span::call_site()); + + if attrs.debug_transparent() { + quote_spanned! {arm_name.span()=> + #(#field_prints)* + } + } else { + let name = arm_name.to_string(); + quote_spanned! {arm_name.span()=> + let mut __debug_trait_builder = #formatter.#method(#name); + #(#field_prints)* + __debug_trait_builder.finish() + } + } + }); + + let name = &input.ident; + + let generics = utils::build_impl_generics( + input, + &debug_trait_path, + needs_debug_bound, + |field| field.debug_bound(), + |input| input.debug_bound(), + ); + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + // don't attach a span to prevent issue #58 + let match_self = quote!(match *self); + quote_spanned! {input.span=> + #[allow(unused_qualifications)] + #[allow(clippy::unneeded_field_pattern)] + impl #impl_generics #debug_trait_path for #name #ty_generics #where_clause { + fn fmt(&self, #formatter: &mut #fmt_path::Formatter) -> #fmt_path::Result { + #match_self { + #body + } + } + } + } +} + +fn needs_debug_bound(attrs: &attr::Field) -> bool { + !attrs.ignore_debug() && attrs.debug_bound().is_none() +} + +/// Return the path of the `Debug` trait, that is `::std::fmt::Debug`. +fn debug_trait_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::fmt::Debug) + } else { + parse_quote!(::std::fmt::Debug) + } +} + +/// Return the path of the `fmt` module, that is `::std::fmt`. +fn fmt_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::fmt) + } else { + parse_quote!(::std::fmt) + } +} + +/// Return the path of the `PhantomData` type, that is `::std::marker::PhantomData`. +fn phantom_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::marker::PhantomData) + } else { + parse_quote!(::std::marker::PhantomData) + } +} + +fn format_with( + f: &ast::Field, + bounds: &Option<&[syn::WherePredicate]>, + arg_expr: &proc_macro2::TokenStream, + arg_ident: &syn::Ident, + format_fn: &syn::Path, + mut generics: syn::Generics, +) -> proc_macro2::TokenStream { + let debug_trait_path = debug_trait_path(); + let fmt_path = fmt_path(); + let phantom_path = phantom_path(); + + 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 = syn::Path::from(syn::PathSegment::from(ty.ident.clone())); + + syn::WherePredicate::Type(syn::PredicateType { + lifetimes: None, + bounded_ty: syn::Type::Path(syn::TypePath { qself: None, path }), + colon_token: Default::default(), + bounds, + }) + }) + .chain(bounds.iter().flat_map(|b| b.iter().cloned())) + .collect::<Vec<_>>(); + 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.type_params().map(|tp| &tp.ident); + + let mut ctor_generics = generics.clone(); + *ctor_generics + .lifetimes_mut() + .last() + .expect("There must be a '_derivative lifetime") = syn::LifetimeDef::new(parse_quote!('_)); + let (_, ctor_ty_generics, _) = ctor_generics.split_for_impl(); + let ctor_ty_generics = ctor_ty_generics.as_turbofish(); + + // don't attach a span to prevent issue #58 + let match_self = quote!(match self.0); + quote_spanned!(format_fn.span()=> + let #arg_ident = { + struct Dummy #impl_generics (&'_derivative #ty, #phantom_path <(#(#phantom,)*)>) #where_clause; + + impl #impl_generics #debug_trait_path for Dummy #ty_generics #where_clause { + fn fmt(&self, __f: &mut #fmt_path::Formatter) -> #fmt_path::Result { + #match_self { + this => #format_fn(this, __f) + } + } + } + + Dummy #ctor_ty_generics (&&#arg_expr, #phantom_path) + }; + ) +} diff --git a/vendor/derivative/src/default.rs b/vendor/derivative/src/default.rs new file mode 100644 index 000000000..f36947533 --- /dev/null +++ b/vendor/derivative/src/default.rs @@ -0,0 +1,120 @@ +use proc_macro2; + +use ast; +use attr; +use syn; +use utils; + +/// Derive `Default` for `input`. +pub fn derive(input: &ast::Input, default: &attr::InputDefault) -> proc_macro2::TokenStream { + fn make_variant_data( + variant_name: &proc_macro2::TokenStream, + style: ast::Style, + fields: &[ast::Field], + ) -> proc_macro2::TokenStream { + let default_trait_path = default_trait_path(); + + 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!(#default_trait_path::default()), |v| quote!(#v)); + + defaults.push(quote!(#name: #default)); + } + + 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!(#default_trait_path::default()), |v| quote!(#v)); + + defaults.push(default); + } + + quote!(#variant_name ( #(#defaults),* )) + } + ast::Style::Unit => quote!(#variant_name), + } + } + + let name = &input.ident; + let default_trait_path = default_trait_path(); + let generics = utils::build_impl_generics( + input, + &default_trait_path, + |attrs| attrs.default_bound().is_none(), + |field| field.default_bound(), + |input| input.default_bound(), + ); + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + 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( + "e!(#name::#vname), + variant.style, + &variant.fields, + )) + } else { + None + } + }); + + quote!(#(#arms),*) + } + ast::Body::Struct(style, ref vd) => make_variant_data("e!(#name), style, vd), + }; + + let new_fn = if default.new { + Some(quote!( + #[allow(unused_qualifications)] + impl #impl_generics #name #ty_generics #where_clause { + /// Creates a default value for this type. + #[inline] + pub fn new() -> Self { + <Self as #default_trait_path>::default() + } + } + )) + } else { + None + }; + + quote!( + #new_fn + + #[allow(unused_qualifications)] + impl #impl_generics #default_trait_path for #name #ty_generics #where_clause { + fn default() -> Self { + #body + } + } + ) +} + +/// Return the path of the `Default` trait, that is `::std::default::Default`. +fn default_trait_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::default::Default) + } else { + parse_quote!(::std::default::Default) + } +} diff --git a/vendor/derivative/src/hash.rs b/vendor/derivative/src/hash.rs new file mode 100644 index 000000000..8f13f80f2 --- /dev/null +++ b/vendor/derivative/src/hash.rs @@ -0,0 +1,97 @@ +use proc_macro2; + +use ast; +use attr; +use matcher; +use paths; +use syn; +use utils; + +pub fn derive(input: &ast::Input) -> proc_macro2::TokenStream { + let hasher_trait_path = hasher_trait_path(); + let hash_trait_path = hash_trait_path(); + + let discriminant = if let ast::Body::Enum(_) = input.body { + let discriminant = paths::discriminant_path(); + Some(quote!( + #hash_trait_path::hash(&#discriminant(self), __state); + )) + } else { + None + }; + + let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed).build_arms( + input, + "__arg", + |_, _, _, _, _, bis| { + let field_prints = bis.iter().filter_map(|bi| { + if bi.field.attrs.ignore_hash() { + return None; + } + + let arg = &bi.expr; + + if let Some(hash_with) = bi.field.attrs.hash_with() { + Some(quote! { + #hash_with(&#arg, __state); + }) + } else { + Some(quote! { + #hash_trait_path::hash(&#arg, __state); + }) + } + }); + + quote! { + #(#field_prints)* + } + }, + ); + + let name = &input.ident; + let generics = utils::build_impl_generics( + input, + &hash_trait_path, + needs_hash_bound, + |field| field.hash_bound(), + |input| input.hash_bound(), + ); + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let hasher_ty_parameter = utils::hygienic_type_parameter(input, "__H"); + quote! { + #[allow(unused_qualifications)] + 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 + { + #discriminant + match *self { + #body + } + } + } + } +} + +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 { + if cfg!(feature = "use_core") { + parse_quote!(::core::hash::Hash) + } else { + parse_quote!(::std::hash::Hash) + } +} + +/// Return the path of the `Hasher` trait, that is `::std::hash::Hasher`. +fn hasher_trait_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::hash::Hasher) + } else { + parse_quote!(::std::hash::Hasher) + } +} diff --git a/vendor/derivative/src/lib.rs b/vendor/derivative/src/lib.rs new file mode 100644 index 000000000..d891948f9 --- /dev/null +++ b/vendor/derivative/src/lib.rs @@ -0,0 +1,94 @@ +// We need to support Rust 1.34 to stable +#![allow(deprecated)] + +#![allow(renamed_and_removed_lints)] // support for multiple Clippy versions +#![allow(clippy::unknown_clippy_lints)] // because of other #![allow]s +#![allow(clippy::mem_replace_with_default)] // needs rustc 1.40 +#![allow(clippy::option_as_ref_deref)] // needs rustc 1.40 +#![allow(clippy::cyclomatic_complexity)] // old name of cognitive_complexity +#![allow(clippy::cognitive_complexity)] // in code generated by macros +#![allow(clippy::redundant_closure)] + +extern crate proc_macro; +extern crate proc_macro2; +#[macro_use] +extern crate syn; + +#[macro_use] +extern crate quote; + +mod ast; +mod attr; +mod bound; +mod clone; +mod cmp; +mod debug; +mod default; +mod hash; +mod matcher; +mod paths; +mod utils; + +use proc_macro::TokenStream; + +fn derive_impls( + input: &mut ast::Input, + errors: &mut proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + let mut tokens = proc_macro2::TokenStream::new(); + + if input.attrs.clone.is_some() { + tokens.extend(clone::derive_clone(input)); + } + if input.attrs.copy.is_some() { + tokens.extend(clone::derive_copy(input)); + } + if input.attrs.debug.is_some() { + tokens.extend(debug::derive(input)); + } + if let Some(ref default) = input.attrs.default { + tokens.extend(default::derive(input, default)); + } + if input.attrs.eq.is_some() { + tokens.extend(cmp::derive_eq(input)); + } + if input.attrs.hash.is_some() { + tokens.extend(hash::derive(input)); + } + if input.attrs.partial_eq.is_some() { + tokens.extend(cmp::derive_partial_eq(input)); + } + if input.attrs.partial_ord.is_some() { + tokens.extend(cmp::derive_partial_ord(input, errors)); + } + if input.attrs.ord.is_some() { + tokens.extend(cmp::derive_ord(input, errors)); + } + + tokens.extend(std::mem::replace( + errors, + Default::default(), + )); + + tokens +} + +#[cfg_attr(not(test), proc_macro_derive(Derivative, attributes(derivative)))] +pub fn derivative(input: TokenStream) -> TokenStream { + let mut errors = proc_macro2::TokenStream::new(); + + let mut output = match syn::parse::<syn::DeriveInput>(input) { + Ok(parsed) => { + ast::Input::from_ast(&parsed, &mut errors) + .map(|mut input| derive_impls(&mut input, &mut errors)) + .unwrap_or_default() + }, + Err(error) => { + errors.extend(error.to_compile_error()); + Default::default() + } + }; + + output.extend(errors); + output.into() +}
\ No newline at end of file diff --git a/vendor/derivative/src/matcher.rs b/vendor/derivative/src/matcher.rs new file mode 100644 index 000000000..4b78fa4da --- /dev/null +++ b/vendor/derivative/src/matcher.rs @@ -0,0 +1,336 @@ +#![allow(dead_code)] // TODO: remove + +// This is inspired from `synstructure`, but `synstructure` is not adapted in severals ways +// including: +// * `&mut` everywhere +// * not generic, we use our own `ast`, `synstructure` only knows about `syn` +// * missing information (what arm are we in?, what attributes? etc.) + +use proc_macro2::{self, TokenStream}; +use quote::ToTokens; +use syn; + +use ast; +use attr; +use quote; + +/// The type of binding to use when generating a pattern. +#[derive(Debug, Copy, Clone)] +pub enum BindingStyle { + /// `x` + Move, + /// `mut x` + MoveMut, + /// `ref x` + Ref, + /// `ref mut x` + RefMut, +} + +impl BindingStyle { + fn with_packed(self, is_packed: bool) -> BindingStyle { + match self { + BindingStyle::Move | BindingStyle::MoveMut => self, + BindingStyle::Ref if is_packed => BindingStyle::Move, + BindingStyle::RefMut if is_packed => BindingStyle::MoveMut, + BindingStyle::Ref | BindingStyle::RefMut => self, + } + } +} + +impl quote::ToTokens for BindingStyle { + fn to_tokens(&self, tokens: &mut TokenStream) { + match *self { + BindingStyle::Move => (), + BindingStyle::MoveMut => tokens.extend(quote!(mut)), + BindingStyle::Ref => tokens.extend(quote!(ref)), + BindingStyle::RefMut => { + tokens.extend(quote!(ref mut)); + } + } + } +} + +#[derive(Debug)] +pub struct BindingInfo<'a> { + pub expr: TokenStream, + pub ident: syn::Ident, + pub field: &'a ast::Field<'a>, +} + +#[derive(Debug)] +pub struct CommonVariant<'a> { + path: syn::Path, + name: &'a syn::Ident, + style: ast::Style, + attrs: &'a attr::Input, +} + +pub struct Matcher<T> { + binding_name: String, + binding_style: BindingStyle, + is_packed: bool, + field_filter: T, +} + +impl Matcher<fn (&ast::Field) -> bool> { + pub fn new(style: BindingStyle, is_packed: bool) -> Self { + Matcher { + binding_name: "__arg".into(), + binding_style: style.with_packed(is_packed), + is_packed, + field_filter: |_| true, + } + } +} + +impl<T: Fn (&ast::Field) -> bool> Matcher<T> { + pub fn with_name(self, name: String) -> Self { + Matcher { + binding_name: name, + ..self + } + } + + pub fn with_field_filter<P>(self, field_filter: P) -> Matcher<P> { + Matcher { + field_filter, + binding_name: self.binding_name, + binding_style: self.binding_style, + is_packed: self.is_packed, + } + } + + pub fn build_arms<F>(self, input: &ast::Input, binding_name: &str, f: F) -> TokenStream + where + F: Fn( + syn::Path, + usize, + &syn::Ident, + ast::Style, + &attr::Input, + Vec<BindingInfo>, + ) -> TokenStream, + { + let variants = self.build_match_pattern(input, binding_name); + + // Now that we have the patterns, generate the actual branches of the match + // expression + let mut t = TokenStream::new(); + for (i, (variant, (pat, bindings))) in variants.into_iter().enumerate() { + let body = f( + variant.path, + i, + variant.name, + variant.style, + variant.attrs, + bindings, + ); + quote!(#pat => { #body }).to_tokens(&mut t); + } + + t + } + + pub fn build_2_arms<F>( + self, + (left_matched_expr, right_matched_expr): (TokenStream, TokenStream), + left: (&ast::Input, &str), + right: (&ast::Input, &str), + f: F, + ) -> TokenStream + where + F: Fn( + usize, + CommonVariant, + CommonVariant, + (Vec<BindingInfo>, Vec<BindingInfo>), + ) -> TokenStream, + { + let left_variants = self.build_match_pattern(left.0, left.1); + let right_variants = self.build_match_pattern(right.0, right.1); + + assert_eq!(left_variants.len(), right_variants.len()); + + if left_variants.len() == 1 { + let (left, (left_pat, left_bindings)) = left_variants.into_iter().next().unwrap(); + let (right, (right_pat, right_bindings)) = right_variants.into_iter().next().unwrap(); + + let body = f(0, left, right, (left_bindings, right_bindings)); + + quote! { + match #left_matched_expr { + #left_pat => match #right_matched_expr { + #right_pat => #body, + }, + } + } + } else { + // Now that we have the patterns, generate the actual branches of the match + // expression + let mut t = TokenStream::new(); + for (i, (left, right)) in left_variants.into_iter().zip(right_variants).enumerate() { + let (left, (left_pat, left_bindings)) = left; + let (right, (right_pat, right_bindings)) = right; + + let body = f(i, left, right, (left_bindings, right_bindings)); + quote!((#left_pat, #right_pat) => { #body }).to_tokens(&mut t); + } + + quote! { + match (&#left_matched_expr, &#right_matched_expr) { + #t + _ => unreachable!(), + } + } + } + } + + /// Generate patterns for matching against all of the variants + pub fn build_match_pattern<'a>( + &self, + input: &'a ast::Input, + binding_name: &str, + ) -> Vec<(CommonVariant<'a>, (TokenStream, Vec<BindingInfo<'a>>))> { + let ident = &input.ident; + + match input.body { + ast::Body::Enum(ref variants) => variants + .iter() + .map(|variant| { + let variant_ident = &variant.ident; + let path = parse_quote!(#ident::#variant_ident); + + let pat = self.build_match_pattern_impl( + &path, + variant.style, + &variant.fields, + binding_name, + ); + + ( + CommonVariant { + path, + name: variant_ident, + style: variant.style, + attrs: &variant.attrs, + }, + pat, + ) + }) + .collect(), + ast::Body::Struct(style, ref vd) => { + let path = parse_quote!(#ident); + vec![( + CommonVariant { + path, + name: ident, + style, + attrs: &input.attrs, + }, + self.build_match_pattern_impl(ident, style, vd, binding_name), + )] + } + } + } + + fn build_match_pattern_impl<'a, N>( + &self, + name: &N, + style: ast::Style, + fields: &'a [ast::Field<'a>], + binding_name: &str, + ) -> (TokenStream, Vec<BindingInfo<'a>>) + where + N: quote::ToTokens, + { + let (stream, matches) = match style { + ast::Style::Unit => (TokenStream::new(), Vec::new()), + ast::Style::Tuple => { + let (stream, matches) = fields.iter().enumerate().fold( + (TokenStream::new(), Vec::new()), + |(stream, matches), field| { + self.build_inner_pattern( + (stream, matches), + field, + binding_name, + |f, ident, binding| { + if (self.field_filter)(f) { + quote!(#binding #ident ,) + } else { + quote!(_ ,) + } + }, + ) + }, + ); + + (quote! { ( #stream ) }, matches) + } + ast::Style::Struct => { + let (stream, matches) = fields.iter().enumerate().fold( + (TokenStream::new(), Vec::new()), + |(stream, matches), field| { + self.build_inner_pattern( + (stream, matches), + field, + binding_name, + |field, ident, binding| { + let field_name = field.ident.as_ref().unwrap(); + if (self.field_filter)(field) { + quote!(#field_name : #binding #ident ,) + } else { + quote!(#field_name : _ ,) + } + }, + ) + }, + ); + + (quote! { { #stream } }, matches) + } + }; + + let mut all_tokens = TokenStream::new(); + name.to_tokens(&mut all_tokens); + all_tokens.extend(stream); + + (all_tokens, matches) + } + + fn build_inner_pattern<'a>( + &self, + (mut stream, mut matches): (TokenStream, Vec<BindingInfo<'a>>), + (i, field): (usize, &'a ast::Field), + binding_name: &str, + f: impl FnOnce(&ast::Field, &syn::Ident, BindingStyle) -> TokenStream, + ) -> (TokenStream, Vec<BindingInfo<'a>>) { + let binding_style = self.binding_style; + + let ident: syn::Ident = syn::Ident::new( + &format!("{}_{}", binding_name, i), + proc_macro2::Span::call_site(), + ); + let expr = syn::Expr::Path(syn::ExprPath { + attrs: vec![], + qself: None, + path: syn::Path::from(ident.clone()) + }); + + let expr = if self.is_packed { + expr.into_token_stream() + } else { + quote!((*#expr)) + }; + + f(field, &ident, binding_style).to_tokens(&mut stream); + + matches.push(BindingInfo { + expr, + ident, + field, + }); + + (stream, matches) + } +} diff --git a/vendor/derivative/src/paths.rs b/vendor/derivative/src/paths.rs new file mode 100644 index 000000000..7a6ec7de3 --- /dev/null +++ b/vendor/derivative/src/paths.rs @@ -0,0 +1,10 @@ +//! Contains some standard paths. + +/// Return the path of the `discriminant` function, that is `::std::mem::discriminant`. +pub fn discriminant_path() -> syn::Path { + if cfg!(feature = "use_core") { + parse_quote!(::core::mem::discriminant) + } else { + parse_quote!(::std::mem::discriminant) + } +}
\ No newline at end of file diff --git a/vendor/derivative/src/utils.rs b/vendor/derivative/src/utils.rs new file mode 100644 index 000000000..9db21f5be --- /dev/null +++ b/vendor/derivative/src/utils.rs @@ -0,0 +1,43 @@ +use proc_macro2; + +use ast; +use attr; +use bound; +use syn; + +/// Make generic with all the generics in the input, plus a bound `T: <trait_path>` for each +/// generic field type that will be shown. +pub fn build_impl_generics<F, G, H>( + item: &ast::Input, + trait_path: &syn::Path, + needs_debug_bound: F, + 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]>, +{ + let generics = bound::without_defaults(item.generics); + 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), + } +} + +/// 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::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 + }); + + syn::Ident::new(&typaram, proc_macro2::Span::call_site()) +} |