diff --git a/src/attributes/diagnostics.md b/src/attributes/diagnostics.md index b294ec5fc7..561d381070 100644 --- a/src/attributes/diagnostics.md +++ b/src/attributes/diagnostics.md @@ -345,120 +345,198 @@ The [RFC][1270-deprecation.md] contains motivations and more details. [1270-deprecation.md]: https://github.com/rust-lang/rfcs/blob/master/text/1270-deprecation.md + r[attributes.diagnostics.must_use] ## The `must_use` attribute r[attributes.diagnostics.must_use.intro] -The *`must_use` attribute* is used to issue a diagnostic warning when a value -is not "used". +The *`must_use` [attribute]* marks a value that should be used. + +r[attributes.diagnostics.must_use.syntax] +The `must_use` attribute uses the [MetaWord] and [MetaNameValueStr] syntaxes. + +> [!EXAMPLE] +> ```rust +> #[must_use] +> fn use_me1() -> u8 { 0 } +> +> #[must_use = "explanation of why it should be used"] +> fn use_me2() -> u8 { 0 } +> ``` r[attributes.diagnostics.must_use.allowed-positions] -The `must_use` attribute can be applied to user-defined composite types -([`struct`s][struct], [`enum`s][enum], and [`union`s][union]), [functions], -and [traits]. +The `must_use` attribute may be applied to a: + +- [Struct] +- [Enumeration] +- [Union] +- [Function] +- [Trait] + +> [!NOTE] +> `rustc` ignores use in other positions but lints against it. This may become an error in the future. + +r[attributes.diagnostics.must_use.duplicates] +The `must_use` attribute may be used only once on an item. + +> [!NOTE] +> `rustc` lints against any use following the first. This may become an error in the future. r[attributes.diagnostics.must_use.message] -The `must_use` attribute may include a message by using the -[MetaNameValueStr] syntax such as `#[must_use = "example message"]`. The -message will be given alongside the warning. +The `must_use` attribute may include a message by using the [MetaNameValueStr] syntax, e.g., `#[must_use = "example message"]`. The message may be emitted as part of the lint. r[attributes.diagnostics.must_use.type] -When used on user-defined composite types, if the [expression] of an -[expression statement] has that type, then the `unused_must_use` lint is -violated. +When the attribute is applied to a [struct], [enumeration], or [union], if the [expression] of an [expression statement] has that type, the use triggers the `unused_must_use` lint. -```rust +```rust,compile_fail +#![deny(unused_must_use)] #[must_use] -struct MustUse { - // some fields -} +struct MustUse(); +MustUse(); // ERROR: Unused value that must be used. +``` -# impl MustUse { -# fn new() -> MustUse { MustUse {} } -# } -# -// Violates the `unused_must_use` lint. -MustUse::new(); +r[attributes.diagnostics.must_use.type.uninhabited] +As an exception to [attributes.diagnostics.must_use.type], the lint does not fire for `Result<(), E>` when `E` is [uninhabited] or for `ControlFlow` when `B` is [uninhabited]. A `#[non_exhaustive]` type from an external crate is not considered uninhabited for this purpose, because it may gain constructors in the future. + +```rust +#![deny(unused_must_use)] +# use core::ops::ControlFlow; +enum Empty {} +fn f1() -> Result<(), Empty> { Ok(()) } +f1(); // OK: `Empty` is uninhabited. +fn f2() -> ControlFlow { ControlFlow::Continue(()) } +f2(); // OK: `Empty` is uninhabited. ``` r[attributes.diagnostics.must_use.fn] -When used on a function, if the [expression] of an [expression statement] is a -[call expression] to that function, then the `unused_must_use` lint is -violated. +If the [expression] of an [expression statement] is a [call expression] or [method call expression] whose function operand is a function to which the attribute is applied, the use triggers the `unused_must_use` lint. -```rust +```rust,compile_fail +#![deny(unused_must_use)] #[must_use] -fn five() -> i32 { 5i32 } - -// Violates the unused_must_use lint. -five(); +fn f() {} +f(); // ERROR: Unused return value that must be used. ``` r[attributes.diagnostics.must_use.trait] -When used on a [trait declaration], a [call expression] of an [expression -statement] to a function that returns an [impl trait] or a [dyn trait] of that trait violates -the `unused_must_use` lint. +If the [expression] of an [expression statement] is a [call expression] or [method call expression] whose function operand is a function that returns an [impl trait] or a [dyn trait] type where one or more traits in the bound are marked with the attribute, the use triggers the `unused_must_use` lint. -```rust +```rust,compile_fail +#![deny(unused_must_use)] #[must_use] -trait Critical {} -impl Critical for i32 {} - -fn get_critical() -> impl Critical { - 4i32 -} - -// Violates the `unused_must_use` lint. -get_critical(); +trait Tr {} +impl Tr for () {} +fn f() -> impl Tr {} +f(); // ERROR: Unused implementor that must be used. ``` r[attributes.diagnostics.must_use.trait-function] -When used on a function in a trait declaration, then the behavior also applies -when the call expression is a function from an implementation of the trait. +When the attribute is applied to a function in a trait declaration, the rules described in [attributes.diagnostics.must_use.fn] also apply when the function operand of the [call expression] or [method call expression] is an implementation of that function. -```rust -trait Trait { +```rust,compile_fail +#![deny(unused_must_use)] +trait Tr { #[must_use] - fn use_me(&self) -> i32; + fn use_me(&self); } -impl Trait for i32 { - fn use_me(&self) -> i32 { 0i32 } +impl Tr for () { + fn use_me(&self) {} } -// Violates the `unused_must_use` lint. -5i32.use_me(); +().use_me(); // ERROR: Unused return value that must be used. +``` + +```rust,compile_fail +# #![deny(unused_must_use)] +# trait Tr { +# #[must_use] +# fn use_me(&self); +# } +# +# impl Tr for () { +# fn use_me(&self) {} +# } +# +<() as Tr>::use_me(&()); +// ^^^^^^^^^^^ ERROR: Unused return value that must be used. +``` + +r[attributes.diagnostics.must_use.block-expr] +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. + +```rust,compile_fail +#![deny(unused_must_use)] +#[must_use] +fn f() {} + +{ f() }; // ERROR: The lint looks through block expressions. +unsafe { f() }; // ERROR: The lint looks through `unsafe` blocks. +{ { f() } }; // ERROR: The lint looks through nested blocks. ``` r[attributes.diagnostics.must_use.trait-impl-function] When used on a function in a trait implementation, the attribute does nothing. +```rust +#![deny(unused_must_use)] +trait Tr { + fn f(&self); +} + +impl Tr for () { + #[must_use] // This has no effect. + fn f(&self) {} +} + +().f(); // OK. +``` + > [!NOTE] -> Trivial no-op expressions containing the value will not violate the lint. Examples include wrapping the value in a type that does not implement [`Drop`] and then not using that type and being the final expression of a [block expression] that is not used. +> `rustc` lints against use on functions in trait implementations. This may become an error in the future. + +r[attributes.diagnostics.must_use.wrapping-suppression] +> [!NOTE] +> 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]`. > > ```rust +> #![deny(unused_must_use)] > #[must_use] -> fn five() -> i32 { 5i32 } +> fn f() {} > -> // None of these violate the unused_must_use lint. -> (five(),); -> Some(five()); -> { five() }; -> if true { five() } else { 0i32 }; -> match true { -> _ => five() +> // The fn-based check does not fire for any of these, because the +> // expression of the expression statement is not a call to a +> // `#[must_use]` function. +> (f(),); // Expression is a tuple, not a call. +> Some(f()); // Callee `Some` is not `#[must_use]`. +> if true { f() } else {}; // Expression is an `if`, not a call. +> match true { // Expression is a `match`, not a call. +> _ => f() > }; > ``` +> +> ```rust,compile_fail +> #![deny(unused_must_use)] +> #[must_use] +> struct MustUse; +> fn g() -> MustUse { MustUse } +> +> // Despite the `if` expression not being a call, the type-based check +> // fires because the type of the expression is `MustUse`, which has +> // the `#[must_use]` attribute. +> if true { g() } else { MustUse }; // ERROR: Must be used. +> ``` +r[attributes.diagnostics.must_use.underscore-idiom] > [!NOTE] -> It is idiomatic to use a [let statement] with a pattern of `_` when a must-used value is purposely discarded. +> Using a [let statement] or [destructuring assignment] with a pattern of `_` when a must-used value is purposely discarded is idiomatic. > > ```rust +> #![deny(unused_must_use)] > #[must_use] -> fn five() -> i32 { 5i32 } -> -> // Does not violate the unused_must_use lint. -> let _ = five(); +> fn f() {} +> let _ = f(); // OK. +> _ = f(); // OK. > ``` r[attributes.diagnostic.namespace] @@ -664,12 +742,16 @@ The first error message includes a somewhat confusing error message about the re [Clippy]: https://github.com/rust-lang/rust-clippy [`Drop`]: ../special-types-and-traits.md#drop +[`unsafe` blocks]: ../expressions/block-expr.md#unsafe-blocks +[attribute]: ../attributes.md [attributes]: ../attributes.md [block expression]: ../expressions/block-expr.md [call expression]: ../expressions/call-expr.md +[destructuring assignment]: expr.assign.destructure +[method call expression]: ../expressions/method-call-expr.md [dyn trait]: ../types/trait-object.md [enum variant]: ../items/enumerations.md -[enum]: ../items/enumerations.md +[enumeration]: ../items/enumerations.md [expression statement]: ../statements.md#expression-statements [expression]: ../expressions.md [external block item]: ../items/external-blocks.md @@ -677,6 +759,7 @@ The first error message includes a somewhat confusing error message about the re [impl trait]: ../types/impl-trait.md [implementation]: ../items/implementations.md [item]: ../items.md +[labeled block expressions]: ../expressions/block-expr.md#labeled-block-expressions [let statement]: ../statements.md#let-statements [macro definition]: ../macros-by-example.md [module]: ../items/modules.md @@ -686,8 +769,10 @@ The first error message includes a somewhat confusing error message about the re [rustdoc]: ../../rustdoc/lints.html [struct field]: ../items/structs.md [struct]: ../items/structs.md +[external block]: ../items/external-blocks.md [trait declaration]: ../items/traits.md [trait item]: ../items/traits.md [trait-impl]: ../items/implementations.md#trait-implementations [traits]: ../items/traits.md +[uninhabited]: glossary.uninhabited [union]: ../items/unions.md