-
Notifications
You must be signed in to change notification settings - Fork 1.6k
lint unportable clike enum discriminants #710
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The description says this lints There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. simple, this one is inferred to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. related issues rust-lang/rust#24290 and UB because of it rust-lang/rust#31886 |
||
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<T: Trait> { | ||
X = Trait::Number, | ||
Y = 0, | ||
} | ||
|
||
trait Trait { | ||
const Number: usize = 0x1_0000_0000; | ||
} | ||
*/ | ||
|
||
fn main() { | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I'm not comparing against constants is explained here and in the preceding posts