1 use clippy_utils
::consts
::{constant_simple, Constant}
;
2 use clippy_utils
::diagnostics
::span_lint
;
3 use clippy_utils
::{match_def_path, 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}
;
8 use std
::cmp
::Ordering
;
10 declare_clippy_lint
! {
12 /// Checks for expressions where `std::cmp::min` and `max` are
13 /// used to clamp values, but switched so that the result is constant.
15 /// ### Why is this bad?
16 /// This is in all probability not the intended outcome. At
17 /// the least it hurts readability of the code.
21 /// min(0, max(100, x))
27 /// It will always be equal to `0`. Probably the author meant to clamp the value
28 /// between 0 and 100, but has erroneously swapped `min` and `max`.
29 #[clippy::version = "pre 1.29.0"]
32 "`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant"
35 declare_lint_pass
!(MinMaxPass
=> [MIN_MAX
]);
37 impl<'tcx
> LateLintPass
<'tcx
> for MinMaxPass
{
38 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) {
39 if let Some((outer_max
, outer_c
, oe
)) = min_max(cx
, expr
) {
40 if let Some((inner_max
, inner_c
, ie
)) = min_max(cx
, oe
) {
41 if outer_max
== inner_max
{
46 Constant
::partial_cmp(cx
.tcx
, cx
.typeck_results().expr_ty(ie
), &outer_c
, &inner_c
),
48 (_
, None
) | (MinMax
::Max
, Some(Ordering
::Less
)) | (MinMax
::Min
, Some(Ordering
::Greater
)) => (),
54 "this `min`/`max` combination leads to constant result",
63 #[derive(PartialEq, Eq, Debug, Clone, Copy)]
69 fn min_max
<'a
>(cx
: &LateContext
<'_
>, expr
: &'a Expr
<'a
>) -> Option
<(MinMax
, Constant
, &'a Expr
<'a
>)> {
71 ExprKind
::Call(path
, args
) => {
72 if let ExprKind
::Path(ref qpath
) = path
.kind
{
74 .qpath_res(qpath
, path
.hir_id
)
77 if match_def_path(cx
, def_id
, &paths
::CMP_MIN
) {
78 fetch_const(cx
, args
, MinMax
::Min
)
79 } else if match_def_path(cx
, def_id
, &paths
::CMP_MAX
) {
80 fetch_const(cx
, args
, MinMax
::Max
)
89 ExprKind
::MethodCall(path
, args
, _
) => {
91 if let [obj
, _
] = args
;
92 if cx
.typeck_results().expr_ty(obj
).is_floating_point() || match_trait_method(cx
, expr
, &paths
::ORD
);
94 if path
.ident
.name
== sym
!(max
) {
95 fetch_const(cx
, args
, MinMax
::Max
)
96 } else if path
.ident
.name
== sym
!(min
) {
97 fetch_const(cx
, args
, MinMax
::Min
)
110 fn fetch_const
<'a
>(cx
: &LateContext
<'_
>, args
: &'a
[Expr
<'a
>], m
: MinMax
) -> Option
<(MinMax
, Constant
, &'a Expr
<'a
>)> {
114 constant_simple(cx
, cx
.typeck_results(), &args
[0]).map_or_else(
115 || constant_simple(cx
, cx
.typeck_results(), &args
[1]).map(|c
| (m
, c
, &args
[0])),
117 if constant_simple(cx
, cx
.typeck_results(), &args
[1]).is_none() {
119 Some((m
, c
, &args
[1]))