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
This commit is contained in:
Esteban Küber 2024-02-07 02:35:49 +00:00
parent 933a05bd0b
commit e5b3c7ef14
20 changed files with 180 additions and 29 deletions

View File

@ -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

View File

@ -607,6 +607,7 @@ impl<T: Ord, A: Allocator> BinaryHeap<T, A> {
/// 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<T, A: Allocator> BinaryHeap<T, A> {
/// ```
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_confusables("length", "size")]
pub fn len(&self) -> usize {
self.data.len()
}

View File

@ -979,6 +979,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
/// 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<V>
where
K: Ord,
@ -1041,6 +1042,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
/// assert_eq!(map.remove(&1), None);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_confusables("delete", "take")]
pub fn remove<Q: ?Sized>(&mut self, key: &Q) -> Option<V>
where
K: Borrow<Q> + Ord,
@ -2495,6 +2497,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
issue = "71835",
implied_by = "const_btree_new"
)]
#[rustc_confusables("length", "size")]
pub const fn len(&self) -> usize {
self.length
}

View File

@ -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
}

View File

@ -790,6 +790,7 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> {
/// ```
#[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<T, A: Allocator + Clone> BTreeSet<T, A> {
/// ```
#[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<T, A: Allocator + Clone> BTreeSet<T, A> {
/// 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<T, A: Allocator + Clone> BTreeSet<T, A> {
/// 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<T>
where
T: Ord,
@ -1152,6 +1156,7 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> {
issue = "71835",
implied_by = "const_btree_new"
)]
#[rustc_confusables("length", "size")]
pub const fn len(&self) -> usize {
self.map.len()
}

View File

@ -656,6 +656,7 @@ impl<T, A: Allocator> LinkedList<T, A> {
#[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<T, A: Allocator> LinkedList<T, A> {
#[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<T, A: Allocator> LinkedList<T, A> {
/// 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<T, A: Allocator> LinkedList<T, A> {
/// 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<T> {
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()
}

View File

@ -1209,6 +1209,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
/// 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<T, A: Allocator> VecDeque<T, A> {
/// 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<T, A: Allocator> VecDeque<T, A> {
/// 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<T, A: Allocator> VecDeque<T, A> {
/// 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<T, A: Allocator> VecDeque<T, A> {
/// assert_eq!(buf, [1, 3]);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_confusables("delete", "take")]
pub fn remove(&mut self, index: usize) -> Option<T> {
if self.len <= index {
return None;

View File

@ -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()
}

View File

@ -1554,6 +1554,7 @@ impl<T, A: Allocator> Vec<T, A> {
/// ```
#[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<T, A: Allocator> Vec<T, A> {
#[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<T, A: Allocator> Vec<T, A> {
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_confusables("length", "size")]
pub fn len(&self) -> usize {
self.len
}

View File

@ -471,6 +471,7 @@ impl<T> Cell<T> {
/// ```
#[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<T> RefCell<T> {
#[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)
}

View File

@ -25,6 +25,7 @@ impl<T: LambdaL> ScopedCell<T> {
/// 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: <T as ApplyL<'a>>::Out,

View File

@ -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<V> {
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<Q: ?Sized>(&mut self, k: &Q) -> Option<V>
where
K: Borrow<Q>,

View File

@ -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<T> {
self.base.replace(value)
}
@ -930,6 +932,7 @@ where
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_confusables("delete", "take")]
pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> bool
where
T: Borrow<Q>,

View File

@ -255,6 +255,7 @@ impl OsString {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
#[rustc_confusables("append", "put")]
pub fn push<T: AsRef<OsStr>>(&mut self, s: T) {
self.inner.push_slice(&s.as_ref().inner)
}

View File

@ -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<P: AsRef<Path>>(&mut self, path: P) {
self._push(path.as_ref())
}

View File

@ -435,6 +435,7 @@ impl<T: 'static> LocalKey<Cell<T>> {
/// 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<T: 'static> LocalKey<RefCell<T>> {
/// 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))
}

View File

@ -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;

View File

@ -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

View File

@ -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
}

View File

@ -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`.