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:** Checks 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.
19 /// let x = [1,2,3,4];
25 pub OUT_OF_BOUNDS_INDEXING,
27 "out of bounds constant indexing"
30 /// **What it does:** Checks for usage of indexing or slicing.
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
36 /// **Known problems:** Hopefully none.
44 declare_restriction_lint! {
46 "indexing/slicing usage"
50 pub struct ArrayIndexing;
52 impl LintPass for ArrayIndexing {
53 fn get_lints(&self) -> LintArray {
54 lint_array!(INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING)
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);
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");
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))
82 .map(|end| eval_const_expr_partial(cx.tcx, end, ExprTypeChecked, None))
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");
94 if let Some(range) = higher::range(index) {
95 // Full ranges are always valid
96 if range.start.is_none() && range.end.is_none() {
100 // Impossible to know if indexing or slicing is correct
101 utils::span_lint(cx, INDEXING_SLICING, e.span, "slicing may panic");
103 utils::span_lint(cx, INDEXING_SLICING, e.span, "indexing may panic");
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),
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")
127 Some(_) => return None,