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