Skip to content

clippy::option_if_let_else suggestion doesn't compile when passing a matched reference to a function #10729

@Kage-Yami

Description

@Kage-Yami

Summary

If you're matching on some reference type (e.g. &Option<String>) and in the Some case are passing the produced inner reference (e.g. &String) to a function taking a reference, then the result from following clippy:option_if_let_else's suggestion will not compile.

Reproducer

I tried this code:

#![warn(clippy::option_if_let_else)]

pub fn reproduce(initial: &Option<String>) {
    match initial {
        Some(value) => do_something(value),
        None => {}
    }
}

fn do_something(_value: &str) {}

Clippy produces the following warning:

❯ cargo clippy
warning: use Option::map_or instead of an if let/else
 --> src/lib.rs:4:5
  |
4 | /     match initial {
5 | |         Some(value) => do_something(value),
6 | |         None => {}
7 | |     }
  | |_____^ help: try: `initial.map_or({}, |value| do_something(value))`
  |
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
note: the lint level is defined here
 --> src/lib.rs:1:9
  |
1 | #![warn(clippy::option_if_let_else)]
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: `clippy-bug-repro` (lib) generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 0.10s

Following the suggestion results in this code:

#![warn(clippy::option_if_let_else)]

pub fn reproduce(initial: &Option<String>) {
    initial.map_or({}, |value| do_something(value))
}

fn do_something(_value: &str) {}

Which fails to compile with:

❯ cargo build 
   Compiling clippy-bug-repro v0.1.0 (/home/yami/dev/repos/clippy-bug-repro)
error[E0308]: mismatched types
 --> src/lib.rs:4:45
  |
4 |     initial.map_or({}, |value| do_something(value))
  |                                ------------ ^^^^^
  |                                |            |
  |                                |            expected `&str`, found `String`
  |                                |            help: consider borrowing here: `&value`
  |                                arguments to this function are incorrect
  |
note: function defined here
 --> src/lib.rs:7:4
  |
7 | fn do_something(_value: &str) {}
  |    ^^^^^^^^^^^^ ------------

For more information about this error, try `rustc --explain E0308`.
error: could not compile `clippy-bug-repro` (lib) due to previous error
Further "whack-a-mole" results that may not be considered directly relevant to this issue, but are probably still useful as a reference.

Following that suggestion also does not compile:

❯ cargo build
   Compiling clippy-bug-repro v0.1.0 (/home/yami/dev/repos/clippy-bug-repro)
error[E0507]: cannot move out of `*initial` which is behind a shared reference
    --> src/lib.rs:4:5
     |
4    |     initial.map_or({}, |value| do_something(&value))
     |     ^^^^^^^^----------------------------------------
     |     |       |
     |     |       `*initial` moved due to this method call
     |     help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents
     |     move occurs because `*initial` has type `Option<String>`, which does not implement the `Copy` trait
     |
note: `Option::<T>::map_or` takes ownership of the receiver `self`, which moves `*initial`
    --> /home/yami/.rustup/toolchains/beta-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:1155:31
     |
1155 |     pub const fn map_or<U, F>(self, default: U, f: F) -> U
     |                               ^^^^
help: you can `clone` the value and consume it, but this might not be your desired behavior
     |
4    |     initial.clone().map_or({}, |value| do_something(&value))
     |             ++++++++

For more information about this error, try `rustc --explain E0507`.
error: could not compile `clippy-bug-repro` (lib) due to previous error

Following the suggestion to change initial.map_or(...) to initial.as_ref().map_or(...) starts to compile, but results in two more warnings:

❯ cargo clippy
warning: passing a unit value to a function
 --> src/lib.rs:4:5
  |
4 |     initial.as_ref().map_or({}, |value| do_something(&value))
  |     ^^^^^^^^^^^^^^^^^^^^^^^^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |                             |
  |                             help: use a unit literal instead: `()`
  |
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
  = note: `#[warn(clippy::unit_arg)]` on by default

warning: this expression creates a reference which is immediately dereferenced by the compiler
 --> src/lib.rs:4:54
  |
4 |     initial.as_ref().map_or({}, |value| do_something(&value))
  |                                                      ^^^^^^ help: change this to: `value`
  |
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
  = note: `#[warn(clippy::needless_borrow)]` on by default

warning: `clippy-bug-repro` (lib) generated 2 warnings (run `cargo clippy --fix --lib -p clippy-bug-repro` to apply 2 suggestions)
    Finished dev [unoptimized + debuginfo] target(s) in 0.10s

The clippy::unit_arg is fair, and I imagine is purely an artefact of my reproduction's None branch being a no-op.

However, the suggestion for clippy::needless_borrow actually undoes the original (incorrect) suggestion made in the context of clippy::option_if_let_else. I imagine this would need to be considered when "fixing" this issue.

Version

rustc 1.70.0-beta.2 (071f14baa 2023-04-30)
binary: rustc
commit-hash: 071f14baae931e17a5a99366bf216e76384cc4f6
commit-date: 2023-04-30
host: x86_64-unknown-linux-gnu
release: 1.70.0-beta.2
LLVM version: 16.0.2

Additional Labels

@rustbot label +I-suggestion-causes-error

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: Clippy is not doing the correct thingI-suggestion-causes-errorIssue: The suggestions provided by this Lint cause an ICE/error when applied

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions