@@ -29,7 +29,7 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>
2929 let is_public = cx. effective_visibilities . is_exported ( item. owner_id . def_id ) ;
3030 let fn_header_span = item. span . with_hi ( sig. decl . output . span ( ) . hi ( ) ) ;
3131 if let Some ( attr) = attr {
32- check_needless_must_use ( cx, sig. decl , item. owner_id , item. span , fn_header_span, attr, sig) ;
32+ check_needless_must_use ( cx, sig. decl , item. owner_id , item. span , fn_header_span, attr, attrs , sig) ;
3333 } else if is_public && !is_proc_macro ( attrs) && !attrs. iter ( ) . any ( |a| a. has_name ( sym:: no_mangle) ) {
3434 check_must_use_candidate (
3535 cx,
@@ -51,7 +51,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp
5151 let attrs = cx. tcx . hir ( ) . attrs ( item. hir_id ( ) ) ;
5252 let attr = cx. tcx . get_attr ( item. owner_id , sym:: must_use) ;
5353 if let Some ( attr) = attr {
54- check_needless_must_use ( cx, sig. decl , item. owner_id , item. span , fn_header_span, attr, sig) ;
54+ check_needless_must_use ( cx, sig. decl , item. owner_id , item. span , fn_header_span, attr, attrs , sig) ;
5555 } else if is_public && !is_proc_macro ( attrs) && trait_ref_of_method ( cx, item. owner_id . def_id ) . is_none ( ) {
5656 check_must_use_candidate (
5757 cx,
@@ -74,7 +74,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
7474 let attrs = cx. tcx . hir ( ) . attrs ( item. hir_id ( ) ) ;
7575 let attr = cx. tcx . get_attr ( item. owner_id , sym:: must_use) ;
7676 if let Some ( attr) = attr {
77- check_needless_must_use ( cx, sig. decl , item. owner_id , item. span , fn_header_span, attr, sig) ;
77+ check_needless_must_use ( cx, sig. decl , item. owner_id , item. span , fn_header_span, attr, attrs , sig) ;
7878 } else if let hir:: TraitFn :: Provided ( eid) = * eid {
7979 let body = cx. tcx . hir ( ) . body ( eid) ;
8080 if attr. is_none ( ) && is_public && !is_proc_macro ( attrs) {
@@ -92,28 +92,62 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
9292 }
9393}
9494
95+ #[ allow( clippy:: too_many_arguments) ]
9596fn check_needless_must_use (
9697 cx : & LateContext < ' _ > ,
9798 decl : & hir:: FnDecl < ' _ > ,
9899 item_id : hir:: OwnerId ,
99100 item_span : Span ,
100101 fn_header_span : Span ,
101102 attr : & Attribute ,
103+ attrs : & [ Attribute ] ,
102104 sig : & FnSig < ' _ > ,
103105) {
104106 if in_external_macro ( cx. sess ( ) , item_span) {
105107 return ;
106108 }
107109 if returns_unit ( decl) {
108- span_lint_and_then (
109- cx,
110- MUST_USE_UNIT ,
111- fn_header_span,
112- "this unit-returning function has a `#[must_use]` attribute" ,
113- |diag| {
114- diag. span_suggestion ( attr. span , "remove the attribute" , "" , Applicability :: MachineApplicable ) ;
115- } ,
116- ) ;
110+ if attrs. len ( ) == 1 {
111+ span_lint_and_then (
112+ cx,
113+ MUST_USE_UNIT ,
114+ fn_header_span,
115+ "this unit-returning function has a `#[must_use]` attribute" ,
116+ |diag| {
117+ diag. span_suggestion ( attr. span , "remove the attribute" , "" , Applicability :: MachineApplicable ) ;
118+ } ,
119+ ) ;
120+ } else {
121+ // When there are multiple attributes, it is not sufficient to simply make `must_use` empty, see
122+ // issue #12320.
123+ span_lint_and_then (
124+ cx,
125+ MUST_USE_UNIT ,
126+ fn_header_span,
127+ "this unit-returning function has a `#[must_use]` attribute" ,
128+ |diag| {
129+ let mut attrs_without_must_use = attrs. to_vec ( ) ;
130+ attrs_without_must_use. retain ( |a| a. id != attr. id ) ;
131+ let sugg_str = attrs_without_must_use
132+ . iter ( )
133+ . map ( |a| {
134+ if a. value_str ( ) . is_none ( ) {
135+ return a. name_or_empty ( ) . to_string ( ) ;
136+ }
137+ format ! ( "{} = \" {}\" " , a. name_or_empty( ) , a. value_str( ) . unwrap( ) )
138+ } )
139+ . collect :: < Vec < _ > > ( )
140+ . join ( ", " ) ;
141+
142+ diag. span_suggestion (
143+ attrs[ 0 ] . span . with_hi ( attrs[ attrs. len ( ) - 1 ] . span . hi ( ) ) ,
144+ "change these attributes to" ,
145+ sugg_str,
146+ Applicability :: MachineApplicable ,
147+ ) ;
148+ } ,
149+ ) ;
150+ }
117151 } else if attr. value_str ( ) . is_none ( ) && is_must_use_ty ( cx, return_ty ( cx, item_id) ) {
118152 // Ignore async functions unless Future::Output type is a must_use type
119153 if sig. header . is_async ( ) {
0 commit comments