From d65a7e6f95230d36327a64534095fa22ac896da6 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 16 Jun 2017 13:55:26 +0200 Subject: [PATCH 1/2] Avoid an unwrap in #[derive(ToCss)] --- components/style_derive/to_css.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/components/style_derive/to_css.rs b/components/style_derive/to_css.rs index 8531b39ab46..6f628ef485d 100644 --- a/components/style_derive/to_css.rs +++ b/components/style_derive/to_css.rs @@ -17,12 +17,7 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { let style = synstructure::BindStyle::Ref.into(); let match_body = synstructure::each_variant(&input, &style, |bindings, variant| { let mut identifier = to_css_identifier(variant.ident.as_ref()); - let mut expr = if bindings.is_empty() { - quote! { - ::std::fmt::Write::write_str(dest, #identifier) - } - } else { - let (first, rest) = bindings.split_first().expect("unit variants are not yet supported"); + let mut expr = if let Some((first, rest)) = bindings.split_first() { where_clause.predicates.push(where_predicate(first.field.ty.clone())); let mut expr = quote! { ::style_traits::ToCss::to_css(#first, dest) @@ -36,6 +31,10 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { }; } expr + } else { + quote! { + ::std::fmt::Write::write_str(dest, #identifier) + } }; let mut css_attrs = variant.attrs.iter().filter(|attr| attr.name() == "css"); let is_function = css_attrs.next().map_or(false, |attr| { From b7c83eeb648502149ad67d64ab89ba23c4efc4ac Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 16 Jun 2017 14:52:36 +0200 Subject: [PATCH 2/2] Limit trait bounds for derive(ToCss) to types with free variables --- components/style_derive/Cargo.toml | 2 +- components/style_derive/to_css.rs | 33 ++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/components/style_derive/Cargo.toml b/components/style_derive/Cargo.toml index d56f3225689..80356dd6457 100644 --- a/components/style_derive/Cargo.toml +++ b/components/style_derive/Cargo.toml @@ -11,5 +11,5 @@ proc-macro = true [dependencies] quote = "0.3" -syn = "0.11" +syn = { version = "0.11", features = ["visit"] } synstructure = "0.5.2" diff --git a/components/style_derive/to_css.rs b/components/style_derive/to_css.rs index 6f628ef485d..5f2fa3aa8d1 100644 --- a/components/style_derive/to_css.rs +++ b/components/style_derive/to_css.rs @@ -18,12 +18,16 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { let match_body = synstructure::each_variant(&input, &style, |bindings, variant| { let mut identifier = to_css_identifier(variant.ident.as_ref()); let mut expr = if let Some((first, rest)) = bindings.split_first() { - where_clause.predicates.push(where_predicate(first.field.ty.clone())); + if has_free_params(&first.field.ty, &input.generics.ty_params) { + where_clause.predicates.push(where_predicate(first.field.ty.clone())); + } let mut expr = quote! { ::style_traits::ToCss::to_css(#first, dest) }; for binding in rest { - where_clause.predicates.push(where_predicate(binding.field.ty.clone())); + if has_free_params(&binding.field.ty, &input.generics.ty_params) { + where_clause.predicates.push(where_predicate(binding.field.ty.clone())); + } expr = quote! { #expr?; ::std::fmt::Write::write_str(dest, " ")?; @@ -90,6 +94,31 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { } } +/// Returns whether `ty` is parameterized by any parameter from `params`. +fn has_free_params(ty: &syn::Ty, params: &[syn::TyParam]) -> bool { + use syn::visit::Visitor; + + struct HasFreeParams<'a> { + params: &'a [syn::TyParam], + has_free: bool, + } + + impl<'a> Visitor for HasFreeParams<'a> { + fn visit_path(&mut self, path: &syn::Path) { + if !path.global && path.segments.len() == 1 { + if self.params.iter().any(|param| param.ident == path.segments[0].ident) { + self.has_free = true; + } + } + syn::visit::walk_path(self, path); + } + } + + let mut visitor = HasFreeParams { params: params, has_free: false }; + visitor.visit_ty(ty); + visitor.has_free +} + /// `#ty: ::style_traits::ToCss` fn where_predicate(ty: syn::Ty) -> syn::WherePredicate { syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate {