From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_errors/src/lib.rs | 1589 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1589 insertions(+) create mode 100644 compiler/rustc_errors/src/lib.rs (limited to 'compiler/rustc_errors/src/lib.rs') diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs new file mode 100644 index 000000000..2409c0b5a --- /dev/null +++ b/compiler/rustc_errors/src/lib.rs @@ -0,0 +1,1589 @@ +//! Diagnostics creation and emission for `rustc`. +//! +//! This module contains the code for creating and emitting diagnostics. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(drain_filter)] +#![feature(backtrace)] +#![feature(if_let_guard)] +#![feature(let_chains)] +#![feature(let_else)] +#![feature(never_type)] +#![feature(adt_const_params)] +#![feature(rustc_attrs)] +#![allow(incomplete_features)] +#![allow(rustc::potential_query_instability)] + +#[macro_use] +extern crate rustc_macros; + +#[macro_use] +extern crate tracing; + +pub use emitter::ColorConfig; + +use rustc_lint_defs::LintExpectationId; +use Level::*; + +use emitter::{is_case_difference, Emitter, EmitterWriter}; +use registry::Registry; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; +use rustc_data_structures::stable_hasher::StableHasher; +use rustc_data_structures::sync::{self, Lock, Lrc}; +use rustc_data_structures::AtomicRef; +pub use rustc_error_messages::{ + fallback_fluent_bundle, fluent, fluent_bundle, DiagnosticMessage, FluentBundle, + LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagnosticMessage, + DEFAULT_LOCALE_RESOURCES, +}; +pub use rustc_lint_defs::{pluralize, Applicability}; +use rustc_span::source_map::SourceMap; +use rustc_span::HashStableContext; +use rustc_span::{Loc, Span}; + +use std::borrow::Cow; +use std::hash::Hash; +use std::num::NonZeroUsize; +use std::panic; +use std::path::Path; +use std::{error, fmt}; + +use termcolor::{Color, ColorSpec}; + +pub mod annotate_snippet_emitter_writer; +mod diagnostic; +mod diagnostic_builder; +pub mod emitter; +pub mod json; +mod lock; +pub mod registry; +mod snippet; +mod styled_buffer; + +pub use snippet::Style; + +pub type PResult<'a, T> = Result>; + +// `PResult` is used a lot. Make sure it doesn't unintentionally get bigger. +// (See also the comment on `DiagnosticBuilder`'s `diagnostic` field.) +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +rustc_data_structures::static_assert_size!(PResult<'_, ()>, 16); +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24); + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)] +pub enum SuggestionStyle { + /// Hide the suggested code when displaying this suggestion inline. + HideCodeInline, + /// Always hide the suggested code but display the message. + HideCodeAlways, + /// Do not display this suggestion in the cli output, it is only meant for tools. + CompletelyHidden, + /// Always show the suggested code. + /// This will *not* show the code if the suggestion is inline *and* the suggested code is + /// empty. + ShowCode, + /// Always show the suggested code independently. + ShowAlways, +} + +impl SuggestionStyle { + fn hide_inline(&self) -> bool { + !matches!(*self, SuggestionStyle::ShowCode) + } +} + +#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] +pub struct CodeSuggestion { + /// Each substitute can have multiple variants due to multiple + /// applicable suggestions + /// + /// `foo.bar` might be replaced with `a.b` or `x.y` by replacing + /// `foo` and `bar` on their own: + /// + /// ```ignore (illustrative) + /// vec![ + /// Substitution { parts: vec![(0..3, "a"), (4..7, "b")] }, + /// Substitution { parts: vec![(0..3, "x"), (4..7, "y")] }, + /// ] + /// ``` + /// + /// or by replacing the entire span: + /// + /// ```ignore (illustrative) + /// vec![ + /// Substitution { parts: vec![(0..7, "a.b")] }, + /// Substitution { parts: vec![(0..7, "x.y")] }, + /// ] + /// ``` + pub substitutions: Vec, + pub msg: DiagnosticMessage, + /// Visual representation of this suggestion. + pub style: SuggestionStyle, + /// Whether or not the suggestion is approximate + /// + /// Sometimes we may show suggestions with placeholders, + /// which are useful for users but not useful for + /// tools like rustfix + pub applicability: Applicability, +} + +#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] +/// See the docs on `CodeSuggestion::substitutions` +pub struct Substitution { + pub parts: Vec, +} + +#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] +pub struct SubstitutionPart { + pub span: Span, + pub snippet: String, +} + +/// Used to translate between `Span`s and byte positions within a single output line in highlighted +/// code of structured suggestions. +#[derive(Debug, Clone, Copy)] +pub struct SubstitutionHighlight { + start: usize, + end: usize, +} + +impl SubstitutionPart { + pub fn is_addition(&self, sm: &SourceMap) -> bool { + !self.snippet.is_empty() + && sm + .span_to_snippet(self.span) + .map_or(self.span.is_empty(), |snippet| snippet.trim().is_empty()) + } + + pub fn is_deletion(&self) -> bool { + self.snippet.trim().is_empty() + } + + pub fn is_replacement(&self, sm: &SourceMap) -> bool { + !self.snippet.is_empty() + && sm + .span_to_snippet(self.span) + .map_or(!self.span.is_empty(), |snippet| !snippet.trim().is_empty()) + } +} + +impl CodeSuggestion { + /// Returns the assembled code suggestions, whether they should be shown with an underline + /// and whether the substitution only differs in capitalization. + pub fn splice_lines( + &self, + sm: &SourceMap, + ) -> Vec<(String, Vec, Vec>, bool)> { + // For the `Vec>` value, the first level of the vector + // corresponds to the output snippet's lines, while the second level corresponds to the + // substrings within that line that should be highlighted. + + use rustc_span::{CharPos, Pos}; + + /// Append to a buffer the remainder of the line of existing source code, and return the + /// count of lines that have been added for accurate highlighting. + fn push_trailing( + buf: &mut String, + line_opt: Option<&Cow<'_, str>>, + lo: &Loc, + hi_opt: Option<&Loc>, + ) -> usize { + let mut line_count = 0; + let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi| hi.col.to_usize())); + if let Some(line) = line_opt { + if let Some(lo) = line.char_indices().map(|(i, _)| i).nth(lo) { + let hi_opt = hi_opt.and_then(|hi| line.char_indices().map(|(i, _)| i).nth(hi)); + match hi_opt { + Some(hi) if hi > lo => { + line_count = line[lo..hi].matches('\n').count(); + buf.push_str(&line[lo..hi]) + } + Some(_) => (), + None => { + line_count = line[lo..].matches('\n').count(); + buf.push_str(&line[lo..]) + } + } + } + if hi_opt.is_none() { + buf.push('\n'); + } + } + line_count + } + + assert!(!self.substitutions.is_empty()); + + self.substitutions + .iter() + .filter(|subst| { + // Suggestions coming from macros can have malformed spans. This is a heavy + // handed approach to avoid ICEs by ignoring the suggestion outright. + let invalid = subst.parts.iter().any(|item| sm.is_valid_span(item.span).is_err()); + if invalid { + debug!("splice_lines: suggestion contains an invalid span: {:?}", subst); + } + !invalid + }) + .cloned() + .filter_map(|mut substitution| { + // Assumption: all spans are in the same file, and all spans + // are disjoint. Sort in ascending order. + substitution.parts.sort_by_key(|part| part.span.lo()); + + // Find the bounding span. + let lo = substitution.parts.iter().map(|part| part.span.lo()).min()?; + let hi = substitution.parts.iter().map(|part| part.span.hi()).max()?; + let bounding_span = Span::with_root_ctxt(lo, hi); + // The different spans might belong to different contexts, if so ignore suggestion. + let lines = sm.span_to_lines(bounding_span).ok()?; + assert!(!lines.lines.is_empty() || bounding_span.is_dummy()); + + // We can't splice anything if the source is unavailable. + if !sm.ensure_source_file_source_present(lines.file.clone()) { + return None; + } + + let mut highlights = vec![]; + // To build up the result, we do this for each span: + // - push the line segment trailing the previous span + // (at the beginning a "phantom" span pointing at the start of the line) + // - push lines between the previous and current span (if any) + // - if the previous and current span are not on the same line + // push the line segment leading up to the current span + // - splice in the span substitution + // + // Finally push the trailing line segment of the last span + let sf = &lines.file; + let mut prev_hi = sm.lookup_char_pos(bounding_span.lo()); + prev_hi.col = CharPos::from_usize(0); + let mut prev_line = + lines.lines.get(0).and_then(|line0| sf.get_line(line0.line_index)); + let mut buf = String::new(); + + let mut line_highlight = vec![]; + // We need to keep track of the difference between the existing code and the added + // or deleted code in order to point at the correct column *after* substitution. + let mut acc = 0; + for part in &substitution.parts { + let cur_lo = sm.lookup_char_pos(part.span.lo()); + if prev_hi.line == cur_lo.line { + let mut count = + push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo)); + while count > 0 { + highlights.push(std::mem::take(&mut line_highlight)); + acc = 0; + count -= 1; + } + } else { + acc = 0; + highlights.push(std::mem::take(&mut line_highlight)); + let mut count = push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None); + while count > 0 { + highlights.push(std::mem::take(&mut line_highlight)); + count -= 1; + } + // push lines between the previous and current span (if any) + for idx in prev_hi.line..(cur_lo.line - 1) { + if let Some(line) = sf.get_line(idx) { + buf.push_str(line.as_ref()); + buf.push('\n'); + highlights.push(std::mem::take(&mut line_highlight)); + } + } + if let Some(cur_line) = sf.get_line(cur_lo.line - 1) { + let end = match cur_line.char_indices().nth(cur_lo.col.to_usize()) { + Some((i, _)) => i, + None => cur_line.len(), + }; + buf.push_str(&cur_line[..end]); + } + } + // Add a whole line highlight per line in the snippet. + let len: isize = part + .snippet + .split('\n') + .next() + .unwrap_or(&part.snippet) + .chars() + .map(|c| match c { + '\t' => 4, + _ => 1, + }) + .sum(); + line_highlight.push(SubstitutionHighlight { + start: (cur_lo.col.0 as isize + acc) as usize, + end: (cur_lo.col.0 as isize + acc + len) as usize, + }); + buf.push_str(&part.snippet); + let cur_hi = sm.lookup_char_pos(part.span.hi()); + if prev_hi.line == cur_lo.line && cur_hi.line == cur_lo.line { + // Account for the difference between the width of the current code and the + // snippet being suggested, so that the *later* suggestions are correctly + // aligned on the screen. + acc += len as isize - (cur_hi.col.0 - cur_lo.col.0) as isize; + } + prev_hi = cur_hi; + prev_line = sf.get_line(prev_hi.line - 1); + for line in part.snippet.split('\n').skip(1) { + acc = 0; + highlights.push(std::mem::take(&mut line_highlight)); + let end: usize = line + .chars() + .map(|c| match c { + '\t' => 4, + _ => 1, + }) + .sum(); + line_highlight.push(SubstitutionHighlight { start: 0, end }); + } + } + highlights.push(std::mem::take(&mut line_highlight)); + let only_capitalization = is_case_difference(sm, &buf, bounding_span); + // if the replacement already ends with a newline, don't print the next line + if !buf.ends_with('\n') { + push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None); + } + // remove trailing newlines + while buf.ends_with('\n') { + buf.pop(); + } + Some((buf, substitution.parts, highlights, only_capitalization)) + }) + .collect() + } +} + +pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker}; + +/// Signifies that the compiler died with an explicit call to `.bug` +/// or `.span_bug` rather than a failed assertion, etc. +#[derive(Copy, Clone, Debug)] +pub struct ExplicitBug; + +impl fmt::Display for ExplicitBug { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "parser internal bug") + } +} + +impl error::Error for ExplicitBug {} + +pub use diagnostic::{ + AddSubdiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgFromDisplay, + DiagnosticArgValue, DiagnosticId, DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic, +}; +pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, LintDiagnosticBuilder}; +use std::backtrace::Backtrace; + +/// A handler deals with errors and other compiler output. +/// Certain errors (fatal, bug, unimpl) may cause immediate exit, +/// others log errors for later reporting. +pub struct Handler { + flags: HandlerFlags, + inner: Lock, +} + +/// This inner struct exists to keep it all behind a single lock; +/// this is done to prevent possible deadlocks in a multi-threaded compiler, +/// as well as inconsistent state observation. +struct HandlerInner { + flags: HandlerFlags, + /// The number of lint errors that have been emitted. + lint_err_count: usize, + /// The number of errors that have been emitted, including duplicates. + /// + /// This is not necessarily the count that's reported to the user once + /// compilation ends. + err_count: usize, + warn_count: usize, + deduplicated_err_count: usize, + emitter: Box, + delayed_span_bugs: Vec, + delayed_good_path_bugs: Vec, + /// This flag indicates that an expected diagnostic was emitted and suppressed. + /// This is used for the `delayed_good_path_bugs` check. + suppressed_expected_diag: bool, + + /// This set contains the `DiagnosticId` of all emitted diagnostics to avoid + /// emitting the same diagnostic with extended help (`--teach`) twice, which + /// would be unnecessary repetition. + taught_diagnostics: FxHashSet, + + /// Used to suggest rustc --explain + emitted_diagnostic_codes: FxHashSet, + + /// This set contains a hash of every diagnostic that has been emitted by + /// this handler. These hashes is used to avoid emitting the same error + /// twice. + emitted_diagnostics: FxHashSet, + + /// Stashed diagnostics emitted in one stage of the compiler that may be + /// stolen by other stages (e.g. to improve them and add more information). + /// The stashed diagnostics count towards the total error count. + /// When `.abort_if_errors()` is called, these are also emitted. + stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>, + + /// The warning count, used for a recap upon finishing + deduplicated_warn_count: usize, + + future_breakage_diagnostics: Vec, + + /// The [`Self::unstable_expect_diagnostics`] should be empty when this struct is + /// dropped. However, it can have values if the compilation is stopped early + /// or is only partially executed. To avoid ICEs, like in rust#94953 we only + /// check if [`Self::unstable_expect_diagnostics`] is empty, if the expectation ids + /// have been converted. + check_unstable_expect_diagnostics: bool, + + /// Expected [`Diagnostic`]s store a [`LintExpectationId`] as part of + /// the lint level. [`LintExpectationId`]s created early during the compilation + /// (before `HirId`s have been defined) are not stable and can therefore not be + /// stored on disk. This buffer stores these diagnostics until the ID has been + /// replaced by a stable [`LintExpectationId`]. The [`Diagnostic`]s are the + /// submitted for storage and added to the list of fulfilled expectations. + unstable_expect_diagnostics: Vec, + + /// expected diagnostic will have the level `Expect` which additionally + /// carries the [`LintExpectationId`] of the expectation that can be + /// marked as fulfilled. This is a collection of all [`LintExpectationId`]s + /// that have been marked as fulfilled this way. + /// + /// [RFC-2383]: https://rust-lang.github.io/rfcs/2383-lint-reasons.html + fulfilled_expectations: FxHashSet, +} + +/// A key denoting where from a diagnostic was stashed. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum StashKey { + ItemNoType, +} + +fn default_track_diagnostic(_: &Diagnostic) {} + +pub static TRACK_DIAGNOSTICS: AtomicRef = + AtomicRef::new(&(default_track_diagnostic as fn(&_))); + +#[derive(Copy, Clone, Default)] +pub struct HandlerFlags { + /// If false, warning-level lints are suppressed. + /// (rustc: see `--allow warnings` and `--cap-lints`) + pub can_emit_warnings: bool, + /// If true, error-level diagnostics are upgraded to bug-level. + /// (rustc: see `-Z treat-err-as-bug`) + pub treat_err_as_bug: Option, + /// If true, immediately emit diagnostics that would otherwise be buffered. + /// (rustc: see `-Z dont-buffer-diagnostics` and `-Z treat-err-as-bug`) + pub dont_buffer_diagnostics: bool, + /// If true, immediately print bugs registered with `delay_span_bug`. + /// (rustc: see `-Z report-delayed-bugs`) + pub report_delayed_bugs: bool, + /// Show macro backtraces. + /// (rustc: see `-Z macro-backtrace`) + pub macro_backtrace: bool, + /// If true, identical diagnostics are reported only once. + pub deduplicate_diagnostics: bool, +} + +impl Drop for HandlerInner { + fn drop(&mut self) { + self.emit_stashed_diagnostics(); + + if !self.has_errors() { + let bugs = std::mem::replace(&mut self.delayed_span_bugs, Vec::new()); + self.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued"); + } + + // FIXME(eddyb) this explains what `delayed_good_path_bugs` are! + // They're `delayed_span_bugs` but for "require some diagnostic happened" + // instead of "require some error happened". Sadly that isn't ideal, as + // lints can be `#[allow]`'d, potentially leading to this triggering. + // Also, "good path" should be replaced with a better naming. + if !self.has_any_message() && !self.suppressed_expected_diag { + let bugs = std::mem::replace(&mut self.delayed_good_path_bugs, Vec::new()); + self.flush_delayed( + bugs.into_iter().map(DelayedDiagnostic::decorate), + "no warnings or errors encountered even though `delayed_good_path_bugs` issued", + ); + } + + if self.check_unstable_expect_diagnostics { + assert!( + self.unstable_expect_diagnostics.is_empty(), + "all diagnostics with unstable expectations should have been converted", + ); + } + } +} + +impl Handler { + pub fn with_tty_emitter( + color_config: ColorConfig, + can_emit_warnings: bool, + treat_err_as_bug: Option, + sm: Option>, + fluent_bundle: Option>, + fallback_bundle: LazyFallbackBundle, + ) -> Self { + Self::with_tty_emitter_and_flags( + color_config, + sm, + fluent_bundle, + fallback_bundle, + HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, + ) + } + + pub fn with_tty_emitter_and_flags( + color_config: ColorConfig, + sm: Option>, + fluent_bundle: Option>, + fallback_bundle: LazyFallbackBundle, + flags: HandlerFlags, + ) -> Self { + let emitter = Box::new(EmitterWriter::stderr( + color_config, + sm, + fluent_bundle, + fallback_bundle, + false, + false, + None, + flags.macro_backtrace, + )); + Self::with_emitter_and_flags(emitter, flags) + } + + pub fn with_emitter( + can_emit_warnings: bool, + treat_err_as_bug: Option, + emitter: Box, + ) -> Self { + Handler::with_emitter_and_flags( + emitter, + HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, + ) + } + + pub fn with_emitter_and_flags( + emitter: Box, + flags: HandlerFlags, + ) -> Self { + Self { + flags, + inner: Lock::new(HandlerInner { + flags, + lint_err_count: 0, + err_count: 0, + warn_count: 0, + deduplicated_err_count: 0, + deduplicated_warn_count: 0, + emitter, + delayed_span_bugs: Vec::new(), + delayed_good_path_bugs: Vec::new(), + suppressed_expected_diag: false, + taught_diagnostics: Default::default(), + emitted_diagnostic_codes: Default::default(), + emitted_diagnostics: Default::default(), + stashed_diagnostics: Default::default(), + future_breakage_diagnostics: Vec::new(), + check_unstable_expect_diagnostics: false, + unstable_expect_diagnostics: Vec::new(), + fulfilled_expectations: Default::default(), + }), + } + } + + // This is here to not allow mutation of flags; + // as of this writing it's only used in tests in librustc_middle. + pub fn can_emit_warnings(&self) -> bool { + self.flags.can_emit_warnings + } + + /// Resets the diagnostic error count as well as the cached emitted diagnostics. + /// + /// NOTE: *do not* call this function from rustc. It is only meant to be called from external + /// tools that want to reuse a `Parser` cleaning the previously emitted diagnostics as well as + /// the overall count of emitted error diagnostics. + pub fn reset_err_count(&self) { + let mut inner = self.inner.borrow_mut(); + inner.err_count = 0; + inner.warn_count = 0; + inner.deduplicated_err_count = 0; + inner.deduplicated_warn_count = 0; + + // actually free the underlying memory (which `clear` would not do) + inner.delayed_span_bugs = Default::default(); + inner.delayed_good_path_bugs = Default::default(); + inner.taught_diagnostics = Default::default(); + inner.emitted_diagnostic_codes = Default::default(); + inner.emitted_diagnostics = Default::default(); + inner.stashed_diagnostics = Default::default(); + } + + /// Stash a given diagnostic with the given `Span` and `StashKey` as the key for later stealing. + pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: Diagnostic) { + let mut inner = self.inner.borrow_mut(); + // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic + // if/when we have a more robust macro-friendly replacement for `(span, key)` as a key. + // See the PR for a discussion. + inner.stashed_diagnostics.insert((span, key), diag); + } + + /// Steal a previously stashed diagnostic with the given `Span` and `StashKey` as the key. + pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option> { + self.inner + .borrow_mut() + .stashed_diagnostics + .remove(&(span, key)) + .map(|diag| DiagnosticBuilder::new_diagnostic(self, diag)) + } + + /// Emit all stashed diagnostics. + pub fn emit_stashed_diagnostics(&self) -> Option { + self.inner.borrow_mut().emit_stashed_diagnostics() + } + + /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. + /// + /// Attempting to `.emit()` the builder will only emit if either: + /// * `can_emit_warnings` is `true` + /// * `is_force_warn` was set in `DiagnosticId::Lint` + #[rustc_lint_diagnostics] + pub fn struct_span_warn( + &self, + span: impl Into, + msg: impl Into, + ) -> DiagnosticBuilder<'_, ()> { + let mut result = self.struct_warn(msg); + result.set_span(span); + result + } + + /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. + /// The `id` is used for lint emissions which should also fulfill a lint expectation. + /// + /// Attempting to `.emit()` the builder will only emit if either: + /// * `can_emit_warnings` is `true` + /// * `is_force_warn` was set in `DiagnosticId::Lint` + pub fn struct_span_warn_with_expectation( + &self, + span: impl Into, + msg: impl Into, + id: LintExpectationId, + ) -> DiagnosticBuilder<'_, ()> { + let mut result = self.struct_warn_with_expectation(msg, id); + result.set_span(span); + result + } + + /// Construct a builder at the `Allow` level at the given `span` and with the `msg`. + #[rustc_lint_diagnostics] + pub fn struct_span_allow( + &self, + span: impl Into, + msg: impl Into, + ) -> DiagnosticBuilder<'_, ()> { + let mut result = self.struct_allow(msg); + result.set_span(span); + result + } + + /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. + /// Also include a code. + #[rustc_lint_diagnostics] + pub fn struct_span_warn_with_code( + &self, + span: impl Into, + msg: impl Into, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_, ()> { + let mut result = self.struct_span_warn(span, msg); + result.code(code); + result + } + + /// Construct a builder at the `Warning` level with the `msg`. + /// + /// Attempting to `.emit()` the builder will only emit if either: + /// * `can_emit_warnings` is `true` + /// * `is_force_warn` was set in `DiagnosticId::Lint` + #[rustc_lint_diagnostics] + pub fn struct_warn(&self, msg: impl Into) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Level::Warning(None), msg) + } + + /// Construct a builder at the `Warning` level with the `msg`. The `id` is used for + /// lint emissions which should also fulfill a lint expectation. + /// + /// Attempting to `.emit()` the builder will only emit if either: + /// * `can_emit_warnings` is `true` + /// * `is_force_warn` was set in `DiagnosticId::Lint` + pub fn struct_warn_with_expectation( + &self, + msg: impl Into, + id: LintExpectationId, + ) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Level::Warning(Some(id)), msg) + } + + /// Construct a builder at the `Allow` level with the `msg`. + #[rustc_lint_diagnostics] + pub fn struct_allow(&self, msg: impl Into) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Level::Allow, msg) + } + + /// Construct a builder at the `Expect` level with the `msg`. + #[rustc_lint_diagnostics] + pub fn struct_expect( + &self, + msg: impl Into, + id: LintExpectationId, + ) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Level::Expect(id), msg) + } + + /// Construct a builder at the `Error` level at the given `span` and with the `msg`. + #[rustc_lint_diagnostics] + pub fn struct_span_err( + &self, + span: impl Into, + msg: impl Into, + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + let mut result = self.struct_err(msg); + result.set_span(span); + result + } + + /// Construct a builder at the `Error` level at the given `span`, with the `msg`, and `code`. + #[rustc_lint_diagnostics] + pub fn struct_span_err_with_code( + &self, + span: impl Into, + msg: impl Into, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + let mut result = self.struct_span_err(span, msg); + result.code(code); + result + } + + /// Construct a builder at the `Error` level with the `msg`. + // FIXME: This method should be removed (every error should have an associated error code). + #[rustc_lint_diagnostics] + pub fn struct_err( + &self, + msg: impl Into, + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>(self, msg) + } + + /// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors. + #[doc(hidden)] + pub fn struct_err_lint(&self, msg: impl Into) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Level::Error { lint: true }, msg) + } + + /// Construct a builder at the `Error` level with the `msg` and the `code`. + #[rustc_lint_diagnostics] + pub fn struct_err_with_code( + &self, + msg: impl Into, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + let mut result = self.struct_err(msg); + result.code(code); + result + } + + /// Construct a builder at the `Warn` level with the `msg` and the `code`. + #[rustc_lint_diagnostics] + pub fn struct_warn_with_code( + &self, + msg: impl Into, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_, ()> { + let mut result = self.struct_warn(msg); + result.code(code); + result + } + + /// Construct a builder at the `Fatal` level at the given `span` and with the `msg`. + #[rustc_lint_diagnostics] + pub fn struct_span_fatal( + &self, + span: impl Into, + msg: impl Into, + ) -> DiagnosticBuilder<'_, !> { + let mut result = self.struct_fatal(msg); + result.set_span(span); + result + } + + /// Construct a builder at the `Fatal` level at the given `span`, with the `msg`, and `code`. + #[rustc_lint_diagnostics] + pub fn struct_span_fatal_with_code( + &self, + span: impl Into, + msg: impl Into, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_, !> { + let mut result = self.struct_span_fatal(span, msg); + result.code(code); + result + } + + /// Construct a builder at the `Error` level with the `msg`. + #[rustc_lint_diagnostics] + pub fn struct_fatal(&self, msg: impl Into) -> DiagnosticBuilder<'_, !> { + DiagnosticBuilder::new_fatal(self, msg) + } + + /// Construct a builder at the `Help` level with the `msg`. + #[rustc_lint_diagnostics] + pub fn struct_help(&self, msg: impl Into) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Level::Help, msg) + } + + /// Construct a builder at the `Note` level with the `msg`. + #[rustc_lint_diagnostics] + pub fn struct_note_without_error( + &self, + msg: impl Into, + ) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Level::Note, msg) + } + + #[rustc_lint_diagnostics] + pub fn span_fatal(&self, span: impl Into, msg: impl Into) -> ! { + self.emit_diag_at_span(Diagnostic::new(Fatal, msg), span); + FatalError.raise() + } + + #[rustc_lint_diagnostics] + pub fn span_fatal_with_code( + &self, + span: impl Into, + msg: impl Into, + code: DiagnosticId, + ) -> ! { + self.emit_diag_at_span(Diagnostic::new_with_code(Fatal, Some(code), msg), span); + FatalError.raise() + } + + #[rustc_lint_diagnostics] + pub fn span_err( + &self, + span: impl Into, + msg: impl Into, + ) -> ErrorGuaranteed { + self.emit_diag_at_span(Diagnostic::new(Error { lint: false }, msg), span).unwrap() + } + + #[rustc_lint_diagnostics] + pub fn span_err_with_code( + &self, + span: impl Into, + msg: impl Into, + code: DiagnosticId, + ) { + self.emit_diag_at_span( + Diagnostic::new_with_code(Error { lint: false }, Some(code), msg), + span, + ); + } + + #[rustc_lint_diagnostics] + pub fn span_warn(&self, span: impl Into, msg: impl Into) { + self.emit_diag_at_span(Diagnostic::new(Warning(None), msg), span); + } + + #[rustc_lint_diagnostics] + pub fn span_warn_with_code( + &self, + span: impl Into, + msg: impl Into, + code: DiagnosticId, + ) { + self.emit_diag_at_span(Diagnostic::new_with_code(Warning(None), Some(code), msg), span); + } + + pub fn span_bug(&self, span: impl Into, msg: impl Into) -> ! { + self.inner.borrow_mut().span_bug(span, msg) + } + + #[track_caller] + pub fn delay_span_bug( + &self, + span: impl Into, + msg: impl Into, + ) -> ErrorGuaranteed { + self.inner.borrow_mut().delay_span_bug(span, msg) + } + + // FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's + // where the explanation of what "good path" is (also, it should be renamed). + pub fn delay_good_path_bug(&self, msg: impl Into) { + self.inner.borrow_mut().delay_good_path_bug(msg) + } + + pub fn span_bug_no_panic(&self, span: impl Into, msg: impl Into) { + self.emit_diag_at_span(Diagnostic::new(Bug, msg), span); + } + + pub fn span_note_without_error( + &self, + span: impl Into, + msg: impl Into, + ) { + self.emit_diag_at_span(Diagnostic::new(Note, msg), span); + } + + pub fn span_note_diag( + &self, + span: Span, + msg: impl Into, + ) -> DiagnosticBuilder<'_, ()> { + let mut db = DiagnosticBuilder::new(self, Note, msg); + db.set_span(span); + db + } + + // NOTE: intentionally doesn't raise an error so rustc_codegen_ssa only reports fatal errors in the main thread + pub fn fatal(&self, msg: impl Into) -> FatalError { + self.inner.borrow_mut().fatal(msg) + } + + pub fn err(&self, msg: impl Into) -> ErrorGuaranteed { + self.inner.borrow_mut().err(msg) + } + + pub fn warn(&self, msg: impl Into) { + let mut db = DiagnosticBuilder::new(self, Warning(None), msg); + db.emit(); + } + + pub fn note_without_error(&self, msg: impl Into) { + DiagnosticBuilder::new(self, Note, msg).emit(); + } + + pub fn bug(&self, msg: impl Into) -> ! { + self.inner.borrow_mut().bug(msg) + } + + #[inline] + pub fn err_count(&self) -> usize { + self.inner.borrow().err_count() + } + + pub fn has_errors(&self) -> Option { + if self.inner.borrow().has_errors() { Some(ErrorGuaranteed(())) } else { None } + } + pub fn has_errors_or_lint_errors(&self) -> Option { + if self.inner.borrow().has_errors_or_lint_errors() { + Some(ErrorGuaranteed(())) + } else { + None + } + } + pub fn has_errors_or_delayed_span_bugs(&self) -> bool { + self.inner.borrow().has_errors_or_delayed_span_bugs() + } + + pub fn print_error_count(&self, registry: &Registry) { + self.inner.borrow_mut().print_error_count(registry) + } + + pub fn take_future_breakage_diagnostics(&self) -> Vec { + std::mem::take(&mut self.inner.borrow_mut().future_breakage_diagnostics) + } + + pub fn abort_if_errors(&self) { + self.inner.borrow_mut().abort_if_errors() + } + + /// `true` if we haven't taught a diagnostic with this code already. + /// The caller must then teach the user about such a diagnostic. + /// + /// Used to suppress emitting the same error multiple times with extended explanation when + /// calling `-Zteach`. + pub fn must_teach(&self, code: &DiagnosticId) -> bool { + self.inner.borrow_mut().must_teach(code) + } + + pub fn force_print_diagnostic(&self, db: Diagnostic) { + self.inner.borrow_mut().force_print_diagnostic(db) + } + + pub fn emit_diagnostic(&self, diagnostic: &mut Diagnostic) -> Option { + self.inner.borrow_mut().emit_diagnostic(diagnostic) + } + + fn emit_diag_at_span( + &self, + mut diag: Diagnostic, + sp: impl Into, + ) -> Option { + let mut inner = self.inner.borrow_mut(); + inner.emit_diagnostic(diag.set_span(sp)) + } + + pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) { + self.inner.borrow_mut().emit_artifact_notification(path, artifact_type) + } + + pub fn emit_future_breakage_report(&self, diags: Vec) { + self.inner.borrow_mut().emitter.emit_future_breakage_report(diags) + } + + pub fn emit_unused_externs( + &self, + lint_level: rustc_lint_defs::Level, + loud: bool, + unused_externs: &[&str], + ) { + let mut inner = self.inner.borrow_mut(); + + if loud && lint_level.is_error() { + inner.bump_err_count(); + } + + inner.emit_unused_externs(lint_level, unused_externs) + } + + pub fn update_unstable_expectation_id( + &self, + unstable_to_stable: &FxHashMap, + ) { + let mut inner = self.inner.borrow_mut(); + let diags = std::mem::take(&mut inner.unstable_expect_diagnostics); + inner.check_unstable_expect_diagnostics = true; + + if !diags.is_empty() { + inner.suppressed_expected_diag = true; + for mut diag in diags.into_iter() { + diag.update_unstable_expectation_id(unstable_to_stable); + + // Here the diagnostic is given back to `emit_diagnostic` where it was first + // intercepted. Now it should be processed as usual, since the unstable expectation + // id is now stable. + inner.emit_diagnostic(&mut diag); + } + } + + inner + .stashed_diagnostics + .values_mut() + .for_each(|diag| diag.update_unstable_expectation_id(unstable_to_stable)); + inner + .future_breakage_diagnostics + .iter_mut() + .for_each(|diag| diag.update_unstable_expectation_id(unstable_to_stable)); + } + + /// This methods steals all [`LintExpectationId`]s that are stored inside + /// [`HandlerInner`] and indicate that the linked expectation has been fulfilled. + #[must_use] + pub fn steal_fulfilled_expectation_ids(&self) -> FxHashSet { + assert!( + self.inner.borrow().unstable_expect_diagnostics.is_empty(), + "`HandlerInner::unstable_expect_diagnostics` should be empty at this point", + ); + std::mem::take(&mut self.inner.borrow_mut().fulfilled_expectations) + } +} + +impl HandlerInner { + fn must_teach(&mut self, code: &DiagnosticId) -> bool { + self.taught_diagnostics.insert(code.clone()) + } + + fn force_print_diagnostic(&mut self, mut db: Diagnostic) { + self.emitter.emit_diagnostic(&mut db); + } + + /// Emit all stashed diagnostics. + fn emit_stashed_diagnostics(&mut self) -> Option { + let diags = self.stashed_diagnostics.drain(..).map(|x| x.1).collect::>(); + let mut reported = None; + for mut diag in diags { + if diag.is_error() { + reported = Some(ErrorGuaranteed(())); + } + self.emit_diagnostic(&mut diag); + } + reported + } + + // FIXME(eddyb) this should ideally take `diagnostic` by value. + fn emit_diagnostic(&mut self, diagnostic: &mut Diagnostic) -> Option { + // The `LintExpectationId` can be stable or unstable depending on when it was created. + // Diagnostics created before the definition of `HirId`s are unstable and can not yet + // be stored. Instead, they are buffered until the `LintExpectationId` is replaced by + // a stable one by the `LintLevelsBuilder`. + if let Some(LintExpectationId::Unstable { .. }) = diagnostic.level.get_expectation_id() { + self.unstable_expect_diagnostics.push(diagnostic.clone()); + return None; + } + + if diagnostic.level == Level::DelayedBug { + // FIXME(eddyb) this should check for `has_errors` and stop pushing + // once *any* errors were emitted (and truncate `delayed_span_bugs` + // when an error is first emitted, also), but maybe there's a case + // in which that's not sound? otherwise this is really inefficient. + self.delayed_span_bugs.push(diagnostic.clone()); + + if !self.flags.report_delayed_bugs { + return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); + } + } + + if diagnostic.has_future_breakage() { + self.future_breakage_diagnostics.push(diagnostic.clone()); + } + + if let Some(expectation_id) = diagnostic.level.get_expectation_id() { + self.suppressed_expected_diag = true; + self.fulfilled_expectations.insert(expectation_id); + } + + if matches!(diagnostic.level, Warning(_)) + && !self.flags.can_emit_warnings + && !diagnostic.is_force_warn() + { + if diagnostic.has_future_breakage() { + (*TRACK_DIAGNOSTICS)(diagnostic); + } + return None; + } + + (*TRACK_DIAGNOSTICS)(diagnostic); + + if matches!(diagnostic.level, Level::Expect(_) | Level::Allow) { + return None; + } + + if let Some(ref code) = diagnostic.code { + self.emitted_diagnostic_codes.insert(code.clone()); + } + + let already_emitted = |this: &mut Self| { + let mut hasher = StableHasher::new(); + diagnostic.hash(&mut hasher); + let diagnostic_hash = hasher.finish(); + !this.emitted_diagnostics.insert(diagnostic_hash) + }; + + // Only emit the diagnostic if we've been asked to deduplicate or + // haven't already emitted an equivalent diagnostic. + if !(self.flags.deduplicate_diagnostics && already_emitted(self)) { + debug!(?diagnostic); + debug!(?self.emitted_diagnostics); + let already_emitted_sub = |sub: &mut SubDiagnostic| { + debug!(?sub); + if sub.level != Level::OnceNote { + return false; + } + 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.drain_filter(already_emitted_sub).for_each(|_| {}); + + self.emitter.emit_diagnostic(&diagnostic); + if diagnostic.is_error() { + self.deduplicated_err_count += 1; + } else if let Warning(_) = diagnostic.level { + self.deduplicated_warn_count += 1; + } + } + if diagnostic.is_error() { + if matches!(diagnostic.level, Level::Error { lint: true }) { + self.bump_lint_err_count(); + } else { + self.bump_err_count(); + } + + Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()) + } else { + self.bump_warn_count(); + + None + } + } + + fn emit_artifact_notification(&mut self, path: &Path, artifact_type: &str) { + self.emitter.emit_artifact_notification(path, artifact_type); + } + + fn emit_unused_externs(&mut self, lint_level: rustc_lint_defs::Level, unused_externs: &[&str]) { + self.emitter.emit_unused_externs(lint_level, unused_externs); + } + + fn treat_err_as_bug(&self) -> bool { + self.flags + .treat_err_as_bug + .map_or(false, |c| self.err_count() + self.lint_err_count >= c.get()) + } + + fn print_error_count(&mut self, registry: &Registry) { + self.emit_stashed_diagnostics(); + + let warnings = match self.deduplicated_warn_count { + 0 => String::new(), + 1 => "1 warning emitted".to_string(), + count => format!("{count} warnings emitted"), + }; + let errors = match self.deduplicated_err_count { + 0 => String::new(), + 1 => "aborting due to previous error".to_string(), + count => format!("aborting due to {count} previous errors"), + }; + if self.treat_err_as_bug() { + return; + } + + match (errors.len(), warnings.len()) { + (0, 0) => return, + (0, _) => self.emitter.emit_diagnostic(&Diagnostic::new( + Level::Warning(None), + DiagnosticMessage::Str(warnings), + )), + (_, 0) => { + let _ = self.fatal(&errors); + } + (_, _) => { + let _ = self.fatal(&format!("{}; {}", &errors, &warnings)); + } + } + + let can_show_explain = self.emitter.should_show_explain(); + let are_there_diagnostics = !self.emitted_diagnostic_codes.is_empty(); + if can_show_explain && are_there_diagnostics { + let mut error_codes = self + .emitted_diagnostic_codes + .iter() + .filter_map(|x| match &x { + DiagnosticId::Error(s) + if registry.try_find_description(s).map_or(false, |o| o.is_some()) => + { + Some(s.clone()) + } + _ => None, + }) + .collect::>(); + if !error_codes.is_empty() { + error_codes.sort(); + if error_codes.len() > 1 { + let limit = if error_codes.len() > 9 { 9 } else { error_codes.len() }; + self.failure(&format!( + "Some errors have detailed explanations: {}{}", + error_codes[..limit].join(", "), + if error_codes.len() > 9 { "..." } else { "." } + )); + self.failure(&format!( + "For more information about an error, try \ + `rustc --explain {}`.", + &error_codes[0] + )); + } else { + self.failure(&format!( + "For more information about this error, try \ + `rustc --explain {}`.", + &error_codes[0] + )); + } + } + } + } + + #[inline] + fn err_count(&self) -> usize { + self.err_count + self.stashed_diagnostics.len() + } + + fn has_errors(&self) -> bool { + self.err_count() > 0 + } + fn has_errors_or_lint_errors(&self) -> bool { + self.has_errors() || self.lint_err_count > 0 + } + fn has_errors_or_delayed_span_bugs(&self) -> bool { + self.has_errors() || !self.delayed_span_bugs.is_empty() + } + fn has_any_message(&self) -> bool { + self.err_count() > 0 || self.lint_err_count > 0 || self.warn_count > 0 + } + + fn abort_if_errors(&mut self) { + self.emit_stashed_diagnostics(); + + if self.has_errors() { + FatalError.raise(); + } + } + + fn span_bug(&mut self, sp: impl Into, msg: impl Into) -> ! { + self.emit_diag_at_span(Diagnostic::new(Bug, msg), sp); + panic::panic_any(ExplicitBug); + } + + fn emit_diag_at_span(&mut self, mut diag: Diagnostic, sp: impl Into) { + self.emit_diagnostic(diag.set_span(sp)); + } + + #[track_caller] + fn delay_span_bug( + &mut self, + sp: impl Into, + msg: impl Into, + ) -> ErrorGuaranteed { + // This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before + // incrementing `err_count` by one, so we need to +1 the comparing. + // FIXME: Would be nice to increment err_count in a more coherent way. + if self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() + 1 >= c.get()) { + // FIXME: don't abort here if report_delayed_bugs is off + self.span_bug(sp, msg); + } + let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg); + diagnostic.set_span(sp.into()); + diagnostic.note(&format!("delayed at {}", std::panic::Location::caller())); + self.emit_diagnostic(&mut diagnostic).unwrap() + } + + // FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's + // where the explanation of what "good path" is (also, it should be renamed). + fn delay_good_path_bug(&mut self, msg: impl Into) { + let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg); + if self.flags.report_delayed_bugs { + self.emit_diagnostic(&mut diagnostic); + } + let backtrace = std::backtrace::Backtrace::force_capture(); + self.delayed_good_path_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace)); + } + + fn failure(&mut self, msg: impl Into) { + self.emit_diagnostic(&mut Diagnostic::new(FailureNote, msg)); + } + + fn fatal(&mut self, msg: impl Into) -> FatalError { + self.emit(Fatal, msg); + FatalError + } + + fn err(&mut self, msg: impl Into) -> ErrorGuaranteed { + self.emit(Error { lint: false }, msg) + } + + /// Emit an error; level should be `Error` or `Fatal`. + fn emit(&mut self, level: Level, msg: impl Into) -> ErrorGuaranteed { + if self.treat_err_as_bug() { + self.bug(msg); + } + self.emit_diagnostic(&mut Diagnostic::new(level, msg)).unwrap() + } + + fn bug(&mut self, msg: impl Into) -> ! { + self.emit_diagnostic(&mut Diagnostic::new(Bug, msg)); + panic::panic_any(ExplicitBug); + } + + fn flush_delayed( + &mut self, + bugs: impl IntoIterator, + explanation: impl Into + Copy, + ) { + let mut no_bugs = true; + for mut bug in bugs { + if no_bugs { + // Put the overall explanation before the `DelayedBug`s, to + // frame them better (e.g. separate warnings from them). + self.emit_diagnostic(&mut Diagnostic::new(Bug, explanation)); + no_bugs = false; + } + + // "Undelay" the `DelayedBug`s (into plain `Bug`s). + if bug.level != Level::DelayedBug { + // NOTE(eddyb) not panicking here because we're already producing + // an ICE, and the more information the merrier. + bug.note(&format!( + "`flushed_delayed` got diagnostic with level {:?}, \ + instead of the expected `DelayedBug`", + bug.level, + )); + } + bug.level = Level::Bug; + + self.emit_diagnostic(&mut bug); + } + + // Panic with `ExplicitBug` to avoid "unexpected panic" messages. + if !no_bugs { + panic::panic_any(ExplicitBug); + } + } + + fn bump_lint_err_count(&mut self) { + self.lint_err_count += 1; + self.panic_if_treat_err_as_bug(); + } + + fn bump_err_count(&mut self) { + self.err_count += 1; + self.panic_if_treat_err_as_bug(); + } + + fn bump_warn_count(&mut self) { + self.warn_count += 1; + } + + fn panic_if_treat_err_as_bug(&self) { + if self.treat_err_as_bug() { + match ( + self.err_count() + self.lint_err_count, + self.flags.treat_err_as_bug.map(|c| c.get()).unwrap_or(0), + ) { + (1, 1) => panic!("aborting due to `-Z treat-err-as-bug=1`"), + (0 | 1, _) => {} + (count, as_bug) => panic!( + "aborting after {} errors due to `-Z treat-err-as-bug={}`", + count, as_bug, + ), + } + } + } +} + +struct DelayedDiagnostic { + inner: Diagnostic, + note: Backtrace, +} + +impl DelayedDiagnostic { + fn with_backtrace(diagnostic: Diagnostic, backtrace: Backtrace) -> Self { + DelayedDiagnostic { inner: diagnostic, note: backtrace } + } + + fn decorate(mut self) -> Diagnostic { + self.inner.note(&format!("delayed at {}", self.note)); + self.inner + } +} + +#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)] +pub enum Level { + Bug, + DelayedBug, + Fatal, + Error { + /// If this error comes from a lint, don't abort compilation even when abort_if_errors() is called. + lint: bool, + }, + /// This [`LintExpectationId`] is used for expected lint diagnostics, which should + /// also emit a warning due to the `force-warn` flag. In all other cases this should + /// be `None`. + Warning(Option), + Note, + /// A note that is only emitted once. + OnceNote, + Help, + FailureNote, + Allow, + Expect(LintExpectationId), +} + +impl fmt::Display for Level { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.to_str().fmt(f) + } +} + +impl Level { + fn color(self) -> ColorSpec { + let mut spec = ColorSpec::new(); + match self { + Bug | DelayedBug | Fatal | Error { .. } => { + spec.set_fg(Some(Color::Red)).set_intense(true); + } + Warning(_) => { + spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows)); + } + Note | OnceNote => { + spec.set_fg(Some(Color::Green)).set_intense(true); + } + Help => { + spec.set_fg(Some(Color::Cyan)).set_intense(true); + } + FailureNote => {} + Allow | Expect(_) => unreachable!(), + } + spec + } + + pub fn to_str(self) -> &'static str { + match self { + Bug | DelayedBug => "error: internal compiler error", + Fatal | Error { .. } => "error", + Warning(_) => "warning", + Note | OnceNote => "note", + Help => "help", + FailureNote => "failure-note", + Allow => panic!("Shouldn't call on allowed error"), + Expect(_) => panic!("Shouldn't call on expected error"), + } + } + + pub fn is_failure_note(&self) -> bool { + matches!(*self, FailureNote) + } + + pub fn get_expectation_id(&self) -> Option { + match self { + Level::Expect(id) | Level::Warning(Some(id)) => Some(*id), + _ => None, + } + } +} + +// FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite. +pub fn add_elided_lifetime_in_path_suggestion( + source_map: &SourceMap, + diag: &mut Diagnostic, + n: usize, + path_span: Span, + incl_angl_brckt: bool, + insertion_span: Span, +) { + diag.span_label(path_span, format!("expected lifetime parameter{}", pluralize!(n))); + if !source_map.is_span_accessible(insertion_span) { + // Do not try to suggest anything if generated by a proc-macro. + return; + } + let anon_lts = vec!["'_"; n].join(", "); + let suggestion = + if incl_angl_brckt { format!("<{}>", anon_lts) } else { format!("{}, ", anon_lts) }; + diag.span_suggestion_verbose( + insertion_span.shrink_to_hi(), + &format!("indicate the anonymous lifetime{}", pluralize!(n)), + suggestion, + Applicability::MachineApplicable, + ); +} + +/// Useful type to use with `Result<>` indicate that an error has already +/// been reported to the user, so no need to continue checking. +#[derive(Clone, Copy, Debug, Encodable, Decodable, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[derive(HashStable_Generic)] +pub struct ErrorGuaranteed(()); + +impl ErrorGuaranteed { + /// To be used only if you really know what you are doing... ideally, we would find a way to + /// eliminate all calls to this method. + pub fn unchecked_claim_error_was_emitted() -> Self { + ErrorGuaranteed(()) + } +} -- cgit v1.2.3