]> git.lizzy.rs Git - rust.git/commitdiff
Add #[allow_internal_unstable] to track stability for macros better.
authorHuon Wilson <dbau.pp+github@gmail.com>
Sun, 1 Mar 2015 03:09:28 +0000 (14:09 +1100)
committerHuon Wilson <dbau.pp+github@gmail.com>
Thu, 5 Mar 2015 13:18:28 +0000 (00:18 +1100)
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.

E.g.

    #[allow_internal_unstable]
    macro_rules! foo {
        ($e: expr) => {{
            $e;
            unstable(); // no warning
            only_called_by_foo!();
        }}
    }

    macro_rules! only_called_by_foo {
        () => { unstable() } // warning
    }

    foo!(unstable()) // warning

The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).

In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.

The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.

21 files changed:
src/doc/reference.md
src/libcore/num/mod.rs
src/librustc/metadata/creader.rs
src/librustc/metadata/macro_import.rs
src/librustc/middle/stability.rs
src/librustc/plugin/registry.rs
src/libsyntax/ast.rs
src/libsyntax/codemap.rs
src/libsyntax/ext/asm.rs
src/libsyntax/ext/base.rs
src/libsyntax/ext/deriving/generic/mod.rs
src/libsyntax/ext/expand.rs
src/libsyntax/ext/tt/macro_rules.rs
src/libsyntax/feature_gate.rs
src/libsyntax/test.rs
src/test/auxiliary/internal_unstable.rs [new file with mode: 0644]
src/test/auxiliary/plugin_args.rs
src/test/compile-fail/feature-gate-allow-internal-unstable-nested-macro.rs [new file with mode: 0644]
src/test/compile-fail/feature-gate-allow-internal-unstable.rs [new file with mode: 0644]
src/test/compile-fail/internal-unstable-noallow.rs [new file with mode: 0644]
src/test/compile-fail/internal-unstable.rs [new file with mode: 0755]

index fa4986816aa1ca5f044b2eda87830f32b4046575..95ccc71532a34f740a413da5b081a5f6a41ed95a 100644 (file)
@@ -2555,6 +2555,14 @@ The currently implemented features of the reference compiler are:
                             types, e.g. as the return type of a public function.
                             This capability may be removed in the future.
 
+* `allow_internal_unstable` - Allows `macro_rules!` macros to be tagged with the
+                              `#[allow_internal_unstable]` attribute, designed
+                              to allow `std` macros to call
+                              `#[unstable]`/feature-gated functionality
+                              internally without imposing on callers
+                              (i.e. making them behave like function calls in
+                              terms of encapsulation).
+
 If a feature is promoted to a language feature, then all existing programs will
 start to receive compilation warnings about #[feature] directives which enabled
 the new feature (because the directive is no longer necessary). However, if a
index 92cdd84160b7ac1672855c2ce8b159c662829610..76edec80896bb400c4a3d154960b1ed5c99cbd92 100644 (file)
@@ -1238,6 +1238,7 @@ pub fn from_f64<A: FromPrimitive>(n: f64) -> Option<A> {
 
 macro_rules! impl_from_primitive {
     ($T:ty, $to_ty:ident) => (
+        #[allow(deprecated)]
         impl FromPrimitive for $T {
             #[inline] fn from_int(n: int) -> Option<$T> { n.$to_ty() }
             #[inline] fn from_i8(n: i8) -> Option<$T> { n.$to_ty() }
@@ -1299,6 +1300,7 @@ macro_rules! impl_num_cast {
     ($T:ty, $conv:ident) => (
         impl NumCast for $T {
             #[inline]
+            #[allow(deprecated)]
             fn from<N: ToPrimitive>(n: N) -> Option<$T> {
                 // `$conv` could be generated using `concat_idents!`, but that
                 // macro seems to be broken at the moment
index a19ccceb0571538ce22375c6d33b219853816e75..515e85410d9fd031767da5a65446b7217daf4e3a 100644 (file)
@@ -542,6 +542,7 @@ pub fn read_exported_macros(&mut self, krate: &ast::Item) -> Vec<ast::MacroDef>
                     // overridden in plugin/load.rs
                     export: false,
                     use_locally: false,
+                    allow_internal_unstable: false,
 
                     body: body,
                 });
index d25dc4f58a5dfa6181472a12ff73c75237c69155..336ebb55e0c42bf9ad8ae50e44c2125bf9833324 100644 (file)
@@ -166,6 +166,9 @@ fn load_macros<'b>(&mut self,
                 Some(sel) => sel.contains_key(&name),
             };
             def.export = reexport.contains_key(&name);
+            def.allow_internal_unstable = attr::contains_name(&def.attrs,
+                                                              "allow_internal_unstable");
+            debug!("load_macros: loaded: {:?}", def);
             self.macros.push(def);
         }
 
index ddac6cc75143ba85ab176ab3a5d89b013f2884c0..3599ba5a0f779a4c2eb2f5ff0bd5a604ab65f017 100644 (file)
@@ -362,8 +362,6 @@ pub fn check_item(tcx: &ty::ctxt, item: &ast::Item, warn_about_defns: bool,
 /// Helper for discovering nodes to check for stability
 pub fn check_expr(tcx: &ty::ctxt, e: &ast::Expr,
                   cb: &mut FnMut(ast::DefId, Span, &Option<Stability>)) {
-    if is_internal(tcx, e.span) { return; }
-
     let span;
     let id = match e.node {
         ast::ExprMethodCall(i, _, _) => {
@@ -527,12 +525,13 @@ pub fn check_pat(tcx: &ty::ctxt, pat: &ast::Pat,
 fn maybe_do_stability_check(tcx: &ty::ctxt, id: ast::DefId, span: Span,
                             cb: &mut FnMut(ast::DefId, Span, &Option<Stability>)) {
     if !is_staged_api(tcx, id) { return  }
+    if is_internal(tcx, span) { return }
     let ref stability = lookup(tcx, id);
     cb(id, span, stability);
 }
 
 fn is_internal(tcx: &ty::ctxt, span: Span) -> bool {
-    tcx.sess.codemap().span_is_internal(span)
+    tcx.sess.codemap().span_allows_unstable(span)
 }
 
 fn is_staged_api(tcx: &ty::ctxt, id: DefId) -> bool {
index 6f98b79e782c0bcd7841427fd7a0bd795fcb9e01..78f7b3b91ddf74d5c9935466183229933fd87dda 100644 (file)
@@ -81,8 +81,12 @@ pub fn args<'b>(&'b self) -> &'b Vec<P<ast::MetaItem>> {
     /// This is the most general hook into `libsyntax`'s expansion behavior.
     pub fn register_syntax_extension(&mut self, name: ast::Name, extension: SyntaxExtension) {
         self.syntax_exts.push((name, match extension {
-            NormalTT(ext, _) => NormalTT(ext, Some(self.krate_span)),
-            IdentTT(ext, _) => IdentTT(ext, Some(self.krate_span)),
+            NormalTT(ext, _, allow_internal_unstable) => {
+                NormalTT(ext, Some(self.krate_span), allow_internal_unstable)
+            }
+            IdentTT(ext, _, allow_internal_unstable) => {
+                IdentTT(ext, Some(self.krate_span), allow_internal_unstable)
+            }
             Decorator(ext) => Decorator(ext),
             Modifier(ext) => Modifier(ext),
             MultiModifier(ext) => MultiModifier(ext),
@@ -99,7 +103,8 @@ pub fn register_syntax_extension(&mut self, name: ast::Name, extension: SyntaxEx
     /// It builds for you a `NormalTT` that calls `expander`,
     /// and also takes care of interning the macro's name.
     pub fn register_macro(&mut self, name: &str, expander: MacroExpanderFn) {
-        self.register_syntax_extension(token::intern(name), NormalTT(Box::new(expander), None));
+        self.register_syntax_extension(token::intern(name),
+                                       NormalTT(Box::new(expander), None, false));
     }
 
     /// Register a compiler lint pass.
index 05348ee77e81f24d7f6c3cd45fb01d54ee2b4d1f..b41eb05ded695041108291b4d5b63935bb6c96d7 100644 (file)
@@ -1743,6 +1743,7 @@ pub struct MacroDef {
     pub imported_from: Option<Ident>,
     pub export: bool,
     pub use_locally: bool,
+    pub allow_internal_unstable: bool,
     pub body: Vec<TokenTree>,
 }
 
index 162da2ac54c12ee111e380302ce5b430a60332a9..44df8a6d3daec74139a3b91e3aa234ebf4da2a1d 100644 (file)
@@ -243,6 +243,10 @@ pub struct NameAndSpan {
     pub name: String,
     /// The format with which the macro was invoked.
     pub format: MacroFormat,
+    /// Whether the macro is allowed to use #[unstable]/feature-gated
+    /// features internally without forcing the whole crate to opt-in
+    /// to them.
+    pub allow_internal_unstable: bool,
     /// The span of the macro definition itself. The macro may not
     /// have a sensible definition span (e.g. something defined
     /// completely inside libsyntax) in which case this is None.
@@ -830,41 +834,43 @@ pub fn with_expn_info<T, F>(&self, id: ExpnId, f: F) -> T where
         }
     }
 
-    /// Check if a span is "internal" to a macro. This means that it is entirely generated by a
-    /// macro expansion and contains no code that was passed in as an argument.
-    pub fn span_is_internal(&self, span: Span) -> bool {
-        // first, check if the given expression was generated by a macro or not
-        // we need to go back the expn_info tree to check only the arguments
-        // of the initial macro call, not the nested ones.
-        let mut is_internal = false;
-        let mut expnid = span.expn_id;
-        while self.with_expn_info(expnid, |expninfo| {
-            match expninfo {
-                Some(ref info) => {
-                    // save the parent expn_id for next loop iteration
-                    expnid = info.call_site.expn_id;
-                    if info.callee.name == "format_args" {
-                        // This is a hack because the format_args builtin calls unstable APIs.
-                        // I spent like 6 hours trying to solve this more generally but am stupid.
-                        is_internal = true;
-                        false
-                    } else if info.callee.span.is_none() {
-                        // it's a compiler built-in, we *really* don't want to mess with it
-                        // so we skip it, unless it was called by a regular macro, in which case
-                        // we will handle the caller macro next turn
-                        is_internal = true;
-                        true // continue looping
+    /// Check if a span is "internal" to a macro in which #[unstable]
+    /// items can be used (that is, a macro marked with
+    /// `#[allow_internal_unstable]`).
+    pub fn span_allows_unstable(&self, span: Span) -> bool {
+        debug!("span_allows_unstable(span = {:?})", span);
+        let mut allows_unstable = false;
+        let mut expn_id = span.expn_id;
+        loop {
+            let quit = self.with_expn_info(expn_id, |expninfo| {
+                debug!("span_allows_unstable: expninfo = {:?}", expninfo);
+                expninfo.map_or(/* hit the top level */ true, |info| {
+
+                    let span_comes_from_this_expansion =
+                        info.callee.span.map_or(span == info.call_site, |mac_span| {
+                            mac_span.lo <= span.lo && span.hi < mac_span.hi
+                        });
+
+                    debug!("span_allows_unstable: from this expansion? {}, allows unstable? {}",
+                           span_comes_from_this_expansion,
+                           info.callee.allow_internal_unstable);
+                    if span_comes_from_this_expansion {
+                        allows_unstable = info.callee.allow_internal_unstable;
+                        // we've found the right place, stop looking
+                        true
                     } else {
-                        // was this expression from the current macro arguments ?
-                        is_internal = !( span.lo > info.call_site.lo &&
-                                         span.hi < info.call_site.hi );
-                        true // continue looping
+                        // not the right place, keep looking
+                        expn_id = info.call_site.expn_id;
+                        false
                     }
-                },
-                _ => false // stop looping
+                })
+            });
+            if quit {
+                break
             }
-        }) { /* empty while loop body */ }
-        return is_internal;
+        }
+        debug!("span_allows_unstable? {}", allows_unstable);
+        allows_unstable
     }
 }
 
index e58a3de41c05c2d9270fe42f5c6345cb55ba3dc9..d256698b88598cd5cd764379514d23aaf3056785 100644 (file)
@@ -214,6 +214,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
             name: "asm".to_string(),
             format: codemap::MacroBang,
             span: None,
+            allow_internal_unstable: false,
         },
     });
 
index f6bdd693cfa219fea4bd23010f413a502f5bb0e0..80b86507f6ea7763c54eb436d007e7dc254b9c92 100644 (file)
@@ -430,12 +430,15 @@ pub enum SyntaxExtension {
     /// A normal, function-like syntax extension.
     ///
     /// `bytes!` is a `NormalTT`.
-    NormalTT(Box<TTMacroExpander + 'static>, Option<Span>),
+    ///
+    /// The `bool` dictates whether the contents of the macro can
+    /// directly use `#[unstable]` things (true == yes).
+    NormalTT(Box<TTMacroExpander + 'static>, Option<Span>, bool),
 
     /// A function-like syntax extension that has an extra ident before
     /// the block.
     ///
-    IdentTT(Box<IdentMacroExpander + 'static>, Option<Span>),
+    IdentTT(Box<IdentMacroExpander + 'static>, Option<Span>, bool),
 
     /// Represents `macro_rules!` itself.
     MacroRulesTT,
@@ -465,14 +468,14 @@ 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::new(f), None)
+        NormalTT(Box::new(f), None, false)
     }
 
     let mut syntax_expanders = SyntaxEnv::new();
     syntax_expanders.insert(intern("macro_rules"), MacroRulesTT);
     syntax_expanders.insert(intern("format_args"),
-                            builtin_normal_expander(
-                                ext::format::expand_format_args));
+                            // format_args uses `unstable` things internally.
+                            NormalTT(Box::new(ext::format::expand_format_args), None, true));
     syntax_expanders.insert(intern("env"),
                             builtin_normal_expander(
                                     ext::env::expand_env));
index a36d3a155b8d86de2db26d46d15627bfba53e99d..9cd965a8138e083d475da73ced390cc630b3f347 100644 (file)
@@ -1216,7 +1216,8 @@ fn set_expn_info(&self,
             callee: codemap::NameAndSpan {
                 name: format!("derive({})", trait_name),
                 format: codemap::MacroAttribute,
-                span: Some(self.span)
+                span: Some(self.span),
+                allow_internal_unstable: false,
             }
         });
         to_set
index bea57ae14e4af27c91d8600e7016525bdda30456..8953689a0c2824881254c94468369fb795afb22d 100644 (file)
@@ -22,7 +22,7 @@
 use codemap;
 use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
 use ext::base::*;
-use feature_gate::{Features};
+use feature_gate::{self, Features};
 use fold;
 use fold::*;
 use parse;
@@ -395,13 +395,14 @@ fn expand_mac_invoc<T, F, G>(mac: ast::Mac, span: codemap::Span,
                     None
                 }
                 Some(rc) => match *rc {
-                    NormalTT(ref expandfun, exp_span) => {
+                    NormalTT(ref expandfun, exp_span, allow_internal_unstable) => {
                         fld.cx.bt_push(ExpnInfo {
                                 call_site: span,
                                 callee: NameAndSpan {
                                     name: extnamestr.to_string(),
                                     format: MacroBang,
                                     span: exp_span,
+                                    allow_internal_unstable: allow_internal_unstable,
                                 },
                             });
                         let fm = fresh_mark();
@@ -530,6 +531,9 @@ fn expand_item_modifiers(mut it: P<ast::Item>, fld: &mut MacroExpander)
                             name: mname.to_string(),
                             format: MacroAttribute,
                             span: None,
+                            // attributes can do whatever they like,
+                            // for now
+                            allow_internal_unstable: true,
                         }
                     });
                     it = mac.expand(fld.cx, attr.span, &*attr.node.value, it);
@@ -614,7 +618,7 @@ pub fn expand_item_mac(it: P<ast::Item>,
             }
 
             Some(rc) => match *rc {
-                NormalTT(ref expander, span) => {
+                NormalTT(ref expander, span, allow_internal_unstable) => {
                     if it.ident.name != parse::token::special_idents::invalid.name {
                         fld.cx
                             .span_err(path_span,
@@ -628,14 +632,15 @@ pub fn expand_item_mac(it: P<ast::Item>,
                         callee: NameAndSpan {
                             name: extnamestr.to_string(),
                             format: MacroBang,
-                            span: span
+                            span: span,
+                            allow_internal_unstable: allow_internal_unstable,
                         }
                     });
                     // mark before expansion:
                     let marked_before = mark_tts(&tts[..], fm);
                     expander.expand(fld.cx, it.span, &marked_before[..])
                 }
-                IdentTT(ref expander, span) => {
+                IdentTT(ref expander, span, allow_internal_unstable) => {
                     if it.ident.name == parse::token::special_idents::invalid.name {
                         fld.cx.span_err(path_span,
                                         &format!("macro {}! expects an ident argument",
@@ -647,7 +652,8 @@ pub fn expand_item_mac(it: P<ast::Item>,
                         callee: NameAndSpan {
                             name: extnamestr.to_string(),
                             format: MacroBang,
-                            span: span
+                            span: span,
+                            allow_internal_unstable: allow_internal_unstable,
                         }
                     });
                     // mark before expansion:
@@ -661,16 +667,35 @@ pub fn expand_item_mac(it: P<ast::Item>,
                                         );
                         return SmallVector::zero();
                     }
+
                     fld.cx.bt_push(ExpnInfo {
                         call_site: it.span,
                         callee: NameAndSpan {
                             name: extnamestr.to_string(),
                             format: MacroBang,
                             span: None,
+                            // `macro_rules!` doesn't directly allow
+                            // unstable (this is orthogonal to whether
+                            // the macro it creates allows it)
+                            allow_internal_unstable: false,
                         }
                     });
                     // DON'T mark before expansion.
 
+                    let allow_internal_unstable = attr::contains_name(&it.attrs,
+                                                                      "allow_internal_unstable");
+
+                    // ensure any #[allow_internal_unstable]s are
+                    // detected (including nested macro definitions
+                    // etc.)
+                    if allow_internal_unstable && !fld.cx.ecfg.enable_allow_internal_unstable() {
+                        feature_gate::emit_feature_err(
+                            &fld.cx.parse_sess.span_diagnostic,
+                            "allow_internal_unstable",
+                            it.span,
+                            feature_gate::EXPLAIN_ALLOW_INTERNAL_UNSTABLE)
+                    }
+
                     let def = ast::MacroDef {
                         ident: it.ident,
                         attrs: it.attrs.clone(),
@@ -679,6 +704,7 @@ pub fn expand_item_mac(it: P<ast::Item>,
                         imported_from: None,
                         export: attr::contains_name(&it.attrs, "macro_export"),
                         use_locally: true,
+                        allow_internal_unstable: allow_internal_unstable,
                         body: tts,
                     };
                     fld.cx.insert_macro(def);
@@ -959,13 +985,14 @@ fn expand_pat(p: P<ast::Pat>, fld: &mut MacroExpander) -> P<ast::Pat> {
             }
 
             Some(rc) => match *rc {
-                NormalTT(ref expander, tt_span) => {
+                NormalTT(ref expander, tt_span, allow_internal_unstable) => {
                     fld.cx.bt_push(ExpnInfo {
                         call_site: span,
                         callee: NameAndSpan {
                             name: extnamestr.to_string(),
                             format: MacroBang,
-                            span: tt_span
+                            span: tt_span,
+                            allow_internal_unstable: allow_internal_unstable,
                         }
                     });
 
@@ -1094,7 +1121,10 @@ fn expand_annotatable(a: Annotatable,
                         callee: NameAndSpan {
                             name: mname.to_string(),
                             format: MacroAttribute,
-                            span: None
+                            span: None,
+                            // attributes can do whatever they like,
+                            // for now.
+                            allow_internal_unstable: true,
                         }
                     });
 
@@ -1244,6 +1274,9 @@ fn expand_item_multi_modifier(mut it: Annotatable,
                             name: mname.to_string(),
                             format: MacroAttribute,
                             span: None,
+                            // attributes can do whatever they like,
+                            // for now
+                            allow_internal_unstable: true,
                         }
                     });
                     it = mac.expand(fld.cx, attr.span, &*attr.node.value, it);
@@ -1457,6 +1490,13 @@ pub fn enable_trace_macros(&self) -> bool {
             _ => false,
         }
     }
+
+    pub fn enable_allow_internal_unstable(&self) -> bool {
+        match self.features {
+            Some(&Features { allow_internal_unstable: true, .. }) => true,
+            _ => false
+        }
+    }
 }
 
 pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess,
index d15eb3df6394bb089385369a5f6e71b8cb6e41d5..644c6cd7e2833f60c9ff8f4350d306a35eec2355 100644 (file)
@@ -267,7 +267,7 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
         rhses: rhses,
     };
 
-    NormalTT(exp, Some(def.span))
+    NormalTT(exp, Some(def.span), def.allow_internal_unstable)
 }
 
 fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &NamedMatch, sp: Span) {
index 18d3f85f4b58ed80d11fb26c53b81f4d72a234ee..3b2d86cffe22c8841398718f6d783964bff1535e 100644 (file)
 
     // Allows the use of `static_assert`
     ("static_assert", "1.0.0", Active),
+
+    // Allows the use of #[allow_internal_unstable]. This is an
+    // attribute on macro_rules! and can't use the attribute handling
+    // below (it has to be checked before expansion possibly makes
+    // macros disappear).
+    ("allow_internal_unstable", "1.0.0", Active),
 ];
 // (changing above list without updating src/doc/reference.md makes @cmr sad)
 
@@ -308,6 +314,7 @@ pub struct Features {
     pub allow_log_syntax: bool,
     pub allow_concat_idents: bool,
     pub allow_trace_macros: bool,
+    pub allow_internal_unstable: bool,
     pub old_orphan_check: bool,
     pub simd_ffi: bool,
     pub unmarked_api: bool,
@@ -328,6 +335,7 @@ pub fn new() -> Features {
             allow_log_syntax: false,
             allow_concat_idents: false,
             allow_trace_macros: false,
+            allow_internal_unstable: false,
             old_orphan_check: false,
             simd_ffi: false,
             unmarked_api: false,
@@ -387,6 +395,8 @@ pub fn emit_feature_warn(diag: &SpanHandler, feature: &str, span: Span, explain:
 
 pub const EXPLAIN_TRACE_MACROS: &'static str =
     "`trace_macros` is not stable enough for use and is subject to change";
+pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &'static str =
+    "allow_internal_unstable side-steps feature gating and stability checks";
 
 struct MacroVisitor<'a> {
     context: &'a Context<'a>
@@ -421,6 +431,13 @@ fn visit_mac(&mut self, mac: &ast::Mac) {
             self.context.gate_feature("concat_idents", path.span, EXPLAIN_CONCAT_IDENTS);
         }
     }
+
+    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)
+        }
+    }
 }
 
 struct PostExpansionVisitor<'a> {
@@ -429,7 +446,7 @@ struct PostExpansionVisitor<'a> {
 
 impl<'a> PostExpansionVisitor<'a> {
     fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
-        if !self.context.cm.span_is_internal(span) {
+        if !self.context.cm.span_allows_unstable(span) {
             self.context.gate_feature(feature, span, explain)
         }
     }
@@ -754,6 +771,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C
         allow_log_syntax: cx.has_feature("log_syntax"),
         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"),
         old_orphan_check: cx.has_feature("old_orphan_check"),
         simd_ffi: cx.has_feature("simd_ffi"),
         unmarked_api: cx.has_feature("unmarked_api"),
index 5bada41badfd8077b4980f4bb79deb6c6fc4066a..d889453a5f977127708805124cf273979392a68c 100644 (file)
@@ -256,7 +256,8 @@ fn generate_test_harness(sess: &ParseSess,
         callee: NameAndSpan {
             name: "test".to_string(),
             format: MacroAttribute,
-            span: None
+            span: None,
+            allow_internal_unstable: false,
         }
     });
 
@@ -288,7 +289,8 @@ fn ignored_span(cx: &TestCtxt, sp: Span) -> Span {
         callee: NameAndSpan {
             name: "test".to_string(),
             format: MacroAttribute,
-            span: None
+            span: None,
+            allow_internal_unstable: true,
         }
     };
     let expn_id = cx.sess.span_diagnostic.cm.record_expansion(info);
diff --git a/src/test/auxiliary/internal_unstable.rs b/src/test/auxiliary/internal_unstable.rs
new file mode 100644 (file)
index 0000000..3d59b8e
--- /dev/null
@@ -0,0 +1,60 @@
+// 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.
+
+#![feature(staged_api, allow_internal_unstable)]
+#![staged_api]
+#![stable(feature = "stable", since = "1.0.0")]
+
+#[unstable(feature = "function")]
+pub fn unstable() {}
+
+
+#[stable(feature = "stable", since = "1.0.0")]
+pub struct Foo {
+    #[unstable(feature = "struct_field")]
+    pub x: u8
+}
+
+#[allow_internal_unstable]
+#[macro_export]
+macro_rules! call_unstable_allow {
+    () => { $crate::unstable() }
+}
+
+#[allow_internal_unstable]
+#[macro_export]
+macro_rules! construct_unstable_allow {
+    ($e: expr) => {
+        $crate::Foo { x: $e }
+    }
+}
+
+#[allow_internal_unstable]
+#[macro_export]
+macro_rules! pass_through_allow {
+    ($e: expr) => { $e }
+}
+
+#[macro_export]
+macro_rules! call_unstable_noallow {
+    () => { $crate::unstable() }
+}
+
+#[macro_export]
+macro_rules! construct_unstable_noallow {
+    ($e: expr) => {
+        $crate::Foo { x: $e }
+    }
+}
+
+#[macro_export]
+macro_rules! pass_through_noallow {
+    ($e: expr) => { $e }
+}
index 20c84c4ba5b4a9863d9623e558bb940d9cc7bb98..30b18a3618ffa09174f70bcb64757472a20a5934 100644 (file)
@@ -47,5 +47,5 @@ pub fn plugin_registrar(reg: &mut Registry) {
     let args = reg.args().clone();
     reg.register_syntax_extension(token::intern("plugin_args"),
         // FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
-        NormalTT(Box::new(Expander { args: args, }), None));
+        NormalTT(Box::new(Expander { args: args, }), None, false));
 }
diff --git a/src/test/compile-fail/feature-gate-allow-internal-unstable-nested-macro.rs b/src/test/compile-fail/feature-gate-allow-internal-unstable-nested-macro.rs
new file mode 100644 (file)
index 0000000..c9251c9
--- /dev/null
@@ -0,0 +1,23 @@
+// 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.
+
+macro_rules! bar {
+    () => {
+        // more layers don't help:
+        #[allow_internal_unstable]
+        macro_rules! baz { //~ ERROR allow_internal_unstable side-steps
+            () => {}
+        }
+    }
+}
+
+bar!();
+
+fn main() {}
diff --git a/src/test/compile-fail/feature-gate-allow-internal-unstable.rs b/src/test/compile-fail/feature-gate-allow-internal-unstable.rs
new file mode 100644 (file)
index 0000000..8a2d8dd
--- /dev/null
@@ -0,0 +1,16 @@
+// 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.
+
+#[allow_internal_unstable] //~ ERROR allow_internal_unstable side-steps
+macro_rules! foo {
+    () => {}
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/internal-unstable-noallow.rs b/src/test/compile-fail/internal-unstable-noallow.rs
new file mode 100644 (file)
index 0000000..4e29619
--- /dev/null
@@ -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.
+
+// this has to be separate to internal-unstable.rs because these tests
+// have error messages pointing deep into the internals of the
+// cross-crate macros, and hence need to use error-pattern instead of
+// the // ~ form.
+
+// aux-build:internal_unstable.rs
+// error-pattern:use of unstable library feature 'function'
+// error-pattern:use of unstable library feature 'struct_field'
+// error-pattern:compilation successful
+#![feature(rustc_attrs)]
+
+#[macro_use]
+extern crate internal_unstable;
+
+#[rustc_error]
+fn main() {
+    call_unstable_noallow!();
+
+    construct_unstable_noallow!(0);
+}
diff --git a/src/test/compile-fail/internal-unstable.rs b/src/test/compile-fail/internal-unstable.rs
new file mode 100755 (executable)
index 0000000..8674e8a
--- /dev/null
@@ -0,0 +1,51 @@
+// 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.
+
+// aux-build:internal_unstable.rs
+
+#![feature(rustc_attrs, allow_internal_unstable)]
+
+#[macro_use]
+extern crate internal_unstable;
+
+macro_rules! foo {
+    ($e: expr, $f: expr) => {{
+        $e;
+        $f;
+        internal_unstable::unstable(); //~ WARN use of unstable
+    }}
+}
+
+#[allow_internal_unstable]
+macro_rules! bar {
+    ($e: expr) => {{
+        foo!($e,
+             internal_unstable::unstable());
+        internal_unstable::unstable();
+    }}
+}
+
+#[rustc_error]
+fn main() { //~ ERROR
+    // ok, the instability is contained.
+    call_unstable_allow!();
+    construct_unstable_allow!(0);
+
+    // bad.
+    pass_through_allow!(internal_unstable::unstable()); //~ WARN use of unstable
+
+    pass_through_noallow!(internal_unstable::unstable()); //~ WARN use of unstable
+
+
+
+    println!("{:?}", internal_unstable::unstable()); //~ WARN use of unstable
+
+    bar!(internal_unstable::unstable()); //~ WARN use of unstable
+}