Skip to content

Commit cf9cffa

Browse files
Fixes for missing_asserts_for_indexing (#14108)
This PR fixes issues with the `missing_asserts_for_indexing` lint. - false positive when the first index is the highest(or equal) value in a list of indexes: ```rust pub fn foo(slice: &[i32]) -> i32{ slice[1] * slice[0] } ``` - false negative when an assert statement if found after the indexing operation. ```rust pub fn bar(slice: &[i32]) -> i32 { let product = slice[0] * slice[1]; assert!(slice.len() > 1); product } ``` examples: https://godbolt.org/z/s7Y47vKdE closes: #14079 changelog: [`missing_asserts_for_indexing`]: ignore asserts found after indexing, and do not require asserts if the first index is highest.
2 parents a5a033d + 2cd3ea1 commit cf9cffa

5 files changed

+78
-13
lines changed

clippy_lints/src/missing_asserts_for_indexing.rs

+32-12
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ enum IndexEntry<'hir> {
168168
/// if the `assert!` asserts the right length.
169169
AssertWithIndex {
170170
highest_index: usize,
171+
is_first_highest: bool,
171172
asserted_len: usize,
172173
assert_span: Span,
173174
slice: &'hir Expr<'hir>,
@@ -177,6 +178,7 @@ enum IndexEntry<'hir> {
177178
/// Indexing without an `assert!`
178179
IndexWithoutAssert {
179180
highest_index: usize,
181+
is_first_highest: bool,
180182
indexes: Vec<Span>,
181183
slice: &'hir Expr<'hir>,
182184
},
@@ -244,28 +246,41 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Uni
244246
assert_span,
245247
slice,
246248
} => {
247-
*entry = IndexEntry::AssertWithIndex {
248-
highest_index: index,
249-
asserted_len: *asserted_len,
250-
assert_span: *assert_span,
251-
slice,
252-
indexes: vec![expr.span],
253-
comparison: *comparison,
254-
};
249+
if slice.span.lo() > assert_span.lo() {
250+
*entry = IndexEntry::AssertWithIndex {
251+
highest_index: index,
252+
is_first_highest: true,
253+
asserted_len: *asserted_len,
254+
assert_span: *assert_span,
255+
slice,
256+
indexes: vec![expr.span],
257+
comparison: *comparison,
258+
};
259+
}
255260
},
256261
IndexEntry::IndexWithoutAssert {
257-
highest_index, indexes, ..
262+
highest_index,
263+
indexes,
264+
is_first_highest,
265+
..
258266
}
259267
| IndexEntry::AssertWithIndex {
260-
highest_index, indexes, ..
268+
highest_index,
269+
indexes,
270+
is_first_highest,
271+
..
261272
} => {
262273
indexes.push(expr.span);
274+
if *is_first_highest {
275+
(*is_first_highest) = *highest_index >= index;
276+
}
263277
*highest_index = (*highest_index).max(index);
264278
},
265279
}
266280
} else {
267281
indexes.push(IndexEntry::IndexWithoutAssert {
268282
highest_index: index,
283+
is_first_highest: true,
269284
indexes: vec![expr.span],
270285
slice,
271286
});
@@ -284,13 +299,16 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un
284299
if let Some(entry) = entry {
285300
if let IndexEntry::IndexWithoutAssert {
286301
highest_index,
302+
is_first_highest,
287303
indexes,
288304
slice,
289305
} = entry
306+
&& expr.span.lo() <= slice.span.lo()
290307
{
291308
*entry = IndexEntry::AssertWithIndex {
292309
highest_index: *highest_index,
293310
indexes: mem::take(indexes),
311+
is_first_highest: *is_first_highest,
294312
slice,
295313
assert_span: expr.span,
296314
comparison,
@@ -325,12 +343,13 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap<u64, Vec<IndexEntry<'_>
325343
match *entry {
326344
IndexEntry::AssertWithIndex {
327345
highest_index,
346+
is_first_highest,
328347
asserted_len,
329348
ref indexes,
330349
comparison,
331350
assert_span,
332351
slice,
333-
} if indexes.len() > 1 => {
352+
} if indexes.len() > 1 && !is_first_highest => {
334353
// if we have found an `assert!`, let's also check that it's actually right
335354
// and if it covers the highest index and if not, suggest the correct length
336355
let sugg = match comparison {
@@ -378,8 +397,9 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap<u64, Vec<IndexEntry<'_>
378397
IndexEntry::IndexWithoutAssert {
379398
ref indexes,
380399
highest_index,
400+
is_first_highest,
381401
slice,
382-
} if indexes.len() > 1 => {
402+
} if indexes.len() > 1 && !is_first_highest => {
383403
// if there was no `assert!` but more than one index, suggest
384404
// adding an `assert!` that covers the highest index
385405
report_lint(

tests/ui/missing_asserts_for_indexing.fixed

+10
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,14 @@ fn issue11835(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) {
139139
let _ = v4[0] + v4[1] + v4[2];
140140
}
141141

142+
// ok
143+
fn same_index_multiple_times(v1: &[u8]) {
144+
let _ = v1[0] + v1[0];
145+
}
146+
147+
// ok
148+
fn highest_index_first(v1: &[u8]) {
149+
let _ = v1[2] + v1[1] + v1[0];
150+
}
151+
142152
fn main() {}

tests/ui/missing_asserts_for_indexing.rs

+10
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,14 @@ fn issue11835(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) {
139139
let _ = v4[0] + v4[1] + v4[2];
140140
}
141141

142+
// ok
143+
fn same_index_multiple_times(v1: &[u8]) {
144+
let _ = v1[0] + v1[0];
145+
}
146+
147+
// ok
148+
fn highest_index_first(v1: &[u8]) {
149+
let _ = v1[2] + v1[1] + v1[0];
150+
}
151+
142152
fn main() {}

tests/ui/missing_asserts_for_indexing_unfixable.rs

+6
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,10 @@ pub fn issue11856(values: &[i32]) -> usize {
7373
ascending.len()
7474
}
7575

76+
fn assert_after_indexing(v1: &[u8]) {
77+
let _ = v1[1] + v1[2];
78+
//~^ ERROR: indexing into a slice multiple times without an `assert`
79+
assert!(v1.len() > 2);
80+
}
81+
7682
fn main() {}

tests/ui/missing_asserts_for_indexing_unfixable.stderr

+20-1
Original file line numberDiff line numberDiff line change
@@ -180,5 +180,24 @@ LL | let _ = x[0] + x[1];
180180
| ^^^^
181181
= note: asserting the length before indexing will elide bounds checks
182182

183-
error: aborting due to 8 previous errors
183+
error: indexing into a slice multiple times without an `assert`
184+
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13
185+
|
186+
LL | let _ = v1[1] + v1[2];
187+
| ^^^^^^^^^^^^^
188+
|
189+
= help: consider asserting the length before indexing: `assert!(v1.len() > 2);`
190+
note: slice indexed here
191+
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13
192+
|
193+
LL | let _ = v1[1] + v1[2];
194+
| ^^^^^
195+
note: slice indexed here
196+
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:21
197+
|
198+
LL | let _ = v1[1] + v1[2];
199+
| ^^^^^
200+
= note: asserting the length before indexing will elide bounds checks
201+
202+
error: aborting due to 9 previous errors
184203

0 commit comments

Comments
 (0)