Rollup merge of #132469 - estebank:issue-132041, r=Nadrieril

Do not suggest borrow that is already there in fully-qualified call

When encountering `&str::from("value")` do not suggest `&&str::from("value")`.

Fix #132041.
This commit is contained in:
Matthias Krüger 2025-07-07 19:55:31 +02:00 committed by GitHub
commit 00a4418158
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 169 additions and 7 deletions

View file

@ -1187,6 +1187,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
has_custom_message: bool,
) -> bool {
let span = obligation.cause.span;
let param_env = obligation.param_env;
let mk_result = |trait_pred_and_new_ty| {
let obligation =
self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty);
self.predicate_must_hold_modulo_regions(&obligation)
};
let code = match obligation.cause.code() {
ObligationCauseCode::FunctionArg { parent_code, .. } => parent_code,
@ -1195,6 +1202,76 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
c @ ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, _)
if self.tcx.hir_span(*hir_id).lo() == span.lo() =>
{
// `hir_id` corresponds to the HIR node that introduced a `where`-clause obligation.
// If that obligation comes from a type in an associated method call, we need
// special handling here.
if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(*hir_id)
&& let hir::ExprKind::Call(base, _) = expr.kind
&& let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, segment)) = base.kind
&& let hir::Node::Expr(outer) = self.tcx.parent_hir_node(expr.hir_id)
&& let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mtbl, _) = outer.kind
&& ty.span == span
{
// We've encountered something like `&str::from("")`, where the intended code
// was likely `<&str>::from("")`. The former is interpreted as "call method
// `from` on `str` and borrow the result", while the latter means "call method
// `from` on `&str`".
let trait_pred_and_imm_ref = poly_trait_pred.map_bound(|p| {
(p, Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, p.self_ty()))
});
let trait_pred_and_mut_ref = poly_trait_pred.map_bound(|p| {
(p, Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, p.self_ty()))
});
let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref);
let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref);
let sugg_msg = |pre: &str| {
format!(
"you likely meant to call the associated function `{FN}` for type \
`&{pre}{TY}`, but the code as written calls associated function `{FN}` on \
type `{TY}`",
FN = segment.ident,
TY = poly_trait_pred.self_ty(),
)
};
match (imm_ref_self_ty_satisfies_pred, mut_ref_self_ty_satisfies_pred, mtbl) {
(true, _, hir::Mutability::Not) | (_, true, hir::Mutability::Mut) => {
err.multipart_suggestion_verbose(
sugg_msg(mtbl.prefix_str()),
vec![
(outer.span.shrink_to_lo(), "<".to_string()),
(span.shrink_to_hi(), ">".to_string()),
],
Applicability::MachineApplicable,
);
}
(true, _, hir::Mutability::Mut) => {
// There's an associated function found on the immutable borrow of the
err.multipart_suggestion_verbose(
sugg_msg("mut "),
vec![
(outer.span.shrink_to_lo().until(span), "<&".to_string()),
(span.shrink_to_hi(), ">".to_string()),
],
Applicability::MachineApplicable,
);
}
(_, true, hir::Mutability::Not) => {
err.multipart_suggestion_verbose(
sugg_msg(""),
vec![
(outer.span.shrink_to_lo().until(span), "<&mut ".to_string()),
(span.shrink_to_hi(), ">".to_string()),
],
Applicability::MachineApplicable,
);
}
_ => {}
}
// If we didn't return early here, we would instead suggest `&&str::from("")`.
return false;
}
c
}
c if matches!(
@ -1220,8 +1297,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
never_suggest_borrow.push(def_id);
}
let param_env = obligation.param_env;
// Try to apply the original trait bound by borrowing.
let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>,
blacklist: &[DefId]|
@ -1243,11 +1318,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
)
});
let mk_result = |trait_pred_and_new_ty| {
let obligation =
self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty);
self.predicate_must_hold_modulo_regions(&obligation)
};
let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref);
let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref);

View file

@ -0,0 +1,17 @@
//@ run-rustfix
struct S;
trait Trait {
fn foo() {}
}
impl Trait for &S {}
impl Trait for &mut S {}
fn main() {
let _ = <&str>::from("value");
//~^ ERROR the trait bound `str: From<_>` is not satisfied
//~| ERROR the size for values of type `str` cannot be known at compilation time
let _ = <&mut S>::foo();
//~^ ERROR the trait bound `S: Trait` is not satisfied
let _ = <&S>::foo();
//~^ ERROR the trait bound `S: Trait` is not satisfied
}

View file

@ -0,0 +1,17 @@
//@ run-rustfix
struct S;
trait Trait {
fn foo() {}
}
impl Trait for &S {}
impl Trait for &mut S {}
fn main() {
let _ = &str::from("value");
//~^ ERROR the trait bound `str: From<_>` is not satisfied
//~| ERROR the size for values of type `str` cannot be known at compilation time
let _ = &mut S::foo();
//~^ ERROR the trait bound `S: Trait` is not satisfied
let _ = &S::foo();
//~^ ERROR the trait bound `S: Trait` is not satisfied
}

View file

@ -0,0 +1,58 @@
error[E0277]: the trait bound `str: From<_>` is not satisfied
--> $DIR/dont-suggest-borrowing-existing-borrow.rs:10:14
|
LL | let _ = &str::from("value");
| ^^^ the trait `From<_>` is not implemented for `str`
|
= help: the following other types implement trait `From<T>`:
`String` implements `From<&String>`
`String` implements `From<&mut str>`
`String` implements `From<&str>`
`String` implements `From<Box<str>>`
`String` implements `From<Cow<'_, str>>`
`String` implements `From<char>`
help: you likely meant to call the associated function `from` for type `&str`, but the code as written calls associated function `from` on type `str`
|
LL | let _ = <&str>::from("value");
| + +
error[E0277]: the trait bound `S: Trait` is not satisfied
--> $DIR/dont-suggest-borrowing-existing-borrow.rs:13:18
|
LL | let _ = &mut S::foo();
| ^ the trait `Trait` is not implemented for `S`
|
= help: the following other types implement trait `Trait`:
&S
&mut S
help: you likely meant to call the associated function `foo` for type `&mut S`, but the code as written calls associated function `foo` on type `S`
|
LL | let _ = <&mut S>::foo();
| + +
error[E0277]: the trait bound `S: Trait` is not satisfied
--> $DIR/dont-suggest-borrowing-existing-borrow.rs:15:14
|
LL | let _ = &S::foo();
| ^ the trait `Trait` is not implemented for `S`
|
= help: the following other types implement trait `Trait`:
&S
&mut S
help: you likely meant to call the associated function `foo` for type `&S`, but the code as written calls associated function `foo` on type `S`
|
LL | let _ = <&S>::foo();
| + +
error[E0277]: the size for values of type `str` cannot be known at compilation time
--> $DIR/dont-suggest-borrowing-existing-borrow.rs:10:14
|
LL | let _ = &str::from("value");
| ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `str`
= note: the return type of a function must have a statically known size
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0277`.