]>
Commit | Line | Data |
---|---|---|
ea8adc8c | 1 | use rustc::lint::*; |
ea8adc8c | 2 | use rustc::ty; |
ea8adc8c XL |
3 | use rustc::hir; |
4 | use syntax::ast::RangeLimits; | |
5 | use utils::{self, higher}; | |
0531ce1d | 6 | use consts::{constant, Constant}; |
ea8adc8c XL |
7 | |
8 | /// **What it does:** Checks for out of bounds array indexing with a constant | |
9 | /// index. | |
10 | /// | |
11 | /// **Why is this bad?** This will always panic at runtime. | |
12 | /// | |
13 | /// **Known problems:** Hopefully none. | |
14 | /// | |
15 | /// **Example:** | |
16 | /// ```rust | |
17 | /// let x = [1,2,3,4]; | |
18 | /// ... | |
19 | /// x[9]; | |
20 | /// &x[2..9]; | |
21 | /// ``` | |
0531ce1d | 22 | declare_clippy_lint! { |
ea8adc8c | 23 | pub OUT_OF_BOUNDS_INDEXING, |
0531ce1d | 24 | correctness, |
ea8adc8c XL |
25 | "out of bounds constant indexing" |
26 | } | |
27 | ||
28 | /// **What it does:** Checks for usage of indexing or slicing. | |
29 | /// | |
30 | /// **Why is this bad?** Usually, this can be safely allowed. However, in some | |
31 | /// domains such as kernel development, a panic can cause the whole operating | |
32 | /// system to crash. | |
33 | /// | |
34 | /// **Known problems:** Hopefully none. | |
35 | /// | |
36 | /// **Example:** | |
37 | /// ```rust | |
38 | /// ... | |
39 | /// x[2]; | |
40 | /// &x[0..2]; | |
41 | /// ``` | |
0531ce1d | 42 | declare_clippy_lint! { |
ea8adc8c | 43 | pub INDEXING_SLICING, |
0531ce1d | 44 | restriction, |
ea8adc8c XL |
45 | "indexing/slicing usage" |
46 | } | |
47 | ||
48 | #[derive(Copy, Clone)] | |
49 | pub struct ArrayIndexing; | |
50 | ||
51 | impl LintPass for ArrayIndexing { | |
52 | fn get_lints(&self) -> LintArray { | |
53 | lint_array!(INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING) | |
54 | } | |
55 | } | |
56 | ||
57 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing { | |
58 | fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx hir::Expr) { | |
59 | if let hir::ExprIndex(ref array, ref index) = e.node { | |
60 | // Array with known size can be checked statically | |
61 | let ty = cx.tables.expr_ty(array); | |
62 | if let ty::TyArray(_, size) = ty.sty { | |
0531ce1d | 63 | let size = size.val.to_raw_bits().unwrap(); |
ea8adc8c XL |
64 | |
65 | // Index is a constant uint | |
0531ce1d XL |
66 | if let Some((Constant::Int(const_index), _)) = constant(cx, index) { |
67 | if size <= const_index { | |
68 | utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "const index is out of bounds"); | |
abe05a73 | 69 | } |
0531ce1d XL |
70 | |
71 | return; | |
ea8adc8c XL |
72 | } |
73 | ||
74 | // Index is a constant range | |
75 | if let Some(range) = higher::range(index) { | |
0531ce1d XL |
76 | let start = range.start.map(|start| constant(cx, start).map(|(c, _)| c)); |
77 | let end = range.end.map(|end| constant(cx, end).map(|(c, _)| c)); | |
ea8adc8c XL |
78 | |
79 | if let Some((start, end)) = to_const_range(&start, &end, range.limits, size) { | |
80 | if start > size || end > size { | |
81 | utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "range is out of bounds"); | |
82 | } | |
83 | return; | |
84 | } | |
85 | } | |
86 | } | |
87 | ||
88 | if let Some(range) = higher::range(index) { | |
89 | // Full ranges are always valid | |
90 | if range.start.is_none() && range.end.is_none() { | |
91 | return; | |
92 | } | |
93 | ||
94 | // Impossible to know if indexing or slicing is correct | |
95 | utils::span_lint(cx, INDEXING_SLICING, e.span, "slicing may panic"); | |
96 | } else { | |
97 | utils::span_lint(cx, INDEXING_SLICING, e.span, "indexing may panic"); | |
98 | } | |
99 | } | |
100 | } | |
101 | } | |
102 | ||
103 | /// Returns an option containing a tuple with the start and end (exclusive) of | |
104 | /// the range. | |
105 | fn to_const_range( | |
0531ce1d XL |
106 | start: &Option<Option<Constant>>, |
107 | end: &Option<Option<Constant>>, | |
ea8adc8c | 108 | limits: RangeLimits, |
0531ce1d XL |
109 | array_size: u128, |
110 | ) -> Option<(u128, u128)> { | |
ea8adc8c | 111 | let start = match *start { |
0531ce1d | 112 | Some(Some(Constant::Int(x))) => x, |
ea8adc8c | 113 | Some(_) => return None, |
0531ce1d | 114 | None => 0, |
ea8adc8c XL |
115 | }; |
116 | ||
117 | let end = match *end { | |
0531ce1d XL |
118 | Some(Some(Constant::Int(x))) => if limits == RangeLimits::Closed { |
119 | x + 1 | |
abe05a73 XL |
120 | } else { |
121 | x | |
ea8adc8c XL |
122 | }, |
123 | Some(_) => return None, | |
124 | None => array_size, | |
125 | }; | |
126 | ||
127 | Some((start, end)) | |
128 | } |