Always show end line of multiline annotations

```rust
error[E0046]: not all trait items implemented, missing: `Item`
  --> $DIR/issue-23729.rs:20:9
   |
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;`
```

instead of

```rust
error[E0046]: not all trait items implemented, missing: `Item`
  --> $DIR/issue-23729.rs:20:9
   |
20 |         impl Iterator for Recurrence {
   |         ^ missing `Item` in implementation
   |
   = note: `Item` from trait: `type Item;`
```
This commit is contained in:
Esteban Küber 2017-04-06 12:18:18 -07:00
parent 9e84bf8096
commit 4bc7f5b52c
6 changed files with 269 additions and 97 deletions

View File

@ -21,6 +21,8 @@
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 @@ fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>,
}
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 @@ fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>,
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 @@ fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>,
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 @@ fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>,
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 @@ fn render_source_line(&self,
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 @@ fn render_source_line(&self,
// 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 @@ fn render_source_line(&self,
// 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,8 +461,7 @@ fn render_source_line(&self,
}
}
// 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:
//
@ -492,7 +469,7 @@ fn render_source_line(&self,
// | __________
// | | |
// | |
// 3 | |
// 3 |
// 4 | | }
// | |_
for &(pos, annotation) in &annotations_position {
@ -528,16 +505,6 @@ fn render_source_line(&self,
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 @@ fn render_source_line(&self,
//
// 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 @@ fn render_source_line(&self,
//
// 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 @@ fn render_source_line(&self,
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 @@ fn emit_message_default(&mut self,
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 @@ fn emit_message_default(&mut self,
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

View File

@ -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 {

View File

@ -932,3 +932,137 @@ fn 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
"#);
}

View File

@ -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

View File

@ -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;`

View File

@ -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