]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/array_indexing.rs
Merge pull request #2202 from topecongiro/format
[rust.git] / clippy_lints / src / array_indexing.rs
1 use rustc::lint::*;
2 use rustc::middle::const_val::ConstVal;
3 use rustc::ty;
4 use rustc::ty::subst::Substs;
5 use rustc_const_eval::ConstContext;
6 use rustc_const_math::{ConstInt, ConstIsize, ConstUsize};
7 use rustc::hir;
8 use syntax::ast::RangeLimits;
9 use utils::{self, higher};
10 use utils::const_to_u64;
11
12 /// **What it does:** Checks for out of bounds array indexing with a constant
13 /// index.
14 ///
15 /// **Why is this bad?** This will always panic at runtime.
16 ///
17 /// **Known problems:** Hopefully none.
18 ///
19 /// **Example:**
20 /// ```rust
21 /// let x = [1,2,3,4];
22 /// ...
23 /// x[9];
24 /// &x[2..9];
25 /// ```
26 declare_lint! {
27     pub OUT_OF_BOUNDS_INDEXING,
28     Deny,
29     "out of bounds constant indexing"
30 }
31
32 /// **What it does:** Checks for usage of indexing or slicing.
33 ///
34 /// **Why is this bad?** Usually, this can be safely allowed. However, in some
35 /// domains such as kernel development, a panic can cause the whole operating
36 /// system to crash.
37 ///
38 /// **Known problems:** Hopefully none.
39 ///
40 /// **Example:**
41 /// ```rust
42 /// ...
43 /// x[2];
44 /// &x[0..2];
45 /// ```
46 declare_restriction_lint! {
47     pub INDEXING_SLICING,
48     "indexing/slicing usage"
49 }
50
51 #[derive(Copy, Clone)]
52 pub struct ArrayIndexing;
53
54 impl LintPass for ArrayIndexing {
55     fn get_lints(&self) -> LintArray {
56         lint_array!(INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING)
57     }
58 }
59
60 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
61     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx hir::Expr) {
62         if let hir::ExprIndex(ref array, ref index) = e.node {
63             // Array with known size can be checked statically
64             let ty = cx.tables.expr_ty(array);
65             if let ty::TyArray(_, size) = ty.sty {
66                 let size = ConstInt::Usize(
67                     ConstUsize::new(const_to_u64(size), cx.sess().target.usize_ty).expect("array size is invalid"),
68                 );
69                 let parent_item = cx.tcx.hir.get_parent(e.id);
70                 let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
71                 let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
72                 let constcx = ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables);
73
74                 // Index is a constant uint
75                 if let Ok(const_index) = constcx.eval(index) {
76                     if let ConstVal::Integral(const_index) = const_index.val {
77                         if size <= const_index {
78                             utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "const index is out of bounds");
79                         }
80
81                         return;
82                     }
83                 }
84
85                 // Index is a constant range
86                 if let Some(range) = higher::range(index) {
87                     let start = range.start.map(|start| constcx.eval(start)).map(|v| v.ok());
88                     let end = range.end.map(|end| constcx.eval(end)).map(|v| v.ok());
89
90                     if let Some((start, end)) = to_const_range(&start, &end, range.limits, size) {
91                         if start > size || end > size {
92                             utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "range is out of bounds");
93                         }
94                         return;
95                     }
96                 }
97             }
98
99             if let Some(range) = higher::range(index) {
100                 // Full ranges are always valid
101                 if range.start.is_none() && range.end.is_none() {
102                     return;
103                 }
104
105                 // Impossible to know if indexing or slicing is correct
106                 utils::span_lint(cx, INDEXING_SLICING, e.span, "slicing may panic");
107             } else {
108                 utils::span_lint(cx, INDEXING_SLICING, e.span, "indexing may panic");
109             }
110         }
111     }
112 }
113
114 /// Returns an option containing a tuple with the start and end (exclusive) of
115 /// the range.
116 fn to_const_range(
117     start: &Option<Option<&ty::Const>>,
118     end: &Option<Option<&ty::Const>>,
119     limits: RangeLimits,
120     array_size: ConstInt,
121 ) -> Option<(ConstInt, ConstInt)> {
122     let start = match *start {
123         Some(Some(&ty::Const {
124             val: ConstVal::Integral(x),
125             ..
126         })) => x,
127         Some(_) => return None,
128         None => ConstInt::U8(0),
129     };
130
131     let end = match *end {
132         Some(Some(&ty::Const {
133             val: ConstVal::Integral(x),
134             ..
135         })) => if limits == RangeLimits::Closed {
136             match x {
137                 ConstInt::U8(_) => (x + ConstInt::U8(1)),
138                 ConstInt::U16(_) => (x + ConstInt::U16(1)),
139                 ConstInt::U32(_) => (x + ConstInt::U32(1)),
140                 ConstInt::U64(_) => (x + ConstInt::U64(1)),
141                 ConstInt::U128(_) => (x + ConstInt::U128(1)),
142                 ConstInt::Usize(ConstUsize::Us16(_)) => (x + ConstInt::Usize(ConstUsize::Us16(1))),
143                 ConstInt::Usize(ConstUsize::Us32(_)) => (x + ConstInt::Usize(ConstUsize::Us32(1))),
144                 ConstInt::Usize(ConstUsize::Us64(_)) => (x + ConstInt::Usize(ConstUsize::Us64(1))),
145                 ConstInt::I8(_) => (x + ConstInt::I8(1)),
146                 ConstInt::I16(_) => (x + ConstInt::I16(1)),
147                 ConstInt::I32(_) => (x + ConstInt::I32(1)),
148                 ConstInt::I64(_) => (x + ConstInt::I64(1)),
149                 ConstInt::I128(_) => (x + ConstInt::I128(1)),
150                 ConstInt::Isize(ConstIsize::Is16(_)) => (x + ConstInt::Isize(ConstIsize::Is16(1))),
151                 ConstInt::Isize(ConstIsize::Is32(_)) => (x + ConstInt::Isize(ConstIsize::Is32(1))),
152                 ConstInt::Isize(ConstIsize::Is64(_)) => (x + ConstInt::Isize(ConstIsize::Is64(1))),
153             }.expect("such a big array is not realistic")
154         } else {
155             x
156         },
157         Some(_) => return None,
158         None => array_size,
159     };
160
161     Some((start, end))
162 }