Description
Related: #59024
Rustc comes with a respectable number of lints, but clippy comes with a lot. Furthermore, Clippy has the concept of a "restriction" lint: a lint that is probably not useful to most users (and is not itself a judgement on "good rust style") but will be useful for codebases operating under certain constraints (for example, the lints that forbid unwrap/expect/panic/indexing).
The current linting infrastructure runs all lints at all times, only using lint levels to determine if a lint must be emitted. This basically means that most codebases running clippy are spending a lot of time running the code for lints they never see. This is true for rustc as well, though it has fewer Allow lints by default.
I proposed #59024 in the past so that lint code only gets run when the lint is enabled. There are some tricky architecture constraints around hotswapping the lint list whilst traversing the AST.
However, I don't think there's anything stopping us from doing this at a global level: it should not be hard or expensive to collect the list of enabled lints in the crate by the time late lint passes run, just collect all non-allow()
lint attributes and combine them with the defaults, removing any that were manually disabled at the crate level.
Thoughts? Clippy is not super slow and this isn't a bottleneck, but as we add lints this becomes more of a problem, and it also prevents us from adding potentially computationally expensive restriction lints (one of the best things about restriction lints is that we can be more flexible around them!)
cc @rust-lang/clippy
Activity
Mark-Simulacrum commentedon Jan 17, 2023
cc #74704
flip1995 commentedon Jan 19, 2023
I think the biggest challenge that we need to solve is that Lint<>LintPass is (or can be) a many-to-many relationship. One Lint can be emitted from multiple passes and one LintPass can emit multiple lints. AFAIK we don't do the former in Clippy, but we do a lot of the latter.
I think introducing a policy that allow-by-default lints must be in their own pass is not feasible and would worsen the dev experience of Clippy.
We do however have a list of all lints a lint pass can emit. So I think the best first step (and maybe this is already enough) would be to check if
any
of those lints are >=warn-by-default at some point and then run lint passes depending on that.xFrednet commentedon Jan 19, 2023
While working on
#[expect]
I noticed that a few rustc lints, likenon_ascii_idents
, already implement this behavior. (Playground). I like the idea, but found it confusing, that#[warn/deny/forbid()]
attributes didn't work on items/statements/expressions. If this behavior is implemented, there should be some kind of warning, if a user tries to enable such a lint on non-crate levels. Maybe with a new lint, that clearly explains that this lint first has to be enabled on a crate level.Agreed! What we could do, on the implementation level, is to first check if the lint is enabled, before we do most of the individual linting logic. Basically, how
is_lint_allowed
is used in rare cases right now. This might be annoying if we do it for all lints, but for restriction lints this could definitely make sense.Manishearth commentedon Jan 19, 2023
@flip1995 This is an optimization and doesn't have to be perfect, we can exclude passes which are all-allowed, as you said.
I think a policy to try and put allow-by-default lints into their own passes unless they are related is fine and is already roughly what Clippy does.
You've misunderstood the proposal, when I say "disabled in the entire crate", I mean "disabled in the entire crate and never enabled ever". This is for lints whose name is never mentioned in an allow attribute at all.
Nah, because that breaks the ability to enable the lint at the item level.
I was thinking that we could have rustc mutate the lint pass vector (or create a new one) based on this. Everything else works the same.
write.rs
torustc_ast::FormatArgs
rust-lang/rust-clippy#10275significant_drop_tightening
rust-lang/rust-clippy#10533RalfJung commentedon Sep 23, 2023
This has to be done very carefully -- while
impl_lint_pass
takes a list of lints that is somehow associated with the pass, it is currently entirely undocumented what that list is for, so we cannot expect that this list means "this pass will only emit the following lints (and can be skipped if those lints are all allowed)".Also, it's not currently possible to have the same lint in that list for multiple passes (that will ICE saying something about a lint being registered twice), so for lints that are emitted from multiple different places, this list will necessarily be incomplete.
(I'm entirely talking about rustc lints here, I don't know much about Clippy.)
That sounds like a bug. If you have an example where a lint cannot be enabled with function-level
warn
, please file an issue.Manishearth commentedon Sep 23, 2023
I would be very surprised if any rustc lint violated this. Clippy, a project with way more lints certainly doesn't.
yeah but those are builtins and emitted outside of lint passes entirely. Worst that can happen there is the optimization doesn't skip some lints.
21 remaining items