Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 32 additions & 28 deletions clippy_lints/src/functions/must_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rustc_middle::ty::{self, Ty};
use rustc_span::{Span, sym};

use clippy_utils::attrs::is_proc_macro;
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_indent;
use clippy_utils::ty::is_must_use_ty;
use clippy_utils::visitors::for_each_expr_without_closures;
Expand Down Expand Up @@ -145,31 +145,24 @@ fn check_needless_must_use(
return;
}
if returns_unit(decl) {
if attrs.len() == 1 {
span_lint_and_then(
cx,
MUST_USE_UNIT,
fn_header_span,
"this unit-returning function has a `#[must_use]` attribute",
|diag| {
span_lint_and_then(
cx,
MUST_USE_UNIT,
fn_header_span,
"this unit-returning function has a `#[must_use]` attribute",
|diag| {
// When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see
// issue #12320.
// FIXME(jdonszelmann): this used to give a machine-applicable fix. However, it was super fragile,
// honestly looked incorrect, and is a little hard to support for a little bit now. Some day this
// could be re-added.
if attrs.len() == 1 {
diag.span_suggestion(attr_span, "remove the attribute", "", Applicability::MachineApplicable);
},
);
} else {
// When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see
// issue #12320.
// FIXME(jdonszelmann): this used to give a machine-applicable fix. However, it was super fragile,
// honestly looked incorrect, and is a little hard to support for a little bit now. Some day this
// could be re-added.
span_lint_and_help(
cx,
MUST_USE_UNIT,
fn_header_span,
"this unit-returning function has a `#[must_use]` attribute",
Some(attr_span),
"remove `must_use`",
);
}
} else {
diag.span_help(attr_span, "remove `must_use`");
}
},
);
} else if reason.is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
// Ignore async functions unless Future::Output type is a must_use type
if sig.header.is_async() {
Expand All @@ -181,13 +174,24 @@ fn check_needless_must_use(
}
}

span_lint_and_help(
span_lint_and_then(
cx,
DOUBLE_MUST_USE,
fn_header_span,
"this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`",
None,
"either add some descriptive message or remove the attribute",
|diag| {
// When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see
// issue #12320.
// FIXME(jdonszelmann): this used to give a machine-applicable fix. However, it was super fragile,
// honestly looked incorrect, and is a little hard to support for a little bit now. Some day this
// could be re-added.
if attrs.len() == 1 {
diag.span_suggestion(attr_span, "remove the attribute", "", Applicability::MachineApplicable);
Comment thread
LebedevRI marked this conversation as resolved.
} else {
diag.span_help(attr_span, "remove `must_use`");
}
diag.note("alternatively, you may add an explicit reason to the `must_use` attribute");
},
);
}
}
Expand Down
65 changes: 65 additions & 0 deletions tests/ui/double_must_use.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#![warn(clippy::double_must_use)]
#![expect(clippy::result_unit_err)]
#![feature(never_type)]

use std::ops::ControlFlow;

pub fn must_use_result() -> Result<(), ()> {
//~^ double_must_use

unimplemented!();
}

pub fn must_use_tuple() -> (Result<(), ()>, u8) {
//~^ double_must_use

unimplemented!();
}

pub fn must_use_array() -> [Result<(), ()>; 1] {
//~^ double_must_use

unimplemented!();
}

#[must_use = "With note"]
pub fn must_use_with_note() -> Result<(), ()> {
unimplemented!();
}

// vvvv Should not lint (#10486)
#[must_use]
async fn async_must_use() -> usize {
unimplemented!();
}

async fn async_must_use_result() -> Result<(), ()> {
//~^ double_must_use

Ok(())
}

#[must_use]
pub fn must_use_result_with_uninhabited() -> Result<(), !> {
unimplemented!();
}

#[must_use]
pub struct T;

pub fn must_use_result_with_uninhabited_2() -> Result<T, !> {
//~^ double_must_use
unimplemented!();
}

#[must_use]
pub fn must_use_controlflow_with_uninhabited() -> ControlFlow<std::convert::Infallible> {
unimplemented!();
}

pub fn must_use_controlflow_with_uninhabited_2() -> ControlFlow<std::convert::Infallible, T> {
//~^ double_must_use
unimplemented!();
}

fn main() {}
24 changes: 18 additions & 6 deletions tests/ui/double_must_use.stderr
Original file line number Diff line number Diff line change
@@ -1,52 +1,64 @@
error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
--> tests/ui/double_must_use.rs:8:1
|
LL | #[must_use]
| ----------- help: remove the attribute
LL | pub fn must_use_result() -> Result<(), ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: either add some descriptive message or remove the attribute
= note: alternatively, you may add an explicit reason to the `must_use` attribute
= note: `-D clippy::double-must-use` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::double_must_use)]`

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
--> tests/ui/double_must_use.rs:15:1
|
LL | #[must_use]
| ----------- help: remove the attribute
LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: either add some descriptive message or remove the attribute
= note: alternatively, you may add an explicit reason to the `must_use` attribute

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
--> tests/ui/double_must_use.rs:22:1
|
LL | #[must_use]
| ----------- help: remove the attribute
LL | pub fn must_use_array() -> [Result<(), ()>; 1] {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: either add some descriptive message or remove the attribute
= note: alternatively, you may add an explicit reason to the `must_use` attribute

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
--> tests/ui/double_must_use.rs:40:1
|
LL | #[must_use]
| ----------- help: remove the attribute
LL | async fn async_must_use_result() -> Result<(), ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: either add some descriptive message or remove the attribute
= note: alternatively, you may add an explicit reason to the `must_use` attribute

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
--> tests/ui/double_must_use.rs:55:1
|
LL | #[must_use]
| ----------- help: remove the attribute
LL | pub fn must_use_result_with_uninhabited_2() -> Result<T, !> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: either add some descriptive message or remove the attribute
= note: alternatively, you may add an explicit reason to the `must_use` attribute

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
--> tests/ui/double_must_use.rs:66:1
|
LL | #[must_use]
| ----------- help: remove the attribute
LL | pub fn must_use_controlflow_with_uninhabited_2() -> ControlFlow<std::convert::Infallible, T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: either add some descriptive message or remove the attribute
= note: alternatively, you may add an explicit reason to the `must_use` attribute

error: aborting due to 6 previous errors

17 changes: 17 additions & 0 deletions tests/ui/double_must_use_unfixable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#![warn(clippy::double_must_use)]
#![expect(clippy::result_unit_err)]
#![feature(never_type)]

#[cfg_attr(all(), must_use, deprecated)]
pub fn issue_12320() -> Result<(), ()> {
//~^ double_must_use
unimplemented!();
}

#[cfg_attr(all(), deprecated, must_use)]
pub fn issue_12320_2() -> Result<(), ()> {
//~^ double_must_use
unimplemented!();
}

fn main() {}
30 changes: 30 additions & 0 deletions tests/ui/double_must_use_unfixable.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
--> tests/ui/double_must_use_unfixable.rs:6:1
|
LL | pub fn issue_12320() -> Result<(), ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove `must_use`
--> tests/ui/double_must_use_unfixable.rs:5:19
|
LL | #[cfg_attr(all(), must_use, deprecated)]
| ^^^^^^^^
= note: alternatively, you may add an explicit reason to the `must_use` attribute
= note: `-D clippy::double-must-use` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::double_must_use)]`

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
--> tests/ui/double_must_use_unfixable.rs:12:1
|
LL | pub fn issue_12320_2() -> Result<(), ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove `must_use`
--> tests/ui/double_must_use_unfixable.rs:11:31
|
LL | #[cfg_attr(all(), deprecated, must_use)]
| ^^^^^^^^
= note: alternatively, you may add an explicit reason to the `must_use` attribute

error: aborting due to 2 previous errors