Add lint for checking exceeding bitshifts #17713
This commit is contained in:
parent
51a25c7f96
commit
e5058a8f0c
3 changed files with 113 additions and 3 deletions
|
|
@ -30,6 +30,7 @@ use middle::def::*;
|
|||
use middle::typeck::astconv::ast_ty_to_ty;
|
||||
use middle::typeck::infer;
|
||||
use middle::{typeck, ty, def, pat_util, stability};
|
||||
use middle::const_eval::{eval_const_expr_partial, const_int, const_uint};
|
||||
use util::ppaux::{ty_to_string};
|
||||
use util::nodemap::NodeSet;
|
||||
use lint::{Context, LintPass, LintArray};
|
||||
|
|
@ -38,14 +39,16 @@ use std::cmp;
|
|||
use std::collections::HashMap;
|
||||
use std::collections::hashmap::{Occupied, Vacant};
|
||||
use std::slice;
|
||||
use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
|
||||
use std::{int, i8, i16, i32, i64, uint, u8, u16, u32, u64, f32, f64};
|
||||
use syntax::abi;
|
||||
use syntax::ast_map;
|
||||
use syntax::ast_util::is_shift_binop;
|
||||
use syntax::attr::AttrMetaMethods;
|
||||
use syntax::attr;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::parse::token;
|
||||
use syntax::{ast, ast_util, visit};
|
||||
use syntax::ast::{TyI, TyU, TyI8, TyU8, TyI16, TyU16, TyI32, TyU32, TyI64, TyU64};
|
||||
use syntax::ptr::P;
|
||||
use syntax::visit::Visitor;
|
||||
|
||||
|
|
@ -113,6 +116,9 @@ declare_lint!(UNUSED_COMPARISONS, Warn,
|
|||
declare_lint!(OVERFLOWING_LITERALS, Warn,
|
||||
"literal out of range for its type")
|
||||
|
||||
declare_lint!(EXCEEDING_BITSHIFTS, Deny,
|
||||
"shift exceeds the type's number of bits")
|
||||
|
||||
pub struct TypeLimits {
|
||||
/// Id of the last visited negated expression
|
||||
negated_expr_id: ast::NodeId,
|
||||
|
|
@ -128,7 +134,8 @@ impl TypeLimits {
|
|||
|
||||
impl LintPass for TypeLimits {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array!(UNSIGNED_NEGATION, UNUSED_COMPARISONS, OVERFLOWING_LITERALS)
|
||||
lint_array!(UNSIGNED_NEGATION, UNUSED_COMPARISONS, OVERFLOWING_LITERALS,
|
||||
EXCEEDING_BITSHIFTS)
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
|
||||
|
|
@ -170,6 +177,31 @@ impl LintPass for TypeLimits {
|
|||
cx.span_lint(UNUSED_COMPARISONS, e.span,
|
||||
"comparison is useless due to type limits");
|
||||
}
|
||||
|
||||
if is_shift_binop(binop) {
|
||||
let opt_ty_bits = match ty::get(ty::expr_ty(cx.tcx, &**l)).sty {
|
||||
ty::ty_int(t) => Some(int_ty_bits(t)),
|
||||
ty::ty_uint(t) => Some(uint_ty_bits(t)),
|
||||
_ => None
|
||||
};
|
||||
|
||||
if let Some(bits) = opt_ty_bits {
|
||||
let exceeding = if let ast::ExprLit(ref lit) = r.node {
|
||||
if let ast::LitInt(shift, _) = lit.node { shift > bits }
|
||||
else { false }
|
||||
} else {
|
||||
match eval_const_expr_partial(cx.tcx, &**r) {
|
||||
Ok(const_int(shift)) => { shift as u64 > bits },
|
||||
Ok(const_uint(shift)) => { shift > bits },
|
||||
_ => { false }
|
||||
}
|
||||
};
|
||||
if exceeding {
|
||||
cx.span_lint(EXCEEDING_BITSHIFTS, e.span,
|
||||
"bitshift exceeds the type's number of bits");
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
ast::ExprLit(ref lit) => {
|
||||
match ty::get(ty::expr_ty(cx.tcx, e)).sty {
|
||||
|
|
@ -280,6 +312,26 @@ impl LintPass for TypeLimits {
|
|||
}
|
||||
}
|
||||
|
||||
fn int_ty_bits(int_ty: ast::IntTy) -> u64 {
|
||||
match int_ty {
|
||||
ast::TyI => int::BITS as u64,
|
||||
ast::TyI8 => i8::BITS as u64,
|
||||
ast::TyI16 => i16::BITS as u64,
|
||||
ast::TyI32 => i32::BITS as u64,
|
||||
ast::TyI64 => i64::BITS as u64
|
||||
}
|
||||
}
|
||||
|
||||
fn uint_ty_bits(uint_ty: ast::UintTy) -> u64 {
|
||||
match uint_ty {
|
||||
ast::TyU => uint::BITS as u64,
|
||||
ast::TyU8 => u8::BITS as u64,
|
||||
ast::TyU16 => u16::BITS as u64,
|
||||
ast::TyU32 => u32::BITS as u64,
|
||||
ast::TyU64 => u64::BITS as u64
|
||||
}
|
||||
}
|
||||
|
||||
fn check_limits(tcx: &ty::ctxt, binop: ast::BinOp,
|
||||
l: &ast::Expr, r: &ast::Expr) -> bool {
|
||||
let (lit, expr, swap) = match (&l.node, &r.node) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue