]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/minmax.rs
Merge pull request #1032 from Manishearth/mut_mut
[rust.git] / clippy_lints / src / minmax.rs
1 use consts::{Constant, constant_simple};
2 use rustc::lint::*;
3 use rustc::hir::*;
4 use std::cmp::{PartialOrd, Ordering};
5 use syntax::ptr::P;
6 use utils::{match_def_path, paths, span_lint};
7
8 /// **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.
9 ///
10 /// **Why is this bad?** This is in all probability not the intended outcome. At the least it hurts readability of the code.
11 ///
12 /// **Known problems:** None
13 ///
14 /// **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`.
15 declare_lint! {
16     pub MIN_MAX, Warn,
17     "`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant"
18 }
19
20 #[allow(missing_copy_implementations)]
21 pub struct MinMaxPass;
22
23 impl LintPass for MinMaxPass {
24     fn get_lints(&self) -> LintArray {
25         lint_array!(MIN_MAX)
26     }
27 }
28
29 impl LateLintPass for MinMaxPass {
30     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
31         if let Some((outer_max, outer_c, oe)) = min_max(cx, expr) {
32             if let Some((inner_max, inner_c, _)) = min_max(cx, oe) {
33                 if outer_max == inner_max {
34                     return;
35                 }
36                 match (outer_max, outer_c.partial_cmp(&inner_c)) {
37                     (_, None) |
38                     (MinMax::Max, Some(Ordering::Less)) |
39                     (MinMax::Min, Some(Ordering::Greater)) => (),
40                     _ => {
41                         span_lint(cx, MIN_MAX, expr.span, "this min/max combination leads to constant result");
42                     }
43                 }
44             }
45         }
46     }
47 }
48
49 #[derive(PartialEq, Eq, Debug)]
50 enum MinMax {
51     Min,
52     Max,
53 }
54
55 fn min_max<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(MinMax, Constant, &'a Expr)> {
56     if let ExprCall(ref path, ref args) = expr.node {
57         if let ExprPath(None, _) = path.node {
58             let def_id = cx.tcx.expect_def(path.id).def_id();
59
60             if match_def_path(cx, def_id, &paths::CMP_MIN) {
61                 fetch_const(args, MinMax::Min)
62             } else if match_def_path(cx, def_id, &paths::CMP_MAX) {
63                 fetch_const(args, MinMax::Max)
64             } else {
65                 None
66             }
67         } else {
68             None
69         }
70     } else {
71         None
72     }
73 }
74
75 fn fetch_const(args: &[P<Expr>], m: MinMax) -> Option<(MinMax, Constant, &Expr)> {
76     if args.len() != 2 {
77         return None;
78     }
79     if let Some(c) = constant_simple(&args[0]) {
80         if let None = constant_simple(&args[1]) {
81             // otherwise ignore
82             Some((m, c, &args[1]))
83         } else {
84             None
85         }
86     } else if let Some(c) = constant_simple(&args[1]) {
87         Some((m, c, &args[0]))
88     } else {
89         None
90     }
91 }