Skip to content

S25: #[allow(...)] to silence warnings

S25Severity: Medium

A warning is hidden instead of fixed, with no reason given.

Clippy: allow_attributes_without_reason, allow_attributes

#[allow(unused_variables)]
pub fn apply_discount(total_cents: u64, percent: u64) -> u64 {
total_cents // the warning said that `percent` is never used. It was right.
}
#[allow(unused_must_use)]
pub fn save_report(path: &Path, report: &str) {
fs::write(path, report); // a failed write is now silent
}
#[allow(clippy::cast_possible_truncation)]
pub fn quantity(input: u64) -> u16 {
input as u16 // 70_000 becomes 4_464
}

Each #[allow(...)] hides a real bug:

  • unused_variables: the discount is never applied. The function returns the full price.
  • unused_must_use: a failed write is ignored. See S08: Ignored Result (let _ =, .ok();).
  • clippy::cast_possible_truncation: 70_000 as u16 is 4_464, with no error.

The test worse_hides_real_bugs shows all three.

  • The task says “fix the warnings”. An #[allow] makes the warning go away in one line, and the agent counts that as done.
  • The compiler output suggests it. Every Clippy warning ends with “to override … add #[allow(...)]”. An agent that reads the last line takes it.
  • It is common in training data. Many projects have #[allow(dead_code)] at the top of a file, often for good reasons during early development.
  • The real fix is larger. Using percent correctly, or returning a Result, changes the function signature and its callers. The #[allow] changes nothing else.
  • The warning was right. Rust warnings and the default Clippy lints have few false positives. A hidden warning is usually a hidden bug.
  • It stays forever. If the code changes so that the warning no longer applies, #[allow] says nothing. It keeps silencing the lint for any new code in the same item.
  • It gives no reason. The reviewer cannot tell a considered exception from a shortcut.
  • On a module or crate, #![allow(...)] turns a lint off for all the code below it, also for code written later.
  1. WorseThree #[allow] attributes. Each one hides a bug.

    #[allow(unused_variables)]
    pub fn apply_discount(total_cents: u64, percent: u64) -> u64 {
    total_cents // the warning said that `percent` is never used. It was right.
    }
    #[allow(unused_must_use)]
    pub fn save_report(path: &Path, report: &str) {
    fs::write(path, report); // a failed write is now silent
    }
    #[allow(clippy::cast_possible_truncation)]
    pub fn quantity(input: u64) -> u16 {
    input as u16 // 70_000 becomes 4_464
    }
  2. BetterThe discount is applied, the write error is returned, and u16::try_from rejects a value that does not fit.

    pub fn apply_discount(total_cents: u64, percent: u64) -> u64 {
    total_cents - total_cents * percent.min(100) / 100
    }
    pub fn save_report(path: &Path, report: &str) -> io::Result<()> {
    fs::write(path, report)
    }
    pub fn quantity(input: u64) -> Result<u16, TryFromIntError> {
    u16::try_from(input)
    }

Fix the code that the warning describes. The fix is often a type change: -> io::Result<()> instead of (), or try_from instead of as. The test better_fixes_them checks each fix.

Sometimes the lint is wrong for this code. Then use #[expect(...)] with a reason:

/// The percentage of paid orders, from 0 to 100.
#[expect(
clippy::cast_possible_truncation,
reason = "the percentage is at most 100, so it fits in a u8"
)]
pub fn paid_percent(paid: u64, total: u64) -> u8 {
if total == 0 {
return 0;
}
(paid.min(total) * 100 / total) as u8
}
  • #[expect] instead of #[allow] (stable since Rust 1.81). If the lint stops firing, the compiler warns that the expectation is unfulfilled, so the attribute cannot outlive its reason.
  • reason = "..." says why the lint does not apply. Here the code makes sure that the value is at most 100.
  • On the smallest item: one function or one statement, not the module.
  • Generated code, FFI bindings, and test helpers often have module-level allows with a comment. That is normal.
  • Search the diff for allow(. Every new one needs a reason.
  • Clippy allow_attributes (restriction group) reports every #[allow] and suggests #[expect]. allow_attributes_without_reason (restriction group) reports an #[allow] or #[expect] with no reason. Both are off by default. Turn them on in [lints.clippy] of Cargo.toml.
  • Look at [lints] in Cargo.toml too. A lint set to "allow" there turns it off for the whole crate.
Do not add #[allow(...)] to make a warning go away. Fix the code that the
warning describes. If the lint is wrong for one specific item, use
#[expect(lint_name, reason = "...")] on that item only, and explain in the
reason why the lint does not apply. Remove any #[allow] you added in this
change, then run cargo clippy --all-targets -- -D warnings and the tests.

Related: S08: Ignored Result (let _ =, .ok();), S02: unwrap() / expect() in non-test code, and Tooling.