about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDaniel Campbell <campbelldj@hotmail.com>2016-01-29 20:22:55 +1300
committerDaniel Campbell <campbelldj@hotmail.com>2016-02-01 19:09:18 +1300
commit1d326419a13edf205f905be4de9c699207777934 (patch)
tree5d1864aebfbb57353029479031fcd07aef847453
parent142214d1f2232a4e88ff7bd99951b01f36052c61 (diff)
downloadrust-1d326419a13edf205f905be4de9c699207777934.tar.gz
rust-1d326419a13edf205f905be4de9c699207777934.zip
Implemented macro referencing for save analysis
-rw-r--r--src/librustc/session/mod.rs8
-rw-r--r--src/librustc_metadata/common.rs2
-rw-r--r--src/librustc_metadata/creader.rs8
-rw-r--r--src/librustc_metadata/decoder.rs15
-rw-r--r--src/librustc_metadata/encoder.rs5
-rw-r--r--src/librustc_trans/save/dump_csv.rs56
-rw-r--r--src/librustc_trans/save/mod.rs63
-rw-r--r--src/librustc_trans/save/recorder.rs25
-rw-r--r--src/librustc_trans/save/span_utils.rs29
-rw-r--r--src/libsyntax/codemap.rs21
10 files changed, 221 insertions, 11 deletions
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index a55a6918b5b..ea8fb5e7ca1 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -30,7 +30,7 @@ use rustc_back::target::Target;
 
 use std::path::{Path, PathBuf};
 use std::cell::{Cell, RefCell};
-use std::collections::HashSet;
+use std::collections::{HashMap, HashSet};
 use std::env;
 use std::rc::Rc;
 
@@ -77,6 +77,11 @@ pub struct Session {
     /// available in this crate
     pub available_macros: RefCell<HashSet<Name>>,
 
+    /// Map from imported macro spans (which consist of
+    /// the localized span for the macro body) to the
+    /// macro name and defintion span in the source crate.
+    pub imported_macro_spans: RefCell<HashMap<Span, (String, Span)>>,
+
     next_node_id: Cell<ast::NodeId>,
 }
 
@@ -490,6 +495,7 @@ pub fn build_session_(sopts: config::Options,
         next_node_id: Cell::new(1),
         injected_allocator: Cell::new(None),
         available_macros: RefCell::new(HashSet::new()),
+        imported_macro_spans: RefCell::new(HashMap::new()),
     };
 
     sess
diff --git a/src/librustc_metadata/common.rs b/src/librustc_metadata/common.rs
index 479ab759278..991cbe137ec 100644
--- a/src/librustc_metadata/common.rs
+++ b/src/librustc_metadata/common.rs
@@ -223,6 +223,8 @@ pub const tag_polarity: usize = 0x9d;
 pub const tag_macro_defs: usize = 0x10e; // top-level only
 pub const tag_macro_def: usize = 0x9e;
 pub const tag_macro_def_body: usize = 0x9f;
+pub const tag_macro_def_span_lo: usize = 0xa8;
+pub const tag_macro_def_span_hi: usize = 0xa9;
 
 pub const tag_paren_sugar: usize = 0xa0;
 
diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs
index 9c75007a8db..b569739c40c 100644
--- a/src/librustc_metadata/creader.rs
+++ b/src/librustc_metadata/creader.rs
@@ -494,7 +494,7 @@ impl<'a> CrateReader<'a> {
         let mut macros = vec![];
         decoder::each_exported_macro(ekrate.metadata.as_slice(),
                                      &*self.cstore.intr,
-            |name, attrs, body| {
+            |name, attrs, span, body| {
                 // NB: Don't use parse::parse_tts_from_source_str because it parses with
                 // quote_depth > 0.
                 let mut p = parse::new_parser_from_source_str(&self.sess.parse_sess,
@@ -509,7 +509,7 @@ impl<'a> CrateReader<'a> {
                         panic!(FatalError);
                     }
                 };
-                let span = mk_sp(lo, p.last_span.hi);
+                let local_span = mk_sp(lo, p.last_span.hi);
 
                 // Mark the attrs as used
                 for attr in &attrs {
@@ -520,7 +520,7 @@ impl<'a> CrateReader<'a> {
                     ident: ast::Ident::with_empty_ctxt(name),
                     attrs: attrs,
                     id: ast::DUMMY_NODE_ID,
-                    span: span,
+                    span: local_span,
                     imported_from: Some(item.ident),
                     // overridden in plugin/load.rs
                     export: false,
@@ -529,6 +529,8 @@ impl<'a> CrateReader<'a> {
 
                     body: body,
                 });
+                self.sess.imported_macro_spans.borrow_mut()
+                    .insert(local_span, (name.as_str().to_string(), span));
                 true
             }
         );
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index bca8cb3995a..e37e4592ee3 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -52,7 +52,7 @@ use syntax::parse::token::{IdentInterner, special_idents};
 use syntax::parse::token;
 use syntax::ast;
 use syntax::abi;
-use syntax::codemap::{self, Span};
+use syntax::codemap::{self, Span, BytePos, NO_EXPANSION};
 use syntax::print::pprust;
 use syntax::ptr::P;
 
@@ -1461,19 +1461,28 @@ pub fn get_plugin_registrar_fn(data: &[u8]) -> Option<DefIndex> {
 }
 
 pub fn each_exported_macro<F>(data: &[u8], intr: &IdentInterner, mut f: F) where
-    F: FnMut(ast::Name, Vec<ast::Attribute>, String) -> bool,
+    F: FnMut(ast::Name, Vec<ast::Attribute>, Span, String) -> bool,
 {
     let macros = reader::get_doc(rbml::Doc::new(data), tag_macro_defs);
     for macro_doc in reader::tagged_docs(macros, tag_macro_def) {
         let name = item_name(intr, macro_doc);
         let attrs = get_attributes(macro_doc);
+        let span = get_macro_span(macro_doc);
         let body = reader::get_doc(macro_doc, tag_macro_def_body);
-        if !f(name, attrs, body.as_str().to_string()) {
+        if !f(name, attrs, span, body.as_str().to_string()) {
             break;
         }
     }
 }
 
+pub fn get_macro_span(doc: rbml::Doc) -> Span {
+    let lo_doc = reader::get_doc(doc, tag_macro_def_span_lo);
+    let lo = BytePos(reader::doc_as_u32(lo_doc));
+    let hi_doc = reader::get_doc(doc, tag_macro_def_span_hi);
+    let hi = BytePos(reader::doc_as_u32(hi_doc));
+    return Span { lo: lo, hi: hi, expn_id: NO_EXPANSION };
+}
+
 pub fn get_dylib_dependency_formats(cdata: Cmd)
     -> Vec<(ast::CrateNum, LinkagePreference)>
 {
diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index 45cbb22e6c9..1e50868f664 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -42,6 +42,7 @@ use std::rc::Rc;
 use std::u32;
 use syntax::abi;
 use syntax::ast::{self, NodeId, Name, CRATE_NODE_ID, CrateNum};
+use syntax::codemap::BytePos;
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
 use syntax::errors::Handler;
@@ -1727,6 +1728,10 @@ fn encode_macro_defs(rbml_w: &mut Encoder,
 
         encode_name(rbml_w, def.name);
         encode_attributes(rbml_w, &def.attrs);
+        let &BytePos(lo) = &def.span.lo;
+        let &BytePos(hi) = &def.span.hi;
+        rbml_w.wr_tagged_u32(tag_macro_def_span_lo, lo);
+        rbml_w.wr_tagged_u32(tag_macro_def_span_hi, hi);
 
         rbml_w.wr_tagged_str(tag_macro_def_body,
                              &::syntax::print::pprust::tts_to_string(&def.body));
diff --git a/src/librustc_trans/save/dump_csv.rs b/src/librustc_trans/save/dump_csv.rs
index 2951cf2ee1b..267b790fede 100644
--- a/src/librustc_trans/save/dump_csv.rs
+++ b/src/librustc_trans/save/dump_csv.rs
@@ -37,6 +37,8 @@ use middle::def_id::DefId;
 use middle::ty;
 
 use std::fs::File;
+use std::hash::*;
+use std::collections::HashSet;
 
 use syntax::ast::{self, NodeId};
 use syntax::codemap::*;
@@ -70,6 +72,14 @@ pub struct DumpCsvVisitor<'l, 'tcx: 'l> {
     fmt: FmtStrs<'l, 'tcx>,
 
     cur_scope: NodeId,
+
+    // Set of macro definition (callee) spans, and the set
+    // of macro use (callsite) spans. We store these to ensure
+    // we only write one macro def per unique macro definition, and
+    // one macro use per unique callsite span.
+    mac_defs: HashSet<Span>,
+    mac_uses: HashSet<Span>,
+
 }
 
 impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> {
@@ -92,6 +102,8 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> {
                               span_utils,
                               tcx),
             cur_scope: 0,
+            mac_defs: HashSet::new(),
+            mac_uses: HashSet::new(),
         }
     }
 
@@ -814,10 +826,41 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> {
                                   &typ);
         }
     }
+
+    /// Extract macro use and definition information from the AST node defined
+    /// by the given NodeId, using the expansion information from the node's
+    /// span.
+    ///
+    /// If the span is not macro-generated, do nothing, else use callee and
+    /// callsite spans to record macro definition and use data, using the
+    /// mac_uses and mac_defs sets to prevent multiples.
+    fn process_macro_use(&mut self, span: Span, id: NodeId) {
+        let data = match self.save_ctxt.get_macro_use_data(span, id) {
+            None => return,
+            Some(data) => data,
+        };
+        let mut hasher = SipHasher::new();
+        data.callee_span.hash(&mut hasher);
+        let hash = hasher.finish();
+        let qualname = format!("{}::{}", data.name, hash);
+        // Don't write macro definition for imported macros
+        if !self.mac_defs.contains(&data.callee_span)
+            && !data.imported {
+            self.mac_defs.insert(data.callee_span);
+            self.fmt.macro_str(data.callee_span, data.callee_span,
+                               data.name.clone(), qualname.clone());
+        }
+        if !self.mac_uses.contains(&data.span) {
+             self.mac_uses.insert(data.span);
+             self.fmt.macro_use_str(data.span, data.span, data.name,
+                                   qualname, data.scope);
+        }
+    }
 }
 
 impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> {
     fn visit_item(&mut self, item: &ast::Item) {
+        self.process_macro_use(item.span, item.id);
         match item.node {
             ast::ItemUse(ref use_item) => {
                 match use_item.node {
@@ -970,6 +1013,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> {
     }
 
     fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) {
+        self.process_macro_use(trait_item.span, trait_item.id);
         match trait_item.node {
             ast::ConstTraitItem(ref ty, Some(ref expr)) => {
                 self.process_const(trait_item.id,
@@ -991,6 +1035,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> {
     }
 
     fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) {
+        self.process_macro_use(impl_item.span, impl_item.id);
         match impl_item.node {
             ast::ImplItemKind::Const(ref ty, ref expr) => {
                 self.process_const(impl_item.id,
@@ -1012,6 +1057,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> {
     }
 
     fn visit_ty(&mut self, t: &ast::Ty) {
+        self.process_macro_use(t.span, t.id);
         match t.node {
             ast::TyPath(_, ref path) => {
                 match self.lookup_type_ref(t.id) {
@@ -1031,6 +1077,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> {
     }
 
     fn visit_expr(&mut self, ex: &ast::Expr) {
+        self.process_macro_use(ex.span, ex.id);
         match ex.node {
             ast::ExprCall(ref _f, ref _args) => {
                 // Don't need to do anything for function calls,
@@ -1117,11 +1164,13 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> {
         }
     }
 
-    fn visit_mac(&mut self, _: &ast::Mac) {
-        // Just stop, macros are poison to us.
+    fn visit_mac(&mut self, mac: &ast::Mac) {
+        // These shouldn't exist in the AST at this point, log a span bug.
+        self.sess.span_bug(mac.span, "macro invocation should have been expanded out of AST");
     }
 
     fn visit_pat(&mut self, p: &ast::Pat) {
+        self.process_macro_use(p.span, p.id);
         self.process_pat(p);
     }
 
@@ -1177,10 +1226,13 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> {
     }
 
     fn visit_stmt(&mut self, s: &ast::Stmt) {
+        let id = s.node.id();
+        self.process_macro_use(s.span, id.unwrap());
         visit::walk_stmt(self, s)
     }
 
     fn visit_local(&mut self, l: &ast::Local) {
+        self.process_macro_use(l.span, l.id);
         let value = self.span.snippet(l.span);
         self.process_var_decl(&l.pat, value);
 
diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs
index 37b23d6ee9c..7a2b6a4103d 100644
--- a/src/librustc_trans/save/mod.rs
+++ b/src/librustc_trans/save/mod.rs
@@ -73,6 +73,8 @@ pub enum Data {
     FunctionCallData(FunctionCallData),
     /// Data about a method call.
     MethodCallData(MethodCallData),
+    /// Data about a macro use.
+    MacroUseData(MacroUseData),
 }
 
 /// Data for all kinds of functions and methods.
@@ -174,6 +176,22 @@ pub struct MethodCallData {
     pub decl_id: Option<DefId>,
 }
 
+/// Data about a macro use.
+#[derive(Debug)]
+pub struct MacroUseData {
+    pub span: Span,
+    pub name: String,
+    // Because macro expansion happens before ref-ids are determined,
+    // we use the callee span to reference the associated macro definition.
+    pub callee_span: Span,
+    pub scope: NodeId,
+    pub imported: bool,
+}
+
+macro_rules! option_try(
+    ($e:expr) => (match $e { Some(e) => e, None => return None })
+);
+
 
 
 impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
@@ -652,6 +670,51 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
         })
     }
 
+    /// Attempt to return MacroUseData for any AST node.
+    ///
+    /// For a given piece of AST defined by the supplied Span and NodeId,
+    /// returns None if the node is not macro-generated or the span is malformed,
+    /// else uses the expansion callsite and callee to return some MacroUseData.
+    pub fn get_macro_use_data(&self, span: Span, id: NodeId) -> Option<MacroUseData> {
+        if !generated_code(span) {
+            return None;
+        }
+        // Note we take care to use the source callsite/callee, to handle
+        // nested expansions and ensure we only generate data for source-visible
+        // macro uses.
+        let callsite = self.tcx.sess.codemap().source_callsite(span);
+        let callee = self.tcx.sess.codemap().source_callee(span);
+        let callee = option_try!(callee);
+        let callee_span = option_try!(callee.span);
+
+        // Ignore attribute macros, their spans are usually mangled
+        if let MacroAttribute(_) = callee.format {
+            return None;
+        }
+
+        // If the callee is an imported macro from an external crate, need to get
+        // the source span and name from the session, as their spans are localized
+        // when read in, and no longer correspond to the source.
+        if let Some(mac) = self.tcx.sess.imported_macro_spans.borrow().get(&callee_span) {
+            let &(ref mac_name, mac_span) = mac;
+            return Some(MacroUseData {
+                                        span: callsite,
+                                        name: mac_name.clone(),
+                                        callee_span: mac_span,
+                                        scope: self.enclosing_scope(id),
+                                        imported: true,
+                                    });
+        }
+
+        Some(MacroUseData {
+            span: callsite,
+            name: callee.name().to_string(),
+            callee_span: callee_span,
+            scope: self.enclosing_scope(id),
+            imported: false,
+        })
+    }
+
     pub fn get_data_for_id(&self, _id: &NodeId) -> Data {
         // FIXME
         unimplemented!();
diff --git a/src/librustc_trans/save/recorder.rs b/src/librustc_trans/save/recorder.rs
index eaba366c1d4..c0083bb9480 100644
--- a/src/librustc_trans/save/recorder.rs
+++ b/src/librustc_trans/save/recorder.rs
@@ -96,6 +96,8 @@ pub enum Row {
     VarRef,
     TypeRef,
     FnRef,
+    Macro,
+    MacroUse,
 }
 
 impl<'a, 'tcx: 'a> FmtStrs<'a, 'tcx> {
@@ -219,6 +221,14 @@ impl<'a, 'tcx: 'a> FmtStrs<'a, 'tcx> {
                       vec!("refid", "refidcrate", "qualname", "scopeid"),
                       true,
                       true),
+            Macro => ("macro",
+                         vec!("name", "qualname"),
+                         true,
+                         true),
+            MacroUse => ("macro_use",
+                         vec!("callee_name", "qualname", "scopeid"),
+                         true,
+                         true),
         }
     }
 
@@ -686,4 +696,19 @@ impl<'a, 'tcx: 'a> FmtStrs<'a, 'tcx> {
                               sub_span,
                               svec!(id.index.as_usize(), id.krate, "", scope_id));
     }
+
+    pub fn macro_str(&mut self, span: Span, sub_span: Span, name: String, qualname: String) {
+        self.record_with_span(Macro, span, sub_span, svec!(name, qualname));
+    }
+
+    pub fn macro_use_str(&mut self,
+                         span: Span,
+                         sub_span: Span,
+                         name: String,
+                         qualname: String,
+                         scope_id: NodeId) {
+        let scope_id = self.normalize_node_id(scope_id);
+        self.record_with_span(MacroUse, span, sub_span,
+                              svec!(name, qualname, scope_id));
+    }
 }
diff --git a/src/librustc_trans/save/span_utils.rs b/src/librustc_trans/save/span_utils.rs
index 344431032d6..95c1d7bd031 100644
--- a/src/librustc_trans/save/span_utils.rs
+++ b/src/librustc_trans/save/span_utils.rs
@@ -378,6 +378,25 @@ impl<'a> SpanUtils<'a> {
         }
     }
 
+    // Given a macro_rules definition span, return the span of the macro's name.
+    pub fn span_for_macro_name(&self, span: Span) -> Option<Span> {
+        let mut toks = self.retokenise_span(span);
+        loop {
+            let ts = toks.real_token();
+            if ts.tok == token::Eof {
+                return None;
+            }
+            if ts.tok == token::Not {
+                let ts = toks.real_token();
+                if ts.tok.is_ident() {
+                    return self.make_sub_span(span, Some(ts.sp));
+                } else {
+                    return None;
+                }
+            }
+        }
+    }
+
     /// Return true if the span is generated code, and
     /// it is not a subspan of the root callsite.
     ///
@@ -395,10 +414,16 @@ impl<'a> SpanUtils<'a> {
         if sub_span.is_none() {
             return true;
         }
-        // A generated span is deemed invalid if it is not a sub-span of the root
+
+        //If the span comes from a fake filemap, filter it.
+        if !self.sess.codemap().lookup_char_pos(parent.lo).file.is_real_file() {
+            return true;
+        }
+
+        // Otherwise, a generated span is deemed invalid if it is not a sub-span of the root
         // callsite. This filters out macro internal variables and most malformed spans.
         let span = self.sess.codemap().source_callsite(parent);
-        !(parent.lo >= span.lo && parent.hi <= span.hi)
+        !(span.contains(parent))
     }
 }
 
diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs
index ca01623fef9..e80c9e4a6ce 100644
--- a/src/libsyntax/codemap.rs
+++ b/src/libsyntax/codemap.rs
@@ -1065,6 +1065,27 @@ impl CodeMap {
         span
     }
 
+    /// Return the source callee.
+    ///
+    /// Returns None if the supplied span has no expansion trace,
+    /// else returns the NameAndSpan for the macro definition
+    /// corresponding to the source callsite.
+    pub fn source_callee(&self, sp: Span) -> Option<NameAndSpan> {
+        let mut span = sp;
+        while let Some(callsite) = self.with_expn_info(span.expn_id,
+                                            |ei| ei.map(|ei| ei.call_site.clone())) {
+            if let Some(_) = self.with_expn_info(callsite.expn_id,
+                                                |ei| ei.map(|ei| ei.call_site.clone())) {
+                span = callsite;
+            }
+            else {
+                return self.with_expn_info(span.expn_id,
+                                           |ei| ei.map(|ei| ei.callee.clone()));
+            }
+        }
+        None
+    }
+
     pub fn span_to_filename(&self, sp: Span) -> FileName {
         self.lookup_char_pos(sp.lo).file.name.to_string()
     }