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)] on mod ffi_bridge.
  • Option B: Put #[forbid(unsafe_code)] on the safe business-logic modules (#[forbid(unsafe_code)] mod parser;) so unsafe is 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