Skip to content

Commit 0775ef3

Browse files
committed
Fix must_use lint behavior for block expressions
The admonition for `must_use` claimed that wrapping a value in a block expression, as in `{ f() };`, suppresses the `unused_must_use` lint. Testing against rustc shows this isn't true -- the lint explicitly looks through block expressions (including `unsafe` and labeled blocks) to their trailing expression before checking. Let's fix that.
1 parent 93fe20d commit 0775ef3

1 file changed

Lines changed: 35 additions & 7 deletions

File tree

src/attributes/diagnostics.md

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,19 @@ impl Tr for () {
449449
// ^^^^^^^^^^^ ERROR: Unused return value that must be used.
450450
```
451451

452+
r[attributes.diagnostics.must_use.block-expr]
453+
When checking the [expression] of an [expression statement] for [attributes.diagnostics.must_use.type], [attributes.diagnostics.must_use.fn], [attributes.diagnostics.must_use.trait], and [attributes.diagnostics.must_use.trait-function], the lint looks through [block expressions][block expression] (including [`unsafe` blocks] and [labeled block expressions]) to the trailing expression of each. This applies recursively for nested block expressions.
454+
455+
```rust,compile_fail
456+
#![deny(unused_must_use)]
457+
#[must_use]
458+
fn f() {}
459+
460+
{ f() }; // ERROR: The lint looks through block expressions.
461+
unsafe { f() }; // ERROR: The lint looks through `unsafe` blocks.
462+
{ { f() } }; // ERROR: The lint looks through nested blocks.
463+
```
464+
452465
r[attributes.diagnostics.must_use.trait-impl-function]
453466
When used on a function in a trait implementation, the attribute does nothing.
454467

@@ -470,22 +483,35 @@ impl Tr for () {
470483
> `rustc` lints against use on functions in trait implementations. This may become an error in the future.
471484
472485
> [!NOTE]
473-
> Wrapping the value, even trivially, will suppress the lint.
486+
> Wrapping the result of a `#[must_use]` function in certain expressions can suppress the [fn-based check][attributes.diagnostics.must_use.fn], because the [expression] of the [expression statement] is not a [call expression] or [method call expression] to a `#[must_use]` function. The [type-based check][attributes.diagnostics.must_use.type] still applies if the type of the overall expression is `#[must_use]`.
474487
>
475488
> ```rust
476489
> #![deny(unused_must_use)]
477490
> #[must_use]
478491
> fn f() {}
479492
>
480-
> // None of these trigger the `unused_must_use` lint.
481-
> (f(),);
482-
> Some(f());
483-
> { f() };
484-
> if true { f() } else {};
485-
> match true {
493+
> // The fn-based check does not fire for any of these, because the
494+
> // expression of the expression statement is not a call to a
495+
> // `#[must_use]` function.
496+
> (f(),); // Expression is a tuple, not a call.
497+
> Some(f()); // Callee `Some` is not `#[must_use]`.
498+
> if true { f() } else {}; // Expression is an `if`, not a call.
499+
> match true { // Expression is a `match`, not a call.
486500
> _ => f()
487501
> };
488502
> ```
503+
>
504+
> ```rust,compile_fail
505+
> #![deny(unused_must_use)]
506+
> #[must_use]
507+
> struct MustUse;
508+
> fn g() -> MustUse { MustUse }
509+
>
510+
> // Despite the `if` expression not being a call, the type-based check
511+
> // fires because the type of the expression is `MustUse`, which has
512+
> // the `#[must_use]` attribute.
513+
> if true { g() } else { MustUse }; // ERROR: Must be used.
514+
> ```
489515
490516
> [!NOTE]
491517
> Using a [let statement] with a pattern of `_` when a must-used value is purposely discarded is idiomatic.
@@ -700,6 +726,7 @@ The first error message includes a somewhat confusing error message about the re
700726

701727
[Clippy]: https://github.com/rust-lang/rust-clippy
702728
[`Drop`]: ../special-types-and-traits.md#drop
729+
[`unsafe` blocks]: ../expressions/block-expr.md#unsafe-blocks
703730
[attribute]: ../attributes.md
704731
[attributes]: ../attributes.md
705732
[block expression]: ../expressions/block-expr.md
@@ -715,6 +742,7 @@ The first error message includes a somewhat confusing error message about the re
715742
[impl trait]: ../types/impl-trait.md
716743
[implementation]: ../items/implementations.md
717744
[item]: ../items.md
745+
[labeled block expressions]: ../expressions/block-expr.md#labeled-block-expressions
718746
[let statement]: ../statements.md#let-statements
719747
[macro definition]: ../macros-by-example.md
720748
[module]: ../items/modules.md

0 commit comments

Comments
 (0)