Configuring Clippy globally or per project

clippy.toml seems to be limited to a select set of configurable lints. I'd like to allow/warn/deny lints based on best practice at my company as a default that developer could override if they have good cause to ignore Clippy. I'd strongly like to avoid adding a header that gets copied and pasted across every project, but all that leaves that I'm aware of is CLI configuration. Running a custom cargo clippy command in CI/CD isn't quite good enough either. I'd like developers to be able to see the lints as-configured by the Rust guild within my organization.

Is there a way to do this that doesn't involve hacking up Clippy to take a configuration file of defaults in the home directory? I've been googling around extensively for this and I'm starting to lose it a bit as I recall linting tools in other languages having config files that could live in the project directory or somewhere that applies globally.

I find it hard to believe there is a "good cause to ignore Clippy.".

Do you have some examples?

I admit that I may not always agree with Clippy (or cargo fmt), but I feel it's preferable that the global Rust community works to the same standard. Thus oiling the wheels of moving programmers from project to project, code base to code base with minimal surprises and head scratching. Eliminating all the needless argument over styles and formatting that has plagued the software industry since forever and wasted countless hours of human life.

Isn't that worth more than some personal preferences of some project or developer?

If you enable lints that are allowed by default, it can be useful to ignore them in some situations. For example, I have a project with #![deny(clippy::print_stdout, clippy::print_stderr, clippy::unwrap_used)] set for all crates. Tests are allowed to use unwrap with #[allow(clippy::unwrap_used)] since proper error handling and messaging is not particularly valuable there. A single function that is used to report results in JSON using stdout is given permission to do so with #[allow(clippy::print_stdout)].

As for the original question, I don't think there's a good way to do what you want at this point. See this issue. In the comments someone from Embark details how they currently handle this:

This is one of our top Clippy feature requests from us at Embark, we have a standard set of an increasing amount of Clippy (and a few Rust) lints that we enable by default across 50+ crates in 10+ repos. See EmbarkStudios/rust-ecosystem#59 for the set of lints and our process.

This is currently done through a code snippet that add to every lib.rs & main.rs and search'n'replace on changes, which works and adds a lot of value. But having this proper configuration file later on will make the workflow way easier and also easier to review changes without having to see 40 crates enabling a lint in the same PR.

Here is how the our "lint configuration" looks like right now: rust-ecosystem/lints.rs at main · EmbarkStudios/rust-ecosystem · GitHub.

For the complexity group of lints the workaround to fix it may be more complex that the original code. For the perf group of lints sometimes the suggestion is actually slower than the original. For example large_enum_variant triggers on enum Test { A(i32), B([i32; 8000]) } even if Test::B is the most common variant and thus boxing it would slow things down. Or or_fun_call triggers on .unwrap_or(very_cheap_func_call(some_arg)).

Note that clippy is mostly for lints that are opinionated and may not be suitable for every project. It is very normal to turn lints on and off on a per project basis.

2 Likes