Skip to content

Commit 091017c

Browse files
authored
Rollup merge of #100898 - compiler-errors:too-many-expr-fields, r=spastorino
Do not report too many expr field candidates When considering "this expressions' field has a {field/method}" suggestions: 1. Don't report methods that are out of scope 2. Use `span_suggestions` instead of reporting each field candidate, which caps the number of suggestions to 4 4. Blacklist some common traits like `Clone` and `Deref` Fixes #100894
2 parents fcc2bdd + c3f568b commit 091017c

File tree

8 files changed

+160
-69
lines changed

8 files changed

+160
-69
lines changed

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ symbols! {
157157
BTreeSet,
158158
BinaryHeap,
159159
Borrow,
160+
BorrowMut,
160161
Break,
161162
C,
162163
CStr,

compiler/rustc_typeck/src/check/expr.rs

+29-22
Original file line numberDiff line numberDiff line change
@@ -2605,32 +2605,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26052605
if let Some((fields, substs)) =
26062606
self.get_field_candidates_considering_privacy(span, expr_t, mod_id)
26072607
{
2608-
for candidate_field in fields {
2609-
if let Some(mut field_path) = self.check_for_nested_field_satisfying(
2610-
span,
2611-
&|candidate_field, _| candidate_field.ident(self.tcx()) == field,
2612-
candidate_field,
2613-
substs,
2614-
vec![],
2615-
mod_id,
2616-
) {
2617-
// field_path includes `field` that we're looking for, so pop it.
2608+
let candidate_fields: Vec<_> = fields
2609+
.filter_map(|candidate_field| {
2610+
self.check_for_nested_field_satisfying(
2611+
span,
2612+
&|candidate_field, _| candidate_field.ident(self.tcx()) == field,
2613+
candidate_field,
2614+
substs,
2615+
vec![],
2616+
mod_id,
2617+
)
2618+
})
2619+
.map(|mut field_path| {
26182620
field_path.pop();
2619-
2620-
let field_path_str = field_path
2621+
field_path
26212622
.iter()
26222623
.map(|id| id.name.to_ident_string())
26232624
.collect::<Vec<String>>()
2624-
.join(".");
2625-
debug!("field_path_str: {:?}", field_path_str);
2626-
2627-
err.span_suggestion_verbose(
2628-
field.span.shrink_to_lo(),
2629-
"one of the expressions' fields has a field of the same name",
2630-
format!("{field_path_str}."),
2631-
Applicability::MaybeIncorrect,
2632-
);
2633-
}
2625+
.join(".")
2626+
})
2627+
.collect::<Vec<_>>();
2628+
2629+
let len = candidate_fields.len();
2630+
if len > 0 {
2631+
err.span_suggestions(
2632+
field.span.shrink_to_lo(),
2633+
format!(
2634+
"{} of the expressions' fields {} a field of the same name",
2635+
if len > 1 { "some" } else { "one" },
2636+
if len > 1 { "have" } else { "has" },
2637+
),
2638+
candidate_fields.iter().map(|path| format!("{path}.")),
2639+
Applicability::MaybeIncorrect,
2640+
);
26342641
}
26352642
}
26362643
err

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

+57-31
Original file line numberDiff line numberDiff line change
@@ -1350,42 +1350,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13501350
item_name: Ident,
13511351
) {
13521352
if let SelfSource::MethodCall(expr) = source
1353-
&& let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
1354-
&& let Some((fields, substs)) = self.get_field_candidates_considering_privacy(span, actual, mod_id)
1353+
&& let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
1354+
&& let Some((fields, substs)) =
1355+
self.get_field_candidates_considering_privacy(span, actual, mod_id)
13551356
{
13561357
let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
1357-
for candidate_field in fields {
1358-
if let Some(field_path) = self.check_for_nested_field_satisfying(
1359-
span,
1360-
&|_, field_ty| {
1361-
self.lookup_probe(
1362-
span,
1363-
item_name,
1364-
field_ty,
1365-
call_expr,
1366-
ProbeScope::AllTraits,
1367-
)
1368-
.is_ok()
1369-
},
1370-
candidate_field,
1371-
substs,
1372-
vec![],
1373-
mod_id,
1374-
) {
1375-
let field_path_str = field_path
1358+
1359+
let lang_items = self.tcx.lang_items();
1360+
let never_mention_traits = [
1361+
lang_items.clone_trait(),
1362+
lang_items.deref_trait(),
1363+
lang_items.deref_mut_trait(),
1364+
self.tcx.get_diagnostic_item(sym::AsRef),
1365+
self.tcx.get_diagnostic_item(sym::AsMut),
1366+
self.tcx.get_diagnostic_item(sym::Borrow),
1367+
self.tcx.get_diagnostic_item(sym::BorrowMut),
1368+
];
1369+
let candidate_fields: Vec<_> = fields
1370+
.filter_map(|candidate_field| {
1371+
self.check_for_nested_field_satisfying(
1372+
span,
1373+
&|_, field_ty| {
1374+
self.lookup_probe(
1375+
span,
1376+
item_name,
1377+
field_ty,
1378+
call_expr,
1379+
ProbeScope::TraitsInScope,
1380+
)
1381+
.map_or(false, |pick| {
1382+
!never_mention_traits
1383+
.iter()
1384+
.flatten()
1385+
.any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id)
1386+
})
1387+
},
1388+
candidate_field,
1389+
substs,
1390+
vec![],
1391+
mod_id,
1392+
)
1393+
})
1394+
.map(|field_path| {
1395+
field_path
13761396
.iter()
13771397
.map(|id| id.name.to_ident_string())
13781398
.collect::<Vec<String>>()
1379-
.join(".");
1380-
debug!("field_path_str: {:?}", field_path_str);
1381-
1382-
err.span_suggestion_verbose(
1383-
item_name.span.shrink_to_lo(),
1384-
"one of the expressions' fields has a method of the same name",
1385-
format!("{field_path_str}."),
1386-
Applicability::MaybeIncorrect,
1387-
);
1388-
}
1399+
.join(".")
1400+
})
1401+
.collect();
1402+
1403+
let len = candidate_fields.len();
1404+
if len > 0 {
1405+
err.span_suggestions(
1406+
item_name.span.shrink_to_lo(),
1407+
format!(
1408+
"{} of the expressions' fields {} a method of the same name",
1409+
if len > 1 { "some" } else { "one" },
1410+
if len > 1 { "have" } else { "has" },
1411+
),
1412+
candidate_fields.iter().map(|path| format!("{path}.")),
1413+
Applicability::MaybeIncorrect,
1414+
);
13891415
}
13901416
}
13911417
}

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

-4
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ 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-
| ++
1713

1814
error: aborting due to previous error
1915

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

-4
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ 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-
| ++
1713

1814
error: aborting due to previous error
1915

src/test/ui/noncopyable-class.stderr

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

2214
error: aborting due to previous error
2315

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
struct Thing {
2+
a0: Foo,
3+
a1: Foo,
4+
a2: Foo,
5+
a3: Foo,
6+
a4: Foo,
7+
a5: Foo,
8+
a6: Foo,
9+
a7: Foo,
10+
a8: Foo,
11+
a9: Foo,
12+
}
13+
14+
struct Foo {
15+
field: Field,
16+
}
17+
18+
struct Field;
19+
20+
impl Foo {
21+
fn bar(&self) {}
22+
}
23+
24+
fn bar(t: Thing) {
25+
t.bar(); //~ ERROR no method named `bar` found for struct `Thing`
26+
t.field; //~ ERROR no field `field` on type `Thing`
27+
}
28+
29+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
error[E0599]: no method named `bar` found for struct `Thing` in the current scope
2+
--> $DIR/too-many-field-suggestions.rs:25:7
3+
|
4+
LL | struct Thing {
5+
| ------------ method `bar` not found for this struct
6+
...
7+
LL | t.bar();
8+
| ^^^ method not found in `Thing`
9+
|
10+
help: some of the expressions' fields have a method of the same name
11+
|
12+
LL | t.a0.bar();
13+
| +++
14+
LL | t.a1.bar();
15+
| +++
16+
LL | t.a2.bar();
17+
| +++
18+
LL | t.a3.bar();
19+
| +++
20+
and 6 other candidates
21+
22+
error[E0609]: no field `field` on type `Thing`
23+
--> $DIR/too-many-field-suggestions.rs:26:7
24+
|
25+
LL | t.field;
26+
| ^^^^^ unknown field
27+
|
28+
= note: available fields are: `a0`, `a1`, `a2`, `a3`, `a4` ... and 5 others
29+
help: some of the expressions' fields have a field of the same name
30+
|
31+
LL | t.a0.field;
32+
| +++
33+
LL | t.a1.field;
34+
| +++
35+
LL | t.a2.field;
36+
| +++
37+
LL | t.a3.field;
38+
| +++
39+
and 6 other candidates
40+
41+
error: aborting due to 2 previous errors
42+
43+
Some errors have detailed explanations: E0599, E0609.
44+
For more information about an error, try `rustc --explain E0599`.

0 commit comments

Comments
 (0)