Add generic allow-in-tests configuration - #17520
Conversation
| concat!("`", $name_str, "` is deprecated"), | ||
| ) | ||
| .with_help(format!( | ||
| "use `allow-in-tests = [{}]` instead, which works for any lint", |
There was a problem hiding this comment.
but it doesn't work for "any lint", only for late pass lints
There was a problem hiding this comment.
This is fixed now, it works for non late-pass lints too.
| /// | ||
| /// This supersedes the per-lint `allow-<lint>-in-tests` options, which are deprecated but | ||
| /// still honored: a lint is suppressed in test code if it is listed here or its own | ||
| /// `allow-<lint>-in-tests` option is set. |
There was a problem hiding this comment.
what if the allow-*-in-tests is explicitly set to false, but here the lint is listed - it should be made clear that if either place says the lint should be allowed then it is
| /// Resolves the lint names given in the `allow-in-tests` configuration and hands them to | ||
| /// `clippy_utils`, which drops their diagnostics when they are emitted from test code. | ||
| /// | ||
| /// Names which don't refer to a Clippy lint are reported and ignored. |
There was a problem hiding this comment.
what about non-late-pass lints - can they be reported too?
There was a problem hiding this comment.
I've added support for non-late-pass lints in the latest version.
| ## `allow-expect-in-tests` | ||
| Whether `expect` should be allowed in test functions or `#[cfg(test)]` | ||
|
|
||
| Deprecated in favor of [`allow-in-tests`](#allow-in-tests), which works for any | ||
| lint. This option still works, but new configurations should use `allow-in-tests`. |
There was a problem hiding this comment.
Community review:
I am not sure that this is a good idea.
I think it is not uncommon to want to allow .expect, but not dbg! in tests.
Therefore this seems like a lot of churn for some users and not an 100% pure win at that.
There was a problem hiding this comment.
@CommanderStorm what in particular is not a good idea? Yes, the premise of this change is that things should be spelled differently, for the benefit of consistency and searchability going forward, as well as immediate extensibility to many more lints. Are you voting that such benefits are not worth the eventual respelling?
There was a problem hiding this comment.
I think there are people for which this is an regression.
As said:
I think it is not uncommon to want to allow
.expect, but notdbg!in tests.
Not everyone wants to opt into an "allow everything, including all the future things we will allow" switch, there are also others who I see wanting exactly this.
Thus, I think it is not a clear win
There was a problem hiding this comment.
allow-in-tests doesn't allow everything though. It takes a list of specific lints to allow in tests. For the scenario you mentioned, you can get it with this:
allow-in-tests = ["expect_used"] # `.expect` allowed in tests, `dbg!` still linted
I've reworded these lines in the latest version to be clearer.
|
Was AI used in this PR? If so, we require disclosure. |
Thanks for the flag. Yes, I used AI (Opus 5) and will figure out where to disclose that. |
This comment has been minimized.
This comment has been minimized.
|
Ping @dcsommer 🏓! Are you there? |
Who determines this? How do I know if I should make an FCP? |
I think only team members can actually start an FCP (e.g. I can't), so this was mostly just a heads up to you that it might take some discussion before this gets merged |
Big user facing changes like new lints or configuration options just do. We should write this process down btw 😉
You cannot do an FCP, only people with merge rights can. Once a PR is ready, it gets the @rustbot label +needs-fcp +llm-assisted |
allow-in-tests configurationallow-in-tests (and inverse check-in-tests) configuration
39817f9 to
26c9a14
Compare
|
@dcsommer have you had a chance to review our policy? In particular regarding the docs, diagnostics and the PR description? |
|
I'm reading it now, but it seems to be scoped to rust-lang/rust. Just
checking to make sure it also is in scope here, and if so what brings it in
scope.
…On Wed, Sep 9, 2026, 2:23 AM Frank Elsinga ***@***.***> wrote:
*CommanderStorm* left a comment (rust-lang/rust-clippy#17520)
<#17520 (comment)>
@dcsommer <https://github.com/dcsommer> have you had a chance to review
our policy? In particular regarding the docs, diagnostics and the PR
description?
https://forge.rust-lang.org/policies/llm-usage.html#-banned
—
Reply to this email directly, view it on GitHub
<#17520?email_source=notifications&email_token=AAIIKIDH4GJJWSDYGS37XKD5OEORTA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKNJZHE2TGMJXGU2KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#issuecomment-5599531754>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAIIKIC5TTCPRFDUU4FHCYD5OEORTAVCNFSNUABEKJSXA33TNF2G64TZHMZDMOBVGA2DIMZ3JFZXG5LFHM2TAOJTGQ4DEMJZHCQXMAQ>
.
Triage notifications, keep track of coding agent tasks and review pull
requests on the go with GitHub Mobile for iOS
<https://github.com/notifications/mobile/ios/AAIIKIHEN3ICVK57HJBDRKT5OEORTA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKNJZHE2TGMJXGU2KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJKTGN5XXIZLSL5UW64Y>
and Android
<https://github.com/notifications/mobile/android/AAIIKIARNS3XHSPBL3CZHFD5OEORTA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKNJZHE2TGMJXGU2KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLTGN5XXIZLSL5QW4ZDSN5UWI>.
Download it today!
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
|
Yes, here is the link of clippy ratifying this https://doc.rust-lang.org/nightly/clippy/development/llm_usage.html |
|
I can split out the "inverse" option if you want, so one option per PR.
…On Wed, Sep 9, 2026, 7:22 AM Gri-ffin ***@***.***> wrote:
***@***.**** commented on this pull request.
Its quite...daunting to review this PR, its trying to do too much, it
should probably be split into separate PRs.
*View changes since this review
<https://triagebot.infra.rust-lang.org/gh-changes-since/rust-lang/rust-clippy/17520/c9d938fb4d39fe1c56362436ac944a8e4550f969..26c9a14848b5fc50e2b6ca5c03e5a74a3622c368>*
—
Reply to this email directly, view it on GitHub
<#17520?email_source=notifications&email_token=AAIIKIHD5HYLI5T4OFGQ5T35OFRQ5A5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTKMJVGU3DEMRUGQ22M4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#pullrequestreview-5155622445>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAIIKIBPK3R75VDHA3HDPAT5OFRQ5AVCNFSNUABEKJSXA33TNF2G64TZHMZDMOBVGA2DIMZ3JFZXG5LFHM2TAOJTGQ4DEMJZHCQXMAQ>
.
Triage notifications, keep track of coding agent tasks and review pull
requests on the go with GitHub Mobile for iOS
<https://github.com/notifications/mobile/ios/AAIIKIF22YYUUJRQK4SEQBT5OFRQ5A5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTKMJVGU3DEMRUGQ22M4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJKTGN5XXIZLSL5UW64Y>
and Android
<https://github.com/notifications/mobile/android/AAIIKIBZP434WVM5R42ZQ7T5OFRQ5A5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTKMJVGU3DEMRUGQ22M4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLTGN5XXIZLSL5QW4ZDSN5UWI>.
Download it today!
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Clippy has accumulated seven per-lint options for suppressing a lint in test code, each implemented from scratch inside the lint it controls: - `allow-dbg-in-tests` (`dbg_macro`) - `allow-expect-in-tests` (`expect_used`) - `allow-indexing-slicing-in-tests` (`indexing_slicing`) - `allow-panic-in-tests` (`panic`) - `allow-print-in-tests` (`print_stderr`, `print_stdout`) - `allow-unwrap-in-tests` (`unwrap_used`) - `allow-useless-vec-in-tests` (`useless_vec`) `allow-in-tests` replaces them with one option naming lints directly. Suppression happens once at diagnostic emission in `clippy_utils`, so it covers every lint rather than needing per-lint plumbing, and it resolves test-ness against the same node rustc resolves `#[allow]` against. Every one of those seven lints is emitted from a late pass, because the existing technique needs a `TyCtxt` and a HIR node; early-pass lints could never have had such an option. They are covered here as well: they have no parent chain to walk, so a collector records the spans of `#[cfg(test)]` items and `#[test]` functions in `check_crate` and emission matches a lint's span against them. The walk is skipped entirely unless the option names a lint. The superseded options keep working and are deprecated in documentation only, with no warning on use; `#[replaced_by_allow_in_tests]` records the replacement so the warning can be turned on once this has shipped. Lint names are resolved against the declared lints at startup, and names which don't resolve are reported with a span into `clippy.toml`. changelog: Added [`allow-in-tests`] configuration to suppress any lint in test code changelog: Deprecated the per-lint `allow-*-in-tests` options in favor of [`allow-in-tests`]
26c9a14 to
b657b66
Compare
allow-in-tests (and inverse check-in-tests) configurationallow-in-tests configuration
View all comments
Fixes #12581
NOTE: some of the code for this PR originated from an LLM (Claude Opus 5), so I am disclosing this per the
disclosure guidelines. I (the human) reviewed all code and wrote the human-facing parts (PR description, documentation, error messages) myself, to comply with the LLM policy which also applies to
rust-clippyrepo.Problem
Clippy has nine per-lint options controlling whether a lint applies to test code:
allow-dbg-in-testsdbg_macroallow-expect-in-testsexpect_usedallow-indexing-slicing-in-testsindexing_slicingallow-large-stack-frames-in-testslarge_stack_framesallow-panic-in-testspanicallow-print-in-testsprint_stderr,print_stdoutallow-unwrap-in-testsunwrap_usedallow-useless-vec-in-testsuseless_veccheck-incompatible-msrv-in-testsincompatible_msrvThey all express the same idea: should this lint be applied to test code?. Each is implemented from scratch inside the lint it controls.
What each individual
allow-*-in-testscosts to addA recent proposed addition (#17516) of a single option (
allow-panic-in-result-fn-in-tests) touches 8 files and adds 66 lines, of which only a handful were the actual behavior. None of this code is shared with the otherallow-*-in-testsoptions.Each
allow-*option addition pollutes user-facing documentation in the form of extra search completions, CHANGELOG entries, and option entries.It is also a burden on maintainers to continue reviewing PRs for each consequent
allow-*.Given the number of these
allow-*options, I think we can safely assume this trend will continue, and the costs will continue to be accrued.Behavioral drift
Because each lint implements the check itself, each has its own method of determining "are we inside test code currently?" Already there is variance: some use
expr.hir_id, otherslocal_def_id_to_hir_id(..). Nothing enforces consistency, despite theallow-*-in-testsnaming scheme implying some sort of common implementation.For users
One option name to learn and grep for to solve the problem of "how do I turn off lint X in a test?" instead of a bespoke name per lint would be a much better UX.
Solution
One new option,
allow-in-tests = [...], which suppresses the lints specified in tests.Rather than threading a flag into each lint, suppression happens once at diagnostic emission. This avoids having to refactor every lint to be aware of
allow-in-tests.In order to support non-late-pass lints, there is a new AST pass to collect spans of test config code. When lints are emitted within those test-config spans, they are eligible for suppression via
allow-in-tests.As one would expect, if you specify a non-existent lint in the list for this option, you get an error.
Deprecation
The existing
allow-*-in-testsoptions keep working but are deprecated in documentation only. Setting a legacy allow option does not warn for now. Each option's docs name the exact replacement line to write.#[replaced_by_allow_in_tests]records the mapping in machine-readable form so the warning can be switched on in a follow-up onceallow-in-testshas shipped everywhere.On naming
allow-in-testsallow-in-testsgeneralizes theallow-*-in-testsoptions it supersedes.allow-*is by far the most common prefix inclippy.toml(17 options), and it maps exactly onto#[allow]: the same operation, applied by configuration instead of by attribute. Sticking with historical precedent minimizes mental churn for users.However, thinking ahead to an "inverse" option, one that enables a lint to apply to test code where it didn't before, its worth considering how this PR's new option and that follow-up option (coming in a later PR) would pair together. Here are the pros/cons for the considered naming schemes.
allow-/check-(chosen scheme)allowmatches#[allow]semantics;checkmeans "don't skip".allow-/disallow-disallowrisks "double negative" confusion as it is read.disable-/enable-allow-dbg-in-tests = truemeans don't lint, sodisable-in-testswould be its synonym andenable-in-testsits opposite. Every migrating user has to flip the "sign" of the configuration language (fromallowtodisable).skip-/check-allow-*.Prior art
This has been attempted once before, in #15600 and its continuation rust-lang/rust#156396. That approach suppressed lints whenever
--testwas passed to rustc, which also silenced them in non-test code.@y21and@flip1995independently identified the per-node check — whatclippy_utils::is_in_testdoes, and what the existingallow-*-in-testsoptions do — as the behavior users actually expect, and the PR was closed on that basis. This PR uses that per-node check.Checklist
.stderrfiles)cargo testpasses locallycargo dev fmtandcargo bless --test config-metadatachangelog: Added [
allow-in-tests] configuration to suppress any lint in test codechangelog: Deprecated the per-lint
allow-*-in-testsoptions in favor ofallow-in-testsPrior requests for this feature
This is not a new idea. It has been asked for repeatedly since 2016.
disabled-in-testsconfiguration to disable arbitrary lints in tests #15600disabled-in-testsconfiguration to Clippy rust#156396Related
allow-unwrap-in-benchesoption #16122clippy::unwrap_unsedin integration tests withallow-unwrap-in-tests=trueand helper function #11119unwrap_expect_usedin test modules defined by complex configuration predicates (#16369) #16989#[expect(clippy::missing_docs_in_private_items)]causesunfulfilled_lint_expectationswhen building tests #16934Follow-up work suggested by these related issues: