]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
New upstream version 1.76.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / partialeq_to_none.rs
1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::ty::is_type_diagnostic_item;
3 use clippy_utils::{is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg};
4 use rustc_errors::Applicability;
5 use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem};
6 use rustc_lint::{LateContext, LateLintPass};
7 use rustc_session::declare_lint_pass;
8 use rustc_span::sym;
9
10 declare_clippy_lint! {
11 /// ### What it does
12 ///
13 /// Checks for binary comparisons to a literal `Option::None`.
14 ///
15 /// ### Why is this bad?
16 ///
17 /// A programmer checking if some `foo` is `None` via a comparison `foo == None`
18 /// is usually inspired from other programming languages (e.g. `foo is None`
19 /// in Python).
20 /// Checking if a value of type `Option<T>` is (not) equal to `None` in that
21 /// way relies on `T: PartialEq` to do the comparison, which is unneeded.
22 ///
23 /// ### Example
24 /// ```no_run
25 /// fn foo(f: Option<u32>) -> &'static str {
26 /// if f != None { "yay" } else { "nay" }
27 /// }
28 /// ```
29 /// Use instead:
30 /// ```no_run
31 /// fn foo(f: Option<u32>) -> &'static str {
32 /// if f.is_some() { "yay" } else { "nay" }
33 /// }
34 /// ```
35 #[clippy::version = "1.65.0"]
36 pub PARTIALEQ_TO_NONE,
37 style,
38 "Binary comparison to `Option<T>::None` relies on `T: PartialEq`, which is unneeded"
39 }
40 declare_lint_pass!(PartialeqToNone => [PARTIALEQ_TO_NONE]);
41
42 impl<'tcx> LateLintPass<'tcx> for PartialeqToNone {
43 fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
44 // Skip expanded code, as we have no control over it anyway...
45 if e.span.from_expansion() {
46 return;
47 }
48
49 // If the expression is of type `Option`
50 let is_ty_option =
51 |expr: &Expr<'_>| is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr).peel_refs(), sym::Option);
52
53 // If the expression is a literal `Option::None`
54 let is_none_ctor = |expr: &Expr<'_>| {
55 !expr.span.from_expansion()
56 && is_res_lang_ctor(cx, path_res(cx, peel_hir_expr_refs(expr).0), LangItem::OptionNone)
57 };
58
59 let mut applicability = Applicability::MachineApplicable;
60
61 if let ExprKind::Binary(op, left_side, right_side) = e.kind {
62 // All other comparisons (e.g. `>= None`) have special meaning wrt T
63 let is_eq = match op.node {
64 BinOpKind::Eq => true,
65 BinOpKind::Ne => false,
66 _ => return,
67 };
68
69 // We are only interested in comparisons between `Option` and a literal `Option::None`
70 let scrutinee = match (
71 is_none_ctor(left_side) && is_ty_option(right_side),
72 is_none_ctor(right_side) && is_ty_option(left_side),
73 ) {
74 (true, false) => right_side,
75 (false, true) => left_side,
76 _ => return,
77 };
78
79 // Peel away refs/derefs (as long as we don't cross manual deref impls), as
80 // autoref/autoderef will take care of those
81 let sugg = format!(
82 "{}.{}",
83 sugg::Sugg::hir_with_applicability(cx, peel_ref_operators(cx, scrutinee), "..", &mut applicability)
84 .maybe_par(),
85 if is_eq { "is_none()" } else { "is_some()" }
86 );
87
88 span_lint_and_sugg(
89 cx,
90 PARTIALEQ_TO_NONE,
91 e.span,
92 "binary comparison to literal `Option::None`",
93 if is_eq {
94 "use `Option::is_none()` instead"
95 } else {
96 "use `Option::is_some()` instead"
97 },
98 sugg,
99 applicability,
100 );
101 }
102 }
103 }