mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-24 05:33:27 +00:00
commit
c215702555
29 changed files with 407 additions and 606 deletions
|
@ -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
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 don’t 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 don’t 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, _) => {
|
||||
// don’t 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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::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",
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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, "..");
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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: ®ex_syntax::hir::Hir) -> Option<&'static str> {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue