]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/functions.rs
Rustfmt
[rust.git] / clippy_lints / src / functions.rs
1 use rustc::hir::intravisit;
2 use rustc::hir;
3 use rustc::lint::*;
4 use std::collections::HashSet;
5 use syntax::ast;
6 use syntax::abi::Abi;
7 use syntax::codemap::Span;
8 use utils::{span_lint, type_is_unsafe_function, iter_input_pats};
9
10 /// **What it does:** Checks for functions with too many parameters.
11 ///
12 /// **Why is this bad?** Functions with lots of parameters are considered bad
13 /// style and reduce readability (“what does the 5th parameter mean?”). Consider
14 /// grouping some parameters into a new type.
15 ///
16 /// **Known problems:** None.
17 ///
18 /// **Example:**
19 /// ```rust
20 /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b:
21 /// f32) { .. }
22 /// ```
23 declare_lint! {
24     pub TOO_MANY_ARGUMENTS,
25     Warn,
26     "functions with too many arguments"
27 }
28
29 /// **What it does:** Checks for public functions that dereferences raw pointer
30 /// arguments but are not marked unsafe.
31 ///
32 /// **Why is this bad?** The function should probably be marked `unsafe`, since
33 /// for an arbitrary raw pointer, there is no way of telling for sure if it is
34 /// valid.
35 ///
36 /// **Known problems:**
37 ///
38 /// * It does not check functions recursively so if the pointer is passed to a
39 /// private non-`unsafe` function which does the dereferencing, the lint won't
40 /// trigger.
41 /// * It only checks for arguments whose type are raw pointers, not raw pointers
42 /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
43 /// `some_argument.get_raw_ptr()`).
44 ///
45 /// **Example:**
46 /// ```rust
47 /// pub fn foo(x: *const u8) { println!("{}", unsafe { *x }); }
48 /// ```
49 declare_lint! {
50     pub NOT_UNSAFE_PTR_ARG_DEREF,
51     Warn,
52     "public functions dereferencing raw pointer arguments but not marked `unsafe`"
53 }
54
55 #[derive(Copy, Clone)]
56 pub struct Functions {
57     threshold: u64,
58 }
59
60 impl Functions {
61     pub fn new(threshold: u64) -> Functions {
62         Functions { threshold: threshold }
63     }
64 }
65
66 impl LintPass for Functions {
67     fn get_lints(&self) -> LintArray {
68         lint_array!(TOO_MANY_ARGUMENTS, NOT_UNSAFE_PTR_ARG_DEREF)
69     }
70 }
71
72 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions {
73     fn check_fn(
74         &mut self,
75         cx: &LateContext<'a, 'tcx>,
76         kind: intravisit::FnKind<'tcx>,
77         decl: &'tcx hir::FnDecl,
78         body: &'tcx hir::Body,
79         span: Span,
80         nodeid: ast::NodeId,
81     ) {
82         use rustc::hir::map::Node::*;
83
84         let is_impl = if let Some(NodeItem(item)) = cx.tcx.hir.find(cx.tcx.hir.get_parent_node(nodeid)) {
85             matches!(item.node, hir::ItemImpl(_, _, _, _, Some(_), _, _) | hir::ItemDefaultImpl(..))
86         } else {
87             false
88         };
89
90         let unsafety = match kind {
91             hir::intravisit::FnKind::ItemFn(_, _, unsafety, _, _, _, _) => unsafety,
92             hir::intravisit::FnKind::Method(_, sig, _, _) => sig.unsafety,
93             hir::intravisit::FnKind::Closure(_) => return,
94         };
95
96         // don't warn for implementations, it's not their fault
97         if !is_impl {
98             // don't lint extern functions decls, it's not their fault either
99             match kind {
100                 hir::intravisit::FnKind::Method(_, &hir::MethodSig { abi: Abi::Rust, .. }, _, _) |
101                 hir::intravisit::FnKind::ItemFn(_, _, _, _, Abi::Rust, _, _) => self.check_arg_number(cx, decl, span),
102                 _ => {},
103             }
104         }
105
106         self.check_raw_ptr(cx, unsafety, decl, body, nodeid);
107     }
108
109     fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem) {
110         if let hir::TraitItemKind::Method(ref sig, ref eid) = item.node {
111             // don't lint extern functions decls, it's not their fault
112             if sig.abi == Abi::Rust {
113                 self.check_arg_number(cx, &sig.decl, item.span);
114             }
115
116             if let hir::TraitMethod::Provided(eid) = *eid {
117                 let body = cx.tcx.hir.body(eid);
118                 self.check_raw_ptr(cx, sig.unsafety, &sig.decl, body, item.id);
119             }
120         }
121     }
122 }
123
124 impl<'a, 'tcx> Functions {
125     fn check_arg_number(&self, cx: &LateContext, decl: &hir::FnDecl, span: Span) {
126         let args = decl.inputs.len() as u64;
127         if args > self.threshold {
128             span_lint(
129                 cx,
130                 TOO_MANY_ARGUMENTS,
131                 span,
132                 &format!("this function has too many arguments ({}/{})", args, self.threshold),
133             );
134         }
135     }
136
137     fn check_raw_ptr(
138         &self,
139         cx: &LateContext<'a, 'tcx>,
140         unsafety: hir::Unsafety,
141         decl: &'tcx hir::FnDecl,
142         body: &'tcx hir::Body,
143         nodeid: ast::NodeId,
144     ) {
145         let expr = &body.value;
146         if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(nodeid) {
147             let raw_ptrs = iter_input_pats(decl, body)
148                 .zip(decl.inputs.iter())
149                 .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty))
150                 .collect::<HashSet<_>>();
151
152             if !raw_ptrs.is_empty() {
153                 let mut v = DerefVisitor {
154                     cx: cx,
155                     ptrs: raw_ptrs,
156                 };
157
158                 hir::intravisit::walk_expr(&mut v, expr);
159             }
160         }
161     }
162 }
163
164 fn raw_ptr_arg(arg: &hir::Arg, ty: &hir::Ty) -> Option<hir::def_id::DefId> {
165     if let (&hir::PatKind::Binding(_, def_id, _, _), &hir::TyPtr(_)) = (&arg.pat.node, &ty.node) {
166         Some(def_id)
167     } else {
168         None
169     }
170 }
171
172 struct DerefVisitor<'a, 'tcx: 'a> {
173     cx: &'a LateContext<'a, 'tcx>,
174     ptrs: HashSet<hir::def_id::DefId>,
175 }
176
177 impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
178     fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
179         match expr.node {
180             hir::ExprCall(ref f, ref args) => {
181                 let ty = self.cx.tables.expr_ty(f);
182
183                 if type_is_unsafe_function(self.cx, ty) {
184                     for arg in args {
185                         self.check_arg(arg);
186                     }
187                 }
188             },
189             hir::ExprMethodCall(_, _, ref args) => {
190                 let def_id = self.cx.tables.type_dependent_defs[&expr.id].def_id();
191                 let base_type = self.cx.tcx.type_of(def_id);
192
193                 if type_is_unsafe_function(self.cx, base_type) {
194                     for arg in args {
195                         self.check_arg(arg);
196                     }
197                 }
198             },
199             hir::ExprUnary(hir::UnDeref, ref ptr) => self.check_arg(ptr),
200             _ => (),
201         }
202
203         hir::intravisit::walk_expr(self, expr);
204     }
205     fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
206         intravisit::NestedVisitorMap::None
207     }
208 }
209
210 impl<'a, 'tcx: 'a> DerefVisitor<'a, 'tcx> {
211     fn check_arg(&self, ptr: &hir::Expr) {
212         if let hir::ExprPath(ref qpath) = ptr.node {
213             let def = self.cx.tables.qpath_def(qpath, ptr.id);
214             if self.ptrs.contains(&def.def_id()) {
215                 span_lint(
216                     self.cx,
217                     NOT_UNSAFE_PTR_ARG_DEREF,
218                     ptr.span,
219                     "this public function dereferences a raw pointer but is not marked `unsafe`",
220                 );
221             }
222         }
223     }
224 }