1 use clippy_utils
::consts
::{constant_simple, Constant}
;
2 use clippy_utils
::diagnostics
::span_lint
;
3 use clippy_utils
::{match_trait_method, paths}
;
4 use if_chain
::if_chain
;
5 use rustc_hir
::{Expr, ExprKind}
;
6 use rustc_lint
::{LateContext, LateLintPass}
;
7 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
9 use std
::cmp
::Ordering
;
11 declare_clippy_lint
! {
13 /// Checks for expressions where `std::cmp::min` and `max` are
14 /// used to clamp values, but switched so that the result is constant.
16 /// ### Why is this bad?
17 /// This is in all probability not the intended outcome. At
18 /// the least it hurts readability of the code.
22 /// min(0, max(100, x))
28 /// It will always be equal to `0`. Probably the author meant to clamp the value
29 /// between 0 and 100, but has erroneously swapped `min` and `max`.
30 #[clippy::version = "pre 1.29.0"]
33 "`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant"
36 declare_lint_pass
!(MinMaxPass
=> [MIN_MAX
]);
38 impl<'tcx
> LateLintPass
<'tcx
> for MinMaxPass
{
39 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) {
40 if let Some((outer_max
, outer_c
, oe
)) = min_max(cx
, expr
) {
41 if let Some((inner_max
, inner_c
, ie
)) = min_max(cx
, oe
) {
42 if outer_max
== inner_max
{
47 Constant
::partial_cmp(cx
.tcx
, cx
.typeck_results().expr_ty(ie
), &outer_c
, &inner_c
),
49 (_
, None
) | (MinMax
::Max
, Some(Ordering
::Less
)) | (MinMax
::Min
, Some(Ordering
::Greater
)) => (),
55 "this `min`/`max` combination leads to constant result",
64 #[derive(PartialEq, Eq, Debug, Clone, Copy)]
70 fn min_max
<'a
>(cx
: &LateContext
<'_
>, expr
: &'a Expr
<'a
>) -> Option
<(MinMax
, Constant
, &'a Expr
<'a
>)> {
72 ExprKind
::Call(path
, args
) => {
73 if let ExprKind
::Path(ref qpath
) = path
.kind
{
75 .qpath_res(qpath
, path
.hir_id
)
77 .and_then(|def_id
| match cx
.tcx
.get_diagnostic_name(def_id
) {
78 Some(sym
::cmp_min
) => fetch_const(cx
, args
, MinMax
::Min
),
79 Some(sym
::cmp_max
) => fetch_const(cx
, args
, MinMax
::Max
),
86 ExprKind
::MethodCall(path
, args
, _
) => {
88 if let [obj
, _
] = args
;
89 if cx
.typeck_results().expr_ty(obj
).is_floating_point() || match_trait_method(cx
, expr
, &paths
::ORD
);
91 if path
.ident
.name
== sym
!(max
) {
92 fetch_const(cx
, args
, MinMax
::Max
)
93 } else if path
.ident
.name
== sym
!(min
) {
94 fetch_const(cx
, args
, MinMax
::Min
)
107 fn fetch_const
<'a
>(cx
: &LateContext
<'_
>, args
: &'a
[Expr
<'a
>], m
: MinMax
) -> Option
<(MinMax
, Constant
, &'a Expr
<'a
>)> {
111 constant_simple(cx
, cx
.typeck_results(), &args
[0]).map_or_else(
112 || constant_simple(cx
, cx
.typeck_results(), &args
[1]).map(|c
| (m
, c
, &args
[0])),
114 if constant_simple(cx
, cx
.typeck_results(), &args
[1]).is_none() {
116 Some((m
, c
, &args
[1]))