needless_collect: confusing suggestion & questionable transformation #6164
Labels
C-bug
Category: Clippy is not doing the correct thing
C-enhancement
Category: Enhancement of lints, like adding more cases or adding help messages
E-medium
Call for participation: Medium difficulty level problem and requires some initial experience.
good-first-issue
These issues are a good way to get started with Clippy
I-false-positive
Issue: The lint was triggered on code it shouldn't have
L-nursery
Lint: Currently in the nursery group
L-suggestion
Lint: Improving, adding or fixing lint suggestions
Take the following example:
(Note that in a real-world scenario, all of
mock_array
, the number of iterations of thefor
loop, and the input to.contains()
might all be dynamic. Don't read too much into that things in the example are constant.)Running
cargo clippy
on this gives the following output:There are two issues with this:
The suggestion is elided
The bottom of the diagnostic from
clippy
quotes lines 3–8; that leaves the reader wondering … why? If I remove those lines (here, a large comment, but in our actual code that we hit this with, it was a mix of code, comments, and blank lines), we see then thatclippy
is attempting to output a suggestion:Unfortunately, in both our real world case & in this test case, the actual suggestion gets elided.
The suggestion is questionable
Here, the suggestion to not use
collect
in combination with thefor
loop means that we are now repeating the.map()
call multiple times, for each item. In the original code, the work of themap
is done once, outside the for loop, and the collected results then allow us to amortize the cost of that over the many searches done by thefor
loop. Whether it is worth it to repeat the.map()
in each search or to.collect
the result into aVec
that thefor
loop can re-use depends heavily on what themap
is doing.In our test case, we're lowercasing a bunch of strings. Each of the
to_lowercase
calls will require a heap allocation to store the result. It's enough that I think the original author's use of aVec
isn't wrong.Meta
cargo clippy -V
: clippy 0.0.212 (18bf6b4f0 2020-10-07)rustc -Vv
:More meta
Worse, in our real world case, since clippy's suggestion was truncated, this resulted in dropping the
.collect
call, making the iteratormut
forany
, and replacingcontains
withany
: this transformation is subtly different from the suggestion clippy failed to make: we don't move the creation of the iterator into the for loop; on subsequent passes through the loop, we re-use the partially or fully exhausted iterator. We caught this in code-review, and the subsequent discussion led to this bug report.The original PR for this lint seemed to just detect cases of
….collect().contains()
, which I think is good, always. A subsequent PR added a check for indirection, that is, to still lint that pattern even if we break up the.collect()
and.contains()
calls, like,Which still mostly makes sense; I think the trouble is when that indirection crosses into a loop of some sort. Then, you're not just doing whatever iterator work you were doing once, you're doing it once & amortizing it across the loop. But, the lint doesn't detect that, I think.
I think the some of the relevant code is this? https://github.com/rust-lang/rust/blob/085e4170873f3e411c87ee009572f7d2b5130856/src/tools/clippy/clippy_lints/src/loops.rs#L2598-L2640
The text was updated successfully, but these errors were encountered: