Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustc: Tweak expansion order of custom derive #36782

Merged
merged 1 commit into from
Sep 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 122 additions & 94 deletions src/libsyntax_ext/deriving/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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") &&
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ trait Append {
Append,
Eq)]
struct A {
//~^ ERROR: the semantics of constant patterns is not yet settled
inner: u32,
}

Expand Down
4 changes: 2 additions & 2 deletions src/test/run-pass-fulldeps/rustc-macro/auxiliary/derive-a.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ use rustc_macro::TokenStream;
pub fn derive(input: TokenStream) -> TokenStream {
let input = input.to_string();
assert!(input.contains("struct A;"));
assert!(input.contains("#[derive(Eq, Copy, Clone)]"));
"#[derive(Eq, Copy, Clone)] struct A;".parse().unwrap()
assert!(input.contains("#[derive(Debug, PartialEq, Eq, Copy, Clone)]"));
"#[derive(Debug, PartialEq, Eq, Copy, Clone)] struct A;".parse().unwrap()
}