about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAustin Bonander <austin.bonander@gmail.com>2018-02-24 19:11:06 -0800
committerAustin Bonander <austin.bonander@gmail.com>2018-03-07 16:52:28 -0800
commit69035f20b92870a7ad5dbc22c65aee971d8f8698 (patch)
treea9cf77112ebc776b84824a270c8407b739b311d9
parent4a7206323b70f6b146e54ae41652efbb61112024 (diff)
downloadrust-69035f20b92870a7ad5dbc22c65aee971d8f8698.tar.gz
rust-69035f20b92870a7ad5dbc22c65aee971d8f8698.zip
check stability of macro invocations
-rw-r--r--src/librustc_plugin/registry.rs7
-rw-r--r--src/libsyntax/ext/base.rs4
-rw-r--r--src/libsyntax/ext/expand.rs56
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs13
-rw-r--r--src/libsyntax_ext/lib.rs2
-rw-r--r--src/test/compile-fail/auxiliary/unstable-macros.rs16
-rw-r--r--src/test/compile-fail/macro-stability.rs22
-rw-r--r--src/test/run-pass-fulldeps/auxiliary/plugin_args.rs1
-rw-r--r--src/test/run-pass/auxiliary/unstable-macros.rs16
-rw-r--r--src/test/run-pass/macro-stability.rs23
10 files changed, 141 insertions, 19 deletions
diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs
index 3f74093241d..ebfd8785a0a 100644
--- a/src/librustc_plugin/registry.rs
+++ b/src/librustc_plugin/registry.rs
@@ -106,14 +106,16 @@ impl<'a> Registry<'a> {
                 expander,
                 def_info: _,
                 allow_internal_unstable,
-                allow_internal_unsafe
+                allow_internal_unsafe,
+                unstable_feature
             } => {
                 let nid = ast::CRATE_NODE_ID;
                 NormalTT {
                     expander,
                     def_info: Some((nid, self.krate_span)),
                     allow_internal_unstable,
-                    allow_internal_unsafe
+                    allow_internal_unsafe,
+                    unstable_feature
                 }
             }
             IdentTT(ext, _, allow_internal_unstable) => {
@@ -149,6 +151,7 @@ impl<'a> Registry<'a> {
             def_info: None,
             allow_internal_unstable: false,
             allow_internal_unsafe: false,
+            unstable_feature: None,
         });
     }
 
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 7b333270d04..23c42972912 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -555,6 +555,8 @@ pub enum SyntaxExtension {
         /// Whether the contents of the macro can use `unsafe`
         /// without triggering the `unsafe_code` lint.
         allow_internal_unsafe: bool,
+        /// The macro's feature name if it is unstable, and the stability feature
+        unstable_feature: Option<(Symbol, u32)>,
     },
 
     /// A function-like syntax extension that has an extra ident before
@@ -670,6 +672,7 @@ pub struct ExpansionData {
     pub depth: usize,
     pub module: Rc<ModuleData>,
     pub directory_ownership: DirectoryOwnership,
+    pub crate_span: Option<Span>,
 }
 
 /// One of these is made during expansion and incrementally updated as we go;
@@ -701,6 +704,7 @@ impl<'a> ExtCtxt<'a> {
                 depth: 0,
                 module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }),
                 directory_ownership: DirectoryOwnership::Owned { relative: None },
+                crate_span: None,
             },
             expansions: HashMap::new(),
         }
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 377f47a3ea5..2cca37a3c93 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -18,7 +18,7 @@ use ext::base::*;
 use ext::derive::{add_derived_markers, collect_derives};
 use ext::hygiene::{Mark, SyntaxContext};
 use ext::placeholders::{placeholder, PlaceholderExpander};
-use feature_gate::{self, Features, is_builtin_attr};
+use feature_gate::{self, Features, GateIssue, is_builtin_attr, emit_feature_err};
 use fold;
 use fold::*;
 use parse::{DirectoryOwnership, PResult};
@@ -229,6 +229,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         module.directory.pop();
         self.cx.root_path = module.directory.clone();
         self.cx.current_expansion.module = Rc::new(module);
+        self.cx.current_expansion.crate_span = Some(krate.span);
 
         let orig_mod_span = krate.module.inner;
 
@@ -531,11 +532,36 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         let path = &mac.node.path;
 
         let ident = ident.unwrap_or_else(|| keywords::Invalid.ident());
-        let validate_and_set_expn_info = |def_site_span,
+        let validate_and_set_expn_info = |this: &mut Self, // arg instead of capture
+                                          def_site_span: Option<Span>,
                                           allow_internal_unstable,
-                                          allow_internal_unsafe| {
+                                          allow_internal_unsafe,
+                                          // can't infer this type
+                                          unstable_feature: Option<(Symbol, u32)>| {
+
+            // feature-gate the macro invocation
+            if let Some((feature, issue)) = unstable_feature {
+                let crate_span = this.cx.current_expansion.crate_span.unwrap();
+                // don't stability-check macros in the same crate
+                // (the only time this is null is for syntax extensions registered as macros)
+                if def_site_span.map_or(false, |def_span| !crate_span.contains(def_span))
+                    && !span.allows_unstable() && this.cx.ecfg.features.map_or(true, |feats| {
+                    // macro features will count as lib features
+                    !feats.declared_lib_features.iter().any(|&(feat, _)| feat == feature)
+                }) {
+                    let explain = format!("macro {}! is unstable", path);
+                    emit_feature_err(this.cx.parse_sess, &*feature.as_str(), span,
+                                     GateIssue::Library(Some(issue)), &explain);
+                    this.cx.trace_macros_diag();
+                    return Err(kind.dummy(span));
+                }
+            }
+
             if ident.name != keywords::Invalid.name() {
-                return Err(format!("macro {}! expects no ident argument, given '{}'", path, ident));
+                let msg = format!("macro {}! expects no ident argument, given '{}'", path, ident);
+                this.cx.span_err(path.span, &msg);
+                this.cx.trace_macros_diag();
+                return Err(kind.dummy(span));
             }
             mark.set_expn_info(ExpnInfo {
                 call_site: span,
@@ -551,11 +577,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
 
         let opt_expanded = match *ext {
             DeclMacro(ref expand, def_span) => {
-                if let Err(msg) = validate_and_set_expn_info(def_span.map(|(_, s)| s),
-                                                             false, false) {
-                    self.cx.span_err(path.span, &msg);
-                    self.cx.trace_macros_diag();
-                    kind.dummy(span)
+                if let Err(dummy_span) = validate_and_set_expn_info(self, def_span.map(|(_, s)| s),
+                                                                    false, false, None) {
+                    dummy_span
                 } else {
                     kind.make_from(expand.expand(self.cx, span, mac.node.stream()))
                 }
@@ -565,14 +589,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 ref expander,
                 def_info,
                 allow_internal_unstable,
-                allow_internal_unsafe
+                allow_internal_unsafe,
+                unstable_feature,
             } => {
-                if let Err(msg) = validate_and_set_expn_info(def_info.map(|(_, s)| s),
-                                                             allow_internal_unstable,
-                                                             allow_internal_unsafe) {
-                    self.cx.span_err(path.span, &msg);
-                    self.cx.trace_macros_diag();
-                    kind.dummy(span)
+                if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s),
+                                                                    allow_internal_unstable,
+                                                                    allow_internal_unsafe,
+                                                                    unstable_feature) {
+                    dummy_span
                 } else {
                     kind.make_from(expander.expand(self.cx, span, mac.node.stream()))
                 }
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index 728b3e4076d..3b0d1d49899 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -284,11 +284,22 @@ pub fn compile(sess: &ParseSess, features: &RefCell<Features>, def: &ast::Item)
     if body.legacy {
         let allow_internal_unstable = attr::contains_name(&def.attrs, "allow_internal_unstable");
         let allow_internal_unsafe = attr::contains_name(&def.attrs, "allow_internal_unsafe");
+
+        let unstable_feature = attr::find_stability(&sess.span_diagnostic,
+                                                    &def.attrs, def.span).and_then(|stability| {
+            if let attr::StabilityLevel::Unstable { issue, .. } = stability.level {
+                Some((stability.feature, issue))
+            } else {
+                None
+            }
+        });
+
         NormalTT {
             expander,
             def_info: Some((def.id, def.span)),
             allow_internal_unstable,
-            allow_internal_unsafe
+            allow_internal_unsafe,
+            unstable_feature
         }
     } else {
         SyntaxExtension::DeclMacro(expander, Some((def.id, def.span)))
diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs
index 772dec72ab9..5b078535852 100644
--- a/src/libsyntax_ext/lib.rs
+++ b/src/libsyntax_ext/lib.rs
@@ -67,6 +67,7 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver,
                         def_info: None,
                         allow_internal_unstable: false,
                         allow_internal_unsafe: false,
+                        unstable_feature: None,
                     });
         )* }
     }
@@ -120,6 +121,7 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver,
                 def_info: None,
                 allow_internal_unstable: true,
                 allow_internal_unsafe: false,
+                unstable_feature: None
             });
 
     for (name, ext) in user_exts {
diff --git a/src/test/compile-fail/auxiliary/unstable-macros.rs b/src/test/compile-fail/auxiliary/unstable-macros.rs
new file mode 100644
index 00000000000..6462c11af48
--- /dev/null
+++ b/src/test/compile-fail/auxiliary/unstable-macros.rs
@@ -0,0 +1,16 @@
+// Copyright 2018 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)]
+#![stable(feature = "unit_test", since = "0.0.0")]
+
+#[unstable(feature = "unstable_macros", issue = "0")]
+#[macro_export]
+macro_rules! unstable_macro{ () => () }
diff --git a/src/test/compile-fail/macro-stability.rs b/src/test/compile-fail/macro-stability.rs
new file mode 100644
index 00000000000..a4b922c0fe1
--- /dev/null
+++ b/src/test/compile-fail/macro-stability.rs
@@ -0,0 +1,22 @@
+// Copyright 2018 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:unstable-macros.rs
+
+#![feature(staged_api)]
+#[macro_use] extern crate unstable_macros;
+
+#[unstable(feature = "local_unstable", issue = "0")]
+macro_rules! local_unstable { () => () }
+
+fn main() {
+    local_unstable!();
+    unstable_macro!(); //~ ERROR: macro unstable_macro! is unstable
+}
diff --git a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs
index 8da2ae8b29a..231ed2898f1 100644
--- a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs
+++ b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs
@@ -53,5 +53,6 @@ pub fn plugin_registrar(reg: &mut Registry) {
             def_info: None,
             allow_internal_unstable: false,
             allow_internal_unsafe: false,
+            unstable_feature: None,
         });
 }
diff --git a/src/test/run-pass/auxiliary/unstable-macros.rs b/src/test/run-pass/auxiliary/unstable-macros.rs
new file mode 100644
index 00000000000..6462c11af48
--- /dev/null
+++ b/src/test/run-pass/auxiliary/unstable-macros.rs
@@ -0,0 +1,16 @@
+// Copyright 2018 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)]
+#![stable(feature = "unit_test", since = "0.0.0")]
+
+#[unstable(feature = "unstable_macros", issue = "0")]
+#[macro_export]
+macro_rules! unstable_macro{ () => () }
diff --git a/src/test/run-pass/macro-stability.rs b/src/test/run-pass/macro-stability.rs
new file mode 100644
index 00000000000..9afcd51aa85
--- /dev/null
+++ b/src/test/run-pass/macro-stability.rs
@@ -0,0 +1,23 @@
+// Copyright 2018 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:unstable-macros.rs
+
+#![feature(unstable_macros)]
+
+#[macro_use] extern crate unstable_macros;
+
+#[unstable(feature = "local_unstable", issue = "0")]
+macro_rules! local_unstable { () => () }
+
+fn main() {
+    unstable_macro!();
+    local_unstable!();
+}