about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs36
-rw-r--r--compiler/rustc_typeck/src/check/method/suggest.rs42
-rw-r--r--src/test/ui/hrtb/issue-30786.migrate.stderr8
-rw-r--r--src/test/ui/hrtb/issue-30786.nll.stderr8
-rw-r--r--src/test/ui/suggestions/field-has-method.rs23
-rw-r--r--src/test/ui/suggestions/field-has-method.stderr17
6 files changed, 113 insertions, 21 deletions
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index e88082dbb97..ffc4e5f9705 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -2277,14 +2277,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // try to add a suggestion in case the field is a nested field of a field of the Adt
         if let Some((fields, substs)) = self.get_field_candidates(span, expr_t) {
             for candidate_field in fields.iter() {
-                if let Some(field_path) = self.check_for_nested_field(
+                if let Some(mut field_path) = self.check_for_nested_field_satisfying(
                     span,
-                    field,
+                    &|candidate_field, _| candidate_field.ident(self.tcx()) == field,
                     candidate_field,
                     substs,
                     vec![],
                     self.tcx.parent_module(id).to_def_id(),
                 ) {
+                    // field_path includes `field` that we're looking for, so pop it.
+                    field_path.pop();
+
                     let field_path_str = field_path
                         .iter()
                         .map(|id| id.name.to_ident_string())
@@ -2304,7 +2307,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err
     }
 
-    fn get_field_candidates(
+    crate fn get_field_candidates(
         &self,
         span: Span,
         base_t: Ty<'tcx>,
@@ -2329,49 +2332,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     /// This method is called after we have encountered a missing field error to recursively
     /// search for the field
-    fn check_for_nested_field(
+    crate fn check_for_nested_field_satisfying(
         &self,
         span: Span,
-        target_field: Ident,
+        matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool,
         candidate_field: &ty::FieldDef,
         subst: SubstsRef<'tcx>,
         mut field_path: Vec<Ident>,
         id: DefId,
     ) -> Option<Vec<Ident>> {
         debug!(
-            "check_for_nested_field(span: {:?}, candidate_field: {:?}, field_path: {:?}",
+            "check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}",
             span, candidate_field, field_path
         );
 
-        if candidate_field.ident(self.tcx) == target_field {
-            Some(field_path)
-        } else if field_path.len() > 3 {
+        if field_path.len() > 3 {
             // For compile-time reasons and to avoid infinite recursion we only check for fields
             // up to a depth of three
             None
         } else {
             // recursively search fields of `candidate_field` if it's a ty::Adt
-
             field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0());
             let field_ty = candidate_field.ty(self.tcx, subst);
             if let Some((nested_fields, subst)) = self.get_field_candidates(span, field_ty) {
                 for field in nested_fields.iter() {
-                    let accessible = field.vis.is_accessible_from(id, self.tcx);
-                    if accessible {
-                        let ident = field.ident(self.tcx).normalize_to_macros_2_0();
-                        if ident == target_field {
+                    if field.vis.is_accessible_from(id, self.tcx) {
+                        if matches(candidate_field, field_ty) {
                             return Some(field_path);
-                        }
-                        let field_path = field_path.clone();
-                        if let Some(path) = self.check_for_nested_field(
+                        } else if let Some(field_path) = self.check_for_nested_field_satisfying(
                             span,
-                            target_field,
+                            matches,
                             field,
                             subst,
-                            field_path,
+                            field_path.clone(),
                             id,
                         ) {
-                            return Some(path);
+                            return Some(field_path);
                         }
                     }
                 }
diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs
index 2921176ca4b..88e0a4bada8 100644
--- a/compiler/rustc_typeck/src/check/method/suggest.rs
+++ b/compiler/rustc_typeck/src/check/method/suggest.rs
@@ -28,7 +28,7 @@ use rustc_trait_selection::traits::{
 use std::cmp::Ordering;
 use std::iter;
 
-use super::probe::Mode;
+use super::probe::{Mode, ProbeScope};
 use super::{CandidateSource, MethodError, NoMatchData};
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -1129,6 +1129,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     label_span_not_found();
                 }
 
+                if let SelfSource::MethodCall(expr) = source
+                    && let Some((fields, substs)) = self.get_field_candidates(span, actual)
+                {
+                    let call_expr =
+                        self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
+                    for candidate_field in fields.iter() {
+                        if let Some(field_path) = self.check_for_nested_field_satisfying(
+                            span,
+                            &|_, field_ty| {
+                                self.lookup_probe(
+                                    span,
+                                    item_name,
+                                    field_ty,
+                                    call_expr,
+                                    ProbeScope::AllTraits,
+                                )
+                                .is_ok()
+                            },
+                            candidate_field,
+                            substs,
+                            vec![],
+                            self.tcx.parent_module(expr.hir_id).to_def_id(),
+                        ) {
+                            let field_path_str = field_path
+                                .iter()
+                                .map(|id| id.name.to_ident_string())
+                                .collect::<Vec<String>>()
+                                .join(".");
+                            debug!("field_path_str: {:?}", field_path_str);
+
+                            err.span_suggestion_verbose(
+                                item_name.span.shrink_to_lo(),
+                                "one of the expressions' fields has a method of the same name",
+                                format!("{field_path_str}."),
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                    }
+                }
+
                 bound_spans.sort();
                 bound_spans.dedup();
                 for (span, msg) in bound_spans.into_iter() {
diff --git a/src/test/ui/hrtb/issue-30786.migrate.stderr b/src/test/ui/hrtb/issue-30786.migrate.stderr
index 7ffe2f4cd7e..7157b186fc8 100644
--- a/src/test/ui/hrtb/issue-30786.migrate.stderr
+++ b/src/test/ui/hrtb/issue-30786.migrate.stderr
@@ -18,6 +18,10 @@ note: the following trait bounds were not satisfied:
    |
 LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
    |         ---------     -                          ^^^^^^ unsatisfied trait bound introduced here
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let filter = map.stream.filterx(|x: &_| true);
+   |                      +++++++
 
 error[E0599]: the method `countx` exists for struct `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:139:30: 139:42]>`, but its trait bounds were not satisfied
   --> $DIR/issue-30786.rs:140:24
@@ -39,6 +43,10 @@ note: the following trait bounds were not satisfied:
    |
 LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
    |         ---------     -                          ^^^^^^ unsatisfied trait bound introduced here
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let count = filter.stream.countx();
+   |                        +++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/hrtb/issue-30786.nll.stderr b/src/test/ui/hrtb/issue-30786.nll.stderr
index 7ffe2f4cd7e..7157b186fc8 100644
--- a/src/test/ui/hrtb/issue-30786.nll.stderr
+++ b/src/test/ui/hrtb/issue-30786.nll.stderr
@@ -18,6 +18,10 @@ note: the following trait bounds were not satisfied:
    |
 LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
    |         ---------     -                          ^^^^^^ unsatisfied trait bound introduced here
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let filter = map.stream.filterx(|x: &_| true);
+   |                      +++++++
 
 error[E0599]: the method `countx` exists for struct `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:139:30: 139:42]>`, but its trait bounds were not satisfied
   --> $DIR/issue-30786.rs:140:24
@@ -39,6 +43,10 @@ note: the following trait bounds were not satisfied:
    |
 LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
    |         ---------     -                          ^^^^^^ unsatisfied trait bound introduced here
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let count = filter.stream.countx();
+   |                        +++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/suggestions/field-has-method.rs b/src/test/ui/suggestions/field-has-method.rs
new file mode 100644
index 00000000000..980000151e2
--- /dev/null
+++ b/src/test/ui/suggestions/field-has-method.rs
@@ -0,0 +1,23 @@
+struct Kind;
+
+struct Ty {
+    kind: Kind,
+}
+
+impl Ty {
+    fn kind(&self) -> Kind {
+        todo!()
+    }
+}
+
+struct InferOk<T> {
+    value: T,
+    predicates: Vec<()>,
+}
+
+fn foo(i: InferOk<Ty>) {
+    let k = i.kind();
+    //~^ no method named `kind` found for struct `InferOk` in the current scope
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/field-has-method.stderr b/src/test/ui/suggestions/field-has-method.stderr
new file mode 100644
index 00000000000..3a57436f200
--- /dev/null
+++ b/src/test/ui/suggestions/field-has-method.stderr
@@ -0,0 +1,17 @@
+error[E0599]: no method named `kind` found for struct `InferOk` in the current scope
+  --> $DIR/field-has-method.rs:19:15
+   |
+LL | struct InferOk<T> {
+   | ----------------- method `kind` not found for this
+...
+LL |     let k = i.kind();
+   |               ^^^^ method not found in `InferOk<Ty>`
+   |
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let k = i.value.kind();
+   |               ++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.