Repository: rust
Issue: Suggest replacing Map with Set if it fixes errors
Labels: A-diagnostics,T-compiler

<!--
Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
along with any information you feel relevant to replicating the bug.

If you cannot produce a minimal reproduction case (something that would work in
isolation), please provide the steps or even link to a repository that causes
the problematic output to occur.
-->

The types `HashMap` and `HashSet` (and `BTreeMap` and `BTreeSet`) come up often enough that a user may confuse them. We could probably leverage this by marking them as "confusable types" and offer to swap one with the other if it helps fix errors.

For example, given the following code: [(playground link)](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d1f0012892dfb0722209dc2147f0c8af)

```rust
let _ = HashMap::from([1, 2]);
```

In this case, a user meant to create the set `{1, 2}`. However, they accidentally typed `HashMap` instead of `HashMap` and the code fails. Right now, we already offer a decent suggestion, which is that instead of providing an array of values, `HashMap` requires an array of key-value pairs:

```
error[[E0277]](https://doc.rust-lang.org/stable/error-index.html#E0277): the trait bound `HashMap<_, _, _>: From<[{integer}; 2]>` is not satisfied
 [--> src/main.rs:4:13
](https://play.rust-lang.org/#)  |
4 |     let _ = HashMap::from([1, 2]);
  |             ^^^^^^^^^^^^^ the trait `From<[{integer}; 2]>` is not implemented for `HashMap<_, _, _>`
  |
  = help: the following implementations were found:
            <HashMap<K, V> as From<[(K, V); N]>>
```

Hopefully, the user should realise their mistake now. However, we could do better and note that `HashMap` and `HashSet` are easily confused, and look for an eligible usage of `HashSet` instead. Here, we're in luck, because `HashSet` has the desired impl, and we should suggest that as well:

```diff
-let _ = HashMap::from([1, 2]);
+let _ = HashSet::from([1, 2]);
```

Now, that's one direction. The other direction is trickier, since sets can also contain key-value pairs, even though they won't actually be treated the same way. Take this code: [(playground link)](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c00aa4e10c3522468566ec706fb0f4cf)

```rust
let map = HashSet::from([(1, 2), (3, 4)]);
assert_eq!(map[1], 2);
```

In this case, we have no valid suggestion, and simply tell the user that they can't index `HashSet`s with integers:

```
error[[E0608]](https://doc.rust-lang.org/stable/error-index.html#E0608): cannot index into a value of type `HashSet<({integer}, {integer})>`
 [--> src/main.rs:5:16
](https://play.rust-lang.org/#)  |
5 |     assert_eq!(map[1], 2);
  |                ^^^^^^
```

What we could do here is notice that this is possible on `HashMap`s, and suggest to the user to replace their `HashSet` with a `HashMap`:

```diff
-let map = HashSet::from([(1, 2), (3, 4)]);
+let map = HashMap::from([(1, 2), (3, 4)]);
assert_eq!(map[1], 2);
```

One potential problem with this approach is that it could definitely slow down compilcation by a decent factor if we're checking for these on every single invalid invocation, and so it might make sense to limit scope initially, or find an alternative way of doing this. However, there are other cases besides map/set, like confusing arrays with `Vec`s: [(playground link)](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8720e2f46424e6f9c92db153ee5bbd69)

```rust
let _ = [1, 2, 3].capacity();
```