1
1
//! Checks for usage of `&Vec[_]` and `&String`.
2
2
3
3
use rustc:: hir:: * ;
4
+ use rustc:: hir:: intravisit:: { walk_expr, NestedVisitorMap , Visitor } ;
4
5
use rustc:: hir:: map:: NodeItem ;
5
6
use rustc:: lint:: * ;
6
7
use rustc:: ty;
7
- use syntax:: ast:: NodeId ;
8
+ use syntax:: ast:: { Name , NodeId } ;
8
9
use syntax:: codemap:: Span ;
9
10
use syntax_pos:: MultiSpan ;
10
- use utils:: { match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_then,
11
- span_lint_and_sugg, walk_ptrs_hir_ty} ;
11
+ use utils:: { get_pat_name, match_qpath, match_type, match_var, paths,
12
+ snippet, snippet_opt, span_lint, span_lint_and_then,
13
+ walk_ptrs_hir_ty} ;
12
14
13
15
/// **What it does:** This lint checks for function arguments of type `&String`
14
- /// or `&Vec` unless
15
- /// the references are mutable.
16
+ /// or `&Vec` unless the references are mutable. It will also suggest you
17
+ /// replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`
18
+ /// calls.
16
19
///
17
20
/// **Why is this bad?** Requiring the argument to be of the specific size
18
- /// makes the function less
19
- /// useful for no benefit; slices in the form of `&[T]` or `&str` usually
20
- /// suffice and can be
21
- /// obtained from other types, too.
21
+ /// makes the function less useful for no benefit; slices in the form of `&[T]`
22
+ /// or `&str` usually suffice and can be obtained from other types, too.
22
23
///
23
- /// **Known problems:** None.
24
+ /// **Known problems:** The lint does not follow data. So if you have an
25
+ /// argument `x` and write `let y = x; y.clone()` the lint will not suggest
26
+ /// changing that `.clone()` to `.to_owned()`.
27
+ ///
28
+ /// Other functions called from this function taking a `&String` or `&Vec`
29
+ /// argument may also fail to compile if you change the argument. Applying
30
+ /// this lint on them will fix the problem, but they may be in other crates.
31
+ ///
32
+ /// Also there may be `fn(&Vec)`-typed references pointing to your function.
33
+ /// If you have them, you will get a compiler error after applying this lint's
34
+ /// suggestions. You then have the choice to undo your changes or change the
35
+ /// type of the reference.
36
+ ///
37
+ /// Note that if the function is part of your public interface, there may be
38
+ /// other crates referencing it you may not be aware. Carefully deprecate the
39
+ /// function before applying the lint suggestions in this case.
24
40
///
25
41
/// **Example:**
26
42
/// ```rust
@@ -87,25 +103,26 @@ impl LintPass for PointerPass {
87
103
88
104
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for PointerPass {
89
105
fn check_item ( & mut self , cx : & LateContext < ' a , ' tcx > , item : & ' tcx Item ) {
90
- if let ItemFn ( ref decl, _, _, _, _, _ ) = item. node {
91
- check_fn ( cx, decl, item. id ) ;
106
+ if let ItemFn ( ref decl, _, _, _, _, body_id ) = item. node {
107
+ check_fn ( cx, decl, item. id , Some ( body_id ) ) ;
92
108
}
93
109
}
94
110
95
111
fn check_impl_item ( & mut self , cx : & LateContext < ' a , ' tcx > , item : & ' tcx ImplItem ) {
96
- if let ImplItemKind :: Method ( ref sig, _ ) = item. node {
112
+ if let ImplItemKind :: Method ( ref sig, body_id ) = item. node {
97
113
if let Some ( NodeItem ( it) ) = cx. tcx . hir . find ( cx. tcx . hir . get_parent ( item. id ) ) {
98
114
if let ItemImpl ( _, _, _, _, Some ( _) , _, _) = it. node {
99
115
return ; // ignore trait impls
100
116
}
101
117
}
102
- check_fn ( cx, & sig. decl , item. id ) ;
118
+ check_fn ( cx, & sig. decl , item. id , Some ( body_id ) ) ;
103
119
}
104
120
}
105
121
106
122
fn check_trait_item ( & mut self , cx : & LateContext < ' a , ' tcx > , item : & ' tcx TraitItem ) {
107
- if let TraitItemKind :: Method ( ref sig, _) = item. node {
108
- check_fn ( cx, & sig. decl , item. id ) ;
123
+ if let TraitItemKind :: Method ( ref sig, ref trait_method) = item. node {
124
+ let body_id = if let TraitMethod :: Provided ( b) = * trait_method { Some ( b) } else { None } ;
125
+ check_fn ( cx, & sig. decl , item. id , body_id) ;
109
126
}
110
127
}
111
128
@@ -123,12 +140,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PointerPass {
123
140
}
124
141
}
125
142
126
- fn check_fn ( cx : & LateContext , decl : & FnDecl , fn_id : NodeId ) {
143
+ fn check_fn ( cx : & LateContext , decl : & FnDecl , fn_id : NodeId , opt_body_id : Option < BodyId > ) {
127
144
let fn_def_id = cx. tcx . hir . local_def_id ( fn_id) ;
128
145
let sig = cx. tcx . fn_sig ( fn_def_id) ;
129
146
let fn_ty = sig. skip_binder ( ) ;
130
147
131
- for ( arg, ty) in decl. inputs . iter ( ) . zip ( fn_ty. inputs ( ) ) {
148
+ for ( idx , ( arg, ty) ) in decl. inputs . iter ( ) . zip ( fn_ty. inputs ( ) ) . enumerate ( ) {
132
149
if let ty:: TyRef (
133
150
_,
134
151
ty:: TypeAndMut {
@@ -146,7 +163,7 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) {
146
163
] , {
147
164
ty_snippet = snippet_opt( cx, parameters. types[ 0 ] . span) ;
148
165
} ) ;
149
- //TODO: Suggestion
166
+ let spans = get_spans ( cx , opt_body_id , idx , "to_owned" ) ;
150
167
span_lint_and_then (
151
168
cx,
152
169
PTR_ARG ,
@@ -159,16 +176,30 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) {
159
176
"change this to" ,
160
177
format ! ( "&[{}]" , snippet) ) ;
161
178
}
179
+ for ( clonespan, suggestion) in spans {
180
+ db. span_suggestion ( clonespan,
181
+ "change the `.clone()` to" ,
182
+ suggestion) ;
183
+ }
162
184
}
163
185
) ;
164
186
} else if match_type ( cx, ty, & paths:: STRING ) {
165
- span_lint_and_sugg (
187
+ let spans = get_spans ( cx, opt_body_id, idx, "to_string" ) ;
188
+ span_lint_and_then (
166
189
cx,
167
190
PTR_ARG ,
168
191
arg. span ,
169
192
"writing `&String` instead of `&str` involves a new object where a slice will do." ,
170
- "change this to" ,
171
- "&str" . to_string ( )
193
+ |db| {
194
+ db. span_suggestion ( arg. span ,
195
+ "change this to" ,
196
+ "&str" . into ( ) ) ;
197
+ for ( clonespan, suggestion) in spans {
198
+ db. span_suggestion_short ( clonespan,
199
+ "change the `.clone` to " ,
200
+ suggestion) ;
201
+ }
202
+ }
172
203
) ;
173
204
}
174
205
}
@@ -198,6 +229,54 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) {
198
229
}
199
230
}
200
231
232
+ fn get_spans ( cx : & LateContext , opt_body_id : Option < BodyId > , idx : usize , fn_name : & ' static str ) -> Vec < ( Span , String ) > {
233
+ if let Some ( body) = opt_body_id. map ( |id| cx. tcx . hir . body ( id) ) {
234
+ get_binding_name ( & body. arguments [ idx] ) . map_or_else ( Vec :: new,
235
+ |name| extract_clone_suggestions ( cx, name, fn_name, body) )
236
+ } else {
237
+ vec ! [ ]
238
+ }
239
+ }
240
+
241
+ fn extract_clone_suggestions < ' a , ' tcx : ' a > ( cx : & LateContext < ' a , ' tcx > , name : Name , fn_name : & ' static str , body : & ' tcx Body ) -> Vec < ( Span , String ) > {
242
+ let mut visitor = PtrCloneVisitor {
243
+ cx,
244
+ name,
245
+ fn_name,
246
+ spans : vec ! [ ]
247
+ } ;
248
+ visitor. visit_body ( body) ;
249
+ visitor. spans
250
+ }
251
+
252
+ struct PtrCloneVisitor < ' a , ' tcx : ' a > {
253
+ cx : & ' a LateContext < ' a , ' tcx > ,
254
+ name : Name ,
255
+ fn_name : & ' static str ,
256
+ spans : Vec < ( Span , String ) > ,
257
+ }
258
+
259
+ impl < ' a , ' tcx : ' a > Visitor < ' tcx > for PtrCloneVisitor < ' a , ' tcx > {
260
+ fn visit_expr ( & mut self , expr : & ' tcx Expr ) {
261
+ if let ExprMethodCall ( ref seg, _, ref args) = expr. node {
262
+ if args. len ( ) == 1 && match_var ( & args[ 0 ] , self . name ) && seg. name == "clone" {
263
+ self . spans . push ( ( expr. span , format ! ( "{}.{}()" , snippet( self . cx, args[ 0 ] . span, "_" ) , self . fn_name) ) ) ;
264
+ }
265
+ return ;
266
+ }
267
+ walk_expr ( self , expr) ;
268
+ }
269
+
270
+ fn nested_visit_map < ' this > ( & ' this mut self ) -> NestedVisitorMap < ' this , ' tcx > {
271
+ NestedVisitorMap :: None
272
+ }
273
+
274
+ }
275
+
276
+ fn get_binding_name ( arg : & Arg ) -> Option < Name > {
277
+ get_pat_name ( & arg. pat )
278
+ }
279
+
201
280
fn get_rptr_lm ( ty : & Ty ) -> Option < ( & Lifetime , Mutability , Span ) > {
202
281
if let Ty_ :: TyRptr ( ref lt, ref m) = ty. node {
203
282
Some ( ( lt, m. mutbl , ty. span ) )
0 commit comments