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

Make #[derive(Anything)] into sugar for #[derive_Anything] #23137

Merged
merged 3 commits into from
Mar 7, 2015
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
4 changes: 4 additions & 0 deletions src/doc/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2429,6 +2429,10 @@ The currently implemented features of the reference compiler are:
so that new attributes can be added in a bacwards compatible
manner (RFC 572).

* `custom_derive` - Allows the use of `#[derive(Foo,Bar)]` as sugar for
`#[derive_Foo] #[derive_Bar]`, which can be user-defined syntax
extensions.

* `intrinsics` - Allows use of the "rust-intrinsics" ABI. Compiler intrinsics
are inherently unstable and no promise about them is made.

Expand Down
6 changes: 2 additions & 4 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,10 +491,8 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
syntax_expanders.insert(intern("log_syntax"),
builtin_normal_expander(
ext::log_syntax::expand_syntax_ext));
syntax_expanders.insert(intern("derive"),
Decorator(Box::new(ext::deriving::expand_meta_derive)));
syntax_expanders.insert(intern("deriving"),
Decorator(Box::new(ext::deriving::expand_deprecated_deriving)));

ext::deriving::register_all(&mut syntax_expanders);

if ecfg.enable_quotes() {
// Quasi-quoting expanders
Expand Down
45 changes: 16 additions & 29 deletions src/libsyntax/ext/deriving/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,34 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use ast::{MetaItem, MetaWord, Item};
use ast::{MetaItem, Item};
use codemap::Span;
use ext::base::ExtCtxt;
use ext::deriving::generic::*;
use ext::deriving::generic::ty::*;
use ptr::P;

pub fn expand_deriving_bound<F>(cx: &mut ExtCtxt,
span: Span,
mitem: &MetaItem,
item: &Item,
push: F) where
pub fn expand_deriving_unsafe_bound<F>(cx: &mut ExtCtxt,
span: Span,
_: &MetaItem,
_: &Item,
_: F) where
F: FnOnce(P<Item>),
{
let name = match mitem.node {
MetaWord(ref tname) => {
match &tname[..] {
"Copy" => "Copy",
"Send" | "Sync" => {
return cx.span_err(span,
&format!("{} is an unsafe trait and it \
should be implemented explicitly",
*tname))
}
ref tname => {
cx.span_bug(span,
&format!("expected built-in trait name but \
found {}", *tname))
}
}
},
_ => {
return cx.span_err(span, "unexpected value in deriving, expected \
a trait")
}
};
cx.span_err(span, "this unsafe trait should be implemented explicitly");
}

pub fn expand_deriving_copy<F>(cx: &mut ExtCtxt,
span: Span,
mitem: &MetaItem,
item: &Item,
push: F) where
F: FnOnce(P<Item>),
{
let path = Path::new(vec![
if cx.use_std { "std" } else { "core" },
"marker",
name
"Copy",
]);

let trait_def = TraitDef {
Expand Down
212 changes: 124 additions & 88 deletions src/libsyntax/ext/deriving/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
Expand All @@ -13,9 +13,13 @@
//! FIXME (#2810): hygiene. Search for "__" strings (in other files too). We also assume "extra" is
//! the standard library, and "std" is the core library.

use ast::{Item, MetaItem, MetaList, MetaNameValue, MetaWord};
use ext::base::ExtCtxt;
use ast::{Item, MetaItem, MetaWord};
use attr::AttrMetaMethods;
use ext::base::{ExtCtxt, SyntaxEnv, Decorator, ItemDecorator, Modifier};
use ext::build::AstBuilder;
use feature_gate;
use codemap::Span;
use parse::token::{intern, intern_and_get_ident};
use ptr::P;

macro_rules! pathvec {
Expand Down Expand Up @@ -74,101 +78,133 @@ pub mod totalord;

pub mod generic;

pub fn expand_deprecated_deriving(cx: &mut ExtCtxt,
span: Span,
_: &MetaItem,
_: &Item,
_: &mut FnMut(P<Item>)) {
fn expand_deprecated_deriving(cx: &mut ExtCtxt,
span: Span,
_: &MetaItem,
_: &Item,
_: &mut FnMut(P<Item>)) {
cx.span_err(span, "`deriving` has been renamed to `derive`");
}

pub fn expand_meta_derive(cx: &mut ExtCtxt,
_span: Span,
mitem: &MetaItem,
item: &Item,
push: &mut FnMut(P<Item>)) {
match mitem.node {
MetaNameValue(_, ref l) => {
cx.span_err(l.span, "unexpected value in `derive`");
fn expand_derive(cx: &mut ExtCtxt,
_: Span,
mitem: &MetaItem,
item: P<Item>) -> P<Item> {
item.map(|mut item| {
if mitem.value_str().is_some() {
cx.span_err(mitem.span, "unexpected value in `derive`");
}
MetaWord(_) => {

let traits = mitem.meta_item_list().unwrap_or(&[]);
if traits.is_empty() {
cx.span_warn(mitem.span, "empty trait list in `derive`");
}
MetaList(_, ref titems) if titems.len() == 0 => {
cx.span_warn(mitem.span, "empty trait list in `derive`");

for titem in traits.iter().rev() {
let tname = match titem.node {
MetaWord(ref tname) => tname,
_ => {
cx.span_err(titem.span, "malformed `derive` entry");
continue;
}
};

if !(is_builtin_trait(tname) || cx.ecfg.enable_custom_derive()) {
feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're treating this change as some nice sugar for libraries that are already opting into the unstable plugin infrastructure, do we need a separate feature for this? It should be covered under #![feature(plugin)] and I'm not sure if there's much benefit to be gained from having a separate feature for custom derives.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this check, a misspelling like

#[derive(Eqq)] struct Foo;

will produce a warning or error about unknown attribute derive_Eqq. And that may not happen if compilation fails first due to a missing trait impl. The gate produces a better error message.

It also controls whether you can use e.g. #[derive_Eq] directly. We could probably forbid this outright, but I don't see a need.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, cool.

"custom_derive",
titem.span,
feature_gate::EXPLAIN_CUSTOM_DERIVE);
continue;
}

// #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar]
item.attrs.push(cx.attribute(titem.span, cx.meta_word(titem.span,
intern_and_get_ident(&format!("derive_{}", tname)))));
}
MetaList(_, ref titems) => {
for titem in titems.iter().rev() {
match titem.node {
MetaNameValue(ref tname, _) |
MetaList(ref tname, _) |
MetaWord(ref tname) => {
macro_rules! expand {
($func:path) => ($func(cx, titem.span, &**titem, item,
|i| push(i)))
}

match &tname[..] {
"Clone" => expand!(clone::expand_deriving_clone),

"Hash" => expand!(hash::expand_deriving_hash),

"RustcEncodable" => {
expand!(encodable::expand_deriving_rustc_encodable)
}
"RustcDecodable" => {
expand!(decodable::expand_deriving_rustc_decodable)
}
"Encodable" => {
cx.span_warn(titem.span,
"derive(Encodable) is deprecated \
in favor of derive(RustcEncodable)");

expand!(encodable::expand_deriving_encodable)
}
"Decodable" => {
cx.span_warn(titem.span,
"derive(Decodable) is deprecated \
in favor of derive(RustcDecodable)");

expand!(decodable::expand_deriving_decodable)
}

"PartialEq" => expand!(eq::expand_deriving_eq),
"Eq" => expand!(totaleq::expand_deriving_totaleq),
"PartialOrd" => expand!(ord::expand_deriving_ord),
"Ord" => expand!(totalord::expand_deriving_totalord),

"Rand" => expand!(rand::expand_deriving_rand),

"Show" => {
cx.span_warn(titem.span,
"derive(Show) is deprecated \
in favor of derive(Debug)");

expand!(show::expand_deriving_show)
},

"Debug" => expand!(show::expand_deriving_show),

"Default" => expand!(default::expand_deriving_default),

"FromPrimitive" => expand!(primitive::expand_deriving_from_primitive),

"Send" => expand!(bounds::expand_deriving_bound),
"Sync" => expand!(bounds::expand_deriving_bound),
"Copy" => expand!(bounds::expand_deriving_bound),

ref tname => {
cx.span_err(titem.span,
&format!("unknown `derive` \
trait: `{}`",
*tname));
}
};

item
})
}

macro_rules! derive_traits {
($( $name:expr => $func:path, )*) => {
pub fn register_all(env: &mut SyntaxEnv) {
// Define the #[derive_*] extensions.
$({
struct DeriveExtension;

impl ItemDecorator for DeriveExtension {
fn expand(&self,
ecx: &mut ExtCtxt,
sp: Span,
mitem: &MetaItem,
item: &Item,
push: &mut FnMut(P<Item>)) {
warn_if_deprecated(ecx, sp, $name);
$func(ecx, sp, mitem, item, |i| push(i));
}
}

env.insert(intern(concat!("derive_", $name)),
Decorator(Box::new(DeriveExtension)));
})*

env.insert(intern("derive"),
Modifier(Box::new(expand_derive)));
env.insert(intern("deriving"),
Decorator(Box::new(expand_deprecated_deriving)));
}

fn is_builtin_trait(name: &str) -> bool {
match name {
$( $name )|* => true,
_ => false,
}
}
}
}

derive_traits! {
"Clone" => clone::expand_deriving_clone,

"Hash" => hash::expand_deriving_hash,

"RustcEncodable" => encodable::expand_deriving_rustc_encodable,

"RustcDecodable" => decodable::expand_deriving_rustc_decodable,

"PartialEq" => eq::expand_deriving_eq,
"Eq" => totaleq::expand_deriving_totaleq,
"PartialOrd" => ord::expand_deriving_ord,
"Ord" => totalord::expand_deriving_totalord,

"Rand" => rand::expand_deriving_rand,

"Debug" => show::expand_deriving_show,

"Default" => default::expand_deriving_default,

"FromPrimitive" => primitive::expand_deriving_from_primitive,

"Send" => bounds::expand_deriving_unsafe_bound,
"Sync" => bounds::expand_deriving_unsafe_bound,
"Copy" => bounds::expand_deriving_copy,

// deprecated
"Show" => show::expand_deriving_show,
"Encodable" => encodable::expand_deriving_encodable,
"Decodable" => decodable::expand_deriving_decodable,
}

#[inline] // because `name` is a compile-time constant
fn warn_if_deprecated(ecx: &mut ExtCtxt, sp: Span, name: &str) {
if let Some(replacement) = match name {
"Show" => Some("Debug"),
"Encodable" => Some("RustcEncodable"),
"Decodable" => Some("RustcDecodable"),
_ => None,
} {
ecx.span_warn(sp, &format!("derive({}) is deprecated in favor of derive({})",
name, replacement));
}
}
Loading