Skip to content

unfulfilled_lint_expectations false-positive for unused struct field inside unused struct #160942

Closed

Description

@opara-tor

Code

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=5d195cb9edb0e5df995955f71b9af04e

// This struct is only used on windows.
#[cfg_attr(not(target_os = "windows"), expect(unused))]
struct Foo {
    // This field is never used regardless of whether on windows or other.
    #[expect(unused)]
    x: u32,
}

Current output

On linux:

warning: this lint expectation is unfulfilled
 --> src/lib.rs:5:14
  |
5 |     #[expect(unused)]
  |              ^^^^^^
  |
  = note: `#[warn(unfulfilled_lint_expectations)]` on by default

Desired output

No warning.

Rationale and extra context

In this example, we have a struct Foo that may or may not be used, depending on the platform. So we conditionally set it to expect(unused).

We also have a field x that is never used, regardless of the platform. So we always set it to expect(unused).

It seems that when we have an expect(unused) field inside an expect(unused) struct, rust will warn that the field's lint expectation is unfulfilled. But this seems wrong because the field is always unused.

We could instead use #[cfg_attr(target_os = "windows", expect(unused))] on the field to avoid the warning, but this seems unnecessary and overly verbose, and it's easy for the two "cfg_attr" conditions to become out-of-sync.

Rust Version

This is in the playground, where:

Build using the Nightly version: 1.99.0-nightly (2026-08-10 12c36e2539c54397c51d)

Anything else?

No response

Activity

  1. added
    A-diagnosticsArea: Messages for errors, warnings, and lints
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Aug 11, 2026
  2. dronavallipranav commented on Aug 12, 2026

    @dronavallipranav
    Contributor

    @rustbot claim

    gonna try to take a look at this as a first real issue

  3. cclfmht commented on Aug 13, 2026

    @cclfmht
    Contributor

    From what I understand, if dead_code lint fires on the struct itself (e.g., struct Foo is never constructed, in your example this is suppressed by expect(unused) at line 1), it won't further fire on all its fields one-by-one. Thus, adding a #[expect(unused)] on the struct's field triggers unfulfilled_lint_expectations lint.

    If you change struct Foo to pub struct Foo, then you can see dead_code lint fire on field x, because now Foo is reachable from outside world and we cannot treat it as dead code. In that case, fields of Foo would be checked individually.

    Current behavior seems reasonable to me, I am not sure if there is something really need to be fixed here.

  4. opara-tor commented on Aug 13, 2026

    @opara-tor
    Author

    Current behavior seems reasonable to me, I am not sure if there is something really need to be fixed here.

    Yeah I guess this makes sense from a technical viewpoint if you know how the unfulfilled_lint_expectations lint works. It does mean that expect(unused) is a bit of a footgun if you don't though. For example adding #![allow(unused)] to the lib.rs will break all instances of #[expect(unused)] on struct fields for non-public structs in the crate. (Edit: This was a bad example.) So I guess then that expect(unused) isn't the right tool for enforcing that something in the code isn't used, which is maybe a bit unexpected.

  5. opara-tor commented on Aug 25, 2026

    @opara-tor
    Author

    I see there's discussion in #161005 but I'll post here so I don't sidetrack it.

    While I do think it would be nice if the original example worked, I also think that if the proposed behaviour isn't the intended behaviour, then just improving the error message would be useful. The compiler does a good job at explaining confusing/unexpected behaviour, and I think this is a case where a better error (well, warning) message could make the intent of expect clearer. Some sort of message that the expect(unused) can never be satisfied for fields when the struct itself is unused. But I'm sure this is easier said than done.

  6. added 6 commits that reference this issue on Sep 18, 2026
    76eac87
    0dbacdb
    1fa08ef
    b2ca77f
    d67f5f4
    e4c2a93
  7. added a commit that references this issue on Sep 19, 2026
    8b750f2
  8. added 2 commits that reference this issue on Sep 19, 2026
    8acdf20
    893ad9a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

A-diagnosticsArea: Messages for errors, warnings, and lintsT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions