Skip to content

Discussion: enable Clippy lints in rustfmt source tree and CI #6923

Description

@shulaoda

Background

We don't currently run Clippy on rustfmt's own code. The rust-clippy job in integration.yml runs Clippy on a bunch of external crates to check that rustfmt formats them correctly; it's a formatter regression test, not a lint check on us. The only Clippy-related thing in the source is a handful of stray #[allow(clippy::*)].

If you run cargo clippy --all-targets against main today (toolchain nightly-2026-02-19), you get 269 lint hits in 50 different lints, all from Clippy's default groups. Most are warnings nobody ever sees. But src/cargo-fmt/main.rs and src/format-diff/main.rs both have #![deny(warnings)] at the top, so a Clippy warning in either of those crates becomes a compile error, and cargo clippy --all-targets doesn't actually finish cleanly. We have a Clippy gate already; it just happens to cover exactly two files by accident.

Which lints would this enable

"Default" means everything in clippy::all:

Group Default What it's about
clippy::correctness deny Code that's almost certainly a bug
clippy::suspicious warn Probably wrong, occasionally intentional
clippy::style warn Idiomatic Rust style
clippy::complexity warn Has a simpler equivalent form
clippy::perf warn Avoidable performance cost

The other groups (pedantic, nursery, restriction, cargo) are allow-by-default and would stay off. Individual lints from those can be opted into later if anyone wants.

Lint reference: https://rust-lang.github.io/rust-clippy/master/index.html

What's firing on main today

Run on nightly-2026-02-19. The four lints marked (E) are the ones currently breaking the build (they have at least one site inside cargo-fmt or format-diff and trip #![deny(warnings)]).

50 lints, 269 sites total
Sites Lint
51 clippy::bool_assert_comparison (E)
41 clippy::unnecessary_map_or
26 clippy::borrow_deref_ref
14 clippy::needless_borrow
13 clippy::explicit_auto_deref
10 clippy::too_many_arguments
9 clippy::field_reassign_with_default
8 clippy::unnecessary_lazy_evaluations
7 clippy::wrong_self_convention
6 clippy::io_other_error (E)
6 clippy::needless_borrows_for_generic_args
6 clippy::expect_fun_call
6 clippy::to_string_in_format_args
6 clippy::arc_with_non_send_sync
5 clippy::redundant_field_names
5 clippy::unnecessary_unwrap
4 clippy::needless_splitn
4 clippy::default_constructed_unit_structs
3 clippy::redundant_pattern
2 clippy::derivable_impls, clippy::useless_conversion, clippy::len_zero, clippy::manual_pattern_char_comparison, clippy::get_first, clippy::while_let_on_iterator, clippy::skip_while_next, clippy::let_unit_value
1 clippy::is_digit_ascii_radix, clippy::nonminimal_bool, clippy::needless_return, clippy::enum_variant_names, clippy::write_with_newline, clippy::needless_lifetimes, clippy::manual_inspect, clippy::ptr_arg, clippy::needless_question_mark, clippy::bind_instead_of_map, clippy::redundant_guards, clippy::clone_on_copy, clippy::collapsible_if, clippy::manual_repeat_n, clippy::borrowed_box, clippy::type_complexity, clippy::extra_unused_lifetimes, clippy::assign_op_pattern, clippy::unwrap_or_default, clippy::legacy_numeric_constants, clippy::map_flatten, clippy::non_canonical_partial_ord_impl (E), clippy::into_iter_on_ref (E)

Suggested next step

Rather than try to agree on the whole list up front, I think the simplest start is one small PR that just lays the plumbing:

  1. Add a [workspace.lints.clippy] table to Cargo.toml with all = "allow". Nothing changes in behavior, and the two cargo-fmt / format-diff build failures go away because there's nothing for #![deny(warnings)] to catch anymore.
  2. Add a cargo clippy --all-targets -- -D warnings step to CI. Safe to turn on immediately, since at this point nothing warns.

After that, each lint we want to actually enforce is its own small PR: flip one entry in the table from "allow" to "warn" (or "deny") and include the fixes for that lint in the same PR. The Cargo.toml ends up being the canonical record of which lints we've consciously adopted, and we never get a flood of unrelated warnings landing together.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions