about summary refs log tree commit diff
path: root/src/libsyntax/ext
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-03-16 02:46:23 +0000
committerbors <bors@rust-lang.org>2018-03-16 02:46:23 +0000
commita7170b0412d1baa4e30cb31d1ea326617021f086 (patch)
treec29e3be6c0201716b609c9e2f714f8703a580b3a /src/libsyntax/ext
parent36b66873187e37a9d79adad89563088a9cb86028 (diff)
parent69035f20b92870a7ad5dbc22c65aee971d8f8698 (diff)
downloadrust-a7170b0412d1baa4e30cb31d1ea326617021f086.tar.gz
rust-a7170b0412d1baa4e30cb31d1ea326617021f086.zip
Auto merge of #48524 - abonander:check-macro-stability, r=petrochenkov
check stability of macro invocations

I haven't implemented tests yet but this should be a pretty solid prototype. I think as-implemented it will also stability-check macro invocations in the same crate, dunno if we want that or not.

I don't know if we want this to go through `rustc::middle::stability` or not, considering the information there wouldn't be available at the time of macro expansion (even for external crates, right?).

r? @nrc
closes #34079
cc @petrochenkov @durka @jseyfried #38356
Diffstat (limited to 'src/libsyntax/ext')
-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
3 files changed, 56 insertions, 17 deletions
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 3e75afcee1c..7ccace014d0 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;
 
@@ -533,11 +534,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,
@@ -553,11 +579,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()))
                 }
@@ -567,14 +591,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 9162a582599..a4b2c3990f5 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -283,11 +283,22 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> Syntax
     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)))