1 use rustc
::hir
::intravisit
;
5 use std
::collections
::HashSet
;
8 use syntax
::codemap
::Span
;
9 use utils
::{span_lint, type_is_unsafe_function, iter_input_pats}
;
11 /// **What it does:** Checks for functions with too many parameters.
13 /// **Why is this bad?** Functions with lots of parameters are considered bad
14 /// style and reduce readability (“what does the 5th parameter mean?”). Consider
15 /// grouping some parameters into a new type.
17 /// **Known problems:** None.
21 /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b:
25 pub TOO_MANY_ARGUMENTS
,
27 "functions with too many arguments"
30 /// **What it does:** Checks for public functions that dereferences raw pointer
31 /// arguments but are not marked unsafe.
33 /// **Why is this bad?** The function should probably be marked `unsafe`, since
34 /// for an arbitrary raw pointer, there is no way of telling for sure if it is
37 /// **Known problems:**
39 /// * It does not check functions recursively so if the pointer is passed to a
40 /// private non-`unsafe` function which does the dereferencing, the lint won't
42 /// * It only checks for arguments whose type are raw pointers, not raw pointers
43 /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
44 /// `some_argument.get_raw_ptr()`).
48 /// pub fn foo(x: *const u8) { println!("{}", unsafe { *x }); }
51 pub NOT_UNSAFE_PTR_ARG_DEREF
,
53 "public functions dereferencing raw pointer arguments but not marked `unsafe`"
56 #[derive(Copy, Clone)]
57 pub struct Functions
{
62 pub fn new(threshold
: u64) -> Self {
63 Self { threshold: threshold }
67 impl LintPass
for Functions
{
68 fn get_lints(&self) -> LintArray
{
69 lint_array
!(TOO_MANY_ARGUMENTS
, NOT_UNSAFE_PTR_ARG_DEREF
)
73 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for Functions
{
76 cx
: &LateContext
<'a
, 'tcx
>,
77 kind
: intravisit
::FnKind
<'tcx
>,
78 decl
: &'tcx hir
::FnDecl
,
79 body
: &'tcx hir
::Body
,
83 use rustc
::hir
::map
::Node
::*;
85 let is_impl
= if let Some(NodeItem(item
)) = cx
.tcx
.hir
.find(cx
.tcx
.hir
.get_parent_node(nodeid
)) {
86 matches
!(item
.node
, hir
::ItemImpl(_
, _
, _
, _
, Some(_
), _
, _
) | hir
::ItemDefaultImpl(..))
91 let unsafety
= match kind
{
92 hir
::intravisit
::FnKind
::ItemFn(_
, _
, unsafety
, _
, _
, _
, _
) => unsafety
,
93 hir
::intravisit
::FnKind
::Method(_
, sig
, _
, _
) => sig
.unsafety
,
94 hir
::intravisit
::FnKind
::Closure(_
) => return,
97 // don't warn for implementations, it's not their fault
99 // don't lint extern functions decls, it's not their fault either
101 hir
::intravisit
::FnKind
::Method(_
, &hir
::MethodSig { abi: Abi::Rust, .. }
, _
, _
) |
102 hir
::intravisit
::FnKind
::ItemFn(_
, _
, _
, _
, Abi
::Rust
, _
, _
) => self.check_arg_number(cx
, decl
, span
),
107 self.check_raw_ptr(cx
, unsafety
, decl
, body
, nodeid
);
110 fn check_trait_item(&mut self, cx
: &LateContext
<'a
, 'tcx
>, item
: &'tcx hir
::TraitItem
) {
111 if let hir
::TraitItemKind
::Method(ref sig
, ref eid
) = item
.node
{
112 // don't lint extern functions decls, it's not their fault
113 if sig
.abi
== Abi
::Rust
{
114 self.check_arg_number(cx
, &sig
.decl
, item
.span
);
117 if let hir
::TraitMethod
::Provided(eid
) = *eid
{
118 let body
= cx
.tcx
.hir
.body(eid
);
119 self.check_raw_ptr(cx
, sig
.unsafety
, &sig
.decl
, body
, item
.id
);
125 impl<'a
, 'tcx
> Functions
{
126 fn check_arg_number(&self, cx
: &LateContext
, decl
: &hir
::FnDecl
, span
: Span
) {
127 let args
= decl
.inputs
.len() as u64;
128 if args
> self.threshold
{
133 &format
!("this function has too many arguments ({}/{})", args
, self.threshold
),
140 cx
: &LateContext
<'a
, 'tcx
>,
141 unsafety
: hir
::Unsafety
,
142 decl
: &'tcx hir
::FnDecl
,
143 body
: &'tcx hir
::Body
,
146 let expr
= &body
.value
;
147 if unsafety
== hir
::Unsafety
::Normal
&& cx
.access_levels
.is_exported(nodeid
) {
148 let raw_ptrs
= iter_input_pats(decl
, body
)
149 .zip(decl
.inputs
.iter())
150 .filter_map(|(arg
, ty
)| raw_ptr_arg(arg
, ty
))
151 .collect
::<HashSet
<_
>>();
153 if !raw_ptrs
.is_empty() {
154 let tables
= cx
.tcx
.body_tables(body
.id());
155 let mut v
= DerefVisitor
{
161 hir
::intravisit
::walk_expr(&mut v
, expr
);
167 fn raw_ptr_arg(arg
: &hir
::Arg
, ty
: &hir
::Ty
) -> Option
<hir
::def_id
::DefId
> {
168 if let (&hir
::PatKind
::Binding(_
, def_id
, _
, _
), &hir
::TyPtr(_
)) = (&arg
.pat
.node
, &ty
.node
) {
175 struct DerefVisitor
<'a
, 'tcx
: 'a
> {
176 cx
: &'a LateContext
<'a
, 'tcx
>,
177 ptrs
: HashSet
<hir
::def_id
::DefId
>,
178 tables
: &'a ty
::TypeckTables
<'tcx
>,
181 impl<'a
, 'tcx
> hir
::intravisit
::Visitor
<'tcx
> for DerefVisitor
<'a
, 'tcx
> {
182 fn visit_expr(&mut self, expr
: &'tcx hir
::Expr
) {
184 hir
::ExprCall(ref f
, ref args
) => {
185 let ty
= self.tables
.expr_ty(f
);
187 if type_is_unsafe_function(self.cx
, ty
) {
193 hir
::ExprMethodCall(_
, _
, ref args
) => {
194 let def_id
= self.tables
.type_dependent_defs()[expr
.hir_id
].def_id();
195 let base_type
= self.cx
.tcx
.type_of(def_id
);
197 if type_is_unsafe_function(self.cx
, base_type
) {
203 hir
::ExprUnary(hir
::UnDeref
, ref ptr
) => self.check_arg(ptr
),
207 hir
::intravisit
::walk_expr(self, expr
);
209 fn nested_visit_map
<'this
>(&'this
mut self) -> intravisit
::NestedVisitorMap
<'this
, 'tcx
> {
210 intravisit
::NestedVisitorMap
::None
214 impl<'a
, 'tcx
: 'a
> DerefVisitor
<'a
, 'tcx
> {
215 fn check_arg(&self, ptr
: &hir
::Expr
) {
216 if let hir
::ExprPath(ref qpath
) = ptr
.node
{
217 let def
= self.cx
.tables
.qpath_def(qpath
, ptr
.hir_id
);
218 if self.ptrs
.contains(&def
.def_id()) {
221 NOT_UNSAFE_PTR_ARG_DEREF
,
223 "this public function dereferences a raw pointer but is not marked `unsafe`",