Move some stuff into the ambiguity and suggestion modules

This commit is contained in:
Michael Goulet 2024-07-08 16:44:00 -04:00
parent 7af825fea4
commit cd68a28daa
4 changed files with 742 additions and 734 deletions

View File

@ -1,10 +1,27 @@
use std::ops::ControlFlow;
use rustc_errors::{
struct_span_code_err, Applicability, Diag, MultiSpan, StashKey, E0283, E0284, E0790,
};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor as _;
use rustc_hir::LangItem;
use rustc_infer::infer::error_reporting::{TypeAnnotationNeeded, TypeErrCtxt};
use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt}; use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt};
use rustc_infer::traits::util::elaborate; use rustc_infer::traits::util::elaborate;
use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation}; use rustc_infer::traits::{
use rustc_middle::ty; Obligation, ObligationCause, ObligationCauseCode, PolyTraitObligation, PredicateObligation,
use rustc_span::{Span, DUMMY_SP}; };
use rustc_macros::extension;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable as _, TypeVisitableExt as _};
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
use crate::error_reporting::traits::suggestions::TypeErrCtxtExt as _;
use crate::error_reporting::traits::{
to_pretty_impl_header, FindExprBySpan, InferCtxtPrivExt as _,
};
use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::ObligationCtxt; use crate::traits::ObligationCtxt;
@ -134,3 +151,548 @@ pub fn compute_applicable_impls_for_diagnostics<'tcx>(
ambiguities ambiguities
} }
#[extension(pub trait TypeErrCtxtAmbiguityExt<'a, 'tcx>)]
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
#[instrument(skip(self), level = "debug")]
fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed {
// Unable to successfully determine, probably means
// insufficient type information, but could mean
// ambiguous impls. The latter *ought* to be a
// coherence violation, so we don't report it here.
let predicate = self.resolve_vars_if_possible(obligation.predicate);
let span = obligation.cause.span;
debug!(?predicate, obligation.cause.code = ?obligation.cause.code());
// Ambiguity errors are often caused as fallout from earlier errors.
// We ignore them if this `infcx` is tainted in some cases below.
let bound_predicate = predicate.kind();
let mut err = match bound_predicate.skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
let trait_ref = bound_predicate.rebind(data.trait_ref);
debug!(?trait_ref);
if let Err(e) = predicate.error_reported() {
return e;
}
if let Err(guar) = self.tcx.ensure().coherent_trait(trait_ref.def_id()) {
// Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
// other `Foo` impls are incoherent.
return guar;
}
// This is kind of a hack: it frequently happens that some earlier
// error prevents types from being fully inferred, and then we get
// a bunch of uninteresting errors saying something like "<generic
// #0> doesn't implement Sized". It may even be true that we
// could just skip over all checks where the self-ty is an
// inference variable, but I was afraid that there might be an
// inference variable created, registered as an obligation, and
// then never forced by writeback, and hence by skipping here we'd
// be ignoring the fact that we don't KNOW the type works
// out. Though even that would probably be harmless, given that
// we're only talking about builtin traits, which are known to be
// inhabited. We used to check for `self.tcx.sess.has_errors()` to
// avoid inundating the user with unnecessary errors, but we now
// check upstream for type errors and don't add the obligations to
// begin with in those cases.
if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) {
match self.tainted_by_errors() {
None => {
let err = self.emit_inference_failure_err(
obligation.cause.body_id,
span,
trait_ref.self_ty().skip_binder().into(),
TypeAnnotationNeeded::E0282,
false,
);
return err.stash(span, StashKey::MaybeForgetReturn).unwrap();
}
Some(e) => return e,
}
}
// Typically, this ambiguity should only happen if
// there are unresolved type inference variables
// (otherwise it would suggest a coherence
// failure). But given #21974 that is not necessarily
// the case -- we can have multiple where clauses that
// are only distinguished by a region, which results
// in an ambiguity even when all types are fully
// known, since we don't dispatch based on region
// relationships.
// Pick the first generic parameter that still contains inference variables as the one
// we're going to emit an error for. If there are none (see above), fall back to
// a more general error.
let arg = data.trait_ref.args.iter().find(|s| s.has_non_region_infer());
let mut err = if let Some(arg) = arg {
self.emit_inference_failure_err(
obligation.cause.body_id,
span,
arg,
TypeAnnotationNeeded::E0283,
true,
)
} else {
struct_span_code_err!(
self.dcx(),
span,
E0283,
"type annotations needed: cannot satisfy `{}`",
predicate,
)
};
let mut ambiguities = compute_applicable_impls_for_diagnostics(
self.infcx,
&obligation.with(self.tcx, trait_ref),
);
let has_non_region_infer =
trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer());
// It doesn't make sense to talk about applicable impls if there are more than a
// handful of them. If there are a lot of them, but only a few of them have no type
// params, we only show those, as they are more likely to be useful/intended.
if ambiguities.len() > 5 {
let infcx = self.infcx;
if !ambiguities.iter().all(|option| match option {
CandidateSource::DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
CandidateSource::ParamEnv(_) => true,
}) {
// If not all are blanket impls, we filter blanked impls out.
ambiguities.retain(|option| match option {
CandidateSource::DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
CandidateSource::ParamEnv(_) => true,
});
}
}
if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
if let Some(e) = self.tainted_by_errors()
&& arg.is_none()
{
// If `arg.is_none()`, then this is probably two param-env
// candidates or impl candidates that are equal modulo lifetimes.
// Therefore, if we've already emitted an error, just skip this
// one, since it's not particularly actionable.
err.cancel();
return e;
}
self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate);
} else {
if let Some(e) = self.tainted_by_errors() {
err.cancel();
return e;
}
err.note(format!("cannot satisfy `{predicate}`"));
let impl_candidates =
self.find_similar_impl_candidates(predicate.as_trait_clause().unwrap());
if impl_candidates.len() < 40 {
self.report_similar_impl_candidates(
impl_candidates.as_slice(),
trait_ref,
obligation.cause.body_id,
&mut err,
false,
obligation.param_env,
);
}
}
if let ObligationCauseCode::WhereClause(def_id, _)
| ObligationCauseCode::WhereClauseInExpr(def_id, ..) = *obligation.cause.code()
{
self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
}
if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack())
&& let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id)
{
let mut expr_finder = FindExprBySpan::new(span, self.tcx);
expr_finder.visit_expr(&body.value);
if let Some(hir::Expr {
kind:
hir::ExprKind::Call(
hir::Expr {
kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
..
},
_,
)
| hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
..
}) = expr_finder.result
&& let [
..,
trait_path_segment @ hir::PathSegment {
res: Res::Def(DefKind::Trait, trait_id),
..
},
hir::PathSegment {
ident: assoc_item_name,
res: Res::Def(_, item_id),
..
},
] = path.segments
&& data.trait_ref.def_id == *trait_id
&& self.tcx.trait_of_item(*item_id) == Some(*trait_id)
&& let None = self.tainted_by_errors()
{
let (verb, noun) = match self.tcx.associated_item(item_id).kind {
ty::AssocKind::Const => ("refer to the", "constant"),
ty::AssocKind::Fn => ("call", "function"),
// This is already covered by E0223, but this following single match
// arm doesn't hurt here.
ty::AssocKind::Type => ("refer to the", "type"),
};
// Replace the more general E0283 with a more specific error
err.cancel();
err = self.dcx().struct_span_err(
span,
format!(
"cannot {verb} associated {noun} on trait without specifying the \
corresponding `impl` type",
),
);
err.code(E0790);
if let Some(local_def_id) = data.trait_ref.def_id.as_local()
&& let hir::Node::Item(hir::Item {
ident: trait_name,
kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs),
..
}) = self.tcx.hir_node_by_def_id(local_def_id)
&& let Some(method_ref) = trait_item_refs
.iter()
.find(|item_ref| item_ref.ident == *assoc_item_name)
{
err.span_label(
method_ref.span,
format!("`{trait_name}::{assoc_item_name}` defined here"),
);
}
err.span_label(span, format!("cannot {verb} associated {noun} of trait"));
let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id);
if let Some(impl_def_id) =
trait_impls.non_blanket_impls().values().flatten().next()
{
let non_blanket_impl_count =
trait_impls.non_blanket_impls().values().flatten().count();
// If there is only one implementation of the trait, suggest using it.
// Otherwise, use a placeholder comment for the implementation.
let (message, self_type) = if non_blanket_impl_count == 1 {
(
"use the fully-qualified path to the only available \
implementation",
format!(
"{}",
self.tcx.type_of(impl_def_id).instantiate_identity()
),
)
} else {
(
"use a fully-qualified path to a specific available \
implementation",
"/* self type */".to_string(),
)
};
let mut suggestions =
vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))];
if let Some(generic_arg) = trait_path_segment.args {
let between_span =
trait_path_segment.ident.span.between(generic_arg.span_ext);
// get rid of :: between Trait and <type>
// must be '::' between them, otherwise the parser won't accept the code
suggestions.push((between_span, "".to_string()));
suggestions
.push((generic_arg.span_ext.shrink_to_hi(), ">".to_string()));
} else {
suggestions.push((
trait_path_segment.ident.span.shrink_to_hi(),
">".to_string(),
));
}
err.multipart_suggestion(
message,
suggestions,
Applicability::MaybeIncorrect,
);
}
}
};
err
}
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
// Same hacky approach as above to avoid deluging user
// with error messages.
if let Err(e) = arg.error_reported() {
return e;
}
if let Some(e) = self.tainted_by_errors() {
return e;
}
self.emit_inference_failure_err(
obligation.cause.body_id,
span,
arg,
TypeAnnotationNeeded::E0282,
false,
)
}
ty::PredicateKind::Subtype(data) => {
if let Err(e) = data.error_reported() {
return e;
}
if let Some(e) = self.tainted_by_errors() {
return e;
}
let ty::SubtypePredicate { a_is_expected: _, a, b } = data;
// both must be type variables, or the other would've been instantiated
assert!(a.is_ty_var() && b.is_ty_var());
self.emit_inference_failure_err(
obligation.cause.body_id,
span,
a.into(),
TypeAnnotationNeeded::E0282,
true,
)
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
if let Err(e) = predicate.error_reported() {
return e;
}
if let Some(e) = self.tainted_by_errors() {
return e;
}
if let Err(guar) =
self.tcx.ensure().coherent_trait(self.tcx.parent(data.projection_term.def_id))
{
// Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
// other `Foo` impls are incoherent.
return guar;
}
let arg = data
.projection_term
.args
.iter()
.chain(Some(data.term.into_arg()))
.find(|g| g.has_non_region_infer());
if let Some(arg) = arg {
self.emit_inference_failure_err(
obligation.cause.body_id,
span,
arg,
TypeAnnotationNeeded::E0284,
true,
)
.with_note(format!("cannot satisfy `{predicate}`"))
} else {
// If we can't find a generic parameter, just print a generic error
struct_span_code_err!(
self.dcx(),
span,
E0284,
"type annotations needed: cannot satisfy `{}`",
predicate,
)
.with_span_label(span, format!("cannot satisfy `{predicate}`"))
}
}
ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => {
if let Err(e) = predicate.error_reported() {
return e;
}
if let Some(e) = self.tainted_by_errors() {
return e;
}
let arg = data.walk().find(|g| g.is_non_region_infer());
if let Some(arg) = arg {
let err = self.emit_inference_failure_err(
obligation.cause.body_id,
span,
arg,
TypeAnnotationNeeded::E0284,
true,
);
err
} else {
// If we can't find a generic parameter, just print a generic error
struct_span_code_err!(
self.dcx(),
span,
E0284,
"type annotations needed: cannot satisfy `{}`",
predicate,
)
.with_span_label(span, format!("cannot satisfy `{predicate}`"))
}
}
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ..)) => self
.emit_inference_failure_err(
obligation.cause.body_id,
span,
ct.into(),
TypeAnnotationNeeded::E0284,
true,
),
ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })
if term.is_infer() =>
{
if let Some(e) = self.tainted_by_errors() {
return e;
}
struct_span_code_err!(
self.dcx(),
span,
E0284,
"type annotations needed: cannot normalize `{alias}`",
)
.with_span_label(span, format!("cannot normalize `{alias}`"))
}
_ => {
if let Some(e) = self.tainted_by_errors() {
return e;
}
struct_span_code_err!(
self.dcx(),
span,
E0284,
"type annotations needed: cannot satisfy `{}`",
predicate,
)
.with_span_label(span, format!("cannot satisfy `{predicate}`"))
}
};
self.note_obligation_cause(&mut err, obligation);
err.emit()
}
fn annotate_source_of_ambiguity(
&self,
err: &mut Diag<'_>,
ambiguities: &[CandidateSource],
predicate: ty::Predicate<'tcx>,
) {
let mut spans = vec![];
let mut crates = vec![];
let mut post = vec![];
let mut has_param_env = false;
for ambiguity in ambiguities {
match ambiguity {
CandidateSource::DefId(impl_def_id) => match self.tcx.span_of_impl(*impl_def_id) {
Ok(span) => spans.push(span),
Err(name) => {
crates.push(name);
if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) {
post.push(header);
}
}
},
CandidateSource::ParamEnv(span) => {
has_param_env = true;
spans.push(*span);
}
}
}
let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect();
crate_names.sort();
crate_names.dedup();
post.sort();
post.dedup();
if self.tainted_by_errors().is_some()
&& (crate_names.len() == 1
&& spans.len() == 0
&& ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str())
|| predicate.visit_with(&mut HasNumericInferVisitor).is_break())
{
// Avoid complaining about other inference issues for expressions like
// `42 >> 1`, where the types are still `{integer}`, but we want to
// Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too?
// NOTE(eddyb) this was `.cancel()`, but `err`
// is borrowed, so we can't fully defuse it.
err.downgrade_to_delayed_bug();
return;
}
let msg = format!(
"multiple `impl`s{} satisfying `{}` found",
if has_param_env { " or `where` clauses" } else { "" },
predicate
);
let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) {
format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"),)
} else if post.len() == 1 {
format!(": `{}`", post[0])
} else {
String::new()
};
match (spans.len(), crates.len(), crate_names.len()) {
(0, 0, 0) => {
err.note(format!("cannot satisfy `{predicate}`"));
}
(0, _, 1) => {
err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,));
}
(0, _, _) => {
err.note(format!(
"{} in the following crates: {}{}",
msg,
crate_names.join(", "),
post,
));
}
(_, 0, 0) => {
let span: MultiSpan = spans.into();
err.span_note(span, msg);
}
(_, 1, 1) => {
let span: MultiSpan = spans.into();
err.span_note(span, msg);
err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,));
}
_ => {
let span: MultiSpan = spans.into();
err.span_note(span, msg);
err.note(format!(
"and more `impl`s found in the following crates: {}{}",
crate_names.join(", "),
post,
));
}
}
}
}
struct HasNumericInferVisitor;
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HasNumericInferVisitor {
type Result = ControlFlow<()>;
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
}

View File

@ -15,7 +15,6 @@ use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode, Pred
use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::print::PrintTraitRefExt as _;
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::Span; use rustc_span::Span;
use std::ops::ControlFlow;
pub use self::infer_ctxt_ext::*; pub use self::infer_ctxt_ext::*;
pub use self::overflow::*; pub use self::overflow::*;
@ -91,49 +90,6 @@ impl<'v> Visitor<'v> for FindExprBySpan<'v> {
} }
} }
/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting
/// `param: ?Sized` would be a valid constraint.
struct FindTypeParam {
param: rustc_span::Symbol,
invalid_spans: Vec<Span>,
nested: bool,
}
impl<'v> Visitor<'v> for FindTypeParam {
fn visit_where_predicate(&mut self, _: &'v hir::WherePredicate<'v>) {
// Skip where-clauses, to avoid suggesting indirection for type parameters found there.
}
fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
// We collect the spans of all uses of the "bare" type param, like in `field: T` or
// `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be
// valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized`
// obligations like `Box<T>` and `Vec<T>`, but we perform no extra analysis for those cases
// and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors
// in that case should make what happened clear enough.
match ty.kind {
hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {}
hir::TyKind::Path(hir::QPath::Resolved(None, path))
if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
{
if !self.nested {
debug!(?ty, "FindTypeParam::visit_ty");
self.invalid_spans.push(ty.span);
}
}
hir::TyKind::Path(_) => {
let prev = self.nested;
self.nested = true;
hir::intravisit::walk_ty(self, ty);
self.nested = prev;
}
_ => {
hir::intravisit::walk_ty(self, ty);
}
}
}
}
/// Summarizes information /// Summarizes information
#[derive(Clone)] #[derive(Clone)]
pub enum ArgKind { pub enum ArgKind {
@ -165,20 +121,6 @@ impl ArgKind {
} }
} }
struct HasNumericInferVisitor;
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HasNumericInferVisitor {
type Result = ControlFlow<()>;
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum DefIdOrName { pub enum DefIdOrName {
DefId(DefId), DefId(DefId),

View File

@ -4623,6 +4623,132 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
); );
} }
} }
#[instrument(level = "debug", skip_all)]
fn suggest_unsized_bound_if_applicable(
&self,
err: &mut Diag<'_>,
obligation: &PredicateObligation<'tcx>,
) {
let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
obligation.predicate.kind().skip_binder()
else {
return;
};
let (ObligationCauseCode::WhereClause(item_def_id, span)
| ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..)) =
*obligation.cause.code().peel_derives()
else {
return;
};
if span.is_dummy() {
return;
}
debug!(?pred, ?item_def_id, ?span);
let (Some(node), true) = (
self.tcx.hir().get_if_local(item_def_id),
self.tcx.is_lang_item(pred.def_id(), LangItem::Sized),
) else {
return;
};
let Some(generics) = node.generics() else {
return;
};
let sized_trait = self.tcx.lang_items().sized_trait();
debug!(?generics.params);
debug!(?generics.predicates);
let Some(param) = generics.params.iter().find(|param| param.span == span) else {
return;
};
// Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
// `Sized` bound is there intentionally and we don't need to suggest relaxing it.
let explicitly_sized = generics
.bounds_for_param(param.def_id)
.flat_map(|bp| bp.bounds)
.any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait);
if explicitly_sized {
return;
}
debug!(?param);
match node {
hir::Node::Item(
item @ hir::Item {
// Only suggest indirection for uses of type parameters in ADTs.
kind:
hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..),
..
},
) => {
if self.suggest_indirection_for_unsized(err, item, param) {
return;
}
}
_ => {}
};
// Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`.
let (span, separator, open_paren_sp) =
if let Some((s, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) {
(s, " +", open_paren_sp)
} else {
(param.name.ident().span.shrink_to_hi(), ":", None)
};
let mut suggs = vec![];
let suggestion = format!("{separator} ?Sized");
if let Some(open_paren_sp) = open_paren_sp {
suggs.push((open_paren_sp, "(".to_string()));
suggs.push((span, format!("){suggestion}")));
} else {
suggs.push((span, suggestion));
}
err.multipart_suggestion_verbose(
"consider relaxing the implicit `Sized` restriction",
suggs,
Applicability::MachineApplicable,
);
}
fn suggest_indirection_for_unsized(
&self,
err: &mut Diag<'_>,
item: &hir::Item<'tcx>,
param: &hir::GenericParam<'tcx>,
) -> bool {
// Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a
// borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);`
// is not. Look for invalid "bare" parameter uses, and suggest using indirection.
let mut visitor =
FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false };
visitor.visit_item(item);
if visitor.invalid_spans.is_empty() {
return false;
}
let mut multispan: MultiSpan = param.span.into();
multispan.push_span_label(
param.span,
format!("this could be changed to `{}: ?Sized`...", param.name.ident()),
);
for sp in visitor.invalid_spans {
multispan.push_span_label(
sp,
format!("...if indirection were used here: `Box<{}>`", param.name.ident()),
);
}
err.span_help(
multispan,
format!(
"you could relax the implicit `Sized` bound on `{T}` if it were \
used through indirection like `&{T}` or `Box<{T}>`",
T = param.name.ident(),
),
);
true
}
} }
/// Add a hint to add a missing borrow or remove an unnecessary one. /// Add a hint to add a missing borrow or remove an unnecessary one.
@ -5126,3 +5252,46 @@ fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) {
(ty, refs) (ty, refs)
} }
/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting
/// `param: ?Sized` would be a valid constraint.
struct FindTypeParam {
param: rustc_span::Symbol,
invalid_spans: Vec<Span>,
nested: bool,
}
impl<'v> Visitor<'v> for FindTypeParam {
fn visit_where_predicate(&mut self, _: &'v hir::WherePredicate<'v>) {
// Skip where-clauses, to avoid suggesting indirection for type parameters found there.
}
fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
// We collect the spans of all uses of the "bare" type param, like in `field: T` or
// `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be
// valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized`
// obligations like `Box<T>` and `Vec<T>`, but we perform no extra analysis for those cases
// and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors
// in that case should make what happened clear enough.
match ty.kind {
hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {}
hir::TyKind::Path(hir::QPath::Resolved(None, path))
if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
{
if !self.nested {
debug!(?ty, "FindTypeParam::visit_ty");
self.invalid_spans.push(ty.span);
}
}
hir::TyKind::Path(_) => {
let prev = self.nested;
self.nested = true;
hir::intravisit::walk_ty(self, ty);
self.nested = prev;
}
_ => {
hir::intravisit::walk_ty(self, ty);
}
}
}
}

View File

@ -1,15 +1,12 @@
// ignore-tidy-filelength :( use super::ambiguity::TypeErrCtxtAmbiguityExt as _;
use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _}; use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _};
use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _}; use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _};
use crate::error_reporting::traits::infer_ctxt_ext::InferCtxtExt; use crate::error_reporting::traits::infer_ctxt_ext::InferCtxtExt;
use crate::error_reporting::traits::overflow::TypeErrCtxtOverflowExt; use crate::error_reporting::traits::overflow::TypeErrCtxtOverflowExt;
use crate::error_reporting::traits::to_pretty_impl_header;
use crate::error_reporting::traits::{ambiguity, ambiguity::CandidateSource::*};
use crate::errors::{ use crate::errors::{
AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch,
}; };
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; use crate::infer::error_reporting::TyCategory;
use crate::infer::InferCtxtExt as _; use crate::infer::InferCtxtExt as _;
use crate::infer::{self, InferCtxt}; use crate::infer::{self, InferCtxt};
use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
@ -23,13 +20,13 @@ use core::ops::ControlFlow;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_data_structures::unord::UnordSet; use rustc_data_structures::unord::UnordSet;
use rustc_errors::codes::*; use rustc_errors::codes::*;
use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart}; use rustc_errors::{pluralize, struct_span_code_err, Applicability, StringPart};
use rustc_errors::{Diag, ErrorGuaranteed, StashKey}; use rustc_errors::{Diag, ErrorGuaranteed, StashKey};
use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def::Namespace;
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor; use rustc_hir::intravisit::Visitor;
use rustc_hir::Node;
use rustc_hir::{self as hir, LangItem}; use rustc_hir::{self as hir, LangItem};
use rustc_hir::{GenericParam, Item, Node};
use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::error_reporting::TypeErrCtxt;
use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_infer::infer::{InferOk, TypeTrace};
use rustc_macros::extension; use rustc_macros::extension;
@ -43,8 +40,7 @@ use rustc_middle::ty::print::{
PrintTraitRefExt as _, PrintTraitRefExt as _,
}; };
use rustc_middle::ty::{ use rustc_middle::ty::{
self, SubtypePredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable, self, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast,
TypeVisitableExt, Upcast,
}; };
use rustc_middle::{bug, span_bug}; use rustc_middle::{bug, span_bug};
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
@ -53,8 +49,7 @@ use std::borrow::Cow;
use std::iter; use std::iter;
use super::{ use super::{
ArgKind, CandidateSimilarity, FindExprBySpan, FindTypeParam, GetSafeTransmuteErrorAndReason, ArgKind, CandidateSimilarity, GetSafeTransmuteErrorAndReason, ImplCandidate, UnsatisfiedConst,
HasNumericInferVisitor, ImplCandidate, UnsatisfiedConst,
}; };
pub use rustc_infer::traits::error_reporting::*; pub use rustc_infer::traits::error_reporting::*;
@ -956,7 +951,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
} }
/// When the `E` of the resulting `Result<T, E>` in an expression `foo().bar().baz()?`, /// When the `E` of the resulting `Result<T, E>` in an expression `foo().bar().baz()?`,
/// identify thoe method chain sub-expressions that could or could not have been annotated /// identify those method chain sub-expressions that could or could not have been annotated
/// with `?`. /// with `?`.
fn try_conversion_context( fn try_conversion_context(
&self, &self,
@ -2172,536 +2167,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
Obligation::new(self.tcx, ObligationCause::dummy(), param_env, trait_pred) Obligation::new(self.tcx, ObligationCause::dummy(), param_env, trait_pred)
} }
#[instrument(skip(self), level = "debug")]
fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed {
// Unable to successfully determine, probably means
// insufficient type information, but could mean
// ambiguous impls. The latter *ought* to be a
// coherence violation, so we don't report it here.
let predicate = self.resolve_vars_if_possible(obligation.predicate);
let span = obligation.cause.span;
debug!(?predicate, obligation.cause.code = ?obligation.cause.code());
// Ambiguity errors are often caused as fallout from earlier errors.
// We ignore them if this `infcx` is tainted in some cases below.
let bound_predicate = predicate.kind();
let mut err = match bound_predicate.skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
let trait_ref = bound_predicate.rebind(data.trait_ref);
debug!(?trait_ref);
if let Err(e) = predicate.error_reported() {
return e;
}
if let Err(guar) = self.tcx.ensure().coherent_trait(trait_ref.def_id()) {
// Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
// other `Foo` impls are incoherent.
return guar;
}
// This is kind of a hack: it frequently happens that some earlier
// error prevents types from being fully inferred, and then we get
// a bunch of uninteresting errors saying something like "<generic
// #0> doesn't implement Sized". It may even be true that we
// could just skip over all checks where the self-ty is an
// inference variable, but I was afraid that there might be an
// inference variable created, registered as an obligation, and
// then never forced by writeback, and hence by skipping here we'd
// be ignoring the fact that we don't KNOW the type works
// out. Though even that would probably be harmless, given that
// we're only talking about builtin traits, which are known to be
// inhabited. We used to check for `self.tcx.sess.has_errors()` to
// avoid inundating the user with unnecessary errors, but we now
// check upstream for type errors and don't add the obligations to
// begin with in those cases.
if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) {
match self.tainted_by_errors() {
None => {
let err = self.emit_inference_failure_err(
obligation.cause.body_id,
span,
trait_ref.self_ty().skip_binder().into(),
ErrorCode::E0282,
false,
);
return err.stash(span, StashKey::MaybeForgetReturn).unwrap();
}
Some(e) => return e,
}
}
// Typically, this ambiguity should only happen if
// there are unresolved type inference variables
// (otherwise it would suggest a coherence
// failure). But given #21974 that is not necessarily
// the case -- we can have multiple where clauses that
// are only distinguished by a region, which results
// in an ambiguity even when all types are fully
// known, since we don't dispatch based on region
// relationships.
// Pick the first generic parameter that still contains inference variables as the one
// we're going to emit an error for. If there are none (see above), fall back to
// a more general error.
let arg = data.trait_ref.args.iter().find(|s| s.has_non_region_infer());
let mut err = if let Some(arg) = arg {
self.emit_inference_failure_err(
obligation.cause.body_id,
span,
arg,
ErrorCode::E0283,
true,
)
} else {
struct_span_code_err!(
self.dcx(),
span,
E0283,
"type annotations needed: cannot satisfy `{}`",
predicate,
)
};
let mut ambiguities = ambiguity::compute_applicable_impls_for_diagnostics(
self.infcx,
&obligation.with(self.tcx, trait_ref),
);
let has_non_region_infer =
trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer());
// It doesn't make sense to talk about applicable impls if there are more than a
// handful of them. If there are a lot of them, but only a few of them have no type
// params, we only show those, as they are more likely to be useful/intended.
if ambiguities.len() > 5 {
let infcx = self.infcx;
if !ambiguities.iter().all(|option| match option {
DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
ParamEnv(_) => true,
}) {
// If not all are blanket impls, we filter blanked impls out.
ambiguities.retain(|option| match option {
DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
ParamEnv(_) => true,
});
}
}
if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
if let Some(e) = self.tainted_by_errors()
&& arg.is_none()
{
// If `arg.is_none()`, then this is probably two param-env
// candidates or impl candidates that are equal modulo lifetimes.
// Therefore, if we've already emitted an error, just skip this
// one, since it's not particularly actionable.
err.cancel();
return e;
}
self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate);
} else {
if let Some(e) = self.tainted_by_errors() {
err.cancel();
return e;
}
err.note(format!("cannot satisfy `{predicate}`"));
let impl_candidates =
self.find_similar_impl_candidates(predicate.as_trait_clause().unwrap());
if impl_candidates.len() < 40 {
self.report_similar_impl_candidates(
impl_candidates.as_slice(),
trait_ref,
obligation.cause.body_id,
&mut err,
false,
obligation.param_env,
);
}
}
if let ObligationCauseCode::WhereClause(def_id, _)
| ObligationCauseCode::WhereClauseInExpr(def_id, ..) = *obligation.cause.code()
{
self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
}
if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack())
&& let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id)
{
let mut expr_finder = FindExprBySpan::new(span, self.tcx);
expr_finder.visit_expr(&body.value);
if let Some(hir::Expr {
kind:
hir::ExprKind::Call(
hir::Expr {
kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
..
},
_,
)
| hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
..
}) = expr_finder.result
&& let [
..,
trait_path_segment @ hir::PathSegment {
res: Res::Def(DefKind::Trait, trait_id),
..
},
hir::PathSegment {
ident: assoc_item_name,
res: Res::Def(_, item_id),
..
},
] = path.segments
&& data.trait_ref.def_id == *trait_id
&& self.tcx.trait_of_item(*item_id) == Some(*trait_id)
&& let None = self.tainted_by_errors()
{
let (verb, noun) = match self.tcx.associated_item(item_id).kind {
ty::AssocKind::Const => ("refer to the", "constant"),
ty::AssocKind::Fn => ("call", "function"),
// This is already covered by E0223, but this following single match
// arm doesn't hurt here.
ty::AssocKind::Type => ("refer to the", "type"),
};
// Replace the more general E0283 with a more specific error
err.cancel();
err = self.dcx().struct_span_err(
span,
format!(
"cannot {verb} associated {noun} on trait without specifying the \
corresponding `impl` type",
),
);
err.code(E0790);
if let Some(local_def_id) = data.trait_ref.def_id.as_local()
&& let hir::Node::Item(hir::Item {
ident: trait_name,
kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs),
..
}) = self.tcx.hir_node_by_def_id(local_def_id)
&& let Some(method_ref) = trait_item_refs
.iter()
.find(|item_ref| item_ref.ident == *assoc_item_name)
{
err.span_label(
method_ref.span,
format!("`{trait_name}::{assoc_item_name}` defined here"),
);
}
err.span_label(span, format!("cannot {verb} associated {noun} of trait"));
let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id);
if let Some(impl_def_id) =
trait_impls.non_blanket_impls().values().flatten().next()
{
let non_blanket_impl_count =
trait_impls.non_blanket_impls().values().flatten().count();
// If there is only one implementation of the trait, suggest using it.
// Otherwise, use a placeholder comment for the implementation.
let (message, self_type) = if non_blanket_impl_count == 1 {
(
"use the fully-qualified path to the only available \
implementation",
format!(
"{}",
self.tcx.type_of(impl_def_id).instantiate_identity()
),
)
} else {
(
"use a fully-qualified path to a specific available \
implementation",
"/* self type */".to_string(),
)
};
let mut suggestions =
vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))];
if let Some(generic_arg) = trait_path_segment.args {
let between_span =
trait_path_segment.ident.span.between(generic_arg.span_ext);
// get rid of :: between Trait and <type>
// must be '::' between them, otherwise the parser won't accept the code
suggestions.push((between_span, "".to_string()));
suggestions
.push((generic_arg.span_ext.shrink_to_hi(), ">".to_string()));
} else {
suggestions.push((
trait_path_segment.ident.span.shrink_to_hi(),
">".to_string(),
));
}
err.multipart_suggestion(
message,
suggestions,
Applicability::MaybeIncorrect,
);
}
}
};
err
}
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
// Same hacky approach as above to avoid deluging user
// with error messages.
if let Err(e) = arg.error_reported() {
return e;
}
if let Some(e) = self.tainted_by_errors() {
return e;
}
self.emit_inference_failure_err(
obligation.cause.body_id,
span,
arg,
ErrorCode::E0282,
false,
)
}
ty::PredicateKind::Subtype(data) => {
if let Err(e) = data.error_reported() {
return e;
}
if let Some(e) = self.tainted_by_errors() {
return e;
}
let SubtypePredicate { a_is_expected: _, a, b } = data;
// both must be type variables, or the other would've been instantiated
assert!(a.is_ty_var() && b.is_ty_var());
self.emit_inference_failure_err(
obligation.cause.body_id,
span,
a.into(),
ErrorCode::E0282,
true,
)
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
if let Err(e) = predicate.error_reported() {
return e;
}
if let Some(e) = self.tainted_by_errors() {
return e;
}
if let Err(guar) =
self.tcx.ensure().coherent_trait(self.tcx.parent(data.projection_term.def_id))
{
// Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
// other `Foo` impls are incoherent.
return guar;
}
let arg = data
.projection_term
.args
.iter()
.chain(Some(data.term.into_arg()))
.find(|g| g.has_non_region_infer());
if let Some(arg) = arg {
self.emit_inference_failure_err(
obligation.cause.body_id,
span,
arg,
ErrorCode::E0284,
true,
)
.with_note(format!("cannot satisfy `{predicate}`"))
} else {
// If we can't find a generic parameter, just print a generic error
struct_span_code_err!(
self.dcx(),
span,
E0284,
"type annotations needed: cannot satisfy `{}`",
predicate,
)
.with_span_label(span, format!("cannot satisfy `{predicate}`"))
}
}
ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => {
if let Err(e) = predicate.error_reported() {
return e;
}
if let Some(e) = self.tainted_by_errors() {
return e;
}
let arg = data.walk().find(|g| g.is_non_region_infer());
if let Some(arg) = arg {
let err = self.emit_inference_failure_err(
obligation.cause.body_id,
span,
arg,
ErrorCode::E0284,
true,
);
err
} else {
// If we can't find a generic parameter, just print a generic error
struct_span_code_err!(
self.dcx(),
span,
E0284,
"type annotations needed: cannot satisfy `{}`",
predicate,
)
.with_span_label(span, format!("cannot satisfy `{predicate}`"))
}
}
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ..)) => self
.emit_inference_failure_err(
obligation.cause.body_id,
span,
ct.into(),
ErrorCode::E0284,
true,
),
ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })
if term.is_infer() =>
{
if let Some(e) = self.tainted_by_errors() {
return e;
}
struct_span_code_err!(
self.dcx(),
span,
E0284,
"type annotations needed: cannot normalize `{alias}`",
)
.with_span_label(span, format!("cannot normalize `{alias}`"))
}
_ => {
if let Some(e) = self.tainted_by_errors() {
return e;
}
struct_span_code_err!(
self.dcx(),
span,
E0284,
"type annotations needed: cannot satisfy `{}`",
predicate,
)
.with_span_label(span, format!("cannot satisfy `{predicate}`"))
}
};
self.note_obligation_cause(&mut err, obligation);
err.emit()
}
fn annotate_source_of_ambiguity(
&self,
err: &mut Diag<'_>,
ambiguities: &[ambiguity::CandidateSource],
predicate: ty::Predicate<'tcx>,
) {
let mut spans = vec![];
let mut crates = vec![];
let mut post = vec![];
let mut has_param_env = false;
for ambiguity in ambiguities {
match ambiguity {
ambiguity::CandidateSource::DefId(impl_def_id) => {
match self.tcx.span_of_impl(*impl_def_id) {
Ok(span) => spans.push(span),
Err(name) => {
crates.push(name);
if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) {
post.push(header);
}
}
}
}
ambiguity::CandidateSource::ParamEnv(span) => {
has_param_env = true;
spans.push(*span);
}
}
}
let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect();
crate_names.sort();
crate_names.dedup();
post.sort();
post.dedup();
if self.tainted_by_errors().is_some()
&& (crate_names.len() == 1
&& spans.len() == 0
&& ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str())
|| predicate.visit_with(&mut HasNumericInferVisitor).is_break())
{
// Avoid complaining about other inference issues for expressions like
// `42 >> 1`, where the types are still `{integer}`, but we want to
// Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too?
// NOTE(eddyb) this was `.cancel()`, but `err`
// is borrowed, so we can't fully defuse it.
err.downgrade_to_delayed_bug();
return;
}
let msg = format!(
"multiple `impl`s{} satisfying `{}` found",
if has_param_env { " or `where` clauses" } else { "" },
predicate
);
let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) {
format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"),)
} else if post.len() == 1 {
format!(": `{}`", post[0])
} else {
String::new()
};
match (spans.len(), crates.len(), crate_names.len()) {
(0, 0, 0) => {
err.note(format!("cannot satisfy `{predicate}`"));
}
(0, _, 1) => {
err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,));
}
(0, _, _) => {
err.note(format!(
"{} in the following crates: {}{}",
msg,
crate_names.join(", "),
post,
));
}
(_, 0, 0) => {
let span: MultiSpan = spans.into();
err.span_note(span, msg);
}
(_, 1, 1) => {
let span: MultiSpan = spans.into();
err.span_note(span, msg);
err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,));
}
_ => {
let span: MultiSpan = spans.into();
err.span_note(span, msg);
err.note(format!(
"and more `impl`s found in the following crates: {}{}",
crate_names.join(", "),
post,
));
}
}
}
/// Returns `true` if the trait predicate may apply for *some* assignment /// Returns `true` if the trait predicate may apply for *some* assignment
/// to the type parameters. /// to the type parameters.
fn predicate_can_apply( fn predicate_can_apply(
@ -2769,136 +2234,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
} }
} }
#[instrument(level = "debug", skip_all)]
fn suggest_unsized_bound_if_applicable(
&self,
err: &mut Diag<'_>,
obligation: &PredicateObligation<'tcx>,
) {
let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
obligation.predicate.kind().skip_binder()
else {
return;
};
let (ObligationCauseCode::WhereClause(item_def_id, span)
| ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..)) =
*obligation.cause.code().peel_derives()
else {
return;
};
if span.is_dummy() {
return;
}
debug!(?pred, ?item_def_id, ?span);
let (Some(node), true) = (
self.tcx.hir().get_if_local(item_def_id),
self.tcx.is_lang_item(pred.def_id(), LangItem::Sized),
) else {
return;
};
self.maybe_suggest_unsized_generics(err, span, node);
}
#[instrument(level = "debug", skip_all)]
fn maybe_suggest_unsized_generics(&self, err: &mut Diag<'_>, span: Span, node: Node<'tcx>) {
let Some(generics) = node.generics() else {
return;
};
let sized_trait = self.tcx.lang_items().sized_trait();
debug!(?generics.params);
debug!(?generics.predicates);
let Some(param) = generics.params.iter().find(|param| param.span == span) else {
return;
};
// Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
// `Sized` bound is there intentionally and we don't need to suggest relaxing it.
let explicitly_sized = generics
.bounds_for_param(param.def_id)
.flat_map(|bp| bp.bounds)
.any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait);
if explicitly_sized {
return;
}
debug!(?param);
match node {
hir::Node::Item(
item @ hir::Item {
// Only suggest indirection for uses of type parameters in ADTs.
kind:
hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..),
..
},
) => {
if self.maybe_indirection_for_unsized(err, item, param) {
return;
}
}
_ => {}
};
// Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`.
let (span, separator, open_paren_sp) =
if let Some((s, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) {
(s, " +", open_paren_sp)
} else {
(param.name.ident().span.shrink_to_hi(), ":", None)
};
let mut suggs = vec![];
let suggestion = format!("{separator} ?Sized");
if let Some(open_paren_sp) = open_paren_sp {
suggs.push((open_paren_sp, "(".to_string()));
suggs.push((span, format!("){suggestion}")));
} else {
suggs.push((span, suggestion));
}
err.multipart_suggestion_verbose(
"consider relaxing the implicit `Sized` restriction",
suggs,
Applicability::MachineApplicable,
);
}
fn maybe_indirection_for_unsized(
&self,
err: &mut Diag<'_>,
item: &Item<'tcx>,
param: &GenericParam<'tcx>,
) -> bool {
// Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a
// borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);`
// is not. Look for invalid "bare" parameter uses, and suggest using indirection.
let mut visitor =
FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false };
visitor.visit_item(item);
if visitor.invalid_spans.is_empty() {
return false;
}
let mut multispan: MultiSpan = param.span.into();
multispan.push_span_label(
param.span,
format!("this could be changed to `{}: ?Sized`...", param.name.ident()),
);
for sp in visitor.invalid_spans {
multispan.push_span_label(
sp,
format!("...if indirection were used here: `Box<{}>`", param.name.ident()),
);
}
err.span_help(
multispan,
format!(
"you could relax the implicit `Sized` bound on `{T}` if it were \
used through indirection like `&{T}` or `Box<{T}>`",
T = param.name.ident(),
),
);
true
}
fn is_recursive_obligation( fn is_recursive_obligation(
&self, &self,
obligated_types: &mut Vec<Ty<'tcx>>, obligated_types: &mut Vec<Ty<'tcx>>,