]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / compiler / rustc_errors / src / annotate_snippet_emitter_writer.rs
CommitLineData
dc9dc135
XL
1//! Emit diagnostics using the `annotate-snippets` library
2//!
3//! This is the equivalent of `./emitter.rs` but making use of the
4//! [`annotate-snippets`][annotate_snippets] library instead of building the output ourselves.
5//!
6//! [annotate_snippets]: https://docs.rs/crate/annotate-snippets/
7
dc9dc135 8use crate::emitter::FileWithAnnotatedLines;
dc9dc135 9use crate::snippet::Line;
2b03887a 10use crate::translation::{to_fluent_args, Translate};
04454e1e
FG
11use crate::{
12 CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle,
13 LazyFallbackBundle, Level, MultiSpan, Style, SubDiagnostic,
14};
f035d41b 15use annotate_snippets::display_list::{DisplayList, FormatOptions};
dfeec247
XL
16use annotate_snippets::snippet::*;
17use rustc_data_structures::sync::Lrc;
04454e1e 18use rustc_error_messages::FluentArgs;
dfeec247 19use rustc_span::source_map::SourceMap;
04454e1e 20use rustc_span::SourceFile;
dc9dc135
XL
21
22/// Generates diagnostics using annotate-snippet
23pub struct AnnotateSnippetEmitterWriter {
60c5eb7d 24 source_map: Option<Lrc<SourceMap>>,
04454e1e
FG
25 fluent_bundle: Option<Lrc<FluentBundle>>,
26 fallback_bundle: LazyFallbackBundle,
27
dc9dc135
XL
28 /// If true, hides the longer explanation text
29 short_message: bool,
416331ca 30 /// If true, will normalize line numbers with `LL` to prevent noise in UI test diffs.
dc9dc135 31 ui_testing: bool,
e1599b0c 32
74b04a01 33 macro_backtrace: bool,
dc9dc135
XL
34}
35
f2b60f7d
FG
36impl Translate for AnnotateSnippetEmitterWriter {
37 fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
38 self.fluent_bundle.as_ref()
39 }
40
41 fn fallback_fluent_bundle(&self) -> &FluentBundle {
487cf647 42 &self.fallback_bundle
f2b60f7d
FG
43 }
44}
45
dc9dc135
XL
46impl Emitter for AnnotateSnippetEmitterWriter {
47 /// The entry point for the diagnostics generation
e74abb32 48 fn emit_diagnostic(&mut self, diag: &Diagnostic) {
2b03887a 49 let fluent_args = to_fluent_args(diag.args());
04454e1e 50
e74abb32 51 let mut children = diag.children.clone();
487cf647 52 let (mut primary_span, suggestions) = self.primary_span_formatted(diag, &fluent_args);
dc9dc135 53
74b04a01 54 self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
dfeec247
XL
55 &mut primary_span,
56 &mut children,
57 &diag.level,
74b04a01 58 self.macro_backtrace,
dfeec247
XL
59 );
60
61 self.emit_messages_default(
62 &diag.level,
04454e1e
FG
63 &diag.message,
64 &fluent_args,
dfeec247
XL
65 &diag.code,
66 &primary_span,
67 &children,
487cf647 68 suggestions,
dfeec247 69 );
dc9dc135
XL
70 }
71
60c5eb7d 72 fn source_map(&self) -> Option<&Lrc<SourceMap>> {
e74abb32
XL
73 self.source_map.as_ref()
74 }
75
dc9dc135
XL
76 fn should_show_explain(&self) -> bool {
77 !self.short_message
78 }
79}
80
f035d41b
XL
81/// Provides the source string for the given `line` of `file`
82fn source_string(file: Lrc<SourceFile>, line: &Line) -> String {
83 file.get_line(line.line_index - 1).map(|a| a.to_string()).unwrap_or_default()
dc9dc135
XL
84}
85
f035d41b
XL
86/// Maps `Diagnostic::Level` to `snippet::AnnotationType`
87fn annotation_type_for_level(level: Level) -> AnnotationType {
88 match level {
5e7ed085
FG
89 Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error { .. } => {
90 AnnotationType::Error
91 }
923072b8 92 Level::Warning(_) => AnnotationType::Warning,
5e7ed085 93 Level::Note | Level::OnceNote => AnnotationType::Note,
f035d41b 94 Level::Help => AnnotationType::Help,
5e7ed085
FG
95 // FIXME(#59346): Not sure how to map this level
96 Level::FailureNote => AnnotationType::Error,
29967ef6 97 Level::Allow => panic!("Should not call with Allow"),
5e7ed085 98 Level::Expect(_) => panic!("Should not call with Expect"),
dc9dc135
XL
99 }
100}
101
102impl AnnotateSnippetEmitterWriter {
103 pub fn new(
60c5eb7d 104 source_map: Option<Lrc<SourceMap>>,
04454e1e
FG
105 fluent_bundle: Option<Lrc<FluentBundle>>,
106 fallback_bundle: LazyFallbackBundle,
e1599b0c 107 short_message: bool,
74b04a01 108 macro_backtrace: bool,
dc9dc135 109 ) -> Self {
04454e1e
FG
110 Self {
111 source_map,
112 fluent_bundle,
113 fallback_bundle,
114 short_message,
115 ui_testing: false,
116 macro_backtrace,
117 }
dc9dc135
XL
118 }
119
120 /// Allows to modify `Self` to enable or disable the `ui_testing` flag.
121 ///
122 /// If this is set to true, line numbers will be normalized as `LL` in the output.
dc9dc135
XL
123 pub fn ui_testing(mut self, ui_testing: bool) -> Self {
124 self.ui_testing = ui_testing;
125 self
126 }
127
128 fn emit_messages_default(
129 &mut self,
130 level: &Level,
04454e1e
FG
131 messages: &[(DiagnosticMessage, Style)],
132 args: &FluentArgs<'_>,
dc9dc135
XL
133 code: &Option<DiagnosticId>,
134 msp: &MultiSpan,
f035d41b
XL
135 _children: &[SubDiagnostic],
136 _suggestions: &[CodeSuggestion],
dc9dc135 137 ) {
04454e1e 138 let message = self.translate_messages(messages, args);
f035d41b
XL
139 if let Some(source_map) = &self.source_map {
140 // Make sure our primary file comes first
141 let primary_lo = if let Some(ref primary_span) = msp.primary_span().as_ref() {
142 if primary_span.is_dummy() {
143 // FIXME(#59346): Not sure when this is the case and what
144 // should be done if it happens
145 return;
146 } else {
147 source_map.lookup_char_pos(primary_span.lo())
148 }
149 } else {
150 // FIXME(#59346): Not sure when this is the case and what
151 // should be done if it happens
152 return;
153 };
04454e1e 154 let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp);
f035d41b
XL
155 if let Ok(pos) =
156 annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name))
157 {
158 annotated_files.swap(0, pos);
159 }
160 // owned: line source, line index, annotations
161 type Owned = (String, usize, Vec<crate::snippet::Annotation>);
94222f64 162 let filename = source_map.filename_for_diagnostics(&primary_lo.file.name);
17df50a5 163 let origin = filename.to_string_lossy();
f035d41b
XL
164 let annotated_files: Vec<Owned> = annotated_files
165 .into_iter()
166 .flat_map(|annotated_file| {
167 let file = annotated_file.file;
168 annotated_file
169 .lines
170 .into_iter()
171 .map(|line| {
172 (source_string(file.clone(), &line), line.line_index, line.annotations)
173 })
174 .collect::<Vec<Owned>>()
175 })
176 .collect();
177 let snippet = Snippet {
178 title: Some(Annotation {
179 label: Some(&message),
180 id: code.as_ref().map(|c| match c {
136023e0
XL
181 DiagnosticId::Error(val) | DiagnosticId::Lint { name: val, .. } => {
182 val.as_str()
183 }
f035d41b
XL
184 }),
185 annotation_type: annotation_type_for_level(*level),
186 }),
187 footer: vec![],
f2b60f7d
FG
188 opt: FormatOptions {
189 color: true,
190 anonymized_line_numbers: self.ui_testing,
191 margin: None,
192 },
f035d41b
XL
193 slices: annotated_files
194 .iter()
195 .map(|(source, line_index, annotations)| {
196 Slice {
197 source,
198 line_start: *line_index,
199 origin: Some(&origin),
200 // FIXME(#59346): Not really sure when `fold` should be true or false
201 fold: false,
202 annotations: annotations
203 .iter()
204 .map(|annotation| SourceAnnotation {
353b0b11
FG
205 range: (
206 annotation.start_col.display,
207 annotation.end_col.display,
208 ),
f035d41b
XL
209 label: annotation.label.as_deref().unwrap_or_default(),
210 annotation_type: annotation_type_for_level(*level),
211 })
212 .collect(),
213 }
214 })
215 .collect(),
216 };
dc9dc135
XL
217 // FIXME(#59346): Figure out if we can _always_ print to stderr or not.
218 // `emitter.rs` has the `Destination` enum that lists various possible output
219 // destinations.
f035d41b
XL
220 eprintln!("{}", DisplayList::from(snippet))
221 }
222 // FIXME(#59346): Is it ok to return None if there's no source_map?
dc9dc135
XL
223 }
224}