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