Attributes and Conditional Compilation
#[forbid(...)]
#[forbid(lint_name)] (and #![forbid(lint_name)]) is the strictest lint level in Rust: like #[deny(...)], it turns any violation of lint_name into a hard compilation error, and it additionally makes it a compilation error (E0453) to use #[allow(lint_name)], #[expect(lint_name)], or #[warn(lint_name)] anywhere inside the enclosed scope.
The Lint Level Hierarchy
Lint Severity & Override Rules:
#[allow(lint)] <---> #[expect(lint)] <---> #[warn(lint)] <---> #[deny(lint)]
(All four levels can freely override one another in narrower inner scopes)
|
v (One-way lock!)
#[forbid(lint)]
(Cannot be downgraded by ANY inner `#[allow]`, `#[expect]`, or `#[warn]` -> Error E0453!)
| Attribute | Violation Outcome | Can Be Overridden by Inner #[allow(...)]? |
|---|---|---|
#[allow(lint)] |
Silent | N/A |
#[expect(lint)] |
Silent (warns if unfulfilled) | Yes |
#[warn(lint)] |
Compiler warning | Yes |
#[deny(lint)] |
Compiler error | Yes |
#[forbid(lint)] |
Compiler error | No (E0453) |
Attempting to Override #![forbid(...)] Fails with E0453
If a crate places #![forbid(unsafe_code)] at the top of src/lib.rs, a security auditor only needs to read Line 1 of src/lib.rs (and Cargo.toml / build.rs) to know that no submodule, function, or macro expansion in the crate can smuggle in an #[allow(unsafe_code)] override:
// ❌ does not compile
#![forbid(unsafe_code)]
mod sneaky {
// Error E0453: cannot override `#![forbid(unsafe_code)]`!
#[allow(unsafe_code)]
pub fn touch_raw_ptr(p: *const i32) -> i32 {
unsafe { *p }
}
}
fn main() {}
error[E0453]: allow(unsafe_code) incompatible with previous forbid
--> src/main.rs:6:13
|
2 | #![forbid(unsafe_code)]
| ----------- `forbid` level set here
...
6 | #[allow(unsafe_code)]
| ^^^^^^^^^^^ overruled by previous forbid
Combining Crate-Wide #![deny] with Module-Level #[forbid]
If 95% of your crate is safe Rust and 1 low-level module (mod ffi_bridge) requires unsafe:
- Option A: Put
#![deny(unsafe_code)]at the crate root and#[allow(unsafe_code)]onmod ffi_bridge. - Option B: Put
#[forbid(unsafe_code)]on the safe business-logic modules (#[forbid(unsafe_code)] mod parser;) sounsafeis permanently locked out of those modules:
#[forbid(unsafe_code)]
mod business_logic {
pub fn calculate_discount(price_cents: u64) -> u64 {
price_cents * 90 / 100
}
}
fn main() {
println!("discounted: {}", business_logic::calculate_discount(1000));
}
discounted: 900