mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-26 13:01:27 +03:00
Rollup merge of #155219 - nataliakokoromyti:fix-155088-borrow-suggestion-v2, r=JohnTitor
Do not suggest borrowing enclosing calls for nested where-clause obligations In rust-lang/rust#155088, the compiler was blaming the whole call expr instead of the value that actually failed the trait bound, so for foo(&[String::from("a")]) it was suggesting stuff like &foo(...). I changed the suggestion logic so it only emits borrow help if the expr it found actually matches the failed self type, and used the same check for the “similar impl exists” help too. So now the compiler should give the normal error + required bound note. Fix rust-lang/rust#155088
This commit is contained in:
@@ -363,6 +363,7 @@ pub(super) fn maybe_report_ambiguity(
|
||||
if impl_candidates.len() < 40 {
|
||||
self.report_similar_impl_candidates(
|
||||
impl_candidates.as_slice(),
|
||||
obligation,
|
||||
trait_pred,
|
||||
obligation.cause.body_id,
|
||||
&mut err,
|
||||
|
||||
@@ -1973,6 +1973,7 @@ pub(super) fn find_similar_impl_candidates(
|
||||
pub(super) fn report_similar_impl_candidates(
|
||||
&self,
|
||||
impl_candidates: &[ImplCandidate<'tcx>],
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
body_def_id: LocalDefId,
|
||||
err: &mut Diag<'_>,
|
||||
@@ -2037,6 +2038,20 @@ pub(super) fn report_similar_impl_candidates(
|
||||
};
|
||||
|
||||
if let [single] = &impl_candidates {
|
||||
let self_ty = trait_pred.skip_binder().self_ty();
|
||||
if !self_ty.has_escaping_bound_vars() {
|
||||
let self_ty = self.tcx.instantiate_bound_regions_with_erased(trait_pred.self_ty());
|
||||
if let ty::Ref(_, inner_ty, _) = self_ty.kind()
|
||||
&& self.can_eq(param_env, single.trait_ref.self_ty(), *inner_ty)
|
||||
&& !self.where_clause_expr_matches_failed_self_ty(obligation, self_ty)
|
||||
{
|
||||
// Avoid pointing at a nearby impl like `String: Borrow<str>` when the
|
||||
// failing obligation comes from something nested inside an enclosing call
|
||||
// expression such as `foo(&[String::from("a")])`.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a single implementation, try to unify it with the trait ref
|
||||
// that failed. This should uncover a better hint for what *is* implemented.
|
||||
if self.probe(|_| {
|
||||
@@ -2456,6 +2471,7 @@ fn report_similar_impl_candidates_for_root_obligation(
|
||||
let impl_candidates = self.find_similar_impl_candidates(trait_pred);
|
||||
self.report_similar_impl_candidates(
|
||||
&impl_candidates,
|
||||
obligation,
|
||||
trait_pred,
|
||||
body_def_id,
|
||||
err,
|
||||
@@ -3137,6 +3153,7 @@ fn try_to_add_help_message(
|
||||
let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
|
||||
if !self.report_similar_impl_candidates(
|
||||
&impl_candidates,
|
||||
obligation,
|
||||
trait_predicate,
|
||||
body_def_id,
|
||||
err,
|
||||
|
||||
@@ -1584,6 +1584,39 @@ pub fn extract_callable_info(
|
||||
if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
|
||||
}
|
||||
|
||||
pub(super) fn where_clause_expr_matches_failed_self_ty(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
old_self_ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let ObligationCauseCode::WhereClauseInExpr(..) = obligation.cause.code() else {
|
||||
return true;
|
||||
};
|
||||
let (Some(typeck_results), Some(body)) = (
|
||||
self.typeck_results.as_ref(),
|
||||
self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id),
|
||||
) else {
|
||||
return true;
|
||||
};
|
||||
|
||||
let mut expr_finder = FindExprBySpan::new(obligation.cause.span, self.tcx);
|
||||
expr_finder.visit_expr(body.value);
|
||||
let Some(expr) = expr_finder.result else {
|
||||
return true;
|
||||
};
|
||||
|
||||
let inner_old_self_ty = match old_self_ty.kind() {
|
||||
ty::Ref(_, inner_ty, _) => Some(*inner_ty),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
[typeck_results.expr_ty_adjusted_opt(expr)].into_iter().flatten().any(|expr_ty| {
|
||||
self.can_eq(obligation.param_env, expr_ty, old_self_ty)
|
||||
|| inner_old_self_ty
|
||||
.is_some_and(|inner_ty| self.can_eq(obligation.param_env, expr_ty, inner_ty))
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn suggest_add_reference_to_arg(
|
||||
&self,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
@@ -1857,6 +1890,15 @@ pub(super) fn suggest_add_reference_to_arg(
|
||||
if let hir::ExprKind::AddrOf(_, _, _) = expr.kind {
|
||||
return false;
|
||||
}
|
||||
let old_self_ty = old_pred.skip_binder().self_ty();
|
||||
if !old_self_ty.has_escaping_bound_vars()
|
||||
&& !self.where_clause_expr_matches_failed_self_ty(
|
||||
obligation,
|
||||
self.tcx.instantiate_bound_regions_with_erased(old_pred.self_ty()),
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
let needs_parens_post = expr_needs_parens(expr);
|
||||
let needs_parens_pre = match self.tcx.parent_hir_node(expr.hir_id) {
|
||||
Node::Expr(e)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
use std::borrow::Borrow;
|
||||
|
||||
fn foo(_v: impl IntoIterator<Item = impl Borrow<str>>) {}
|
||||
|
||||
fn main() {
|
||||
foo(&[String::from("a")]);
|
||||
//~^ ERROR the trait bound `&String: Borrow<str>` is not satisfied
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
error[E0277]: the trait bound `&String: Borrow<str>` is not satisfied
|
||||
--> $DIR/dont-suggest-borrow-whole-call-issue-155088.rs:6:5
|
||||
|
|
||||
LL | foo(&[String::from("a")]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Borrow<str>` is not implemented for `&String`
|
||||
|
|
||||
note: required by a bound in `foo`
|
||||
--> $DIR/dont-suggest-borrow-whole-call-issue-155088.rs:3:42
|
||||
|
|
||||
LL | fn foo(_v: impl IntoIterator<Item = impl Borrow<str>>) {}
|
||||
| ^^^^^^^^^^^ required by this bound in `foo`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
Reference in New Issue
Block a user