rustc_errors/snippet.rs
1// Code for annotating snippets.
2
3use rustc_macros::{Decodable, Encodable};
4
5use crate::{Level, Loc};
6
7#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
8pub(crate) struct Line {
9 pub line_index: usize,
10 pub annotations: Vec<Annotation>,
11}
12
13#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Default)]
14pub(crate) struct AnnotationColumn {
15 /// the (0-indexed) column for *display* purposes, counted in characters, not utf-8 bytes
16 pub display: usize,
17 /// the (0-indexed) column in the file, counted in characters, not utf-8 bytes.
18 ///
19 /// this may be different from `self.display`,
20 /// e.g. if the file contains hard tabs, because we convert tabs to spaces for error messages.
21 ///
22 /// for example:
23 /// ```text
24 /// (hard tab)hello
25 /// ^ this is display column 4, but file column 1
26 /// ```
27 ///
28 /// we want to keep around the correct file offset so that column numbers in error messages
29 /// are correct. (motivated by <https://github.com/rust-lang/rust/issues/109537>)
30 pub file: usize,
31}
32
33impl AnnotationColumn {
34 pub(crate) fn from_loc(loc: &Loc) -> AnnotationColumn {
35 AnnotationColumn { display: loc.col_display, file: loc.col.0 }
36 }
37}
38
39#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
40pub(crate) struct MultilineAnnotation {
41 pub depth: usize,
42 pub line_start: usize,
43 pub line_end: usize,
44 pub start_col: AnnotationColumn,
45 pub end_col: AnnotationColumn,
46 pub is_primary: bool,
47 pub label: Option<String>,
48 pub overlaps_exactly: bool,
49}
50
51impl MultilineAnnotation {
52 pub(crate) fn increase_depth(&mut self) {
53 self.depth += 1;
54 }
55
56 /// Compare two `MultilineAnnotation`s considering only the `Span` they cover.
57 pub(crate) fn same_span(&self, other: &MultilineAnnotation) -> bool {
58 self.line_start == other.line_start
59 && self.line_end == other.line_end
60 && self.start_col == other.start_col
61 && self.end_col == other.end_col
62 }
63
64 pub(crate) fn as_start(&self) -> Annotation {
65 Annotation {
66 start_col: self.start_col,
67 end_col: AnnotationColumn {
68 // these might not correspond to the same place anymore,
69 // but that's okay for our purposes
70 display: self.start_col.display + 1,
71 file: self.start_col.file + 1,
72 },
73 is_primary: self.is_primary,
74 label: None,
75 annotation_type: AnnotationType::MultilineStart(self.depth),
76 }
77 }
78
79 pub(crate) fn as_end(&self) -> Annotation {
80 Annotation {
81 start_col: AnnotationColumn {
82 // these might not correspond to the same place anymore,
83 // but that's okay for our purposes
84 display: self.end_col.display.saturating_sub(1),
85 file: self.end_col.file.saturating_sub(1),
86 },
87 end_col: self.end_col,
88 is_primary: self.is_primary,
89 label: self.label.clone(),
90 annotation_type: AnnotationType::MultilineEnd(self.depth),
91 }
92 }
93
94 pub(crate) fn as_line(&self) -> Annotation {
95 Annotation {
96 start_col: Default::default(),
97 end_col: Default::default(),
98 is_primary: self.is_primary,
99 label: None,
100 annotation_type: AnnotationType::MultilineLine(self.depth),
101 }
102 }
103}
104
105#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
106pub(crate) enum AnnotationType {
107 /// Annotation under a single line of code
108 Singleline,
109
110 // The Multiline type above is replaced with the following three in order
111 // to reuse the current label drawing code.
112 //
113 // Each of these corresponds to one part of the following diagram:
114 //
115 // x | foo(1 + bar(x,
116 // | _________^ < MultilineStart
117 // x | | y), < MultilineLine
118 // | |______________^ label < MultilineEnd
119 // x | z);
120 /// Annotation marking the first character of a fully shown multiline span
121 MultilineStart(usize),
122 /// Annotation marking the last character of a fully shown multiline span
123 MultilineEnd(usize),
124 /// Line at the left enclosing the lines of a fully shown multiline span
125 // Just a placeholder for the drawing algorithm, to know that it shouldn't skip the first 4
126 // and last 2 lines of code. The actual line is drawn in `emit_message_default` and not in
127 // `draw_multiline_line`.
128 MultilineLine(usize),
129}
130
131#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
132pub(crate) struct Annotation {
133 /// Start column.
134 /// Note that it is important that this field goes
135 /// first, so that when we sort, we sort orderings by start
136 /// column.
137 pub start_col: AnnotationColumn,
138
139 /// End column within the line (exclusive)
140 pub end_col: AnnotationColumn,
141
142 /// Is this annotation derived from primary span
143 pub is_primary: bool,
144
145 /// Optional label to display adjacent to the annotation.
146 pub label: Option<String>,
147
148 /// Is this a single line, multiline or multiline span minimized down to a
149 /// smaller span.
150 pub annotation_type: AnnotationType,
151}
152
153impl Annotation {
154 /// Whether this annotation is a vertical line placeholder.
155 pub(crate) fn is_line(&self) -> bool {
156 matches!(self.annotation_type, AnnotationType::MultilineLine(_))
157 }
158
159 /// Length of this annotation as displayed in the stderr output
160 pub(crate) fn len(&self) -> usize {
161 // Account for usize underflows
162 if self.end_col.display > self.start_col.display {
163 self.end_col.display - self.start_col.display
164 } else {
165 self.start_col.display - self.end_col.display
166 }
167 }
168
169 pub(crate) fn has_label(&self) -> bool {
170 if let Some(ref label) = self.label {
171 // Consider labels with no text as effectively not being there
172 // to avoid weird output with unnecessary vertical lines, like:
173 //
174 // X | fn foo(x: u32) {
175 // | -------^------
176 // | | |
177 // | |
178 // |
179 //
180 // Note that this would be the complete output users would see.
181 !label.is_empty()
182 } else {
183 false
184 }
185 }
186
187 pub(crate) fn takes_space(&self) -> bool {
188 // Multiline annotations always have to keep vertical space.
189 matches!(
190 self.annotation_type,
191 AnnotationType::MultilineStart(_) | AnnotationType::MultilineEnd(_)
192 )
193 }
194}
195
196#[derive(Debug)]
197pub(crate) struct StyledString {
198 pub text: String,
199 pub style: Style,
200}
201
202#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
203pub enum Style {
204 MainHeaderMsg,
205 HeaderMsg,
206 LineAndColumn,
207 LineNumber,
208 Quotation,
209 UnderlinePrimary,
210 UnderlineSecondary,
211 LabelPrimary,
212 LabelSecondary,
213 NoStyle,
214 Level(Level),
215 Highlight,
216 Addition,
217 Removal,
218}