]> git.lizzy.rs Git - rust.git/blob - src/vec.rs
Merge pull request #957 from oli-obk/needs_borrow
[rust.git] / src / vec.rs
1 use rustc::lint::*;
2 use rustc::ty::TypeVariants;
3 use rustc::hir::*;
4 use syntax::codemap::Span;
5 use syntax::ptr::P;
6 use utils::{is_expn_of, match_path, paths, recover_for_loop, snippet, span_lint_and_then};
7
8 /// **What it does:** This lint warns about using `&vec![..]` when using `&[..]` would be possible.
9 ///
10 /// **Why is this bad?** This is less efficient.
11 ///
12 /// **Known problems:** None.
13 ///
14 /// **Example:**
15 /// ```rust,ignore
16 /// foo(&vec![1, 2])
17 /// ```
18 declare_lint! {
19     pub USELESS_VEC,
20     Warn,
21     "useless `vec!`"
22 }
23
24 #[derive(Copy, Clone, Debug)]
25 pub struct UselessVec;
26
27 impl LintPass for UselessVec {
28     fn get_lints(&self) -> LintArray {
29         lint_array!(USELESS_VEC)
30     }
31 }
32
33 impl LateLintPass for UselessVec {
34     fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
35         // search for `&vec![_]` expressions where the adjusted type is `&[_]`
36         if_let_chain!{[
37             let TypeVariants::TyRef(_, ref ty) = cx.tcx.expr_ty_adjusted(expr).sty,
38             let TypeVariants::TySlice(..) = ty.ty.sty,
39             let ExprAddrOf(_, ref addressee) = expr.node,
40         ], {
41             check_vec_macro(cx, addressee, expr.span);
42         }}
43
44         // search for `for _ in vec![…]`
45         if let Some((_, arg, _)) = recover_for_loop(expr) {
46             // report the error around the `vec!` not inside `<std macros>:`
47             let span = cx.sess().codemap().source_callsite(arg.span);
48             check_vec_macro(cx, arg, span);
49         }
50     }
51 }
52
53 fn check_vec_macro(cx: &LateContext, vec: &Expr, span: Span) {
54     if let Some(vec_args) = unexpand_vec(cx, vec) {
55         let snippet = match vec_args {
56             VecArgs::Repeat(elem, len) => {
57                 format!("&[{}; {}]", snippet(cx, elem.span, "elem"), snippet(cx, len.span, "len")).into()
58             }
59             VecArgs::Vec(args) => {
60                 if let Some(last) = args.iter().last() {
61                     let span = Span {
62                         lo: args[0].span.lo,
63                         hi: last.span.hi,
64                         expn_id: args[0].span.expn_id,
65                     };
66
67                     format!("&[{}]", snippet(cx, span, "..")).into()
68                 } else {
69                     "&[]".into()
70                 }
71             }
72         };
73
74         span_lint_and_then(cx, USELESS_VEC, span, "useless use of `vec!`", |db| {
75             db.span_suggestion(span, "you can use a slice directly", snippet);
76         });
77     }
78 }
79
80 /// Represent the pre-expansion arguments of a `vec!` invocation.
81 pub enum VecArgs<'a> {
82     /// `vec![elem; len]`
83     Repeat(&'a P<Expr>, &'a P<Expr>),
84     /// `vec![a, b, c]`
85     Vec(&'a [P<Expr>]),
86 }
87
88 /// Returns the arguments of the `vec!` macro if this expression was expanded from `vec!`.
89 pub fn unexpand_vec<'e>(cx: &LateContext, expr: &'e Expr) -> Option<VecArgs<'e>> {
90     if_let_chain!{[
91         let ExprCall(ref fun, ref args) = expr.node,
92         let ExprPath(_, ref path) = fun.node,
93         is_expn_of(cx, fun.span, "vec").is_some()
94     ], {
95         return if match_path(path, &paths::VEC_FROM_ELEM) && args.len() == 2 {
96             // `vec![elem; size]` case
97             Some(VecArgs::Repeat(&args[0], &args[1]))
98         }
99         else if match_path(path, &["into_vec"]) && args.len() == 1 {
100             // `vec![a, b, c]` case
101             if_let_chain!{[
102                 let ExprBox(ref boxed) = args[0].node,
103                 let ExprVec(ref args) = boxed.node
104             ], {
105                 return Some(VecArgs::Vec(&*args));
106             }}
107
108             None
109         }
110         else {
111             None
112         };
113     }}
114
115     None
116 }