Skip to content

Strange behaviour with #[expect]'ing lints from #[derive] attributes #15708

@ejmount

Description

@ejmount

Summary

Running clippy on the below code sample raises both an error from the deny-by-default derived_hash_with_manual_eq and a unfulfilled_lint_expectations warning from the #[expect] failing to fire. This would be understandable on its own, if it were a case of the #[expect] not properly applying to the code generated by the #[derive]. However, I'm left confused because if I instead use #[allow], that emits nothing as I'd expect, i.e. allow, but not expect, is correctly picking up on the lint and silencing it.

I checked my understanding of #[expect] in the reference, but the relevant section only seems to talk about what happens if the warning is silenced by other things in inner scopes relative to the #[expect], rather than how #[expect] applies to non-warnings.

Would it be possible to clarify this behaviour? I also don't know if this is coming from clippy or rustc proper

Lint Name

unfulfilled_lint_expectations

Reproducer

I tried this code:

#[expect(
    clippy::derived_hash_with_manual_eq,
    reason = "..."
)]
#[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord, Hash)]
pub struct Span {
    start: usize,
    end: usize,
}

impl PartialEq for Span {
    fn eq(&self, other: &Self) -> bool {
        self.start
            .cmp(&other.start)
            .then(self.end.cmp(&other.end))
            .is_eq()
    }
}

I saw this happen:

27 | #[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord, Hash)]
   |                                                   ^^^^
   |
note: `PartialEq` implemented here
  --> src\token_data.rs:89:1
   |
89 | impl PartialEq for Span {
   | ^^^^^^^^^^^^^^^^^^^^^^^
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#derived_hash_with_manual_eq
   = note: `#[deny(clippy::derived_hash_with_manual_eq)]` on by default

warning: this lint expectation is unfulfilled
  --> src\token_data.rs:24:5
   |
24 |     clippy::derived_hash_with_manual_eq,
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: In order to override dummy values in test mode
   = note: `#[warn(unfulfilled_lint_expectations)]` on by default

I expected to see this happen:

  • No emitted lints.

Version

rustc 1.88.0 (6b00bc388 2025-06-23)
binary: rustc
commit-hash: 6b00bc3880198600130e1cf62b8f8a93494488cc
commit-date: 2025-06-23
host: x86_64-pc-windows-msvc
release: 1.88.0
LLVM version: 20.1.5

Additional Labels

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: Clippy is not doing the correct thingI-false-positiveIssue: The lint was triggered on code it shouldn't have

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions