Skip to content

Commit

Permalink
Lint for Vec<Box<T: Sized>> - Closes rust-lang#3530
Browse files Browse the repository at this point in the history
  • Loading branch information
Kampfkarren committed Dec 13, 2018
1 parent 379c934 commit ab07050
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 2 deletions.
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
types::UNIT_ARG,
types::UNIT_CMP,
types::UNNECESSARY_CAST,
types::VEC_BOX_SIZED,
unicode::ZERO_WIDTH_SPACE,
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
unused_io_amount::UNUSED_IO_AMOUNT,
Expand Down Expand Up @@ -931,6 +932,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
types::TYPE_COMPLEXITY,
types::UNIT_ARG,
types::UNNECESSARY_CAST,
types::VEC_BOX_SIZED,
unused_label::UNUSED_LABEL,
zero_div_zero::ZERO_DIVIDED_BY_ZERO,
]);
Expand Down
65 changes: 63 additions & 2 deletions clippy_lints/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::rustc_target::spec::abi::Abi;
use crate::rustc_typeck::hir_ty_to_ty;
use crate::syntax::ast::{FloatTy, IntTy, UintTy};
use crate::syntax::errors::DiagnosticBuilder;
use crate::syntax::source_map::Span;
use crate::syntax::source_map::{DUMMY_SP, Span};
use crate::utils::paths;
use crate::utils::{
clip, comparisons, differing_macro_contexts, higher, in_constant, in_macro, int_bits, last_path_segment,
Expand Down Expand Up @@ -68,6 +68,33 @@ declare_clippy_lint! {
"usage of `Box<Vec<T>>`, vector elements are already on the heap"
}

/// **What it does:** Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code.
///
/// **Why is this bad?** `Vec` already keeps its contents in a separate area on
/// the heap. So if you `Box` its contents, you just add another level of indirection.
///
/// **Known problems:** Vec<Box<T: Sized>> makes sense if T is a large type (see #3530, 1st comment).
///
/// **Example:**
/// ```rust
/// struct X {
/// values: Vec<Box<i32>>,
/// }
/// ```
///
/// Better:
///
/// ```rust
/// struct X {
/// values: Vec<i32>,
/// }
/// ```
declare_clippy_lint! {
pub VEC_BOX_SIZED,
complexity,
"usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap"
}

/// **What it does:** Checks for use of `Option<Option<_>>` in function signatures and type
/// definitions
///
Expand Down Expand Up @@ -148,7 +175,7 @@ declare_clippy_lint! {

impl LintPass for TypePass {
fn get_lints(&self) -> LintArray {
lint_array!(BOX_VEC, OPTION_OPTION, LINKEDLIST, BORROWED_BOX)
lint_array!(BOX_VEC, VEC_BOX_SIZED, OPTION_OPTION, LINKEDLIST, BORROWED_BOX)
}
}

Expand Down Expand Up @@ -238,6 +265,40 @@ fn check_ty(cx: &LateContext<'_, '_>, ast_ty: &hir::Ty, is_local: bool) {
);
return; // don't recurse into the type
}
} else if match_def_path(cx.tcx, def_id, &paths::VEC) {
if_chain! {
// Get the _ part of Vec<_>
if let Some(ref last) = last_path_segment(qpath).args;
if let Some(ty) = last.args.iter().find_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty),
GenericArg::Lifetime(_) => None,
});
// ty is now _ at this point
if let TyKind::Path(ref ty_qpath) = ty.node;
let def = cx.tables.qpath_def(ty_qpath, ty.hir_id);
if let Some(def_id) = opt_def_id(def);
if Some(def_id) == cx.tcx.lang_items().owned_box();
// At this point, we know ty is Box<T>, now get T
if let Some(ref last) = last_path_segment(ty_qpath).args;
if let Some(ty) = last.args.iter().find_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty),
GenericArg::Lifetime(_) => None,
});
if let TyKind::Path(ref ty_qpath) = ty.node;
let def = cx.tables.qpath_def(ty_qpath, ty.hir_id);
if let Some(def_id) = opt_def_id(def);
let boxed_type = cx.tcx.type_of(def_id);
if boxed_type.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env);
then {
span_help_and_lint(
cx,
VEC_BOX_SIZED,
ast_ty.span,
"you seem to be trying to use `Vec<Box<T>>`, but T is Sized. Consider using just `Vec<T>`",
"`Vec<T>` is already on the heap, `Vec<Box<T>>` makes an extra allocation.",
)
}
}
} else if match_def_path(cx.tcx, def_id, &paths::OPTION) {
if match_type_parameter(cx, qpath, &paths::OPTION) {
span_lint(
Expand Down
17 changes: 17 additions & 0 deletions tests/ui/vec_box_sized.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
struct SizedStruct {
_a: i32,
}

struct UnsizedStruct {
_a: [i32],
}

struct StructWithVecBox {
sized_type: Vec<Box<SizedStruct>>,
}

struct StructWithVecBoxButItsUnsized {
unsized_type: Vec<Box<UnsizedStruct>>,
}

fn main() {}
11 changes: 11 additions & 0 deletions tests/ui/vec_box_sized.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error: you seem to be trying to use `Vec<Box<T>>`, but T is Sized. Consider using just `Vec<T>`
--> $DIR/vec_box_sized.rs:10:14
|
10 | sized_type: Vec<Box<SizedStruct>>,
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::vec-box-sized` implied by `-D warnings`
= help: `Vec<T>` is already on the heap, `Vec<Box<T>>` makes an extra allocation.

error: aborting due to previous error

0 comments on commit ab07050

Please sign in to comment.