Skip to content

Commit 26c12c7

Browse files
committed
Account for bare tuples in field searching logic
When looking for the field names and types of a given type, account for tuples. This allows suggestions for incorrectly nested field accesses and field name typos to trigger as intended. Previously these suggestions only worked on `ty::Adt`, including tuple structs which are no different to tuples, so they should behave the same in suggestions. ``` error[E0599]: no method named `get_ref` found for tuple `(BufReader<File>,)` in the current scope --> $DIR/missing-field-access.rs:11: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(); | ++ ```
1 parent 2fd855f commit 26c12c7

File tree

12 files changed

+174
-22
lines changed

12 files changed

+174
-22
lines changed

compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3321,18 +3321,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33213321
} else {
33223322
(base_ty, "")
33233323
};
3324-
for (found_fields, args) in
3324+
for found_fields in
33253325
self.get_field_candidates_considering_privacy_for_diag(span, ty, mod_id, expr.hir_id)
33263326
{
3327-
let field_names = found_fields.iter().map(|field| field.name).collect::<Vec<_>>();
3327+
let field_names = found_fields.iter().map(|field| field.0.name).collect::<Vec<_>>();
33283328
let mut candidate_fields: Vec<_> = found_fields
33293329
.into_iter()
33303330
.filter_map(|candidate_field| {
33313331
self.check_for_nested_field_satisfying_condition_for_diag(
33323332
span,
3333-
&|candidate_field, _| candidate_field.ident(self.tcx()) == field,
3333+
&|candidate_field, _| candidate_field == field,
33343334
candidate_field,
3335-
args,
33363335
vec![],
33373336
mod_id,
33383337
expr.hir_id,
@@ -3396,7 +3395,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33963395
base_ty: Ty<'tcx>,
33973396
mod_id: DefId,
33983397
hir_id: HirId,
3399-
) -> Vec<(Vec<&'tcx ty::FieldDef>, GenericArgsRef<'tcx>)> {
3398+
) -> Vec<Vec<(Ident, Ty<'tcx>)>> {
34003399
debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty);
34013400

34023401
let mut autoderef = self.autoderef(span, base_ty).silence_errors();
@@ -3422,7 +3421,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
34223421
if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) {
34233422
return None;
34243423
}
3425-
return Some((
3424+
return Some(
34263425
fields
34273426
.iter()
34283427
.filter(move |field| {
@@ -3431,9 +3430,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
34313430
})
34323431
// For compile-time reasons put a limit on number of fields we search
34333432
.take(100)
3433+
.map(|field_def| {
3434+
(
3435+
field_def.ident(self.tcx).normalize_to_macros_2_0(),
3436+
field_def.ty(self.tcx, args),
3437+
)
3438+
})
3439+
.collect::<Vec<_>>(),
3440+
);
3441+
}
3442+
ty::Tuple(types) => {
3443+
return Some(
3444+
types
3445+
.iter()
3446+
.enumerate()
3447+
// For compile-time reasons put a limit on number of fields we search
3448+
.take(100)
3449+
.map(|(i, ty)| (Ident::from_str(&i.to_string()), ty))
34343450
.collect::<Vec<_>>(),
3435-
*args,
3436-
));
3451+
);
34373452
}
34383453
_ => None,
34393454
}
@@ -3446,9 +3461,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
34463461
pub(crate) fn check_for_nested_field_satisfying_condition_for_diag(
34473462
&self,
34483463
span: Span,
3449-
matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool,
3450-
candidate_field: &ty::FieldDef,
3451-
subst: GenericArgsRef<'tcx>,
3464+
matches: &impl Fn(Ident, Ty<'tcx>) -> bool,
3465+
candidate_field: (Ident, Ty<'tcx>),
34523466
mut field_path: Vec<Ident>,
34533467
mod_id: DefId,
34543468
hir_id: HirId,
@@ -3463,24 +3477,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
34633477
// up to a depth of three
34643478
None
34653479
} else {
3466-
field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0());
3467-
let field_ty = candidate_field.ty(self.tcx, subst);
3468-
if matches(candidate_field, field_ty) {
3480+
field_path.push(candidate_field.0);
3481+
let field_ty = candidate_field.1;
3482+
if matches(candidate_field.0, field_ty) {
34693483
return Some(field_path);
34703484
} else {
3471-
for (nested_fields, subst) in self
3472-
.get_field_candidates_considering_privacy_for_diag(
3473-
span, field_ty, mod_id, hir_id,
3474-
)
3475-
{
3485+
for nested_fields in self.get_field_candidates_considering_privacy_for_diag(
3486+
span, field_ty, mod_id, hir_id,
3487+
) {
34763488
// recursively search fields of `candidate_field` if it's a ty::Adt
34773489
for field in nested_fields {
34783490
if let Some(field_path) = self
34793491
.check_for_nested_field_satisfying_condition_for_diag(
34803492
span,
34813493
matches,
34823494
field,
3483-
subst,
34843495
field_path.clone(),
34853496
mod_id,
34863497
hir_id,

compiler/rustc_hir_typeck/src/method/suggest.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2792,7 +2792,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27922792
) {
27932793
if let SelfSource::MethodCall(expr) = source {
27942794
let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id();
2795-
for (fields, args) in self.get_field_candidates_considering_privacy_for_diag(
2795+
for fields in self.get_field_candidates_considering_privacy_for_diag(
27962796
span,
27972797
actual,
27982798
mod_id,
@@ -2831,7 +2831,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
28312831
})
28322832
},
28332833
candidate_field,
2834-
args,
28352834
vec![],
28362835
mod_id,
28372836
expr.hir_id,

tests/ui/coherence/coherence-tuple-conflict.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ error[E0609]: no field `dummy` on type `&(A, B)`
1212
|
1313
LL | fn get(&self) -> usize { self.dummy }
1414
| ^^^^^ unknown field
15+
|
16+
= note: available fields are: `0`, `1`
1517

1618
error: aborting due to 2 previous errors
1719

tests/ui/consts/issue-19244-1.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ error[E0609]: no field `1` on type `(usize,)`
33
|
44
LL | let a: [isize; TUP.1];
55
| ^ unknown field
6+
|
7+
help: a field with a similar name exists
8+
|
9+
LL - let a: [isize; TUP.1];
10+
LL + let a: [isize; TUP.0];
11+
|
612

713
error: aborting due to 1 previous error
814

tests/ui/diagnostic-width/long-E0609.stderr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ error[E0609]: no field `field` on type `(..., ..., ..., ...)`
44
LL | x.field;
55
| ^^^^^ unknown field
66
|
7+
= note: available fields are: `0`, `1`, `2`, `3`
78
= note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0609.long-type-$LONG_TYPE_HASH.txt'
89
= note: consider using `--verbose` to print the full type name to the console
910

tests/ui/offset-of/offset-of-tuple-field.stderr

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,60 +15,108 @@ error[E0609]: no field `_0` on type `(u8, u8)`
1515
|
1616
LL | offset_of!((u8, u8), _0);
1717
| ^^
18+
|
19+
help: a field with a similar name exists
20+
|
21+
LL - offset_of!((u8, u8), _0);
22+
LL + offset_of!((u8, u8), 0);
23+
|
1824

1925
error[E0609]: no field `01` on type `(u8, u8)`
2026
--> $DIR/offset-of-tuple-field.rs:7:26
2127
|
2228
LL | offset_of!((u8, u8), 01);
2329
| ^^
30+
|
31+
help: a field with a similar name exists
32+
|
33+
LL - offset_of!((u8, u8), 01);
34+
LL + offset_of!((u8, u8), 0);
35+
|
2436

2537
error[E0609]: no field `1e2` on type `(u8, u8)`
2638
--> $DIR/offset-of-tuple-field.rs:8:26
2739
|
2840
LL | offset_of!((u8, u8), 1e2);
2941
| ^^^
42+
|
43+
= note: available fields are: `0`, `1`
3044

3145
error[E0609]: no field `1_` on type `(u8, u8)`
3246
--> $DIR/offset-of-tuple-field.rs:9:26
3347
|
3448
LL | offset_of!((u8, u8), 1_u8);
3549
| ^^^^
50+
|
51+
help: a field with a similar name exists
52+
|
53+
LL - offset_of!((u8, u8), 1_u8);
54+
LL + offset_of!((u8, u8), 1);
55+
|
3656

3757
error[E0609]: no field `1e2` on type `(u8, u8)`
3858
--> $DIR/offset-of-tuple-field.rs:12:35
3959
|
4060
LL | builtin # offset_of((u8, u8), 1e2);
4161
| ^^^
62+
|
63+
= note: available fields are: `0`, `1`
4264

4365
error[E0609]: no field `_0` on type `(u8, u8)`
4466
--> $DIR/offset-of-tuple-field.rs:13:35
4567
|
4668
LL | builtin # offset_of((u8, u8), _0);
4769
| ^^
70+
|
71+
help: a field with a similar name exists
72+
|
73+
LL - builtin # offset_of((u8, u8), _0);
74+
LL + builtin # offset_of((u8, u8), 0);
75+
|
4876

4977
error[E0609]: no field `01` on type `(u8, u8)`
5078
--> $DIR/offset-of-tuple-field.rs:14:35
5179
|
5280
LL | builtin # offset_of((u8, u8), 01);
5381
| ^^
82+
|
83+
help: a field with a similar name exists
84+
|
85+
LL - builtin # offset_of((u8, u8), 01);
86+
LL + builtin # offset_of((u8, u8), 0);
87+
|
5488

5589
error[E0609]: no field `1_` on type `(u8, u8)`
5690
--> $DIR/offset-of-tuple-field.rs:15:35
5791
|
5892
LL | builtin # offset_of((u8, u8), 1_u8);
5993
| ^^^^
94+
|
95+
help: a field with a similar name exists
96+
|
97+
LL - builtin # offset_of((u8, u8), 1_u8);
98+
LL + builtin # offset_of((u8, u8), 1);
99+
|
60100

61101
error[E0609]: no field `2` on type `(u8, u16)`
62102
--> $DIR/offset-of-tuple-field.rs:18:47
63103
|
64104
LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.2);
65105
| ^
106+
|
107+
help: a field with a similar name exists
108+
|
109+
LL - offset_of!(((u8, u16), (u32, u16, u8)), 0.2);
110+
LL + offset_of!(((u8, u16), (u32, u16, u8)), 0.0);
111+
|
66112

67113
error[E0609]: no field `1e2` on type `(u8, u16)`
68114
--> $DIR/offset-of-tuple-field.rs:19:47
69115
|
70116
LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.1e2);
71117
| ^^^
118+
|
119+
= note: available fields are: `0`, `1`
72120

73121
error[E0609]: no field `0` on type `u8`
74122
--> $DIR/offset-of-tuple-field.rs:21:49

tests/ui/parser/float-field.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,8 @@ error[E0609]: no field `1e1` on type `(u8, u8)`
305305
|
306306
LL | { s.1.1e1; }
307307
| ^^^ unknown field
308+
|
309+
= note: available fields are: `0`, `1`
308310

309311
error[E0609]: no field `0x1e1` on type `S`
310312
--> $DIR/float-field.rs:34:9
@@ -343,12 +345,16 @@ error[E0609]: no field `f32` on type `(u8, u8)`
343345
|
344346
LL | { s.1.f32; }
345347
| ^^^ unknown field
348+
|
349+
= note: available fields are: `0`, `1`
346350

347351
error[E0609]: no field `1e1` on type `(u8, u8)`
348352
--> $DIR/float-field.rs:71:9
349353
|
350354
LL | { s.1.1e1f32; }
351355
| ^^^^^^^^ unknown field
356+
|
357+
= note: available fields are: `0`, `1`
352358

353359
error: aborting due to 57 previous errors
354360

tests/ui/traits/well-formed-recursion-limit.stderr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@ error[E0609]: no field `ab` on type `(Box<(dyn Fn(Option<A>) -> Option<B> + 'sta
33
|
44
LL | let (ab, ba) = (i.ab, i.ba);
55
| ^^ unknown field
6+
|
7+
= note: available fields are: `0`, `1`
68

79
error[E0609]: no field `ba` on type `(Box<(dyn Fn(Option<A>) -> Option<B> + 'static)>, Box<(dyn Fn(Option<B>) -> Option<A> + 'static)>)`
810
--> $DIR/well-formed-recursion-limit.rs:12:29
911
|
1012
LL | let (ab, ba) = (i.ab, i.ba);
1113
| ^^ unknown field
14+
|
15+
= note: available fields are: `0`, `1`
1216

1317
error[E0275]: overflow assigning `_` to `Option<_>`
1418
--> $DIR/well-formed-recursion-limit.rs:15:33

tests/ui/tuple/index-invalid.stderr

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,32 @@ error[E0609]: no field `1` on type `(((),),)`
33
|
44
LL | let _ = (((),),).1.0;
55
| ^ unknown field
6+
|
7+
help: a field with a similar name exists
8+
|
9+
LL - let _ = (((),),).1.0;
10+
LL + let _ = (((),),).0.0;
11+
|
612

713
error[E0609]: no field `1` on type `((),)`
814
--> $DIR/index-invalid.rs:4:24
915
|
1016
LL | let _ = (((),),).0.1;
1117
| ^ unknown field
18+
|
19+
help: a field with a similar name exists
20+
|
21+
LL - let _ = (((),),).0.1;
22+
LL + let _ = (((),),).0.0;
23+
|
1224

1325
error[E0609]: no field `000` on type `(((),),)`
1426
--> $DIR/index-invalid.rs:6:22
1527
|
1628
LL | let _ = (((),),).000.000;
1729
| ^^^ unknown field
30+
|
31+
= note: available field is: `0`
1832

1933
error: aborting due to 3 previous errors
2034

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use std::{fs::File, io::BufReader};
2+
3+
struct F(BufReader<File>);
4+
5+
fn main() {
6+
let f = F(BufReader::new(File::open("x").unwrap()));
7+
let x = f.get_ref(); //~ ERROR E0599
8+
//~^ HELP one of the expressions' fields has a method of the same name
9+
//~| HELP consider pinning the expression
10+
let f = (BufReader::new(File::open("x").unwrap()), );
11+
let x = f.get_ref(); //~ ERROR E0599
12+
//~^ HELP one of the expressions' fields has a method of the same name
13+
//~| HELP consider pinning the expression
14+
15+
// FIXME(estebank): the pinning suggestion should not be included in either case.
16+
// https://github.com/rust-lang/rust/issues/144602
17+
}

0 commit comments

Comments
 (0)