Skip to content

Commit 8b6da96

Browse files
authored
Unrolled build for #144649
Rollup merge of #144649 - estebank:issue-144602, r=lcnr Account for bare tuples and `Pin` methods 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. When suggesting field access which would encounter a method not found, do not suggest pinning when those methods are on `impl Pin` itself. ``` 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(); | ++ ``` instead of ``` 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: consider pinning the expression | LL ~ let mut pinned = std::pin::pin!(f); LL ~ let x = pinned.as_ref().get_ref(); | ``` Fix #144602.
2 parents de3efa7 + eace82c commit 8b6da96

14 files changed

+169
-65
lines changed

compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 54 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2745,6 +2745,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27452745
let available_field_names = self.available_field_names(variant, expr, skip_fields);
27462746
if let Some(field_name) =
27472747
find_best_match_for_name(&available_field_names, field.ident.name, None)
2748+
&& !(field.ident.name.as_str().parse::<usize>().is_ok()
2749+
&& field_name.as_str().parse::<usize>().is_ok())
27482750
{
27492751
err.span_label(field.ident.span, "unknown field");
27502752
err.span_suggestion_verbose(
@@ -3321,18 +3323,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33213323
} else {
33223324
(base_ty, "")
33233325
};
3324-
for (found_fields, args) in
3326+
for found_fields in
33253327
self.get_field_candidates_considering_privacy_for_diag(span, ty, mod_id, expr.hir_id)
33263328
{
3327-
let field_names = found_fields.iter().map(|field| field.name).collect::<Vec<_>>();
3329+
let field_names = found_fields.iter().map(|field| field.0.name).collect::<Vec<_>>();
33283330
let mut candidate_fields: Vec<_> = found_fields
33293331
.into_iter()
33303332
.filter_map(|candidate_field| {
33313333
self.check_for_nested_field_satisfying_condition_for_diag(
33323334
span,
3333-
&|candidate_field, _| candidate_field.ident(self.tcx()) == field,
3335+
&|candidate_field, _| candidate_field == field,
33343336
candidate_field,
3335-
args,
33363337
vec![],
33373338
mod_id,
33383339
expr.hir_id,
@@ -3361,6 +3362,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33613362
);
33623363
} else if let Some(field_name) =
33633364
find_best_match_for_name(&field_names, field.name, None)
3365+
&& !(field.name.as_str().parse::<usize>().is_ok()
3366+
&& field_name.as_str().parse::<usize>().is_ok())
33643367
{
33653368
err.span_suggestion_verbose(
33663369
field.span,
@@ -3396,7 +3399,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33963399
base_ty: Ty<'tcx>,
33973400
mod_id: DefId,
33983401
hir_id: HirId,
3399-
) -> Vec<(Vec<&'tcx ty::FieldDef>, GenericArgsRef<'tcx>)> {
3402+
) -> Vec<Vec<(Ident, Ty<'tcx>)>> {
34003403
debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty);
34013404

34023405
let mut autoderef = self.autoderef(span, base_ty).silence_errors();
@@ -3422,7 +3425,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
34223425
if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) {
34233426
return None;
34243427
}
3425-
return Some((
3428+
return Some(
34263429
fields
34273430
.iter()
34283431
.filter(move |field| {
@@ -3431,9 +3434,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
34313434
})
34323435
// For compile-time reasons put a limit on number of fields we search
34333436
.take(100)
3437+
.map(|field_def| {
3438+
(
3439+
field_def.ident(self.tcx).normalize_to_macros_2_0(),
3440+
field_def.ty(self.tcx, args),
3441+
)
3442+
})
3443+
.collect::<Vec<_>>(),
3444+
);
3445+
}
3446+
ty::Tuple(types) => {
3447+
return Some(
3448+
types
3449+
.iter()
3450+
.enumerate()
3451+
// For compile-time reasons put a limit on number of fields we search
3452+
.take(100)
3453+
.map(|(i, ty)| (Ident::from_str(&i.to_string()), ty))
34343454
.collect::<Vec<_>>(),
3435-
*args,
3436-
));
3455+
);
34373456
}
34383457
_ => None,
34393458
}
@@ -3443,56 +3462,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
34433462

34443463
/// This method is called after we have encountered a missing field error to recursively
34453464
/// search for the field
3465+
#[instrument(skip(self, matches, mod_id, hir_id), level = "debug")]
34463466
pub(crate) fn check_for_nested_field_satisfying_condition_for_diag(
34473467
&self,
34483468
span: Span,
3449-
matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool,
3450-
candidate_field: &ty::FieldDef,
3451-
subst: GenericArgsRef<'tcx>,
3469+
matches: &impl Fn(Ident, Ty<'tcx>) -> bool,
3470+
(candidate_name, candidate_ty): (Ident, Ty<'tcx>),
34523471
mut field_path: Vec<Ident>,
34533472
mod_id: DefId,
34543473
hir_id: HirId,
34553474
) -> Option<Vec<Ident>> {
3456-
debug!(
3457-
"check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}",
3458-
span, candidate_field, field_path
3459-
);
3460-
34613475
if field_path.len() > 3 {
34623476
// For compile-time reasons and to avoid infinite recursion we only check for fields
34633477
// up to a depth of three
3464-
None
3465-
} 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) {
3469-
return Some(field_path);
3470-
} 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-
{
3476-
// recursively search fields of `candidate_field` if it's a ty::Adt
3477-
for field in nested_fields {
3478-
if let Some(field_path) = self
3479-
.check_for_nested_field_satisfying_condition_for_diag(
3480-
span,
3481-
matches,
3482-
field,
3483-
subst,
3484-
field_path.clone(),
3485-
mod_id,
3486-
hir_id,
3487-
)
3488-
{
3489-
return Some(field_path);
3490-
}
3491-
}
3478+
return None;
3479+
}
3480+
field_path.push(candidate_name);
3481+
if matches(candidate_name, candidate_ty) {
3482+
return Some(field_path);
3483+
}
3484+
for nested_fields in self.get_field_candidates_considering_privacy_for_diag(
3485+
span,
3486+
candidate_ty,
3487+
mod_id,
3488+
hir_id,
3489+
) {
3490+
// recursively search fields of `candidate_field` if it's a ty::Adt
3491+
for field in nested_fields {
3492+
if let Some(field_path) = self.check_for_nested_field_satisfying_condition_for_diag(
3493+
span,
3494+
matches,
3495+
field,
3496+
field_path.clone(),
3497+
mod_id,
3498+
hir_id,
3499+
) {
3500+
return Some(field_path);
34923501
}
34933502
}
3494-
None
34953503
}
3504+
None
34963505
}
34973506

34983507
fn check_expr_index(

compiler/rustc_hir_typeck/src/method/suggest.rs

Lines changed: 9 additions & 5 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,
@@ -3671,7 +3670,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
36713670
)
36723671
{
36733672
debug!("try_alt_rcvr: pick candidate {:?}", pick);
3674-
let did = Some(pick.item.container_id(self.tcx));
3673+
let did = pick.item.trait_container(self.tcx);
36753674
// We don't want to suggest a container type when the missing
36763675
// method is `.clone()` or `.deref()` otherwise we'd suggest
36773676
// `Arc::new(foo).clone()`, which is far from what the user wants.
@@ -3720,8 +3719,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
37203719
&& !alt_rcvr_sugg
37213720
// `T: !Unpin`
37223721
&& !unpin
3723-
// The method isn't `as_ref`, as it would provide a wrong suggestion for `Pin`.
3724-
&& sym::as_ref != item_name.name
37253722
// Either `Pin::as_ref` or `Pin::as_mut`.
37263723
&& let Some(pin_call) = pin_call
37273724
// Search for `item_name` as a method accessible on `Pin<T>`.
@@ -3735,6 +3732,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
37353732
// We skip some common traits that we don't want to consider because autoderefs
37363733
// would take care of them.
37373734
&& !skippable.contains(&Some(pick.item.container_id(self.tcx)))
3735+
// Do not suggest pinning when the method is directly on `Pin`.
3736+
&& pick.item.impl_container(self.tcx).map_or(true, |did| {
3737+
match self.tcx.type_of(did).skip_binder().kind() {
3738+
ty::Adt(def, _) => Some(def.did()) != self.tcx.lang_items().pin_type(),
3739+
_ => true,
3740+
}
3741+
})
37383742
// We don't want to go through derefs.
37393743
&& pick.autoderefs == 0
37403744
// Check that the method of the same name that was found on the new `Pin<T>`

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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ error[E0609]: no field `1` on type `(usize,)`
33
|
44
LL | let a: [isize; TUP.1];
55
| ^ unknown field
6+
|
7+
= note: available field is: `0`
68

79
error: aborting due to 1 previous error
810

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/error-codes/ex-E0612.stderr

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ error[E0609]: no field `1` on type `Foo`
44
LL | y.1;
55
| ^ unknown field
66
|
7-
help: a field with a similar name exists
8-
|
9-
LL - y.1;
10-
LL + y.0;
11-
|
7+
= note: available field is: `0`
128

139
error: aborting due to 1 previous error
1410

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,60 +15,96 @@ 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+
= note: available fields are: `0`, `1`
2432

2533
error[E0609]: no field `1e2` on type `(u8, u8)`
2634
--> $DIR/offset-of-tuple-field.rs:8:26
2735
|
2836
LL | offset_of!((u8, u8), 1e2);
2937
| ^^^
38+
|
39+
= note: available fields are: `0`, `1`
3040

3141
error[E0609]: no field `1_` on type `(u8, u8)`
3242
--> $DIR/offset-of-tuple-field.rs:9:26
3343
|
3444
LL | offset_of!((u8, u8), 1_u8);
3545
| ^^^^
46+
|
47+
help: a field with a similar name exists
48+
|
49+
LL - offset_of!((u8, u8), 1_u8);
50+
LL + offset_of!((u8, u8), 1);
51+
|
3652

3753
error[E0609]: no field `1e2` on type `(u8, u8)`
3854
--> $DIR/offset-of-tuple-field.rs:12:35
3955
|
4056
LL | builtin # offset_of((u8, u8), 1e2);
4157
| ^^^
58+
|
59+
= note: available fields are: `0`, `1`
4260

4361
error[E0609]: no field `_0` on type `(u8, u8)`
4462
--> $DIR/offset-of-tuple-field.rs:13:35
4563
|
4664
LL | builtin # offset_of((u8, u8), _0);
4765
| ^^
66+
|
67+
help: a field with a similar name exists
68+
|
69+
LL - builtin # offset_of((u8, u8), _0);
70+
LL + builtin # offset_of((u8, u8), 0);
71+
|
4872

4973
error[E0609]: no field `01` on type `(u8, u8)`
5074
--> $DIR/offset-of-tuple-field.rs:14:35
5175
|
5276
LL | builtin # offset_of((u8, u8), 01);
5377
| ^^
78+
|
79+
= note: available fields are: `0`, `1`
5480

5581
error[E0609]: no field `1_` on type `(u8, u8)`
5682
--> $DIR/offset-of-tuple-field.rs:15:35
5783
|
5884
LL | builtin # offset_of((u8, u8), 1_u8);
5985
| ^^^^
86+
|
87+
help: a field with a similar name exists
88+
|
89+
LL - builtin # offset_of((u8, u8), 1_u8);
90+
LL + builtin # offset_of((u8, u8), 1);
91+
|
6092

6193
error[E0609]: no field `2` on type `(u8, u16)`
6294
--> $DIR/offset-of-tuple-field.rs:18:47
6395
|
6496
LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.2);
6597
| ^
98+
|
99+
= note: available fields are: `0`, `1`
66100

67101
error[E0609]: no field `1e2` on type `(u8, u16)`
68102
--> $DIR/offset-of-tuple-field.rs:19:47
69103
|
70104
LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.1e2);
71105
| ^^^
106+
|
107+
= note: available fields are: `0`, `1`
72108

73109
error[E0609]: no field `0` on type `u8`
74110
--> $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/structs/tuple-struct-field-naming-47073.stderr

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ error[E0609]: no field `00` on type `Verdict`
44
LL | let _condemned = justice.00;
55
| ^^ unknown field
66
|
7-
help: a field with a similar name exists
8-
|
9-
LL - let _condemned = justice.00;
10-
LL + let _condemned = justice.0;
11-
|
7+
= note: available fields are: `0`, `1`
128

139
error[E0609]: no field `001` on type `Verdict`
1410
--> $DIR/tuple-struct-field-naming-47073.rs:11:31

0 commit comments

Comments
 (0)