rustc_errors/
annotate_snippet_emitter_writer.rs
1use 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::{Translate, to_fluent_args};
19use crate::{
20 CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, FluentBundle, LazyFallbackBundle,
21 Level, MultiSpan, Style, Subdiag,
22};
23
24pub struct AnnotateSnippetEmitter {
26 source_map: Option<Arc<SourceMap>>,
27 fluent_bundle: Option<Arc<FluentBundle>>,
28 fallback_bundle: LazyFallbackBundle,
29
30 short_message: bool,
32 ui_testing: bool,
34
35 macro_backtrace: bool,
36}
37
38impl Translate for AnnotateSnippetEmitter {
39 fn fluent_bundle(&self) -> Option<&FluentBundle> {
40 self.fluent_bundle.as_deref()
41 }
42
43 fn fallback_fluent_bundle(&self) -> &FluentBundle {
44 &self.fallback_bundle
45 }
46}
47
48impl Emitter for AnnotateSnippetEmitter {
49 fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) {
51 let fluent_args = to_fluent_args(diag.args.iter());
52
53 let mut suggestions = diag.suggestions.unwrap_tag();
54 self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args);
55
56 self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
57 &mut diag.span,
58 &mut diag.children,
59 &diag.level,
60 self.macro_backtrace,
61 );
62
63 self.emit_messages_default(
64 &diag.level,
65 &diag.messages,
66 &fluent_args,
67 &diag.code,
68 &diag.span,
69 &diag.children,
70 &suggestions,
71 );
72 }
73
74 fn source_map(&self) -> Option<&SourceMap> {
75 self.source_map.as_deref()
76 }
77
78 fn should_show_explain(&self) -> bool {
79 !self.short_message
80 }
81}
82
83fn source_string(file: Arc<SourceFile>, line: &Line) -> String {
85 file.get_line(line.line_index - 1).map(|a| a.to_string()).unwrap_or_default()
86}
87
88fn annotation_level_for_level(level: Level) -> annotate_snippets::Level {
90 match level {
91 Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => {
92 annotate_snippets::Level::Error
93 }
94 Level::ForceWarning(_) | Level::Warning => annotate_snippets::Level::Warning,
95 Level::Note | Level::OnceNote => annotate_snippets::Level::Note,
96 Level::Help | Level::OnceHelp => annotate_snippets::Level::Help,
97 Level::FailureNote => annotate_snippets::Level::Error,
99 Level::Allow => panic!("Should not call with Allow"),
100 Level::Expect(_) => panic!("Should not call with Expect"),
101 }
102}
103
104impl AnnotateSnippetEmitter {
105 pub fn new(
106 source_map: Option<Arc<SourceMap>>,
107 fluent_bundle: Option<Arc<FluentBundle>>,
108 fallback_bundle: LazyFallbackBundle,
109 short_message: bool,
110 macro_backtrace: bool,
111 ) -> Self {
112 Self {
113 source_map,
114 fluent_bundle,
115 fallback_bundle,
116 short_message,
117 ui_testing: false,
118 macro_backtrace,
119 }
120 }
121
122 pub fn ui_testing(mut self, ui_testing: bool) -> Self {
126 self.ui_testing = ui_testing;
127 self
128 }
129
130 fn emit_messages_default(
131 &mut self,
132 level: &Level,
133 messages: &[(DiagMessage, Style)],
134 args: &FluentArgs<'_>,
135 code: &Option<ErrCode>,
136 msp: &MultiSpan,
137 _children: &[Subdiag],
138 _suggestions: &[CodeSuggestion],
139 ) {
140 let message = self.translate_messages(messages, args);
141 if let Some(source_map) = &self.source_map {
142 let primary_lo = if let Some(primary_span) = msp.primary_span().as_ref() {
144 if primary_span.is_dummy() {
145 return;
148 } else {
149 source_map.lookup_char_pos(primary_span.lo())
150 }
151 } else {
152 return;
155 };
156 let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp);
157 if let Ok(pos) =
158 annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name))
159 {
160 annotated_files.swap(0, pos);
161 }
162 type Owned = (String, String, usize, Vec<crate::snippet::Annotation>);
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_map.ensure_source_file_source_present(&file);
176 (
177 format!("{}", source_map.filename_for_diagnostics(&file.name)),
178 source_string(Arc::clone(&file), &line),
179 line.line_index,
180 line.annotations,
181 )
182 })
183 .collect::<Vec<Owned>>()
184 })
185 .collect();
186 let code = code.map(|code| code.to_string());
187
188 let snippets =
189 annotated_files.iter().map(|(file_name, source, line_index, annotations)| {
190 Snippet::source(source)
191 .line_start(*line_index)
192 .origin(file_name)
193 .fold(false)
195 .annotations(annotations.iter().map(|annotation| {
196 annotation_level_for_level(*level)
197 .span(annotation.start_col.display..annotation.end_col.display)
198 .label(annotation.label.as_deref().unwrap_or_default())
199 }))
200 });
201 let mut message = annotation_level_for_level(*level).title(&message).snippets(snippets);
202 if let Some(code) = code.as_deref() {
203 message = message.id(code)
204 }
205 let renderer = Renderer::plain().anonymized_line_numbers(self.ui_testing);
209 eprintln!("{}", renderer.render(message))
210 }
211 }
213}