]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
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 | } |