Rollup merge of #91873 - estebank:mention-impls-for-unsatisfied-trait, r=davidtwco
Mention implementers of unsatisfied trait When encountering an unsatisfied trait bound, if there are no other suggestions, mention all the types that *do* implement that trait: ``` error[E0277]: the trait bound `f32: Foo` is not satisfied --> $DIR/impl_wf.rs:22:6 | LL | impl Baz<f32> for f32 { } | ^^^^^^^^ the trait `Foo` is not implemented for `f32` | = help: the trait `Foo` is implemented for `i32` note: required by a bound in `Baz` --> $DIR/impl_wf.rs:18:31 | LL | trait Baz<U: ?Sized> where U: Foo { } | ^^^ required by this bound in `Baz` ``` ``` error[E0277]: the trait bound `u32: Foo` is not satisfied --> $DIR/associated-types-path-2.rs:29:5 | LL | f1(2u32, 4u32); | ^^ the trait `Foo` is not implemented for `u32` | = help: the trait `Foo` is implemented for `i32` note: required by a bound in `f1` --> $DIR/associated-types-path-2.rs:13:14 | LL | pub fn f1<T: Foo>(a: T, x: T::A) {} | ^^^ required by this bound in `f1` ``` Suggest dereferencing in more cases. Fix #87437, fix #90970.
This commit is contained in:
commit
a5c81695a9
136 changed files with 1242 additions and 292 deletions
|
@ -2,10 +2,11 @@ pub mod on_unimplemented;
|
|||
pub mod suggestions;
|
||||
|
||||
use super::{
|
||||
EvaluationResult, FulfillmentContext, FulfillmentError, FulfillmentErrorCode,
|
||||
MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode,
|
||||
OnUnimplementedDirective, OnUnimplementedNote, OutputTypeParameterMismatch, Overflow,
|
||||
PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe,
|
||||
DerivedObligationCause, EvaluationResult, FulfillmentContext, FulfillmentError,
|
||||
FulfillmentErrorCode, ImplDerivedObligationCause, MismatchedProjectionTypes, Obligation,
|
||||
ObligationCause, ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote,
|
||||
OutputTypeParameterMismatch, Overflow, PredicateObligation, SelectionContext, SelectionError,
|
||||
TraitNotObjectSafe,
|
||||
};
|
||||
|
||||
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
|
||||
|
@ -14,6 +15,7 @@ use crate::infer::{self, InferCtxt, TyCtxtInferExt};
|
|||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{
|
||||
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
|
||||
Style,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
|
@ -28,7 +30,7 @@ use rustc_middle::traits::select::OverflowError;
|
|||
use rustc_middle::ty::error::ExpectedFound;
|
||||
use rustc_middle::ty::fold::TypeFolder;
|
||||
use rustc_middle::ty::{
|
||||
self, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable,
|
||||
self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
|
||||
};
|
||||
use rustc_span::symbol::{kw, sym};
|
||||
use rustc_span::{ExpnKind, MultiSpan, Span, DUMMY_SP};
|
||||
|
@ -354,7 +356,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
let have_alt_message = message.is_some() || label.is_some();
|
||||
let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id());
|
||||
let is_unsize =
|
||||
{ Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait() };
|
||||
Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait();
|
||||
let (message, note, append_const_msg) = if is_try_conversion {
|
||||
(
|
||||
Some(format!(
|
||||
|
@ -363,7 +365,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
)),
|
||||
Some(
|
||||
"the question mark operation (`?`) implicitly performs a \
|
||||
conversion on the error value using the `From` trait"
|
||||
conversion on the error value using the `From` trait"
|
||||
.to_owned(),
|
||||
),
|
||||
Some(None),
|
||||
|
@ -525,10 +527,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref);
|
||||
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
|
||||
self.suggest_fn_call(&obligation, &mut err, trait_predicate);
|
||||
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
|
||||
self.suggest_semicolon_removal(
|
||||
let mut suggested =
|
||||
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
|
||||
suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate);
|
||||
suggested |=
|
||||
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
|
||||
suggested |= self.suggest_semicolon_removal(
|
||||
&obligation,
|
||||
&mut err,
|
||||
span,
|
||||
|
@ -654,10 +658,80 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
trait_predicate,
|
||||
obligation.cause.body_id,
|
||||
);
|
||||
} else if !have_alt_message {
|
||||
} else if !suggested {
|
||||
// Can't show anything else useful, try to find similar impls.
|
||||
let impl_candidates = self.find_similar_impl_candidates(trait_ref);
|
||||
self.report_similar_impl_candidates(impl_candidates, &mut err);
|
||||
if !self.report_similar_impl_candidates(
|
||||
impl_candidates,
|
||||
trait_ref,
|
||||
&mut err,
|
||||
) {
|
||||
// This is *almost* equivalent to
|
||||
// `obligation.cause.code().peel_derives()`, but it gives us the
|
||||
// trait predicate for that corresponding root obligation. This
|
||||
// lets us get a derived obligation from a type parameter, like
|
||||
// when calling `string.strip_suffix(p)` where `p` is *not* an
|
||||
// implementer of `Pattern<'_>`.
|
||||
let mut code = obligation.cause.code();
|
||||
let mut trait_pred = trait_predicate;
|
||||
let mut peeled = false;
|
||||
loop {
|
||||
match &*code {
|
||||
ObligationCauseCode::FunctionArgumentObligation {
|
||||
parent_code,
|
||||
..
|
||||
} => {
|
||||
code = &parent_code;
|
||||
}
|
||||
ObligationCauseCode::ImplDerivedObligation(
|
||||
box ImplDerivedObligationCause {
|
||||
derived:
|
||||
DerivedObligationCause {
|
||||
parent_code,
|
||||
parent_trait_pred,
|
||||
},
|
||||
..
|
||||
},
|
||||
)
|
||||
| ObligationCauseCode::BuiltinDerivedObligation(
|
||||
DerivedObligationCause {
|
||||
parent_code,
|
||||
parent_trait_pred,
|
||||
},
|
||||
)
|
||||
| ObligationCauseCode::DerivedObligation(
|
||||
DerivedObligationCause {
|
||||
parent_code,
|
||||
parent_trait_pred,
|
||||
},
|
||||
) => {
|
||||
peeled = true;
|
||||
code = &parent_code;
|
||||
trait_pred = *parent_trait_pred;
|
||||
}
|
||||
_ => break,
|
||||
};
|
||||
}
|
||||
let def_id = trait_pred.def_id();
|
||||
// Mention *all* the `impl`s for the *top most* obligation, the
|
||||
// user might have meant to use one of them, if any found. We skip
|
||||
// auto-traits or fundamental traits that might not be exactly what
|
||||
// the user might expect to be presented with. Instead this is
|
||||
// useful for less general traits.
|
||||
if peeled
|
||||
&& !self.tcx.trait_is_auto(def_id)
|
||||
&& !self.tcx.lang_items().items().contains(&Some(def_id))
|
||||
{
|
||||
let trait_ref = trait_pred.to_poly_trait_ref();
|
||||
let impl_candidates =
|
||||
self.find_similar_impl_candidates(trait_ref);
|
||||
self.report_similar_impl_candidates(
|
||||
impl_candidates,
|
||||
trait_ref,
|
||||
&mut err,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Changing mutability doesn't make a difference to whether we have
|
||||
|
@ -682,7 +756,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
});
|
||||
let unit_obligation = obligation.with(predicate.to_predicate(tcx));
|
||||
if self.predicate_may_hold(&unit_obligation) {
|
||||
err.note("this trait is implemented for `()`");
|
||||
err.note(
|
||||
"this error might have been caused by changes to \
|
||||
Rust's type-inference algorithm (see issue #48950 \
|
||||
|
@ -1307,8 +1380,9 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
|
|||
fn report_similar_impl_candidates(
|
||||
&self,
|
||||
impl_candidates: Vec<ImplCandidate<'tcx>>,
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
err: &mut Diagnostic,
|
||||
);
|
||||
) -> bool;
|
||||
|
||||
/// Gets the parent trait chain start
|
||||
fn get_parent_trait_ref(
|
||||
|
@ -1319,7 +1393,11 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
|
|||
/// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
|
||||
/// with the same path as `trait_ref`, a help message about
|
||||
/// a probable version mismatch is added to `err`
|
||||
fn note_version_mismatch(&self, err: &mut Diagnostic, trait_ref: &ty::PolyTraitRef<'tcx>);
|
||||
fn note_version_mismatch(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
trait_ref: &ty::PolyTraitRef<'tcx>,
|
||||
) -> bool;
|
||||
|
||||
/// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the
|
||||
/// `trait_ref`.
|
||||
|
@ -1681,14 +1759,82 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
|||
fn report_similar_impl_candidates(
|
||||
&self,
|
||||
impl_candidates: Vec<ImplCandidate<'tcx>>,
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
err: &mut Diagnostic,
|
||||
) {
|
||||
if impl_candidates.is_empty() {
|
||||
return;
|
||||
}
|
||||
) -> bool {
|
||||
let report = |mut candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| {
|
||||
candidates.sort();
|
||||
candidates.dedup();
|
||||
let len = candidates.len();
|
||||
if candidates.len() == 0 {
|
||||
return false;
|
||||
}
|
||||
if candidates.len() == 1 {
|
||||
err.highlighted_help(vec![
|
||||
(
|
||||
format!("the trait `{}` ", candidates[0].print_only_trait_path()),
|
||||
Style::NoStyle,
|
||||
),
|
||||
("is".to_string(), Style::Highlight),
|
||||
(" implemented for `".to_string(), Style::NoStyle),
|
||||
(candidates[0].self_ty().to_string(), Style::Highlight),
|
||||
("`".to_string(), Style::NoStyle),
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
let trait_ref = TraitRef::identity(self.tcx, candidates[0].def_id);
|
||||
// Check if the trait is the same in all cases. If so, we'll only show the type.
|
||||
let mut traits: Vec<_> =
|
||||
candidates.iter().map(|c| c.print_only_trait_path().to_string()).collect();
|
||||
traits.sort();
|
||||
traits.dedup();
|
||||
|
||||
let len = impl_candidates.len();
|
||||
let end = if impl_candidates.len() <= 5 { impl_candidates.len() } else { 4 };
|
||||
let mut candidates: Vec<String> = candidates
|
||||
.into_iter()
|
||||
.map(|c| {
|
||||
if traits.len() == 1 {
|
||||
format!("\n {}", c.self_ty())
|
||||
} else {
|
||||
format!("\n {}", c)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
candidates.sort();
|
||||
candidates.dedup();
|
||||
let end = if candidates.len() <= 9 { candidates.len() } else { 8 };
|
||||
err.help(&format!(
|
||||
"the following other types implement trait `{}`:{}{}",
|
||||
trait_ref.print_only_trait_path(),
|
||||
candidates[..end].join(""),
|
||||
if len > 9 { format!("\nand {} others", len - 8) } else { String::new() }
|
||||
));
|
||||
true
|
||||
};
|
||||
|
||||
let def_id = trait_ref.def_id();
|
||||
if impl_candidates.is_empty() {
|
||||
if self.tcx.trait_is_auto(def_id)
|
||||
|| self.tcx.lang_items().items().contains(&Some(def_id))
|
||||
|| self.tcx.get_diagnostic_name(def_id).is_some()
|
||||
{
|
||||
// Mentioning implementers of `Copy`, `Debug` and friends is not useful.
|
||||
return false;
|
||||
}
|
||||
let normalized_impl_candidates: Vec<_> = self
|
||||
.tcx
|
||||
.all_impls(def_id)
|
||||
// Ignore automatically derived impls and `!Trait` impls.
|
||||
.filter(|&def_id| {
|
||||
self.tcx.impl_polarity(def_id) != ty::ImplPolarity::Negative
|
||||
|| self.tcx.is_builtin_derive(def_id)
|
||||
})
|
||||
.filter_map(|def_id| self.tcx.impl_trait_ref(def_id))
|
||||
// Avoid mentioning type parameters.
|
||||
.filter(|trait_ref| !matches!(trait_ref.self_ty().kind(), ty::Param(_)))
|
||||
.collect();
|
||||
return report(normalized_impl_candidates, err);
|
||||
}
|
||||
|
||||
let normalize = |candidate| {
|
||||
self.tcx.infer_ctxt().enter(|ref infcx| {
|
||||
|
@ -1697,8 +1843,8 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
|||
.normalize(candidate)
|
||||
.ok();
|
||||
match normalized {
|
||||
Some(normalized) => format!("\n {}", normalized.value),
|
||||
None => format!("\n {}", candidate),
|
||||
Some(normalized) => normalized.value,
|
||||
None => candidate,
|
||||
}
|
||||
})
|
||||
};
|
||||
|
@ -1717,17 +1863,14 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
normalized_impl_candidates_and_similarities.sort();
|
||||
normalized_impl_candidates_and_similarities.dedup();
|
||||
|
||||
let normalized_impl_candidates = normalized_impl_candidates_and_similarities
|
||||
.into_iter()
|
||||
.map(|(_, normalized)| normalized)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
err.help(&format!(
|
||||
"the following implementations were found:{}{}",
|
||||
normalized_impl_candidates[..end].join(""),
|
||||
if len > 5 { format!("\nand {} others", len - 4) } else { String::new() }
|
||||
));
|
||||
report(normalized_impl_candidates, err)
|
||||
}
|
||||
|
||||
/// Gets the parent trait chain start
|
||||
|
@ -1758,7 +1901,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
|||
/// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
|
||||
/// with the same path as `trait_ref`, a help message about
|
||||
/// a probable version mismatch is added to `err`
|
||||
fn note_version_mismatch(&self, err: &mut Diagnostic, trait_ref: &ty::PolyTraitRef<'tcx>) {
|
||||
fn note_version_mismatch(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
trait_ref: &ty::PolyTraitRef<'tcx>,
|
||||
) -> bool {
|
||||
let get_trait_impl = |trait_def_id| {
|
||||
self.tcx.find_map_relevant_impl(trait_def_id, trait_ref.skip_binder().self_ty(), Some)
|
||||
};
|
||||
|
@ -1769,6 +1916,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
|||
.filter(|trait_def_id| *trait_def_id != trait_ref.def_id())
|
||||
.filter(|trait_def_id| self.tcx.def_path_str(*trait_def_id) == required_trait_path)
|
||||
.collect();
|
||||
let mut suggested = false;
|
||||
for trait_with_same_path in traits_with_same_path {
|
||||
if let Some(impl_def_id) = get_trait_impl(trait_with_same_path) {
|
||||
let impl_span = self.tcx.def_span(impl_def_id);
|
||||
|
@ -1779,8 +1927,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
|||
trait_crate
|
||||
);
|
||||
err.note(&crate_msg);
|
||||
suggested = true;
|
||||
}
|
||||
}
|
||||
suggested
|
||||
}
|
||||
|
||||
fn mk_trait_obligation_with_new_self_ty(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::{
|
||||
EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation,
|
||||
SelectionContext,
|
||||
DerivedObligationCause, EvaluationResult, ImplDerivedObligationCause, Obligation,
|
||||
ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext,
|
||||
};
|
||||
|
||||
use crate::autoderef::Autoderef;
|
||||
|
@ -58,7 +58,7 @@ pub trait InferCtxtExt<'tcx> {
|
|||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diagnostic,
|
||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
);
|
||||
) -> bool;
|
||||
|
||||
fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<String>;
|
||||
|
||||
|
@ -67,7 +67,7 @@ pub trait InferCtxtExt<'tcx> {
|
|||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diagnostic,
|
||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
);
|
||||
) -> bool;
|
||||
|
||||
fn suggest_add_reference_to_arg(
|
||||
&self,
|
||||
|
@ -90,7 +90,7 @@ pub trait InferCtxtExt<'tcx> {
|
|||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diagnostic,
|
||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
);
|
||||
) -> bool;
|
||||
|
||||
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic);
|
||||
|
||||
|
@ -107,7 +107,7 @@ pub trait InferCtxtExt<'tcx> {
|
|||
err: &mut Diagnostic,
|
||||
span: Span,
|
||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
);
|
||||
) -> bool;
|
||||
|
||||
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span>;
|
||||
|
||||
|
@ -502,54 +502,87 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diagnostic,
|
||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
) {
|
||||
) -> bool {
|
||||
// It only make sense when suggesting dereferences for arguments
|
||||
let code = if let ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } =
|
||||
obligation.cause.code()
|
||||
{
|
||||
parent_code.clone()
|
||||
} else {
|
||||
return;
|
||||
let ObligationCauseCode::FunctionArgumentObligation { .. } = obligation.cause.code() else {
|
||||
return false;
|
||||
};
|
||||
let param_env = obligation.param_env;
|
||||
let body_id = obligation.cause.body_id;
|
||||
let span = obligation.cause.span;
|
||||
let real_trait_pred = match &*code {
|
||||
ObligationCauseCode::ImplDerivedObligation(cause) => cause.derived.parent_trait_pred,
|
||||
ObligationCauseCode::DerivedObligation(cause)
|
||||
| ObligationCauseCode::BuiltinDerivedObligation(cause) => cause.parent_trait_pred,
|
||||
_ => trait_pred,
|
||||
};
|
||||
let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else {
|
||||
return;
|
||||
};
|
||||
let mut real_trait_pred = trait_pred;
|
||||
let mut code = obligation.cause.code();
|
||||
loop {
|
||||
match &code {
|
||||
ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => {
|
||||
code = &parent_code;
|
||||
}
|
||||
ObligationCauseCode::ImplDerivedObligation(box ImplDerivedObligationCause {
|
||||
derived: DerivedObligationCause { parent_code, parent_trait_pred },
|
||||
..
|
||||
})
|
||||
| ObligationCauseCode::BuiltinDerivedObligation(DerivedObligationCause {
|
||||
parent_code,
|
||||
parent_trait_pred,
|
||||
})
|
||||
| ObligationCauseCode::DerivedObligation(DerivedObligationCause {
|
||||
parent_code,
|
||||
parent_trait_pred,
|
||||
}) => {
|
||||
code = &parent_code;
|
||||
real_trait_pred = *parent_trait_pred;
|
||||
}
|
||||
_ => break,
|
||||
};
|
||||
let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
|
||||
let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span);
|
||||
if let Some(steps) = autoderef.find_map(|(ty, steps)| {
|
||||
// Re-add the `&`
|
||||
let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
|
||||
let obligation =
|
||||
self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty);
|
||||
Some(steps).filter(|_| self.predicate_may_hold(&obligation))
|
||||
}) {
|
||||
if steps > 0 {
|
||||
if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) {
|
||||
// Don't care about `&mut` because `DerefMut` is used less
|
||||
// often and user will not expect autoderef happens.
|
||||
if src.starts_with('&') && !src.starts_with("&mut ") {
|
||||
let derefs = "*".repeat(steps);
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"consider adding dereference here",
|
||||
format!("&{}{}", derefs, &src[1..]),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
|
||||
let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span);
|
||||
if let Some(steps) = autoderef.find_map(|(ty, steps)| {
|
||||
// Re-add the `&`
|
||||
let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
|
||||
let obligation =
|
||||
self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty);
|
||||
Some(steps).filter(|_| self.predicate_may_hold(&obligation))
|
||||
}) {
|
||||
if steps > 0 {
|
||||
if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) {
|
||||
// Don't care about `&mut` because `DerefMut` is used less
|
||||
// often and user will not expect autoderef happens.
|
||||
if src.starts_with('&') && !src.starts_with("&mut ") {
|
||||
let derefs = "*".repeat(steps);
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"consider dereferencing here",
|
||||
format!("&{}{}", derefs, &src[1..]),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if real_trait_pred != trait_pred {
|
||||
// This branch addresses #87437.
|
||||
let obligation = self.mk_trait_obligation_with_new_self_ty(
|
||||
param_env,
|
||||
real_trait_pred,
|
||||
base_ty,
|
||||
);
|
||||
if self.predicate_may_hold(&obligation) {
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_lo(),
|
||||
"consider dereferencing here",
|
||||
"*".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Given a closure's `DefId`, return the given name of the closure.
|
||||
|
@ -592,22 +625,22 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diagnostic,
|
||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
) {
|
||||
) -> bool {
|
||||
let Some(self_ty) = trait_pred.self_ty().no_bound_vars() else {
|
||||
return;
|
||||
return false;
|
||||
};
|
||||
|
||||
let (def_id, output_ty, callable) = match *self_ty.kind() {
|
||||
ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"),
|
||||
ty::FnDef(def_id, _) => (def_id, self_ty.fn_sig(self.tcx).output(), "function"),
|
||||
_ => return,
|
||||
_ => return false,
|
||||
};
|
||||
let msg = format!("use parentheses to call the {}", callable);
|
||||
|
||||
// `mk_trait_obligation_with_new_self_ty` only works for types with no escaping bound
|
||||
// variables, so bail out if we have any.
|
||||
let Some(output_ty) = output_ty.no_bound_vars() else {
|
||||
return;
|
||||
return false;
|
||||
};
|
||||
|
||||
let new_obligation =
|
||||
|
@ -619,7 +652,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
| EvaluationResult::EvaluatedToOkModuloRegions
|
||||
| EvaluationResult::EvaluatedToAmbig,
|
||||
) => {}
|
||||
_ => return,
|
||||
_ => return false,
|
||||
}
|
||||
let hir = self.tcx.hir();
|
||||
// Get the name of the callable and the arguments to be used in the suggestion.
|
||||
|
@ -630,7 +663,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
})) => {
|
||||
err.span_label(*span, "consider calling this closure");
|
||||
let Some(name) = self.get_closure_name(def_id, err, &msg) else {
|
||||
return;
|
||||
return false;
|
||||
};
|
||||
let args = decl.inputs.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
|
||||
let sugg = format!("({})", args);
|
||||
|
@ -658,7 +691,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
let sugg = format!("({})", args);
|
||||
(format!("{}{}", ident, sugg), sugg)
|
||||
}
|
||||
_ => return,
|
||||
_ => return false,
|
||||
};
|
||||
if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. })
|
||||
{
|
||||
|
@ -675,6 +708,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
} else {
|
||||
err.help(&format!("{}: `{}`", msg, snippet));
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn suggest_add_reference_to_arg(
|
||||
|
@ -845,19 +879,20 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
obligation: &PredicateObligation<'tcx>,
|
||||
err: &mut Diagnostic,
|
||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
) {
|
||||
) -> bool {
|
||||
let span = obligation.cause.span;
|
||||
|
||||
let mut suggested = false;
|
||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
|
||||
let refs_number =
|
||||
snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count();
|
||||
if let Some('\'') = snippet.chars().filter(|c| !c.is_whitespace()).nth(refs_number) {
|
||||
// Do not suggest removal of borrow from type arguments.
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
let Some(mut suggested_ty) = trait_pred.self_ty().no_bound_vars() else {
|
||||
return;
|
||||
return false;
|
||||
};
|
||||
|
||||
for refs_remaining in 0..refs_number {
|
||||
|
@ -893,10 +928,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
suggested = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
suggested
|
||||
}
|
||||
|
||||
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) {
|
||||
|
@ -1033,7 +1070,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
err: &mut Diagnostic,
|
||||
span: Span,
|
||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
) {
|
||||
) -> bool {
|
||||
let hir = self.tcx.hir();
|
||||
let parent_node = hir.get_parent_node(obligation.cause.body_id);
|
||||
let node = hir.find(parent_node);
|
||||
|
@ -1052,7 +1089,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
{
|
||||
let sp = self.tcx.sess.source_map().end_point(stmt.span);
|
||||
err.span_label(sp, "consider removing this semicolon");
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue