Skip to content

unnecessary_lazy_evaluations does not take cost of expression into account #8522

Open
@jhpratt

Description

@jhpratt

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: Clippy is not doing the correct thingI-false-positiveIssue: The lint was triggered on code it shouldn't have

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions