# lints: Add map_lookup_then_insert lint \(\#62681\) · gitcafe/zed

[View on GitCafe](https://git.cafe/gitcafe/zed/commit/ca2b5741bc838ba14553e7e212aff01742c855a1)

Repository: [gitcafe/zed](https://git.cafe/gitcafe/zed)

Visibility: public

Requested revision: ca2b5741bc838ba14553e7e212aff01742c855a1

Requested commit: ca2b5741bc838ba14553e7e212aff01742c855a1

Commit: ca2b5741bc838ba14553e7e212aff01742c855a1

Tree: 64f4c51f18ec4537b11d55d40128ccaf8e1958c0

Author: Miguel Raz Guzmán Macedo

Committer: GitHub

## Message

````
lints: Add map_lookup_then_insert lint (#62681)

## Objective

Sometimes we can do a hash lookup twice, which is slow and unidiomatic.
We should use the Entry API instead.

## Solution

Add another `dylint` lint called `map_lookup_then_insert` that catches
those cases.

This works with HashMap, BTreeMap, HashSet, BTreeSet and
entry-compatible ADTs.

## What it catches

See the examples in the `tooling/lints/ui/map_lookup_then_insert.rs`.

It's mostly things of the form

```rust
if let Some(value) = map.get(&key) {
    *value
} else {
    map.insert(key, 1);
    1
}
```

which should be 

```rust
*map.entry(key).or_insert(1)
```

The two key expressions are matched up to borrows/derefs,
value-preserving
conversions (`clone`, `to_owned`, `to_string` on string types, `to_vec`
on
slices, `String::from` on `&str` — each resolved by definition, not by
name),
and immutable local aliases. So looking up `&key` and inserting
`key.clone()`
is caught too. When a conversion had to be peeled, the diagnostic
carries a
note that an entry-API rewrite would perform that copy on hits as well
as
misses.

## What it deliberately does not catch

Forms Clippy already catches.

## Findings

A `--workspace` run finds 31 warnings at 30 unique sites in `zed`:
the 16 the plain key matching finds, plus 15 more that require the
conversion/alias matching (e.g. `tiles_by_key.get(key)` followed by
`tiles_by_key.insert(key.clone(), tile)` in `metal_atlas.rs`).

Will post those fixes in a follow up PR.

## Testing

UI tests cover every `Entry` method positively and negatively, BTreeMap
mirrors, set cases, and the Clippy-deferral boundary. The
key-normalization logic is covered by nine positive cases (cloned keys,
UFCS forms, `to_owned`/`to_string`/`to_vec`/`String::from`, deref
layers, local aliases, sets) and six negative cases (inherent methods
merely named `clone`/`to_owned`, mutated aliases, a clone of a different
key, `to_string` on a non-string key, and side-effecting initializers).

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable


Release Notes:

- N/A
````

## Parents

- [aa347c3025c215cb22e187f1b98838807f432a4f](https://git.cafe/gitcafe/zed/commit/aa347c3025c215cb22e187f1b98838807f432a4f?format=markdown)

[Source at this commit](https://git.cafe/gitcafe/zed/tree/ca2b5741bc838ba14553e7e212aff01742c855a1?format=markdown)
