2015-09-18 21:53:04 -05:00
use rustc ::lint ::* ;
2015-09-05 05:46:34 -05:00
use rustc_front ::hir ::* ;
use syntax ::ptr ::P ;
use std ::cmp ::PartialOrd ;
use std ::cmp ::Ordering ::* ;
2015-09-05 06:15:18 -05:00
use consts ::{ Constant , constant_simple } ;
2015-11-11 10:08:33 -06:00
use utils ::{ match_def_path , span_lint } ;
2015-09-05 05:46:34 -05:00
use self ::MinMax ::{ Min , Max } ;
2016-01-01 10:48:19 -06: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. It is `Warn` by default.
2015-12-10 18:22:27 -06: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`.
2015-09-05 07:20:35 -05:00
declare_lint! ( pub MIN_MAX , Warn ,
2015-09-05 05:46:34 -05:00
" `min(_, max(_, _))` (or vice versa) with bounds clamping the result \
to a constant " );
#[ allow(missing_copy_implementations) ]
pub struct MinMaxPass ;
impl LintPass for MinMaxPass {
fn get_lints ( & self ) -> LintArray {
2015-11-16 23:22:57 -06:00
lint_array! ( MIN_MAX )
2015-09-05 05:46:34 -05:00
}
2015-09-18 21:53:04 -05:00
}
2015-09-05 05:46:34 -05:00
2015-09-18 21:53:04 -05:00
impl LateLintPass for MinMaxPass {
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2015-11-11 10:08:33 -06: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-03 22:26:12 -06:00
if outer_max = = inner_max {
return ;
}
2015-09-05 05:46:34 -05:00
match ( outer_max , outer_c . partial_cmp ( & inner_c ) ) {
( _ , None ) | ( Max , Some ( Less ) ) | ( Min , Some ( Greater ) ) = > ( ) ,
_ = > {
2016-01-03 22:26:12 -06:00
span_lint ( cx , MIN_MAX , expr . span , " this min/max combination leads to constant result " ) ;
2015-11-16 22:39:42 -06:00
}
2015-09-05 05:46:34 -05:00
}
}
}
}
}
#[ derive(PartialEq, Eq, Debug) ]
enum MinMax {
Min ,
Max ,
}
2015-11-11 10:08:33 -06:00
fn min_max < ' a > ( cx : & LateContext , expr : & ' a Expr ) -> Option < ( MinMax , Constant , & ' a Expr ) > {
2015-09-05 06:15:18 -05:00
if let ExprCall ( ref path , ref args ) = expr . node {
2015-11-11 10:08:33 -06:00
if let ExprPath ( None , _ ) = path . node {
let def_id = cx . tcx . def_map . borrow ( ) [ & path . id ] . def_id ( ) ;
if match_def_path ( cx , def_id , & [ " core " , " cmp " , " min " ] ) {
2015-09-05 06:15:18 -05:00
fetch_const ( args , Min )
2016-01-13 11:32:05 -06:00
} else if match_def_path ( cx , def_id , & [ " core " , " cmp " , " max " ] ) {
fetch_const ( args , Max )
2015-09-05 05:46:34 -05:00
} else {
2016-01-13 11:32:05 -06:00
None
2015-09-05 05:46:34 -05:00
}
2016-01-03 22:26:12 -06:00
} else {
None
}
} else {
None
}
}
2015-09-05 05:46:34 -05:00
2016-01-03 22:26:12 -06:00
fn fetch_const ( args : & [ P < Expr > ] , m : MinMax ) -> Option < ( MinMax , Constant , & Expr ) > {
if args . len ( ) ! = 2 {
return None ;
}
2015-09-05 06:15:18 -05:00
if let Some ( c ) = constant_simple ( & args [ 0 ] ) {
2016-01-03 22:26:12 -06:00
if let None = constant_simple ( & args [ 1 ] ) {
// otherwise ignore
2015-09-05 05:46:34 -05:00
Some ( ( m , c , & args [ 1 ] ) )
2016-01-03 22:26:12 -06:00
} else {
None
}
2015-09-05 05:46:34 -05:00
} else {
2015-09-05 06:15:18 -05:00
if let Some ( c ) = constant_simple ( & args [ 1 ] ) {
2015-09-05 05:46:34 -05:00
Some ( ( m , c , & args [ 0 ] ) )
2016-01-03 22:26:12 -06:00
} else {
None
}
2015-09-05 05:46:34 -05:00
}
}