Skip to content

Commit

Permalink
Rollup merge of rust-lang#100898 - compiler-errors:too-many-expr-fiel…
Browse files Browse the repository at this point in the history
…ds, 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 rust-lang#100894
  • Loading branch information
Dylan-DPC committed Aug 27, 2022
2 parents 6185c88 + c3f568b commit 11905c6
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 69 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ symbols! {
BTreeSet,
BinaryHeap,
Borrow,
BorrowMut,
Break,
C,
CStr,
Expand Down
51 changes: 29 additions & 22 deletions compiler/rustc_typeck/src/check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2605,32 +2605,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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![],
mod_id,
) {
// field_path includes `field` that we're looking for, so pop it.
let candidate_fields: Vec<_> = fields
.filter_map(|candidate_field| {
self.check_for_nested_field_satisfying(
span,
&|candidate_field, _| candidate_field.ident(self.tcx()) == field,
candidate_field,
substs,
vec![],
mod_id,
)
})
.map(|mut field_path| {
field_path.pop();

let field_path_str = field_path
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(
field.span.shrink_to_lo(),
"one of the expressions' fields has a field of the same name",
format!("{field_path_str}."),
Applicability::MaybeIncorrect,
);
}
.join(".")
})
.collect::<Vec<_>>();

let len = candidate_fields.len();
if len > 0 {
err.span_suggestions(
field.span.shrink_to_lo(),
format!(
"{} of the expressions' fields {} a field of the same name",
if len > 1 { "some" } else { "one" },
if len > 1 { "have" } else { "has" },
),
candidate_fields.iter().map(|path| format!("{path}.")),
Applicability::MaybeIncorrect,
);
}
}
err
Expand Down
88 changes: 57 additions & 31 deletions compiler/rustc_typeck/src/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1351,42 +1351,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
item_name: Ident,
) {
if let SelfSource::MethodCall(expr) = source
&& 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 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 {
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![],
mod_id,
) {
let field_path_str = field_path

let lang_items = self.tcx.lang_items();
let never_mention_traits = [
lang_items.clone_trait(),
lang_items.deref_trait(),
lang_items.deref_mut_trait(),
self.tcx.get_diagnostic_item(sym::AsRef),
self.tcx.get_diagnostic_item(sym::AsMut),
self.tcx.get_diagnostic_item(sym::Borrow),
self.tcx.get_diagnostic_item(sym::BorrowMut),
];
let candidate_fields: Vec<_> = fields
.filter_map(|candidate_field| {
self.check_for_nested_field_satisfying(
span,
&|_, field_ty| {
self.lookup_probe(
span,
item_name,
field_ty,
call_expr,
ProbeScope::TraitsInScope,
)
.map_or(false, |pick| {
!never_mention_traits
.iter()
.flatten()
.any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id)
})
},
candidate_field,
substs,
vec![],
mod_id,
)
})
.map(|field_path| {
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,
);
}
.join(".")
})
.collect();

let len = candidate_fields.len();
if len > 0 {
err.span_suggestions(
item_name.span.shrink_to_lo(),
format!(
"{} of the expressions' fields {} a method of the same name",
if len > 1 { "some" } else { "one" },
if len > 1 { "have" } else { "has" },
),
candidate_fields.iter().map(|path| format!("{path}.")),
Applicability::MaybeIncorrect,
);
}
}
}
Expand Down
4 changes: 0 additions & 4 deletions src/test/ui/copy-a-resource.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ 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
4 changes: 0 additions & 4 deletions src/test/ui/issues/issue-2823.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ 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: 0 additions & 8 deletions src/test/ui/noncopyable-class.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,6 @@ 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
29 changes: 29 additions & 0 deletions src/test/ui/suggestions/too-many-field-suggestions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
struct Thing {
a0: Foo,
a1: Foo,
a2: Foo,
a3: Foo,
a4: Foo,
a5: Foo,
a6: Foo,
a7: Foo,
a8: Foo,
a9: Foo,
}

struct Foo {
field: Field,
}

struct Field;

impl Foo {
fn bar(&self) {}
}

fn bar(t: Thing) {
t.bar(); //~ ERROR no method named `bar` found for struct `Thing`
t.field; //~ ERROR no field `field` on type `Thing`
}

fn main() {}
44 changes: 44 additions & 0 deletions src/test/ui/suggestions/too-many-field-suggestions.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
error[E0599]: no method named `bar` found for struct `Thing` in the current scope
--> $DIR/too-many-field-suggestions.rs:25:7
|
LL | struct Thing {
| ------------ method `bar` not found for this struct
...
LL | t.bar();
| ^^^ method not found in `Thing`
|
help: some of the expressions' fields have a method of the same name
|
LL | t.a0.bar();
| +++
LL | t.a1.bar();
| +++
LL | t.a2.bar();
| +++
LL | t.a3.bar();
| +++
and 6 other candidates

error[E0609]: no field `field` on type `Thing`
--> $DIR/too-many-field-suggestions.rs:26:7
|
LL | t.field;
| ^^^^^ unknown field
|
= note: available fields are: `a0`, `a1`, `a2`, `a3`, `a4` ... and 5 others
help: some of the expressions' fields have a field of the same name
|
LL | t.a0.field;
| +++
LL | t.a1.field;
| +++
LL | t.a2.field;
| +++
LL | t.a3.field;
| +++
and 6 other candidates

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0599, E0609.
For more information about an error, try `rustc --explain E0599`.

0 comments on commit 11905c6

Please sign in to comment.