@@ -68,6 +68,34 @@ declare_clippy_lint! {
6868 "usage of `Box<Vec<T>>`, vector elements are already on the heap"
6969}
7070
71+ /// **What it does:** Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code.
72+ ///
73+ /// **Why is this bad?** `Vec` already keeps its contents in a separate area on
74+ /// the heap. So if you `Box` its contents, you just add another level of indirection.
75+ ///
76+ /// **Known problems:** Vec<Box<T: Sized>> makes sense if T is a large type (see #3530,
77+ /// 1st comment).
78+ ///
79+ /// **Example:**
80+ /// ```rust
81+ /// struct X {
82+ /// values: Vec<Box<i32>>,
83+ /// }
84+ /// ```
85+ ///
86+ /// Better:
87+ ///
88+ /// ```rust
89+ /// struct X {
90+ /// values: Vec<i32>,
91+ /// }
92+ /// ```
93+ declare_clippy_lint ! {
94+ pub VEC_BOX ,
95+ complexity,
96+ "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap"
97+ }
98+
7199/// **What it does:** Checks for use of `Option<Option<_>>` in function signatures and type
72100/// definitions
73101///
@@ -148,7 +176,7 @@ declare_clippy_lint! {
148176
149177impl LintPass for TypePass {
150178 fn get_lints ( & self ) -> LintArray {
151- lint_array ! ( BOX_VEC , OPTION_OPTION , LINKEDLIST , BORROWED_BOX )
179+ lint_array ! ( BOX_VEC , VEC_BOX , OPTION_OPTION , LINKEDLIST , BORROWED_BOX )
152180 }
153181}
154182
@@ -238,6 +266,43 @@ fn check_ty(cx: &LateContext<'_, '_>, ast_ty: &hir::Ty, is_local: bool) {
238266 ) ;
239267 return ; // don't recurse into the type
240268 }
269+ } else if match_def_path ( cx. tcx , def_id, & paths:: VEC ) {
270+ if_chain ! {
271+ // Get the _ part of Vec<_>
272+ if let Some ( ref last) = last_path_segment( qpath) . args;
273+ if let Some ( ty) = last. args. iter( ) . find_map( |arg| match arg {
274+ GenericArg :: Type ( ty) => Some ( ty) ,
275+ GenericArg :: Lifetime ( _) => None ,
276+ } ) ;
277+ // ty is now _ at this point
278+ if let TyKind :: Path ( ref ty_qpath) = ty. node;
279+ let def = cx. tables. qpath_def( ty_qpath, ty. hir_id) ;
280+ if let Some ( def_id) = opt_def_id( def) ;
281+ if Some ( def_id) == cx. tcx. lang_items( ) . owned_box( ) ;
282+ // At this point, we know ty is Box<T>, now get T
283+ if let Some ( ref last) = last_path_segment( ty_qpath) . args;
284+ if let Some ( ty) = last. args. iter( ) . find_map( |arg| match arg {
285+ GenericArg :: Type ( ty) => Some ( ty) ,
286+ GenericArg :: Lifetime ( _) => None ,
287+ } ) ;
288+ if let TyKind :: Path ( ref ty_qpath) = ty. node;
289+ let def = cx. tables. qpath_def( ty_qpath, ty. hir_id) ;
290+ if let Some ( def_id) = opt_def_id( def) ;
291+ let boxed_type = cx. tcx. type_of( def_id) ;
292+ if boxed_type. is_sized( cx. tcx. at( ty. span) , cx. param_env) ;
293+ then {
294+ span_lint_and_sugg(
295+ cx,
296+ VEC_BOX ,
297+ ast_ty. span,
298+ "`Vec<T>` is already on the heap, the boxing is unnecessary." ,
299+ "try" ,
300+ format!( "Vec<{}>" , boxed_type) ,
301+ Applicability :: MaybeIncorrect ,
302+ ) ;
303+ return ; // don't recurse into the type
304+ }
305+ }
241306 } else if match_def_path ( cx. tcx , def_id, & paths:: OPTION ) {
242307 if match_type_parameter ( cx, qpath, & paths:: OPTION ) {
243308 span_lint (
0 commit comments