Skip to content

Commit

Permalink
Rollup merge of rust-lang#96372 - compiler-errors:field-method-sugges…
Browse files Browse the repository at this point in the history
…t, 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()`.
  • Loading branch information
compiler-errors authored Apr 25, 2022
2 parents 9fe2d8a + dff7f25 commit aff1933
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 21 deletions.
36 changes: 16 additions & 20 deletions compiler/rustc_typeck/src/check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2277,14 +2277,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// try to add a suggestion in case the field is a nested field of a field of the Adt
if let Some((fields, substs)) = self.get_field_candidates(span, expr_t) {
for candidate_field in fields.iter() {
if let Some(field_path) = self.check_for_nested_field(
if let Some(mut field_path) = self.check_for_nested_field_satisfying(
span,
field,
&|candidate_field, _| candidate_field.ident(self.tcx()) == field,
candidate_field,
substs,
vec![],
self.tcx.parent_module(id).to_def_id(),
) {
// field_path includes `field` that we're looking for, so pop it.
field_path.pop();

let field_path_str = field_path
.iter()
.map(|id| id.name.to_ident_string())
Expand All @@ -2304,7 +2307,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err
}

fn get_field_candidates(
crate fn get_field_candidates(
&self,
span: Span,
base_t: Ty<'tcx>,
Expand All @@ -2329,49 +2332,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

/// This method is called after we have encountered a missing field error to recursively
/// search for the field
fn check_for_nested_field(
crate fn check_for_nested_field_satisfying(
&self,
span: Span,
target_field: Ident,
matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool,
candidate_field: &ty::FieldDef,
subst: SubstsRef<'tcx>,
mut field_path: Vec<Ident>,
id: DefId,
) -> Option<Vec<Ident>> {
debug!(
"check_for_nested_field(span: {:?}, candidate_field: {:?}, field_path: {:?}",
"check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}",
span, candidate_field, field_path
);

if candidate_field.ident(self.tcx) == target_field {
Some(field_path)
} else if field_path.len() > 3 {
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 {
// recursively search fields of `candidate_field` if it's a ty::Adt

field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0());
let field_ty = candidate_field.ty(self.tcx, subst);
if let Some((nested_fields, subst)) = self.get_field_candidates(span, field_ty) {
for field in nested_fields.iter() {
let accessible = field.vis.is_accessible_from(id, self.tcx);
if accessible {
let ident = field.ident(self.tcx).normalize_to_macros_2_0();
if ident == target_field {
if field.vis.is_accessible_from(id, self.tcx) {
if matches(candidate_field, field_ty) {
return Some(field_path);
}
let field_path = field_path.clone();
if let Some(path) = self.check_for_nested_field(
} else if let Some(field_path) = self.check_for_nested_field_satisfying(
span,
target_field,
matches,
field,
subst,
field_path,
field_path.clone(),
id,
) {
return Some(path);
return Some(field_path);
}
}
}
Expand Down
42 changes: 41 additions & 1 deletion compiler/rustc_typeck/src/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use rustc_trait_selection::traits::{
use std::cmp::Ordering;
use std::iter;

use super::probe::Mode;
use super::probe::{Mode, ProbeScope};
use super::{CandidateSource, MethodError, NoMatchData};

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Expand Down Expand Up @@ -1129,6 +1129,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
label_span_not_found();
}

if let SelfSource::MethodCall(expr) = source
&& let Some((fields, substs)) = self.get_field_candidates(span, actual)
{
let call_expr =
self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
for candidate_field in fields.iter() {
if let Some(field_path) = self.check_for_nested_field_satisfying(
span,
&|_, field_ty| {
self.lookup_probe(
span,
item_name,
field_ty,
call_expr,
ProbeScope::AllTraits,
)
.is_ok()
},
candidate_field,
substs,
vec![],
self.tcx.parent_module(expr.hir_id).to_def_id(),
) {
let field_path_str = field_path
.iter()
.map(|id| id.name.to_ident_string())
.collect::<Vec<String>>()
.join(".");
debug!("field_path_str: {:?}", field_path_str);

err.span_suggestion_verbose(
item_name.span.shrink_to_lo(),
"one of the expressions' fields has a method of the same name",
format!("{field_path_str}."),
Applicability::MaybeIncorrect,
);
}
}
}

bound_spans.sort();
bound_spans.dedup();
for (span, msg) in bound_spans.into_iter() {
Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/hrtb/issue-30786.migrate.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ note: the following trait bounds were not satisfied:
|
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
| --------- - ^^^^^^ unsatisfied trait bound introduced here
help: one of the expressions' fields has a method of the same name
|
LL | let filter = map.stream.filterx(|x: &_| true);
| +++++++

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
--> $DIR/issue-30786.rs:140:24
Expand All @@ -39,6 +43,10 @@ note: the following trait bounds were not satisfied:
|
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
| --------- - ^^^^^^ unsatisfied trait bound introduced here
help: one of the expressions' fields has a method of the same name
|
LL | let count = filter.stream.countx();
| +++++++

error: aborting due to 2 previous errors

Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/hrtb/issue-30786.nll.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ note: the following trait bounds were not satisfied:
|
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
| --------- - ^^^^^^ unsatisfied trait bound introduced here
help: one of the expressions' fields has a method of the same name
|
LL | let filter = map.stream.filterx(|x: &_| true);
| +++++++

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
--> $DIR/issue-30786.rs:140:24
Expand All @@ -39,6 +43,10 @@ note: the following trait bounds were not satisfied:
|
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
| --------- - ^^^^^^ unsatisfied trait bound introduced here
help: one of the expressions' fields has a method of the same name
|
LL | let count = filter.stream.countx();
| +++++++

error: aborting due to 2 previous errors

Expand Down
23 changes: 23 additions & 0 deletions src/test/ui/suggestions/field-has-method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
struct Kind;

struct Ty {
kind: Kind,
}

impl Ty {
fn kind(&self) -> Kind {
todo!()
}
}

struct InferOk<T> {
value: T,
predicates: Vec<()>,
}

fn foo(i: InferOk<Ty>) {
let k = i.kind();
//~^ no method named `kind` found for struct `InferOk` in the current scope
}

fn main() {}
17 changes: 17 additions & 0 deletions src/test/ui/suggestions/field-has-method.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0599]: no method named `kind` found for struct `InferOk` in the current scope
--> $DIR/field-has-method.rs:19:15
|
LL | struct InferOk<T> {
| ----------------- method `kind` not found for this
...
LL | let k = i.kind();
| ^^^^ method not found in `InferOk<Ty>`
|
help: one of the expressions' fields has a method of the same name
|
LL | let k = i.value.kind();
| ++++++

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.

0 comments on commit aff1933

Please sign in to comment.