Skip to content

Commit 35e00e2

Browse files
committed
Merge pull request #710 from oli-obk/enum_disrc_portability
lint unportable clike enum discriminants
2 parents 105a14f + 67213c9 commit 35e00e2

File tree

4 files changed

+114
-1
lines changed

4 files changed

+114
-1
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ A collection of lints to catch common mistakes and improve your Rust code.
88
[Jump to usage instructions](#usage)
99

1010
##Lints
11-
There are 127 lints included in this crate:
11+
There are 128 lints included in this crate:
1212

1313
name | default | meaning
1414
---------------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -38,6 +38,7 @@ name
3838
[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
3939
[duplicate_underscore_argument](https://github.com/Manishearth/rust-clippy/wiki#duplicate_underscore_argument) | warn | Function arguments having names which only differ by an underscore
4040
[empty_loop](https://github.com/Manishearth/rust-clippy/wiki#empty_loop) | warn | empty `loop {}` detected
41+
[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`
4142
[enum_glob_use](https://github.com/Manishearth/rust-clippy/wiki#enum_glob_use) | allow | finds use items that import all variants of an enum
4243
[enum_variant_names](https://github.com/Manishearth/rust-clippy/wiki#enum_variant_names) | warn | finds enums where all variants share a prefix/postfix
4344
[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`)

src/enum_clike.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//! lint on C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`
2+
3+
use rustc::lint::*;
4+
use syntax::ast::{IntTy, UintTy};
5+
use syntax::attr::*;
6+
use rustc_front::hir::*;
7+
use rustc::middle::const_eval::{ConstVal, EvalHint, eval_const_expr_partial};
8+
use rustc::middle::ty;
9+
use utils::span_lint;
10+
11+
/// **What it does:** Lints on C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`.
12+
///
13+
/// **Why is this bad?** This will truncate the variant value on 32bit architectures, but works fine on 64 bit.
14+
///
15+
/// **Known problems:** None
16+
///
17+
/// **Example:** `#[repr(usize)] enum NonPortable { X = 0x1_0000_0000, Y = 0 }`
18+
declare_lint! {
19+
pub ENUM_CLIKE_UNPORTABLE_VARIANT, Warn,
20+
"finds C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`"
21+
}
22+
23+
pub struct EnumClikeUnportableVariant;
24+
25+
impl LintPass for EnumClikeUnportableVariant {
26+
fn get_lints(&self) -> LintArray {
27+
lint_array!(ENUM_CLIKE_UNPORTABLE_VARIANT)
28+
}
29+
}
30+
31+
impl LateLintPass for EnumClikeUnportableVariant {
32+
#[allow(cast_possible_truncation, cast_sign_loss)]
33+
fn check_item(&mut self, cx: &LateContext, item: &Item) {
34+
if let ItemEnum(ref def, _) = item.node {
35+
for var in &def.variants {
36+
let variant = &var.node;
37+
if let Some(ref disr) = variant.disr_expr {
38+
let cv = eval_const_expr_partial(cx.tcx, &**disr, EvalHint::ExprTypeChecked, None);
39+
let bad = match (cv, &cx.tcx.expr_ty(&**disr).sty) {
40+
(Ok(ConstVal::Int(i)), &ty::TyInt(IntTy::Is)) => i as i32 as i64 != i,
41+
(Ok(ConstVal::Uint(i)), &ty::TyInt(IntTy::Is)) => i as i32 as u64 != i,
42+
(Ok(ConstVal::Int(i)), &ty::TyUint(UintTy::Us)) => (i < 0) || (i as u32 as i64 != i),
43+
(Ok(ConstVal::Uint(i)), &ty::TyUint(UintTy::Us)) => i as u32 as u64 != i,
44+
_ => false,
45+
};
46+
if bad {
47+
span_lint(cx,
48+
ENUM_CLIKE_UNPORTABLE_VARIANT,
49+
var.span,
50+
"Clike enum variant discriminant is not portable to 32-bit targets");
51+
}
52+
}
53+
}
54+
}
55+
}
56+
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ pub mod cyclomatic_complexity;
5151
pub mod derive;
5252
pub mod drop_ref;
5353
pub mod entry;
54+
pub mod enum_clike;
5455
pub mod enum_glob_use;
5556
pub mod enum_variants;
5657
pub mod eq_op;
@@ -108,6 +109,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
108109
reg.register_late_lint_pass(box eq_op::EqOp);
109110
reg.register_early_lint_pass(box enum_variants::EnumVariantNames);
110111
reg.register_late_lint_pass(box enum_glob_use::EnumGlobUse);
112+
reg.register_late_lint_pass(box enum_clike::EnumClikeUnportableVariant);
111113
reg.register_late_lint_pass(box bit_mask::BitMask);
112114
reg.register_late_lint_pass(box ptr_arg::PtrArg);
113115
reg.register_late_lint_pass(box needless_bool::NeedlessBool);
@@ -211,6 +213,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
211213
derive::EXPL_IMPL_CLONE_ON_COPY,
212214
drop_ref::DROP_REF,
213215
entry::MAP_ENTRY,
216+
enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
214217
enum_variants::ENUM_VARIANT_NAMES,
215218
eq_op::EQ_OP,
216219
escape::BOXED_LOCAL,

tests/compile-fail/enums_clike.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#![feature(plugin, associated_consts)]
2+
#![plugin(clippy)]
3+
#![deny(clippy)]
4+
5+
#![allow(unused)]
6+
7+
#[repr(usize)]
8+
enum NonPortable {
9+
X = 0x1_0000_0000, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets
10+
Y = 0,
11+
Z = 0x7FFF_FFFF,
12+
A = 0xFFFF_FFFF,
13+
}
14+
15+
enum NonPortableNoHint {
16+
X = 0x1_0000_0000, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets
17+
Y = 0,
18+
Z = 0x7FFF_FFFF,
19+
A = 0xFFFF_FFFF, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets
20+
}
21+
22+
#[repr(isize)]
23+
enum NonPortableSigned {
24+
X = -1,
25+
Y = 0x7FFF_FFFF,
26+
Z = 0xFFFF_FFFF, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets
27+
A = 0x1_0000_0000, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets
28+
B = std::i32::MIN as isize,
29+
C = (std::i32::MIN as isize) - 1, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets
30+
}
31+
32+
enum NonPortableSignedNoHint {
33+
X = -1,
34+
Y = 0x7FFF_FFFF,
35+
Z = 0xFFFF_FFFF, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets
36+
A = 0x1_0000_0000, //~ ERROR: Clike enum variant discriminant is not portable to 32-bit targets
37+
}
38+
39+
/*
40+
FIXME: uncomment once https://github.com/rust-lang/rust/issues/31910 is fixed
41+
#[repr(usize)]
42+
enum NonPortable2<T: Trait> {
43+
X = Trait::Number,
44+
Y = 0,
45+
}
46+
47+
trait Trait {
48+
const Number: usize = 0x1_0000_0000;
49+
}
50+
*/
51+
52+
fn main() {
53+
}

0 commit comments

Comments
 (0)