1use std::borrow::Cow;
9use std::fmt::Debug;
10use std::io;
11use std::io::Write;
12use std::sync::Arc;
13
14use annotate_snippets::renderer::DEFAULT_TERM_WIDTH;
15use annotate_snippets::{AnnotationKind, Group, Origin, Padding, Patch, Renderer, Snippet};
16use anstream::ColorChoice;
17use derive_setters::Setters;
18use rustc_data_structures::sync::IntoDynSyncSend;
19use rustc_error_messages::{DiagArgMap, SpanLabel};
20use rustc_lint_defs::pluralize;
21use rustc_span::source_map::SourceMap;
22use rustc_span::{BytePos, FileName, Pos, SourceFile, Span};
23use tracing::debug;
24
25use crate::emitter::{
26 ConfusionType, Destination, MAX_SUGGESTIONS, OutputTheme, detect_confusion_type, is_different,
27 normalize_whitespace, should_show_source_code,
28};
29use crate::formatting::{format_diag_message, format_diag_messages};
30use crate::{
31 CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, Level, MultiSpan, Style, Subdiag,
32 SuggestionStyle, TerminalUrl,
33};
34
35#[derive(impl AnnotateSnippetEmitter {
#[must_use]
pub fn sm(mut self, value: Option<Arc<SourceMap>>) -> Self {
self.sm = value;
self
}
#[must_use]
pub fn short_message(mut self, value: bool) -> Self {
self.short_message = value;
self
}
#[must_use]
pub fn ui_testing(mut self, value: bool) -> Self {
self.ui_testing = value;
self
}
#[must_use]
pub fn ignored_directories_in_source_blocks(mut self, value: Vec<String>)
-> Self {
self.ignored_directories_in_source_blocks = value;
self
}
#[must_use]
pub fn diagnostic_width(mut self, value: Option<usize>) -> Self {
self.diagnostic_width = value;
self
}
#[must_use]
pub fn macro_backtrace(mut self, value: bool) -> Self {
self.macro_backtrace = value;
self
}
#[must_use]
pub fn track_diagnostics(mut self, value: bool) -> Self {
self.track_diagnostics = value;
self
}
#[must_use]
pub fn terminal_url(mut self, value: TerminalUrl) -> Self {
self.terminal_url = value;
self
}
#[must_use]
pub fn theme(mut self, value: OutputTheme) -> Self {
self.theme = value;
self
}
}Setters)]
37pub struct AnnotateSnippetEmitter {
38 #[setters(skip)]
39 dst: IntoDynSyncSend<Destination>,
40 sm: Option<Arc<SourceMap>>,
41 short_message: bool,
42 ui_testing: bool,
43 ignored_directories_in_source_blocks: Vec<String>,
44 diagnostic_width: Option<usize>,
45 macro_backtrace: bool,
46 track_diagnostics: bool,
47 terminal_url: TerminalUrl,
48 theme: OutputTheme,
49}
50
51impl Debug for AnnotateSnippetEmitter {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 let AnnotateSnippetEmitter {
54 dst,
55 sm,
56 short_message,
57 ui_testing,
58 ignored_directories_in_source_blocks,
59 diagnostic_width,
60 macro_backtrace,
61 track_diagnostics,
62 terminal_url,
63 theme,
64 } = self;
65
66 f.debug_struct("AnnotateSnippetEmitter")
67 .field("dst", &format_args!("<writer@{0:p}>", dst)format_args!("<writer@{dst:p}>"))
68 .field("sm", sm)
69 .field("short_message", short_message)
70 .field("ui_testing", ui_testing)
71 .field("ignored_directories_in_source_blocks", ignored_directories_in_source_blocks)
72 .field("diagnostic_width", diagnostic_width)
73 .field("macro_backtrace", macro_backtrace)
74 .field("track_diagnostics", track_diagnostics)
75 .field("terminal_url", terminal_url)
76 .field("theme", theme)
77 .finish()
78 }
79}
80
81impl Emitter for AnnotateSnippetEmitter {
82 fn emit_diagnostic(&mut self, mut diag: DiagInner) {
84 if self.track_diagnostics && diag.span.has_primary_spans() && !diag.span.is_dummy() {
85 diag.children.insert(0, diag.emitted_at_sub_diag());
86 }
87
88 let mut suggestions = diag.suggestions.unwrap_tag();
89 self.primary_span_formatted(&mut diag.span, &mut suggestions, &diag.args);
90
91 self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
92 &mut diag.span,
93 &mut diag.children,
94 &diag.level,
95 self.macro_backtrace,
96 );
97
98 self.emit_messages_default(
99 &diag.level,
100 &diag.messages,
101 &diag.args,
102 &diag.code,
103 &diag.span,
104 &diag.children,
105 suggestions,
106 );
107 }
108
109 fn source_map(&self) -> Option<&SourceMap> {
110 self.sm.as_deref()
111 }
112
113 fn should_show_explain(&self) -> bool {
114 !self.short_message
115 }
116
117 fn supports_color(&self) -> bool {
118 false
119 }
120}
121
122fn annotation_level_for_level(level: Level) -> annotate_snippets::level::Level<'static> {
123 match level {
124 Level::Bug | Level::DelayedBug => {
125 annotate_snippets::Level::ERROR.with_name("error: internal compiler error")
126 }
127 Level::Fatal | Level::Error => annotate_snippets::level::ERROR,
128 Level::ForceWarning | Level::Warning => annotate_snippets::Level::WARNING,
129 Level::Note | Level::OnceNote => annotate_snippets::Level::NOTE,
130 Level::Help | Level::OnceHelp => annotate_snippets::Level::HELP,
131 Level::FailureNote => annotate_snippets::Level::NOTE.no_name(),
132 Level::Allow => { ::core::panicking::panic_fmt(format_args!("Should not call with Allow")); }panic!("Should not call with Allow"),
133 Level::Expect => { ::core::panicking::panic_fmt(format_args!("Should not call with Expect")); }panic!("Should not call with Expect"),
134 }
135}
136
137impl AnnotateSnippetEmitter {
138 pub fn new(dst: Destination) -> Self {
139 Self {
140 dst: IntoDynSyncSend(dst),
141 sm: None,
142 short_message: false,
143 ui_testing: false,
144 ignored_directories_in_source_blocks: Vec::new(),
145 diagnostic_width: None,
146 macro_backtrace: false,
147 track_diagnostics: false,
148 terminal_url: TerminalUrl::No,
149 theme: OutputTheme::Ascii,
150 }
151 }
152
153 fn emit_messages_default(
154 &mut self,
155 level: &Level,
156 msgs: &[(DiagMessage, Style)],
157 args: &DiagArgMap,
158 code: &Option<ErrCode>,
159 msp: &MultiSpan,
160 children: &[Subdiag],
161 suggestions: Vec<CodeSuggestion>,
162 ) {
163 let renderer = self.renderer();
164 let annotation_level = annotation_level_for_level(*level);
165
166 let mut title = if msgs.iter().any(|(_, style)| style != &crate::Style::NoStyle) {
169 annotation_level
170 .clone()
171 .secondary_title(Cow::Owned(self.pre_style_msgs(msgs, *level, args)))
172 } else {
173 annotation_level.clone().primary_title(format_diag_messages(msgs, args))
174 };
175
176 if let Some(c) = code {
177 title = title.id(c.to_string());
178 if let TerminalUrl::Yes = self.terminal_url {
179 title = title.id_url(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("https://doc.rust-lang.org/error_codes/{0}.html",
c))
})format!("https://doc.rust-lang.org/error_codes/{c}.html"));
180 }
181 }
182
183 let mut report = ::alloc::vec::Vec::new()vec![];
184 let mut group = Group::with_title(title);
185
186 let Some(sm) = self.sm.as_ref() else {
188 group = group.elements(children.iter().map(|c| {
189 let msg = format_diag_messages(&c.messages, args).to_string();
190 let level = annotation_level_for_level(c.level);
191 level.message(msg)
192 }));
193
194 report.push(group);
195 if let Err(e) = emit_to_destination(
196 renderer.render(&report),
197 level,
198 &mut self.dst,
199 self.short_message,
200 ) {
201 {
::core::panicking::panic_fmt(format_args!("failed to emit error: {0}",
e));
};panic!("failed to emit error: {e}");
202 }
203 return;
204 };
205
206 let mut file_ann = collect_annotations(args, msp, sm);
207
208 let primary_span = msp.primary_span().unwrap_or_default();
210 if !primary_span.is_dummy() {
211 let primary_lo = sm.lookup_char_pos(primary_span.lo());
212 if let Ok(pos) = file_ann.binary_search_by(|(f, _)| f.name.cmp(&primary_lo.file.name)) {
213 file_ann.swap(0, pos);
214 }
215
216 let file_ann_len = file_ann.len();
217 for (file_idx, (file, annotations)) in file_ann.into_iter().enumerate() {
218 if should_show_source_code(&self.ignored_directories_in_source_blocks, sm, &file) {
219 if let Some(snippet) = self.annotated_snippet(annotations, &file.name, sm) {
220 group = group.element(snippet);
221 }
222 } else if !self.short_message {
224 group = self.unannotated_messages(
226 annotations,
227 &file.name,
228 sm,
229 file_idx,
230 &mut report,
231 group,
232 &annotation_level,
233 );
234 if let Some(c) = children.first()
243 && (!c.span.has_primary_spans() && !c.span.has_span_labels())
244 && file_idx == file_ann_len - 1
245 {
246 group = group.element(Padding);
247 }
248 }
249 }
250 }
251
252 for c in children {
253 let level = annotation_level_for_level(c.level);
254
255 let msg = if c.messages.iter().any(|(_, style)| style != &crate::Style::NoStyle) {
258 Cow::Owned(self.pre_style_msgs(&c.messages, c.level, args))
259 } else {
260 format_diag_messages(&c.messages, args)
261 };
262
263 if !c.span.has_primary_spans() && !c.span.has_span_labels() {
265 group = group.element(level.clone().message(msg));
266 continue;
267 }
268
269 report.push(std::mem::replace(
270 &mut group,
271 Group::with_title(level.clone().secondary_title(msg)),
272 ));
273
274 let mut file_ann = collect_annotations(args, &c.span, sm);
275 let primary_span = c.span.primary_span().unwrap_or_default();
276 if !primary_span.is_dummy() {
277 let primary_lo = sm.lookup_char_pos(primary_span.lo());
278 if let Ok(pos) =
279 file_ann.binary_search_by(|(f, _)| f.name.cmp(&primary_lo.file.name))
280 {
281 file_ann.swap(0, pos);
282 }
283 }
284
285 for (file_idx, (file, annotations)) in file_ann.into_iter().enumerate() {
286 if should_show_source_code(&self.ignored_directories_in_source_blocks, sm, &file) {
287 if let Some(snippet) = self.annotated_snippet(annotations, &file.name, sm) {
288 group = group.element(snippet);
289 }
290 } else if !self.short_message {
292 group = self.unannotated_messages(
294 annotations,
295 &file.name,
296 sm,
297 file_idx,
298 &mut report,
299 group,
300 &level,
301 );
302 }
303 }
304 }
305
306 for suggestion in suggestions {
307 match suggestion.style {
308 SuggestionStyle::CompletelyHidden => {
309 }
311 SuggestionStyle::HideCodeAlways => {
312 let msg = format_diag_messages(
313 &[(suggestion.msg.to_owned(), Style::HeaderMsg)],
314 args,
315 );
316 group = group.element(annotate_snippets::Level::HELP.message(msg));
317 }
318 SuggestionStyle::HideCodeInline
319 | SuggestionStyle::ShowCode
320 | SuggestionStyle::ShowAlways => {
321 let substitutions = suggestion
322 .substitutions
323 .into_iter()
324 .filter(|subst| {
325 let invalid =
328 subst.parts.iter().any(|item| sm.is_valid_span(item.span).is_err());
329 if invalid {
330 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs:330",
"rustc_errors::annotate_snippet_emitter_writer",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs"),
::tracing_core::__macro_support::Option::Some(330u32),
::tracing_core::__macro_support::Option::Some("rustc_errors::annotate_snippet_emitter_writer"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("suggestion contains an invalid span: {0:?}",
subst) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("suggestion contains an invalid span: {:?}", subst);
331 }
332 !invalid
333 })
334 .filter_map(|mut subst| {
335 subst.parts.sort_by_key(|part| part.span.lo());
338 if true {
{
match (&subst.parts.array_windows().find(|[a, b]|
a.span.overlaps(b.span)), &None) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val,
::core::option::Option::Some(format_args!("all spans must be disjoint")));
}
}
}
};
};debug_assert_eq!(
340 subst.parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
341 None,
342 "all spans must be disjoint",
343 );
344
345 let lo = subst.parts.iter().map(|part| part.span.lo()).min()?;
346 let lo_file = sm.lookup_source_file(lo);
347 let hi = subst.parts.iter().map(|part| part.span.hi()).max()?;
348 let hi_file = sm.lookup_source_file(hi);
349
350 if lo_file.stable_id != hi_file.stable_id {
352 return None;
353 }
354
355 if !sm.ensure_source_file_source_present(&lo_file) {
357 return None;
358 }
359
360 subst.parts.retain(|p| is_different(sm, &p.snippet, p.span));
364
365 if subst.parts.is_empty() { None } else { Some(subst) }
366 })
367 .collect::<Vec<_>>();
368
369 if substitutions.is_empty() {
370 continue;
371 }
372 let mut msg = format_diag_message(&suggestion.msg, args).to_string();
373
374 let lo = substitutions
375 .iter()
376 .find_map(|sub| sub.parts.first().map(|p| p.span.lo()))
377 .unwrap();
378 let file = sm.lookup_source_file(lo);
379
380 let filename =
381 sm.filename_for_diagnostics(&file.name).to_string_lossy().to_string();
382
383 let other_suggestions = substitutions.len().saturating_sub(MAX_SUGGESTIONS);
384
385 let subs = substitutions
386 .into_iter()
387 .take(MAX_SUGGESTIONS)
388 .filter_map(|sub| {
389 let mut confusion_type = ConfusionType::None;
390 for part in &sub.parts {
391 let part_confusion =
392 detect_confusion_type(sm, &part.snippet, part.span);
393 confusion_type = confusion_type.combine(part_confusion);
394 }
395
396 if !#[allow(non_exhaustive_omitted_patterns)] match confusion_type {
ConfusionType::None => true,
_ => false,
}matches!(confusion_type, ConfusionType::None) {
397 msg.push_str(confusion_type.label_text());
398 }
399
400 let mut parts = sub
401 .parts
402 .into_iter()
403 .filter_map(|p| {
404 if is_different(sm, &p.snippet, p.span) {
405 Some((p.span, p.snippet))
406 } else {
407 None
408 }
409 })
410 .collect::<Vec<_>>();
411
412 if parts.is_empty() {
413 None
414 } else {
415 let spans = parts.iter().map(|(span, _)| *span).collect::<Vec<_>>();
416 let fold = if let [(p, snippet)] = &mut parts[..]
423 && snippet.trim().starts_with("#[")
424 && snippet.trim().ends_with("]")
426 && snippet.ends_with('\n')
427 && p.hi() == p.lo()
428 && let Ok(b) = sm.span_to_prev_source(*p)
429 && let b = b.rsplit_once('\n').unwrap_or_else(|| ("", &b)).1
430 && b.trim().is_empty()
431 {
432 if !b.is_empty() && !snippet.ends_with(b) {
458 snippet.insert_str(0, b);
459 let offset = BytePos(b.len() as u32);
460 *p = p.with_lo(p.lo() - offset).shrink_to_lo();
461 }
462 false
463 } else {
464 true
465 };
466
467 if let Some((bounding_span, source, line_offset)) =
468 shrink_file(spans.as_slice(), &file.name, sm)
469 {
470 let adj_lo = bounding_span.lo().to_usize();
471 Some(
472 Snippet::source(source)
473 .line_start(line_offset)
474 .path(filename.clone())
475 .fold(fold)
476 .patches(parts.into_iter().map(
477 |(span, replacement)| {
478 let lo =
479 span.lo().to_usize().saturating_sub(adj_lo);
480 let hi =
481 span.hi().to_usize().saturating_sub(adj_lo);
482
483 Patch::new(lo..hi, replacement)
484 },
485 )),
486 )
487 } else {
488 None
489 }
490 }
491 })
492 .collect::<Vec<_>>();
493 if !subs.is_empty() {
494 report.push(std::mem::replace(
495 &mut group,
496 Group::with_title(annotate_snippets::Level::HELP.secondary_title(msg)),
497 ));
498
499 group = group.elements(subs);
500 if other_suggestions > 0 {
501 group = group.element(
502 annotate_snippets::Level::NOTE.no_name().message(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("and {0} other candidate{1}",
other_suggestions,
if other_suggestions == 1 { "" } else { "s" }))
})format!(
503 "and {} other candidate{}",
504 other_suggestions,
505 pluralize!(other_suggestions)
506 )),
507 );
508 }
509 }
510 }
511 }
512 }
513
514 if !group.is_empty() {
515 report.push(group);
516 }
517 if let Err(e) =
518 emit_to_destination(renderer.render(&report), level, &mut self.dst, self.short_message)
519 {
520 {
::core::panicking::panic_fmt(format_args!("failed to emit error: {0}",
e));
};panic!("failed to emit error: {e}");
521 }
522 }
523
524 fn renderer(&self) -> Renderer {
525 let width = if let Some(width) = self.diagnostic_width {
526 width
527 } else if self.ui_testing || falsecfg!(miri) {
528 DEFAULT_TERM_WIDTH
529 } else {
530 termize::dimensions().map(|(w, _)| w).unwrap_or(DEFAULT_TERM_WIDTH)
531 };
532 let decor_style = match self.theme {
533 OutputTheme::Ascii => annotate_snippets::renderer::DecorStyle::Ascii,
534 OutputTheme::Unicode => annotate_snippets::renderer::DecorStyle::Unicode,
535 };
536
537 match self.dst.current_choice() {
538 ColorChoice::AlwaysAnsi | ColorChoice::Always | ColorChoice::Auto => Renderer::styled(),
539 ColorChoice::Never => Renderer::plain(),
540 }
541 .term_width(width)
542 .anonymized_line_numbers(self.ui_testing)
543 .decor_style(decor_style)
544 .short_message(self.short_message)
545 }
546
547 fn pre_style_msgs(
548 &self,
549 msgs: &[(DiagMessage, Style)],
550 level: Level,
551 args: &DiagArgMap,
552 ) -> String {
553 msgs.iter()
554 .filter_map(|(m, style)| {
555 let text = format_diag_message(m, args);
556 let style = style.anstyle(level);
557 if text.is_empty() { None } else { Some(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}{0:#}", style, text))
})format!("{style}{text}{style:#}")) }
558 })
559 .collect()
560 }
561
562 fn annotated_snippet<'a>(
563 &self,
564 annotations: Vec<Annotation>,
565 file_name: &FileName,
566 sm: &Arc<SourceMap>,
567 ) -> Option<Snippet<'a, annotate_snippets::Annotation<'a>>> {
568 let spans = annotations.iter().map(|a| a.span).collect::<Vec<_>>();
569 if let Some((bounding_span, source, offset_line)) = shrink_file(&spans, file_name, sm) {
570 let adj_lo = bounding_span.lo().to_usize();
571 let filename = sm.filename_for_diagnostics(file_name).to_string_lossy().to_string();
572 Some(Snippet::source(source).line_start(offset_line).path(filename).annotations(
573 annotations.into_iter().map(move |a| {
574 let lo = a.span.lo().to_usize().saturating_sub(adj_lo);
575 let hi = a.span.hi().to_usize().saturating_sub(adj_lo);
576 let ann = a.kind.span(lo..hi);
577 if let Some(label) = a.label { ann.label(label) } else { ann }
578 }),
579 ))
580 } else {
581 None
582 }
583 }
584
585 fn unannotated_messages<'a>(
586 &self,
587 annotations: Vec<Annotation>,
588 file_name: &FileName,
589 sm: &Arc<SourceMap>,
590 file_idx: usize,
591 report: &mut Vec<Group<'a>>,
592 mut group: Group<'a>,
593 level: &annotate_snippets::level::Level<'static>,
594 ) -> Group<'a> {
595 let filename = sm.filename_for_diagnostics(file_name).to_string_lossy().to_string();
596 let mut line_tracker = ::alloc::vec::Vec::new()vec![];
597 for (i, a) in annotations.into_iter().enumerate() {
598 let lo = sm.lookup_char_pos(a.span.lo());
599 let hi = sm.lookup_char_pos(a.span.hi());
600 if i == 0 || (a.label.is_some()) {
601 if i == 0 && file_idx != 0 {
612 report.push(std::mem::replace(&mut group, Group::with_level(level.clone())));
613 }
614
615 if !line_tracker.contains(&lo.line) && (i == 0 || hi.line <= lo.line) {
616 line_tracker.push(lo.line);
617 group = group.element(
622 Origin::path(filename.clone())
623 .line(sm.doctest_offset_line(file_name, lo.line))
624 .char_column(lo.col_display),
625 );
626 }
627
628 if hi.line > lo.line
629 && a.label.as_ref().is_some_and(|l| !l.is_empty())
630 && !line_tracker.contains(&hi.line)
631 {
632 line_tracker.push(hi.line);
633 group = group.element(
638 Origin::path(filename.clone())
639 .line(sm.doctest_offset_line(file_name, hi.line))
640 .char_column(hi.col_display),
641 );
642 }
643
644 if let Some(label) = a.label
645 && !label.is_empty()
646 {
647 group = group
652 .element(Padding)
653 .element(annotate_snippets::Level::NOTE.message(label));
654 }
655 }
656 }
657 group
658 }
659}
660
661fn emit_to_destination(
662 rendered: String,
663 lvl: &Level,
664 dst: &mut Destination,
665 short_message: bool,
666) -> io::Result<()> {
667 use crate::lock;
668 let _buffer_lock = lock::acquire_global_lock("rustc_errors");
669 dst.write_fmt(format_args!("{0}\n", rendered))writeln!(dst, "{rendered}")?;
670 if !short_message && !lvl.is_failure_note() {
671 dst.write_fmt(format_args!("\n"))writeln!(dst)?;
672 }
673 dst.flush()?;
674 Ok(())
675}
676
677#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Annotation {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f, "Annotation",
"kind", &self.kind, "span", &self.span, "label", &&self.label)
}
}Debug)]
678struct Annotation {
679 kind: AnnotationKind,
680 span: Span,
681 label: Option<String>,
682}
683
684fn collect_annotations(
685 args: &DiagArgMap,
686 msp: &MultiSpan,
687 sm: &Arc<SourceMap>,
688) -> Vec<(Arc<SourceFile>, Vec<Annotation>)> {
689 let mut output: Vec<(Arc<SourceFile>, Vec<Annotation>)> = ::alloc::vec::Vec::new()vec![];
690
691 for SpanLabel { span, is_primary, label } in msp.span_labels() {
692 let span = match (span.is_dummy(), msp.primary_span()) {
695 (_, None) | (false, _) => span,
696 (true, Some(span)) => span,
697 };
698 let file = sm.lookup_source_file(span.lo());
699
700 let kind = if is_primary { AnnotationKind::Primary } else { AnnotationKind::Context };
701
702 let label = label.as_ref().map(|m| normalize_whitespace(&format_diag_message(m, args)));
703
704 let ann = Annotation { kind, span, label };
705 if sm.is_valid_span(ann.span).is_ok() {
706 if let Some((_, annotations)) =
711 output.iter_mut().find(|(f, _)| f.stable_id == file.stable_id)
712 {
713 annotations.push(ann);
714 } else {
715 output.push((file, ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[ann]))vec![ann]));
716 }
717 }
718 }
719
720 for (_, ann) in output.iter_mut() {
722 ann.sort_by_key(|a| {
723 let lo = sm.lookup_char_pos(a.span.lo());
724 lo.line
725 });
726 }
727 output
728}
729
730fn shrink_file(
731 spans: &[Span],
732 file_name: &FileName,
733 sm: &Arc<SourceMap>,
734) -> Option<(Span, String, usize)> {
735 let lo_byte = spans.iter().map(|s| s.lo()).min()?;
736 let lo_loc = sm.lookup_char_pos(lo_byte);
737
738 let hi_byte = spans.iter().map(|s| s.hi()).max()?;
739 let hi_loc = sm.lookup_char_pos(hi_byte);
740
741 if lo_loc.file.stable_id != hi_loc.file.stable_id {
742 return None;
744 }
745
746 let lo = lo_loc.file.line_bounds(lo_loc.line.saturating_sub(1)).start;
747 let hi = hi_loc.file.line_bounds(hi_loc.line.saturating_sub(1)).end;
748
749 let bounding_span = Span::with_root_ctxt(lo, hi);
750 let source = sm.span_to_snippet(bounding_span).ok()?;
751 let offset_line = sm.doctest_offset_line(file_name, lo_loc.line);
752
753 Some((bounding_span, source, offset_line))
754}