]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/array_indexing.rs
New upstream version 1.26.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / array_indexing.rs
1 use rustc::lint::*;
2 use rustc::ty;
3 use rustc::hir;
4 use syntax::ast::RangeLimits;
5 use utils::{self, higher};
6 use consts::{constant, Constant};
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 /// ```
22 declare_clippy_lint! {
23 pub OUT_OF_BOUNDS_INDEXING,
24 correctness,
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 /// ```
42 declare_clippy_lint! {
43 pub INDEXING_SLICING,
44 restriction,
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 {
63 let size = size.val.to_raw_bits().unwrap();
64
65 // Index is a constant uint
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");
69 }
70
71 return;
72 }
73
74 // Index is a constant range
75 if let Some(range) = higher::range(index) {
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));
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(
106 start: &Option<Option<Constant>>,
107 end: &Option<Option<Constant>>,
108 limits: RangeLimits,
109 array_size: u128,
110 ) -> Option<(u128, u128)> {
111 let start = match *start {
112 Some(Some(Constant::Int(x))) => x,
113 Some(_) => return None,
114 None => 0,
115 };
116
117 let end = match *end {
118 Some(Some(Constant::Int(x))) => if limits == RangeLimits::Closed {
119 x + 1
120 } else {
121 x
122 },
123 Some(_) => return None,
124 None => array_size,
125 };
126
127 Some((start, end))
128 }