Skip to content

False positive for clippy::question_mark #13417

Closed

Description

@nojima

Summary

clippy claims that let ... else should be replaced by ? in the following code. However, actually replacing it with ? results in a compile error.

Lint Name

question_mark

Reproducer

I tried this code:

#![allow(clippy::disallowed_names)]

struct Foo {
    opt_x: Option<String>,
}

fn bar(foo: &mut Foo) -> Option<String> {
    let Some(ref x) = foo.opt_x else {
        return None;
    };
    let opt_y = Some(x.clone());
    std::mem::replace(&mut foo.opt_x, opt_y)
}

I saw this happen:

❱ cargo clippy
warning: this `let...else` may be rewritten with the `?` operator
  --> src/main.rs:8:5
   |
8  | /     let Some(ref x) = foo.opt_x else {
9  | |         return None;
10 | |     };
   | |______^ help: replace it with: `let ref x = foo.opt_x?;`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
   = note: `#[warn(clippy::question_mark)]` on by default

warning: `rust-sandbox` (bin "rust-sandbox") generated 1 warning
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.07s

I expected to see this happen:

No warnings should be emitted, or a correct correction should be suggested.

Version

rustc 1.81.0 (eeb90cda1 2024-09-04)
binary: rustc
commit-hash: eeb90cda1969383f56a2637cbd3037bdf598841c
commit-date: 2024-09-04
host: x86_64-unknown-linux-gnu
release: 1.81.0
LLVM version: 18.1.7

Additional Labels

No response

Activity

  1. added
    C-bugCategory: Clippy is not doing the correct thing
    I-false-positiveIssue: The lint was triggered on code it shouldn't have
    on Sep 19, 2024
  2. nojima commented on Sep 19, 2024

    @nojima
    Author

    Replacing the code as clippy suggests will result in the following compile error.

    #![allow(clippy::disallowed_names)]
    
    struct Foo {
        opt_x: Option<String>,
    }
    
    fn bar(foo: &mut Foo) -> Option<String> {
        let ref x = foo.opt_x()?; // ← replaced with ? operator
        let opt_y = Some(x.clone());
        std::mem::replace(&mut foo.opt_x, opt_y)
    }
    error[E0507]: cannot move out of `foo.opt_x` which is behind a mutable reference
       --> src/main.rs:8:17
        |
    8   |     let ref x = foo.opt_x?;
        |                 ^^^^^^^^^-
        |                 |
        |                 `foo.opt_x` moved due to this method call
        |                 move occurs because `foo.opt_x` has type `Option<String>`, which does not implement the `Copy` trait
        |
    

    This is because the ? operator takes ownership of opt_x. Therefore, we can avoid this error by inserting as_ref in the middle. The following code compiles:

    fn bar(foo: &mut Foo) -> Option<String> {
        let x = foo.opt_x.as_ref()?; // ← inserted as_ref() here
        ...
    }

    In general, a sentence of the following form

    let Some(ref var) = expr else { return None };

    should be replaced with

    let var = expr.as_ref()?;
  3. sanpii commented on Oct 26, 2024

    @sanpii

    And:

    let Some(ref mut var) = expr else { return None };

    with:

    let var = expr.as_mut()?;
  4. added a commit that references this issue on Mar 16, 2025
    4800557
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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