rustc_errors/
annotate_snippet_emitter_writer.rs1use std::sync::Arc;
9
10use annotate_snippets::{Renderer, Snippet};
11use rustc_error_messages::FluentArgs;
12use rustc_span::SourceFile;
13use rustc_span::source_map::SourceMap;
14
15use crate::emitter::FileWithAnnotatedLines;
16use crate::registry::Registry;
17use crate::snippet::Line;
18use crate::translation::{Translator, to_fluent_args};
19use crate::{
20 CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, Level, MultiSpan, Style, Subdiag,
21};
22
23pub struct AnnotateSnippetEmitter {
25 source_map: Option<Arc<SourceMap>>,
26 translator: Translator,
27
28 short_message: bool,
30 ui_testing: bool,
32
33 macro_backtrace: bool,
34}
35
36impl Emitter for AnnotateSnippetEmitter {
37 fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) {
39 let fluent_args = to_fluent_args(diag.args.iter());
40
41 let mut suggestions = diag.suggestions.unwrap_tag();
42 self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args);
43
44 self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
45 &mut diag.span,
46 &mut diag.children,
47 &diag.level,
48 self.macro_backtrace,
49 );
50
51 self.emit_messages_default(
52 &diag.level,
53 &diag.messages,
54 &fluent_args,
55 &diag.code,
56 &diag.span,
57 &diag.children,
58 &suggestions,
59 );
60 }
61
62 fn source_map(&self) -> Option<&SourceMap> {
63 self.source_map.as_deref()
64 }
65
66 fn should_show_explain(&self) -> bool {
67 !self.short_message
68 }
69
70 fn translator(&self) -> &Translator {
71 &self.translator
72 }
73}
74
75fn source_string(file: Arc<SourceFile>, line: &Line) -> String {
77 file.get_line(line.line_index - 1).map(|a| a.to_string()).unwrap_or_default()
78}
79
80fn annotation_level_for_level(level: Level) -> annotate_snippets::Level {
82 match level {
83 Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => {
84 annotate_snippets::Level::Error
85 }
86 Level::ForceWarning | Level::Warning => annotate_snippets::Level::Warning,
87 Level::Note | Level::OnceNote => annotate_snippets::Level::Note,
88 Level::Help | Level::OnceHelp => annotate_snippets::Level::Help,
89 Level::FailureNote => annotate_snippets::Level::Error,
91 Level::Allow => panic!("Should not call with Allow"),
92 Level::Expect => panic!("Should not call with Expect"),
93 }
94}
95
96impl AnnotateSnippetEmitter {
97 pub fn new(
98 source_map: Option<Arc<SourceMap>>,
99 translator: Translator,
100 short_message: bool,
101 macro_backtrace: bool,
102 ) -> Self {
103 Self { source_map, translator, short_message, ui_testing: false, macro_backtrace }
104 }
105
106 pub fn ui_testing(mut self, ui_testing: bool) -> Self {
110 self.ui_testing = ui_testing;
111 self
112 }
113
114 fn emit_messages_default(
115 &mut self,
116 level: &Level,
117 messages: &[(DiagMessage, Style)],
118 args: &FluentArgs<'_>,
119 code: &Option<ErrCode>,
120 msp: &MultiSpan,
121 _children: &[Subdiag],
122 _suggestions: &[CodeSuggestion],
123 ) {
124 let message = self.translator.translate_messages(messages, args);
125 if let Some(source_map) = &self.source_map {
126 let primary_lo = if let Some(primary_span) = msp.primary_span().as_ref() {
128 if primary_span.is_dummy() {
129 return;
132 } else {
133 source_map.lookup_char_pos(primary_span.lo())
134 }
135 } else {
136 return;
139 };
140 let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp);
141 if let Ok(pos) =
142 annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name))
143 {
144 annotated_files.swap(0, pos);
145 }
146 type Owned = (String, String, usize, Vec<crate::snippet::Annotation>);
148 let annotated_files: Vec<Owned> = annotated_files
149 .into_iter()
150 .flat_map(|annotated_file| {
151 let file = annotated_file.file;
152 annotated_file
153 .lines
154 .into_iter()
155 .map(|line| {
156 source_map.ensure_source_file_source_present(&file);
160 (
161 format!("{}", source_map.filename_for_diagnostics(&file.name)),
162 source_string(Arc::clone(&file), &line),
163 line.line_index,
164 line.annotations,
165 )
166 })
167 .collect::<Vec<Owned>>()
168 })
169 .collect();
170 let code = code.map(|code| code.to_string());
171
172 let snippets =
173 annotated_files.iter().map(|(file_name, source, line_index, annotations)| {
174 Snippet::source(source)
175 .line_start(*line_index)
176 .origin(file_name)
177 .fold(false)
179 .annotations(annotations.iter().map(|annotation| {
180 annotation_level_for_level(*level)
181 .span(annotation.start_col.display..annotation.end_col.display)
182 .label(annotation.label.as_deref().unwrap_or_default())
183 }))
184 });
185 let mut message = annotation_level_for_level(*level).title(&message).snippets(snippets);
186 if let Some(code) = code.as_deref() {
187 message = message.id(code)
188 }
189 let renderer = Renderer::plain().anonymized_line_numbers(self.ui_testing);
193 eprintln!("{}", renderer.render(message))
194 }
195 }
197}