]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/vec.rs
Auto merge of #81993 - flip1995:clippyup, r=Manishearth
[rust.git] / clippy_lints / src / vec.rs
1 use crate::consts::{constant, Constant};
2 use crate::rustc_target::abi::LayoutOf;
3 use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_sugg};
4 use if_chain::if_chain;
5 use rustc_errors::Applicability;
6 use rustc_hir::{BorrowKind, Expr, ExprKind};
7 use rustc_lint::{LateContext, LateLintPass};
8 use rustc_middle::ty::{self, Ty};
9 use rustc_session::{declare_tool_lint, impl_lint_pass};
10 use rustc_span::source_map::Span;
11
12 #[allow(clippy::module_name_repetitions)]
13 #[derive(Copy, Clone)]
14 pub struct UselessVec {
15     pub too_large_for_stack: u64,
16 }
17
18 declare_clippy_lint! {
19     /// **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would
20     /// be possible.
21     ///
22     /// **Why is this bad?** This is less efficient.
23     ///
24     /// **Known problems:** None.
25     ///
26     /// **Example:**
27     /// ```rust
28     /// # fn foo(my_vec: &[u8]) {}
29     ///
30     /// // Bad
31     /// foo(&vec![1, 2]);
32     ///
33     /// // Good
34     /// foo(&[1, 2]);
35     /// ```
36     pub USELESS_VEC,
37     perf,
38     "useless `vec!`"
39 }
40
41 impl_lint_pass!(UselessVec => [USELESS_VEC]);
42
43 impl<'tcx> LateLintPass<'tcx> for UselessVec {
44     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
45         // search for `&vec![_]` expressions where the adjusted type is `&[_]`
46         if_chain! {
47             if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
48             if let ty::Slice(..) = ty.kind();
49             if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind;
50             if let Some(vec_args) = higher::vec_macro(cx, addressee);
51             then {
52                 self.check_vec_macro(cx, &vec_args, expr.span);
53             }
54         }
55
56         // search for `for _ in vec![…]`
57         if_chain! {
58             if let Some((_, arg, _, _)) = higher::for_loop(expr);
59             if let Some(vec_args) = higher::vec_macro(cx, arg);
60             if is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(arg)));
61             then {
62                 // report the error around the `vec!` not inside `<std macros>:`
63                 let span = arg.span
64                     .ctxt()
65                     .outer_expn_data()
66                     .call_site
67                     .ctxt()
68                     .outer_expn_data()
69                     .call_site;
70                 self.check_vec_macro(cx, &vec_args, span);
71             }
72         }
73     }
74 }
75
76 impl UselessVec {
77     fn check_vec_macro<'tcx>(self, cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) {
78         let mut applicability = Applicability::MachineApplicable;
79         let snippet = match *vec_args {
80             higher::VecArgs::Repeat(elem, len) => {
81                 if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) {
82                     #[allow(clippy::cast_possible_truncation)]
83                     if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack {
84                         return;
85                     }
86
87                     format!(
88                         "&[{}; {}]",
89                         snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
90                         snippet_with_applicability(cx, len.span, "len", &mut applicability)
91                     )
92                 } else {
93                     return;
94                 }
95             },
96             higher::VecArgs::Vec(args) => {
97                 if let Some(last) = args.iter().last() {
98                     #[allow(clippy::cast_possible_truncation)]
99                     if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack {
100                         return;
101                     }
102                     let span = args[0].span.to(last.span);
103
104                     format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability))
105                 } else {
106                     "&[]".into()
107                 }
108             },
109         };
110
111         span_lint_and_sugg(
112             cx,
113             USELESS_VEC,
114             span,
115             "useless use of `vec!`",
116             "you can use a slice directly",
117             snippet,
118             applicability,
119         );
120     }
121 }
122
123 fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 {
124     let ty = cx.typeck_results().expr_ty_adjusted(expr);
125     cx.layout_of(ty).map_or(0, |l| l.size.bytes())
126 }
127
128 /// Returns the item type of the vector (i.e., the `T` in `Vec<T>`).
129 fn vec_type(ty: Ty<'_>) -> Ty<'_> {
130     if let ty::Adt(_, substs) = ty.kind() {
131         substs.type_at(0)
132     } else {
133         panic!("The type of `vec!` is a not a struct?");
134     }
135 }