Add suggestion to diagnostic when user has array but trait wants slice.
For #90528.
This commit is contained in:
parent
81be7b86d3
commit
8ac7d0eef5
10 changed files with 490 additions and 7 deletions
|
@ -1035,7 +1035,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
// Can't show anything else useful, try to find similar impls.
|
||||
let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
|
||||
if !self.report_similar_impl_candidates(
|
||||
impl_candidates,
|
||||
&impl_candidates,
|
||||
trait_ref,
|
||||
body_hir_id,
|
||||
&mut err,
|
||||
|
@ -1071,7 +1071,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
let impl_candidates =
|
||||
self.find_similar_impl_candidates(trait_pred);
|
||||
self.report_similar_impl_candidates(
|
||||
impl_candidates,
|
||||
&impl_candidates,
|
||||
trait_ref,
|
||||
body_hir_id,
|
||||
&mut err,
|
||||
|
@ -1079,6 +1079,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.maybe_suggest_convert_to_slice(
|
||||
&mut err,
|
||||
trait_ref,
|
||||
impl_candidates.as_slice(),
|
||||
span,
|
||||
);
|
||||
}
|
||||
|
||||
// Changing mutability doesn't make a difference to whether we have
|
||||
|
@ -1529,7 +1536,7 @@ trait InferCtxtPrivExt<'tcx> {
|
|||
|
||||
fn report_similar_impl_candidates(
|
||||
&self,
|
||||
impl_candidates: Vec<ImplCandidate<'tcx>>,
|
||||
impl_candidates: &[ImplCandidate<'tcx>],
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
body_id: hir::HirId,
|
||||
err: &mut Diagnostic,
|
||||
|
@ -2027,7 +2034,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
|
||||
fn report_similar_impl_candidates(
|
||||
&self,
|
||||
impl_candidates: Vec<ImplCandidate<'tcx>>,
|
||||
impl_candidates: &[ImplCandidate<'tcx>],
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
body_id: hir::HirId,
|
||||
err: &mut Diagnostic,
|
||||
|
@ -2138,7 +2145,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
// Prefer more similar candidates first, then sort lexicographically
|
||||
// by their normalized string representation.
|
||||
let mut normalized_impl_candidates_and_similarities = impl_candidates
|
||||
.into_iter()
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|ImplCandidate { trait_ref, similarity }| {
|
||||
// FIXME(compiler-errors): This should be using `NormalizeExt::normalize`
|
||||
let normalized = self
|
||||
|
@ -2351,7 +2359,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
let hir =
|
||||
self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id);
|
||||
self.report_similar_impl_candidates(
|
||||
impl_candidates,
|
||||
impl_candidates.as_slice(),
|
||||
trait_ref,
|
||||
body_id.map(|id| id.hir_id).unwrap_or(hir),
|
||||
&mut err,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// ignore-tidy-filelength
|
||||
|
||||
use super::{
|
||||
DefIdOrName, FindExprBySpan, Obligation, ObligationCause, ObligationCauseCode,
|
||||
DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode,
|
||||
PredicateObligation,
|
||||
};
|
||||
|
||||
|
@ -382,6 +382,14 @@ pub trait TypeErrCtxtExt<'tcx> {
|
|||
body_id: hir::HirId,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>>;
|
||||
|
||||
fn maybe_suggest_convert_to_slice(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
|
||||
candidate_impls: &[ImplCandidate<'tcx>],
|
||||
span: Span,
|
||||
);
|
||||
}
|
||||
|
||||
fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {
|
||||
|
@ -3826,6 +3834,73 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
assocs_in_this_method
|
||||
}
|
||||
|
||||
/// If the type that failed selection is an array or a reference to an array,
|
||||
/// but the trait is implemented for slices, suggest that the user converts
|
||||
/// the array into a slice.
|
||||
fn maybe_suggest_convert_to_slice(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
|
||||
candidate_impls: &[ImplCandidate<'tcx>],
|
||||
span: Span,
|
||||
) {
|
||||
// Three cases where we can make a suggestion:
|
||||
// 1. `[T; _]` (array of T)
|
||||
// 2. `&[T; _]` (reference to array of T)
|
||||
// 3. `&mut [T; _]` (mutable reference to array of T)
|
||||
let (element_ty, mut mutability) = match *trait_ref.skip_binder().self_ty().kind() {
|
||||
ty::Array(element_ty, _) => (element_ty, None),
|
||||
|
||||
ty::Ref(_, pointee_ty, mutability) => match *pointee_ty.kind() {
|
||||
ty::Array(element_ty, _) => (element_ty, Some(mutability)),
|
||||
_ => return,
|
||||
},
|
||||
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// Go through all the candidate impls to see if any of them is for
|
||||
// slices of `element_ty` with `mutability`.
|
||||
let mut is_slice = |candidate: Ty<'tcx>| match *candidate.kind() {
|
||||
ty::RawPtr(ty::TypeAndMut { ty: t, mutbl: m }) | ty::Ref(_, t, m) => {
|
||||
if matches!(*t.kind(), ty::Slice(e) if e == element_ty)
|
||||
&& m == mutability.unwrap_or(m)
|
||||
{
|
||||
// Use the candidate's mutability going forward.
|
||||
mutability = Some(m);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
// Grab the first candidate that matches, if any, and make a suggestion.
|
||||
if let Some(slice_ty) = candidate_impls
|
||||
.iter()
|
||||
.map(|trait_ref| trait_ref.trait_ref.self_ty())
|
||||
.filter(|t| is_slice(*t))
|
||||
.next()
|
||||
{
|
||||
let msg = &format!("convert the array to a `{}` slice instead", slice_ty);
|
||||
|
||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
|
||||
let mut suggestions = vec![];
|
||||
if snippet.starts_with('&') {
|
||||
} else if let Some(hir::Mutability::Mut) = mutability {
|
||||
suggestions.push((span.shrink_to_lo(), "&mut ".into()));
|
||||
} else {
|
||||
suggestions.push((span.shrink_to_lo(), "&".into()));
|
||||
}
|
||||
suggestions.push((span.shrink_to_hi(), "[..]".into()));
|
||||
err.multipart_suggestion_verbose(msg, suggestions, Applicability::MaybeIncorrect);
|
||||
} else {
|
||||
err.span_help(span, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a hint to add a missing borrow or remove an unnecessary one.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue