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`");
+ });
}
}
--- /dev/null
+//! 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");
+ });
+ }
+ });
+ }
+ }
+ }
+}
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;
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());
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.
///
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);
+ });
}
}
}
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)) |
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();
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, "..");
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);
+ });
}
}
}
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| {
- 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);
- });
- };
+ 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);
+ });
+ };
if !is_bit_op(op) {
return;
&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,
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");
+ });
}
}
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 {
},
};
- 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>`).