1use std::borrow::Cow;
11use std::io::prelude::*;
12use std::io::{self, IsTerminal};
13use std::iter;
14use std::path::Path;
15
16use anstream::{AutoStream, ColorChoice};
17use anstyle::{AnsiColor, Effects};
18use rustc_data_structures::fx::FxIndexSet;
19use rustc_data_structures::sync::DynSend;
20use rustc_error_messages::DiagArgMap;
21use rustc_span::hygiene::{ExpnKind, MacroKind};
22use rustc_span::source_map::SourceMap;
23use rustc_span::{FileName, SourceFile, Span};
24use tracing::{debug, warn};
25
26use crate::formatting::format_diag_message;
27use crate::timings::TimingRecord;
28use crate::{
29 CodeSuggestion, DiagInner, DiagMessage, Level, MultiSpan, Style, Subdiag, SuggestionStyle,
30};
31
32#[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_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<bool>;
}
}Eq)]
34pub struct HumanReadableErrorType {
35 pub short: bool,
36 pub unicode: bool,
37}
38
39impl HumanReadableErrorType {
40 pub fn short(&self) -> bool {
41 self.short
42 }
43}
44
45pub enum TimingEvent {
46 Start,
47 End,
48}
49
50pub type DynEmitter = dyn Emitter + DynSend;
51
52pub trait Emitter {
54 fn emit_diagnostic(&mut self, diag: DiagInner);
56
57 fn emit_artifact_notification(&mut self, _path: &Path, _artifact_type: &str) {}
60
61 fn emit_timing_section(&mut self, _record: TimingRecord, _event: TimingEvent) {}
64
65 fn emit_future_breakage_report(&mut self, _diags: Vec<DiagInner>) {}
68
69 fn emit_unused_externs(
72 &mut self,
73 _lint_level: rustc_lint_defs::Level,
74 _unused_externs: &[&str],
75 ) {
76 }
77
78 fn should_show_explain(&self) -> bool {
80 true
81 }
82
83 fn supports_color(&self) -> bool {
85 false
86 }
87
88 fn source_map(&self) -> Option<&SourceMap>;
89
90 fn primary_span_formatted(
102 &self,
103 primary_span: &mut MultiSpan,
104 suggestions: &mut Vec<CodeSuggestion>,
105 fluent_args: &DiagArgMap,
106 ) {
107 if let Some((sugg, rest)) = suggestions.split_first() {
108 let msg = format_diag_message(&sugg.msg, fluent_args);
109 if rest.is_empty()
110 && let [substitution] = sugg.substitutions.as_slice()
113 && let [part] = substitution.parts.as_slice()
115 && msg.split_whitespace().count() < 10
117 && !part.snippet.contains('\n')
119 && ![
120 SuggestionStyle::HideCodeAlways,
122 SuggestionStyle::CompletelyHidden,
124 SuggestionStyle::ShowAlways,
126 ].contains(&sugg.style)
127 {
128 let snippet = part.snippet.trim();
129 let msg = if snippet.is_empty() || sugg.style.hide_inline() {
130 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("help: {0}", msg))
})format!("help: {msg}")
133 } else {
134 let confusion_type = self
136 .source_map()
137 .map(|sm| detect_confusion_type(sm, snippet, part.span))
138 .unwrap_or(ConfusionType::None);
139 ::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,)
140 };
141 primary_span.push_span_label(part.span, msg);
142
143 suggestions.clear();
145 } else {
146 }
151 } else {
152 }
154 }
155
156 fn fix_multispans_in_extern_macros_and_render_macro_backtrace(
157 &self,
158 span: &mut MultiSpan,
159 children: &mut Vec<Subdiag>,
160 level: &Level,
161 backtrace: bool,
162 ) {
163 let has_macro_spans: Vec<_> = iter::once(&*span)
166 .chain(children.iter().map(|child| &child.span))
167 .flat_map(|span| span.primary_spans())
168 .flat_map(|sp| sp.macro_backtrace())
169 .filter_map(|expn_data| {
170 match expn_data.kind {
171 ExpnKind::Root => None,
172
173 ExpnKind::Desugaring(..) | ExpnKind::AstPass(..) => None,
176
177 ExpnKind::Macro(macro_kind, name) => {
178 Some((macro_kind, name, expn_data.diagnostic_opaque))
179 }
180 }
181 })
182 .collect();
183
184 if !backtrace {
185 self.fix_multispans_in_extern_macros(span, children);
186 }
187
188 self.render_multispans_macro_backtrace(span, children, backtrace);
189
190 if !backtrace {
191 if let Some((macro_kind, name, _)) = has_macro_spans.first()
193 && let Some((_, _, false)) = has_macro_spans.last()
194 {
195 let and_then = if let Some((macro_kind, last_name, _)) = has_macro_spans.last()
197 && last_name != name
198 {
199 let descr = macro_kind.descr();
200 ::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}`")
201 } else {
202 "".to_string()
203 };
204
205 let descr = macro_kind.descr();
206 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!(
207 "this {level} originates in the {descr} `{name}`{and_then} \
208 (in Nightly builds, run with -Z macro-backtrace for more info)",
209 );
210
211 children.push(Subdiag {
212 level: Level::Note,
213 messages: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(DiagMessage::from(msg), Style::NoStyle)]))vec![(DiagMessage::from(msg), Style::NoStyle)],
214 span: MultiSpan::new(),
215 });
216 }
217 }
218 }
219
220 fn render_multispans_macro_backtrace(
221 &self,
222 span: &mut MultiSpan,
223 children: &mut Vec<Subdiag>,
224 backtrace: bool,
225 ) {
226 for span in iter::once(span).chain(children.iter_mut().map(|child| &mut child.span)) {
227 self.render_multispan_macro_backtrace(span, backtrace);
228 }
229 }
230
231 fn render_multispan_macro_backtrace(&self, span: &mut MultiSpan, always_backtrace: bool) {
232 let mut new_labels = FxIndexSet::default();
233
234 for &sp in span.primary_spans() {
235 if sp.is_dummy() {
236 continue;
237 }
238
239 let macro_backtrace: Vec<_> = sp.macro_backtrace().collect();
243 for (i, trace) in macro_backtrace.iter().rev().enumerate() {
244 if trace.def_site.is_dummy() {
245 continue;
246 }
247
248 if always_backtrace {
249 new_labels.insert((
250 trace.def_site,
251 ::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!(
252 "in this expansion of `{}`{}",
253 trace.kind.descr(),
254 if macro_backtrace.len() > 1 {
255 format!(" (#{})", i + 1)
258 } else {
259 String::new()
260 },
261 ),
262 ));
263 }
264
265 let redundant_span = trace.call_site.contains(sp);
277
278 if !redundant_span || always_backtrace {
279 let msg: Cow<'static, _> = match trace.kind {
280 ExpnKind::Macro(MacroKind::Attr, _) => {
281 "this attribute macro expansion".into()
282 }
283 ExpnKind::Macro(MacroKind::Derive, _) => {
284 "this derive macro expansion".into()
285 }
286 ExpnKind::Macro(MacroKind::Bang, _) => "this macro invocation".into(),
287 ExpnKind::Root => "the crate root".into(),
288 ExpnKind::AstPass(kind) => kind.descr().into(),
289 ExpnKind::Desugaring(kind) => {
290 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("this {0} desugaring",
kind.descr()))
})format!("this {} desugaring", kind.descr()).into()
291 }
292 };
293 new_labels.insert((
294 trace.call_site,
295 ::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!(
296 "in {}{}",
297 msg,
298 if macro_backtrace.len() > 1 && always_backtrace {
299 format!(" (#{})", i + 1)
302 } else {
303 String::new()
304 },
305 ),
306 ));
307 }
308 if !always_backtrace {
309 break;
310 }
311 }
312 }
313
314 for (label_span, label_text) in new_labels {
315 span.push_span_label(label_span, label_text);
316 }
317 }
318
319 fn fix_multispans_in_extern_macros(&self, span: &mut MultiSpan, children: &mut Vec<Subdiag>) {
323 {
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:323",
"rustc_errors::emitter", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_errors/src/emitter.rs"),
::tracing_core::__macro_support::Option::Some(323u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("fix_multispans_in_extern_macros: before: span={0:?} children={1:?}",
span, children) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("fix_multispans_in_extern_macros: before: span={:?} children={:?}", span, children);
324 self.fix_multispan_in_extern_macros(span);
325 for child in children.iter_mut() {
326 self.fix_multispan_in_extern_macros(&mut child.span);
327 }
328 {
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:328",
"rustc_errors::emitter", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_errors/src/emitter.rs"),
::tracing_core::__macro_support::Option::Some(328u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("fix_multispans_in_extern_macros: after: span={0:?} children={1:?}",
span, children) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("fix_multispans_in_extern_macros: after: span={:?} children={:?}", span, children);
329 }
330
331 fn fix_multispan_in_extern_macros(&self, span: &mut MultiSpan) {
335 let Some(source_map) = self.source_map() else { return };
336 let should_hide = |span| {
337 source_map.is_imported(span) || {
338 let expn = span.data().ctxt.outer_expn_data();
339 expn.diagnostic_opaque && #[allow(non_exhaustive_omitted_patterns)] match expn.kind {
ExpnKind::Macro(MacroKind::Bang, _) => true,
_ => false,
}matches!(expn.kind, ExpnKind::Macro(MacroKind::Bang, _))
340 }
341 };
342
343 let replacements: Vec<(Span, Span)> = span
345 .primary_spans()
346 .iter()
347 .copied()
348 .chain(span.span_labels().iter().map(|sp_label| sp_label.span))
349 .filter_map(|sp| {
350 if !sp.is_dummy() && should_hide(sp) {
351 let mut span = sp;
352 while let Some(callsite) = span.parent_callsite() {
353 span = callsite;
354 if !should_hide(span) {
355 return Some((sp, span));
356 }
357 }
358 }
359 None
360 })
361 .collect();
362
363 for (from, to) in replacements {
365 span.replace(from, to);
366 }
367 }
368}
369
370pub struct EmitterWithNote {
372 pub emitter: Box<dyn Emitter + DynSend>,
373 pub note: String,
374}
375
376impl Emitter for EmitterWithNote {
377 fn source_map(&self) -> Option<&SourceMap> {
378 None
379 }
380
381 fn emit_diagnostic(&mut self, mut diag: DiagInner) {
382 diag.sub(Level::Note, self.note.clone(), MultiSpan::new());
383 self.emitter.emit_diagnostic(diag);
384 }
385}
386
387pub struct SilentEmitter;
388
389impl Emitter for SilentEmitter {
390 fn source_map(&self) -> Option<&SourceMap> {
391 None
392 }
393
394 fn emit_diagnostic(&mut self, _diag: DiagInner) {}
395}
396
397pub const MAX_SUGGESTIONS: usize = 4;
401
402#[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_fields_are_eq(&self) {}
}Eq)]
403pub enum ColorConfig {
404 Auto,
405 Always,
406 Never,
407}
408
409impl ColorConfig {
410 pub fn to_color_choice(self) -> ColorChoice {
411 match self {
412 ColorConfig::Always => {
413 if io::stderr().is_terminal() {
414 ColorChoice::Always
415 } else {
416 ColorChoice::AlwaysAnsi
417 }
418 }
419 ColorConfig::Never => ColorChoice::Never,
420 ColorConfig::Auto if io::stderr().is_terminal() => ColorChoice::Auto,
421 ColorConfig::Auto => ColorChoice::Never,
422 }
423 }
424}
425
426#[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_fields_are_eq(&self) {}
}Eq)]
427pub enum OutputTheme {
428 Ascii,
429 Unicode,
430}
431
432const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[
435 ('\0', "␀"),
439 ('\u{0001}', "␁"),
440 ('\u{0002}', "␂"),
441 ('\u{0003}', "␃"),
442 ('\u{0004}', "␄"),
443 ('\u{0005}', "␅"),
444 ('\u{0006}', "␆"),
445 ('\u{0007}', "␇"),
446 ('\u{0008}', "␈"),
447 ('\t', " "), ('\u{000b}', "␋"),
449 ('\u{000c}', "␌"),
450 ('\u{000d}', "␍"),
451 ('\u{000e}', "␎"),
452 ('\u{000f}', "␏"),
453 ('\u{0010}', "␐"),
454 ('\u{0011}', "␑"),
455 ('\u{0012}', "␒"),
456 ('\u{0013}', "␓"),
457 ('\u{0014}', "␔"),
458 ('\u{0015}', "␕"),
459 ('\u{0016}', "␖"),
460 ('\u{0017}', "␗"),
461 ('\u{0018}', "␘"),
462 ('\u{0019}', "␙"),
463 ('\u{001a}', "␚"),
464 ('\u{001b}', "␛"),
465 ('\u{001c}', "␜"),
466 ('\u{001d}', "␝"),
467 ('\u{001e}', "␞"),
468 ('\u{001f}', "␟"),
469 ('\u{007f}', "␡"),
470 ('\u{200d}', ""), ('\u{202a}', "�"), ('\u{202b}', "�"), ('\u{202c}', "�"), ('\u{202d}', "�"),
475 ('\u{202e}', "�"),
476 ('\u{2066}', "�"),
477 ('\u{2067}', "�"),
478 ('\u{2068}', "�"),
479 ('\u{2069}', "�"),
480];
481
482pub(crate) fn normalize_whitespace(s: &str) -> String {
483 const {
484 let mut i = 1;
485 while i < OUTPUT_REPLACEMENTS.len() {
486 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!(
487 OUTPUT_REPLACEMENTS[i - 1].0 < OUTPUT_REPLACEMENTS[i].0,
488 "The OUTPUT_REPLACEMENTS array must be sorted (for binary search to work) \
489 and must contain no duplicate entries"
490 );
491 i += 1;
492 }
493 }
494 s.chars().fold(String::with_capacity(s.len()), |mut s, c| {
498 match OUTPUT_REPLACEMENTS.binary_search_by_key(&c, |(k, _)| *k) {
499 Ok(i) => s.push_str(OUTPUT_REPLACEMENTS[i].1),
500 _ => s.push(c),
501 }
502 s
503 })
504}
505
506pub type Destination = AutoStream<Box<dyn Write + Send>>;
507
508struct Buffy {
509 buffer_writer: std::io::Stderr,
510 buffer: Vec<u8>,
511}
512
513impl Write for Buffy {
514 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
515 self.buffer.write(buf)
516 }
517
518 fn flush(&mut self) -> io::Result<()> {
519 self.buffer_writer.write_all(&self.buffer)?;
520 self.buffer.clear();
521 Ok(())
522 }
523}
524
525impl Drop for Buffy {
526 fn drop(&mut self) {
527 if !self.buffer.is_empty() {
528 self.flush().unwrap();
529 {
::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");
530 }
531 }
532}
533
534pub fn stderr_destination(color: ColorConfig) -> Destination {
535 let buffer_writer = std::io::stderr();
536 let choice = get_stderr_color_choice(color, &buffer_writer);
539 if falsecfg!(windows) {
546 AutoStream::new(Box::new(buffer_writer), choice)
547 } else {
548 let buffer = Vec::new();
549 AutoStream::new(Box::new(Buffy { buffer_writer, buffer }), choice)
550 }
551}
552
553pub fn get_stderr_color_choice(color: ColorConfig, stderr: &std::io::Stderr) -> ColorChoice {
554 let choice = color.to_color_choice();
555 if #[allow(non_exhaustive_omitted_patterns)] match choice {
ColorChoice::Auto => true,
_ => false,
}matches!(choice, ColorChoice::Auto) { AutoStream::choice(stderr) } else { choice }
556}
557
558const BRIGHT_BLUE: anstyle::Style = if falsecfg!(windows) {
562 AnsiColor::BrightCyan.on_default()
563} else {
564 AnsiColor::BrightBlue.on_default()
565};
566
567impl Style {
568 pub(crate) fn anstyle(&self, lvl: Level) -> anstyle::Style {
569 match self {
570 Style::Addition => AnsiColor::BrightGreen.on_default(),
571 Style::Removal => AnsiColor::BrightRed.on_default(),
572 Style::LineAndColumn => anstyle::Style::new(),
573 Style::LineNumber => BRIGHT_BLUE.effects(Effects::BOLD),
574 Style::Quotation => anstyle::Style::new(),
575 Style::MainHeaderMsg => if falsecfg!(windows) {
576 AnsiColor::BrightWhite.on_default()
577 } else {
578 anstyle::Style::new()
579 }
580 .effects(Effects::BOLD),
581 Style::UnderlinePrimary | Style::LabelPrimary => lvl.color().effects(Effects::BOLD),
582 Style::UnderlineSecondary | Style::LabelSecondary => BRIGHT_BLUE.effects(Effects::BOLD),
583 Style::HeaderMsg | Style::NoStyle => anstyle::Style::new(),
584 Style::Level(lvl) => lvl.color().effects(Effects::BOLD),
585 Style::Highlight => AnsiColor::Magenta.on_default().effects(Effects::BOLD),
586 }
587 }
588}
589
590pub fn is_different(sm: &SourceMap, suggested: &str, sp: Span) -> bool {
592 let found = match sm.span_to_snippet(sp) {
593 Ok(snippet) => snippet,
594 Err(e) => {
595 {
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:595",
"rustc_errors::emitter", ::tracing::Level::WARN,
::tracing_core::__macro_support::Option::Some("compiler/rustc_errors/src/emitter.rs"),
::tracing_core::__macro_support::Option::Some(595u32),
::tracing_core::__macro_support::Option::Some("rustc_errors::emitter"),
::tracing_core::field::FieldSet::new(&["message",
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("error")
}> =
::tracing::__macro_support::FieldName::new("error");
NAME.as_str()
}], ::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Invalid span {0:?}",
sp) as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&e)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};warn!(error = ?e, "Invalid span {:?}", sp);
596 return true;
597 }
598 };
599 found != suggested
600}
601
602pub fn detect_confusion_type(sm: &SourceMap, suggested: &str, sp: Span) -> ConfusionType {
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",
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("error")
}> =
::tracing::__macro_support::FieldName::new("error");
NAME.as_str()
}], ::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Invalid span {0:?}",
sp) as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&e)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};warn!(error = ?e, "Invalid span {:?}", sp);
608 return ConfusionType::None;
609 }
610 };
611
612 let mut has_case_confusion = false;
613 let mut has_digit_letter_confusion = false;
614
615 if found.len() == suggested.len() {
616 let mut has_case_diff = false;
617 let mut has_digit_letter_confusable = false;
618 let mut has_other_diff = false;
619
620 let ascii_confusables = &['c', 'f', 'i', 'k', 'o', 's', 'u', 'v', 'w', 'x', 'y', 'z'];
623
624 let digit_letter_confusables = [('0', 'O'), ('1', 'l'), ('5', 'S'), ('8', 'B'), ('9', 'g')];
625
626 for (f, s) in iter::zip(found.chars(), suggested.chars()) {
627 if f != s {
628 if f.eq_ignore_ascii_case(&s) {
629 if ascii_confusables.contains(&f) || ascii_confusables.contains(&s) {
631 has_case_diff = true;
632 } else {
633 has_other_diff = true;
634 }
635 } else if digit_letter_confusables.contains(&(f, s))
636 || digit_letter_confusables.contains(&(s, f))
637 {
638 has_digit_letter_confusable = true;
640 } else {
641 has_other_diff = true;
642 }
643 }
644 }
645
646 if has_case_diff && !has_other_diff && found != suggested {
648 has_case_confusion = true;
649 }
650 if has_digit_letter_confusable && !has_other_diff && found != suggested {
651 has_digit_letter_confusion = true;
652 }
653 }
654
655 match (has_case_confusion, has_digit_letter_confusion) {
656 (true, true) => ConfusionType::Both,
657 (true, false) => ConfusionType::Case,
658 (false, true) => ConfusionType::DigitLetter,
659 (false, false) => ConfusionType::None,
660 }
661}
662
663#[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_fields_are_eq(&self) {}
}Eq)]
665pub enum ConfusionType {
666 None,
668 Case,
670 DigitLetter,
672 Both,
674}
675
676impl ConfusionType {
677 pub fn label_text(&self) -> &'static str {
679 match self {
680 ConfusionType::None => "",
681 ConfusionType::Case => " (notice the capitalization)",
682 ConfusionType::DigitLetter => " (notice the digit/letter confusion)",
683 ConfusionType::Both => " (notice the capitalization and digit/letter confusion)",
684 }
685 }
686
687 pub fn combine(self, other: ConfusionType) -> ConfusionType {
691 match (self, other) {
692 (ConfusionType::None, other) => other,
693 (this, ConfusionType::None) => this,
694 (ConfusionType::Both, _) | (_, ConfusionType::Both) => ConfusionType::Both,
695 (ConfusionType::Case, ConfusionType::DigitLetter)
696 | (ConfusionType::DigitLetter, ConfusionType::Case) => ConfusionType::Both,
697 (ConfusionType::Case, ConfusionType::Case) => ConfusionType::Case,
698 (ConfusionType::DigitLetter, ConfusionType::DigitLetter) => ConfusionType::DigitLetter,
699 }
700 }
701
702 pub fn has_confusion(&self) -> bool {
704 *self != ConfusionType::None
705 }
706}
707
708pub(crate) fn should_show_source_code(
709 ignored_directories: &[String],
710 sm: &SourceMap,
711 file: &SourceFile,
712) -> bool {
713 if !sm.ensure_source_file_source_present(file) {
714 return false;
715 }
716
717 let FileName::Real(name) = &file.name else { return true };
718 name.local_path()
719 .map(|path| ignored_directories.iter().all(|dir| !path.starts_with(dir)))
720 .unwrap_or(true)
721}