Skip to content

Commit ab07050

Browse files
committed
Lint for Vec<Box<T: Sized>> - Closes #3530
1 parent 379c934 commit ab07050

File tree

4 files changed

+93
-2
lines changed

4 files changed

+93
-2
lines changed

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
766766
types::UNIT_ARG,
767767
types::UNIT_CMP,
768768
types::UNNECESSARY_CAST,
769+
types::VEC_BOX_SIZED,
769770
unicode::ZERO_WIDTH_SPACE,
770771
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
771772
unused_io_amount::UNUSED_IO_AMOUNT,
@@ -931,6 +932,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
931932
types::TYPE_COMPLEXITY,
932933
types::UNIT_ARG,
933934
types::UNNECESSARY_CAST,
935+
types::VEC_BOX_SIZED,
934936
unused_label::UNUSED_LABEL,
935937
zero_div_zero::ZERO_DIVIDED_BY_ZERO,
936938
]);

clippy_lints/src/types.rs

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::rustc_target::spec::abi::Abi;
2424
use crate::rustc_typeck::hir_ty_to_ty;
2525
use crate::syntax::ast::{FloatTy, IntTy, UintTy};
2626
use crate::syntax::errors::DiagnosticBuilder;
27-
use crate::syntax::source_map::Span;
27+
use crate::syntax::source_map::{DUMMY_SP, Span};
2828
use crate::utils::paths;
2929
use crate::utils::{
3030
clip, comparisons, differing_macro_contexts, higher, in_constant, in_macro, int_bits, last_path_segment,
@@ -68,6 +68,33 @@ 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, 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+
7198
/// **What it does:** Checks for use of `Option<Option<_>>` in function signatures and type
7299
/// definitions
73100
///
@@ -148,7 +175,7 @@ declare_clippy_lint! {
148175

149176
impl LintPass for TypePass {
150177
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)
152179
}
153180
}
154181

@@ -238,6 +265,40 @@ fn check_ty(cx: &LateContext<'_, '_>, ast_ty: &hir::Ty, is_local: bool) {
238265
);
239266
return; // don't recurse into the type
240267
}
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+
}
241302
} else if match_def_path(cx.tcx, def_id, &paths::OPTION) {
242303
if match_type_parameter(cx, qpath, &paths::OPTION) {
243304
span_lint(

tests/ui/vec_box_sized.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
struct SizedStruct {
2+
_a: i32,
3+
}
4+
5+
struct UnsizedStruct {
6+
_a: [i32],
7+
}
8+
9+
struct StructWithVecBox {
10+
sized_type: Vec<Box<SizedStruct>>,
11+
}
12+
13+
struct StructWithVecBoxButItsUnsized {
14+
unsized_type: Vec<Box<UnsizedStruct>>,
15+
}
16+
17+
fn main() {}

tests/ui/vec_box_sized.stderr

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: you seem to be trying to use `Vec<Box<T>>`, but T is Sized. Consider using just `Vec<T>`
2+
--> $DIR/vec_box_sized.rs:10:14
3+
|
4+
10 | sized_type: Vec<Box<SizedStruct>>,
5+
| ^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::vec-box-sized` implied by `-D warnings`
8+
= help: `Vec<T>` is already on the heap, `Vec<Box<T>>` makes an extra allocation.
9+
10+
error: aborting due to previous error
11+

0 commit comments

Comments
 (0)