2015-04-30 04:48:43 -05:00
use rustc ::lint ::* ;
2016-01-22 06:24:44 -06:00
use rustc ::middle ::def ::{ Def , PathResolution } ;
2016-03-31 10:05:43 -05:00
use rustc_const_eval ::lookup_const_by_id ;
2015-09-03 09:42:17 -05:00
use rustc_front ::hir ::* ;
use rustc_front ::util ::is_comparison_binop ;
2016-02-12 11:35:44 -06:00
use syntax ::ast ::LitKind ;
2016-02-24 10:38:57 -06:00
use syntax ::codemap ::Span ;
2015-07-26 09:53:11 -05:00
use utils ::span_lint ;
2015-04-30 04:48:43 -05:00
2016-02-05 17:41:54 -06:00
/// **What it does:** This lint checks for incompatible bit masks in comparisons.
2015-12-10 18:22:27 -06:00
///
/// 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
///
2016-03-28 14:23:21 -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` |
/// |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |
/// |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |
2015-12-10 18:22:27 -06:00
///
/// **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:
///
2016-03-19 11:48:29 -05:00
/// |Comparison| Bit Op |Example |equals |
2015-12-10 18:22:27 -06:00
/// |----------|---------|-----------|-------|
/// |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|
/// |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|
///
/// **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
///
2016-03-28 14:23:21 -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` |
/// |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |
/// |`<=` 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.
///
2016-03-28 14:23:21 -05:00
/// |Comparison|Bit Op |Example |equals |Formula|
2015-08-19 02:07:50 -05:00
/// |`>` / `<=`|`|` / `^`|`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 ) {
2016-01-03 22:26:12 -06:00
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-08-11 13:22:20 -05:00
}
}
2015-04-30 04:48:43 -05:00
}
}
2016-01-03 22:26:12 -06: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 ,
2016-01-03 22:26:12 -06:00
_ = > BiOr , // Dummy
2015-08-11 13:22:20 -05:00
}
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 ;
}
2016-01-03 22:26:12 -06:00
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
}
2016-01-03 22:26:12 -06:00
fn check_bit_mask ( cx : & LateContext , bit_op : BinOp_ , cmp_op : BinOp_ , mask_value : u64 , cmp_value : u64 , span : & Span ) {
2015-08-11 13:22:20 -05:00
match cmp_op {
2016-01-03 22:26:12 -06:00
BiEq | BiNe = > {
match bit_op {
BiBitAnd = > {
if mask_value & cmp_value ! = cmp_value {
if cmp_value ! = 0 {
span_lint ( cx ,
BAD_BIT_MASK ,
* span ,
& format! ( " incompatible bit mask: `_ & {} ` can never be equal to ` {} ` " ,
mask_value ,
cmp_value ) ) ;
}
2016-01-13 11:32:05 -06:00
} else if mask_value = = 0 {
span_lint ( cx , BAD_BIT_MASK , * span , " &-masking with zero " ) ;
2016-01-03 22:26:12 -06:00
}
2015-08-11 13:22:20 -05:00
}
2016-01-03 22:26:12 -06:00
BiBitOr = > {
if mask_value | cmp_value ! = cmp_value {
span_lint ( cx ,
BAD_BIT_MASK ,
* span ,
& format! ( " incompatible bit mask: `_ | {} ` can never be equal to ` {} ` " ,
mask_value ,
cmp_value ) ) ;
}
2015-08-11 13:22:20 -05:00
}
2016-01-03 22:26:12 -06:00
_ = > ( ) ,
}
}
BiLt | BiGe = > {
match bit_op {
BiBitAnd = > {
if mask_value < cmp_value {
span_lint ( cx ,
BAD_BIT_MASK ,
* span ,
& format! ( " incompatible bit mask: `_ & {} ` will always be lower than ` {} ` " ,
mask_value ,
cmp_value ) ) ;
2016-01-13 11:32:05 -06:00
} else if mask_value = = 0 {
span_lint ( cx , BAD_BIT_MASK , * span , " &-masking with zero " ) ;
2016-01-03 22:26:12 -06:00
}
2015-08-11 13:22:20 -05:00
}
2016-01-03 22:26:12 -06:00
BiBitOr = > {
if mask_value > = cmp_value {
span_lint ( cx ,
BAD_BIT_MASK ,
* span ,
& format! ( " incompatible bit mask: `_ | {} ` will never be lower than ` {} ` " ,
mask_value ,
cmp_value ) ) ;
} else {
check_ineffective_lt ( cx , * span , mask_value , cmp_value , " | " ) ;
}
2015-08-11 13:22:20 -05:00
}
2016-01-03 22:26:12 -06:00
BiBitXor = > check_ineffective_lt ( cx , * span , mask_value , cmp_value , " ^ " ) ,
_ = > ( ) ,
}
}
BiLe | BiGt = > {
match bit_op {
BiBitAnd = > {
if mask_value < = cmp_value {
span_lint ( cx ,
BAD_BIT_MASK ,
* span ,
& format! ( " incompatible bit mask: `_ & {} ` will never be higher than ` {} ` " ,
mask_value ,
cmp_value ) ) ;
2016-01-13 11:32:05 -06:00
} else if mask_value = = 0 {
span_lint ( cx , BAD_BIT_MASK , * span , " &-masking with zero " ) ;
2016-01-03 22:26:12 -06:00
}
}
BiBitOr = > {
if mask_value > cmp_value {
span_lint ( cx ,
BAD_BIT_MASK ,
* span ,
& format! ( " incompatible bit mask: `_ | {} ` will always be higher than ` {} ` " ,
mask_value ,
cmp_value ) ) ;
} else {
check_ineffective_gt ( cx , * span , mask_value , cmp_value , " | " ) ;
}
}
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 {
2016-01-03 22:26:12 -06:00
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-08-19 02:07:50 -05:00
}
}
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 {
2016-01-03 22:26:12 -06:00
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-08-19 02:07:50 -05:00
}
}
2016-01-03 22:26:12 -06: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 ) = > {
2016-02-12 11:35:44 -06:00
if let LitKind ::Int ( value , _ ) = lit_ptr . node {
2015-11-27 09:47:24 -06:00
Some ( value ) //TODO: Handle sign
2016-01-03 22:26:12 -06:00
} else {
None
}
2015-11-16 22:39:42 -06:00
}
2015-08-21 13:44:48 -05:00
ExprPath ( _ , _ ) = > {
2016-01-03 22:26:12 -06: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 ) {
2016-01-22 06:24:44 -06:00
Some ( & PathResolution { base_def : Def ::Const ( def_id ) , .. } ) = > Some ( def_id ) ,
2016-01-03 22:26:12 -06:00
_ = > None ,
}
2015-05-06 23:41:54 -05:00
}
2016-03-20 15:24:18 -05:00
. and_then ( | def_id | lookup_const_by_id ( cx . tcx , def_id , None ) )
2016-03-15 14:09:53 -05:00
. and_then ( | ( l , _ty ) | fetch_int_literal ( cx , l ) )
2015-08-11 13:22:20 -05:00
}
2016-01-03 22:26:12 -06:00
_ = > None ,
2015-08-11 13:22:20 -05:00
}
2015-04-30 04:48:43 -05:00
}