@@ -29,7 +29,7 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>
29
29
let is_public = cx. effective_visibilities . is_exported ( item. owner_id . def_id ) ;
30
30
let fn_header_span = item. span . with_hi ( sig. decl . output . span ( ) . hi ( ) ) ;
31
31
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) ;
33
33
} else if is_public && !is_proc_macro ( attrs) && !attrs. iter ( ) . any ( |a| a. has_name ( sym:: no_mangle) ) {
34
34
check_must_use_candidate (
35
35
cx,
@@ -51,7 +51,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp
51
51
let attrs = cx. tcx . hir ( ) . attrs ( item. hir_id ( ) ) ;
52
52
let attr = cx. tcx . get_attr ( item. owner_id , sym:: must_use) ;
53
53
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) ;
55
55
} else if is_public && !is_proc_macro ( attrs) && trait_ref_of_method ( cx, item. owner_id . def_id ) . is_none ( ) {
56
56
check_must_use_candidate (
57
57
cx,
@@ -74,7 +74,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
74
74
let attrs = cx. tcx . hir ( ) . attrs ( item. hir_id ( ) ) ;
75
75
let attr = cx. tcx . get_attr ( item. owner_id , sym:: must_use) ;
76
76
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) ;
78
78
} else if let hir:: TraitFn :: Provided ( eid) = * eid {
79
79
let body = cx. tcx . hir ( ) . body ( eid) ;
80
80
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
92
92
}
93
93
}
94
94
95
+ #[ allow( clippy:: too_many_arguments) ]
95
96
fn check_needless_must_use (
96
97
cx : & LateContext < ' _ > ,
97
98
decl : & hir:: FnDecl < ' _ > ,
98
99
item_id : hir:: OwnerId ,
99
100
item_span : Span ,
100
101
fn_header_span : Span ,
101
102
attr : & Attribute ,
103
+ attrs : & [ Attribute ] ,
102
104
sig : & FnSig < ' _ > ,
103
105
) {
104
106
if in_external_macro ( cx. sess ( ) , item_span) {
105
107
return ;
106
108
}
107
109
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
+ }
117
151
} else if attr. value_str ( ) . is_none ( ) && is_must_use_ty ( cx, return_ty ( cx, item_id) ) {
118
152
// Ignore async functions unless Future::Output type is a must_use type
119
153
if sig. header . is_async ( ) {
0 commit comments