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