@@ -171,11 +171,35 @@ declare_clippy_lint! {
171
171
"a borrow of a boxed type"
172
172
}
173
173
174
+ declare_clippy_lint ! {
175
+ /// **What it does:** Checks for use of redundant allocations anywhere in the code.
176
+ ///
177
+ /// **Why is this bad?** Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Box<T>>`, `Box<&T>`
178
+ /// add an unnecessary level of indirection.
179
+ ///
180
+ /// **Known problems:** None.
181
+ ///
182
+ /// **Example:**
183
+ /// ```rust
184
+ /// # use std::rc::Rc;
185
+ /// fn foo(bar: Rc<&usize>) {}
186
+ /// ```
187
+ ///
188
+ /// Better:
189
+ ///
190
+ /// ```rust
191
+ /// fn foo(bar: &usize) {}
192
+ /// ```
193
+ pub REDUNDANT_ALLOCATION ,
194
+ perf,
195
+ "redundant allocation"
196
+ }
197
+
174
198
pub struct Types {
175
199
vec_box_size_threshold : u64 ,
176
200
}
177
201
178
- impl_lint_pass ! ( Types => [ BOX_VEC , VEC_BOX , OPTION_OPTION , LINKEDLIST , BORROWED_BOX ] ) ;
202
+ impl_lint_pass ! ( Types => [ BOX_VEC , VEC_BOX , OPTION_OPTION , LINKEDLIST , BORROWED_BOX , REDUNDANT_ALLOCATION ] ) ;
179
203
180
204
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for Types {
181
205
fn check_fn (
@@ -217,7 +241,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Types {
217
241
}
218
242
219
243
/// Checks if `qpath` has last segment with type parameter matching `path`
220
- fn match_type_parameter ( cx : & LateContext < ' _ , ' _ > , qpath : & QPath < ' _ > , path : & [ & str ] ) -> bool {
244
+ fn match_type_parameter ( cx : & LateContext < ' _ , ' _ > , qpath : & QPath < ' _ > , path : & [ & str ] ) -> Option < Span > {
221
245
let last = last_path_segment ( qpath) ;
222
246
if_chain ! {
223
247
if let Some ( ref params) = last. args;
@@ -230,10 +254,27 @@ fn match_type_parameter(cx: &LateContext<'_, '_>, qpath: &QPath<'_>, path: &[&st
230
254
if let Some ( did) = qpath_res( cx, qpath, ty. hir_id) . opt_def_id( ) ;
231
255
if match_def_path( cx, did, path) ;
232
256
then {
233
- return true ;
257
+ return Some ( ty . span ) ;
234
258
}
235
259
}
236
- false
260
+ None
261
+ }
262
+
263
+ fn match_borrows_parameter ( _cx : & LateContext < ' _ , ' _ > , qpath : & QPath < ' _ > ) -> Option < Span > {
264
+ let last = last_path_segment ( qpath) ;
265
+ if_chain ! {
266
+ if let Some ( ref params) = last. args;
267
+ if !params. parenthesized;
268
+ if let Some ( ty) = params. args. iter( ) . find_map( |arg| match arg {
269
+ GenericArg :: Type ( ty) => Some ( ty) ,
270
+ _ => None ,
271
+ } ) ;
272
+ if let TyKind :: Rptr ( ..) = ty. kind;
273
+ then {
274
+ return Some ( ty. span) ;
275
+ }
276
+ }
277
+ None
237
278
}
238
279
239
280
impl Types {
@@ -257,6 +298,7 @@ impl Types {
257
298
/// The parameter `is_local` distinguishes the context of the type; types from
258
299
/// local bindings should only be checked for the `BORROWED_BOX` lint.
259
300
#[ allow( clippy:: too_many_lines) ]
301
+ #[ allow( clippy:: cognitive_complexity) ]
260
302
fn check_ty ( & mut self , cx : & LateContext < ' _ , ' _ > , hir_ty : & hir:: Ty < ' _ > , is_local : bool ) {
261
303
if hir_ty. span . from_expansion ( ) {
262
304
return ;
@@ -267,7 +309,19 @@ impl Types {
267
309
let res = qpath_res ( cx, qpath, hir_id) ;
268
310
if let Some ( def_id) = res. opt_def_id ( ) {
269
311
if Some ( def_id) == cx. tcx . lang_items ( ) . owned_box ( ) {
270
- if match_type_parameter ( cx, qpath, & paths:: VEC ) {
312
+ if let Some ( span) = match_borrows_parameter ( cx, qpath) {
313
+ span_lint_and_sugg (
314
+ cx,
315
+ REDUNDANT_ALLOCATION ,
316
+ hir_ty. span ,
317
+ "usage of `Box<&T>`" ,
318
+ "try" ,
319
+ snippet ( cx, span, ".." ) . to_string ( ) ,
320
+ Applicability :: MachineApplicable ,
321
+ ) ;
322
+ return ; // don't recurse into the type
323
+ }
324
+ if match_type_parameter ( cx, qpath, & paths:: VEC ) . is_some ( ) {
271
325
span_lint_and_help (
272
326
cx,
273
327
BOX_VEC ,
@@ -277,6 +331,43 @@ impl Types {
277
331
) ;
278
332
return ; // don't recurse into the type
279
333
}
334
+ } else if Some ( def_id) == cx. tcx . lang_items ( ) . rc ( ) {
335
+ if let Some ( span) = match_type_parameter ( cx, qpath, & paths:: RC ) {
336
+ span_lint_and_sugg (
337
+ cx,
338
+ REDUNDANT_ALLOCATION ,
339
+ hir_ty. span ,
340
+ "usage of `Rc<Rc<T>>`" ,
341
+ "try" ,
342
+ snippet ( cx, span, ".." ) . to_string ( ) ,
343
+ Applicability :: MachineApplicable ,
344
+ ) ;
345
+ return ; // don't recurse into the type
346
+ }
347
+ if let Some ( span) = match_type_parameter ( cx, qpath, & paths:: BOX ) {
348
+ span_lint_and_sugg (
349
+ cx,
350
+ REDUNDANT_ALLOCATION ,
351
+ hir_ty. span ,
352
+ "usage of `Rc<Box<T>>`" ,
353
+ "try" ,
354
+ snippet ( cx, span, ".." ) . to_string ( ) ,
355
+ Applicability :: MachineApplicable ,
356
+ ) ;
357
+ return ; // don't recurse into the type
358
+ }
359
+ if let Some ( span) = match_borrows_parameter ( cx, qpath) {
360
+ span_lint_and_sugg (
361
+ cx,
362
+ REDUNDANT_ALLOCATION ,
363
+ hir_ty. span ,
364
+ "usage of `Rc<&T>`" ,
365
+ "try" ,
366
+ snippet ( cx, span, ".." ) . to_string ( ) ,
367
+ Applicability :: MachineApplicable ,
368
+ ) ;
369
+ return ; // don't recurse into the type
370
+ }
280
371
} else if cx. tcx . is_diagnostic_item ( Symbol :: intern ( "vec_type" ) , def_id) {
281
372
if_chain ! {
282
373
// Get the _ part of Vec<_>
@@ -314,7 +405,7 @@ impl Types {
314
405
}
315
406
}
316
407
} else if match_def_path ( cx, def_id, & paths:: OPTION ) {
317
- if match_type_parameter ( cx, qpath, & paths:: OPTION ) {
408
+ if let Some ( _ ) = match_type_parameter ( cx, qpath, & paths:: OPTION ) {
318
409
span_lint (
319
410
cx,
320
411
OPTION_OPTION ,
0 commit comments