Attributes and Conditional Compilation

#[deny(...)]

#[deny(lint_name)] (and #![deny(lint_name)]) elevates a lint to a hard compilation error: if the lint triggers anywhere within the annotated scope (unless overridden by a narrower inner #[allow(lint_name)]), compilation fails immediately with a non-zero exit code.

How #[deny] Interacts with Narrower #[allow] Overrides

Unlike #[forbid(...)] (which locks the lint level permanently), #[deny(...)] can still be overridden in an inner scope by #[allow(...)] or #[expect(...)]. This makes #![deny(...)] ideal for enforcing a strict default policy across a crate or module while still permitting explicitly documented, audited exceptions on individual functions:

Crate or Module Policy:
  #![deny(clippy::undocumented_unsafe_blocks)]
        |
        +-- Unannotated `unsafe {}` block ---> Hard Compile Error!
        |
        +-- Specific legacy function with `#[allow(...)]` ---> Allowed Exception!

Example: Triggering a Hard Error with #[deny(...)]

By default, ignoring a #[must_use] return value (such as a Result) emits the unused_must_use warning. Applying #[deny(unused_must_use)] (or #[deny( warnings )]) turns that warning into a compilation error:

// ❌ does not compile
#[deny(unused_must_use)]
fn process_transaction() {
    let res: Result<u32, &str> = Err("insufficient funds");
    // Ignored `Result` triggers `unused_must_use`, which is denied here:
    res.map(|v| v + 1);
}

fn main() {
    process_transaction();
}
error: unused `Result` that must be used
 --> src/main.rs:6:5
  |
6 |     res.map(|v| v + 1);
  |     ^^^^^^^^^^^^^^^^^^
  |
  = note: this `Result` may be an `Err` variant, which should be handled
note: the lint level is defined here
 --> src/main.rs:2:8
  |
2 | #[deny(unused_must_use)]
  |        ^^^^^^^^^^^^^^^

Audited Local Override Inside a #![deny(...)] Scope

When a crate denies a lint (such as unsafe_code or dead_code), an inner item can explicitly opt out with #[allow(..., reason = "...")]:

#![deny(dead_code)]

pub fn active_entry_point() -> u32 {
    42
}

// Even though the crate has `#![deny(dead_code)]`, an inner `#[allow]` can
// override `#![deny]` for a specific item:
#[allow(dead_code, reason = "Kept for upcoming benchmark harness")]
fn benchmark_helper() -> u32 {
    active_entry_point() * 2
}

fn main() {
    println!("entry point: {}", active_entry_point());
}
entry point: 42

Built-In Lints That Are deny-by-Default in rustc

Several safety and correctness lints are set to deny by default in the compiler (such as static_mut_refs and unsafe_op_in_unsafe_fn in later Rust 2024 lint stages, improper_ctypes_definitions, ambiguous_associated_items, and mutable_transmutes).