]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
bump version to 1.74.1+dfsg1-1~bpo12+pve1
[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,
781aab86 94 Level::Help | Level::OnceHelp => 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 }
add651ee
FG
160 // owned: file name, line source, line index, annotations
161 type Owned = (String, String, usize, Vec<crate::snippet::Annotation>);
f035d41b
XL
162 let annotated_files: Vec<Owned> = annotated_files
163 .into_iter()
164 .flat_map(|annotated_file| {
165 let file = annotated_file.file;
166 annotated_file
167 .lines
168 .into_iter()
169 .map(|line| {
add651ee
FG
170 // Ensure the source file is present before we try
171 // to load a string from it.
781aab86
FG
172 // FIXME(#115869): support -Z ignore-directory-in-diagnostics-source-blocks
173 source_map.ensure_source_file_source_present(&file);
add651ee
FG
174 (
175 format!("{}", source_map.filename_for_diagnostics(&file.name)),
176 source_string(file.clone(), &line),
177 line.line_index,
178 line.annotations,
179 )
f035d41b
XL
180 })
181 .collect::<Vec<Owned>>()
182 })
183 .collect();
184 let snippet = Snippet {
185 title: Some(Annotation {
186 label: Some(&message),
187 id: code.as_ref().map(|c| match c {
136023e0
XL
188 DiagnosticId::Error(val) | DiagnosticId::Lint { name: val, .. } => {
189 val.as_str()
190 }
f035d41b
XL
191 }),
192 annotation_type: annotation_type_for_level(*level),
193 }),
194 footer: vec![],
f2b60f7d
FG
195 opt: FormatOptions {
196 color: true,
197 anonymized_line_numbers: self.ui_testing,
198 margin: None,
199 },
f035d41b
XL
200 slices: annotated_files
201 .iter()
add651ee 202 .map(|(file_name, source, line_index, annotations)| {
f035d41b
XL
203 Slice {
204 source,
205 line_start: *line_index,
add651ee 206 origin: Some(&file_name),
f035d41b
XL
207 // FIXME(#59346): Not really sure when `fold` should be true or false
208 fold: false,
209 annotations: annotations
210 .iter()
211 .map(|annotation| SourceAnnotation {
353b0b11
FG
212 range: (
213 annotation.start_col.display,
214 annotation.end_col.display,
215 ),
f035d41b
XL
216 label: annotation.label.as_deref().unwrap_or_default(),
217 annotation_type: annotation_type_for_level(*level),
218 })
219 .collect(),
220 }
221 })
222 .collect(),
223 };
dc9dc135
XL
224 // FIXME(#59346): Figure out if we can _always_ print to stderr or not.
225 // `emitter.rs` has the `Destination` enum that lists various possible output
226 // destinations.
f035d41b
XL
227 eprintln!("{}", DisplayList::from(snippet))
228 }
229 // FIXME(#59346): Is it ok to return None if there's no source_map?
dc9dc135
XL
230 }
231}