11use 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 ;
33use clippy_utils:: source:: snippet;
44use clippy_utils:: { is_from_proc_macro, is_self} ;
55use if_chain:: if_chain;
@@ -12,7 +12,7 @@ use rustc_infer::infer::TyCtxtInferExt;
1212use rustc_lint:: { LateContext , LateLintPass } ;
1313use rustc_middle:: mir:: FakeReadCause ;
1414use 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 } ;
1616use rustc_span:: def_id:: LocalDefId ;
1717use rustc_span:: symbol:: kw;
1818use rustc_span:: Span ;
@@ -46,7 +46,21 @@ declare_clippy_lint! {
4646 suspicious,
4747 "using a `&mut` argument when it's not mutated"
4848}
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 ] ) ;
5064
5165fn should_skip < ' tcx > (
5266 cx : & LateContext < ' tcx > ,
@@ -134,26 +148,45 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut {
134148 ctx
135149 } ;
136150
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 {
142163 // Only take `&mut` arguments.
143164 if_chain ! {
144165 if let PatKind :: Binding ( _, canonical_id, ..) = arg. pat. kind;
145166 if !mutably_used_vars. contains( & canonical_id) ;
146167 if let rustc_hir:: TyKind :: Ref ( _, inner_ty) = input. kind;
147168 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(
150172 cx,
151173 NEEDLESS_PASS_BY_REF_MUT ,
152- input . span ,
174+ sp ,
153175 "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+ } ,
157190 ) ;
158191 }
159192 }
0 commit comments