1 use rustc_hir::{self as hir, intravisit, HirIdSet};
2 use rustc_lint::LateContext;
4 use rustc_span::def_id::LocalDefId;
6 use clippy_utils::diagnostics::span_lint;
7 use clippy_utils::ty::type_is_unsafe_function;
8 use clippy_utils::visitors::for_each_expr_with_closures;
9 use clippy_utils::{iter_input_pats, path_to_local};
11 use core::ops::ControlFlow;
13 use super::NOT_UNSAFE_PTR_ARG_DEREF;
15 pub(super) fn check_fn<'tcx>(
16 cx: &LateContext<'tcx>,
17 kind: intravisit::FnKind<'tcx>,
18 decl: &'tcx hir::FnDecl<'tcx>,
19 body: &'tcx hir::Body<'tcx>,
22 let unsafety = match kind {
23 intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }) => unsafety,
24 intravisit::FnKind::Method(_, sig) => sig.header.unsafety,
25 intravisit::FnKind::Closure => return,
28 check_raw_ptr(cx, unsafety, decl, body, def_id)
31 pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
32 if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind {
33 let body = cx.tcx.hir().body(eid);
34 check_raw_ptr(cx, sig.header.unsafety, sig.decl, body, item.owner_id.def_id);
38 fn check_raw_ptr<'tcx>(
39 cx: &LateContext<'tcx>,
40 unsafety: hir::Unsafety,
41 decl: &'tcx hir::FnDecl<'tcx>,
42 body: &'tcx hir::Body<'tcx>,
45 if unsafety == hir::Unsafety::Normal && cx.effective_visibilities.is_exported(def_id) {
46 let raw_ptrs = iter_input_pats(decl, body)
47 .filter_map(|arg| raw_ptr_arg(cx, arg))
48 .collect::<HirIdSet>();
50 if !raw_ptrs.is_empty() {
51 let typeck = cx.tcx.typeck_body(body.id());
52 let _: Option<!> = for_each_expr_with_closures(cx, body.value, |e| {
54 hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => {
56 check_arg(cx, &raw_ptrs, arg);
59 hir::ExprKind::MethodCall(_, recv, args, _) => {
60 let def_id = typeck.type_dependent_def_id(e.hir_id).unwrap();
61 if cx.tcx.fn_sig(def_id).skip_binder().skip_binder().unsafety == hir::Unsafety::Unsafe {
62 check_arg(cx, &raw_ptrs, recv);
64 check_arg(cx, &raw_ptrs, arg);
68 hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => check_arg(cx, &raw_ptrs, ptr),
71 ControlFlow::Continue(())
77 fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option<hir::HirId> {
78 if let (&hir::PatKind::Binding(_, id, _, _), Some(&ty::RawPtr(_))) = (
80 cx.maybe_typeck_results()
81 .map(|typeck_results| typeck_results.pat_ty(arg.pat).kind()),
89 fn check_arg(cx: &LateContext<'_>, raw_ptrs: &HirIdSet, arg: &hir::Expr<'_>) {
90 if path_to_local(arg).map_or(false, |id| raw_ptrs.contains(&id)) {
93 NOT_UNSAFE_PTR_ARG_DEREF,
95 "this public function might dereference a raw pointer but is not marked `unsafe`",