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