@@ -24,7 +24,7 @@ use crate::rustc_target::spec::abi::Abi;
24
24
use crate :: rustc_typeck:: hir_ty_to_ty;
25
25
use crate :: syntax:: ast:: { FloatTy , IntTy , UintTy } ;
26
26
use crate :: syntax:: errors:: DiagnosticBuilder ;
27
- use crate :: syntax:: source_map:: Span ;
27
+ use crate :: syntax:: source_map:: { DUMMY_SP , Span } ;
28
28
use crate :: utils:: paths;
29
29
use crate :: utils:: {
30
30
clip, comparisons, differing_macro_contexts, higher, in_constant, in_macro, int_bits, last_path_segment,
@@ -68,6 +68,33 @@ declare_clippy_lint! {
68
68
"usage of `Box<Vec<T>>`, vector elements are already on the heap"
69
69
}
70
70
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, 1st comment).
77
+ ///
78
+ /// **Example:**
79
+ /// ```rust
80
+ /// struct X {
81
+ /// values: Vec<Box<i32>>,
82
+ /// }
83
+ /// ```
84
+ ///
85
+ /// Better:
86
+ ///
87
+ /// ```rust
88
+ /// struct X {
89
+ /// values: Vec<i32>,
90
+ /// }
91
+ /// ```
92
+ declare_clippy_lint ! {
93
+ pub VEC_BOX_SIZED ,
94
+ complexity,
95
+ "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap"
96
+ }
97
+
71
98
/// **What it does:** Checks for use of `Option<Option<_>>` in function signatures and type
72
99
/// definitions
73
100
///
@@ -148,7 +175,7 @@ declare_clippy_lint! {
148
175
149
176
impl LintPass for TypePass {
150
177
fn get_lints ( & self ) -> LintArray {
151
- lint_array ! ( BOX_VEC , OPTION_OPTION , LINKEDLIST , BORROWED_BOX )
178
+ lint_array ! ( BOX_VEC , VEC_BOX_SIZED , OPTION_OPTION , LINKEDLIST , BORROWED_BOX )
152
179
}
153
180
}
154
181
@@ -238,6 +265,40 @@ fn check_ty(cx: &LateContext<'_, '_>, ast_ty: &hir::Ty, is_local: bool) {
238
265
) ;
239
266
return ; // don't recurse into the type
240
267
}
268
+ } else if match_def_path ( cx. tcx , def_id, & paths:: VEC ) {
269
+ if_chain ! {
270
+ // Get the _ part of Vec<_>
271
+ if let Some ( ref last) = last_path_segment( qpath) . args;
272
+ if let Some ( ty) = last. args. iter( ) . find_map( |arg| match arg {
273
+ GenericArg :: Type ( ty) => Some ( ty) ,
274
+ GenericArg :: Lifetime ( _) => None ,
275
+ } ) ;
276
+ // ty is now _ at this point
277
+ if let TyKind :: Path ( ref ty_qpath) = ty. node;
278
+ let def = cx. tables. qpath_def( ty_qpath, ty. hir_id) ;
279
+ if let Some ( def_id) = opt_def_id( def) ;
280
+ if Some ( def_id) == cx. tcx. lang_items( ) . owned_box( ) ;
281
+ // At this point, we know ty is Box<T>, now get T
282
+ if let Some ( ref last) = last_path_segment( ty_qpath) . args;
283
+ if let Some ( ty) = last. args. iter( ) . find_map( |arg| match arg {
284
+ GenericArg :: Type ( ty) => Some ( ty) ,
285
+ GenericArg :: Lifetime ( _) => None ,
286
+ } ) ;
287
+ if let TyKind :: Path ( ref ty_qpath) = ty. node;
288
+ let def = cx. tables. qpath_def( ty_qpath, ty. hir_id) ;
289
+ if let Some ( def_id) = opt_def_id( def) ;
290
+ let boxed_type = cx. tcx. type_of( def_id) ;
291
+ if boxed_type. is_sized( cx. tcx. at( DUMMY_SP ) , cx. param_env) ;
292
+ then {
293
+ span_help_and_lint(
294
+ cx,
295
+ VEC_BOX_SIZED ,
296
+ ast_ty. span,
297
+ "you seem to be trying to use `Vec<Box<T>>`, but T is Sized. Consider using just `Vec<T>`" ,
298
+ "`Vec<T>` is already on the heap, `Vec<Box<T>>` makes an extra allocation." ,
299
+ )
300
+ }
301
+ }
241
302
} else if match_def_path ( cx. tcx , def_id, & paths:: OPTION ) {
242
303
if match_type_parameter ( cx, qpath, & paths:: OPTION ) {
243
304
span_lint (
0 commit comments