From e5b3c7ef1439fa946b3ba49a3bbbadc34d801a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 7 Feb 2024 02:35:49 +0000 Subject: [PATCH 01/15] Add `rustc_confusables` annotations to some stdlib APIs Help with common API confusion, like asking for `push` when the data structure really has `append`. ``` error[E0599]: no method named `size` found for struct `Vec<{integer}>` in the current scope --> $DIR/rustc_confusables_std_cases.rs:17:7 | LL | x.size(); | ^^^^ | help: you might have meant to use `len` | LL | x.len(); | ~~~ help: there is a method with a similar name | LL | x.resize(); | ~~~~~~ ``` #59450 --- .../rustc_hir_typeck/src/method/suggest.rs | 59 ++++++++------- .../alloc/src/collections/binary_heap/mod.rs | 2 + library/alloc/src/collections/btree/map.rs | 3 + .../alloc/src/collections/btree/map/entry.rs | 3 + library/alloc/src/collections/btree/set.rs | 5 ++ library/alloc/src/collections/linked_list.rs | 10 +++ .../alloc/src/collections/vec_deque/mod.rs | 5 ++ library/alloc/src/string.rs | 3 + library/alloc/src/vec/mod.rs | 3 + library/core/src/cell.rs | 2 + library/proc_macro/src/bridge/scoped_cell.rs | 1 + library/std/src/collections/hash/map.rs | 2 + library/std/src/collections/hash/set.rs | 3 + library/std/src/ffi/os_str.rs | 1 + library/std/src/path.rs | 1 + library/std/src/thread/local.rs | 2 + tests/ui/attributes/rustc_confusables.rs | 2 +- tests/ui/attributes/rustc_confusables.stderr | 7 +- .../attributes/rustc_confusables_std_cases.rs | 21 ++++++ .../rustc_confusables_std_cases.stderr | 74 +++++++++++++++++++ 20 files changed, 180 insertions(+), 29 deletions(-) create mode 100644 tests/ui/attributes/rustc_confusables_std_cases.rs create mode 100644 tests/ui/attributes/rustc_confusables_std_cases.stderr diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index cc111af5d8f..b7ca5413243 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1209,32 +1209,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.note(format!( "the {item_kind} was found for\n{type_candidates}{additional_types}" )); - } else { - 'outer: for inherent_impl_did in - self.tcx.inherent_impls(adt.did()).into_iter().flatten() - { - for inherent_method in - self.tcx.associated_items(inherent_impl_did).in_definition_order() - { - if let Some(attr) = self - .tcx - .get_attr(inherent_method.def_id, sym::rustc_confusables) - && let Some(candidates) = parse_confusables(attr) - && candidates.contains(&item_name.name) - { - err.span_suggestion_verbose( - item_name.span, - format!( - "you might have meant to use `{}`", - inherent_method.name - ), - inherent_method.name, - Applicability::MaybeIncorrect, - ); - break 'outer; - } - } - } } } } else { @@ -1260,6 +1234,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { label_span_not_found(&mut err); } + let mut confusable_suggested = None; + if let ty::Adt(adt, _) = rcvr_ty.kind() { + 'outer: for inherent_impl_did in + self.tcx.inherent_impls(adt.did()).into_iter().flatten() + { + for inherent_method in + self.tcx.associated_items(inherent_impl_did).in_definition_order() + { + if let Some(attr) = + self.tcx.get_attr(inherent_method.def_id, sym::rustc_confusables) + && let Some(candidates) = parse_confusables(attr) + && candidates.contains(&item_name.name) + { + { + err.span_suggestion_verbose( + item_name.span, + format!("you might have meant to use `{}`", inherent_method.name), + inherent_method.name, + Applicability::MaybeIncorrect, + ); + confusable_suggested = Some(inherent_method.name); + break 'outer; + } + } + } + } + } + // Don't suggest (for example) `expr.field.clone()` if `expr.clone()` // can't be called due to `typeof(expr): Clone` not holding. if unsatisfied_predicates.is_empty() { @@ -1361,7 +1363,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if let Some(similar_candidate) = similar_candidate { // Don't emit a suggestion if we found an actual method // that had unsatisfied trait bounds - if unsatisfied_predicates.is_empty() { + if unsatisfied_predicates.is_empty() + // ...or if we already suggested that name because of `rustc_confusable` annotation. + && Some(similar_candidate.name) != confusable_suggested + { let def_kind = similar_candidate.kind.as_def_kind(); // Methods are defined within the context of a struct and their first parameter is always self, // which represents the instance of the struct the method is being called on diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index c89a3806280..83b2678f7f5 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -607,6 +607,7 @@ impl BinaryHeap { /// occurs when capacity is exhausted and needs a resize. The resize cost /// has been amortized in the previous figures. #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("append", "put")] pub fn push(&mut self, item: T) { let old_len = self.len(); self.data.push(item); @@ -1264,6 +1265,7 @@ impl BinaryHeap { /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("length", "size")] pub fn len(&self) -> usize { self.data.len() } diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index addcc71a2d4..80be4ee57c5 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -979,6 +979,7 @@ impl BTreeMap { /// assert_eq!(map[&37], "c"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("push", "put", "set")] pub fn insert(&mut self, key: K, value: V) -> Option where K: Ord, @@ -1041,6 +1042,7 @@ impl BTreeMap { /// assert_eq!(map.remove(&1), None); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("delete", "take")] pub fn remove(&mut self, key: &Q) -> Option where K: Borrow + Ord, @@ -2495,6 +2497,7 @@ impl BTreeMap { issue = "71835", implied_by = "const_btree_new" )] + #[rustc_confusables("length", "size")] pub const fn len(&self) -> usize { self.length } diff --git a/library/alloc/src/collections/btree/map/entry.rs b/library/alloc/src/collections/btree/map/entry.rs index e9366eec9ce..0a894258f46 100644 --- a/library/alloc/src/collections/btree/map/entry.rs +++ b/library/alloc/src/collections/btree/map/entry.rs @@ -347,6 +347,7 @@ impl<'a, K: Ord, V, A: Allocator + Clone> VacantEntry<'a, K, V, A> { /// assert_eq!(map["poneyland"], 37); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("push", "put")] pub fn insert(mut self, value: V) -> &'a mut V { let out_ptr = match self.handle { None => { @@ -524,6 +525,7 @@ impl<'a, K: Ord, V, A: Allocator + Clone> OccupiedEntry<'a, K, V, A> { /// assert_eq!(map["poneyland"], 15); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("push", "put")] pub fn insert(&mut self, value: V) -> V { mem::replace(self.get_mut(), value) } @@ -546,6 +548,7 @@ impl<'a, K: Ord, V, A: Allocator + Clone> OccupiedEntry<'a, K, V, A> { /// // println!("{}", map["poneyland"]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("delete", "take")] pub fn remove(self) -> V { self.remove_kv().1 } diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 0e03551286e..ed91ae1a66e 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -790,6 +790,7 @@ impl BTreeSet { /// ``` #[must_use] #[stable(feature = "map_first_last", since = "1.66.0")] + #[rustc_confusables("front")] pub fn first(&self) -> Option<&T> where T: Ord, @@ -816,6 +817,7 @@ impl BTreeSet { /// ``` #[must_use] #[stable(feature = "map_first_last", since = "1.66.0")] + #[rustc_confusables("back")] pub fn last(&self) -> Option<&T> where T: Ord, @@ -896,6 +898,7 @@ impl BTreeSet { /// assert_eq!(set.len(), 1); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("push", "put")] pub fn insert(&mut self, value: T) -> bool where T: Ord, @@ -919,6 +922,7 @@ impl BTreeSet { /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 10); /// ``` #[stable(feature = "set_recovery", since = "1.9.0")] + #[rustc_confusables("swap")] pub fn replace(&mut self, value: T) -> Option where T: Ord, @@ -1152,6 +1156,7 @@ impl BTreeSet { issue = "71835", implied_by = "const_btree_new" )] + #[rustc_confusables("length", "size")] pub const fn len(&self) -> usize { self.map.len() } diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 9e109feb3d3..6dfb82ac807 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -656,6 +656,7 @@ impl LinkedList { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("length", "size")] pub fn len(&self) -> usize { self.len } @@ -740,6 +741,7 @@ impl LinkedList { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("first")] pub fn front(&self) -> Option<&T> { unsafe { self.head.as_ref().map(|node| &node.as_ref().element) } } @@ -890,6 +892,7 @@ impl LinkedList { /// assert_eq!(3, *d.back().unwrap()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("push", "append")] pub fn push_back(&mut self, elt: T) { let node = Box::new_in(Node::new(elt), &self.alloc); let node_ptr = NonNull::from(Box::leak(node)); @@ -1004,6 +1007,7 @@ impl LinkedList { /// assert_eq!(d.remove(0), 1); /// ``` #[unstable(feature = "linked_list_remove", issue = "69210")] + #[rustc_confusables("delete", "take")] pub fn remove(&mut self, at: usize) -> T { let len = self.len(); assert!(at < len, "Cannot remove at an index outside of the list bounds"); @@ -1478,6 +1482,7 @@ impl<'a, T, A: Allocator> Cursor<'a, T, A> { /// or None if the list is empty. #[must_use] #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("first")] pub fn front(&self) -> Option<&'a T> { self.list.front() } @@ -1486,6 +1491,7 @@ impl<'a, T, A: Allocator> Cursor<'a, T, A> { /// or None if the list is empty. #[must_use] #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("last")] pub fn back(&self) -> Option<&'a T> { self.list.back() } @@ -1788,6 +1794,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { /// /// This operation should compute in *O*(1) time. #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("push", "append")] pub fn push_back(&mut self, elt: T) { // Safety: We know that `push_back` does not change the position in // memory of other nodes. This ensures that `self.current` remains @@ -1834,6 +1841,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { /// /// This operation should compute in *O*(1) time. #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("pop")] pub fn pop_back(&mut self) -> Option { if self.list.is_empty() { None @@ -1854,6 +1862,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { /// or None if the list is empty. #[must_use] #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("first")] pub fn front(&self) -> Option<&T> { self.list.front() } @@ -1870,6 +1879,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { /// or None if the list is empty. #[must_use] #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("last")] pub fn back(&self) -> Option<&T> { self.list.back() } diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index bcba6525fda..c35bab5ef66 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1209,6 +1209,7 @@ impl VecDeque { /// assert_eq!(deque.len(), 1); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("length", "size")] pub fn len(&self) -> usize { self.len } @@ -1491,6 +1492,7 @@ impl VecDeque { /// assert_eq!(d.front(), Some(&1)); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("first")] pub fn front(&self) -> Option<&T> { self.get(0) } @@ -1535,6 +1537,7 @@ impl VecDeque { /// assert_eq!(d.back(), Some(&2)); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("last")] pub fn back(&self) -> Option<&T> { self.get(self.len.wrapping_sub(1)) } @@ -1654,6 +1657,7 @@ impl VecDeque { /// assert_eq!(3, *buf.back().unwrap()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("push", "put", "append")] pub fn push_back(&mut self, value: T) { if self.is_full() { self.grow(); @@ -1813,6 +1817,7 @@ impl VecDeque { /// assert_eq!(buf, [1, 3]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("delete", "take")] pub fn remove(&mut self, index: usize) -> Option { if self.len <= index { return None; diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 4d6968157de..6ad783bee27 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1445,6 +1445,7 @@ impl String { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("delete", "take")] pub fn remove(&mut self, idx: usize) -> char { let ch = match self[idx..].chars().next() { Some(ch) => ch, @@ -1639,6 +1640,7 @@ impl String { #[cfg(not(no_global_oom_handling))] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("set")] pub fn insert(&mut self, idx: usize, ch: char) { assert!(self.is_char_boundary(idx)); let mut bits = [0; 4]; @@ -1738,6 +1740,7 @@ impl String { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("length", "size")] pub fn len(&self) -> usize { self.vec.len() } diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 08e3cdedc66..b59699219eb 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1554,6 +1554,7 @@ impl Vec { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[track_caller] + #[rustc_confusables("delete", "take")] pub fn remove(&mut self, index: usize) -> T { #[cold] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] @@ -1915,6 +1916,7 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("push_back", "put", "append")] pub fn push(&mut self, value: T) { // This will panic or abort if we would allocate > isize::MAX bytes // or if the length increment would overflow for zero-sized types. @@ -2141,6 +2143,7 @@ impl Vec { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("length", "size")] pub fn len(&self) -> usize { self.len } diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index c77fa371cc7..19b05448c87 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -471,6 +471,7 @@ impl Cell { /// ``` #[inline] #[stable(feature = "move_cell", since = "1.17.0")] + #[rustc_confusables("swap")] pub fn replace(&self, val: T) -> T { // SAFETY: This can cause data races if called from a separate thread, // but `Cell` is `!Sync` so this won't happen. @@ -862,6 +863,7 @@ impl RefCell { #[inline] #[stable(feature = "refcell_replace", since = "1.24.0")] #[track_caller] + #[rustc_confusables("swap")] pub fn replace(&self, t: T) -> T { mem::replace(&mut *self.borrow_mut(), t) } diff --git a/library/proc_macro/src/bridge/scoped_cell.rs b/library/proc_macro/src/bridge/scoped_cell.rs index 53eae1ebdb0..a8b34143908 100644 --- a/library/proc_macro/src/bridge/scoped_cell.rs +++ b/library/proc_macro/src/bridge/scoped_cell.rs @@ -25,6 +25,7 @@ impl ScopedCell { /// running `f`, which gets the old value, mutably. /// The old value will be restored after `f` exits, even /// by panic, including modifications made to it by `f`. + #[rustc_confusables("swap")] pub fn replace<'a, R>( &self, replacement: >::Out, diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 0d4c1fa05cc..627befb63a1 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -1101,6 +1101,7 @@ where /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("push", "append")] pub fn insert(&mut self, k: K, v: V) -> Option { self.base.insert(k, v) } @@ -1155,6 +1156,7 @@ where /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("delete", "take")] pub fn remove(&mut self, k: &Q) -> Option where K: Borrow, diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index dcb2fa0f771..371201ff44c 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -885,6 +885,7 @@ where /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("push", "append")] pub fn insert(&mut self, value: T) -> bool { self.base.insert(value) } @@ -906,6 +907,7 @@ where /// ``` #[inline] #[stable(feature = "set_recovery", since = "1.9.0")] + #[rustc_confusables("swap")] pub fn replace(&mut self, value: T) -> Option { self.base.replace(value) } @@ -930,6 +932,7 @@ where /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("delete", "take")] pub fn remove(&mut self, value: &Q) -> bool where T: Borrow, diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 28747ad8f34..bbcbbc52843 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -255,6 +255,7 @@ impl OsString { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[rustc_confusables("append", "put")] pub fn push>(&mut self, s: T) { self.inner.push_slice(&s.as_ref().inner) } diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 60562f64c90..89fbd5c4c64 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1270,6 +1270,7 @@ impl PathBuf { /// assert_eq!(path, PathBuf::from("/etc")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("append", "put")] pub fn push>(&mut self, path: P) { self._push(path.as_ref()) } diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index d1213e2f166..34d30a259ee 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -435,6 +435,7 @@ impl LocalKey> { /// assert_eq!(X.replace(3), 2); /// ``` #[stable(feature = "local_key_cell_methods", since = "1.73.0")] + #[rustc_confusables("swap")] pub fn replace(&'static self, value: T) -> T { self.with(|cell| cell.replace(value)) } @@ -606,6 +607,7 @@ impl LocalKey> { /// X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3])); /// ``` #[stable(feature = "local_key_cell_methods", since = "1.73.0")] + #[rustc_confusables("swap")] pub fn replace(&'static self, value: T) -> T { self.with(|cell| cell.replace(value)) } diff --git a/tests/ui/attributes/rustc_confusables.rs b/tests/ui/attributes/rustc_confusables.rs index a88432ead75..fed6c49ce6c 100644 --- a/tests/ui/attributes/rustc_confusables.rs +++ b/tests/ui/attributes/rustc_confusables.rs @@ -21,7 +21,7 @@ fn main() { //~^ ERROR no method named x.pulled(); //~^ ERROR no method named - //~| HELP there is a method with a similar name + //~| HELP you might have meant to use `pull` } struct Bar; diff --git a/tests/ui/attributes/rustc_confusables.stderr b/tests/ui/attributes/rustc_confusables.stderr index 9fd4470cdbb..93c31fd5a34 100644 --- a/tests/ui/attributes/rustc_confusables.stderr +++ b/tests/ui/attributes/rustc_confusables.stderr @@ -60,7 +60,12 @@ error[E0599]: no method named `pulled` found for struct `rustc_confusables_acros --> $DIR/rustc_confusables.rs:22:7 | LL | x.pulled(); - | ^^^^^^ help: there is a method with a similar name: `pull` + | ^^^^^^ + | +help: you might have meant to use `pull` + | +LL | x.pull(); + | ~~~~ error: aborting due to 9 previous errors diff --git a/tests/ui/attributes/rustc_confusables_std_cases.rs b/tests/ui/attributes/rustc_confusables_std_cases.rs new file mode 100644 index 00000000000..f408a09a81f --- /dev/null +++ b/tests/ui/attributes/rustc_confusables_std_cases.rs @@ -0,0 +1,21 @@ +use std::collections::BTreeSet; +use std::collections::VecDeque; + +fn main() { + let mut x = BTreeSet::new(); + x.push(1); //~ ERROR E0599 + //~^ HELP you might have meant to use `insert` + let mut x = Vec::new(); + x.push_back(1); //~ ERROR E0599 + //~^ HELP you might have meant to use `push` + let mut x = VecDeque::new(); + x.push(1); //~ ERROR E0599 + //~^ HELP you might have meant to use `push_back` + let mut x = vec![1, 2, 3]; + x.length(); //~ ERROR E0599 + //~^ HELP you might have meant to use `len` + x.size(); //~ ERROR E0599 + //~^ HELP you might have meant to use `len` + //~| HELP there is a method with a similar name + String::new().push(""); //~ ERROR E0308 +} diff --git a/tests/ui/attributes/rustc_confusables_std_cases.stderr b/tests/ui/attributes/rustc_confusables_std_cases.stderr new file mode 100644 index 00000000000..ce3bf3921dc --- /dev/null +++ b/tests/ui/attributes/rustc_confusables_std_cases.stderr @@ -0,0 +1,74 @@ +error[E0599]: no method named `push` found for struct `BTreeSet` in the current scope + --> $DIR/rustc_confusables_std_cases.rs:6:7 + | +LL | x.push(1); + | ^^^^ method not found in `BTreeSet<_>` + | +help: you might have meant to use `insert` + | +LL | x.insert(1); + | ~~~~~~ + +error[E0599]: no method named `push_back` found for struct `Vec<_>` in the current scope + --> $DIR/rustc_confusables_std_cases.rs:9:7 + | +LL | x.push_back(1); + | ^^^^^^^^^ method not found in `Vec<_>` + | +help: you might have meant to use `push` + | +LL | x.push(1); + | ~~~~ + +error[E0599]: no method named `push` found for struct `VecDeque` in the current scope + --> $DIR/rustc_confusables_std_cases.rs:12:7 + | +LL | x.push(1); + | ^^^^ method not found in `VecDeque<_>` + | +help: you might have meant to use `push_back` + | +LL | x.push_back(1); + | ~~~~~~~~~ + +error[E0599]: no method named `length` found for struct `Vec<{integer}>` in the current scope + --> $DIR/rustc_confusables_std_cases.rs:15:7 + | +LL | x.length(); + | ^^^^^^ + | +help: you might have meant to use `len` + | +LL | x.len(); + | ~~~ + +error[E0599]: no method named `size` found for struct `Vec<{integer}>` in the current scope + --> $DIR/rustc_confusables_std_cases.rs:17:7 + | +LL | x.size(); + | ^^^^ + | +help: you might have meant to use `len` + | +LL | x.len(); + | ~~~ +help: there is a method with a similar name + | +LL | x.resize(); + | ~~~~~~ + +error[E0308]: mismatched types + --> $DIR/rustc_confusables_std_cases.rs:20:24 + | +LL | String::new().push(""); + | ---- ^^ expected `char`, found `&str` + | | + | arguments to this method are incorrect + | +note: method defined here + --> $SRC_DIR/alloc/src/string.rs:LL:COL + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0308, E0599. +For more information about an error, try `rustc --explain E0308`. From 0e894656729662450b1cf8f879f901fd6c2d3f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 7 Feb 2024 05:13:06 +0000 Subject: [PATCH 02/15] On type error of method call arguments, look at confusables for suggestion --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 46 ++++++++++-- .../rustc_hir_typeck/src/method/suggest.rs | 74 ++++++++++++------- library/alloc/src/string.rs | 1 + .../attributes/rustc_confusables_std_cases.rs | 3 + .../rustc_confusables_std_cases.stderr | 17 ++++- 5 files changed, 106 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 2d9ec9f6bab..15dc0ba444b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -451,7 +451,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &'tcx hir::Expr<'tcx>, ) -> ErrorGuaranteed { // Next, let's construct the error - let (error_span, full_call_span, call_name, is_method) = match &call_expr.kind { + let (error_span, call_ident, full_call_span, call_name, is_method) = match &call_expr.kind { hir::ExprKind::Call( hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. }, _, @@ -463,12 +463,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { CtorOf::Struct => "struct", CtorOf::Variant => "enum variant", }; - (call_span, *span, name, false) + (call_span, None, *span, name, false) } else { - (call_span, *span, "function", false) + (call_span, None, *span, "function", false) } } - hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, "function", false), + hir::ExprKind::Call(hir::Expr { span, .. }, _) => { + (call_span, None, *span, "function", false) + } hir::ExprKind::MethodCall(path_segment, _, _, span) => { let ident_span = path_segment.ident.span; let ident_span = if let Some(args) = path_segment.args { @@ -476,7 +478,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { ident_span }; - (*span, ident_span, "method", true) + (*span, Some(path_segment.ident), ident_span, "method", true) } k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), }; @@ -530,6 +532,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let callee_ty = callee_expr .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr)); + let suggest_confusable = |err: &mut Diagnostic| { + if let Some(call_name) = call_ident + && let Some(callee_ty) = callee_ty + { + // FIXME: check in the following order + // - methods marked as `rustc_confusables` with the provided arguments (done) + // - methods marked as `rustc_confusables` with the right number of arguments + // - methods marked as `rustc_confusables` (done) + // - methods with the same argument type/count and short levenshtein distance + // - methods with short levenshtein distance + // - methods with the same argument type/count + self.confusable_method_name( + err, + callee_ty.peel_refs(), + call_name, + Some(provided_arg_tys.iter().map(|(ty, _)| *ty).collect()), + ) + .or_else(|| { + self.confusable_method_name(err, callee_ty.peel_refs(), call_name, None) + }); + } + }; // A "softer" version of the `demand_compatible`, which checks types without persisting them, // and treats error types differently // This will allow us to "probe" for other argument orders that would likely have been correct @@ -694,6 +718,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(mismatch_idx), is_method, ); + suggest_confusable(&mut err); return err.emit(); } } @@ -718,7 +743,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if cfg!(debug_assertions) { span_bug!(error_span, "expected errors from argument matrix"); } else { - return tcx.dcx().emit_err(errors::ArgMismatchIndeterminate { span: error_span }); + let mut err = + tcx.dcx().create_err(errors::ArgMismatchIndeterminate { span: error_span }); + suggest_confusable(&mut err); + return err.emit(); } } @@ -733,7 +761,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let trace = mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty); if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) { - reported = Some(self.err_ctxt().report_and_explain_type_error(trace, *e).emit()); + let mut err = self.err_ctxt().report_and_explain_type_error(trace, *e); + suggest_confusable(&mut err); + reported = Some(err.emit()); return false; } true @@ -802,6 +832,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(expected_idx.as_usize()), is_method, ); + suggest_confusable(&mut err); return err.emit(); } @@ -829,6 +860,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .with_code(err_code.to_owned()) }; + suggest_confusable(&mut err); // As we encounter issues, keep track of what we want to provide for the suggestion let mut labels = vec![]; // If there is a single error, we give a specific suggestion; otherwise, we change to diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index b7ca5413243..6226f2ee796 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1234,33 +1234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { label_span_not_found(&mut err); } - let mut confusable_suggested = None; - if let ty::Adt(adt, _) = rcvr_ty.kind() { - 'outer: for inherent_impl_did in - self.tcx.inherent_impls(adt.did()).into_iter().flatten() - { - for inherent_method in - self.tcx.associated_items(inherent_impl_did).in_definition_order() - { - if let Some(attr) = - self.tcx.get_attr(inherent_method.def_id, sym::rustc_confusables) - && let Some(candidates) = parse_confusables(attr) - && candidates.contains(&item_name.name) - { - { - err.span_suggestion_verbose( - item_name.span, - format!("you might have meant to use `{}`", inherent_method.name), - inherent_method.name, - Applicability::MaybeIncorrect, - ); - confusable_suggested = Some(inherent_method.name); - break 'outer; - } - } - } - } - } + let confusable_suggested = self.confusable_method_name(&mut err, rcvr_ty, item_name, None); // Don't suggest (for example) `expr.field.clone()` if `expr.clone()` // can't be called due to `typeof(expr): Clone` not holding. @@ -1442,6 +1416,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(err) } + pub(crate) fn confusable_method_name( + &self, + err: &mut Diagnostic, + rcvr_ty: Ty<'tcx>, + item_name: Ident, + args: Option>>, + ) -> Option { + if let ty::Adt(adt, _) = rcvr_ty.kind() { + for inherent_impl_did in self.tcx.inherent_impls(adt.did()).into_iter().flatten() { + for inherent_method in + self.tcx.associated_items(inherent_impl_did).in_definition_order() + { + if let Some(attr) = + self.tcx.get_attr(inherent_method.def_id, sym::rustc_confusables) + && let Some(candidates) = parse_confusables(attr) + && candidates.contains(&item_name.name) + { + let mut matches_args = args.is_none(); + if let ty::AssocKind::Fn = inherent_method.kind + && let Some(ref args) = args + { + let fn_sig = + self.tcx.fn_sig(inherent_method.def_id).instantiate_identity(); + matches_args = fn_sig + .inputs() + .skip_binder() + .iter() + .skip(1) + .zip(args.into_iter()) + .all(|(expected, found)| self.can_coerce(*expected, *found)); + } + if matches_args { + err.span_suggestion_verbose( + item_name.span, + format!("you might have meant to use `{}`", inherent_method.name), + inherent_method.name, + Applicability::MaybeIncorrect, + ); + return Some(inherent_method.name); + } + } + } + } + } + None + } fn note_candidates_on_method_error( &self, rcvr_ty: Ty<'tcx>, diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 6ad783bee27..6dadbc8e364 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1049,6 +1049,7 @@ impl String { #[cfg(not(no_global_oom_handling))] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("append", "push")] pub fn push_str(&mut self, string: &str) { self.vec.extend_from_slice(string.as_bytes()) } diff --git a/tests/ui/attributes/rustc_confusables_std_cases.rs b/tests/ui/attributes/rustc_confusables_std_cases.rs index f408a09a81f..eda35e4b633 100644 --- a/tests/ui/attributes/rustc_confusables_std_cases.rs +++ b/tests/ui/attributes/rustc_confusables_std_cases.rs @@ -18,4 +18,7 @@ fn main() { //~^ HELP you might have meant to use `len` //~| HELP there is a method with a similar name String::new().push(""); //~ ERROR E0308 + //~^ HELP you might have meant to use `push_str` + String::new().append(""); //~ ERROR E0599 + //~^ HELP you might have meant to use `push_str` } diff --git a/tests/ui/attributes/rustc_confusables_std_cases.stderr b/tests/ui/attributes/rustc_confusables_std_cases.stderr index ce3bf3921dc..f69b79b4028 100644 --- a/tests/ui/attributes/rustc_confusables_std_cases.stderr +++ b/tests/ui/attributes/rustc_confusables_std_cases.stderr @@ -67,8 +67,23 @@ LL | String::new().push(""); | note: method defined here --> $SRC_DIR/alloc/src/string.rs:LL:COL +help: you might have meant to use `push_str` + | +LL | String::new().push_str(""); + | ~~~~~~~~ -error: aborting due to 6 previous errors +error[E0599]: no method named `append` found for struct `String` in the current scope + --> $DIR/rustc_confusables_std_cases.rs:22:19 + | +LL | String::new().append(""); + | ^^^^^^ method not found in `String` + | +help: you might have meant to use `push_str` + | +LL | String::new().push_str(""); + | ~~~~~~~~ + +error: aborting due to 7 previous errors Some errors have detailed explanations: E0308, E0599. For more information about an error, try `rustc --explain E0308`. From e13d4521113a32ec3aaabf659fc045874acb91c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 9 Feb 2024 20:10:39 +0000 Subject: [PATCH 03/15] drive-by fmt cleanup --- compiler/rustc_middle/src/ty/print/pretty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 92ec1a83bee..1910841f268 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2929,7 +2929,7 @@ define_print_and_forward_display! { ty::ExistentialTraitRef<'tcx> { // Use a type that can't appear in defaults of type parameters. - let dummy_self = Ty::new_fresh(cx.tcx(),0); + let dummy_self = Ty::new_fresh(cx.tcx(), 0); let trait_ref = self.with_self_ty(cx.tcx(), dummy_self); p!(print(trait_ref.print_only_trait_path())) } From d30dfb0af72ab71ee267eecef26a6270cee8822b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 9 Feb 2024 20:54:26 +0000 Subject: [PATCH 04/15] Provide more and more accurate suggestions when calling the wrong method ``` error[E0308]: mismatched types --> $DIR/rustc_confusables_std_cases.rs:20:14 | LL | x.append(42); | ------ ^^ expected `&mut Vec<{integer}>`, found integer | | | arguments to this method are incorrect | = note: expected mutable reference `&mut Vec<{integer}>` found type `{integer}` note: method defined here --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: you might have meant to use `push` | LL | x.push(42); | ~~~~ ``` --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 106 ++++++++++++++++-- compiler/rustc_hir_typeck/src/method/probe.rs | 24 +++- .../rustc_hir_typeck/src/method/suggest.rs | 65 ++++++++--- .../attributes/rustc_confusables_std_cases.rs | 2 + .../rustc_confusables_std_cases.stderr | 23 +++- 5 files changed, 189 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 15dc0ba444b..b686e65a1d9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1,7 +1,11 @@ use crate::coercion::CoerceMany; use crate::errors::SuggestPtrNullMut; use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx}; +use crate::fn_ctxt::infer::FnCall; use crate::gather_locals::Declaration; +use crate::method::probe::IsSuggestion; +use crate::method::probe::Mode::MethodCall; +use crate::method::probe::ProbeScope::TraitsInScope; use crate::method::MethodCallee; use crate::TupleArgumentsFlag::*; use crate::{errors, Expectation::*}; @@ -532,25 +536,111 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let callee_ty = callee_expr .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr)); + // Obtain another method on `Self` that have similar name. + let similar_assoc = |call_name: Ident| -> Option<(ty::AssocItem, ty::FnSig<'_>)> { + if let Some(callee_ty) = callee_ty + && let Ok(Some(assoc)) = self.probe_op( + call_name.span, + MethodCall, + Some(call_name), + None, + IsSuggestion(true), + callee_ty.peel_refs(), + callee_expr.unwrap().hir_id, + TraitsInScope, + |mut ctxt| ctxt.probe_for_similar_candidate(), + ) + && let ty::AssocKind::Fn = assoc.kind + && assoc.fn_has_self_parameter + { + let fn_sig = + if let ty::Adt(_, args) = callee_ty.peel_refs().kind() { + let args = ty::GenericArgs::identity_for_item(tcx, assoc.def_id) + .rebase_onto(tcx, assoc.container_id(tcx), args); + tcx.fn_sig(assoc.def_id).instantiate(tcx, args) + } else { + tcx.fn_sig(assoc.def_id).instantiate_identity() + }; + let fn_sig = + self.instantiate_binder_with_fresh_vars(call_name.span, FnCall, fn_sig); + Some((assoc, fn_sig)); + } + None + }; + let suggest_confusable = |err: &mut Diagnostic| { if let Some(call_name) = call_ident && let Some(callee_ty) = callee_ty { - // FIXME: check in the following order - // - methods marked as `rustc_confusables` with the provided arguments (done) - // - methods marked as `rustc_confusables` with the right number of arguments - // - methods marked as `rustc_confusables` (done) - // - methods with the same argument type/count and short levenshtein distance - // - methods with short levenshtein distance - // - methods with the same argument type/count + let input_types: Vec> = provided_arg_tys.iter().map(|(ty, _)| *ty).collect(); + // Check for other methods in the following order + // - methods marked as `rustc_confusables` with the provided arguments + // - methods with the same argument type/count and short levenshtein distance + // - methods marked as `rustc_confusables` (done) + // - methods with short levenshtein distance + + // Look for commonly confusable method names considering arguments. self.confusable_method_name( err, callee_ty.peel_refs(), call_name, - Some(provided_arg_tys.iter().map(|(ty, _)| *ty).collect()), + Some(input_types.clone()), ) .or_else(|| { + // Look for method names with short levenshtein distance, considering arguments. + if let Some((assoc, fn_sig)) = similar_assoc(call_name) + && fn_sig.inputs()[1..] + .iter() + .zip(input_types.iter()) + .all(|(expected, found)| self.can_coerce(*expected, *found)) + && fn_sig.inputs()[1..].len() == input_types.len() + { + err.span_suggestion_verbose( + call_name.span, + format!("you might have meant to use `{}`", assoc.name), + assoc.name, + Applicability::MaybeIncorrect, + ); + return Some(assoc.name); + } + None + }) + .or_else(|| { + // Look for commonly confusable method names disregarding arguments. self.confusable_method_name(err, callee_ty.peel_refs(), call_name, None) + }) + .or_else(|| { + // Look for similarly named methods with levenshtein distance with the right + // number of arguments. + if let Some((assoc, fn_sig)) = similar_assoc(call_name) + && fn_sig.inputs()[1..].len() == input_types.len() + { + err.span_note( + tcx.def_span(assoc.def_id), + format!( + "there's is a method with similar name `{}`, but the arguments \ + don't match", + assoc.name, + ), + ); + return Some(assoc.name); + } + None + }) + .or_else(|| { + // Fallthrough: look for similarly named methods with levenshtein distance. + if let Some((assoc, _)) = similar_assoc(call_name) { + err.span_note( + tcx.def_span(assoc.def_id), + format!( + "there's is a method with similar name `{}`, but their argument \ + count doesn't match", + assoc.name, + ), + ); + return Some(assoc.name); + } + None }); } }; diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index a58e194e20a..2d83e74b6db 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -54,7 +54,7 @@ pub use self::PickKind::*; #[derive(Clone, Copy, Debug)] pub struct IsSuggestion(pub bool); -struct ProbeContext<'a, 'tcx> { +pub(crate) struct ProbeContext<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, span: Span, mode: Mode, @@ -355,7 +355,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap() } - fn probe_op( + pub(crate) fn probe_op( &'a self, span: Span, mode: Mode, @@ -1750,7 +1750,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// Similarly to `probe_for_return_type`, this method attempts to find the best matching /// candidate method where the method name may have been misspelled. Similarly to other /// edit distance based suggestions, we provide at most one such suggestion. - fn probe_for_similar_candidate(&mut self) -> Result, MethodError<'tcx>> { + pub(crate) fn probe_for_similar_candidate( + &mut self, + ) -> Result, MethodError<'tcx>> { debug!("probing for method names similar to {:?}", self.method_name); self.probe(|_| { @@ -1942,7 +1944,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let hir_id = self.fcx.tcx.local_def_id_to_hir_id(local_def_id); let attrs = self.fcx.tcx.hir().attrs(hir_id); for attr in attrs { - let sym::doc = attr.name_or_empty() else { + if sym::doc == attr.name_or_empty() { + } else if sym::rustc_confusables == attr.name_or_empty() { + let Some(confusables) = attr.meta_item_list() else { + continue; + }; + // #[rustc_confusables("foo", "bar"))] + for n in confusables { + if let Some(lit) = n.lit() + && name.as_str() == lit.symbol.as_str() + { + return true; + } + } + continue; + } else { continue; }; let Some(values) = attr.meta_item_list() else { diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 6226f2ee796..989b9d7741d 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -23,6 +23,7 @@ use rustc_hir::PatKind::Binding; use rustc_hir::PathSegment; use rustc_hir::{ExprKind, Node, QPath}; use rustc_infer::infer::{ + self, type_variable::{TypeVariableOrigin, TypeVariableOriginKind}, RegionVariableOrigin, }; @@ -1234,7 +1235,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { label_span_not_found(&mut err); } - let confusable_suggested = self.confusable_method_name(&mut err, rcvr_ty, item_name, None); + let confusable_suggested = self.confusable_method_name( + &mut err, + rcvr_ty, + item_name, + args.map(|args| { + args.iter() + .map(|expr| { + self.node_ty_opt(expr.hir_id).unwrap_or_else(|| { + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: expr.span, + }) + }) + }) + .collect() + }), + ); // Don't suggest (for example) `expr.field.clone()` if `expr.clone()` // can't be called due to `typeof(expr): Clone` not holding. @@ -1421,9 +1438,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err: &mut Diagnostic, rcvr_ty: Ty<'tcx>, item_name: Ident, - args: Option>>, + call_args: Option>>, ) -> Option { - if let ty::Adt(adt, _) = rcvr_ty.kind() { + if let ty::Adt(adt, adt_args) = rcvr_ty.kind() { for inherent_impl_did in self.tcx.inherent_impls(adt.did()).into_iter().flatten() { for inherent_method in self.tcx.associated_items(inherent_impl_did).in_definition_order() @@ -1432,22 +1449,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.get_attr(inherent_method.def_id, sym::rustc_confusables) && let Some(candidates) = parse_confusables(attr) && candidates.contains(&item_name.name) + && let ty::AssocKind::Fn = inherent_method.kind { - let mut matches_args = args.is_none(); - if let ty::AssocKind::Fn = inherent_method.kind - && let Some(ref args) = args - { - let fn_sig = - self.tcx.fn_sig(inherent_method.def_id).instantiate_identity(); - matches_args = fn_sig - .inputs() - .skip_binder() + let args = + ty::GenericArgs::identity_for_item(self.tcx, inherent_method.def_id) + .rebase_onto( + self.tcx, + inherent_method.container_id(self.tcx), + adt_args, + ); + let fn_sig = + self.tcx.fn_sig(inherent_method.def_id).instantiate(self.tcx, args); + let fn_sig = self.instantiate_binder_with_fresh_vars( + item_name.span, + infer::FnCall, + fn_sig, + ); + if let Some(ref args) = call_args + && fn_sig.inputs()[1..] .iter() - .skip(1) .zip(args.into_iter()) - .all(|(expected, found)| self.can_coerce(*expected, *found)); - } - if matches_args { + .all(|(expected, found)| self.can_coerce(*expected, *found)) + && fn_sig.inputs()[1..].len() == args.len() + { err.span_suggestion_verbose( item_name.span, format!("you might have meant to use `{}`", inherent_method.name), @@ -1455,6 +1479,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); return Some(inherent_method.name); + } else if let None = call_args { + err.span_note( + self.tcx.def_span(inherent_method.def_id), + format!( + "you might have meant to use method `{}`", + inherent_method.name, + ), + ); + return Some(inherent_method.name); } } } diff --git a/tests/ui/attributes/rustc_confusables_std_cases.rs b/tests/ui/attributes/rustc_confusables_std_cases.rs index eda35e4b633..95093f5e72c 100644 --- a/tests/ui/attributes/rustc_confusables_std_cases.rs +++ b/tests/ui/attributes/rustc_confusables_std_cases.rs @@ -17,6 +17,8 @@ fn main() { x.size(); //~ ERROR E0599 //~^ HELP you might have meant to use `len` //~| HELP there is a method with a similar name + x.append(42); //~ ERROR E0308 + //~^ HELP you might have meant to use `push` String::new().push(""); //~ ERROR E0308 //~^ HELP you might have meant to use `push_str` String::new().append(""); //~ ERROR E0599 diff --git a/tests/ui/attributes/rustc_confusables_std_cases.stderr b/tests/ui/attributes/rustc_confusables_std_cases.stderr index f69b79b4028..7d2410bb0ef 100644 --- a/tests/ui/attributes/rustc_confusables_std_cases.stderr +++ b/tests/ui/attributes/rustc_confusables_std_cases.stderr @@ -58,7 +58,24 @@ LL | x.resize(); | ~~~~~~ error[E0308]: mismatched types - --> $DIR/rustc_confusables_std_cases.rs:20:24 + --> $DIR/rustc_confusables_std_cases.rs:20:14 + | +LL | x.append(42); + | ------ ^^ expected `&mut Vec<{integer}>`, found integer + | | + | arguments to this method are incorrect + | + = note: expected mutable reference `&mut Vec<{integer}>` + found type `{integer}` +note: method defined here + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL +help: you might have meant to use `push` + | +LL | x.push(42); + | ~~~~ + +error[E0308]: mismatched types + --> $DIR/rustc_confusables_std_cases.rs:22:24 | LL | String::new().push(""); | ---- ^^ expected `char`, found `&str` @@ -73,7 +90,7 @@ LL | String::new().push_str(""); | ~~~~~~~~ error[E0599]: no method named `append` found for struct `String` in the current scope - --> $DIR/rustc_confusables_std_cases.rs:22:19 + --> $DIR/rustc_confusables_std_cases.rs:24:19 | LL | String::new().append(""); | ^^^^^^ method not found in `String` @@ -83,7 +100,7 @@ help: you might have meant to use `push_str` LL | String::new().push_str(""); | ~~~~~~~~ -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors Some errors have detailed explanations: E0308, E0599. For more information about an error, try `rustc --explain E0308`. From 385eea1d4610f5bd756dd930f36762a7fc8f75e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 10 Feb 2024 03:33:20 +0000 Subject: [PATCH 05/15] Consider methods from traits when suggesting typos Do not provide a structured suggestion when the arguments don't match. ``` error[E0599]: no method named `test_mut` found for struct `Vec<{integer}>` in the current scope --> $DIR/auto-ref-slice-plus-ref.rs:7:7 | LL | a.test_mut(); | ^^^^^^^^ | = help: items from traits can only be used if the trait is implemented and in scope note: `MyIter` defines an item `test_mut`, perhaps you need to implement it --> $DIR/auto-ref-slice-plus-ref.rs:14:1 | LL | trait MyIter { | ^^^^^^^^^^^^ help: there is a method `get_mut` with a similar name, but with different arguments --> $SRC_DIR/core/src/slice/mod.rs:LL:COL ``` Consider methods beyond inherent ones when suggesting typos. ``` error[E0599]: no method named `owned` found for reference `&dyn Foo` in the current scope --> $DIR/object-pointer-types.rs:11:7 | LL | fn owned(self: Box); | --------- the method might not be found because of this arbitrary self type ... LL | x.owned(); | ^^^^^ help: there is a method with a similar name: `to_owned` ``` Fix #101013. --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 10 +-- compiler/rustc_hir_typeck/src/method/probe.rs | 2 + .../rustc_hir_typeck/src/method/suggest.rs | 90 ++++++++++++++++--- .../associated-item-enum.stderr | 2 +- .../attributes/rustc_confusables_std_cases.rs | 2 +- .../rustc_confusables_std_cases.stderr | 6 +- tests/ui/auto-ref-slice-plus-ref.stderr | 4 +- .../issue-33784.stderr | 4 + tests/ui/hygiene/no_implicit_prelude.stderr | 4 +- .../no-method-suggested-traits.stderr | 36 ++++++-- tests/ui/issues/issue-28344.stderr | 16 ++-- tests/ui/issues/issue-39175.stderr | 4 +- tests/ui/issues/issue-56175.stderr | 12 ++- tests/ui/object-pointer-types.stderr | 2 +- .../rust-2018/trait-import-suggestions.stderr | 18 ++-- .../future-prelude-collision-shadow.stderr | 6 +- .../dont-suggest-pin-array-dot-set.stderr | 5 +- tests/ui/suggestions/suggest-methods.rs | 5 +- tests/ui/suggestions/suggest-methods.stderr | 16 ++-- .../suggest-tryinto-edition-change.rs | 1 - .../suggest-tryinto-edition-change.stderr | 12 ++- tests/ui/traits/issue-117794.stderr | 8 +- tests/ui/traits/item-privacy.stderr | 28 +++++- 23 files changed, 224 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index b686e65a1d9..0ca7a8bb47b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -553,14 +553,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let ty::AssocKind::Fn = assoc.kind && assoc.fn_has_self_parameter { - let fn_sig = - if let ty::Adt(_, args) = callee_ty.peel_refs().kind() { - let args = ty::GenericArgs::identity_for_item(tcx, assoc.def_id) - .rebase_onto(tcx, assoc.container_id(tcx), args); - tcx.fn_sig(assoc.def_id).instantiate(tcx, args) - } else { - tcx.fn_sig(assoc.def_id).instantiate_identity() - }; + let args = self.infcx.fresh_args_for_item(call_name.span, assoc.def_id); + let fn_sig = tcx.fn_sig(assoc.def_id).instantiate(tcx, args); let fn_sig = self.instantiate_binder_with_fresh_vars(call_name.span, FnCall, fn_sig); Some((assoc, fn_sig)); diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 2d83e74b6db..7117a59c409 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1768,6 +1768,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ); pcx.allow_similar_names = true; pcx.assemble_inherent_candidates(); + pcx.assemble_extension_candidates_for_all_traits(); let method_names = pcx.candidate_method_names(|_| true); pcx.allow_similar_names = false; @@ -1777,6 +1778,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { pcx.reset(); pcx.method_name = Some(method_name); pcx.assemble_inherent_candidates(); + pcx.assemble_extension_candidates_for_all_traits(); pcx.pick_core().and_then(|pick| pick.ok()).map(|pick| pick.item) }) .collect(); diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 989b9d7741d..735a1b17d7e 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1359,28 +1359,90 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && Some(similar_candidate.name) != confusable_suggested { let def_kind = similar_candidate.kind.as_def_kind(); - // Methods are defined within the context of a struct and their first parameter is always self, - // which represents the instance of the struct the method is being called on - // Associated functions don’t take self as a parameter and - // they are not methods because they don’t have an instance of the struct to work with. - if def_kind == DefKind::AssocFn && similar_candidate.fn_has_self_parameter { - err.span_suggestion( - span, - "there is a method with a similar name", - similar_candidate.name, - Applicability::MaybeIncorrect, - ); - } else { + let an = self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id); + // Methods are defined within the context of a struct and their first parameter + // is always `self`, which represents the instance of the struct the method is + // being called on Associated functions don’t take self as a parameter and they are + // not methods because they don’t have an instance of the struct to work with. + if def_kind == DefKind::AssocFn { + let ty_args = self.infcx.fresh_args_for_item(span, similar_candidate.def_id); + let fn_sig = tcx.fn_sig(similar_candidate.def_id).instantiate(tcx, ty_args); + let fn_sig = + self.instantiate_binder_with_fresh_vars(span, infer::FnCall, fn_sig); + if similar_candidate.fn_has_self_parameter { + if let Some(args) = args + && fn_sig.inputs()[1..].len() == args.len() + { + // We found a method with the same number of arguments as the method + // call expression the user wrote. + err.span_suggestion( + span, + format!("there is {an} method with a similar name"), + similar_candidate.name, + Applicability::MaybeIncorrect, + ); + } else { + // We found a method but either the expression is not a method call or + // the argument count didn't match. + err.span_help( + tcx.def_span(similar_candidate.def_id), + format!( + "there is {an} method `{}` with a similar name{}", + similar_candidate.name, + if let None = args { + "" + } else { + ", but with different arguments" + }, + ), + ); + } + } else if let Some(args) = args + && fn_sig.inputs().len() == args.len() + { + // We have fn call expression and the argument count match the associated + // function we found. + err.span_suggestion( + span, + format!( + "there is {an} {} with a similar name", + self.tcx.def_kind_descr(def_kind, similar_candidate.def_id) + ), + similar_candidate.name, + Applicability::MaybeIncorrect, + ); + } else { + err.span_help( + tcx.def_span(similar_candidate.def_id), + format!( + "there is {an} {} `{}` with a similar name", + self.tcx.def_kind_descr(def_kind, similar_candidate.def_id), + similar_candidate.name, + ), + ); + } + } else if let Mode::Path = mode { + // We have an associated item syntax and we found something that isn't an fn. err.span_suggestion( span, format!( - "there is {} {} with a similar name", - self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id), + "there is {an} {} with a similar name", self.tcx.def_kind_descr(def_kind, similar_candidate.def_id) ), similar_candidate.name, Applicability::MaybeIncorrect, ); + } else { + // The expression is a function or method call, but the item we found is an + // associated const or type. + err.span_help( + tcx.def_span(similar_candidate.def_id), + format!( + "there is {an} {} `{}` with a similar name", + self.tcx.def_kind_descr(def_kind, similar_candidate.def_id), + similar_candidate.name, + ), + ); } } } diff --git a/tests/ui/associated-item/associated-item-enum.stderr b/tests/ui/associated-item/associated-item-enum.stderr index ebf3c5499a6..9e9f5c0daad 100644 --- a/tests/ui/associated-item/associated-item-enum.stderr +++ b/tests/ui/associated-item/associated-item-enum.stderr @@ -20,7 +20,7 @@ LL | Enum::mispellable_trait(); | ^^^^^^^^^^^^^^^^^ | | | variant or associated item not found in `Enum` - | help: there is an associated function with a similar name: `misspellable` + | help: there is an associated function with a similar name: `misspellable_trait` error[E0599]: no variant or associated item named `MISPELLABLE` found for enum `Enum` in the current scope --> $DIR/associated-item-enum.rs:19:11 diff --git a/tests/ui/attributes/rustc_confusables_std_cases.rs b/tests/ui/attributes/rustc_confusables_std_cases.rs index 95093f5e72c..d9121695950 100644 --- a/tests/ui/attributes/rustc_confusables_std_cases.rs +++ b/tests/ui/attributes/rustc_confusables_std_cases.rs @@ -16,7 +16,7 @@ fn main() { //~^ HELP you might have meant to use `len` x.size(); //~ ERROR E0599 //~^ HELP you might have meant to use `len` - //~| HELP there is a method with a similar name + //~| HELP there is a method `resize` with a similar name x.append(42); //~ ERROR E0308 //~^ HELP you might have meant to use `push` String::new().push(""); //~ ERROR E0308 diff --git a/tests/ui/attributes/rustc_confusables_std_cases.stderr b/tests/ui/attributes/rustc_confusables_std_cases.stderr index 7d2410bb0ef..45d571f435c 100644 --- a/tests/ui/attributes/rustc_confusables_std_cases.stderr +++ b/tests/ui/attributes/rustc_confusables_std_cases.stderr @@ -48,14 +48,12 @@ error[E0599]: no method named `size` found for struct `Vec<{integer}>` in the cu LL | x.size(); | ^^^^ | +help: there is a method `resize` with a similar name, but with different arguments + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: you might have meant to use `len` | LL | x.len(); | ~~~ -help: there is a method with a similar name - | -LL | x.resize(); - | ~~~~~~ error[E0308]: mismatched types --> $DIR/rustc_confusables_std_cases.rs:20:14 diff --git a/tests/ui/auto-ref-slice-plus-ref.stderr b/tests/ui/auto-ref-slice-plus-ref.stderr index e2883050720..806c1ee064f 100644 --- a/tests/ui/auto-ref-slice-plus-ref.stderr +++ b/tests/ui/auto-ref-slice-plus-ref.stderr @@ -2,7 +2,7 @@ error[E0599]: no method named `test_mut` found for struct `Vec<{integer}>` in th --> $DIR/auto-ref-slice-plus-ref.rs:7:7 | LL | a.test_mut(); - | ^^^^^^^^ help: there is a method with a similar name: `get_mut` + | ^^^^^^^^ | = help: items from traits can only be used if the trait is implemented and in scope note: `MyIter` defines an item `test_mut`, perhaps you need to implement it @@ -10,6 +10,8 @@ note: `MyIter` defines an item `test_mut`, perhaps you need to implement it | LL | trait MyIter { | ^^^^^^^^^^^^ +help: there is a method `get_mut` with a similar name, but with different arguments + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL error[E0599]: no method named `test` found for struct `Vec<{integer}>` in the current scope --> $DIR/auto-ref-slice-plus-ref.rs:8:7 diff --git a/tests/ui/confuse-field-and-method/issue-33784.stderr b/tests/ui/confuse-field-and-method/issue-33784.stderr index aaf953a66d5..f6678dc8543 100644 --- a/tests/ui/confuse-field-and-method/issue-33784.stderr +++ b/tests/ui/confuse-field-and-method/issue-33784.stderr @@ -8,6 +8,10 @@ help: to call the function stored in `closure`, surround the field access with p | LL | (p.closure)(); | + + +help: there is a method with a similar name + | +LL | p.clone(); + | ~~~~~ error[E0599]: no method named `fn_ptr` found for reference `&&Obj<{closure@$DIR/issue-33784.rs:25:43: 25:45}>` in the current scope --> $DIR/issue-33784.rs:29:7 diff --git a/tests/ui/hygiene/no_implicit_prelude.stderr b/tests/ui/hygiene/no_implicit_prelude.stderr index 96187b1c501..646863a884c 100644 --- a/tests/ui/hygiene/no_implicit_prelude.stderr +++ b/tests/ui/hygiene/no_implicit_prelude.stderr @@ -20,9 +20,11 @@ LL | fn f() { ::bar::m!(); } | ----------- in this macro invocation ... LL | ().clone() - | ^^^^^ method not found in `()` + | ^^^^^ | = help: items from traits can only be used if the trait is in scope +help: there is a method `clone_from` with a similar name, but with different arguments + --> $SRC_DIR/core/src/clone.rs:LL:COL = note: this error originates in the macro `::bar::m` (in Nightly builds, run with -Z macro-backtrace for more info) help: the following trait is implemented but not in scope; perhaps add a `use` for it: | diff --git a/tests/ui/impl-trait/no-method-suggested-traits.stderr b/tests/ui/impl-trait/no-method-suggested-traits.stderr index 160cc044078..cf1bf59ab20 100644 --- a/tests/ui/impl-trait/no-method-suggested-traits.stderr +++ b/tests/ui/impl-trait/no-method-suggested-traits.stderr @@ -2,7 +2,7 @@ error[E0599]: no method named `method` found for type `u32` in the current scope --> $DIR/no-method-suggested-traits.rs:23:10 | LL | 1u32.method(); - | ^^^^^^ method not found in `u32` + | ^^^^^^ | = help: items from traits can only be used if the trait is in scope help: the following traits are implemented but not in scope; perhaps add a `use` for one of them: @@ -15,12 +15,16 @@ LL + use no_method_suggested_traits::foo::PubPub; | LL + use no_method_suggested_traits::qux::PrivPub; | +help: there is a method with a similar name + | +LL | 1u32.method2(); + | ~~~~~~~ error[E0599]: no method named `method` found for struct `Rc<&mut Box<&u32>>` in the current scope --> $DIR/no-method-suggested-traits.rs:26:44 | LL | std::rc::Rc::new(&mut Box::new(&1u32)).method(); - | ^^^^^^ method not found in `Rc<&mut Box<&u32>>` + | ^^^^^^ | = help: items from traits can only be used if the trait is in scope help: the following traits are implemented but not in scope; perhaps add a `use` for one of them: @@ -33,6 +37,10 @@ LL + use no_method_suggested_traits::foo::PubPub; | LL + use no_method_suggested_traits::qux::PrivPub; | +help: there is a method with a similar name + | +LL | std::rc::Rc::new(&mut Box::new(&1u32)).method2(); + | ~~~~~~~ error[E0599]: no method named `method` found for type `char` in the current scope --> $DIR/no-method-suggested-traits.rs:30:9 @@ -41,31 +49,39 @@ LL | fn method(&self) {} | ------ the method is available for `char` here ... LL | 'a'.method(); - | ^^^^^^ method not found in `char` + | ^^^^^^ | = help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it: | LL + use foo::Bar; | +help: there is a method with a similar name + | +LL | 'a'.method2(); + | ~~~~~~~ error[E0599]: no method named `method` found for struct `Rc<&mut Box<&char>>` in the current scope --> $DIR/no-method-suggested-traits.rs:32:43 | LL | std::rc::Rc::new(&mut Box::new(&'a')).method(); - | ^^^^^^ method not found in `Rc<&mut Box<&char>>` + | ^^^^^^ | = help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it: | LL + use foo::Bar; | +help: there is a method with a similar name + | +LL | std::rc::Rc::new(&mut Box::new(&'a')).method2(); + | ~~~~~~~ error[E0599]: no method named `method` found for type `i32` in the current scope --> $DIR/no-method-suggested-traits.rs:35:10 | LL | 1i32.method(); - | ^^^^^^ method not found in `i32` + | ^^^^^^ | ::: $DIR/auxiliary/no_method_suggested_traits.rs:8:12 | @@ -77,18 +93,26 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f | LL + use no_method_suggested_traits::foo::PubPub; | +help: there is a method with a similar name + | +LL | 1i32.method3(); + | ~~~~~~~ error[E0599]: no method named `method` found for struct `Rc<&mut Box<&i32>>` in the current scope --> $DIR/no-method-suggested-traits.rs:37:44 | LL | std::rc::Rc::new(&mut Box::new(&1i32)).method(); - | ^^^^^^ method not found in `Rc<&mut Box<&i32>>` + | ^^^^^^ | = help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it: | LL + use no_method_suggested_traits::foo::PubPub; | +help: there is a method with a similar name + | +LL | std::rc::Rc::new(&mut Box::new(&1i32)).method3(); + | ~~~~~~~ error[E0599]: no method named `method` found for struct `Foo` in the current scope --> $DIR/no-method-suggested-traits.rs:40:9 diff --git a/tests/ui/issues/issue-28344.stderr b/tests/ui/issues/issue-28344.stderr index 8b427b692a7..ddc1027c16b 100644 --- a/tests/ui/issues/issue-28344.stderr +++ b/tests/ui/issues/issue-28344.stderr @@ -22,10 +22,10 @@ error[E0599]: no function or associated item named `bitor` found for trait objec --> $DIR/issue-28344.rs:4:25 | LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); - | ^^^^^ - | | - | function or associated item not found in `dyn BitXor<_>` - | help: there is a method with a similar name: `bitxor` + | ^^^^^ function or associated item not found in `dyn BitXor<_>` + | +help: there is a method `bitxor` with a similar name, but with different arguments + --> $SRC_DIR/core/src/ops/bit.rs:LL:COL warning: trait objects without an explicit `dyn` are deprecated --> $DIR/issue-28344.rs:10:13 @@ -50,10 +50,10 @@ error[E0599]: no function or associated item named `bitor` found for trait objec --> $DIR/issue-28344.rs:10:21 | LL | let g = BitXor::bitor; - | ^^^^^ - | | - | function or associated item not found in `dyn BitXor<_>` - | help: there is a method with a similar name: `bitxor` + | ^^^^^ function or associated item not found in `dyn BitXor<_>` + | +help: there is a method `bitxor` with a similar name, but with different arguments + --> $SRC_DIR/core/src/ops/bit.rs:LL:COL error: aborting due to 4 previous errors; 2 warnings emitted diff --git a/tests/ui/issues/issue-39175.stderr b/tests/ui/issues/issue-39175.stderr index 1bc11dab327..bdc9e422e86 100644 --- a/tests/ui/issues/issue-39175.stderr +++ b/tests/ui/issues/issue-39175.stderr @@ -2,9 +2,11 @@ error[E0599]: no method named `exec` found for mutable reference `&mut Command` --> $DIR/issue-39175.rs:14:39 | LL | Command::new("echo").arg("hello").exec(); - | ^^^^ method not found in `&mut Command` + | ^^^^ | = help: items from traits can only be used if the trait is in scope +help: there is a method `pre_exec` with a similar name, but with different arguments + --> $SRC_DIR/std/src/os/unix/process.rs:LL:COL help: the following trait is implemented but not in scope; perhaps add a `use` for it: | LL + use std::os::unix::process::CommandExt; diff --git a/tests/ui/issues/issue-56175.stderr b/tests/ui/issues/issue-56175.stderr index 1ddee1f4895..d8c757c1583 100644 --- a/tests/ui/issues/issue-56175.stderr +++ b/tests/ui/issues/issue-56175.stderr @@ -2,7 +2,7 @@ error[E0599]: no method named `trait_method` found for struct `FooStruct` in the --> $DIR/issue-56175.rs:5:33 | LL | reexported_trait::FooStruct.trait_method(); - | ^^^^^^^^^^^^ method not found in `FooStruct` + | ^^^^^^^^^^^^ | ::: $DIR/auxiliary/reexported-trait.rs:3:12 | @@ -14,12 +14,16 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f | LL + use reexported_trait::Trait; | +help: there is a method with a similar name + | +LL | reexported_trait::FooStruct.trait_method_b(); + | ~~~~~~~~~~~~~~ error[E0599]: no method named `trait_method_b` found for struct `FooStruct` in the current scope --> $DIR/issue-56175.rs:7:33 | LL | reexported_trait::FooStruct.trait_method_b(); - | ^^^^^^^^^^^^^^ method not found in `FooStruct` + | ^^^^^^^^^^^^^^ | ::: $DIR/auxiliary/reexported-trait.rs:7:12 | @@ -31,6 +35,10 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f | LL + use reexported_trait::TraitBRename; | +help: there is a method with a similar name + | +LL | reexported_trait::FooStruct.trait_method(); + | ~~~~~~~~~~~~ error: aborting due to 2 previous errors diff --git a/tests/ui/object-pointer-types.stderr b/tests/ui/object-pointer-types.stderr index 2c8df3b616f..fe21bf3aede 100644 --- a/tests/ui/object-pointer-types.stderr +++ b/tests/ui/object-pointer-types.stderr @@ -5,7 +5,7 @@ LL | fn owned(self: Box); | --------- the method might not be found because of this arbitrary self type ... LL | x.owned(); - | ^^^^^ method not found in `&dyn Foo` + | ^^^^^ help: there is a method with a similar name: `to_owned` error[E0599]: no method named `owned` found for mutable reference `&mut dyn Foo` in the current scope --> $DIR/object-pointer-types.rs:17:7 diff --git a/tests/ui/rust-2018/trait-import-suggestions.stderr b/tests/ui/rust-2018/trait-import-suggestions.stderr index 325c5976e7c..a9d625df9ba 100644 --- a/tests/ui/rust-2018/trait-import-suggestions.stderr +++ b/tests/ui/rust-2018/trait-import-suggestions.stderr @@ -5,13 +5,17 @@ LL | fn foobar(&self) { } | ------ the method is available for `u32` here ... LL | x.foobar(); - | ^^^^^^ method not found in `u32` + | ^^^^^^ | = help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it: | LL + use crate::foo::foobar::Foobar; | +help: there is a method with a similar name + | +LL | x.bar(); + | ~~~ error[E0599]: no method named `bar` found for type `u32` in the current scope --> $DIR/trait-import-suggestions.rs:28:7 @@ -20,19 +24,23 @@ LL | fn bar(&self) { } | --- the method is available for `u32` here ... LL | x.bar(); - | ^^^ method not found in `u32` + | ^^^ | = help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it: | LL + use crate::foo::Bar; | +help: there is a method with a similar name + | +LL | x.foobar(); + | ~~~~~~ error[E0599]: no method named `baz` found for type `u32` in the current scope --> $DIR/trait-import-suggestions.rs:29:7 | LL | x.baz(); - | ^^^ method not found in `u32` + | ^^^ help: there is a method with a similar name: `bar` error[E0599]: no function or associated item named `from_str` found for type `u32` in the current scope --> $DIR/trait-import-suggestions.rs:30:18 @@ -47,8 +55,8 @@ LL + use std::str::FromStr; | help: there is an associated function with a similar name | -LL | let y = u32::from_str_radix("33"); - | ~~~~~~~~~~~~~~ +LL | let y = u32::from("33"); + | ~~~~ error: aborting due to 4 previous errors diff --git a/tests/ui/rust-2021/future-prelude-collision-shadow.stderr b/tests/ui/rust-2021/future-prelude-collision-shadow.stderr index d2b2e5b2fd7..1ca5708e753 100644 --- a/tests/ui/rust-2021/future-prelude-collision-shadow.stderr +++ b/tests/ui/rust-2021/future-prelude-collision-shadow.stderr @@ -2,7 +2,7 @@ error[E0599]: no method named `try_into` found for type `u8` in the current scop --> $DIR/future-prelude-collision-shadow.rs:27:26 | LL | let _: u32 = 3u8.try_into().unwrap(); - | ^^^^^^^^ method not found in `u8` + | ^^^^^^^^ | = help: items from traits can only be used if the trait is in scope = note: 'std::convert::TryInto' is included in the prelude starting in Edition 2021 @@ -12,6 +12,10 @@ LL + use crate::m::TryIntoU32; | LL + use std::convert::TryInto; | +help: there is a method with a similar name + | +LL | let _: u32 = 3u8.into().unwrap(); + | ~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/dont-suggest-pin-array-dot-set.stderr b/tests/ui/suggestions/dont-suggest-pin-array-dot-set.stderr index 8f738465d38..b1a618e7176 100644 --- a/tests/ui/suggestions/dont-suggest-pin-array-dot-set.stderr +++ b/tests/ui/suggestions/dont-suggest-pin-array-dot-set.stderr @@ -2,7 +2,10 @@ error[E0599]: no method named `set` found for array `[u8; 1]` in the current sco --> $DIR/dont-suggest-pin-array-dot-set.rs:14:7 | LL | a.set(0, 3); - | ^^^ help: there is a method with a similar name: `get` + | ^^^ + | +help: there is a method `get` with a similar name, but with different arguments + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/suggest-methods.rs b/tests/ui/suggestions/suggest-methods.rs index f40b9ed99b8..f8095795447 100644 --- a/tests/ui/suggestions/suggest-methods.rs +++ b/tests/ui/suggestions/suggest-methods.rs @@ -7,6 +7,7 @@ impl Foo { trait FooT { fn bag(&self); + //~^ HELP there is a method } impl FooT for Foo { @@ -19,12 +20,14 @@ fn main() { let s = "foo".to_string(); let _ = s.is_emtpy(); //~ ERROR no method named + //~^ HELP there is a method // Generates a warning for `count_zeros()`. `count_ones()` is also a close // match, but the former is closer. let _ = 63u32.count_eos(); //~ ERROR no method named + //~^ HELP there is a method - // Does not generate a warning let _ = 63u32.count_o(); //~ ERROR no method named + //~^ HELP there is a method } diff --git a/tests/ui/suggestions/suggest-methods.stderr b/tests/ui/suggestions/suggest-methods.stderr index 03cb9c77922..293009f289b 100644 --- a/tests/ui/suggestions/suggest-methods.stderr +++ b/tests/ui/suggestions/suggest-methods.stderr @@ -1,26 +1,32 @@ error[E0599]: no method named `bat` found for struct `Foo` in the current scope - --> $DIR/suggest-methods.rs:18:7 + --> $DIR/suggest-methods.rs:19:7 | LL | struct Foo; | ---------- method `bat` not found for this struct ... LL | f.bat(1.0); - | ^^^ help: there is a method with a similar name: `bar` + | ^^^ + | +help: there is a method `bag` with a similar name, but with different arguments + --> $DIR/suggest-methods.rs:9:5 + | +LL | fn bag(&self); + | ^^^^^^^^^^^^^^ error[E0599]: no method named `is_emtpy` found for struct `String` in the current scope - --> $DIR/suggest-methods.rs:21:15 + --> $DIR/suggest-methods.rs:22:15 | LL | let _ = s.is_emtpy(); | ^^^^^^^^ help: there is a method with a similar name: `is_empty` error[E0599]: no method named `count_eos` found for type `u32` in the current scope - --> $DIR/suggest-methods.rs:25:19 + --> $DIR/suggest-methods.rs:27:19 | LL | let _ = 63u32.count_eos(); | ^^^^^^^^^ help: there is a method with a similar name: `count_zeros` error[E0599]: no method named `count_o` found for type `u32` in the current scope - --> $DIR/suggest-methods.rs:28:19 + --> $DIR/suggest-methods.rs:30:19 | LL | let _ = 63u32.count_o(); | ^^^^^^^ help: there is a method with a similar name: `count_ones` diff --git a/tests/ui/suggestions/suggest-tryinto-edition-change.rs b/tests/ui/suggestions/suggest-tryinto-edition-change.rs index c4a24ffee93..37321774874 100644 --- a/tests/ui/suggestions/suggest-tryinto-edition-change.rs +++ b/tests/ui/suggestions/suggest-tryinto-edition-change.rs @@ -5,7 +5,6 @@ fn test() { let _i: i16 = 0_i32.try_into().unwrap(); //~^ ERROR no method named `try_into` found for type `i32` in the current scope - //~| NOTE method not found in `i32` //~| NOTE 'std::convert::TryInto' is included in the prelude starting in Edition 2021 let _i: i16 = TryFrom::try_from(0_i32).unwrap(); diff --git a/tests/ui/suggestions/suggest-tryinto-edition-change.stderr b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr index 057e37dbe10..01fabbadbff 100644 --- a/tests/ui/suggestions/suggest-tryinto-edition-change.stderr +++ b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr @@ -1,5 +1,5 @@ error[E0433]: failed to resolve: use of undeclared type `TryFrom` - --> $DIR/suggest-tryinto-edition-change.rs:11:19 + --> $DIR/suggest-tryinto-edition-change.rs:10:19 | LL | let _i: i16 = TryFrom::try_from(0_i32).unwrap(); | ^^^^^^^ use of undeclared type `TryFrom` @@ -14,7 +14,7 @@ LL + use std::convert::TryFrom; | error[E0433]: failed to resolve: use of undeclared type `TryInto` - --> $DIR/suggest-tryinto-edition-change.rs:17:19 + --> $DIR/suggest-tryinto-edition-change.rs:16:19 | LL | let _i: i16 = TryInto::try_into(0_i32).unwrap(); | ^^^^^^^ use of undeclared type `TryInto` @@ -29,7 +29,7 @@ LL + use std::convert::TryInto; | error[E0433]: failed to resolve: use of undeclared type `FromIterator` - --> $DIR/suggest-tryinto-edition-change.rs:23:22 + --> $DIR/suggest-tryinto-edition-change.rs:22:22 | LL | let _v: Vec<_> = FromIterator::from_iter(&[1]); | ^^^^^^^^^^^^ use of undeclared type `FromIterator` @@ -51,7 +51,7 @@ error[E0599]: no method named `try_into` found for type `i32` in the current sco --> $DIR/suggest-tryinto-edition-change.rs:6:25 | LL | let _i: i16 = 0_i32.try_into().unwrap(); - | ^^^^^^^^ method not found in `i32` + | ^^^^^^^^ --> $SRC_DIR/core/src/convert/mod.rs:LL:COL | = note: the method is available for `i32` here @@ -62,6 +62,10 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f | LL + use std::convert::TryInto; | +help: there is a method with a similar name + | +LL | let _i: i16 = 0_i32.into().unwrap(); + | ~~~~ error: aborting due to 4 previous errors diff --git a/tests/ui/traits/issue-117794.stderr b/tests/ui/traits/issue-117794.stderr index af63b47f07d..66561ffa4ce 100644 --- a/tests/ui/traits/issue-117794.stderr +++ b/tests/ui/traits/issue-117794.stderr @@ -2,7 +2,13 @@ error[E0599]: no method named `b` found for reference `&Self` in the current sco --> $DIR/issue-117794.rs:5:14 | LL | self.b(|| 0) - | ^ help: there is a method with a similar name: `a` + | ^ + | +help: there is a method `a` with a similar name, but with different arguments + --> $DIR/issue-117794.rs:4:5 + | +LL | fn a(&self) -> impl Foo { + | ^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/traits/item-privacy.stderr b/tests/ui/traits/item-privacy.stderr index 244cc2fc592..ede1764efdc 100644 --- a/tests/ui/traits/item-privacy.stderr +++ b/tests/ui/traits/item-privacy.stderr @@ -5,13 +5,17 @@ LL | struct S; | -------- method `a` not found for this struct ... LL | S.a(); - | ^ method not found in `S` + | ^ | = help: items from traits can only be used if the trait is implemented and in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it: | LL + use method::A; | +help: there is a method with a similar name + | +LL | S.b(); + | ~ error[E0599]: no method named `b` found for struct `S` in the current scope --> $DIR/item-privacy.rs:68:7 @@ -23,13 +27,17 @@ LL | fn b(&self) { } | - the method is available for `S` here ... LL | S.b(); - | ^ method not found in `S` + | ^ | = help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it: | LL + use method::B; | +help: there is a method with a similar name + | +LL | S.c(); + | ~ error[E0624]: method `a` is private --> $DIR/item-privacy.rs:72:7 @@ -54,6 +62,10 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f | LL + use method::A; | +help: there is an associated constant with a similar name + | +LL | S::B(&S); + | ~ error[E0599]: no function or associated item named `b` found for struct `S` in the current scope --> $DIR/item-privacy.rs:80:8 @@ -69,6 +81,10 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f | LL + use method::B; | +help: there is an associated constant with a similar name + | +LL | S::B(&S); + | ~ error[E0624]: method `a` is private --> $DIR/item-privacy.rs:84:14 @@ -93,6 +109,10 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f | LL + use assoc_const::A; | +help: there is an associated constant with a similar name + | +LL | S::B; + | ~ error[E0599]: no associated item named `B` found for struct `S` in the current scope --> $DIR/item-privacy.rs:98:8 @@ -108,6 +128,10 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f | LL + use assoc_const::B; | +help: there is a method with a similar name + | +LL | S::b; + | ~ error[E0624]: associated constant `A` is private --> $DIR/item-privacy.rs:101:14 From e1e4da2b0ad6a052e8ec4460d65db95c81d6314b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 10 Feb 2024 03:37:08 +0000 Subject: [PATCH 06/15] Make confusable suggestions `verbose` --- .../rustc_hir_typeck/src/method/suggest.rs | 6 ++-- .../associated-item-enum.stderr | 30 +++++++++++-------- tests/ui/attributes/rustc_confusables.stderr | 7 ++++- tests/ui/block-result/issue-3563.stderr | 7 ++++- tests/ui/methods/issues/issue-105732.stderr | 7 ++++- .../method-not-found-but-doc-alias.stderr | 7 ++++- tests/ui/object-pointer-types.stderr | 7 ++++- tests/ui/parser/emoji-identifiers.stderr | 9 +++--- .../typo-suggestion-mistyped-in-path.stderr | 10 ++++--- .../rust-2018/trait-import-suggestions.stderr | 7 ++++- .../arbitrary_self_type_mut_difference.stderr | 12 ++++++-- tests/ui/suggestions/issue-109291.stderr | 9 +++--- tests/ui/suggestions/suggest-methods.stderr | 21 +++++++++++-- .../trait-upcasting/subtrait-method.stderr | 30 +++++++++++++++---- 14 files changed, 126 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 735a1b17d7e..202df8dcc12 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1375,7 +1375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { // We found a method with the same number of arguments as the method // call expression the user wrote. - err.span_suggestion( + err.span_suggestion_verbose( span, format!("there is {an} method with a similar name"), similar_candidate.name, @@ -1402,7 +1402,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { // We have fn call expression and the argument count match the associated // function we found. - err.span_suggestion( + err.span_suggestion_verbose( span, format!( "there is {an} {} with a similar name", @@ -1423,7 +1423,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } else if let Mode::Path = mode { // We have an associated item syntax and we found something that isn't an fn. - err.span_suggestion( + err.span_suggestion_verbose( span, format!( "there is {an} {} with a similar name", diff --git a/tests/ui/associated-item/associated-item-enum.stderr b/tests/ui/associated-item/associated-item-enum.stderr index 9e9f5c0daad..a966468e3dd 100644 --- a/tests/ui/associated-item/associated-item-enum.stderr +++ b/tests/ui/associated-item/associated-item-enum.stderr @@ -5,10 +5,12 @@ LL | enum Enum { Variant } | --------- variant or associated item `mispellable` not found for this enum ... LL | Enum::mispellable(); - | ^^^^^^^^^^^ - | | - | variant or associated item not found in `Enum` - | help: there is an associated function with a similar name: `misspellable` + | ^^^^^^^^^^^ variant or associated item not found in `Enum` + | +help: there is an associated function with a similar name + | +LL | Enum::misspellable(); + | ~~~~~~~~~~~~ error[E0599]: no variant or associated item named `mispellable_trait` found for enum `Enum` in the current scope --> $DIR/associated-item-enum.rs:18:11 @@ -17,10 +19,12 @@ LL | enum Enum { Variant } | --------- variant or associated item `mispellable_trait` not found for this enum ... LL | Enum::mispellable_trait(); - | ^^^^^^^^^^^^^^^^^ - | | - | variant or associated item not found in `Enum` - | help: there is an associated function with a similar name: `misspellable_trait` + | ^^^^^^^^^^^^^^^^^ variant or associated item not found in `Enum` + | +help: there is an associated function with a similar name + | +LL | Enum::misspellable_trait(); + | ~~~~~~~~~~~~~~~~~~ error[E0599]: no variant or associated item named `MISPELLABLE` found for enum `Enum` in the current scope --> $DIR/associated-item-enum.rs:19:11 @@ -29,10 +33,12 @@ LL | enum Enum { Variant } | --------- variant or associated item `MISPELLABLE` not found for this enum ... LL | Enum::MISPELLABLE; - | ^^^^^^^^^^^ - | | - | variant or associated item not found in `Enum` - | help: there is an associated constant with a similar name: `MISSPELLABLE` + | ^^^^^^^^^^^ variant or associated item not found in `Enum` + | +help: there is an associated constant with a similar name + | +LL | Enum::MISSPELLABLE; + | ~~~~~~~~~~~~ error: aborting due to 3 previous errors diff --git a/tests/ui/attributes/rustc_confusables.stderr b/tests/ui/attributes/rustc_confusables.stderr index 93c31fd5a34..60dc0e396ba 100644 --- a/tests/ui/attributes/rustc_confusables.stderr +++ b/tests/ui/attributes/rustc_confusables.stderr @@ -31,7 +31,12 @@ error[E0599]: no method named `inser` found for struct `rustc_confusables_across --> $DIR/rustc_confusables.rs:12:7 | LL | x.inser(); - | ^^^^^ help: there is a method with a similar name: `insert` + | ^^^^^ + | +help: there is a method with a similar name + | +LL | x.insert(); + | ~~~~~~ error[E0599]: no method named `foo` found for struct `rustc_confusables_across_crate::BTreeSet` in the current scope --> $DIR/rustc_confusables.rs:15:7 diff --git a/tests/ui/block-result/issue-3563.stderr b/tests/ui/block-result/issue-3563.stderr index c473a84413e..3381ae5f657 100644 --- a/tests/ui/block-result/issue-3563.stderr +++ b/tests/ui/block-result/issue-3563.stderr @@ -2,7 +2,12 @@ error[E0599]: no method named `b` found for reference `&Self` in the current sco --> $DIR/issue-3563.rs:3:17 | LL | || self.b() - | ^ help: there is a method with a similar name: `a` + | ^ + | +help: there is a method with a similar name + | +LL | || self.a() + | ~ error: aborting due to 1 previous error diff --git a/tests/ui/methods/issues/issue-105732.stderr b/tests/ui/methods/issues/issue-105732.stderr index 19ccd2de685..906bd6c8636 100644 --- a/tests/ui/methods/issues/issue-105732.stderr +++ b/tests/ui/methods/issues/issue-105732.stderr @@ -10,7 +10,12 @@ error[E0599]: no method named `g` found for reference `&Self` in the current sco --> $DIR/issue-105732.rs:10:14 | LL | self.g(); - | ^ help: there is a method with a similar name: `f` + | ^ + | +help: there is a method with a similar name + | +LL | self.f(); + | ~ error: aborting due to 2 previous errors diff --git a/tests/ui/methods/method-not-found-but-doc-alias.stderr b/tests/ui/methods/method-not-found-but-doc-alias.stderr index 9746c404013..d8c2ea00137 100644 --- a/tests/ui/methods/method-not-found-but-doc-alias.stderr +++ b/tests/ui/methods/method-not-found-but-doc-alias.stderr @@ -5,7 +5,12 @@ LL | struct Foo; | ---------- method `quux` not found for this struct ... LL | Foo.quux(); - | ^^^^ help: there is a method with a similar name: `bar` + | ^^^^ + | +help: there is a method with a similar name + | +LL | Foo.bar(); + | ~~~ error: aborting due to 1 previous error diff --git a/tests/ui/object-pointer-types.stderr b/tests/ui/object-pointer-types.stderr index fe21bf3aede..e581d2d40bd 100644 --- a/tests/ui/object-pointer-types.stderr +++ b/tests/ui/object-pointer-types.stderr @@ -5,7 +5,12 @@ LL | fn owned(self: Box); | --------- the method might not be found because of this arbitrary self type ... LL | x.owned(); - | ^^^^^ help: there is a method with a similar name: `to_owned` + | ^^^^^ + | +help: there is a method with a similar name + | +LL | x.to_owned(); + | ~~~~~~~~ error[E0599]: no method named `owned` found for mutable reference `&mut dyn Foo` in the current scope --> $DIR/object-pointer-types.rs:17:7 diff --git a/tests/ui/parser/emoji-identifiers.stderr b/tests/ui/parser/emoji-identifiers.stderr index 1fc561d32c6..cd306696dde 100644 --- a/tests/ui/parser/emoji-identifiers.stderr +++ b/tests/ui/parser/emoji-identifiers.stderr @@ -71,16 +71,17 @@ LL | struct 👀; | --------- function or associated item `full_of✨` not found for this struct ... LL | 👀::full_of✨() - | ^^^^^^^^^ - | | - | function or associated item not found in `👀` - | help: there is an associated function with a similar name: `full_of_✨` + | ^^^^^^^^^ function or associated item not found in `👀` | note: if you're trying to build a new `👀`, consider using `👀::full_of_✨` which returns `👀` --> $DIR/emoji-identifiers.rs:4:5 | LL | fn full_of_✨() -> 👀 { | ^^^^^^^^^^^^^^^^^^^^^ +help: there is an associated function with a similar name + | +LL | 👀::full_of_✨() + | ~~~~~~~~~~ error[E0425]: cannot find function `i_like_to_😄_a_lot` in this scope --> $DIR/emoji-identifiers.rs:13:13 diff --git a/tests/ui/resolve/typo-suggestion-mistyped-in-path.stderr b/tests/ui/resolve/typo-suggestion-mistyped-in-path.stderr index 89b69e14099..b0061217500 100644 --- a/tests/ui/resolve/typo-suggestion-mistyped-in-path.stderr +++ b/tests/ui/resolve/typo-suggestion-mistyped-in-path.stderr @@ -14,10 +14,12 @@ LL | struct Struct; | ------------- function or associated item `fob` not found for this struct ... LL | Struct::fob(); - | ^^^ - | | - | function or associated item not found in `Struct` - | help: there is an associated function with a similar name: `foo` + | ^^^ function or associated item not found in `Struct` + | +help: there is an associated function with a similar name + | +LL | Struct::foo(); + | ~~~ error[E0433]: failed to resolve: use of undeclared type `Struc` --> $DIR/typo-suggestion-mistyped-in-path.rs:27:5 diff --git a/tests/ui/rust-2018/trait-import-suggestions.stderr b/tests/ui/rust-2018/trait-import-suggestions.stderr index a9d625df9ba..36cce0aab51 100644 --- a/tests/ui/rust-2018/trait-import-suggestions.stderr +++ b/tests/ui/rust-2018/trait-import-suggestions.stderr @@ -40,7 +40,12 @@ error[E0599]: no method named `baz` found for type `u32` in the current scope --> $DIR/trait-import-suggestions.rs:29:7 | LL | x.baz(); - | ^^^ help: there is a method with a similar name: `bar` + | ^^^ + | +help: there is a method with a similar name + | +LL | x.bar(); + | ~~~ error[E0599]: no function or associated item named `from_str` found for type `u32` in the current scope --> $DIR/trait-import-suggestions.rs:30:18 diff --git a/tests/ui/self/arbitrary_self_type_mut_difference.stderr b/tests/ui/self/arbitrary_self_type_mut_difference.stderr index a56d58694aa..2a7192a83f5 100644 --- a/tests/ui/self/arbitrary_self_type_mut_difference.stderr +++ b/tests/ui/self/arbitrary_self_type_mut_difference.stderr @@ -2,25 +2,33 @@ error[E0599]: no method named `x` found for struct `Pin<&S>` in the current scop --> $DIR/arbitrary_self_type_mut_difference.rs:11:18 | LL | Pin::new(&S).x(); - | ^ help: there is a method with a similar name: `y` + | ^ | note: method is available for `Pin<&mut S>` --> $DIR/arbitrary_self_type_mut_difference.rs:6:5 | LL | fn x(self: Pin<&mut Self>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: there is a method with a similar name + | +LL | Pin::new(&S).y(); + | ~ error[E0599]: no method named `y` found for struct `Pin<&mut S>` in the current scope --> $DIR/arbitrary_self_type_mut_difference.rs:12:22 | LL | Pin::new(&mut S).y(); - | ^ help: there is a method with a similar name: `x` + | ^ | note: method is available for `Pin<&S>` --> $DIR/arbitrary_self_type_mut_difference.rs:7:5 | LL | fn y(self: Pin<&Self>) {} | ^^^^^^^^^^^^^^^^^^^^^^ +help: there is a method with a similar name + | +LL | Pin::new(&mut S).x(); + | ~ error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/issue-109291.stderr b/tests/ui/suggestions/issue-109291.stderr index c787be4de7c..7a2821a069b 100644 --- a/tests/ui/suggestions/issue-109291.stderr +++ b/tests/ui/suggestions/issue-109291.stderr @@ -2,10 +2,7 @@ error[E0599]: no function or associated item named `forced_capture` found for st --> $DIR/issue-109291.rs:2:65 | LL | println!("Custom backtrace: {}", std::backtrace::Backtrace::forced_capture()); - | ^^^^^^^^^^^^^^ - | | - | function or associated item not found in `Backtrace` - | help: there is an associated function with a similar name: `force_capture` + | ^^^^^^^^^^^^^^ function or associated item not found in `Backtrace` | note: if you're trying to build a new `Backtrace` consider using one of the following associated functions: Backtrace::capture @@ -13,6 +10,10 @@ note: if you're trying to build a new `Backtrace` consider using one of the foll Backtrace::disabled Backtrace::create --> $SRC_DIR/std/src/backtrace.rs:LL:COL +help: there is an associated function with a similar name + | +LL | println!("Custom backtrace: {}", std::backtrace::Backtrace::force_capture()); + | ~~~~~~~~~~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/suggest-methods.stderr b/tests/ui/suggestions/suggest-methods.stderr index 293009f289b..5115a072426 100644 --- a/tests/ui/suggestions/suggest-methods.stderr +++ b/tests/ui/suggestions/suggest-methods.stderr @@ -17,19 +17,34 @@ error[E0599]: no method named `is_emtpy` found for struct `String` in the curren --> $DIR/suggest-methods.rs:22:15 | LL | let _ = s.is_emtpy(); - | ^^^^^^^^ help: there is a method with a similar name: `is_empty` + | ^^^^^^^^ + | +help: there is a method with a similar name + | +LL | let _ = s.is_empty(); + | ~~~~~~~~ error[E0599]: no method named `count_eos` found for type `u32` in the current scope --> $DIR/suggest-methods.rs:27:19 | LL | let _ = 63u32.count_eos(); - | ^^^^^^^^^ help: there is a method with a similar name: `count_zeros` + | ^^^^^^^^^ + | +help: there is a method with a similar name + | +LL | let _ = 63u32.count_zeros(); + | ~~~~~~~~~~~ error[E0599]: no method named `count_o` found for type `u32` in the current scope --> $DIR/suggest-methods.rs:30:19 | LL | let _ = 63u32.count_o(); - | ^^^^^^^ help: there is a method with a similar name: `count_ones` + | ^^^^^^^ + | +help: there is a method with a similar name + | +LL | let _ = 63u32.count_ones(); + | ~~~~~~~~~~ error: aborting due to 4 previous errors diff --git a/tests/ui/traits/trait-upcasting/subtrait-method.stderr b/tests/ui/traits/trait-upcasting/subtrait-method.stderr index 918159e845b..f3eb86c7681 100644 --- a/tests/ui/traits/trait-upcasting/subtrait-method.stderr +++ b/tests/ui/traits/trait-upcasting/subtrait-method.stderr @@ -2,7 +2,7 @@ error[E0599]: no method named `c` found for reference `&dyn Bar` in the current --> $DIR/subtrait-method.rs:55:9 | LL | bar.c(); - | ^ help: there is a method with a similar name: `a` + | ^ | = help: items from traits can only be used if the trait is implemented and in scope note: `Baz` defines an item `c`, perhaps you need to implement it @@ -10,12 +10,16 @@ note: `Baz` defines an item `c`, perhaps you need to implement it | LL | trait Baz: Bar { | ^^^^^^^^^^^^^^ +help: there is a method with a similar name + | +LL | bar.a(); + | ~ error[E0599]: no method named `b` found for reference `&dyn Foo` in the current scope --> $DIR/subtrait-method.rs:59:9 | LL | foo.b(); - | ^ help: there is a method with a similar name: `a` + | ^ | = help: items from traits can only be used if the trait is implemented and in scope note: `Bar` defines an item `b`, perhaps you need to implement it @@ -23,12 +27,16 @@ note: `Bar` defines an item `b`, perhaps you need to implement it | LL | trait Bar: Foo { | ^^^^^^^^^^^^^^ +help: there is a method with a similar name + | +LL | foo.a(); + | ~ error[E0599]: no method named `c` found for reference `&dyn Foo` in the current scope --> $DIR/subtrait-method.rs:61:9 | LL | foo.c(); - | ^ help: there is a method with a similar name: `a` + | ^ | = help: items from traits can only be used if the trait is implemented and in scope note: `Baz` defines an item `c`, perhaps you need to implement it @@ -36,12 +44,16 @@ note: `Baz` defines an item `c`, perhaps you need to implement it | LL | trait Baz: Bar { | ^^^^^^^^^^^^^^ +help: there is a method with a similar name + | +LL | foo.a(); + | ~ error[E0599]: no method named `b` found for reference `&dyn Foo` in the current scope --> $DIR/subtrait-method.rs:65:9 | LL | foo.b(); - | ^ help: there is a method with a similar name: `a` + | ^ | = help: items from traits can only be used if the trait is implemented and in scope note: `Bar` defines an item `b`, perhaps you need to implement it @@ -49,12 +61,16 @@ note: `Bar` defines an item `b`, perhaps you need to implement it | LL | trait Bar: Foo { | ^^^^^^^^^^^^^^ +help: there is a method with a similar name + | +LL | foo.a(); + | ~ error[E0599]: no method named `c` found for reference `&dyn Foo` in the current scope --> $DIR/subtrait-method.rs:67:9 | LL | foo.c(); - | ^ help: there is a method with a similar name: `a` + | ^ | = help: items from traits can only be used if the trait is implemented and in scope note: `Baz` defines an item `c`, perhaps you need to implement it @@ -62,6 +78,10 @@ note: `Baz` defines an item `c`, perhaps you need to implement it | LL | trait Baz: Bar { | ^^^^^^^^^^^^^^ +help: there is a method with a similar name + | +LL | foo.a(); + | ~ error: aborting due to 5 previous errors From 76885673d64c5da4657a2059c80390f06d76a112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 13 Feb 2024 22:07:39 +0000 Subject: [PATCH 07/15] fix test --- tests/ui/atomic-from-mut-not-available.stderr | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/ui/atomic-from-mut-not-available.stderr b/tests/ui/atomic-from-mut-not-available.stderr index a3edf189356..0f37784be78 100644 --- a/tests/ui/atomic-from-mut-not-available.stderr +++ b/tests/ui/atomic-from-mut-not-available.stderr @@ -7,6 +7,10 @@ LL | core::sync::atomic::AtomicU64::from_mut(&mut 0u64); note: if you're trying to build a new `AtomicU64`, consider using `AtomicU64::new` which returns `AtomicU64` --> $SRC_DIR/core/src/sync/atomic.rs:LL:COL = note: this error originates in the macro `atomic_int` (in Nightly builds, run with -Z macro-backtrace for more info) +help: there is an associated function with a similar name + | +LL | core::sync::atomic::AtomicU64::from(&mut 0u64); + | ~~~~ error: aborting due to 1 previous error From 14277ef20128751ce6cc1c8ebd48f988c6607f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 13 Feb 2024 22:28:46 +0000 Subject: [PATCH 08/15] Better account for associated const found for fn call expr --- .../rustc_hir_typeck/src/method/suggest.rs | 4 +++- tests/ui/traits/item-privacy.stderr | 18 ++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 202df8dcc12..56329123a8a 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1421,7 +1421,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); } - } else if let Mode::Path = mode { + } else if let Mode::Path = mode + && args.unwrap_or(&[]).is_empty() + { // We have an associated item syntax and we found something that isn't an fn. err.span_suggestion_verbose( span, diff --git a/tests/ui/traits/item-privacy.stderr b/tests/ui/traits/item-privacy.stderr index ede1764efdc..a9469952976 100644 --- a/tests/ui/traits/item-privacy.stderr +++ b/tests/ui/traits/item-privacy.stderr @@ -58,14 +58,15 @@ LL | S::a(&S); | ^ function or associated item not found in `S` | = help: items from traits can only be used if the trait is implemented and in scope +help: there is an associated constant `B` with a similar name + --> $DIR/item-privacy.rs:29:9 + | +LL | const B: u8 = 0; + | ^^^^^^^^^^^ help: the following trait is implemented but not in scope; perhaps add a `use` for it: | LL + use method::A; | -help: there is an associated constant with a similar name - | -LL | S::B(&S); - | ~ error[E0599]: no function or associated item named `b` found for struct `S` in the current scope --> $DIR/item-privacy.rs:80:8 @@ -77,14 +78,15 @@ LL | S::b(&S); | ^ function or associated item not found in `S` | = help: items from traits can only be used if the trait is in scope +help: there is an associated constant `B` with a similar name + --> $DIR/item-privacy.rs:29:9 + | +LL | const B: u8 = 0; + | ^^^^^^^^^^^ help: the following trait is implemented but not in scope; perhaps add a `use` for it: | LL + use method::B; | -help: there is an associated constant with a similar name - | -LL | S::B(&S); - | ~ error[E0624]: method `a` is private --> $DIR/item-privacy.rs:84:14 From caa216d2451e669a8efd1e2ef19b57fb010fde75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 13 Feb 2024 23:05:23 +0000 Subject: [PATCH 09/15] Tweak wording of "implemented trait isn't imported" suggestion --- .../rustc_hir_typeck/src/method/suggest.rs | 27 ++++++++++++++----- ...project-to-specializable-projection.stderr | 2 +- tests/ui/coherence/coherence_inherent.stderr | 2 +- .../ui/coherence/coherence_inherent_cc.stderr | 2 +- tests/ui/hygiene/no_implicit_prelude.stderr | 2 +- tests/ui/hygiene/trait_items.stderr | 2 +- .../no-method-suggested-traits.stderr | 12 ++++----- tests/ui/imports/overlapping_pub_trait.rs | 2 +- tests/ui/imports/overlapping_pub_trait.stderr | 2 +- tests/ui/imports/unnamed_pub_trait.rs | 2 +- tests/ui/imports/unnamed_pub_trait.stderr | 2 +- tests/ui/issues/issue-10465.stderr | 2 +- tests/ui/issues/issue-39175.stderr | 2 +- tests/ui/issues/issue-56175.stderr | 4 +-- .../rust-2018/trait-import-suggestions.stderr | 6 ++--- .../uniform-paths/issue-87932.stderr | 2 +- .../future-prelude-collision-shadow.stderr | 2 +- .../ui/shadowed/shadowed-trait-methods.stderr | 2 +- .../dont-wrap-ambiguous-receivers.rs | 2 +- .../dont-wrap-ambiguous-receivers.stderr | 2 +- .../import-trait-for-method-call.stderr | 2 +- .../suggest-tryinto-edition-change.stderr | 2 +- .../suggestions/use-placement-typeck.stderr | 2 +- tests/ui/traits/item-privacy.stderr | 12 ++++----- tests/ui/traits/method-private.stderr | 2 +- tests/ui/typeck/issue-43189.stderr | 2 +- tests/ui/underscore-imports/shadow.stderr | 2 +- 27 files changed, 59 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 56329123a8a..2196daa9878 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -275,7 +275,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .span_if_local(def_id) .unwrap_or_else(|| self.tcx.def_span(def_id)); err.span_label(sp, format!("private {kind} defined here")); - self.suggest_valid_traits(&mut err, out_of_scope_traits, true); + self.suggest_valid_traits(&mut err, item_name, out_of_scope_traits, true); err.emit(); } @@ -2890,6 +2890,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_valid_traits( &self, err: &mut DiagnosticBuilder<'_>, + item_name: Ident, valid_out_of_scope_traits: Vec, explain: bool, ) -> bool { @@ -2908,9 +2909,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help("items from traits can only be used if the trait is in scope"); } let msg = format!( - "the following {traits_are} implemented but not in scope; \ - perhaps add a `use` for {one_of_them}:", - traits_are = if candidates.len() == 1 { "trait is" } else { "traits are" }, + "{this_trait_is} implemented but not in scope; perhaps you want to import \ + {one_of_them}", + this_trait_is = if candidates.len() == 1 { + format!( + "trait `{}` which provides `{item_name}` is", + self.tcx.item_name(candidates[0]), + ) + } else { + format!("the following traits which provide `{item_name}` are") + }, one_of_them = if candidates.len() == 1 { "it" } else { "one of them" }, ); @@ -3118,7 +3126,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - if self.suggest_valid_traits(err, valid_out_of_scope_traits, true) { + if self.suggest_valid_traits(err, item_name, valid_out_of_scope_traits, true) { return; } @@ -3404,7 +3412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { [] => {} [trait_info] if trait_info.def_id.is_local() => { if impls_trait(trait_info.def_id) { - self.suggest_valid_traits(err, vec![trait_info.def_id], false); + self.suggest_valid_traits(err, item_name, vec![trait_info.def_id], false); } else { err.subdiagnostic( self.dcx(), @@ -3431,7 +3439,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); for (i, trait_info) in trait_infos.iter().enumerate() { if impls_trait(trait_info.def_id) { - self.suggest_valid_traits(err, vec![trait_info.def_id], false); + self.suggest_valid_traits( + err, + item_name, + vec![trait_info.def_id], + false, + ); } msg.push_str(&format!( "\ncandidate #{}: `{}`", diff --git a/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr b/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr index b7336485eb8..3d82f572a1a 100644 --- a/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr +++ b/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr @@ -30,7 +30,7 @@ LL | match fut.as_mut().poll(ctx) { = note: the method is available for `Pin<&mut impl Future>` here | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `Future` which provides `poll` is implemented but not in scope; perhaps you want to import it | LL + use std::future::Future; | diff --git a/tests/ui/coherence/coherence_inherent.stderr b/tests/ui/coherence/coherence_inherent.stderr index da8c03847ed..17b49279de7 100644 --- a/tests/ui/coherence/coherence_inherent.stderr +++ b/tests/ui/coherence/coherence_inherent.stderr @@ -5,7 +5,7 @@ LL | s.the_fn(); | ^^^^^^ method not found in `&TheStruct` | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `TheTrait` which provides `the_fn` is implemented but not in scope; perhaps you want to import it | LL + use Lib::TheTrait; | diff --git a/tests/ui/coherence/coherence_inherent_cc.stderr b/tests/ui/coherence/coherence_inherent_cc.stderr index d34f6fa213b..b3c1125d63e 100644 --- a/tests/ui/coherence/coherence_inherent_cc.stderr +++ b/tests/ui/coherence/coherence_inherent_cc.stderr @@ -5,7 +5,7 @@ LL | s.the_fn(); | ^^^^^^ method not found in `&TheStruct` | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `TheTrait` which provides `the_fn` is implemented but not in scope; perhaps you want to import it | LL + use coherence_inherent_cc_lib::TheTrait; | diff --git a/tests/ui/hygiene/no_implicit_prelude.stderr b/tests/ui/hygiene/no_implicit_prelude.stderr index 646863a884c..5de6e3db327 100644 --- a/tests/ui/hygiene/no_implicit_prelude.stderr +++ b/tests/ui/hygiene/no_implicit_prelude.stderr @@ -26,7 +26,7 @@ LL | ().clone() help: there is a method `clone_from` with a similar name, but with different arguments --> $SRC_DIR/core/src/clone.rs:LL:COL = note: this error originates in the macro `::bar::m` (in Nightly builds, run with -Z macro-backtrace for more info) -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `Clone` which provides `clone` is implemented but not in scope; perhaps you want to import it | LL + use std::clone::Clone; | diff --git a/tests/ui/hygiene/trait_items.stderr b/tests/ui/hygiene/trait_items.stderr index 016ee8f71f9..0e276bf69d6 100644 --- a/tests/ui/hygiene/trait_items.stderr +++ b/tests/ui/hygiene/trait_items.stderr @@ -12,7 +12,7 @@ LL | pub macro m() { ().f() } | = help: items from traits can only be used if the trait is in scope = note: this error originates in the macro `::baz::m` (in Nightly builds, run with -Z macro-backtrace for more info) -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `T` which provides `f` is implemented but not in scope; perhaps you want to import it | LL + use foo::T; | diff --git a/tests/ui/impl-trait/no-method-suggested-traits.stderr b/tests/ui/impl-trait/no-method-suggested-traits.stderr index cf1bf59ab20..b9a6a281b84 100644 --- a/tests/ui/impl-trait/no-method-suggested-traits.stderr +++ b/tests/ui/impl-trait/no-method-suggested-traits.stderr @@ -5,7 +5,7 @@ LL | 1u32.method(); | ^^^^^^ | = help: items from traits can only be used if the trait is in scope -help: the following traits are implemented but not in scope; perhaps add a `use` for one of them: +help: the following traits which provide `method` are implemented but not in scope; perhaps you want to import one of them | LL + use foo::Bar; | @@ -27,7 +27,7 @@ LL | std::rc::Rc::new(&mut Box::new(&1u32)).method(); | ^^^^^^ | = help: items from traits can only be used if the trait is in scope -help: the following traits are implemented but not in scope; perhaps add a `use` for one of them: +help: the following traits which provide `method` are implemented but not in scope; perhaps you want to import one of them | LL + use foo::Bar; | @@ -52,7 +52,7 @@ LL | 'a'.method(); | ^^^^^^ | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `Bar` which provides `method` is implemented but not in scope; perhaps you want to import it | LL + use foo::Bar; | @@ -68,7 +68,7 @@ LL | std::rc::Rc::new(&mut Box::new(&'a')).method(); | ^^^^^^ | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `Bar` which provides `method` is implemented but not in scope; perhaps you want to import it | LL + use foo::Bar; | @@ -89,7 +89,7 @@ LL | fn method(&self) {} | ------ the method is available for `i32` here | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `PubPub` which provides `method` is implemented but not in scope; perhaps you want to import it | LL + use no_method_suggested_traits::foo::PubPub; | @@ -105,7 +105,7 @@ LL | std::rc::Rc::new(&mut Box::new(&1i32)).method(); | ^^^^^^ | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `PubPub` which provides `method` is implemented but not in scope; perhaps you want to import it | LL + use no_method_suggested_traits::foo::PubPub; | diff --git a/tests/ui/imports/overlapping_pub_trait.rs b/tests/ui/imports/overlapping_pub_trait.rs index cde9f7d8edb..9a56b96adbf 100644 --- a/tests/ui/imports/overlapping_pub_trait.rs +++ b/tests/ui/imports/overlapping_pub_trait.rs @@ -4,7 +4,7 @@ * This crate declares two public paths, `m::Tr` and `prelude::_`. Make sure we prefer the former. */ extern crate overlapping_pub_trait_source; -//~^ HELP the following trait is implemented but not in scope; perhaps add a `use` for it: +//~^ HELP trait `Tr` which provides `method` is implemented but not in scope; perhaps you want to import it //~| SUGGESTION overlapping_pub_trait_source::m::Tr fn main() { diff --git a/tests/ui/imports/overlapping_pub_trait.stderr b/tests/ui/imports/overlapping_pub_trait.stderr index a82a4101ce0..51a8bec85b7 100644 --- a/tests/ui/imports/overlapping_pub_trait.stderr +++ b/tests/ui/imports/overlapping_pub_trait.stderr @@ -10,7 +10,7 @@ LL | pub trait Tr { fn method(&self); } | ------ the method is available for `S` here | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `Tr` which provides `method` is implemented but not in scope; perhaps you want to import it | LL + use overlapping_pub_trait_source::m::Tr; | diff --git a/tests/ui/imports/unnamed_pub_trait.rs b/tests/ui/imports/unnamed_pub_trait.rs index 09a01643c5e..1527dfef147 100644 --- a/tests/ui/imports/unnamed_pub_trait.rs +++ b/tests/ui/imports/unnamed_pub_trait.rs @@ -5,7 +5,7 @@ * importing it by name, and instead we suggest importing it by glob. */ extern crate unnamed_pub_trait_source; -//~^ HELP the following trait is implemented but not in scope; perhaps add a `use` for it: +//~^ HELP trait `Tr` which provides `method` is implemented but not in scope; perhaps you want to import it //~| SUGGESTION unnamed_pub_trait_source::prelude::*; // trait Tr fn main() { diff --git a/tests/ui/imports/unnamed_pub_trait.stderr b/tests/ui/imports/unnamed_pub_trait.stderr index 41772b8e694..7d6b7742981 100644 --- a/tests/ui/imports/unnamed_pub_trait.stderr +++ b/tests/ui/imports/unnamed_pub_trait.stderr @@ -10,7 +10,7 @@ LL | pub trait Tr { fn method(&self); } | ------ the method is available for `S` here | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `Tr` which provides `method` is implemented but not in scope; perhaps you want to import it | LL + use unnamed_pub_trait_source::prelude::*; // trait Tr | diff --git a/tests/ui/issues/issue-10465.stderr b/tests/ui/issues/issue-10465.stderr index c6bc0786af1..0f46ebe505a 100644 --- a/tests/ui/issues/issue-10465.stderr +++ b/tests/ui/issues/issue-10465.stderr @@ -5,7 +5,7 @@ LL | b.foo(); | ^^^ method not found in `&B` | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `A` which provides `foo` is implemented but not in scope; perhaps you want to import it | LL + use a::A; | diff --git a/tests/ui/issues/issue-39175.stderr b/tests/ui/issues/issue-39175.stderr index bdc9e422e86..bbe8badb652 100644 --- a/tests/ui/issues/issue-39175.stderr +++ b/tests/ui/issues/issue-39175.stderr @@ -7,7 +7,7 @@ LL | Command::new("echo").arg("hello").exec(); = help: items from traits can only be used if the trait is in scope help: there is a method `pre_exec` with a similar name, but with different arguments --> $SRC_DIR/std/src/os/unix/process.rs:LL:COL -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `CommandExt` which provides `exec` is implemented but not in scope; perhaps you want to import it | LL + use std::os::unix::process::CommandExt; | diff --git a/tests/ui/issues/issue-56175.stderr b/tests/ui/issues/issue-56175.stderr index d8c757c1583..882d4e99327 100644 --- a/tests/ui/issues/issue-56175.stderr +++ b/tests/ui/issues/issue-56175.stderr @@ -10,7 +10,7 @@ LL | fn trait_method(&self) { | ------------ the method is available for `FooStruct` here | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `Trait` which provides `trait_method` is implemented but not in scope; perhaps you want to import it | LL + use reexported_trait::Trait; | @@ -31,7 +31,7 @@ LL | fn trait_method_b(&self) { | -------------- the method is available for `FooStruct` here | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `TraitB` which provides `trait_method_b` is implemented but not in scope; perhaps you want to import it | LL + use reexported_trait::TraitBRename; | diff --git a/tests/ui/rust-2018/trait-import-suggestions.stderr b/tests/ui/rust-2018/trait-import-suggestions.stderr index 36cce0aab51..9d7b68041e2 100644 --- a/tests/ui/rust-2018/trait-import-suggestions.stderr +++ b/tests/ui/rust-2018/trait-import-suggestions.stderr @@ -8,7 +8,7 @@ LL | x.foobar(); | ^^^^^^ | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `Foobar` which provides `foobar` is implemented but not in scope; perhaps you want to import it | LL + use crate::foo::foobar::Foobar; | @@ -27,7 +27,7 @@ LL | x.bar(); | ^^^ | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `Bar` which provides `bar` is implemented but not in scope; perhaps you want to import it | LL + use crate::foo::Bar; | @@ -54,7 +54,7 @@ LL | let y = u32::from_str("33"); | ^^^^^^^^ function or associated item not found in `u32` | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `FromStr` which provides `from_str` is implemented but not in scope; perhaps you want to import it | LL + use std::str::FromStr; | diff --git a/tests/ui/rust-2018/uniform-paths/issue-87932.stderr b/tests/ui/rust-2018/uniform-paths/issue-87932.stderr index 4a874a834bb..3e6fc3ff2a5 100644 --- a/tests/ui/rust-2018/uniform-paths/issue-87932.stderr +++ b/tests/ui/rust-2018/uniform-paths/issue-87932.stderr @@ -8,7 +8,7 @@ LL | A::deserialize(); | ^^^^^^^^^^^ function or associated item not found in `A` | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `Deserialize` which provides `deserialize` is implemented but not in scope; perhaps you want to import it | LL + use ::deserialize::_a::Deserialize; | diff --git a/tests/ui/rust-2021/future-prelude-collision-shadow.stderr b/tests/ui/rust-2021/future-prelude-collision-shadow.stderr index 1ca5708e753..a8fcf43cd63 100644 --- a/tests/ui/rust-2021/future-prelude-collision-shadow.stderr +++ b/tests/ui/rust-2021/future-prelude-collision-shadow.stderr @@ -6,7 +6,7 @@ LL | let _: u32 = 3u8.try_into().unwrap(); | = help: items from traits can only be used if the trait is in scope = note: 'std::convert::TryInto' is included in the prelude starting in Edition 2021 -help: the following traits are implemented but not in scope; perhaps add a `use` for one of them: +help: the following traits which provide `try_into` are implemented but not in scope; perhaps you want to import one of them | LL + use crate::m::TryIntoU32; | diff --git a/tests/ui/shadowed/shadowed-trait-methods.stderr b/tests/ui/shadowed/shadowed-trait-methods.stderr index 0bcf32790bc..2c990fababf 100644 --- a/tests/ui/shadowed/shadowed-trait-methods.stderr +++ b/tests/ui/shadowed/shadowed-trait-methods.stderr @@ -8,7 +8,7 @@ LL | ().f() | ^ method not found in `()` | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `T` which provides `f` is implemented but not in scope; perhaps you want to import it | LL + use foo::T; | diff --git a/tests/ui/suggestions/dont-wrap-ambiguous-receivers.rs b/tests/ui/suggestions/dont-wrap-ambiguous-receivers.rs index baa2128eb8e..06aed9ac98f 100644 --- a/tests/ui/suggestions/dont-wrap-ambiguous-receivers.rs +++ b/tests/ui/suggestions/dont-wrap-ambiguous-receivers.rs @@ -1,5 +1,5 @@ mod banana { - //~^ HELP the following traits are implemented but not in scope + //~^ HELP the following traits which provide `pick` are implemented but not in scope pub struct Chaenomeles; pub trait Apple { diff --git a/tests/ui/suggestions/dont-wrap-ambiguous-receivers.stderr b/tests/ui/suggestions/dont-wrap-ambiguous-receivers.stderr index 974aedd13cf..41ca7d0f8ea 100644 --- a/tests/ui/suggestions/dont-wrap-ambiguous-receivers.stderr +++ b/tests/ui/suggestions/dont-wrap-ambiguous-receivers.stderr @@ -8,7 +8,7 @@ LL | banana::Chaenomeles.pick() | ^^^^ method not found in `Chaenomeles` | = help: items from traits can only be used if the trait is in scope -help: the following traits are implemented but not in scope; perhaps add a `use` for one of them: +help: the following traits which provide `pick` are implemented but not in scope; perhaps you want to import one of them | LL + use banana::Apple; | diff --git a/tests/ui/suggestions/import-trait-for-method-call.stderr b/tests/ui/suggestions/import-trait-for-method-call.stderr index 3f54daf136f..58b07fe7a42 100644 --- a/tests/ui/suggestions/import-trait-for-method-call.stderr +++ b/tests/ui/suggestions/import-trait-for-method-call.stderr @@ -8,7 +8,7 @@ LL | h.finish() = note: the method is available for `DefaultHasher` here | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `Hasher` which provides `finish` is implemented but not in scope; perhaps you want to import it | LL + use std::hash::Hasher; | diff --git a/tests/ui/suggestions/suggest-tryinto-edition-change.stderr b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr index 01fabbadbff..a25a3f44ad2 100644 --- a/tests/ui/suggestions/suggest-tryinto-edition-change.stderr +++ b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr @@ -58,7 +58,7 @@ LL | let _i: i16 = 0_i32.try_into().unwrap(); | = help: items from traits can only be used if the trait is in scope = note: 'std::convert::TryInto' is included in the prelude starting in Edition 2021 -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `TryInto` which provides `try_into` is implemented but not in scope; perhaps you want to import it | LL + use std::convert::TryInto; | diff --git a/tests/ui/suggestions/use-placement-typeck.stderr b/tests/ui/suggestions/use-placement-typeck.stderr index d8f2d58a248..dc2bd96bb21 100644 --- a/tests/ui/suggestions/use-placement-typeck.stderr +++ b/tests/ui/suggestions/use-placement-typeck.stderr @@ -11,7 +11,7 @@ LL | pub struct S; | ------------ method `abc` not found for this struct | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `Foo` which provides `abc` is implemented but not in scope; perhaps you want to import it | LL + use m::Foo; | diff --git a/tests/ui/traits/item-privacy.stderr b/tests/ui/traits/item-privacy.stderr index a9469952976..00d75b14227 100644 --- a/tests/ui/traits/item-privacy.stderr +++ b/tests/ui/traits/item-privacy.stderr @@ -8,7 +8,7 @@ LL | S.a(); | ^ | = help: items from traits can only be used if the trait is implemented and in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `A` which provides `a` is implemented but not in scope; perhaps you want to import it | LL + use method::A; | @@ -30,7 +30,7 @@ LL | S.b(); | ^ | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `B` which provides `b` is implemented but not in scope; perhaps you want to import it | LL + use method::B; | @@ -63,7 +63,7 @@ help: there is an associated constant `B` with a similar name | LL | const B: u8 = 0; | ^^^^^^^^^^^ -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `A` which provides `a` is implemented but not in scope; perhaps you want to import it | LL + use method::A; | @@ -83,7 +83,7 @@ help: there is an associated constant `B` with a similar name | LL | const B: u8 = 0; | ^^^^^^^^^^^ -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `B` which provides `b` is implemented but not in scope; perhaps you want to import it | LL + use method::B; | @@ -107,7 +107,7 @@ LL | S::A; | ^ associated item not found in `S` | = help: items from traits can only be used if the trait is implemented and in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `A` which provides `A` is implemented but not in scope; perhaps you want to import it | LL + use assoc_const::A; | @@ -126,7 +126,7 @@ LL | S::B; | ^ associated item not found in `S` | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `B` which provides `B` is implemented but not in scope; perhaps you want to import it | LL + use assoc_const::B; | diff --git a/tests/ui/traits/method-private.stderr b/tests/ui/traits/method-private.stderr index d19f0bc086b..274767331bd 100644 --- a/tests/ui/traits/method-private.stderr +++ b/tests/ui/traits/method-private.stderr @@ -8,7 +8,7 @@ LL | foo.method(); | ^^^^^^ private method | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `Bar` which provides `method` is implemented but not in scope; perhaps you want to import it | LL + use inner::Bar; | diff --git a/tests/ui/typeck/issue-43189.stderr b/tests/ui/typeck/issue-43189.stderr index 2e12651699d..8432cbeca2a 100644 --- a/tests/ui/typeck/issue-43189.stderr +++ b/tests/ui/typeck/issue-43189.stderr @@ -10,7 +10,7 @@ LL | fn a(&self) {} | - the method is available for `()` here | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `A` which provides `a` is implemented but not in scope; perhaps you want to import it | LL + use xcrate_issue_43189_b::xcrate_issue_43189_a::A; | diff --git a/tests/ui/underscore-imports/shadow.stderr b/tests/ui/underscore-imports/shadow.stderr index da263163892..4743d14dfb9 100644 --- a/tests/ui/underscore-imports/shadow.stderr +++ b/tests/ui/underscore-imports/shadow.stderr @@ -5,7 +5,7 @@ LL | x.deref(); | ^^^^^ method not found in `&()` | = help: items from traits can only be used if the trait is in scope -help: the following trait is implemented but not in scope; perhaps add a `use` for it: +help: trait `Deref` which provides `deref` is implemented but not in scope; perhaps you want to import it | LL + use std::ops::Deref; | From f566867ace85044ef80f73cf9c214876a5aa4e77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 14 Feb 2024 16:04:40 +0000 Subject: [PATCH 10/15] Add `flatmap`/`flat_map` -> `and_then` suggestions --- library/core/src/option.rs | 1 + library/core/src/result.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 0e5eb03239c..53c9ae498c0 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1403,6 +1403,7 @@ impl Option { #[doc(alias = "flatmap")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("flat_map", "flatmap")] pub fn and_then(self, f: F) -> Option where F: FnOnce(T) -> Option, diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 80f7fe75e82..6879ac03f70 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -1315,6 +1315,7 @@ impl Result { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("flat_map", "flatmap")] pub fn and_then Result>(self, op: F) -> Result { match self { Ok(t) => op(t), From b59bd7fd16d6dfd3d09d1d708325d39763ec0ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 14 Feb 2024 18:11:19 +0000 Subject: [PATCH 11/15] review comments: clean up --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 137 ++++++------- .../rustc_hir_typeck/src/method/suggest.rs | 189 ++++++++++-------- 2 files changed, 166 insertions(+), 160 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 0ca7a8bb47b..612f645d5d5 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -563,80 +563,73 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let suggest_confusable = |err: &mut Diagnostic| { - if let Some(call_name) = call_ident - && let Some(callee_ty) = callee_ty - { - let input_types: Vec> = provided_arg_tys.iter().map(|(ty, _)| *ty).collect(); - // Check for other methods in the following order - // - methods marked as `rustc_confusables` with the provided arguments - // - methods with the same argument type/count and short levenshtein distance - // - methods marked as `rustc_confusables` (done) - // - methods with short levenshtein distance + let call_name = call_ident?; + let callee_ty = callee_ty?; + let input_types: Vec> = provided_arg_tys.iter().map(|(ty, _)| *ty).collect(); + // Check for other methods in the following order + // - methods marked as `rustc_confusables` with the provided arguments + // - methods with the same argument type/count and short levenshtein distance + // - methods marked as `rustc_confusables` (done) + // - methods with short levenshtein distance - // Look for commonly confusable method names considering arguments. - self.confusable_method_name( - err, - callee_ty.peel_refs(), - call_name, - Some(input_types.clone()), - ) - .or_else(|| { - // Look for method names with short levenshtein distance, considering arguments. - if let Some((assoc, fn_sig)) = similar_assoc(call_name) - && fn_sig.inputs()[1..] - .iter() - .zip(input_types.iter()) - .all(|(expected, found)| self.can_coerce(*expected, *found)) - && fn_sig.inputs()[1..].len() == input_types.len() - { - err.span_suggestion_verbose( - call_name.span, - format!("you might have meant to use `{}`", assoc.name), - assoc.name, - Applicability::MaybeIncorrect, - ); - return Some(assoc.name); - } - None - }) - .or_else(|| { - // Look for commonly confusable method names disregarding arguments. - self.confusable_method_name(err, callee_ty.peel_refs(), call_name, None) - }) - .or_else(|| { - // Look for similarly named methods with levenshtein distance with the right - // number of arguments. - if let Some((assoc, fn_sig)) = similar_assoc(call_name) - && fn_sig.inputs()[1..].len() == input_types.len() - { - err.span_note( - tcx.def_span(assoc.def_id), - format!( - "there's is a method with similar name `{}`, but the arguments \ - don't match", - assoc.name, - ), - ); - return Some(assoc.name); - } - None - }) - .or_else(|| { - // Fallthrough: look for similarly named methods with levenshtein distance. - if let Some((assoc, _)) = similar_assoc(call_name) { - err.span_note( - tcx.def_span(assoc.def_id), - format!( - "there's is a method with similar name `{}`, but their argument \ - count doesn't match", - assoc.name, - ), - ); - return Some(assoc.name); - } - None - }); + // Look for commonly confusable method names considering arguments. + if let Some(name) = self.confusable_method_name( + err, + callee_ty.peel_refs(), + call_name, + Some(input_types.clone()), + ) { + return Some(name); } + // Look for method names with short levenshtein distance, considering arguments. + if let Some((assoc, fn_sig)) = similar_assoc(call_name) + && fn_sig.inputs()[1..] + .iter() + .zip(input_types.iter()) + .all(|(expected, found)| self.can_coerce(*expected, *found)) + && fn_sig.inputs()[1..].len() == input_types.len() + { + err.span_suggestion_verbose( + call_name.span, + format!("you might have meant to use `{}`", assoc.name), + assoc.name, + Applicability::MaybeIncorrect, + ); + return Some(assoc.name); + } + // Look for commonly confusable method names disregarding arguments. + if let Some(name) = + self.confusable_method_name(err, callee_ty.peel_refs(), call_name, None) + { + return Some(name); + } + // Look for similarly named methods with levenshtein distance with the right + // number of arguments. + if let Some((assoc, fn_sig)) = similar_assoc(call_name) + && fn_sig.inputs()[1..].len() == input_types.len() + { + err.span_note( + tcx.def_span(assoc.def_id), + format!( + "there's is a method with similar name `{}`, but the arguments don't match", + assoc.name, + ), + ); + return Some(assoc.name); + } + // Fallthrough: look for similarly named methods with levenshtein distance. + if let Some((assoc, _)) = similar_assoc(call_name) { + err.span_note( + tcx.def_span(assoc.def_id), + format!( + "there's is a method with similar name `{}`, but their argument count \ + doesn't match", + assoc.name, + ), + ); + return Some(assoc.name); + } + None }; // A "softer" version of the `demand_compatible`, which checks types without persisting them, // and treats error types differently diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 2196daa9878..ae40cb29ed6 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1358,94 +1358,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // ...or if we already suggested that name because of `rustc_confusable` annotation. && Some(similar_candidate.name) != confusable_suggested { - let def_kind = similar_candidate.kind.as_def_kind(); - let an = self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id); - // Methods are defined within the context of a struct and their first parameter - // is always `self`, which represents the instance of the struct the method is - // being called on Associated functions don’t take self as a parameter and they are - // not methods because they don’t have an instance of the struct to work with. - if def_kind == DefKind::AssocFn { - let ty_args = self.infcx.fresh_args_for_item(span, similar_candidate.def_id); - let fn_sig = tcx.fn_sig(similar_candidate.def_id).instantiate(tcx, ty_args); - let fn_sig = - self.instantiate_binder_with_fresh_vars(span, infer::FnCall, fn_sig); - if similar_candidate.fn_has_self_parameter { - if let Some(args) = args - && fn_sig.inputs()[1..].len() == args.len() - { - // We found a method with the same number of arguments as the method - // call expression the user wrote. - err.span_suggestion_verbose( - span, - format!("there is {an} method with a similar name"), - similar_candidate.name, - Applicability::MaybeIncorrect, - ); - } else { - // We found a method but either the expression is not a method call or - // the argument count didn't match. - err.span_help( - tcx.def_span(similar_candidate.def_id), - format!( - "there is {an} method `{}` with a similar name{}", - similar_candidate.name, - if let None = args { - "" - } else { - ", but with different arguments" - }, - ), - ); - } - } else if let Some(args) = args - && fn_sig.inputs().len() == args.len() - { - // We have fn call expression and the argument count match the associated - // function we found. - err.span_suggestion_verbose( - span, - format!( - "there is {an} {} with a similar name", - self.tcx.def_kind_descr(def_kind, similar_candidate.def_id) - ), - similar_candidate.name, - Applicability::MaybeIncorrect, - ); - } else { - err.span_help( - tcx.def_span(similar_candidate.def_id), - format!( - "there is {an} {} `{}` with a similar name", - self.tcx.def_kind_descr(def_kind, similar_candidate.def_id), - similar_candidate.name, - ), - ); - } - } else if let Mode::Path = mode - && args.unwrap_or(&[]).is_empty() - { - // We have an associated item syntax and we found something that isn't an fn. - err.span_suggestion_verbose( - span, - format!( - "there is {an} {} with a similar name", - self.tcx.def_kind_descr(def_kind, similar_candidate.def_id) - ), - similar_candidate.name, - Applicability::MaybeIncorrect, - ); - } else { - // The expression is a function or method call, but the item we found is an - // associated const or type. - err.span_help( - tcx.def_span(similar_candidate.def_id), - format!( - "there is {an} {} `{}` with a similar name", - self.tcx.def_kind_descr(def_kind, similar_candidate.def_id), - similar_candidate.name, - ), - ); - } + self.find_likely_intended_associated_item( + &mut err, + similar_candidate, + span, + args, + mode, + ); } } // If an appropriate error source is not found, check method chain for possible candiates @@ -1497,6 +1416,100 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(err) } + fn find_likely_intended_associated_item( + &self, + err: &mut Diagnostic, + similar_candidate: ty::AssocItem, + span: Span, + args: Option<&'tcx [hir::Expr<'tcx>]>, + mode: Mode, + ) { + let tcx = self.tcx; + let def_kind = similar_candidate.kind.as_def_kind(); + let an = self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id); + // Methods are defined within the context of a struct and their first parameter + // is always `self`, which represents the instance of the struct the method is + // being called on Associated functions don’t take self as a parameter and they are + // not methods because they don’t have an instance of the struct to work with. + if def_kind == DefKind::AssocFn { + let ty_args = self.infcx.fresh_args_for_item(span, similar_candidate.def_id); + let fn_sig = tcx.fn_sig(similar_candidate.def_id).instantiate(tcx, ty_args); + let fn_sig = self.instantiate_binder_with_fresh_vars(span, infer::FnCall, fn_sig); + if similar_candidate.fn_has_self_parameter { + if let Some(args) = args + && fn_sig.inputs()[1..].len() == args.len() + { + // We found a method with the same number of arguments as the method + // call expression the user wrote. + err.span_suggestion_verbose( + span, + format!("there is {an} method with a similar name"), + similar_candidate.name, + Applicability::MaybeIncorrect, + ); + } else { + // We found a method but either the expression is not a method call or + // the argument count didn't match. + err.span_help( + tcx.def_span(similar_candidate.def_id), + format!( + "there is {an} method `{}` with a similar name{}", + similar_candidate.name, + if let None = args { "" } else { ", but with different arguments" }, + ), + ); + } + } else if let Some(args) = args + && fn_sig.inputs().len() == args.len() + { + // We have fn call expression and the argument count match the associated + // function we found. + err.span_suggestion_verbose( + span, + format!( + "there is {an} {} with a similar name", + self.tcx.def_kind_descr(def_kind, similar_candidate.def_id) + ), + similar_candidate.name, + Applicability::MaybeIncorrect, + ); + } else { + err.span_help( + tcx.def_span(similar_candidate.def_id), + format!( + "there is {an} {} `{}` with a similar name", + self.tcx.def_kind_descr(def_kind, similar_candidate.def_id), + similar_candidate.name, + ), + ); + } + } else if let Mode::Path = mode + && args.unwrap_or(&[]).is_empty() + { + // We have an associated item syntax and we found something that isn't an fn. + err.span_suggestion_verbose( + span, + format!( + "there is {an} {} with a similar name", + self.tcx.def_kind_descr(def_kind, similar_candidate.def_id) + ), + similar_candidate.name, + Applicability::MaybeIncorrect, + ); + } else { + // The expression is a function or method call, but the item we found is an + // associated const or type. + err.span_help( + tcx.def_span(similar_candidate.def_id), + format!( + "there is {an} {} `{}` with a similar name", + self.tcx.def_kind_descr(def_kind, similar_candidate.def_id), + similar_candidate.name, + ), + ); + } + } + pub(crate) fn confusable_method_name( &self, err: &mut Diagnostic, From 28c028737d6d004c55f1bbb2e1201d6330657548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 14 Feb 2024 18:23:01 +0000 Subject: [PATCH 12/15] Deduplicate some logic and reword output --- .../rustc_hir_typeck/src/method/suggest.rs | 38 ++++++------------- .../associated-item-enum.stderr | 6 +-- tests/ui/attributes/rustc_confusables.rs | 2 +- tests/ui/attributes/rustc_confusables.stderr | 2 +- tests/ui/block-result/issue-3563.stderr | 2 +- .../issue-33784.stderr | 2 +- .../no-method-suggested-traits.stderr | 12 +++--- tests/ui/issues/issue-56175.stderr | 4 +- tests/ui/methods/issues/issue-105732.stderr | 2 +- .../method-not-found-but-doc-alias.stderr | 2 +- tests/ui/object-pointer-types.stderr | 2 +- tests/ui/parser/emoji-identifiers.stderr | 2 +- .../typo-suggestion-mistyped-in-path.stderr | 2 +- .../rust-2018/trait-import-suggestions.stderr | 8 ++-- .../future-prelude-collision-shadow.stderr | 2 +- .../arbitrary_self_type_mut_difference.stderr | 4 +- tests/ui/suggestions/issue-109291.stderr | 2 +- tests/ui/suggestions/suggest-methods.stderr | 6 +-- .../suggest-tryinto-edition-change.stderr | 2 +- tests/ui/traits/item-privacy.stderr | 8 ++-- .../trait-upcasting/subtrait-method.stderr | 10 ++--- 21 files changed, 52 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index ae40cb29ed6..aa6817002b8 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1427,6 +1427,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; let def_kind = similar_candidate.kind.as_def_kind(); let an = self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id); + let msg = format!( + "there is {an} {} `{}` with a similar name", + self.tcx.def_kind_descr(def_kind, similar_candidate.def_id), + similar_candidate.name, + ); // Methods are defined within the context of a struct and their first parameter // is always `self`, which represents the instance of the struct the method is // being called on Associated functions don’t take self as a parameter and they are @@ -1443,7 +1448,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // call expression the user wrote. err.span_suggestion_verbose( span, - format!("there is {an} method with a similar name"), + msg, similar_candidate.name, Applicability::MaybeIncorrect, ); @@ -1453,8 +1458,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_help( tcx.def_span(similar_candidate.def_id), format!( - "there is {an} method `{}` with a similar name{}", - similar_candidate.name, + "{msg}{}", if let None = args { "" } else { ", but with different arguments" }, ), ); @@ -1466,22 +1470,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // function we found. err.span_suggestion_verbose( span, - format!( - "there is {an} {} with a similar name", - self.tcx.def_kind_descr(def_kind, similar_candidate.def_id) - ), + msg, similar_candidate.name, Applicability::MaybeIncorrect, ); } else { - err.span_help( - tcx.def_span(similar_candidate.def_id), - format!( - "there is {an} {} `{}` with a similar name", - self.tcx.def_kind_descr(def_kind, similar_candidate.def_id), - similar_candidate.name, - ), - ); + err.span_help(tcx.def_span(similar_candidate.def_id), msg); } } else if let Mode::Path = mode && args.unwrap_or(&[]).is_empty() @@ -1489,24 +1483,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We have an associated item syntax and we found something that isn't an fn. err.span_suggestion_verbose( span, - format!( - "there is {an} {} with a similar name", - self.tcx.def_kind_descr(def_kind, similar_candidate.def_id) - ), + msg, similar_candidate.name, Applicability::MaybeIncorrect, ); } else { // The expression is a function or method call, but the item we found is an // associated const or type. - err.span_help( - tcx.def_span(similar_candidate.def_id), - format!( - "there is {an} {} `{}` with a similar name", - self.tcx.def_kind_descr(def_kind, similar_candidate.def_id), - similar_candidate.name, - ), - ); + err.span_help(tcx.def_span(similar_candidate.def_id), msg); } } diff --git a/tests/ui/associated-item/associated-item-enum.stderr b/tests/ui/associated-item/associated-item-enum.stderr index a966468e3dd..3b00588f1d1 100644 --- a/tests/ui/associated-item/associated-item-enum.stderr +++ b/tests/ui/associated-item/associated-item-enum.stderr @@ -7,7 +7,7 @@ LL | enum Enum { Variant } LL | Enum::mispellable(); | ^^^^^^^^^^^ variant or associated item not found in `Enum` | -help: there is an associated function with a similar name +help: there is an associated function `misspellable` with a similar name | LL | Enum::misspellable(); | ~~~~~~~~~~~~ @@ -21,7 +21,7 @@ LL | enum Enum { Variant } LL | Enum::mispellable_trait(); | ^^^^^^^^^^^^^^^^^ variant or associated item not found in `Enum` | -help: there is an associated function with a similar name +help: there is an associated function `misspellable_trait` with a similar name | LL | Enum::misspellable_trait(); | ~~~~~~~~~~~~~~~~~~ @@ -35,7 +35,7 @@ LL | enum Enum { Variant } LL | Enum::MISPELLABLE; | ^^^^^^^^^^^ variant or associated item not found in `Enum` | -help: there is an associated constant with a similar name +help: there is an associated constant `MISSPELLABLE` with a similar name | LL | Enum::MISSPELLABLE; | ~~~~~~~~~~~~ diff --git a/tests/ui/attributes/rustc_confusables.rs b/tests/ui/attributes/rustc_confusables.rs index fed6c49ce6c..93d9a7d572c 100644 --- a/tests/ui/attributes/rustc_confusables.rs +++ b/tests/ui/attributes/rustc_confusables.rs @@ -11,7 +11,7 @@ fn main() { let x = BTreeSet {}; x.inser(); //~^ ERROR no method named - //~| HELP there is a method with a similar name + //~| HELP there is a method `insert` with a similar name x.foo(); //~^ ERROR no method named x.push(); diff --git a/tests/ui/attributes/rustc_confusables.stderr b/tests/ui/attributes/rustc_confusables.stderr index 60dc0e396ba..9e37d5f5083 100644 --- a/tests/ui/attributes/rustc_confusables.stderr +++ b/tests/ui/attributes/rustc_confusables.stderr @@ -33,7 +33,7 @@ error[E0599]: no method named `inser` found for struct `rustc_confusables_across LL | x.inser(); | ^^^^^ | -help: there is a method with a similar name +help: there is a method `insert` with a similar name | LL | x.insert(); | ~~~~~~ diff --git a/tests/ui/block-result/issue-3563.stderr b/tests/ui/block-result/issue-3563.stderr index 3381ae5f657..22606a52f85 100644 --- a/tests/ui/block-result/issue-3563.stderr +++ b/tests/ui/block-result/issue-3563.stderr @@ -4,7 +4,7 @@ error[E0599]: no method named `b` found for reference `&Self` in the current sco LL | || self.b() | ^ | -help: there is a method with a similar name +help: there is a method `a` with a similar name | LL | || self.a() | ~ diff --git a/tests/ui/confuse-field-and-method/issue-33784.stderr b/tests/ui/confuse-field-and-method/issue-33784.stderr index f6678dc8543..8acd1f8ff1e 100644 --- a/tests/ui/confuse-field-and-method/issue-33784.stderr +++ b/tests/ui/confuse-field-and-method/issue-33784.stderr @@ -8,7 +8,7 @@ help: to call the function stored in `closure`, surround the field access with p | LL | (p.closure)(); | + + -help: there is a method with a similar name +help: there is a method `clone` with a similar name | LL | p.clone(); | ~~~~~ diff --git a/tests/ui/impl-trait/no-method-suggested-traits.stderr b/tests/ui/impl-trait/no-method-suggested-traits.stderr index b9a6a281b84..7a4dc366618 100644 --- a/tests/ui/impl-trait/no-method-suggested-traits.stderr +++ b/tests/ui/impl-trait/no-method-suggested-traits.stderr @@ -15,7 +15,7 @@ LL + use no_method_suggested_traits::foo::PubPub; | LL + use no_method_suggested_traits::qux::PrivPub; | -help: there is a method with a similar name +help: there is a method `method2` with a similar name | LL | 1u32.method2(); | ~~~~~~~ @@ -37,7 +37,7 @@ LL + use no_method_suggested_traits::foo::PubPub; | LL + use no_method_suggested_traits::qux::PrivPub; | -help: there is a method with a similar name +help: there is a method `method2` with a similar name | LL | std::rc::Rc::new(&mut Box::new(&1u32)).method2(); | ~~~~~~~ @@ -56,7 +56,7 @@ help: trait `Bar` which provides `method` is implemented but not in scope; perha | LL + use foo::Bar; | -help: there is a method with a similar name +help: there is a method `method2` with a similar name | LL | 'a'.method2(); | ~~~~~~~ @@ -72,7 +72,7 @@ help: trait `Bar` which provides `method` is implemented but not in scope; perha | LL + use foo::Bar; | -help: there is a method with a similar name +help: there is a method `method2` with a similar name | LL | std::rc::Rc::new(&mut Box::new(&'a')).method2(); | ~~~~~~~ @@ -93,7 +93,7 @@ help: trait `PubPub` which provides `method` is implemented but not in scope; pe | LL + use no_method_suggested_traits::foo::PubPub; | -help: there is a method with a similar name +help: there is a method `method3` with a similar name | LL | 1i32.method3(); | ~~~~~~~ @@ -109,7 +109,7 @@ help: trait `PubPub` which provides `method` is implemented but not in scope; pe | LL + use no_method_suggested_traits::foo::PubPub; | -help: there is a method with a similar name +help: there is a method `method3` with a similar name | LL | std::rc::Rc::new(&mut Box::new(&1i32)).method3(); | ~~~~~~~ diff --git a/tests/ui/issues/issue-56175.stderr b/tests/ui/issues/issue-56175.stderr index 882d4e99327..6ed35c3a3d3 100644 --- a/tests/ui/issues/issue-56175.stderr +++ b/tests/ui/issues/issue-56175.stderr @@ -14,7 +14,7 @@ help: trait `Trait` which provides `trait_method` is implemented but not in scop | LL + use reexported_trait::Trait; | -help: there is a method with a similar name +help: there is a method `trait_method_b` with a similar name | LL | reexported_trait::FooStruct.trait_method_b(); | ~~~~~~~~~~~~~~ @@ -35,7 +35,7 @@ help: trait `TraitB` which provides `trait_method_b` is implemented but not in s | LL + use reexported_trait::TraitBRename; | -help: there is a method with a similar name +help: there is a method `trait_method` with a similar name | LL | reexported_trait::FooStruct.trait_method(); | ~~~~~~~~~~~~ diff --git a/tests/ui/methods/issues/issue-105732.stderr b/tests/ui/methods/issues/issue-105732.stderr index 906bd6c8636..a4924b3e663 100644 --- a/tests/ui/methods/issues/issue-105732.stderr +++ b/tests/ui/methods/issues/issue-105732.stderr @@ -12,7 +12,7 @@ error[E0599]: no method named `g` found for reference `&Self` in the current sco LL | self.g(); | ^ | -help: there is a method with a similar name +help: there is a method `f` with a similar name | LL | self.f(); | ~ diff --git a/tests/ui/methods/method-not-found-but-doc-alias.stderr b/tests/ui/methods/method-not-found-but-doc-alias.stderr index d8c2ea00137..c49ffa8971f 100644 --- a/tests/ui/methods/method-not-found-but-doc-alias.stderr +++ b/tests/ui/methods/method-not-found-but-doc-alias.stderr @@ -7,7 +7,7 @@ LL | struct Foo; LL | Foo.quux(); | ^^^^ | -help: there is a method with a similar name +help: there is a method `bar` with a similar name | LL | Foo.bar(); | ~~~ diff --git a/tests/ui/object-pointer-types.stderr b/tests/ui/object-pointer-types.stderr index e581d2d40bd..7d915ebdab6 100644 --- a/tests/ui/object-pointer-types.stderr +++ b/tests/ui/object-pointer-types.stderr @@ -7,7 +7,7 @@ LL | fn owned(self: Box); LL | x.owned(); | ^^^^^ | -help: there is a method with a similar name +help: there is a method `to_owned` with a similar name | LL | x.to_owned(); | ~~~~~~~~ diff --git a/tests/ui/parser/emoji-identifiers.stderr b/tests/ui/parser/emoji-identifiers.stderr index cd306696dde..536afc53f0c 100644 --- a/tests/ui/parser/emoji-identifiers.stderr +++ b/tests/ui/parser/emoji-identifiers.stderr @@ -78,7 +78,7 @@ note: if you're trying to build a new `👀`, consider using `👀::full_of_✨` | LL | fn full_of_✨() -> 👀 { | ^^^^^^^^^^^^^^^^^^^^^ -help: there is an associated function with a similar name +help: there is an associated function `full_of_✨` with a similar name | LL | 👀::full_of_✨() | ~~~~~~~~~~ diff --git a/tests/ui/resolve/typo-suggestion-mistyped-in-path.stderr b/tests/ui/resolve/typo-suggestion-mistyped-in-path.stderr index b0061217500..f4fb7fd955f 100644 --- a/tests/ui/resolve/typo-suggestion-mistyped-in-path.stderr +++ b/tests/ui/resolve/typo-suggestion-mistyped-in-path.stderr @@ -16,7 +16,7 @@ LL | struct Struct; LL | Struct::fob(); | ^^^ function or associated item not found in `Struct` | -help: there is an associated function with a similar name +help: there is an associated function `foo` with a similar name | LL | Struct::foo(); | ~~~ diff --git a/tests/ui/rust-2018/trait-import-suggestions.stderr b/tests/ui/rust-2018/trait-import-suggestions.stderr index 9d7b68041e2..85262888579 100644 --- a/tests/ui/rust-2018/trait-import-suggestions.stderr +++ b/tests/ui/rust-2018/trait-import-suggestions.stderr @@ -12,7 +12,7 @@ help: trait `Foobar` which provides `foobar` is implemented but not in scope; pe | LL + use crate::foo::foobar::Foobar; | -help: there is a method with a similar name +help: there is a method `bar` with a similar name | LL | x.bar(); | ~~~ @@ -31,7 +31,7 @@ help: trait `Bar` which provides `bar` is implemented but not in scope; perhaps | LL + use crate::foo::Bar; | -help: there is a method with a similar name +help: there is a method `foobar` with a similar name | LL | x.foobar(); | ~~~~~~ @@ -42,7 +42,7 @@ error[E0599]: no method named `baz` found for type `u32` in the current scope LL | x.baz(); | ^^^ | -help: there is a method with a similar name +help: there is a method `bar` with a similar name | LL | x.bar(); | ~~~ @@ -58,7 +58,7 @@ help: trait `FromStr` which provides `from_str` is implemented but not in scope; | LL + use std::str::FromStr; | -help: there is an associated function with a similar name +help: there is an associated function `from` with a similar name | LL | let y = u32::from("33"); | ~~~~ diff --git a/tests/ui/rust-2021/future-prelude-collision-shadow.stderr b/tests/ui/rust-2021/future-prelude-collision-shadow.stderr index a8fcf43cd63..d9c0fa47eca 100644 --- a/tests/ui/rust-2021/future-prelude-collision-shadow.stderr +++ b/tests/ui/rust-2021/future-prelude-collision-shadow.stderr @@ -12,7 +12,7 @@ LL + use crate::m::TryIntoU32; | LL + use std::convert::TryInto; | -help: there is a method with a similar name +help: there is a method `into` with a similar name | LL | let _: u32 = 3u8.into().unwrap(); | ~~~~ diff --git a/tests/ui/self/arbitrary_self_type_mut_difference.stderr b/tests/ui/self/arbitrary_self_type_mut_difference.stderr index 2a7192a83f5..ffc61ee0d78 100644 --- a/tests/ui/self/arbitrary_self_type_mut_difference.stderr +++ b/tests/ui/self/arbitrary_self_type_mut_difference.stderr @@ -9,7 +9,7 @@ note: method is available for `Pin<&mut S>` | LL | fn x(self: Pin<&mut Self>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: there is a method with a similar name +help: there is a method `y` with a similar name | LL | Pin::new(&S).y(); | ~ @@ -25,7 +25,7 @@ note: method is available for `Pin<&S>` | LL | fn y(self: Pin<&Self>) {} | ^^^^^^^^^^^^^^^^^^^^^^ -help: there is a method with a similar name +help: there is a method `x` with a similar name | LL | Pin::new(&mut S).x(); | ~ diff --git a/tests/ui/suggestions/issue-109291.stderr b/tests/ui/suggestions/issue-109291.stderr index 7a2821a069b..a173bbbb490 100644 --- a/tests/ui/suggestions/issue-109291.stderr +++ b/tests/ui/suggestions/issue-109291.stderr @@ -10,7 +10,7 @@ note: if you're trying to build a new `Backtrace` consider using one of the foll Backtrace::disabled Backtrace::create --> $SRC_DIR/std/src/backtrace.rs:LL:COL -help: there is an associated function with a similar name +help: there is an associated function `force_capture` with a similar name | LL | println!("Custom backtrace: {}", std::backtrace::Backtrace::force_capture()); | ~~~~~~~~~~~~~ diff --git a/tests/ui/suggestions/suggest-methods.stderr b/tests/ui/suggestions/suggest-methods.stderr index 5115a072426..5bacad8c6e8 100644 --- a/tests/ui/suggestions/suggest-methods.stderr +++ b/tests/ui/suggestions/suggest-methods.stderr @@ -19,7 +19,7 @@ error[E0599]: no method named `is_emtpy` found for struct `String` in the curren LL | let _ = s.is_emtpy(); | ^^^^^^^^ | -help: there is a method with a similar name +help: there is a method `is_empty` with a similar name | LL | let _ = s.is_empty(); | ~~~~~~~~ @@ -30,7 +30,7 @@ error[E0599]: no method named `count_eos` found for type `u32` in the current sc LL | let _ = 63u32.count_eos(); | ^^^^^^^^^ | -help: there is a method with a similar name +help: there is a method `count_zeros` with a similar name | LL | let _ = 63u32.count_zeros(); | ~~~~~~~~~~~ @@ -41,7 +41,7 @@ error[E0599]: no method named `count_o` found for type `u32` in the current scop LL | let _ = 63u32.count_o(); | ^^^^^^^ | -help: there is a method with a similar name +help: there is a method `count_ones` with a similar name | LL | let _ = 63u32.count_ones(); | ~~~~~~~~~~ diff --git a/tests/ui/suggestions/suggest-tryinto-edition-change.stderr b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr index a25a3f44ad2..db7c40101cd 100644 --- a/tests/ui/suggestions/suggest-tryinto-edition-change.stderr +++ b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr @@ -62,7 +62,7 @@ help: trait `TryInto` which provides `try_into` is implemented but not in scope; | LL + use std::convert::TryInto; | -help: there is a method with a similar name +help: there is a method `into` with a similar name | LL | let _i: i16 = 0_i32.into().unwrap(); | ~~~~ diff --git a/tests/ui/traits/item-privacy.stderr b/tests/ui/traits/item-privacy.stderr index 00d75b14227..d08bb4745bf 100644 --- a/tests/ui/traits/item-privacy.stderr +++ b/tests/ui/traits/item-privacy.stderr @@ -12,7 +12,7 @@ help: trait `A` which provides `a` is implemented but not in scope; perhaps you | LL + use method::A; | -help: there is a method with a similar name +help: there is a method `b` with a similar name | LL | S.b(); | ~ @@ -34,7 +34,7 @@ help: trait `B` which provides `b` is implemented but not in scope; perhaps you | LL + use method::B; | -help: there is a method with a similar name +help: there is a method `c` with a similar name | LL | S.c(); | ~ @@ -111,7 +111,7 @@ help: trait `A` which provides `A` is implemented but not in scope; perhaps you | LL + use assoc_const::A; | -help: there is an associated constant with a similar name +help: there is an associated constant `B` with a similar name | LL | S::B; | ~ @@ -130,7 +130,7 @@ help: trait `B` which provides `B` is implemented but not in scope; perhaps you | LL + use assoc_const::B; | -help: there is a method with a similar name +help: there is a method `b` with a similar name | LL | S::b; | ~ diff --git a/tests/ui/traits/trait-upcasting/subtrait-method.stderr b/tests/ui/traits/trait-upcasting/subtrait-method.stderr index f3eb86c7681..0408be6986b 100644 --- a/tests/ui/traits/trait-upcasting/subtrait-method.stderr +++ b/tests/ui/traits/trait-upcasting/subtrait-method.stderr @@ -10,7 +10,7 @@ note: `Baz` defines an item `c`, perhaps you need to implement it | LL | trait Baz: Bar { | ^^^^^^^^^^^^^^ -help: there is a method with a similar name +help: there is a method `a` with a similar name | LL | bar.a(); | ~ @@ -27,7 +27,7 @@ note: `Bar` defines an item `b`, perhaps you need to implement it | LL | trait Bar: Foo { | ^^^^^^^^^^^^^^ -help: there is a method with a similar name +help: there is a method `a` with a similar name | LL | foo.a(); | ~ @@ -44,7 +44,7 @@ note: `Baz` defines an item `c`, perhaps you need to implement it | LL | trait Baz: Bar { | ^^^^^^^^^^^^^^ -help: there is a method with a similar name +help: there is a method `a` with a similar name | LL | foo.a(); | ~ @@ -61,7 +61,7 @@ note: `Bar` defines an item `b`, perhaps you need to implement it | LL | trait Bar: Foo { | ^^^^^^^^^^^^^^ -help: there is a method with a similar name +help: there is a method `a` with a similar name | LL | foo.a(); | ~ @@ -78,7 +78,7 @@ note: `Baz` defines an item `c`, perhaps you need to implement it | LL | trait Baz: Bar { | ^^^^^^^^^^^^^^ -help: there is a method with a similar name +help: there is a method `a` with a similar name | LL | foo.a(); | ~ From fdaaaa1951c45dd66c12c8d78f3615260d13af30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 14 Feb 2024 19:20:56 +0000 Subject: [PATCH 13/15] fix test --- tests/ui/atomic-from-mut-not-available.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/atomic-from-mut-not-available.stderr b/tests/ui/atomic-from-mut-not-available.stderr index 0f37784be78..a4514524f48 100644 --- a/tests/ui/atomic-from-mut-not-available.stderr +++ b/tests/ui/atomic-from-mut-not-available.stderr @@ -7,7 +7,7 @@ LL | core::sync::atomic::AtomicU64::from_mut(&mut 0u64); note: if you're trying to build a new `AtomicU64`, consider using `AtomicU64::new` which returns `AtomicU64` --> $SRC_DIR/core/src/sync/atomic.rs:LL:COL = note: this error originates in the macro `atomic_int` (in Nightly builds, run with -Z macro-backtrace for more info) -help: there is an associated function with a similar name +help: there is an associated function `from` with a similar name | LL | core::sync::atomic::AtomicU64::from(&mut 0u64); | ~~~~ From d67dcf5a8bb14f6c83f5c86edea0c72835974aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 14 Feb 2024 19:27:53 +0000 Subject: [PATCH 14/15] review comment: remove unnused return value --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 612f645d5d5..3b0baee7180 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -563,8 +563,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let suggest_confusable = |err: &mut Diagnostic| { - let call_name = call_ident?; - let callee_ty = callee_ty?; + let Some(call_name) = call_ident else { + return; + }; + let Some(callee_ty) = callee_ty else { + return; + }; let input_types: Vec> = provided_arg_tys.iter().map(|(ty, _)| *ty).collect(); // Check for other methods in the following order // - methods marked as `rustc_confusables` with the provided arguments @@ -573,13 +577,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // - methods with short levenshtein distance // Look for commonly confusable method names considering arguments. - if let Some(name) = self.confusable_method_name( + if let Some(_name) = self.confusable_method_name( err, callee_ty.peel_refs(), call_name, Some(input_types.clone()), ) { - return Some(name); + return; } // Look for method names with short levenshtein distance, considering arguments. if let Some((assoc, fn_sig)) = similar_assoc(call_name) @@ -595,13 +599,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assoc.name, Applicability::MaybeIncorrect, ); - return Some(assoc.name); + return; } // Look for commonly confusable method names disregarding arguments. - if let Some(name) = + if let Some(_name) = self.confusable_method_name(err, callee_ty.peel_refs(), call_name, None) { - return Some(name); + return; } // Look for similarly named methods with levenshtein distance with the right // number of arguments. @@ -615,7 +619,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assoc.name, ), ); - return Some(assoc.name); + return; } // Fallthrough: look for similarly named methods with levenshtein distance. if let Some((assoc, _)) = similar_assoc(call_name) { @@ -627,9 +631,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assoc.name, ), ); - return Some(assoc.name); + return; } - None }; // A "softer" version of the `demand_compatible`, which checks types without persisting them, // and treats error types differently From 91d0b371ef2b7a6cdca5f723ba96b6fbfd08ca4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 22 Feb 2024 18:38:10 +0000 Subject: [PATCH 15/15] Fix rebase --- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 2 +- compiler/rustc_hir_typeck/src/method/suggest.rs | 4 ++-- tests/ui/impl-trait/where-allowed.stderr | 2 ++ tests/ui/ufcs/bad-builder.stderr | 9 +++++---- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 3b0baee7180..4bc86435482 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -562,7 +562,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None }; - let suggest_confusable = |err: &mut Diagnostic| { + let suggest_confusable = |err: &mut DiagnosticBuilder<'_>| { let Some(call_name) = call_ident else { return; }; diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index aa6817002b8..babdbce0005 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1418,7 +1418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn find_likely_intended_associated_item( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, similar_candidate: ty::AssocItem, span: Span, args: Option<&'tcx [hir::Expr<'tcx>]>, @@ -1496,7 +1496,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn confusable_method_name( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'_>, rcvr_ty: Ty<'tcx>, item_name: Ident, call_args: Option>>, diff --git a/tests/ui/impl-trait/where-allowed.stderr b/tests/ui/impl-trait/where-allowed.stderr index f203f4cabc8..c4bdd484fdb 100644 --- a/tests/ui/impl-trait/where-allowed.stderr +++ b/tests/ui/impl-trait/where-allowed.stderr @@ -395,6 +395,8 @@ error[E0599]: no function or associated item named `into_vec` found for slice `[ LL | vec![vec![0; 10], vec![12; 7], vec![8; 3]] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item not found in `[_]` | +help: there is an associated function `to_vec` with a similar name + --> $SRC_DIR/alloc/src/slice.rs:LL:COL = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0053]: method `in_trait_impl_return` has an incompatible type for trait diff --git a/tests/ui/ufcs/bad-builder.stderr b/tests/ui/ufcs/bad-builder.stderr index 7fa47c82de2..e1c5e45b3eb 100644 --- a/tests/ui/ufcs/bad-builder.stderr +++ b/tests/ui/ufcs/bad-builder.stderr @@ -2,10 +2,7 @@ error[E0599]: no function or associated item named `mew` found for struct `Vec $DIR/bad-builder.rs:2:15 | LL | Vec::::mew() - | ^^^ - | | - | function or associated item not found in `Vec` - | help: there is an associated function with a similar name: `new` + | ^^^ function or associated item not found in `Vec` | note: if you're trying to build a new `Vec` consider using one of the following associated functions: Vec::::new @@ -14,6 +11,10 @@ note: if you're trying to build a new `Vec` consider using one of the followi Vec::::new_in and 2 others --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL +help: there is an associated function `new` with a similar name + | +LL | Vec::::new() + | ~~~ error: aborting due to 1 previous error