]> git.lizzy.rs Git - rust.git/blob - src/vec.rs
Fix issue with `DOC_MARKDOWN` and punctuation
[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, expr, addressee);
42         }}
43
44         // search for `for _ in vec![…]`
45         if let Some((_, arg, _)) = recover_for_loop(expr) {
46             check_vec_macro(cx, arg, arg);
47         }
48     }
49 }
50
51 fn check_vec_macro(cx: &LateContext, expr: &Expr, vec: &Expr) {
52     if let Some(vec_args) = unexpand_vec(cx, vec) {
53         let snippet = match vec_args {
54             VecArgs::Repeat(elem, len) => {
55                 format!("&[{}; {}]", snippet(cx, elem.span, "elem"), snippet(cx, len.span, "len")).into()
56             }
57             VecArgs::Vec(args) => {
58                 if let Some(last) = args.iter().last() {
59                     let span = Span {
60                         lo: args[0].span.lo,
61                         hi: last.span.hi,
62                         expn_id: args[0].span.expn_id,
63                     };
64
65                     format!("&[{}]", snippet(cx, span, "..")).into()
66                 } else {
67                     "&[]".into()
68                 }
69             }
70         };
71
72         span_lint_and_then(cx, USELESS_VEC, expr.span, "useless use of `vec!`", |db| {
73             db.span_suggestion(expr.span, "you can use a slice directly", snippet);
74         });
75     }
76 }
77
78 /// Represent the pre-expansion arguments of a `vec!` invocation.
79 pub enum VecArgs<'a> {
80     /// `vec![elem; len]`
81     Repeat(&'a P<Expr>, &'a P<Expr>),
82     /// `vec![a, b, c]`
83     Vec(&'a [P<Expr>]),
84 }
85
86 /// Returns the arguments of the `vec!` macro if this expression was expanded from `vec!`.
87 pub fn unexpand_vec<'e>(cx: &LateContext, expr: &'e Expr) -> Option<VecArgs<'e>> {
88     if_let_chain!{[
89         let ExprCall(ref fun, ref args) = expr.node,
90         let ExprPath(_, ref path) = fun.node,
91         is_expn_of(cx, fun.span, "vec").is_some()
92     ], {
93         return if match_path(path, &paths::VEC_FROM_ELEM) && args.len() == 2 {
94             // `vec![elem; size]` case
95             Some(VecArgs::Repeat(&args[0], &args[1]))
96         }
97         else if match_path(path, &["into_vec"]) && args.len() == 1 {
98             // `vec![a, b, c]` case
99             if_let_chain!{[
100                 let ExprBox(ref boxed) = args[0].node,
101                 let ExprVec(ref args) = boxed.node
102             ], {
103                 return Some(VecArgs::Vec(&*args));
104             }}
105
106             None
107         }
108         else {
109             None
110         };
111     }}
112
113     None
114 }