Suggest dereferncing when possible in E0277, fix #87437
This commit is contained in:
parent
ac8cbbd200
commit
883b93c7b7
@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation,
|
||||
SelectionContext,
|
||||
DerivedObligationCause, EvaluationResult, ImplDerivedObligationCause, Obligation,
|
||||
ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext,
|
||||
};
|
||||
|
||||
use crate::autoderef::Autoderef;
|
||||
@ -496,50 +496,78 @@ fn suggest_dereferences(
|
||||
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 {
|
||||
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 false;
|
||||
};
|
||||
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,
|
||||
);
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ LL | let _errors = TcpListener::bind(&bad);
|
||||
| ----------------- ^^^^
|
||||
| | |
|
||||
| | the trait `ToSocketAddrs` is not implemented for `NoToSocketAddrs`
|
||||
| | help: consider adding dereference here: `&*bad`
|
||||
| | help: consider dereferencing here: `&*bad`
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= note: required because of the requirements on the impl of `ToSocketAddrs` for `&NoToSocketAddrs`
|
||||
|
@ -5,7 +5,7 @@ LL | takes_type_parameter(&string); // Error
|
||||
| -------------------- ^^^^^^^
|
||||
| | |
|
||||
| | the trait `SomeTrait` is not implemented for `&String`
|
||||
| | help: consider adding dereference here: `&*string`
|
||||
| | help: consider dereferencing here: `&*string`
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required by a bound in `takes_type_parameter`
|
||||
|
@ -5,7 +5,7 @@ LL | foo(&baz);
|
||||
| --- ^^^^
|
||||
| | |
|
||||
| | the trait `Happy` is not implemented for `&Baz`
|
||||
| | help: consider adding dereference here: `&***baz`
|
||||
| | help: consider dereferencing here: `&***baz`
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required by a bound in `foo`
|
||||
|
14
src/test/ui/traits/suggest-deferences/root-obligation.fixed
Normal file
14
src/test/ui/traits/suggest-deferences/root-obligation.fixed
Normal file
@ -0,0 +1,14 @@
|
||||
// run-rustfix
|
||||
|
||||
fn get_vowel_count(string: &str) -> usize {
|
||||
string
|
||||
.chars()
|
||||
.filter(|c| "aeiou".contains(*c))
|
||||
//~^ ERROR expected a `Fn<(char,)>` closure, found `char`
|
||||
.count()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = get_vowel_count("asdf");
|
||||
}
|
||||
|
14
src/test/ui/traits/suggest-deferences/root-obligation.rs
Normal file
14
src/test/ui/traits/suggest-deferences/root-obligation.rs
Normal file
@ -0,0 +1,14 @@
|
||||
// run-rustfix
|
||||
|
||||
fn get_vowel_count(string: &str) -> usize {
|
||||
string
|
||||
.chars()
|
||||
.filter(|c| "aeiou".contains(c))
|
||||
//~^ ERROR expected a `Fn<(char,)>` closure, found `char`
|
||||
.count()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = get_vowel_count("asdf");
|
||||
}
|
||||
|
24
src/test/ui/traits/suggest-deferences/root-obligation.stderr
Normal file
24
src/test/ui/traits/suggest-deferences/root-obligation.stderr
Normal file
@ -0,0 +1,24 @@
|
||||
error[E0277]: expected a `Fn<(char,)>` closure, found `char`
|
||||
--> $DIR/root-obligation.rs:6:38
|
||||
|
|
||||
LL | .filter(|c| "aeiou".contains(c))
|
||||
| -------- ^ expected an `Fn<(char,)>` closure, found `char`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `Fn<(char,)>` is not implemented for `char`
|
||||
= note: required because of the requirements on the impl of `FnOnce<(char,)>` for `&char`
|
||||
= note: required because of the requirements on the impl of `Pattern<'_>` for `&char`
|
||||
note: required by a bound in `core::str::<impl str>::contains`
|
||||
--> $SRC_DIR/core/src/str/mod.rs:LL:COL
|
||||
|
|
||||
LL | pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool {
|
||||
| ^^^^^^^^^^^ required by this bound in `core::str::<impl str>::contains`
|
||||
help: consider dereferencing here
|
||||
|
|
||||
LL | .filter(|c| "aeiou".contains(*c))
|
||||
| +
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
Loading…
Reference in New Issue
Block a user