about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2015-04-09 14:48:15 -0400
committerNiko Matsakis <niko@alum.mit.edu>2015-04-10 06:11:28 -0400
commit906a9728ffd4cd9f40c96d7704260baf17845651 (patch)
tree448fb8de4dda0fb0d1e963069493ef5d55abd4de
parent5156b3a6cd1c60982f0bea9f3b7243f66cab9bb5 (diff)
downloadrust-906a9728ffd4cd9f40c96d7704260baf17845651.tar.gz
rust-906a9728ffd4cd9f40c96d7704260baf17845651.zip
Add a new `span_suggestion` infrastructure. This lets you edit a snippet
of text (perhaps obtained by span_snippet) and then splice that edited
form back into the original file in the form of a suggestion.
-rw-r--r--src/librustc/session/mod.rs7
-rw-r--r--src/libsyntax/diagnostic.rs66
2 files changed, 72 insertions, 1 deletions
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 148f484b0ed..88faf1cb68a 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -143,6 +143,13 @@ impl Session {
     pub fn span_end_note(&self, sp: Span, msg: &str) {
         self.diagnostic().span_end_note(sp, msg)
     }
+
+    /// Prints out a message with a suggested edit of the code.
+    ///
+    /// See `diagnostic::RenderSpan::Suggestion` for more information.
+    pub fn span_suggestion(&self, sp: Span, msg: &str, suggestion: String) {
+        self.diagnostic().span_suggestion(sp, msg, suggestion)
+    }
     pub fn span_help(&self, sp: Span, msg: &str) {
         self.diagnostic().span_help(sp, msg)
     }
diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs
index 32509ba6065..ed7bdcd898e 100644
--- a/src/libsyntax/diagnostic.rs
+++ b/src/libsyntax/diagnostic.rs
@@ -18,6 +18,7 @@ use codemap;
 use diagnostics;
 
 use std::cell::{RefCell, Cell};
+use std::cmp;
 use std::fmt;
 use std::io::prelude::*;
 use std::io;
@@ -28,7 +29,7 @@ use libc;
 /// maximum number of lines we will print for each error; arbitrary.
 const MAX_LINES: usize = 6;
 
-#[derive(Clone, Copy)]
+#[derive(Clone)]
 pub enum RenderSpan {
     /// A FullSpan renders with both with an initial line for the
     /// message, prefixed by file:linenum, followed by a summary of
@@ -42,6 +43,12 @@ pub enum RenderSpan {
     /// of the span).
     EndSpan(Span),
 
+    /// A suggestion renders with both with an initial line for the
+    /// message, prefixed by file:linenum, followed by a summary
+    /// of hypothetical source code, where the `String` is spliced
+    /// into the lines in place of the code covered by the span.
+    Suggestion(Span, String),
+
     /// A FileLine renders with just a line for the message prefixed
     /// by file:linenum.
     FileLine(Span),
@@ -51,6 +58,7 @@ impl RenderSpan {
     fn span(&self) -> Span {
         match *self {
             FullSpan(s) |
+            Suggestion(s, _) |
             EndSpan(s) |
             FileLine(s) =>
                 s
@@ -124,6 +132,12 @@ impl SpanHandler {
     pub fn span_help(&self, sp: Span, msg: &str) {
         self.handler.emit(Some((&self.cm, sp)), msg, Help);
     }
+    /// Prints out a message with a suggested edit of the code.
+    ///
+    /// See `diagnostic::RenderSpan::Suggestion` for more information.
+    pub fn span_suggestion(&self, sp: Span, msg: &str, suggestion: String) {
+        self.handler.custom_emit(&self.cm, Suggestion(sp, suggestion), msg, Help);
+    }
     pub fn fileline_note(&self, sp: Span, msg: &str) {
         self.handler.custom_emit(&self.cm, FileLine(sp), msg, Note);
     }
@@ -455,6 +469,9 @@ fn emit(dst: &mut EmitterWriter, cm: &codemap::CodeMap, rsp: RenderSpan,
         EndSpan(_) => {
             try!(end_highlight_lines(dst, cm, sp, lvl, cm.span_to_lines(sp)));
         }
+        Suggestion(_, ref suggestion) => {
+            try!(highlight_suggestion(dst, cm, sp, suggestion));
+        }
         FileLine(..) => {
             // no source text in this case!
         }
@@ -479,6 +496,53 @@ fn emit(dst: &mut EmitterWriter, cm: &codemap::CodeMap, rsp: RenderSpan,
     Ok(())
 }
 
+fn highlight_suggestion(err: &mut EmitterWriter,
+                        cm: &codemap::CodeMap,
+                        sp: Span,
+                        suggestion: &str)
+                        -> io::Result<()>
+{
+    let lines = cm.span_to_lines(sp);
+    assert!(!lines.lines.is_empty());
+
+    // To build up the result, we want to take the snippet from the first
+    // line that precedes the span, prepend that with the suggestion, and
+    // then append the snippet from the last line that trails the span.
+    let fm = &lines.file;
+
+    let first_line = &lines.lines[0];
+    let prefix = fm.get_line(first_line.line_index)
+                   .map(|l| &l[..first_line.start_col.0])
+                   .unwrap_or("");
+
+    let last_line = lines.lines.last().unwrap();
+    let suffix = fm.get_line(last_line.line_index)
+                   .map(|l| &l[last_line.end_col.0..])
+                   .unwrap_or("");
+
+    let complete = format!("{}{}{}", prefix, suggestion, suffix);
+
+    // print the suggestion without any line numbers, but leave
+    // space for them. This helps with lining up with previous
+    // snippets from the actual error being reported.
+    let fm = &*lines.file;
+    let mut lines = complete.lines();
+    for (line, line_index) in lines.by_ref().take(MAX_LINES).zip(first_line.line_index..) {
+        let elided_line_num = format!("{}", line_index+1);
+        try!(write!(&mut err.dst, "{0}:{1:2$} {3}\n",
+                    fm.name, "", elided_line_num.len(), line));
+    }
+
+    // if we elided some lines, add an ellipsis
+    if lines.next().is_some() {
+        let elided_line_num = format!("{}", first_line.line_index + MAX_LINES + 1);
+        try!(write!(&mut err.dst, "{0:1$} {0:2$} ...\n",
+                    "", fm.name.len(), elided_line_num.len()));
+    }
+
+    Ok(())
+}
+
 fn highlight_lines(err: &mut EmitterWriter,
                    cm: &codemap::CodeMap,
                    sp: Span,