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;
8 use syntax::ast::RangeLimits;
9 use utils::{self, higher};
11 /// **What it does:** Check for out of bounds array indexing with a constant index.
13 /// **Why is this bad?** This will always panic at runtime.
15 /// **Known problems:** Hopefully none.
20 /// let x = [1,2,3,4];
26 pub OUT_OF_BOUNDS_INDEXING,
28 "out of bound constant indexing"
31 /// **What it does:** Check for usage of indexing or slicing.
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.
37 /// **Known problems:** Hopefully none.
49 "indexing/slicing usage"
53 pub struct ArrayIndexing;
55 impl LintPass for ArrayIndexing {
56 fn get_lints(&self) -> LintArray {
57 lint_array!(INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING)
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);
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");
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))
85 .map(|end| eval_const_expr_partial(cx.tcx, end, ExprTypeChecked, None))
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");
97 if let Some(range) = higher::range(index) {
98 // Full ranges are always valid
99 if range.start.is_none() && range.end.is_none() {
103 // Impossible to know if indexing or slicing is correct
104 utils::span_lint(cx, INDEXING_SLICING, e.span, "slicing may panic");
106 utils::span_lint(cx, INDEXING_SLICING, e.span, "indexing may panic");
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),
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")
130 Some(_) => return None,