1
1
use super :: needless_pass_by_value:: requires_exact_signature;
2
- use clippy_utils:: diagnostics:: span_lint_and_sugg ;
2
+ use clippy_utils:: diagnostics:: span_lint_and_then ;
3
3
use clippy_utils:: source:: snippet;
4
4
use clippy_utils:: { is_from_proc_macro, is_self} ;
5
5
use if_chain:: if_chain;
@@ -12,7 +12,7 @@ use rustc_infer::infer::TyCtxtInferExt;
12
12
use rustc_lint:: { LateContext , LateLintPass } ;
13
13
use rustc_middle:: mir:: FakeReadCause ;
14
14
use rustc_middle:: ty:: { self , Ty } ;
15
- use rustc_session:: { declare_lint_pass , declare_tool_lint } ;
15
+ use rustc_session:: { declare_tool_lint , impl_lint_pass } ;
16
16
use rustc_span:: def_id:: LocalDefId ;
17
17
use rustc_span:: symbol:: kw;
18
18
use rustc_span:: Span ;
@@ -46,7 +46,21 @@ declare_clippy_lint! {
46
46
suspicious,
47
47
"using a `&mut` argument when it's not mutated"
48
48
}
49
- declare_lint_pass ! ( NeedlessPassByRefMut => [ NEEDLESS_PASS_BY_REF_MUT ] ) ;
49
+
50
+ #[ derive( Copy , Clone ) ]
51
+ pub struct NeedlessPassByRefMut {
52
+ avoid_breaking_exported_api : bool ,
53
+ }
54
+
55
+ impl NeedlessPassByRefMut {
56
+ pub fn new ( avoid_breaking_exported_api : bool ) -> Self {
57
+ Self {
58
+ avoid_breaking_exported_api,
59
+ }
60
+ }
61
+ }
62
+
63
+ impl_lint_pass ! ( NeedlessPassByRefMut => [ NEEDLESS_PASS_BY_REF_MUT ] ) ;
50
64
51
65
fn should_skip < ' tcx > (
52
66
cx : & LateContext < ' tcx > ,
@@ -134,26 +148,45 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut {
134
148
ctx
135
149
} ;
136
150
137
- for ( ( & input, & ty) , arg) in decl. inputs . iter ( ) . zip ( fn_sig. inputs ( ) ) . zip ( body. params ) {
138
- if should_skip ( cx, input, ty, arg) {
139
- continue ;
140
- }
141
-
151
+ let mut it = decl
152
+ . inputs
153
+ . iter ( )
154
+ . zip ( fn_sig. inputs ( ) )
155
+ . zip ( body. params )
156
+ . filter ( |( ( & input, & ty) , arg) | !should_skip ( cx, input, ty, arg) )
157
+ . peekable ( ) ;
158
+ if it. peek ( ) . is_none ( ) {
159
+ return ;
160
+ }
161
+ let show_semver_warning = self . avoid_breaking_exported_api && cx. effective_visibilities . is_exported ( fn_def_id) ;
162
+ for ( ( & input, & _) , arg) in it {
142
163
// Only take `&mut` arguments.
143
164
if_chain ! {
144
165
if let PatKind :: Binding ( _, canonical_id, ..) = arg. pat. kind;
145
166
if !mutably_used_vars. contains( & canonical_id) ;
146
167
if let rustc_hir:: TyKind :: Ref ( _, inner_ty) = input. kind;
147
168
then {
148
- // If the argument is never used mutably, we emit the error.
149
- span_lint_and_sugg(
169
+ // If the argument is never used mutably, we emit the warning.
170
+ let sp = input. span;
171
+ span_lint_and_then(
150
172
cx,
151
173
NEEDLESS_PASS_BY_REF_MUT ,
152
- input . span ,
174
+ sp ,
153
175
"this argument is a mutable reference, but not used mutably" ,
154
- "consider changing to" ,
155
- format!( "&{}" , snippet( cx, cx. tcx. hir( ) . span( inner_ty. ty. hir_id) , "_" ) ) ,
156
- Applicability :: Unspecified ,
176
+ |diag| {
177
+ diag. span_suggestion(
178
+ sp,
179
+ "consider changing to" . to_string( ) ,
180
+ format!(
181
+ "&{}" ,
182
+ snippet( cx, cx. tcx. hir( ) . span( inner_ty. ty. hir_id) , "_" ) ,
183
+ ) ,
184
+ Applicability :: Unspecified ,
185
+ ) ;
186
+ if show_semver_warning {
187
+ diag. warn( "changing this function will impact semver compatibility" ) ;
188
+ }
189
+ } ,
157
190
) ;
158
191
}
159
192
}
0 commit comments