1use std::borrow::Cow;
11use std::error::Report;
12use std::io::prelude::*;
13use std::io::{self, IsTerminal};
14use std::iter;
15use std::path::Path;
16
17use anstream::{AutoStream, ColorChoice};
18use anstyle::{AnsiColor, Effects};
19use rustc_data_structures::fx::FxIndexSet;
20use rustc_data_structures::sync::DynSend;
21use rustc_error_messages::FluentArgs;
22use rustc_span::hygiene::{ExpnKind, MacroKind};
23use rustc_span::source_map::SourceMap;
24use rustc_span::{FileName, SourceFile, Span};
25use tracing::{debug, warn};
26
27use crate::registry::Registry;
28use crate::timings::TimingRecord;
29use crate::translation::Translator;
30use crate::{
31 CodeSuggestion, DiagInner, DiagMessage, Level, MultiSpan, Style, Subdiag, SuggestionStyle,
32};
33
34#[derive(#[automatically_derived]
impl ::core::clone::Clone for HumanReadableErrorType {
#[inline]
fn clone(&self) -> HumanReadableErrorType {
let _: ::core::clone::AssertParamIsClone<bool>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for HumanReadableErrorType { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for HumanReadableErrorType {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f,
"HumanReadableErrorType", "short", &self.short, "unicode",
&&self.unicode)
}
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for HumanReadableErrorType {
#[inline]
fn eq(&self, other: &HumanReadableErrorType) -> bool {
self.short == other.short && self.unicode == other.unicode
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for HumanReadableErrorType {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<bool>;
}
}Eq)]
36pub struct HumanReadableErrorType {
37 pub short: bool,
38 pub unicode: bool,
39}
40
41impl HumanReadableErrorType {
42 pub fn short(&self) -> bool {
43 self.short
44 }
45}
46
47pub enum TimingEvent {
48 Start,
49 End,
50}
51
52pub type DynEmitter = dyn Emitter + DynSend;
53
54pub trait Emitter {
56 fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry);
58
59 fn emit_artifact_notification(&mut self, _path: &Path, _artifact_type: &str) {}
62
63 fn emit_timing_section(&mut self, _record: TimingRecord, _event: TimingEvent) {}
66
67 fn emit_future_breakage_report(&mut self, _diags: Vec<DiagInner>, _registry: &Registry) {}
70
71 fn emit_unused_externs(
74 &mut self,
75 _lint_level: rustc_lint_defs::Level,
76 _unused_externs: &[&str],
77 ) {
78 }
79
80 fn should_show_explain(&self) -> bool {
82 true
83 }
84
85 fn supports_color(&self) -> bool {
87 false
88 }
89
90 fn source_map(&self) -> Option<&SourceMap>;
91
92 fn translator(&self) -> &Translator;
93
94 fn primary_span_formatted(
106 &self,
107 primary_span: &mut MultiSpan,
108 suggestions: &mut Vec<CodeSuggestion>,
109 fluent_args: &FluentArgs<'_>,
110 ) {
111 if let Some((sugg, rest)) = suggestions.split_first() {
112 let msg = self
113 .translator()
114 .translate_message(&sugg.msg, fluent_args)
115 .map_err(Report::new)
116 .unwrap();
117 if rest.is_empty()
118 && let [substitution] = sugg.substitutions.as_slice()
121 && let [part] = substitution.parts.as_slice()
123 && msg.split_whitespace().count() < 10
125 && !part.snippet.contains('\n')
127 && ![
128 SuggestionStyle::HideCodeAlways,
130 SuggestionStyle::CompletelyHidden,
132 SuggestionStyle::ShowAlways,
134 ].contains(&sugg.style)
135 {
136 let snippet = part.snippet.trim();
137 let msg = if snippet.is_empty() || sugg.style.hide_inline() {
138 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("help: {0}", msg))
})format!("help: {msg}")
141 } else {
142 let confusion_type = self
144 .source_map()
145 .map(|sm| detect_confusion_type(sm, snippet, part.span))
146 .unwrap_or(ConfusionType::None);
147 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("help: {0}{1}: `{2}`", msg,
confusion_type.label_text(), snippet))
})format!("help: {}{}: `{}`", msg, confusion_type.label_text(), snippet,)
148 };
149 primary_span.push_span_label(part.span, msg);
150
151 suggestions.clear();
153 } else {
154 }
159 } else {
160 }
162 }
163
164 fn fix_multispans_in_extern_macros_and_render_macro_backtrace(
165 &self,
166 span: &mut MultiSpan,
167 children: &mut Vec<Subdiag>,
168 level: &Level,
169 backtrace: bool,
170 ) {
171 let has_macro_spans: Vec<_> = iter::once(&*span)
174 .chain(children.iter().map(|child| &child.span))
175 .flat_map(|span| span.primary_spans())
176 .flat_map(|sp| sp.macro_backtrace())
177 .filter_map(|expn_data| {
178 match expn_data.kind {
179 ExpnKind::Root => None,
180
181 ExpnKind::Desugaring(..) | ExpnKind::AstPass(..) => None,
184
185 ExpnKind::Macro(macro_kind, name) => {
186 Some((macro_kind, name, expn_data.hide_backtrace))
187 }
188 }
189 })
190 .collect();
191
192 if !backtrace {
193 self.fix_multispans_in_extern_macros(span, children);
194 }
195
196 self.render_multispans_macro_backtrace(span, children, backtrace);
197
198 if !backtrace {
199 if let Some((macro_kind, name, _)) = has_macro_spans.first()
202 && let Some((_, _, false)) = has_macro_spans.last()
203 {
204 let and_then = if let Some((macro_kind, last_name, _)) = has_macro_spans.last()
206 && last_name != name
207 {
208 let descr = macro_kind.descr();
209 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" which comes from the expansion of the {0} `{1}`",
descr, last_name))
})format!(" which comes from the expansion of the {descr} `{last_name}`")
210 } else {
211 "".to_string()
212 };
213
214 let descr = macro_kind.descr();
215 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("this {0} originates in the {1} `{2}`{3} (in Nightly builds, run with -Z macro-backtrace for more info)",
level, descr, name, and_then))
})format!(
216 "this {level} originates in the {descr} `{name}`{and_then} \
217 (in Nightly builds, run with -Z macro-backtrace for more info)",
218 );
219
220 children.push(Subdiag {
221 level: Level::Note,
222 messages: <[_]>::into_vec(::alloc::boxed::box_new([(DiagMessage::from(msg),
Style::NoStyle)]))vec![(DiagMessage::from(msg), Style::NoStyle)],
223 span: MultiSpan::new(),
224 });
225 }
226 }
227 }
228
229 fn render_multispans_macro_backtrace(
230 &self,
231 span: &mut MultiSpan,
232 children: &mut Vec<Subdiag>,
233 backtrace: bool,
234 ) {
235 for span in iter::once(span).chain(children.iter_mut().map(|child| &mut child.span)) {
236 self.render_multispan_macro_backtrace(span, backtrace);
237 }
238 }
239
240 fn render_multispan_macro_backtrace(&self, span: &mut MultiSpan, always_backtrace: bool) {
241 let mut new_labels = FxIndexSet::default();
242
243 for &sp in span.primary_spans() {
244 if sp.is_dummy() {
245 continue;
246 }
247
248 let macro_backtrace: Vec<_> = sp.macro_backtrace().collect();
252 for (i, trace) in macro_backtrace.iter().rev().enumerate() {
253 if trace.def_site.is_dummy() {
254 continue;
255 }
256
257 if always_backtrace {
258 new_labels.insert((
259 trace.def_site,
260 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("in this expansion of `{0}`{1}",
trace.kind.descr(),
if macro_backtrace.len() > 1 {
::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" (#{0})", i + 1))
})
} else { String::new() }))
})format!(
261 "in this expansion of `{}`{}",
262 trace.kind.descr(),
263 if macro_backtrace.len() > 1 {
264 format!(" (#{})", i + 1)
267 } else {
268 String::new()
269 },
270 ),
271 ));
272 }
273
274 let redundant_span = trace.call_site.contains(sp);
286
287 if !redundant_span || always_backtrace {
288 let msg: Cow<'static, _> = match trace.kind {
289 ExpnKind::Macro(MacroKind::Attr, _) => {
290 "this attribute macro expansion".into()
291 }
292 ExpnKind::Macro(MacroKind::Derive, _) => {
293 "this derive macro expansion".into()
294 }
295 ExpnKind::Macro(MacroKind::Bang, _) => "this macro invocation".into(),
296 ExpnKind::Root => "the crate root".into(),
297 ExpnKind::AstPass(kind) => kind.descr().into(),
298 ExpnKind::Desugaring(kind) => {
299 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("this {0} desugaring",
kind.descr()))
})format!("this {} desugaring", kind.descr()).into()
300 }
301 };
302 new_labels.insert((
303 trace.call_site,
304 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("in {0}{1}", msg,
if macro_backtrace.len() > 1 && always_backtrace {
::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" (#{0})", i + 1))
})
} else { String::new() }))
})format!(
305 "in {}{}",
306 msg,
307 if macro_backtrace.len() > 1 && always_backtrace {
308 format!(" (#{})", i + 1)
311 } else {
312 String::new()
313 },
314 ),
315 ));
316 }
317 if !always_backtrace {
318 break;
319 }
320 }
321 }
322
323 for (label_span, label_text) in new_labels {
324 span.push_span_label(label_span, label_text);
325 }
326 }
327
328 fn fix_multispans_in_extern_macros(&self, span: &mut MultiSpan, children: &mut Vec<Subdiag>) {
332 {
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/emitter.rs:332",
"rustc_errors::emitter", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_errors/src/emitter.rs"),
::tracing_core::__macro_support::Option::Some(332u32),
::tracing_core::__macro_support::Option::Some("rustc_errors::emitter"),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("fix_multispans_in_extern_macros: before: span={0:?} children={1:?}",
span, children) as &dyn Value))])
});
} else { ; }
};debug!("fix_multispans_in_extern_macros: before: span={:?} children={:?}", span, children);
333 self.fix_multispan_in_extern_macros(span);
334 for child in children.iter_mut() {
335 self.fix_multispan_in_extern_macros(&mut child.span);
336 }
337 {
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/emitter.rs:337",
"rustc_errors::emitter", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_errors/src/emitter.rs"),
::tracing_core::__macro_support::Option::Some(337u32),
::tracing_core::__macro_support::Option::Some("rustc_errors::emitter"),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("fix_multispans_in_extern_macros: after: span={0:?} children={1:?}",
span, children) as &dyn Value))])
});
} else { ; }
};debug!("fix_multispans_in_extern_macros: after: span={:?} children={:?}", span, children);
338 }
339
340 fn fix_multispan_in_extern_macros(&self, span: &mut MultiSpan) {
344 let Some(source_map) = self.source_map() else { return };
345 let replacements: Vec<(Span, Span)> = span
347 .primary_spans()
348 .iter()
349 .copied()
350 .chain(span.span_labels().iter().map(|sp_label| sp_label.span))
351 .filter_map(|sp| {
352 if !sp.is_dummy() && source_map.is_imported(sp) {
353 let mut span = sp;
354 while let Some(callsite) = span.parent_callsite() {
355 span = callsite;
356 if !source_map.is_imported(span) {
357 return Some((sp, span));
358 }
359 }
360 }
361 None
362 })
363 .collect();
364
365 for (from, to) in replacements {
367 span.replace(from, to);
368 }
369 }
370}
371
372pub struct EmitterWithNote {
374 pub emitter: Box<dyn Emitter + DynSend>,
375 pub note: String,
376}
377
378impl Emitter for EmitterWithNote {
379 fn source_map(&self) -> Option<&SourceMap> {
380 None
381 }
382
383 fn emit_diagnostic(&mut self, mut diag: DiagInner, registry: &Registry) {
384 diag.sub(Level::Note, self.note.clone(), MultiSpan::new());
385 self.emitter.emit_diagnostic(diag, registry);
386 }
387
388 fn translator(&self) -> &Translator {
389 self.emitter.translator()
390 }
391}
392
393pub struct SilentEmitter {
394 pub translator: Translator,
395}
396
397impl Emitter for SilentEmitter {
398 fn source_map(&self) -> Option<&SourceMap> {
399 None
400 }
401
402 fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {}
403
404 fn translator(&self) -> &Translator {
405 &self.translator
406 }
407}
408
409pub const MAX_SUGGESTIONS: usize = 4;
413
414#[derive(#[automatically_derived]
impl ::core::clone::Clone for ColorConfig {
#[inline]
fn clone(&self) -> ColorConfig { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for ColorConfig { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for ColorConfig {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
ColorConfig::Auto => "Auto",
ColorConfig::Always => "Always",
ColorConfig::Never => "Never",
})
}
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for ColorConfig {
#[inline]
fn eq(&self, other: &ColorConfig) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for ColorConfig {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {}
}Eq)]
415pub enum ColorConfig {
416 Auto,
417 Always,
418 Never,
419}
420
421impl ColorConfig {
422 pub fn to_color_choice(self) -> ColorChoice {
423 match self {
424 ColorConfig::Always => {
425 if io::stderr().is_terminal() {
426 ColorChoice::Always
427 } else {
428 ColorChoice::AlwaysAnsi
429 }
430 }
431 ColorConfig::Never => ColorChoice::Never,
432 ColorConfig::Auto if io::stderr().is_terminal() => ColorChoice::Auto,
433 ColorConfig::Auto => ColorChoice::Never,
434 }
435 }
436}
437
438#[derive(#[automatically_derived]
impl ::core::fmt::Debug for OutputTheme {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
OutputTheme::Ascii => "Ascii",
OutputTheme::Unicode => "Unicode",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for OutputTheme {
#[inline]
fn clone(&self) -> OutputTheme { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for OutputTheme { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for OutputTheme {
#[inline]
fn eq(&self, other: &OutputTheme) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for OutputTheme {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {}
}Eq)]
439pub enum OutputTheme {
440 Ascii,
441 Unicode,
442}
443
444const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[
447 ('\0', "␀"),
451 ('\u{0001}', "␁"),
452 ('\u{0002}', "␂"),
453 ('\u{0003}', "␃"),
454 ('\u{0004}', "␄"),
455 ('\u{0005}', "␅"),
456 ('\u{0006}', "␆"),
457 ('\u{0007}', "␇"),
458 ('\u{0008}', "␈"),
459 ('\t', " "), ('\u{000b}', "␋"),
461 ('\u{000c}', "␌"),
462 ('\u{000d}', "␍"),
463 ('\u{000e}', "␎"),
464 ('\u{000f}', "␏"),
465 ('\u{0010}', "␐"),
466 ('\u{0011}', "␑"),
467 ('\u{0012}', "␒"),
468 ('\u{0013}', "␓"),
469 ('\u{0014}', "␔"),
470 ('\u{0015}', "␕"),
471 ('\u{0016}', "␖"),
472 ('\u{0017}', "␗"),
473 ('\u{0018}', "␘"),
474 ('\u{0019}', "␙"),
475 ('\u{001a}', "␚"),
476 ('\u{001b}', "␛"),
477 ('\u{001c}', "␜"),
478 ('\u{001d}', "␝"),
479 ('\u{001e}', "␞"),
480 ('\u{001f}', "␟"),
481 ('\u{007f}', "␡"),
482 ('\u{200d}', ""), ('\u{202a}', "�"), ('\u{202b}', "�"), ('\u{202c}', "�"), ('\u{202d}', "�"),
487 ('\u{202e}', "�"),
488 ('\u{2066}', "�"),
489 ('\u{2067}', "�"),
490 ('\u{2068}', "�"),
491 ('\u{2069}', "�"),
492];
493
494pub(crate) fn normalize_whitespace(s: &str) -> String {
495 const {
496 let mut i = 1;
497 while i < OUTPUT_REPLACEMENTS.len() {
498 if !(OUTPUT_REPLACEMENTS[i - 1].0 < OUTPUT_REPLACEMENTS[i].0) {
{
::core::panicking::panic_fmt(format_args!("The OUTPUT_REPLACEMENTS array must be sorted (for binary search to work) and must contain no duplicate entries"));
}
};assert!(
499 OUTPUT_REPLACEMENTS[i - 1].0 < OUTPUT_REPLACEMENTS[i].0,
500 "The OUTPUT_REPLACEMENTS array must be sorted (for binary search to work) \
501 and must contain no duplicate entries"
502 );
503 i += 1;
504 }
505 }
506 s.chars().fold(String::with_capacity(s.len()), |mut s, c| {
510 match OUTPUT_REPLACEMENTS.binary_search_by_key(&c, |(k, _)| *k) {
511 Ok(i) => s.push_str(OUTPUT_REPLACEMENTS[i].1),
512 _ => s.push(c),
513 }
514 s
515 })
516}
517
518pub type Destination = AutoStream<Box<dyn Write + Send>>;
519
520struct Buffy {
521 buffer_writer: std::io::Stderr,
522 buffer: Vec<u8>,
523}
524
525impl Write for Buffy {
526 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
527 self.buffer.write(buf)
528 }
529
530 fn flush(&mut self) -> io::Result<()> {
531 self.buffer_writer.write_all(&self.buffer)?;
532 self.buffer.clear();
533 Ok(())
534 }
535}
536
537impl Drop for Buffy {
538 fn drop(&mut self) {
539 if !self.buffer.is_empty() {
540 self.flush().unwrap();
541 {
::core::panicking::panic_fmt(format_args!("buffers need to be flushed in order to print their contents"));
};panic!("buffers need to be flushed in order to print their contents");
542 }
543 }
544}
545
546pub fn stderr_destination(color: ColorConfig) -> Destination {
547 let buffer_writer = std::io::stderr();
548 let choice = get_stderr_color_choice(color, &buffer_writer);
551 if falsecfg!(windows) {
558 AutoStream::new(Box::new(buffer_writer), choice)
559 } else {
560 let buffer = Vec::new();
561 AutoStream::new(Box::new(Buffy { buffer_writer, buffer }), choice)
562 }
563}
564
565pub fn get_stderr_color_choice(color: ColorConfig, stderr: &std::io::Stderr) -> ColorChoice {
566 let choice = color.to_color_choice();
567 if #[allow(non_exhaustive_omitted_patterns)] match choice {
ColorChoice::Auto => true,
_ => false,
}matches!(choice, ColorChoice::Auto) { AutoStream::choice(stderr) } else { choice }
568}
569
570const BRIGHT_BLUE: anstyle::Style = if falsecfg!(windows) {
574 AnsiColor::BrightCyan.on_default()
575} else {
576 AnsiColor::BrightBlue.on_default()
577};
578
579impl Style {
580 pub(crate) fn anstyle(&self, lvl: Level) -> anstyle::Style {
581 match self {
582 Style::Addition => AnsiColor::BrightGreen.on_default(),
583 Style::Removal => AnsiColor::BrightRed.on_default(),
584 Style::LineAndColumn => anstyle::Style::new(),
585 Style::LineNumber => BRIGHT_BLUE.effects(Effects::BOLD),
586 Style::Quotation => anstyle::Style::new(),
587 Style::MainHeaderMsg => if falsecfg!(windows) {
588 AnsiColor::BrightWhite.on_default()
589 } else {
590 anstyle::Style::new()
591 }
592 .effects(Effects::BOLD),
593 Style::UnderlinePrimary | Style::LabelPrimary => lvl.color().effects(Effects::BOLD),
594 Style::UnderlineSecondary | Style::LabelSecondary => BRIGHT_BLUE.effects(Effects::BOLD),
595 Style::HeaderMsg | Style::NoStyle => anstyle::Style::new(),
596 Style::Level(lvl) => lvl.color().effects(Effects::BOLD),
597 Style::Highlight => AnsiColor::Magenta.on_default().effects(Effects::BOLD),
598 }
599 }
600}
601
602pub fn is_different(sm: &SourceMap, suggested: &str, sp: Span) -> bool {
604 let found = match sm.span_to_snippet(sp) {
605 Ok(snippet) => snippet,
606 Err(e) => {
607 {
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/emitter.rs:607",
"rustc_errors::emitter", ::tracing::Level::WARN,
::tracing_core::__macro_support::Option::Some("compiler/rustc_errors/src/emitter.rs"),
::tracing_core::__macro_support::Option::Some(607u32),
::tracing_core::__macro_support::Option::Some("rustc_errors::emitter"),
::tracing_core::field::FieldSet::new(&["message", "error"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::WARN <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::WARN <=
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("Invalid span {0:?}",
sp) as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&e) as
&dyn Value))])
});
} else { ; }
};warn!(error = ?e, "Invalid span {:?}", sp);
608 return true;
609 }
610 };
611 found != suggested
612}
613
614pub fn detect_confusion_type(sm: &SourceMap, suggested: &str, sp: Span) -> ConfusionType {
616 let found = match sm.span_to_snippet(sp) {
617 Ok(snippet) => snippet,
618 Err(e) => {
619 {
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/emitter.rs:619",
"rustc_errors::emitter", ::tracing::Level::WARN,
::tracing_core::__macro_support::Option::Some("compiler/rustc_errors/src/emitter.rs"),
::tracing_core::__macro_support::Option::Some(619u32),
::tracing_core::__macro_support::Option::Some("rustc_errors::emitter"),
::tracing_core::field::FieldSet::new(&["message", "error"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::WARN <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::WARN <=
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("Invalid span {0:?}",
sp) as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&e) as
&dyn Value))])
});
} else { ; }
};warn!(error = ?e, "Invalid span {:?}", sp);
620 return ConfusionType::None;
621 }
622 };
623
624 let mut has_case_confusion = false;
625 let mut has_digit_letter_confusion = false;
626
627 if found.len() == suggested.len() {
628 let mut has_case_diff = false;
629 let mut has_digit_letter_confusable = false;
630 let mut has_other_diff = false;
631
632 let ascii_confusables = &['c', 'f', 'i', 'k', 'o', 's', 'u', 'v', 'w', 'x', 'y', 'z'];
635
636 let digit_letter_confusables = [('0', 'O'), ('1', 'l'), ('5', 'S'), ('8', 'B'), ('9', 'g')];
637
638 for (f, s) in iter::zip(found.chars(), suggested.chars()) {
639 if f != s {
640 if f.eq_ignore_ascii_case(&s) {
641 if ascii_confusables.contains(&f) || ascii_confusables.contains(&s) {
643 has_case_diff = true;
644 } else {
645 has_other_diff = true;
646 }
647 } else if digit_letter_confusables.contains(&(f, s))
648 || digit_letter_confusables.contains(&(s, f))
649 {
650 has_digit_letter_confusable = true;
652 } else {
653 has_other_diff = true;
654 }
655 }
656 }
657
658 if has_case_diff && !has_other_diff && found != suggested {
660 has_case_confusion = true;
661 }
662 if has_digit_letter_confusable && !has_other_diff && found != suggested {
663 has_digit_letter_confusion = true;
664 }
665 }
666
667 match (has_case_confusion, has_digit_letter_confusion) {
668 (true, true) => ConfusionType::Both,
669 (true, false) => ConfusionType::Case,
670 (false, true) => ConfusionType::DigitLetter,
671 (false, false) => ConfusionType::None,
672 }
673}
674
675#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ConfusionType {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
ConfusionType::None => "None",
ConfusionType::Case => "Case",
ConfusionType::DigitLetter => "DigitLetter",
ConfusionType::Both => "Both",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for ConfusionType {
#[inline]
fn clone(&self) -> ConfusionType { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for ConfusionType { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for ConfusionType {
#[inline]
fn eq(&self, other: &ConfusionType) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for ConfusionType {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {}
}Eq)]
677pub enum ConfusionType {
678 None,
680 Case,
682 DigitLetter,
684 Both,
686}
687
688impl ConfusionType {
689 pub fn label_text(&self) -> &'static str {
691 match self {
692 ConfusionType::None => "",
693 ConfusionType::Case => " (notice the capitalization)",
694 ConfusionType::DigitLetter => " (notice the digit/letter confusion)",
695 ConfusionType::Both => " (notice the capitalization and digit/letter confusion)",
696 }
697 }
698
699 pub fn combine(self, other: ConfusionType) -> ConfusionType {
703 match (self, other) {
704 (ConfusionType::None, other) => other,
705 (this, ConfusionType::None) => this,
706 (ConfusionType::Both, _) | (_, ConfusionType::Both) => ConfusionType::Both,
707 (ConfusionType::Case, ConfusionType::DigitLetter)
708 | (ConfusionType::DigitLetter, ConfusionType::Case) => ConfusionType::Both,
709 (ConfusionType::Case, ConfusionType::Case) => ConfusionType::Case,
710 (ConfusionType::DigitLetter, ConfusionType::DigitLetter) => ConfusionType::DigitLetter,
711 }
712 }
713
714 pub fn has_confusion(&self) -> bool {
716 *self != ConfusionType::None
717 }
718}
719
720pub(crate) fn should_show_source_code(
721 ignored_directories: &[String],
722 sm: &SourceMap,
723 file: &SourceFile,
724) -> bool {
725 if !sm.ensure_source_file_source_present(file) {
726 return false;
727 }
728
729 let FileName::Real(name) = &file.name else { return true };
730 name.local_path()
731 .map(|path| ignored_directories.iter().all(|dir| !path.starts_with(dir)))
732 .unwrap_or(true)
733}