]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
Auto merge of #8231 - Jarcho:implicit_clone_8227, r=camsteffen
[rust.git] / 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::{hir::map::Map, 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::{iter_input_pats, path_to_local};
9
10 use super::NOT_UNSAFE_PTR_ARG_DEREF;
11
12 pub(super) fn check_fn<'tcx>(
13     cx: &LateContext<'tcx>,
14     kind: intravisit::FnKind<'tcx>,
15     decl: &'tcx hir::FnDecl<'tcx>,
16     body: &'tcx hir::Body<'tcx>,
17     hir_id: hir::HirId,
18 ) {
19     let unsafety = match kind {
20         intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _) => unsafety,
21         intravisit::FnKind::Method(_, sig, _) => sig.header.unsafety,
22         intravisit::FnKind::Closure => return,
23     };
24
25     check_raw_ptr(cx, unsafety, decl, body, cx.tcx.hir().local_def_id(hir_id));
26 }
27
28 pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
29     if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind {
30         let body = cx.tcx.hir().body(eid);
31         check_raw_ptr(cx, sig.header.unsafety, sig.decl, body, item.def_id);
32     }
33 }
34
35 fn check_raw_ptr<'tcx>(
36     cx: &LateContext<'tcx>,
37     unsafety: hir::Unsafety,
38     decl: &'tcx hir::FnDecl<'tcx>,
39     body: &'tcx hir::Body<'tcx>,
40     def_id: LocalDefId,
41 ) {
42     let expr = &body.value;
43     if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(def_id) {
44         let raw_ptrs = iter_input_pats(decl, body)
45             .filter_map(|arg| raw_ptr_arg(cx, arg))
46             .collect::<HirIdSet>();
47
48         if !raw_ptrs.is_empty() {
49             let typeck_results = cx.tcx.typeck_body(body.id());
50             let mut v = DerefVisitor {
51                 cx,
52                 ptrs: raw_ptrs,
53                 typeck_results,
54             };
55
56             intravisit::walk_expr(&mut v, expr);
57         }
58     }
59 }
60
61 fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option<hir::HirId> {
62     if let (&hir::PatKind::Binding(_, id, _, _), Some(&ty::RawPtr(_))) = (
63         &arg.pat.kind,
64         cx.maybe_typeck_results()
65             .map(|typeck_results| typeck_results.pat_ty(arg.pat).kind()),
66     ) {
67         Some(id)
68     } else {
69         None
70     }
71 }
72
73 struct DerefVisitor<'a, 'tcx> {
74     cx: &'a LateContext<'tcx>,
75     ptrs: HirIdSet,
76     typeck_results: &'a ty::TypeckResults<'tcx>,
77 }
78
79 impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
80     type Map = Map<'tcx>;
81
82     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
83         match expr.kind {
84             hir::ExprKind::Call(f, args) => {
85                 let ty = self.typeck_results.expr_ty(f);
86
87                 if type_is_unsafe_function(self.cx, ty) {
88                     for arg in args {
89                         self.check_arg(arg);
90                     }
91                 }
92             },
93             hir::ExprKind::MethodCall(_, _, args, _) => {
94                 let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap();
95                 let base_type = self.cx.tcx.type_of(def_id);
96
97                 if type_is_unsafe_function(self.cx, base_type) {
98                     for arg in args {
99                         self.check_arg(arg);
100                     }
101                 }
102             },
103             hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => self.check_arg(ptr),
104             _ => (),
105         }
106
107         intravisit::walk_expr(self, expr);
108     }
109
110     fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
111         intravisit::NestedVisitorMap::None
112     }
113 }
114
115 impl<'a, 'tcx> DerefVisitor<'a, 'tcx> {
116     fn check_arg(&self, ptr: &hir::Expr<'_>) {
117         if let Some(id) = path_to_local(ptr) {
118             if self.ptrs.contains(&id) {
119                 span_lint(
120                     self.cx,
121                     NOT_UNSAFE_PTR_ARG_DEREF,
122                     ptr.span,
123                     "this public function might dereference a raw pointer but is not marked `unsafe`",
124                 );
125             }
126         }
127     }
128 }