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