Skip to content

Commit 867453e

Browse files
authored
Rollup merge of #100098 - compiler-errors:field-suggestion-fixups, r=davidtwco
Some "this expression has a field"-related fixes Each commit does something different and is worth reviewing, but the final diff from `master..HEAD` contains the sum of the changes to the UI tests, since some commits added UI tests "regressions" which were later removed in other commits. The only change I could see adding on top of this is suppressing `Clone::clone` from the "this expression has a field that has this method" suggestion, since it's so commonly implemented by types that it's not worthwhile suggesting in general.
2 parents 2059391 + 603ffeb commit 867453e

File tree

8 files changed

+117
-41
lines changed

8 files changed

+117
-41
lines changed

compiler/rustc_typeck/src/check/expr.rs

+43-29
Original file line numberDiff line numberDiff line change
@@ -2526,15 +2526,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
25262526
);
25272527

25282528
// try to add a suggestion in case the field is a nested field of a field of the Adt
2529-
if let Some((fields, substs)) = self.get_field_candidates(span, expr_t) {
2530-
for candidate_field in fields.iter() {
2529+
let mod_id = self.tcx.parent_module(id).to_def_id();
2530+
if let Some((fields, substs)) =
2531+
self.get_field_candidates_considering_privacy(span, expr_t, mod_id)
2532+
{
2533+
for candidate_field in fields {
25312534
if let Some(mut field_path) = self.check_for_nested_field_satisfying(
25322535
span,
25332536
&|candidate_field, _| candidate_field.ident(self.tcx()) == field,
25342537
candidate_field,
25352538
substs,
25362539
vec![],
2537-
self.tcx.parent_module(id).to_def_id(),
2540+
mod_id,
25382541
) {
25392542
// field_path includes `field` that we're looking for, so pop it.
25402543
field_path.pop();
@@ -2558,22 +2561,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
25582561
err
25592562
}
25602563

2561-
pub(crate) fn get_field_candidates(
2564+
pub(crate) fn get_field_candidates_considering_privacy(
25622565
&self,
25632566
span: Span,
2564-
base_t: Ty<'tcx>,
2565-
) -> Option<(&[ty::FieldDef], SubstsRef<'tcx>)> {
2566-
debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_t);
2567+
base_ty: Ty<'tcx>,
2568+
mod_id: DefId,
2569+
) -> Option<(impl Iterator<Item = &'tcx ty::FieldDef> + 'tcx, SubstsRef<'tcx>)> {
2570+
debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty);
25672571

2568-
for (base_t, _) in self.autoderef(span, base_t) {
2572+
for (base_t, _) in self.autoderef(span, base_ty) {
25692573
match base_t.kind() {
25702574
ty::Adt(base_def, substs) if !base_def.is_enum() => {
2575+
let tcx = self.tcx;
25712576
let fields = &base_def.non_enum_variant().fields;
2572-
// For compile-time reasons put a limit on number of fields we search
2573-
if fields.len() > 100 {
2574-
return None;
2577+
// Some struct, e.g. some that impl `Deref`, have all private fields
2578+
// because you're expected to deref them to access the _real_ fields.
2579+
// This, for example, will help us suggest accessing a field through a `Box<T>`.
2580+
if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) {
2581+
continue;
25752582
}
2576-
return Some((fields, substs));
2583+
return Some((
2584+
fields
2585+
.iter()
2586+
.filter(move |field| field.vis.is_accessible_from(mod_id, tcx))
2587+
// For compile-time reasons put a limit on number of fields we search
2588+
.take(100),
2589+
substs,
2590+
));
25772591
}
25782592
_ => {}
25792593
}
@@ -2590,7 +2604,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
25902604
candidate_field: &ty::FieldDef,
25912605
subst: SubstsRef<'tcx>,
25922606
mut field_path: Vec<Ident>,
2593-
id: DefId,
2607+
mod_id: DefId,
25942608
) -> Option<Vec<Ident>> {
25952609
debug!(
25962610
"check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}",
@@ -2602,24 +2616,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26022616
// up to a depth of three
26032617
None
26042618
} else {
2605-
// recursively search fields of `candidate_field` if it's a ty::Adt
26062619
field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0());
26072620
let field_ty = candidate_field.ty(self.tcx, subst);
2608-
if let Some((nested_fields, subst)) = self.get_field_candidates(span, field_ty) {
2609-
for field in nested_fields.iter() {
2610-
if field.vis.is_accessible_from(id, self.tcx) {
2611-
if matches(candidate_field, field_ty) {
2612-
return Some(field_path);
2613-
} else if let Some(field_path) = self.check_for_nested_field_satisfying(
2614-
span,
2615-
matches,
2616-
field,
2617-
subst,
2618-
field_path.clone(),
2619-
id,
2620-
) {
2621-
return Some(field_path);
2622-
}
2621+
if matches(candidate_field, field_ty) {
2622+
return Some(field_path);
2623+
} else if let Some((nested_fields, subst)) =
2624+
self.get_field_candidates_considering_privacy(span, field_ty, mod_id)
2625+
{
2626+
// recursively search fields of `candidate_field` if it's a ty::Adt
2627+
for field in nested_fields {
2628+
if let Some(field_path) = self.check_for_nested_field_satisfying(
2629+
span,
2630+
matches,
2631+
field,
2632+
subst,
2633+
field_path.clone(),
2634+
mod_id,
2635+
) {
2636+
return Some(field_path);
26232637
}
26242638
}
26252639
}

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

+9-4
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10001000
label_span_not_found(&mut err);
10011001
}
10021002

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

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

@@ -1334,10 +1338,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13341338
item_name: Ident,
13351339
) {
13361340
if let SelfSource::MethodCall(expr) = source
1337-
&& let Some((fields, substs)) = self.get_field_candidates(span, actual)
1341+
&& let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
1342+
&& let Some((fields, substs)) = self.get_field_candidates_considering_privacy(span, actual, mod_id)
13381343
{
13391344
let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
1340-
for candidate_field in fields.iter() {
1345+
for candidate_field in fields {
13411346
if let Some(field_path) = self.check_for_nested_field_satisfying(
13421347
span,
13431348
&|_, field_ty| {
@@ -1353,7 +1358,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13531358
candidate_field,
13541359
substs,
13551360
vec![],
1356-
self.tcx.parent_module(expr.hir_id).to_def_id(),
1361+
mod_id,
13571362
) {
13581363
let field_path_str = field_path
13591364
.iter()

src/test/ui/copy-a-resource.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ LL | let _y = x.clone();
1010
= help: items from traits can only be used if the trait is implemented and in scope
1111
= note: the following trait defines an item `clone`, perhaps you need to implement it:
1212
candidate #1: `Clone`
13+
help: one of the expressions' fields has a method of the same name
14+
|
15+
LL | let _y = x.i.clone();
16+
| ++
1317

1418
error: aborting due to previous error
1519

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

-8
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ 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-
| +++++++
2521

2622
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
2723
--> $DIR/issue-30786.rs:130:24
@@ -43,10 +39,6 @@ note: the following trait bounds were not satisfied:
4339
|
4440
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
4541
| --------- - ^^^^^^ 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-
| +++++++
5042

5143
error: aborting due to 2 previous errors
5244

src/test/ui/issues/issue-2823.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ LL | let _d = c.clone();
1010
= help: items from traits can only be used if the trait is implemented and in scope
1111
= note: the following trait defines an item `clone`, perhaps you need to implement it:
1212
candidate #1: `Clone`
13+
help: one of the expressions' fields has a method of the same name
14+
|
15+
LL | let _d = c.x.clone();
16+
| ++
1317

1418
error: aborting due to previous error
1519

src/test/ui/noncopyable-class.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ LL | let _y = x.clone();
1010
= help: items from traits can only be used if the trait is implemented and in scope
1111
= note: the following trait defines an item `clone`, perhaps you need to implement it:
1212
candidate #1: `Clone`
13+
help: one of the expressions' fields has a method of the same name
14+
|
15+
LL | let _y = x.i.clone();
16+
| ++
17+
help: one of the expressions' fields has a method of the same name
18+
|
19+
LL | let _y = x.j.x.clone();
20+
| ++++
1321

1422
error: aborting due to previous error
1523

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use a::TyCtxt;
2+
3+
mod a {
4+
use std::ops::Deref;
5+
pub struct TyCtxt<'tcx> {
6+
gcx: &'tcx GlobalCtxt<'tcx>,
7+
}
8+
9+
impl<'tcx> Deref for TyCtxt<'tcx> {
10+
type Target = &'tcx GlobalCtxt<'tcx>;
11+
12+
fn deref(&self) -> &Self::Target {
13+
&self.gcx
14+
}
15+
}
16+
17+
pub struct GlobalCtxt<'tcx> {
18+
pub sess: &'tcx Session,
19+
_t: &'tcx (),
20+
}
21+
22+
pub struct Session {
23+
pub opts: (),
24+
}
25+
}
26+
27+
mod b {
28+
fn foo<'tcx>(tcx: crate::TyCtxt<'tcx>) {
29+
tcx.opts;
30+
//~^ ERROR no field `opts` on type `TyCtxt<'tcx>`
31+
//~| HELP one of the expressions' fields has a field of the same name
32+
}
33+
}
34+
35+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0609]: no field `opts` on type `TyCtxt<'tcx>`
2+
--> $DIR/field-access-considering-privacy.rs:29:13
3+
|
4+
LL | tcx.opts;
5+
| ^^^^ unknown field
6+
|
7+
help: one of the expressions' fields has a field of the same name
8+
|
9+
LL | tcx.sess.opts;
10+
| +++++
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0609`.

0 commit comments

Comments
 (0)