diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2022-08-29 21:12:54 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-08-29 21:12:54 +0200 |
| commit | 091017c244af24adc3ef44a853189eebbee943cf (patch) | |
| tree | 04a9f7bf46d019fb2eaed83a5e9742321cf54906 | |
| parent | fcc2bddd262b26c3a4aeff2328e39b8e2b6d3254 (diff) | |
| parent | c3f568b3312bed99d0e4b95daecd3c9eca1ae895 (diff) | |
| download | rust-091017c244af24adc3ef44a853189eebbee943cf.tar.gz rust-091017c244af24adc3ef44a853189eebbee943cf.zip | |
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
| -rw-r--r-- | compiler/rustc_span/src/symbol.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/expr.rs | 51 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/method/suggest.rs | 88 | ||||
| -rw-r--r-- | src/test/ui/copy-a-resource.stderr | 4 | ||||
| -rw-r--r-- | src/test/ui/issues/issue-2823.stderr | 4 | ||||
| -rw-r--r-- | src/test/ui/noncopyable-class.stderr | 8 | ||||
| -rw-r--r-- | src/test/ui/suggestions/too-many-field-suggestions.rs | 29 | ||||
| -rw-r--r-- | src/test/ui/suggestions/too-many-field-suggestions.stderr | 44 |
8 files changed, 160 insertions, 69 deletions
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1b47d11f6f8..6eca7dc52b2 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -157,6 +157,7 @@ symbols! { BTreeSet, BinaryHeap, Borrow, + BorrowMut, Break, C, CStr, diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 71ae54bedce..33d74249e7b 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -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 diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 8bb8c7ac515..e532f392157 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -1350,42 +1350,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, + ); } } } diff --git a/src/test/ui/copy-a-resource.stderr b/src/test/ui/copy-a-resource.stderr index b92449c6e0a..128087f1e37 100644 --- a/src/test/ui/copy-a-resource.stderr +++ b/src/test/ui/copy-a-resource.stderr @@ -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 diff --git a/src/test/ui/issues/issue-2823.stderr b/src/test/ui/issues/issue-2823.stderr index 208b340d064..b5a2b2f55a6 100644 --- a/src/test/ui/issues/issue-2823.stderr +++ b/src/test/ui/issues/issue-2823.stderr @@ -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 diff --git a/src/test/ui/noncopyable-class.stderr b/src/test/ui/noncopyable-class.stderr index 15e22e946da..0c696163a26 100644 --- a/src/test/ui/noncopyable-class.stderr +++ b/src/test/ui/noncopyable-class.stderr @@ -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 diff --git a/src/test/ui/suggestions/too-many-field-suggestions.rs b/src/test/ui/suggestions/too-many-field-suggestions.rs new file mode 100644 index 00000000000..905f9502cf5 --- /dev/null +++ b/src/test/ui/suggestions/too-many-field-suggestions.rs @@ -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() {} diff --git a/src/test/ui/suggestions/too-many-field-suggestions.stderr b/src/test/ui/suggestions/too-many-field-suggestions.stderr new file mode 100644 index 00000000000..63ad6fdb169 --- /dev/null +++ b/src/test/ui/suggestions/too-many-field-suggestions.stderr @@ -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`. |
