summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_infer/src/infer/error_reporting/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_infer/src/infer/error_reporting/mod.rs')
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs747
1 files changed, 330 insertions, 417 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 72cfc1337..26d071a01 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -59,20 +59,19 @@ use crate::traits::{
};
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
+use rustc_errors::{error_code, Applicability, DiagnosticBuilder, DiagnosticStyledString};
use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg};
-use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem;
-use rustc_hir::Node;
use rustc_middle::dep_graph::DepContext;
-use rustc_middle::ty::print::with_forced_trimmed_paths;
+use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError};
use rustc_middle::ty::relate::{self, RelateResult, TypeRelation};
use rustc_middle::ty::{
- self, error::TypeError, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
- TypeVisitable, TypeVisitableExt,
+ self, error::TypeError, IsSuggestable, List, Region, Ty, TyCtxt, TypeFoldable,
+ TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
};
use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
use rustc_target::spec::abi;
@@ -228,8 +227,10 @@ fn msg_span_from_named_region<'tcx>(
let scope = region.free_region_binding_scope(tcx).expect_local();
match fr.bound_region {
ty::BoundRegionKind::BrNamed(_, name) => {
- let span = if let Some(param) =
- tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name))
+ let span = if let Some(param) = tcx
+ .hir()
+ .get_generics(scope)
+ .and_then(|generics| generics.get_named(name))
{
param.span
} else {
@@ -244,7 +245,7 @@ fn msg_span_from_named_region<'tcx>(
}
ty::BrAnon => (
"the anonymous lifetime as defined here".to_string(),
- Some(tcx.def_span(scope))
+ Some(tcx.def_span(scope)),
),
_ => (
format!("the lifetime `{region}` as defined here"),
@@ -579,76 +580,68 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
struct AbsolutePathPrinter<'tcx> {
tcx: TyCtxt<'tcx>,
+ segments: Vec<String>,
}
- struct NonTrivialPath;
-
impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
- type Error = NonTrivialPath;
-
- type Path = Vec<String>;
- type Region = !;
- type Type = !;
- type DynExistential = !;
- type Const = !;
-
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
self.tcx
}
- fn print_region(self, _region: ty::Region<'_>) -> Result<Self::Region, Self::Error> {
- Err(NonTrivialPath)
+ fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> {
+ Err(fmt::Error)
}
- fn print_type(self, _ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
- Err(NonTrivialPath)
+ fn print_type(&mut self, _ty: Ty<'tcx>) -> Result<(), PrintError> {
+ Err(fmt::Error)
}
fn print_dyn_existential(
- self,
+ &mut self,
_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
- ) -> Result<Self::DynExistential, Self::Error> {
- Err(NonTrivialPath)
+ ) -> Result<(), PrintError> {
+ Err(fmt::Error)
}
- fn print_const(self, _ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
- Err(NonTrivialPath)
+ fn print_const(&mut self, _ct: ty::Const<'tcx>) -> Result<(), PrintError> {
+ Err(fmt::Error)
}
- fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
- Ok(vec![self.tcx.crate_name(cnum).to_string()])
+ fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
+ self.segments = vec![self.tcx.crate_name(cnum).to_string()];
+ Ok(())
}
fn path_qualified(
- self,
+ &mut self,
_self_ty: Ty<'tcx>,
_trait_ref: Option<ty::TraitRef<'tcx>>,
- ) -> Result<Self::Path, Self::Error> {
- Err(NonTrivialPath)
+ ) -> Result<(), PrintError> {
+ Err(fmt::Error)
}
fn path_append_impl(
- self,
- _print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
+ &mut self,
+ _print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
_disambiguated_data: &DisambiguatedDefPathData,
_self_ty: Ty<'tcx>,
_trait_ref: Option<ty::TraitRef<'tcx>>,
- ) -> Result<Self::Path, Self::Error> {
- Err(NonTrivialPath)
+ ) -> Result<(), PrintError> {
+ Err(fmt::Error)
}
fn path_append(
- self,
- print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
+ &mut self,
+ print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
disambiguated_data: &DisambiguatedDefPathData,
- ) -> Result<Self::Path, Self::Error> {
- let mut path = print_prefix(self)?;
- path.push(disambiguated_data.to_string());
- Ok(path)
+ ) -> Result<(), PrintError> {
+ print_prefix(self)?;
+ self.segments.push(disambiguated_data.to_string());
+ Ok(())
}
fn path_generic_args(
- self,
- print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
+ &mut self,
+ print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
_args: &[GenericArg<'tcx>],
- ) -> Result<Self::Path, Self::Error> {
+ ) -> Result<(), PrintError> {
print_prefix(self)
}
}
@@ -658,12 +651,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
// are from a local module we could have false positives, e.g.
// let _ = [{struct Foo; Foo}, {struct Foo; Foo}];
if did1.krate != did2.krate {
- let abs_path =
- |def_id| AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]);
+ let abs_path = |def_id| {
+ let mut printer = AbsolutePathPrinter { tcx: self.tcx, segments: vec![] };
+ printer.print_def_path(def_id, &[]).map(|_| printer.segments)
+ };
// We compare strings because DefPath can be different
// for imported and non-imported crates
- let same_path = || -> Result<_, NonTrivialPath> {
+ let same_path = || -> Result<_, PrintError> {
Ok(self.tcx.def_path_str(did1) == self.tcx.def_path_str(did2)
|| abs_path(did1)? == abs_path(did2)?)
};
@@ -716,13 +711,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
&& let ty::Adt(def, args) = ty.kind()
&& Some(def.did()) == self.tcx.get_diagnostic_item(sym::Option)
{
- err.span_label(span, format!("this is an iterator with items of type `{}`", args.type_at(0)));
+ err.span_label(
+ span,
+ format!("this is an iterator with items of type `{}`", args.type_at(0)),
+ );
} else {
- err.span_label(span, format!("this expression has type `{ty}`"));
- }
+ err.span_label(span, format!("this expression has type `{ty}`"));
+ }
}
if let Some(ty::error::ExpectedFound { found, .. }) = exp_found
- && ty.is_box() && ty.boxed_ty() == found
+ && ty.is_box()
+ && ty.boxed_ty() == found
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
{
err.span_suggestion(
@@ -744,9 +743,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id);
let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind {
let arg_expr = args.first().expect("try desugaring call w/out arg");
- self.typeck_results.as_ref().and_then(|typeck_results| {
- typeck_results.expr_ty_opt(arg_expr)
- })
+ self.typeck_results
+ .as_ref()
+ .and_then(|typeck_results| typeck_results.expr_ty_opt(arg_expr))
} else {
bug!("try desugaring w/out call expr as scrutinee");
};
@@ -764,7 +763,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
_ => {}
}
}
- },
+ }
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
arm_block_id,
arm_span,
@@ -776,6 +775,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
ref prior_arms,
opt_suggest_box_span,
scrut_span,
+ scrut_hir_id,
..
}) => match source {
hir::MatchSource::TryDesugar(scrut_hir_id) => {
@@ -783,9 +783,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id);
let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind {
let arg_expr = args.first().expect("try desugaring call w/out arg");
- self.typeck_results.as_ref().and_then(|typeck_results| {
- typeck_results.expr_ty_opt(arg_expr)
- })
+ self.typeck_results
+ .as_ref()
+ .and_then(|typeck_results| typeck_results.expr_ty_opt(arg_expr))
} else {
bug!("try desugaring w/out call expr as scrutinee");
};
@@ -843,6 +843,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
) {
err.subdiagnostic(subdiag);
}
+ if let Some(hir::Node::Expr(m)) = self.tcx.hir().find_parent(scrut_hir_id)
+ && let Some(hir::Node::Stmt(stmt)) = self.tcx.hir().find_parent(m.hir_id)
+ && let hir::StmtKind::Expr(_) = stmt.kind
+ {
+ err.span_suggestion_verbose(
+ stmt.span.shrink_to_hi(),
+ "consider using a semicolon here, but this will discard any values \
+ in the match arms",
+ ";",
+ Applicability::MaybeIncorrect,
+ );
+ }
if let Some(ret_sp) = opt_suggest_box_span {
// Get return type span and point to it.
self.suggest_boxing_for_return_impl_trait(
@@ -879,8 +891,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
// don't suggest wrapping either blocks in `if .. {} else {}`
let is_empty_arm = |id| {
- let hir::Node::Block(blk) = self.tcx.hir().get(id)
- else {
+ let hir::Node::Block(blk) = self.tcx.hir().get(id) else {
return false;
};
if blk.expr.is_some() || !blk.stmts.is_empty() {
@@ -909,12 +920,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
_ => {
if let ObligationCauseCode::BindingObligation(_, span)
- | ObligationCauseCode::ExprBindingObligation(_, span, ..)
- = cause.code().peel_derives()
+ | ObligationCauseCode::ExprBindingObligation(_, span, ..) =
+ cause.code().peel_derives()
&& let TypeError::RegionsPlaceholderMismatch = terr
{
- err.span_note( * span,
- "the lifetime requirement is introduced here");
+ err.span_note(*span, "the lifetime requirement is introduced here");
}
}
}
@@ -1060,7 +1070,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let get_lifetimes = |sig| {
use rustc_hir::def::Namespace;
- let (_, sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS)
+ let (sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS)
.name_all_regions(sig)
.unwrap();
let lts: Vec<String> = reg.into_values().map(|kind| kind.to_string()).collect();
@@ -1574,14 +1584,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
target: &str,
types: &FxIndexMap<TyCategory, FxIndexSet<Span>>,
) {
- for (key, values) in types.iter() {
+ for (kind, values) in types.iter() {
let count = values.len();
- let kind = key.descr();
for &sp in values {
err.span_label(
sp,
format!(
- "{}{} {}{}",
+ "{}{} {:#}{}",
if count == 1 { "the " } else { "one of the " },
target,
kind,
@@ -1743,19 +1752,25 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
let similarity = |ExpectedFound { expected, found }: ExpectedFound<Ty<'tcx>>| {
- if let ty::Adt(expected, _) = expected.kind() && let Some(primitive) = found.primitive_symbol() {
+ if let ty::Adt(expected, _) = expected.kind()
+ && let Some(primitive) = found.primitive_symbol()
+ {
let path = self.tcx.def_path(expected.did()).data;
let name = path.last().unwrap().data.get_opt_name();
if name == Some(primitive) {
return Some(Similar::PrimitiveFound { expected: *expected, found });
}
- } else if let Some(primitive) = expected.primitive_symbol() && let ty::Adt(found, _) = found.kind() {
+ } else if let Some(primitive) = expected.primitive_symbol()
+ && let ty::Adt(found, _) = found.kind()
+ {
let path = self.tcx.def_path(found.did()).data;
let name = path.last().unwrap().data.get_opt_name();
if name == Some(primitive) {
return Some(Similar::PrimitiveExpected { expected, found: *found });
}
- } else if let ty::Adt(expected, _) = expected.kind() && let ty::Adt(found, _) = found.kind() {
+ } else if let ty::Adt(expected, _) = expected.kind()
+ && let ty::Adt(found, _) = found.kind()
+ {
if !expected.did().is_local() && expected.did().krate == found.did().krate {
// Most likely types from different versions of the same crate
// are in play, in which case this message isn't so helpful.
@@ -1765,8 +1780,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let f_path = self.tcx.def_path(found.did()).data;
let e_path = self.tcx.def_path(expected.did()).data;
- if let (Some(e_last), Some(f_last)) = (e_path.last(), f_path.last()) && e_last == f_last {
- return Some(Similar::Adts{expected: *expected, found: *found});
+ if let (Some(e_last), Some(f_last)) = (e_path.last(), f_path.last())
+ && e_last == f_last
+ {
+ return Some(Similar::Adts { expected: *expected, found: *found });
}
}
None
@@ -1797,7 +1814,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
};
let diagnose_adts =
- |expected_adt : ty::AdtDef<'tcx>,
+ |expected_adt: ty::AdtDef<'tcx>,
found_adt: ty::AdtDef<'tcx>,
diagnostic: &mut Diagnostic| {
let found_name = values.found.sort_string(self.tcx);
@@ -1817,8 +1834,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
.tcx
.parent_module_from_def_id(defid.expect_local())
.to_def_id();
- let module_name = self.tcx.def_path(module).to_string_no_crate_verbose();
- format!("{name} is defined in module `crate{module_name}` of the current crate")
+ let module_name =
+ self.tcx.def_path(module).to_string_no_crate_verbose();
+ format!(
+ "{name} is defined in module `crate{module_name}` of the current crate"
+ )
} else if defid.is_local() {
format!("{name} is defined in the current crate")
} else {
@@ -1830,13 +1850,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
};
match s {
- Similar::Adts{expected, found} => {
- diagnose_adts(expected, found, diag)
- }
- Similar::PrimitiveFound{expected, found: prim} => {
+ Similar::Adts { expected, found } => diagnose_adts(expected, found, diag),
+ Similar::PrimitiveFound { expected, found: prim } => {
diagnose_primitive(prim, values.expected, expected.did(), diag)
}
- Similar::PrimitiveExpected{expected: prim, found} => {
+ Similar::PrimitiveExpected { expected: prim, found } => {
diagnose_primitive(prim, values.found, found.did(), diag)
}
}
@@ -1878,7 +1896,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
s
};
- if !(values.expected.is_simple_text(self.tcx) && values.found.is_simple_text(self.tcx))
+ if !(values.expected.is_simple_text(self.tcx)
+ && values.found.is_simple_text(self.tcx))
|| (exp_found.is_some_and(|ef| {
// This happens when the type error is a subset of the expectation,
// like when you have two references but one is `usize` and the other
@@ -1968,13 +1987,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
&& let exp_found = TypeError::Sorts(exp_found)
&& exp_found != terr
{
- self.note_and_explain_type_err(
- diag,
- exp_found,
- cause,
- span,
- cause.body_id.to_def_id(),
- );
+ self.note_and_explain_type_err(diag, exp_found, cause, span, cause.body_id.to_def_id());
}
if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values
@@ -1984,7 +1997,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
{
let span = self.tcx.def_span(def_id);
diag.span_note(span, "this closure does not fulfill the lifetime requirements");
- self.suggest_for_all_lifetime_closure(span, self.tcx.hir().get_by_def_id(def_id), &exp_found, diag);
+ self.suggest_for_all_lifetime_closure(
+ span,
+ self.tcx.hir().get_by_def_id(def_id),
+ &exp_found,
+ diag,
+ );
}
// It reads better to have the error origin as the final
@@ -2010,7 +2028,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
// parentheses around it, perhaps the user meant to write `(expr,)` to
// build a tuple (issue #86100)
(ty::Tuple(fields), _) => {
- suggestions.extend(self.suggest_wrap_to_build_a_tuple( span, found, fields))
+ suggestions.extend(self.suggest_wrap_to_build_a_tuple(span, found, fields))
}
// If a byte was expected and the found expression is a char literal
// containing a single ASCII character, perhaps the user meant to write `b'c'` to
@@ -2060,8 +2078,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
// For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`,
// we try to suggest to add the missing `let` for `if let Some(..) = expr`
- (ty::Bool, ty::Tuple(list)) => if list.len() == 0 {
- suggestions.extend(self.suggest_let_for_letchains(&trace.cause, span));
+ (ty::Bool, ty::Tuple(list)) => {
+ if list.len() == 0 {
+ suggestions.extend(self.suggest_let_for_letchains(&trace.cause, span));
+ }
}
(ty::Array(_, _), ty::Array(_, _)) => {
suggestions.extend(self.suggest_specify_actual_length(terr, trace, span))
@@ -2071,8 +2091,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
let code = trace.cause.code();
if let &(MatchExpressionArm(box MatchExpressionArmCause { source, .. })
- | BlockTailExpression(.., source)
- ) = code
+ | BlockTailExpression(.., source)) = code
&& let hir::MatchSource::TryDesugar(_) = source
&& let Some((expected_ty, found_ty, _, _)) = self.values_str(trace.values)
{
@@ -2109,17 +2128,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
// Find a local statement where the initializer has
// the same span as the error and the type is specified.
if let hir::Stmt {
- kind: hir::StmtKind::Local(hir::Local {
- init: Some(hir::Expr {
- span: init_span,
+ kind:
+ hir::StmtKind::Local(hir::Local {
+ init: Some(hir::Expr { span: init_span, .. }),
+ ty: Some(array_ty),
..
}),
- ty: Some(array_ty),
- ..
- }),
..
} = s
- && init_span == &self.span {
+ && init_span == &self.span
+ {
self.result = Some(*array_ty);
}
}
@@ -2317,113 +2335,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
bound_kind: GenericKind<'tcx>,
sub: Region<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- // Attempt to obtain the span of the parameter so we can
- // suggest adding an explicit lifetime bound to it.
- let generics = self.tcx.generics_of(generic_param_scope);
- // type_param_span is (span, has_bounds)
- let mut is_synthetic = false;
- let mut ast_generics = None;
- let type_param_span = match bound_kind {
- GenericKind::Param(ref param) => {
- // Account for the case where `param` corresponds to `Self`,
- // which doesn't have the expected type argument.
- if !(generics.has_self && param.index == 0) {
- let type_param = generics.type_param(param, self.tcx);
- is_synthetic = type_param.kind.is_synthetic();
- type_param.def_id.as_local().map(|def_id| {
- // Get the `hir::Param` to verify whether it already has any bounds.
- // We do this to avoid suggesting code that ends up as `T: 'a'b`,
- // instead we suggest `T: 'a + 'b` in that case.
- let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
- ast_generics = self.tcx.hir().get_generics(hir_id.owner.def_id);
- let bounds =
- ast_generics.and_then(|g| g.bounds_span_for_suggestions(def_id));
- // `sp` only covers `T`, change it so that it covers
- // `T:` when appropriate
- if let Some(span) = bounds {
- (span, true)
- } else {
- let sp = self.tcx.def_span(def_id);
- (sp.shrink_to_hi(), false)
- }
- })
- } else {
- None
- }
- }
- _ => None,
- };
-
- let new_lt = {
- let mut possible = (b'a'..=b'z').map(|c| format!("'{}", c as char));
- let lts_names =
- iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))
- .flat_map(|g| &g.params)
- .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
- .map(|p| p.name.as_str())
- .collect::<Vec<_>>();
- possible
- .find(|candidate| !lts_names.contains(&&candidate[..]))
- .unwrap_or("'lt".to_string())
- };
-
- let mut add_lt_suggs: Vec<Option<_>> = vec![];
- if is_synthetic {
- if let Some(ast_generics) = ast_generics {
- let named_lifetime_param_exist = ast_generics.params.iter().any(|p| {
- matches!(
- p.kind,
- hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }
- )
- });
- if named_lifetime_param_exist && let [param, ..] = ast_generics.params
- {
- add_lt_suggs.push(Some((
- self.tcx.def_span(param.def_id).shrink_to_lo(),
- format!("{new_lt}, "),
- )));
- } else {
- add_lt_suggs
- .push(Some((ast_generics.span.shrink_to_hi(), format!("<{new_lt}>"))));
- }
- }
- } else {
- if let [param, ..] = &generics.params[..] && let Some(def_id) = param.def_id.as_local()
- {
- add_lt_suggs
- .push(Some((self.tcx.def_span(def_id).shrink_to_lo(), format!("{new_lt}, "))));
- }
- }
-
- if let Some(ast_generics) = ast_generics {
- for p in ast_generics.params {
- if p.is_elided_lifetime() {
- if self
- .tcx
- .sess
- .source_map()
- .span_to_prev_source(p.span.shrink_to_hi())
- .ok()
- .is_some_and(|s| *s.as_bytes().last().unwrap() == b'&')
- {
- add_lt_suggs
- .push(Some(
- (
- p.span.shrink_to_hi(),
- if let Ok(snip) = self.tcx.sess.source_map().span_to_next_source(p.span)
- && snip.starts_with(' ')
- {
- new_lt.to_string()
- } else {
- format!("{new_lt} ")
- }
- )
- ));
- } else {
- add_lt_suggs.push(Some((p.span.shrink_to_hi(), format!("<{new_lt}>"))));
- }
- }
- }
+ if let Some(SubregionOrigin::CompareImplItemObligation {
+ span,
+ impl_item_def_id,
+ trait_item_def_id,
+ }) = origin
+ {
+ return self.report_extra_impl_obligation(
+ span,
+ impl_item_def_id,
+ trait_item_def_id,
+ &format!("`{bound_kind}: {sub}`"),
+ );
}
let labeled_user_string = match bound_kind {
@@ -2437,223 +2360,211 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
},
};
- if let Some(SubregionOrigin::CompareImplItemObligation {
+ let mut err = self.tcx.sess.struct_span_err_with_code(
span,
- impl_item_def_id,
- trait_item_def_id,
- }) = origin
- {
- return self.report_extra_impl_obligation(
- span,
- impl_item_def_id,
- trait_item_def_id,
- &format!("`{bound_kind}: {sub}`"),
- );
+ format!("{labeled_user_string} may not live long enough"),
+ match sub.kind() {
+ ty::ReEarlyBound(_) | ty::ReFree(_) if sub.has_name() => error_code!(E0309),
+ ty::ReStatic => error_code!(E0310),
+ _ => error_code!(E0311),
+ },
+ );
+
+ '_explain: {
+ let (description, span) = match sub.kind() {
+ ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => {
+ msg_span_from_named_region(self.tcx, sub, Some(span))
+ }
+ _ => (format!("lifetime `{sub}`"), Some(span)),
+ };
+ let prefix = format!("{labeled_user_string} must be valid for ");
+ label_msg_span(&mut err, &prefix, description, span, "...");
+ if let Some(origin) = origin {
+ self.note_region_origin(&mut err, &origin);
+ }
}
- fn binding_suggestion<'tcx, S: fmt::Display>(
- err: &mut Diagnostic,
- type_param_span: Option<(Span, bool)>,
- bound_kind: GenericKind<'tcx>,
- sub: S,
- add_lt_suggs: Vec<Option<(Span, String)>>,
- ) {
+ 'suggestion: {
let msg = "consider adding an explicit lifetime bound";
- if let Some((sp, has_lifetimes)) = type_param_span {
- let suggestion =
- if has_lifetimes { format!(" + {sub}") } else { format!(": {sub}") };
- let mut suggestions = vec![(sp, suggestion)];
- for add_lt_sugg in add_lt_suggs.into_iter().flatten() {
- suggestions.push(add_lt_sugg);
+
+ if (bound_kind, sub).has_infer_regions()
+ || (bound_kind, sub).has_placeholders()
+ || !bound_kind.is_suggestable(self.tcx, false)
+ {
+ let lt_name = sub.get_name_or_anon().to_string();
+ err.help(format!("{msg} `{bound_kind}: {lt_name}`..."));
+ break 'suggestion;
+ }
+
+ let mut generic_param_scope = generic_param_scope;
+ while self.tcx.def_kind(generic_param_scope) == DefKind::OpaqueTy {
+ generic_param_scope = self.tcx.local_parent(generic_param_scope);
+ }
+
+ // type_param_sugg_span is (span, has_bounds)
+ let (type_scope, type_param_sugg_span) = match bound_kind {
+ GenericKind::Param(ref param) => {
+ let generics = self.tcx.generics_of(generic_param_scope);
+ let def_id = generics.type_param(param, self.tcx).def_id.expect_local();
+ let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id;
+ // Get the `hir::Param` to verify whether it already has any bounds.
+ // We do this to avoid suggesting code that ends up as `T: 'a'b`,
+ // instead we suggest `T: 'a + 'b` in that case.
+ let hir_generics = self.tcx.hir().get_generics(scope).unwrap();
+ let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) {
+ Some(span) => Some((span, true)),
+ // If `param` corresponds to `Self`, no usable suggestion span.
+ None if generics.has_self && param.index == 0 => None,
+ None => Some((self.tcx.def_span(def_id).shrink_to_hi(), false)),
+ };
+ (scope, sugg_span)
}
- err.multipart_suggestion_verbose(
- format!("{msg}..."),
- suggestions,
- Applicability::MaybeIncorrect, // Issue #41966
- );
+ _ => (generic_param_scope, None),
+ };
+ let suggestion_scope = {
+ let lifetime_scope = match sub.kind() {
+ ty::ReStatic => hir::def_id::CRATE_DEF_ID,
+ _ => match self.tcx.is_suitable_region(sub) {
+ Some(info) => info.def_id,
+ None => generic_param_scope,
+ },
+ };
+ match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) {
+ true => type_scope,
+ false => lifetime_scope,
+ }
+ };
+
+ let mut suggs = vec![];
+ let lt_name = self.suggest_name_region(sub, &mut suggs);
+
+ if let Some((sp, has_lifetimes)) = type_param_sugg_span
+ && suggestion_scope == type_scope
+ {
+ let suggestion =
+ if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") };
+ suggs.push((sp, suggestion))
+ } else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) {
+ let pred = format!("{bound_kind}: {lt_name}");
+ let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred);
+ suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion))
} else {
let consider = format!("{msg} `{bound_kind}: {sub}`...");
err.help(consider);
}
+
+ if !suggs.is_empty() {
+ err.multipart_suggestion_verbose(
+ format!("{msg}"),
+ suggs,
+ Applicability::MaybeIncorrect, // Issue #41966
+ );
+ }
}
- let new_binding_suggestion =
- |err: &mut Diagnostic, type_param_span: Option<(Span, bool)>| {
- let msg = "consider introducing an explicit lifetime bound";
- if let Some((sp, has_lifetimes)) = type_param_span {
- let suggestion =
- if has_lifetimes { format!(" + {new_lt}") } else { format!(": {new_lt}") };
- let mut sugg =
- vec![(sp, suggestion), (span.shrink_to_hi(), format!(" + {new_lt}"))];
- for lt in add_lt_suggs.clone().into_iter().flatten() {
- sugg.push(lt);
- sugg.rotate_right(1);
- }
- // `MaybeIncorrect` due to issue #41966.
- err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect);
- }
- };
+ err
+ }
- #[derive(Debug)]
- enum SubOrigin<'hir> {
- GAT(&'hir hir::Generics<'hir>),
- Impl,
- Trait,
- Fn,
- Unknown,
- }
- let sub_origin = 'origin: {
- match *sub {
- ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, .. }) => {
- let node = self.tcx.hir().get_if_local(def_id).unwrap();
- match node {
- Node::GenericParam(param) => {
- for h in self.tcx.hir().parent_iter(param.hir_id) {
- break 'origin match h.1 {
- Node::ImplItem(hir::ImplItem {
- kind: hir::ImplItemKind::Type(..),
- generics,
- ..
- })
- | Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Type(..),
- generics,
- ..
- }) => SubOrigin::GAT(generics),
- Node::ImplItem(hir::ImplItem {
- kind: hir::ImplItemKind::Fn(..),
- ..
- })
- | Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Fn(..),
- ..
- })
- | Node::Item(hir::Item {
- kind: hir::ItemKind::Fn(..), ..
- }) => SubOrigin::Fn,
- Node::Item(hir::Item {
- kind: hir::ItemKind::Trait(..),
- ..
- }) => SubOrigin::Trait,
- Node::Item(hir::Item {
- kind: hir::ItemKind::Impl(..), ..
- }) => SubOrigin::Impl,
- _ => continue,
- };
- }
+ pub fn suggest_name_region(
+ &self,
+ lifetime: Region<'tcx>,
+ add_lt_suggs: &mut Vec<(Span, String)>,
+ ) -> String {
+ struct LifetimeReplaceVisitor<'tcx, 'a> {
+ tcx: TyCtxt<'tcx>,
+ needle: hir::LifetimeName,
+ new_lt: &'a str,
+ add_lt_suggs: &'a mut Vec<(Span, String)>,
+ }
+
+ impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'tcx, '_> {
+ fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) {
+ if lt.res == self.needle {
+ let (pos, span) = lt.suggestion_position();
+ let new_lt = &self.new_lt;
+ let sugg = match pos {
+ hir::LifetimeSuggestionPosition::Normal => format!("{new_lt}"),
+ hir::LifetimeSuggestionPosition::Ampersand => format!("{new_lt} "),
+ hir::LifetimeSuggestionPosition::ElidedPath => format!("<{new_lt}>"),
+ hir::LifetimeSuggestionPosition::ElidedPathArgument => {
+ format!("{new_lt}, ")
}
- _ => {}
- }
+ hir::LifetimeSuggestionPosition::ObjectDefault => format!("+ {new_lt}"),
+ };
+ self.add_lt_suggs.push((span, sugg));
}
- _ => {}
}
- SubOrigin::Unknown
- };
- debug!(?sub_origin);
-
- let mut err = match (*sub, sub_origin) {
- // In the case of GATs, we have to be careful. If we a type parameter `T` on an impl,
- // but a lifetime `'a` on an associated type, then we might need to suggest adding
- // `where T: 'a`. Importantly, this is on the GAT span, not on the `T` declaration.
- (ty::ReEarlyBound(ty::EarlyBoundRegion { name: _, .. }), SubOrigin::GAT(generics)) => {
- // Does the required lifetime have a nice name we can print?
- let mut err = struct_span_err!(
- self.tcx.sess,
- span,
- E0309,
- "{} may not live long enough",
- labeled_user_string
- );
- let pred = format!("{bound_kind}: {sub}");
- let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred,);
- err.span_suggestion(
- generics.tail_span_for_predicate_suggestion(),
- "consider adding a where clause",
- suggestion,
- Applicability::MaybeIncorrect,
- );
- err
+
+ fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) {
+ let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else {
+ return hir::intravisit::walk_ty(self, ty);
+ };
+ let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty();
+ if let Some(&(_, b)) =
+ opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle)
+ {
+ let prev_needle =
+ std::mem::replace(&mut self.needle, hir::LifetimeName::Param(b));
+ for bound in opaque_ty.bounds {
+ self.visit_param_bound(bound);
+ }
+ self.needle = prev_needle;
+ }
}
- (
- ty::ReEarlyBound(ty::EarlyBoundRegion { name, .. })
- | ty::ReFree(ty::FreeRegion { bound_region: ty::BrNamed(_, name), .. }),
- _,
- ) if name != kw::UnderscoreLifetime => {
- // Does the required lifetime have a nice name we can print?
- let mut err = struct_span_err!(
- self.tcx.sess,
- span,
- E0309,
- "{} may not live long enough",
- labeled_user_string
- );
- // Explicitly use the name instead of `sub`'s `Display` impl. The `Display` impl
- // for the bound is not suitable for suggestions when `-Zverbose` is set because it
- // uses `Debug` output, so we handle it specially here so that suggestions are
- // always correct.
- binding_suggestion(&mut err, type_param_span, bound_kind, name, vec![]);
- err
- }
-
- (ty::ReStatic, _) => {
- // Does the required lifetime have a nice name we can print?
- let mut err = struct_span_err!(
- self.tcx.sess,
- span,
- E0310,
- "{} may not live long enough",
- labeled_user_string
- );
- binding_suggestion(&mut err, type_param_span, bound_kind, "'static", vec![]);
- err
+ }
+
+ let (lifetime_def_id, lifetime_scope) = match self.tcx.is_suitable_region(lifetime) {
+ Some(info) if !lifetime.has_name() => {
+ (info.boundregion.get_id().unwrap().expect_local(), info.def_id)
}
+ _ => return lifetime.get_name_or_anon().to_string(),
+ };
- _ => {
- // If not, be less specific.
- let mut err = struct_span_err!(
- self.tcx.sess,
- span,
- E0311,
- "{} may not live long enough",
- labeled_user_string
- );
- note_and_explain_region(
- self.tcx,
- &mut err,
- &format!("{labeled_user_string} must be valid for "),
- sub,
- "...",
- None,
- );
- if let Some(infer::RelateParamBound(_, t, _)) = origin {
- let t = self.resolve_vars_if_possible(t);
- match t.kind() {
- // We've got:
- // fn get_later<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
- // suggest:
- // fn get_later<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
- ty::Closure(..) | ty::Alias(ty::Opaque, ..) => {
- new_binding_suggestion(&mut err, type_param_span);
- }
- _ => {
- binding_suggestion(
- &mut err,
- type_param_span,
- bound_kind,
- new_lt,
- add_lt_suggs,
- );
- }
+ let new_lt = {
+ let generics = self.tcx.generics_of(lifetime_scope);
+ let mut used_names =
+ iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))
+ .flat_map(|g| &g.params)
+ .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
+ .map(|p| p.name)
+ .collect::<Vec<_>>();
+ if let Some(hir_id) = self.tcx.opt_local_def_id_to_hir_id(lifetime_scope) {
+ // consider late-bound lifetimes ...
+ used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map(|p| {
+ match p {
+ ty::BoundVariableKind::Region(lt) => lt.get_name(),
+ _ => None,
}
- }
- err
+ }))
}
+ (b'a'..=b'z')
+ .map(|c| format!("'{}", c as char))
+ .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate))
+ .unwrap_or("'lt".to_string())
};
- if let Some(origin) = origin {
- self.note_region_origin(&mut err, &origin);
- }
- err
+ let mut visitor = LifetimeReplaceVisitor {
+ tcx: self.tcx,
+ needle: hir::LifetimeName::Param(lifetime_def_id),
+ add_lt_suggs,
+ new_lt: &new_lt,
+ };
+ match self.tcx.hir().expect_owner(lifetime_scope) {
+ hir::OwnerNode::Item(i) => visitor.visit_item(i),
+ hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i),
+ hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i),
+ hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i),
+ hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"),
+ }
+
+ let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap();
+ let sugg = ast_generics
+ .span_for_lifetime_suggestion()
+ .map(|span| (span, format!("{new_lt}, ")))
+ .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>")));
+ add_lt_suggs.push(sugg);
+
+ new_lt
}
fn report_sub_sup_conflict(
@@ -2925,7 +2836,7 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
// say, also take a look at the error code, maybe we can
// tailor to that.
_ => match terr {
- TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => Error0644,
+ TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_coroutine() => Error0644,
TypeError::IntrinsicCast => Error0308,
_ => Error0308,
},
@@ -2972,7 +2883,7 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
// say, also take a look at the error code, maybe we can
// tailor to that.
_ => match terr {
- TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => {
+ TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_coroutine() => {
ObligationCauseFailureCode::ClosureSelfref { span }
}
TypeError::IntrinsicCast => {
@@ -3040,21 +2951,23 @@ pub enum TyCategory {
Closure,
Opaque,
OpaqueFuture,
- Generator(hir::GeneratorKind),
+ Coroutine(hir::CoroutineKind),
Foreign,
}
-impl TyCategory {
- fn descr(&self) -> &'static str {
+impl fmt::Display for TyCategory {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
- Self::Closure => "closure",
- Self::Opaque => "opaque type",
- Self::OpaqueFuture => "future",
- Self::Generator(gk) => gk.descr(),
- Self::Foreign => "foreign type",
+ Self::Closure => "closure".fmt(f),
+ Self::Opaque => "opaque type".fmt(f),
+ Self::OpaqueFuture => "future".fmt(f),
+ Self::Coroutine(gk) => gk.fmt(f),
+ Self::Foreign => "foreign type".fmt(f),
}
}
+}
+impl TyCategory {
pub fn from_ty(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Self, DefId)> {
match *ty.kind() {
ty::Closure(def_id, _) => Some((Self::Closure, def_id)),
@@ -3063,8 +2976,8 @@ impl TyCategory {
if tcx.ty_is_opaque_future(ty) { Self::OpaqueFuture } else { Self::Opaque };
Some((kind, def_id))
}
- ty::Generator(def_id, ..) => {
- Some((Self::Generator(tcx.generator_kind(def_id).unwrap()), def_id))
+ ty::Coroutine(def_id, ..) => {
+ Some((Self::Coroutine(tcx.coroutine_kind(def_id).unwrap()), def_id))
}
ty::Foreign(def_id) => Some((Self::Foreign, def_id)),
_ => None,