2016-02-24 17:38:57 +01:00
use consts ::{ Constant , constant_simple } ;
2015-09-19 08:23:04 +05:30
use rustc ::lint ::* ;
2016-04-07 17:46:48 +02:00
use rustc ::hir ::* ;
2016-02-03 15:39:22 +01:00
use std ::cmp ::{ PartialOrd , Ordering } ;
2016-02-24 17:38:57 +01:00
use syntax ::ptr ::P ;
2016-04-26 13:31:52 +02:00
use utils ::{ match_def_path , paths , span_lint } ;
2015-09-05 12:46:34 +02:00
2016-02-06 00:41:54 +01:00
/// **What it does:** This lint checks for expressions where `std::cmp::min` and `max` are used to clamp values, but switched so that the result is constant.
2015-12-11 01:22:27 +01:00
///
/// **Why is this bad?** This is in all probability not the intended outcome. At the least it hurts readability of the code.
///
/// **Known problems:** None
///
/// **Example:** `min(0, max(100, x))` will always be equal to `0`. Probably the author meant to clamp the value between 0 and 100, but has erroneously swapped `min` and `max`.
2016-02-06 00:13:29 +01:00
declare_lint! {
pub MIN_MAX , Warn ,
" `min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant "
}
2015-09-05 12:46:34 +02:00
#[ allow(missing_copy_implementations) ]
pub struct MinMaxPass ;
impl LintPass for MinMaxPass {
fn get_lints ( & self ) -> LintArray {
2015-11-17 14:22:57 +09:00
lint_array! ( MIN_MAX )
2015-09-05 12:46:34 +02:00
}
2015-09-19 08:23:04 +05:30
}
2015-09-05 12:46:34 +02:00
2015-09-19 08:23:04 +05:30
impl LateLintPass for MinMaxPass {
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2015-11-11 11:08:33 -05:00
if let Some ( ( outer_max , outer_c , oe ) ) = min_max ( cx , expr ) {
if let Some ( ( inner_max , inner_c , _ ) ) = min_max ( cx , oe ) {
2016-01-04 09:56:12 +05:30
if outer_max = = inner_max {
return ;
}
2015-09-05 12:46:34 +02:00
match ( outer_max , outer_c . partial_cmp ( & inner_c ) ) {
2016-04-14 20:14:03 +02:00
( _ , None ) |
( MinMax ::Max , Some ( Ordering ::Less ) ) |
( MinMax ::Min , Some ( Ordering ::Greater ) ) = > ( ) ,
2015-09-05 12:46:34 +02:00
_ = > {
2016-01-04 09:56:12 +05:30
span_lint ( cx , MIN_MAX , expr . span , " this min/max combination leads to constant result " ) ;
2015-11-17 13:39:42 +09:00
}
2015-09-05 12:46:34 +02:00
}
}
}
}
}
#[ derive(PartialEq, Eq, Debug) ]
enum MinMax {
Min ,
Max ,
}
2015-11-11 11:08:33 -05:00
fn min_max < ' a > ( cx : & LateContext , expr : & ' a Expr ) -> Option < ( MinMax , Constant , & ' a Expr ) > {
2015-09-05 13:15:18 +02:00
if let ExprCall ( ref path , ref args ) = expr . node {
2015-11-11 11:08:33 -05:00
if let ExprPath ( None , _ ) = path . node {
2016-06-10 19:47:10 +02:00
let def_id = cx . tcx . expect_def ( path . id ) . def_id ( ) ;
2015-11-11 11:08:33 -05:00
2016-04-26 13:31:52 +02:00
if match_def_path ( cx , def_id , & paths ::CMP_MIN ) {
2016-02-24 17:38:57 +01:00
fetch_const ( args , MinMax ::Min )
2016-04-26 13:31:52 +02:00
} else if match_def_path ( cx , def_id , & paths ::CMP_MAX ) {
2016-02-24 17:38:57 +01:00
fetch_const ( args , MinMax ::Max )
2015-09-05 12:46:34 +02:00
} else {
2016-01-13 18:32:05 +01:00
None
2015-09-05 12:46:34 +02:00
}
2016-01-04 09:56:12 +05:30
} else {
None
}
} else {
None
}
}
2015-09-05 12:46:34 +02:00
2016-01-04 09:56:12 +05:30
fn fetch_const ( args : & [ P < Expr > ] , m : MinMax ) -> Option < ( MinMax , Constant , & Expr ) > {
if args . len ( ) ! = 2 {
return None ;
}
2015-09-05 13:15:18 +02:00
if let Some ( c ) = constant_simple ( & args [ 0 ] ) {
2016-01-04 09:56:12 +05:30
if let None = constant_simple ( & args [ 1 ] ) {
// otherwise ignore
2015-09-05 12:46:34 +02:00
Some ( ( m , c , & args [ 1 ] ) )
2016-01-04 09:56:12 +05:30
} else {
None
}
2016-06-21 23:53:24 +02:00
} else if let Some ( c ) = constant_simple ( & args [ 1 ] ) {
Some ( ( m , c , & args [ 0 ] ) )
2015-09-05 12:46:34 +02:00
} else {
2016-06-21 23:53:24 +02:00
None
2015-09-05 12:46:34 +02:00
}
}