Merge branch 'miri'

This commit is contained in:
Mateusz Mikuła 2018-03-15 10:49:13 +01:00
commit 3662ee76ab
29 changed files with 408 additions and 607 deletions

View file

@ -1,13 +1,9 @@
use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::ty;
use rustc::ty::subst::Substs;
use rustc_const_eval::ConstContext;
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize};
use rustc::hir;
use syntax::ast::RangeLimits;
use utils::{self, higher};
use utils::const_to_u64;
use consts::{constant, Constant};
/// **What it does:** Checks for out of bounds array indexing with a constant
/// index.
@ -63,29 +59,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
// Array with known size can be checked statically
let ty = cx.tables.expr_ty(array);
if let ty::TyArray(_, size) = ty.sty {
let size = ConstInt::Usize(
ConstUsize::new(const_to_u64(size), cx.sess().target.usize_ty).expect("array size is invalid"),
);
let parent_item = cx.tcx.hir.get_parent(e.id);
let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
let constcx = ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables);
let size = size.val.to_raw_bits().unwrap();
// Index is a constant uint
if let Ok(const_index) = constcx.eval(index) {
if let ConstVal::Integral(const_index) = const_index.val {
if size <= const_index {
utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "const index is out of bounds");
}
return;
if let Some((Constant::Int(const_index), _)) = constant(cx, index) {
if size <= const_index {
utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "const index is out of bounds");
}
return;
}
// Index is a constant range
if let Some(range) = higher::range(index) {
let start = range.start.map(|start| constcx.eval(start)).map(|v| v.ok());
let end = range.end.map(|end| constcx.eval(end)).map(|v| v.ok());
let start = range.start.map(|start| constant(cx, start).map(|(c, _)| c));
let end = range.end.map(|end| constant(cx, end).map(|(c, _)| c));
if let Some((start, end)) = to_const_range(&start, &end, range.limits, size) {
if start > size || end > size {
@ -114,43 +102,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
/// Returns an option containing a tuple with the start and end (exclusive) of
/// the range.
fn to_const_range(
start: &Option<Option<&ty::Const>>,
end: &Option<Option<&ty::Const>>,
start: &Option<Option<Constant>>,
end: &Option<Option<Constant>>,
limits: RangeLimits,
array_size: ConstInt,
) -> Option<(ConstInt, ConstInt)> {
array_size: u128,
) -> Option<(u128, u128)> {
let start = match *start {
Some(Some(&ty::Const {
val: ConstVal::Integral(x),
..
})) => x,
Some(Some(Constant::Int(x))) => x,
Some(_) => return None,
None => ConstInt::U8(0),
None => 0,
};
let end = match *end {
Some(Some(&ty::Const {
val: ConstVal::Integral(x),
..
})) => if limits == RangeLimits::Closed {
match x {
ConstInt::U8(_) => (x + ConstInt::U8(1)),
ConstInt::U16(_) => (x + ConstInt::U16(1)),
ConstInt::U32(_) => (x + ConstInt::U32(1)),
ConstInt::U64(_) => (x + ConstInt::U64(1)),
ConstInt::U128(_) => (x + ConstInt::U128(1)),
ConstInt::Usize(ConstUsize::Us16(_)) => (x + ConstInt::Usize(ConstUsize::Us16(1))),
ConstInt::Usize(ConstUsize::Us32(_)) => (x + ConstInt::Usize(ConstUsize::Us32(1))),
ConstInt::Usize(ConstUsize::Us64(_)) => (x + ConstInt::Usize(ConstUsize::Us64(1))),
ConstInt::I8(_) => (x + ConstInt::I8(1)),
ConstInt::I16(_) => (x + ConstInt::I16(1)),
ConstInt::I32(_) => (x + ConstInt::I32(1)),
ConstInt::I64(_) => (x + ConstInt::I64(1)),
ConstInt::I128(_) => (x + ConstInt::I128(1)),
ConstInt::Isize(ConstIsize::Is16(_)) => (x + ConstInt::Isize(ConstIsize::Is16(1))),
ConstInt::Isize(ConstIsize::Is32(_)) => (x + ConstInt::Isize(ConstIsize::Is32(1))),
ConstInt::Isize(ConstIsize::Is64(_)) => (x + ConstInt::Isize(ConstIsize::Is64(1))),
}.expect("such a big array is not realistic")
Some(Some(Constant::Int(x))) => if limits == RangeLimits::Closed {
x + 1
} else {
x
},

View file

@ -1,11 +1,10 @@
use rustc::hir::*;
use rustc::hir::def::Def;
use rustc::lint::*;
use rustc_const_eval::lookup_const_by_id;
use syntax::ast::LitKind;
use syntax::codemap::Span;
use utils::{span_lint, span_lint_and_then};
use utils::sugg::Sugg;
use consts::{constant, Constant};
/// **What it does:** Checks for incompatible bit masks in comparisons.
///
@ -302,31 +301,8 @@ fn check_ineffective_gt(cx: &LateContext, span: Span, m: u128, c: u128, op: &str
}
fn fetch_int_literal(cx: &LateContext, lit: &Expr) -> Option<u128> {
use rustc::ty::subst::Substs;
match lit.node {
ExprLit(ref lit_ptr) => {
if let LitKind::Int(value, _) = lit_ptr.node {
Some(value) // TODO: Handle sign
} else {
None
}
},
ExprPath(ref qpath) => {
let def = cx.tables.qpath_def(qpath, lit.hir_id);
if let Def::Const(def_id) = def {
lookup_const_by_id(cx.tcx, cx.param_env.and((def_id, Substs::empty()))).and_then(|(l, _ty)| {
let body = if let Some(id) = cx.tcx.hir.as_local_node_id(l) {
cx.tcx.mir_const_qualif(def_id);
cx.tcx.hir.body(cx.tcx.hir.body_owned_by(id))
} else {
cx.tcx.extern_const_body(def_id).body
};
fetch_int_literal(cx, &body.value)
})
} else {
None
}
},
match constant(cx, lit)?.0 {
Constant::Int(n) => Some(n),
_ => None,
}
}

View file

@ -2,19 +2,18 @@
use rustc::lint::LateContext;
use rustc::hir::def::Def;
use rustc_const_eval::lookup_const_by_id;
use rustc_const_math::ConstInt;
use rustc::hir::*;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::{self, Ty, TyCtxt, Instance};
use rustc::ty::subst::{Subst, Substs};
use std::cmp::Ordering::{self, Equal};
use std::cmp::PartialOrd;
use std::hash::{Hash, Hasher};
use std::mem;
use std::rc::Rc;
use syntax::ast::{FloatTy, LitKind, StrStyle};
use syntax::ast::{FloatTy, LitKind};
use syntax::ptr::P;
use utils::const_to_u64;
use rustc::middle::const_val::ConstVal;
use utils::{sext, unsext, clip};
#[derive(Debug, Copy, Clone)]
pub enum FloatWidth {
@ -36,15 +35,17 @@ impl From<FloatTy> for FloatWidth {
#[derive(Debug, Clone)]
pub enum Constant {
/// a String "abc"
Str(String, StrStyle),
Str(String),
/// a Binary String b"abc"
Binary(Rc<Vec<u8>>),
/// a single char 'a'
Char(char),
/// an integer, third argument is whether the value is negated
Int(ConstInt),
/// a float with given type
Float(String, FloatWidth),
/// an integer's bit representation
Int(u128),
/// an f32
F32(f32),
/// an f64
F64(f64),
/// true or false
Bool(bool),
/// an array of constants
@ -58,20 +59,21 @@ pub enum Constant {
impl PartialEq for Constant {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(&Constant::Str(ref ls, ref l_sty), &Constant::Str(ref rs, ref r_sty)) => ls == rs && l_sty == r_sty,
(&Constant::Str(ref ls), &Constant::Str(ref rs)) => ls == rs,
(&Constant::Binary(ref l), &Constant::Binary(ref r)) => l == r,
(&Constant::Char(l), &Constant::Char(r)) => l == r,
(&Constant::Int(l), &Constant::Int(r)) => {
l.is_negative() == r.is_negative() && l.to_u128_unchecked() == r.to_u128_unchecked()
},
(&Constant::Float(ref ls, _), &Constant::Float(ref rs, _)) => {
(&Constant::Int(l), &Constant::Int(r)) => l == r,
(&Constant::F64(l), &Constant::F64(r)) => {
// we want `Fw32 == FwAny` and `FwAny == Fw64`, by transitivity we must have
// `Fw32 == Fw64` so dont compare them
match (ls.parse::<f64>(), rs.parse::<f64>()) {
// mem::transmute is required to catch non-matching 0.0, -0.0, and NaNs
(Ok(l), Ok(r)) => unsafe { mem::transmute::<f64, u64>(l) == mem::transmute::<f64, u64>(r) },
_ => false,
}
// mem::transmute is required to catch non-matching 0.0, -0.0, and NaNs
unsafe { mem::transmute::<f64, u64>(l) == mem::transmute::<f64, u64>(r) }
},
(&Constant::F32(l), &Constant::F32(r)) => {
// we want `Fw32 == FwAny` and `FwAny == Fw64`, by transitivity we must have
// `Fw32 == Fw64` so dont compare them
// mem::transmute is required to catch non-matching 0.0, -0.0, and NaNs
unsafe { mem::transmute::<f64, u64>(l as f64) == mem::transmute::<f64, u64>(r as f64) }
},
(&Constant::Bool(l), &Constant::Bool(r)) => l == r,
(&Constant::Vec(ref l), &Constant::Vec(ref r)) | (&Constant::Tuple(ref l), &Constant::Tuple(ref r)) => l == r,
@ -87,9 +89,8 @@ impl Hash for Constant {
H: Hasher,
{
match *self {
Constant::Str(ref s, ref k) => {
Constant::Str(ref s) => {
s.hash(state);
k.hash(state);
},
Constant::Binary(ref b) => {
b.hash(state);
@ -98,14 +99,13 @@ impl Hash for Constant {
c.hash(state);
},
Constant::Int(i) => {
i.to_u128_unchecked().hash(state);
i.is_negative().hash(state);
i.hash(state);
},
Constant::Float(ref f, _) => {
// dont use the width here because of PartialEq implementation
if let Ok(f) = f.parse::<f64>() {
unsafe { mem::transmute::<f64, u64>(f) }.hash(state);
}
Constant::F32(f) => {
unsafe { mem::transmute::<f64, u64>(f as f64) }.hash(state);
},
Constant::F64(f) => {
unsafe { mem::transmute::<f64, u64>(f) }.hash(state);
},
Constant::Bool(b) => {
b.hash(state);
@ -124,25 +124,11 @@ impl Hash for Constant {
impl PartialOrd for Constant {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(&Constant::Str(ref ls, ref l_sty), &Constant::Str(ref rs, ref r_sty)) => if l_sty == r_sty {
Some(ls.cmp(rs))
} else {
None
},
(&Constant::Str(ref ls), &Constant::Str(ref rs)) => Some(ls.cmp(rs)),
(&Constant::Char(ref l), &Constant::Char(ref r)) => Some(l.cmp(r)),
(&Constant::Int(l), &Constant::Int(r)) => Some(l.cmp(&r)),
(&Constant::Float(ref ls, _), &Constant::Float(ref rs, _)) => {
match (ls.parse::<f64>(), rs.parse::<f64>()) {
(Ok(ref l), Ok(ref r)) => {
match (l.partial_cmp(r), l.is_sign_positive() == r.is_sign_positive()) {
// Check for comparison of -0.0 and 0.0
(Some(Ordering::Equal), false) => None,
(x, _) => x,
}
},
_ => None,
}
},
(&Constant::F64(l), &Constant::F64(r)) => l.partial_cmp(&r),
(&Constant::F32(l), &Constant::F32(r)) => l.partial_cmp(&r),
(&Constant::Bool(ref l), &Constant::Bool(ref r)) => Some(l.cmp(r)),
(&Constant::Tuple(ref l), &Constant::Tuple(ref r)) | (&Constant::Vec(ref l), &Constant::Vec(ref r)) => {
l.partial_cmp(r)
@ -157,63 +143,25 @@ impl PartialOrd for Constant {
}
/// parse a `LitKind` to a `Constant`
#[allow(cast_possible_wrap)]
pub fn lit_to_constant<'a, 'tcx>(lit: &LitKind, tcx: TyCtxt<'a, 'tcx, 'tcx>, mut ty: Ty<'tcx>) -> Constant {
pub fn lit_to_constant<'a, 'tcx>(lit: &LitKind, ty: Ty<'tcx>) -> Constant {
use syntax::ast::*;
use syntax::ast::LitIntType::*;
use rustc::ty::util::IntTypeExt;
if let ty::TyAdt(adt, _) = ty.sty {
if adt.is_enum() {
ty = adt.repr.discr_type().to_ty(tcx)
}
}
match *lit {
LitKind::Str(ref is, style) => Constant::Str(is.to_string(), style),
LitKind::Byte(b) => Constant::Int(ConstInt::U8(b)),
LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
LitKind::Byte(b) => Constant::Int(b as u128),
LitKind::ByteStr(ref s) => Constant::Binary(Rc::clone(s)),
LitKind::Char(c) => Constant::Char(c),
LitKind::Int(n, hint) => match (&ty.sty, hint) {
(&ty::TyInt(ity), _) | (_, Signed(ity)) => {
Constant::Int(ConstInt::new_signed_truncating(n as i128, ity, tcx.sess.target.isize_ty))
},
(&ty::TyUint(uty), _) | (_, Unsigned(uty)) => {
Constant::Int(ConstInt::new_unsigned_truncating(n as u128, uty, tcx.sess.target.usize_ty))
},
LitKind::Int(n, _) => Constant::Int(n),
LitKind::Float(ref is, _) |
LitKind::FloatUnsuffixed(ref is) => match ty.sty {
ty::TyFloat(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()),
ty::TyFloat(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()),
_ => bug!(),
},
LitKind::Float(ref is, ty) => Constant::Float(is.to_string(), ty.into()),
LitKind::FloatUnsuffixed(ref is) => Constant::Float(is.to_string(), FloatWidth::Any),
LitKind::Bool(b) => Constant::Bool(b),
}
}
fn constant_not(o: &Constant) -> Option<Constant> {
use self::Constant::*;
match *o {
Bool(b) => Some(Bool(!b)),
Int(value) => (!value).ok().map(Int),
_ => None,
}
}
fn constant_negate(o: Constant) -> Option<Constant> {
use self::Constant::*;
match o {
Int(value) => (-value).ok().map(Int),
Float(is, ty) => Some(Float(neg_float_str(&is), ty)),
_ => None,
}
}
fn neg_float_str(s: &str) -> String {
if s.starts_with('-') {
s[1..].to_owned()
} else {
format!("-{}", s)
}
}
pub fn constant(lcx: &LateContext, e: &Expr) -> Option<(Constant, bool)> {
let mut cx = ConstEvalLateContext {
tcx: lcx.tcx,
@ -255,19 +203,19 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
ExprPath(ref qpath) => self.fetch_path(qpath, e.hir_id),
ExprBlock(ref block) => self.block(block),
ExprIf(ref cond, ref then, ref otherwise) => self.ifthenelse(cond, then, otherwise),
ExprLit(ref lit) => Some(lit_to_constant(&lit.node, self.tcx, self.tables.expr_ty(e))),
ExprLit(ref lit) => Some(lit_to_constant(&lit.node, self.tables.expr_ty(e))),
ExprArray(ref vec) => self.multi(vec).map(Constant::Vec),
ExprTup(ref tup) => self.multi(tup).map(Constant::Tuple),
ExprRepeat(ref value, _) => {
let n = match self.tables.expr_ty(e).sty {
ty::TyArray(_, n) => const_to_u64(n),
ty::TyArray(_, n) => n.val.to_raw_bits().expect("array length"),
_ => span_bug!(e.span, "typeck error"),
};
self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
self.expr(value).map(|v| Constant::Repeat(Box::new(v), n as u64))
},
ExprUnary(op, ref operand) => self.expr(operand).and_then(|o| match op {
UnNot => constant_not(&o),
UnNeg => constant_negate(o),
UnNot => self.constant_not(&o, self.tables.expr_ty(e)),
UnNeg => self.constant_negate(o, self.tables.expr_ty(e)),
UnDeref => Some(o),
}),
ExprBinary(op, ref left, ref right) => self.binop(op, left, right),
@ -276,6 +224,42 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
}
}
fn constant_not(&self, o: &Constant, ty: ty::Ty) -> Option<Constant> {
use self::Constant::*;
match *o {
Bool(b) => Some(Bool(!b)),
Int(value) => {
let mut value = !value;
match ty.sty {
ty::TyInt(ity) => Some(Int(unsext(self.tcx, value as i128, ity))),
ty::TyUint(ity) => Some(Int(clip(self.tcx, value, ity))),
_ => None,
}
},
_ => None,
}
}
fn constant_negate(&self, o: Constant, ty: ty::Ty) -> Option<Constant> {
use self::Constant::*;
match o {
Int(value) => {
let ity = match ty.sty {
ty::TyInt(ity) => ity,
_ => return None,
};
// sign extend
let value = sext(self.tcx, value, ity);
let value = value.checked_neg()?;
// clear unused bits
Some(Int(unsext(self.tcx, value, ity)))
},
F32(f) => Some(F32(-f)),
F64(f) => Some(F64(-f)),
_ => None,
}
}
/// create `Some(Vec![..])` of all constants, unless there is any
/// non-constant part
fn multi(&mut self, vec: &[Expr]) -> Option<Vec<Constant>> {
@ -295,27 +279,18 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
} else {
substs.subst(self.tcx, self.substs)
};
let param_env = self.param_env.and((def_id, substs));
if let Some((def_id, substs)) = lookup_const_by_id(self.tcx, param_env) {
let mut cx = Self {
tcx: self.tcx,
tables: self.tcx.typeck_tables_of(def_id),
needed_resolution: false,
substs: substs,
param_env: param_env.param_env,
};
let body = if let Some(id) = self.tcx.hir.as_local_node_id(def_id) {
self.tcx.mir_const_qualif(def_id);
self.tcx.hir.body(self.tcx.hir.body_owned_by(id))
} else {
self.tcx.extern_const_body(def_id).body
};
let ret = cx.expr(&body.value);
if ret.is_some() {
self.needed_resolution = true;
}
return ret;
let instance = Instance::resolve(self.tcx, self.param_env, def_id, substs)?;
let gid = GlobalId {
instance,
promoted: None,
};
use rustc::mir::interpret::GlobalId;
let result = self.tcx.const_eval(self.param_env.and(gid)).ok()?;
let ret = miri_to_const(self.tcx, result);
if ret.is_some() {
self.needed_resolution = true;
}
return ret;
},
_ => {},
}
@ -344,36 +319,127 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
}
fn binop(&mut self, op: BinOp, left: &Expr, right: &Expr) -> Option<Constant> {
let l = if let Some(l) = self.expr(left) {
l
} else {
return None;
};
let l = self.expr(left)?;
let r = self.expr(right);
match (op.node, l, r) {
(BiAdd, Constant::Int(l), Some(Constant::Int(r))) => (l + r).ok().map(Constant::Int),
(BiSub, Constant::Int(l), Some(Constant::Int(r))) => (l - r).ok().map(Constant::Int),
(BiMul, Constant::Int(l), Some(Constant::Int(r))) => (l * r).ok().map(Constant::Int),
(BiDiv, Constant::Int(l), Some(Constant::Int(r))) => (l / r).ok().map(Constant::Int),
(BiRem, Constant::Int(l), Some(Constant::Int(r))) => (l % r).ok().map(Constant::Int),
(BiAnd, Constant::Bool(false), _) => Some(Constant::Bool(false)),
(BiOr, Constant::Bool(true), _) => Some(Constant::Bool(true)),
(BiAnd, Constant::Bool(true), Some(r)) | (BiOr, Constant::Bool(false), Some(r)) => Some(r),
(BiBitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)),
(BiBitXor, Constant::Int(l), Some(Constant::Int(r))) => (l ^ r).ok().map(Constant::Int),
(BiBitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)),
(BiBitAnd, Constant::Int(l), Some(Constant::Int(r))) => (l & r).ok().map(Constant::Int),
(BiBitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)),
(BiBitOr, Constant::Int(l), Some(Constant::Int(r))) => (l | r).ok().map(Constant::Int),
(BiShl, Constant::Int(l), Some(Constant::Int(r))) => (l << r).ok().map(Constant::Int),
(BiShr, Constant::Int(l), Some(Constant::Int(r))) => (l >> r).ok().map(Constant::Int),
(BiEq, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l == r)),
(BiNe, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l != r)),
(BiLt, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l < r)),
(BiLe, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l <= r)),
(BiGe, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l >= r)),
(BiGt, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l > r)),
_ => None,
match (l, r) {
(Constant::Int(l), Some(Constant::Int(r))) => {
match self.tables.expr_ty(left).sty {
ty::TyInt(ity) => {
let l = sext(self.tcx, l, ity);
let r = sext(self.tcx, r, ity);
let zext = |n: i128| Constant::Int(unsext(self.tcx, n, ity));
match op.node {
BiAdd => l.checked_add(r).map(zext),
BiSub => l.checked_sub(r).map(zext),
BiMul => l.checked_mul(r).map(zext),
BiDiv if r != 0 => l.checked_div(r).map(zext),
BiRem if r != 0 => l.checked_rem(r).map(zext),
BiShr => l.checked_shr(r as u128 as u32).map(zext),
BiShl => l.checked_shl(r as u128 as u32).map(zext),
BiBitXor => Some(zext(l ^ r)),
BiBitOr => Some(zext(l | r)),
BiBitAnd => Some(zext(l & r)),
BiEq => Some(Constant::Bool(l == r)),
BiNe => Some(Constant::Bool(l != r)),
BiLt => Some(Constant::Bool(l < r)),
BiLe => Some(Constant::Bool(l <= r)),
BiGe => Some(Constant::Bool(l >= r)),
BiGt => Some(Constant::Bool(l > r)),
_ => None,
}
}
ty::TyUint(_) => {
match op.node {
BiAdd => l.checked_add(r).map(Constant::Int),
BiSub => l.checked_sub(r).map(Constant::Int),
BiMul => l.checked_mul(r).map(Constant::Int),
BiDiv => l.checked_div(r).map(Constant::Int),
BiRem => l.checked_rem(r).map(Constant::Int),
BiShr => l.checked_shr(r as u32).map(Constant::Int),
BiShl => l.checked_shl(r as u32).map(Constant::Int),
BiBitXor => Some(Constant::Int(l ^ r)),
BiBitOr => Some(Constant::Int(l | r)),
BiBitAnd => Some(Constant::Int(l & r)),
BiEq => Some(Constant::Bool(l == r)),
BiNe => Some(Constant::Bool(l != r)),
BiLt => Some(Constant::Bool(l < r)),
BiLe => Some(Constant::Bool(l <= r)),
BiGe => Some(Constant::Bool(l >= r)),
BiGt => Some(Constant::Bool(l > r)),
_ => None,
}
},
_ => None,
}
},
(Constant::F32(l), Some(Constant::F32(r))) => match op.node {
BiAdd => Some(Constant::F32(l + r)),
BiSub => Some(Constant::F32(l - r)),
BiMul => Some(Constant::F32(l * r)),
BiDiv => Some(Constant::F32(l / r)),
BiRem => Some(Constant::F32(l * r)),
BiEq => Some(Constant::Bool(l == r)),
BiNe => Some(Constant::Bool(l != r)),
BiLt => Some(Constant::Bool(l < r)),
BiLe => Some(Constant::Bool(l <= r)),
BiGe => Some(Constant::Bool(l >= r)),
BiGt => Some(Constant::Bool(l > r)),
_ => None,
},
(Constant::F64(l), Some(Constant::F64(r))) => match op.node {
BiAdd => Some(Constant::F64(l + r)),
BiSub => Some(Constant::F64(l - r)),
BiMul => Some(Constant::F64(l * r)),
BiDiv => Some(Constant::F64(l / r)),
BiRem => Some(Constant::F64(l * r)),
BiEq => Some(Constant::Bool(l == r)),
BiNe => Some(Constant::Bool(l != r)),
BiLt => Some(Constant::Bool(l < r)),
BiLe => Some(Constant::Bool(l <= r)),
BiGe => Some(Constant::Bool(l >= r)),
BiGt => Some(Constant::Bool(l > r)),
_ => None,
},
(l, r) => match (op.node, l, r) {
(BiAnd, Constant::Bool(false), _) => Some(Constant::Bool(false)),
(BiOr, Constant::Bool(true), _) => Some(Constant::Bool(true)),
(BiAnd, Constant::Bool(true), Some(r)) | (BiOr, Constant::Bool(false), Some(r)) => Some(r),
(BiBitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)),
(BiBitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)),
(BiBitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)),
_ => None,
},
}
}
}
pub fn miri_to_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, result: &ty::Const<'tcx>) -> Option<Constant> {
use rustc::mir::interpret::{Value, PrimVal};
match result.val {
ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => match result.ty.sty {
ty::TyBool => Some(Constant::Bool(b == 1)),
ty::TyUint(_) | ty::TyInt(_) => Some(Constant::Int(b)),
ty::TyFloat(FloatTy::F32) => Some(Constant::F32(f32::from_bits(b as u32))),
ty::TyFloat(FloatTy::F64) => Some(Constant::F64(f64::from_bits(b as u64))),
// FIXME: implement other conversion
_ => None,
},
ConstVal::Value(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(n))) => match result.ty.sty {
ty::TyRef(_, tam) => match tam.ty.sty {
ty::TyStr => {
let alloc = tcx
.interpret_interner
.get_alloc(ptr.alloc_id)
.unwrap();
let offset = ptr.offset as usize;
let n = n as usize;
String::from_utf8(alloc.bytes[offset..(offset + n)].to_owned()).ok().map(Constant::Str)
},
_ => None,
},
_ => None,
}
// FIXME: implement other conversions
_ => None,
}
}

View file

@ -2,13 +2,14 @@
//! don't fit into an `i32`
use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc_const_math::*;
use rustc::hir::*;
use rustc::ty;
use rustc::traits::Reveal;
use rustc::{ty, traits};
use rustc::ty::subst::Substs;
use syntax::ast::{IntTy, UintTy};
use utils::span_lint;
use consts::{Constant, miri_to_const};
use rustc::ty::util::IntTypeExt;
use rustc::mir::interpret::GlobalId;
/// **What it does:** Checks for C-like enumerations that are
/// `repr(isize/usize)` and have values that don't fit into an `i32`.
@ -43,36 +44,46 @@ impl LintPass for UnportableVariant {
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnportableVariant {
#[allow(cast_possible_truncation, cast_sign_loss)]
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
if cx.tcx.data_layout.pointer_size.bits() != 64 {
return;
}
if let ItemEnum(ref def, _) = item.node {
for var in &def.variants {
let variant = &var.node;
if let Some(body_id) = variant.disr_expr {
let expr = &cx.tcx.hir.body(body_id).value;
let param_env = ty::ParamEnv::empty();
let did = cx.tcx.hir.body_owner_def_id(body_id);
let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
let substs = Substs::identity_for_item(cx.tcx.global_tcx(), did);
let bad = match cx.tcx
.at(expr.span)
.const_eval(param_env.and((did, substs)))
{
Ok(&ty::Const {
val: ConstVal::Integral(Usize(Us64(i))),
..
}) => u64::from(i as u32) != i,
Ok(&ty::Const {
val: ConstVal::Integral(Isize(Is64(i))),
..
}) => i64::from(i as i32) != i,
_ => false,
let instance = ty::Instance::new(did, substs);
let cid = GlobalId {
instance,
promoted: None
};
if bad {
let constant = cx.tcx.const_eval(param_env.and(cid)).ok();
if let Some(Constant::Int(val)) = constant.and_then(|c| miri_to_const(cx.tcx, c)) {
let mut ty = cx.tcx.type_of(did);
if let ty::TyAdt(adt, _) = ty.sty {
if adt.is_enum() {
ty = adt.repr.discr_type().to_ty(cx.tcx);
}
}
match ty.sty {
ty::TyInt(IntTy::Isize) => {
let val = ((val as i128) << 64) >> 64;
if val <= i32::max_value() as i128 && val >= i32::min_value() as i128 {
continue;
}
}
ty::TyUint(UintTy::Usize) if val > u32::max_value() as u128 => {},
_ => continue,
}
span_lint(
cx,
ENUM_CLIKE_UNPORTABLE_VARIANT,
var.span,
"Clike enum variant discriminant is not portable to 32-bit targets",
);
}
};
}
}
}

View file

@ -51,7 +51,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ErasingOp {
fn check(cx: &LateContext, e: &Expr, span: Span) {
if let Some(Constant::Int(v)) = constant_simple(cx, e) {
if v.to_u128_unchecked() == 0 {
if v == 0 {
span_lint(
cx,
ERASING_OP,

View file

@ -1,9 +1,9 @@
use consts::{constant_simple, Constant};
use rustc::hir::*;
use rustc::lint::*;
use rustc_const_math::ConstInt;
use syntax::codemap::Span;
use utils::{in_macro, snippet, span_lint};
use utils::{in_macro, snippet, span_lint, unsext, clip};
use rustc::ty;
/// **What it does:** Checks for identity operations, e.g. `x + 0`.
///
@ -58,29 +58,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityOp {
}
}
fn all_ones(v: &ConstInt) -> bool {
match *v {
ConstInt::I8(i) => i == !0,
ConstInt::I16(i) => i == !0,
ConstInt::I32(i) => i == !0,
ConstInt::I64(i) => i == !0,
ConstInt::I128(i) => i == !0,
ConstInt::U8(i) => i == !0,
ConstInt::U16(i) => i == !0,
ConstInt::U32(i) => i == !0,
ConstInt::U64(i) => i == !0,
ConstInt::U128(i) => i == !0,
_ => false,
}
}
#[allow(cast_possible_wrap)]
fn check(cx: &LateContext, e: &Expr, m: i8, span: Span, arg: Span) {
if let Some(Constant::Int(v)) = constant_simple(cx, e) {
let check = match cx.tables.expr_ty(e).sty {
ty::TyInt(ity) => unsext(cx.tcx, -1i128, ity),
ty::TyUint(uty) => clip(cx.tcx, !0, uty),
_ => return,
};
if match m {
0 => v.to_u128_unchecked() == 0,
-1 => all_ones(&v),
1 => v.to_u128_unchecked() == 1,
0 => v == 0,
-1 => v == check,
1 => v == 1,
_ => unreachable!(),
} {
span_lint(

View file

@ -10,6 +10,7 @@
#![feature(conservative_impl_trait)]
#![feature(inclusive_range_syntax, range_contains)]
#![feature(macro_vis_matcher)]
#![feature(dotdoteq_in_patterns)]
#![allow(unknown_lints, indexing_slicing, shadow_reuse, missing_docs_in_private_items)]
#![recursion_limit = "256"]
@ -37,7 +38,6 @@ extern crate regex_syntax;
extern crate quine_mc_cluskey;
extern crate rustc_const_eval;
extern crate rustc_const_math;
extern crate rustc_errors;
extern crate rustc_plugin;

View file

@ -6,23 +6,19 @@ use rustc::hir::def_id;
use rustc::hir::intravisit::{walk_block, walk_decl, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
use rustc::hir::map::Node::{NodeBlock, NodeExpr, NodeStmt};
use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::middle::region;
// use rustc::middle::region::CodeExtent;
use rustc::middle::expr_use_visitor::*;
use rustc::middle::mem_categorization::Categorization;
use rustc::middle::mem_categorization::cmt;
use rustc::ty::{self, Ty};
use rustc::ty::subst::{Subst, Substs};
use rustc_const_eval::ConstContext;
use rustc::ty::subst::Subst;
use std::collections::{HashMap, HashSet};
use std::iter::{once, Iterator};
use syntax::ast;
use syntax::codemap::Span;
use utils::sugg;
use utils::const_to_u64;
use consts::constant;
use utils::{sugg, sext};
use consts::{constant, Constant};
use utils::{get_enclosing_block, get_parent_expr, higher, in_external_macro, is_integer_literal, is_refutable,
last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, snippet, snippet_opt,
@ -1113,27 +1109,22 @@ fn check_for_loop_reverse_range<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arg: &'tcx
}) = higher::range(arg)
{
// ...and both sides are compile-time constant integers...
let parent_item = cx.tcx.hir.get_parent(arg.id);
let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
let constcx = ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables);
if let Ok(start_idx) = constcx.eval(start) {
if let Ok(end_idx) = constcx.eval(end) {
if let Some((start_idx, _)) = constant(cx, start) {
if let Some((end_idx, _)) = constant(cx, end) {
// ...and the start index is greater than the end index,
// this loop will never run. This is often confusing for developers
// who think that this will iterate from the larger value to the
// smaller value.
let ty = cx.tables.expr_ty(start);
let (sup, eq) = match (start_idx, end_idx) {
(
&ty::Const {
val: ConstVal::Integral(start_idx),
..
},
&ty::Const {
val: ConstVal::Integral(end_idx),
..
},
) => (start_idx > end_idx, start_idx == end_idx),
Constant::Int(start_idx),
Constant::Int(end_idx),
) => (match ty.sty {
ty::TyInt(ity) => sext(cx.tcx, start_idx, ity) > sext(cx.tcx, end_idx, ity),
ty::TyUint(_) => start_idx > end_idx,
_ => false,
}, start_idx == end_idx),
_ => (false, false),
};
@ -1220,7 +1211,7 @@ fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) {
match cx.tables.expr_ty(&args[0]).sty {
// If the length is greater than 32 no traits are implemented for array and
// therefore we cannot use `&`.
ty::TypeVariants::TyArray(_, size) if const_to_u64(size) > 32 => (),
ty::TypeVariants::TyArray(_, size) if size.val.to_raw_bits().expect("array size") > 32 => (),
_ => lint_iter_method(cx, args, arg, method_name),
};
} else {
@ -1795,7 +1786,7 @@ fn is_ref_iterable_type(cx: &LateContext, e: &Expr) -> bool {
fn is_iterable_array(ty: Ty) -> bool {
// IntoIterator is currently only implemented for array sizes <= 32 in rustc
match ty.sty {
ty::TyArray(_, n) => (0..=32).contains(const_to_u64(n)),
ty::TyArray(_, n) => (0..=32).contains(n.val.to_raw_bits().expect("array length")),
_ => false,
}
}
@ -2249,4 +2240,4 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
}
fn decl_without_init(&mut self, _: NodeId, _: Span) {}
}
}

View file

@ -1,19 +1,15 @@
use rustc::hir::*;
use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::ty::{self, Ty};
use rustc::ty::subst::Substs;
use rustc_const_eval::ConstContext;
use rustc_const_math::ConstInt;
use std::cmp::Ordering;
use std::collections::Bound;
use syntax::ast::LitKind;
use syntax::ast::NodeId;
use syntax::codemap::Span;
use utils::paths;
use utils::{expr_block, in_external_macro, is_allowed, is_expn_of, match_qpath, match_type, multispan_sugg,
remove_blocks, snippet, span_lint_and_sugg, span_lint_and_then, span_note_and_lint, walk_ptrs_ty};
use utils::sugg::Sugg;
use consts::{constant, Constant};
/// **What it does:** Checks for matches with a single arm where an `if let`
/// will usually suffice.
@ -343,7 +339,7 @@ fn check_match_bool(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
fn check_overlapping_arms<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ex: &'tcx Expr, arms: &'tcx [Arm]) {
if arms.len() >= 2 && cx.tables.expr_ty(ex).is_integral() {
let ranges = all_ranges(cx, arms, ex.id);
let ranges = all_ranges(cx, arms);
let type_ranges = type_ranges(&ranges);
if !type_ranges.is_empty() {
if let Some((start, end)) = overlapping(&type_ranges) {
@ -460,12 +456,7 @@ fn check_match_as_ref(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
fn all_ranges<'a, 'tcx>(
cx: &LateContext<'a, 'tcx>,
arms: &'tcx [Arm],
id: NodeId,
) -> Vec<SpannedRange<&'tcx ty::Const<'tcx>>> {
let parent_item = cx.tcx.hir.get_parent(id);
let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
let constcx = ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables);
) -> Vec<SpannedRange<Constant>> {
arms.iter()
.flat_map(|arm| {
if let Arm {
@ -478,25 +469,19 @@ fn all_ranges<'a, 'tcx>(
} else {
[].iter()
}.filter_map(|pat| {
if_chain! {
if let PatKind::Range(ref lhs, ref rhs, ref range_end) = pat.node;
if let Ok(lhs) = constcx.eval(lhs);
if let Ok(rhs) = constcx.eval(rhs);
then {
let rhs = match *range_end {
RangeEnd::Included => Bound::Included(rhs),
RangeEnd::Excluded => Bound::Excluded(rhs),
};
return Some(SpannedRange { span: pat.span, node: (lhs, rhs) });
}
if let PatKind::Range(ref lhs, ref rhs, ref range_end) = pat.node {
let lhs = constant(cx, lhs)?.0;
let rhs = constant(cx, rhs)?.0;
let rhs = match *range_end {
RangeEnd::Included => Bound::Included(rhs),
RangeEnd::Excluded => Bound::Excluded(rhs),
};
return Some(SpannedRange { span: pat.span, node: (lhs, rhs) });
}
if_chain! {
if let PatKind::Lit(ref value) = pat.node;
if let Ok(value) = constcx.eval(value);
then {
return Some(SpannedRange { span: pat.span, node: (value, Bound::Included(value)) });
}
if let PatKind::Lit(ref value) = pat.node {
let value = constant(cx, value)?.0;
return Some(SpannedRange { span: pat.span, node: (value.clone(), Bound::Included(value)) });
}
None
@ -511,46 +496,31 @@ pub struct SpannedRange<T> {
pub node: (T, Bound<T>),
}
type TypedRanges = Vec<SpannedRange<ConstInt>>;
type TypedRanges = Vec<SpannedRange<u128>>;
/// Get all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
/// and other types than
/// `Uint` and `Int` probably don't make sense.
fn type_ranges(ranges: &[SpannedRange<&ty::Const>]) -> TypedRanges {
fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges {
ranges
.iter()
.filter_map(|range| match range.node {
(
&ty::Const {
val: ConstVal::Integral(start),
..
},
Bound::Included(&ty::Const {
val: ConstVal::Integral(end),
..
}),
Constant::Int(start),
Bound::Included(Constant::Int(end)),
) => Some(SpannedRange {
span: range.span,
node: (start, Bound::Included(end)),
}),
(
&ty::Const {
val: ConstVal::Integral(start),
..
},
Bound::Excluded(&ty::Const {
val: ConstVal::Integral(end),
..
}),
Constant::Int(start),
Bound::Excluded(Constant::Int(end)),
) => Some(SpannedRange {
span: range.span,
node: (start, Bound::Excluded(end)),
}),
(
&ty::Const {
val: ConstVal::Integral(start),
..
},
Constant::Int(start),
Bound::Unbounded,
) => Some(SpannedRange {
span: range.span,

View file

@ -1,10 +1,7 @@
use rustc::hir;
use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::ty::{self, Ty};
use rustc::hir::def::Def;
use rustc::ty::subst::Substs;
use rustc_const_eval::ConstContext;
use std::borrow::Cow;
use std::fmt;
use std::iter;
@ -16,7 +13,7 @@ use utils::{get_arg_name, get_trait_def_id, implements_trait, in_external_macro,
span_lint, span_lint_and_sugg, span_lint_and_then, span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth};
use utils::paths;
use utils::sugg;
use utils::const_to_u64;
use consts::{constant, Constant};
#[derive(Clone)]
pub struct Pass;
@ -1302,7 +1299,7 @@ fn derefs_to_slice(cx: &LateContext, expr: &hir::Expr, ty: Ty) -> Option<sugg::S
ty::TySlice(_) => true,
ty::TyAdt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
ty::TyAdt(..) => match_type(cx, ty, &paths::VEC),
ty::TyArray(_, size) => const_to_u64(size) < 32,
ty::TyArray(_, size) => size.val.to_raw_bits().expect("array length") < 32,
ty::TyRef(_, ty::TypeAndMut { ty: inner, .. }) => may_slice(cx, inner),
_ => false,
}
@ -1754,14 +1751,7 @@ fn lint_chars_last_cmp_with_unwrap<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, info: &
/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
fn lint_single_char_pattern<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr, arg: &'tcx hir::Expr) {
let parent_item = cx.tcx.hir.get_parent(arg.id);
let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
if let Ok(&ty::Const {
val: ConstVal::Str(r),
..
}) = ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables).eval(arg)
{
if let Some((Constant::Str(r), _)) = constant(cx, arg) {
if r.len() == 1 {
let c = r.chars().next().unwrap();
let snip = snippet(cx, expr.span, "..");

View file

@ -2,18 +2,14 @@ use reexport::*;
use rustc::hir::*;
use rustc::hir::intravisit::FnKind;
use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::ty;
use rustc::ty::subst::Substs;
use rustc_const_eval::ConstContext;
use rustc_const_math::ConstFloat;
use syntax::codemap::{ExpnFormat, Span};
use utils::{get_item_name, get_parent_expr, implements_trait, in_constant, in_macro, is_integer_literal,
iter_input_pats, last_path_segment, match_qpath, match_trait_method, paths, snippet, span_lint,
span_lint_and_then, walk_ptrs_ty};
use utils::sugg::Sugg;
use syntax::ast::{FloatTy, LitKind, CRATE_NODE_ID};
use consts::constant;
use syntax::ast::{LitKind, CRATE_NODE_ID};
use consts::{constant, Constant};
/// **What it does:** Checks for function arguments and let bindings denoted as
/// `ref`.
@ -457,58 +453,10 @@ fn is_named_constant<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) ->
}
fn is_allowed<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) -> bool {
let parent_item = cx.tcx.hir.get_parent(expr.id);
let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
let res = ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables).eval(expr);
if let Ok(&ty::Const {
val: ConstVal::Float(val),
..
}) = res
{
use std::cmp::Ordering;
match val.ty {
FloatTy::F32 => {
let zero = ConstFloat {
ty: FloatTy::F32,
bits: u128::from(0.0_f32.to_bits()),
};
let infinity = ConstFloat {
ty: FloatTy::F32,
bits: u128::from(::std::f32::INFINITY.to_bits()),
};
let neg_infinity = ConstFloat {
ty: FloatTy::F32,
bits: u128::from(::std::f32::NEG_INFINITY.to_bits()),
};
val.try_cmp(zero) == Ok(Ordering::Equal) || val.try_cmp(infinity) == Ok(Ordering::Equal)
|| val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
},
FloatTy::F64 => {
let zero = ConstFloat {
ty: FloatTy::F64,
bits: u128::from(0.0_f64.to_bits()),
};
let infinity = ConstFloat {
ty: FloatTy::F64,
bits: u128::from(::std::f64::INFINITY.to_bits()),
};
let neg_infinity = ConstFloat {
ty: FloatTy::F64,
bits: u128::from(::std::f64::NEG_INFINITY.to_bits()),
};
val.try_cmp(zero) == Ok(Ordering::Equal) || val.try_cmp(infinity) == Ok(Ordering::Equal)
|| val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
},
}
} else {
false
match constant(cx, expr) {
Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(),
Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(),
_ => false,
}
}

View file

@ -205,7 +205,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
let sugg = |db: &mut DiagnosticBuilder| {
if let ty::TypeVariants::TyAdt(ref def, ..) = ty.sty {
if let Some(span) = cx.tcx.hir.span_if_local(def.did) {
let param_env = ty::ParamEnv::empty(traits::Reveal::UserFacing);
let param_env = ty::ParamEnv::empty();
if param_env.can_type_implement_copy(cx.tcx, ty, span).is_ok() {
db.span_help(span, "consider marking this type as Copy");
}

View file

@ -47,8 +47,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NegMultiply {
fn check_mul(cx: &LateContext, span: Span, lit: &Expr, exp: &Expr) {
if_chain! {
if let ExprLit(ref l) = lit.node;
if let Constant::Int(ref ci) = consts::lit_to_constant(&l.node, cx.tcx, cx.tables.expr_ty(lit));
if let Some(val) = ci.to_u64();
if let Constant::Int(val) = consts::lit_to_constant(&l.node, cx.tables.expr_ty(lit));
if val == 1;
if cx.tables.expr_ty(exp).is_integral();
then {

View file

@ -94,16 +94,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
// Range with step_by(0).
if name == "step_by" && args.len() == 2 && has_step_by(cx, &args[0]) {
use consts::{constant, Constant};
use rustc_const_math::ConstInt::Usize;
if let Some((Constant::Int(Usize(us)), _)) = constant(cx, &args[1]) {
if us.as_u64() == 0 {
span_lint(
cx,
ITERATOR_STEP_BY_ZERO,
expr.span,
"Iterator::step_by(0) will panic at runtime",
);
}
if let Some((Constant::Int(0), _)) = constant(cx, &args[1]) {
span_lint(
cx,
ITERATOR_STEP_BY_ZERO,
expr.span,
"Iterator::step_by(0) will panic at runtime",
);
}
} else if name == "zip" && args.len() == 2 {
let iter = &args[0].node;

View file

@ -1,15 +1,11 @@
use regex_syntax;
use rustc::hir::*;
use rustc::lint::*;
use rustc::ty;
use rustc::middle::const_val::ConstVal;
use rustc_const_eval::ConstContext;
use rustc::ty::subst::Substs;
use std::collections::HashSet;
use syntax::ast::{LitKind, NodeId, StrStyle};
use syntax::codemap::{BytePos, Span};
use syntax::symbol::InternedString;
use utils::{is_expn_of, match_def_path, match_type, opt_def_id, paths, span_help_and_lint, span_lint};
use consts::{constant, Constant};
/// **What it does:** Checks [regex](https://crates.io/crates/regex) creation
/// (with `Regex::new`,`RegexBuilder::new` or `RegexSet::new`) for correct
@ -141,17 +137,11 @@ fn str_span(base: Span, c: regex_syntax::ast::Span, offset: usize) -> Span {
Span::new(start, end, base.ctxt())
}
fn const_str<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) -> Option<InternedString> {
let parent_item = cx.tcx.hir.get_parent(e.id);
let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
match ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables).eval(e) {
Ok(&ty::Const {
val: ConstVal::Str(r),
..
}) => Some(r),
fn const_str<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) -> Option<String> {
constant(cx, e).and_then(|(c, _)| match c {
Constant::Str(s) => Some(s),
_ => None,
}
})
}
fn is_trivial_regex(s: &regex_syntax::hir::Hir) -> Option<&'static str> {

View file

@ -5,19 +5,18 @@ use rustc::hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisito
use rustc::lint::*;
use rustc::ty::{self, Ty, TyCtxt, TypeckTables};
use rustc::ty::layout::LayoutOf;
use rustc::ty::subst::Substs;
use rustc_typeck::hir_ty_to_ty;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::borrow::Cow;
use syntax::ast::{FloatTy, IntTy, UintTy};
use syntax::attr::IntType;
use syntax::codemap::Span;
use syntax::errors::DiagnosticBuilder;
use utils::{comparisons, higher, in_constant, in_external_macro, in_macro, last_path_segment, match_def_path, match_path,
multispan_sugg, opt_def_id, same_tys, snippet, snippet_opt, span_help_and_lint, span_lint,
span_lint_and_sugg, span_lint_and_then};
span_lint_and_sugg, span_lint_and_then, clip, unsext, sext, int_bits};
use utils::paths;
use consts::{constant, Constant};
/// Handles all the linting of funky types
#[allow(missing_copy_implementations)]
@ -1298,58 +1297,20 @@ fn detect_absurd_comparison<'a, 'tcx>(
}
fn detect_extreme_expr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) -> Option<ExtremeExpr<'tcx>> {
use rustc::middle::const_val::ConstVal::*;
use rustc_const_math::*;
use rustc_const_eval::*;
use types::ExtremeType::*;
let ty = cx.tables.expr_ty(expr);
match ty.sty {
ty::TyBool | ty::TyInt(_) | ty::TyUint(_) => (),
_ => return None,
};
let cv = constant(cx, expr)?.0;
let parent_item = cx.tcx.hir.get_parent(expr.id);
let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
let cv = match ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables).eval(expr) {
Ok(val) => val,
Err(_) => return None,
};
let which = match (&ty.sty, cv) {
(&ty::TyBool, Constant::Bool(false)) |
(&ty::TyUint(_), Constant::Int(0)) => Minimum,
(&ty::TyInt(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::min_value() >> (128 - int_bits(cx.tcx, ity)), ity) => Minimum,
let which = match (&ty.sty, cv.val) {
(&ty::TyBool, Bool(false)) |
(&ty::TyInt(IntTy::Isize), Integral(Isize(Is32(::std::i32::MIN)))) |
(&ty::TyInt(IntTy::Isize), Integral(Isize(Is64(::std::i64::MIN)))) |
(&ty::TyInt(IntTy::I8), Integral(I8(::std::i8::MIN))) |
(&ty::TyInt(IntTy::I16), Integral(I16(::std::i16::MIN))) |
(&ty::TyInt(IntTy::I32), Integral(I32(::std::i32::MIN))) |
(&ty::TyInt(IntTy::I64), Integral(I64(::std::i64::MIN))) |
(&ty::TyInt(IntTy::I128), Integral(I128(::std::i128::MIN))) |
(&ty::TyUint(UintTy::Usize), Integral(Usize(Us32(::std::u32::MIN)))) |
(&ty::TyUint(UintTy::Usize), Integral(Usize(Us64(::std::u64::MIN)))) |
(&ty::TyUint(UintTy::U8), Integral(U8(::std::u8::MIN))) |
(&ty::TyUint(UintTy::U16), Integral(U16(::std::u16::MIN))) |
(&ty::TyUint(UintTy::U32), Integral(U32(::std::u32::MIN))) |
(&ty::TyUint(UintTy::U64), Integral(U64(::std::u64::MIN))) |
(&ty::TyUint(UintTy::U128), Integral(U128(::std::u128::MIN))) => Minimum,
(&ty::TyBool, Bool(true)) |
(&ty::TyInt(IntTy::Isize), Integral(Isize(Is32(::std::i32::MAX)))) |
(&ty::TyInt(IntTy::Isize), Integral(Isize(Is64(::std::i64::MAX)))) |
(&ty::TyInt(IntTy::I8), Integral(I8(::std::i8::MAX))) |
(&ty::TyInt(IntTy::I16), Integral(I16(::std::i16::MAX))) |
(&ty::TyInt(IntTy::I32), Integral(I32(::std::i32::MAX))) |
(&ty::TyInt(IntTy::I64), Integral(I64(::std::i64::MAX))) |
(&ty::TyInt(IntTy::I128), Integral(I128(::std::i128::MAX))) |
(&ty::TyUint(UintTy::Usize), Integral(Usize(Us32(::std::u32::MAX)))) |
(&ty::TyUint(UintTy::Usize), Integral(Usize(Us64(::std::u64::MAX)))) |
(&ty::TyUint(UintTy::U8), Integral(U8(::std::u8::MAX))) |
(&ty::TyUint(UintTy::U16), Integral(U16(::std::u16::MAX))) |
(&ty::TyUint(UintTy::U32), Integral(U32(::std::u32::MAX))) |
(&ty::TyUint(UintTy::U64), Integral(U64(::std::u64::MAX))) |
(&ty::TyUint(UintTy::U128), Integral(U128(::std::u128::MAX))) => Maximum,
(&ty::TyBool, Constant::Bool(true)) => Maximum,
(&ty::TyInt(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::max_value() >> (128 - int_bits(cx.tcx, ity)), ity) => Maximum,
(&ty::TyUint(uty), Constant::Int(i)) if clip(cx.tcx, u128::max_value(), uty) == i => Maximum,
_ => return None,
};
@ -1524,24 +1485,16 @@ fn numeric_cast_precast_bounds<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(
}
}
#[allow(cast_possible_wrap)]
fn node_as_const_fullint<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) -> Option<FullInt> {
use rustc::middle::const_val::ConstVal::*;
use rustc_const_eval::ConstContext;
let parent_item = cx.tcx.hir.get_parent(expr.id);
let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
match ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables).eval(expr) {
Ok(val) => if let Integral(const_int) = val.val {
match const_int.int_type() {
IntType::SignedInt(_) => Some(FullInt::S(const_int.to_u128_unchecked() as i128)),
IntType::UnsignedInt(_) => Some(FullInt::U(const_int.to_u128_unchecked())),
}
} else {
None
},
Err(_) => None,
let val = constant(cx, expr)?.0;
if let Constant::Int(const_int) = val {
match cx.tables.expr_ty(expr).sty {
ty::TyInt(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))),
ty::TyUint(_) => Some(FullInt::U(const_int)),
_ => None,
}
} else {
None
}
}

View file

@ -8,7 +8,7 @@ use rustc::hir::map::Node;
use rustc::lint::{LateContext, Level, Lint, LintContext};
use rustc::session::Session;
use rustc::traits;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::{self, Ty, TyCtxt, layout};
use rustc_errors;
use std::borrow::Cow;
use std::env;
@ -276,14 +276,6 @@ pub fn path_to_def(cx: &LateContext, path: &[&str]) -> Option<def::Def> {
}
}
pub fn const_to_u64(c: &ty::Const) -> u64 {
c.val
.to_const_int()
.expect("eddyb says this works")
.to_u64()
.expect("see previous expect")
}
/// Convenience function to get the `DefId` of a trait by path.
pub fn get_trait_def_id(cx: &LateContext, path: &[&str]) -> Option<DefId> {
let def = match path_to_def(cx, path) {
@ -1071,3 +1063,26 @@ pub fn get_arg_name(pat: &Pat) -> Option<ast::Name> {
_ => None,
}
}
pub fn int_bits(tcx: TyCtxt, ity: ast::IntTy) -> u64 {
layout::Integer::from_attr(tcx, attr::IntType::SignedInt(ity)).size().bits()
}
/// Turn a constant int byte representation into an i128
pub fn sext(tcx: TyCtxt, u: u128, ity: ast::IntTy) -> i128 {
let amt = 128 - int_bits(tcx, ity);
((u as i128) << amt) >> amt
}
/// clip unused bytes
pub fn unsext(tcx: TyCtxt, u: i128, ity: ast::IntTy) -> u128 {
let amt = 128 - int_bits(tcx, ity);
((u as u128) << amt) >> amt
}
/// clip unused bytes
pub fn clip(tcx: TyCtxt, u: u128, ity: ast::UintTy) -> u128 {
let bits = layout::Integer::from_attr(tcx, attr::IntType::UnsignedInt(ity)).size().bits();
let amt = 128 - bits;
(u << amt) >> amt
}

View file

@ -1,10 +1,9 @@
use rustc::hir::*;
use rustc::lint::*;
use rustc::ty::{self, Ty};
use rustc::ty::subst::Substs;
use rustc_const_eval::ConstContext;
use syntax::codemap::Span;
use utils::{higher, is_copy, snippet, span_lint_and_sugg};
use consts::constant;
/// **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would
/// be possible.
@ -67,13 +66,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
fn check_vec_macro<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) {
let snippet = match *vec_args {
higher::VecArgs::Repeat(elem, len) => {
let parent_item = cx.tcx.hir.get_parent(len.id);
let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
if ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables)
.eval(len)
.is_ok()
{
if constant(cx, len).is_some() {
format!("&[{}; {}]", snippet(cx, elem.span, "elem"), snippet(cx, len.span, "len"))
} else {
return;

View file

@ -1,4 +1,4 @@
use consts::{constant_simple, Constant, FloatWidth};
use consts::{constant_simple, Constant};
use rustc::lint::*;
use rustc::hir::*;
use utils::span_help_and_lint;
@ -37,16 +37,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
// TODO - constant_simple does not fold many operations involving floats.
// That's probably fine for this lint - it's pretty unlikely that someone would
// do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too.
if let Some(Constant::Float(ref lhs_value, lhs_width)) = constant_simple(cx, left);
if let Some(Constant::Float(ref rhs_value, rhs_width)) = constant_simple(cx, right);
if Ok(0.0) == lhs_value.parse();
if Ok(0.0) == rhs_value.parse();
if let Some(lhs_value) = constant_simple(cx, left);
if let Some(rhs_value) = constant_simple(cx, right);
if Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value;
if Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value;
then {
// since we're about to suggest a use of std::f32::NaN or std::f64::NaN,
// match the precision of the literals that are given.
let float_type = match (lhs_width, rhs_width) {
(FloatWidth::F64, _)
| (_, FloatWidth::F64) => "f64",
let float_type = match (lhs_value, rhs_value) {
(Constant::F64(_), _)
| (_, Constant::F64(_)) => "f64",
_ => "f32"
};
span_help_and_lint(

View file

@ -19,4 +19,4 @@ error[E0308]: mismatched types
error: aborting due to 2 previous errors
If you want more information on this error, try using "rustc --explain E0308"
For more information about this error, try `rustc --explain E0308`.

View file

@ -8,4 +8,4 @@ error[E0658]: compiler plugins are experimental and possibly buggy (see issue #2
error: aborting due to previous error
If you want more information on this error, try using "rustc --explain E0658"
For more information about this error, try `rustc --explain E0658`.

View file

@ -8,4 +8,4 @@ error[E0658]: compiler plugins are experimental and possibly buggy (see issue #2
error: aborting due to previous error
If you want more information on this error, try using "rustc --explain E0658"
For more information about this error, try `rustc --explain E0658`.

View file

@ -8,4 +8,4 @@ error[E0658]: compiler plugins are experimental and possibly buggy (see issue #2
error: aborting due to previous error
If you want more information on this error, try using "rustc --explain E0658"
For more information about this error, try `rustc --explain E0658`.

View file

@ -8,4 +8,4 @@ error[E0658]: compiler plugins are experimental and possibly buggy (see issue #2
error: aborting due to previous error
If you want more information on this error, try using "rustc --explain E0658"
For more information about this error, try `rustc --explain E0658`.

View file

@ -8,4 +8,4 @@ error[E0658]: compiler plugins are experimental and possibly buggy (see issue #2
error: aborting due to previous error
If you want more information on this error, try using "rustc --explain E0658"
For more information about this error, try `rustc --explain E0658`.

View file

@ -8,4 +8,4 @@ error[E0658]: compiler plugins are experimental and possibly buggy (see issue #2
error: aborting due to previous error
If you want more information on this error, try using "rustc --explain E0658"
For more information about this error, try `rustc --explain E0658`.

View file

@ -1,70 +1,10 @@
error: strict comparison of f32 or f64
--> $DIR/float_cmp.rs:43:5
|
43 | ONE == 1f32;
| ^^^^^^^^^^^ help: consider comparing them within some error: `(ONE - 1f32).abs() < error`
|
= note: `-D float-cmp` implied by `-D warnings`
note: std::f32::EPSILON and std::f64::EPSILON are available.
--> $DIR/float_cmp.rs:43:5
|
43 | ONE == 1f32;
| ^^^^^^^^^^^
error: strict comparison of f32 or f64
--> $DIR/float_cmp.rs:44:5
|
44 | ONE == 1.0 + 0.0;
| ^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE - (1.0 + 0.0)).abs() < error`
|
note: std::f32::EPSILON and std::f64::EPSILON are available.
--> $DIR/float_cmp.rs:44:5
|
44 | ONE == 1.0 + 0.0;
| ^^^^^^^^^^^^^^^^
error: strict comparison of f32 or f64
--> $DIR/float_cmp.rs:45:5
|
45 | ONE + ONE == ZERO + ONE + ONE;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE + ONE - (ZERO + ONE + ONE)).abs() < error`
|
note: std::f32::EPSILON and std::f64::EPSILON are available.
--> $DIR/float_cmp.rs:45:5
|
45 | ONE + ONE == ZERO + ONE + ONE;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: strict comparison of f32 or f64
--> $DIR/float_cmp.rs:46:5
|
46 | ONE != 2.0;
| ^^^^^^^^^^ help: consider comparing them within some error: `(ONE - 2.0).abs() < error`
|
note: std::f32::EPSILON and std::f64::EPSILON are available.
--> $DIR/float_cmp.rs:46:5
|
46 | ONE != 2.0;
| ^^^^^^^^^^
error: strict comparison of f32 or f64
--> $DIR/float_cmp.rs:48:5
|
48 | twice(ONE) != ONE;
| ^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(twice(ONE) - ONE).abs() < error`
|
note: std::f32::EPSILON and std::f64::EPSILON are available.
--> $DIR/float_cmp.rs:48:5
|
48 | twice(ONE) != ONE;
| ^^^^^^^^^^^^^^^^^
error: strict comparison of f32 or f64
--> $DIR/float_cmp.rs:49:5
|
49 | ONE as f64 != 2.0;
| ^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE as f64 - 2.0).abs() < error`
|
= note: `-D float-cmp` implied by `-D warnings`
note: std::f32::EPSILON and std::f64::EPSILON are available.
--> $DIR/float_cmp.rs:49:5
|
@ -95,5 +35,5 @@ note: std::f32::EPSILON and std::f64::EPSILON are available.
57 | twice(x) != twice(ONE as f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 8 previous errors
error: aborting due to 3 previous errors

View file

@ -10,5 +10,13 @@ help: use the values directly
13 | let foo = 5 - 6;
|
error: aborting due to previous error
error: taken reference of right operand
--> $DIR/op_ref.rs:21:8
|
21 | if b < &a {
| ^^^^--
| |
| help: use the right value directly: `a`
error: aborting due to 2 previous errors

View file

@ -13,7 +13,7 @@ error: constant division of 0.0 with 0.0 will always result in NaN
| ^^^^^^^^^
|
= note: `-D zero-divided-by-zero` implied by `-D warnings`
= help: Consider using `std::f32::NAN` if you would like a constant representing NaN
= help: Consider using `std::f64::NAN` if you would like a constant representing NaN
error: equal expressions as operands to `/`
--> $DIR/zero_div_zero.rs:8:19