Change:
I tried this code: I expected to catch lint and suppress it, as it should. Instead, compiler issues a diagnostic both about unused value and unfulfilled lint expectation: It does work as expected though if is applied to function. I used the latest stable (Rust 1.81), but it is reproducible both on latest beta (2024-09-04 ) and latest nightly (2024-09-05 ). labels: +F-lintreasons, +A-diagnostics <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
claim
This issue is not limited to just . Others attrs like or don't work either. For instance, the following variant of the reproducer with replaced with emits the warning even though it shouldn't: Playground: This broken behaviour goes as far back as at least 2021 (couldn't build and test earlier rustc) We emit the lint on the HIR node corresponding to . However, the or levels, which are supposed to override the default level of the lint, are associated with or bound to the node underlying the as shown in the figure below: The end result is that the lint's level is never knocked down to or since when searching for levels we start from the node on which the lint is triggered and go up the chain of parents. So in this case we look for the level on and then on the and so on. We never look for it on the where the levels actually exist. As it happens in the lowered HIR does have the / attrs for both the node and its child node. However when adding the levels in the we do not add anything for the node as shown here (note the comment): If were to add the level for as well the issue will go away. However, if we do that we will still have one problem in the case of which is that we will end up with two expectations and the one on the will not be fullfilled and hence we will still see a warning about unfulfilled expectation. To prevent that I think we should take the following approach: when an attr like or etc. are present for more than one HIR node in the and if one node is the (direct or indirect) parent of the other node, then add the level only_ for parent node. This will take care of any lints on the child (since we always walk up the HIR hierachy as mentioned earlier) as well as the parent and it will not cause any warnings about missed expectations. I am new to this area so if someone can review this idea or suggest alternatives that will be great. since you seemed to have worked in this area recently, can you please weigh in?
Your investigation is accurate, and the fix is simple: visit the statement node when building lint levels. Do you mind making such a pr ? I don't think we need any special adjustment for cases, duplicated attributes are already taken into account, but a test won't hurt.
Thanks . Yes, I'll put together a PR. My only concern is if all we do is add the level at , although it will work perfectly for , etc., in the case of we will end up with an unfulfilled expectation on the node which will appear to the user as a warning. But anyway let me have a go and see what happens.
Opened draft PR that adds the lint on . It does suffer from the extra warning problem.

Discussion:
intravisit::walk_foreign_item(self, it); } <del> fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) { // We will call `add_id` when we walk // the `StmtKind`. The outer statement itself doesn't // define the lint levels. intravisit::walk_stmt(self, e); </del> <ins> fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) { self.add_id(s.hir_id); intravisit::walk_stmt(self, s); </ins> } fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {

|                  ^^^^^^^^^^^^^^^^^^^^^^^ error: this lint expectation is unfulfilled <ins> --> tests/ui/expect_tool_lint_rfc_2383.rs:36:18 | LL |         #[expect(invalid_nan_comparisons)] |                  ^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: this lint expectation is unfulfilled </ins> --> tests/ui/expect_tool_lint_rfc_2383.rs:107:14 | LL |     #[expect(clippy::almost_swapped)]

LL |     #[expect(clippy::overly_complex_bool_expr)] |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <del> error: aborting due to 6 previous errors </del> <ins> error: aborting due to 7 previous errors </ins>

<ins> // Regression test for issue #130142 // Checks that we emit no warnings when a lint's level // is overridden by an expect or allow attr on a Stmt node //@ check-pass #[must_use] pub fn must_use_result() -> i32 { 42 } fn main() { #[expect(unused_must_use)] must_use_result(); #[allow(unused_must_use)] must_use_result(); } </ins>

#[expect(invalid_nan_comparisons)] //~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] <ins> //~| WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] //~| NOTE duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` </ins> let _b = x == 5; } }

LL |         #[expect(invalid_nan_comparisons)] |                  ^^^^^^^^^^^^^^^^^^^^^^^ <del> warning: 2 warnings emitted </del> <ins> warning: this lint expectation is unfulfilled --> $DIR/expect_tool_lint_rfc_2383.rs:38:18 | LL |         #[expect(invalid_nan_comparisons)] |                  ^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: 3 warnings emitted </ins>

pub fn normal_test_fn() { #[expect(unused_mut, reason = "this expectation will create a diagnostic with the default lint level")] //~^ WARNING this lint expectation is unfulfilled <ins> //~| WARNING this lint expectation is unfulfilled </ins> //~| NOTE this expectation will create a diagnostic with the default lint level <ins> //~| NOTE this expectation will create a diagnostic with the default lint level //~| NOTE duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` </ins> let mut v = vec![1, 1, 2, 3, 5]; v.sort(); // Check that lint lists including `unfulfilled_lint_expectations` are also handled correctly #[expect(unused, unfulfilled_lint_expectations, reason = "the expectation for `unused` should be fulfilled")] //~^ WARNING this lint expectation is unfulfilled <ins> //~| WARNING this lint expectation is unfulfilled //~| NOTE the expectation for `unused` should be fulfilled </ins> //~| NOTE the expectation for `unused` should be fulfilled //~| NOTE the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message <ins> //~| NOTE the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message //~| NOTE duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` </ins> let value = "I'm unused"; }

= note: this expectation will create a diagnostic with the default lint level warning: this lint expectation is unfulfilled <del> --> $DIR/expect_unfulfilled_expectation.rs:24:22 </del> <ins> --> $DIR/expect_unfulfilled_expectation.rs:17:14 | LL |     #[expect(unused_mut, reason = "this expectation will create a diagnostic with the default lint level")] |              ^^^^^^^^^^ | = note: this expectation will create a diagnostic with the default lint level = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: this lint expectation is unfulfilled --> $DIR/expect_unfulfilled_expectation.rs:27:22 | LL |     #[expect(unused, unfulfilled_lint_expectations, reason = "the expectation for `unused` should be fulfilled")] |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the expectation for `unused` should be fulfilled = note: the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message warning: this lint expectation is unfulfilled --> $DIR/expect_unfulfilled_expectation.rs:27:22 </ins> | LL |     #[expect(unused, unfulfilled_lint_expectations, reason = "the expectation for `unused` should be fulfilled")] |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the expectation for `unused` should be fulfilled = note: the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message <ins> = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` </ins> <del> warning: 4 warnings emitted </del> <ins> warning: 6 warnings emitted </ins>