about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorDaniel Campbell <campbelldj@hotmail.com>2015-11-25 11:05:27 +1300
committerDaniel Campbell <campbelldj@hotmail.com>2015-12-08 15:35:28 +1300
commit6b6fb7bbcb3c164dc17236ce52022c0bed8e0cd8 (patch)
treee4c2c326872fcc021006e8f9a93af912982c2b12 /src/libsyntax
parent2a0ce4a62954ad30f37d67f1c47b1a6978fc6bc4 (diff)
downloadrust-6b6fb7bbcb3c164dc17236ce52022c0bed8e0cd8.tar.gz
rust-6b6fb7bbcb3c164dc17236ce52022c0bed8e0cd8.zip
Added pretty-printing for span expansion chains through span_to_expanded_string.
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/codemap.rs204
1 files changed, 204 insertions, 0 deletions
diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs
index db011265c73..18659cb2e78 100644
--- a/src/libsyntax/codemap.rs
+++ b/src/libsyntax/codemap.rs
@@ -807,6 +807,96 @@ impl CodeMap {
                         hi.col.to_usize() + 1)).to_string()
     }
 
+    // Returns true if two spans have the same callee
+    // (Assumes the same ExpnFormat implies same callee)
+    fn match_callees(&self, sp_a: &Span, sp_b: &Span) -> bool {
+        let fmt_a = self
+            .with_expn_info(sp_a.expn_id,
+                            |ei| ei.map(|ei| ei.callee.format.clone()));
+
+        let fmt_b = self
+            .with_expn_info(sp_b.expn_id,
+                            |ei| ei.map(|ei| ei.callee.format.clone()));
+        fmt_a == fmt_b
+    }
+
+    /// Returns a formatted string showing the expansion chain of a span
+    ///
+    /// Spans are printed in the following format:
+    ///
+    /// filename:start_line:col: end_line:col
+    /// snippet
+    ///   Callee:
+    ///   Callee span
+    ///   Callsite:
+    ///   Callsite span
+    ///
+    /// Callees and callsites are printed recursively (if available, otherwise header
+    /// and span is omitted), expanding into their own callee/callsite spans.
+    /// Each layer of recursion has an increased indent, and snippets are truncated
+    /// to at most 50 characters. Finally, recursive calls to the same macro are squashed,
+    /// with '...' used to represent any number of recursive calls.
+    pub fn span_to_expanded_string(&self, sp: Span) -> String {
+        self.span_to_expanded_string_internal(sp, "")
+    }
+
+    fn span_to_expanded_string_internal(&self, sp:Span, indent: &str) -> String {
+        let mut indent = indent.to_owned();
+        let mut output = "".to_owned();
+        let span_str = self.span_to_string(sp);
+        let mut span_snip = self.span_to_snippet(sp)
+            .unwrap_or("Snippet unavailable".to_owned());
+        if span_snip.len() > 50 {
+            span_snip.truncate(50);
+            span_snip.push_str("...");
+        }
+        output.push_str(&format!("{}{}\n{}`{}`\n", indent, span_str, indent, span_snip));
+
+        if sp.expn_id == NO_EXPANSION || sp.expn_id == COMMAND_LINE_EXPN {
+            return output;
+        }
+
+        let mut callee = self.with_expn_info(sp.expn_id,
+                                             |ei| ei.and_then(|ei| ei.callee.span.clone()));
+        let mut callsite = self.with_expn_info(sp.expn_id,
+                                               |ei| ei.map(|ei| ei.call_site.clone()));
+
+        indent.push_str("  ");
+        let mut is_recursive = false;
+
+        while callee.is_some() && self.match_callees(&sp, &callee.unwrap()) {
+            callee = self.with_expn_info(callee.unwrap().expn_id,
+                                         |ei| ei.and_then(|ei| ei.callee.span.clone()));
+            is_recursive = true;
+        }
+        if let Some(span) = callee {
+            output.push_str(&indent);
+            output.push_str("Callee:\n");
+            if is_recursive {
+                output.push_str(&indent);
+                output.push_str("...\n");
+            }
+            output.push_str(&(self.span_to_expanded_string_internal(span, &indent)));
+        }
+
+        is_recursive = false;
+        while callsite.is_some() && self.match_callees(&sp, &callsite.unwrap()) {
+            callsite = self.with_expn_info(callsite.unwrap().expn_id,
+                                           |ei| ei.map(|ei| ei.call_site.clone()));
+            is_recursive = true;
+        }
+        if let Some(span) = callsite {
+            output.push_str(&indent);
+            output.push_str("Callsite:\n");
+            if is_recursive {
+                output.push_str(&indent);
+                output.push_str("...\n");
+            }
+            output.push_str(&(self.span_to_expanded_string_internal(span, &indent)));
+        }
+        output
+    }
+
     pub fn span_to_filename(&self, sp: Span) -> FileName {
         self.lookup_char_pos(sp.lo).file.name.to_string()
     }
@@ -1274,4 +1364,118 @@ mod tests {
 
         assert_eq!(sstr, "blork.rs:2:1: 2:12");
     }
+
+    #[test]
+    fn t10() {
+        // Test span_to_expanded_string works in base case (no expansion)
+        let cm = init_code_map();
+        let span = Span { lo: BytePos(0), hi: BytePos(11), expn_id: NO_EXPANSION };
+        let sstr = cm.span_to_expanded_string(span);
+        assert_eq!(sstr, "blork.rs:1:1: 1:12\n`first line.`\n");
+
+        let span = Span { lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION };
+        let sstr =  cm.span_to_expanded_string(span);
+        assert_eq!(sstr, "blork.rs:2:1: 2:12\n`second line`\n");
+    }
+
+    #[test]
+    fn t11() {
+        // Test span_to_expanded_string works with expansion
+        use ast::Name;
+        let cm = init_code_map();
+        let root = Span { lo: BytePos(0), hi: BytePos(11), expn_id: NO_EXPANSION };
+        let format = ExpnFormat::MacroBang(Name(0u32));
+        let callee = NameAndSpan { format: format,
+                                   allow_internal_unstable: false,
+                                   span: None };
+
+        let info = ExpnInfo { call_site: root, callee: callee };
+        let id = cm.record_expansion(info);
+        let sp = Span { lo: BytePos(12), hi: BytePos(23), expn_id: id };
+
+        let sstr = cm.span_to_expanded_string(sp);
+        assert_eq!(sstr,
+                   "blork.rs:2:1: 2:12\n`second line`\n  Callsite:\n  \
+                    blork.rs:1:1: 1:12\n  `first line.`\n");
+    }
+
+    fn init_expansion_chain(cm: &CodeMap) -> Span {
+        // Creates an expansion chain containing two recursive calls
+        // root -> expA -> expA -> expB -> expB -> end
+        use ast::Name;
+
+        let root = Span { lo: BytePos(0), hi: BytePos(11), expn_id: NO_EXPANSION };
+
+        let format_root = ExpnFormat::MacroBang(Name(0u32));
+        let callee_root = NameAndSpan { format: format_root,
+                                        allow_internal_unstable: false,
+                                        span: Some(root) };
+
+        let info_a1 = ExpnInfo { call_site: root, callee: callee_root };
+        let id_a1 = cm.record_expansion(info_a1);
+        let span_a1 = Span { lo: BytePos(12), hi: BytePos(23), expn_id: id_a1 };
+
+        let format_a = ExpnFormat::MacroBang(Name(1u32));
+        let callee_a = NameAndSpan { format: format_a,
+                                      allow_internal_unstable: false,
+                                      span: Some(span_a1) };
+
+        let info_a2 = ExpnInfo { call_site: span_a1, callee: callee_a.clone() };
+        let id_a2 = cm.record_expansion(info_a2);
+        let span_a2 = Span { lo: BytePos(12), hi: BytePos(23), expn_id: id_a2 };
+
+        let info_b1 = ExpnInfo { call_site: span_a2, callee: callee_a };
+        let id_b1 = cm.record_expansion(info_b1);
+        let span_b1 = Span { lo: BytePos(25), hi: BytePos(36), expn_id: id_b1 };
+
+        let format_b = ExpnFormat::MacroBang(Name(2u32));
+        let callee_b = NameAndSpan { format: format_b,
+                                     allow_internal_unstable: false,
+                                     span: None };
+
+        let info_b2 = ExpnInfo { call_site: span_b1, callee: callee_b.clone() };
+        let id_b2 = cm.record_expansion(info_b2);
+        let span_b2 = Span { lo: BytePos(25), hi: BytePos(36), expn_id: id_b2 };
+
+        let info_end = ExpnInfo { call_site: span_b2, callee: callee_b };
+        let id_end = cm.record_expansion(info_end);
+        Span { lo: BytePos(37), hi: BytePos(48), expn_id: id_end }
+    }
+
+    #[test]
+    fn t12() {
+        // Test span_to_expanded_string collapses recursive macros and handles
+        // recursive callsite and callee expansions
+        let cm = init_code_map();
+        let end = init_expansion_chain(&cm);
+        let sstr = cm.span_to_expanded_string(end);
+        let res_str =
+r"blork2.rs:2:1: 2:12
+`second line`
+  Callsite:
+  ...
+  blork2.rs:1:1: 1:12
+  `first line.`
+    Callee:
+    blork.rs:2:1: 2:12
+    `second line`
+      Callee:
+      blork.rs:1:1: 1:12
+      `first line.`
+      Callsite:
+      blork.rs:1:1: 1:12
+      `first line.`
+    Callsite:
+    ...
+    blork.rs:2:1: 2:12
+    `second line`
+      Callee:
+      blork.rs:1:1: 1:12
+      `first line.`
+      Callsite:
+      blork.rs:1:1: 1:12
+      `first line.`
+";
+        assert_eq!(sstr, res_str);
+    }
 }