about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs99
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs14
-rw-r--r--tests/ui/coherence/coherence-tuple-conflict.stderr2
-rw-r--r--tests/ui/consts/issue-19244-1.stderr2
-rw-r--r--tests/ui/diagnostic-width/long-E0609.stderr1
-rw-r--r--tests/ui/error-codes/ex-E0612.stderr6
-rw-r--r--tests/ui/offset-of/offset-of-tuple-field.stderr36
-rw-r--r--tests/ui/parser/float-field.stderr6
-rw-r--r--tests/ui/structs/tuple-struct-field-naming-47073.stderr6
-rw-r--r--tests/ui/traits/well-formed-recursion-limit.stderr4
-rw-r--r--tests/ui/tuple/index-invalid.stderr6
-rw-r--r--tests/ui/tuple/missing-field-access.rs16
-rw-r--r--tests/ui/tuple/missing-field-access.stderr28
-rw-r--r--tests/ui/tuple/tuple-index-out-of-bounds.stderr8
14 files changed, 169 insertions, 65 deletions
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 454ec7ddcaf..0498a938366 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -2745,6 +2745,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let available_field_names = self.available_field_names(variant, expr, skip_fields);
                 if let Some(field_name) =
                     find_best_match_for_name(&available_field_names, field.ident.name, None)
+                    && !(field.ident.name.as_str().parse::<usize>().is_ok()
+                        && field_name.as_str().parse::<usize>().is_ok())
                 {
                     err.span_label(field.ident.span, "unknown field");
                     err.span_suggestion_verbose(
@@ -3321,18 +3323,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         } else {
             (base_ty, "")
         };
-        for (found_fields, args) in
+        for found_fields in
             self.get_field_candidates_considering_privacy_for_diag(span, ty, mod_id, expr.hir_id)
         {
-            let field_names = found_fields.iter().map(|field| field.name).collect::<Vec<_>>();
+            let field_names = found_fields.iter().map(|field| field.0.name).collect::<Vec<_>>();
             let mut candidate_fields: Vec<_> = found_fields
                 .into_iter()
                 .filter_map(|candidate_field| {
                     self.check_for_nested_field_satisfying_condition_for_diag(
                         span,
-                        &|candidate_field, _| candidate_field.ident(self.tcx()) == field,
+                        &|candidate_field, _| candidate_field == field,
                         candidate_field,
-                        args,
                         vec![],
                         mod_id,
                         expr.hir_id,
@@ -3361,6 +3362,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 );
             } else if let Some(field_name) =
                 find_best_match_for_name(&field_names, field.name, None)
+                && !(field.name.as_str().parse::<usize>().is_ok()
+                    && field_name.as_str().parse::<usize>().is_ok())
             {
                 err.span_suggestion_verbose(
                     field.span,
@@ -3396,7 +3399,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         base_ty: Ty<'tcx>,
         mod_id: DefId,
         hir_id: HirId,
-    ) -> Vec<(Vec<&'tcx ty::FieldDef>, GenericArgsRef<'tcx>)> {
+    ) -> Vec<Vec<(Ident, Ty<'tcx>)>> {
         debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty);
 
         let mut autoderef = self.autoderef(span, base_ty).silence_errors();
@@ -3422,7 +3425,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) {
                             return None;
                         }
-                        return Some((
+                        return Some(
                             fields
                                 .iter()
                                 .filter(move |field| {
@@ -3431,9 +3434,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 })
                                 // For compile-time reasons put a limit on number of fields we search
                                 .take(100)
+                                .map(|field_def| {
+                                    (
+                                        field_def.ident(self.tcx).normalize_to_macros_2_0(),
+                                        field_def.ty(self.tcx, args),
+                                    )
+                                })
+                                .collect::<Vec<_>>(),
+                        );
+                    }
+                    ty::Tuple(types) => {
+                        return Some(
+                            types
+                                .iter()
+                                .enumerate()
+                                // For compile-time reasons put a limit on number of fields we search
+                                .take(100)
+                                .map(|(i, ty)| (Ident::from_str(&i.to_string()), ty))
                                 .collect::<Vec<_>>(),
-                            *args,
-                        ));
+                        );
                     }
                     _ => None,
                 }
@@ -3443,56 +3462,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     /// This method is called after we have encountered a missing field error to recursively
     /// search for the field
+    #[instrument(skip(self, matches, mod_id, hir_id), level = "debug")]
     pub(crate) fn check_for_nested_field_satisfying_condition_for_diag(
         &self,
         span: Span,
-        matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool,
-        candidate_field: &ty::FieldDef,
-        subst: GenericArgsRef<'tcx>,
+        matches: &impl Fn(Ident, Ty<'tcx>) -> bool,
+        (candidate_name, candidate_ty): (Ident, Ty<'tcx>),
         mut field_path: Vec<Ident>,
         mod_id: DefId,
         hir_id: HirId,
     ) -> Option<Vec<Ident>> {
-        debug!(
-            "check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}",
-            span, candidate_field, field_path
-        );
-
         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 {
-            field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0());
-            let field_ty = candidate_field.ty(self.tcx, subst);
-            if matches(candidate_field, field_ty) {
-                return Some(field_path);
-            } else {
-                for (nested_fields, subst) in self
-                    .get_field_candidates_considering_privacy_for_diag(
-                        span, field_ty, mod_id, hir_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_condition_for_diag(
-                                span,
-                                matches,
-                                field,
-                                subst,
-                                field_path.clone(),
-                                mod_id,
-                                hir_id,
-                            )
-                        {
-                            return Some(field_path);
-                        }
-                    }
+            return None;
+        }
+        field_path.push(candidate_name);
+        if matches(candidate_name, candidate_ty) {
+            return Some(field_path);
+        }
+        for nested_fields in self.get_field_candidates_considering_privacy_for_diag(
+            span,
+            candidate_ty,
+            mod_id,
+            hir_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_condition_for_diag(
+                    span,
+                    matches,
+                    field,
+                    field_path.clone(),
+                    mod_id,
+                    hir_id,
+                ) {
+                    return Some(field_path);
                 }
             }
-            None
         }
+        None
     }
 
     fn check_expr_index(
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 7848d9323c5..f7430f7af4e 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -2773,7 +2773,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) {
         if let SelfSource::MethodCall(expr) = source {
             let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id();
-            for (fields, args) in self.get_field_candidates_considering_privacy_for_diag(
+            for fields in self.get_field_candidates_considering_privacy_for_diag(
                 span,
                 actual,
                 mod_id,
@@ -2812,7 +2812,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 })
                             },
                             candidate_field,
-                            args,
                             vec![],
                             mod_id,
                             expr.hir_id,
@@ -3652,7 +3651,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         )
                     {
                         debug!("try_alt_rcvr: pick candidate {:?}", pick);
-                        let did = Some(pick.item.container_id(self.tcx));
+                        let did = pick.item.trait_container(self.tcx);
                         // We don't want to suggest a container type when the missing
                         // method is `.clone()` or `.deref()` otherwise we'd suggest
                         // `Arc::new(foo).clone()`, which is far from what the user wants.
@@ -3701,8 +3700,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     && !alt_rcvr_sugg
                     // `T: !Unpin`
                     && !unpin
-                    // The method isn't `as_ref`, as it would provide a wrong suggestion for `Pin`.
-                    && sym::as_ref != item_name.name
                     // Either `Pin::as_ref` or `Pin::as_mut`.
                     && let Some(pin_call) = pin_call
                     // Search for `item_name` as a method accessible on `Pin<T>`.
@@ -3716,6 +3713,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // We skip some common traits that we don't want to consider because autoderefs
                     // would take care of them.
                     && !skippable.contains(&Some(pick.item.container_id(self.tcx)))
+                    // Do not suggest pinning when the method is directly on `Pin`.
+                    && pick.item.impl_container(self.tcx).map_or(true, |did| {
+                        match self.tcx.type_of(did).skip_binder().kind() {
+                            ty::Adt(def, _) => Some(def.did()) != self.tcx.lang_items().pin_type(),
+                            _ => true,
+                        }
+                    })
                     // We don't want to go through derefs.
                     && pick.autoderefs == 0
                     // Check that the method of the same name that was found on the new `Pin<T>`
diff --git a/tests/ui/coherence/coherence-tuple-conflict.stderr b/tests/ui/coherence/coherence-tuple-conflict.stderr
index 95f9a1a8841..8ce65f79aca 100644
--- a/tests/ui/coherence/coherence-tuple-conflict.stderr
+++ b/tests/ui/coherence/coherence-tuple-conflict.stderr
@@ -12,6 +12,8 @@ error[E0609]: no field `dummy` on type `&(A, B)`
    |
 LL |     fn get(&self) -> usize { self.dummy }
    |                                   ^^^^^ unknown field
+   |
+   = note: available fields are: `0`, `1`
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/consts/issue-19244-1.stderr b/tests/ui/consts/issue-19244-1.stderr
index 9c1336402e6..98a33817b4c 100644
--- a/tests/ui/consts/issue-19244-1.stderr
+++ b/tests/ui/consts/issue-19244-1.stderr
@@ -3,6 +3,8 @@ error[E0609]: no field `1` on type `(usize,)`
    |
 LL |     let a: [isize; TUP.1];
    |                        ^ unknown field
+   |
+   = note: available field is: `0`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/diagnostic-width/long-E0609.stderr b/tests/ui/diagnostic-width/long-E0609.stderr
index 36ef8545746..70092ea34bc 100644
--- a/tests/ui/diagnostic-width/long-E0609.stderr
+++ b/tests/ui/diagnostic-width/long-E0609.stderr
@@ -4,6 +4,7 @@ error[E0609]: no field `field` on type `(..., ..., ..., ...)`
 LL |     x.field;
    |       ^^^^^ unknown field
    |
+   = note: available fields are: `0`, `1`, `2`, `3`
    = note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0609.long-type-$LONG_TYPE_HASH.txt'
    = note: consider using `--verbose` to print the full type name to the console
 
diff --git a/tests/ui/error-codes/ex-E0612.stderr b/tests/ui/error-codes/ex-E0612.stderr
index 54451d3d452..e6062f6061d 100644
--- a/tests/ui/error-codes/ex-E0612.stderr
+++ b/tests/ui/error-codes/ex-E0612.stderr
@@ -4,11 +4,7 @@ error[E0609]: no field `1` on type `Foo`
 LL |    y.1;
    |      ^ unknown field
    |
-help: a field with a similar name exists
-   |
-LL -    y.1;
-LL +    y.0;
-   |
+   = note: available field is: `0`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/offset-of/offset-of-tuple-field.stderr b/tests/ui/offset-of/offset-of-tuple-field.stderr
index 4da0d851650..01622c5fa2d 100644
--- a/tests/ui/offset-of/offset-of-tuple-field.stderr
+++ b/tests/ui/offset-of/offset-of-tuple-field.stderr
@@ -15,60 +15,96 @@ error[E0609]: no field `_0` on type `(u8, u8)`
    |
 LL |     offset_of!((u8, u8), _0);
    |                          ^^
+   |
+help: a field with a similar name exists
+   |
+LL -     offset_of!((u8, u8), _0);
+LL +     offset_of!((u8, u8), 0);
+   |
 
 error[E0609]: no field `01` on type `(u8, u8)`
   --> $DIR/offset-of-tuple-field.rs:7:26
    |
 LL |     offset_of!((u8, u8), 01);
    |                          ^^
+   |
+   = note: available fields are: `0`, `1`
 
 error[E0609]: no field `1e2` on type `(u8, u8)`
   --> $DIR/offset-of-tuple-field.rs:8:26
    |
 LL |     offset_of!((u8, u8), 1e2);
    |                          ^^^
+   |
+   = note: available fields are: `0`, `1`
 
 error[E0609]: no field `1_` on type `(u8, u8)`
   --> $DIR/offset-of-tuple-field.rs:9:26
    |
 LL |     offset_of!((u8, u8), 1_u8);
    |                          ^^^^
+   |
+help: a field with a similar name exists
+   |
+LL -     offset_of!((u8, u8), 1_u8);
+LL +     offset_of!((u8, u8), 1);
+   |
 
 error[E0609]: no field `1e2` on type `(u8, u8)`
   --> $DIR/offset-of-tuple-field.rs:12:35
    |
 LL |     builtin # offset_of((u8, u8), 1e2);
    |                                   ^^^
+   |
+   = note: available fields are: `0`, `1`
 
 error[E0609]: no field `_0` on type `(u8, u8)`
   --> $DIR/offset-of-tuple-field.rs:13:35
    |
 LL |     builtin # offset_of((u8, u8), _0);
    |                                   ^^
+   |
+help: a field with a similar name exists
+   |
+LL -     builtin # offset_of((u8, u8), _0);
+LL +     builtin # offset_of((u8, u8), 0);
+   |
 
 error[E0609]: no field `01` on type `(u8, u8)`
   --> $DIR/offset-of-tuple-field.rs:14:35
    |
 LL |     builtin # offset_of((u8, u8), 01);
    |                                   ^^
+   |
+   = note: available fields are: `0`, `1`
 
 error[E0609]: no field `1_` on type `(u8, u8)`
   --> $DIR/offset-of-tuple-field.rs:15:35
    |
 LL |     builtin # offset_of((u8, u8), 1_u8);
    |                                   ^^^^
+   |
+help: a field with a similar name exists
+   |
+LL -     builtin # offset_of((u8, u8), 1_u8);
+LL +     builtin # offset_of((u8, u8), 1);
+   |
 
 error[E0609]: no field `2` on type `(u8, u16)`
   --> $DIR/offset-of-tuple-field.rs:18:47
    |
 LL |     offset_of!(((u8, u16), (u32, u16, u8)), 0.2);
    |                                               ^
+   |
+   = note: available fields are: `0`, `1`
 
 error[E0609]: no field `1e2` on type `(u8, u16)`
   --> $DIR/offset-of-tuple-field.rs:19:47
    |
 LL |     offset_of!(((u8, u16), (u32, u16, u8)), 0.1e2);
    |                                               ^^^
+   |
+   = note: available fields are: `0`, `1`
 
 error[E0609]: no field `0` on type `u8`
   --> $DIR/offset-of-tuple-field.rs:21:49
diff --git a/tests/ui/parser/float-field.stderr b/tests/ui/parser/float-field.stderr
index 0cc1b0767dc..078d16a4117 100644
--- a/tests/ui/parser/float-field.stderr
+++ b/tests/ui/parser/float-field.stderr
@@ -305,6 +305,8 @@ error[E0609]: no field `1e1` on type `(u8, u8)`
    |
 LL |     { s.1.1e1; }
    |           ^^^ unknown field
+   |
+   = note: available fields are: `0`, `1`
 
 error[E0609]: no field `0x1e1` on type `S`
   --> $DIR/float-field.rs:34:9
@@ -343,12 +345,16 @@ error[E0609]: no field `f32` on type `(u8, u8)`
    |
 LL |     { s.1.f32; }
    |           ^^^ unknown field
+   |
+   = note: available fields are: `0`, `1`
 
 error[E0609]: no field `1e1` on type `(u8, u8)`
   --> $DIR/float-field.rs:71:9
    |
 LL |     { s.1.1e1f32; }
    |         ^^^^^^^^ unknown field
+   |
+   = note: available fields are: `0`, `1`
 
 error: aborting due to 57 previous errors
 
diff --git a/tests/ui/structs/tuple-struct-field-naming-47073.stderr b/tests/ui/structs/tuple-struct-field-naming-47073.stderr
index efbdaeca4ea..09ba2fb406a 100644
--- a/tests/ui/structs/tuple-struct-field-naming-47073.stderr
+++ b/tests/ui/structs/tuple-struct-field-naming-47073.stderr
@@ -4,11 +4,7 @@ error[E0609]: no field `00` on type `Verdict`
 LL |     let _condemned = justice.00;
    |                              ^^ unknown field
    |
-help: a field with a similar name exists
-   |
-LL -     let _condemned = justice.00;
-LL +     let _condemned = justice.0;
-   |
+   = note: available fields are: `0`, `1`
 
 error[E0609]: no field `001` on type `Verdict`
   --> $DIR/tuple-struct-field-naming-47073.rs:11:31
diff --git a/tests/ui/traits/well-formed-recursion-limit.stderr b/tests/ui/traits/well-formed-recursion-limit.stderr
index e0270ecabbd..a4c85c4fcbd 100644
--- a/tests/ui/traits/well-formed-recursion-limit.stderr
+++ b/tests/ui/traits/well-formed-recursion-limit.stderr
@@ -3,12 +3,16 @@ error[E0609]: no field `ab` on type `(Box<(dyn Fn(Option<A>) -> Option<B> + 'sta
    |
 LL |     let (ab, ba) = (i.ab, i.ba);
    |                       ^^ unknown field
+   |
+   = note: available fields are: `0`, `1`
 
 error[E0609]: no field `ba` on type `(Box<(dyn Fn(Option<A>) -> Option<B> + 'static)>, Box<(dyn Fn(Option<B>) -> Option<A> + 'static)>)`
   --> $DIR/well-formed-recursion-limit.rs:12:29
    |
 LL |     let (ab, ba) = (i.ab, i.ba);
    |                             ^^ unknown field
+   |
+   = note: available fields are: `0`, `1`
 
 error[E0275]: overflow assigning `_` to `Option<_>`
   --> $DIR/well-formed-recursion-limit.rs:15:33
diff --git a/tests/ui/tuple/index-invalid.stderr b/tests/ui/tuple/index-invalid.stderr
index ae2c275f52c..fee09b7947c 100644
--- a/tests/ui/tuple/index-invalid.stderr
+++ b/tests/ui/tuple/index-invalid.stderr
@@ -3,18 +3,24 @@ error[E0609]: no field `1` on type `(((),),)`
    |
 LL |     let _ = (((),),).1.0;
    |                      ^ unknown field
+   |
+   = note: available field is: `0`
 
 error[E0609]: no field `1` on type `((),)`
   --> $DIR/index-invalid.rs:4:24
    |
 LL |     let _ = (((),),).0.1;
    |                        ^ unknown field
+   |
+   = note: available field is: `0`
 
 error[E0609]: no field `000` on type `(((),),)`
   --> $DIR/index-invalid.rs:6:22
    |
 LL |     let _ = (((),),).000.000;
    |                      ^^^ unknown field
+   |
+   = note: available field is: `0`
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/tuple/missing-field-access.rs b/tests/ui/tuple/missing-field-access.rs
new file mode 100644
index 00000000000..b94b7cf977c
--- /dev/null
+++ b/tests/ui/tuple/missing-field-access.rs
@@ -0,0 +1,16 @@
+// Ensure that suggestions to search for missing intermediary field accesses are available for both
+// tuple structs *and* regular tuples.
+// Ensure that we do not suggest pinning the expression just because `Pin::get_ref` exists.
+// https://github.com/rust-lang/rust/issues/144602
+use std::{fs::File, io::BufReader};
+
+struct F(BufReader<File>);
+
+fn main() {
+    let f = F(BufReader::new(File::open("x").unwrap()));
+    let x = f.get_ref(); //~ ERROR E0599
+    //~^ HELP one of the expressions' fields has a method of the same name
+    let f = (BufReader::new(File::open("x").unwrap()), );
+    let x = f.get_ref(); //~ ERROR E0599
+    //~^ HELP one of the expressions' fields has a method of the same name
+}
diff --git a/tests/ui/tuple/missing-field-access.stderr b/tests/ui/tuple/missing-field-access.stderr
new file mode 100644
index 00000000000..fd9f01f8ff6
--- /dev/null
+++ b/tests/ui/tuple/missing-field-access.stderr
@@ -0,0 +1,28 @@
+error[E0599]: no method named `get_ref` found for struct `F` in the current scope
+  --> $DIR/missing-field-access.rs:11:15
+   |
+LL | struct F(BufReader<File>);
+   | -------- method `get_ref` not found for this struct
+...
+LL |     let x = f.get_ref();
+   |               ^^^^^^^ method not found in `F`
+   |
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let x = f.0.get_ref();
+   |               ++
+
+error[E0599]: no method named `get_ref` found for tuple `(BufReader<File>,)` in the current scope
+  --> $DIR/missing-field-access.rs:14:15
+   |
+LL |     let x = f.get_ref();
+   |               ^^^^^^^ method not found in `(BufReader<File>,)`
+   |
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let x = f.0.get_ref();
+   |               ++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/tests/ui/tuple/tuple-index-out-of-bounds.stderr b/tests/ui/tuple/tuple-index-out-of-bounds.stderr
index 8b3c835c3e3..2be9d5631f7 100644
--- a/tests/ui/tuple/tuple-index-out-of-bounds.stderr
+++ b/tests/ui/tuple/tuple-index-out-of-bounds.stderr
@@ -4,17 +4,15 @@ error[E0609]: no field `2` on type `Point`
 LL |     origin.2;
    |            ^ unknown field
    |
-help: a field with a similar name exists
-   |
-LL -     origin.2;
-LL +     origin.0;
-   |
+   = note: available fields are: `0`, `1`
 
 error[E0609]: no field `2` on type `({integer}, {integer})`
   --> $DIR/tuple-index-out-of-bounds.rs:12:11
    |
 LL |     tuple.2;
    |           ^ unknown field
+   |
+   = note: available fields are: `0`, `1`
 
 error: aborting due to 2 previous errors