diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 5ade636a3271d..966c82163860f 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -724,9 +724,7 @@ impl<'a> Context<'a> { 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"); + self.gate_feature("custom_derive", attr.span, EXPLAIN_DERIVE_UNDERSCORE); } else { // Only run the custom attribute lint during regular // feature gate checking. Macro gating runs @@ -801,6 +799,8 @@ pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &'static str = pub const EXPLAIN_CUSTOM_DERIVE: &'static str = "`#[derive]` for custom traits is not stable enough for use and is subject to change"; +pub const EXPLAIN_DERIVE_UNDERSCORE: &'static str = + "attributes of the form `#[derive_*]` are reserved for the compiler"; struct MacroVisitor<'a> { context: &'a Context<'a> diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index 92a141fb4ec86..92aae0bd355fc 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -96,6 +96,10 @@ fn expand_derive(cx: &mut ExtCtxt, let mut found_partial_eq = false; let mut found_eq = false; + // See below for how this is used with #[structural_match]. + let unstable_span = Span { expn_id: cx.backtrace(), .. span }; + assert!(cx.parse_sess.codemap().span_allows_unstable(unstable_span)); + for titem in traits.iter().rev() { let tname = match titem.node { MetaItemKind::Word(ref tname) => tname, @@ -120,8 +124,18 @@ fn expand_derive(cx: &mut ExtCtxt, found_partial_eq = true; } + // Mark the attributes we generate as allowing unstable code, + // to bypass the feature-gating of #[derive_*] attributes. + let mut tspan = titem.span; + tspan.expn_id = unstable_span.expn_id; + if !cx.parse_sess.codemap().span_allows_unstable(tspan) { + // If we can't use the trait span, use the full #[...] span. + // See below for how this works with #[structural_match]. + tspan = unstable_span; + } + // #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar] - item.attrs.push(cx.attribute(titem.span, cx.meta_word(titem.span, + item.attrs.push(cx.attribute(tspan, cx.meta_word(tspan, intern_and_get_ident(&format!("derive_{}", tname))))); } @@ -155,12 +169,10 @@ fn expand_derive(cx: &mut ExtCtxt, // // See tests src/run-pass/rfc1445 for // examples. --nmatsakis - let span = Span { expn_id: cx.backtrace(), .. span }; - assert!(cx.parse_sess.codemap().span_allows_unstable(span)); - debug!("inserting structural_match with span {:?}", span); + debug!("inserting structural_match with span {:?}", unstable_span); let structural_match = intern_and_get_ident("structural_match"); - item.attrs.push(cx.attribute(span, - cx.meta_word(span, + item.attrs.push(cx.attribute(unstable_span, + cx.meta_word(unstable_span, structural_match))); } @@ -188,7 +200,7 @@ macro_rules! derive_traits { mitem: &MetaItem, annotatable: &Annotatable, push: &mut FnMut(Annotatable)) { - warn_if_deprecated(ecx, sp, $name); + check_builtin_derive(ecx, sp, $name); $func(ecx, sp, mitem, annotatable, push); } } @@ -238,7 +250,15 @@ derive_traits! { } #[inline] // because `name` is a compile-time constant -fn warn_if_deprecated(ecx: &mut ExtCtxt, sp: Span, name: &str) { +fn check_builtin_derive(ecx: &mut ExtCtxt, sp: Span, name: &str) { + let allows_unstable = ecx.parse_sess.codemap().span_allows_unstable(sp); + if !(allows_unstable || ecx.ecfg.enable_custom_derive()) { + feature_gate::emit_feature_err(&ecx.parse_sess.span_diagnostic, + "custom_derive", + sp, + feature_gate::GateIssue::Language, + feature_gate::EXPLAIN_DERIVE_UNDERSCORE); + } if let Some(replacement) = match name { "Encodable" => Some("RustcEncodable"), "Decodable" => Some("RustcDecodable"), diff --git a/src/test/compile-fail/single-derive-attr-2.rs b/src/test/compile-fail/single-derive-attr-2.rs new file mode 100644 index 0000000000000..6fb3118df5305 --- /dev/null +++ b/src/test/compile-fail/single-derive-attr-2.rs @@ -0,0 +1,21 @@ +// 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. + +macro_rules! expand_to_unstable { + () => { + #[derive_Clone] + //~^ ERROR attributes of the form `#[derive_*]` are reserved + struct Test; + } +} + +expand_to_unstable!(); + +pub fn main() {} diff --git a/src/test/run-pass/single-derive-attr-allow-unstable.rs b/src/test/run-pass/single-derive-attr-allow-unstable.rs new file mode 100644 index 0000000000000..6b2a221503093 --- /dev/null +++ b/src/test/run-pass/single-derive-attr-allow-unstable.rs @@ -0,0 +1,30 @@ +// Copyright 2016 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. + +// ignore-pretty : (#23623) problems when ending with // comments + +#![feature(allow_internal_unstable)] + +#[allow_internal_unstable] +macro_rules! expand_to_unstable { + () => { + // FIXME(eddyb) #[allow_internal_unstable] + // doesn't actually work for some reason. + + #[derive(Clone)] //#[derive_Clone] + struct Test; + } +} + +expand_to_unstable!(); + +pub fn main() { + Test.clone(); +}