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(); | ++ ```
This commit is contained in:
@@ -3321,18 +3321,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,
|
||||
@@ -3396,7 +3395,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 +3421,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 +3430,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<_>>(),
|
||||
*args,
|
||||
));
|
||||
);
|
||||
}
|
||||
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<_>>(),
|
||||
);
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
@@ -3446,9 +3461,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
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_field: (Ident, Ty<'tcx>),
|
||||
mut field_path: Vec<Ident>,
|
||||
mod_id: DefId,
|
||||
hir_id: HirId,
|
||||
@@ -3463,16 +3477,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// 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) {
|
||||
field_path.push(candidate_field.0);
|
||||
let field_ty = candidate_field.1;
|
||||
if matches(candidate_field.0, 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,
|
||||
)
|
||||
{
|
||||
for nested_fields 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
|
||||
@@ -3480,7 +3492,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
span,
|
||||
matches,
|
||||
field,
|
||||
subst,
|
||||
field_path.clone(),
|
||||
mod_id,
|
||||
hir_id,
|
||||
|
||||
@@ -2792,7 +2792,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,
|
||||
@@ -2831,7 +2831,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
})
|
||||
},
|
||||
candidate_field,
|
||||
args,
|
||||
vec![],
|
||||
mod_id,
|
||||
expr.hir_id,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -3,6 +3,12 @@ error[E0609]: no field `1` on type `(usize,)`
|
||||
|
|
||||
LL | let a: [isize; TUP.1];
|
||||
| ^ unknown field
|
||||
|
|
||||
help: a field with a similar name exists
|
||||
|
|
||||
LL - let a: [isize; TUP.1];
|
||||
LL + let a: [isize; TUP.0];
|
||||
|
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -15,60 +15,108 @@ 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);
|
||||
| ^^
|
||||
|
|
||||
help: a field with a similar name exists
|
||||
|
|
||||
LL - offset_of!((u8, u8), 01);
|
||||
LL + offset_of!((u8, u8), 0);
|
||||
|
|
||||
|
||||
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);
|
||||
| ^^
|
||||
|
|
||||
help: a field with a similar name exists
|
||||
|
|
||||
LL - builtin # offset_of((u8, u8), 01);
|
||||
LL + builtin # offset_of((u8, u8), 0);
|
||||
|
|
||||
|
||||
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);
|
||||
| ^
|
||||
|
|
||||
help: a field with a similar name exists
|
||||
|
|
||||
LL - offset_of!(((u8, u16), (u32, u16, u8)), 0.2);
|
||||
LL + offset_of!(((u8, u16), (u32, u16, u8)), 0.0);
|
||||
|
|
||||
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,18 +3,32 @@ error[E0609]: no field `1` on type `(((),),)`
|
||||
|
|
||||
LL | let _ = (((),),).1.0;
|
||||
| ^ unknown field
|
||||
|
|
||||
help: a field with a similar name exists
|
||||
|
|
||||
LL - let _ = (((),),).1.0;
|
||||
LL + let _ = (((),),).0.0;
|
||||
|
|
||||
|
||||
error[E0609]: no field `1` on type `((),)`
|
||||
--> $DIR/index-invalid.rs:4:24
|
||||
|
|
||||
LL | let _ = (((),),).0.1;
|
||||
| ^ unknown field
|
||||
|
|
||||
help: a field with a similar name exists
|
||||
|
|
||||
LL - let _ = (((),),).0.1;
|
||||
LL + let _ = (((),),).0.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
|
||||
|
||||
|
||||
17
tests/ui/tuple/missing-field-access.rs
Normal file
17
tests/ui/tuple/missing-field-access.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
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
|
||||
//~| HELP consider pinning the expression
|
||||
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
|
||||
//~| HELP consider pinning the expression
|
||||
|
||||
// FIXME(estebank): the pinning suggestion should not be included in either case.
|
||||
// https://github.com/rust-lang/rust/issues/144602
|
||||
}
|
||||
38
tests/ui/tuple/missing-field-access.stderr
Normal file
38
tests/ui/tuple/missing-field-access.stderr
Normal file
@@ -0,0 +1,38 @@
|
||||
error[E0599]: no method named `get_ref` found for struct `F` in the current scope
|
||||
--> $DIR/missing-field-access.rs:7: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();
|
||||
| ++
|
||||
help: consider pinning the expression
|
||||
|
|
||||
LL ~ let mut pinned = std::pin::pin!(f);
|
||||
LL ~ let x = pinned.as_ref().get_ref();
|
||||
|
|
||||
|
||||
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();
|
||||
| ++
|
||||
help: consider pinning the expression
|
||||
|
|
||||
LL ~ let mut pinned = std::pin::pin!(f);
|
||||
LL ~ let x = pinned.as_ref().get_ref();
|
||||
|
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
||||
@@ -15,6 +15,12 @@ error[E0609]: no field `2` on type `({integer}, {integer})`
|
||||
|
|
||||
LL | tuple.2;
|
||||
| ^ unknown field
|
||||
|
|
||||
help: a field with a similar name exists
|
||||
|
|
||||
LL - tuple.2;
|
||||
LL + tuple.0;
|
||||
|
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
||||
Reference in New Issue
Block a user