@@ -171,11 +171,35 @@ declare_clippy_lint! {
171171 "a borrow of a boxed type"
172172}
173173
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+
174198pub struct Types {
175199 vec_box_size_threshold : u64 ,
176200}
177201
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 ] ) ;
179203
180204impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for Types {
181205 fn check_fn (
@@ -217,7 +241,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Types {
217241}
218242
219243/// 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 > {
221245 let last = last_path_segment ( qpath) ;
222246 if_chain ! {
223247 if let Some ( ref params) = last. args;
@@ -230,10 +254,27 @@ fn match_type_parameter(cx: &LateContext<'_, '_>, qpath: &QPath<'_>, path: &[&st
230254 if let Some ( did) = qpath_res( cx, qpath, ty. hir_id) . opt_def_id( ) ;
231255 if match_def_path( cx, did, path) ;
232256 then {
233- return true ;
257+ return Some ( ty . span ) ;
234258 }
235259 }
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
237278}
238279
239280impl Types {
@@ -257,6 +298,7 @@ impl Types {
257298 /// The parameter `is_local` distinguishes the context of the type; types from
258299 /// local bindings should only be checked for the `BORROWED_BOX` lint.
259300 #[ allow( clippy:: too_many_lines) ]
301+ #[ allow( clippy:: cognitive_complexity) ]
260302 fn check_ty ( & mut self , cx : & LateContext < ' _ , ' _ > , hir_ty : & hir:: Ty < ' _ > , is_local : bool ) {
261303 if hir_ty. span . from_expansion ( ) {
262304 return ;
@@ -267,7 +309,19 @@ impl Types {
267309 let res = qpath_res ( cx, qpath, hir_id) ;
268310 if let Some ( def_id) = res. opt_def_id ( ) {
269311 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 ( ) {
271325 span_lint_and_help (
272326 cx,
273327 BOX_VEC ,
@@ -277,6 +331,43 @@ impl Types {
277331 ) ;
278332 return ; // don't recurse into the type
279333 }
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+ }
280371 } else if cx. tcx . is_diagnostic_item ( Symbol :: intern ( "vec_type" ) , def_id) {
281372 if_chain ! {
282373 // Get the _ part of Vec<_>
@@ -314,7 +405,7 @@ impl Types {
314405 }
315406 }
316407 } else if match_def_path ( cx, def_id, & paths:: OPTION ) {
317- if match_type_parameter ( cx, qpath, & paths:: OPTION ) {
408+ if match_type_parameter ( cx, qpath, & paths:: OPTION ) . is_some ( ) {
318409 span_lint (
319410 cx,
320411 OPTION_OPTION ,
0 commit comments