about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs72
-rw-r--r--compiler/rustc_typeck/src/check/method/suggest.rs13
-rw-r--r--src/test/ui/copy-a-resource.stderr4
-rw-r--r--src/test/ui/hrtb/issue-30786.stderr8
-rw-r--r--src/test/ui/issues/issue-2823.stderr4
-rw-r--r--src/test/ui/noncopyable-class.stderr8
-rw-r--r--src/test/ui/suggestions/field-access-considering-privacy.rs35
-rw-r--r--src/test/ui/suggestions/field-access-considering-privacy.stderr14
8 files changed, 117 insertions, 41 deletions
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index fb78632069e..a685add7f56 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -2526,15 +2526,18 @@ 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() {
+        let mod_id = self.tcx.parent_module(id).to_def_id();
+        if let Some((fields, substs)) =
+            self.get_field_candidates_considering_privacy(span, expr_t, mod_id)
+        {
+            for candidate_field in fields {
                 if let Some(mut field_path) = self.check_for_nested_field_satisfying(
                     span,
                     &|candidate_field, _| candidate_field.ident(self.tcx()) == field,
                     candidate_field,
                     substs,
                     vec![],
-                    self.tcx.parent_module(id).to_def_id(),
+                    mod_id,
                 ) {
                     // field_path includes `field` that we're looking for, so pop it.
                     field_path.pop();
@@ -2558,22 +2561,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err
     }
 
-    pub(crate) fn get_field_candidates(
+    pub(crate) fn get_field_candidates_considering_privacy(
         &self,
         span: Span,
-        base_t: Ty<'tcx>,
-    ) -> Option<(&[ty::FieldDef], SubstsRef<'tcx>)> {
-        debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_t);
+        base_ty: Ty<'tcx>,
+        mod_id: DefId,
+    ) -> Option<(impl Iterator<Item = &'tcx ty::FieldDef> + 'tcx, SubstsRef<'tcx>)> {
+        debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty);
 
-        for (base_t, _) in self.autoderef(span, base_t) {
+        for (base_t, _) in self.autoderef(span, base_ty) {
             match base_t.kind() {
                 ty::Adt(base_def, substs) if !base_def.is_enum() => {
+                    let tcx = self.tcx;
                     let fields = &base_def.non_enum_variant().fields;
-                    // For compile-time reasons put a limit on number of fields we search
-                    if fields.len() > 100 {
-                        return None;
+                    // Some struct, e.g. some that impl `Deref`, have all private fields
+                    // because you're expected to deref them to access the _real_ fields.
+                    // This, for example, will help us suggest accessing a field through a `Box<T>`.
+                    if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) {
+                        continue;
                     }
-                    return Some((fields, substs));
+                    return Some((
+                        fields
+                            .iter()
+                            .filter(move |field| field.vis.is_accessible_from(mod_id, tcx))
+                            // For compile-time reasons put a limit on number of fields we search
+                            .take(100),
+                        substs,
+                    ));
                 }
                 _ => {}
             }
@@ -2590,7 +2604,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         candidate_field: &ty::FieldDef,
         subst: SubstsRef<'tcx>,
         mut field_path: Vec<Ident>,
-        id: DefId,
+        mod_id: DefId,
     ) -> Option<Vec<Ident>> {
         debug!(
             "check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}",
@@ -2602,24 +2616,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // 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() {
-                    if field.vis.is_accessible_from(id, self.tcx) {
-                        if matches(candidate_field, field_ty) {
-                            return Some(field_path);
-                        } else if let Some(field_path) = self.check_for_nested_field_satisfying(
-                            span,
-                            matches,
-                            field,
-                            subst,
-                            field_path.clone(),
-                            id,
-                        ) {
-                            return Some(field_path);
-                        }
+            if matches(candidate_field, field_ty) {
+                return Some(field_path);
+            } else if let Some((nested_fields, subst)) =
+                self.get_field_candidates_considering_privacy(span, field_ty, mod_id)
+            {
+                // recursively search fields of `candidate_field` if it's a ty::Adt
+                for field in nested_fields {
+                    if let Some(field_path) = self.check_for_nested_field_satisfying(
+                        span,
+                        matches,
+                        field,
+                        subst,
+                        field_path.clone(),
+                        mod_id,
+                    ) {
+                        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 c92b93cbc22..f73d0fbb277 100644
--- a/compiler/rustc_typeck/src/check/method/suggest.rs
+++ b/compiler/rustc_typeck/src/check/method/suggest.rs
@@ -1000,7 +1000,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     label_span_not_found(&mut err);
                 }
 
-                self.check_for_field_method(&mut err, source, span, actual, item_name);
+                // Don't suggest (for example) `expr.field.method()` if `expr.method()`
+                // doesn't exist due to unsatisfied predicates.
+                if unsatisfied_predicates.is_empty() {
+                    self.check_for_field_method(&mut err, source, span, actual, item_name);
+                }
 
                 self.check_for_unwrap_self(&mut err, source, span, actual, item_name);
 
@@ -1334,10 +1338,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         item_name: Ident,
     ) {
         if let SelfSource::MethodCall(expr) = source
-            && let Some((fields, substs)) = self.get_field_candidates(span, actual)
+            && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
+            && let Some((fields, substs)) = self.get_field_candidates_considering_privacy(span, actual, mod_id)
         {
             let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
-            for candidate_field in fields.iter() {
+            for candidate_field in fields {
                 if let Some(field_path) = self.check_for_nested_field_satisfying(
                     span,
                     &|_, field_ty| {
@@ -1353,7 +1358,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     candidate_field,
                     substs,
                     vec![],
-                    self.tcx.parent_module(expr.hir_id).to_def_id(),
+                    mod_id,
                 ) {
                     let field_path_str = field_path
                         .iter()
diff --git a/src/test/ui/copy-a-resource.stderr b/src/test/ui/copy-a-resource.stderr
index 128087f1e37..b92449c6e0a 100644
--- a/src/test/ui/copy-a-resource.stderr
+++ b/src/test/ui/copy-a-resource.stderr
@@ -10,6 +10,10 @@ LL |     let _y = x.clone();
    = help: items from traits can only be used if the trait is implemented and in scope
    = note: the following trait defines an item `clone`, perhaps you need to implement it:
            candidate #1: `Clone`
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let _y = x.i.clone();
+   |                ++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/hrtb/issue-30786.stderr b/src/test/ui/hrtb/issue-30786.stderr
index bc7b5e914e1..ffe3d7b81f5 100644
--- a/src/test/ui/hrtb/issue-30786.stderr
+++ b/src/test/ui/hrtb/issue-30786.stderr
@@ -18,10 +18,6 @@ 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:129:30: 129:37]>`, but its trait bounds were not satisfied
   --> $DIR/issue-30786.rs:130:24
@@ -43,10 +39,6 @@ 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/issues/issue-2823.stderr b/src/test/ui/issues/issue-2823.stderr
index b5a2b2f55a6..208b340d064 100644
--- a/src/test/ui/issues/issue-2823.stderr
+++ b/src/test/ui/issues/issue-2823.stderr
@@ -10,6 +10,10 @@ LL |     let _d = c.clone();
    = help: items from traits can only be used if the trait is implemented and in scope
    = note: the following trait defines an item `clone`, perhaps you need to implement it:
            candidate #1: `Clone`
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let _d = c.x.clone();
+   |                ++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/noncopyable-class.stderr b/src/test/ui/noncopyable-class.stderr
index 0c696163a26..15e22e946da 100644
--- a/src/test/ui/noncopyable-class.stderr
+++ b/src/test/ui/noncopyable-class.stderr
@@ -10,6 +10,14 @@ LL |     let _y = x.clone();
    = help: items from traits can only be used if the trait is implemented and in scope
    = note: the following trait defines an item `clone`, perhaps you need to implement it:
            candidate #1: `Clone`
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let _y = x.i.clone();
+   |                ++
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let _y = x.j.x.clone();
+   |                ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggestions/field-access-considering-privacy.rs b/src/test/ui/suggestions/field-access-considering-privacy.rs
new file mode 100644
index 00000000000..3de06b21420
--- /dev/null
+++ b/src/test/ui/suggestions/field-access-considering-privacy.rs
@@ -0,0 +1,35 @@
+use a::TyCtxt;
+
+mod a {
+    use std::ops::Deref;
+    pub struct TyCtxt<'tcx> {
+        gcx: &'tcx GlobalCtxt<'tcx>,
+    }
+
+    impl<'tcx> Deref for TyCtxt<'tcx> {
+        type Target = &'tcx GlobalCtxt<'tcx>;
+
+        fn deref(&self) -> &Self::Target {
+            &self.gcx
+        }
+    }
+
+    pub struct GlobalCtxt<'tcx> {
+        pub sess: &'tcx Session,
+        _t: &'tcx (),
+    }
+
+    pub struct Session {
+        pub opts: (),
+    }
+}
+
+mod b {
+    fn foo<'tcx>(tcx: crate::TyCtxt<'tcx>) {
+        tcx.opts;
+        //~^ ERROR no field `opts` on type `TyCtxt<'tcx>`
+        //~| HELP one of the expressions' fields has a field of the same name
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/field-access-considering-privacy.stderr b/src/test/ui/suggestions/field-access-considering-privacy.stderr
new file mode 100644
index 00000000000..cbf6f3d1002
--- /dev/null
+++ b/src/test/ui/suggestions/field-access-considering-privacy.stderr
@@ -0,0 +1,14 @@
+error[E0609]: no field `opts` on type `TyCtxt<'tcx>`
+  --> $DIR/field-access-considering-privacy.rs:29:13
+   |
+LL |         tcx.opts;
+   |             ^^^^ unknown field
+   |
+help: one of the expressions' fields has a field of the same name
+   |
+LL |         tcx.sess.opts;
+   |             +++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0609`.