rust-lang / rust-lang/rust-clippy

Invalid `manual_map` suggestion for `#[track_caller]` function

Open
#17,706 1 comment 0 reactions 1 assignee View on GitHub

@willwang-io is already working on this.

Since Sep 8, 2026.

C-bug I-false-positive I-suggestion-causes-bug
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

Summary

The manual_map lint does not take into account that introducing a closure breaks propagation of #[track_caller].

Lint Name

manual_map

Reproducer

I tried this code:

#[track_caller]
pub fn timeout<F>(duration: Duration, future: F) -> Timeout<F::IntoFuture>
where
    F: IntoFuture,
{
    Timeout {
        value: future.into_future(),
        delay: match Instant::now().checked_add(duration) {
            Some(deadline) => Some(Sleep::new_timeout(deadline, trace::caller_location())),
            None => None,
        },
    }
}

https://github.com/tokio-rs/tokio/blob/6bea73e4c1ffd1acbd3065cd6a6421585004042e/tokio/src/time/timeout.rs#L85-L99

I saw this happen:

warning: manual implementation of `Option::map`
  --> tokio/src/time/timeout.rs:93:16
   |
93 |           delay: match Instant::now().checked_add(duration) {
   |  ________________^
94 | |             Some(deadline) => Some(Sleep::new_timeout(deadline, trace:...
95 | |             None => None,
96 | |         },
   | |_________^ help: try: `Instant::now().checked_add(duration).map(|deadline| Sleep::new_timeout(deadline, trace::caller_location()))`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.97.0/index.html#manual_map
   = note: `#[warn(clippy::manual_map)]` on by default

But this suggestion breaks this test:

#[test]
fn timeout_panic_caller() -> Result<(), Box<dyn Error>> {
    let panic_location_file = test_panic(|| {
        // Runtime without `enable_time` so it has no current timer set.
        let rt = Builder::new_current_thread().build().unwrap();
        rt.block_on(async {
            let _timeout = timeout(Duration::from_millis(5), future::pending::<()>());
        });
    });

    // The panic location should be in this file
    assert_eq!(&panic_location_file.unwrap(), file!());

    Ok(())
}
running 1 test
test timeout_panic_caller ... FAILED

failures:

---- timeout_panic_caller stdout ----

thread 'timeout_panic_caller' (1565071) panicked at tokio/tests/time_panic.rs:135:5:
assertion `left == right` failed
  left: "tokio/src/time/timeout.rs"
 right: "tokio/tests/time_panic.rs"
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    timeout_panic_caller

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 5 filtered out; finished in 0.00s

error: test failed, to rerun pass `--test time_panic`

Clippy should not emit this warning when the surrounding function is #[track_caller] and the closure invokes an #[track_caller] method.

Version
rustc 1.97.0 (2d8144b78 2026-07-07)
binary: rustc
commit-hash: 2d8144b7880597b6e6d3dfd63a9a9efae3f533d3
commit-date: 2026-07-07
host: x86_64-unknown-linux-gnu
release: 1.97.0
LLVM version: 22.1.6
Additional Labels

@rustbot label +I-suggestion-causes-bug

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.