Skip to content

fix missing_asserts_for_indexing ignores match for length#17400

Open
wasd243 wants to merge 5 commits into
rust-lang:masterfrom
wasd243:fix/missing_asserts_for_indexing-ignores-match-for-length
Open

fix missing_asserts_for_indexing ignores match for length#17400
wasd243 wants to merge 5 commits into
rust-lang:masterfrom
wasd243:fix/missing_asserts_for_indexing-ignores-match-for-length

Conversation

@wasd243

@wasd243 wasd243 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #17398

missing_asserts_for_indexing previously ignored match expressions, so indexing inside a match on the slice's length was linted even when the match arms already guarantee the indices are in bounds. For example:

match supported.len() { // `match` without assert
    0 => {},
    1 => println!("{}", supported[0]),
    _ => println!("{} or {}", supported[0], supported[1]),
}

Here the 0 and 1 arms rule out all lengths below 2, so the indexing in the wildcard arm cannot fail and the bounds checks are already elided without an assert!.

This PR suppresses the lint for an index inside a match on slice.len() when the non-wildcard arms are integer literals contiguously covering 0..=max (so the wildcard arm implies len > max) and the index is at most max.

The check is deliberately conservative and falls back to linting as before when:

  • the arm literals don't contiguously cover 0..=max (e.g. 5 => .., _ => .., where _ can still be 0)
  • any arm has a guard
  • an arm uses an or-pattern or any non-literal pattern (e.g. 0 | 1 => .. is safe in principle but not analyzed)

One behavioral note: suppression is per-index. If a match guarantees len > 1 but the wildcard arm indexes [0], [1] and [2], only [2] remains unsuppressed, which is equivalent to a single unchecked access and therefore doesn't lint (consistent with the lint never firing on single accesses).

changelog: [missing_asserts_for_indexing]: fix false positive when a match on the slice length guarantees the indices are in bounds

@rustbot rustbot added S-waiting-on-community-reviews Status: This is awaiting for positive reviews from the community before a maintainer is assigned. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Jul 10, 2026
@rustbot

rustbot commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the pull request, and welcome!You should hear from one of our reviewers after this PR is reviewed by at least 2 reviewers from the community

Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (S-waiting-on-review and S-waiting-on-author) stays updated, invoking these commands when appropriate:

  • @rustbot author: the review is finished, PR author should check the comments and take action accordingly
  • @rustbot review: the author is ready for a review, this PR will be queued again in the reviewer's queue

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown

Lintcheck changes for cd5f376

Lint Added Removed Changed
clippy::missing_asserts_for_indexing 0 2 1

This comment will be updated if you push new changes

Comment thread clippy_lints/src/missing_asserts_for_indexing.rs
Comment thread clippy_lints/src/missing_asserts_for_indexing.rs Outdated
&& is_slice_len_expr(cx, scrutinee, slice)
&& match_arms_bound_len(cx, index_expr, arms)
{
return arms.iter().any(|arm| is_wild(arm.pat));

@Gri-ffin Gri-ffin Jul 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumes that if we have a wildcard anywhere, then the entire match is safe, even if we are accessing indexes we can't safely assume.

fn foo(supported: &[u8]) {
    match supported.len() {
        0 => {},
        1 => {},
        2 => println!("{} {}", supported[0], supported[2]),
        _ => println!("{} {}", supported[0], supported[2]),
    }
}

View changes since the review

@wasd243 wasd243 Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed -- literal arms now require index < n (the arm pins len to exactly n), instead of the shared index <= max check.
Lints now:

match supported.len() {
    0 => {},
    1 => {},
    2 => println!("{} {}", supported[2], supported[3]), // both out of bounds for len == 2 → lints
    _ => {},
}

The example doesn't lint but for a pre-existing reason unrelated to the suppression:

match supported.len() {
    0 => {},
    1 => {},
    2 => println!("{} {}", supported[0], supported[2]), // [0] in bounds → suppressed; [2] NOT suppressed
    _ => println!("{} {}", supported[0], supported[2]), // both in bounds (len >= 3) → suppressed
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-community-reviews Status: This is awaiting for positive reviews from the community before a maintainer is assigned. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties

Projects

None yet

Development

Successfully merging this pull request may close these issues.

missing_asserts_for_indexing ignores match for length

3 participants