Merge pull request #2531 from mati865/master

Rustup
This commit is contained in:
Oliver Schneider 2018-03-15 16:42:09 +01:00 committed by GitHub
commit c215702555
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 407 additions and 606 deletions

View File

@ -1,13 +1,9 @@
use rustc::lint::*; use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::ty; use rustc::ty;
use rustc::ty::subst::Substs;
use rustc_const_eval::ConstContext;
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize};
use rustc::hir; use rustc::hir;
use syntax::ast::RangeLimits; use syntax::ast::RangeLimits;
use utils::{self, higher}; 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 /// **What it does:** Checks for out of bounds array indexing with a constant
/// index. /// index.
@ -63,29 +59,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
// Array with known size can be checked statically // Array with known size can be checked statically
let ty = cx.tables.expr_ty(array); let ty = cx.tables.expr_ty(array);
if let ty::TyArray(_, size) = ty.sty { if let ty::TyArray(_, size) = ty.sty {
let size = ConstInt::Usize( let size = size.val.to_raw_bits().unwrap();
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);
// Index is a constant uint // Index is a constant uint
if let Ok(const_index) = constcx.eval(index) { if let Some((Constant::Int(const_index), _)) = constant(cx, index) {
if let ConstVal::Integral(const_index) = const_index.val { if size <= const_index {
if size <= const_index { utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "const index is out of bounds");
utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "const index is out of bounds");
}
return;
} }
return;
} }
// Index is a constant range // Index is a constant range
if let Some(range) = higher::range(index) { if let Some(range) = higher::range(index) {
let start = range.start.map(|start| constcx.eval(start)).map(|v| v.ok()); let start = range.start.map(|start| constant(cx, start).map(|(c, _)| c));
let end = range.end.map(|end| constcx.eval(end)).map(|v| v.ok()); 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 let Some((start, end)) = to_const_range(&start, &end, range.limits, size) {
if start > size || end > 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 /// Returns an option containing a tuple with the start and end (exclusive) of
/// the range. /// the range.
fn to_const_range( fn to_const_range(
start: &Option<Option<&ty::Const>>, start: &Option<Option<Constant>>,
end: &Option<Option<&ty::Const>>, end: &Option<Option<Constant>>,
limits: RangeLimits, limits: RangeLimits,
array_size: ConstInt, array_size: u128,
) -> Option<(ConstInt, ConstInt)> { ) -> Option<(u128, u128)> {
let start = match *start { let start = match *start {
Some(Some(&ty::Const { Some(Some(Constant::Int(x))) => x,
val: ConstVal::Integral(x),
..
})) => x,
Some(_) => return None, Some(_) => return None,
None => ConstInt::U8(0), None => 0,
}; };
let end = match *end { let end = match *end {
Some(Some(&ty::Const { Some(Some(Constant::Int(x))) => if limits == RangeLimits::Closed {
val: ConstVal::Integral(x), x + 1
..
})) => 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")
} else { } else {
x x
}, },

View File

@ -1,11 +1,10 @@
use rustc::hir::*; use rustc::hir::*;
use rustc::hir::def::Def;
use rustc::lint::*; use rustc::lint::*;
use rustc_const_eval::lookup_const_by_id;
use syntax::ast::LitKind; use syntax::ast::LitKind;
use syntax::codemap::Span; use syntax::codemap::Span;
use utils::{span_lint, span_lint_and_then}; use utils::{span_lint, span_lint_and_then};
use utils::sugg::Sugg; use utils::sugg::Sugg;
use consts::{constant, Constant};
/// **What it does:** Checks for incompatible bit masks in comparisons. /// **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> { fn fetch_int_literal(cx: &LateContext, lit: &Expr) -> Option<u128> {
use rustc::ty::subst::Substs; match constant(cx, lit)?.0 {
match lit.node { Constant::Int(n) => Some(n),
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
}
},
_ => None, _ => None,
} }
} }

View File

@ -2,19 +2,18 @@
use rustc::lint::LateContext; use rustc::lint::LateContext;
use rustc::hir::def::Def; use rustc::hir::def::Def;
use rustc_const_eval::lookup_const_by_id;
use rustc_const_math::ConstInt;
use rustc::hir::*; use rustc::hir::*;
use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::{self, Ty, TyCtxt, Instance};
use rustc::ty::subst::{Subst, Substs}; use rustc::ty::subst::{Subst, Substs};
use std::cmp::Ordering::{self, Equal}; use std::cmp::Ordering::{self, Equal};
use std::cmp::PartialOrd; use std::cmp::PartialOrd;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::mem; use std::mem;
use std::rc::Rc; use std::rc::Rc;
use syntax::ast::{FloatTy, LitKind, StrStyle}; use syntax::ast::{FloatTy, LitKind};
use syntax::ptr::P; use syntax::ptr::P;
use utils::const_to_u64; use rustc::middle::const_val::ConstVal;
use utils::{sext, unsext, clip};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum FloatWidth { pub enum FloatWidth {
@ -36,15 +35,17 @@ impl From<FloatTy> for FloatWidth {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Constant { pub enum Constant {
/// a String "abc" /// a String "abc"
Str(String, StrStyle), Str(String),
/// a Binary String b"abc" /// a Binary String b"abc"
Binary(Rc<Vec<u8>>), Binary(Rc<Vec<u8>>),
/// a single char 'a' /// a single char 'a'
Char(char), Char(char),
/// an integer, third argument is whether the value is negated /// an integer's bit representation
Int(ConstInt), Int(u128),
/// a float with given type /// an f32
Float(String, FloatWidth), F32(f32),
/// an f64
F64(f64),
/// true or false /// true or false
Bool(bool), Bool(bool),
/// an array of constants /// an array of constants
@ -58,20 +59,21 @@ pub enum Constant {
impl PartialEq for Constant { impl PartialEq for Constant {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
match (self, other) { 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::Binary(ref l), &Constant::Binary(ref r)) => l == r,
(&Constant::Char(l), &Constant::Char(r)) => l == r, (&Constant::Char(l), &Constant::Char(r)) => l == r,
(&Constant::Int(l), &Constant::Int(r)) => { (&Constant::Int(l), &Constant::Int(r)) => l == r,
l.is_negative() == r.is_negative() && l.to_u128_unchecked() == r.to_u128_unchecked() (&Constant::F64(l), &Constant::F64(r)) => {
},
(&Constant::Float(ref ls, _), &Constant::Float(ref rs, _)) => {
// we want `Fw32 == FwAny` and `FwAny == Fw64`, by transitivity we must have // we want `Fw32 == FwAny` and `FwAny == Fw64`, by transitivity we must have
// `Fw32 == Fw64` so dont compare them // `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
// 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) }
(Ok(l), Ok(r)) => unsafe { mem::transmute::<f64, u64>(l) == mem::transmute::<f64, u64>(r) }, },
_ => false, (&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::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, (&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, H: Hasher,
{ {
match *self { match *self {
Constant::Str(ref s, ref k) => { Constant::Str(ref s) => {
s.hash(state); s.hash(state);
k.hash(state);
}, },
Constant::Binary(ref b) => { Constant::Binary(ref b) => {
b.hash(state); b.hash(state);
@ -98,14 +99,13 @@ impl Hash for Constant {
c.hash(state); c.hash(state);
}, },
Constant::Int(i) => { Constant::Int(i) => {
i.to_u128_unchecked().hash(state); i.hash(state);
i.is_negative().hash(state);
}, },
Constant::Float(ref f, _) => { Constant::F32(f) => {
// dont use the width here because of PartialEq implementation unsafe { mem::transmute::<f64, u64>(f as f64) }.hash(state);
if let Ok(f) = f.parse::<f64>() { },
unsafe { mem::transmute::<f64, u64>(f) }.hash(state); Constant::F64(f) => {
} unsafe { mem::transmute::<f64, u64>(f) }.hash(state);
}, },
Constant::Bool(b) => { Constant::Bool(b) => {
b.hash(state); b.hash(state);
@ -124,25 +124,11 @@ impl Hash for Constant {
impl PartialOrd for Constant { impl PartialOrd for Constant {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) { match (self, other) {
(&Constant::Str(ref ls, ref l_sty), &Constant::Str(ref rs, ref r_sty)) => if l_sty == r_sty { (&Constant::Str(ref ls), &Constant::Str(ref rs)) => Some(ls.cmp(rs)),
Some(ls.cmp(rs))
} else {
None
},
(&Constant::Char(ref l), &Constant::Char(ref r)) => Some(l.cmp(r)), (&Constant::Char(ref l), &Constant::Char(ref r)) => Some(l.cmp(r)),
(&Constant::Int(l), &Constant::Int(r)) => Some(l.cmp(&r)), (&Constant::Int(l), &Constant::Int(r)) => Some(l.cmp(&r)),
(&Constant::Float(ref ls, _), &Constant::Float(ref rs, _)) => { (&Constant::F64(l), &Constant::F64(r)) => l.partial_cmp(&r),
match (ls.parse::<f64>(), rs.parse::<f64>()) { (&Constant::F32(l), &Constant::F32(r)) => l.partial_cmp(&r),
(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::Bool(ref l), &Constant::Bool(ref r)) => Some(l.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)) => { (&Constant::Tuple(ref l), &Constant::Tuple(ref r)) | (&Constant::Vec(ref l), &Constant::Vec(ref r)) => {
l.partial_cmp(r) l.partial_cmp(r)
@ -157,63 +143,25 @@ impl PartialOrd for Constant {
} }
/// parse a `LitKind` to a `Constant` /// parse a `LitKind` to a `Constant`
#[allow(cast_possible_wrap)] pub fn lit_to_constant<'a, 'tcx>(lit: &LitKind, ty: Ty<'tcx>) -> Constant {
pub fn lit_to_constant<'a, 'tcx>(lit: &LitKind, tcx: TyCtxt<'a, 'tcx, 'tcx>, mut ty: Ty<'tcx>) -> Constant {
use syntax::ast::*; 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 { match *lit {
LitKind::Str(ref is, style) => Constant::Str(is.to_string(), style), LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
LitKind::Byte(b) => Constant::Int(ConstInt::U8(b)), LitKind::Byte(b) => Constant::Int(b as u128),
LitKind::ByteStr(ref s) => Constant::Binary(Rc::clone(s)), LitKind::ByteStr(ref s) => Constant::Binary(Rc::clone(s)),
LitKind::Char(c) => Constant::Char(c), LitKind::Char(c) => Constant::Char(c),
LitKind::Int(n, hint) => match (&ty.sty, hint) { LitKind::Int(n, _) => Constant::Int(n),
(&ty::TyInt(ity), _) | (_, Signed(ity)) => { LitKind::Float(ref is, _) |
Constant::Int(ConstInt::new_signed_truncating(n as i128, ity, tcx.sess.target.isize_ty)) LitKind::FloatUnsuffixed(ref is) => match ty.sty {
}, ty::TyFloat(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()),
(&ty::TyUint(uty), _) | (_, Unsigned(uty)) => { ty::TyFloat(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()),
Constant::Int(ConstInt::new_unsigned_truncating(n as u128, uty, tcx.sess.target.usize_ty))
},
_ => bug!(), _ => 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), 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)> { pub fn constant(lcx: &LateContext, e: &Expr) -> Option<(Constant, bool)> {
let mut cx = ConstEvalLateContext { let mut cx = ConstEvalLateContext {
tcx: lcx.tcx, tcx: lcx.tcx,
@ -255,19 +203,19 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
ExprPath(ref qpath) => self.fetch_path(qpath, e.hir_id), ExprPath(ref qpath) => self.fetch_path(qpath, e.hir_id),
ExprBlock(ref block) => self.block(block), ExprBlock(ref block) => self.block(block),
ExprIf(ref cond, ref then, ref otherwise) => self.ifthenelse(cond, then, otherwise), 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), ExprArray(ref vec) => self.multi(vec).map(Constant::Vec),
ExprTup(ref tup) => self.multi(tup).map(Constant::Tuple), ExprTup(ref tup) => self.multi(tup).map(Constant::Tuple),
ExprRepeat(ref value, _) => { ExprRepeat(ref value, _) => {
let n = match self.tables.expr_ty(e).sty { 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"), _ => 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 { ExprUnary(op, ref operand) => self.expr(operand).and_then(|o| match op {
UnNot => constant_not(&o), UnNot => self.constant_not(&o, self.tables.expr_ty(e)),
UnNeg => constant_negate(o), UnNeg => self.constant_negate(o, self.tables.expr_ty(e)),
UnDeref => Some(o), UnDeref => Some(o),
}), }),
ExprBinary(op, ref left, ref right) => self.binop(op, left, right), 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 /// create `Some(Vec![..])` of all constants, unless there is any
/// non-constant part /// non-constant part
fn multi(&mut self, vec: &[Expr]) -> Option<Vec<Constant>> { fn multi(&mut self, vec: &[Expr]) -> Option<Vec<Constant>> {
@ -295,27 +279,18 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
} else { } else {
substs.subst(self.tcx, self.substs) substs.subst(self.tcx, self.substs)
}; };
let param_env = self.param_env.and((def_id, substs)); let instance = Instance::resolve(self.tcx, self.param_env, def_id, substs)?;
if let Some((def_id, substs)) = lookup_const_by_id(self.tcx, param_env) { let gid = GlobalId {
let mut cx = Self { instance,
tcx: self.tcx, promoted: None,
tables: self.tcx.typeck_tables_of(def_id), };
needed_resolution: false, use rustc::mir::interpret::GlobalId;
substs: substs, let result = self.tcx.const_eval(self.param_env.and(gid)).ok()?;
param_env: param_env.param_env, let ret = miri_to_const(self.tcx, result);
}; if ret.is_some() {
let body = if let Some(id) = self.tcx.hir.as_local_node_id(def_id) { self.needed_resolution = true;
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;
} }
return ret;
}, },
_ => {}, _ => {},
} }
@ -344,36 +319,127 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
} }
fn binop(&mut self, op: BinOp, left: &Expr, right: &Expr) -> Option<Constant> { fn binop(&mut self, op: BinOp, left: &Expr, right: &Expr) -> Option<Constant> {
let l = if let Some(l) = self.expr(left) { let l = self.expr(left)?;
l
} else {
return None;
};
let r = self.expr(right); let r = self.expr(right);
match (op.node, l, r) { match (l, r) {
(BiAdd, Constant::Int(l), Some(Constant::Int(r))) => (l + r).ok().map(Constant::Int), (Constant::Int(l), Some(Constant::Int(r))) => {
(BiSub, Constant::Int(l), Some(Constant::Int(r))) => (l - r).ok().map(Constant::Int), match self.tables.expr_ty(left).sty {
(BiMul, Constant::Int(l), Some(Constant::Int(r))) => (l * r).ok().map(Constant::Int), ty::TyInt(ity) => {
(BiDiv, Constant::Int(l), Some(Constant::Int(r))) => (l / r).ok().map(Constant::Int), let l = sext(self.tcx, l, ity);
(BiRem, Constant::Int(l), Some(Constant::Int(r))) => (l % r).ok().map(Constant::Int), let r = sext(self.tcx, r, ity);
(BiAnd, Constant::Bool(false), _) => Some(Constant::Bool(false)), let zext = |n: i128| Constant::Int(unsext(self.tcx, n, ity));
(BiOr, Constant::Bool(true), _) => Some(Constant::Bool(true)), match op.node {
(BiAnd, Constant::Bool(true), Some(r)) | (BiOr, Constant::Bool(false), Some(r)) => Some(r), BiAdd => l.checked_add(r).map(zext),
(BiBitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)), BiSub => l.checked_sub(r).map(zext),
(BiBitXor, Constant::Int(l), Some(Constant::Int(r))) => (l ^ r).ok().map(Constant::Int), BiMul => l.checked_mul(r).map(zext),
(BiBitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)), BiDiv if r != 0 => l.checked_div(r).map(zext),
(BiBitAnd, Constant::Int(l), Some(Constant::Int(r))) => (l & r).ok().map(Constant::Int), BiRem if r != 0 => l.checked_rem(r).map(zext),
(BiBitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)), BiShr => l.checked_shr(r as u128 as u32).map(zext),
(BiBitOr, Constant::Int(l), Some(Constant::Int(r))) => (l | r).ok().map(Constant::Int), BiShl => l.checked_shl(r as u128 as u32).map(zext),
(BiShl, Constant::Int(l), Some(Constant::Int(r))) => (l << r).ok().map(Constant::Int), BiBitXor => Some(zext(l ^ r)),
(BiShr, Constant::Int(l), Some(Constant::Int(r))) => (l >> r).ok().map(Constant::Int), BiBitOr => Some(zext(l | r)),
(BiEq, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l == r)), BiBitAnd => Some(zext(l & r)),
(BiNe, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l != r)), BiEq => Some(Constant::Bool(l == r)),
(BiLt, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l < r)), BiNe => Some(Constant::Bool(l != r)),
(BiLe, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l <= r)), BiLt => Some(Constant::Bool(l < r)),
(BiGe, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l >= r)), BiLe => Some(Constant::Bool(l <= r)),
(BiGt, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l > r)), BiGe => Some(Constant::Bool(l >= r)),
_ => None, 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` //! don't fit into an `i32`
use rustc::lint::*; use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc_const_math::*;
use rustc::hir::*; use rustc::hir::*;
use rustc::ty; use rustc::ty;
use rustc::traits::Reveal;
use rustc::ty::subst::Substs; use rustc::ty::subst::Substs;
use syntax::ast::{IntTy, UintTy};
use utils::span_lint; 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 /// **What it does:** Checks for C-like enumerations that are
/// `repr(isize/usize)` and have values that don't fit into an `i32`. /// `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 { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnportableVariant {
#[allow(cast_possible_truncation, cast_sign_loss)] #[allow(cast_possible_truncation, cast_sign_loss)]
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) { 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 { if let ItemEnum(ref def, _) = item.node {
for var in &def.variants { for var in &def.variants {
let variant = &var.node; let variant = &var.node;
if let Some(body_id) = variant.disr_expr { 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 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 substs = Substs::identity_for_item(cx.tcx.global_tcx(), did);
let bad = match cx.tcx let instance = ty::Instance::new(did, substs);
.at(expr.span) let cid = GlobalId {
.const_eval(param_env.and((did, substs))) instance,
{ promoted: None
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,
}; };
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( span_lint(
cx, cx,
ENUM_CLIKE_UNPORTABLE_VARIANT, ENUM_CLIKE_UNPORTABLE_VARIANT,
var.span, var.span,
"Clike enum variant discriminant is not portable to 32-bit targets", "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) { fn check(cx: &LateContext, e: &Expr, span: Span) {
if let Some(Constant::Int(v)) = constant_simple(cx, e) { if let Some(Constant::Int(v)) = constant_simple(cx, e) {
if v.to_u128_unchecked() == 0 { if v == 0 {
span_lint( span_lint(
cx, cx,
ERASING_OP, ERASING_OP,

View File

@ -1,9 +1,9 @@
use consts::{constant_simple, Constant}; use consts::{constant_simple, Constant};
use rustc::hir::*; use rustc::hir::*;
use rustc::lint::*; use rustc::lint::*;
use rustc_const_math::ConstInt;
use syntax::codemap::Span; 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`. /// **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)] #[allow(cast_possible_wrap)]
fn check(cx: &LateContext, e: &Expr, m: i8, span: Span, arg: Span) { fn check(cx: &LateContext, e: &Expr, m: i8, span: Span, arg: Span) {
if let Some(Constant::Int(v)) = constant_simple(cx, e) { 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 { if match m {
0 => v.to_u128_unchecked() == 0, 0 => v == 0,
-1 => all_ones(&v), -1 => v == check,
1 => v.to_u128_unchecked() == 1, 1 => v == 1,
_ => unreachable!(), _ => unreachable!(),
} { } {
span_lint( span_lint(

View File

@ -10,6 +10,7 @@
#![feature(conservative_impl_trait)] #![feature(conservative_impl_trait)]
#![feature(inclusive_range_syntax, range_contains)] #![feature(inclusive_range_syntax, range_contains)]
#![feature(macro_vis_matcher)] #![feature(macro_vis_matcher)]
#![feature(dotdoteq_in_patterns)]
#![allow(unknown_lints, indexing_slicing, shadow_reuse, missing_docs_in_private_items)] #![allow(unknown_lints, indexing_slicing, shadow_reuse, missing_docs_in_private_items)]
#![recursion_limit = "256"] #![recursion_limit = "256"]
@ -37,7 +38,6 @@ extern crate regex_syntax;
extern crate quine_mc_cluskey; extern crate quine_mc_cluskey;
extern crate rustc_const_eval;
extern crate rustc_const_math; extern crate rustc_const_math;
extern crate rustc_errors; extern crate rustc_errors;
extern crate rustc_plugin; 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::intravisit::{walk_block, walk_decl, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
use rustc::hir::map::Node::{NodeBlock, NodeExpr, NodeStmt}; use rustc::hir::map::Node::{NodeBlock, NodeExpr, NodeStmt};
use rustc::lint::*; use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::middle::region; use rustc::middle::region;
// use rustc::middle::region::CodeExtent; // use rustc::middle::region::CodeExtent;
use rustc::middle::expr_use_visitor::*; use rustc::middle::expr_use_visitor::*;
use rustc::middle::mem_categorization::Categorization; use rustc::middle::mem_categorization::Categorization;
use rustc::middle::mem_categorization::cmt; use rustc::middle::mem_categorization::cmt;
use rustc::ty::{self, Ty}; use rustc::ty::{self, Ty};
use rustc::ty::subst::{Subst, Substs}; use rustc::ty::subst::Subst;
use rustc_const_eval::ConstContext;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::iter::{once, Iterator}; use std::iter::{once, Iterator};
use syntax::ast; use syntax::ast;
use syntax::codemap::Span; use syntax::codemap::Span;
use utils::sugg; use utils::{sugg, sext};
use utils::const_to_u64; use consts::{constant, Constant};
use consts::constant;
use utils::{get_enclosing_block, get_parent_expr, higher, in_external_macro, is_integer_literal, is_refutable, 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, 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) }) = higher::range(arg)
{ {
// ...and both sides are compile-time constant integers... // ...and both sides are compile-time constant integers...
let parent_item = cx.tcx.hir.get_parent(arg.id); if let Some((start_idx, _)) = constant(cx, start) {
let parent_def_id = cx.tcx.hir.local_def_id(parent_item); if let Some((end_idx, _)) = constant(cx, end) {
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) {
// ...and the start index is greater than the end index, // ...and the start index is greater than the end index,
// this loop will never run. This is often confusing for developers // this loop will never run. This is often confusing for developers
// who think that this will iterate from the larger value to the // who think that this will iterate from the larger value to the
// smaller value. // smaller value.
let ty = cx.tables.expr_ty(start);
let (sup, eq) = match (start_idx, end_idx) { let (sup, eq) = match (start_idx, end_idx) {
( (
&ty::Const { Constant::Int(start_idx),
val: ConstVal::Integral(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::Const { ty::TyUint(_) => start_idx > end_idx,
val: ConstVal::Integral(end_idx), _ => false,
.. }, start_idx == end_idx),
},
) => (start_idx > end_idx, start_idx == end_idx),
_ => (false, false), _ => (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 { match cx.tables.expr_ty(&args[0]).sty {
// If the length is greater than 32 no traits are implemented for array and // If the length is greater than 32 no traits are implemented for array and
// therefore we cannot use `&`. // 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), _ => lint_iter_method(cx, args, arg, method_name),
}; };
} else { } else {
@ -1795,7 +1786,7 @@ fn is_ref_iterable_type(cx: &LateContext, e: &Expr) -> bool {
fn is_iterable_array(ty: Ty) -> bool { fn is_iterable_array(ty: Ty) -> bool {
// IntoIterator is currently only implemented for array sizes <= 32 in rustc // IntoIterator is currently only implemented for array sizes <= 32 in rustc
match ty.sty { 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, _ => false,
} }
} }
@ -2249,4 +2240,4 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
} }
fn decl_without_init(&mut self, _: NodeId, _: Span) {} fn decl_without_init(&mut self, _: NodeId, _: Span) {}
} }

View File

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

View File

@ -1,10 +1,7 @@
use rustc::hir; use rustc::hir;
use rustc::lint::*; use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::ty::{self, Ty}; use rustc::ty::{self, Ty};
use rustc::hir::def::Def; use rustc::hir::def::Def;
use rustc::ty::subst::Substs;
use rustc_const_eval::ConstContext;
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::iter; 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}; 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::paths;
use utils::sugg; use utils::sugg;
use utils::const_to_u64; use consts::{constant, Constant};
#[derive(Clone)] #[derive(Clone)]
pub struct Pass; 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::TySlice(_) => true,
ty::TyAdt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), ty::TyAdt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
ty::TyAdt(..) => match_type(cx, ty, &paths::VEC), 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), ty::TyRef(_, ty::TypeAndMut { ty: inner, .. }) => may_slice(cx, inner),
_ => false, _ => 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` /// 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) { 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); if let Some((Constant::Str(r), _)) = constant(cx, arg) {
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 r.len() == 1 { if r.len() == 1 {
let c = r.chars().next().unwrap(); let c = r.chars().next().unwrap();
let snip = snippet(cx, expr.span, ".."); let snip = snippet(cx, expr.span, "..");

View File

@ -2,18 +2,14 @@ use reexport::*;
use rustc::hir::*; use rustc::hir::*;
use rustc::hir::intravisit::FnKind; use rustc::hir::intravisit::FnKind;
use rustc::lint::*; use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::ty; use rustc::ty;
use rustc::ty::subst::Substs;
use rustc_const_eval::ConstContext;
use rustc_const_math::ConstFloat;
use syntax::codemap::{ExpnFormat, Span}; use syntax::codemap::{ExpnFormat, Span};
use utils::{get_item_name, get_parent_expr, implements_trait, in_constant, in_macro, is_integer_literal, 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, iter_input_pats, last_path_segment, match_qpath, match_trait_method, paths, snippet, span_lint,
span_lint_and_then, walk_ptrs_ty}; span_lint_and_then, walk_ptrs_ty};
use utils::sugg::Sugg; use utils::sugg::Sugg;
use syntax::ast::{FloatTy, LitKind, CRATE_NODE_ID}; use syntax::ast::{LitKind, CRATE_NODE_ID};
use consts::constant; use consts::{constant, Constant};
/// **What it does:** Checks for function arguments and let bindings denoted as /// **What it does:** Checks for function arguments and let bindings denoted as
/// `ref`. /// `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 { fn is_allowed<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) -> bool {
let parent_item = cx.tcx.hir.get_parent(expr.id); match constant(cx, expr) {
let parent_def_id = cx.tcx.hir.local_def_id(parent_item); Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(),
let substs = Substs::identity_for_item(cx.tcx, parent_def_id); Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(),
let res = ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables).eval(expr); _ => false,
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
} }
} }

View File

@ -205,7 +205,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
let sugg = |db: &mut DiagnosticBuilder| { let sugg = |db: &mut DiagnosticBuilder| {
if let ty::TypeVariants::TyAdt(ref def, ..) = ty.sty { if let ty::TypeVariants::TyAdt(ref def, ..) = ty.sty {
if let Some(span) = cx.tcx.hir.span_if_local(def.did) { 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() { if param_env.can_type_implement_copy(cx.tcx, ty, span).is_ok() {
db.span_help(span, "consider marking this type as Copy"); 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) { fn check_mul(cx: &LateContext, span: Span, lit: &Expr, exp: &Expr) {
if_chain! { if_chain! {
if let ExprLit(ref l) = lit.node; 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 Constant::Int(val) = consts::lit_to_constant(&l.node, cx.tables.expr_ty(lit));
if let Some(val) = ci.to_u64();
if val == 1; if val == 1;
if cx.tables.expr_ty(exp).is_integral(); if cx.tables.expr_ty(exp).is_integral();
then { then {

View File

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

View File

@ -1,15 +1,11 @@
use regex_syntax; use regex_syntax;
use rustc::hir::*; use rustc::hir::*;
use rustc::lint::*; 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 std::collections::HashSet;
use syntax::ast::{LitKind, NodeId, StrStyle}; use syntax::ast::{LitKind, NodeId, StrStyle};
use syntax::codemap::{BytePos, Span}; 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 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 /// **What it does:** Checks [regex](https://crates.io/crates/regex) creation
/// (with `Regex::new`,`RegexBuilder::new` or `RegexSet::new`) for correct /// (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()) Span::new(start, end, base.ctxt())
} }
fn const_str<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) -> Option<InternedString> { fn const_str<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) -> Option<String> {
let parent_item = cx.tcx.hir.get_parent(e.id); constant(cx, e).and_then(|(c, _)| match c {
let parent_def_id = cx.tcx.hir.local_def_id(parent_item); Constant::Str(s) => Some(s),
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),
_ => None, _ => None,
} })
} }
fn is_trivial_regex(s: &regex_syntax::hir::Hir) -> Option<&'static str> { 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::lint::*;
use rustc::ty::{self, Ty, TyCtxt, TypeckTables}; use rustc::ty::{self, Ty, TyCtxt, TypeckTables};
use rustc::ty::layout::LayoutOf; use rustc::ty::layout::LayoutOf;
use rustc::ty::subst::Substs;
use rustc_typeck::hir_ty_to_ty; use rustc_typeck::hir_ty_to_ty;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::borrow::Cow; use std::borrow::Cow;
use syntax::ast::{FloatTy, IntTy, UintTy}; use syntax::ast::{FloatTy, IntTy, UintTy};
use syntax::attr::IntType;
use syntax::codemap::Span; use syntax::codemap::Span;
use syntax::errors::DiagnosticBuilder; use syntax::errors::DiagnosticBuilder;
use utils::{comparisons, higher, in_constant, in_external_macro, in_macro, last_path_segment, match_def_path, match_path, 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, 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 utils::paths;
use consts::{constant, Constant};
/// Handles all the linting of funky types /// Handles all the linting of funky types
#[allow(missing_copy_implementations)] #[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>> { 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::*; use types::ExtremeType::*;
let ty = cx.tables.expr_ty(expr); let ty = cx.tables.expr_ty(expr);
match ty.sty { let cv = constant(cx, expr)?.0;
ty::TyBool | ty::TyInt(_) | ty::TyUint(_) => (),
_ => return None,
};
let parent_item = cx.tcx.hir.get_parent(expr.id); let which = match (&ty.sty, cv) {
let parent_def_id = cx.tcx.hir.local_def_id(parent_item); (&ty::TyBool, Constant::Bool(false)) |
let substs = Substs::identity_for_item(cx.tcx, parent_def_id); (&ty::TyUint(_), Constant::Int(0)) => Minimum,
let cv = match ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables).eval(expr) { (&ty::TyInt(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::min_value() >> (128 - int_bits(cx.tcx, ity)), ity) => Minimum,
Ok(val) => val,
Err(_) => return None,
};
let which = match (&ty.sty, cv.val) { (&ty::TyBool, Constant::Bool(true)) => Maximum,
(&ty::TyBool, Bool(false)) | (&ty::TyInt(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::max_value() >> (128 - int_bits(cx.tcx, ity)), ity) => Maximum,
(&ty::TyInt(IntTy::Isize), Integral(Isize(Is32(::std::i32::MIN)))) | (&ty::TyUint(uty), Constant::Int(i)) if clip(cx.tcx, u128::max_value(), uty) == i => Maximum,
(&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,
_ => return None, _ => 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> { fn node_as_const_fullint<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) -> Option<FullInt> {
use rustc::middle::const_val::ConstVal::*; let val = constant(cx, expr)?.0;
use rustc_const_eval::ConstContext; if let Constant::Int(const_int) = val {
match cx.tables.expr_ty(expr).sty {
let parent_item = cx.tcx.hir.get_parent(expr.id); ty::TyInt(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))),
let parent_def_id = cx.tcx.hir.local_def_id(parent_item); ty::TyUint(_) => Some(FullInt::U(const_int)),
let substs = Substs::identity_for_item(cx.tcx, parent_def_id); _ => None,
match ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables).eval(expr) { }
Ok(val) => if let Integral(const_int) = val.val { } else {
match const_int.int_type() { None
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,
} }
} }

View File

@ -8,7 +8,7 @@ use rustc::hir::map::Node;
use rustc::lint::{LateContext, Level, Lint, LintContext}; use rustc::lint::{LateContext, Level, Lint, LintContext};
use rustc::session::Session; use rustc::session::Session;
use rustc::traits; use rustc::traits;
use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::{self, Ty, TyCtxt, layout};
use rustc_errors; use rustc_errors;
use std::borrow::Cow; use std::borrow::Cow;
use std::env; 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. /// Convenience function to get the `DefId` of a trait by path.
pub fn get_trait_def_id(cx: &LateContext, path: &[&str]) -> Option<DefId> { pub fn get_trait_def_id(cx: &LateContext, path: &[&str]) -> Option<DefId> {
let def = match path_to_def(cx, path) { let def = match path_to_def(cx, path) {
@ -1071,3 +1063,26 @@ pub fn get_arg_name(pat: &Pat) -> Option<ast::Name> {
_ => None, _ => 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::hir::*;
use rustc::lint::*; use rustc::lint::*;
use rustc::ty::{self, Ty}; use rustc::ty::{self, Ty};
use rustc::ty::subst::Substs;
use rustc_const_eval::ConstContext;
use syntax::codemap::Span; use syntax::codemap::Span;
use utils::{higher, is_copy, snippet, span_lint_and_sugg}; use utils::{higher, is_copy, snippet, span_lint_and_sugg};
use consts::constant;
/// **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would /// **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would
/// be possible. /// 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) { fn check_vec_macro<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) {
let snippet = match *vec_args { let snippet = match *vec_args {
higher::VecArgs::Repeat(elem, len) => { higher::VecArgs::Repeat(elem, len) => {
let parent_item = cx.tcx.hir.get_parent(len.id); if constant(cx, len).is_some() {
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()
{
format!("&[{}; {}]", snippet(cx, elem.span, "elem"), snippet(cx, len.span, "len")) format!("&[{}; {}]", snippet(cx, elem.span, "elem"), snippet(cx, len.span, "len"))
} else { } else {
return; return;

View File

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

View File

@ -19,4 +19,4 @@ error[E0308]: mismatched types
error: aborting due to 2 previous errors 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 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 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 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 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 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 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 error: strict comparison of f32 or f64
--> $DIR/float_cmp.rs:49:5 --> $DIR/float_cmp.rs:49:5
| |
49 | ONE as f64 != 2.0; 49 | ONE as f64 != 2.0;
| ^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE as f64 - 2.0).abs() < error` | ^^^^^^^^^^^^^^^^^ 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. note: std::f32::EPSILON and std::f64::EPSILON are available.
--> $DIR/float_cmp.rs:49:5 --> $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); 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; 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` = 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 `/` error: equal expressions as operands to `/`
--> $DIR/zero_div_zero.rs:8:19 --> $DIR/zero_div_zero.rs:8:19