]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
Auto merge of #107843 - bjorn3:sync_cg_clif-2023-02-09, r=bjorn3
[rust.git] / src / tools / clippy / clippy_lints / src / functions / not_unsafe_ptr_arg_deref.rs
1 use rustc_hir::{self as hir, intravisit, HirIdSet};
2 use rustc_lint::LateContext;
3 use rustc_middle::ty;
4 use rustc_span::def_id::LocalDefId;
5
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};
10
11 use core::ops::ControlFlow;
12
13 use super::NOT_UNSAFE_PTR_ARG_DEREF;
14
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>,
20     def_id: LocalDefId,
21 ) {
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,
26     };
27
28     check_raw_ptr(cx, unsafety, decl, body, def_id)
29 }
30
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);
35     }
36 }
37
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>,
43     def_id: LocalDefId,
44 ) {
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>();
49
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| {
53                 match e.kind {
54                     hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => {
55                         for arg in args {
56                             check_arg(cx, &raw_ptrs, arg);
57                         }
58                     },
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);
63                             for arg in args {
64                                 check_arg(cx, &raw_ptrs, arg);
65                             }
66                         }
67                     },
68                     hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => check_arg(cx, &raw_ptrs, ptr),
69                     _ => (),
70                 }
71                 ControlFlow::Continue(())
72             });
73         }
74     }
75 }
76
77 fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option<hir::HirId> {
78     if let (&hir::PatKind::Binding(_, id, _, _), Some(&ty::RawPtr(_))) = (
79         &arg.pat.kind,
80         cx.maybe_typeck_results()
81             .map(|typeck_results| typeck_results.pat_ty(arg.pat).kind()),
82     ) {
83         Some(id)
84     } else {
85         None
86     }
87 }
88
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)) {
91         span_lint(
92             cx,
93             NOT_UNSAFE_PTR_ARG_DEREF,
94             arg.span,
95             "this public function might dereference a raw pointer but is not marked `unsafe`",
96         );
97     }
98 }