2015-04-30 04:48:43 -05:00
use rustc ::lint ::* ;
2015-09-03 09:42:17 -05:00
use rustc_front ::hir ::* ;
use rustc_front ::util as ast_util ;
2015-04-30 04:48:43 -05:00
use syntax ::ptr ::P ;
2015-08-16 01:54:43 -05:00
2015-08-21 05:19:07 -05:00
use consts ::constant ;
2015-07-26 09:53:11 -05:00
use utils ::span_lint ;
2015-04-30 04:48:43 -05:00
2015-12-10 18:22:27 -06:00
/// **What it does:** This lint checks for equal operands to comparisons and bitwise binary operators (`&`, `|` and `^`). It is `Warn` by default.
///
/// **Why is this bad?** This is usually just a typo.
///
/// **Known problems:** False negatives: We had some false positives regarding calls (notably [racer](https://github.com/phildawes/racer) had one instance of `x.pop() && x.pop()`), so we removed matching any function or method calls. We may introduce a whitelist of known pure functions in the future.
///
/// **Example:** `x + 1 == x + 1`
2015-04-30 04:48:43 -05:00
declare_lint! {
pub EQ_OP ,
Warn ,
2015-08-13 03:32:35 -05:00
" equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`) "
2015-04-30 04:48:43 -05:00
}
#[ derive(Copy,Clone) ]
pub struct EqOp ;
impl LintPass for EqOp {
fn get_lints ( & self ) -> LintArray {
lint_array! ( EQ_OP )
}
2015-09-18 21:53:04 -05:00
}
2015-08-11 13:22:20 -05:00
2015-09-18 21:53:04 -05:00
impl LateLintPass for EqOp {
fn check_expr ( & mut self , cx : & LateContext , e : & Expr ) {
2015-04-30 04:48:43 -05:00
if let ExprBinary ( ref op , ref left , ref right ) = e . node {
2015-08-21 05:19:07 -05:00
if is_cmp_or_bit ( op ) & & is_exp_equal ( cx , left , right ) {
2015-07-26 09:53:11 -05:00
span_lint ( cx , EQ_OP , e . span , & format! (
2015-08-11 13:22:20 -05:00
" equal expressions as operands to {} " ,
2015-08-05 08:10:45 -05:00
ast_util ::binop_to_string ( op . node ) ) ) ;
2015-04-30 04:48:43 -05:00
}
}
}
}
2015-09-18 21:53:04 -05:00
pub fn is_exp_equal ( cx : & LateContext , left : & Expr , right : & Expr ) -> bool {
2015-08-25 05:34:27 -05:00
if let ( Some ( l ) , Some ( r ) ) = ( constant ( cx , left ) , constant ( cx , right ) ) {
if l = = r {
return true ;
}
}
match ( & left . node , & right . node ) {
2015-08-11 13:22:20 -05:00
( & ExprField ( ref lfexp , ref lfident ) ,
& ExprField ( ref rfexp , ref rfident ) ) = >
2015-08-21 05:19:07 -05:00
lfident . node = = rfident . node & & is_exp_equal ( cx , lfexp , rfexp ) ,
2015-08-05 08:10:45 -05:00
( & ExprLit ( ref l ) , & ExprLit ( ref r ) ) = > l . node = = r . node ,
2015-08-11 13:22:20 -05:00
( & ExprPath ( ref lqself , ref lsubpath ) ,
& ExprPath ( ref rqself , ref rsubpath ) ) = >
2015-08-25 05:34:27 -05:00
both ( lqself , rqself , is_qself_equal ) & &
2015-08-11 13:22:20 -05:00
is_path_equal ( lsubpath , rsubpath ) ,
( & ExprTup ( ref ltup ) , & ExprTup ( ref rtup ) ) = >
2015-08-21 05:19:07 -05:00
is_exps_equal ( cx , ltup , rtup ) ,
( & ExprVec ( ref l ) , & ExprVec ( ref r ) ) = > is_exps_equal ( cx , l , r ) ,
2015-08-25 05:34:27 -05:00
( & ExprCast ( ref lx , ref lt ) , & ExprCast ( ref rx , ref rt ) ) = >
is_exp_equal ( cx , lx , rx ) & & is_cast_ty_equal ( lt , rt ) ,
2015-08-05 08:10:45 -05:00
_ = > false
}
2015-04-30 04:48:43 -05:00
}
2015-09-18 21:53:04 -05:00
fn is_exps_equal ( cx : & LateContext , left : & [ P < Expr > ] , right : & [ P < Expr > ] ) -> bool {
2015-08-21 05:19:07 -05:00
over ( left , right , | l , r | is_exp_equal ( cx , l , r ) )
2015-04-30 04:48:43 -05:00
}
fn is_path_equal ( left : & Path , right : & Path ) -> bool {
2015-08-02 10:29:12 -05:00
// The == of idents doesn't work with different contexts,
2015-08-05 08:10:45 -05:00
// we have to be explicit about hygiene
2015-08-11 13:22:20 -05:00
left . global = = right . global & & over ( & left . segments , & right . segments ,
2015-08-05 08:10:45 -05:00
| l , r | l . identifier . name = = r . identifier . name
& & l . parameters = = r . parameters )
2015-04-30 04:48:43 -05:00
}
fn is_qself_equal ( left : & QSelf , right : & QSelf ) -> bool {
2015-08-05 08:10:45 -05:00
left . ty . node = = right . ty . node & & left . position = = right . position
2015-04-30 04:48:43 -05:00
}
2015-08-11 13:22:20 -05:00
fn over < X , F > ( left : & [ X ] , right : & [ X ] , mut eq_fn : F ) -> bool
2015-08-05 08:10:45 -05:00
where F : FnMut ( & X , & X ) -> bool {
2015-08-11 13:22:20 -05:00
left . len ( ) = = right . len ( ) & & left . iter ( ) . zip ( right ) . all ( | ( x , y ) |
2015-08-05 08:10:45 -05:00
eq_fn ( x , y ) )
2015-04-30 04:48:43 -05:00
}
2015-08-11 13:22:20 -05:00
fn both < X , F > ( l : & Option < X > , r : & Option < X > , mut eq_fn : F ) -> bool
2015-08-05 08:10:45 -05:00
where F : FnMut ( & X , & X ) -> bool {
l . as_ref ( ) . map_or_else ( | | r . is_none ( ) , | x | r . as_ref ( ) . map_or ( false ,
| y | eq_fn ( x , y ) ) )
2015-04-30 04:48:43 -05:00
}
fn is_cmp_or_bit ( op : & BinOp ) -> bool {
match op . node {
2015-08-11 13:22:20 -05:00
BiEq | BiLt | BiLe | BiGt | BiGe | BiNe | BiAnd | BiOr |
2015-05-22 17:49:13 -05:00
BiBitXor | BiBitAnd | BiBitOr = > true ,
2015-04-30 04:48:43 -05:00
_ = > false
}
}
2015-08-25 05:34:27 -05:00
fn is_cast_ty_equal ( left : & Ty , right : & Ty ) -> bool {
match ( & left . node , & right . node ) {
( & TyVec ( ref lvec ) , & TyVec ( ref rvec ) ) = > is_cast_ty_equal ( lvec , rvec ) ,
( & TyPtr ( ref lmut ) , & TyPtr ( ref rmut ) ) = >
lmut . mutbl = = rmut . mutbl & &
is_cast_ty_equal ( & * lmut . ty , & * rmut . ty ) ,
( & TyRptr ( _ , ref lrmut ) , & TyRptr ( _ , ref rrmut ) ) = >
lrmut . mutbl = = rrmut . mutbl & &
is_cast_ty_equal ( & * lrmut . ty , & * rrmut . ty ) ,
( & TyPath ( ref lq , ref lpath ) , & TyPath ( ref rq , ref rpath ) ) = >
both ( lq , rq , is_qself_equal ) & & is_path_equal ( lpath , rpath ) ,
( & TyInfer , & TyInfer ) = > true ,
_ = > false
}
}