#[macro_use]
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
extern crate self as rustc_errors;
pub use alloc::borrow::Cow;
use core::cell::Cell;
use core::hash::Hash;
use core::num::NonZero;
use core::ops::DerefMut;
use eko::path::{Path, PathBuf};
use eko::thread::ThreadId;
use core::fmt::Write as _;
use core::{fmt, mem};
use crate::assert_matches;
use Level::*;
pub use codes::*;
pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer};
pub use diagnostic::{
BugAbort, Diag, DiagDecorator, DiagInner, DiagLocation, DiagStyledString, Diagnostic,
EmissionGuarantee, FatalAbort, StringPart, Subdiag, Subdiagnostic,
};
pub use diagnostic_impls::{
DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter,
IndicateAnonymousLifetime, SingleLabelManySpans,
};
pub use emitter::ColorConfig;
use emitter::{DynEmitter, Emitter};
use crate::rustc_ast::attr::version::RustcVersion;
use crate::rustc_data_structures::AtomicRef;
use crate::rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use crate::rustc_data_structures::stable_hash::StableHasher;
use crate::rustc_data_structures::sync::Lock;
pub use crate::rustc_error_messages::{
DiagArg, DiagArgFromDisplay, DiagArgMap, DiagArgName, DiagArgValue, DiagMessage, IntoDiagArg,
LongTyPath, MultiSpan, SpanLabel, into_diag_arg_using_display,
};
use crate::rustc_hashes::Hash128;
use crate::rustc_lint_defs::LintExpectationId;
pub use crate::rustc_lint_defs::{Applicability, listify, pluralize};
pub use rustc_macros::msg;
use rustc_macros::{Decodable, Encodable};
pub use crate::rustc_span::ErrorGuaranteed;
pub use crate::rustc_span::fatal_error::{FatalError, FatalErrorMarker, catch_fatal_errors};
use crate::rustc_span::source_map::SourceMap;
use crate::rustc_span::{DUMMY_SP, Span};
use tracing::debug;
use crate::rustc_errors::emitter::TimingEvent;
use crate::rustc_errors::formatting::DiagMessageAddArg;
pub use crate::rustc_errors::formatting::{diag_message_source, format_diag_message, try_format_diag_message};
use crate::rustc_errors::timings::TimingRecord;
pub mod plain_emitter;
pub mod codes;
mod decorate_diag;
mod diagnostic;
mod diagnostic_impls;
pub mod emitter;
pub mod formatting;
pub mod json;
mod lock;
pub mod timings;
pub type PResult<'a, T> = Result<T, Diag<'a>>;
#[cfg(target_pointer_width = "64")]
crate::static_assert_size!(PResult<'_, ()>, 24);
#[cfg(target_pointer_width = "64")]
crate::static_assert_size!(PResult<'_, bool>, 24);
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)]
pub enum SuggestionStyle {
HideCodeInline,
HideCodeAlways,
CompletelyHidden,
ShowCode,
ShowAlways,
}
impl SuggestionStyle {
fn hide_inline(&self) -> bool {
!matches!(*self, SuggestionStyle::ShowCode)
}
}
#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
pub enum Suggestions {
Enabled(Vec<CodeSuggestion>),
Sealed(Box<[CodeSuggestion]>),
Disabled,
}
impl Suggestions {
pub fn unwrap_tag(self) -> Vec<CodeSuggestion> {
match self {
Suggestions::Enabled(suggestions) => suggestions,
Suggestions::Sealed(suggestions) => suggestions.into_vec(),
Suggestions::Disabled => Vec::new(),
}
}
pub fn len(&self) -> usize {
match self {
Suggestions::Enabled(suggestions) => suggestions.len(),
Suggestions::Sealed(suggestions) => suggestions.len(),
Suggestions::Disabled => 0,
}
}
}
impl Default for Suggestions {
fn default() -> Self {
Self::Enabled(vec![])
}
}
#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
pub struct CodeSuggestion {
pub substitutions: Vec<Substitution>,
pub msg: DiagMessage,
pub style: SuggestionStyle,
pub applicability: Applicability,
}
#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
pub struct Substitution {
pub parts: Vec<SubstitutionPart>,
}
#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
pub struct SubstitutionPart {
pub span: Span,
pub snippet: String,
}
#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
pub struct TrimmedSubstitutionPart {
pub original_span: Span,
pub span: Span,
pub snippet: String,
}
impl TrimmedSubstitutionPart {
pub fn is_addition(&self, sm: &SourceMap) -> bool {
!self.snippet.is_empty() && !self.replaces_meaningful_content(sm)
}
pub fn is_deletion(&self, sm: &SourceMap) -> bool {
self.snippet.trim().is_empty() && self.replaces_meaningful_content(sm)
}
pub fn is_replacement(&self, sm: &SourceMap) -> bool {
!self.snippet.is_empty() && self.replaces_meaningful_content(sm)
}
pub fn is_destructive_replacement(&self, sm: &SourceMap) -> bool {
self.is_replacement(sm)
&& !sm
.span_to_snippet(self.span)
.is_ok_and(|snippet| as_substr(snippet.trim(), self.snippet.trim()).is_some())
}
fn replaces_meaningful_content(&self, sm: &SourceMap) -> bool {
sm.span_to_snippet(self.span)
.map_or(!self.span.is_empty(), |snippet| !snippet.trim().is_empty())
}
}
fn as_substr<'a>(original: &'a str, suggestion: &'a str) -> Option<(usize, &'a str, usize)> {
let common_prefix = original
.chars()
.zip(suggestion.chars())
.take_while(|(c1, c2)| c1 == c2)
.map(|(c, _)| c.len_utf8())
.sum();
let original = &original[common_prefix..];
let suggestion = &suggestion[common_prefix..];
if suggestion.ends_with(original) {
let common_suffix = original.len();
Some((common_prefix, &suggestion[..suggestion.len() - original.len()], common_suffix))
} else {
None
}
}
pub struct ExplicitBug;
pub struct DelayedBugPanic;
pub struct DiagCtxt {
inner: Lock<DiagCtxtInner>,
}
#[derive(Copy, Clone)]
pub struct DiagCtxtHandle<'a> {
dcx: &'a DiagCtxt,
tainted_with_errors: Option<&'a Cell<Option<ErrorGuaranteed>>>,
}
impl<'a> core::ops::Deref for DiagCtxtHandle<'a> {
type Target = &'a DiagCtxt;
fn deref(&self) -> &Self::Target {
&self.dcx
}
}
struct DiagCtxtInner {
flags: DiagCtxtFlags,
err_guars: Vec<(ErrorGuaranteed, ThreadId)>,
lint_err_guars: Vec<(ErrorGuaranteed, ThreadId)>,
delayed_bugs: Vec<(DelayedDiagInner, ErrorGuaranteed)>,
deduplicated_err_count: usize,
deduplicated_warn_count: usize,
emitter: Box<DynEmitter>,
must_produce_diag: Option<()>,
has_printed: bool,
suppressed_expected_diag: bool,
taught_diagnostics: FxHashSet<ErrCode>,
emitted_diagnostic_codes: FxIndexSet<ErrCode>,
emitted_diagnostics: FxHashSet<Hash128>,
emitted_recursion_depth_exceeding_limit: bool,
stashed_diagnostics:
FxIndexMap<StashKey, FxIndexMap<Span, (DiagInner, Option<ErrorGuaranteed>, ThreadId)>>,
future_breakage_diagnostics: Vec<DiagInner>,
fulfilled_expectations: FxIndexSet<LintExpectationId>,
ice_file: Option<PathBuf>,
msrv: Option<RustcVersion>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum StashKey {
ItemNoType,
UnderscoreForArrayLengths,
EarlySyntaxWarning,
CallIntoMethod,
LifetimeIsChar,
MaybeFruTypo,
CallAssocMethod,
AssociatedTypeSuggestion,
UndeterminedMacroResolution,
ExprInPat,
GenericInFieldExpr,
ReturnTypeNotation,
}
fn default_track_diagnostic<R>(diag: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R {
(*f)(diag)
}
pub static TRACK_DIAGNOSTIC: AtomicRef<
fn(DiagInner, &mut dyn FnMut(DiagInner) -> Option<ErrorGuaranteed>) -> Option<ErrorGuaranteed>,
> = AtomicRef::new(&(default_track_diagnostic as _));
#[derive(Copy, Clone, Default)]
pub struct DiagCtxtFlags {
pub can_emit_warnings: bool,
pub treat_err_as_bug: Option<NonZero<usize>>,
pub eagerly_emit_delayed_bugs: bool,
pub macro_backtrace: bool,
pub deduplicate_diagnostics: bool,
pub track_diagnostics: bool,
}
impl Drop for DiagCtxtInner {
fn drop(&mut self) {
self.emit_stashed_diagnostics();
self.flush_delayed();
if !self.has_printed && !self.suppressed_expected_diag {
if self.must_produce_diag.is_some() {
panic!(
"`trimmed_def_paths` called, diagnostics were expected but none were emitted. \
Use `with_no_trimmed_paths` for debugging. (no backtrace: the compiler no \
longer links std, so the call site cannot be captured.)"
);
}
}
}
}
impl DiagCtxt {
pub fn disable_warnings(mut self) -> Self {
self.inner.get_mut().flags.can_emit_warnings = false;
self
}
pub fn with_flags(mut self, flags: DiagCtxtFlags) -> Self {
self.inner.get_mut().flags = flags;
self
}
pub fn with_ice_file(mut self, ice_file: PathBuf) -> Self {
self.inner.get_mut().ice_file = Some(ice_file);
self
}
pub fn with_msrv(mut self, msrv: RustcVersion) -> Self {
self.inner.get_mut().msrv = Some(msrv);
self
}
pub fn new(emitter: Box<DynEmitter>) -> Self {
Self { inner: Lock::new(DiagCtxtInner::new(emitter)) }
}
pub fn make_silent(&self) {
let mut inner = self.inner.borrow_mut();
inner.emitter = Box::new(emitter::SilentEmitter {});
}
pub fn set_emitter(&self, emitter: Box<dyn Emitter>) {
self.inner.borrow_mut().emitter = emitter;
}
pub fn can_emit_warnings(&self) -> bool {
self.inner.borrow_mut().flags.can_emit_warnings
}
pub fn reset_err_count(&self) {
let mut inner = self.inner.borrow_mut();
let DiagCtxtInner {
flags: _,
err_guars,
lint_err_guars,
delayed_bugs,
deduplicated_err_count,
deduplicated_warn_count,
emitter: _,
must_produce_diag,
has_printed,
suppressed_expected_diag,
taught_diagnostics,
emitted_diagnostic_codes,
emitted_diagnostics,
emitted_recursion_depth_exceeding_limit,
stashed_diagnostics,
future_breakage_diagnostics,
fulfilled_expectations,
ice_file: _,
msrv: _,
} = inner.deref_mut();
*err_guars = Default::default();
*lint_err_guars = Default::default();
*delayed_bugs = Default::default();
*deduplicated_err_count = 0;
*deduplicated_warn_count = 0;
*must_produce_diag = None;
*has_printed = false;
*suppressed_expected_diag = false;
*taught_diagnostics = Default::default();
*emitted_diagnostic_codes = Default::default();
*emitted_diagnostics = Default::default();
*emitted_recursion_depth_exceeding_limit = false;
*stashed_diagnostics = Default::default();
*future_breakage_diagnostics = Default::default();
*fulfilled_expectations = Default::default();
}
pub fn handle<'a>(&'a self) -> DiagCtxtHandle<'a> {
DiagCtxtHandle { dcx: self, tainted_with_errors: None }
}
pub fn taintable_handle<'a>(
&'a self,
tainted_with_errors: &'a Cell<Option<ErrorGuaranteed>>,
) -> DiagCtxtHandle<'a> {
DiagCtxtHandle { dcx: self, tainted_with_errors: Some(tainted_with_errors) }
}
}
impl<'a> DiagCtxtHandle<'a> {
pub fn stash_diagnostic(
&self,
span: Span,
key: StashKey,
diag: DiagInner,
) -> Option<ErrorGuaranteed> {
let guar = match diag.level {
Bug | Fatal => {
self.span_bug(
span,
format!("invalid level in `stash_diagnostic`: {:?}", diag.level),
);
}
Error => Some(self.span_delayed_bug(span, format!("stashing {key:?}"))),
DelayedBug => {
return self.inner.borrow_mut().emit_diagnostic(diag, self.tainted_with_errors);
}
ForceWarning | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow
| Expect => None,
};
self.inner
.borrow_mut()
.stashed_diagnostics
.entry(key)
.or_default()
.insert(span.with_parent(None), (diag, guar, eko::thread::current_id()));
guar
}
pub fn steal_non_err(self, span: Span, key: StashKey) -> Option<Diag<'a, ()>> {
let (diag, guar, _) = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then(
|stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)),
)?;
assert!(!diag.is_error());
assert!(guar.is_none());
Some(Diag::new_diagnostic(self, diag))
}
pub fn try_steal_modify_and_emit_err<F>(
self,
span: Span,
key: StashKey,
mut modify_err: F,
) -> Option<ErrorGuaranteed>
where
F: FnMut(&mut Diag<'_>),
{
let err = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then(
|stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)),
);
err.map(|(err, guar, _)| {
assert_eq!(err.level, Error);
assert!(guar.is_some());
let mut err = Diag::<ErrorGuaranteed>::new_diagnostic(self, err);
modify_err(&mut err);
assert_eq!(err.level, Error);
err.emit()
})
}
pub fn try_steal_replace_and_emit_err(
self,
span: Span,
key: StashKey,
new_err: Diag<'_>,
) -> ErrorGuaranteed {
let old_err = self.inner.borrow_mut().stashed_diagnostics.get_mut(&key).and_then(
|stashed_diagnostics| stashed_diagnostics.swap_remove(&span.with_parent(None)),
);
match old_err {
Some((old_err, guar, _)) => {
assert_eq!(old_err.level, Error);
assert!(guar.is_some());
Diag::<ErrorGuaranteed>::new_diagnostic(self, old_err).cancel();
}
None => {}
};
new_err.emit()
}
pub fn has_stashed_diagnostic(&self, span: Span, key: StashKey) -> bool {
let inner = self.inner.borrow();
if let Some(stashed_diagnostics) = inner.stashed_diagnostics.get(&key)
&& !stashed_diagnostics.is_empty()
{
stashed_diagnostics.contains_key(&span.with_parent(None))
} else {
false
}
}
pub fn emit_stashed_diagnostics(&self) -> Option<ErrorGuaranteed> {
self.inner.borrow_mut().emit_stashed_diagnostics()
}
#[inline]
pub fn err_count(&self) -> usize {
let inner = self.inner.borrow();
inner.err_guars.len()
+ inner.lint_err_guars.len()
+ inner
.stashed_diagnostics
.values()
.map(|a| a.values().filter(|(_, guar, _)| guar.is_some()).count())
.sum::<usize>()
}
pub fn err_count_on_current_thread(&self) -> usize {
let inner = self.inner.borrow();
let current = eko::thread::current_id();
inner.err_guars.iter().filter(|(_, thread)| *thread == current).count()
+ inner.lint_err_guars.iter().filter(|(_, thread)| *thread == current).count()
+ inner
.stashed_diagnostics
.values()
.map(|a| {
a.values()
.filter(|(_, guar, thread)| guar.is_some() && *thread == current)
.count()
})
.sum::<usize>()
}
pub fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
self.inner.borrow().has_errors_excluding_lint_errors()
}
pub fn has_errors(&self) -> Option<ErrorGuaranteed> {
self.inner.borrow().has_errors()
}
pub fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
self.inner.borrow().has_errors_or_delayed_bugs()
}
pub fn print_error_count(&self) {
let mut inner = self.inner.borrow_mut();
assert!(inner.stashed_diagnostics.is_empty());
if inner.treat_err_as_bug() {
return;
}
let warnings = match inner.deduplicated_warn_count {
0 => Cow::from(""),
1 => Cow::from("1 warning emitted"),
count => Cow::from(format!("{count} warnings emitted")),
};
let errors = match inner.deduplicated_err_count {
0 => Cow::from(""),
1 => Cow::from("aborting due to 1 previous error"),
count => Cow::from(format!("aborting due to {count} previous errors")),
};
match (errors.len(), warnings.len()) {
(0, 0) => return,
(0, _) => {
inner.emit_diagnostic(
DiagInner::new(ForceWarning, DiagMessage::Str(warnings)),
None,
);
}
(_, 0) => {
inner.emit_diagnostic(DiagInner::new(Error, errors), self.tainted_with_errors);
}
(_, _) => {
inner.emit_diagnostic(
DiagInner::new(Error, format!("{errors}; {warnings}")),
self.tainted_with_errors,
);
}
}
}
pub fn abort_if_errors(&self) {
if let Some(guar) = self.has_errors() {
guar.raise_fatal();
}
}
pub fn must_teach(&self, code: ErrCode) -> bool {
self.inner.borrow_mut().taught_diagnostics.insert(code)
}
pub fn emit_diagnostic(&self, diagnostic: DiagInner) -> Option<ErrorGuaranteed> {
self.inner.borrow_mut().emit_diagnostic(diagnostic, self.tainted_with_errors)
}
pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) {
self.inner.borrow_mut().emitter.emit_artifact_notification(path, artifact_type);
}
pub fn emit_timing_section_start(&self, record: TimingRecord) {
self.inner.borrow_mut().emitter.emit_timing_section(record, TimingEvent::Start);
}
pub fn emit_timing_section_end(&self, record: TimingRecord) {
self.inner.borrow_mut().emitter.emit_timing_section(record, TimingEvent::End);
}
pub fn emit_future_breakage_report(&self) {
let inner = &mut *self.inner.borrow_mut();
let diags = mem::take(&mut inner.future_breakage_diagnostics);
if !diags.is_empty() {
inner.emitter.emit_future_breakage_report(diags);
}
}
pub fn emit_unused_externs(
&self,
lint_level: crate::rustc_lint_defs::Level,
loud: bool,
unused_externs: &[&str],
) {
let mut inner = self.inner.borrow_mut();
if loud && lint_level.is_error() {
#[allow(deprecated)]
let guar = ErrorGuaranteed::unchecked_error_guaranteed();
inner.lint_err_guars.push((guar, eko::thread::current_id()));
inner.panic_if_treat_err_as_bug();
}
inner.emitter.emit_unused_externs(lint_level, unused_externs)
}
#[must_use]
pub fn steal_fulfilled_expectation_ids(&self) -> FxIndexSet<LintExpectationId> {
mem::take(&mut self.inner.borrow_mut().fulfilled_expectations)
}
pub fn flush_delayed(&self) {
self.inner.borrow_mut().flush_delayed();
}
#[track_caller]
pub fn set_must_produce_diag(&self) {
assert!(
self.inner.borrow().must_produce_diag.is_none(),
"should only need to collect a backtrace once"
);
self.inner.borrow_mut().must_produce_diag = Some(());
}
}
impl<'a> DiagCtxtHandle<'a> {
#[track_caller]
pub fn struct_bug(self, msg: impl Into<Cow<'static, str>>) -> Diag<'a, BugAbort> {
Diag::new(self, Bug, msg.into())
}
#[track_caller]
pub fn bug(self, msg: impl Into<Cow<'static, str>>) -> ! {
self.struct_bug(msg).emit()
}
#[track_caller]
pub fn struct_span_bug(
self,
span: impl Into<MultiSpan>,
msg: impl Into<Cow<'static, str>>,
) -> Diag<'a, BugAbort> {
self.struct_bug(msg).with_span(span)
}
#[track_caller]
pub fn span_bug(self, span: impl Into<MultiSpan>, msg: impl Into<Cow<'static, str>>) -> ! {
self.struct_span_bug(span, msg.into()).emit()
}
#[track_caller]
pub fn create_bug(self, bug: impl Diagnostic<'a, BugAbort>) -> Diag<'a, BugAbort> {
bug.into_diag(self, Bug)
}
#[track_caller]
pub fn emit_bug(self, bug: impl Diagnostic<'a, BugAbort>) -> ! {
self.create_bug(bug).emit()
}
#[track_caller]
pub fn struct_fatal(self, msg: impl Into<DiagMessage>) -> Diag<'a, FatalAbort> {
Diag::new(self, Fatal, msg)
}
#[track_caller]
pub fn fatal(self, msg: impl Into<DiagMessage>) -> ! {
self.struct_fatal(msg).emit()
}
#[track_caller]
pub fn struct_span_fatal(
self,
span: impl Into<MultiSpan>,
msg: impl Into<DiagMessage>,
) -> Diag<'a, FatalAbort> {
self.struct_fatal(msg).with_span(span)
}
#[track_caller]
pub fn span_fatal(self, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) -> ! {
self.struct_span_fatal(span, msg).emit()
}
#[track_caller]
pub fn create_fatal(self, fatal: impl Diagnostic<'a, FatalAbort>) -> Diag<'a, FatalAbort> {
fatal.into_diag(self, Fatal)
}
#[track_caller]
pub fn emit_fatal(self, fatal: impl Diagnostic<'a, FatalAbort>) -> ! {
self.create_fatal(fatal).emit()
}
#[track_caller]
pub fn create_almost_fatal(
self,
fatal: impl Diagnostic<'a, FatalError>,
) -> Diag<'a, FatalError> {
fatal.into_diag(self, Fatal)
}
#[track_caller]
pub fn emit_almost_fatal(self, fatal: impl Diagnostic<'a, FatalError>) -> FatalError {
self.create_almost_fatal(fatal).emit()
}
#[track_caller]
pub fn struct_err(self, msg: impl Into<DiagMessage>) -> Diag<'a> {
Diag::new(self, Error, msg)
}
#[track_caller]
pub fn err(self, msg: impl Into<DiagMessage>) -> ErrorGuaranteed {
self.struct_err(msg).emit()
}
#[track_caller]
pub fn struct_span_err(
self,
span: impl Into<MultiSpan>,
msg: impl Into<DiagMessage>,
) -> Diag<'a> {
self.struct_err(msg).with_span(span)
}
#[track_caller]
pub fn span_err(
self,
span: impl Into<MultiSpan>,
msg: impl Into<DiagMessage>,
) -> ErrorGuaranteed {
self.struct_span_err(span, msg).emit()
}
#[track_caller]
pub fn create_err(self, err: impl Diagnostic<'a>) -> Diag<'a> {
err.into_diag(self, Error)
}
#[track_caller]
pub fn emit_err(self, err: impl Diagnostic<'a>) -> ErrorGuaranteed {
self.create_err(err).emit()
}
#[track_caller]
pub fn delayed_bug(self, msg: impl Into<Cow<'static, str>>) -> ErrorGuaranteed {
Diag::<ErrorGuaranteed>::new(self, DelayedBug, msg.into()).emit()
}
#[track_caller]
pub fn span_delayed_bug(
self,
sp: impl Into<MultiSpan>,
msg: impl Into<Cow<'static, str>>,
) -> ErrorGuaranteed {
Diag::<ErrorGuaranteed>::new(self, DelayedBug, msg.into()).with_span(sp).emit()
}
#[track_caller]
pub fn struct_warn(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
Diag::new(self, Warning, msg)
}
#[track_caller]
pub fn warn(self, msg: impl Into<DiagMessage>) {
self.struct_warn(msg).emit()
}
#[track_caller]
pub fn struct_span_warn(
self,
span: impl Into<MultiSpan>,
msg: impl Into<DiagMessage>,
) -> Diag<'a, ()> {
self.struct_warn(msg).with_span(span)
}
#[track_caller]
pub fn span_warn(self, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
self.struct_span_warn(span, msg).emit()
}
#[track_caller]
pub fn create_warn(self, warning: impl Diagnostic<'a, ()>) -> Diag<'a, ()> {
warning.into_diag(self, Warning)
}
#[track_caller]
pub fn emit_warn(self, warning: impl Diagnostic<'a, ()>) {
self.create_warn(warning).emit()
}
#[track_caller]
pub fn struct_note(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
Diag::new(self, Note, msg)
}
#[track_caller]
pub fn note(&self, msg: impl Into<DiagMessage>) {
self.struct_note(msg).emit()
}
#[track_caller]
pub fn struct_span_note(
self,
span: impl Into<MultiSpan>,
msg: impl Into<DiagMessage>,
) -> Diag<'a, ()> {
self.struct_note(msg).with_span(span)
}
#[track_caller]
pub fn span_note(self, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
self.struct_span_note(span, msg).emit()
}
#[track_caller]
pub fn create_note(self, note: impl Diagnostic<'a, ()>) -> Diag<'a, ()> {
note.into_diag(self, Note)
}
#[track_caller]
pub fn emit_note(self, note: impl Diagnostic<'a, ()>) {
self.create_note(note).emit()
}
#[track_caller]
pub fn struct_help(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
Diag::new(self, Help, msg)
}
#[track_caller]
pub fn struct_failure_note(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
Diag::new(self, FailureNote, msg)
}
#[track_caller]
pub fn struct_allow(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
Diag::new(self, Allow, msg)
}
#[track_caller]
pub fn struct_expect(self, msg: impl Into<DiagMessage>, id: LintExpectationId) -> Diag<'a, ()> {
Diag::new(self, Expect, msg).with_lint_id(id)
}
}
impl DiagCtxtInner {
fn new(emitter: Box<DynEmitter>) -> Self {
Self {
flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() },
err_guars: Vec::new(),
lint_err_guars: Vec::new(),
delayed_bugs: Vec::new(),
deduplicated_err_count: 0,
deduplicated_warn_count: 0,
emitter,
must_produce_diag: None,
has_printed: false,
suppressed_expected_diag: false,
taught_diagnostics: Default::default(),
emitted_diagnostic_codes: Default::default(),
emitted_diagnostics: Default::default(),
emitted_recursion_depth_exceeding_limit: false,
stashed_diagnostics: Default::default(),
future_breakage_diagnostics: Vec::new(),
fulfilled_expectations: Default::default(),
ice_file: None,
msrv: None,
}
}
fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
let mut guar = None;
let has_errors = !self.err_guars.is_empty();
for (_, stashed_diagnostics) in mem::take(&mut self.stashed_diagnostics).into_iter() {
for (_, (diag, _guar, _thread)) in stashed_diagnostics {
if !diag.is_error() {
if !diag.is_force_warn() && has_errors {
continue;
}
}
guar = guar.or(self.emit_diagnostic(diag, None));
}
}
guar
}
fn emit_diagnostic(
&mut self,
mut diagnostic: DiagInner,
taint: Option<&Cell<Option<ErrorGuaranteed>>>,
) -> Option<ErrorGuaranteed> {
if diagnostic.has_future_breakage() {
assert_matches!(diagnostic.level, Error | ForceWarning | Warning | Allow | Expect);
self.future_breakage_diagnostics.push(diagnostic.clone());
}
match diagnostic.level {
Bug => {}
Fatal | Error => {
if self.treat_next_err_as_bug() {
diagnostic.level = Bug;
}
}
DelayedBug => {
if self.flags.eagerly_emit_delayed_bugs {
if self.treat_next_err_as_bug() {
diagnostic.level = Bug;
} else {
diagnostic.level = Error;
}
} else {
return if let Some(guar) = self.has_errors() {
Some(guar)
} else {
#[allow(deprecated)]
let guar = ErrorGuaranteed::unchecked_error_guaranteed();
self.delayed_bugs.push((DelayedDiagInner::new(diagnostic), guar));
Some(guar)
};
}
}
ForceWarning if diagnostic.lint_id.is_none() => {} Warning => {
if !self.flags.can_emit_warnings {
if diagnostic.has_future_breakage() {
TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
}
return None;
}
}
Note | Help | FailureNote => {}
OnceNote | OnceHelp => panic!("bad level: {:?}", diagnostic.level),
Allow => {
if diagnostic.has_future_breakage() {
TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
self.suppressed_expected_diag = true;
}
return None;
}
Expect | ForceWarning => {
self.fulfilled_expectations.insert(diagnostic.lint_id.unwrap());
if let Expect = diagnostic.level {
TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
self.suppressed_expected_diag = true;
return None;
}
}
}
if let (Some(msrv), Some(diag_msrv)) = (self.msrv, diagnostic.rust_version())
&& diag_msrv > msrv
{
return None;
}
TRACK_DIAGNOSTIC(diagnostic, &mut |mut diagnostic| {
if let Some(code) = diagnostic.code {
self.emitted_diagnostic_codes.insert(code);
}
let already_emitted = {
let mut hasher = StableHasher::new();
diagnostic.hash(&mut hasher);
let diagnostic_hash = hasher.finish();
!self.emitted_diagnostics.insert(diagnostic_hash)
};
let is_error = diagnostic.is_error();
let is_lint = diagnostic.is_lint.is_some();
let silence_recursion_depth_exceeded_limit =
diagnostic.is_lint.as_ref().is_some_and(|lint| {
lint.name.eq_ignore_ascii_case(
crate::rustc_lint_defs::builtin::RECURSION_DEPTH_EXCEEDING_LIMIT.name,
) && mem::replace(&mut self.emitted_recursion_depth_exceeding_limit, true)
});
if !silence_recursion_depth_exceeded_limit
&& !(self.flags.deduplicate_diagnostics && already_emitted)
{
debug!(?diagnostic);
debug!(?self.emitted_diagnostics);
let not_yet_emitted = |sub: &mut Subdiag| {
debug!(?sub);
if sub.level != OnceNote && sub.level != OnceHelp {
return true;
}
let mut hasher = StableHasher::new();
sub.hash(&mut hasher);
let diagnostic_hash = hasher.finish();
debug!(?diagnostic_hash);
self.emitted_diagnostics.insert(diagnostic_hash)
};
diagnostic.children.retain_mut(not_yet_emitted);
if already_emitted {
let msg = "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`";
diagnostic.sub(Note, msg, MultiSpan::new());
}
if is_error {
self.deduplicated_err_count += 1;
} else if matches!(diagnostic.level, ForceWarning | Warning) {
self.deduplicated_warn_count += 1;
}
self.has_printed = true;
self.emitter.emit_diagnostic(diagnostic);
}
if is_error {
if !self.delayed_bugs.is_empty() {
assert_eq!(self.lint_err_guars.len() + self.err_guars.len(), 0);
self.delayed_bugs.clear();
self.delayed_bugs.shrink_to_fit();
}
#[allow(deprecated)]
let guar = ErrorGuaranteed::unchecked_error_guaranteed();
let thread = eko::thread::current_id();
if is_lint {
self.lint_err_guars.push((guar, thread));
} else {
if let Some(taint) = taint {
taint.set(Some(guar));
}
self.err_guars.push((guar, thread));
}
self.panic_if_treat_err_as_bug();
Some(guar)
} else {
None
}
})
}
fn treat_err_as_bug(&self) -> bool {
self.flags
.treat_err_as_bug
.is_some_and(|c| self.err_guars.len() + self.lint_err_guars.len() >= c.get())
}
fn treat_next_err_as_bug(&self) -> bool {
self.flags
.treat_err_as_bug
.is_some_and(|c| self.err_guars.len() + self.lint_err_guars.len() + 1 >= c.get())
}
fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
self.err_guars.get(0).map(|(guar, _)| *guar).or_else(|| {
if let Some((_diag, guar, _)) = self
.stashed_diagnostics
.values()
.flat_map(|stashed_diagnostics| stashed_diagnostics.values())
.find(|(diag, guar, _)| guar.is_some() && diag.is_lint.is_none())
{
*guar
} else {
None
}
})
}
fn has_errors(&self) -> Option<ErrorGuaranteed> {
self.err_guars
.get(0)
.map(|(guar, _)| *guar)
.or_else(|| self.lint_err_guars.get(0).map(|(guar, _)| *guar))
.or_else(|| {
self.stashed_diagnostics.values().find_map(|stashed_diagnostics| {
stashed_diagnostics.values().find_map(|(_, guar, _)| *guar)
})
})
}
fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
self.has_errors().or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied())
}
fn flush_delayed(&mut self) {
assert!(self.stashed_diagnostics.is_empty());
if !self.err_guars.is_empty() {
return;
}
if self.delayed_bugs.is_empty() {
return;
}
let bugs: Vec<_> = mem::take(&mut self.delayed_bugs).into_iter().map(|(b, _)| b).collect();
let backtrace = eko::env::var_os("RUST_BACKTRACE").as_deref() != Some(b"0".as_slice());
let decorate = backtrace || self.ice_file.is_none();
let mut out = self
.ice_file
.as_ref()
.and_then(|file| eko::file::File::append(file).ok());
let note1 = "no errors encountered even though delayed bugs were created";
let note2 = "those delayed bugs will now be shown as internal compiler errors";
self.emit_diagnostic(DiagInner::new(Note, note1), None);
self.emit_diagnostic(DiagInner::new(Note, note2), None);
for bug in bugs {
if let Some(out) = &mut out {
let message: String = bug
.inner
.messages
.iter()
.map(|(msg, _)| {
try_format_diag_message(msg, &bug.inner.args)
.unwrap_or_else(|| Cow::Borrowed(diag_message_source(msg)))
})
.collect();
_ = write!(out, "delayed bug: {message}\n");
}
let mut bug = if decorate { bug.decorate() } else { bug.inner };
if bug.level != DelayedBug {
let msg = msg!(
"`flushed_delayed` got diagnostic with level {$level}, instead of the expected `DelayedBug`"
).arg("level", bug.level).format();
bug.sub(Note, msg, bug.span.primary_span().unwrap().into());
}
bug.level = Bug;
self.emit_diagnostic(bug, None);
}
panic!("{}", stringify!(DelayedBugPanic));
}
fn panic_if_treat_err_as_bug(&self) {
if self.treat_err_as_bug() {
let n = self.flags.treat_err_as_bug.map(|c| c.get()).unwrap();
assert_eq!(n, self.err_guars.len() + self.lint_err_guars.len());
if n == 1 {
panic!("aborting due to `-Z treat-err-as-bug=1`");
} else {
panic!("aborting after {n} errors due to `-Z treat-err-as-bug={n}`");
}
}
}
}
struct DelayedDiagInner {
inner: DiagInner,
}
impl DelayedDiagInner {
fn new(diagnostic: DiagInner) -> Self {
DelayedDiagInner { inner: diagnostic }
}
fn decorate(self) -> DiagInner {
let mut diag = self.inner;
let msg = msg!("delayed at {$emitted_at}")
.arg("emitted_at", diag.emitted_at.clone())
.format();
diag.sub(Note, msg, diag.span.primary_span().unwrap_or(DUMMY_SP).into());
diag
}
}
#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)]
pub enum Level {
Bug,
Fatal,
Error,
DelayedBug,
ForceWarning,
Warning,
Note,
OnceNote,
Help,
OnceHelp,
FailureNote,
Allow,
Expect,
}
impl fmt::Display for Level {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.to_str().fmt(f)
}
}
impl Level {
pub fn to_str(self) -> &'static str {
match self {
Bug | DelayedBug => "error: internal compiler error",
Fatal | Error => "error",
ForceWarning | Warning => "warning",
Note | OnceNote => "note",
Help | OnceHelp => "help",
FailureNote => "failure-note",
Allow | Expect => unreachable!(),
}
}
pub fn is_failure_note(&self) -> bool {
matches!(*self, FailureNote)
}
}
impl IntoDiagArg for Level {
fn into_diag_arg(self, _: &mut crate::rustc_error_messages::LongTyPath) -> DiagArgValue {
DiagArgValue::Str(Cow::from(self.to_string()))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
pub enum Style {
MainHeaderMsg,
HeaderMsg,
LineAndColumn,
LineNumber,
Quotation,
UnderlinePrimary,
UnderlineSecondary,
LabelPrimary,
LabelSecondary,
NoStyle,
Level(Level),
Highlight,
Addition,
Removal,
}
pub fn elided_lifetime_in_path_suggestion(
source_map: &SourceMap,
n: usize,
path_span: Span,
incl_angl_brckt: bool,
insertion_span: Span,
) -> ElidedLifetimeInPathSubdiag {
let expected = ExpectedLifetimeParameter { span: path_span, count: n };
let indicate = source_map.is_span_accessible(insertion_span).then(|| {
let anon_lts = vec!["'_"; n].join(", ");
let suggestion =
if incl_angl_brckt { format!("<{anon_lts}>") } else { format!("{anon_lts}, ") };
IndicateAnonymousLifetime { span: insertion_span.shrink_to_hi(), count: n, suggestion }
});
ElidedLifetimeInPathSubdiag { expected, indicate }
}
pub fn a_or_an(s: &str) -> &'static str {
let mut chars = s.chars();
let Some(mut first_alpha_char) = chars.next() else {
return "a";
};
if first_alpha_char == '`' {
let Some(next) = chars.next() else {
return "a";
};
first_alpha_char = next;
}
if ["a", "e", "i", "o", "u", "&"].contains(&&first_alpha_char.to_lowercase().to_string()[..]) {
"an"
} else {
"a"
}
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum TerminalUrl {
No,
Yes,
Auto,
}
pub use crate::struct_span_code_err;