diff options
| author | Philipp Krones <hello@philkrones.com> | 2022-11-21 20:34:47 +0100 |
|---|---|---|
| committer | Philipp Krones <hello@philkrones.com> | 2022-11-21 20:51:52 +0100 |
| commit | 46c5a5d234f13dcf4bb4cf4241b2addedbf0be14 (patch) | |
| tree | 56726625e55224ecb09ed11f509a964507b9c333 /clippy_lints/src/from_raw_with_void_ptr.rs | |
| parent | 3597ed5a099488aa77caf444106a0550b7e5d2e8 (diff) | |
| download | rust-46c5a5d234f13dcf4bb4cf4241b2addedbf0be14.tar.gz rust-46c5a5d234f13dcf4bb4cf4241b2addedbf0be14.zip | |
Merge commit 'f4850f7292efa33759b4f7f9b7621268979e9914' into clippyup
Diffstat (limited to 'clippy_lints/src/from_raw_with_void_ptr.rs')
| -rw-r--r-- | clippy_lints/src/from_raw_with_void_ptr.rs | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/clippy_lints/src/from_raw_with_void_ptr.rs b/clippy_lints/src/from_raw_with_void_ptr.rs new file mode 100644 index 00000000000..00f5ba56496 --- /dev/null +++ b/clippy_lints/src/from_raw_with_void_ptr.rs @@ -0,0 +1,77 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_c_void; +use clippy_utils::{match_def_path, path_def_id, paths}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::RawPtr; +use rustc_middle::ty::TypeAndMut; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks if we're passing a `c_void` raw pointer to `{Box,Rc,Arc,Weak}::from_raw(_)` + /// + /// ### Why is this bad? + /// 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. + /// 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`). + /// 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. + /// + /// ### Example + /// ```rust + /// # use std::ffi::c_void; + /// let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void; + /// let _ = unsafe { Box::from_raw(ptr) }; + /// ``` + /// Use instead: + /// ```rust + /// # use std::ffi::c_void; + /// # let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void; + /// let _ = unsafe { Box::from_raw(ptr as *mut usize) }; + /// ``` + /// + #[clippy::version = "1.66.0"] + pub FROM_RAW_WITH_VOID_PTR, + suspicious, + "creating a `Box` from a void raw pointer" +} +declare_lint_pass!(FromRawWithVoidPtr => [FROM_RAW_WITH_VOID_PTR]); + +impl LateLintPass<'_> for FromRawWithVoidPtr { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Call(box_from_raw, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind + && seg.ident.name == sym!(from_raw) + && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id)) + && let arg_kind = cx.typeck_results().expr_ty(arg).kind() + && let RawPtr(TypeAndMut { ty, .. }) = arg_kind + && is_c_void(cx, *ty) { + let msg = format!("creating a `{type_str}` from a void raw pointer"); + 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"); + } + } +} + +/// Checks whether a `DefId` matches `Box`, `Rc`, `Arc`, or one of the `Weak` types. +/// Returns a static string slice with the name of the type, if one was found. +fn def_id_matches_type(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> { + // Box + if Some(def_id) == cx.tcx.lang_items().owned_box() { + return Some("Box"); + } + + if let Some(symbol) = cx.tcx.get_diagnostic_name(def_id) { + if symbol == sym::Arc { + return Some("Arc"); + } else if symbol == sym::Rc { + return Some("Rc"); + } + } + + if match_def_path(cx, def_id, &paths::WEAK_RC) || match_def_path(cx, def_id, &paths::WEAK_ARC) { + Some("Weak") + } else { + None + } +} |
