Auto merge of #10692 - y21:missing-asserts, r=Alexendoo

new lint: `missing_asserts_for_indexing`

Fixes #8296

This lint looks for repeated slice indexing and suggests adding an `assert!` beforehand that helps LLVM elide bounds checks. The lint documentation has an example.

I'm not really sure what category this should be in. It seems like a nice lint for the `perf` category but I suspect this has a pretty high FP rate, so it might have to be a pedantic lint or something.
I'm also not sure about the name. If someone knows a better name for this lint, I'd be fine with changing it.

changelog: new lint [`missing_asserts_for_indexing`]
This commit is contained in:
bors 2023-08-31 23:48:23 +00:00
commit 79c684dde0
9 changed files with 1101 additions and 0 deletions

View File

@ -5132,6 +5132,7 @@ Released 2018-09-13
[`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters
[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
[`missing_assert_message`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_assert_message
[`missing_asserts_for_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_asserts_for_indexing
[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames

View File

@ -457,6 +457,7 @@
crate::misc_early::ZERO_PREFIXED_LITERAL_INFO,
crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO,
crate::missing_assert_message::MISSING_ASSERT_MESSAGE_INFO,
crate::missing_asserts_for_indexing::MISSING_ASSERTS_FOR_INDEXING_INFO,
crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO,
crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO,
crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO,

View File

@ -210,6 +210,7 @@
mod misc_early;
mod mismatching_type_param_order;
mod missing_assert_message;
mod missing_asserts_for_indexing;
mod missing_const_for_fn;
mod missing_doc;
mod missing_enforced_import_rename;
@ -1100,6 +1101,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns));
store.register_late_pass(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default());
store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls));
store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View File

@ -0,0 +1,391 @@
use std::mem;
use std::ops::ControlFlow;
use clippy_utils::comparisons::{normalize_comparison, Rel};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use clippy_utils::visitors::for_each_expr;
use clippy_utils::{eq_expr_value, hash_expr, higher};
use rustc_ast::{LitKind, RangeLimits};
use rustc_data_structures::unhash::UnhashMap;
use rustc_errors::{Applicability, Diagnostic};
use rustc_hir::{BinOp, Block, Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
/// Checks for repeated slice indexing without asserting beforehand that the length
/// is greater than the largest index used to index into the slice.
///
/// ### Why is this bad?
/// In the general case where the compiler does not have a lot of information
/// about the length of a slice, indexing it repeatedly will generate a bounds check
/// for every single index.
///
/// Asserting that the length of the slice is at least as large as the largest value
/// to index beforehand gives the compiler enough information to elide the bounds checks,
/// effectively reducing the number of bounds checks from however many times
/// the slice was indexed to just one (the assert).
///
/// ### Drawbacks
/// False positives. It is, in general, very difficult to predict how well
/// the optimizer will be able to elide bounds checks and it very much depends on
/// the surrounding code. For example, indexing into the slice yielded by the
/// [`slice::chunks_exact`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.chunks_exact)
/// iterator will likely have all of the bounds checks elided even without an assert
/// if the `chunk_size` is a constant.
///
/// Asserts are not tracked across function calls. Asserting the length of a slice
/// in a different function likely gives the optimizer enough information
/// about the length of a slice, but this lint will not detect that.
///
/// ### Example
/// ```rust
/// fn sum(v: &[u8]) -> u8 {
/// // 4 bounds checks
/// v[0] + v[1] + v[2] + v[3]
/// }
/// ```
/// Use instead:
/// ```rust
/// fn sum(v: &[u8]) -> u8 {
/// assert!(v.len() > 4);
/// // no bounds checks
/// v[0] + v[1] + v[2] + v[3]
/// }
/// ```
#[clippy::version = "1.70.0"]
pub MISSING_ASSERTS_FOR_INDEXING,
restriction,
"indexing into a slice multiple times without an `assert`"
}
declare_lint_pass!(MissingAssertsForIndexing => [MISSING_ASSERTS_FOR_INDEXING]);
fn report_lint<F>(cx: &LateContext<'_>, full_span: Span, msg: &str, indexes: &[Span], f: F)
where
F: FnOnce(&mut Diagnostic),
{
span_lint_and_then(cx, MISSING_ASSERTS_FOR_INDEXING, full_span, msg, |diag| {
f(diag);
for span in indexes {
diag.span_note(*span, "slice indexed here");
}
diag.note("asserting the length before indexing will elide bounds checks");
});
}
#[derive(Copy, Clone, Debug)]
enum LengthComparison {
/// `v.len() < 5`
LengthLessThanInt,
/// `5 < v.len()`
IntLessThanLength,
/// `v.len() <= 5`
LengthLessThanOrEqualInt,
/// `5 <= v.len()`
IntLessThanOrEqualLength,
}
/// Extracts parts out of a length comparison expression.
///
/// E.g. for `v.len() > 5` this returns `Some((LengthComparison::IntLessThanLength, 5, `v.len()`))`
fn len_comparison<'hir>(
bin_op: BinOp,
left: &'hir Expr<'hir>,
right: &'hir Expr<'hir>,
) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> {
macro_rules! int_lit_pat {
($id:ident) => {
ExprKind::Lit(Spanned {
node: LitKind::Int($id, _),
..
})
};
}
// normalize comparison, `v.len() > 4` becomes `4 < v.len()`
// this simplifies the logic a bit
let (op, left, right) = normalize_comparison(bin_op.node, left, right)?;
match (op, &left.kind, &right.kind) {
(Rel::Lt, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanLength, *left as usize, right)),
(Rel::Lt, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanInt, *right as usize, left)),
(Rel::Le, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanOrEqualLength, *left as usize, right)),
(Rel::Le, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanOrEqualInt, *right as usize, left)),
_ => None,
}
}
/// Attempts to extract parts out of an `assert!`-like expression
/// in the form `assert!(some_slice.len() > 5)`.
///
/// `assert!` has expanded to an if expression at the HIR, so this
/// actually works not just with `assert!` specifically, but anything
/// that has a never type expression in the `then` block (e.g. `panic!`).
fn assert_len_expr<'hir>(
cx: &LateContext<'_>,
expr: &'hir Expr<'hir>,
) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> {
if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr)
&& let ExprKind::Unary(UnOp::Not, condition) = &cond.kind
&& let ExprKind::Binary(bin_op, left, right) = &condition.kind
&& let Some((cmp, asserted_len, slice_len)) = len_comparison(*bin_op, left, right)
&& let ExprKind::MethodCall(method, recv, ..) = &slice_len.kind
&& cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
&& method.ident.name == sym::len
// check if `then` block has a never type expression
&& let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind
&& cx.typeck_results().expr_ty(then_expr).is_never()
{
Some((cmp, asserted_len, recv))
} else {
None
}
}
#[derive(Debug)]
enum IndexEntry<'hir> {
/// `assert!` without any indexing (so far)
StrayAssert {
asserted_len: usize,
comparison: LengthComparison,
assert_span: Span,
slice: &'hir Expr<'hir>,
},
/// `assert!` with indexing
///
/// We also store the highest index to be able to check
/// if the `assert!` asserts the right length.
AssertWithIndex {
highest_index: usize,
asserted_len: usize,
assert_span: Span,
slice: &'hir Expr<'hir>,
indexes: Vec<Span>,
comparison: LengthComparison,
},
/// Indexing without an `assert!`
IndexWithoutAssert {
highest_index: usize,
indexes: Vec<Span>,
slice: &'hir Expr<'hir>,
},
}
impl<'hir> IndexEntry<'hir> {
pub fn slice(&self) -> &'hir Expr<'hir> {
match self {
IndexEntry::StrayAssert { slice, .. }
| IndexEntry::AssertWithIndex { slice, .. }
| IndexEntry::IndexWithoutAssert { slice, .. } => slice,
}
}
pub fn index_spans(&self) -> Option<&[Span]> {
match self {
IndexEntry::StrayAssert { .. } => None,
IndexEntry::AssertWithIndex { indexes, .. } | IndexEntry::IndexWithoutAssert { indexes, .. } => {
Some(indexes)
},
}
}
}
/// Extracts the upper index of a slice indexing expression.
///
/// E.g. for `5` this returns `Some(5)`, for `..5` this returns `Some(4)`,
/// for `..=5` this returns `Some(5)`
fn upper_index_expr(expr: &Expr<'_>) -> Option<usize> {
if let ExprKind::Lit(lit) = &expr.kind && let LitKind::Int(index, _) = lit.node {
Some(index as usize)
} else if let Some(higher::Range { end: Some(end), limits, .. }) = higher::Range::hir(expr)
&& let ExprKind::Lit(lit) = &end.kind
&& let LitKind::Int(index @ 1.., _) = lit.node
{
match limits {
RangeLimits::HalfOpen => Some(index as usize - 1),
RangeLimits::Closed => Some(index as usize),
}
} else {
None
}
}
/// Checks if the expression is an index into a slice and adds it to `indexes`
fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap<u64, Vec<IndexEntry<'hir>>>) {
if let ExprKind::Index(slice, index_lit, _) = expr.kind
&& cx.typeck_results().expr_ty_adjusted(slice).peel_refs().is_slice()
&& let Some(index) = upper_index_expr(index_lit)
{
let hash = hash_expr(cx, slice);
let indexes = map.entry(hash).or_default();
let entry = indexes.iter_mut().find(|entry| eq_expr_value(cx, entry.slice(), slice));
if let Some(entry) = entry {
match entry {
IndexEntry::StrayAssert { asserted_len, comparison, assert_span, slice } => {
*entry = IndexEntry::AssertWithIndex {
highest_index: index,
asserted_len: *asserted_len,
assert_span: *assert_span,
slice,
indexes: vec![expr.span],
comparison: *comparison,
};
},
IndexEntry::IndexWithoutAssert { highest_index, indexes, .. }
| IndexEntry::AssertWithIndex { highest_index, indexes, .. } => {
indexes.push(expr.span);
*highest_index = (*highest_index).max(index);
},
}
} else {
indexes.push(IndexEntry::IndexWithoutAssert {
highest_index: index,
indexes: vec![expr.span],
slice,
});
}
}
}
/// Checks if the expression is an `assert!` expression and adds it to `asserts`
fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap<u64, Vec<IndexEntry<'hir>>>) {
if let Some((comparison, asserted_len, slice)) = assert_len_expr(cx, expr) {
let hash = hash_expr(cx, slice);
let indexes = map.entry(hash).or_default();
let entry = indexes.iter_mut().find(|entry| eq_expr_value(cx, entry.slice(), slice));
if let Some(entry) = entry {
if let IndexEntry::IndexWithoutAssert {
highest_index,
indexes,
slice,
} = entry
{
*entry = IndexEntry::AssertWithIndex {
highest_index: *highest_index,
indexes: mem::take(indexes),
slice,
assert_span: expr.span,
comparison,
asserted_len,
};
}
} else {
indexes.push(IndexEntry::StrayAssert {
asserted_len,
comparison,
assert_span: expr.span,
slice,
});
}
}
}
/// Inspects indexes and reports lints.
///
/// Called at the end of this lint after all indexing and `assert!` expressions have been collected.
fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>>>) {
for bucket in map.values() {
for entry in bucket {
let Some(full_span) = entry
.index_spans()
.and_then(|spans| spans.first().zip(spans.last()))
.map(|(low, &high)| low.to(high))
else {
continue;
};
match entry {
IndexEntry::AssertWithIndex {
highest_index,
asserted_len,
indexes,
comparison,
assert_span,
slice,
} if indexes.len() > 1 => {
// if we have found an `assert!`, let's also check that it's actually right
// and if it convers the highest index and if not, suggest the correct length
let sugg = match comparison {
// `v.len() < 5` and `v.len() <= 5` does nothing in terms of bounds checks.
// The user probably meant `v.len() > 5`
LengthComparison::LengthLessThanInt | LengthComparison::LengthLessThanOrEqualInt => Some(
format!("assert!({}.len() > {highest_index})", snippet(cx, slice.span, "..")),
),
// `5 < v.len()` == `v.len() > 5`
LengthComparison::IntLessThanLength if asserted_len < highest_index => Some(format!(
"assert!({}.len() > {highest_index})",
snippet(cx, slice.span, "..")
)),
// `5 <= v.len() == `v.len() >= 5`
LengthComparison::IntLessThanOrEqualLength if asserted_len <= highest_index => Some(format!(
"assert!({}.len() > {highest_index})",
snippet(cx, slice.span, "..")
)),
_ => None,
};
if let Some(sugg) = sugg {
report_lint(
cx,
full_span,
"indexing into a slice multiple times with an `assert` that does not cover the highest index",
indexes,
|diag| {
diag.span_suggestion(
*assert_span,
"provide the highest index that is indexed with",
sugg,
Applicability::MachineApplicable,
);
},
);
}
},
IndexEntry::IndexWithoutAssert {
indexes,
highest_index,
slice,
} if indexes.len() > 1 => {
// if there was no `assert!` but more than one index, suggest
// adding an `assert!` that covers the highest index
report_lint(
cx,
full_span,
"indexing into a slice multiple times without an `assert`",
indexes,
|diag| {
diag.help(format!(
"consider asserting the length before indexing: `assert!({}.len() > {highest_index});`",
snippet(cx, slice.span, "..")
));
},
);
},
_ => {},
}
}
}
}
impl LateLintPass<'_> for MissingAssertsForIndexing {
fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) {
let mut map = UnhashMap::default();
for_each_expr(block, |expr| {
check_index(cx, expr, &mut map);
check_assert(cx, expr, &mut map);
ControlFlow::<!, ()>::Continue(())
});
report_indexes(cx, &map);
}
}

View File

@ -0,0 +1,121 @@
#![allow(unused)]
#![warn(clippy::missing_asserts_for_indexing)]
// ok
fn sum_with_assert(v: &[u8]) -> u8 {
assert!(v.len() > 4);
v[0] + v[1] + v[2] + v[3] + v[4]
}
// ok
fn sum_with_assert_other_way(v: &[u8]) -> u8 {
assert!(5 <= v.len());
v[0] + v[1] + v[2] + v[3] + v[4]
}
// ok
fn sum_with_assert_ge(v: &[u8]) -> u8 {
assert!(v.len() >= 5);
v[0] + v[1] + v[2] + v[3] + v[4]
}
// ok
fn sum_with_assert_ge_other_way(v: &[u8]) -> u8 {
assert!(4 < v.len());
v[0] + v[1] + v[2] + v[3] + v[4]
}
fn sum_with_assert_lt(v: &[u8]) -> u8 {
assert!(v.len() > 4);
v[0] + v[1] + v[2] + v[3] + v[4]
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
}
fn sum_with_assert_le(v: &[u8]) -> u8 {
assert!(v.len() > 4);
v[0] + v[1] + v[2] + v[3] + v[4]
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
}
fn sum_with_incorrect_assert_len(v: &[u8]) -> u8 {
assert!(v.len() > 4);
v[0] + v[1] + v[2] + v[3] + v[4]
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
}
fn sum_with_incorrect_assert_len2(v: &[u8]) -> u8 {
assert!(v.len() > 4);
v[0] + v[1] + v[2] + v[3] + v[4]
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
}
// ok, don't lint for single array access
fn single_access(v: &[u8]) -> u8 {
v[0]
}
// ok
fn subslice_ok(v: &[u8]) {
assert!(v.len() > 3);
let _ = v[0];
let _ = v[1..4];
}
fn subslice_bad(v: &[u8]) {
assert!(v.len() > 3);
let _ = v[0];
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
let _ = v[1..4];
}
// ok
fn subslice_inclusive_ok(v: &[u8]) {
assert!(v.len() > 4);
let _ = v[0];
let _ = v[1..=4];
}
fn subslice_inclusive_bad(v: &[u8]) {
assert!(v.len() > 4);
let _ = v[0];
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
let _ = v[1..=4];
}
fn index_different_slices_ok(v1: &[u8], v2: &[u8]) {
assert!(v1.len() > 12);
assert!(v2.len() > 15);
let _ = v1[0] + v1[12];
let _ = v2[5] + v2[15];
}
fn index_different_slices_wrong_len(v1: &[u8], v2: &[u8]) {
assert!(v1.len() > 12);
assert!(v2.len() > 15);
let _ = v1[0] + v1[12];
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
let _ = v2[5] + v2[15];
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
}
fn index_different_slices_one_wrong_len(v1: &[u8], v2: &[u8]) {
assert!(v1.len() > 12);
assert!(v2.len() > 15);
let _ = v1[0] + v1[12];
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
let _ = v2[5] + v2[15];
}
fn side_effect() -> &'static [u8] {
&[]
}
fn index_side_effect_expr() {
let _ = side_effect()[0] + side_effect()[1];
}
// ok, single access for different slices
fn index_different_slice_in_same_expr(v1: &[u8], v2: &[u8]) {
let _ = v1[0] + v2[1];
}
fn main() {}

View File

@ -0,0 +1,121 @@
#![allow(unused)]
#![warn(clippy::missing_asserts_for_indexing)]
// ok
fn sum_with_assert(v: &[u8]) -> u8 {
assert!(v.len() > 4);
v[0] + v[1] + v[2] + v[3] + v[4]
}
// ok
fn sum_with_assert_other_way(v: &[u8]) -> u8 {
assert!(5 <= v.len());
v[0] + v[1] + v[2] + v[3] + v[4]
}
// ok
fn sum_with_assert_ge(v: &[u8]) -> u8 {
assert!(v.len() >= 5);
v[0] + v[1] + v[2] + v[3] + v[4]
}
// ok
fn sum_with_assert_ge_other_way(v: &[u8]) -> u8 {
assert!(4 < v.len());
v[0] + v[1] + v[2] + v[3] + v[4]
}
fn sum_with_assert_lt(v: &[u8]) -> u8 {
assert!(v.len() < 5);
v[0] + v[1] + v[2] + v[3] + v[4]
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
}
fn sum_with_assert_le(v: &[u8]) -> u8 {
assert!(v.len() <= 5);
v[0] + v[1] + v[2] + v[3] + v[4]
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
}
fn sum_with_incorrect_assert_len(v: &[u8]) -> u8 {
assert!(v.len() > 3);
v[0] + v[1] + v[2] + v[3] + v[4]
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
}
fn sum_with_incorrect_assert_len2(v: &[u8]) -> u8 {
assert!(v.len() >= 4);
v[0] + v[1] + v[2] + v[3] + v[4]
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
}
// ok, don't lint for single array access
fn single_access(v: &[u8]) -> u8 {
v[0]
}
// ok
fn subslice_ok(v: &[u8]) {
assert!(v.len() > 3);
let _ = v[0];
let _ = v[1..4];
}
fn subslice_bad(v: &[u8]) {
assert!(v.len() >= 3);
let _ = v[0];
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
let _ = v[1..4];
}
// ok
fn subslice_inclusive_ok(v: &[u8]) {
assert!(v.len() > 4);
let _ = v[0];
let _ = v[1..=4];
}
fn subslice_inclusive_bad(v: &[u8]) {
assert!(v.len() >= 4);
let _ = v[0];
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
let _ = v[1..=4];
}
fn index_different_slices_ok(v1: &[u8], v2: &[u8]) {
assert!(v1.len() > 12);
assert!(v2.len() > 15);
let _ = v1[0] + v1[12];
let _ = v2[5] + v2[15];
}
fn index_different_slices_wrong_len(v1: &[u8], v2: &[u8]) {
assert!(v1.len() >= 12);
assert!(v2.len() >= 15);
let _ = v1[0] + v1[12];
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
let _ = v2[5] + v2[15];
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
}
fn index_different_slices_one_wrong_len(v1: &[u8], v2: &[u8]) {
assert!(v1.len() >= 12);
assert!(v2.len() > 15);
let _ = v1[0] + v1[12];
//~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
let _ = v2[5] + v2[15];
}
fn side_effect() -> &'static [u8] {
&[]
}
fn index_side_effect_expr() {
let _ = side_effect()[0] + side_effect()[1];
}
// ok, single access for different slices
fn index_different_slice_in_same_expr(v1: &[u8], v2: &[u8]) {
let _ = v1[0] + v2[1];
}
fn main() {}

View File

@ -0,0 +1,252 @@
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
--> $DIR/missing_asserts_for_indexing.rs:30:5
|
LL | assert!(v.len() < 5);
| -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:30:5
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:30:12
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:30:19
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:30:26
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:30:33
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
= note: asserting the length before indexing will elide bounds checks
= note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings`
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
--> $DIR/missing_asserts_for_indexing.rs:36:5
|
LL | assert!(v.len() <= 5);
| --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:36:5
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:36:12
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:36:19
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:36:26
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:36:33
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
= note: asserting the length before indexing will elide bounds checks
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
--> $DIR/missing_asserts_for_indexing.rs:42:5
|
LL | assert!(v.len() > 3);
| -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:42:5
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:42:12
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:42:19
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:42:26
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:42:33
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
= note: asserting the length before indexing will elide bounds checks
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
--> $DIR/missing_asserts_for_indexing.rs:48:5
|
LL | assert!(v.len() >= 4);
| --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:48:5
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:48:12
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:48:19
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:48:26
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:48:33
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
= note: asserting the length before indexing will elide bounds checks
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
--> $DIR/missing_asserts_for_indexing.rs:66:13
|
LL | assert!(v.len() >= 3);
| --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 3)`
LL | let _ = v[0];
| _____________^
LL | |
LL | | let _ = v[1..4];
| |___________________^
|
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:66:13
|
LL | let _ = v[0];
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:68:13
|
LL | let _ = v[1..4];
| ^^^^^^^
= note: asserting the length before indexing will elide bounds checks
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
--> $DIR/missing_asserts_for_indexing.rs:80:13
|
LL | assert!(v.len() >= 4);
| --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
LL | let _ = v[0];
| _____________^
LL | |
LL | | let _ = v[1..=4];
| |____________________^
|
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:80:13
|
LL | let _ = v[0];
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:82:13
|
LL | let _ = v[1..=4];
| ^^^^^^^^
= note: asserting the length before indexing will elide bounds checks
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
--> $DIR/missing_asserts_for_indexing.rs:95:13
|
LL | assert!(v1.len() >= 12);
| ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)`
LL | assert!(v2.len() >= 15);
LL | let _ = v1[0] + v1[12];
| ^^^^^^^^^^^^^^
|
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:95:13
|
LL | let _ = v1[0] + v1[12];
| ^^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:95:21
|
LL | let _ = v1[0] + v1[12];
| ^^^^^^
= note: asserting the length before indexing will elide bounds checks
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
--> $DIR/missing_asserts_for_indexing.rs:97:13
|
LL | assert!(v2.len() >= 15);
| ----------------------- help: provide the highest index that is indexed with: `assert!(v2.len() > 15)`
...
LL | let _ = v2[5] + v2[15];
| ^^^^^^^^^^^^^^
|
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:97:13
|
LL | let _ = v2[5] + v2[15];
| ^^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:97:21
|
LL | let _ = v2[5] + v2[15];
| ^^^^^^
= note: asserting the length before indexing will elide bounds checks
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
--> $DIR/missing_asserts_for_indexing.rs:103:13
|
LL | assert!(v1.len() >= 12);
| ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)`
LL | assert!(v2.len() > 15);
LL | let _ = v1[0] + v1[12];
| ^^^^^^^^^^^^^^
|
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:103:13
|
LL | let _ = v1[0] + v1[12];
| ^^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing.rs:103:21
|
LL | let _ = v1[0] + v1[12];
| ^^^^^^
= note: asserting the length before indexing will elide bounds checks
error: aborting due to 9 previous errors

View File

@ -0,0 +1,49 @@
#![allow(unused)]
#![warn(clippy::missing_asserts_for_indexing)]
fn sum(v: &[u8]) -> u8 {
v[0] + v[1] + v[2] + v[3] + v[4]
//~^ ERROR: indexing into a slice multiple times without an `assert`
}
fn subslice(v: &[u8]) {
let _ = v[0];
//~^ ERROR: indexing into a slice multiple times without an `assert`
let _ = v[1..4];
}
fn variables(v: &[u8]) -> u8 {
let a = v[0];
//~^ ERROR: indexing into a slice multiple times without an `assert`
let b = v[1];
let c = v[2];
a + b + c
}
fn index_different_slices(v1: &[u8], v2: &[u8]) {
let _ = v1[0] + v1[12];
let _ = v2[5] + v2[15];
}
fn index_different_slices2(v1: &[u8], v2: &[u8]) {
assert!(v1.len() > 12);
let _ = v1[0] + v1[12];
let _ = v2[5] + v2[15];
}
struct Foo<'a> {
v: &'a [u8],
v2: &'a [u8],
}
fn index_struct_field(f: &Foo<'_>) {
let _ = f.v[0] + f.v[1];
//~^ ERROR: indexing into a slice multiple times without an `assert`
}
fn index_struct_different_fields(f: &Foo<'_>) {
// ok, different fields
let _ = f.v[0] + f.v2[1];
}
fn main() {}

View File

@ -0,0 +1,163 @@
error: indexing into a slice multiple times without an `assert`
--> $DIR/missing_asserts_for_indexing_unfixable.rs:5:5
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider asserting the length before indexing: `assert!(v.len() > 4);`
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:5:5
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:5:12
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:5:19
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:5:26
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:5:33
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
| ^^^^
= note: asserting the length before indexing will elide bounds checks
= note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings`
error: indexing into a slice multiple times without an `assert`
--> $DIR/missing_asserts_for_indexing_unfixable.rs:10:13
|
LL | let _ = v[0];
| _____________^
LL | |
LL | | let _ = v[1..4];
| |___________________^
|
= help: consider asserting the length before indexing: `assert!(v.len() > 3);`
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:10:13
|
LL | let _ = v[0];
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:12:13
|
LL | let _ = v[1..4];
| ^^^^^^^
= note: asserting the length before indexing will elide bounds checks
error: indexing into a slice multiple times without an `assert`
--> $DIR/missing_asserts_for_indexing_unfixable.rs:16:13
|
LL | let a = v[0];
| _____________^
LL | |
LL | | let b = v[1];
LL | | let c = v[2];
| |________________^
|
= help: consider asserting the length before indexing: `assert!(v.len() > 2);`
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:16:13
|
LL | let a = v[0];
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:18:13
|
LL | let b = v[1];
| ^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:19:13
|
LL | let c = v[2];
| ^^^^
= note: asserting the length before indexing will elide bounds checks
error: indexing into a slice multiple times without an `assert`
--> $DIR/missing_asserts_for_indexing_unfixable.rs:24:13
|
LL | let _ = v1[0] + v1[12];
| ^^^^^^^^^^^^^^
|
= help: consider asserting the length before indexing: `assert!(v1.len() > 12);`
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:24:13
|
LL | let _ = v1[0] + v1[12];
| ^^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:24:21
|
LL | let _ = v1[0] + v1[12];
| ^^^^^^
= note: asserting the length before indexing will elide bounds checks
error: indexing into a slice multiple times without an `assert`
--> $DIR/missing_asserts_for_indexing_unfixable.rs:25:13
|
LL | let _ = v2[5] + v2[15];
| ^^^^^^^^^^^^^^
|
= help: consider asserting the length before indexing: `assert!(v2.len() > 15);`
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:25:13
|
LL | let _ = v2[5] + v2[15];
| ^^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:25:21
|
LL | let _ = v2[5] + v2[15];
| ^^^^^^
= note: asserting the length before indexing will elide bounds checks
error: indexing into a slice multiple times without an `assert`
--> $DIR/missing_asserts_for_indexing_unfixable.rs:31:13
|
LL | let _ = v2[5] + v2[15];
| ^^^^^^^^^^^^^^
|
= help: consider asserting the length before indexing: `assert!(v2.len() > 15);`
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:31:13
|
LL | let _ = v2[5] + v2[15];
| ^^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:31:21
|
LL | let _ = v2[5] + v2[15];
| ^^^^^^
= note: asserting the length before indexing will elide bounds checks
error: indexing into a slice multiple times without an `assert`
--> $DIR/missing_asserts_for_indexing_unfixable.rs:40:13
|
LL | let _ = f.v[0] + f.v[1];
| ^^^^^^^^^^^^^^^
|
= help: consider asserting the length before indexing: `assert!(f.v.len() > 1);`
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:40:13
|
LL | let _ = f.v[0] + f.v[1];
| ^^^^^^
note: slice indexed here
--> $DIR/missing_asserts_for_indexing_unfixable.rs:40:22
|
LL | let _ = f.v[0] + f.v[1];
| ^^^^^^
= note: asserting the length before indexing will elide bounds checks
error: aborting due to 7 previous errors