Bug 1639689 - Support field_bound in #[derive(Parse)]. r=boris
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 21 May 2020 13:48:36 +0000
changeset 531452 b2cc9667df966192f3643c5b065de16649c8e7d4
parent 531451 3968a313e1d633cdd7b9dc6fef2b01ace624f37e
child 531453 3c2c3ec68a8feaab9870a70b34b0adb502c6a2b1
push id37439
push userbtara@mozilla.com
push dateThu, 21 May 2020 21:49:34 +0000
treeherdermozilla-central@92c11f0bf14b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersboris
bugs1639689
milestone78.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1639689 - Support field_bound in #[derive(Parse)]. r=boris Differential Revision: https://phabricator.services.mozilla.com/D76268
servo/components/style_derive/parse.rs
--- a/servo/components/style_derive/parse.rs
+++ b/servo/components/style_derive/parse.rs
@@ -10,34 +10,48 @@ use synstructure::{Structure, VariantInf
 
 #[darling(attributes(parse), default)]
 #[derive(Default, FromVariant)]
 pub struct ParseVariantAttrs {
     pub aliases: Option<String>,
     pub condition: Option<Path>,
 }
 
+#[darling(attributes(parse), default)]
+#[derive(Default, FromField)]
+pub struct ParseFieldAttrs {
+    field_bound: bool,
+}
+
 fn parse_non_keyword_variant(
+    where_clause: &mut Option<syn::WhereClause>,
     name: &syn::Ident,
     variant: &VariantInfo,
     variant_attrs: &CssVariantAttrs,
     parse_attrs: &ParseVariantAttrs,
     skip_try: bool,
 ) -> TokenStream {
     let bindings = variant.bindings();
     assert!(parse_attrs.aliases.is_none());
     assert!(variant_attrs.function.is_none());
     assert!(variant_attrs.keyword.is_none());
     assert_eq!(
         bindings.len(),
         1,
         "We only support deriving parse for simple variants"
     );
     let variant_name = &variant.ast().ident;
-    let ty = &bindings[0].ast().ty;
+    let binding_ast = &bindings[0].ast();
+    let ty = &binding_ast.ty;
+
+    let field_attrs = cg::parse_field_attrs::<ParseFieldAttrs>(binding_ast);
+    if field_attrs.field_bound {
+        cg::add_predicate(where_clause, parse_quote!(#ty: crate::parser::Parse));
+    }
+
     let mut parse = if skip_try {
         quote! {
             let v = <#ty as crate::parser::Parse>::parse(context, input)?;
             return Ok(#name::#variant_name(v));
         }
     } else {
         quote! {
             if let Ok(v) = input.try(|i| <#ty as crate::parser::Parse>::parse(context, i)) {
@@ -62,43 +76,41 @@ fn parse_non_keyword_variant(
             };
         }
     }
 
     parse
 }
 
 pub fn derive(mut input: DeriveInput) -> TokenStream {
-    {
-        let mut where_clause = input.generics.where_clause.take();
-        for param in input.generics.type_params() {
-            cg::add_predicate(
-                &mut where_clause,
-                parse_quote!(#param: crate::parser::Parse),
-            );
-        }
-        input.generics.where_clause = where_clause;
+    let mut where_clause = input.generics.where_clause.take();
+    for param in input.generics.type_params() {
+        cg::add_predicate(
+            &mut where_clause,
+            parse_quote!(#param: crate::parser::Parse),
+        );
     }
 
     let name = &input.ident;
     let s = Structure::new(&input);
 
     let mut saw_condition = false;
     let mut match_keywords = quote! {};
     let mut non_keywords = vec![];
 
     let mut effective_variants = 0;
     for variant in s.variants().iter() {
         let css_variant_attrs = cg::parse_variant_attrs_from_ast::<CssVariantAttrs>(&variant.ast());
-        let parse_attrs = cg::parse_variant_attrs_from_ast::<ParseVariantAttrs>(&variant.ast());
         if css_variant_attrs.skip {
             continue;
         }
         effective_variants += 1;
 
+        let parse_attrs = cg::parse_variant_attrs_from_ast::<ParseVariantAttrs>(&variant.ast());
+
         saw_condition |= parse_attrs.condition.is_some();
 
         if !variant.bindings().is_empty() {
             non_keywords.push((variant, css_variant_attrs, parse_attrs));
             continue;
         }
 
         let identifier = cg::to_css_identifier(
@@ -138,17 +150,17 @@ pub fn derive(mut input: DeriveInput) ->
     };
 
     let has_keywords = non_keywords.len() != effective_variants;
 
     let mut parse_non_keywords = quote! {};
     for (i, (variant, css_attrs, parse_attrs)) in non_keywords.iter().enumerate() {
         let skip_try = !has_keywords && i == non_keywords.len() - 1;
         let parse_variant =
-            parse_non_keyword_variant(name, variant, css_attrs, parse_attrs, skip_try);
+            parse_non_keyword_variant(&mut where_clause, name, variant, css_attrs, parse_attrs, skip_try);
         parse_non_keywords.extend(parse_variant);
     }
 
     let parse_body = if needs_context {
         let parse_keywords = if has_keywords {
             quote! {
                 let location = input.current_source_location();
                 let ident = input.expect_ident()?;
@@ -166,16 +178,19 @@ pub fn derive(mut input: DeriveInput) ->
         quote! {
             #parse_non_keywords
             #parse_keywords
         }
     } else {
         quote! { Self::parse(input) }
     };
 
+    let has_non_keywords = !non_keywords.is_empty();
+
+    input.generics.where_clause = where_clause;
     let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
 
     let parse_trait_impl = quote! {
         impl #impl_generics crate::parser::Parse for #name #ty_generics #where_clause {
             #[inline]
             fn parse<'i, 't>(
                 #context_ident: &crate::parser::ParserContext,
                 input: &mut cssparser::Parser<'i, 't>,
@@ -184,17 +199,17 @@ pub fn derive(mut input: DeriveInput) ->
             }
         }
     };
 
     if needs_context {
         return parse_trait_impl;
     }
 
-    assert!(non_keywords.is_empty());
+    assert!(!has_non_keywords);
 
     // TODO(emilio): It'd be nice to get rid of these, but that makes the
     // conversion harder...
     let methods_impl = quote! {
         impl #name {
             /// Parse this keyword.
             #[inline]
             pub fn parse<'i, 't>(