1
1
use rustc:: hir:: * ;
2
2
use rustc:: hir:: intravisit:: FnKind ;
3
3
use rustc:: lint:: * ;
4
- use rustc:: ty:: { self , TypeFoldable } ;
4
+ use rustc:: ty:: { self , RegionKind , TypeFoldable } ;
5
5
use rustc:: traits;
6
6
use rustc:: middle:: expr_use_visitor as euv;
7
7
use rustc:: middle:: mem_categorization as mc;
8
8
use syntax:: ast:: NodeId ;
9
9
use syntax_pos:: Span ;
10
10
use syntax:: errors:: DiagnosticBuilder ;
11
11
use utils:: { get_trait_def_id, implements_trait, in_macro, is_copy, is_self, match_type, multispan_sugg, paths,
12
- snippet, span_lint_and_then} ;
12
+ snippet, snippet_opt, span_lint_and_then} ;
13
+ use utils:: ptr:: get_spans;
13
14
use std:: collections:: { HashMap , HashSet } ;
15
+ use std:: borrow:: Cow ;
14
16
15
17
/// **What it does:** Checks for functions taking arguments by value, but not
16
18
/// consuming them in its
@@ -73,18 +75,29 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
73
75
_ => return ,
74
76
}
75
77
76
- // Allows these to be passed by value.
77
- let fn_trait = need ! ( cx. tcx. lang_items( ) . fn_trait( ) ) ;
78
- let asref_trait = need ! ( get_trait_def_id( cx, & paths:: ASREF_TRAIT ) ) ;
78
+ // Allow `Borrow` or functions to be taken by value
79
79
let borrow_trait = need ! ( get_trait_def_id( cx, & paths:: BORROW_TRAIT ) ) ;
80
+ let fn_traits = [
81
+ need ! ( cx. tcx. lang_items( ) . fn_trait( ) ) ,
82
+ need ! ( cx. tcx. lang_items( ) . fn_once_trait( ) ) ,
83
+ need ! ( cx. tcx. lang_items( ) . fn_mut_trait( ) ) ,
84
+ ] ;
85
+
86
+ let sized_trait = need ! ( cx. tcx. lang_items( ) . sized_trait( ) ) ;
80
87
81
88
let fn_def_id = cx. tcx . hir . local_def_id ( node_id) ;
82
89
83
- let preds: Vec < ty:: Predicate > = {
84
- traits:: elaborate_predicates ( cx. tcx , cx. param_env . caller_bounds . to_vec ( ) )
85
- . filter ( |p| !p. is_global ( ) )
86
- . collect ( )
87
- } ;
90
+ let preds = traits:: elaborate_predicates ( cx. tcx , cx. param_env . caller_bounds . to_vec ( ) )
91
+ . filter ( |p| !p. is_global ( ) )
92
+ . filter_map ( |pred| if let ty:: Predicate :: Trait ( poly_trait_ref) = pred {
93
+ if poly_trait_ref. def_id ( ) == sized_trait || poly_trait_ref. skip_binder ( ) . has_escaping_regions ( ) {
94
+ return None ;
95
+ }
96
+ Some ( poly_trait_ref)
97
+ } else {
98
+ None
99
+ } )
100
+ . collect :: < Vec < _ > > ( ) ;
88
101
89
102
// Collect moved variables and spans which will need dereferencings from the
90
103
// function body.
@@ -102,45 +115,56 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
102
115
let fn_sig = cx. tcx . fn_sig ( fn_def_id) ;
103
116
let fn_sig = cx. tcx . erase_late_bound_regions ( & fn_sig) ;
104
117
105
- for ( ( input, & ty) , arg) in decl. inputs . iter ( ) . zip ( fn_sig. inputs ( ) ) . zip ( & body. arguments ) {
106
- // Determines whether `ty` implements `Borrow<U>` (U != ty) specifically.
107
- // This is needed due to the `Borrow<T> for T` blanket impl.
108
- let implements_borrow_trait = preds
109
- . iter ( )
110
- . filter_map ( |pred| if let ty:: Predicate :: Trait ( ref poly_trait_ref) = * pred {
111
- Some ( poly_trait_ref. skip_binder ( ) )
112
- } else {
113
- None
114
- } )
115
- . filter ( |tpred| tpred. def_id ( ) == borrow_trait && tpred. self_ty ( ) == ty)
116
- . any ( |tpred| {
117
- tpred
118
- . input_types ( )
119
- . nth ( 1 )
120
- . expect ( "Borrow trait must have an parameter" ) != ty
121
- } ) ;
118
+ for ( idx, ( ( input, & ty) , arg) ) in decl. inputs
119
+ . iter ( )
120
+ . zip ( fn_sig. inputs ( ) )
121
+ . zip ( & body. arguments )
122
+ . enumerate ( )
123
+ {
124
+ // * Exclude a type that is specifically bounded by `Borrow`.
125
+ // * Exclude a type whose reference also fulfills its bound.
126
+ // (e.g. `std::convert::AsRef`, `serde::Serialize`)
127
+ let ( implements_borrow_trait, all_borrowable_trait) = {
128
+ let preds = preds
129
+ . iter ( )
130
+ . filter ( |t| t. skip_binder ( ) . self_ty ( ) == ty)
131
+ . collect :: < Vec < _ > > ( ) ;
132
+
133
+ (
134
+ preds. iter ( ) . any ( |t| t. def_id ( ) == borrow_trait) ,
135
+ !preds. is_empty ( ) && preds. iter ( ) . all ( |t| {
136
+ implements_trait (
137
+ cx,
138
+ cx. tcx . mk_imm_ref ( & RegionKind :: ReErased , ty) ,
139
+ t. def_id ( ) ,
140
+ & t. skip_binder ( ) . input_types ( ) . skip ( 1 ) . collect :: < Vec < _ > > ( ) ,
141
+ )
142
+ } ) ,
143
+ )
144
+ } ;
122
145
123
146
if_let_chain ! { [
124
147
!is_self( arg) ,
125
148
!ty. is_mutable_pointer( ) ,
126
149
!is_copy( cx, ty) ,
127
- !implements_trait( cx, ty, fn_trait, & [ ] ) ,
128
- !implements_trait( cx, ty, asref_trait, & [ ] ) ,
150
+ !fn_traits. iter( ) . any( |& t| implements_trait( cx, ty, t, & [ ] ) ) ,
129
151
!implements_borrow_trait,
152
+ !all_borrowable_trait,
130
153
131
154
let PatKind :: Binding ( mode, canonical_id, ..) = arg. pat. node,
132
155
!moved_vars. contains( & canonical_id) ,
133
156
] , {
134
- // Note: `toplevel_ref_arg` warns if `BindByRef`
135
157
if mode == BindingAnnotation :: Mutable || mode == BindingAnnotation :: RefMut {
136
158
continue ;
137
159
}
138
160
139
- // Suggestion logic
161
+ // Dereference suggestion
140
162
let sugg = |db: & mut DiagnosticBuilder | {
141
163
let deref_span = spans_need_deref. get( & canonical_id) ;
142
164
if_let_chain! { [
143
165
match_type( cx, ty, & paths:: VEC ) ,
166
+ let Some ( clone_spans) =
167
+ get_spans( cx, Some ( body. id( ) ) , idx, & [ ( "clone" , ".to_owned()" ) ] ) ,
144
168
let TyPath ( QPath :: Resolved ( _, ref path) ) = input. node,
145
169
let Some ( elem_ty) = path. segments. iter( )
146
170
. find( |seg| seg. name == "Vec" )
@@ -151,34 +175,68 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
151
175
db. span_suggestion( input. span,
152
176
"consider changing the type to" ,
153
177
slice_ty) ;
178
+
179
+ for ( span, suggestion) in clone_spans {
180
+ db. span_suggestion(
181
+ span,
182
+ & snippet_opt( cx, span)
183
+ . map_or(
184
+ "change the call to" . into( ) ,
185
+ |x| Cow :: from( format!( "change `{}` to" , x) ) ,
186
+ ) ,
187
+ suggestion. into( )
188
+ ) ;
189
+ }
190
+
191
+ // cannot be destructured, no need for `*` suggestion
154
192
assert!( deref_span. is_none( ) ) ;
155
- return ; // `Vec` and `String` cannot be destructured - no need for `*` suggestion
193
+ return ;
156
194
} }
157
195
158
196
if match_type( cx, ty, & paths:: STRING ) {
159
- db. span_suggestion( input. span,
160
- "consider changing the type to" ,
161
- "&str" . to_string( ) ) ;
162
- assert!( deref_span. is_none( ) ) ;
163
- return ;
197
+ if let Some ( clone_spans) =
198
+ get_spans( cx, Some ( body. id( ) ) , idx, & [ ( "clone" , ".to_string()" ) , ( "as_str" , "" ) ] ) {
199
+ db. span_suggestion( input. span, "consider changing the type to" , "&str" . to_string( ) ) ;
200
+
201
+ for ( span, suggestion) in clone_spans {
202
+ db. span_suggestion(
203
+ span,
204
+ & snippet_opt( cx, span)
205
+ . map_or(
206
+ "change the call to" . into( ) ,
207
+ |x| Cow :: from( format!( "change `{}` to" , x) )
208
+ ) ,
209
+ suggestion. into( ) ,
210
+ ) ;
211
+ }
212
+
213
+ assert!( deref_span. is_none( ) ) ;
214
+ return ;
215
+ }
164
216
}
165
217
166
218
let mut spans = vec![ ( input. span, format!( "&{}" , snippet( cx, input. span, "_" ) ) ) ] ;
167
219
168
220
// Suggests adding `*` to dereference the added reference.
169
221
if let Some ( deref_span) = deref_span {
170
- spans. extend( deref_span. iter( ) . cloned( )
171
- . map( |span| ( span, format!( "*{}" , snippet( cx, span, "<expr>" ) ) ) ) ) ;
222
+ spans. extend(
223
+ deref_span
224
+ . iter( )
225
+ . cloned( )
226
+ . map( |span| ( span, format!( "*{}" , snippet( cx, span, "<expr>" ) ) ) ) ,
227
+ ) ;
172
228
spans. sort_by_key( |& ( span, _) | span) ;
173
229
}
174
230
multispan_sugg( db, "consider taking a reference instead" . to_string( ) , spans) ;
175
231
} ;
176
232
177
- span_lint_and_then( cx,
178
- NEEDLESS_PASS_BY_VALUE ,
179
- input. span,
180
- "this argument is passed by value, but not consumed in the function body" ,
181
- sugg) ;
233
+ span_lint_and_then(
234
+ cx,
235
+ NEEDLESS_PASS_BY_VALUE ,
236
+ input. span,
237
+ "this argument is passed by value, but not consumed in the function body" ,
238
+ sugg,
239
+ ) ;
182
240
} }
183
241
}
184
242
}
@@ -188,8 +246,7 @@ struct MovedVariablesCtxt<'a, 'tcx: 'a> {
188
246
cx : & ' a LateContext < ' a , ' tcx > ,
189
247
moved_vars : HashSet < NodeId > ,
190
248
/// Spans which need to be prefixed with `*` for dereferencing the
191
- /// suggested additional
192
- /// reference.
249
+ /// suggested additional reference.
193
250
spans_need_deref : HashMap < NodeId , HashSet < Span > > ,
194
251
}
195
252
@@ -213,9 +270,7 @@ impl<'a, 'tcx> MovedVariablesCtxt<'a, 'tcx> {
213
270
fn non_moving_pat ( & mut self , matched_pat : & Pat , cmt : mc:: cmt < ' tcx > ) {
214
271
let cmt = unwrap_downcast_or_interior ( cmt) ;
215
272
216
- if_let_chain ! { [
217
- let mc:: Categorization :: Local ( vid) = cmt. cat,
218
- ] , {
273
+ if let mc:: Categorization :: Local ( vid) = cmt. cat {
219
274
let mut id = matched_pat. id ;
220
275
loop {
221
276
let parent = self . cx . tcx . hir . get_parent_node ( id) ;
@@ -235,7 +290,7 @@ impl<'a, 'tcx> MovedVariablesCtxt<'a, 'tcx> {
235
290
. or_insert_with ( HashSet :: new)
236
291
. insert ( c. span ) ;
237
292
}
238
- }
293
+ } ,
239
294
240
295
map:: Node :: NodeStmt ( s) => {
241
296
// `let <pat> = x;`
@@ -251,13 +306,13 @@ impl<'a, 'tcx> MovedVariablesCtxt<'a, 'tcx> {
251
306
. map( |e| e. span)
252
307
. expect( "`let` stmt without init aren't caught by match_pat" ) ) ;
253
308
} }
254
- }
309
+ } ,
255
310
256
- _ => { }
311
+ _ => { } ,
257
312
}
258
313
}
259
314
}
260
- } }
315
+ }
261
316
}
262
317
}
263
318
0 commit comments