librustc_errors: Move annotation collection to own impl
Extracted from work on #59346. This moves the annotation collection to the `FileWithAnnotatedLines` impl to allow re-use in a separate EmitterWriter.
This commit is contained in:
parent
f492693982
commit
96e3fb255b
@ -162,6 +162,7 @@ impl ColorConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short`
|
||||
pub struct EmitterWriter {
|
||||
dst: Destination,
|
||||
sm: Option<Lrc<SourceMapperDyn>>,
|
||||
@ -170,7 +171,8 @@ pub struct EmitterWriter {
|
||||
ui_testing: bool,
|
||||
}
|
||||
|
||||
struct FileWithAnnotatedLines {
|
||||
#[derive(Debug)]
|
||||
pub struct FileWithAnnotatedLines {
|
||||
file: Lrc<SourceFile>,
|
||||
lines: Vec<Line>,
|
||||
multiline_depth: usize,
|
||||
@ -221,169 +223,6 @@ impl EmitterWriter {
|
||||
}
|
||||
}
|
||||
|
||||
fn preprocess_annotations(&mut self, msp: &MultiSpan) -> Vec<FileWithAnnotatedLines> {
|
||||
fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>,
|
||||
file: Lrc<SourceFile>,
|
||||
line_index: usize,
|
||||
ann: Annotation) {
|
||||
|
||||
for slot in file_vec.iter_mut() {
|
||||
// Look through each of our files for the one we're adding to
|
||||
if slot.file.name == file.name {
|
||||
// See if we already have a line for it
|
||||
for line_slot in &mut slot.lines {
|
||||
if line_slot.line_index == line_index {
|
||||
line_slot.annotations.push(ann);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// We don't have a line yet, create one
|
||||
slot.lines.push(Line {
|
||||
line_index,
|
||||
annotations: vec![ann],
|
||||
});
|
||||
slot.lines.sort();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// This is the first time we're seeing the file
|
||||
file_vec.push(FileWithAnnotatedLines {
|
||||
file,
|
||||
lines: vec![Line {
|
||||
line_index,
|
||||
annotations: vec![ann],
|
||||
}],
|
||||
multiline_depth: 0,
|
||||
});
|
||||
}
|
||||
|
||||
let mut output = vec![];
|
||||
let mut multiline_annotations = vec![];
|
||||
|
||||
if let Some(ref sm) = self.sm {
|
||||
for span_label in msp.span_labels() {
|
||||
if span_label.span.is_dummy() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let lo = sm.lookup_char_pos(span_label.span.lo());
|
||||
let mut hi = sm.lookup_char_pos(span_label.span.hi());
|
||||
|
||||
// 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
|
||||
// 6..7. This is degenerate input, but it's best to degrade
|
||||
// gracefully -- and the parser likes to supply a span like
|
||||
// that for EOF, in particular.
|
||||
|
||||
if lo.col_display == hi.col_display && lo.line == hi.line {
|
||||
hi.col_display += 1;
|
||||
}
|
||||
|
||||
let ann_type = if lo.line != hi.line {
|
||||
let ml = MultilineAnnotation {
|
||||
depth: 1,
|
||||
line_start: lo.line,
|
||||
line_end: hi.line,
|
||||
start_col: lo.col_display,
|
||||
end_col: hi.col_display,
|
||||
is_primary: span_label.is_primary,
|
||||
label: span_label.label.clone(),
|
||||
overlaps_exactly: false,
|
||||
};
|
||||
multiline_annotations.push((lo.file.clone(), ml.clone()));
|
||||
AnnotationType::Multiline(ml)
|
||||
} else {
|
||||
AnnotationType::Singleline
|
||||
};
|
||||
let ann = Annotation {
|
||||
start_col: lo.col_display,
|
||||
end_col: hi.col_display,
|
||||
is_primary: span_label.is_primary,
|
||||
label: span_label.label.clone(),
|
||||
annotation_type: ann_type,
|
||||
};
|
||||
|
||||
if !ann.is_multiline() {
|
||||
add_annotation_to_file(&mut output, lo.file, lo.line, ann);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find overlapping multiline annotations, put them at different depths
|
||||
multiline_annotations.sort_by_key(|&(_, ref ml)| (ml.line_start, ml.line_end));
|
||||
for item in multiline_annotations.clone() {
|
||||
let ann = item.1;
|
||||
for item in multiline_annotations.iter_mut() {
|
||||
let ref mut a = item.1;
|
||||
// Move all other multiline annotations overlapping with this one
|
||||
// one level to the right.
|
||||
if !(ann.same_span(a)) &&
|
||||
num_overlap(ann.line_start, ann.line_end, a.line_start, a.line_end, true)
|
||||
{
|
||||
a.increase_depth();
|
||||
} else if ann.same_span(a) && &ann != a {
|
||||
a.overlaps_exactly = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut max_depth = 0; // max overlapping multiline spans
|
||||
for (file, ann) in multiline_annotations {
|
||||
if ann.depth > max_depth {
|
||||
max_depth = ann.depth;
|
||||
}
|
||||
let mut end_ann = ann.as_end();
|
||||
if !ann.overlaps_exactly {
|
||||
// avoid output like
|
||||
//
|
||||
// | foo(
|
||||
// | _____^
|
||||
// | |_____|
|
||||
// | || bar,
|
||||
// | || );
|
||||
// | || ^
|
||||
// | ||______|
|
||||
// | |______foo
|
||||
// | baz
|
||||
//
|
||||
// and instead get
|
||||
//
|
||||
// | foo(
|
||||
// | _____^
|
||||
// | | bar,
|
||||
// | | );
|
||||
// | | ^
|
||||
// | | |
|
||||
// | |______foo
|
||||
// | baz
|
||||
add_annotation_to_file(&mut output, file.clone(), ann.line_start, ann.as_start());
|
||||
// 4 is the minimum vertical length of a multiline span when presented: two lines
|
||||
// of code and two lines of underline. This is not true for the special case where
|
||||
// the beginning doesn't have an underline, but the current logic seems to be
|
||||
// working correctly.
|
||||
let middle = min(ann.line_start + 4, ann.line_end);
|
||||
for line in ann.line_start + 1..middle {
|
||||
// Every `|` that joins the beginning of the span (`___^`) to the end (`|__^`).
|
||||
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());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
end_ann.annotation_type = AnnotationType::Singleline;
|
||||
}
|
||||
add_annotation_to_file(&mut output, file, ann.line_end, end_ann);
|
||||
}
|
||||
for file_vec in output.iter_mut() {
|
||||
file_vec.multiline_depth = max_depth;
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
fn render_source_line(&self,
|
||||
buffer: &mut StyledBuffer,
|
||||
file: Lrc<SourceFile>,
|
||||
@ -1093,9 +932,7 @@ impl EmitterWriter {
|
||||
}
|
||||
}
|
||||
|
||||
// Preprocess all the annotations so that they are grouped by file and by line number
|
||||
// This helps us quickly iterate over the whole message (including secondary file spans)
|
||||
let mut annotated_files = self.preprocess_annotations(msp);
|
||||
let mut annotated_files = FileWithAnnotatedLines::collect_annotations(msp, &self.sm);
|
||||
|
||||
// Make sure our primary file comes first
|
||||
let (primary_lo, sm) = if let (Some(sm), Some(ref primary_span)) =
|
||||
@ -1503,6 +1340,176 @@ impl EmitterWriter {
|
||||
}
|
||||
}
|
||||
|
||||
impl FileWithAnnotatedLines {
|
||||
/// Preprocess all the annotations so that they are grouped by file and by line number
|
||||
/// This helps us quickly iterate over the whole message (including secondary file spans)
|
||||
pub fn collect_annotations(
|
||||
msp: &MultiSpan,
|
||||
source_map: &Option<Lrc<SourceMapperDyn>>
|
||||
) -> Vec<FileWithAnnotatedLines> {
|
||||
fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>,
|
||||
file: Lrc<SourceFile>,
|
||||
line_index: usize,
|
||||
ann: Annotation) {
|
||||
|
||||
for slot in file_vec.iter_mut() {
|
||||
// Look through each of our files for the one we're adding to
|
||||
if slot.file.name == file.name {
|
||||
// See if we already have a line for it
|
||||
for line_slot in &mut slot.lines {
|
||||
if line_slot.line_index == line_index {
|
||||
line_slot.annotations.push(ann);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// We don't have a line yet, create one
|
||||
slot.lines.push(Line {
|
||||
line_index,
|
||||
annotations: vec![ann],
|
||||
});
|
||||
slot.lines.sort();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// This is the first time we're seeing the file
|
||||
file_vec.push(FileWithAnnotatedLines {
|
||||
file,
|
||||
lines: vec![Line {
|
||||
line_index,
|
||||
annotations: vec![ann],
|
||||
}],
|
||||
multiline_depth: 0,
|
||||
});
|
||||
}
|
||||
|
||||
let mut output = vec![];
|
||||
let mut multiline_annotations = vec![];
|
||||
|
||||
if let Some(ref sm) = source_map {
|
||||
for span_label in msp.span_labels() {
|
||||
if span_label.span.is_dummy() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let lo = sm.lookup_char_pos(span_label.span.lo());
|
||||
let mut hi = sm.lookup_char_pos(span_label.span.hi());
|
||||
|
||||
// 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
|
||||
// 6..7. This is degenerate input, but it's best to degrade
|
||||
// gracefully -- and the parser likes to supply a span like
|
||||
// that for EOF, in particular.
|
||||
|
||||
if lo.col_display == hi.col_display && lo.line == hi.line {
|
||||
hi.col_display += 1;
|
||||
}
|
||||
|
||||
let ann_type = if lo.line != hi.line {
|
||||
let ml = MultilineAnnotation {
|
||||
depth: 1,
|
||||
line_start: lo.line,
|
||||
line_end: hi.line,
|
||||
start_col: lo.col_display,
|
||||
end_col: hi.col_display,
|
||||
is_primary: span_label.is_primary,
|
||||
label: span_label.label.clone(),
|
||||
overlaps_exactly: false,
|
||||
};
|
||||
multiline_annotations.push((lo.file.clone(), ml.clone()));
|
||||
AnnotationType::Multiline(ml)
|
||||
} else {
|
||||
AnnotationType::Singleline
|
||||
};
|
||||
let ann = Annotation {
|
||||
start_col: lo.col_display,
|
||||
end_col: hi.col_display,
|
||||
is_primary: span_label.is_primary,
|
||||
label: span_label.label.clone(),
|
||||
annotation_type: ann_type,
|
||||
};
|
||||
|
||||
if !ann.is_multiline() {
|
||||
add_annotation_to_file(&mut output, lo.file, lo.line, ann);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find overlapping multiline annotations, put them at different depths
|
||||
multiline_annotations.sort_by_key(|&(_, ref ml)| (ml.line_start, ml.line_end));
|
||||
for item in multiline_annotations.clone() {
|
||||
let ann = item.1;
|
||||
for item in multiline_annotations.iter_mut() {
|
||||
let ref mut a = item.1;
|
||||
// Move all other multiline annotations overlapping with this one
|
||||
// one level to the right.
|
||||
if !(ann.same_span(a)) &&
|
||||
num_overlap(ann.line_start, ann.line_end, a.line_start, a.line_end, true)
|
||||
{
|
||||
a.increase_depth();
|
||||
} else if ann.same_span(a) && &ann != a {
|
||||
a.overlaps_exactly = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut max_depth = 0; // max overlapping multiline spans
|
||||
for (file, ann) in multiline_annotations {
|
||||
if ann.depth > max_depth {
|
||||
max_depth = ann.depth;
|
||||
}
|
||||
let mut end_ann = ann.as_end();
|
||||
if !ann.overlaps_exactly {
|
||||
// avoid output like
|
||||
//
|
||||
// | foo(
|
||||
// | _____^
|
||||
// | |_____|
|
||||
// | || bar,
|
||||
// | || );
|
||||
// | || ^
|
||||
// | ||______|
|
||||
// | |______foo
|
||||
// | baz
|
||||
//
|
||||
// and instead get
|
||||
//
|
||||
// | foo(
|
||||
// | _____^
|
||||
// | | bar,
|
||||
// | | );
|
||||
// | | ^
|
||||
// | | |
|
||||
// | |______foo
|
||||
// | baz
|
||||
add_annotation_to_file(&mut output, file.clone(), ann.line_start, ann.as_start());
|
||||
// 4 is the minimum vertical length of a multiline span when presented: two lines
|
||||
// of code and two lines of underline. This is not true for the special case where
|
||||
// the beginning doesn't have an underline, but the current logic seems to be
|
||||
// working correctly.
|
||||
let middle = min(ann.line_start + 4, ann.line_end);
|
||||
for line in ann.line_start + 1..middle {
|
||||
// Every `|` that joins the beginning of the span (`___^`) to the end (`|__^`).
|
||||
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());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
end_ann.annotation_type = AnnotationType::Singleline;
|
||||
}
|
||||
add_annotation_to_file(&mut output, file, ann.line_end, end_ann);
|
||||
}
|
||||
for file_vec in output.iter_mut() {
|
||||
file_vec.multiline_depth = max_depth;
|
||||
}
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
|
||||
buffer.puts(line, col, "| ", Style::LineNumber);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user