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