solar_interface/diagnostics/emitter/
rustc.rs1use crate::{
4 SourceMap,
5 diagnostics::{Level, MultiSpan, SpanLabel},
6 source_map::{Loc, SourceFile},
7};
8use std::{
9 cmp::{max, min},
10 sync::Arc,
11};
12
13#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
14pub(crate) struct Line {
15 pub(crate) line_index: usize,
16 pub(crate) annotations: Vec<Annotation>,
17}
18
19impl Line {
20 pub(crate) fn set_level(&mut self, level: Level) {
21 for ann in &mut self.annotations {
22 ann.level = Some(level);
23 }
24 }
25}
26
27#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Default)]
28pub(crate) struct AnnotationColumn {
29 pub(crate) display: usize,
31 pub(crate) file: usize,
45}
46
47impl AnnotationColumn {
48 pub(crate) fn from_loc(loc: &Loc) -> Self {
49 Self { display: loc.col_display, file: loc.col.0 }
50 }
51}
52
53#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
54pub(crate) struct MultilineAnnotation {
55 pub(crate) depth: usize,
56 pub(crate) line_start: usize,
57 pub(crate) line_end: usize,
58 pub(crate) start_col: AnnotationColumn,
59 pub(crate) end_col: AnnotationColumn,
60 pub(crate) is_primary: bool,
61 pub(crate) label: Option<String>,
62 pub(crate) overlaps_exactly: bool,
63}
64
65impl MultilineAnnotation {
66 pub(crate) fn increase_depth(&mut self) {
67 self.depth += 1;
68 }
69
70 pub(crate) fn same_span(&self, other: &Self) -> bool {
72 self.line_start == other.line_start
73 && self.line_end == other.line_end
74 && self.start_col == other.start_col
75 && self.end_col == other.end_col
76 }
77
78 pub(crate) fn as_start(&self) -> Annotation {
79 Annotation {
80 start_col: self.start_col,
81 end_col: AnnotationColumn {
82 display: self.start_col.display + 1,
85 file: self.start_col.file + 1,
86 },
87 is_primary: self.is_primary,
88 label: None,
89 annotation_type: AnnotationType::MultilineStart(self.depth),
90 level: None,
91 }
92 }
93
94 pub(crate) fn as_end(&self) -> Annotation {
95 Annotation {
96 start_col: AnnotationColumn {
97 display: self.end_col.display.saturating_sub(1),
100 file: self.end_col.file.saturating_sub(1),
101 },
102 end_col: self.end_col,
103 is_primary: self.is_primary,
104 label: self.label.clone(),
105 annotation_type: AnnotationType::MultilineEnd(self.depth),
106 level: None,
107 }
108 }
109
110 pub(crate) fn as_line(&self) -> Annotation {
111 Annotation {
112 start_col: Default::default(),
113 end_col: Default::default(),
114 is_primary: self.is_primary,
115 label: None,
116 annotation_type: AnnotationType::MultilineLine(self.depth),
117 level: None,
118 }
119 }
120}
121
122#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
123pub(crate) enum AnnotationType {
124 Singleline,
126
127 MultilineStart(usize),
139 MultilineEnd(usize),
141 MultilineLine(usize),
146}
147
148#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
149pub(crate) struct Annotation {
150 pub(crate) start_col: AnnotationColumn,
155
156 pub(crate) end_col: AnnotationColumn,
158
159 pub(crate) is_primary: bool,
161
162 pub(crate) label: Option<String>,
164
165 pub(crate) annotation_type: AnnotationType,
168
169 pub(crate) level: Option<Level>,
170}
171
172#[derive(Debug)]
173pub(crate) struct FileWithAnnotatedLines {
174 pub(crate) file: Arc<SourceFile>,
175 pub(crate) lines: Vec<Line>,
176 multiline_depth: usize,
177}
178
179impl FileWithAnnotatedLines {
180 pub(crate) fn collect_annotations(sm: &SourceMap, msp: &MultiSpan) -> Vec<Self> {
183 fn add_annotation_to_file(
184 file_vec: &mut Vec<FileWithAnnotatedLines>,
185 file: Arc<SourceFile>,
186 line_index: usize,
187 ann: Annotation,
188 ) {
189 for slot in file_vec.iter_mut() {
190 if slot.file.name == file.name {
192 for line_slot in &mut slot.lines {
194 if line_slot.line_index == line_index {
195 line_slot.annotations.push(ann);
196 return;
197 }
198 }
199 slot.lines.push(Line { line_index, annotations: vec![ann] });
201 slot.lines.sort();
202 return;
203 }
204 }
205 file_vec.push(FileWithAnnotatedLines {
207 file,
208 lines: vec![Line { line_index, annotations: vec![ann] }],
209 multiline_depth: 0,
210 });
211 }
212
213 let mut output = vec![];
214 let mut multiline_annotations = vec![];
215
216 for SpanLabel { span, is_primary, label } in msp.span_labels() {
217 let span = match (span.is_dummy(), msp.primary_span()) {
220 (_, None) | (false, _) => span,
221 (true, Some(span)) => span,
222 };
223
224 let lo = sm.lookup_char_pos(span.lo());
225 let mut hi = sm.lookup_char_pos(span.hi());
226
227 if lo.col_display == hi.col_display && lo.line == hi.line {
234 hi.col_display += 1;
235 }
236
237 let label = label.as_ref().map(|m| m.as_str().to_string());
238
239 if lo.line != hi.line {
240 let ml = MultilineAnnotation {
241 depth: 1,
242 line_start: lo.line,
243 line_end: hi.line,
244 start_col: AnnotationColumn::from_loc(&lo),
245 end_col: AnnotationColumn::from_loc(&hi),
246 is_primary,
247 label,
248 overlaps_exactly: false,
249 };
250 multiline_annotations.push((lo.file, ml));
251 } else {
252 let ann = Annotation {
253 start_col: AnnotationColumn::from_loc(&lo),
254 end_col: AnnotationColumn::from_loc(&hi),
255 is_primary,
256 label,
257 annotation_type: AnnotationType::Singleline,
258 level: None,
259 };
260 add_annotation_to_file(&mut output, lo.file, lo.line, ann);
261 };
262 }
263
264 multiline_annotations.sort_by_key(|(_, ml)| (ml.line_start, usize::MAX - ml.line_end));
266 for (_, ann) in multiline_annotations.clone() {
267 for (_, a) in multiline_annotations.iter_mut() {
268 if !(ann.same_span(a))
271 && num_overlap(ann.line_start, ann.line_end, a.line_start, a.line_end, true)
272 {
273 a.increase_depth();
274 } else if ann.same_span(a) && &ann != a {
275 a.overlaps_exactly = true;
276 } else {
277 break;
278 }
279 }
280 }
281
282 let mut max_depth = 0; for (_, ann) in &multiline_annotations {
284 max_depth = max(max_depth, ann.depth);
285 }
286 for (_, a) in multiline_annotations.iter_mut() {
288 a.depth = max_depth - a.depth + 1;
289 }
290 for (file, ann) in multiline_annotations {
291 let mut end_ann = ann.as_end();
292 if !ann.overlaps_exactly {
293 add_annotation_to_file(&mut output, file.clone(), ann.line_start, ann.as_start());
316 let middle = min(ann.line_start + 4, ann.line_end);
321 for line in ann.line_start + 1..middle {
322 add_annotation_to_file(&mut output, file.clone(), line, ann.as_line());
324 }
325 let line_end = ann.line_end - 1;
326 if middle < line_end {
327 add_annotation_to_file(&mut output, file.clone(), line_end, ann.as_line());
328 }
329 } else {
330 end_ann.annotation_type = AnnotationType::Singleline;
331 }
332 add_annotation_to_file(&mut output, file, ann.line_end, end_ann);
333 }
334 for file_vec in output.iter_mut() {
335 file_vec.multiline_depth = max_depth;
336 }
337 output
338 }
339
340 pub(crate) fn set_level(&mut self, level: Level) {
341 for line in &mut self.lines {
342 line.set_level(level);
343 }
344 }
345
346 pub(crate) fn add_lines(&mut self, lines: impl IntoIterator<Item = Line>) {
347 debug_assert!(self.lines.is_sorted(), "file lines should be sorted");
348 for line in lines {
349 match self.lines.binary_search_by_key(&line.line_index, |l| l.line_index) {
350 Ok(i) => {
351 self.lines[i].annotations.extend(line.annotations);
352 self.lines[i].annotations.sort();
353 }
354 Err(i) => {
355 self.lines.insert(i, line);
356 }
357 }
358 }
359 debug_assert!(self.lines.is_sorted(), "file lines should still be sorted");
360 }
361}
362
363fn num_overlap(
364 a_start: usize,
365 a_end: usize,
366 b_start: usize,
367 b_end: usize,
368 inclusive: bool,
369) -> bool {
370 let extra = if inclusive { 1 } else { 0 };
371 (b_start..b_end + extra).contains(&a_start) || (a_start..a_end + extra).contains(&b_start)
372}