From 67213c9be408fbfb8b1015d00b1b72a4f3742c75 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 29 Feb 2016 09:36:13 +0100 Subject: [PATCH] lint unportable clike enum discriminants --- README.md | 3 +- src/enum_clike.rs | 56 +++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ tests/compile-fail/enums_clike.rs | 53 +++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 src/enum_clike.rs create mode 100644 tests/compile-fail/enums_clike.rs diff --git a/README.md b/README.md index f421cbdb7c9..d424aca9faa 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A collection of lints to catch common mistakes and improve your Rust code. [Jump to usage instructions](#usage) ##Lints -There are 127 lints included in this crate: +There are 128 lints included in this crate: name | default | meaning ---------------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -38,6 +38,7 @@ name [drop_ref](https://github.com/Manishearth/rust-clippy/wiki#drop_ref) | warn | call to `std::mem::drop` with a reference instead of an owned value, which will not call the `Drop::drop` method on the underlying value [duplicate_underscore_argument](https://github.com/Manishearth/rust-clippy/wiki#duplicate_underscore_argument) | warn | Function arguments having names which only differ by an underscore [empty_loop](https://github.com/Manishearth/rust-clippy/wiki#empty_loop) | warn | empty `loop {}` detected +[enum_clike_unportable_variant](https://github.com/Manishearth/rust-clippy/wiki#enum_clike_unportable_variant) | warn | finds C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32` [enum_glob_use](https://github.com/Manishearth/rust-clippy/wiki#enum_glob_use) | allow | finds use items that import all variants of an enum [enum_variant_names](https://github.com/Manishearth/rust-clippy/wiki#enum_variant_names) | warn | finds enums where all variants share a prefix/postfix [eq_op](https://github.com/Manishearth/rust-clippy/wiki#eq_op) | warn | equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`) diff --git a/src/enum_clike.rs b/src/enum_clike.rs new file mode 100644 index 00000000000..7ee71f41f29 --- /dev/null +++ b/src/enum_clike.rs @@ -0,0 +1,56 @@ +//! lint on C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32` + +use rustc::lint::*; +use syntax::ast::{IntTy, UintTy}; +use syntax::attr::*; +use rustc_front::hir::*; +use rustc::middle::const_eval::{ConstVal, EvalHint, eval_const_expr_partial}; +use rustc::middle::ty; +use utils::span_lint; + +/// **What it does:** Lints on C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`. +/// +/// **Why is this bad?** This will truncate the variant value on 32bit architectures, but works fine on 64 bit. +/// +/// **Known problems:** None +/// +/// **Example:** `#[repr(usize)] enum NonPortable { X = 0x1_0000_0000, Y = 0 }` +declare_lint! { + pub ENUM_CLIKE_UNPORTABLE_VARIANT, Warn, + "finds C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`" +} + +pub struct EnumClikeUnportableVariant; + +impl LintPass for EnumClikeUnportableVariant { + fn get_lints(&self) -> LintArray { + lint_array!(ENUM_CLIKE_UNPORTABLE_VARIANT) + } +} + +impl LateLintPass for EnumClikeUnportableVariant { + #[allow(cast_possible_truncation, cast_sign_loss)] + fn check_item(&mut self, cx: &LateContext, item: &Item) { + if let ItemEnum(ref def, _) = item.node { + for var in &def.variants { + let variant = &var.node; + if let Some(ref disr) = variant.disr_expr { + let cv = eval_const_expr_partial(cx.tcx, &**disr, EvalHint::ExprTypeChecked, None); + let bad = match (cv, &cx.tcx.expr_ty(&**disr).sty) { + (Ok(ConstVal::Int(i)), &ty::TyInt(IntTy::Is)) => i as i32 as i64 != i, + (Ok(ConstVal::Uint(i)), &ty::TyInt(IntTy::Is)) => i as i32 as u64 != i, + (Ok(ConstVal::Int(i)), &ty::TyUint(UintTy::Us)) => (i < 0) || (i as u32 as i64 != i), + (Ok(ConstVal::Uint(i)), &ty::TyUint(UintTy::Us)) => i as u32 as u64 != i, + _ => false, + }; + if bad { + span_lint(cx, + ENUM_CLIKE_UNPORTABLE_VARIANT, + var.span, + "Clike enum variant discriminant is not portable to 32-bit targets"); + } + } + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 5debe2ed50c..50ea765f85d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,6 +51,7 @@ pub mod cyclomatic_complexity; pub mod derive; pub mod drop_ref; pub mod entry; +pub mod enum_clike; pub mod enum_glob_use; pub mod enum_variants; pub mod eq_op; @@ -108,6 +109,7 @@ pub fn plugin_registrar(reg: &mut Registry) { reg.register_late_lint_pass(box eq_op::EqOp); reg.register_early_lint_pass(box enum_variants::EnumVariantNames); reg.register_late_lint_pass(box enum_glob_use::EnumGlobUse); + reg.register_late_lint_pass(box enum_clike::EnumClikeUnportableVariant); reg.register_late_lint_pass(box bit_mask::BitMask); reg.register_late_lint_pass(box ptr_arg::PtrArg); reg.register_late_lint_pass(box needless_bool::NeedlessBool); @@ -211,6 +213,7 @@ pub fn plugin_registrar(reg: &mut Registry) { derive::EXPL_IMPL_CLONE_ON_COPY, drop_ref::DROP_REF, entry::MAP_ENTRY, + enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, enum_variants::ENUM_VARIANT_NAMES, eq_op::EQ_OP, escape::BOXED_LOCAL, diff --git a/tests/compile-fail/enums_clike.rs b/tests/compile-fail/enums_clike.rs new file mode 100644 index 00000000000..f48f9b13de4 --- /dev/null +++ b/tests/compile-fail/enums_clike.rs @@ -0,0 +1,53 @@ +#![feature(plugin, associated_consts)] +#![plugin(clippy)] +#![deny(clippy)] + +#![allow(unused)] + +#[repr(usize)] +enum NonPortable { + X = 0x1_0000_0000, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets + Y = 0, + Z = 0x7FFF_FFFF, + A = 0xFFFF_FFFF, +} + +enum NonPortableNoHint { + X = 0x1_0000_0000, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets + Y = 0, + Z = 0x7FFF_FFFF, + A = 0xFFFF_FFFF, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets +} + +#[repr(isize)] +enum NonPortableSigned { + X = -1, + Y = 0x7FFF_FFFF, + Z = 0xFFFF_FFFF, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets + A = 0x1_0000_0000, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets + B = std::i32::MIN as isize, + C = (std::i32::MIN as isize) - 1, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets +} + +enum NonPortableSignedNoHint { + X = -1, + Y = 0x7FFF_FFFF, + Z = 0xFFFF_FFFF, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets + A = 0x1_0000_0000, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets +} + +/* +FIXME: uncomment once https://github.com/rust-lang/rust/issues/31910 is fixed +#[repr(usize)] +enum NonPortable2 { + X = Trait::Number, + Y = 0, +} + +trait Trait { + const Number: usize = 0x1_0000_0000; +} +*/ + +fn main() { +}