diff options
| author | bors <bors@rust-lang.org> | 2024-02-29 16:10:05 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-02-29 16:10:05 +0000 |
| commit | 1a1876c9790f168fb51afa335a7ba3e6fc267d75 (patch) | |
| tree | 3bd55c979127cf371e5059d7debb7e9e8677c160 /compiler/rustc_errors/src | |
| parent | 384d26fc7e3bdd7687cc17b2662b091f6017ec2a (diff) | |
| parent | 0e9f02d6fa769f1e96fc5e54af7b77b101bcc603 (diff) | |
| download | rust-1a1876c9790f168fb51afa335a7ba3e6fc267d75.tar.gz rust-1a1876c9790f168fb51afa335a7ba3e6fc267d75.zip | |
Auto merge of #121804 - GuillaumeGomez:rollup-jh0v3ex, r=GuillaumeGomez
Rollup of 7 pull requests Successful merges: - #119748 (Increase visibility of `join_path` and `split_paths`) - #120820 (Enable CMPXCHG16B, SSE3, SAHF/LAHF and 128-bit Atomics (in nightly) in Windows x64) - #121000 (pattern_analysis: rework how we hide empty private fields) - #121376 (Skip unnecessary comparison with half-open range patterns) - #121596 (Use volatile access instead of `#[used]` for `on_tls_callback`) - #121669 (Count stashed errors again) - #121783 (Emitter cleanups) r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'compiler/rustc_errors/src')
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic.rs | 8 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/emitter.rs | 61 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/error.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/json.rs | 131 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/json/tests.rs | 12 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/lib.rs | 244 |
6 files changed, 226 insertions, 233 deletions
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 0cf519d2029..1c820bfd01f 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1308,11 +1308,9 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { drop(self); } - /// Stashes diagnostic for possible later improvement in a different, - /// later stage of the compiler. The diagnostic can be accessed with - /// the provided `span` and `key` through [`DiagCtxt::steal_diagnostic()`]. - pub fn stash(mut self, span: Span, key: StashKey) { - self.dcx.stash_diagnostic(span, key, self.take_diag()); + /// See `DiagCtxt::stash_diagnostic` for details. + pub fn stash(mut self, span: Span, key: StashKey) -> Option<ErrorGuaranteed> { + self.dcx.stash_diagnostic(span, key, self.take_diag()) } /// Delay emission of this diagnostic as a bug. diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 304922018eb..5637c05d04c 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -21,12 +21,11 @@ use crate::{ FluentBundle, LazyFallbackBundle, Level, MultiSpan, Subdiag, SubstitutionHighlight, SuggestionStyle, TerminalUrl, }; -use rustc_lint_defs::pluralize; - use derive_setters::Setters; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::sync::{DynSend, IntoDynSyncSend, Lrc}; use rustc_error_messages::{FluentArgs, SpanLabel}; +use rustc_lint_defs::pluralize; use rustc_span::hygiene::{ExpnKind, MacroKind}; use std::borrow::Cow; use std::cmp::{max, min, Reverse}; @@ -35,7 +34,7 @@ use std::io::prelude::*; use std::io::{self, IsTerminal}; use std::iter; use std::path::Path; -use termcolor::{Ansi, Buffer, BufferWriter, ColorChoice, ColorSpec, StandardStream}; +use termcolor::{Buffer, BufferWriter, ColorChoice, ColorSpec, StandardStream}; use termcolor::{Color, WriteColor}; /// Default column width, used in tests and when terminal dimensions cannot be determined. @@ -58,18 +57,6 @@ impl HumanReadableErrorType { HumanReadableErrorType::AnnotateSnippet(cc) => (false, cc), } } - pub fn new_emitter( - self, - mut dst: Box<dyn WriteColor + Send>, - fallback_bundle: LazyFallbackBundle, - ) -> HumanEmitter { - let (short, color_config) = self.unzip(); - let color = color_config.suggests_using_colors(); - if !dst.supports_color() && color { - dst = Box::new(Ansi::new(dst)); - } - HumanEmitter::new(dst, fallback_bundle).short_message(short) - } } #[derive(Clone, Copy, Debug)] @@ -130,8 +117,8 @@ impl Margin { fn was_cut_right(&self, line_len: usize) -> bool { let right = if self.computed_right == self.span_right || self.computed_right == self.label_right { - // Account for the "..." padding given above. Otherwise we end up with code lines that - // do fit but end in "..." as if they were trimmed. + // Account for the "..." padding given above. Otherwise we end up with code lines + // that do fit but end in "..." as if they were trimmed. self.computed_right - 6 } else { self.computed_right @@ -628,12 +615,6 @@ impl ColorConfig { ColorConfig::Auto => ColorChoice::Never, } } - fn suggests_using_colors(self) -> bool { - match self { - ColorConfig::Always | ColorConfig::Auto => true, - ColorConfig::Never => false, - } - } } /// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short` @@ -657,19 +638,14 @@ pub struct HumanEmitter { } #[derive(Debug)] -pub struct FileWithAnnotatedLines { - pub file: Lrc<SourceFile>, - pub lines: Vec<Line>, +pub(crate) struct FileWithAnnotatedLines { + pub(crate) file: Lrc<SourceFile>, + pub(crate) lines: Vec<Line>, multiline_depth: usize, } impl HumanEmitter { - pub fn stderr(color_config: ColorConfig, fallback_bundle: LazyFallbackBundle) -> HumanEmitter { - let dst = from_stderr(color_config); - Self::create(dst, fallback_bundle) - } - - fn create(dst: Destination, fallback_bundle: LazyFallbackBundle) -> HumanEmitter { + pub fn new(dst: Destination, fallback_bundle: LazyFallbackBundle) -> HumanEmitter { HumanEmitter { dst: IntoDynSyncSend(dst), sm: None, @@ -686,13 +662,6 @@ impl HumanEmitter { } } - pub fn new( - dst: Box<dyn WriteColor + Send>, - fallback_bundle: LazyFallbackBundle, - ) -> HumanEmitter { - Self::create(dst, fallback_bundle) - } - fn maybe_anonymized(&self, line_num: usize) -> Cow<'static, str> { if self.ui_testing { Cow::Borrowed(ANONYMIZED_LINE_NUM) @@ -724,8 +693,9 @@ impl HumanEmitter { .skip(left) .take_while(|ch| { // Make sure that the trimming on the right will fall within the terminal width. - // FIXME: `unicode_width` sometimes disagrees with terminals on how wide a `char` is. - // For now, just accept that sometimes the code line will be longer than desired. + // FIXME: `unicode_width` sometimes disagrees with terminals on how wide a `char` + // is. For now, just accept that sometimes the code line will be longer than + // desired. let next = unicode_width::UnicodeWidthChar::width(*ch).unwrap_or(1); if taken + next > right - left { return false; @@ -2228,8 +2198,8 @@ impl HumanEmitter { buffer.puts(*row_num - 1, max_line_num_len + 3, &line, Style::NoStyle); *row_num += 1; } - // If the last line is exactly equal to the line we need to add, we can skip both of them. - // This allows us to avoid output like the following: + // If the last line is exactly equal to the line we need to add, we can skip both of + // them. This allows us to avoid output like the following: // 2 - & // 2 + if true { true } else { false } // 3 - if true { true } else { false } @@ -2586,6 +2556,7 @@ fn num_overlap( let extra = if inclusive { 1 } else { 0 }; (b_start..b_end + extra).contains(&a_start) || (a_start..a_end + extra).contains(&b_start) } + fn overlaps(a1: &Annotation, a2: &Annotation, padding: usize) -> bool { num_overlap( a1.start_col.display, @@ -2632,7 +2603,7 @@ fn emit_to_destination( Ok(()) } -pub type Destination = Box<(dyn WriteColor + Send)>; +pub type Destination = Box<dyn WriteColor + Send>; struct Buffy { buffer_writer: BufferWriter, @@ -2674,7 +2645,7 @@ impl WriteColor for Buffy { } } -fn from_stderr(color: ColorConfig) -> Destination { +pub fn stderr_destination(color: ColorConfig) -> Destination { let choice = color.to_color_choice(); // On Windows we'll be performing global synchronization on the entire // system for emitting rustc errors, so there's no need to buffer diff --git a/compiler/rustc_errors/src/error.rs b/compiler/rustc_errors/src/error.rs index ec0a2fe8cd8..ca818a4d832 100644 --- a/compiler/rustc_errors/src/error.rs +++ b/compiler/rustc_errors/src/error.rs @@ -23,9 +23,11 @@ impl<'args> TranslateError<'args> { pub fn message(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self { Self::One { id, args, kind: TranslateErrorKind::MessageMissing } } + pub fn primary(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self { Self::One { id, args, kind: TranslateErrorKind::PrimaryBundleMissing } } + pub fn attribute( id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>, @@ -33,6 +35,7 @@ impl<'args> TranslateError<'args> { ) -> Self { Self::One { id, args, kind: TranslateErrorKind::AttributeMissing { attr } } } + pub fn value(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self { Self::One { id, args, kind: TranslateErrorKind::ValueMissing } } diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 88a83c8bf78..bc1822f83fc 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -9,42 +9,49 @@ // FIXME: spec the JSON output properly. -use rustc_span::source_map::{FilePathMapping, SourceMap}; -use termcolor::{ColorSpec, WriteColor}; - -use crate::emitter::{should_show_source_code, Emitter, HumanReadableErrorType}; +use crate::emitter::{ + should_show_source_code, ColorConfig, Destination, Emitter, HumanEmitter, + HumanReadableErrorType, +}; use crate::registry::Registry; use crate::translation::{to_fluent_args, Translate}; use crate::{ diagnostic::IsLint, CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, Subdiag, TerminalUrl, }; -use rustc_lint_defs::Applicability; - +use derive_setters::Setters; use rustc_data_structures::sync::{IntoDynSyncSend, Lrc}; use rustc_error_messages::FluentArgs; +use rustc_lint_defs::Applicability; use rustc_span::hygiene::ExpnData; +use rustc_span::source_map::SourceMap; use rustc_span::Span; +use serde::Serialize; use std::error::Report; use std::io::{self, Write}; use std::path::Path; use std::sync::{Arc, Mutex}; use std::vec; - -use serde::Serialize; +use termcolor::{ColorSpec, WriteColor}; #[cfg(test)] mod tests; +#[derive(Setters)] pub struct JsonEmitter { + #[setters(skip)] dst: IntoDynSyncSend<Box<dyn Write + Send>>, registry: Option<Registry>, + #[setters(skip)] sm: Lrc<SourceMap>, fluent_bundle: Option<Lrc<FluentBundle>>, + #[setters(skip)] fallback_bundle: LazyFallbackBundle, + #[setters(skip)] pretty: bool, ui_testing: bool, ignored_directories_in_source_blocks: Vec<String>, + #[setters(skip)] json_rendered: HumanReadableErrorType, diagnostic_width: Option<usize>, macro_backtrace: bool, @@ -53,98 +60,30 @@ pub struct JsonEmitter { } impl JsonEmitter { - pub fn stderr( - registry: Option<Registry>, - source_map: Lrc<SourceMap>, - fluent_bundle: Option<Lrc<FluentBundle>>, - fallback_bundle: LazyFallbackBundle, - pretty: bool, - json_rendered: HumanReadableErrorType, - diagnostic_width: Option<usize>, - macro_backtrace: bool, - track_diagnostics: bool, - terminal_url: TerminalUrl, - ) -> JsonEmitter { - JsonEmitter { - dst: IntoDynSyncSend(Box::new(io::BufWriter::new(io::stderr()))), - registry, - sm: source_map, - fluent_bundle, - fallback_bundle, - pretty, - ui_testing: false, - ignored_directories_in_source_blocks: Vec::new(), - json_rendered, - diagnostic_width, - macro_backtrace, - track_diagnostics, - terminal_url, - } - } - - pub fn basic( - pretty: bool, - json_rendered: HumanReadableErrorType, - fluent_bundle: Option<Lrc<FluentBundle>>, - fallback_bundle: LazyFallbackBundle, - diagnostic_width: Option<usize>, - macro_backtrace: bool, - track_diagnostics: bool, - terminal_url: TerminalUrl, - ) -> JsonEmitter { - let file_path_mapping = FilePathMapping::empty(); - JsonEmitter::stderr( - None, - Lrc::new(SourceMap::new(file_path_mapping)), - fluent_bundle, - fallback_bundle, - pretty, - json_rendered, - diagnostic_width, - macro_backtrace, - track_diagnostics, - terminal_url, - ) - } - pub fn new( dst: Box<dyn Write + Send>, - registry: Option<Registry>, - source_map: Lrc<SourceMap>, - fluent_bundle: Option<Lrc<FluentBundle>>, + sm: Lrc<SourceMap>, fallback_bundle: LazyFallbackBundle, pretty: bool, json_rendered: HumanReadableErrorType, - diagnostic_width: Option<usize>, - macro_backtrace: bool, - track_diagnostics: bool, - terminal_url: TerminalUrl, ) -> JsonEmitter { JsonEmitter { dst: IntoDynSyncSend(dst), - registry, - sm: source_map, - fluent_bundle, + registry: None, + sm, + fluent_bundle: None, fallback_bundle, pretty, ui_testing: false, ignored_directories_in_source_blocks: Vec::new(), json_rendered, - diagnostic_width, - macro_backtrace, - track_diagnostics, - terminal_url, + diagnostic_width: None, + macro_backtrace: false, + track_diagnostics: false, + terminal_url: TerminalUrl::No, } } - pub fn ui_testing(self, ui_testing: bool) -> Self { - Self { ui_testing, ..self } - } - - pub fn ignored_directories_in_source_blocks(self, value: Vec<String>) -> Self { - Self { ignored_directories_in_source_blocks: value, ..self } - } - fn emit(&mut self, val: EmitTyped<'_>) -> io::Result<()> { if self.pretty { serde_json::to_writer_pretty(&mut *self.dst, &val)? @@ -162,7 +101,7 @@ enum EmitTyped<'a> { Diagnostic(Diagnostic), Artifact(ArtifactNotification<'a>), FutureIncompat(FutureIncompatReport<'a>), - UnusedExtern(UnusedExterns<'a, 'a, 'a>), + UnusedExtern(UnusedExterns<'a>), } impl Translate for JsonEmitter { @@ -332,14 +271,15 @@ struct FutureIncompatReport<'a> { // We could unify this struct the one in rustdoc but they have different // ownership semantics, so doing so would create wasteful allocations. #[derive(Serialize)] -struct UnusedExterns<'a, 'b, 'c> { +struct UnusedExterns<'a> { /// The severity level of the unused dependencies lint lint_level: &'a str, /// List of unused externs by their names. - unused_extern_names: &'b [&'c str], + unused_extern_names: &'a [&'a str], } impl Diagnostic { + /// Converts from `rustc_errors::DiagInner` to `Diagnostic`. fn from_errors_diagnostic(diag: crate::DiagInner, je: &JsonEmitter) -> Diagnostic { let args = to_fluent_args(diag.args.iter()); let sugg = diag.suggestions.iter().flatten().map(|sugg| { @@ -405,9 +345,14 @@ impl Diagnostic { .collect(); let buf = BufWriter::default(); - let output = buf.clone(); - je.json_rendered - .new_emitter(Box::new(buf), je.fallback_bundle.clone()) + let mut dst: Destination = Box::new(buf.clone()); + let (short, color_config) = je.json_rendered.unzip(); + match color_config { + ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)), + ColorConfig::Never => {} + } + HumanEmitter::new(dst, je.fallback_bundle.clone()) + .short_message(short) .sm(Some(je.sm.clone())) .fluent_bundle(je.fluent_bundle.clone()) .diagnostic_width(je.diagnostic_width) @@ -417,8 +362,8 @@ impl Diagnostic { .ui_testing(je.ui_testing) .ignored_directories_in_source_blocks(je.ignored_directories_in_source_blocks.clone()) .emit_diagnostic(diag); - let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap(); - let output = String::from_utf8(output).unwrap(); + let buf = Arc::try_unwrap(buf.0).unwrap().into_inner().unwrap(); + let buf = String::from_utf8(buf).unwrap(); Diagnostic { message: translated_message.to_string(), @@ -426,7 +371,7 @@ impl Diagnostic { level, spans, children, - rendered: Some(output), + rendered: Some(buf), } } diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index 303de0a93f6..80b4d2bf75c 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -1,7 +1,7 @@ use super::*; -use crate::emitter::ColorConfig; use crate::DiagCtxt; +use rustc_span::source_map::FilePathMapping; use rustc_span::BytePos; use std::str; @@ -48,20 +48,14 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { let output = Arc::new(Mutex::new(Vec::new())); let je = JsonEmitter::new( Box::new(Shared { data: output.clone() }), - None, sm, - None, fallback_bundle, - true, + true, // pretty HumanReadableErrorType::Short(ColorConfig::Never), - None, - false, - false, - TerminalUrl::No, ); let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1)); - let dcx = DiagCtxt::with_emitter(Box::new(je)); + let dcx = DiagCtxt::new(Box::new(je)); dcx.span_err(span, "foo"); let bytes = output.lock().unwrap(); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 2652feff62a..0a533833e64 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -59,11 +59,11 @@ pub use snippet::Style; // See https://github.com/rust-lang/rust/pull/115393. pub use termcolor::{Color, ColorSpec, WriteColor}; -use emitter::{is_case_difference, DynEmitter, Emitter, HumanEmitter}; +use emitter::{is_case_difference, DynEmitter, Emitter}; use registry::Registry; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::{Hash128, StableHasher}; -use rustc_data_structures::sync::{Lock, Lrc}; +use rustc_data_structures::sync::Lock; use rustc_data_structures::AtomicRef; use rustc_lint_defs::LintExpectationId; use rustc_span::source_map::SourceMap; @@ -217,10 +217,10 @@ impl CodeSuggestion { use rustc_span::{CharPos, Pos}; - /// Extracts a substring from the provided `line_opt` based on the specified low and high indices, - /// appends it to the given buffer `buf`, and returns the count of newline characters in the substring - /// for accurate highlighting. - /// If `line_opt` is `None`, a newline character is appended to the buffer, and 0 is returned. + /// Extracts a substring from the provided `line_opt` based on the specified low and high + /// indices, appends it to the given buffer `buf`, and returns the count of newline + /// characters in the substring for accurate highlighting. If `line_opt` is `None`, a + /// newline character is appended to the buffer, and 0 is returned. /// /// ## Returns /// @@ -434,10 +434,6 @@ struct DiagCtxtInner { /// The delayed bugs and their error guarantees. delayed_bugs: Vec<(DelayedDiagInner, ErrorGuaranteed)>, - /// The number of stashed errors. Unlike the other counts, this can go up - /// and down, so it doesn't guarantee anything. - stashed_err_count: usize, - /// The error count shown to the user at the end. deduplicated_err_count: usize, /// The warning count shown to the user at the end. @@ -475,7 +471,7 @@ struct DiagCtxtInner { /// add more information). All stashed diagnostics must be emitted with /// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped, /// otherwise an assertion failure will occur. - stashed_diagnostics: FxIndexMap<(Span, StashKey), DiagInner>, + stashed_diagnostics: FxIndexMap<(Span, StashKey), (DiagInner, Option<ErrorGuaranteed>)>, future_breakage_diagnostics: Vec<DiagInner>, @@ -486,8 +482,8 @@ struct DiagCtxtInner { /// have been converted. check_unstable_expect_diagnostics: bool, - /// Expected [`DiagInner`][struct@diagnostic::DiagInner]s store a [`LintExpectationId`] as part of - /// the lint level. [`LintExpectationId`]s created early during the compilation + /// Expected [`DiagInner`][struct@diagnostic::DiagInner]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 [`DiagInner`][struct@diagnostic::DiagInner]s @@ -559,10 +555,18 @@ pub struct DiagCtxtFlags { impl Drop for DiagCtxtInner { fn drop(&mut self) { - // Any stashed diagnostics should have been handled by - // `emit_stashed_diagnostics` by now. - assert!(self.stashed_diagnostics.is_empty()); + // For tools using `interface::run_compiler` (e.g. rustc, rustdoc) + // stashed diagnostics will have already been emitted. But for others + // that don't use `interface::run_compiler` (e.g. rustfmt, some clippy + // lints) this fallback is necessary. + // + // Important: it is sound to produce an `ErrorGuaranteed` when stashing + // errors because they are guaranteed to be emitted here or earlier. + self.emit_stashed_diagnostics(); + // Important: it is sound to produce an `ErrorGuaranteed` when emitting + // delayed bugs because they are guaranteed to be emitted here if + // necessary. if self.err_guars.is_empty() { self.flush_delayed() } @@ -586,13 +590,6 @@ impl Drop for DiagCtxtInner { } impl DiagCtxt { - pub fn with_tty_emitter( - sm: Option<Lrc<SourceMap>>, - fallback_bundle: LazyFallbackBundle, - ) -> Self { - let emitter = Box::new(HumanEmitter::stderr(ColorConfig::Auto, fallback_bundle).sm(sm)); - Self::with_emitter(emitter) - } pub fn disable_warnings(mut self) -> Self { self.inner.get_mut().flags.can_emit_warnings = false; self @@ -608,14 +605,13 @@ impl DiagCtxt { self } - pub fn with_emitter(emitter: Box<DynEmitter>) -> Self { + pub fn new(emitter: Box<DynEmitter>) -> Self { Self { inner: Lock::new(DiagCtxtInner { flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() }, err_guars: Vec::new(), lint_err_guars: Vec::new(), delayed_bugs: Vec::new(), - stashed_err_count: 0, deduplicated_err_count: 0, deduplicated_warn_count: 0, emitter, @@ -676,7 +672,6 @@ impl DiagCtxt { err_guars, lint_err_guars, delayed_bugs, - stashed_err_count, deduplicated_err_count, deduplicated_warn_count, emitter: _, @@ -699,7 +694,6 @@ impl DiagCtxt { *err_guars = Default::default(); *lint_err_guars = Default::default(); *delayed_bugs = Default::default(); - *stashed_err_count = 0; *deduplicated_err_count = 0; *deduplicated_warn_count = 0; *must_produce_diag = false; @@ -715,39 +709,111 @@ impl DiagCtxt { *fulfilled_expectations = Default::default(); } - /// Stash a given diagnostic with the given `Span` and [`StashKey`] as the key. - /// Retrieve a stashed diagnostic with `steal_diagnostic`. - pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: DiagInner) { - let mut inner = self.inner.borrow_mut(); - - let key = (span.with_parent(None), key); - - if diag.is_error() { - if diag.is_lint.is_none() { - inner.stashed_err_count += 1; - } - } + /// Stashes a diagnostic for possible later improvement in a different, + /// later stage of the compiler. Possible actions depend on the diagnostic + /// level: + /// - Level::Error: immediately counted as an error that has occurred, because it + /// is guaranteed to be emitted eventually. Can be later accessed with the + /// provided `span` and `key` through + /// [`DiagCtxt::try_steal_modify_and_emit_err`] or + /// [`DiagCtxt::try_steal_replace_and_emit_err`]. These do not allow + /// cancellation or downgrading of the error. Returns + /// `Some(ErrorGuaranteed)`. + /// - Level::Warning and lower (i.e. !is_error()): can be accessed with the + /// provided `span` and `key` through [`DiagCtxt::steal_non_err()`]. This + /// allows cancelling and downgrading of the diagnostic. Returns `None`. + /// - Others: not allowed, will trigger a panic. + pub fn stash_diagnostic( + &self, + span: Span, + key: StashKey, + diag: DiagInner, + ) -> Option<ErrorGuaranteed> { + let guar = if diag.level() == Level::Error { + // This `unchecked_error_guaranteed` is valid. It is where the + // `ErrorGuaranteed` for stashed errors originates. See + // `DiagCtxtInner::drop`. + #[allow(deprecated)] + Some(ErrorGuaranteed::unchecked_error_guaranteed()) + } else if !diag.is_error() { + None + } else { + self.span_bug(span, format!("invalid level in `stash_diagnostic`: {}", diag.level)); + }; // 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(key, diag); + let key = (span.with_parent(None), key); + self.inner.borrow_mut().stashed_diagnostics.insert(key, (diag, guar)); + + guar } - /// Steal a previously stashed diagnostic with the given `Span` and [`StashKey`] as the key. - pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<Diag<'_, ()>> { - let mut inner = self.inner.borrow_mut(); + /// Steal a previously stashed non-error diagnostic with the given `Span` + /// and [`StashKey`] as the key. Panics if the found diagnostic is an + /// error. + pub fn steal_non_err(&self, span: Span, key: StashKey) -> Option<Diag<'_, ()>> { let key = (span.with_parent(None), key); // FIXME(#120456) - is `swap_remove` correct? - let diag = inner.stashed_diagnostics.swap_remove(&key)?; - if diag.is_error() { - if diag.is_lint.is_none() { - inner.stashed_err_count -= 1; - } - } + let (diag, guar) = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key)?; + assert!(!diag.is_error()); + assert!(guar.is_none()); Some(Diag::new_diagnostic(self, diag)) } + /// Steals a previously stashed error with the given `Span` and + /// [`StashKey`] as the key, modifies it, and emits it. Returns `None` if + /// no matching diagnostic is found. Panics if the found diagnostic's level + /// isn't `Level::Error`. + 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 key = (span.with_parent(None), key); + // FIXME(#120456) - is `swap_remove` correct? + let err = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key); + err.map(|(err, guar)| { + // The use of `::<ErrorGuaranteed>` is safe because level is `Level::Error`. + assert_eq!(err.level, Level::Error); + assert!(guar.is_some()); + let mut err = Diag::<ErrorGuaranteed>::new_diagnostic(self, err); + modify_err(&mut err); + assert_eq!(err.level, Level::Error); + err.emit() + }) + } + + /// Steals a previously stashed error with the given `Span` and + /// [`StashKey`] as the key, cancels it if found, and emits `new_err`. + /// Panics if the found diagnostic's level isn't `Level::Error`. + pub fn try_steal_replace_and_emit_err( + &self, + span: Span, + key: StashKey, + new_err: Diag<'_>, + ) -> ErrorGuaranteed { + let key = (span.with_parent(None), key); + // FIXME(#120456) - is `swap_remove` correct? + let old_err = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key); + match old_err { + Some((old_err, guar)) => { + assert_eq!(old_err.level, Level::Error); + assert!(guar.is_some()); + // Because `old_err` has already been counted, it can only be + // safely cancelled because the `new_err` supplants it. + Diag::<ErrorGuaranteed>::new_diagnostic(self, old_err).cancel(); + } + None => {} + }; + new_err.emit() + } + pub fn has_stashed_diagnostic(&self, span: Span, key: StashKey) -> bool { self.inner.borrow().stashed_diagnostics.get(&(span.with_parent(None), key)).is_some() } @@ -757,41 +823,40 @@ impl DiagCtxt { self.inner.borrow_mut().emit_stashed_diagnostics() } - /// This excludes lint errors, delayed bugs and stashed errors. + /// This excludes lint errors, and delayed bugs. #[inline] pub fn err_count_excluding_lint_errs(&self) -> usize { - self.inner.borrow().err_guars.len() + let inner = self.inner.borrow(); + inner.err_guars.len() + + inner + .stashed_diagnostics + .values() + .filter(|(diag, guar)| guar.is_some() && diag.is_lint.is_none()) + .count() } - /// This excludes delayed bugs and stashed errors. + /// This excludes delayed bugs. #[inline] pub fn err_count(&self) -> usize { let inner = self.inner.borrow(); - inner.err_guars.len() + inner.lint_err_guars.len() - } - - /// This excludes normal errors, lint errors, and delayed bugs. Unless - /// absolutely necessary, avoid using this. It's dubious because stashed - /// errors can later be cancelled, so the presence of a stashed error at - /// some point of time doesn't guarantee anything -- there are no - /// `ErrorGuaranteed`s here. - pub fn stashed_err_count(&self) -> usize { - self.inner.borrow().stashed_err_count + inner.err_guars.len() + + inner.lint_err_guars.len() + + inner.stashed_diagnostics.values().filter(|(_diag, guar)| guar.is_some()).count() } - /// This excludes lint errors, delayed bugs, and stashed errors. Unless - /// absolutely necessary, prefer `has_errors` to this method. + /// This excludes lint errors and delayed bugs. Unless absolutely + /// necessary, prefer `has_errors` to this method. pub fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> { self.inner.borrow().has_errors_excluding_lint_errors() } - /// This excludes delayed bugs and stashed errors. + /// This excludes delayed bugs. pub fn has_errors(&self) -> Option<ErrorGuaranteed> { self.inner.borrow().has_errors() } - /// This excludes stashed errors. Unless absolutely necessary, prefer - /// `has_errors` to this method. + /// This excludes nothing. Unless absolutely necessary, prefer `has_errors` + /// to this method. pub fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> { self.inner.borrow().has_errors_or_delayed_bugs() } @@ -876,10 +941,10 @@ impl DiagCtxt { } } - /// This excludes delayed bugs and stashed errors. Used for early aborts - /// after errors occurred -- e.g. because continuing in the face of errors is - /// likely to lead to bad results, such as spurious/uninteresting - /// additional errors -- when returning an error `Result` is difficult. + /// This excludes delayed bugs. Used for early aborts after errors occurred + /// -- e.g. because continuing in the face of errors is likely to lead to + /// bad results, such as spurious/uninteresting additional errors -- when + /// returning an error `Result` is difficult. pub fn abort_if_errors(&self) { if self.has_errors().is_some() { FatalError.raise(); @@ -963,7 +1028,7 @@ impl DiagCtxt { inner .stashed_diagnostics .values_mut() - .for_each(|diag| diag.update_unstable_expectation_id(unstable_to_stable)); + .for_each(|(diag, _guar)| diag.update_unstable_expectation_id(unstable_to_stable)); inner .future_breakage_diagnostics .iter_mut() @@ -1270,12 +1335,8 @@ impl DiagCtxtInner { fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> { let mut guar = None; let has_errors = !self.err_guars.is_empty(); - for (_, diag) in std::mem::take(&mut self.stashed_diagnostics).into_iter() { - if diag.is_error() { - if diag.is_lint.is_none() { - self.stashed_err_count -= 1; - } - } else { + for (_, (diag, _guar)) in std::mem::take(&mut self.stashed_diagnostics).into_iter() { + if !diag.is_error() { // Unless they're forced, don't flush stashed warnings when // there are errors, to avoid causing warning overload. The // stash would've been stolen already if it were important. @@ -1334,7 +1395,8 @@ impl DiagCtxtInner { } else { let backtrace = std::backtrace::Backtrace::capture(); // This `unchecked_error_guaranteed` is valid. It is where the - // `ErrorGuaranteed` for delayed bugs originates. + // `ErrorGuaranteed` for delayed bugs originates. See + // `DiagCtxtInner::drop`. #[allow(deprecated)] let guar = ErrorGuaranteed::unchecked_error_guaranteed(); self.delayed_bugs @@ -1446,11 +1508,31 @@ impl DiagCtxtInner { } fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> { - self.err_guars.get(0).copied() + self.err_guars.get(0).copied().or_else(|| { + if let Some((_diag, guar)) = self + .stashed_diagnostics + .values() + .find(|(diag, guar)| guar.is_some() && diag.is_lint.is_none()) + { + *guar + } else { + None + } + }) } fn has_errors(&self) -> Option<ErrorGuaranteed> { - self.has_errors_excluding_lint_errors().or_else(|| self.lint_err_guars.get(0).copied()) + self.err_guars.get(0).copied().or_else(|| self.lint_err_guars.get(0).copied()).or_else( + || { + if let Some((_diag, guar)) = + self.stashed_diagnostics.values().find(|(_diag, guar)| guar.is_some()) + { + *guar + } else { + None + } + }, + ) } fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> { |
