about summary refs log tree commit diff
path: root/src/libsyntax_ext
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsyntax_ext')
-rw-r--r--src/libsyntax_ext/deriving/mod.rs216
1 files changed, 122 insertions, 94 deletions
diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs
index e3a38d568d3..111596cfe88 100644
--- a/src/libsyntax_ext/deriving/mod.rs
+++ b/src/libsyntax_ext/deriving/mod.rs
@@ -108,11 +108,109 @@ pub fn expand_derive(cx: &mut ExtCtxt,
         cx.span_err(mitem.span, "unexpected value in `derive`");
     }
 
-    let traits = mitem.meta_item_list().unwrap_or(&[]);
+    let mut traits = mitem.meta_item_list().unwrap_or(&[]).to_owned();
     if traits.is_empty() {
         cx.span_warn(mitem.span, "empty trait list in `derive`");
     }
 
+    // First, weed out malformed #[derive]
+    traits.retain(|titem| {
+        if titem.word().is_none() {
+            cx.span_err(titem.span, "malformed `derive` entry");
+            false
+        } else {
+            true
+        }
+    });
+
+    // Next, check for old-style #[derive(Foo)]
+    //
+    // These all get expanded to `#[derive_Foo]` and will get expanded first. If
+    // we actually add any attributes here then we return to get those expanded
+    // and then eventually we'll come back to finish off the other derive modes.
+    let mut new_attributes = Vec::new();
+    traits.retain(|titem| {
+        let tword = titem.word().unwrap();
+        let tname = tword.name();
+
+        let derive_mode = ast::Ident::with_empty_ctxt(intern(&tname));
+        let derive_mode = cx.resolver.resolve_derive_mode(derive_mode);
+        if is_builtin_trait(&tname) || derive_mode.is_some() {
+            return true
+        }
+
+        if !cx.ecfg.enable_custom_derive() {
+            feature_gate::emit_feature_err(&cx.parse_sess,
+                                           "custom_derive",
+                                           titem.span,
+                                           feature_gate::GateIssue::Language,
+                                           feature_gate::EXPLAIN_CUSTOM_DERIVE);
+        } else {
+            let name = intern_and_get_ident(&format!("derive_{}", tname));
+            let mitem = cx.meta_word(titem.span, name);
+            new_attributes.push(cx.attribute(mitem.span, mitem));
+        }
+        false
+    });
+    if new_attributes.len() > 0 {
+        item = item.map(|mut i| {
+            let list = cx.meta_list(mitem.span,
+                                    intern_and_get_ident("derive"),
+                                    traits);
+            i.attrs.extend(new_attributes);
+            i.attrs.push(cx.attribute(mitem.span, list));
+            i
+        });
+        return vec![Annotatable::Item(item)]
+    }
+
+    // Now check for macros-1.1 style custom #[derive].
+    //
+    // Expand each of them in order given, but *before* we expand any built-in
+    // derive modes. The logic here is to:
+    //
+    // 1. Collect the remaining `#[derive]` annotations into a list. If
+    //    there are any left, attach a `#[derive]` attribute to the item
+    //    that we're currently expanding with the remaining derive modes.
+    // 2. Manufacture a `#[derive(Foo)]` attribute to pass to the expander.
+    // 3. Expand the current item we're expanding, getting back a list of
+    //    items that replace it.
+    // 4. Extend the returned list with the current list of items we've
+    //    collected so far.
+    // 5. Return everything!
+    //
+    // If custom derive extensions end up threading through the `#[derive]`
+    // attribute, we'll get called again later on to continue expanding
+    // those modes.
+    let macros_11_derive = traits.iter()
+                                 .cloned()
+                                 .enumerate()
+                                 .filter(|&(_, ref name)| !is_builtin_trait(&name.name().unwrap()))
+                                 .next();
+    if let Some((i, titem)) = macros_11_derive {
+        let tname = ast::Ident::with_empty_ctxt(intern(&titem.name().unwrap()));
+        let ext = cx.resolver.resolve_derive_mode(tname).unwrap();
+        traits.remove(i);
+        if traits.len() > 0 {
+            item = item.map(|mut i| {
+                let list = cx.meta_list(mitem.span,
+                                        intern_and_get_ident("derive"),
+                                        traits);
+                i.attrs.push(cx.attribute(mitem.span, list));
+                i
+            });
+        }
+        let titem = cx.meta_list_item_word(titem.span, titem.name().unwrap());
+        let mitem = cx.meta_list(titem.span,
+                                 intern_and_get_ident("derive"),
+                                 vec![titem]);
+        let item = Annotatable::Item(item);
+        return ext.expand(cx, mitem.span, &mitem, item)
+    }
+
+    // Ok, at this point we know that there are no old-style `#[derive_Foo]` nor
+    // any macros-1.1 style `#[derive(Foo)]`. Expand all built-in traits here.
+
     // RFC #1445. `#[derive(PartialEq, Eq)]` adds a (trusted)
     // `#[structural_match]` attribute.
     if traits.iter().filter_map(|t| t.name()).any(|t| t == "PartialEq") &&
@@ -141,103 +239,33 @@ pub fn expand_derive(cx: &mut ExtCtxt,
         });
     }
 
-    let mut other_items = Vec::new();
-
-    let mut iter = traits.iter();
-    while let Some(titem) = iter.next() {
-
-        let tword = match titem.word() {
-            Some(name) => name,
-            None => {
-                cx.span_err(titem.span, "malformed `derive` entry");
-                continue
-            }
+    let mut items = Vec::new();
+    for titem in traits.iter() {
+        let tname = titem.word().unwrap().name();
+        let name = intern_and_get_ident(&format!("derive({})", tname));
+        let mitem = cx.meta_word(titem.span, name);
+
+        let span = Span {
+            expn_id: cx.codemap().record_expansion(codemap::ExpnInfo {
+                call_site: titem.span,
+                callee: codemap::NameAndSpan {
+                    format: codemap::MacroAttribute(intern(&format!("derive({})", tname))),
+                    span: Some(titem.span),
+                    allow_internal_unstable: true,
+                },
+            }),
+            ..titem.span
         };
-        let tname = tword.name();
 
-        // If this is a built-in derive mode, then we expand it immediately
-        // here.
-        if is_builtin_trait(&tname) {
-            let name = intern_and_get_ident(&format!("derive({})", tname));
-            let mitem = cx.meta_word(titem.span, name);
-
-            let span = Span {
-                expn_id: cx.codemap().record_expansion(codemap::ExpnInfo {
-                    call_site: titem.span,
-                    callee: codemap::NameAndSpan {
-                        format: codemap::MacroAttribute(intern(&format!("derive({})", tname))),
-                        span: Some(titem.span),
-                        allow_internal_unstable: true,
-                    },
-                }),
-                ..titem.span
-            };
-
-            let my_item = Annotatable::Item(item);
-            expand_builtin(&tname, cx, span, &mitem, &my_item, &mut |a| {
-                other_items.push(a);
-            });
-            item = my_item.expect_item();
-
-        // Otherwise if this is a `rustc_macro`-style derive mode, we process it
-        // here. The logic here is to:
-        //
-        // 1. Collect the remaining `#[derive]` annotations into a list. If
-        //    there are any left, attach a `#[derive]` attribute to the item
-        //    that we're currently expanding with the remaining derive modes.
-        // 2. Manufacture a `#[derive(Foo)]` attribute to pass to the expander.
-        // 3. Expand the current item we're expanding, getting back a list of
-        //    items that replace it.
-        // 4. Extend the returned list with the current list of items we've
-        //    collected so far.
-        // 5. Return everything!
-        //
-        // If custom derive extensions end up threading through the `#[derive]`
-        // attribute, we'll get called again later on to continue expanding
-        // those modes.
-        } else if let Some(ext) =
-                   cx.resolver.resolve_derive_mode(ast::Ident::with_empty_ctxt(intern(&tname))) {
-            let remaining_derives = iter.cloned().collect::<Vec<_>>();
-            if remaining_derives.len() > 0 {
-                let list = cx.meta_list(titem.span,
-                                        intern_and_get_ident("derive"),
-                                        remaining_derives);
-                let attr = cx.attribute(titem.span, list);
-                item = item.map(|mut i| {
-                    i.attrs.push(attr);
-                    i
-                });
-            }
-            let titem = cx.meta_list_item_word(titem.span, tname.clone());
-            let mitem = cx.meta_list(titem.span,
-                                     intern_and_get_ident("derive"),
-                                     vec![titem]);
-            let item = Annotatable::Item(item);
-            let mut items = ext.expand(cx, mitem.span, &mitem, item);
-            items.extend(other_items);
-            return items
-
-        // If we've gotten this far then it means that we're in the territory of
-        // the old custom derive mechanism. If the feature isn't enabled, we
-        // issue an error, otherwise manufacture the `derive_Foo` attribute.
-        } else if !cx.ecfg.enable_custom_derive() {
-            feature_gate::emit_feature_err(&cx.parse_sess,
-                                           "custom_derive",
-                                           titem.span,
-                                           feature_gate::GateIssue::Language,
-                                           feature_gate::EXPLAIN_CUSTOM_DERIVE);
-        } else {
-            let name = intern_and_get_ident(&format!("derive_{}", tname));
-            let mitem = cx.meta_word(titem.span, name);
-            item = item.map(|mut i| {
-                i.attrs.push(cx.attribute(mitem.span, mitem));
-                i
-            });
-        }
+        let my_item = Annotatable::Item(item);
+        expand_builtin(&tname, cx, span, &mitem, &my_item, &mut |a| {
+            items.push(a);
+        });
+        item = my_item.expect_item();
     }
 
-    other_items.insert(0, Annotatable::Item(item));
-    return other_items
+    items.insert(0, Annotatable::Item(item));
+    return items
 }
 
 macro_rules! derive_traits {