Skip to content

Commit cd38c8f

Browse files
authored
Rollup merge of rust-lang#61191 - phansch:annotate_snippet_refactorings1, r=estebank
librustc_errors: Move annotation collection to own impl Extracted from work on rust-lang#59346. This moves the annotation collection to the `FileWithAnnotatedLines` impl to allow easier re-use in a separate EmitterWriter. Even without that new EmitterWriter present, I think it makes sense to have this as an associated function.
2 parents aad084a + 96e3fb2 commit cd38c8f

File tree

1 file changed

+174
-167
lines changed

1 file changed

+174
-167
lines changed

src/librustc_errors/emitter.rs

+174-167
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ impl ColorConfig {
162162
}
163163
}
164164

165+
/// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short`
165166
pub struct EmitterWriter {
166167
dst: Destination,
167168
sm: Option<Lrc<SourceMapperDyn>>,
@@ -170,7 +171,8 @@ pub struct EmitterWriter {
170171
ui_testing: bool,
171172
}
172173

173-
struct FileWithAnnotatedLines {
174+
#[derive(Debug)]
175+
pub struct FileWithAnnotatedLines {
174176
file: Lrc<SourceFile>,
175177
lines: Vec<Line>,
176178
multiline_depth: usize,
@@ -221,169 +223,6 @@ impl EmitterWriter {
221223
}
222224
}
223225

224-
fn preprocess_annotations(&mut self, msp: &MultiSpan) -> Vec<FileWithAnnotatedLines> {
225-
fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>,
226-
file: Lrc<SourceFile>,
227-
line_index: usize,
228-
ann: Annotation) {
229-
230-
for slot in file_vec.iter_mut() {
231-
// Look through each of our files for the one we're adding to
232-
if slot.file.name == file.name {
233-
// See if we already have a line for it
234-
for line_slot in &mut slot.lines {
235-
if line_slot.line_index == line_index {
236-
line_slot.annotations.push(ann);
237-
return;
238-
}
239-
}
240-
// We don't have a line yet, create one
241-
slot.lines.push(Line {
242-
line_index,
243-
annotations: vec![ann],
244-
});
245-
slot.lines.sort();
246-
return;
247-
}
248-
}
249-
// This is the first time we're seeing the file
250-
file_vec.push(FileWithAnnotatedLines {
251-
file,
252-
lines: vec![Line {
253-
line_index,
254-
annotations: vec![ann],
255-
}],
256-
multiline_depth: 0,
257-
});
258-
}
259-
260-
let mut output = vec![];
261-
let mut multiline_annotations = vec![];
262-
263-
if let Some(ref sm) = self.sm {
264-
for span_label in msp.span_labels() {
265-
if span_label.span.is_dummy() {
266-
continue;
267-
}
268-
269-
let lo = sm.lookup_char_pos(span_label.span.lo());
270-
let mut hi = sm.lookup_char_pos(span_label.span.hi());
271-
272-
// Watch out for "empty spans". If we get a span like 6..6, we
273-
// want to just display a `^` at 6, so convert that to
274-
// 6..7. This is degenerate input, but it's best to degrade
275-
// gracefully -- and the parser likes to supply a span like
276-
// that for EOF, in particular.
277-
278-
if lo.col_display == hi.col_display && lo.line == hi.line {
279-
hi.col_display += 1;
280-
}
281-
282-
let ann_type = if lo.line != hi.line {
283-
let ml = MultilineAnnotation {
284-
depth: 1,
285-
line_start: lo.line,
286-
line_end: hi.line,
287-
start_col: lo.col_display,
288-
end_col: hi.col_display,
289-
is_primary: span_label.is_primary,
290-
label: span_label.label.clone(),
291-
overlaps_exactly: false,
292-
};
293-
multiline_annotations.push((lo.file.clone(), ml.clone()));
294-
AnnotationType::Multiline(ml)
295-
} else {
296-
AnnotationType::Singleline
297-
};
298-
let ann = Annotation {
299-
start_col: lo.col_display,
300-
end_col: hi.col_display,
301-
is_primary: span_label.is_primary,
302-
label: span_label.label.clone(),
303-
annotation_type: ann_type,
304-
};
305-
306-
if !ann.is_multiline() {
307-
add_annotation_to_file(&mut output, lo.file, lo.line, ann);
308-
}
309-
}
310-
}
311-
312-
// Find overlapping multiline annotations, put them at different depths
313-
multiline_annotations.sort_by_key(|&(_, ref ml)| (ml.line_start, ml.line_end));
314-
for item in multiline_annotations.clone() {
315-
let ann = item.1;
316-
for item in multiline_annotations.iter_mut() {
317-
let ref mut a = item.1;
318-
// Move all other multiline annotations overlapping with this one
319-
// one level to the right.
320-
if !(ann.same_span(a)) &&
321-
num_overlap(ann.line_start, ann.line_end, a.line_start, a.line_end, true)
322-
{
323-
a.increase_depth();
324-
} else if ann.same_span(a) && &ann != a {
325-
a.overlaps_exactly = true;
326-
} else {
327-
break;
328-
}
329-
}
330-
}
331-
332-
let mut max_depth = 0; // max overlapping multiline spans
333-
for (file, ann) in multiline_annotations {
334-
if ann.depth > max_depth {
335-
max_depth = ann.depth;
336-
}
337-
let mut end_ann = ann.as_end();
338-
if !ann.overlaps_exactly {
339-
// avoid output like
340-
//
341-
// | foo(
342-
// | _____^
343-
// | |_____|
344-
// | || bar,
345-
// | || );
346-
// | || ^
347-
// | ||______|
348-
// | |______foo
349-
// | baz
350-
//
351-
// and instead get
352-
//
353-
// | foo(
354-
// | _____^
355-
// | | bar,
356-
// | | );
357-
// | | ^
358-
// | | |
359-
// | |______foo
360-
// | baz
361-
add_annotation_to_file(&mut output, file.clone(), ann.line_start, ann.as_start());
362-
// 4 is the minimum vertical length of a multiline span when presented: two lines
363-
// of code and two lines of underline. This is not true for the special case where
364-
// the beginning doesn't have an underline, but the current logic seems to be
365-
// working correctly.
366-
let middle = min(ann.line_start + 4, ann.line_end);
367-
for line in ann.line_start + 1..middle {
368-
// Every `|` that joins the beginning of the span (`___^`) to the end (`|__^`).
369-
add_annotation_to_file(&mut output, file.clone(), line, ann.as_line());
370-
}
371-
if middle < ann.line_end - 1 {
372-
for line in ann.line_end - 1..ann.line_end {
373-
add_annotation_to_file(&mut output, file.clone(), line, ann.as_line());
374-
}
375-
}
376-
} else {
377-
end_ann.annotation_type = AnnotationType::Singleline;
378-
}
379-
add_annotation_to_file(&mut output, file, ann.line_end, end_ann);
380-
}
381-
for file_vec in output.iter_mut() {
382-
file_vec.multiline_depth = max_depth;
383-
}
384-
output
385-
}
386-
387226
fn render_source_line(&self,
388227
buffer: &mut StyledBuffer,
389228
file: Lrc<SourceFile>,
@@ -1093,9 +932,7 @@ impl EmitterWriter {
1093932
}
1094933
}
1095934

1096-
// Preprocess all the annotations so that they are grouped by file and by line number
1097-
// This helps us quickly iterate over the whole message (including secondary file spans)
1098-
let mut annotated_files = self.preprocess_annotations(msp);
935+
let mut annotated_files = FileWithAnnotatedLines::collect_annotations(msp, &self.sm);
1099936

1100937
// Make sure our primary file comes first
1101938
let (primary_lo, sm) = if let (Some(sm), Some(ref primary_span)) =
@@ -1503,6 +1340,176 @@ impl EmitterWriter {
15031340
}
15041341
}
15051342

1343+
impl FileWithAnnotatedLines {
1344+
/// Preprocess all the annotations so that they are grouped by file and by line number
1345+
/// This helps us quickly iterate over the whole message (including secondary file spans)
1346+
pub fn collect_annotations(
1347+
msp: &MultiSpan,
1348+
source_map: &Option<Lrc<SourceMapperDyn>>
1349+
) -> Vec<FileWithAnnotatedLines> {
1350+
fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>,
1351+
file: Lrc<SourceFile>,
1352+
line_index: usize,
1353+
ann: Annotation) {
1354+
1355+
for slot in file_vec.iter_mut() {
1356+
// Look through each of our files for the one we're adding to
1357+
if slot.file.name == file.name {
1358+
// See if we already have a line for it
1359+
for line_slot in &mut slot.lines {
1360+
if line_slot.line_index == line_index {
1361+
line_slot.annotations.push(ann);
1362+
return;
1363+
}
1364+
}
1365+
// We don't have a line yet, create one
1366+
slot.lines.push(Line {
1367+
line_index,
1368+
annotations: vec![ann],
1369+
});
1370+
slot.lines.sort();
1371+
return;
1372+
}
1373+
}
1374+
// This is the first time we're seeing the file
1375+
file_vec.push(FileWithAnnotatedLines {
1376+
file,
1377+
lines: vec![Line {
1378+
line_index,
1379+
annotations: vec![ann],
1380+
}],
1381+
multiline_depth: 0,
1382+
});
1383+
}
1384+
1385+
let mut output = vec![];
1386+
let mut multiline_annotations = vec![];
1387+
1388+
if let Some(ref sm) = source_map {
1389+
for span_label in msp.span_labels() {
1390+
if span_label.span.is_dummy() {
1391+
continue;
1392+
}
1393+
1394+
let lo = sm.lookup_char_pos(span_label.span.lo());
1395+
let mut hi = sm.lookup_char_pos(span_label.span.hi());
1396+
1397+
// Watch out for "empty spans". If we get a span like 6..6, we
1398+
// want to just display a `^` at 6, so convert that to
1399+
// 6..7. This is degenerate input, but it's best to degrade
1400+
// gracefully -- and the parser likes to supply a span like
1401+
// that for EOF, in particular.
1402+
1403+
if lo.col_display == hi.col_display && lo.line == hi.line {
1404+
hi.col_display += 1;
1405+
}
1406+
1407+
let ann_type = if lo.line != hi.line {
1408+
let ml = MultilineAnnotation {
1409+
depth: 1,
1410+
line_start: lo.line,
1411+
line_end: hi.line,
1412+
start_col: lo.col_display,
1413+
end_col: hi.col_display,
1414+
is_primary: span_label.is_primary,
1415+
label: span_label.label.clone(),
1416+
overlaps_exactly: false,
1417+
};
1418+
multiline_annotations.push((lo.file.clone(), ml.clone()));
1419+
AnnotationType::Multiline(ml)
1420+
} else {
1421+
AnnotationType::Singleline
1422+
};
1423+
let ann = Annotation {
1424+
start_col: lo.col_display,
1425+
end_col: hi.col_display,
1426+
is_primary: span_label.is_primary,
1427+
label: span_label.label.clone(),
1428+
annotation_type: ann_type,
1429+
};
1430+
1431+
if !ann.is_multiline() {
1432+
add_annotation_to_file(&mut output, lo.file, lo.line, ann);
1433+
}
1434+
}
1435+
}
1436+
1437+
// Find overlapping multiline annotations, put them at different depths
1438+
multiline_annotations.sort_by_key(|&(_, ref ml)| (ml.line_start, ml.line_end));
1439+
for item in multiline_annotations.clone() {
1440+
let ann = item.1;
1441+
for item in multiline_annotations.iter_mut() {
1442+
let ref mut a = item.1;
1443+
// Move all other multiline annotations overlapping with this one
1444+
// one level to the right.
1445+
if !(ann.same_span(a)) &&
1446+
num_overlap(ann.line_start, ann.line_end, a.line_start, a.line_end, true)
1447+
{
1448+
a.increase_depth();
1449+
} else if ann.same_span(a) && &ann != a {
1450+
a.overlaps_exactly = true;
1451+
} else {
1452+
break;
1453+
}
1454+
}
1455+
}
1456+
1457+
let mut max_depth = 0; // max overlapping multiline spans
1458+
for (file, ann) in multiline_annotations {
1459+
if ann.depth > max_depth {
1460+
max_depth = ann.depth;
1461+
}
1462+
let mut end_ann = ann.as_end();
1463+
if !ann.overlaps_exactly {
1464+
// avoid output like
1465+
//
1466+
// | foo(
1467+
// | _____^
1468+
// | |_____|
1469+
// | || bar,
1470+
// | || );
1471+
// | || ^
1472+
// | ||______|
1473+
// | |______foo
1474+
// | baz
1475+
//
1476+
// and instead get
1477+
//
1478+
// | foo(
1479+
// | _____^
1480+
// | | bar,
1481+
// | | );
1482+
// | | ^
1483+
// | | |
1484+
// | |______foo
1485+
// | baz
1486+
add_annotation_to_file(&mut output, file.clone(), ann.line_start, ann.as_start());
1487+
// 4 is the minimum vertical length of a multiline span when presented: two lines
1488+
// of code and two lines of underline. This is not true for the special case where
1489+
// the beginning doesn't have an underline, but the current logic seems to be
1490+
// working correctly.
1491+
let middle = min(ann.line_start + 4, ann.line_end);
1492+
for line in ann.line_start + 1..middle {
1493+
// Every `|` that joins the beginning of the span (`___^`) to the end (`|__^`).
1494+
add_annotation_to_file(&mut output, file.clone(), line, ann.as_line());
1495+
}
1496+
if middle < ann.line_end - 1 {
1497+
for line in ann.line_end - 1..ann.line_end {
1498+
add_annotation_to_file(&mut output, file.clone(), line, ann.as_line());
1499+
}
1500+
}
1501+
} else {
1502+
end_ann.annotation_type = AnnotationType::Singleline;
1503+
}
1504+
add_annotation_to_file(&mut output, file, ann.line_end, end_ann);
1505+
}
1506+
for file_vec in output.iter_mut() {
1507+
file_vec.multiline_depth = max_depth;
1508+
}
1509+
output
1510+
}
1511+
}
1512+
15061513
fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
15071514
buffer.puts(line, col, "| ", Style::LineNumber);
15081515
}

0 commit comments

Comments
 (0)