Skip to content

Commit 0fed0c6

Browse files
committed
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.
1 parent 38677c4 commit 0fed0c6

File tree

2 files changed

+72
-1
lines changed

2 files changed

+72
-1
lines changed

src/librustc/session/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ impl Session {
142142
pub fn span_end_note(&self, sp: Span, msg: &str) {
143143
self.diagnostic().span_end_note(sp, msg)
144144
}
145+
146+
/// Prints out a message with a suggested edit of the code.
147+
///
148+
/// See `diagnostic::RenderSpan::Suggestion` for more information.
149+
pub fn span_suggestion(&self, sp: Span, msg: &str, suggestion: String) {
150+
self.diagnostic().span_suggestion(sp, msg, suggestion)
151+
}
145152
pub fn span_help(&self, sp: Span, msg: &str) {
146153
self.diagnostic().span_help(sp, msg)
147154
}

src/libsyntax/diagnostic.rs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use codemap;
1818
use diagnostics;
1919

2020
use std::cell::{RefCell, Cell};
21+
use std::cmp;
2122
use std::fmt;
2223
use std::io::prelude::*;
2324
use std::io;
@@ -28,7 +29,7 @@ use libc;
2829
/// maximum number of lines we will print for each error; arbitrary.
2930
const MAX_LINES: usize = 6;
3031

31-
#[derive(Clone, Copy)]
32+
#[derive(Clone)]
3233
pub enum RenderSpan {
3334
/// A FullSpan renders with both with an initial line for the
3435
/// message, prefixed by file:linenum, followed by a summary of
@@ -42,6 +43,12 @@ pub enum RenderSpan {
4243
/// of the span).
4344
EndSpan(Span),
4445

46+
/// A suggestion renders with both with an initial line for the
47+
/// message, prefixed by file:linenum, followed by a summary
48+
/// of hypothetical source code, where the `String` is spliced
49+
/// into the lines in place of the code covered by the span.
50+
Suggestion(Span, String),
51+
4552
/// A FileLine renders with just a line for the message prefixed
4653
/// by file:linenum.
4754
FileLine(Span),
@@ -51,6 +58,7 @@ impl RenderSpan {
5158
fn span(&self) -> Span {
5259
match *self {
5360
FullSpan(s) |
61+
Suggestion(s, _) |
5462
EndSpan(s) |
5563
FileLine(s) =>
5664
s
@@ -124,6 +132,12 @@ impl SpanHandler {
124132
pub fn span_help(&self, sp: Span, msg: &str) {
125133
self.handler.emit(Some((&self.cm, sp)), msg, Help);
126134
}
135+
/// Prints out a message with a suggested edit of the code.
136+
///
137+
/// See `diagnostic::RenderSpan::Suggestion` for more information.
138+
pub fn span_suggestion(&self, sp: Span, msg: &str, suggestion: String) {
139+
self.handler.custom_emit(&self.cm, Suggestion(sp, suggestion), msg, Help);
140+
}
127141
pub fn fileline_note(&self, sp: Span, msg: &str) {
128142
self.handler.custom_emit(&self.cm, FileLine(sp), msg, Note);
129143
}
@@ -455,6 +469,9 @@ fn emit(dst: &mut EmitterWriter, cm: &codemap::CodeMap, rsp: RenderSpan,
455469
EndSpan(_) => {
456470
try!(end_highlight_lines(dst, cm, sp, lvl, cm.span_to_lines(sp)));
457471
}
472+
Suggestion(_, ref suggestion) => {
473+
try!(highlight_suggestion(dst, cm, sp, suggestion));
474+
}
458475
FileLine(..) => {
459476
// no source text in this case!
460477
}
@@ -479,6 +496,53 @@ fn emit(dst: &mut EmitterWriter, cm: &codemap::CodeMap, rsp: RenderSpan,
479496
Ok(())
480497
}
481498

499+
fn highlight_suggestion(err: &mut EmitterWriter,
500+
cm: &codemap::CodeMap,
501+
sp: Span,
502+
suggestion: &str)
503+
-> io::Result<()>
504+
{
505+
let lines = cm.span_to_lines(sp);
506+
assert!(!lines.lines.is_empty());
507+
508+
// To build up the result, we want to take the snippet from the first
509+
// line that precedes the span, prepend that with the suggestion, and
510+
// then append the snippet from the last line that trails the span.
511+
let fm = &lines.file;
512+
513+
let first_line = &lines.lines[0];
514+
let prefix = fm.get_line(first_line.line_index)
515+
.map(|l| &l[..first_line.start_col.0])
516+
.unwrap_or("");
517+
518+
let last_line = lines.lines.last().unwrap();
519+
let suffix = fm.get_line(last_line.line_index)
520+
.map(|l| &l[last_line.end_col.0..])
521+
.unwrap_or("");
522+
523+
let complete = format!("{}{}{}", prefix, suggestion, suffix);
524+
525+
// print the suggestion without any line numbers, but leave
526+
// space for them. This helps with lining up with previous
527+
// snippets from the actual error being reported.
528+
let line_num_space = format!("{0:1$}", "", format!("{}", first_line.line_index+1).len());
529+
let fm = &*lines.file;
530+
let mut lines = complete.lines();
531+
for line in lines.by_ref().take(MAX_LINES) {
532+
try!(write!(&mut err.dst, "{}:{} {}\n", fm.name, line_num_space, line));
533+
}
534+
535+
// if we elided some lines, add an ellipsis
536+
if lines.next().is_some() {
537+
try!(write!(&mut err.dst, "{0:1$} {2} ...\n",
538+
"",
539+
fm.name.len(),
540+
line_num_space));
541+
}
542+
543+
Ok(())
544+
}
545+
482546
fn highlight_lines(err: &mut EmitterWriter,
483547
cm: &codemap::CodeMap,
484548
sp: Span,

0 commit comments

Comments
 (0)