Skip to content

Commit aff1933

Browse files
Rollup merge of rust-lang#96372 - compiler-errors:field-method-suggest, r=oli-obk
Suggest calling method on nested field when struct is missing method Similar to the suggestion to change `x.field` to `x.nested.field`, implement a similar suggestion for when `x.method()` should be replaced with `x.nested.method()`.
2 parents 9fe2d8a + dff7f25 commit aff1933

File tree

6 files changed

+113
-21
lines changed

6 files changed

+113
-21
lines changed

compiler/rustc_typeck/src/check/expr.rs

+16-20
Original file line numberDiff line numberDiff line change
@@ -2277,14 +2277,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22772277
// try to add a suggestion in case the field is a nested field of a field of the Adt
22782278
if let Some((fields, substs)) = self.get_field_candidates(span, expr_t) {
22792279
for candidate_field in fields.iter() {
2280-
if let Some(field_path) = self.check_for_nested_field(
2280+
if let Some(mut field_path) = self.check_for_nested_field_satisfying(
22812281
span,
2282-
field,
2282+
&|candidate_field, _| candidate_field.ident(self.tcx()) == field,
22832283
candidate_field,
22842284
substs,
22852285
vec![],
22862286
self.tcx.parent_module(id).to_def_id(),
22872287
) {
2288+
// field_path includes `field` that we're looking for, so pop it.
2289+
field_path.pop();
2290+
22882291
let field_path_str = field_path
22892292
.iter()
22902293
.map(|id| id.name.to_ident_string())
@@ -2304,7 +2307,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23042307
err
23052308
}
23062309

2307-
fn get_field_candidates(
2310+
crate fn get_field_candidates(
23082311
&self,
23092312
span: Span,
23102313
base_t: Ty<'tcx>,
@@ -2329,49 +2332,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23292332

23302333
/// This method is called after we have encountered a missing field error to recursively
23312334
/// search for the field
2332-
fn check_for_nested_field(
2335+
crate fn check_for_nested_field_satisfying(
23332336
&self,
23342337
span: Span,
2335-
target_field: Ident,
2338+
matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool,
23362339
candidate_field: &ty::FieldDef,
23372340
subst: SubstsRef<'tcx>,
23382341
mut field_path: Vec<Ident>,
23392342
id: DefId,
23402343
) -> Option<Vec<Ident>> {
23412344
debug!(
2342-
"check_for_nested_field(span: {:?}, candidate_field: {:?}, field_path: {:?}",
2345+
"check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}",
23432346
span, candidate_field, field_path
23442347
);
23452348

2346-
if candidate_field.ident(self.tcx) == target_field {
2347-
Some(field_path)
2348-
} else if field_path.len() > 3 {
2349+
if field_path.len() > 3 {
23492350
// For compile-time reasons and to avoid infinite recursion we only check for fields
23502351
// up to a depth of three
23512352
None
23522353
} else {
23532354
// recursively search fields of `candidate_field` if it's a ty::Adt
2354-
23552355
field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0());
23562356
let field_ty = candidate_field.ty(self.tcx, subst);
23572357
if let Some((nested_fields, subst)) = self.get_field_candidates(span, field_ty) {
23582358
for field in nested_fields.iter() {
2359-
let accessible = field.vis.is_accessible_from(id, self.tcx);
2360-
if accessible {
2361-
let ident = field.ident(self.tcx).normalize_to_macros_2_0();
2362-
if ident == target_field {
2359+
if field.vis.is_accessible_from(id, self.tcx) {
2360+
if matches(candidate_field, field_ty) {
23632361
return Some(field_path);
2364-
}
2365-
let field_path = field_path.clone();
2366-
if let Some(path) = self.check_for_nested_field(
2362+
} else if let Some(field_path) = self.check_for_nested_field_satisfying(
23672363
span,
2368-
target_field,
2364+
matches,
23692365
field,
23702366
subst,
2371-
field_path,
2367+
field_path.clone(),
23722368
id,
23732369
) {
2374-
return Some(path);
2370+
return Some(field_path);
23752371
}
23762372
}
23772373
}

compiler/rustc_typeck/src/check/method/suggest.rs

+41-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use rustc_trait_selection::traits::{
2828
use std::cmp::Ordering;
2929
use std::iter;
3030

31-
use super::probe::Mode;
31+
use super::probe::{Mode, ProbeScope};
3232
use super::{CandidateSource, MethodError, NoMatchData};
3333

3434
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -1129,6 +1129,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11291129
label_span_not_found();
11301130
}
11311131

1132+
if let SelfSource::MethodCall(expr) = source
1133+
&& let Some((fields, substs)) = self.get_field_candidates(span, actual)
1134+
{
1135+
let call_expr =
1136+
self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
1137+
for candidate_field in fields.iter() {
1138+
if let Some(field_path) = self.check_for_nested_field_satisfying(
1139+
span,
1140+
&|_, field_ty| {
1141+
self.lookup_probe(
1142+
span,
1143+
item_name,
1144+
field_ty,
1145+
call_expr,
1146+
ProbeScope::AllTraits,
1147+
)
1148+
.is_ok()
1149+
},
1150+
candidate_field,
1151+
substs,
1152+
vec![],
1153+
self.tcx.parent_module(expr.hir_id).to_def_id(),
1154+
) {
1155+
let field_path_str = field_path
1156+
.iter()
1157+
.map(|id| id.name.to_ident_string())
1158+
.collect::<Vec<String>>()
1159+
.join(".");
1160+
debug!("field_path_str: {:?}", field_path_str);
1161+
1162+
err.span_suggestion_verbose(
1163+
item_name.span.shrink_to_lo(),
1164+
"one of the expressions' fields has a method of the same name",
1165+
format!("{field_path_str}."),
1166+
Applicability::MaybeIncorrect,
1167+
);
1168+
}
1169+
}
1170+
}
1171+
11321172
bound_spans.sort();
11331173
bound_spans.dedup();
11341174
for (span, msg) in bound_spans.into_iter() {

src/test/ui/hrtb/issue-30786.migrate.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ note: the following trait bounds were not satisfied:
1818
|
1919
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
2020
| --------- - ^^^^^^ unsatisfied trait bound introduced here
21+
help: one of the expressions' fields has a method of the same name
22+
|
23+
LL | let filter = map.stream.filterx(|x: &_| true);
24+
| +++++++
2125

2226
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:139:30: 139:42]>`, but its trait bounds were not satisfied
2327
--> $DIR/issue-30786.rs:140:24
@@ -39,6 +43,10 @@ note: the following trait bounds were not satisfied:
3943
|
4044
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
4145
| --------- - ^^^^^^ unsatisfied trait bound introduced here
46+
help: one of the expressions' fields has a method of the same name
47+
|
48+
LL | let count = filter.stream.countx();
49+
| +++++++
4250

4351
error: aborting due to 2 previous errors
4452

src/test/ui/hrtb/issue-30786.nll.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ note: the following trait bounds were not satisfied:
1818
|
1919
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
2020
| --------- - ^^^^^^ unsatisfied trait bound introduced here
21+
help: one of the expressions' fields has a method of the same name
22+
|
23+
LL | let filter = map.stream.filterx(|x: &_| true);
24+
| +++++++
2125

2226
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:139:30: 139:42]>`, but its trait bounds were not satisfied
2327
--> $DIR/issue-30786.rs:140:24
@@ -39,6 +43,10 @@ note: the following trait bounds were not satisfied:
3943
|
4044
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
4145
| --------- - ^^^^^^ unsatisfied trait bound introduced here
46+
help: one of the expressions' fields has a method of the same name
47+
|
48+
LL | let count = filter.stream.countx();
49+
| +++++++
4250

4351
error: aborting due to 2 previous errors
4452

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
struct Kind;
2+
3+
struct Ty {
4+
kind: Kind,
5+
}
6+
7+
impl Ty {
8+
fn kind(&self) -> Kind {
9+
todo!()
10+
}
11+
}
12+
13+
struct InferOk<T> {
14+
value: T,
15+
predicates: Vec<()>,
16+
}
17+
18+
fn foo(i: InferOk<Ty>) {
19+
let k = i.kind();
20+
//~^ no method named `kind` found for struct `InferOk` in the current scope
21+
}
22+
23+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0599]: no method named `kind` found for struct `InferOk` in the current scope
2+
--> $DIR/field-has-method.rs:19:15
3+
|
4+
LL | struct InferOk<T> {
5+
| ----------------- method `kind` not found for this
6+
...
7+
LL | let k = i.kind();
8+
| ^^^^ method not found in `InferOk<Ty>`
9+
|
10+
help: one of the expressions' fields has a method of the same name
11+
|
12+
LL | let k = i.value.kind();
13+
| ++++++
14+
15+
error: aborting due to previous error
16+
17+
For more information about this error, try `rustc --explain E0599`.

0 commit comments

Comments
 (0)