about summary refs log tree commit diff
path: root/src/libsyntax/errors
diff options
context:
space:
mode:
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
+            }
+        }
+    }
+}
+