From 63ee3fe566762781d1e60f190014b416f99fed36 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Fri, 6 Mar 2015 12:56:28 -0800 Subject: [PATCH 1/3] Consolidate ExpansionConfig feature tests --- src/libsyntax/ext/expand.rs | 60 +++++++++++++------------------------ 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index d48108f17ff9a..84b0299925211 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1447,6 +1447,19 @@ pub struct ExpansionConfig<'feat> { pub recursion_limit: usize, } +macro_rules! feature_tests { + ($( fn $getter:ident = $field:ident, )*) => { + $( + pub fn $getter(&self) -> bool { + match self.features { + Some(&Features { $field: true, .. }) => true, + _ => false, + } + } + )* + } +} + impl<'feat> ExpansionConfig<'feat> { pub fn default(crate_name: String) -> ExpansionConfig<'static> { ExpansionConfig { @@ -1456,46 +1469,13 @@ impl<'feat> ExpansionConfig<'feat> { } } - pub fn enable_quotes(&self) -> bool { - match self.features { - Some(&Features { allow_quote: true, .. }) => true, - _ => false, - } - } - - pub fn enable_asm(&self) -> bool { - match self.features { - Some(&Features { allow_asm: true, .. }) => true, - _ => false, - } - } - - pub fn enable_log_syntax(&self) -> bool { - match self.features { - Some(&Features { allow_log_syntax: true, .. }) => true, - _ => false, - } - } - - pub fn enable_concat_idents(&self) -> bool { - match self.features { - Some(&Features { allow_concat_idents: true, .. }) => true, - _ => false, - } - } - - pub fn enable_trace_macros(&self) -> bool { - match self.features { - Some(&Features { allow_trace_macros: true, .. }) => true, - _ => false, - } - } - - pub fn enable_allow_internal_unstable(&self) -> bool { - match self.features { - Some(&Features { allow_internal_unstable: true, .. }) => true, - _ => false - } + feature_tests! { + fn enable_quotes = allow_quote, + fn enable_asm = allow_asm, + fn enable_log_syntax = allow_log_syntax, + fn enable_concat_idents = allow_concat_idents, + fn enable_trace_macros = allow_trace_macros, + fn enable_allow_internal_unstable = allow_internal_unstable, } } From e60e6f0693adfd03340dff31023c7517dc3af1b5 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Fri, 6 Mar 2015 15:10:20 -0800 Subject: [PATCH 2/3] Check gated attributes before and after macro expansion This is important because attributes can affect expansion. --- src/libsyntax/feature_gate.rs | 75 ++++++++++--------- src/test/compile-fail/deprecated-phase.rs | 2 + ...ng-items.rs => feature-gate-intrinsics.rs} | 4 - .../compile-fail/feature-gate-lang-items.rs | 15 ++++ src/test/compile-fail/linkage1.rs | 1 - src/test/compile-fail/malformed-plugin-1.rs | 1 + src/test/compile-fail/malformed-plugin-2.rs | 1 + src/test/compile-fail/malformed-plugin-3.rs | 1 + .../plugin-extern-crate-attr-deprecated.rs | 2 + .../compile-fail/reserved-attr-on-macro.rs | 18 +++++ src/test/run-pass-fulldeps/macro-crate.rs | 2 +- 11 files changed, 79 insertions(+), 43 deletions(-) rename src/test/compile-fail/{feature-gate-intrinsics-and-lang-items.rs => feature-gate-intrinsics.rs} (89%) create mode 100644 src/test/compile-fail/feature-gate-lang-items.rs create mode 100644 src/test/compile-fail/reserved-attr-on-macro.rs diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index c6a94b26a0a51..eff86757fd0b2 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -196,6 +196,7 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[ ("no_mangle", Normal), ("no_link", Normal), ("derive", Normal), + ("deriving", Normal), // deprecation err in expansion ("should_fail", Normal), ("should_panic", Normal), ("ignore", Normal), @@ -235,6 +236,9 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[ "the `#[rustc_move_fragments]` attribute \ is an experimental feature")), + ("allow_internal_unstable", Gated("allow_internal_unstable", + EXPLAIN_ALLOW_INTERNAL_UNSTABLE)), + // FIXME: #14408 whitelist docs since rustdoc looks at them ("doc", Whitelisted), @@ -369,6 +373,33 @@ impl<'a> Context<'a> { fn has_feature(&self, feature: &str) -> bool { self.features.iter().any(|&n| n == feature) } + + fn check_attribute(&self, attr: &ast::Attribute) { + debug!("check_attribute(attr = {:?})", attr); + let name = &*attr.name(); + for &(n, ty) in KNOWN_ATTRIBUTES { + if n == name { + if let Gated(gate, desc) = ty { + self.gate_feature(gate, attr.span, desc); + } + debug!("check_attribute: {:?} is known, {:?}", name, ty); + return; + } + } + if name.starts_with("rustc_") { + self.gate_feature("rustc_attrs", attr.span, + "unless otherwise specified, attributes \ + with the prefix `rustc_` \ + are reserved for internal compiler diagnostics"); + } else { + self.gate_feature("custom_attribute", attr.span, + format!("The attribute `{}` is currently \ + unknown to the the compiler and \ + may have meaning \ + added to it in the future", + name).as_slice()); + } + } } pub fn emit_feature_err(diag: &SpanHandler, feature: &str, span: Span, explain: &str) { @@ -436,10 +467,7 @@ impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> { } fn visit_attribute(&mut self, attr: &'v ast::Attribute) { - if attr.name() == "allow_internal_unstable" { - self.context.gate_feature("allow_internal_unstable", attr.span, - EXPLAIN_ALLOW_INTERNAL_UNSTABLE) - } + self.context.check_attribute(attr); } } @@ -456,6 +484,12 @@ impl<'a> PostExpansionVisitor<'a> { } impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { + fn visit_attribute(&mut self, attr: &ast::Attribute) { + if !self.context.cm.span_allows_unstable(attr.span) { + self.context.check_attribute(attr); + } + } + fn visit_name(&mut self, sp: Span, name: ast::Name) { if !token::get_name(name).is_ascii() { self.gate_feature("non_ascii_idents", sp, @@ -556,12 +590,6 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { } fn visit_foreign_item(&mut self, i: &ast::ForeignItem) { - if attr::contains_name(&i.attrs, "linkage") { - self.gate_feature("linkage", i.span, - "the `linkage` attribute is experimental \ - and not portable across platforms") - } - let links_to_llvm = match attr::first_attr_value_str_by_name(&i.attrs, "link_name") { Some(val) => val.starts_with("llvm."), @@ -636,33 +664,6 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { visit::walk_expr(self, e); } - fn visit_attribute(&mut self, attr: &ast::Attribute) { - debug!("visit_attribute(attr = {:?})", attr); - let name = &*attr.name(); - for &(n, ty) in KNOWN_ATTRIBUTES { - if n == name { - if let Gated(gate, desc) = ty { - self.gate_feature(gate, attr.span, desc); - } - debug!("visit_attribute: {:?} is known, {:?}", name, ty); - return; - } - } - if name.starts_with("rustc_") { - self.gate_feature("rustc_attrs", attr.span, - "unless otherwise specified, attributes \ - with the prefix `rustc_` \ - are reserved for internal compiler diagnostics"); - } else { - self.gate_feature("custom_attribute", attr.span, - format!("The attribute `{}` is currently \ - unknown to the the compiler and \ - may have meaning \ - added to it in the future", - name).as_slice()); - } - } - fn visit_pat(&mut self, pattern: &ast::Pat) { match pattern.node { ast::PatVec(_, Some(_), ref last) if !last.is_empty() => { diff --git a/src/test/compile-fail/deprecated-phase.rs b/src/test/compile-fail/deprecated-phase.rs index 1401494d987a2..22fc4a94cd25a 100644 --- a/src/test/compile-fail/deprecated-phase.rs +++ b/src/test/compile-fail/deprecated-phase.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(custom_attribute)] + #[phase(blah)] //~^ ERROR #[phase] is deprecated extern crate foo; diff --git a/src/test/compile-fail/feature-gate-intrinsics-and-lang-items.rs b/src/test/compile-fail/feature-gate-intrinsics.rs similarity index 89% rename from src/test/compile-fail/feature-gate-intrinsics-and-lang-items.rs rename to src/test/compile-fail/feature-gate-intrinsics.rs index 986d52b1787ec..a4c09b21c90a6 100644 --- a/src/test/compile-fail/feature-gate-intrinsics-and-lang-items.rs +++ b/src/test/compile-fail/feature-gate-intrinsics.rs @@ -8,9 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[lang="foo"] //~ ERROR language items are subject to change -trait Foo {} - extern "rust-intrinsic" { //~ ERROR intrinsics are subject to change fn bar(); } @@ -20,4 +17,3 @@ extern "rust-intrinsic" fn baz() { //~ ERROR intrinsics are subject to change fn main() { } - diff --git a/src/test/compile-fail/feature-gate-lang-items.rs b/src/test/compile-fail/feature-gate-lang-items.rs new file mode 100644 index 0000000000000..0435ff4c332d5 --- /dev/null +++ b/src/test/compile-fail/feature-gate-lang-items.rs @@ -0,0 +1,15 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[lang="foo"] //~ ERROR language items are subject to change +trait Foo {} + +fn main() { +} diff --git a/src/test/compile-fail/linkage1.rs b/src/test/compile-fail/linkage1.rs index 35f93c13fb5e2..555cc2b9a7aad 100644 --- a/src/test/compile-fail/linkage1.rs +++ b/src/test/compile-fail/linkage1.rs @@ -11,5 +11,4 @@ extern { #[linkage = "extern_weak"] static foo: isize; //~^ ERROR: the `linkage` attribute is experimental and not portable - //~^^ ERROR: the `linkage` attribute is experimental and not portable } diff --git a/src/test/compile-fail/malformed-plugin-1.rs b/src/test/compile-fail/malformed-plugin-1.rs index 254a797ef1cd0..214a5e5e3eb11 100644 --- a/src/test/compile-fail/malformed-plugin-1.rs +++ b/src/test/compile-fail/malformed-plugin-1.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(plugin)] #![plugin] //~ ERROR malformed plugin attribute fn main() {} diff --git a/src/test/compile-fail/malformed-plugin-2.rs b/src/test/compile-fail/malformed-plugin-2.rs index 884087b7bc534..1b112608beeb2 100644 --- a/src/test/compile-fail/malformed-plugin-2.rs +++ b/src/test/compile-fail/malformed-plugin-2.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(plugin)] #![plugin="bleh"] //~ ERROR malformed plugin attribute fn main() {} diff --git a/src/test/compile-fail/malformed-plugin-3.rs b/src/test/compile-fail/malformed-plugin-3.rs index 4885bb901df68..0c948831de217 100644 --- a/src/test/compile-fail/malformed-plugin-3.rs +++ b/src/test/compile-fail/malformed-plugin-3.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(plugin)] #![plugin(foo="bleh")] //~ ERROR malformed plugin attribute fn main() {} diff --git a/src/test/compile-fail/plugin-extern-crate-attr-deprecated.rs b/src/test/compile-fail/plugin-extern-crate-attr-deprecated.rs index ccda5cbdceba6..efa352e386d4d 100644 --- a/src/test/compile-fail/plugin-extern-crate-attr-deprecated.rs +++ b/src/test/compile-fail/plugin-extern-crate-attr-deprecated.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(plugin)] + #[plugin] //~ ERROR #[plugin] on `extern crate` is deprecated //~^ HELP use a crate attribute instead, i.e. #![plugin(std)] extern crate std; diff --git a/src/test/compile-fail/reserved-attr-on-macro.rs b/src/test/compile-fail/reserved-attr-on-macro.rs new file mode 100644 index 0000000000000..db8f82a70e103 --- /dev/null +++ b/src/test/compile-fail/reserved-attr-on-macro.rs @@ -0,0 +1,18 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[rustc_attribute_should_be_reserved] //~ ERROR attributes with the prefix `rustc_` are reserved +macro_rules! foo { + () => (()); +} + +fn main() { + foo!(); +} diff --git a/src/test/run-pass-fulldeps/macro-crate.rs b/src/test/run-pass-fulldeps/macro-crate.rs index 58ccd79b7121d..7a2846c31b663 100644 --- a/src/test/run-pass-fulldeps/macro-crate.rs +++ b/src/test/run-pass-fulldeps/macro-crate.rs @@ -11,7 +11,7 @@ // aux-build:macro_crate_test.rs // ignore-stage1 -#![feature(plugin)] +#![feature(plugin, custom_attribute)] #![plugin(macro_crate_test)] #[macro_use] #[no_link] From 491054f08e2aaaa8438f1a9943f115dad9da1c6b Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Fri, 6 Mar 2015 13:15:54 -0800 Subject: [PATCH 3/3] 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] --- src/doc/reference.md | 4 + src/libsyntax/ext/base.rs | 6 +- src/libsyntax/ext/deriving/bounds.rs | 45 ++-- src/libsyntax/ext/deriving/mod.rs | 212 ++++++++++-------- src/libsyntax/ext/expand.rs | 1 + src/libsyntax/feature_gate.rs | 14 ++ src/test/auxiliary/custom_derive_plugin.rs | 74 ++++++ src/test/compile-fail/deriving-bounds.rs | 6 +- .../deriving-meta-unknown-trait.rs | 3 +- .../compile-fail/malformed-derive-entry.rs | 25 +++ src/test/compile-fail/single-derive-attr.rs | 15 ++ src/test/run-pass-fulldeps/derive-totalsum.rs | 59 +++++ src/test/run-pass/deprecated-derive.rs | 15 ++ .../run-pass/single-derive-attr-with-gate.rs | 18 ++ 14 files changed, 372 insertions(+), 125 deletions(-) create mode 100644 src/test/auxiliary/custom_derive_plugin.rs create mode 100644 src/test/compile-fail/malformed-derive-entry.rs create mode 100644 src/test/compile-fail/single-derive-attr.rs create mode 100644 src/test/run-pass-fulldeps/derive-totalsum.rs create mode 100644 src/test/run-pass/deprecated-derive.rs create mode 100644 src/test/run-pass/single-derive-attr-with-gate.rs diff --git a/src/doc/reference.md b/src/doc/reference.md index 95ccc71532a34..a772d98583e49 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -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. diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 5513b44235d2c..8aeafe419daa9 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 93098484ae013..e408c99935d1c 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(cx: &mut ExtCtxt, - span: Span, - mitem: &MetaItem, - item: &Item, - push: F) where +pub fn expand_deriving_unsafe_bound(cx: &mut ExtCtxt, + span: Span, + _: &MetaItem, + _: &Item, + _: F) where F: FnOnce(P), { - 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(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Item, + push: F) where + F: FnOnce(P), +{ 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 973c8f5fa1e0b..2631c28cf2fe9 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)) { +fn expand_deprecated_deriving(cx: &mut ExtCtxt, + span: Span, + _: &MetaItem, + _: &Item, + _: &mut FnMut(P)) { 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)) { - 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) -> P { + 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)) { + 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 84b0299925211..db8819ef82c2e 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 eff86757fd0b2..425c517cb29fd 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(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"), diff --git a/src/test/auxiliary/custom_derive_plugin.rs b/src/test/auxiliary/custom_derive_plugin.rs new file mode 100644 index 0000000000000..e268896480480 --- /dev/null +++ b/src/test/auxiliary/custom_derive_plugin.rs @@ -0,0 +1,74 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// force-host + +#![feature(plugin_registrar)] +#![feature(box_syntax)] +#![feature(rustc_private)] + +extern crate syntax; +extern crate rustc; + +use syntax::ast; +use syntax::codemap::Span; +use syntax::ext::base::{Decorator, ExtCtxt}; +use syntax::ext::build::AstBuilder; +use syntax::ext::deriving::generic::{cs_fold, TraitDef, MethodDef, combine_substructure}; +use syntax::ext::deriving::generic::ty::{Literal, LifetimeBounds, Path, borrowed_explicit_self}; +use syntax::parse::token; +use syntax::ptr::P; +use rustc::plugin::Registry; + +#[plugin_registrar] +pub fn plugin_registrar(reg: &mut Registry) { + reg.register_syntax_extension( + token::intern("derive_TotalSum"), + Decorator(box expand)); +} + +fn expand(cx: &mut ExtCtxt, + span: Span, + mitem: &ast::MetaItem, + item: &ast::Item, + push: &mut FnMut(P)) { + let trait_def = TraitDef { + span: span, + attributes: vec![], + path: Path::new(vec!["TotalSum"]), + additional_bounds: vec![], + generics: LifetimeBounds::empty(), + associated_types: vec![], + methods: vec![ + MethodDef { + name: "total_sum", + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: vec![], + ret_ty: Literal(Path::new_local("isize")), + attributes: vec![], + combine_substructure: combine_substructure(box |cx, span, substr| { + let zero = cx.expr_int(span, 0); + cs_fold(false, + |cx, span, subexpr, field, _| { + cx.expr_binary(span, ast::BiAdd, subexpr, + cx.expr_method_call(span, field, + token::str_to_ident("total_sum"), vec![])) + }, + zero, + box |cx, span, _, _| { cx.span_bug(span, "wtf??"); }, + cx, span, substr) + }), + }, + ], + }; + + trait_def.expand(cx, mitem, item, |i| push(i)) +} diff --git a/src/test/compile-fail/deriving-bounds.rs b/src/test/compile-fail/deriving-bounds.rs index c0bcbb284a15e..72d06274de471 100644 --- a/src/test/compile-fail/deriving-bounds.rs +++ b/src/test/compile-fail/deriving-bounds.rs @@ -8,12 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[derive(Copy(Bad))] -//~^ ERROR unexpected value in deriving, expected a trait +#[derive(Send)] +//~^ ERROR this unsafe trait should be implemented explicitly struct Test; #[derive(Sync)] -//~^ ERROR Sync is an unsafe trait and it should be implemented explicitly +//~^ ERROR this unsafe trait should be implemented explicitly struct Test1; pub fn main() {} diff --git a/src/test/compile-fail/deriving-meta-unknown-trait.rs b/src/test/compile-fail/deriving-meta-unknown-trait.rs index 6b85656bdd9e6..e223499469355 100644 --- a/src/test/compile-fail/deriving-meta-unknown-trait.rs +++ b/src/test/compile-fail/deriving-meta-unknown-trait.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[derive(Eqr)] //~ ERROR unknown `derive` trait: `Eqr` +#[derive(Eqr)] +//~^ ERROR `#[derive]` for custom traits is not stable enough for use and is subject to change struct Foo; pub fn main() {} diff --git a/src/test/compile-fail/malformed-derive-entry.rs b/src/test/compile-fail/malformed-derive-entry.rs new file mode 100644 index 0000000000000..62dbc21495a54 --- /dev/null +++ b/src/test/compile-fail/malformed-derive-entry.rs @@ -0,0 +1,25 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[derive(Copy(Bad))] +//~^ ERROR malformed `derive` entry +struct Test1; + +#[derive(Copy="bad")] +//~^ ERROR malformed `derive` entry +struct Test2; + +#[derive()] +//~^ WARNING empty trait list +struct Test3; + +#[derive] +//~^ WARNING empty trait list +struct Test4; diff --git a/src/test/compile-fail/single-derive-attr.rs b/src/test/compile-fail/single-derive-attr.rs new file mode 100644 index 0000000000000..0b1b3141f5bf7 --- /dev/null +++ b/src/test/compile-fail/single-derive-attr.rs @@ -0,0 +1,15 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[derive_Clone] +//~^ ERROR attributes of the form `#[derive_*]` are reserved +struct Test; + +pub fn main() {} diff --git a/src/test/run-pass-fulldeps/derive-totalsum.rs b/src/test/run-pass-fulldeps/derive-totalsum.rs new file mode 100644 index 0000000000000..848b2425e4496 --- /dev/null +++ b/src/test/run-pass-fulldeps/derive-totalsum.rs @@ -0,0 +1,59 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:custom_derive_plugin.rs +// ignore-stage1 + +#![feature(plugin, custom_derive)] +#![plugin(custom_derive_plugin)] + +trait TotalSum { + fn total_sum(&self) -> isize; +} + +impl TotalSum for isize { + fn total_sum(&self) -> isize { + *self + } +} + +struct Seven; + +impl TotalSum for Seven { + fn total_sum(&self) -> isize { + 7 + } +} + +#[derive(TotalSum)] +struct Foo { + seven: Seven, + bar: Bar, + baz: isize, +} + +#[derive(TotalSum)] +struct Bar { + quux: isize, + bleh: isize, +} + + +pub fn main() { + let v = Foo { + seven: Seven, + bar: Bar { + quux: 9, + bleh: 3, + }, + baz: 80, + }; + assert_eq!(v.total_sum(), 99); +} diff --git a/src/test/run-pass/deprecated-derive.rs b/src/test/run-pass/deprecated-derive.rs new file mode 100644 index 0000000000000..494d62c773770 --- /dev/null +++ b/src/test/run-pass/deprecated-derive.rs @@ -0,0 +1,15 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[derive(Show)] +//~^ WARNING derive(Show) is deprecated +struct Test1; + +fn main() { } diff --git a/src/test/run-pass/single-derive-attr-with-gate.rs b/src/test/run-pass/single-derive-attr-with-gate.rs new file mode 100644 index 0000000000000..cc5d8fc78911d --- /dev/null +++ b/src/test/run-pass/single-derive-attr-with-gate.rs @@ -0,0 +1,18 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(custom_derive)] + +#[derive_Clone] +struct Test; + +pub fn main() { + Test.clone(); +}