Skip to content

Commit

Permalink
Add rustc_confusables annotations to some stdlib APIs
Browse files Browse the repository at this point in the history
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();
   |       ~~~~~~
```

rust-lang#59450
  • Loading branch information
estebank committed Feb 12, 2024
1 parent bdc1592 commit df986a9
Show file tree
Hide file tree
Showing 20 changed files with 180 additions and 29 deletions.
59 changes: 32 additions & 27 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1113,32 +1113,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 {
Expand All @@ -1164,6 +1138,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() {
Expand Down Expand Up @@ -1265,7 +1267,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
Expand Down
2 changes: 2 additions & 0 deletions library/alloc/src/collections/binary_heap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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()
}
Expand Down
3 changes: 3 additions & 0 deletions library/alloc/src/collections/btree/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
}
Expand Down
3 changes: 3 additions & 0 deletions library/alloc/src/collections/btree/map/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down Expand Up @@ -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)
}
Expand All @@ -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
}
Expand Down
5 changes: 5 additions & 0 deletions library/alloc/src/collections/btree/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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()
}
Expand Down
10 changes: 10 additions & 0 deletions library/alloc/src/collections/linked_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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) }
}
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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()
}
Expand All @@ -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()
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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()
}
Expand All @@ -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()
}
Expand Down
5 changes: 5 additions & 0 deletions library/alloc/src/collections/vec_deque/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions library/alloc/src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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()
}
Expand Down
3 changes: 3 additions & 0 deletions library/alloc/src/vec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
}
Expand Down
1 change: 1 addition & 0 deletions library/proc_macro/src/bridge/scoped_cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,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,
Expand Down
2 changes: 2 additions & 0 deletions library/std/src/collections/hash/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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>,
Expand Down
Loading

0 comments on commit df986a9

Please sign in to comment.