Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some "this expression has a field"-related fixes #100098

Merged
merged 4 commits into from
Aug 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 43 additions & 29 deletions compiler/rustc_typeck/src/check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2535,15 +2535,18 @@ 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() {
let mod_id = self.tcx.parent_module(id).to_def_id();
if let Some((fields, substs)) =
self.get_field_candidates_considering_privacy(span, expr_t, mod_id)
{
for candidate_field in fields {
if let Some(mut field_path) = self.check_for_nested_field_satisfying(
span,
&|candidate_field, _| candidate_field.ident(self.tcx()) == field,
candidate_field,
substs,
vec![],
self.tcx.parent_module(id).to_def_id(),
mod_id,
) {
// field_path includes `field` that we're looking for, so pop it.
field_path.pop();
Expand All @@ -2567,22 +2570,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err
}

pub(crate) fn get_field_candidates(
pub(crate) fn get_field_candidates_considering_privacy(
&self,
span: Span,
base_t: Ty<'tcx>,
) -> Option<(&[ty::FieldDef], SubstsRef<'tcx>)> {
debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_t);
base_ty: Ty<'tcx>,
mod_id: DefId,
) -> Option<(impl Iterator<Item = &'tcx ty::FieldDef> + 'tcx, SubstsRef<'tcx>)> {
debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty);

for (base_t, _) in self.autoderef(span, base_t) {
for (base_t, _) in self.autoderef(span, base_ty) {
match base_t.kind() {
ty::Adt(base_def, substs) if !base_def.is_enum() => {
let tcx = self.tcx;
let fields = &base_def.non_enum_variant().fields;
// For compile-time reasons put a limit on number of fields we search
if fields.len() > 100 {
return None;
// Some struct, e.g. some that impl `Deref`, have all private fields
// because you're expected to deref them to access the _real_ fields.
// This, for example, will help us suggest accessing a field through a `Box<T>`.
if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) {
continue;
}
return Some((fields, substs));
return Some((
fields
.iter()
.filter(move |field| field.vis.is_accessible_from(mod_id, tcx))
// For compile-time reasons put a limit on number of fields we search
.take(100),
substs,
));
}
_ => {}
}
Expand All @@ -2599,7 +2613,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
candidate_field: &ty::FieldDef,
subst: SubstsRef<'tcx>,
mut field_path: Vec<Ident>,
id: DefId,
mod_id: DefId,
) -> Option<Vec<Ident>> {
debug!(
"check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}",
Expand All @@ -2611,24 +2625,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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() {
if field.vis.is_accessible_from(id, self.tcx) {
if matches(candidate_field, field_ty) {
return Some(field_path);
} else if let Some(field_path) = self.check_for_nested_field_satisfying(
span,
matches,
field,
subst,
field_path.clone(),
id,
) {
return Some(field_path);
}
if matches(candidate_field, field_ty) {
return Some(field_path);
} else if let Some((nested_fields, subst)) =
self.get_field_candidates_considering_privacy(span, field_ty, mod_id)
{
// recursively search fields of `candidate_field` if it's a ty::Adt
for field in nested_fields {
if let Some(field_path) = self.check_for_nested_field_satisfying(
span,
matches,
field,
subst,
field_path.clone(),
mod_id,
) {
return Some(field_path);
}
}
}
Expand Down
13 changes: 9 additions & 4 deletions compiler/rustc_typeck/src/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1000,7 +1000,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
label_span_not_found(&mut err);
}

self.check_for_field_method(&mut err, source, span, actual, item_name);
// Don't suggest (for example) `expr.field.method()` if `expr.method()`
// doesn't exist due to unsatisfied predicates.
if unsatisfied_predicates.is_empty() {
self.check_for_field_method(&mut err, source, span, actual, item_name);
}

self.check_for_unwrap_self(&mut err, source, span, actual, item_name);

Expand Down Expand Up @@ -1334,10 +1338,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
item_name: Ident,
) {
if let SelfSource::MethodCall(expr) = source
&& let Some((fields, substs)) = self.get_field_candidates(span, actual)
&& let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
&& let Some((fields, substs)) = self.get_field_candidates_considering_privacy(span, actual, mod_id)
{
let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
for candidate_field in fields.iter() {
for candidate_field in fields {
if let Some(field_path) = self.check_for_nested_field_satisfying(
span,
&|_, field_ty| {
Expand All @@ -1353,7 +1358,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
candidate_field,
substs,
vec![],
self.tcx.parent_module(expr.hir_id).to_def_id(),
mod_id,
) {
let field_path_str = field_path
.iter()
Expand Down
4 changes: 4 additions & 0 deletions src/test/ui/copy-a-resource.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ LL | let _y = x.clone();
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
help: one of the expressions' fields has a method of the same name
|
LL | let _y = x.i.clone();
| ++

error: aborting due to previous error

Expand Down
8 changes: 0 additions & 8 deletions src/test/ui/hrtb/issue-30786.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ 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:129:30: 129:37]>`, but its trait bounds were not satisfied
--> $DIR/issue-30786.rs:130:24
Expand All @@ -43,10 +39,6 @@ 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
4 changes: 4 additions & 0 deletions src/test/ui/issues/issue-2823.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ LL | let _d = c.clone();
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
help: one of the expressions' fields has a method of the same name
|
LL | let _d = c.x.clone();
| ++

error: aborting due to previous error

Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/noncopyable-class.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ LL | let _y = x.clone();
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
help: one of the expressions' fields has a method of the same name
|
LL | let _y = x.i.clone();
| ++
help: one of the expressions' fields has a method of the same name
|
LL | let _y = x.j.x.clone();
| ++++

error: aborting due to previous error

Expand Down
35 changes: 35 additions & 0 deletions src/test/ui/suggestions/field-access-considering-privacy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use a::TyCtxt;

mod a {
use std::ops::Deref;
pub struct TyCtxt<'tcx> {
gcx: &'tcx GlobalCtxt<'tcx>,
}

impl<'tcx> Deref for TyCtxt<'tcx> {
type Target = &'tcx GlobalCtxt<'tcx>;

fn deref(&self) -> &Self::Target {
&self.gcx
}
}

pub struct GlobalCtxt<'tcx> {
pub sess: &'tcx Session,
_t: &'tcx (),
}

pub struct Session {
pub opts: (),
}
}

mod b {
fn foo<'tcx>(tcx: crate::TyCtxt<'tcx>) {
tcx.opts;
//~^ ERROR no field `opts` on type `TyCtxt<'tcx>`
//~| HELP one of the expressions' fields has a field of the same name
}
}

fn main() {}
14 changes: 14 additions & 0 deletions src/test/ui/suggestions/field-access-considering-privacy.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0609]: no field `opts` on type `TyCtxt<'tcx>`
--> $DIR/field-access-considering-privacy.rs:29:13
|
LL | tcx.opts;
| ^^^^ unknown field
|
help: one of the expressions' fields has a field of the same name
|
LL | tcx.sess.opts;
| +++++

error: aborting due to previous error

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