From e5058a8f0c8b9ad6dbfe53fb8b84e636db501ae2 Mon Sep 17 00:00:00 2001 From: Falco Hirschenberger Date: Sat, 1 Nov 2014 09:10:02 +0100 Subject: [PATCH] Add lint for checking exceeding bitshifts #17713 --- src/librustc/lint/builtin.rs | 56 +++++++++++++++++- src/test/compile-fail/huge-array-simple.rs | 2 +- .../compile-fail/lint-exceeding-bitshifts.rs | 58 +++++++++++++++++++ 3 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 src/test/compile-fail/lint-exceeding-bitshifts.rs diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 3e53620cbd475..76ef6206d6416 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -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) { diff --git a/src/test/compile-fail/huge-array-simple.rs b/src/test/compile-fail/huge-array-simple.rs index b23d0716e6ddb..17f85c7bd2b8d 100644 --- a/src/test/compile-fail/huge-array-simple.rs +++ b/src/test/compile-fail/huge-array-simple.rs @@ -11,5 +11,5 @@ // error-pattern: too big for the current fn main() { - let fat : [u8, ..(1<<61)+(1<<31)] = [0, ..(1<<61)+(1<<31)]; + let fat : [u8, ..(1<<61)+(1<<31)] = [0, ..(1u64<<61) as uint +(1u64<<31) as uint]; } diff --git a/src/test/compile-fail/lint-exceeding-bitshifts.rs b/src/test/compile-fail/lint-exceeding-bitshifts.rs new file mode 100644 index 0000000000000..f270994bd38ef --- /dev/null +++ b/src/test/compile-fail/lint-exceeding-bitshifts.rs @@ -0,0 +1,58 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(exceeding_bitshifts)] +#![allow(unused_variables)] + +fn main() { + let n = 1u8 << 8; + let n = 1u8 << 9; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u16 << 16; + let n = 1u16 << 17; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u32 << 32; + let n = 1u32 << 33; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u64 << 64; + let n = 1u64 << 65; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i8 << 8; + let n = 1i8 << 9; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i16 << 16; + let n = 1i16 << 17; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i32 << 32; + let n = 1i32 << 33; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i64 << 64; + let n = 1i64 << 65; //~ ERROR: bitshift exceeds the type's number of bits + + let n = 1u8 >> 8; + let n = 1u8 >> 9; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u16 >> 16; + let n = 1u16 >> 17; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u32 >> 32; + let n = 1u32 >> 33; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1u64 >> 64; + let n = 1u64 >> 65; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i8 >> 8; + let n = 1i8 >> 9; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i16 >> 16; + let n = 1i16 >> 17; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i32 >> 32; + let n = 1i32 >> 33; //~ ERROR: bitshift exceeds the type's number of bits + let n = 1i64 >> 64; + let n = 1i64 >> 65; //~ ERROR: bitshift exceeds the type's number of bits + + let n = 1u8; + let n = n << 8; + let n = n << 9; //~ ERROR: bitshift exceeds the type's number of bits + + let n = 1u8 << -9; //~ ERROR: bitshift exceeds the type's number of bits + + let n = 1u8 << (4+4); + let n = 1u8 << (4+5); //~ ERROR: bitshift exceeds the type's number of bits +} +