use super::{
- EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation,
- SelectionContext,
+ DerivedObligationCause, EvaluationResult, ImplDerivedObligationCause, Obligation,
+ ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext,
};
use crate::autoderef::Autoderef;
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>;
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
- );
+ ) -> bool;
fn suggest_add_reference_to_arg(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
- );
+ ) -> bool;
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic);
err: &mut Diagnostic,
span: Span,
trait_pred: ty::PolyTraitPredicate<'tcx>,
- );
+ ) -> bool;
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span>;
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.
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 =
| 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.
})) => {
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);
let sugg = format!("({})", args);
(format!("{}{}", ident, sugg), sugg)
}
- _ => return,
+ _ => return false,
};
if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. })
{
} else {
err.help(&format!("{}: `{}`", msg, snippet));
}
+ true
}
fn suggest_add_reference_to_arg(
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 {
String::new(),
Applicability::MachineApplicable,
);
+ suggested = true;
break;
}
}
}
+ suggested
}
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) {
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);
{
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> {