diff options
| author | Keegan McAllister <kmcallister@mozilla.com> | 2015-03-06 13:15:54 -0800 |
|---|---|---|
| committer | Keegan McAllister <kmcallister@mozilla.com> | 2015-03-06 18:20:16 -0800 |
| commit | 491054f08e2aaaa8438f1a9943f115dad9da1c6b (patch) | |
| tree | 7ab7e4dfd5b60e7d3d3aedb501065da17bc35204 /src/libsyntax | |
| parent | e60e6f0693adfd03340dff31023c7517dc3af1b5 (diff) | |
| download | rust-491054f08e2aaaa8438f1a9943f115dad9da1c6b.tar.gz rust-491054f08e2aaaa8438f1a9943f115dad9da1c6b.zip | |
Make #[derive(Anything)] into sugar for #[derive_Anything]
This is a hack, but I don't think we can do much better as long as `derive` is
running at the syntax expansion phase.
If the custom_derive feature gate is enabled, this works with user-defined
traits and syntax extensions. Without the gate, you can't use e.g. #[derive_Clone]
directly, so this does not change the stable language.
This commit also cleans up the deriving code somewhat, and forbids some
previously-meaningless attribute syntax. For this reason it's technically a
[breaking-change]
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ext/base.rs | 6 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/bounds.rs | 45 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/mod.rs | 212 | ||||
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 1 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 14 |
5 files changed, 157 insertions, 121 deletions
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 5513b44235d..8aeafe419da 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -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 diff --git a/src/libsyntax/ext/deriving/bounds.rs b/src/libsyntax/ext/deriving/bounds.rs index 93098484ae0..e408c99935d 100644 --- a/src/libsyntax/ext/deriving/bounds.rs +++ b/src/libsyntax/ext/deriving/bounds.rs @@ -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 { diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs index 973c8f5fa1e..2631c28cf2f 100644 --- a/src/libsyntax/ext/deriving/mod.rs +++ b/src/libsyntax/ext/deriving/mod.rs @@ -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. // @@ -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 { @@ -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, + "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)); + } +} diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 84b02999252..db8819ef82c 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1476,6 +1476,7 @@ impl<'feat> ExpansionConfig<'feat> { fn enable_concat_idents = allow_concat_idents, fn enable_trace_macros = allow_trace_macros, fn enable_allow_internal_unstable = allow_internal_unstable, + fn enable_custom_derive = allow_custom_derive, } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index eff86757fd0..425c517cb29 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -137,6 +137,10 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[ // Allows the use of custom attributes; RFC 572 ("custom_attribute", "1.0.0", Active), + // Allows the use of #[derive(Anything)] as sugar for + // #[derive_Anything]. + ("custom_derive", "1.0.0", Active), + // Allows the use of rustc_* attributes; RFC 572 ("rustc_attrs", "1.0.0", Active), @@ -319,6 +323,7 @@ pub struct Features { pub allow_concat_idents: bool, pub allow_trace_macros: bool, pub allow_internal_unstable: bool, + pub allow_custom_derive: bool, pub old_orphan_check: bool, pub simd_ffi: bool, pub unmarked_api: bool, @@ -340,6 +345,7 @@ impl Features { allow_concat_idents: false, allow_trace_macros: false, allow_internal_unstable: false, + allow_custom_derive: false, old_orphan_check: false, simd_ffi: false, unmarked_api: false, @@ -391,6 +397,10 @@ impl<'a> Context<'a> { "unless otherwise specified, attributes \ with the prefix `rustc_` \ are reserved for internal compiler diagnostics"); + } else if name.starts_with("derive_") { + self.gate_feature("custom_derive", attr.span, + "attributes of the form `#[derive_*]` are reserved + for the compiler"); } else { self.gate_feature("custom_attribute", attr.span, format!("The attribute `{}` is currently \ @@ -432,6 +442,9 @@ pub const EXPLAIN_TRACE_MACROS: &'static str = pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &'static str = "allow_internal_unstable side-steps feature gating and stability checks"; +pub const EXPLAIN_CUSTOM_DERIVE: &'static str = + "`#[derive]` for custom traits is not stable enough for use and is subject to change"; + struct MacroVisitor<'a> { context: &'a Context<'a> } @@ -780,6 +793,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C allow_concat_idents: cx.has_feature("concat_idents"), allow_trace_macros: cx.has_feature("trace_macros"), allow_internal_unstable: cx.has_feature("allow_internal_unstable"), + allow_custom_derive: cx.has_feature("custom_derive"), old_orphan_check: cx.has_feature("old_orphan_check"), simd_ffi: cx.has_feature("simd_ffi"), unmarked_api: cx.has_feature("unmarked_api"), |
