rust-lang / rust-lang/rust-clippy
`unnecessary_lazy_evaluations` does not take cost of expression into account
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 13.5k
- Forks
- 2.2k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 32
Description
Summary
See reproducer.
Lint Name
unnecessary_lazy_evaluations
Reproducer
I tried this code [playground]:
#![allow(unused)]
struct ParsedItem<'a, T>(&'a [u8], T);
fn first_match<'a, T>(
options: impl IntoIterator<Item = (&'a [u8], T)>,
case_sensitive: bool,
) -> impl FnMut(&'a [u8]) -> Option<ParsedItem<'a, T>> {
move |input| None
}
fn parse(input: &[u8]) {
let zone_literal = first_match(
[
(&b"UT"[..], 0),
(&b"GMT"[..], 0),
(&b"EST"[..], -5),
(&b"EDT"[..], -4),
(&b"CST"[..], -6),
(&b"CDT"[..], -5),
(&b"MST"[..], -7),
(&b"MDT"[..], -6),
(&b"PST"[..], -8),
(&b"PDT"[..], -7),
],
false,
)(input)
.or_else(|| match input {
[
b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z',
rest @ ..,
] => Some(ParsedItem(rest, 0)),
_ => None,
});
}
NB: This is actual code from the time crate. first_match is only present to ensure compilation succeeds.
I saw this happen:
Checking playground v0.0.1 (/playground)
warning: unnecessary closure used to substitute value for `Option::None`
--> src/lib.rs:13:24
13 | let zone_literal = first_match(
| ________________________^
14 | | [
15 | | (&b"UT"[..], 0),
16 | | (&b"GMT"[..], 0),
... |
33 | | _ => None,
34 | | });
| |______^
|
= note: `#[warn(clippy::unnecessary_lazy_evaluations)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
help: use `or` instead
|
13 ~ let zone_literal = first_match(
14 + [
15 + (&b"UT"[..], 0),
16 + (&b"GMT"[..], 0),
17 + (&b"EST"[..], -5),
18 + (&b"EDT"[..], -4),
...
warning: `playground` (lib) generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 5.90s
I expected to see this happen:
unnecessary_lazy_evaluations should not be triggered. The expression inside the or_else closure is arbitrary and non-trivial. By requesting the user change or_else to or, whatever the closure contains is now eagerly evaluated at an unknown cost. As such I believe the lint should only trigger when the closure contains a literal or value that is statically known (setting aside const eval). This would ensure that only trivial cases are linted against.
Version
rustc 1.59.0 (9d1b2106e 2022-02-23)
binary: rustc
commit-hash: 9d1b2106e23b1abd32fce1f17267604a5102f57a
commit-date: 2022-02-23
host: x86_64-unknown-linux-gnu
release: 1.59.0
LLVM version: 13.0.0
Additional Labels
No response
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by locating the unnecessary_lazy_evaluations lint implementation and its tests in rust-lang/rust-clippy, then reproduce the supplied or_else example with the Rust 1.59.0 behavior described. Done means the lint no longer suggests or for a non-trivial closure while existing trivial cases remain covered by tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100