]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/swap_ptr_to_ref.rs
Use `CountIsStart` in clippy
[rust.git] / clippy_lints / src / swap_ptr_to_ref.rs
1 use clippy_utils::diagnostics::span_lint_and_then;
2 use clippy_utils::source::snippet_with_context;
3 use clippy_utils::{match_def_path, path_def_id, paths};
4 use rustc_errors::Applicability;
5 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, UnOp};
6 use rustc_lint::{LateContext, LateLintPass};
7 use rustc_session::{declare_lint_pass, declare_tool_lint};
8 use rustc_span::{Span, SyntaxContext};
9
10 declare_clippy_lint! {
11     /// ### What it does
12     /// Checks for calls to `core::mem::swap` where either parameter is derived from a pointer
13     ///
14     /// ### Why is this bad?
15     /// When at least one parameter to `swap` is derived from a pointer it may overlap with the
16     /// other. This would then lead to undefined behavior.
17     ///
18     /// ### Example
19     /// ```rust
20     /// unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) {
21     ///     for (&x, &y) in x.iter().zip(y) {
22     ///         core::mem::swap(&mut *x, &mut *y);
23     ///     }
24     /// }
25     /// ```
26     /// Use instead:
27     /// ```rust
28     /// unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) {
29     ///     for (&x, &y) in x.iter().zip(y) {
30     ///         core::ptr::swap(x, y);
31     ///     }
32     /// }
33     /// ```
34     #[clippy::version = "1.63.0"]
35     pub SWAP_PTR_TO_REF,
36     suspicious,
37     "call to `mem::swap` using pointer derived references"
38 }
39 declare_lint_pass!(SwapPtrToRef => [SWAP_PTR_TO_REF]);
40
41 impl LateLintPass<'_> for SwapPtrToRef {
42     fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
43         if let ExprKind::Call(fn_expr, [arg1, arg2]) = e.kind
44             && let Some(fn_id) = path_def_id(cx, fn_expr)
45             && match_def_path(cx, fn_id, &paths::MEM_SWAP)
46             && let ctxt = e.span.ctxt()
47             && let (from_ptr1, arg1_span) = is_ptr_to_ref(cx, arg1, ctxt)
48             && let (from_ptr2, arg2_span) = is_ptr_to_ref(cx, arg2, ctxt)
49             && (from_ptr1 || from_ptr2)
50         {
51             span_lint_and_then(
52                 cx,
53                 SWAP_PTR_TO_REF,
54                 e.span,
55                 "call to `core::mem::swap` with a parameter derived from a raw pointer",
56                 |diag| {
57                     if !((from_ptr1 && arg1_span.is_none()) || (from_ptr2 && arg2_span.is_none())) {
58                         let mut app = Applicability::MachineApplicable;
59                         let snip1 = snippet_with_context(cx, arg1_span.unwrap_or(arg1.span), ctxt, "..", &mut app).0;
60                         let snip2 = snippet_with_context(cx, arg2_span.unwrap_or(arg2.span), ctxt, "..", &mut app).0;
61                         diag.span_suggestion(e.span, "use ptr::swap", format!("core::ptr::swap({}, {})", snip1, snip2), app);
62                     }
63                 }
64             );
65         }
66     }
67 }
68
69 /// Checks if the expression converts a mutable pointer to a mutable reference. If it is, also
70 /// returns the span of the pointer expression if it's suitable for making a suggestion.
71 fn is_ptr_to_ref(cx: &LateContext<'_>, e: &Expr<'_>, ctxt: SyntaxContext) -> (bool, Option<Span>) {
72     if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, borrowed_expr) = e.kind
73         && let ExprKind::Unary(UnOp::Deref, derefed_expr) = borrowed_expr.kind
74         && cx.typeck_results().expr_ty(derefed_expr).is_unsafe_ptr()
75     {
76         (true, (borrowed_expr.span.ctxt() == ctxt || derefed_expr.span.ctxt() == ctxt).then_some(derefed_expr.span))
77     } else {
78         (false, None)
79     }
80 }