From 260ae701405f1278202de219bcdd0d60e8060da9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 26 Feb 2024 15:21:01 +1100 Subject: [PATCH] Overhaul how stashed diagnostics work, again. Stashed errors used to be counted as errors, but could then be cancelled, leading to `ErrorGuaranteed` soundness holes. #120828 changed that, closing the soundness hole. But it introduced other difficulties because you sometimes have to account for pending stashed errors when making decisions about whether errors have occured/will occur and it's easy to overlook these. This commit aims for a middle ground. - Stashed errors (not warnings) are counted immediately as emitted errors, avoiding the possibility of forgetting to consider them. - The ability to cancel (or downgrade) stashed errors is eliminated, by disallowing the use of `steal_diagnostic` with errors, and introducing the more restrictive methods `try_steal_{modify,replace}_and_emit_err` that can be used instead. Other things: - `DiagnosticBuilder::stash` and `DiagCtxt::stash_diagnostic` now both return `Option`, which enables the removal of two `delayed_bug` calls and one `Ty::new_error_with_message` call. This is possible because we store error guarantees in `DiagCtxt::stashed_diagnostics`. - Storing the guarantees also saves us having to maintain a counter. - Calls to the `stashed_err_count` method are no longer necessary alongside calls to `has_errors`, which is a nice simplification, and eliminates two more `span_delayed_bug` calls and one FIXME comment. - Tests are added for three of the four fixed PRs mentioned below. - `issue-121108.rs`'s output improved slightly, omitting a non-useful error message. Fixes #121451. Fixes #121477. Fixes #121504. Fixes #121508. --- compiler/rustc_errors/src/diagnostic.rs | 8 +- compiler/rustc_errors/src/lib.rs | 211 +++++++++++++----- .../rustc_hir_analysis/src/astconv/lint.rs | 12 +- .../rustc_hir_analysis/src/check/check.rs | 3 +- .../rustc_hir_analysis/src/collect/type_of.rs | 22 +- compiler/rustc_hir_typeck/src/callee.rs | 17 +- compiler/rustc_hir_typeck/src/expr.rs | 45 ++-- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 22 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 76 +++---- .../rustc_hir_typeck/src/method/suggest.rs | 98 ++++---- compiler/rustc_hir_typeck/src/writeback.rs | 4 - compiler/rustc_infer/src/infer/at.rs | 1 - compiler/rustc_infer/src/infer/mod.rs | 15 -- compiler/rustc_middle/src/ty/mod.rs | 20 +- compiler/rustc_middle/src/ty/region.rs | 4 +- compiler/rustc_middle/src/ty/sty.rs | 4 +- compiler/rustc_parse/src/parser/expr.rs | 29 +-- compiler/rustc_passes/src/check_attr.rs | 20 +- .../rustc_query_system/src/query/plumbing.rs | 3 +- compiler/rustc_session/src/parse.rs | 3 +- .../error_reporting/type_err_ctxt_ext.rs | 18 +- .../ui/crashes/unreachable-array-or-slice.rs | 8 + .../crashes/unreachable-array-or-slice.stderr | 9 + tests/ui/foreign/stashed-issue-121451.rs | 4 + tests/ui/foreign/stashed-issue-121451.stderr | 9 + .../impl-trait/stashed-diag-issue-121504.rs | 13 ++ .../stashed-diag-issue-121504.stderr | 9 + tests/ui/lowering/issue-121108.rs | 2 +- tests/ui/lowering/issue-121108.stderr | 10 +- 29 files changed, 405 insertions(+), 294 deletions(-) create mode 100644 src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.rs create mode 100644 src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.stderr create mode 100644 tests/ui/foreign/stashed-issue-121451.rs create mode 100644 tests/ui/foreign/stashed-issue-121451.stderr create mode 100644 tests/ui/impl-trait/stashed-diag-issue-121504.rs create mode 100644 tests/ui/impl-trait/stashed-diag-issue-121504.stderr diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 01f36ad6a7866..207c616cefc02 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1289,11 +1289,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 { + self.dcx.stash_diagnostic(span, key, self.take_diag()) } /// Delay emission of this diagnostic as a bug. diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index bc338b01d8bb6..60bda9f4c9495 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -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)>, future_breakage_diagnostics: Vec, @@ -561,8 +557,14 @@ impl Drop for DiagCtxtInner { fn drop(&mut self) { // Any stashed diagnostics should have been handled by // `emit_stashed_diagnostics` by now. + // + // Important: it is sound to produce an `ErrorGuaranteed` when stashing + // errors because they are guaranteed to have been emitted by here. assert!(self.stashed_diagnostics.is_empty()); + // 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() } @@ -615,7 +617,6 @@ impl DiagCtxt { 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 +677,6 @@ impl DiagCtxt { err_guars, lint_err_guars, delayed_bugs, - stashed_err_count, deduplicated_err_count, deduplicated_warn_count, emitter: _, @@ -699,7 +699,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 +714,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 { + 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> { - 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> { 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( + &self, + span: Span, + key: StashKey, + mut modify_err: F, + ) -> Option + 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 `::` is safe because level is `Level::Error`. + assert_eq!(err.level, Level::Error); + assert!(guar.is_some()); + let mut err = Diag::::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::::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 +828,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() + inner.err_guars.len() + + inner.lint_err_guars.len() + + inner.stashed_diagnostics.values().filter(|(_diag, guar)| guar.is_some()).count() } - /// 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 - } - - /// 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 { 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 { 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 { self.inner.borrow().has_errors_or_delayed_bugs() } @@ -876,10 +946,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 +1033,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 +1340,8 @@ impl DiagCtxtInner { fn emit_stashed_diagnostics(&mut self) -> Option { 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 +1400,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 +1513,31 @@ impl DiagCtxtInner { } fn has_errors_excluding_lint_errors(&self) -> Option { - 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 { - 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 { diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs index fb5f3426cea6a..227254b4cc8b3 100644 --- a/compiler/rustc_hir_analysis/src/astconv/lint.rs +++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs @@ -1,5 +1,5 @@ use rustc_ast::TraitObjectSyntax; -use rustc_errors::{codes::*, Diag, EmissionGuarantee, StashKey}; +use rustc_errors::{codes::*, Diag, EmissionGuarantee, Level, StashKey}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability}; @@ -237,7 +237,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } // check if the impl trait that we are considering is a impl of a local trait self.maybe_lint_blanket_trait_impl(self_ty, &mut diag); - diag.stash(self_ty.span, StashKey::TraitMissingMethod); + match diag.level() { + Level::Error => { + diag.stash(self_ty.span, StashKey::TraitMissingMethod); + } + Level::DelayedBug => { + diag.emit(); + } + _ => unreachable!(), + } } else { let msg = "trait objects without an explicit `dyn` are deprecated"; tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, msg, |lint| { diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 81e84860b8227..96bebda582815 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1350,8 +1350,7 @@ fn check_type_alias_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalD let ty = tcx.type_of(def_id).instantiate_identity(); if ty.references_error() { // If there is already another error, do not emit an error for not using a type parameter. - // Without the `stashed_err_count` part this can fail (#120856). - assert!(tcx.dcx().has_errors().is_some() || tcx.dcx().stashed_err_count() > 0); + assert!(tcx.dcx().has_errors().is_some()); return; } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index c0128afe2bf6c..417f0fceaa81f 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -596,10 +596,11 @@ fn infer_placeholder_type<'a>( // then the user may have written e.g. `const A = 42;`. // In this case, the parser has stashed a diagnostic for // us to improve in typeck so we do that now. - match tcx.dcx().steal_diagnostic(span, StashKey::ItemNoType) { - Some(mut err) => { + let guar = tcx + .dcx() + .try_steal_modify_and_emit_err(span, StashKey::ItemNoType, |err| { if !ty.references_error() { - // Only suggest adding `:` if it was missing (and suggested by parsing diagnostic) + // Only suggest adding `:` if it was missing (and suggested by parsing diagnostic). let colon = if span == item_ident.span.shrink_to_hi() { ":" } else { "" }; // The parser provided a sub-optimal `HasPlaceholders` suggestion for the type. @@ -622,12 +623,8 @@ fn infer_placeholder_type<'a>( )); } } - - err.emit(); - // diagnostic stashing loses the information of whether something is a hard error. - Ty::new_error_with_message(tcx, span, "ItemNoType is a hard error") - } - None => { + }) + .unwrap_or_else(|| { let mut diag = bad_placeholder(tcx, vec![span], kind); if !ty.references_error() { @@ -645,10 +642,9 @@ fn infer_placeholder_type<'a>( )); } } - - Ty::new_error(tcx, diag.emit()) - } - } + diag.emit() + }); + Ty::new_error(tcx, guar) } fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) { diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index bb6d1ecae02ab..b87d71e353389 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -484,12 +484,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind && let [segment] = path.segments - && let Some(mut diag) = - self.dcx().steal_diagnostic(segment.ident.span, StashKey::CallIntoMethod) { - // Try suggesting `foo(a)` -> `a.foo()` if possible. - self.suggest_call_as_method(&mut diag, segment, arg_exprs, call_expr, expected); - diag.emit(); + self.dcx().try_steal_modify_and_emit_err( + segment.ident.span, + StashKey::CallIntoMethod, + |err| { + // Try suggesting `foo(a)` -> `a.foo()` if possible. + self.suggest_call_as_method( + err, segment, arg_exprs, call_expr, expected, + ); + }, + ); } let err = self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs); @@ -601,7 +606,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// and suggesting the fix if the method probe is successful. fn suggest_call_as_method( &self, - diag: &mut Diag<'_, ()>, + diag: &mut Diag<'_>, segment: &'tcx hir::PathSegment<'tcx>, arg_exprs: &'tcx [hir::Expr<'tcx>], call_expr: &'tcx hir::Expr<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 2b7bce3f48575..054be89a7c4c5 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1460,18 +1460,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length { let span = self.tcx.hir().span(hir_id); - match self.dcx().steal_diagnostic(span, StashKey::UnderscoreForArrayLengths) { - Some(mut err) => { + self.dcx().try_steal_modify_and_emit_err( + span, + StashKey::UnderscoreForArrayLengths, + |err| { err.span_suggestion( span, "consider specifying the array length", array_len, Applicability::MaybeIncorrect, ); - err.emit(); - } - None => (), - } + }, + ); } } @@ -1751,11 +1751,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (_, diag) = self.demand_coerce_diag(field.expr, ty, field_type, None, AllowTwoPhase::No); - if let Some(mut diag) = diag { + if let Some(diag) = diag { if idx == ast_fields.len() - 1 { if remaining_fields.is_empty() { - self.suggest_fru_from_range(field, variant, args, &mut diag); - diag.emit(); + self.suggest_fru_from_range_and_emit(field, variant, args, diag); } else { diag.stash(field.span, StashKey::MaybeFruTypo); } @@ -1986,20 +1985,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}")); if let Some(last) = ast_fields.last() { - self.suggest_fru_from_range(last, variant, args, &mut err); + self.suggest_fru_from_range_and_emit(last, variant, args, err); + } else { + err.emit(); } - - err.emit(); } /// If the last field is a range literal, but it isn't supposed to be, then they probably /// meant to use functional update syntax. - fn suggest_fru_from_range( + fn suggest_fru_from_range_and_emit( &self, last_expr_field: &hir::ExprField<'tcx>, variant: &ty::VariantDef, args: GenericArgsRef<'tcx>, - err: &mut Diag<'_>, + mut err: Diag<'_>, ) { // I don't use 'is_range_literal' because only double-sided, half-open ranges count. if let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [range_start, range_end], _) = @@ -2012,16 +2011,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|adt| adt.did()) != range_def_id { - // Suppress any range expr type mismatches - if let Some(diag) = - self.dcx().steal_diagnostic(last_expr_field.span, StashKey::MaybeFruTypo) - { - diag.delay_as_bug(); - } - // Use a (somewhat arbitrary) filtering heuristic to avoid printing // expressions that are either too long, or have control character - //such as newlines in them. + // such as newlines in them. let expr = self .tcx .sess @@ -2043,6 +2035,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.dcx(), TypeMismatchFruTypo { expr_span: range_start.span, fru_span, expr }, ); + + // Suppress any range expr type mismatches + self.dcx().try_steal_replace_and_emit_err( + last_expr_field.span, + StashKey::MaybeFruTypo, + err, + ); + } else { + err.emit(); } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 1e6467deb011e..dd44fdd889328 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -848,11 +848,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id) .map(|r| { // lint bare trait if the method is found in the trait - if span.edition().at_least_rust_2021() - && let Some(diag) = - self.dcx().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) - { - diag.emit(); + if span.edition().at_least_rust_2021() { + self.dcx().try_steal_modify_and_emit_err( + qself.span, + StashKey::TraitMissingMethod, + |_err| {}, + ); } r }) @@ -882,11 +883,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Emit the diagnostic for bare traits. (We used to cancel for slightly better // error messages, but cancelling stashed diagnostics is no longer allowed because // it causes problems when tracking whether errors have actually occurred.) - if span.edition().at_least_rust_2021() - && let Some(diag) = - self.dcx().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) - { - diag.emit(); + if span.edition().at_least_rust_2021() { + self.dcx().try_steal_modify_and_emit_err( + qself.span, + StashKey::TraitMissingMethod, + |_err| {}, + ); } if item_name.name != kw::Empty { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 61c52422d195d..1311cc8968af0 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1941,53 +1941,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { errors_causecode: Vec<(Span, ObligationCauseCode<'tcx>)>, ) { for (span, code) in errors_causecode { - let Some(mut diag) = self.dcx().steal_diagnostic(span, StashKey::MaybeForgetReturn) - else { - continue; - }; - - if let Some(fn_sig) = self.body_fn_sig() - && let ExprBindingObligation(_, _, hir_id, ..) = code - && !fn_sig.output().is_unit() - { - let mut block_num = 0; - let mut found_semi = false; - for (_, node) in self.tcx.hir().parent_iter(hir_id) { - match node { - hir::Node::Stmt(stmt) => { - if let hir::StmtKind::Semi(expr) = stmt.kind { - let expr_ty = self.typeck_results.borrow().expr_ty(expr); - let return_ty = fn_sig.output(); - if !matches!(expr.kind, hir::ExprKind::Ret(..)) - && self.can_coerce(expr_ty, return_ty) - { - found_semi = true; + self.dcx().try_steal_modify_and_emit_err(span, StashKey::MaybeForgetReturn, |err| { + if let Some(fn_sig) = self.body_fn_sig() + && let ExprBindingObligation(_, _, hir_id, ..) = code + && !fn_sig.output().is_unit() + { + let mut block_num = 0; + let mut found_semi = false; + for (_, node) in self.tcx.hir().parent_iter(hir_id) { + match node { + hir::Node::Stmt(stmt) => { + if let hir::StmtKind::Semi(expr) = stmt.kind { + let expr_ty = self.typeck_results.borrow().expr_ty(expr); + let return_ty = fn_sig.output(); + if !matches!(expr.kind, hir::ExprKind::Ret(..)) + && self.can_coerce(expr_ty, return_ty) + { + found_semi = true; + } } } - } - hir::Node::Block(_block) => { - if found_semi { - block_num += 1; + hir::Node::Block(_block) => { + if found_semi { + block_num += 1; + } } - } - hir::Node::Item(item) => { - if let hir::ItemKind::Fn(..) = item.kind { - break; + hir::Node::Item(item) => { + if let hir::ItemKind::Fn(..) = item.kind { + break; + } } + _ => {} } - _ => {} + } + if block_num > 1 && found_semi { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "you might have meant to return this to infer its type parameters", + "return ", + Applicability::MaybeIncorrect, + ); } } - if block_num > 1 && found_semi { - diag.span_suggestion_verbose( - span.shrink_to_lo(), - "you might have meant to return this to infer its type parameters", - "return ", - Applicability::MaybeIncorrect, - ); - } - } - diag.emit(); + }); } } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 28771ae40f538..e5b0d0ae0da3a 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2194,59 +2194,59 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let [seg1, seg2] = segs else { return; }; - let Some(mut diag) = - self.dcx().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod) - else { - return; - }; - - let map = self.infcx.tcx.hir(); - let body_id = self.tcx.hir().body_owned_by(self.body_id); - let body = map.body(body_id); - struct LetVisitor<'a> { - result: Option<&'a hir::Expr<'a>>, - ident_name: Symbol, - } + self.dcx().try_steal_modify_and_emit_err( + seg1.ident.span, + StashKey::CallAssocMethod, + |err| { + let map = self.infcx.tcx.hir(); + let body_id = self.tcx.hir().body_owned_by(self.body_id); + let body = map.body(body_id); + struct LetVisitor<'a> { + result: Option<&'a hir::Expr<'a>>, + ident_name: Symbol, + } - // FIXME: This really should be taking scoping, etc into account. - impl<'v> Visitor<'v> for LetVisitor<'v> { - fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) { - if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind - && let Binding(_, _, ident, ..) = pat.kind - && ident.name == self.ident_name - { - self.result = *init; - } else { - hir::intravisit::walk_stmt(self, ex); + // FIXME: This really should be taking scoping, etc into account. + impl<'v> Visitor<'v> for LetVisitor<'v> { + fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) { + if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind + && let Binding(_, _, ident, ..) = pat.kind + && ident.name == self.ident_name + { + self.result = *init; + } else { + hir::intravisit::walk_stmt(self, ex); + } + } } - } - } - let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name }; - visitor.visit_body(body); + let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name }; + visitor.visit_body(body); - if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id) - && let Some(expr) = visitor.result - && let Some(self_ty) = self.node_ty_opt(expr.hir_id) - { - let probe = self.lookup_probe_for_diagnostic( - seg2.ident, - self_ty, - call_expr, - ProbeScope::TraitsInScope, - None, - ); - if probe.is_ok() { - let sm = self.infcx.tcx.sess.source_map(); - diag.span_suggestion_verbose( - sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':').unwrap(), - "you may have meant to call an instance method", - ".", - Applicability::MaybeIncorrect, - ); - } - } - diag.emit(); + if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id) + && let Some(expr) = visitor.result + && let Some(self_ty) = self.node_ty_opt(expr.hir_id) + { + let probe = self.lookup_probe_for_diagnostic( + seg2.ident, + self_ty, + call_expr, + ProbeScope::TraitsInScope, + None, + ); + if probe.is_ok() { + let sm = self.infcx.tcx.sess.source_map(); + err.span_suggestion_verbose( + sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':') + .unwrap(), + "you may have meant to call an instance method", + ".", + Applicability::MaybeIncorrect, + ); + } + } + }, + ); } /// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()` diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index ed102a7fac0be..0740a3fd3e970 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -772,10 +772,6 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { fn report_error(&self, p: impl Into>) -> ErrorGuaranteed { if let Some(guar) = self.fcx.dcx().has_errors() { guar - } else if self.fcx.dcx().stashed_err_count() > 0 { - // Without this case we sometimes get uninteresting and extraneous - // "type annotations needed" errors. - self.fcx.dcx().delayed_bug("error in Resolver") } else { self.fcx .err_ctxt() diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index cc09a09468869..a086d89b93335 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -87,7 +87,6 @@ impl<'tcx> InferCtxt<'tcx> { reported_signature_mismatch: self.reported_signature_mismatch.clone(), tainted_by_errors: self.tainted_by_errors.clone(), err_count_on_creation: self.err_count_on_creation, - stashed_err_count_on_creation: self.stashed_err_count_on_creation, universe: self.universe.clone(), intercrate, next_trait_solver: self.next_trait_solver, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index d7e16488508b5..6f52ded355194 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -306,12 +306,6 @@ pub struct InferCtxt<'tcx> { // FIXME(matthewjasper) Merge into `tainted_by_errors` err_count_on_creation: usize, - /// Track how many errors were stashed when this infcx is created. - /// Used for the same purpose as `err_count_on_creation`, even - /// though it's weaker because the count can go up and down. - // FIXME(matthewjasper) Merge into `tainted_by_errors` - stashed_err_count_on_creation: usize, - /// What is the innermost universe we have created? Starts out as /// `UniverseIndex::root()` but grows from there as we enter /// universal quantifiers. @@ -717,7 +711,6 @@ impl<'tcx> InferCtxtBuilder<'tcx> { reported_signature_mismatch: Default::default(), tainted_by_errors: Cell::new(None), err_count_on_creation: tcx.dcx().err_count_excluding_lint_errs(), - stashed_err_count_on_creation: tcx.dcx().stashed_err_count(), universe: Cell::new(ty::UniverseIndex::ROOT), intercrate, next_trait_solver, @@ -1274,14 +1267,6 @@ impl<'tcx> InferCtxt<'tcx> { let guar = self.dcx().has_errors().unwrap(); self.set_tainted_by_errors(guar); Some(guar) - } else if self.dcx().stashed_err_count() > self.stashed_err_count_on_creation { - // Errors stashed since this infcx was made. Not entirely reliable - // because the count of stashed errors can go down. But without - // this case we get a moderate number of uninteresting and - // extraneous "type annotations needed" errors. - let guar = self.dcx().delayed_bug("tainted_by_errors: stashed bug awaiting emission"); - self.set_tainted_by_errors(guar); - Some(guar) } else { None } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 9089d992cd5a7..6fdb03c0babb5 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -847,17 +847,15 @@ impl<'tcx> OpaqueHiddenType<'tcx> { opaque_def_id: LocalDefId, tcx: TyCtxt<'tcx>, ) -> Result, ErrorGuaranteed> { - if let Some(diag) = tcx - .sess - .dcx() - .steal_diagnostic(tcx.def_span(opaque_def_id), StashKey::OpaqueHiddenTypeMismatch) - { - // We used to cancel here for slightly better error messages, but - // cancelling stashed diagnostics is no longer allowed because it - // causes problems when tracking whether errors have actually - // occurred. - diag.emit(); - } + // We used to cancel here for slightly better error messages, but + // cancelling stashed diagnostics is no longer allowed because it + // causes problems when tracking whether errors have actually + // occurred. + tcx.sess.dcx().try_steal_modify_and_emit_err( + tcx.def_span(opaque_def_id), + StashKey::OpaqueHiddenTypeMismatch, + |_err| {}, + ); (self.ty, other.ty).error_reported()?; // Found different concrete types for the opaque type. let sub_diag = if self.span == other.span { diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index b206727f0514e..51a4a9f411c02 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -91,8 +91,8 @@ impl<'tcx> Region<'tcx> { /// Constructs a `RegionKind::ReError` region. #[track_caller] - pub fn new_error(tcx: TyCtxt<'tcx>, reported: ErrorGuaranteed) -> Region<'tcx> { - tcx.intern_region(ty::ReError(reported)) + pub fn new_error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Region<'tcx> { + tcx.intern_region(ty::ReError(guar)) } /// Constructs a `RegionKind::ReError` region and registers a delayed bug to ensure it gets diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 27c78d18d1922..06be8191dc4dd 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1528,8 +1528,8 @@ impl<'tcx> Ty<'tcx> { } /// Constructs a `TyKind::Error` type with current `ErrorGuaranteed` - pub fn new_error(tcx: TyCtxt<'tcx>, reported: ErrorGuaranteed) -> Ty<'tcx> { - Ty::new(tcx, Error(reported)) + pub fn new_error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Ty<'tcx> { + Ty::new(tcx, Error(guar)) } /// Constructs a `TyKind::Error` type and registers a `span_delayed_bug` to ensure it gets used. diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f5a7bfd42ff42..316a9c4f8dfc2 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1762,24 +1762,25 @@ impl<'a> Parser<'a> { err: impl FnOnce(&Self) -> Diag<'a>, ) -> L { assert!(could_be_unclosed_char_literal(ident)); - if let Some(diag) = self.dcx().steal_diagnostic(ident.span, StashKey::LifetimeIsChar) { - diag.with_span_suggestion_verbose( - ident.span.shrink_to_hi(), - "add `'` to close the char literal", - "'", - Applicability::MaybeIncorrect, - ) - .emit(); - } else { - err(self) - .with_span_suggestion_verbose( + self.dcx() + .try_steal_modify_and_emit_err(ident.span, StashKey::LifetimeIsChar, |err| { + err.span_suggestion_verbose( ident.span.shrink_to_hi(), "add `'` to close the char literal", "'", Applicability::MaybeIncorrect, - ) - .emit(); - } + ); + }) + .unwrap_or_else(|| { + err(self) + .with_span_suggestion_verbose( + ident.span.shrink_to_hi(), + "add `'` to close the char literal", + "'", + Applicability::MaybeIncorrect, + ) + .emit() + }); let name = ident.without_first_quote().name; mk_lit_char(name, ident.span) } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index c12d35ec73c99..04c0cf7436e2b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2520,14 +2520,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { if attr.style == AttrStyle::Inner { for attr_to_check in ATTRS_TO_CHECK { if attr.has_name(*attr_to_check) { - if let AttrKind::Normal(ref p) = attr.kind - && let Some(diag) = tcx.dcx().steal_diagnostic( - p.item.path.span, - StashKey::UndeterminedMacroResolution, - ) - { - diag.cancel(); - } let item = tcx .hir() .items() @@ -2537,7 +2529,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { span: item.ident.span, kind: item.kind.descr(), }); - tcx.dcx().emit_err(errors::InvalidAttrAtCrateLevel { + let err = tcx.dcx().create_err(errors::InvalidAttrAtCrateLevel { span: attr.span, sugg_span: tcx .sess @@ -2553,6 +2545,16 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { name: *attr_to_check, item, }); + + if let AttrKind::Normal(ref p) = attr.kind { + tcx.dcx().try_steal_replace_and_emit_err( + p.item.path.span, + StashKey::UndeterminedMacroResolution, + err, + ); + } else { + err.emit(); + } } } } diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index e4c596b10b875..40517407ee6a1 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -149,8 +149,7 @@ where let guar = if let Some(root) = cycle_error.cycle.first() && let Some(span) = root.query.span { - error.stash(span, StashKey::Cycle); - qcx.dep_context().sess().dcx().span_delayed_bug(span, "delayed cycle error") + error.stash(span, StashKey::Cycle).unwrap() } else { error.emit() }; diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 752bb05f3d79d..1b79786c1b832 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -105,8 +105,7 @@ pub fn feature_err_issue( // Cancel an earlier warning for this same error, if it exists. if let Some(span) = span.primary_span() { - if let Some(err) = sess.parse_sess.dcx.steal_diagnostic(span, StashKey::EarlySyntaxWarning) - { + if let Some(err) = sess.parse_sess.dcx.steal_non_err(span, StashKey::EarlySyntaxWarning) { err.cancel() } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index dcbb63f00f78b..d9e49f1eb0ab4 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -889,7 +889,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => self.report_opaque_type_auto_trait_leakage( + SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => return self.report_opaque_type_auto_trait_leakage( &obligation, def_id, ), @@ -2252,8 +2252,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ErrorCode::E0282, false, ); - err.stash(span, StashKey::MaybeForgetReturn); - return self.dcx().delayed_bug("stashed error never reported"); + return err.stash(span, StashKey::MaybeForgetReturn).unwrap(); } Some(e) => return e, } @@ -2766,7 +2765,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.suggest_unsized_bound_if_applicable(err, obligation); if let Some(span) = err.span.primary_span() && let Some(mut diag) = - self.tcx.dcx().steal_diagnostic(span, StashKey::AssociatedTypeSuggestion) + self.tcx.dcx().steal_non_err(span, StashKey::AssociatedTypeSuggestion) && let Ok(ref mut s1) = err.suggestions && let Ok(ref mut s2) = diag.suggestions { @@ -3291,7 +3290,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, def_id: DefId, - ) -> Diag<'tcx> { + ) -> ErrorGuaranteed { let name = match self.tcx.opaque_type_origin(def_id.expect_local()) { hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => { "opaque type".to_string() @@ -3318,12 +3317,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } }; - if let Some(diag) = self.dcx().steal_diagnostic(self.tcx.def_span(def_id), StashKey::Cycle) - { - diag.cancel(); - } - - err + self.note_obligation_cause(&mut err, &obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); + self.dcx().try_steal_replace_and_emit_err(self.tcx.def_span(def_id), StashKey::Cycle, err) } fn report_signature_mismatch_error( diff --git a/src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.rs b/src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.rs new file mode 100644 index 0000000000000..b56abccbd4112 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.rs @@ -0,0 +1,8 @@ +struct Foo(isize, isize, isize, isize); + +pub fn main() { + let Self::anything_here_kills_it(a, b, ..) = Foo(5, 5, 5, 5); + match [5, 5, 5, 5] { + [..] => { } + } +} diff --git a/src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.stderr b/src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.stderr new file mode 100644 index 0000000000000..9e0d3b934b80f --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/unreachable-array-or-slice.stderr @@ -0,0 +1,9 @@ +error[E0433]: failed to resolve: `Self` is only available in impls, traits, and type definitions + --> tests/ui/crashes/unreachable-array-or-slice.rs:4:9 + | +LL | let Self::anything_here_kills_it(a, b, ..) = Foo(5, 5, 5, 5); + | ^^^^ `Self` is only available in impls, traits, and type definitions + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/foreign/stashed-issue-121451.rs b/tests/ui/foreign/stashed-issue-121451.rs new file mode 100644 index 0000000000000..97a4af374758d --- /dev/null +++ b/tests/ui/foreign/stashed-issue-121451.rs @@ -0,0 +1,4 @@ +extern "C" fn _f() -> libc::uintptr_t {} +//~^ ERROR failed to resolve: use of undeclared crate or module `libc` + +fn main() {} diff --git a/tests/ui/foreign/stashed-issue-121451.stderr b/tests/ui/foreign/stashed-issue-121451.stderr new file mode 100644 index 0000000000000..440d98d6f460d --- /dev/null +++ b/tests/ui/foreign/stashed-issue-121451.stderr @@ -0,0 +1,9 @@ +error[E0433]: failed to resolve: use of undeclared crate or module `libc` + --> $DIR/stashed-issue-121451.rs:1:23 + | +LL | extern "C" fn _f() -> libc::uintptr_t {} + | ^^^^ use of undeclared crate or module `libc` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/impl-trait/stashed-diag-issue-121504.rs b/tests/ui/impl-trait/stashed-diag-issue-121504.rs new file mode 100644 index 0000000000000..4ac8ffe8931c6 --- /dev/null +++ b/tests/ui/impl-trait/stashed-diag-issue-121504.rs @@ -0,0 +1,13 @@ +//@ edition: 2021 + +trait MyTrait { + async fn foo(self) -> (Self, i32); +} + +impl MyTrait for xyz::T { //~ ERROR failed to resolve: use of undeclared crate or module `xyz` + async fn foo(self, key: i32) -> (u32, i32) { + (self, key) + } +} + +fn main() {} diff --git a/tests/ui/impl-trait/stashed-diag-issue-121504.stderr b/tests/ui/impl-trait/stashed-diag-issue-121504.stderr new file mode 100644 index 0000000000000..6a881dc7f9fbd --- /dev/null +++ b/tests/ui/impl-trait/stashed-diag-issue-121504.stderr @@ -0,0 +1,9 @@ +error[E0433]: failed to resolve: use of undeclared crate or module `xyz` + --> $DIR/stashed-diag-issue-121504.rs:7:18 + | +LL | impl MyTrait for xyz::T { + | ^^^ use of undeclared crate or module `xyz` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/lowering/issue-121108.rs b/tests/ui/lowering/issue-121108.rs index 6b2dd24e4a842..6dfb7e0082149 100644 --- a/tests/ui/lowering/issue-121108.rs +++ b/tests/ui/lowering/issue-121108.rs @@ -3,7 +3,7 @@ use std::ptr::addr_of; const UNINHABITED_VARIANT: () = unsafe { - let v = *addr_of!(data).cast(); //~ ERROR cannot determine resolution for the macro `addr_of` + let v = *addr_of!(data).cast(); }; fn main() {} diff --git a/tests/ui/lowering/issue-121108.stderr b/tests/ui/lowering/issue-121108.stderr index c2c5746d6f142..e4942e8cb0796 100644 --- a/tests/ui/lowering/issue-121108.stderr +++ b/tests/ui/lowering/issue-121108.stderr @@ -13,13 +13,5 @@ LL - #![derive(Clone, Copy)] LL + #[derive(Clone, Copy)] | -error: cannot determine resolution for the macro `addr_of` - --> $DIR/issue-121108.rs:6:14 - | -LL | let v = *addr_of!(data).cast(); - | ^^^^^^^ - | - = note: import resolution is stuck, try simplifying macro imports - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error