]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs
Rollup merge of #104901 - krtab:filetype_compare, r=the8472
[rust.git] / src / tools / clippy / clippy_lints / src / from_raw_with_void_ptr.rs
1 use clippy_utils::diagnostics::span_lint_and_help;
2 use clippy_utils::ty::is_c_void;
3 use clippy_utils::{match_def_path, path_def_id, paths};
4 use rustc_hir::def_id::DefId;
5 use rustc_hir::{Expr, ExprKind, QPath};
6 use rustc_lint::{LateContext, LateLintPass};
7 use rustc_middle::ty::RawPtr;
8 use rustc_middle::ty::TypeAndMut;
9 use rustc_session::{declare_lint_pass, declare_tool_lint};
10 use rustc_span::sym;
11
12 declare_clippy_lint! {
13     /// ### What it does
14     /// Checks if we're passing a `c_void` raw pointer to `{Box,Rc,Arc,Weak}::from_raw(_)`
15     ///
16     /// ### Why is this bad?
17     /// When dealing with `c_void` raw pointers in FFI, it is easy to run into the pitfall of calling `from_raw` with the `c_void` pointer.
18     /// The type signature of `Box::from_raw` is `fn from_raw(raw: *mut T) -> Box<T>`, so if you pass a `*mut c_void` you will get a `Box<c_void>` (and similarly for `Rc`, `Arc` and `Weak`).
19     /// For this to be safe, `c_void` would need to have the same memory layout as the original type, which is often not the case.
20     ///
21     /// ### Example
22     /// ```rust
23     /// # use std::ffi::c_void;
24     /// let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void;
25     /// let _ = unsafe { Box::from_raw(ptr) };
26     /// ```
27     /// Use instead:
28     /// ```rust
29     /// # use std::ffi::c_void;
30     /// # let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void;
31     /// let _ = unsafe { Box::from_raw(ptr as *mut usize) };
32     /// ```
33     ///
34     #[clippy::version = "1.66.0"]
35     pub FROM_RAW_WITH_VOID_PTR,
36     suspicious,
37     "creating a `Box` from a void raw pointer"
38 }
39 declare_lint_pass!(FromRawWithVoidPtr => [FROM_RAW_WITH_VOID_PTR]);
40
41 impl LateLintPass<'_> for FromRawWithVoidPtr {
42     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
43         if let ExprKind::Call(box_from_raw, [arg]) = expr.kind
44         && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind
45         && seg.ident.name == sym!(from_raw)
46         && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id))
47         && let arg_kind = cx.typeck_results().expr_ty(arg).kind()
48         && let RawPtr(TypeAndMut { ty, .. }) = arg_kind
49         && is_c_void(cx, *ty) {
50             let msg = format!("creating a `{type_str}` from a void raw pointer");
51             span_lint_and_help(cx, FROM_RAW_WITH_VOID_PTR, expr.span, &msg, Some(arg.span), "cast this to a pointer of the appropriate type");
52         }
53     }
54 }
55
56 /// Checks whether a `DefId` matches `Box`, `Rc`, `Arc`, or one of the `Weak` types.
57 /// Returns a static string slice with the name of the type, if one was found.
58 fn def_id_matches_type(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> {
59     // Box
60     if Some(def_id) == cx.tcx.lang_items().owned_box() {
61         return Some("Box");
62     }
63
64     if let Some(symbol) = cx.tcx.get_diagnostic_name(def_id) {
65         if symbol == sym::Arc {
66             return Some("Arc");
67         } else if symbol == sym::Rc {
68             return Some("Rc");
69         }
70     }
71
72     if match_def_path(cx, def_id, &paths::WEAK_RC) || match_def_path(cx, def_id, &paths::WEAK_ARC) {
73         Some("Weak")
74     } else {
75         None
76     }
77 }