Add empty_enum lint (just a copy of large_enum_variant for now)

This commit is contained in:
Owen Sanchez 2017-02-04 21:07:54 -07:00
parent 3fbaacd768
commit e88e637b67
11 changed files with 120 additions and 28 deletions

View file

@ -173,6 +173,8 @@ fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item: &Item, trait_ref
EXPL_IMPL_CLONE_ON_COPY,
item.span,
"you are implementing `Clone` explicitly on a `Copy` type",
|db| { db.span_note(item.span, "consider deriving `Clone` or removing `Copy`"); });
|db| {
db.span_note(item.span, "consider deriving `Clone` or removing `Copy`");
});
}
}

View file

@ -0,0 +1,82 @@
//! lint when there is an enum with no variants
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 `enum`s with no variants.
///
/// **Why is this bad?** Enum's with no variants should be replaced with `!`.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// enum Test {}
/// ```
declare_lint! {
pub EMPTY_ENUM,
Warn,
"enum with no variants"
}
#[derive(Copy,Clone)]
pub struct EmptyEnum;
impl LintPass for EmptyEnum {
fn get_lints(&self) -> LintArray {
lint_array!(EMPTY_ENUM)
}
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EmptyEnum {
fn check_item(&mut self, cx: &LateContext, item: &Item) {
let did = cx.tcx.hir.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 > 0 {
span_lint_and_then(cx, EMPTY_ENUM, 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");
});
}
});
}
}
}
}

View file

@ -72,6 +72,7 @@ pub mod derive;
pub mod doc;
pub mod double_parens;
pub mod drop_forget_ref;
pub mod empty_enum;
pub mod entry;
pub mod enum_clike;
pub mod enum_glob_use;
@ -265,6 +266,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
max_single_char_names: conf.max_single_char_names,
});
reg.register_late_lint_pass(box drop_forget_ref::Pass);
reg.register_late_lint_pass(box empty_enum::EmptyEnum);
reg.register_late_lint_pass(box types::AbsurdExtremeComparisons);
reg.register_late_lint_pass(box types::InvalidUpcastComparisons);
reg.register_late_lint_pass(box regex::Pass::default());

View file

@ -1,8 +1,8 @@
use rustc::lint::*;
use rustc::hir::*;
use syntax::ast;
use utils::{is_adjusted, match_path, match_trait_method, match_type, remove_blocks, paths, snippet, span_help_and_lint,
walk_ptrs_ty, walk_ptrs_ty_depth, iter_input_pats};
use utils::{is_adjusted, match_path, match_trait_method, match_type, remove_blocks, paths, snippet,
span_help_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth, iter_input_pats};
/// **What it does:** Checks for mapping `clone()` over an iterator.
///

View file

@ -1213,7 +1213,9 @@ fn lint_single_char_pattern(cx: &LateContext, expr: &hir::Expr, arg: &hir::Expr)
SINGLE_CHAR_PATTERN,
arg.span,
"single-character string constant used as pattern",
|db| { db.span_suggestion(expr.span, "try using a char instead:", hint); });
|db| {
db.span_suggestion(expr.span, "try using a char instead:", hint);
});
}
}
}

View file

@ -74,7 +74,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBool {
NEEDLESS_BOOL,
e.span,
"this if-then-else expression returns a bool literal",
|db| { db.span_suggestion(e.span, "you can reduce it to", hint); });
|db| {
db.span_suggestion(e.span, "you can reduce it to", hint);
});
};
match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) {
(RetBool(true), RetBool(true)) |
@ -121,7 +123,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoolComparison {
BOOL_COMPARISON,
e.span,
"equality checks against true are unnecessary",
|db| { db.span_suggestion(e.span, "try simplifying it as shown:", hint); });
|db| {
db.span_suggestion(e.span, "try simplifying it as shown:", hint);
});
},
(Other, Bool(true)) => {
let hint = snippet(cx, left_side.span, "..").into_owned();
@ -129,7 +133,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoolComparison {
BOOL_COMPARISON,
e.span,
"equality checks against true are unnecessary",
|db| { db.span_suggestion(e.span, "try simplifying it as shown:", hint); });
|db| {
db.span_suggestion(e.span, "try simplifying it as shown:", hint);
});
},
(Bool(false), Other) => {
let hint = Sugg::hir(cx, right_side, "..");

View file

@ -120,11 +120,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
return;
}
}
span_lint_and_then(cx,
UNNECESSARY_OPERATION,
stmt.span,
"statement can be reduced",
|db| { db.span_suggestion(stmt.span, "replace it with", snippet); });
span_lint_and_then(cx, UNNECESSARY_OPERATION, stmt.span, "statement can be reduced", |db| {
db.span_suggestion(stmt.span, "replace it with", snippet);
});
}
}
}

View file

@ -36,8 +36,7 @@ impl LintPass for Precedence {
impl EarlyLintPass for Precedence {
fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
if let ExprKind::Binary(Spanned { node: op, .. }, ref left, ref right) = expr.node {
let span_sugg =
|expr: &Expr, sugg| {
let span_sugg = |expr: &Expr, sugg| {
span_lint_and_then(cx, PRECEDENCE, expr.span, "operator precedence can trip the unwary", |db| {
db.span_suggestion(expr.span, "consider parenthesizing your expression", sugg);
});

View file

@ -250,7 +250,9 @@ fn lint_shadow<'a, 'tcx: 'a>(
&format!("`{}` is shadowed by itself in `{}`",
snippet(cx, pattern_span, "_"),
snippet(cx, expr.span, "..")),
|db| { db.span_note(prev_span, "previous binding is here"); });
|db| {
db.span_note(prev_span, "previous binding is here");
});
} else if contains_self(cx, name, expr) {
span_lint_and_then(cx,
SHADOW_REUSE,
@ -280,7 +282,9 @@ fn lint_shadow<'a, 'tcx: 'a>(
SHADOW_UNRELATED,
span,
&format!("`{}` shadows a previous declaration", snippet(cx, pattern_span, "_")),
|db| { db.span_note(prev_span, "previous binding is here"); });
|db| {
db.span_note(prev_span, "previous binding is here");
});
}
}

View file

@ -258,9 +258,8 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg, rhs: &Sugg) -> Sugg<'static> {
fn needs_paren(op: &AssocOp, other: &AssocOp, dir: Associativity) -> bool {
other.precedence() < op.precedence() ||
(other.precedence() == op.precedence() &&
((op != other && associativity(op) != dir) ||
(op == other && associativity(op) != Associativity::Both))) || is_shift(op) && is_arith(other) ||
is_shift(other) && is_arith(op)
((op != other && associativity(op) != dir) || (op == other && associativity(op) != Associativity::Both))) ||
is_shift(op) && is_arith(other) || is_shift(other) && is_arith(op)
}
let lhs_paren = if let Sugg::BinOp(ref lop, _) = *lhs {

View file

@ -81,11 +81,9 @@ fn check_vec_macro(cx: &LateContext, vec_args: &higher::VecArgs, span: Span) {
},
};
span_lint_and_then(cx,
USELESS_VEC,
span,
"useless use of `vec!`",
|db| { db.span_suggestion(span, "you can use a slice directly", snippet); });
span_lint_and_then(cx, USELESS_VEC, span, "useless use of `vec!`", |db| {
db.span_suggestion(span, "you can use a slice directly", snippet);
});
}
/// Return the item type of the vector (ie. the `T` in `Vec<T>`).