about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Kuber <esteban@kuber.com.ar>2022-03-26 23:01:29 +0000
committerEsteban Kuber <esteban@kuber.com.ar>2022-04-04 21:06:35 +0000
commit883b93c7b7eb02ec85f4b8f9fb129efc403d4fb2 (patch)
treec1feadfadc8a41ef0cf3ca05ff59e8f49bbb0879
parentac8cbbd200cfa77f03a8560f0305810e4f8f1375 (diff)
downloadrust-883b93c7b7eb02ec85f4b8f9fb129efc403d4fb2.tar.gz
rust-883b93c7b7eb02ec85f4b8f9fb129efc403d4fb2.zip
Suggest dereferncing when possible in E0277, fix #87437
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs104
-rw-r--r--src/test/ui/traits/suggest-deferences/issue-39029.stderr2
-rw-r--r--src/test/ui/traits/suggest-deferences/issue-62530.stderr2
-rw-r--r--src/test/ui/traits/suggest-deferences/multiple-0.stderr2
-rw-r--r--src/test/ui/traits/suggest-deferences/root-obligation.fixed14
-rw-r--r--src/test/ui/traits/suggest-deferences/root-obligation.rs14
-rw-r--r--src/test/ui/traits/suggest-deferences/root-obligation.stderr24
7 files changed, 121 insertions, 41 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 5bf9be87b57..b369c733871 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -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 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         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;
+                    }
                 }
             }
         }
diff --git a/src/test/ui/traits/suggest-deferences/issue-39029.stderr b/src/test/ui/traits/suggest-deferences/issue-39029.stderr
index 2c225f4311d..5c324cd38a3 100644
--- a/src/test/ui/traits/suggest-deferences/issue-39029.stderr
+++ b/src/test/ui/traits/suggest-deferences/issue-39029.stderr
@@ -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`
diff --git a/src/test/ui/traits/suggest-deferences/issue-62530.stderr b/src/test/ui/traits/suggest-deferences/issue-62530.stderr
index b77af7ddf47..d129328dae8 100644
--- a/src/test/ui/traits/suggest-deferences/issue-62530.stderr
+++ b/src/test/ui/traits/suggest-deferences/issue-62530.stderr
@@ -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`
diff --git a/src/test/ui/traits/suggest-deferences/multiple-0.stderr b/src/test/ui/traits/suggest-deferences/multiple-0.stderr
index bf9f85f1b45..efb3c7d123f 100644
--- a/src/test/ui/traits/suggest-deferences/multiple-0.stderr
+++ b/src/test/ui/traits/suggest-deferences/multiple-0.stderr
@@ -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`
diff --git a/src/test/ui/traits/suggest-deferences/root-obligation.fixed b/src/test/ui/traits/suggest-deferences/root-obligation.fixed
new file mode 100644
index 00000000000..9fd19240678
--- /dev/null
+++ b/src/test/ui/traits/suggest-deferences/root-obligation.fixed
@@ -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");
+}
+
diff --git a/src/test/ui/traits/suggest-deferences/root-obligation.rs b/src/test/ui/traits/suggest-deferences/root-obligation.rs
new file mode 100644
index 00000000000..4dd0291b629
--- /dev/null
+++ b/src/test/ui/traits/suggest-deferences/root-obligation.rs
@@ -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");
+}
+
diff --git a/src/test/ui/traits/suggest-deferences/root-obligation.stderr b/src/test/ui/traits/suggest-deferences/root-obligation.stderr
new file mode 100644
index 00000000000..16e03e79c75
--- /dev/null
+++ b/src/test/ui/traits/suggest-deferences/root-obligation.stderr
@@ -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`.