future-incompatibility: default #[allow(...)] incompatible with #[forbid(...)]
Opened this issue · 6 comments
Code
// - have at least one doctest, and
//
// - at the top of lib.rs `forbid` any of the `allow` that rustdoc injects by default. For example
#![doc(test(attr(forbid(unused, dead_code))))]Reproduction Steps
cargo test
...
warning: allow(unused_extern_crates) incompatible with previous forbid
--> src/lib.rs:16:9
|
1 | #![forbid(unused, dead_code)]
| ------ `forbid` level set here
2 | #[allow(unused_extern_crates)]
| ^^^^^^^^^^^^^^^^^^^^ overruled by previous forbid
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #81670 <https://github.com/rust-lang/rust/issues/81670>
= note: `#[warn(forbidden_lint_groups)]` (part of `#[warn(future_incompatible)]`) on by default
Version
rustdoc 1.91.0 (f8297e351 2025-10-28)
installed with rustup, standard x64 Linux
Suggestions
Please,
- provide a configuration for
rustdocNOT to inject the default#[allow(...)]. If there already exists such a configuration, please document - I couldn't see it obvious anywhere at https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html. Or - document which exact lints are injected, so that the author/doc writer can
#[allow(...)]them individually, instead of using a group lint.
Related to #81670.
Should this issue stay in T-rustdoc, or should it go to rust-lang/cargo?
The "future incompatibility" angle is a bit of a red herring here. The FCW is specific to forbidding lint groups, if the forbid listed the individual lints that rustdoc injects then it would already get an error in current and past versions of Rust. So the "forbid to cancel out undesirable allow" approach suggested doesn't work either way.
The way it's phrased in under the "suggestions" heading seems more plausible: a feature request for rustdoc to prevent the allow(..)s from being injected in the first place. Then there's no need to document which lints are "allow-by-default in doctests": authors can simply add allows to some or all doctests based on what lints are triggered in those specific doctests.
(I don't see any way this would be relevant for rust-lang/cargo.)
EDIT: Not too bad, since this doesn't affect crates from being compiled - it only affects cargo test/cargo doc and any CI that uses that.
https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html#showing-warnings-in-doctests suggests we
- "can add
#![warn(unused)]to the top of your example" and - "can also use
[#![doc(test(attr(warn(unused))))]](https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#testattr)"
Then forbid is just one step in the same direction.
So, once we have a solution/workaround, we need to update the above linked docs, too.
Oh, I didn't realize that the scoping was such that lint levels injected with doc(test(attr(...))) are more specific than the default allows. In that case, can't you achieve the desired effect with doc(test(attr(deny(warnings))))?
The only difference between deny and forbid is that the latter rules out downgrading the warning level in nested scopes, e.g., in macro-generated code. But that's a footgun, not a feature! As noted at the time the FCW was introduced (#80988, #81218), common Rust patterns expect to be able to allow lints locally, and a blanket forbid(warnings) turns that into compilation errors that can only be fixed by removing the forbid.
can't you achieve the desired effect with
doc(test(attr(deny(warnings))))?
General intention: I know it may sound bizarre, but, the general intention was specifically to forbid, not just deny. That is, I wanted to restrict any doctest from issuing #[allow(...)], or, to see, how/where it fails if the consumer forbid-s selected warnings/groups.
Specific problem/intention: To improve my doctests/related code (in my case, my macro_rules!-generated code then tested in the doctests). For that, I need
- to know what exact lints rustdoc/
cargo docallows, so that I can override them withforbidordeny, so that I notice my mistakes. But, I also wantforbid, so that I can check/remind myself whether I injected any#[allow(...)]myself, or, whether any 3rd party macro injected them. And/or - disable/undo
#[allow(...)]on all lints that rustdoc/cargo docallows.
For some (compile_fail) rustdocs I have them in separate files, and then I include them in rustdoc 2x: once for stable, and once for nightly with an extra attribute being the expected error code:
/// Doc prose...
/// ```compile_fail
#[doc = include_str!("../violations_coverage/unsafe_method/some_args/arg.rs")]
/// ```
#[macro_export]
macro_rules! unsafe_method {...}
/// ```compile_fail,E0133
#[doc = include_str!("../violations_coverage/unsafe_method/some_args/arg.rs")]
/// ```
#[cfg(doctest)]
pub const _: () = {};By having those doctest sources in separate .rs files, I symlinked to them from a dummy crate, and then I can run cargo check or cargo expand as I wish, and/or prevent lints from a dummy src/lib.rs or Cargo.toml. But I don't want to do that for successful doctests.
Also (even though not related to this issue), forbid is useful e.g. on unsafe_code, maybe deprecated...
forbid(unsafe_code) has legitimate uses. Blanket forbid(warnings) is a bad idea because it applies not only to your "mistakes" but also to implementation details of third party libraries you have no ability or reason to fix. I'm not on any relevant teams, so it's not my call, but I don't think a feature request predicated on "I specifically want forbid(warnings), not deny(warnings)" is very convincing.
Since you already have a highly unconventional setup for your doctests, you have alternatives if you really want to load this footgun. For example, you could add lints.rustc.warnings = "forbid" in the relevant Cargo.toml files so that forbid(warnings) applies when building the code outside of doctests. I'm not sure if that's going to trigger the FCW or the intended "allow vs forbid" error today but there will be a diagnostic and eventually the FCW will go away and forbid-lint-groups will work as intended.