about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorCorey Richardson <corey@octayn.net>2014-12-24 00:44:13 -0500
committerCorey Richardson <corey@octayn.net>2015-01-01 07:06:46 -0500
commit41da99dff417eadf8f296a93529d9810f79e1d1b (patch)
treeca43141bb4f40fd7659d8b31c9ad5865a219bed0 /src/libsyntax
parent84f5ad8679c7fc454473ffbf389030f3e5fee379 (diff)
downloadrust-41da99dff417eadf8f296a93529d9810f79e1d1b.tar.gz
rust-41da99dff417eadf8f296a93529d9810f79e1d1b.zip
Feature gate macro arguments
Uses the same approach as https://github.com/rust-lang/rust/pull/17286 (and
subsequent changes making it more correct), where the visitor will skip any
pieces of the AST that are from "foreign code", where the spans don't line up,
indicating that that piece of code is due to a macro expansion.

If this breaks your code, read the error message to determine which feature
gate you should add to your crate, and bask in the knowledge that your code
won't mysteriously break should you try to use the 1.0 release.

Closes #18102

[breaking-change]
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/codemap.rs32
-rw-r--r--src/libsyntax/feature_gate.rs123
2 files changed, 120 insertions, 35 deletions
diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs
index 6b9af29c604..e61afb8b193 100644
--- a/src/libsyntax/codemap.rs
+++ b/src/libsyntax/codemap.rs
@@ -563,6 +563,38 @@ impl CodeMap {
             ExpnId(i) => f(Some(&(*self.expansions.borrow())[i as uint]))
         }
     }
+
+    /// 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.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
+                    } 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
+                    }
+                },
+                _ => false // stop looping
+            }
+        }) { /* empty while loop body */ }
+        return is_internal;
+    }
 }
 
 #[cfg(test)]
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 4607520655e..b2c2d7eb626 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -24,7 +24,7 @@ use ast::NodeId;
 use ast;
 use attr;
 use attr::AttrMetaMethods;
-use codemap::Span;
+use codemap::{CodeMap, Span};
 use diagnostic::SpanHandler;
 use visit;
 use visit::Visitor;
@@ -127,6 +127,7 @@ impl Features {
 struct Context<'a> {
     features: Vec<&'static str>,
     span_handler: &'a SpanHandler,
+    cm: &'a CodeMap,
 }
 
 impl<'a> Context<'a> {
@@ -144,7 +145,71 @@ impl<'a> Context<'a> {
     }
 }
 
-impl<'a, 'v> Visitor<'v> for Context<'a> {
+struct MacroVisitor<'a> {
+    context: &'a Context<'a>
+}
+
+impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
+    fn visit_view_item(&mut self, i: &ast::ViewItem) {
+        match i.node {
+            ast::ViewItemExternCrate(..) => {
+                for attr in i.attrs.iter() {
+                    if attr.name().get() == "phase"{
+                        self.context.gate_feature("phase", attr.span,
+                                          "compile time crate loading is \
+                                           experimental and possibly buggy");
+                    }
+                }
+            },
+            _ => { }
+        }
+        visit::walk_view_item(self, i)
+    }
+
+    fn visit_mac(&mut self, macro: &ast::Mac) {
+        let ast::MacInvocTT(ref path, _, _) = macro.node;
+        let id = path.segments.last().unwrap().identifier;
+
+        if id == token::str_to_ident("macro_rules") {
+            self.context.gate_feature("macro_rules", path.span, "macro definitions are \
+                not stable enough for use and are subject to change");
+        }
+
+        else 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");
+        }
+
+        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");
+        }
+
+        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");
+        }
+
+        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");
+        }
+    }
+}
+
+struct PostExpansionVisitor<'a> {
+    context: &'a Context<'a>
+}
+
+impl<'a> PostExpansionVisitor<'a> {
+    fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
+        if !self.context.cm.span_is_internal(span) {
+            self.context.gate_feature(feature, span, explain)
+        }
+    }
+}
+
+impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
     fn visit_name(&mut self, sp: Span, name: ast::Name) {
         if !token::get_name(name).get().is_ascii() {
             self.gate_feature("non_ascii_idents", sp,
@@ -217,7 +282,7 @@ impl<'a, 'v> Visitor<'v> for Context<'a> {
             }
 
             ast::ItemImpl(_, _, _, _, ref items) => {
-                if attr::contains_name(i.attrs.as_slice(),
+                if attr::contains_name(i.attrs[],
                                        "unsafe_destructor") {
                     self.gate_feature("unsafe_destructor",
                                       i.span,
@@ -256,36 +321,6 @@ impl<'a, 'v> Visitor<'v> for Context<'a> {
         }
     }
 
-    fn visit_mac(&mut self, macro: &ast::Mac) {
-        let ast::MacInvocTT(ref path, _, _) = macro.node;
-        let id = path.segments.last().unwrap().identifier;
-
-        if id == token::str_to_ident("macro_rules") {
-            self.gate_feature("macro_rules", path.span, "macro definitions are \
-                not stable enough for use and are subject to change");
-        }
-
-        else if id == token::str_to_ident("asm") {
-            self.gate_feature("asm", path.span, "inline assembly is not \
-                stable enough for use and is subject to change");
-        }
-
-        else if id == token::str_to_ident("log_syntax") {
-            self.gate_feature("log_syntax", path.span, "`log_syntax!` is not \
-                stable enough for use and is subject to change");
-        }
-
-        else if id == token::str_to_ident("trace_macros") {
-            self.gate_feature("trace_macros", path.span, "`trace_macros` is not \
-                stable enough for use and is subject to change");
-        }
-
-        else if id == token::str_to_ident("concat_idents") {
-            self.gate_feature("concat_idents", path.span, "`concat_idents` is not \
-                stable enough for use and is subject to change");
-        }
-    }
-
     fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
         if attr::contains_name(i.attrs[], "linkage") {
             self.gate_feature("linkage", i.span,
@@ -371,10 +406,15 @@ impl<'a, 'v> Visitor<'v> for Context<'a> {
     }
 }
 
-pub fn check_crate(span_handler: &SpanHandler, krate: &ast::Crate) -> (Features, Vec<Span>) {
+fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate,
+                        check: F)
+                       -> (Features, Vec<Span>)
+    where F: FnOnce(&mut Context, &ast::Crate)
+{
     let mut cx = Context {
         features: Vec::new(),
         span_handler: span_handler,
+        cm: cm,
     };
 
     let mut unknown_features = Vec::new();
@@ -419,7 +459,7 @@ pub fn check_crate(span_handler: &SpanHandler, krate: &ast::Crate) -> (Features,
         }
     }
 
-    visit::walk_crate(&mut cx, krate);
+    check(&mut cx, krate);
 
     (Features {
         default_type_params: cx.has_feature("default_type_params"),
@@ -432,3 +472,16 @@ pub fn check_crate(span_handler: &SpanHandler, krate: &ast::Crate) -> (Features,
     },
     unknown_features)
 }
+
+pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
+-> (Features, Vec<Span>) {
+    check_crate_inner(cm, span_handler, krate,
+                      |ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
+}
+
+pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
+-> (Features, Vec<Span>) {
+    check_crate_inner(cm, span_handler, krate,
+                      |ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx },
+                                                     krate))
+}