]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/ptr.rs
WIP compiles and doesn't crash (much) but tests are failing
[rust.git] / clippy_lints / src / ptr.rs
1 //! Checks for usage of  `&Vec[_]` and `&String`.
2
3 use rustc::hir::*;
4 use rustc::hir::map::NodeItem;
5 use rustc::lint::*;
6 use rustc::ty;
7 use syntax::ast::NodeId;
8 use utils::{match_path, match_type, paths, span_lint};
9
10 /// **What it does:** This lint checks for function arguments of type `&String` or `&Vec` unless
11 /// the references are mutable.
12 ///
13 /// **Why is this bad?** Requiring the argument to be of the specific size makes the function less
14 /// useful for no benefit; slices in the form of `&[T]` or `&str` usually suffice and can be
15 /// obtained from other types, too.
16 ///
17 /// **Known problems:** None.
18 ///
19 /// **Example:**
20 /// ```rust
21 /// fn foo(&Vec<u32>) { .. }
22 /// ```
23 declare_lint! {
24     pub PTR_ARG,
25     Warn,
26     "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` \
27      instead, respectively"
28 }
29
30 /// **What it does:** This lint checks for equality comparisons with `ptr::null`
31 ///
32 /// **Why is this bad?** It's easier and more readable to use the inherent `.is_null()`
33 /// method instead
34 ///
35 /// **Known problems:** None.
36 ///
37 /// **Example:**
38 /// ```rust
39 /// if x == ptr::null { .. }
40 /// ```
41 declare_lint! {
42     pub CMP_NULL,
43     Warn,
44     "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead."
45 }
46
47
48 #[derive(Copy,Clone)]
49 pub struct PointerPass;
50
51 impl LintPass for PointerPass {
52     fn get_lints(&self) -> LintArray {
53         lint_array!(PTR_ARG, CMP_NULL)
54     }
55 }
56
57 impl LateLintPass for PointerPass {
58     fn check_item(&mut self, cx: &LateContext, item: &Item) {
59         if let ItemFn(ref decl, _, _, _, _, _) = item.node {
60             check_fn(cx, decl, item.id);
61         }
62     }
63
64     fn check_impl_item(&mut self, cx: &LateContext, item: &ImplItem) {
65         if let ImplItemKind::Method(ref sig, _) = item.node {
66             if let Some(NodeItem(it)) = cx.tcx.map.find(cx.tcx.map.get_parent(item.id)) {
67                 if let ItemImpl(_, _, _, Some(_), _, _) = it.node {
68                     return; // ignore trait impls
69                 }
70             }
71             check_fn(cx, &sig.decl, item.id);
72         }
73     }
74
75     fn check_trait_item(&mut self, cx: &LateContext, item: &TraitItem) {
76         if let MethodTraitItem(ref sig, _) = item.node {
77             check_fn(cx, &sig.decl, item.id);
78         }
79     }
80
81     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
82         if let ExprBinary(ref op, ref l, ref r) = expr.node {
83             if (op.node == BiEq || op.node == BiNe) && (is_null_path(l) || is_null_path(r)) {
84                 span_lint(cx,
85                           CMP_NULL,
86                           expr.span,
87                           "Comparing with null is better expressed by the .is_null() method");
88             }
89         }
90     }
91 }
92
93 fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) {
94     let fn_def_id = cx.tcx.map.local_def_id(fn_id);
95     let fn_ty = cx.tcx.item_type(fn_def_id).fn_sig().skip_binder();
96
97     for (arg, ty) in decl.inputs.iter().zip(&fn_ty.inputs) {
98         if let ty::TyRef(_, ty::TypeAndMut { ty, mutbl: MutImmutable }) = ty.sty {
99             if match_type(cx, ty, &paths::VEC) {
100                 span_lint(cx,
101                           PTR_ARG,
102                           arg.ty.span,
103                           "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
104                            with non-Vec-based slices. Consider changing the type to `&[...]`");
105             } else if match_type(cx, ty, &paths::STRING) {
106                 span_lint(cx,
107                           PTR_ARG,
108                           arg.ty.span,
109                           "writing `&String` instead of `&str` involves a new object where a slice will do. \
110                            Consider changing the type to `&str`");
111             }
112         }
113     }
114 }
115
116 fn is_null_path(expr: &Expr) -> bool {
117     if let ExprCall(ref pathexp, ref args) = expr.node {
118         if args.is_empty() {
119             if let ExprPath(ref path) = pathexp.node {
120                 return match_path(path, &paths::PTR_NULL) || match_path(path, &paths::PTR_NULL_MUT)
121             }
122         }
123     }
124     false
125 }