diff options
Diffstat (limited to 'vendor/derivative/src/attr.rs')
-rw-r--r-- | vendor/derivative/src/attr.rs | 887 |
1 files changed, 887 insertions, 0 deletions
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 +} |