Skip to content

Commit 883b93c

Browse files
committed
Suggest dereferncing when possible in E0277, fix #87437
1 parent ac8cbbd commit 883b93c

File tree

7 files changed

+121
-41
lines changed

7 files changed

+121
-41
lines changed

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+66-38
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::{
2-
EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation,
3-
SelectionContext,
2+
DerivedObligationCause, EvaluationResult, ImplDerivedObligationCause, Obligation,
3+
ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext,
44
};
55

66
use crate::autoderef::Autoderef;
@@ -496,50 +496,78 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
496496
trait_pred: ty::PolyTraitPredicate<'tcx>,
497497
) -> bool {
498498
// It only make sense when suggesting dereferences for arguments
499-
let code = if let ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } =
500-
obligation.cause.code()
501-
{
502-
parent_code.clone()
503-
} else {
499+
let ObligationCauseCode::FunctionArgumentObligation { .. } = obligation.cause.code() else {
504500
return false;
505501
};
506502
let param_env = obligation.param_env;
507503
let body_id = obligation.cause.body_id;
508504
let span = obligation.cause.span;
509-
let real_trait_pred = match &*code {
510-
ObligationCauseCode::ImplDerivedObligation(cause) => cause.derived.parent_trait_pred,
511-
ObligationCauseCode::DerivedObligation(cause)
512-
| ObligationCauseCode::BuiltinDerivedObligation(cause) => cause.parent_trait_pred,
513-
_ => trait_pred,
514-
};
515-
let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else {
516-
return false;
517-
};
505+
let mut real_trait_pred = trait_pred;
506+
let mut code = obligation.cause.code();
507+
loop {
508+
match &code {
509+
ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => {
510+
code = &parent_code;
511+
}
512+
ObligationCauseCode::ImplDerivedObligation(box ImplDerivedObligationCause {
513+
derived: DerivedObligationCause { parent_code, parent_trait_pred },
514+
..
515+
})
516+
| ObligationCauseCode::BuiltinDerivedObligation(DerivedObligationCause {
517+
parent_code,
518+
parent_trait_pred,
519+
})
520+
| ObligationCauseCode::DerivedObligation(DerivedObligationCause {
521+
parent_code,
522+
parent_trait_pred,
523+
}) => {
524+
code = &parent_code;
525+
real_trait_pred = *parent_trait_pred;
526+
}
527+
_ => break,
528+
};
529+
let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else {
530+
continue;
531+
};
518532

519-
if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
520-
let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span);
521-
if let Some(steps) = autoderef.find_map(|(ty, steps)| {
522-
// Re-add the `&`
523-
let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
524-
let obligation =
525-
self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty);
526-
Some(steps).filter(|_| self.predicate_may_hold(&obligation))
527-
}) {
528-
if steps > 0 {
529-
if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) {
530-
// Don't care about `&mut` because `DerefMut` is used less
531-
// often and user will not expect autoderef happens.
532-
if src.starts_with('&') && !src.starts_with("&mut ") {
533-
let derefs = "*".repeat(steps);
534-
err.span_suggestion(
535-
span,
536-
"consider adding dereference here",
537-
format!("&{}{}", derefs, &src[1..]),
538-
Applicability::MachineApplicable,
539-
);
540-
return true;
533+
if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
534+
let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span);
535+
if let Some(steps) = autoderef.find_map(|(ty, steps)| {
536+
// Re-add the `&`
537+
let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
538+
let obligation =
539+
self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty);
540+
Some(steps).filter(|_| self.predicate_may_hold(&obligation))
541+
}) {
542+
if steps > 0 {
543+
if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) {
544+
// Don't care about `&mut` because `DerefMut` is used less
545+
// often and user will not expect autoderef happens.
546+
if src.starts_with('&') && !src.starts_with("&mut ") {
547+
let derefs = "*".repeat(steps);
548+
err.span_suggestion(
549+
span,
550+
"consider dereferencing here",
551+
format!("&{}{}", derefs, &src[1..]),
552+
Applicability::MachineApplicable,
553+
);
554+
return true;
555+
}
541556
}
542557
}
558+
} else if real_trait_pred != trait_pred {
559+
// This branch addresses #87437.
560+
let obligation =
561+
self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, base_ty);
562+
if self.predicate_may_hold(&obligation) {
563+
err.span_suggestion_verbose(
564+
span.shrink_to_lo(),
565+
"consider dereferencing here",
566+
"*".to_string(),
567+
Applicability::MachineApplicable,
568+
);
569+
return true;
570+
}
543571
}
544572
}
545573
}

src/test/ui/traits/suggest-deferences/issue-39029.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LL | let _errors = TcpListener::bind(&bad);
55
| ----------------- ^^^^
66
| | |
77
| | the trait `ToSocketAddrs` is not implemented for `NoToSocketAddrs`
8-
| | help: consider adding dereference here: `&*bad`
8+
| | help: consider dereferencing here: `&*bad`
99
| required by a bound introduced by this call
1010
|
1111
= note: required because of the requirements on the impl of `ToSocketAddrs` for `&NoToSocketAddrs`

src/test/ui/traits/suggest-deferences/issue-62530.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LL | takes_type_parameter(&string); // Error
55
| -------------------- ^^^^^^^
66
| | |
77
| | the trait `SomeTrait` is not implemented for `&String`
8-
| | help: consider adding dereference here: `&*string`
8+
| | help: consider dereferencing here: `&*string`
99
| required by a bound introduced by this call
1010
|
1111
note: required by a bound in `takes_type_parameter`

src/test/ui/traits/suggest-deferences/multiple-0.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LL | foo(&baz);
55
| --- ^^^^
66
| | |
77
| | the trait `Happy` is not implemented for `&Baz`
8-
| | help: consider adding dereference here: `&***baz`
8+
| | help: consider dereferencing here: `&***baz`
99
| required by a bound introduced by this call
1010
|
1111
note: required by a bound in `foo`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// run-rustfix
2+
3+
fn get_vowel_count(string: &str) -> usize {
4+
string
5+
.chars()
6+
.filter(|c| "aeiou".contains(*c))
7+
//~^ ERROR expected a `Fn<(char,)>` closure, found `char`
8+
.count()
9+
}
10+
11+
fn main() {
12+
let _ = get_vowel_count("asdf");
13+
}
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// run-rustfix
2+
3+
fn get_vowel_count(string: &str) -> usize {
4+
string
5+
.chars()
6+
.filter(|c| "aeiou".contains(c))
7+
//~^ ERROR expected a `Fn<(char,)>` closure, found `char`
8+
.count()
9+
}
10+
11+
fn main() {
12+
let _ = get_vowel_count("asdf");
13+
}
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error[E0277]: expected a `Fn<(char,)>` closure, found `char`
2+
--> $DIR/root-obligation.rs:6:38
3+
|
4+
LL | .filter(|c| "aeiou".contains(c))
5+
| -------- ^ expected an `Fn<(char,)>` closure, found `char`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the trait `Fn<(char,)>` is not implemented for `char`
10+
= note: required because of the requirements on the impl of `FnOnce<(char,)>` for `&char`
11+
= note: required because of the requirements on the impl of `Pattern<'_>` for `&char`
12+
note: required by a bound in `core::str::<impl str>::contains`
13+
--> $SRC_DIR/core/src/str/mod.rs:LL:COL
14+
|
15+
LL | pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool {
16+
| ^^^^^^^^^^^ required by this bound in `core::str::<impl str>::contains`
17+
help: consider dereferencing here
18+
|
19+
LL | .filter(|c| "aeiou".contains(*c))
20+
| +
21+
22+
error: aborting due to previous error
23+
24+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)