rust-lang / rust-lang/rust-clippy

needless_range_loop suggests loop that stops earlier

Open
#9,352 0 comments 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

C-bug I-false-positive
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

Summary

I ran clippy on some code that included a loop over a range of the form for i in start ..= end, which indexed into a slice of bytes, (bytes[i]) where end happened to be the length of the slice. Note that the original code includes an iteration of the loop where i is equal to bytes.len(), but that there was an additional condition checking for that case, preventing the out of bounds indexing.

In that case, clippy suggested an iterator expression that included calling .take(end + 1) on an iterator over bytes. Since end == bytes.len(), the take call does not actually do anything meaningful, since the iterator will run out before then. More worrying though is the fact that the previous behaviour of running the loop an additional time with i having a value of end was removed if the suggestion was used.

The example below has been reduced from the actual code, to remove irrelevant details.

Playground link

See related, but non-identical https://github.com/rust-lang/rust-clippy/issues/6930

Lint Name

needless_range_loop

Reproducer

I tried this code:

fn foo(bytes: &[u8], in_i: usize) -> bool {
    let len = bytes.len();

    for i in in_i + 1..=len {
        if i == len || bytes[i].is_ascii_whitespace() {
            return true
        }
    }

    false
}

I saw this happen:

warning: the loop variable `i` is used to index `bytes`
 --> src/main.rs:4:14
  |
4 |     for i in in_i + 1..=len {
  |              ^^^^^^^^^^^^^^
  |
  = note: `#[warn(clippy::needless_range_loop)]` on by default
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
help: consider using an iterator
  |
4 |     for (i, <item>) in bytes.iter().enumerate().take(len + 1).skip(in_i + 1) {
  |         ~~~~~~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I expected to see this happen:
Either no lint emitted, or a suggestion that had identical observed behaviour.

Version
rustc 1.62.0 (a8314ef7d 2022-06-27)
binary: rustc
commit-hash: a8314ef7d0ec7b75c336af2c9857bfaf43002bfc
commit-date: 2022-06-27
host: x86_64-unknown-linux-gnu
release: 1.62.0
LLVM version: 14.0.5

I was also able to replicate this on the playground with clippy 0.1.65 (2022-08-16 86c6ebe)
Additional Labels

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the needless_range_loop warning with the provided Rust example and compare the suggested iterator's behavior with the original loop. Inspect the lint's implementation and tests, then ensure the lint is suppressed or its suggestion preserves the extra i == len iteration, with regression coverage for the reproducer.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
devtools
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.