2015-04-30 04:48:43 -05:00
use rustc ::lint ::* ;
2015-04-30 08:17:06 -05:00
use rustc ::middle ::const_eval ::lookup_const_by_id ;
use rustc ::middle ::def ::* ;
2015-09-03 09:42:17 -05:00
use rustc_front ::hir ::* ;
use rustc_front ::util ::is_comparison_binop ;
2015-04-30 04:48:43 -05:00
use syntax ::codemap ::Span ;
2015-09-16 19:01:41 -05:00
use syntax ::ast ::Lit_ ::* ;
2015-08-16 01:54:43 -05:00
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 incompatible bit masks in comparisons. It is `Warn` by default.
///
/// The formula for detecting if an expression of the type `_ <bit_op> m <cmp_op> c` (where `<bit_op>`
2015-12-21 13:47:19 -06:00
/// is one of {`&`, `|`} and `<cmp_op>` is one of {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following table:
2015-12-10 18:22:27 -06:00
///
/// |Comparison |Bit-Op|Example |is always|Formula |
/// |------------|------|------------|---------|----------------------|
/// |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |
/// |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |
/// |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |
/// |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |
/// |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |
/// |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |
///
/// **Why is this bad?** If the bits that the comparison cares about are always set to zero or one by the bit mask, the comparison is constant `true` or `false` (depending on mask, compared value, and operators).
///
/// So the code is actively misleading, and the only reason someone would write this intentionally is to win an underhanded Rust contest or create a test-case for this lint.
///
/// **Known problems:** None
///
/// **Example:** `x & 1 == 2` (also see table above)
2015-04-30 04:48:43 -05:00
declare_lint! {
pub BAD_BIT_MASK ,
2015-09-06 09:09:35 -05:00
Warn ,
2015-08-13 03:32:35 -05:00
" expressions of the form `_ & mask == select` that will only ever return `true` or `false` \
( because in the example ` select ` containing bits that ` mask ` doesn ' t have ) "
2015-04-30 04:48:43 -05:00
}
2015-12-10 18:22:27 -06:00
/// **What it does:** This lint checks for bit masks in comparisons which can be removed without changing the outcome. The basic structure can be seen in the following table:
///
/// |Comparison|Bit-Op |Example |equals |
/// |----------|---------|-----------|-------|
/// |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|
/// |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|
///
/// This lint is `Warn` by default.
///
/// **Why is this bad?** Not equally evil as [`bad_bit_mask`](#bad_bit_mask), but still a bit misleading, because the bit mask is ineffective.
///
/// **Known problems:** False negatives: This lint will only match instances where we have figured out the math (which is for a power-of-two compared value). This means things like `x | 1 >= 7` (which would be better written as `x >= 6`) will not be reported (but bit masks like this are fairly uncommon).
///
/// **Example:** `x | 1 > 3` (also see table above)
2015-05-15 07:09:29 -05:00
declare_lint! {
2015-08-11 13:22:20 -05:00
pub INEFFECTIVE_BIT_MASK ,
Warn ,
2015-08-13 03:32:35 -05:00
" expressions where a bit mask will be rendered useless by a comparison, e.g. `(x | 1) > 2` "
2015-05-15 07:09:29 -05:00
}
2015-08-11 13:22:20 -05:00
/// Checks for incompatible bit masks in comparisons, e.g. `x & 1 == 2`.
2015-05-22 17:49:13 -05:00
/// This cannot work because the bit that makes up the value two was
/// zeroed out by the bit-and with 1. So the formula for detecting if an
2015-08-11 13:22:20 -05:00
/// expression of the type `_ <bit_op> m <cmp_op> c` (where `<bit_op>`
/// is one of {`&`, '|'} and `<cmp_op>` is one of {`!=`, `>=`, `>` ,
2015-05-22 17:49:13 -05:00
/// `!=`, `>=`, `>`}) can be determined from the following table:
2015-08-11 13:22:20 -05:00
///
2015-05-22 17:49:13 -05:00
/// |Comparison |Bit-Op|Example |is always|Formula |
/// |------------|------|------------|---------|----------------------|
/// |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |
/// |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |
/// |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |
/// |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |
2015-05-23 05:32:29 -05:00
/// |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |
2015-05-22 17:49:13 -05:00
/// |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |
2015-08-11 13:22:20 -05:00
///
2015-05-22 17:49:13 -05:00
/// This lint is **deny** by default
///
/// There is also a lint that warns on ineffective masks that is *warn*
2015-08-19 02:07:50 -05:00
/// by default.
///
/// |Comparison|Bit-Op |Example |equals |Formula|
/// |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|`¹ && m <= c`|
/// |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|`¹ && m < c` |
///
/// `¹ power_of_two(c + 1)`
2015-04-30 04:48:43 -05:00
#[ derive(Copy,Clone) ]
pub struct BitMask ;
impl LintPass for BitMask {
fn get_lints ( & self ) -> LintArray {
2015-05-15 07:09:29 -05:00
lint_array! ( BAD_BIT_MASK , INEFFECTIVE_BIT_MASK )
2015-04-30 04:48:43 -05:00
}
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 BitMask {
fn check_expr ( & mut self , cx : & LateContext , e : & Expr ) {
2015-04-30 04:48:43 -05:00
if let ExprBinary ( ref cmp , ref left , ref right ) = e . node {
2015-08-11 13:22:20 -05:00
if is_comparison_binop ( cmp . node ) {
fetch_int_literal ( cx , right ) . map_or_else ( | |
fetch_int_literal ( cx , left ) . map_or ( ( ) , | cmp_val |
check_compare ( cx , right , invert_cmp ( cmp . node ) ,
cmp_val , & e . span ) ) ,
| cmp_opt | check_compare ( cx , left , cmp . node , cmp_opt ,
& e . span ) )
}
}
2015-04-30 04:48:43 -05:00
}
}
2015-05-04 00:17:15 -05:00
fn invert_cmp ( cmp : BinOp_ ) -> BinOp_ {
2015-08-11 13:22:20 -05:00
match cmp {
BiEq = > BiEq ,
BiNe = > BiNe ,
BiLt = > BiGt ,
BiGt = > BiLt ,
BiLe = > BiGe ,
BiGe = > BiLe ,
_ = > BiOr // Dummy
}
2015-05-04 00:17:15 -05:00
}
2015-09-18 21:53:04 -05:00
fn check_compare ( cx : & LateContext , bit_op : & Expr , cmp_op : BinOp_ , cmp_value : u64 , span : & Span ) {
2015-09-18 22:02:56 -05:00
if let ExprBinary ( ref op , ref left , ref right ) = bit_op . node {
if op . node ! = BiBitAnd & & op . node ! = BiBitOr {
return ;
}
fetch_int_literal ( cx , right ) . or_else ( | | fetch_int_literal (
cx , left ) ) . map_or ( ( ) , | mask | check_bit_mask ( cx , op . node ,
cmp_op , mask , cmp_value , span ) )
2015-08-11 13:22:20 -05:00
}
2015-04-30 04:48:43 -05:00
}
2015-09-18 21:53:04 -05:00
fn check_bit_mask ( cx : & LateContext , bit_op : BinOp_ , cmp_op : BinOp_ ,
2015-08-11 13:22:20 -05:00
mask_value : u64 , cmp_value : u64 , span : & Span ) {
match cmp_op {
BiEq | BiNe = > match bit_op {
2015-09-06 09:09:35 -05:00
BiBitAnd = > if mask_value & cmp_value ! = cmp_value {
2015-08-11 13:22:20 -05:00
if cmp_value ! = 0 {
span_lint ( cx , BAD_BIT_MASK , * span , & format! (
2015-08-12 03:46:49 -05:00
" incompatible bit mask: `_ & {}` can never be equal to `{}` " ,
2015-08-11 13:22:20 -05:00
mask_value , cmp_value ) ) ;
}
} else {
if mask_value = = 0 {
2015-08-27 00:39:40 -05:00
span_lint ( cx , BAD_BIT_MASK , * span , " &-masking with zero " ) ;
2015-08-11 13:22:20 -05:00
}
} ,
BiBitOr = > if mask_value | cmp_value ! = cmp_value {
span_lint ( cx , BAD_BIT_MASK , * span , & format! (
2015-08-12 03:46:49 -05:00
" incompatible bit mask: `_ | {}` can never be equal to `{}` " ,
2015-08-11 13:22:20 -05:00
mask_value , cmp_value ) ) ;
} ,
_ = > ( )
} ,
BiLt | BiGe = > match bit_op {
BiBitAnd = > if mask_value < cmp_value {
span_lint ( cx , BAD_BIT_MASK , * span , & format! (
2015-08-12 03:46:49 -05:00
" incompatible bit mask: `_ & {}` will always be lower than `{}` " ,
2015-08-11 13:22:20 -05:00
mask_value , cmp_value ) ) ;
} else {
if mask_value = = 0 {
2015-08-27 00:39:40 -05:00
span_lint ( cx , BAD_BIT_MASK , * span , " &-masking with zero " ) ;
2015-08-11 13:22:20 -05:00
}
} ,
BiBitOr = > if mask_value > = cmp_value {
span_lint ( cx , BAD_BIT_MASK , * span , & format! (
2015-08-12 03:46:49 -05:00
" incompatible bit mask: `_ | {}` will never be lower than `{}` " ,
2015-08-11 13:22:20 -05:00
mask_value , cmp_value ) ) ;
} else {
2015-08-19 02:07:50 -05:00
check_ineffective_lt ( cx , * span , mask_value , cmp_value , " | " ) ;
2015-08-11 13:22:20 -05:00
} ,
2015-08-19 02:07:50 -05:00
BiBitXor = >
check_ineffective_lt ( cx , * span , mask_value , cmp_value , " ^ " ) ,
2015-08-11 13:22:20 -05:00
_ = > ( )
} ,
BiLe | BiGt = > match bit_op {
BiBitAnd = > if mask_value < = cmp_value {
span_lint ( cx , BAD_BIT_MASK , * span , & format! (
2015-08-12 03:46:49 -05:00
" incompatible bit mask: `_ & {}` will never be higher than `{}` " ,
2015-08-11 13:22:20 -05:00
mask_value , cmp_value ) ) ;
} else {
if mask_value = = 0 {
2015-08-27 00:39:40 -05:00
span_lint ( cx , BAD_BIT_MASK , * span , " &-masking with zero " ) ;
2015-08-11 13:22:20 -05:00
}
} ,
BiBitOr = > if mask_value > cmp_value {
span_lint ( cx , BAD_BIT_MASK , * span , & format! (
2015-08-12 03:46:49 -05:00
" incompatible bit mask: `_ | {}` will always be higher than `{}` " ,
2015-08-11 13:22:20 -05:00
mask_value , cmp_value ) ) ;
} else {
2015-08-19 02:07:50 -05:00
check_ineffective_gt ( cx , * span , mask_value , cmp_value , " | " ) ;
2015-08-11 13:22:20 -05:00
} ,
2015-08-19 02:07:50 -05:00
BiBitXor = >
check_ineffective_gt ( cx , * span , mask_value , cmp_value , " ^ " ) ,
2015-08-11 13:22:20 -05:00
_ = > ( )
} ,
_ = > ( )
}
2015-04-30 04:48:43 -05:00
}
2015-09-18 21:53:04 -05:00
fn check_ineffective_lt ( cx : & LateContext , span : Span , m : u64 , c : u64 , op : & str ) {
2015-08-19 02:07:50 -05:00
if c . is_power_of_two ( ) & & m < c {
span_lint ( cx , INEFFECTIVE_BIT_MASK , span , & format! (
" ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly " ,
op , m , c ) ) ;
}
}
2015-09-18 21:53:04 -05:00
fn check_ineffective_gt ( cx : & LateContext , span : Span , m : u64 , c : u64 , op : & str ) {
2015-08-19 02:07:50 -05:00
if ( c + 1 ) . is_power_of_two ( ) & & m < = c {
span_lint ( cx , INEFFECTIVE_BIT_MASK , span , & format! (
" ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly " ,
op , m , c ) ) ;
}
}
2015-09-18 21:53:04 -05:00
fn fetch_int_literal ( cx : & LateContext , lit : & Expr ) -> Option < u64 > {
2015-08-21 13:44:48 -05:00
match lit . node {
ExprLit ( ref lit_ptr ) = > {
2015-11-24 11:44:40 -06:00
if let LitInt ( value , _ ) = lit_ptr . node {
2015-11-27 09:47:24 -06:00
Some ( value ) //TODO: Handle sign
} else { None }
2015-11-16 22:39:42 -06:00
}
2015-08-21 13:44:48 -05:00
ExprPath ( _ , _ ) = > {
2015-08-11 13:22:20 -05:00
// Important to let the borrow expire before the const lookup to avoid double
// borrowing.
let def_map = cx . tcx . def_map . borrow ( ) ;
match def_map . get ( & lit . id ) {
Some ( & PathResolution { base_def : DefConst ( def_id ) , .. } ) = > Some ( def_id ) ,
_ = > None
2015-05-06 23:41:54 -05:00
}
2015-08-11 13:22:20 -05:00
}
2015-11-27 09:47:24 -06:00
. and_then ( | def_id | lookup_const_by_id ( cx . tcx , def_id , None ) )
2015-08-11 13:22:20 -05:00
. and_then ( | l | fetch_int_literal ( cx , l ) ) ,
2015-11-27 09:47:24 -06:00
_ = > None
2015-08-11 13:22:20 -05:00
}
2015-04-30 04:48:43 -05:00
}