Almost there
This commit is contained in:
@@ -3,14 +3,15 @@
|
|||||||
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_eval::lookup_const_by_id;
|
||||||
use rustc_const_math::{ConstInt, ConstUsize, ConstIsize};
|
use rustc_const_math::ConstInt;
|
||||||
use rustc::hir::*;
|
use rustc::hir::*;
|
||||||
|
use rustc::ty::{TyCtxt, self};
|
||||||
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, LitIntType, LitKind, StrStyle, UintTy, IntTy, NodeId};
|
use syntax::ast::{FloatTy, LitKind, StrStyle, NodeId};
|
||||||
use syntax::ptr::P;
|
use syntax::ptr::P;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
@@ -174,29 +175,36 @@ impl PartialOrd for Constant {
|
|||||||
|
|
||||||
/// parse a `LitKind` to a `Constant`
|
/// parse a `LitKind` to a `Constant`
|
||||||
#[allow(cast_possible_wrap)]
|
#[allow(cast_possible_wrap)]
|
||||||
pub fn lit_to_constant(lit: &LitKind) -> Constant {
|
pub fn lit_to_constant<'a, 'tcx>(lit: &LitKind, tcx: TyCtxt<'a, 'tcx, 'tcx>, mut ty: 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 {
|
match *lit {
|
||||||
LitKind::Str(ref is, style) => Constant::Str(is.to_string(), style),
|
LitKind::Str(ref is, style) => Constant::Str(is.to_string(), style),
|
||||||
LitKind::Byte(b) => Constant::Int(ConstInt::U8(b)),
|
LitKind::Byte(b) => Constant::Int(ConstInt::U8(b)),
|
||||||
LitKind::ByteStr(ref s) => Constant::Binary(s.clone()),
|
LitKind::ByteStr(ref s) => Constant::Binary(s.clone()),
|
||||||
LitKind::Char(c) => Constant::Char(c),
|
LitKind::Char(c) => Constant::Char(c),
|
||||||
LitKind::Int(value, LitIntType::Unsuffixed) => Constant::Int(ConstInt::U128(value as u128)),
|
LitKind::Int(n, hint) => {
|
||||||
LitKind::Int(value, LitIntType::Unsigned(UintTy::U8)) => Constant::Int(ConstInt::U8(value as u8)),
|
match (&ty.sty, hint) {
|
||||||
LitKind::Int(value, LitIntType::Unsigned(UintTy::U16)) => Constant::Int(ConstInt::U16(value as u16)),
|
(&ty::TyInt(ity), _) |
|
||||||
LitKind::Int(value, LitIntType::Unsigned(UintTy::U32)) => Constant::Int(ConstInt::U32(value as u32)),
|
(_, Signed(ity)) => {
|
||||||
LitKind::Int(value, LitIntType::Unsigned(UintTy::U64)) => Constant::Int(ConstInt::U64(value as u64)),
|
Constant::Int(ConstInt::new_signed_truncating(n as i128,
|
||||||
LitKind::Int(value, LitIntType::Unsigned(UintTy::U128)) => Constant::Int(ConstInt::U128(value as u128)),
|
ity, tcx.sess.target.int_type))
|
||||||
LitKind::Int(value, LitIntType::Unsigned(UintTy::Us)) => {
|
}
|
||||||
Constant::Int(ConstInt::Usize(ConstUsize::Us32(value as u32)))
|
(&ty::TyUint(uty), _) |
|
||||||
},
|
(_, Unsigned(uty)) => {
|
||||||
LitKind::Int(value, LitIntType::Signed(IntTy::I8)) => Constant::Int(ConstInt::I8(value as i8)),
|
Constant::Int(ConstInt::new_unsigned_truncating(n as u128,
|
||||||
LitKind::Int(value, LitIntType::Signed(IntTy::I16)) => Constant::Int(ConstInt::I16(value as i16)),
|
uty, tcx.sess.target.uint_type))
|
||||||
LitKind::Int(value, LitIntType::Signed(IntTy::I32)) => Constant::Int(ConstInt::I32(value as i32)),
|
}
|
||||||
LitKind::Int(value, LitIntType::Signed(IntTy::I64)) => Constant::Int(ConstInt::I64(value as i64)),
|
_ => bug!()
|
||||||
LitKind::Int(value, LitIntType::Signed(IntTy::I128)) => Constant::Int(ConstInt::I128(value as i128)),
|
}
|
||||||
LitKind::Int(value, LitIntType::Signed(IntTy::Is)) => {
|
}
|
||||||
Constant::Int(ConstInt::Isize(ConstIsize::Is32(value as i32)))
|
|
||||||
},
|
|
||||||
LitKind::Float(ref is, ty) => Constant::Float(is.to_string(), ty.into()),
|
LitKind::Float(ref is, ty) => Constant::Float(is.to_string(), ty.into()),
|
||||||
LitKind::FloatUnsuffixed(ref is) => Constant::Float(is.to_string(), FloatWidth::Any),
|
LitKind::FloatUnsuffixed(ref is) => Constant::Float(is.to_string(), FloatWidth::Any),
|
||||||
LitKind::Bool(b) => Constant::Bool(b),
|
LitKind::Bool(b) => Constant::Bool(b),
|
||||||
@@ -231,22 +239,20 @@ fn neg_float_str(s: &str) -> String {
|
|||||||
|
|
||||||
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 {
|
||||||
lcx: Some(lcx),
|
tcx: lcx.tcx,
|
||||||
|
tables: lcx.tables,
|
||||||
needed_resolution: false,
|
needed_resolution: false,
|
||||||
};
|
};
|
||||||
cx.expr(e).map(|cst| (cst, cx.needed_resolution))
|
cx.expr(e).map(|cst| (cst, cx.needed_resolution))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constant_simple(e: &Expr) -> Option<Constant> {
|
pub fn constant_simple(lcx: &LateContext, e: &Expr) -> Option<Constant> {
|
||||||
let mut cx = ConstEvalLateContext {
|
constant(lcx, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
|
||||||
lcx: None,
|
|
||||||
needed_resolution: false,
|
|
||||||
};
|
|
||||||
cx.expr(e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConstEvalLateContext<'c, 'cc: 'c> {
|
struct ConstEvalLateContext<'a, 'tcx: 'a> {
|
||||||
lcx: Option<&'c LateContext<'c, 'cc>>,
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
|
tables: &'a ty::TypeckTables<'tcx>,
|
||||||
needed_resolution: bool,
|
needed_resolution: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,17 +263,14 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
|
|||||||
ExprPath(ref qpath) => self.fetch_path(qpath, e.id),
|
ExprPath(ref qpath) => self.fetch_path(qpath, e.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)),
|
ExprLit(ref lit) => Some(lit_to_constant(&lit.node, self.tcx, 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, number_id) => {
|
ExprRepeat(ref value, number_id) => {
|
||||||
if let Some(lcx) = self.lcx {
|
let val = &self.tcx.hir.body(number_id).value;
|
||||||
self.binop_apply(value,
|
self.binop_apply(value,
|
||||||
&lcx.tcx.hir.body(number_id).value,
|
val,
|
||||||
|v, n| Some(Constant::Repeat(Box::new(v), n.as_u64() as usize)))
|
|v, n| Some(Constant::Repeat(Box::new(v), n.as_u64() as usize)))
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
ExprUnary(op, ref operand) => {
|
ExprUnary(op, ref operand) => {
|
||||||
self.expr(operand).and_then(|o| match op {
|
self.expr(operand).and_then(|o| match op {
|
||||||
@@ -292,24 +295,27 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
|
|||||||
|
|
||||||
/// lookup a possibly constant expression from a ExprPath
|
/// lookup a possibly constant expression from a ExprPath
|
||||||
fn fetch_path(&mut self, qpath: &QPath, id: NodeId) -> Option<Constant> {
|
fn fetch_path(&mut self, qpath: &QPath, id: NodeId) -> Option<Constant> {
|
||||||
if let Some(lcx) = self.lcx {
|
let def = self.tables.qpath_def(qpath, id);
|
||||||
let def = lcx.tables.qpath_def(qpath, id);
|
match def {
|
||||||
match def {
|
Def::Const(def_id) |
|
||||||
Def::Const(def_id) |
|
Def::AssociatedConst(def_id) => {
|
||||||
Def::AssociatedConst(def_id) => {
|
let substs = self.tables
|
||||||
let substs = lcx.tables
|
.node_id_item_substs(id)
|
||||||
.node_id_item_substs(id)
|
.unwrap_or_else(|| self.tcx.intern_substs(&[]));
|
||||||
.unwrap_or_else(|| lcx.tcx.intern_substs(&[]));
|
if let Some((const_expr, tables)) = lookup_const_by_id(self.tcx, def_id, substs) {
|
||||||
if let Some((const_expr, _ty)) = lookup_const_by_id(lcx.tcx, def_id, substs) {
|
let mut cx = ConstEvalLateContext {
|
||||||
let ret = self.expr(const_expr);
|
tcx: self.tcx,
|
||||||
if ret.is_some() {
|
tables,
|
||||||
self.needed_resolution = true;
|
needed_resolution: false,
|
||||||
}
|
};
|
||||||
return ret;
|
let ret = cx.expr(const_expr);
|
||||||
|
if ret.is_some() {
|
||||||
|
self.needed_resolution = true;
|
||||||
}
|
}
|
||||||
},
|
return ret;
|
||||||
_ => {},
|
}
|
||||||
}
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityOp {
|
|||||||
|
|
||||||
|
|
||||||
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(e) {
|
if let Some(Constant::Int(v)) = constant_simple(cx, e) {
|
||||||
if match m {
|
if match m {
|
||||||
0 => v.to_u128_unchecked() == 0,
|
0 => v.to_u128_unchecked() == 0,
|
||||||
-1 => match v.int_type() {
|
-1 => match v.int_type() {
|
||||||
|
|||||||
@@ -65,9 +65,9 @@ fn min_max<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(MinMax, Constant, &'
|
|||||||
let def_id = cx.tables.qpath_def(qpath, path.id).def_id();
|
let def_id = cx.tables.qpath_def(qpath, path.id).def_id();
|
||||||
|
|
||||||
if match_def_path(cx.tcx, def_id, &paths::CMP_MIN) {
|
if match_def_path(cx.tcx, def_id, &paths::CMP_MIN) {
|
||||||
fetch_const(args, MinMax::Min)
|
fetch_const(cx, args, MinMax::Min)
|
||||||
} else if match_def_path(cx.tcx, def_id, &paths::CMP_MAX) {
|
} else if match_def_path(cx.tcx, def_id, &paths::CMP_MAX) {
|
||||||
fetch_const(args, MinMax::Max)
|
fetch_const(cx, args, MinMax::Max)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -79,18 +79,18 @@ fn min_max<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(MinMax, Constant, &'
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_const(args: &[Expr], m: MinMax) -> Option<(MinMax, Constant, &Expr)> {
|
fn fetch_const<'a>(cx: &LateContext, args: &'a [Expr], m: MinMax) -> Option<(MinMax, Constant, &'a Expr)> {
|
||||||
if args.len() != 2 {
|
if args.len() != 2 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if let Some(c) = constant_simple(&args[0]) {
|
if let Some(c) = constant_simple(cx, &args[0]) {
|
||||||
if constant_simple(&args[1]).is_none() {
|
if constant_simple(cx, &args[1]).is_none() {
|
||||||
// otherwise ignore
|
// otherwise ignore
|
||||||
Some((m, c, &args[1]))
|
Some((m, c, &args[1]))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
} else if let Some(c) = constant_simple(&args[1]) {
|
} else if let Some(c) = constant_simple(cx, &args[1]) {
|
||||||
Some((m, c, &args[0]))
|
Some((m, c, &args[0]))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -47,7 +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_let_chain!([
|
if_let_chain!([
|
||||||
let ExprLit(ref l) = lit.node,
|
let ExprLit(ref l) = lit.node,
|
||||||
let Constant::Int(ref ci) = consts::lit_to_constant(&l.node),
|
let Constant::Int(ref ci) = consts::lit_to_constant(&l.node, cx.tcx, cx.tables.expr_ty(lit)),
|
||||||
let Some(val) = ci.to_u64(),
|
let Some(val) = ci.to_u64(),
|
||||||
val == 1,
|
val == 1,
|
||||||
cx.tables.expr_ty(exp).is_integral()
|
cx.tables.expr_ty(exp).is_integral()
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ 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.
|
||||||
let Some(Constant::Float(ref lhs_value, lhs_width)) = constant_simple(left),
|
let Some(Constant::Float(ref lhs_value, lhs_width)) = constant_simple(cx, left),
|
||||||
let Some(Constant::Float(ref rhs_value, rhs_width)) = constant_simple(right),
|
let Some(Constant::Float(ref rhs_value, rhs_width)) = constant_simple(cx, right),
|
||||||
let Ok(0.0) = lhs_value.parse(),
|
let Ok(0.0) = lhs_value.parse(),
|
||||||
let Ok(0.0) = rhs_value.parse()
|
let Ok(0.0) = rhs_value.parse()
|
||||||
], {
|
], {
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
#![feature(rustc_private)]
|
|
||||||
|
|
||||||
extern crate clippy_lints;
|
|
||||||
extern crate rustc;
|
|
||||||
extern crate rustc_const_eval;
|
|
||||||
extern crate rustc_const_math;
|
|
||||||
extern crate syntax;
|
|
||||||
|
|
||||||
use clippy_lints::consts::{constant_simple, Constant, FloatWidth};
|
|
||||||
use rustc_const_math::ConstInt;
|
|
||||||
use rustc::hir::*;
|
|
||||||
use syntax::ast::{LitIntType, LitKind, NodeId, StrStyle};
|
|
||||||
use syntax::codemap::{Spanned, COMMAND_LINE_SP};
|
|
||||||
use syntax::symbol::Symbol;
|
|
||||||
use syntax::ptr::P;
|
|
||||||
use syntax::util::ThinVec;
|
|
||||||
|
|
||||||
fn spanned<T>(t: T) -> Spanned<T> {
|
|
||||||
Spanned {
|
|
||||||
node: t,
|
|
||||||
span: COMMAND_LINE_SP,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expr(n: Expr_) -> Expr {
|
|
||||||
Expr {
|
|
||||||
id: NodeId::new(1),
|
|
||||||
node: n,
|
|
||||||
span: COMMAND_LINE_SP,
|
|
||||||
attrs: ThinVec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lit(l: LitKind) -> Expr {
|
|
||||||
expr(ExprLit(P(spanned(l))))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn binop(op: BinOp_, l: Expr, r: Expr) -> Expr {
|
|
||||||
expr(ExprBinary(spanned(op), P(l), P(r)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check(expect: Constant, expr: &Expr) {
|
|
||||||
assert_eq!(Some(expect), constant_simple(expr))
|
|
||||||
}
|
|
||||||
|
|
||||||
const TRUE: Constant = Constant::Bool(true);
|
|
||||||
const FALSE: Constant = Constant::Bool(false);
|
|
||||||
const ZERO: Constant = Constant::Int(ConstInt::U8(0));
|
|
||||||
const ONE: Constant = Constant::Int(ConstInt::U8(1));
|
|
||||||
const TWO: Constant = Constant::Int(ConstInt::U8(2));
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_lit() {
|
|
||||||
check(TRUE, &lit(LitKind::Bool(true)));
|
|
||||||
check(FALSE, &lit(LitKind::Bool(false)));
|
|
||||||
check(ZERO, &lit(LitKind::Int(0, LitIntType::Unsuffixed)));
|
|
||||||
check(Constant::Str("cool!".into(), StrStyle::Cooked),
|
|
||||||
&lit(LitKind::Str(Symbol::intern("cool!"), StrStyle::Cooked)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_ops() {
|
|
||||||
check(TRUE, &binop(BiOr, lit(LitKind::Bool(false)), lit(LitKind::Bool(true))));
|
|
||||||
check(FALSE, &binop(BiAnd, lit(LitKind::Bool(false)), lit(LitKind::Bool(true))));
|
|
||||||
|
|
||||||
let litzero = lit(LitKind::Int(0, LitIntType::Unsuffixed));
|
|
||||||
let litone = lit(LitKind::Int(1, LitIntType::Unsuffixed));
|
|
||||||
check(TRUE, &binop(BiEq, litzero.clone(), litzero.clone()));
|
|
||||||
check(TRUE, &binop(BiGe, litzero.clone(), litzero.clone()));
|
|
||||||
check(TRUE, &binop(BiLe, litzero.clone(), litzero.clone()));
|
|
||||||
check(FALSE, &binop(BiNe, litzero.clone(), litzero.clone()));
|
|
||||||
check(FALSE, &binop(BiGt, litzero.clone(), litzero.clone()));
|
|
||||||
check(FALSE, &binop(BiLt, litzero.clone(), litzero.clone()));
|
|
||||||
|
|
||||||
check(ZERO, &binop(BiAdd, litzero.clone(), litzero.clone()));
|
|
||||||
check(TWO, &binop(BiAdd, litone.clone(), litone.clone()));
|
|
||||||
check(ONE, &binop(BiSub, litone.clone(), litzero.clone()));
|
|
||||||
check(ONE, &binop(BiMul, litone.clone(), litone.clone()));
|
|
||||||
check(ONE, &binop(BiDiv, litone.clone(), litone.clone()));
|
|
||||||
|
|
||||||
let half_any = Constant::Float("0.5".into(), FloatWidth::Any);
|
|
||||||
let half32 = Constant::Float("0.5".into(), FloatWidth::F32);
|
|
||||||
let half64 = Constant::Float("0.5".into(), FloatWidth::F64);
|
|
||||||
let pos_zero = Constant::Float("0.0".into(), FloatWidth::F64);
|
|
||||||
let neg_zero = Constant::Float("-0.0".into(), FloatWidth::F64);
|
|
||||||
|
|
||||||
assert_eq!(pos_zero, pos_zero);
|
|
||||||
assert_eq!(neg_zero, neg_zero);
|
|
||||||
assert_eq!(None, pos_zero.partial_cmp(&neg_zero));
|
|
||||||
|
|
||||||
assert_eq!(half_any, half32);
|
|
||||||
assert_eq!(half_any, half64);
|
|
||||||
// for transitivity
|
|
||||||
assert_eq!(half32, half64);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user