Skip to content

Commit 41c9950

Browse files
committed
Show multiline spans in full if short enough
When dealing with multiline spans that span few lines, show the complete span instead of restricting to the first character of the first line. For example, instead of: ``` % ./rustc file2.rs error[E0277]: the trait bound `{integer}: std::ops::Add<()>` is not satisfied --> file2.rs:13:9 | 13 | foo(1 + bar(x, | ^ trait `{integer}: std::ops::Add<()>` not satisfied | ``` show ``` % ./rustc file2.rs error[E0277]: the trait bound `{integer}: std::ops::Add<()>` is not satisfied --> file2.rs:13:9 | 13 | foo(1 + bar(x, | ________^ starting here... 14 | | y), | |_____________^ ...ending here: trait `{integer}: std::ops::Add<()>` not satisfied | ```
1 parent b5f6d7e commit 41c9950

File tree

6 files changed

+335
-60
lines changed

6 files changed

+335
-60
lines changed

src/librustc_errors/emitter.rs

+165-51
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use syntax_pos::{COMMAND_LINE_SP, DUMMY_SP, FileMap, Span, MultiSpan, CharPos};
1414

1515
use {Level, CodeSuggestion, DiagnosticBuilder, SubDiagnostic, CodeMapper};
1616
use RenderSpan::*;
17-
use snippet::{StyledString, Style, Annotation, Line};
17+
use snippet::{Annotation, AnnotationType, Line, StyledString, Style};
1818
use styled_buffer::StyledBuffer;
1919

2020
use std::io::prelude::*;
@@ -65,6 +65,7 @@ pub struct EmitterWriter {
6565
struct FileWithAnnotatedLines {
6666
file: Rc<FileMap>,
6767
lines: Vec<Line>,
68+
multiline_depth: usize,
6869
}
6970

7071

@@ -137,10 +138,12 @@ impl EmitterWriter {
137138
line_index: line_index,
138139
annotations: vec![ann],
139140
}],
141+
multiline_depth: 0,
140142
});
141143
}
142144

143145
let mut output = vec![];
146+
let mut multiline_annotations = vec![];
144147

145148
if let Some(ref cm) = self.cm {
146149
for span_label in msp.span_labels() {
@@ -151,8 +154,9 @@ impl EmitterWriter {
151154
let mut hi = cm.lookup_char_pos(span_label.span.hi);
152155
let mut is_minimized = false;
153156

154-
// If the span is multi-line, simplify down to the span of one character
155-
if lo.line != hi.line {
157+
// If the span is long multi-line, simplify down to the span of one character
158+
let max_multiline_span_length = 8;
159+
if lo.line != hi.line && (hi.line - lo.line) > max_multiline_span_length {
156160
hi.line = lo.line;
157161
hi.col = CharPos(lo.col.0 + 1);
158162
is_minimized = true;
@@ -163,37 +167,92 @@ impl EmitterWriter {
163167
// 6..7. This is degenerate input, but it's best to degrade
164168
// gracefully -- and the parser likes to supply a span like
165169
// that for EOF, in particular.
166-
if lo.col == hi.col {
170+
if lo.col == hi.col && lo.line == hi.line {
167171
hi.col = CharPos(lo.col.0 + 1);
168172
}
169173

170-
add_annotation_to_file(&mut output,
171-
lo.file,
172-
lo.line,
173-
Annotation {
174-
start_col: lo.col.0,
175-
end_col: hi.col.0,
176-
is_primary: span_label.is_primary,
177-
is_minimized: is_minimized,
178-
label: span_label.label.clone(),
179-
});
174+
let mut ann = Annotation {
175+
start_col: lo.col.0,
176+
end_col: hi.col.0,
177+
is_primary: span_label.is_primary,
178+
label: span_label.label.clone(),
179+
annotation_type: AnnotationType::Singleline,
180+
};
181+
if is_minimized {
182+
ann.annotation_type = AnnotationType::Minimized;
183+
} else if lo.line != hi.line {
184+
ann.annotation_type = AnnotationType::Multiline {
185+
depth: 1,
186+
line_start: lo.line,
187+
line_end: hi.line,
188+
};
189+
multiline_annotations.push((lo.file.clone(), ann.clone()));
190+
};
191+
192+
if !ann.is_multiline() {
193+
add_annotation_to_file(&mut output,
194+
lo.file,
195+
lo.line,
196+
ann);
197+
}
198+
}
199+
}
200+
201+
// Find overlapping multiline annotations, put them at different depths
202+
multiline_annotations.sort_by(|a, b| a.1.start_col.cmp(&b.1.start_col));
203+
for item in multiline_annotations.clone() {
204+
let ann = item.1;
205+
for item in multiline_annotations.iter_mut() {
206+
let ref mut a = item.1;
207+
let ann_depth = ann.annotation_type.depth();
208+
let a_depth = a.annotation_type.depth();
209+
// Move all other multiline annotations overlapping with this one
210+
// one level to the right.
211+
if &ann != a && ann.end_col > a.start_col && ann_depth == a_depth {
212+
a.annotation_type.increase_depth();
213+
} else {
214+
break
215+
}
216+
}
217+
}
218+
219+
let mut max_depth = 0; // max overlapping multiline spans
220+
for (file, ann) in multiline_annotations {
221+
if let AnnotationType::Multiline {line_start, line_end, depth} = ann.annotation_type {
222+
if depth > max_depth {
223+
max_depth = depth;
224+
}
225+
add_annotation_to_file(&mut output, file.clone(), line_start, ann.as_start());
226+
for line in line_start + 1..line_end {
227+
add_annotation_to_file(&mut output, file.clone(), line, ann.as_line());
228+
}
229+
add_annotation_to_file(&mut output, file, line_end, ann.as_end());
180230
}
181231
}
232+
for file_vec in output.iter_mut() {
233+
file_vec.multiline_depth = max_depth;
234+
}
182235
output
183236
}
184237

185238
fn render_source_line(&self,
186239
buffer: &mut StyledBuffer,
187240
file: Rc<FileMap>,
188241
line: &Line,
189-
width_offset: usize) {
242+
width_offset: usize,
243+
multiline_depth: usize) {
190244
let source_string = file.get_line(line.line_index - 1)
191245
.unwrap_or("");
192246

193247
let line_offset = buffer.num_lines();
248+
let code_offset = if multiline_depth == 0 {
249+
width_offset
250+
} else {
251+
width_offset + multiline_depth + 1
252+
};
194253

195254
// First create the source line we will highlight.
196-
buffer.puts(line_offset, width_offset, &source_string, Style::Quotation);
255+
buffer.puts(line_offset, code_offset, &source_string, Style::Quotation);
197256
buffer.puts(line_offset,
198257
0,
199258
&(line.line_index.to_string()),
@@ -228,39 +287,74 @@ impl EmitterWriter {
228287
let mut annotations = line.annotations.clone();
229288
annotations.sort();
230289

290+
let mut only_source_shown = true;
231291
// Next, create the highlight line.
232292
for annotation in &annotations {
293+
let (underline, style) = if annotation.is_primary {
294+
('^', Style::UnderlinePrimary)
295+
} else {
296+
('-', Style::UnderlineSecondary)
297+
};
298+
if let AnnotationType::MultilineLine(depth) = annotation.annotation_type {
299+
draw_col_separator_no_space_with_style(buffer,
300+
line_offset,
301+
width_offset + depth - 1,
302+
style);
303+
break;
304+
}
233305
for p in annotation.start_col..annotation.end_col {
234-
if annotation.is_primary {
235-
buffer.putc(line_offset + 1,
236-
width_offset + p,
237-
'^',
238-
Style::UnderlinePrimary);
239-
if !annotation.is_minimized {
240-
buffer.set_style(line_offset, width_offset + p, Style::UnderlinePrimary);
241-
}
242-
} else {
306+
buffer.putc(line_offset + 1,
307+
code_offset + p,
308+
underline,
309+
style);
310+
if !annotation.is_minimized() {
311+
buffer.set_style(line_offset, code_offset + p, style);
312+
}
313+
}
314+
// For multiline spans, add enclosing line
315+
if let AnnotationType::MultilineStart(depth) = annotation.annotation_type {
316+
for p in width_offset + depth..code_offset + annotation.end_col - 1 {
243317
buffer.putc(line_offset + 1,
244-
width_offset + p,
245-
'-',
246-
Style::UnderlineSecondary);
247-
if !annotation.is_minimized {
248-
buffer.set_style(line_offset, width_offset + p, Style::UnderlineSecondary);
249-
}
318+
p,
319+
'_',
320+
style);
250321
}
251322
}
323+
if let AnnotationType::MultilineEnd(depth) = annotation.annotation_type {
324+
draw_col_separator_no_space_with_style(buffer,
325+
line_offset,
326+
width_offset + depth - 1,
327+
style);
328+
draw_col_separator_no_space_with_style(buffer,
329+
line_offset + 1,
330+
width_offset + depth - 1,
331+
style);
332+
draw_range(buffer,
333+
'_',
334+
line_offset + 1,
335+
width_offset + depth,
336+
code_offset + annotation.end_col - 1,
337+
style);
338+
}
339+
only_source_shown = false;
340+
}
341+
if !only_source_shown {
342+
draw_col_separator(buffer, line_offset + 1, width_offset - 2);
252343
}
253-
draw_col_separator(buffer, line_offset + 1, width_offset - 2);
254344

255345
// Now we are going to write labels in. To start, we'll exclude
256346
// the annotations with no labels.
257-
let (labeled_annotations, unlabeled_annotations): (Vec<_>, _) = annotations.into_iter()
258-
.partition(|a| a.label.is_some());
347+
let (labeled_annotations, unlabeled_annotations): (Vec<_>, _) = annotations
348+
.clone().into_iter().partition(|a| a.label.is_some());
259349

260350
// If there are no annotations that need text, we're done.
261351
if labeled_annotations.is_empty() {
262352
return;
263353
}
354+
355+
let multiline_annotations = annotations.iter()
356+
.filter(|a| a.is_multiline()).collect::<Vec<_>>();
357+
264358
// Now add the text labels. We try, when possible, to stick the rightmost
265359
// annotation at the end of the highlight line:
266360
//
@@ -305,11 +399,11 @@ impl EmitterWriter {
305399
// append the label afterwards; we keep it in a separate
306400
// string
307401
let highlight_label: String = format!(" {}", last.label.as_ref().unwrap());
308-
if last.is_primary {
309-
buffer.append(line_offset + 1, &highlight_label, Style::LabelPrimary);
402+
buffer.append(line_offset + 1, &highlight_label, if last.is_primary {
403+
Style::LabelPrimary
310404
} else {
311-
buffer.append(line_offset + 1, &highlight_label, Style::LabelSecondary);
312-
}
405+
Style::LabelSecondary
406+
});
313407
labeled_annotations = previous;
314408
}
315409
}
@@ -330,28 +424,35 @@ impl EmitterWriter {
330424
// For each blank line, draw a `|` at our column. The
331425
// text ought to be long enough for this.
332426
for index in 2..blank_lines {
333-
if annotation.is_primary {
334-
buffer.putc(line_offset + index,
335-
width_offset + annotation.start_col,
336-
'|',
337-
Style::UnderlinePrimary);
427+
let style = if annotation.is_primary {
428+
Style::UnderlinePrimary
338429
} else {
339-
buffer.putc(line_offset + index,
340-
width_offset + annotation.start_col,
341-
'|',
342-
Style::UnderlineSecondary);
430+
Style::UnderlineSecondary
431+
};
432+
draw_col_separator_no_space_with_style(buffer,
433+
line_offset + index,
434+
code_offset + annotation.start_col,
435+
style);
436+
for a in multiline_annotations.iter() {
437+
if let AnnotationType::MultilineLine(depth) = a.annotation_type {
438+
draw_col_separator_no_space_with_style(buffer,
439+
line_offset + index,
440+
width_offset + depth - 1,
441+
style);
442+
}
343443
}
344444
draw_col_separator(buffer, line_offset + index, width_offset - 2);
445+
345446
}
346447

347448
if annotation.is_primary {
348449
buffer.puts(line_offset + blank_lines,
349-
width_offset + annotation.start_col,
450+
code_offset + annotation.start_col,
350451
annotation.label.as_ref().unwrap(),
351452
Style::LabelPrimary);
352453
} else {
353454
buffer.puts(line_offset + blank_lines,
354-
width_offset + annotation.start_col,
455+
code_offset + annotation.start_col,
355456
annotation.label.as_ref().unwrap(),
356457
Style::LabelSecondary);
357458
}
@@ -577,7 +678,8 @@ impl EmitterWriter {
577678
self.render_source_line(&mut buffer,
578679
annotated_file.file.clone(),
579680
&annotated_file.lines[line_idx],
580-
3 + max_line_num_len);
681+
3 + max_line_num_len,
682+
annotated_file.multiline_depth);
581683

582684
// check to see if we need to print out or elide lines that come between
583685
// this annotated line and the next one
@@ -729,7 +831,19 @@ fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
729831
}
730832

731833
fn draw_col_separator_no_space(buffer: &mut StyledBuffer, line: usize, col: usize) {
732-
buffer.puts(line, col, "|", Style::LineNumber);
834+
draw_col_separator_no_space_with_style(buffer, line, col, Style::LineNumber);
835+
}
836+
837+
fn draw_col_separator_no_space_with_style(buffer: &mut StyledBuffer, line: usize,
838+
col: usize, style: Style) {
839+
buffer.putc(line, col, '|', style);
840+
}
841+
842+
fn draw_range(buffer: &mut StyledBuffer, symbol: char, line: usize,
843+
col_from: usize, col_to: usize, style: Style) {
844+
for col in col_from..col_to {
845+
buffer.putc(line, col, symbol, style);
846+
}
733847
}
734848

735849
fn draw_note_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {

0 commit comments

Comments
 (0)