]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/ptr_eq.rs
Auto merge of #71780 - jcotton42:string_remove_matches, r=joshtriplett
[rust.git] / src / tools / clippy / clippy_lints / src / ptr_eq.rs
1 use crate::utils;
2 use if_chain::if_chain;
3 use rustc_errors::Applicability;
4 use rustc_hir::{BinOpKind, Expr, ExprKind};
5 use rustc_lint::{LateContext, LateLintPass};
6 use rustc_session::{declare_lint_pass, declare_tool_lint};
7
8 declare_clippy_lint! {
9     /// **What it does:** Use `std::ptr::eq` when applicable
10     ///
11     /// **Why is this bad?** `ptr::eq` can be used to compare `&T` references
12     /// (which coerce to `*const T` implicitly) by their address rather than
13     /// comparing the values they point to.
14     ///
15     /// **Known problems:** None.
16     ///
17     /// **Example:**
18     ///
19     /// ```rust
20     /// let a = &[1, 2, 3];
21     /// let b = &[1, 2, 3];
22     ///
23     /// assert!(a as *const _ as usize == b as *const _ as usize);
24     /// ```
25     /// Use instead:
26     /// ```rust
27     /// let a = &[1, 2, 3];
28     /// let b = &[1, 2, 3];
29     ///
30     /// assert!(std::ptr::eq(a, b));
31     /// ```
32     pub PTR_EQ,
33     style,
34     "use `std::ptr::eq` when comparing raw pointers"
35 }
36
37 declare_lint_pass!(PtrEq => [PTR_EQ]);
38
39 static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers";
40
41 impl LateLintPass<'_> for PtrEq {
42     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
43         if utils::in_macro(expr.span) {
44             return;
45         }
46
47         if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind {
48             if BinOpKind::Eq == op.node {
49                 let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
50                     (Some(lhs), Some(rhs)) => (lhs, rhs),
51                     _ => (&**left, &**right),
52                 };
53
54                 if_chain! {
55                     if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left);
56                     if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right);
57                     if let Some(left_snip) = utils::snippet_opt(cx, left_var.span);
58                     if let Some(right_snip) = utils::snippet_opt(cx, right_var.span);
59                     then {
60                         utils::span_lint_and_sugg(
61                             cx,
62                             PTR_EQ,
63                             expr.span,
64                             LINT_MSG,
65                             "try",
66                             format!("std::ptr::eq({}, {})", left_snip, right_snip),
67                             Applicability::MachineApplicable,
68                             );
69                     }
70                 }
71             }
72         }
73     }
74 }
75
76 // If the given expression is a cast to an usize, return the lhs of the cast
77 // E.g., `foo as *const _ as usize` returns `foo as *const _`.
78 fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
79     if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize {
80         if let ExprKind::Cast(ref expr, _) = cast_expr.kind {
81             return Some(expr);
82         }
83     }
84     None
85 }
86
87 // If the given expression is a cast to a `*const` pointer, return the lhs of the cast
88 // E.g., `foo as *const _` returns `foo`.
89 fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
90     if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() {
91         if let ExprKind::Cast(ref expr, _) = cast_expr.kind {
92             return Some(expr);
93         }
94     }
95     None
96 }