about summary refs log tree commit diff
path: root/src/libsyntax/errors
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2016-04-15 21:23:50 -0400
committerNiko Matsakis <niko@alum.mit.edu>2016-04-21 04:42:24 -0400
commit01d2b4ab6bdb33e8678c43612b81dbbbad32cc93 (patch)
tree52555f0d2ff0853d7bcfdb22bc02b9c150807397 /src/libsyntax/errors
parent95545e7adcf1715eff2a31a53fe25ce2b012e62b (diff)
downloadrust-01d2b4ab6bdb33e8678c43612b81dbbbad32cc93.tar.gz
rust-01d2b4ab6bdb33e8678c43612b81dbbbad32cc93.zip
port compiletest to use JSON output
This uncovered a lot of bugs in compiletest and also some shortcomings
of our existing JSON output. We had to add information to the JSON
output, such as suggested text and macro backtraces. We also had to fix
various bugs in the existing tests.

Joint work with jntrnr.
Diffstat (limited to 'src/libsyntax/errors')
-rw-r--r--src/libsyntax/errors/emitter.rs47
-rw-r--r--src/libsyntax/errors/json.rs251
2 files changed, 176 insertions, 122 deletions
diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs
index 61fdc8453d8..0b5234769b2 100644
--- a/src/libsyntax/errors/emitter.rs
+++ b/src/libsyntax/errors/emitter.rs
@@ -577,46 +577,17 @@ impl EmitterWriter {
     fn print_macro_backtrace(&mut self,
                              sp: Span)
                              -> io::Result<()> {
-        let mut last_span = codemap::DUMMY_SP;
-        let mut span = sp;
-
-        loop {
-            let span_name_span = self.cm.with_expn_info(span.expn_id, |expn_info| {
-                expn_info.map(|ei| {
-                    let (pre, post) = match ei.callee.format {
-                        codemap::MacroAttribute(..) => ("#[", "]"),
-                        codemap::MacroBang(..) => ("", "!"),
-                    };
-                    let macro_decl_name = format!("in this expansion of {}{}{}",
-                                                  pre,
-                                                  ei.callee.name(),
-                                                  post);
-                    let def_site_span = ei.callee.span;
-                    (ei.call_site, macro_decl_name, def_site_span)
-                })
-            });
-            let (macro_decl_name, def_site_span) = match span_name_span {
-                None => break,
-                Some((sp, macro_decl_name, def_site_span)) => {
-                    span = sp;
-                    (macro_decl_name, def_site_span)
-                }
-            };
-
-            // Don't print recursive invocations
-            if !span.source_equal(&last_span) {
-                let mut diag_string = macro_decl_name;
-                if let Some(def_site_span) = def_site_span {
-                    diag_string.push_str(&format!(" (defined in {})",
-                                                  self.cm.span_to_filename(def_site_span)));
-                }
-
-                let snippet = self.cm.span_to_string(span);
-                print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)?;
+        for trace in self.cm.macro_backtrace(sp) {
+            let mut diag_string =
+                format!("in this expansion of {}", trace.macro_decl_name);
+            if let Some(def_site_span) = trace.def_site_span {
+                diag_string.push_str(
+                    &format!(" (defined in {})",
+                        self.cm.span_to_filename(def_site_span)));
             }
-            last_span = span;
+            let snippet = self.cm.span_to_string(sp);
+            print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)?;
         }
-
         Ok(())
     }
 }
diff --git a/src/libsyntax/errors/json.rs b/src/libsyntax/errors/json.rs
index f369582bc5c..5a195e9f078 100644
--- a/src/libsyntax/errors/json.rs
+++ b/src/libsyntax/errors/json.rs
@@ -1,4 +1,4 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -20,13 +20,14 @@
 // FIXME spec the JSON output properly.
 
 
-use codemap::{self, Span, MultiSpan, CodeMap};
+use codemap::{self, Span, MacroBacktrace, MultiSpan, CodeMap};
 use diagnostics::registry::Registry;
 use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion};
 use errors::emitter::Emitter;
 
 use std::rc::Rc;
 use std::io::{self, Write};
+use std::vec;
 
 use rustc_serialize::json::as_json;
 
@@ -84,8 +85,12 @@ struct Diagnostic<'a> {
     /// "error: internal compiler error", "error", "warning", "note", "help".
     level: &'static str,
     spans: Vec<DiagnosticSpan>,
-    /// Assocaited diagnostic messages.
+    /// Associated diagnostic messages.
     children: Vec<Diagnostic<'a>>,
+    /// The message as rustc would render it. Currently this is only
+    /// `Some` for "suggestions", but eventually it will include all
+    /// snippets.
+    rendered: Option<String>,
 }
 
 #[derive(RustcEncodable)]
@@ -101,17 +106,40 @@ struct DiagnosticSpan {
     column_end: usize,
     /// Source text from the start of line_start to the end of line_end.
     text: Vec<DiagnosticSpanLine>,
+    /// If we are suggesting a replacement, this will contain text
+    /// that should be sliced in atop this span. You may prefer to
+    /// load the fully rendered version from the parent `Diagnostic`,
+    /// however.
+    suggested_replacement: Option<String>,
+    /// Macro invocations that created the code at this span, if any.
+    expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
 }
 
 #[derive(RustcEncodable)]
 struct DiagnosticSpanLine {
     text: String,
+
     /// 1-based, character offset in self.text.
     highlight_start: usize,
+
     highlight_end: usize,
 }
 
 #[derive(RustcEncodable)]
+struct DiagnosticSpanMacroExpansion {
+    /// span where macro was applied to generate this code; note that
+    /// this may itself derive from a macro (if
+    /// `span.expansion.is_some()`)
+    span: DiagnosticSpan,
+
+    /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
+    macro_decl_name: String,
+
+    /// span where macro was defined (if known)
+    def_site_span: Option<DiagnosticSpan>,
+}
+
+#[derive(RustcEncodable)]
 struct DiagnosticCode {
     /// The code itself.
     code: String,
@@ -132,6 +160,7 @@ impl<'a> Diagnostic<'a> {
             level: level.to_str(),
             spans: msp.map_or(vec![], |msp| DiagnosticSpan::from_multispan(msp, je)),
             children: vec![],
+            rendered: None,
         }
     }
 
@@ -146,6 +175,7 @@ impl<'a> Diagnostic<'a> {
             level: level.to_str(),
             spans: DiagnosticSpan::from_render_span(span, je),
             children: vec![],
+            rendered: je.render(span),
         }
     }
 
@@ -160,6 +190,7 @@ impl<'a> Diagnostic<'a> {
             children: db.children.iter().map(|c| {
                 Diagnostic::from_sub_diagnostic(c, je)
             }).collect(),
+            rendered: None,
         }
     }
 
@@ -173,37 +204,93 @@ impl<'a> Diagnostic<'a> {
                      .or_else(|| db.span.as_ref().map(|s| DiagnosticSpan::from_multispan(s, je)))
                      .unwrap_or(vec![]),
             children: vec![],
+            rendered: db.render_span.as_ref()
+                                    .and_then(|rsp| je.render(rsp)),
         }
     }
 }
 
 impl DiagnosticSpan {
+    fn from_span(span: Span, suggestion: Option<&String>, je: &JsonEmitter)
+                 -> DiagnosticSpan {
+        // obtain the full backtrace from the `macro_backtrace`
+        // helper; in some ways, it'd be better to expand the
+        // backtrace ourselves, but the `macro_backtrace` helper makes
+        // some decision, such as dropping some frames, and I don't
+        // want to duplicate that logic here.
+        let backtrace = je.cm.macro_backtrace(span).into_iter();
+        DiagnosticSpan::from_span_and_backtrace(span, suggestion, backtrace, je)
+    }
+
+    fn from_span_and_backtrace(span: Span,
+                               suggestion: Option<&String>,
+                               mut backtrace: vec::IntoIter<MacroBacktrace>,
+                               je: &JsonEmitter)
+                               -> DiagnosticSpan {
+        let start = je.cm.lookup_char_pos(span.lo);
+        let end = je.cm.lookup_char_pos(span.hi);
+        let backtrace_step =
+            backtrace.next()
+                     .map(|bt| {
+                         let call_site =
+                             Self::from_span_and_backtrace(bt.call_site,
+                                                           None,
+                                                           backtrace,
+                                                           je);
+                         let def_site_span =
+                             bt.def_site_span
+                               .map(|sp| {
+                                   Self::from_span_and_backtrace(sp,
+                                                                 None,
+                                                                 vec![].into_iter(),
+                                                                 je)
+                               });
+                         Box::new(DiagnosticSpanMacroExpansion {
+                             span: call_site,
+                             macro_decl_name: bt.macro_decl_name,
+                             def_site_span: def_site_span,
+                         })
+                     });
+        DiagnosticSpan {
+            file_name: start.file.name.clone(),
+            byte_start: span.lo.0,
+            byte_end: span.hi.0,
+            line_start: start.line,
+            line_end: end.line,
+            column_start: start.col.0 + 1,
+            column_end: end.col.0 + 1,
+            text: DiagnosticSpanLine::from_span(span, je),
+            suggested_replacement: suggestion.cloned(),
+            expansion: backtrace_step,
+        }
+    }
+
     fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
-        msp.spans.iter().map(|span| {
-            let start = je.cm.lookup_char_pos(span.lo);
-            let end = je.cm.lookup_char_pos(span.hi);
-            DiagnosticSpan {
-                file_name: start.file.name.clone(),
-                byte_start: span.lo.0,
-                byte_end: span.hi.0,
-                line_start: start.line,
-                line_end: end.line,
-                column_start: start.col.0 + 1,
-                column_end: end.col.0 + 1,
-                text: DiagnosticSpanLine::from_span(span, je),
-            }
-        }).collect()
+        msp.spans.iter().map(|&span| Self::from_span(span, None, je)).collect()
+    }
+
+    fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter)
+                       -> Vec<DiagnosticSpan> {
+        assert_eq!(suggestion.msp.spans.len(), suggestion.substitutes.len());
+        suggestion.msp.spans.iter()
+                            .zip(&suggestion.substitutes)
+                            .map(|(&span, suggestion)| {
+                                DiagnosticSpan::from_span(span, Some(suggestion), je)
+                            })
+                            .collect()
     }
 
     fn from_render_span(rsp: &RenderSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
         match *rsp {
-            RenderSpan::FullSpan(ref msp) |
-            // FIXME(#30701) handle Suggestion properly
-            RenderSpan::Suggestion(CodeSuggestion { ref msp, .. }) => {
+            RenderSpan::FileLine(ref msp) |
+            RenderSpan::FullSpan(ref msp) => {
                 DiagnosticSpan::from_multispan(msp, je)
             }
+            RenderSpan::Suggestion(ref suggestion) => {
+                DiagnosticSpan::from_suggestion(suggestion, je)
+            }
             RenderSpan::EndSpan(ref msp) => {
-                msp.spans.iter().map(|span| {
+                msp.spans.iter().map(|&span| {
                     let end = je.cm.lookup_char_pos(span.hi);
                     DiagnosticSpan {
                         file_name: end.file.name.clone(),
@@ -214,37 +301,11 @@ impl DiagnosticSpan {
                         column_start: end.col.0 + 1,
                         column_end: end.col.0 + 1,
                         text: DiagnosticSpanLine::from_span_end(span, je),
+                        suggested_replacement: None,
+                        expansion: None,
                     }
                 }).collect()
             }
-            RenderSpan::FileLine(ref msp) => {
-                msp.spans.iter().map(|span| {
-                    let start = je.cm.lookup_char_pos(span.lo);
-                    let end = je.cm.lookup_char_pos(span.hi);
-                    DiagnosticSpan {
-                        file_name: start.file.name.clone(),
-                        byte_start: span.lo.0,
-                        byte_end: span.hi.0,
-                        line_start: start.line,
-                        line_end: end.line,
-                        column_start: 0,
-                        column_end: 0,
-                        text: DiagnosticSpanLine::from_span(span, je),
-                    }
-                }).collect()
-            }
-        }
-    }
-}
-
-macro_rules! get_lines_for_span {
-    ($span: ident, $je: ident) => {
-        match $je.cm.span_to_lines(*$span) {
-            Ok(lines) => lines,
-            Err(_) => {
-                debug!("unprintable span");
-                return Vec::new();
-            }
         }
     }
 }
@@ -265,45 +326,49 @@ impl DiagnosticSpanLine {
     /// Create a list of DiagnosticSpanLines from span - each line with any part
     /// of `span` gets a DiagnosticSpanLine, with the highlight indicating the
     /// `span` within the line.
-    fn from_span(span: &Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
-        let lines = get_lines_for_span!(span, je);
-
-        let mut result = Vec::new();
-        let fm = &*lines.file;
-
-        for line in &lines.lines {
-            result.push(DiagnosticSpanLine::line_from_filemap(fm,
-                                                              line.line_index,
-                                                              line.start_col.0 + 1,
-                                                              line.end_col.0 + 1));
-        }
-
-        result
+    fn from_span(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
+        je.cm.span_to_lines(span)
+             .map(|lines| {
+                 let fm = &*lines.file;
+                 lines.lines
+                      .iter()
+                      .map(|line| {
+                          DiagnosticSpanLine::line_from_filemap(fm,
+                                                                line.line_index,
+                                                                line.start_col.0 + 1,
+                                                                line.end_col.0 + 1)
+                      })
+                     .collect()
+             })
+            .unwrap_or(vec![])
     }
 
     /// Create a list of DiagnosticSpanLines from span - the result covers all
     /// of `span`, but the highlight is zero-length and at the end of `span`.
-    fn from_span_end(span: &Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
-        let lines = get_lines_for_span!(span, je);
-
-        let mut result = Vec::new();
-        let fm = &*lines.file;
-
-        for (i, line) in lines.lines.iter().enumerate() {
-            // Invariant - CodeMap::span_to_lines will not return extra context
-            // lines - the last line returned is the last line of `span`.
-            let highlight = if i == lines.lines.len() - 1 {
-                (line.end_col.0 + 1, line.end_col.0 + 1)
-            } else {
-                (0, 0)
-            };
-            result.push(DiagnosticSpanLine::line_from_filemap(fm,
-                                                              line.line_index,
-                                                              highlight.0,
-                                                              highlight.1));
-        }
-
-        result
+    fn from_span_end(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
+        je.cm.span_to_lines(span)
+             .map(|lines| {
+                 let fm = &*lines.file;
+                 lines.lines.iter()
+                            .enumerate()
+                            .map(|(i, line)| {
+                                // Invariant - CodeMap::span_to_lines
+                                // will not return extra context lines
+                                // - the last line returned is the last
+                                // line of `span`.
+                                let highlight = if i == lines.lines.len() - 1 {
+                                    (line.end_col.0 + 1, line.end_col.0 + 1)
+                                } else {
+                                    (0, 0)
+                                };
+                                DiagnosticSpanLine::line_from_filemap(fm,
+                                                                      line.line_index,
+                                                                      highlight.0,
+                                                                      highlight.1)
+                            })
+                            .collect()
+             })
+            .unwrap_or(vec![])
     }
 }
 
@@ -322,3 +387,21 @@ impl DiagnosticCode {
         })
     }
 }
+
+impl JsonEmitter {
+    fn render(&self, render_span: &RenderSpan) -> Option<String> {
+        match *render_span {
+            RenderSpan::FileLine(_) |
+            RenderSpan::FullSpan(_) => {
+                None
+            }
+            RenderSpan::Suggestion(ref suggestion) => {
+                Some(suggestion.splice_lines(&self.cm))
+            }
+            RenderSpan::EndSpan(_) => {
+                None
+            }
+        }
+    }
+}
+