1 use rustc
::hir
::intravisit
;
5 use rustc
::hir
::def
::Def
;
6 use std
::collections
::HashSet
;
9 use syntax
::codemap
::Span
;
10 use utils
::{iter_input_pats, span_lint, type_is_unsafe_function}
;
12 /// **What it does:** Checks for functions with too many parameters.
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.
18 /// **Known problems:** None.
22 /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b:
26 pub TOO_MANY_ARGUMENTS
,
28 "functions with too many arguments"
31 /// **What it does:** Checks for public functions that dereferences raw pointer
32 /// arguments but are not marked unsafe.
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
38 /// **Known problems:**
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
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()`).
49 /// pub fn foo(x: *const u8) { println!("{}", unsafe { *x }); }
52 pub NOT_UNSAFE_PTR_ARG_DEREF
,
54 "public functions dereferencing raw pointer arguments but not marked `unsafe`"
57 #[derive(Copy, Clone)]
58 pub struct Functions
{
63 pub fn new(threshold
: u64) -> Self {
70 impl LintPass
for Functions
{
71 fn get_lints(&self) -> LintArray
{
72 lint_array
!(TOO_MANY_ARGUMENTS
, NOT_UNSAFE_PTR_ARG_DEREF
)
76 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for Functions
{
79 cx
: &LateContext
<'a
, 'tcx
>,
80 kind
: intravisit
::FnKind
<'tcx
>,
81 decl
: &'tcx hir
::FnDecl
,
82 body
: &'tcx hir
::Body
,
86 use rustc
::hir
::map
::Node
::*;
88 let is_impl
= if let Some(NodeItem(item
)) = cx
.tcx
.hir
.find(cx
.tcx
.hir
.get_parent_node(nodeid
)) {
89 matches
!(item
.node
, hir
::ItemImpl(_
, _
, _
, _
, Some(_
), _
, _
) | hir
::ItemAutoImpl(..))
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,
100 // don't warn for implementations, it's not their fault
102 // don't lint extern functions decls, it's not their fault either
104 hir
::intravisit
::FnKind
::Method(_
, &hir
::MethodSig { abi: Abi::Rust, .. }
, _
, _
) |
105 hir
::intravisit
::FnKind
::ItemFn(_
, _
, _
, _
, Abi
::Rust
, _
, _
) => self.check_arg_number(cx
, decl
, span
),
110 self.check_raw_ptr(cx
, unsafety
, decl
, body
, nodeid
);
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
);
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
);
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
{
136 &format
!("this function has too many arguments ({}/{})", args
, self.threshold
),
143 cx
: &LateContext
<'a
, 'tcx
>,
144 unsafety
: hir
::Unsafety
,
145 decl
: &'tcx hir
::FnDecl
,
146 body
: &'tcx hir
::Body
,
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
<_
>>();
156 if !raw_ptrs
.is_empty() {
157 let tables
= cx
.tcx
.body_tables(body
.id());
158 let mut v
= DerefVisitor
{
164 hir
::intravisit
::walk_expr(&mut v
, expr
);
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
) {
178 struct DerefVisitor
<'a
, 'tcx
: 'a
> {
179 cx
: &'a LateContext
<'a
, 'tcx
>,
180 ptrs
: HashSet
<ast
::NodeId
>,
181 tables
: &'a ty
::TypeckTables
<'tcx
>,
184 impl<'a
, 'tcx
> hir
::intravisit
::Visitor
<'tcx
> for DerefVisitor
<'a
, 'tcx
> {
185 fn visit_expr(&mut self, expr
: &'tcx hir
::Expr
) {
187 hir
::ExprCall(ref f
, ref args
) => {
188 let ty
= self.tables
.expr_ty(f
);
190 if type_is_unsafe_function(self.cx
, ty
) {
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
);
200 if type_is_unsafe_function(self.cx
, base_type
) {
206 hir
::ExprUnary(hir
::UnDeref
, ref ptr
) => self.check_arg(ptr
),
210 hir
::intravisit
::walk_expr(self, expr
);
212 fn nested_visit_map
<'this
>(&'this
mut self) -> intravisit
::NestedVisitorMap
<'this
, 'tcx
> {
213 intravisit
::NestedVisitorMap
::None
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
{
220 if let Def
::Local(id
) = self.cx
.tables
.qpath_def(qpath
, ptr
.hir_id
) {
221 if self.ptrs
.contains(&id
) {
224 NOT_UNSAFE_PTR_ARG_DEREF
,
226 "this public function dereferences a raw pointer but is not marked `unsafe`",