Skip to content

Always show end line of multiline annotations #41136

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 9, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 119 additions & 77 deletions src/librustc_errors/emitter.rs
Original file line number Diff line number Diff line change
@@ -21,6 +21,8 @@ use std::io::prelude::*;
use std::io;
use std::rc::Rc;
use term;
use std::collections::HashMap;
use std::cmp::min;

/// Emitter trait for emitting errors.
pub trait Emitter {
@@ -156,15 +158,6 @@ impl EmitterWriter {
}
let lo = cm.lookup_char_pos(span_label.span.lo);
let mut hi = cm.lookup_char_pos(span_label.span.hi);
let mut is_minimized = false;

// If the span is long multi-line, simplify down to the span of one character
let max_multiline_span_length = 8;
if lo.line != hi.line && (hi.line - lo.line) > max_multiline_span_length {
hi.line = lo.line;
hi.col = CharPos(lo.col.0 + 1);
is_minimized = true;
}

// Watch out for "empty spans". If we get a span like 6..6, we
// want to just display a `^` at 6, so convert that to
@@ -175,16 +168,7 @@ impl EmitterWriter {
hi.col = CharPos(lo.col.0 + 1);
}

let mut ann = Annotation {
start_col: lo.col.0,
end_col: hi.col.0,
is_primary: span_label.is_primary,
label: span_label.label.clone(),
annotation_type: AnnotationType::Singleline,
};
if is_minimized {
ann.annotation_type = AnnotationType::Minimized;
} else if lo.line != hi.line {
let ann_type = if lo.line != hi.line {
let ml = MultilineAnnotation {
depth: 1,
line_start: lo.line,
@@ -194,8 +178,17 @@ impl EmitterWriter {
is_primary: span_label.is_primary,
label: span_label.label.clone(),
};
ann.annotation_type = AnnotationType::Multiline(ml.clone());
multiline_annotations.push((lo.file.clone(), ml));
multiline_annotations.push((lo.file.clone(), ml.clone()));
AnnotationType::Multiline(ml)
} else {
AnnotationType::Singleline
};
let ann = Annotation {
start_col: lo.col.0,
end_col: hi.col.0,
is_primary: span_label.is_primary,
label: span_label.label.clone(),
annotation_type: ann_type,
};

if !ann.is_multiline() {
@@ -233,9 +226,15 @@ impl EmitterWriter {
max_depth = ann.depth;
}
add_annotation_to_file(&mut output, file.clone(), ann.line_start, ann.as_start());
for line in ann.line_start + 1..ann.line_end {
let middle = min(ann.line_start + 4, ann.line_end);
for line in ann.line_start + 1..middle {
add_annotation_to_file(&mut output, file.clone(), line, ann.as_line());
}
if middle < ann.line_end - 1 {
for line in ann.line_end - 1..ann.line_end {
add_annotation_to_file(&mut output, file.clone(), line, ann.as_line());
}
}
add_annotation_to_file(&mut output, file, ann.line_end, ann.as_end());
}
for file_vec in output.iter_mut() {
@@ -249,16 +248,11 @@ impl EmitterWriter {
file: Rc<FileMap>,
line: &Line,
width_offset: usize,
multiline_depth: usize) {
code_offset: usize) -> Vec<(usize, Style)> {
let source_string = file.get_line(line.line_index - 1)
.unwrap_or("");

let line_offset = buffer.num_lines();
let code_offset = if multiline_depth == 0 {
width_offset
} else {
width_offset + multiline_depth + 1
};

// First create the source line we will highlight.
buffer.puts(line_offset, code_offset, &source_string, Style::Quotation);
@@ -286,7 +280,7 @@ impl EmitterWriter {
// previous borrow of `vec` occurs here
//
// For this reason, we group the lines into "highlight lines"
// and "annotations lines", where the highlight lines have the `~`.
// and "annotations lines", where the highlight lines have the `^`.

// Sort the annotations by (start, end col)
let mut annotations = line.annotations.clone();
@@ -410,25 +404,9 @@ impl EmitterWriter {
// If there are no annotations or the only annotations on this line are
// MultilineLine, then there's only code being shown, stop processing.
if line.annotations.is_empty() || line.annotations.iter()
.filter(|a| {
// Set the multiline annotation vertical lines to the left of
// the code in this line.
if let AnnotationType::MultilineLine(depth) = a.annotation_type {
buffer.putc(line_offset,
width_offset + depth - 1,
'|',
if a.is_primary {
Style::UnderlinePrimary
} else {
Style::UnderlineSecondary
});
false
} else {
true
}
}).collect::<Vec<_>>().len() == 0
.filter(|a| !a.is_line()).collect::<Vec<_>>().len() == 0
{
return;
return vec![];
}

// Write the colunmn separator.
@@ -483,16 +461,15 @@ impl EmitterWriter {
}
}

// Write the vertical lines for multiline spans and for labels that are
// on a different line as the underline.
// Write the vertical lines for labels that are on a different line as the underline.
//
// After this we will have:
//
// 2 | fn foo() {
// | __________
// | | |
// | |
// 3 | |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this | deleted?

// 3 |
// 4 | | }
// | |_
for &(pos, annotation) in &annotations_position {
@@ -528,16 +505,6 @@ impl EmitterWriter {
style);
}
}
AnnotationType::MultilineLine(depth) => {
// the first line will have already be filled when we checked
// wether there were any annotations for this line.
for p in line_offset + 1..line_offset + line_len + 2 {
buffer.putc(p,
width_offset + depth - 1,
'|',
style);
}
}
_ => (),
}
}
@@ -548,11 +515,11 @@ impl EmitterWriter {
//
// 2 | fn foo() {
// | __________ starting here...
// | | |
// | | something about `foo`
// 3 | |
// 4 | | }
// | |_ ...ending here: test
// | |
// | something about `foo`
// 3 |
// 4 | }
// | _ ...ending here: test
for &(pos, annotation) in &annotations_position {
let style = if annotation.is_primary {
Style::LabelPrimary
@@ -591,11 +558,11 @@ impl EmitterWriter {
//
// 2 | fn foo() {
// | ____-_____^ starting here...
// | | |
// | | something about `foo`
// 3 | |
// 4 | | }
// | |_^ ...ending here: test
// | |
// | something about `foo`
// 3 |
// 4 | }
// | _^ ...ending here: test
for &(_, annotation) in &annotations_position {
let (underline, style) = if annotation.is_primary {
('^', Style::UnderlinePrimary)
@@ -609,6 +576,20 @@ impl EmitterWriter {
style);
}
}
annotations_position.iter().filter_map(|&(_, annotation)| {
match annotation.annotation_type {
AnnotationType::MultilineStart(p) | AnnotationType::MultilineEnd(p) => {
let style = if annotation.is_primary {
Style::LabelPrimary
} else {
Style::LabelSecondary
};
Some((p, style))
},
_ => None
}

}).collect::<Vec<_>>()
}

fn get_multispan_max_line_num(&mut self, msp: &MultiSpan) -> usize {
@@ -902,22 +883,64 @@ impl EmitterWriter {
let buffer_msg_line_offset = buffer.num_lines();
draw_col_separator_no_space(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1);

// Contains the vertical lines' positions for active multiline annotations
let mut multilines = HashMap::new();

// Next, output the annotate source for this file
for line_idx in 0..annotated_file.lines.len() {
self.render_source_line(&mut buffer,
annotated_file.file.clone(),
&annotated_file.lines[line_idx],
3 + max_line_num_len,
annotated_file.multiline_depth);
let previous_buffer_line = buffer.num_lines();

let width_offset = 3 + max_line_num_len;
let code_offset = if annotated_file.multiline_depth == 0 {
width_offset
} else {
width_offset + annotated_file.multiline_depth + 1
};

let depths = self.render_source_line(&mut buffer,
annotated_file.file.clone(),
&annotated_file.lines[line_idx],
width_offset,
code_offset);

let mut to_add = HashMap::new();

for (depth, style) in depths {
if multilines.get(&depth).is_some() {
multilines.remove(&depth);
} else {
to_add.insert(depth, style);
}
}

// Set the multiline annotation vertical lines to the left of
// the code in this line.
for (depth, style) in &multilines {
for line in previous_buffer_line..buffer.num_lines() {
draw_multiline_line(&mut buffer,
line,
width_offset,
*depth,
*style);
}
}
// check to see if we need to print out or elide lines that come between
// this annotated line and the next one
// this annotated line and the next one.
if line_idx < (annotated_file.lines.len() - 1) {
let line_idx_delta = annotated_file.lines[line_idx + 1].line_index -
annotated_file.lines[line_idx].line_index;
if line_idx_delta > 2 {
let last_buffer_line_num = buffer.num_lines();
buffer.puts(last_buffer_line_num, 0, "...", Style::LineNumber);

// Set the multiline annotation vertical lines on `...` bridging line.
for (depth, style) in &multilines {
draw_multiline_line(&mut buffer,
last_buffer_line_num,
width_offset,
*depth,
*style);
}
} else if line_idx_delta == 2 {
let unannotated_line = annotated_file.file
.get_line(annotated_file.lines[line_idx].line_index)
@@ -932,11 +955,21 @@ impl EmitterWriter {
Style::LineNumber);
draw_col_separator(&mut buffer, last_buffer_line_num, 1 + max_line_num_len);
buffer.puts(last_buffer_line_num,
3 + max_line_num_len,
code_offset,
&unannotated_line,
Style::Quotation);

for (depth, style) in &multilines {
draw_multiline_line(&mut buffer,
last_buffer_line_num,
width_offset,
*depth,
*style);
}
}
}

multilines.extend(&to_add);
}
}

@@ -1085,6 +1118,15 @@ fn draw_note_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
buffer.puts(line, col, "= ", Style::LineNumber);
}

fn draw_multiline_line(buffer: &mut StyledBuffer,
line: usize,
offset: usize,
depth: usize,
style: Style)
{
buffer.putc(line, offset + depth - 1, '|', style);
}

fn num_overlap(a_start: usize, a_end: usize, b_start: usize, b_end:usize, inclusive: bool) -> bool {
let extra = if inclusive {
1
13 changes: 3 additions & 10 deletions src/librustc_errors/snippet.rs
Original file line number Diff line number Diff line change
@@ -97,9 +97,6 @@ pub enum AnnotationType {
/// Annotation under a single line of code
Singleline,

/// Annotation under the first character of a multiline span
Minimized,

/// Annotation enclosing the first and last character of a multiline span
Multiline(MultilineAnnotation),

@@ -118,6 +115,9 @@ pub enum AnnotationType {
/// Annotation marking the last character of a fully shown multiline span
MultilineEnd(usize),
/// Line at the left enclosing the lines of a fully shown multiline span
// Just a placeholder for the drawing algorithm, to know that it shouldn't skip the first 4
// and last 2 lines of code. The actual line is drawn in `emit_message_default` and not in
// `draw_multiline_line`.
MultilineLine(usize),
}

@@ -144,13 +144,6 @@ pub struct Annotation {
}

impl Annotation {
pub fn is_minimized(&self) -> bool {
match self.annotation_type {
AnnotationType::Minimized => true,
_ => false,
}
}

/// Wether this annotation is a vertical line placeholder.
pub fn is_line(&self) -> bool {
if let AnnotationType::MultilineLine(_) = self.annotation_type {
134 changes: 134 additions & 0 deletions src/libsyntax/test_snippet.rs
Original file line number Diff line number Diff line change
@@ -932,3 +932,137 @@ error: foo
"#);
}

#[test]
fn long_snippet() {
test_harness(r#"
fn foo() {
X0 Y0 Z0
X1 Y1 Z1
1
2
3
4
5
6
7
8
9
10
X2 Y2 Z2
X3 Y3 Z3
}
"#,
vec![
SpanLabel {
start: Position {
string: "Y0",
count: 1,
},
end: Position {
string: "X1",
count: 1,
},
label: "`X` is a good letter",
},
SpanLabel {
start: Position {
string: "Z1",
count: 1,
},
end: Position {
string: "Z3",
count: 1,
},
label: "`Y` is a good letter too",
},
],
r#"
error: foo
--> test.rs:3:6
|
3 | X0 Y0 Z0
| ______^ starting here...
4 | | X1 Y1 Z1
| |____^____- starting here...
| ||____|
| | ...ending here: `X` is a good letter
5 | | 1
6 | | 2
7 | | 3
... |
15 | | X2 Y2 Z2
16 | | X3 Y3 Z3
| |___________- ...ending here: `Y` is a good letter too
"#);
}

#[test]
fn long_snippet_multiple_spans() {
test_harness(r#"
fn foo() {
X0 Y0 Z0
1
2
3
X1 Y1 Z1
4
5
6
X2 Y2 Z2
7
8
9
10
X3 Y3 Z3
}
"#,
vec![
SpanLabel {
start: Position {
string: "Y0",
count: 1,
},
end: Position {
string: "Y3",
count: 1,
},
label: "`Y` is a good letter",
},
SpanLabel {
start: Position {
string: "Z1",
count: 1,
},
end: Position {
string: "Z2",
count: 1,
},
label: "`Z` is a good letter too",
},
],
r#"
error: foo
--> test.rs:3:6
|
3 | X0 Y0 Z0
| ______^ starting here...
4 | | 1
5 | | 2
6 | | 3
7 | | X1 Y1 Z1
| |_________- starting here...
8 | || 4
9 | || 5
10 | || 6
11 | || X2 Y2 Z2
| ||__________- ...ending here: `Z` is a good letter too
... |
15 | | 10
16 | | X3 Y3 Z3
| |_______^ ...ending here: `Y` is a good letter
"#);
}

9 changes: 3 additions & 6 deletions src/test/ui/span/impl-wrong-item-for-trait.stderr
Original file line number Diff line number Diff line change
@@ -24,8 +24,7 @@ error[E0046]: not all trait items implemented, missing: `bar`
23 | | //~^ ERROR E0046
24 | | //~| NOTE missing `bar` in implementation
25 | | const bar: u64 = 1;
26 | | //~^ ERROR E0323
27 | | //~| NOTE does not match trait
... |
28 | | const MY_CONST: u32 = 1;
29 | | }
| |_^ ...ending here: missing `bar` in implementation
@@ -50,8 +49,7 @@ error[E0046]: not all trait items implemented, missing: `MY_CONST`
34 | | //~^ ERROR E0046
35 | | //~| NOTE missing `MY_CONST` in implementation
36 | | fn bar(&self) {}
37 | | fn MY_CONST() {}
38 | | //~^ ERROR E0324
... |
39 | | //~| NOTE does not match trait
40 | | }
| |_^ ...ending here: missing `MY_CONST` in implementation
@@ -76,8 +74,7 @@ error[E0046]: not all trait items implemented, missing: `bar`
45 | | //~^ ERROR E0046
46 | | //~| NOTE missing `bar` in implementation
47 | | type bar = u64;
48 | | //~^ ERROR E0325
49 | | //~| NOTE does not match trait
... |
50 | | const MY_CONST: u32 = 1;
51 | | }
| |_^ ...ending here: missing `bar` in implementation
11 changes: 9 additions & 2 deletions src/test/ui/span/issue-23729.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
error[E0046]: not all trait items implemented, missing: `Item`
--> $DIR/issue-23729.rs:20:9
|
20 | impl Iterator for Recurrence {
| ^ missing `Item` in implementation
20 | impl Iterator for Recurrence {
| _________^ starting here...
21 | | //~^ ERROR E0046
22 | | //~| NOTE missing `Item` in implementation
23 | | //~| NOTE `Item` from trait: `type Item;`
... |
36 | | }
37 | | }
| |_________^ ...ending here: missing `Item` in implementation
|
= note: `Item` from trait: `type Item;`

3 changes: 1 addition & 2 deletions src/test/ui/span/issue-23827.stderr
Original file line number Diff line number Diff line change
@@ -6,8 +6,7 @@ error[E0046]: not all trait items implemented, missing: `Output`
37 | | //~^ ERROR E0046
38 | | //~| NOTE missing `Output` in implementation
39 | | //~| NOTE `Output` from trait: `type Output;`
40 | | extern "rust-call" fn call_once(self, (comp,): (C,)) -> Prototype {
41 | | Fn::call(&self, (comp,))
... |
42 | | }
43 | | }
| |_^ ...ending here: missing `Output` in implementation