1 use clippy_utils
::diagnostics
::span_lint_and_help
;
2 use clippy_utils
::ty
::implements_trait
;
3 use clippy_utils
::{get_trait_def_id, if_sequence, in_constant, is_else_clause, paths, SpanlessEq}
;
4 use rustc_hir
::{BinOpKind, Expr, ExprKind}
;
5 use rustc_lint
::{LateContext, LateLintPass}
;
6 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
10 /// Checks comparison chains written with `if` that can be
11 /// rewritten with `match` and `cmp`.
13 /// ### Why is this bad?
14 /// `if` is not guaranteed to be exhaustive and conditionals can get
17 /// ### Known problems
18 /// The match statement may be slower due to the compiler
19 /// not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354)
26 /// fn f(x: u8, y: u8) {
39 /// use std::cmp::Ordering;
43 /// fn f(x: u8, y: u8) {
45 /// Ordering::Greater => a(),
46 /// Ordering::Less => b(),
47 /// Ordering::Equal => c()
51 #[clippy::version = "1.40.0"]
54 "`if`s that can be rewritten with `match` and `cmp`"
57 declare_lint_pass
!(ComparisonChain
=> [COMPARISON_CHAIN
]);
59 impl<'tcx
> LateLintPass
<'tcx
> for ComparisonChain
{
60 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) {
61 if expr
.span
.from_expansion() {
65 // We only care about the top-most `if` in the chain
66 if is_else_clause(cx
.tcx
, expr
) {
70 if in_constant(cx
, expr
.hir_id
) {
74 // Check that there exists at least one explicit else condition
75 let (conds
, _
) = if_sequence(expr
);
80 for cond
in conds
.windows(2) {
81 if let (&ExprKind
::Binary(ref kind1
, lhs1
, rhs1
), &ExprKind
::Binary(ref kind2
, lhs2
, rhs2
)) =
82 (&cond
[0].kind
, &cond
[1].kind
)
84 if !kind_is_cmp(kind1
.node
) || !kind_is_cmp(kind2
.node
) {
88 // Check that both sets of operands are equal
89 let mut spanless_eq
= SpanlessEq
::new(cx
);
90 let same_fixed_operands
= spanless_eq
.eq_expr(lhs1
, lhs2
) && spanless_eq
.eq_expr(rhs1
, rhs2
);
91 let same_transposed_operands
= spanless_eq
.eq_expr(lhs1
, rhs2
) && spanless_eq
.eq_expr(rhs1
, lhs2
);
93 if !same_fixed_operands
&& !same_transposed_operands
{
97 // Check that if the operation is the same, either it's not `==` or the operands are transposed
98 if kind1
.node
== kind2
.node
{
99 if kind1
.node
== BinOpKind
::Eq
{
102 if !same_transposed_operands
{
107 // Check that the type being compared implements `core::cmp::Ord`
108 let ty
= cx
.typeck_results().expr_ty(lhs1
);
109 let is_ord
= get_trait_def_id(cx
, &paths
::ORD
).map_or(false, |id
| implements_trait(cx
, ty
, id
, &[]));
115 // We only care about comparison chains
123 "`if` chain can be rewritten with `match`",
125 "consider rewriting the `if` chain to use `cmp` and `match`",
130 fn kind_is_cmp(kind
: BinOpKind
) -> bool
{
131 matches
!(kind
, BinOpKind
::Lt
| BinOpKind
::Gt
| BinOpKind
::Eq
)