Rollup merge of #121669 - nnethercote:count-stashed-errs-again, r=estebank

Count stashed errors again

Stashed diagnostics are such a pain. Their "might be emitted, might not" semantics messes with lots of things.

#120828 and #121206 made some big changes to how they work, improving some things, but still leaving some problems, as seen by the issues caused by #121206. This PR aims to fix all of them by restricting them in a way that eliminates the "might be emitted, might not" semantics while still allowing 98% of their benefit. Details in the individual commit logs.

r? `@oli-obk`
This commit is contained in:
Guillaume Gomez 2024-02-29 17:08:38 +01:00 committed by GitHub
commit a5945b5d8d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 476 additions and 343 deletions

View file

@ -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.

View file

@ -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>,
@ -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()
}
@ -615,7 +619,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 +679,6 @@ impl DiagCtxt {
err_guars,
lint_err_guars,
delayed_bugs,
stashed_err_count,
deduplicated_err_count,
deduplicated_warn_count,
emitter: _,
@ -699,7 +701,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 +716,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 +830,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<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 +948,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 +1035,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 +1342,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 +1402,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 +1515,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> {

View file

@ -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| {

View file

@ -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;
}

View file

@ -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) {

View file

@ -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>,

View file

@ -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();
}
}

View file

@ -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
})
@ -879,17 +880,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
// emit or cancel the diagnostic for bare traits
if span.edition().at_least_rust_2021()
&& let Some(diag) =
self.dcx().steal_diagnostic(qself.span, StashKey::TraitMissingMethod)
{
if trait_missing_method {
// cancel the diag for bare traits when meeting `MyTrait::missing_method`
diag.cancel();
} else {
diag.emit();
}
// 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() {
self.dcx().try_steal_modify_and_emit_err(
qself.span,
StashKey::TraitMissingMethod,
|_err| {},
);
}
if item_name.name != kw::Empty {

View file

@ -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();
});
}
}

View file

@ -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,
}
// 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);
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,
}
}
}
let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name };
visitor.visit_body(body);
// 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);
}
}
}
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();
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();
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()`

View file

@ -772,10 +772,6 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) -> 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()

View file

@ -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,

View file

@ -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
}

View file

@ -70,15 +70,11 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
// Check if the method call actually calls the libcore
// `IntoIterator::into_iter`.
let trait_id = cx
.typeck_results()
.type_dependent_def_id(expr.hir_id)
.and_then(|did| cx.tcx.trait_of_item(did));
if trait_id.is_none()
|| !cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_id.unwrap())
{
return;
}
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
match cx.tcx.trait_of_item(def_id) {
Some(trait_id) if cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_id) => {}
_ => return,
};
// As this is a method call expression, we have at least one argument.
let receiver_ty = cx.typeck_results().expr_ty(receiver_arg);

View file

@ -847,13 +847,15 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
opaque_def_id: LocalDefId,
tcx: TyCtxt<'tcx>,
) -> Result<Diag<'tcx>, ErrorGuaranteed> {
if let Some(diag) = tcx
.sess
.dcx()
.steal_diagnostic(tcx.def_span(opaque_def_id), StashKey::OpaqueHiddenTypeMismatch)
{
diag.cancel();
}
// 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 {

View file

@ -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

View file

@ -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.

View file

@ -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)
}

View file

@ -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();
}
}
}
}

View file

@ -988,10 +988,7 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
if let hir::ExprKind::Struct(qpath, fields, ref base) = expr.kind {
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
let Some(adt) = self.typeck_results().expr_ty(expr).ty_adt_def() else {
self.tcx.dcx().span_delayed_bug(expr.span, "no adt_def for expression");
return;
};
let adt = self.typeck_results().expr_ty(expr).ty_adt_def().unwrap();
let variant = adt.variant_of_res(res);
if let Some(base) = *base {
// If the expression uses FRU we need to make sure all the unmentioned fields

View file

@ -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()
};

View file

@ -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()
}
}

View file

@ -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(

View file

@ -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] {
[..] => { }
}
}

View file

@ -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`.

View file

@ -68,7 +68,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
queries: &'tcx rustc_interface::Queries<'tcx>,
) -> Compilation {
queries.global_ctxt().unwrap().enter(|tcx| {
if tcx.sess.dcx().has_errors().is_some() {
if tcx.sess.dcx().has_errors_or_delayed_bugs().is_some() {
tcx.dcx().fatal("miri cannot be run on programs that fail compilation");
}

View file

@ -109,7 +109,7 @@ fn format_project<T: FormatHandler>(
let main_file = input.file_name();
let input_is_stdin = main_file == FileName::Stdin;
let mut parse_session = ParseSess::new(config)?;
let parse_session = ParseSess::new(config)?;
if config.skip_children() && parse_session.ignore_file(&main_file) {
return Ok(FormatReport::new());
}
@ -118,20 +118,11 @@ fn format_project<T: FormatHandler>(
let mut report = FormatReport::new();
let directory_ownership = input.to_directory_ownership();
// rustfmt doesn't use `run_compiler` like other tools, so it must emit any
// stashed diagnostics itself, otherwise the `DiagCtxt` will assert when
// dropped. The final result here combines the parsing result and the
// `emit_stashed_diagnostics` result.
let parse_res = Parser::parse_crate(input, &parse_session);
let stashed_res = parse_session.emit_stashed_diagnostics();
let krate = match (parse_res, stashed_res) {
(Ok(krate), None) => krate,
(parse_res, _) => {
// Surface parse error via Session (errors are merged there from report).
let forbid_verbose = match parse_res {
Err(e) if e != ParserError::ParsePanicError => true,
_ => input_is_stdin,
};
let krate = match Parser::parse_crate(input, &parse_session) {
Ok(krate) => krate,
// Surface parse error via Session (errors are merged there from report)
Err(e) => {
let forbid_verbose = input_is_stdin || e != ParserError::ParsePanicError;
should_emit_verbose(forbid_verbose, config, || {
eprintln!("The Rust parser panicked");
});

View file

@ -5,9 +5,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use rustc_data_structures::sync::{IntoDynSyncSend, Lrc};
use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter};
use rustc_errors::translation::Translate;
use rustc_errors::{
ColorConfig, Diag, DiagCtxt, DiagInner, ErrorGuaranteed, Level as DiagnosticLevel,
};
use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel};
use rustc_session::parse::ParseSess as RawParseSess;
use rustc_span::{
source_map::{FilePathMapping, SourceMap},
@ -230,10 +228,6 @@ impl ParseSess {
self.ignore_path_set.as_ref().is_match(path)
}
pub(crate) fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
self.parse_sess.dcx.emit_stashed_diagnostics()
}
pub(crate) fn set_silent_emitter(&mut self) {
self.parse_sess.dcx = DiagCtxt::with_emitter(silent_emitter());
}

View file

@ -0,0 +1,4 @@
extern "C" fn _f() -> libc::uintptr_t {}
//~^ ERROR failed to resolve: use of undeclared crate or module `libc`
fn main() {}

View file

@ -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`.

View file

@ -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() {}

View file

@ -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`.

View file

@ -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() {}

View file

@ -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

View file

@ -7,5 +7,7 @@ trait Has {
trait HasNot {}
fn main() {
HasNot::has(); //~ ERROR
HasNot::has();
//~^ ERROR trait objects must include the `dyn` keyword
//~| ERROR no function or associated item named `has` found for trait `HasNot`
}

View file

@ -1,3 +1,14 @@
error[E0782]: trait objects must include the `dyn` keyword
--> $DIR/issue-111312.rs:10:5
|
LL | HasNot::has();
| ^^^^^^
|
help: add `dyn` keyword before this trait
|
LL | <dyn HasNot>::has();
| ++++ +
error[E0599]: no function or associated item named `has` found for trait `HasNot`
--> $DIR/issue-111312.rs:10:13
|
@ -10,6 +21,7 @@ note: `Has` defines an item `has`
LL | trait Has {
| ^^^^^^^^^
error: aborting due to 1 previous error
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0599`.
Some errors have detailed explanations: E0599, E0782.
For more information about an error, try `rustc --explain E0599`.

View file

@ -1,5 +1,7 @@
//@ edition: 2021
fn main() {
std::any::Any::create(); //~ ERROR
std::any::Any::create();
//~^ ERROR trait objects must include the `dyn` keyword
//~| ERROR no function or associated item named `create` found for trait `Any`
}

View file

@ -1,9 +1,21 @@
error[E0782]: trait objects must include the `dyn` keyword
--> $DIR/issue-111727.rs:4:5
|
LL | std::any::Any::create();
| ^^^^^^^^^^^^^
|
help: add `dyn` keyword before this trait
|
LL | <dyn std::any::Any>::create();
| ++++ +
error[E0599]: no function or associated item named `create` found for trait `Any`
--> $DIR/issue-111727.rs:4:20
|
LL | std::any::Any::create();
| ^^^^^^ function or associated item not found in `Any`
error: aborting due to 1 previous error
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0599`.
Some errors have detailed explanations: E0599, E0782.
For more information about an error, try `rustc --explain E0599`.

View file

@ -11,6 +11,7 @@ fn foo<'a, 'b>() -> Tait<'a> {
}
let x: Tait<'a> = ();
x
//~^ ERROR concrete type differs from previous defining opaque type use
}
fn main() {}

View file

@ -1,3 +1,15 @@
error: concrete type differs from previous defining opaque type use
--> $DIR/different_defining_uses_never_type-2.rs:13:5
|
LL | x
| ^ expected `i32`, got `()`
|
note: previous use here
--> $DIR/different_defining_uses_never_type-2.rs:8:31
|
LL | let y: Tait<'b> = 1i32;
| ^^^^
error: concrete type differs from previous defining opaque type use
--> $DIR/different_defining_uses_never_type-2.rs:8:31
|
@ -10,5 +22,5 @@ note: previous use here
LL | if { return } {
| ^^^^^^
error: aborting due to 1 previous error
error: aborting due to 2 previous errors