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