]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs
Rollup merge of #103525 - oli-obk:const_impl_on_non_const_trait, r=lcnr
[rust.git] / src / tools / clippy / clippy_lints / src / operators / absurd_extreme_comparisons.rs
1 use rustc_hir::{BinOpKind, Expr, ExprKind};
2 use rustc_lint::LateContext;
3 use rustc_middle::ty;
4
5 use clippy_utils::comparisons::{normalize_comparison, Rel};
6 use clippy_utils::consts::{constant, Constant};
7 use clippy_utils::diagnostics::span_lint_and_help;
8 use clippy_utils::source::snippet;
9 use clippy_utils::ty::is_isize_or_usize;
10 use clippy_utils::{clip, int_bits, unsext};
11
12 use super::ABSURD_EXTREME_COMPARISONS;
13
14 pub(super) fn check<'tcx>(
15     cx: &LateContext<'tcx>,
16     expr: &'tcx Expr<'_>,
17     op: BinOpKind,
18     lhs: &'tcx Expr<'_>,
19     rhs: &'tcx Expr<'_>,
20 ) {
21     if let Some((culprit, result)) = detect_absurd_comparison(cx, op, lhs, rhs) {
22         let msg = "this comparison involving the minimum or maximum element for this \
23                            type contains a case that is always true or always false";
24
25         let conclusion = match result {
26             AbsurdComparisonResult::AlwaysFalse => "this comparison is always false".to_owned(),
27             AbsurdComparisonResult::AlwaysTrue => "this comparison is always true".to_owned(),
28             AbsurdComparisonResult::InequalityImpossible => format!(
29                 "the case where the two sides are not equal never occurs, consider using `{} == {}` \
30                          instead",
31                 snippet(cx, lhs.span, "lhs"),
32                 snippet(cx, rhs.span, "rhs")
33             ),
34         };
35
36         let help = format!(
37             "because `{}` is the {} value for this type, {conclusion}",
38             snippet(cx, culprit.expr.span, "x"),
39             match culprit.which {
40                 ExtremeType::Minimum => "minimum",
41                 ExtremeType::Maximum => "maximum",
42             }
43         );
44
45         span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help);
46     }
47 }
48
49 enum ExtremeType {
50     Minimum,
51     Maximum,
52 }
53
54 struct ExtremeExpr<'a> {
55     which: ExtremeType,
56     expr: &'a Expr<'a>,
57 }
58
59 enum AbsurdComparisonResult {
60     AlwaysFalse,
61     AlwaysTrue,
62     InequalityImpossible,
63 }
64
65 fn is_cast_between_fixed_and_target<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
66     if let ExprKind::Cast(cast_exp, _) = expr.kind {
67         let precast_ty = cx.typeck_results().expr_ty(cast_exp);
68         let cast_ty = cx.typeck_results().expr_ty(expr);
69
70         return is_isize_or_usize(precast_ty) != is_isize_or_usize(cast_ty);
71     }
72
73     false
74 }
75
76 fn detect_absurd_comparison<'tcx>(
77     cx: &LateContext<'tcx>,
78     op: BinOpKind,
79     lhs: &'tcx Expr<'_>,
80     rhs: &'tcx Expr<'_>,
81 ) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> {
82     use AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible};
83     use ExtremeType::{Maximum, Minimum};
84     // absurd comparison only makes sense on primitive types
85     // primitive types don't implement comparison operators with each other
86     if cx.typeck_results().expr_ty(lhs) != cx.typeck_results().expr_ty(rhs) {
87         return None;
88     }
89
90     // comparisons between fix sized types and target sized types are considered unanalyzable
91     if is_cast_between_fixed_and_target(cx, lhs) || is_cast_between_fixed_and_target(cx, rhs) {
92         return None;
93     }
94
95     let (rel, normalized_lhs, normalized_rhs) = normalize_comparison(op, lhs, rhs)?;
96
97     let lx = detect_extreme_expr(cx, normalized_lhs);
98     let rx = detect_extreme_expr(cx, normalized_rhs);
99
100     Some(match rel {
101         Rel::Lt => {
102             match (lx, rx) {
103                 (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x
104                 (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min
105                 _ => return None,
106             }
107         },
108         Rel::Le => {
109             match (lx, rx) {
110                 (Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x
111                 (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), // max <= x
112                 (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min
113                 (_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max
114                 _ => return None,
115             }
116         },
117         Rel::Ne | Rel::Eq => return None,
118     })
119 }
120
121 fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> {
122     let ty = cx.typeck_results().expr_ty(expr);
123
124     let cv = constant(cx, cx.typeck_results(), expr)?.0;
125
126     let which = match (ty.kind(), cv) {
127         (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum,
128         (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => {
129             ExtremeType::Minimum
130         },
131
132         (&ty::Bool, Constant::Bool(true)) => ExtremeType::Maximum,
133         (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MAX >> (128 - int_bits(cx.tcx, ity)), ity) => {
134             ExtremeType::Maximum
135         },
136         (&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::MAX, uty) == i => ExtremeType::Maximum,
137
138         _ => return None,
139     };
140     Some(ExtremeExpr { which, expr })
141 }