summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/borrow_as_ptr.rs
blob: 0993adbae2e6bfdb37c27b85335549c83c773dc9 (plain)
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
96
97
98
99
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_no_std_crate;
use clippy_utils::source::snippet_opt;
use clippy_utils::{meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};

declare_clippy_lint! {
    /// ### What it does
    /// Checks for the usage of `&expr as *const T` or
    /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
    /// `ptr::addr_of_mut` instead.
    ///
    /// ### Why is this bad?
    /// This would improve readability and avoid creating a reference
    /// that points to an uninitialized value or unaligned place.
    /// Read the `ptr::addr_of` docs for more information.
    ///
    /// ### Example
    /// ```rust
    /// let val = 1;
    /// let p = &val as *const i32;
    ///
    /// let mut val_mut = 1;
    /// let p_mut = &mut val_mut as *mut i32;
    /// ```
    /// Use instead:
    /// ```rust
    /// let val = 1;
    /// let p = std::ptr::addr_of!(val);
    ///
    /// let mut val_mut = 1;
    /// let p_mut = std::ptr::addr_of_mut!(val_mut);
    /// ```
    #[clippy::version = "1.60.0"]
    pub BORROW_AS_PTR,
    pedantic,
    "borrowing just to cast to a raw pointer"
}

impl_lint_pass!(BorrowAsPtr => [BORROW_AS_PTR]);

pub struct BorrowAsPtr {
    msrv: Option<RustcVersion>,
}

impl BorrowAsPtr {
    #[must_use]
    pub fn new(msrv: Option<RustcVersion>) -> Self {
        Self { msrv }
    }
}

impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if !meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
            return;
        }

        if expr.span.from_expansion() {
            return;
        }

        if_chain! {
            if let ExprKind::Cast(left_expr, ty) = &expr.kind;
            if let TyKind::Ptr(_) = ty.kind;
            if let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = &left_expr.kind;

            then {
                let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
                let macro_name = match mutability {
                    Mutability::Not => "addr_of",
                    Mutability::Mut => "addr_of_mut",
                };

                span_lint_and_sugg(
                    cx,
                    BORROW_AS_PTR,
                    expr.span,
                    "borrow as raw pointer",
                    "try",
                    format!(
                        "{}::ptr::{}!({})",
                        core_or_std,
                        macro_name,
                        snippet_opt(cx, e.span).unwrap()
                    ),
                    Applicability::MachineApplicable,
                );
            }
        }
    }

    extract_msrv_attr!(LateContext);
}