2015-09-19 08:23:04 +05:30
use rustc ::lint ::* ;
2015-09-05 12:46:34 +02:00
use rustc_front ::hir ::* ;
use syntax ::ptr ::P ;
use std ::cmp ::PartialOrd ;
use std ::cmp ::Ordering ::* ;
2015-09-05 13:15:18 +02:00
use consts ::{ Constant , constant_simple } ;
2015-11-11 11:08:33 -05:00
use utils ::{ match_def_path , span_lint } ;
2015-09-05 12:46:34 +02:00
use self ::MinMax ::{ Min , Max } ;
2015-12-11 01:22:27 +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.
///
/// **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 14:20:35 +02:00
declare_lint! ( pub MIN_MAX , Warn ,
2015-09-05 12:46:34 +02: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-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 ) {
2015-09-05 12:46:34 +02:00
if outer_max = = inner_max { return ; }
match ( outer_max , outer_c . partial_cmp ( & inner_c ) ) {
( _ , None ) | ( Max , Some ( Less ) ) | ( Min , Some ( Greater ) ) = > ( ) ,
_ = > {
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 {
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 13:15:18 +02:00
fetch_const ( args , Min )
2015-09-05 12:46:34 +02:00
} else {
2015-11-11 11:08:33 -05:00
if match_def_path ( cx , def_id , & [ " core " , " cmp " , " max " ] ) {
2015-09-05 13:15:18 +02:00
fetch_const ( args , Max )
2015-09-05 12:46:34 +02:00
} else {
None
}
}
2015-09-05 13:15:18 +02:00
} else { None }
} else { None }
2015-09-05 12:46:34 +02:00
}
2015-09-05 13:15:18 +02:00
fn fetch_const ( args : & [ P < Expr > ] , m : MinMax ) ->
Option < ( MinMax , Constant , & Expr ) > {
2015-09-05 12:46:34 +02:00
if args . len ( ) ! = 2 { return None }
2015-09-05 13:15:18 +02:00
if let Some ( c ) = constant_simple ( & args [ 0 ] ) {
if let None = constant_simple ( & args [ 1 ] ) { // otherwise ignore
2015-09-05 12:46:34 +02:00
Some ( ( m , c , & args [ 1 ] ) )
} else { None }
} else {
2015-09-05 13:15:18 +02:00
if let Some ( c ) = constant_simple ( & args [ 1 ] ) {
2015-09-05 12:46:34 +02:00
Some ( ( m , c , & args [ 0 ] ) )
} else { None }
}
}