diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 367b85ac726db..a52628ceb47ab 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -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, 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::>().len() == 0 + .filter(|a| !a.is_line()).collect::>().len() == 0 { - return; + return vec![]; } // Write the colunmn separator. @@ -483,8 +461,7 @@ 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: // @@ -492,7 +469,7 @@ impl EmitterWriter { // | __________ // | | | // | | - // 3 | | + // 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::>() } 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 diff --git a/src/librustc_errors/snippet.rs b/src/librustc_errors/snippet.rs index 5debbf4d37c20..9aa4682e1afcb 100644 --- a/src/librustc_errors/snippet.rs +++ b/src/librustc_errors/snippet.rs @@ -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 { diff --git a/src/libsyntax/test_snippet.rs b/src/libsyntax/test_snippet.rs index c537a0ee16644..a74f59b004bb7 100644 --- a/src/libsyntax/test_snippet.rs +++ b/src/libsyntax/test_snippet.rs @@ -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 + +"#); +} + diff --git a/src/test/ui/span/impl-wrong-item-for-trait.stderr b/src/test/ui/span/impl-wrong-item-for-trait.stderr index 717f5ee200c74..367af12bb6b1c 100644 --- a/src/test/ui/span/impl-wrong-item-for-trait.stderr +++ b/src/test/ui/span/impl-wrong-item-for-trait.stderr @@ -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 diff --git a/src/test/ui/span/issue-23729.stderr b/src/test/ui/span/issue-23729.stderr index 493ca01778bc1..701576ff6f475 100644 --- a/src/test/ui/span/issue-23729.stderr +++ b/src/test/ui/span/issue-23729.stderr @@ -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;` diff --git a/src/test/ui/span/issue-23827.stderr b/src/test/ui/span/issue-23827.stderr index 6c1c246753011..457fed34ff1ad 100644 --- a/src/test/ui/span/issue-23827.stderr +++ b/src/test/ui/span/issue-23827.stderr @@ -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