lint_name_suggestions.md

· rfc1149's pastes · raw

expires: 2026-08-24

  1# Clippy lint name suggestions (rustc-style naming)
  2
  3This document proposes lint renames to better follow the Rust compiler's lint naming guidance.
  4
  5## Rustc lint naming guidance (key points)
  6
  7- Lint names should read well in attributes like `#[allow(<lint>)]`, ideally as **"allow \<lint\> items"**.
  8- Prefer names that describe the **bad thing** being checked (e.g. `improper_ctypes`, not `ctypes`).
  9- If the lint applies to a grammatical class, mention that class **in the plural** (e.g. `unused_variables`).
 10- Prefer **`unused_*`** when something exists but is never used.
 11- Use snake_case.
 12
 13## Terminology: `redundant_*` vs `unused_*`
 14
 15- **`unused_*`**: something exists but is not used (imports, variables, results, etc.).
 16- **`redundant_*`**: something is superfluous / no-op / adds no information and can be removed without changing meaning.
 17- Avoid subjective words like **needless / unneeded / useless** when a more precise term fits.
 18
 19## Naming principles for `manual_*` lints
 20
 21`manual_*` names describe what the fix looks like ("use the manual approach") rather than what the bad thing is. Prefer:
 22
 23- **`*_reimplementation`** — when the lint catches a handwritten reimplementation of a specific stdlib method.
 24- **describe the bad syntactic pattern** — when the lint catches a specific non-idiomatic construct.
 25
 26## Priority guide
 27
 28| Priority | Meaning |
 29|---|---|
 30| **High** | Current name is actively misleading or describes the wrong thing. |
 31| **Medium** | Current name is imprecise but understandable; improvement is meaningful. |
 32| **Low** | Mechanical word swap only (`needless`/`useless`/`unneeded``redundant`); correct direction, low urgency. |
 33
 34## Scope
 35
 36These suggestions were derived by scanning `clippy_lints/src/**` for `declare_clippy_lint!` (814 lints total),
 37flagging lints that contain `needless_`, `unneeded_`, `useless_`, or `manual_`, or whose name otherwise
 38describes the fix rather than the bad thing.
 39
 40---
 41
 42# Suggestions by category
 43
 44## Root (`clippy_lints/src/*.rs`)
 45
 46| priority | current lint | proposed lint | notes |
 47|---|---|---|---|
 48| High | `needless_for_each` | `for_each_instead_of_for_loop` | `.for_each()` is not redundant — it has an effect. The bad thing is preferring `.for_each()` over a `for` loop. |
 49| High | `needless_late_init` | `split_initialization` | The initialization is not redundant; the *splitting* of declaration and initialization is the bad pattern. `split_initialization` names it precisely. |
 50| High | `needless_pass_by_ref_mut` | `unused_ref_mut_arg` | The `mut` in `&mut` is never used. This fits `unused_*`, not `redundant_*`. |
 51| High | `needless_pass_by_value` | `owned_arg_without_mutation` | Taking ownership when a reference suffices is wasteful, not "redundant". The name should describe the mismatch. |
 52| High | `useless_let_if_seq` | `let_if_sequential_assignment` | The `let`+`if` sequence is not useless — it has an effect. The bad thing is using sequential assignment instead of `let x = if ...`. |
 53| High | `clear_with_drain` | `drain_instead_of_clear` | Current name reads as a recommendation ("clear *with* drain"). The bad thing is using `.drain(..)` where `.clear()` suffices. |
 54| High | `inherent_to_string` | `to_string_without_display` | The bad thing is defining `to_string()` without implementing `Display`, not merely that the impl is "inherent". |
 55| High | `create_dir` | `non_recursive_dir_creation` | Lint flags `fs::create_dir` where `create_dir_all` is safer; the bad thing is non-recursive directory creation. |
 56| High | `manual_assert` | `panic_instead_of_assert` | Flags `if cond { panic!(..) }` that should be `assert!`; the bad thing is using `panic!` where `assert!` belongs. |
 57| High | `manual_let_else` | `non_idiomatic_let_binding` | Flags match/if-let that could be `let-else`; the bad thing is a non-idiomatic let binding pattern. |
 58| High | `manual_non_exhaustive` | `non_exhaustive_via_private_field` | Flags using a private dummy field as a non-exhaustive trick; the bad thing is that pattern specifically. |
 59| High | `manual_retain` | `drain_instead_of_retain` | Flags collect-and-reassign instead of `.retain()`; the bad thing is using drain/collect where retain suffices. |
 60| Medium | `exit` | `process_exit_call` | `exit` gives no context; `process_exit_call` names what the bad thing is (calling `process::exit` directly). |
 61| Medium | `manual_assert_eq` | `assert_with_eq_comparison` | Flags `assert!(a == b)` where `assert_eq!(a, b)` is clearer; describes the bad assertion form. |
 62| Medium | `manual_async_fn` | `non_async_syntax_future_return` | Flags `fn -> impl Future` with an async block body; the bad thing is not using `async fn` syntax. |
 63| Medium | `manual_bits` | `size_of_bits_calculation` | Flags `size_of::<T>() * 8`; bad thing is manually computing bit width instead of `T::BITS`. |
 64| Medium | `manual_clamp` | `clamp_reimplementation` | Flags max/min/if-else patterns that reimplement `.clamp()`. |
 65| Medium | `manual_float_methods` | `float_classification_reimplementation` | Flags manual `is_finite`/`is_infinite` checks; bad thing is reimplementing stdlib float classification methods. |
 66| Medium | `manual_hash_one` | `hash_one_reimplementation` | Flags manual reimplementations of `BuildHasher::hash_one`. |
 67| Medium | `manual_ignore_case_cmp` | `eq_ignore_ascii_case_reimplementation` | Flags manual case-insensitive ASCII comparison instead of `.eq_ignore_ascii_case()`. |
 68| Medium | `manual_ilog2` | `ilog2_reimplementation` | Flags `N - x.leading_zeros()` patterns that reimplement `.ilog2()`. |
 69| Medium | `manual_is_ascii_check` | `ascii_range_check` | Flags `b >= b'a' && b <= b'z'` style checks instead of `.is_ascii_lowercase()` etc. |
 70| Medium | `manual_is_power_of_two` | `power_of_two_check` | Flags `x.count_ones() == 1` or `x & (x - 1) == 0` instead of `.is_power_of_two()`. |
 71| Medium | `manual_main_separator_str` | `main_separator_string_allocation` | Flags `MAIN_SEPARATOR.to_string()` allocating unnecessarily; `MAIN_SEPARATOR_STR` constant exists. |
 72| Medium | `manual_noop_waker` | `empty_wake_impl` | Flags manual empty `Wake` implementations instead of `Waker::noop()`. |
 73| Medium | `manual_option_as_slice` | `option_as_slice_reimplementation` | Flags manual reimplementations of `Option::as_slice`. |
 74| Medium | `manual_pop_if` | `peek_then_pop` | Flags peek + conditional pop pattern that could use `.pop_if()`. |
 75| Medium | `manual_range_patterns` | `or_literal_range_patterns` | Flags OR patterns over consecutive literals that could be a range pattern. |
 76| Medium | `manual_rem_euclid` | `rem_euclid_reimplementation` | Flags `((x % n) + n) % n` patterns reimplementing `.rem_euclid()`. |
 77| Medium | `manual_rotate` | `bit_rotation_reimplementation` | Flags manual bit-rotation expressions instead of `.rotate_left()`/`.rotate_right()`. |
 78| Medium | `manual_slice_size_calculation` | `slice_len_times_size_of` | Flags `slice.len() * size_of::<T>()` instead of `size_of_val(slice)`. |
 79| Medium | `manual_string_new` | `empty_string_to_string` | Flags `"".to_string()` / `"".to_owned()` instead of `String::new()`. |
 80| Medium | `manual_strip` | `starts_with_len_slice` | Flags `if s.starts_with(p) { &s[p.len()..] }` instead of `.strip_prefix(p)`. |
 81| Medium | `manual_take` | `take_via_if` | Flags manual reimplementations of `mem::take` via if-let. |
 82| Low | `needless_arbitrary_self_type` | `redundant_arbitrary_self_type` | `self: Self` is redundant syntax. |
 83| Low | `needless_bool` | `redundant_bool_comparison` | `if x { true } else { false }` or `x == true` — the comparison is a no-op. |
 84| Low | `needless_borrowed_reference` | `redundant_borrowed_reference` | `ref` in a pattern where it is not needed. |
 85| Low | `needless_borrows_for_generic_args` | `redundant_borrows_for_generic_args` | Borrows that coerce immediately add no information. |
 86| Low | `needless_continue` | `redundant_continue` | `continue` as the last statement of a loop body is a no-op. |
 87| Low | `needless_else` | `redundant_else` | `else` after a diverging arm (`return`/`break`/`continue`/`panic`) is a no-op. |
 88| Low | `needless_ifs` | `redundant_ifs` | Multiple `if` conditions mergeable with `&&`; the extra `if` is a no-op. |
 89| Low | `needless_maybe_sized` | `redundant_maybe_sized` | `?Sized` bound that is always `Sized` is a redundant constraint. |
 90| Low | `needless_parens_on_range_literals` | `redundant_parens_on_range_literals` | Parentheses around range literal endpoints are redundant. |
 91| Low | `needless_question_mark` | `redundant_question_mark` | `return Ok(x?)` — the `?` + Ok-wrapping is a no-op. |
 92| Low | `needless_update` | `redundant_struct_update` | Struct update `..Default::default()` when all fields are specified is a no-op. |
 93| Low | `unneeded_struct_pattern` | `redundant_struct_pattern` | Word swap: "unneeded" → "redundant". |
 94| Low | `useless_borrows_in_formatting` | `redundant_borrows_in_formatting` | Borrows in format args that are auto-deref'd are redundant. |
 95| Low | `useless_concat` | `redundant_concat` | Concatenating empty strings or adjacent literals is redundant. |
 96| Low | `useless_conversion` | `redundant_conversion` | Type conversions that are no-ops are redundant. |
 97| Low | `useless_format` | `redundant_format` | `format!("{}", s)` where `s.to_string()` suffices is redundant. |
 98| Low | `useless_vec` | `redundant_vec` | Allocating a `vec![]` where a slice/array suffices is redundant. |
 99
100## `attrs/`
101
102| priority | current lint | proposed lint | notes |
103|---|---|---|---|
104| Low | `useless_attribute` | `redundant_attribute` | Word swap: "useless" → "redundant". |
105
106## `methods/`
107
108| priority | current lint | proposed lint | notes |
109|---|---|---|---|
110| High | `bind_instead_of_map` | `and_then_wrapping_some` | Uses Haskell jargon ("bind" = `and_then`) opaque to many Rust programmers. The bad thing is `and_then(\|x\| Some(y))` — wrapping the closure result in `Some` inside `and_then` instead of using `.map()`. |
111| High | `manual_inspect` | `map_for_side_effects` | Flags `map` closures that return the original item unchanged (side-effect only). `manual_inspect` implies using `.inspect()` is "manual" which is backwards. |
112| High | `manual_is_variant_and` | `map_unwrap_or_default_for_bool` | Flags `option.map(f).unwrap_or_default()` where `f` returns bool; should use `is_some_and(f)`. |
113| Medium | `manual_c_str_literals` | `cstr_from_bytes_with_nul` | Flags constructing `CStr` via `from_bytes_with_nul`/`as_ptr` instead of `c""` literals. |
114| Medium | `manual_clear` | `truncate_zero` | Flags `.truncate(0)` where `.clear()` is clearer and often faster. |
115| Medium | `manual_contains` | `iter_any_on_slice` | Flags `slice.iter().any(|x| x == y)` instead of `slice.contains(&y)`. |
116| Medium | `manual_filter_map` | `filter_then_map_reimplementation` | Flags `_.filter(_).map(_)` that can be `.filter_map(_)`. |
117| Medium | `manual_find_map` | `find_then_map_reimplementation` | Flags `_.find(_).map(_)` that can be `.find_map(_)`. |
118| Medium | `manual_next_back` | `rev_next_instead_of_next_back` | Flags `.rev().next()` on a `DoubleEndedIterator`. |
119| Medium | `manual_ok_or` | `option_ok_or_reimplementation` | Flags patterns reimplementing `Option::ok_or`. |
120| Medium | `manual_option_zip` | `option_zip_reimplementation` | Flags `a.and_then(|a| b.map(|b| (a, b)))` that should be `a.zip(b)`. |
121| Medium | `manual_repeat_n` | `repeat_take` | Flags `repeat(x).take(n)` that can be `repeat_n(x, n)`. |
122| Medium | `manual_saturating_arithmetic` | `checked_add_unwrap_or_max` | Flags `.checked_add/sub(x).unwrap_or(MAX/MIN)` that can be `.saturating_add/sub(x)`. |
123| Medium | `manual_split_once` | `splitn_two` | Flags `str::splitn(2, _)` that can be `str::split_once(_)`. |
124| Medium | `manual_str_repeat` | `repeat_collect` | Flags manual reimplementations of `str::repeat` via collect. |
125| Medium | `manual_try_fold` | `fold_instead_of_try_fold` | Flags `Iterator::fold` with a `Try`-typed accumulator that should be `try_fold`. |
126| Low | `needless_as_bytes` | `redundant_as_bytes` | `.as_bytes()` call that is immediately coerced anyway. |
127| Low | `needless_character_iteration` | `redundant_character_iteration` | Character iteration that doesn't need char boundaries. |
128| Low | `needless_collect` | `redundant_collect` | Collecting into a temporary that is immediately consumed. |
129| Low | `needless_option_as_deref` | `redundant_option_as_deref` | `.as_deref()` on an `Option` that would deref anyway. |
130| Low | `needless_option_take` | `redundant_option_take` | `.take()` on an `Option` that is always `Some`. |
131| Low | `needless_to_owned` | `redundant_to_owned` | `.to_owned()` / `.to_string()` when borrowing suffices. |
132| Low | `useless_asref` | `redundant_asref` | `.as_ref()` call that is a no-op. |
133| Low | `useless_nonzero_new_unchecked` | `redundant_nonzero_new_unchecked` | `NonZero::new_unchecked(x)` where `x` is a known non-zero constant. |
134
135## `misc_early/`
136
137| priority | current lint | proposed lint | notes |
138|---|---|---|---|
139| Low | `unneeded_field_pattern` | `redundant_field_pattern` | Word swap: "unneeded" → "redundant". |
140| Low | `unneeded_wildcard_pattern` | `redundant_wildcard_pattern` | Word swap: "unneeded" → "redundant". |
141
142## `transmute/`
143
144| priority | current lint | proposed lint | notes |
145|---|---|---|---|
146| Low | `useless_transmute` | `redundant_transmute` | `transmute` where a safe cast suffices is a redundant `unsafe` operation. |
147
148---
149
150## Manual-* lint rename proposals (loops, matches, operators)
151
152### `loops/`
153
154| priority | current lint | proposed lint | rationale |
155|---|---|---|---|
156| High | `manual_while_let_some` | `is_empty_pop_loop` | Flags `while !v.is_empty() { let x = v.pop().unwrap(); }` — describes the bad loop pattern directly. |
157| Medium | `manual_find` | `iterator_find_reimplementation` | Flags for-loop patterns manually reimplementing `Iterator::find`. |
158| Medium | `manual_flatten` | `if_let_in_for_loop` | Flags unnecessary `if let` in a `for` loop that could use `.flatten()`. |
159| Medium | `manual_memcpy` | `slice_copy_reimplementation` | Flags for-loops manually copying slice elements instead of `.copy_from_slice()`. |
160| Medium | `manual_slice_fill` | `fill_in_loop` | Flags for-loops manually filling a slice with a value instead of `.fill()`. |
161
162### `matches/`
163
164| priority | current lint | proposed lint | rationale |
165|---|---|---|---|
166| Medium | `manual_filter` | `option_filter_reimplementation` | Flags `match` patterns reimplementing `Option::filter`. |
167| Medium | `manual_map` | `option_map_reimplementation` | Flags `match` patterns reimplementing `Option::map`. |
168| Medium | `manual_ok_err` | `match_instead_of_ok_err` | Flags manual `.ok()` or `.err()` via `match`. |
169| Medium | `manual_unwrap_or` | `match_instead_of_unwrap_or` | Flags `match` patterns reimplementing `Option::unwrap_or` / `Result::unwrap_or`. |
170| Medium | `manual_unwrap_or_default` | `match_instead_of_unwrap_or_default` | Flags `match`/`if let` that could be `.unwrap_or_default()`. |
171
172### `operators/`
173
174| priority | current lint | proposed lint | rationale |
175|---|---|---|---|
176| High | `manual_midpoint` | `midpoint_overflow` | Flags `(x + y) / 2` which can overflow; the bad thing is the overflow risk, not that midpoint is "manual". |
177| Medium | `manual_div_ceil` | `div_ceil_reimplementation` | Flags `(x + (y - 1)) / y` patterns reimplementing integer ceiling division. |
178| Medium | `manual_is_multiple_of` | `divisibility_check` | Flags `x % y == 0` instead of `.is_multiple_of(y)`. |
179
180### `casts/`
181
182| priority | current lint | proposed lint | rationale |
183|---|---|---|---|
184| Medium | `manual_dangling_ptr` | `dangling_ptr_from_int_cast` | Flags integer-constant-to-raw-pointer casts; bad thing is conjuring a dangling pointer via cast. |
185
186---
187
188# Full-corpus findings (all 815 lints)
189
190The remaining findings cover lints not in the `needless_*` / `useless_*` / `unneeded_*` / `manual_*` families already addressed above.
191
192## A. Pluralization issues
193
194Lints that apply to a **class** of things but use a singular form (or vice versa).
195
196| Priority | Current lint | Proposed lint | Group | Short description | Rationale |
197|---|---|---|---|---|---|
198| Low | `collapsible_if` | `collapsible_ifs` | style | nested `if`s that can be collapsed | The description itself uses the plural "nested `if`s"; the lint flags any such nesting, not one specific instance. Consistent with `collapsible_else_if` → same pattern. |
199| Low | `match_overlapping_arm` | `match_overlapping_arms` | correctness | a `match` with overlapping arms | The short description says "overlapping **arms**" (plural); the lint name uses singular `arm`. Every `match` lint that references arms uses the plural form (`match_same_arms`, etc.). |
200| Low | `duplicate_underscore_argument` | `duplicate_underscore_arguments` | style | function arguments having names which only differ by an underscore | The description explicitly says "function **arguments**" (plural). Parallel lints like `fn_params_excessive_bools` use the plural. |
201| Low | `assign_op_pattern` | `assign_op_patterns` | style | assigning the result of an operation on a variable to that same variable | The lint catches any occurrence of this pattern across an entire codebase; `patterns` (plural) is consistent with the class-level framing used by similar lints. |
202
203---
204
205## B. Names describing the fix, not the problem
206
207Lint names that read as the **recommended fix** rather than the thing being flagged.
208
209| Priority | Current lint | Proposed lint | Group | Short description | Rationale |
210|---|---|---|---|---|---|
211| High | `map_entry` | `contains_key_then_insert` | perf | use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap` | The name `map_entry` is the fix API (the Entry API), not the problem. The bad thing is the `contains_key` + `insert` sequence. `#[allow(clippy::map_entry)]` reads as "allow map entry" which reveals nothing about what is being allowed. |
212| High | `question_mark` | `explicit_error_propagation` | style | checks for expressions that could be replaced by the `?` operator | `question_mark` is the fix symbol, not the problem. The bad thing is verbose manual error propagation via `match`/`if let`. `#[allow(clippy::question_mark)]` reveals nothing. |
213| High | `cast_lossless` | `lossless_as_cast` | pedantic | casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8` | `cast_lossless` sounds like a **desirable** property of a cast (lossless = good). The lint actually flags these casts and recommends using `From` instead. The bad thing is performing a known-lossless cast via `as` rather than `From::from`. |
214| Medium | `use_self` | `explicit_type_instead_of_self` | style | unnecessary structure name repetition whereas `Self` is applicable | `use_self` names the fix ("use Self"), not the problem. The bad thing is writing the full type name where `Self` could be used. `#[allow(clippy::use_self)]` is cryptic. |
215| Medium | `from_over_into` | `into_impl_without_from` | style | warns on implementations of `Into<..>` to use `From<..>` | `from_over_into` reads ambiguously as "from is preferred over into", but the bad thing is implementing `Into<T>` directly without a corresponding `From` impl. The name gives no indication of what code is being flagged. |
216| Medium | `ptr_eq` | `raw_ptr_equality_via_cast` | style | use `std::ptr::eq` when comparing raw pointers | `ptr_eq` is the fix function, not the problem. The bad thing is comparing raw pointer addresses via `as usize` casts or `==` without `ptr::eq`. |
217| Medium | `unwrap_or_default` | `explicit_default_in_unwrap_or` | style | using `.unwrap_or`, etc. with an argument that constructs a default value | `unwrap_or_default` is the fix method name. The bad thing is writing `x.unwrap_or(T::default())` or `x.unwrap_or(Vec::new())` when `.unwrap_or_default()` is available. |
218| Medium | `semicolon_if_nothing_returned` | `missing_trailing_semicolon` | style | add a semicolon if nothing is returned | The description begins "add a semicolon" — that's a fix instruction. The lint name also describes the fix condition rather than the problem (an expression statement without a trailing semicolon). |
219| Medium | `collapsible_str_replace` | `consecutive_str_replace_calls` | style | collapse consecutive calls to `str::replace` (2 or more) into a single call | The name starts with a verb ("collapsible") that names the fix action. The bad thing is calling `str::replace` multiple times in sequence where a single call would do. |
220
221---
222
223## C. Too vague / generic names
224
225Names that give insufficient information about what is being flagged.
226
227| Priority | Current lint | Proposed lint | Group | Short description | Rationale |
228|---|---|---|---|---|---|
229| High | `ptr_arg` | `owned_collection_ref_arg` | style | fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead | `ptr_arg` is technically accurate (a pointer argument) but gives no hint that the bad thing is using `&Vec`/`&String` instead of slice/str references. The name looks like it might be about raw `*const`/`*mut` pointers. |
230| High | `try_err` | `return_err_with_question_mark` | style | return errors explicitly rather than hiding them behind a `?` | `try_err` is cryptic. There is no `try` or `err` construct in Rust syntax by that name. The bad thing is `return Err(expr)?` — wrapping in `Err` and then immediately using `?` to unwrap it. |
231| High | `not_unsafe_ptr_arg_deref` | `missing_unsafe_on_ptr_deref_fn` | correctness | public functions dereferencing raw pointer arguments but not marked `unsafe` | `not_unsafe_ptr_arg_deref` uses a double negative (not-unsafe) that is hard to parse. The bad thing is a public function that dereferences a raw pointer argument without being marked `unsafe`. |
232| Medium | `eq_op` | `equal_operands` | correctness | equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`) | `eq_op` is a cryptic abbreviation. The full phrase "equal operands" is already in the description; the name should match it. `#[allow(clippy::eq_op)]` reveals nothing about what is permitted. |
233| Medium | `equatable_if_let` | `if_let_instead_of_eq` | style | using pattern matching instead of equality | `equatable_if_let` sounds like "this if-let can be equated" (a property of the code). The bad thing is using `if let Some(x) = y { x == z }` where `y == Some(z)` would do. The proposed name clearly describes the problem. |
234| Medium | `precedence` | `unclear_operator_precedence` | complexity | operations where precedence may be unclear | `precedence` alone is maximally vague. Every arithmetic expression involves precedence. The bad thing is operator combinations whose precedence is surprising or misleading (e.g., `1 << 1 + 1`). |
235| Medium | `or_fun_call` | `eager_or_argument` | perf | using any `*or` method with a function call, which suggests `*or_else` | `or_fun_call` is cryptic. The bad thing is passing an eagerly-evaluated function call to `unwrap_or(fn())` instead of `unwrap_or_else(fn)`. The lint name gives no hint about eager vs lazy evaluation. |
236| Medium | `linkedlist` | `linkedlist_usage` | perf | usage of LinkedList, usually a vector is faster | `linkedlist` is just the type name with no framing as a problem. Every other collection-related lint is a phrase (`box_collection`, `rc_buffer`, `vec_box`). Even `linkedlist_usage` barely meets the bar; a better name would be `prefer_vec_over_linkedlist` but that's fix-oriented. `linkedlist_usage` at minimum makes it a lint name rather than a type name. |
237| Medium | `ip_constant` | `hardcoded_ip_address` | restriction | hardcoded localhost IP address | `ip_constant` sounds like a constant related to IP (perhaps `IpAddr` constants), not a hardcoded IP string. The description says "hardcoded localhost IP address" — the proposed name matches. |
238| Medium | `error_impl_error` | `error_type_named_error` | restriction | exported types named `Error` that implement `Error` | `error_impl_error` is confusing due to the word "error" appearing twice with different meanings (the type name vs the trait). The bad thing is that a type literally named `Error` implements the `Error` trait — this causes confusion at use sites. |
239| Medium | `suspicious_map` | `map_call_ignoring_return` | suspicious | suspicious usage of map | `suspicious_map` is maximally vague — every `map` call could be described as "suspicious" when called wrong. The actual bad thing is calling `.map(f)` and discarding the return value (where `.for_each(f)` or `.inspect(f)` was intended). |
240| Medium | `index_refutable_slice` | `slice_index_instead_of_destructure` | perf | avoid indexing on slices which could be destructed | `index_refutable_slice` has the words in confusing order and uses "refutable" in a non-obvious way. The bad thing is indexing into a slice in a match arm's body when the slice could be pattern-matched/destructured instead. |
241| Medium | `significant_drop_tightening` | `late_significant_drop` | perf | elements that could be early dropped but are dropped at the end of their scopes | `significant_drop_tightening` describes the fix action ("tighten" the drop point). The bad thing is holding a significant-drop value longer than necessary. `late_significant_drop` names the problem. |
242| Low | `op_ref` | `ref_for_operator_overload` | style | taking a reference to satisfy the type constraints on `==` | `op_ref` is a two-word abbreviation that gives no useful information to the reader. The bad thing is writing `&x == y` to call an operator overload that takes references, when `x == y` would work via auto-ref. |
243| Low | `no_effect_replace` | `replace_with_identical_strings` | correctness | replace with no effect | `no_effect_replace` names the abstract category but the description ("replace with no effect") is similarly vague. The bad thing is a `.replace(x, x)` call where the pattern and replacement are identical. |
244
245---
246
247## D. Inconsistent naming with similar lints
248
249Lints that use a different naming convention than closely related lints in the same area.
250
251### D.1 `unnecessary_*` vs `redundant_*` proliferation
252
253The existing document established `redundant_*` as the preferred term for things that are superfluous / no-op / can be removed without changing meaning. Clippy currently has a large family of `unnecessary_*` lints that describe exactly this category, creating inconsistency with the explicitly-named `redundant_*` lints.
254
255| Priority | Current lint | Proposed lint | Group | Short description | Rationale |
256|---|---|---|---|---|---|
257| Low | `unnecessary_box_returns` | `redundant_box_return` | style | needlessly returning a `Box` | Word swap: the returned `Box` is redundant (caller could receive `T`). |
258| Low | `unnecessary_cast` | `redundant_cast` | complexity | cast to the same type, e.g., `x as i32` where `x: i32` | A self-cast is definitionally redundant (no-op). Aligns with `redundant_conversion`. |
259| Low | `unnecessary_fallible_conversions` | `redundant_fallible_conversion` | style | calling `try_from`/`try_into` when `From`/`Into` is implemented | The fallible path is redundant when an infallible path exists. |
260| Low | `unnecessary_lazy_evaluations` | `redundant_lazy_evaluations` | perf | using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation | The lazy wrapper is redundant when the argument is cheap (a constant or literal). |
261| Low | `unnecessary_literal_unwrap` | `redundant_constructor_unwrap` | complexity | using `unwrap()` related calls on `Result` and `Option` constructors | `Ok(x).unwrap()``x`; the wrapping and unwrapping are mutually redundant. |
262| Low | `unnecessary_map_on_constructor` | `redundant_map_on_constructor` | complexity | using `map`/`map_err` on `Option` or `Result` constructors | `Some(x).map(f)``Some(f(x))`; the constructor + map combo is redundant. |
263| Low | `unnecessary_map_or` | `redundant_map_or` | style | reduce unnecessary calls to `.map_or(bool, …)` | The `map_or` pattern is redundant compared to `is_some_and` / `is_ok_and`. |
264| Low | `unnecessary_min_or_max` | `redundant_min_or_max` | complexity | using `min()`/`max()` when there is no need for it | When one bound is already satisfied by type constraints, the call is a no-op. |
265| Low | `unnecessary_safety_comment` | `redundant_safety_comment` | restriction | annotating safe code with a safety comment | A `// SAFETY:` comment on safe (non-`unsafe`) code is redundant. |
266| Low | `unnecessary_safety_doc` | `redundant_safety_doc` | style | `pub fn` or `pub trait` with `# Safety` docs but no `unsafe` | A `# Safety` section on a non-`unsafe` function is redundant. |
267| Low | `unnecessary_self_imports` | `redundant_self_import` | style | imports ending in `::{self}`, which can be omitted | The `{self}` part of the import path is redundant. |
268| Low | `unnecessary_semicolon` | `redundant_semicolon` | style | unnecessary semicolon after expression returning `()` | The semicolon after a `()` expression is a no-op (no type change). |
269| Low | `unnecessary_to_owned` | `redundant_to_owned` | perf | unnecessary calls to `to_owned`-like functions | Aligns with the existing `needless_to_owned``redundant_to_owned` proposal in Section 1. |
270| Low | `unnecessary_trailing_comma` | `redundant_trailing_comma` | style | unnecessary trailing comma before closing parenthesis | The comma is a no-op syntactically. |
271| Low | `unnecessary_wraps` | `redundant_wraps` | style | functions that only return `Ok` or `Some` | When a function only ever wraps values in `Ok`/`Some`, the wrapping is redundant — the return type should not be `Result`/`Option`. |
272
273**Note:** `unnecessary_unwrap` and `unnecessary_operation` are intentionally excluded from this group — see §E below.
274
275### D.2 `derived_` vs `derive_` inconsistency
276
277| Priority | Current lint | Proposed lint | Group | Short description | Rationale |
278|---|---|---|---|---|---|
279| Low | `derived_hash_with_manual_eq` | `derive_hash_with_manual_eq` | correctness | deriving `Hash` but implementing `PartialEq` explicitly | The parallel lints use `derive_` (present-tense verb prefix): `derive_ord_xor_partial_ord`, `derive_partial_eq_without_eq`. This one anomalously uses `derived_` (past tense / adjective). |
280
281### D.3 `nonminimal_bool` vs `non_minimal_cfg`
282
283| Priority | Current lint | Proposed lint | Group | Short description | Rationale |
284|---|---|---|---|---|---|
285| Low | `nonminimal_bool` | `non_minimal_bool` | complexity | boolean expressions that can be written more concisely | `non_minimal_cfg` uses the three-part snake_case form. `nonminimal_bool` collapses the prefix into one token inconsistently. |
286
287### D.4 `expl_impl_clone_on_copy` abbreviation
288
289| Priority | Current lint | Proposed lint | Group | Short description | Rationale |
290|---|---|---|---|---|---|
291| Low | `expl_impl_clone_on_copy` | `explicit_clone_impl_on_copy` | restriction | implementing `Clone` explicitly on `Copy` types | `expl_` is a non-standard abbreviation. All other lints in the codebase that mean "explicit" use the full word (`explicit_auto_deref`, `explicit_counter_loop`, `explicit_deref_methods`, etc.). |
292
293---
294
295## E. Other naming smells
296
297Miscellaneous issues not covered by the above categories.
298
299| Priority | Current lint | Proposed lint | Group | Short description | Rationale |
300|---|---|---|---|---|---|
301| High | `exhaustive_enums` | `non_exhaustive_missing_on_enums` | restriction | detects exported enums that have not been marked `#[non_exhaustive]` | `exhaustive_enums` flags enums that ARE exhaustive (lacking `#[non_exhaustive]`). The name sounds like a description of the code's property rather than a problem — "allow exhaustive_enums" reads as "allow my enums to be exhaustive", which seems fine. The bad thing is the *missing* attribute. |
302| High | `exhaustive_structs` | `non_exhaustive_missing_on_structs` | restriction | detects exported structs that have not been marked `#[non_exhaustive]` | Same problem as `exhaustive_enums`: the name describes a neutral property (being exhaustive) rather than the missing attribute. |
303| Medium | `almost_swapped` | `accidental_sequential_reassignment` | correctness | `foo = bar; bar = foo` sequence | `almost_swapped` is creative but imprecise — it sounds like there's a partial swap operation in the standard-library sense. The bad thing is two sequential assignments that destroy the original value of `foo` before using it to set `bar`, as would happen during a botched manual swap. |
304| Medium | `unnecessary_unwrap` | `infallible_unwrap` | correctness | checks for calls of `unwrap[_err]()` that cannot fail | `unnecessary_unwrap` implies the call does nothing, but `unwrap()` on an `Ok`/`Some` value does perform a real operation (it extracts the value). The call is not "unnecessary" — it is *infallible*. Compare `panicking_unwrap` (the opposite case). `infallible_unwrap` precisely names the property that makes it flaggable. |
305| Medium | `bool_comparison` | `redundant_bool_comparison` | complexity | comparing a variable to a boolean, e.g., `if x == true` or `if x != true` | `bool_comparison` names the syntactic pattern but gives no hint that the comparison is redundant. `if x == true` is a comparison *against* a bool literal that adds no information. |
306| Medium | `unnecessary_operation` | `expression_with_no_effect` | complexity | outer expressions with no effect | `unnecessary_operation` overlaps confusingly with `no_effect` (which covers *statements* with no effect). The distinction matters: `no_effect` handles free-standing statements; `unnecessary_operation` catches expressions *within* larger expressions that have no effect. The proposed name makes the domain of the lint explicit. |
307| Medium | `return_and_then` | `and_then_instead_of_question_mark` | restriction | using `Option::and_then` or `Result::and_then` to chain a computation that returns an `Option` or a `Result` | `return_and_then` describes the syntactic structure (`return x.and_then(f)`) but not why it's problematic. The bad thing is using `and_then` when the `?` operator would express the same intent more cleanly. |
308| Medium | `iter_with_drain` | `drain_instead_of_into_iter` | perf | replace `.drain(..)` with `.into_iter()` | `iter_with_drain` sounds like it describes a method chain that combines `iter()` with `drain()`. The bad thing is calling `.drain(..)` when `.into_iter()` was intended (drain modifies the source collection; into_iter does not). |
309| Low | `significant_drop_in_scrutinee` | `significant_drop_in_match_scrutinee` | suspicious | warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime | The existing name is at least technically precise. The improvement is minor — adding `match_` makes the term "scrutinee" slightly more navigable. Low urgency. |
310| Low | `needless_bool_assign` | `redundant_bool_assign` | complexity | setting the same boolean variable in both branches of an if-statement | Both branches set the same value; the if/else is redundant. Consistent with the `needless_*``redundant_*` pattern from Section 1 (not included there because it's not a pure `needless_` prefix item). |
311| Low | `needless_borrow` | `redundant_borrow` | style | taking a reference that is going to be automatically dereferenced | The reference adds no information — auto-deref removes it immediately. Consistent with Section 1's `needless_borrows_for_generic_args``redundant_borrows_for_generic_args`. |
312| Low | `needless_bitwise_bool` | `redundant_bitwise_bool` | style | boolean expressions that use bitwise rather than lazy operators | Using `&` / `\|` on booleans instead of `&&` / `\|\|` is needlessly non-lazy. Word swap consistent with Section 1. |
313| Low | `needless_doctest_main` | `redundant_doctest_main` | style | presence of `fn main() {` in code examples | The `fn main()` wrapper in Rust doctests is implicit; making it explicit is redundant. |
314| Low | `needless_lifetimes` | `redundant_lifetimes` | complexity | lifetime annotations that can be elided | Elision rules make explicit lifetime names redundant in these positions. Consistent with Section 1 pattern. |
315| Low | `needless_match` | `redundant_match` | complexity | `match` or match-like `if let` that are unnecessary | A `match` whose only effect is to re-wrap the matched value is redundant. |
316| Low | `needless_pub_self` | `redundant_pub_self` | restriction | checks for usage of `pub(self)` and `pub(in self)` | `pub(self)` is equivalent to private visibility — the `pub` is redundant. |
317| Low | `needless_range_loop` | `index_range_loop` | style | for-looping over a range of indices where an iterator over items would do | Unlike most `needless_` items (which are no-ops), this loop is not "needless" — it does work, just in an non-idiomatic way. The bad thing is the explicit index range (`0..v.len()`). `index_range_loop` describes the actual pattern without implying the loop itself is pointless. |
318| Low | `needless_raw_string_hashes` | `redundant_raw_string_hashes` | style | suggests reducing the number of hashes around a raw string literal | Extra `#` delimiters not required for the string's content are redundant. |
319| Low | `needless_raw_strings` | `redundant_raw_string` | style | suggests using a string literal when a raw string literal is unnecessary | A raw string where no characters require raw-string escaping is redundant. |
320| Low | `needless_return` | `redundant_return` | style | explicit `return` as the last expression in a function body | The `return` keyword at a function's tail position is syntactically redundant in Rust. |
321| Low | `needless_return_with_question_mark` | `redundant_return_with_question_mark` | complexity | using a return statement like `return Err(expr)?;` where removing it would suffice | The `return` is redundant when `?` already propagates the error. |
322| Low | `needless_splitn` | `redundant_splitn` | complexity | usages of `str::splitn` that can be replaced with `str::split` | When the `n` parameter to `splitn` is never the binding limit, the `n` argument is redundant. |
323| Low | `needless_type_cast` | `redundant_type_cast` | restriction | binding defined with one type but always cast to another | The explicit type on the binding is redundant because it is immediately cast away. |
324| Low | `replace_box` | `inefficient_box_assignment` | perf | assigning a newly created box to `Box<T>` is inefficient | `replace_box` reads as "replace the box" (a description of the fix) or "a box replacement" (vague). The bad thing is that assigning a freshly allocated `Box::new(T)` to an existing `Box<T>` variable causes an unnecessary allocation + deallocation instead of reusing the existing box. |
325| Low | `unused_rounding` | `redundant_rounding` | complexity | uselessly rounding a whole number floating-point literal | Rounding a literal like `1.0_f64.round()` has no effect (it's already whole). The rounding call is redundant, not merely "unused". Consistent with Section 1 terminology. |
326| Low | `owned_cow` | `unnecessarily_owned_cow` | perf | needlessly owned `Cow` type | `owned_cow` names the property (it's owned), not why it's a problem. The issue is that the `Cow` contains an owned value where a borrowed one would suffice. The existing description says "needlessly owned". |
327
328---
329
330## Summary by priority
331
332| Priority | Count | Examples |
333|---|---|---|
334| **High** | 9 | `map_entry`, `question_mark`, `cast_lossless`, `ptr_arg`, `try_err`, `exhaustive_enums`, `exhaustive_structs`, `not_unsafe_ptr_arg_deref`, `bind_instead_of_map` |
335| **Medium** | ~25 | `use_self`, `from_over_into`, `eq_op`, `precedence`, `or_fun_call`, `linkedlist`, `ip_constant`, `error_impl_error`, `suspicious_map`, `almost_swapped`, `unnecessary_unwrap`, `bool_comparison`, `unnecessary_operation`, `index_refutable_slice`, `significant_drop_tightening`, `collapsible_str_replace`, `semicolon_if_nothing_returned`, `equatable_if_let`, `ptr_eq`, `unwrap_or_default`, and more |
336| **Low** | ~40 | `collapsible_if`, `match_overlapping_arm`, `duplicate_underscore_argument`, `derived_hash_with_manual_eq`, `nonminimal_bool`, `expl_impl_clone_on_copy`, all `unnecessary_*``redundant_*`, all remaining `needless_*``redundant_*` |