1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use rustc::hir::*;
use rustc::lint::*;
use crate::utils::{self, paths, span_lint, in_external_macro};
/// **What it does:**
/// Checks for the usage of negated comparision operators on types which only implement
/// `PartialOrd` (e.g. `f64`).
///
/// **Why is this bad?**
/// These operators make it easy to forget that the underlying types actually allow not only three
/// potential Orderings (Less, Equal, Greater) but also a forth one (Uncomparable). Escpeccially if
/// the operator based comparision result is negated it is easy to miss that fact.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// use core::cmp::Ordering;
///
/// // Bad
/// let a = 1.0;
/// let b = std::f64::NAN;
///
/// let _not_less_or_equal = !(a <= b);
///
/// // Good
/// let a = 1.0;
/// let b = std::f64::NAN;
///
/// let _not_less_or_equal = match a.partial_cmp(&b) {
/// None | Some(Ordering::Greater) => true,
/// _ => false,
/// };
/// ```
declare_clippy_lint! {
pub NEG_CMP_OP_ON_PARTIAL_ORD,
complexity,
"The use of negated comparision operators on partially orded types may produce confusing code."
}
pub struct NoNegCompOpForPartialOrd;
impl LintPass for NoNegCompOpForPartialOrd {
fn get_lints(&self) -> LintArray {
lint_array!(NEG_CMP_OP_ON_PARTIAL_ORD)
}
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NoNegCompOpForPartialOrd {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
if_chain! {
if !in_external_macro(cx, expr.span);
if let Expr_::ExprUnary(UnOp::UnNot, ref inner) = expr.node;
if let Expr_::ExprBinary(ref op, ref left, _) = inner.node;
if let BinOp_::BiLe | BinOp_::BiGe | BinOp_::BiLt | BinOp_::BiGt = op.node;
then {
let ty = cx.tables.expr_ty(left);
let implements_ord = {
if let Some(id) = utils::get_trait_def_id(cx, &paths::ORD) {
utils::implements_trait(cx, ty, id, &[])
} else {
return;
}
};
let implements_partial_ord = {
if let Some(id) = utils::get_trait_def_id(cx, &paths::PARTIAL_ORD) {
utils::implements_trait(cx, ty, id, &[])
} else {
return;
}
};
if implements_partial_ord && !implements_ord {
span_lint(
cx,
NEG_CMP_OP_ON_PARTIAL_ORD,
expr.span,
"The use of negated comparision operators on partially orded \
types produces code that is hard to read and refactor. Please \
consider to use the `partial_cmp` instead, to make it clear \
that the two values could be incomparable."
)
}
}
}
}
}