about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/functions/must_use.rs58
-rw-r--r--tests/ui/must_use_unit.fixed6
-rw-r--r--tests/ui/must_use_unit.rs6
-rw-r--r--tests/ui/must_use_unit.stderr18
4 files changed, 75 insertions, 13 deletions
diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs
index 175d92d2d79..80813d3ccf1 100644
--- a/clippy_lints/src/functions/must_use.rs
+++ b/clippy_lints/src/functions/must_use.rs
@@ -29,7 +29,7 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>
         let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
         let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
         if let Some(attr) = attr {
-            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, sig);
+            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig);
         } else if is_public && !is_proc_macro(attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) {
             check_must_use_candidate(
                 cx,
@@ -51,7 +51,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp
         let attrs = cx.tcx.hir().attrs(item.hir_id());
         let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
         if let Some(attr) = attr {
-            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, sig);
+            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig);
         } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id.def_id).is_none() {
             check_must_use_candidate(
                 cx,
@@ -74,7 +74,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
         let attrs = cx.tcx.hir().attrs(item.hir_id());
         let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
         if let Some(attr) = attr {
-            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, sig);
+            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig);
         } else if let hir::TraitFn::Provided(eid) = *eid {
             let body = cx.tcx.hir().body(eid);
             if attr.is_none() && is_public && !is_proc_macro(attrs) {
@@ -92,6 +92,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
     }
 }
 
+#[allow(clippy::too_many_arguments)]
 fn check_needless_must_use(
     cx: &LateContext<'_>,
     decl: &hir::FnDecl<'_>,
@@ -99,21 +100,54 @@ fn check_needless_must_use(
     item_span: Span,
     fn_header_span: Span,
     attr: &Attribute,
+    attrs: &[Attribute],
     sig: &FnSig<'_>,
 ) {
     if in_external_macro(cx.sess(), item_span) {
         return;
     }
     if returns_unit(decl) {
-        span_lint_and_then(
-            cx,
-            MUST_USE_UNIT,
-            fn_header_span,
-            "this unit-returning function has a `#[must_use]` attribute",
-            |diag| {
-                diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable);
-            },
-        );
+        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| {
+                    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.
+            span_lint_and_then(
+                cx,
+                MUST_USE_UNIT,
+                fn_header_span,
+                "this unit-returning function has a `#[must_use]` attribute",
+                |diag| {
+                    let mut attrs_without_must_use = attrs.to_vec();
+                    attrs_without_must_use.retain(|a| a.id != attr.id);
+                    let sugg_str = attrs_without_must_use
+                        .iter()
+                        .map(|a| {
+                            if a.value_str().is_none() {
+                                return a.name_or_empty().to_string();
+                            }
+                            format!("{} = \"{}\"", a.name_or_empty(), a.value_str().unwrap())
+                        })
+                        .collect::<Vec<_>>()
+                        .join(", ");
+
+                    diag.span_suggestion(
+                        attrs[0].span.with_hi(attrs[attrs.len() - 1].span.hi()),
+                        "change these attributes to",
+                        sugg_str,
+                        Applicability::MachineApplicable,
+                    );
+                },
+            );
+        }
     } else if attr.value_str().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() {
diff --git a/tests/ui/must_use_unit.fixed b/tests/ui/must_use_unit.fixed
index f255cb66652..b92d9379c90 100644
--- a/tests/ui/must_use_unit.fixed
+++ b/tests/ui/must_use_unit.fixed
@@ -23,3 +23,9 @@ fn main() {
         fn foo() {}
     );
 }
+
+#[cfg_attr(all(), deprecated)]
+fn issue_12320() {}
+
+#[cfg_attr(all(), deprecated, doc = "foo")]
+fn issue_12320_2() {}
diff --git a/tests/ui/must_use_unit.rs b/tests/ui/must_use_unit.rs
index 1305910ed0e..c77e7282750 100644
--- a/tests/ui/must_use_unit.rs
+++ b/tests/ui/must_use_unit.rs
@@ -26,3 +26,9 @@ fn main() {
         fn foo() {}
     );
 }
+
+#[cfg_attr(all(), must_use, deprecated)]
+fn issue_12320() {}
+
+#[cfg_attr(all(), deprecated, doc = "foo", must_use)]
+fn issue_12320_2() {}
diff --git a/tests/ui/must_use_unit.stderr b/tests/ui/must_use_unit.stderr
index c2ee2edda7d..b435568deea 100644
--- a/tests/ui/must_use_unit.stderr
+++ b/tests/ui/must_use_unit.stderr
@@ -25,5 +25,21 @@ LL | #[must_use = "With note"]
 LL | pub fn must_use_with_note() {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 3 previous errors
+error: this unit-returning function has a `#[must_use]` attribute
+  --> tests/ui/must_use_unit.rs:31:1
+   |
+LL | #[cfg_attr(all(), must_use, deprecated)]
+   |                   -------------------- help: change these attributes to: `deprecated`
+LL | fn issue_12320() {}
+   | ^^^^^^^^^^^^^^^^
+
+error: this unit-returning function has a `#[must_use]` attribute
+  --> tests/ui/must_use_unit.rs:34:1
+   |
+LL | #[cfg_attr(all(), deprecated, doc = "foo", must_use)]
+   |                   --------------------------------- help: change these attributes to: `deprecated, doc = "foo"`
+LL | fn issue_12320_2() {}
+   | ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors