about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorManish Goregaokar <manishsmail@gmail.com>2015-02-17 15:54:25 +0530
committerManish Goregaokar <manishsmail@gmail.com>2015-02-17 17:33:20 +0530
commit020e4e4ad981ce0518bd8e3464ac592717c41143 (patch)
treefeca45da907af4dedc7aa930a8292a5fdf78073a /src
parent0e892283612ea21ce5aeb3be930296623facab36 (diff)
parentdc0797c0c99e7079170d0e09f82fc6f66b721932 (diff)
downloadrust-020e4e4ad981ce0518bd8e3464ac592717c41143.tar.gz
rust-020e4e4ad981ce0518bd8e3464ac592717c41143.zip
Rollup merge of #22383 - pnkfelix:pass-features-along-during-expansion, r=huonw
 Pass features along during expansion

Use the set of passed features to detect uses of feature-gated macros without the corresponding feature enabled.

Fix #22234.

----

Also, the framework this add (passing along a reference to the features in the expansion context) is a necessary precursor for landing a properly feature-gated desugaring-based overloaded-`box` and placement-`in` (#22181).

----

This is fixing a bug, but since there might be code out there that is unknowingly taking advantage of that bug, I feel obligated to mark this as a:

[breaking-change]
Diffstat (limited to 'src')
-rw-r--r--src/librustc_driver/driver.rs3
-rw-r--r--src/libsyntax/ext/asm.rs7
-rw-r--r--src/libsyntax/ext/base.rs9
-rw-r--r--src/libsyntax/ext/concat_idents.rs9
-rw-r--r--src/libsyntax/ext/expand.rs60
-rw-r--r--src/libsyntax/ext/log_syntax.rs8
-rw-r--r--src/libsyntax/ext/trace_macros.rs10
-rw-r--r--src/libsyntax/feature_gate.rs53
-rw-r--r--src/test/compile-fail/asm-gated2.rs15
-rw-r--r--src/test/compile-fail/concat_idents-gate.rs19
-rw-r--r--src/test/compile-fail/concat_idents-gate2.rs17
-rw-r--r--src/test/compile-fail/log-syntax-gate2.rs13
-rw-r--r--src/test/compile-fail/trace_macros-gate.rs30
-rw-r--r--src/test/compile-fail/trace_macros-gate2.rs20
-rw-r--r--src/test/compile-fail/trace_macros-gate3.rs20
15 files changed, 265 insertions, 28 deletions
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index eb1dba7159c..728ff647599 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -471,9 +471,10 @@ pub fn phase_2_configure_and_expand(sess: &Session,
                 new_path.extend(env::split_paths(&_old_path));
                 env::set_var("PATH", &env::join_paths(new_path.iter()).unwrap());
             }
+            let features = sess.features.borrow();
             let cfg = syntax::ext::expand::ExpansionConfig {
                 crate_name: crate_name.to_string(),
-                enable_quotes: sess.features.borrow().quote,
+                features: Some(&features),
                 recursion_limit: sess.recursion_limit.get(),
             };
             let ret = syntax::ext::expand::expand_crate(&sess.parse_sess,
diff --git a/src/libsyntax/ext/asm.rs b/src/libsyntax/ext/asm.rs
index 1ceda2e08dd..d8cba139fb5 100644
--- a/src/libsyntax/ext/asm.rs
+++ b/src/libsyntax/ext/asm.rs
@@ -18,6 +18,7 @@ use codemap;
 use codemap::Span;
 use ext::base;
 use ext::base::*;
+use feature_gate;
 use parse::token::InternedString;
 use parse::token;
 use ptr::P;
@@ -48,6 +49,12 @@ static OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
 
 pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
                        -> Box<base::MacResult+'cx> {
+    if !cx.ecfg.enable_asm() {
+        feature_gate::emit_feature_err(
+            &cx.parse_sess.span_diagnostic, "asm", sp, feature_gate::EXPLAIN_ASM);
+        return DummyResult::expr(sp);
+    }
+
     let mut p = cx.new_parser_from_tts(tts);
     let mut asm = InternedString::new("");
     let mut asm_str_style = None;
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 083039995ee..8800ffd1e9b 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -439,7 +439,8 @@ impl BlockInfo {
 
 /// The base map of methods for expanding syntax extension
 /// AST nodes into full ASTs
-fn initial_syntax_expander_table(ecfg: &expand::ExpansionConfig) -> SyntaxEnv {
+fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
+                                        -> SyntaxEnv {
     // utility function to simplify creating NormalTT syntax extensions
     fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension {
         NormalTT(box f, None)
@@ -470,7 +471,7 @@ fn initial_syntax_expander_table(ecfg: &expand::ExpansionConfig) -> SyntaxEnv {
     syntax_expanders.insert(intern("deriving"),
                             Decorator(box ext::deriving::expand_deprecated_deriving));
 
-    if ecfg.enable_quotes {
+    if ecfg.enable_quotes() {
         // Quasi-quoting expanders
         syntax_expanders.insert(intern("quote_tokens"),
                            builtin_normal_expander(
@@ -541,7 +542,7 @@ pub struct ExtCtxt<'a> {
     pub parse_sess: &'a parse::ParseSess,
     pub cfg: ast::CrateConfig,
     pub backtrace: ExpnId,
-    pub ecfg: expand::ExpansionConfig,
+    pub ecfg: expand::ExpansionConfig<'a>,
     pub use_std: bool,
 
     pub mod_path: Vec<ast::Ident> ,
@@ -554,7 +555,7 @@ pub struct ExtCtxt<'a> {
 
 impl<'a> ExtCtxt<'a> {
     pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig,
-               ecfg: expand::ExpansionConfig) -> ExtCtxt<'a> {
+               ecfg: expand::ExpansionConfig<'a>) -> ExtCtxt<'a> {
         let env = initial_syntax_expander_table(&ecfg);
         ExtCtxt {
             parse_sess: parse_sess,
diff --git a/src/libsyntax/ext/concat_idents.rs b/src/libsyntax/ext/concat_idents.rs
index 364cacd735c..63a8bd9ddf1 100644
--- a/src/libsyntax/ext/concat_idents.rs
+++ b/src/libsyntax/ext/concat_idents.rs
@@ -12,12 +12,21 @@ use ast;
 use codemap::Span;
 use ext::base::*;
 use ext::base;
+use feature_gate;
 use parse::token;
 use parse::token::{str_to_ident};
 use ptr::P;
 
 pub fn expand_syntax_ext<'cx>(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
                               -> Box<base::MacResult+'cx> {
+    if !cx.ecfg.enable_concat_idents() {
+        feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
+                                       "concat_idents",
+                                       sp,
+                                       feature_gate::EXPLAIN_CONCAT_IDENTS);
+        return base::DummyResult::expr(sp);
+    }
+
     let mut res_str = String::new();
     for (i, e) in tts.iter().enumerate() {
         if i & 1 == 1 {
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index fd98f42c2ab..6b7cecee815 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -22,6 +22,7 @@ use attr::AttrMetaMethods;
 use codemap;
 use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
 use ext::base::*;
+use feature_gate::{Features};
 use fold;
 use fold::*;
 use parse;
@@ -1408,28 +1409,63 @@ fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
     }
 }
 
-pub struct ExpansionConfig {
+pub struct ExpansionConfig<'feat> {
     pub crate_name: String,
-    pub enable_quotes: bool,
+    pub features: Option<&'feat Features>,
     pub recursion_limit: usize,
 }
 
-impl ExpansionConfig {
-    pub fn default(crate_name: String) -> ExpansionConfig {
+impl<'feat> ExpansionConfig<'feat> {
+    pub fn default(crate_name: String) -> ExpansionConfig<'static> {
         ExpansionConfig {
             crate_name: crate_name,
-            enable_quotes: false,
+            features: None,
             recursion_limit: 64,
         }
     }
+
+    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 expand_crate(parse_sess: &parse::ParseSess,
-                    cfg: ExpansionConfig,
-                    // these are the macros being imported to this crate:
-                    imported_macros: Vec<ast::MacroDef>,
-                    user_exts: Vec<NamedSyntaxExtension>,
-                    c: Crate) -> Crate {
+pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess,
+                           cfg: ExpansionConfig<'feat>,
+                           // these are the macros being imported to this crate:
+                           imported_macros: Vec<ast::MacroDef>,
+                           user_exts: Vec<NamedSyntaxExtension>,
+                           c: Crate) -> Crate {
     let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
     cx.use_std = std_inject::use_std(&c);
 
@@ -1598,7 +1634,7 @@ mod test {
     // these following tests are quite fragile, in that they don't test what
     // *kind* of failure occurs.
 
-    fn test_ecfg() -> ExpansionConfig {
+    fn test_ecfg() -> ExpansionConfig<'static> {
         ExpansionConfig::default("test".to_string())
     }
 
diff --git a/src/libsyntax/ext/log_syntax.rs b/src/libsyntax/ext/log_syntax.rs
index 30301e3b8cc..8173dd93f74 100644
--- a/src/libsyntax/ext/log_syntax.rs
+++ b/src/libsyntax/ext/log_syntax.rs
@@ -11,12 +11,20 @@
 use ast;
 use codemap;
 use ext::base;
+use feature_gate;
 use print;
 
 pub fn expand_syntax_ext<'cx>(cx: &'cx mut base::ExtCtxt,
                               sp: codemap::Span,
                               tts: &[ast::TokenTree])
                               -> Box<base::MacResult+'cx> {
+    if !cx.ecfg.enable_log_syntax() {
+        feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
+                                       "log_syntax",
+                                       sp,
+                                       feature_gate::EXPLAIN_LOG_SYNTAX);
+        return base::DummyResult::any(sp);
+    }
 
     cx.print_backtrace();
 
diff --git a/src/libsyntax/ext/trace_macros.rs b/src/libsyntax/ext/trace_macros.rs
index 76f7b7b0d7b..3fcc6a8d692 100644
--- a/src/libsyntax/ext/trace_macros.rs
+++ b/src/libsyntax/ext/trace_macros.rs
@@ -12,6 +12,7 @@ use ast;
 use codemap::Span;
 use ext::base::ExtCtxt;
 use ext::base;
+use feature_gate;
 use parse::token::keywords;
 
 
@@ -19,6 +20,15 @@ pub fn expand_trace_macros(cx: &mut ExtCtxt,
                            sp: Span,
                            tt: &[ast::TokenTree])
                            -> Box<base::MacResult+'static> {
+    if !cx.ecfg.enable_trace_macros() {
+        feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
+                                       "trace_macros",
+                                       sp,
+                                       feature_gate::EXPLAIN_TRACE_MACROS);
+        return base::DummyResult::any(sp);
+    }
+
+
     match tt {
         [ast::TtToken(_, ref tok)] if tok.is_keyword(keywords::True) => {
             cx.set_trace_macros(true);
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index f6424167bde..3bebba15a57 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -295,7 +295,11 @@ pub struct Features {
     pub unboxed_closures: bool,
     pub rustc_diagnostic_macros: bool,
     pub visible_private_types: bool,
-    pub quote: bool,
+    pub allow_quote: bool,
+    pub allow_asm: bool,
+    pub allow_log_syntax: bool,
+    pub allow_concat_idents: bool,
+    pub allow_trace_macros: bool,
     pub old_orphan_check: bool,
     pub simd_ffi: bool,
     pub unmarked_api: bool,
@@ -311,7 +315,11 @@ impl Features {
             unboxed_closures: false,
             rustc_diagnostic_macros: false,
             visible_private_types: false,
-            quote: false,
+            allow_quote: false,
+            allow_asm: false,
+            allow_log_syntax: false,
+            allow_concat_idents: false,
+            allow_trace_macros: false,
             old_orphan_check: false,
             simd_ffi: false,
             unmarked_api: false,
@@ -360,6 +368,18 @@ pub fn emit_feature_warn(diag: &SpanHandler, feature: &str, span: Span, explain:
     }
 }
 
+pub const EXPLAIN_ASM: &'static str =
+    "inline assembly is not stable enough for use and is subject to change";
+
+pub const EXPLAIN_LOG_SYNTAX: &'static str =
+    "`log_syntax!` is not stable enough for use and is subject to change";
+
+pub const EXPLAIN_CONCAT_IDENTS: &'static str =
+    "`concat_idents` is not stable enough for use and is subject to change";
+
+pub const EXPLAIN_TRACE_MACROS: &'static str =
+    "`trace_macros` is not stable enough for use and is subject to change";
+
 struct MacroVisitor<'a> {
     context: &'a Context<'a>
 }
@@ -369,24 +389,28 @@ impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
         let ast::MacInvocTT(ref path, _, _) = mac.node;
         let id = path.segments.last().unwrap().identifier;
 
+        // Issue 22234: If you add a new case here, make sure to also
+        // add code to catch the macro during or after expansion.
+        //
+        // We still keep this MacroVisitor (rather than *solely*
+        // relying on catching cases during or after expansion) to
+        // catch uses of these macros within conditionally-compiled
+        // code, e.g. `#[cfg]`-guarded functions.
+
         if id == token::str_to_ident("asm") {
-            self.context.gate_feature("asm", path.span, "inline assembly is not \
-                stable enough for use and is subject to change");
+            self.context.gate_feature("asm", path.span, EXPLAIN_ASM);
         }
 
         else if id == token::str_to_ident("log_syntax") {
-            self.context.gate_feature("log_syntax", path.span, "`log_syntax!` is not \
-                stable enough for use and is subject to change");
+            self.context.gate_feature("log_syntax", path.span, EXPLAIN_LOG_SYNTAX);
         }
 
         else if id == token::str_to_ident("trace_macros") {
-            self.context.gate_feature("trace_macros", path.span, "`trace_macros` is not \
-                stable enough for use and is subject to change");
+            self.context.gate_feature("trace_macros", path.span, EXPLAIN_TRACE_MACROS);
         }
 
         else if id == token::str_to_ident("concat_idents") {
-            self.context.gate_feature("concat_idents", path.span, "`concat_idents` is not \
-                stable enough for use and is subject to change");
+            self.context.gate_feature("concat_idents", path.span, EXPLAIN_CONCAT_IDENTS);
         }
     }
 }
@@ -710,11 +734,18 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C
 
     check(&mut cx, krate);
 
+    // FIXME (pnkfelix): Before adding the 99th entry below, change it
+    // to a single-pass (instead of N calls to `.has_feature`).
+
     Features {
         unboxed_closures: cx.has_feature("unboxed_closures"),
         rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
         visible_private_types: cx.has_feature("visible_private_types"),
-        quote: cx.has_feature("quote"),
+        allow_quote: cx.has_feature("quote"),
+        allow_asm: cx.has_feature("asm"),
+        allow_log_syntax: cx.has_feature("log_syntax"),
+        allow_concat_idents: cx.has_feature("concat_idents"),
+        allow_trace_macros: cx.has_feature("trace_macros"),
         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/compile-fail/asm-gated2.rs b/src/test/compile-fail/asm-gated2.rs
new file mode 100644
index 00000000000..d2ee01109f8
--- /dev/null
+++ b/src/test/compile-fail/asm-gated2.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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    unsafe {
+        println!("{}", asm!("")); //~ ERROR inline assembly is not stable
+    }
+}
diff --git a/src/test/compile-fail/concat_idents-gate.rs b/src/test/compile-fail/concat_idents-gate.rs
new file mode 100644
index 00000000000..f4d97445725
--- /dev/null
+++ b/src/test/compile-fail/concat_idents-gate.rs
@@ -0,0 +1,19 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+const XY_1: i32 = 10;
+
+fn main() {
+    const XY_2: i32 = 20;
+    let a = concat_idents!(X, Y_1); //~ ERROR `concat_idents` is not stable
+    let b = concat_idents!(X, Y_2); //~ ERROR `concat_idents` is not stable
+    assert_eq!(a, 10);
+    assert_eq!(b, 20);
+}
diff --git a/src/test/compile-fail/concat_idents-gate2.rs b/src/test/compile-fail/concat_idents-gate2.rs
new file mode 100644
index 00000000000..d8f8f803edc
--- /dev/null
+++ b/src/test/compile-fail/concat_idents-gate2.rs
@@ -0,0 +1,17 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+const XY_1: i32 = 10;
+
+fn main() {
+    const XY_2: i32 = 20;
+    assert_eq!(10, concat_idents!(X, Y_1)); //~ ERROR `concat_idents` is not stable
+    assert_eq!(20, concat_idents!(X, Y_2)); //~ ERROR `concat_idents` is not stable
+}
diff --git a/src/test/compile-fail/log-syntax-gate2.rs b/src/test/compile-fail/log-syntax-gate2.rs
new file mode 100644
index 00000000000..bb19e97ab0f
--- /dev/null
+++ b/src/test/compile-fail/log-syntax-gate2.rs
@@ -0,0 +1,13 @@
+// Copyright 2012 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    println!("{}", log_syntax!()); //~ ERROR `log_syntax!` is not stable
+}
diff --git a/src/test/compile-fail/trace_macros-gate.rs b/src/test/compile-fail/trace_macros-gate.rs
new file mode 100644
index 00000000000..6473bcece91
--- /dev/null
+++ b/src/test/compile-fail/trace_macros-gate.rs
@@ -0,0 +1,30 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that the trace_macros feature gate is on.
+
+fn main() {
+    trace_macros!(); //~ ERROR `trace_macros` is not stable
+    trace_macros!(1); //~ ERROR `trace_macros` is not stable
+    trace_macros!(ident); //~ ERROR `trace_macros` is not stable
+    trace_macros!(for); //~ ERROR `trace_macros` is not stable
+    trace_macros!(true,); //~ ERROR `trace_macros` is not stable
+    trace_macros!(false 1); //~ ERROR `trace_macros` is not stable
+
+    // Errors are signalled early for the above, before expansion.
+    // See trace_macros-gate2 and trace_macros-gate3. for examples
+    // of the below being caught.
+
+    macro_rules! expando {
+        ($x: ident) => { trace_macros!($x) }
+    }
+
+    expando!(true);
+}
diff --git a/src/test/compile-fail/trace_macros-gate2.rs b/src/test/compile-fail/trace_macros-gate2.rs
new file mode 100644
index 00000000000..71cc45e132d
--- /dev/null
+++ b/src/test/compile-fail/trace_macros-gate2.rs
@@ -0,0 +1,20 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that the trace_macros feature gate is on.
+
+fn main() {
+    // (Infrastructure does not attempt to detect uses in macro definitions.)
+    macro_rules! expando {
+        ($x: ident) => { trace_macros!($x) }
+    }
+
+    expando!(true); //~ ERROR `trace_macros` is not stable
+}
diff --git a/src/test/compile-fail/trace_macros-gate3.rs b/src/test/compile-fail/trace_macros-gate3.rs
new file mode 100644
index 00000000000..66d03cf9d80
--- /dev/null
+++ b/src/test/compile-fail/trace_macros-gate3.rs
@@ -0,0 +1,20 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that the trace_macros feature gate is on.
+
+pub fn main() {
+    println!("arg: {}", trace_macros!()); //~ ERROR `trace_macros` is not stable
+    println!("arg: {}", trace_macros!(1)); //~ ERROR `trace_macros` is not stable
+    println!("arg: {}", trace_macros!(ident)); //~ ERROR `trace_macros` is not stable
+    println!("arg: {}", trace_macros!(for)); //~ ERROR `trace_macros` is not stable
+    println!("arg: {}", trace_macros!(true,)); //~ ERROR `trace_macros` is not stable
+    println!("arg: {}", trace_macros!(false 1)); //~ ERROR `trace_macros` is not stable
+}