]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/indexing_slicing.rs
New upstream version 1.52.1+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / indexing_slicing.rs
CommitLineData
f20569fa
XL
1//! lint on indexing and slicing operations
2
3use crate::consts::{constant, Constant};
4use crate::utils::{higher, span_lint, span_lint_and_help};
5use rustc_ast::ast::RangeLimits;
6use rustc_hir::{Expr, ExprKind};
7use rustc_lint::{LateContext, LateLintPass};
8use rustc_middle::ty;
9use rustc_session::{declare_lint_pass, declare_tool_lint};
10
11declare_clippy_lint! {
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 /// ```no_run
21 /// # #![allow(const_err)]
22 /// let x = [1, 2, 3, 4];
23 ///
24 /// // Bad
25 /// x[9];
26 /// &x[2..9];
27 ///
28 /// // Good
29 /// x[0];
30 /// x[3];
31 /// ```
32 pub OUT_OF_BOUNDS_INDEXING,
33 correctness,
34 "out of bounds constant indexing"
35}
36
37declare_clippy_lint! {
38 /// **What it does:** Checks for usage of indexing or slicing. Arrays are special cases, this lint
39 /// does report on arrays if we can tell that slicing operations are in bounds and does not
40 /// lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint.
41 ///
42 /// **Why is this bad?** Indexing and slicing can panic at runtime and there are
43 /// safe alternatives.
44 ///
45 /// **Known problems:** Hopefully none.
46 ///
47 /// **Example:**
48 /// ```rust,no_run
49 /// // Vector
50 /// let x = vec![0; 5];
51 ///
52 /// // Bad
53 /// x[2];
54 /// &x[2..100];
55 /// &x[2..];
56 /// &x[..100];
57 ///
58 /// // Good
59 /// x.get(2);
60 /// x.get(2..100);
61 /// x.get(2..);
62 /// x.get(..100);
63 ///
64 /// // Array
65 /// let y = [0, 1, 2, 3];
66 ///
67 /// // Bad
68 /// &y[10..100];
69 /// &y[10..];
70 /// &y[..100];
71 ///
72 /// // Good
73 /// &y[2..];
74 /// &y[..2];
75 /// &y[0..3];
76 /// y.get(10);
77 /// y.get(10..100);
78 /// y.get(10..);
79 /// y.get(..100);
80 /// ```
81 pub INDEXING_SLICING,
82 restriction,
83 "indexing/slicing usage"
84}
85
86declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]);
87
88impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
89 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
90 if let ExprKind::Index(ref array, ref index) = &expr.kind {
91 let ty = cx.typeck_results().expr_ty(array).peel_refs();
92 if let Some(range) = higher::range(index) {
93 // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
94 if let ty::Array(_, s) = ty.kind() {
95 let size: u128 = if let Some(size) = s.try_eval_usize(cx.tcx, cx.param_env) {
96 size.into()
97 } else {
98 return;
99 };
100
101 let const_range = to_const_range(cx, range, size);
102
103 if let (Some(start), _) = const_range {
104 if start > size {
105 span_lint(
106 cx,
107 OUT_OF_BOUNDS_INDEXING,
108 range.start.map_or(expr.span, |start| start.span),
109 "range is out of bounds",
110 );
111 return;
112 }
113 }
114
115 if let (_, Some(end)) = const_range {
116 if end > size {
117 span_lint(
118 cx,
119 OUT_OF_BOUNDS_INDEXING,
120 range.end.map_or(expr.span, |end| end.span),
121 "range is out of bounds",
122 );
123 return;
124 }
125 }
126
127 if let (Some(_), Some(_)) = const_range {
128 // early return because both start and end are constants
129 // and we have proven above that they are in bounds
130 return;
131 }
132 }
133
134 let help_msg = match (range.start, range.end) {
135 (None, Some(_)) => "consider using `.get(..n)`or `.get_mut(..n)` instead",
136 (Some(_), None) => "consider using `.get(n..)` or .get_mut(n..)` instead",
137 (Some(_), Some(_)) => "consider using `.get(n..m)` or `.get_mut(n..m)` instead",
138 (None, None) => return, // [..] is ok.
139 };
140
141 span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic", None, help_msg);
142 } else {
143 // Catchall non-range index, i.e., [n] or [n << m]
144 if let ty::Array(..) = ty.kind() {
145 // Index is a constant uint.
146 if let Some(..) = constant(cx, cx.typeck_results(), index) {
147 // Let rustc's `const_err` lint handle constant `usize` indexing on arrays.
148 return;
149 }
150 }
151
152 span_lint_and_help(
153 cx,
154 INDEXING_SLICING,
155 expr.span,
156 "indexing may panic",
157 None,
158 "consider using `.get(n)` or `.get_mut(n)` instead",
159 );
160 }
161 }
162 }
163}
164
165/// Returns a tuple of options with the start and end (exclusive) values of
166/// the range. If the start or end is not constant, None is returned.
167fn to_const_range<'tcx>(
168 cx: &LateContext<'tcx>,
169 range: higher::Range<'_>,
170 array_size: u128,
171) -> (Option<u128>, Option<u128>) {
172 let s = range
173 .start
174 .map(|expr| constant(cx, cx.typeck_results(), expr).map(|(c, _)| c));
175 let start = match s {
176 Some(Some(Constant::Int(x))) => Some(x),
177 Some(_) => None,
178 None => Some(0),
179 };
180
181 let e = range
182 .end
183 .map(|expr| constant(cx, cx.typeck_results(), expr).map(|(c, _)| c));
184 let end = match e {
185 Some(Some(Constant::Int(x))) => {
186 if range.limits == RangeLimits::Closed {
187 Some(x + 1)
188 } else {
189 Some(x)
190 }
191 },
192 Some(_) => None,
193 None => Some(array_size),
194 };
195
196 (start, end)
197}