Merge pull request #1492 from Manishearth/largeEnumVariant
large_enum_variants lint suggests to box variants above a configurable limit
This commit is contained in:
commit
b1be0d6457
@ -337,6 +337,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
[`iter_next_loop`]: https://github.com/Manishearth/rust-clippy/wiki#iter_next_loop
|
[`iter_next_loop`]: https://github.com/Manishearth/rust-clippy/wiki#iter_next_loop
|
||||||
[`iter_nth`]: https://github.com/Manishearth/rust-clippy/wiki#iter_nth
|
[`iter_nth`]: https://github.com/Manishearth/rust-clippy/wiki#iter_nth
|
||||||
[`iter_skip_next`]: https://github.com/Manishearth/rust-clippy/wiki#iter_skip_next
|
[`iter_skip_next`]: https://github.com/Manishearth/rust-clippy/wiki#iter_skip_next
|
||||||
|
[`large_enum_variant`]: https://github.com/Manishearth/rust-clippy/wiki#large_enum_variant
|
||||||
[`len_without_is_empty`]: https://github.com/Manishearth/rust-clippy/wiki#len_without_is_empty
|
[`len_without_is_empty`]: https://github.com/Manishearth/rust-clippy/wiki#len_without_is_empty
|
||||||
[`len_zero`]: https://github.com/Manishearth/rust-clippy/wiki#len_zero
|
[`len_zero`]: https://github.com/Manishearth/rust-clippy/wiki#len_zero
|
||||||
[`let_and_return`]: https://github.com/Manishearth/rust-clippy/wiki#let_and_return
|
[`let_and_return`]: https://github.com/Manishearth/rust-clippy/wiki#let_and_return
|
||||||
|
@ -180,7 +180,7 @@ transparently:
|
|||||||
|
|
||||||
## Lints
|
## Lints
|
||||||
|
|
||||||
There are 183 lints included in this crate:
|
There are 184 lints included in this crate:
|
||||||
|
|
||||||
name | default | triggers on
|
name | default | triggers on
|
||||||
-----------------------------------------------------------------------------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------
|
||||||
@ -255,6 +255,7 @@ name
|
|||||||
[iter_next_loop](https://github.com/Manishearth/rust-clippy/wiki#iter_next_loop) | warn | for-looping over `_.next()` which is probably not intended
|
[iter_next_loop](https://github.com/Manishearth/rust-clippy/wiki#iter_next_loop) | warn | for-looping over `_.next()` which is probably not intended
|
||||||
[iter_nth](https://github.com/Manishearth/rust-clippy/wiki#iter_nth) | warn | using `.iter().nth()` on a standard library type with O(1) element access
|
[iter_nth](https://github.com/Manishearth/rust-clippy/wiki#iter_nth) | warn | using `.iter().nth()` on a standard library type with O(1) element access
|
||||||
[iter_skip_next](https://github.com/Manishearth/rust-clippy/wiki#iter_skip_next) | warn | using `.skip(x).next()` on an iterator
|
[iter_skip_next](https://github.com/Manishearth/rust-clippy/wiki#iter_skip_next) | warn | using `.skip(x).next()` on an iterator
|
||||||
|
[large_enum_variant](https://github.com/Manishearth/rust-clippy/wiki#large_enum_variant) | warn | large variants on an enum
|
||||||
[len_without_is_empty](https://github.com/Manishearth/rust-clippy/wiki#len_without_is_empty) | warn | traits or impls with a public `len` method but no corresponding `is_empty` method
|
[len_without_is_empty](https://github.com/Manishearth/rust-clippy/wiki#len_without_is_empty) | warn | traits or impls with a public `len` method but no corresponding `is_empty` method
|
||||||
[len_zero](https://github.com/Manishearth/rust-clippy/wiki#len_zero) | warn | checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead
|
[len_zero](https://github.com/Manishearth/rust-clippy/wiki#len_zero) | warn | checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead
|
||||||
[let_and_return](https://github.com/Manishearth/rust-clippy/wiki#let_and_return) | warn | creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block
|
[let_and_return](https://github.com/Manishearth/rust-clippy/wiki#let_and_return) | warn | creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block
|
||||||
|
98
clippy_lints/src/large_enum_variant.rs
Normal file
98
clippy_lints/src/large_enum_variant.rs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
//! lint when there are large variants on an enum
|
||||||
|
|
||||||
|
use rustc::lint::*;
|
||||||
|
use rustc::hir::*;
|
||||||
|
use utils::{span_lint_and_then, snippet_opt};
|
||||||
|
use rustc::ty::layout::TargetDataLayout;
|
||||||
|
use rustc::ty::TypeFoldable;
|
||||||
|
use rustc::traits::Reveal;
|
||||||
|
|
||||||
|
/// **What it does:** Checks for large variants on `enum`s.
|
||||||
|
///
|
||||||
|
/// **Why is this bad?** Enum size is bounded by the largest variant. Having a large variant
|
||||||
|
/// can penalize the memory layout of that enum.
|
||||||
|
///
|
||||||
|
/// **Known problems:** None.
|
||||||
|
///
|
||||||
|
/// **Example:**
|
||||||
|
/// ```rust
|
||||||
|
/// enum Test {
|
||||||
|
/// A(i32),
|
||||||
|
/// B([i32; 8000]),
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
declare_lint! {
|
||||||
|
pub LARGE_ENUM_VARIANT,
|
||||||
|
Warn,
|
||||||
|
"large variants on an enum"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy,Clone)]
|
||||||
|
pub struct LargeEnumVariant {
|
||||||
|
maximum_variant_size_allowed: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LargeEnumVariant {
|
||||||
|
pub fn new(maximum_variant_size_allowed: u64) -> Self {
|
||||||
|
LargeEnumVariant { maximum_variant_size_allowed: maximum_variant_size_allowed }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LintPass for LargeEnumVariant {
|
||||||
|
fn get_lints(&self) -> LintArray {
|
||||||
|
lint_array!(LARGE_ENUM_VARIANT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant {
|
||||||
|
fn check_item(&mut self, cx: &LateContext, item: &Item) {
|
||||||
|
let did = cx.tcx.map.local_def_id(item.id);
|
||||||
|
if let ItemEnum(ref def, _) = item.node {
|
||||||
|
let ty = cx.tcx.item_type(did);
|
||||||
|
let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
|
||||||
|
for (i, variant) in adt.variants.iter().enumerate() {
|
||||||
|
let data_layout = TargetDataLayout::parse(cx.sess());
|
||||||
|
cx.tcx.infer_ctxt((), Reveal::All).enter(|infcx| {
|
||||||
|
let size: u64 = variant.fields
|
||||||
|
.iter()
|
||||||
|
.map(|f| {
|
||||||
|
let ty = cx.tcx.item_type(f.did);
|
||||||
|
if ty.needs_subst() {
|
||||||
|
0 // we can't reason about generics, so we treat them as zero sized
|
||||||
|
} else {
|
||||||
|
ty.layout(&infcx)
|
||||||
|
.expect("layout should be computable for concrete type")
|
||||||
|
.size(&data_layout)
|
||||||
|
.bytes()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
|
if size > self.maximum_variant_size_allowed {
|
||||||
|
span_lint_and_then(cx,
|
||||||
|
LARGE_ENUM_VARIANT,
|
||||||
|
def.variants[i].span,
|
||||||
|
"large enum variant found",
|
||||||
|
|db| {
|
||||||
|
if variant.fields.len() == 1 {
|
||||||
|
let span = match def.variants[i].node.data {
|
||||||
|
VariantData::Struct(ref fields, _) |
|
||||||
|
VariantData::Tuple(ref fields, _) => fields[0].ty.span,
|
||||||
|
VariantData::Unit(_) => unreachable!(),
|
||||||
|
};
|
||||||
|
if let Some(snip) = snippet_opt(cx, span) {
|
||||||
|
db.span_suggestion(span,
|
||||||
|
"consider boxing the large fields to reduce the total size of \
|
||||||
|
the enum",
|
||||||
|
format!("Box<{}>", snip));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db.span_help(def.variants[i].span,
|
||||||
|
"consider boxing the large fields to reduce the total size of the enum");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -88,6 +88,7 @@ pub mod identity_op;
|
|||||||
pub mod if_let_redundant_pattern_matching;
|
pub mod if_let_redundant_pattern_matching;
|
||||||
pub mod if_not_else;
|
pub mod if_not_else;
|
||||||
pub mod items_after_statements;
|
pub mod items_after_statements;
|
||||||
|
pub mod large_enum_variant;
|
||||||
pub mod len_zero;
|
pub mod len_zero;
|
||||||
pub mod let_if_seq;
|
pub mod let_if_seq;
|
||||||
pub mod lifetimes;
|
pub mod lifetimes;
|
||||||
@ -293,6 +294,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||||||
reg.register_early_lint_pass(box reference::Pass);
|
reg.register_early_lint_pass(box reference::Pass);
|
||||||
reg.register_early_lint_pass(box double_parens::DoubleParens);
|
reg.register_early_lint_pass(box double_parens::DoubleParens);
|
||||||
reg.register_late_lint_pass(box unused_io_amount::UnusedIoAmount);
|
reg.register_late_lint_pass(box unused_io_amount::UnusedIoAmount);
|
||||||
|
reg.register_late_lint_pass(box large_enum_variant::LargeEnumVariant::new(conf.enum_variant_size_threshold));
|
||||||
|
|
||||||
reg.register_lint_group("clippy_restrictions", vec![
|
reg.register_lint_group("clippy_restrictions", vec![
|
||||||
arithmetic::FLOAT_ARITHMETIC,
|
arithmetic::FLOAT_ARITHMETIC,
|
||||||
@ -384,6 +386,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||||||
functions::TOO_MANY_ARGUMENTS,
|
functions::TOO_MANY_ARGUMENTS,
|
||||||
identity_op::IDENTITY_OP,
|
identity_op::IDENTITY_OP,
|
||||||
if_let_redundant_pattern_matching::IF_LET_REDUNDANT_PATTERN_MATCHING,
|
if_let_redundant_pattern_matching::IF_LET_REDUNDANT_PATTERN_MATCHING,
|
||||||
|
large_enum_variant::LARGE_ENUM_VARIANT,
|
||||||
len_zero::LEN_WITHOUT_IS_EMPTY,
|
len_zero::LEN_WITHOUT_IS_EMPTY,
|
||||||
len_zero::LEN_ZERO,
|
len_zero::LEN_ZERO,
|
||||||
let_if_seq::USELESS_LET_IF_SEQ,
|
let_if_seq::USELESS_LET_IF_SEQ,
|
||||||
|
@ -186,6 +186,8 @@ define_Conf! {
|
|||||||
("too-large-for-stack", too_large_for_stack, 200 => u64),
|
("too-large-for-stack", too_large_for_stack, 200 => u64),
|
||||||
/// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
|
/// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
|
||||||
("enum-variant-name-threshold", enum_variant_name_threshold, 3 => u64),
|
("enum-variant-name-threshold", enum_variant_name_threshold, 3 => u64),
|
||||||
|
/// Lint: LARGE_ENUM_VARIANT. The maximum size of a emum's variant to avoid box suggestion
|
||||||
|
("enum-variant-size-threshold", enum_variant_size_threshold, 200 => u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search for the configuration file.
|
/// Search for the configuration file.
|
||||||
|
53
tests/compile-fail/large_enum_variant.rs
Normal file
53
tests/compile-fail/large_enum_variant.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#![feature(plugin)]
|
||||||
|
#![plugin(clippy)]
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
#![deny(large_enum_variant)]
|
||||||
|
|
||||||
|
enum LargeEnum {
|
||||||
|
A(i32),
|
||||||
|
B([i32; 8000]), //~ ERROR large enum variant found
|
||||||
|
//~^ HELP consider boxing the large fields to reduce the total size of the enum
|
||||||
|
//~| SUGGESTION Box<[i32; 8000]>
|
||||||
|
}
|
||||||
|
|
||||||
|
enum GenericEnum<T> {
|
||||||
|
A(i32),
|
||||||
|
B([i32; 8000]), //~ ERROR large enum variant found
|
||||||
|
//~^ HELP consider boxing the large fields to reduce the total size of the enum
|
||||||
|
//~| SUGGESTION Box<[i32; 8000]>
|
||||||
|
C([T; 8000]),
|
||||||
|
D(T, [i32; 8000]), //~ ERROR large enum variant found
|
||||||
|
//~^ HELP consider boxing the large fields to reduce the total size of the enum
|
||||||
|
}
|
||||||
|
|
||||||
|
trait SomeTrait {
|
||||||
|
type Item;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LargeEnumGeneric<A: SomeTrait> {
|
||||||
|
Var(A::Item), // regression test, this used to ICE
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AnotherLargeEnum {
|
||||||
|
VariantOk(i32, u32),
|
||||||
|
ContainingLargeEnum(LargeEnum), //~ ERROR large enum variant found
|
||||||
|
//~^ HELP consider boxing the large fields to reduce the total size of the enum
|
||||||
|
//~| SUGGESTION Box<LargeEnum>
|
||||||
|
ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]), //~ ERROR large enum variant found
|
||||||
|
//~^ HELP consider boxing the large fields to reduce the total size of the enum
|
||||||
|
VoidVariant,
|
||||||
|
StructLikeLittle { x: i32, y: i32 },
|
||||||
|
StructLikeLarge { x: [i32; 8000], y: i32 }, //~ ERROR large enum variant found
|
||||||
|
//~^ HELP consider boxing the large fields to reduce the total size of the enum
|
||||||
|
StructLikeLarge2 { //~ ERROR large enum variant found
|
||||||
|
x:
|
||||||
|
[i32; 8000] //~ SUGGESTION Box<[i32; 8000]>
|
||||||
|
//~^ HELP consider boxing the large fields to reduce the total size of the enum
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user