Skip to content

Conversation

meithecatte
Copy link
Contributor

@meithecatte meithecatte commented Mar 26, 2025

Reference PR:

This PR has two goals:

Background

This change concerns how precise closure captures interact with patterns. As a little known feature, patterns that require inspecting only part of a value will only cause that part of the value to get captured:

fn main() {
    let mut a = (21, 37);
    // only captures a.0, writing to a.1 does not invalidate the closure
    let mut f = || {
        let (ref mut x, _) = a;
        *x = 42;
    };
    a.1 = 69;
    f();
}

I was not able to find any discussion of this behavior being introduced, or discussion of its edge-cases, but it is documented in the Rust reference.

The currently stable behavior is as follows:

  • if any pattern contains a binding, the place it binds gets captured (implemented in current walk_pat)
  • patterns in refutable positions (match, if let, let ... else, but not destructuring let or destructuring function parameters) get processed as follows (maybe_read_scrutinee):
    • if matching against the pattern will at any point require inspecting a discriminant, or it includes a variable binding not followed by an @-pattern, capture the entire scrutinee by reference

You will note that this behavior is quite weird and it's hard to imagine a sensible rationale for at least some of its aspects. It has the following issues:

This PR aims to address all of the above issues. The new behavior is as follows:

  • like before, if a pattern contains a binding, the place it binds gets captured as required by the binding mode
  • if matching against the pattern requires inspecting a disciminant, the place whose discriminant needs to be inspected gets captured by reference

"requires inspecting a discriminant" is also used here to mean "compare something with a constant" and other such decisions. For types other than ADTs, the details are not interesting and aren't changing.

The breaking change

During closure capture analysis, matching an enum against a constructor is considered to require inspecting a discriminant if the enum has more than one variant. Notably, this is the case even if all the other variants happen to be uninhabited. This is motivated by implementation difficulties involved in querying whether types are inhabited before we're done with type inference – without moving mountains to make it happen, you hit this assert:

debug_assert!(!self.has_infer());

Now, because the previous implementation did not concern itself with capturing the discriminants for irrefutable patterns at all, this is a breaking change – the following example, adapted from the testsuite, compiles on current stable, but will not compile with this PR:

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Void {}

pub fn main() {
    let mut r = Result::<Void, (u32, u32)>::Err((0, 0));
    let mut f = || {
        let Err((ref mut a, _)) = r;
        *a = 1;
    };
    let mut g = || {
    //~^ ERROR: cannot borrow `r` as mutable more than once at a time
        let Err((_, ref mut b)) = r;
        *b = 2;
    };
    f();
    g();
    assert_eq!(r, Err((1, 2)));
}

Is the breaking change necessary?

One other option would be to double down, and introduce a set of syntactic rules for determining whether a sub-pattern is in an irrefutable position, instead of querying the types and checking how many variants there are.

This would not eliminate the breaking change, but it would limit it to more contrived examples, such as

let ((true, Err((ref mut a, _, _))) | (false, Err((_, ref mut a, _)))) = x;

In this example, the Errs would not be considered in an irrefutable position, because they are part of an or-pattern. However, current stable would treat this just like a tuple (bool, (T, U, _)).

While introducing such a distinction would limit the impact, I would say that the added complexity would not be commensurate with the benefit it introduces.

The new insta-stable behavior

If a pattern in a match expression or similar has parts it will never read, this part will not be captured anymore:

fn main() {
    let mut a = (21, 37);
    // now only captures a.0, instead of the whole a
    let mut f = || {
        match a {
            (ref mut x, _) => *x = 42,
        }
    };
    a.1 = 69;
    f();
}

Note that this behavior was pretty much already present, but only accessible with this One Weird Trick™:

fn main() {
    let mut a = (21, 37);
    // both stable and this PR only capture a.0, because of the no-op @-pattern
    let mut f = || {
        match a {
            (ref mut x @ _, _) => *x = 42,
        }
    };
    a.1 = 69;
    f();
}

Implementation notes

The PR has two main commits:

The new logic stops making the distinction between one particular example that used to work, and another ICE, tracked as #119786. As this requires an unstable feature, I am leaving this as future work.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Mar 26, 2025
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@meithecatte meithecatte force-pushed the expr-use-visitor branch 2 times, most recently from c225f17 to ce47a4c Compare March 26, 2025 16:21
@rust-log-analyzer

This comment has been minimized.

@meithecatte meithecatte changed the title [WIP] ExprUseVisitor: properly report discriminant reads ExprUseVisitor: properly report discriminant reads Mar 26, 2025
@meithecatte meithecatte marked this pull request as ready for review March 26, 2025 17:28
@rustbot
Copy link
Collaborator

rustbot commented Mar 26, 2025

This PR changes a file inside tests/crashes. If a crash was fixed, please move into the corresponding ui subdir and add 'Fixes #' to the PR description to autoclose the issue upon merge.

@meithecatte
Copy link
Contributor Author

Nadrieril suggested that this should be resolved through a breaking change – updated the PR description accordingly.

@rustbot label +needs-crater

r? @Nadrieril

@rustbot
Copy link
Collaborator

rustbot commented Mar 26, 2025

Error: Label needs-crater can only be set by Rust team members

Please file an issue on GitHub at triagebot if there's a problem with this bot, or reach out on #t-infra on Zulip.

@jieyouxu jieyouxu added the needs-crater This change needs a crater run to check for possible breakage in the ecosystem. label Mar 26, 2025
@meithecatte
Copy link
Contributor Author

@compiler-errors You've requested that the fix for #137553 land in a separate PR. However, ironically, the breaking changes are actually required by #137467 and not #137553. Do you think the removal of the now-obsolete maybe_read_scrutinee should happen in a separate PR, or should I do it here so that it also benefits from the crater run?

@compiler-errors
Copy link
Member

We can crater both together if you think they're not worth separating. I was just trying to accelerate landing the parts that are obviously-not-breaking but it's up to you if you think that effort is worth it or if you're willing to be patient about waiting for the breaking parts (and FCP, etc).

@bors try

bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 26, 2025
ExprUseVisitor: properly report discriminant reads

This PR fixes rust-lang#137467. In order to do so, it needs to introduce a small breaking change surrounding the interaction of closure captures with matching against enums with uninhabited variants. Yes – to fix an ICE!

## Background

The current upvar inference code handles patterns in two parts:
- `ExprUseVisitor::walk_pat` finds the *bindings* being done by the pattern and captures the relevant parts
- `ExprUseVisitor::maybe_read_scrutinee` determines whether matching against the pattern will at any point require inspecting a discriminant, and if so, captures *the entire scrutinee*. It also has some weird logic around bindings, deciding to also capture the entire scrutinee if *pretty much any binding exists in the pattern*, with some weird behavior like rust-lang#137553.

Nevertheless, something like `|| let (a, _) = x;` will only capture `x.0`, because `maybe_read_scrutinee` does not run for irrefutable patterns at all. This causes issues like rust-lang#137467, where the closure wouldn't be capturing enough, because an irrefutable or-pattern can still require inspecting a discriminant, and the match lowering would then panic, because it couldn't find an appropriate upvar in the closure.

My thesis is that this is not a reasonable implementation. To that end, I intend to merge the functionality of both these parts into `walk_pat`, which will bring upvar inference closer to what the MIR lowering actually needs – both in making sure that necessary variables get captured, fixing rust-lang#137467, and in reducing the cases where redundant variables do – fixing rust-lang#137553.

This PR introduces the necessary logic into `walk_pat`, fixing rust-lang#137467. A subsequent PR will remove `maybe_read_scrutinee` entirely, which should now be redundant, fixing rust-lang#137553. The latter is still pending, as my current revision doesn't handle opaque types correctly for some reason I haven't looked into yet.

## The breaking change

The following example, adapted from the testsuite, compiles on current stable, but will not compile with this PR:

```rust
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Void {}

pub fn main() {
    let mut r = Result::<Void, (u32, u32)>::Err((0, 0));
    let mut f = || {
        let Err((ref mut a, _)) = r;
        *a = 1;
    };
    let mut g = || {
    //~^ ERROR: cannot borrow `r` as mutable more than once at a time
        let Err((_, ref mut b)) = r;
        *b = 2;
    };
    f();
    g();
    assert_eq!(r, Err((1, 2)));
}
```

The issue is that, to determine that matching against `Err` here doesn't require inspecting the discriminant, we need to query the `InhabitedPredicate` of the types involved. However, as upvar inference is done during typechecking, the relevant type might not yet be fully inferred. Because of this, performing such a check hits this assertion:

https://github.com/rust-lang/rust/blob/43f0014ef0f242418674f49052ed39b70f73bc1c/compiler/rustc_middle/src/ty/inhabitedness/mod.rs#L121

The code used to compile fine, but only because the compiler incorrectly assumed that patterns used within a `let` cannot possibly be inspecting any discriminants.

## Is the breaking change necessary?

One other option would be to double down, and introduce a deliberate semantics difference between `let $pat = $expr;` and `match $expr { $pat => ... }`, that syntactically determines whether the pattern is in an irrefutable position, instead of querying the types.

**This would not eliminate the breaking change,** but it would limit it to more contrived examples, such as

```rust
let ((true, Err((ref mut a, _, _))) | (false, Err((_, ref mut a, _)))) = x;
```

The cost here, would be the complexity added with very little benefit.

## Other notes

- I performed various cleanups while working on this. The last commit of the PR is the interesting one.
- Due to the temporary duplication of logic between `maybe_read_scrutinee` and `walk_pat`, some of the `#[rustc_capture_analysis]` tests report duplicate messages before deduplication. This is harmless.
@bors
Copy link
Collaborator

bors commented Mar 26, 2025

⌛ Trying commit 8ed61e4 with merge 3b30da3...

@meithecatte
Copy link
Contributor Author

We can crater both together if you think they're not worth separating. I was just trying to accelerate landing the parts that are obviously-not-breaking but it's up to you if you think that effort is worth it or if you're willing to be patient about waiting for the breaking parts (and FCP, etc).

That's the thing – one part is a breaking change, the other introduces insta-stable new behavior. There's no easily mergeable part to this.

@meithecatte meithecatte changed the title ExprUseVisitor: properly report discriminant reads ExprUseVisitor: murder maybe_read_scrutinee in cold blood Mar 26, 2025
@compiler-errors
Copy link
Member

could we give this a less weird pr title pls 💀

@bors try

@bors
Copy link
Collaborator

bors commented Mar 26, 2025

⌛ Trying commit 7d5a892 with merge 630b4e8...

bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 26, 2025
ExprUseVisitor: murder maybe_read_scrutinee in cold blood

This PR fixes rust-lang#137467. In order to do so, it needs to introduce a small breaking change surrounding the interaction of closure captures with matching against enums with uninhabited variants. Yes – to fix an ICE!

## Background

The current upvar inference code handles patterns in two parts:
- `ExprUseVisitor::walk_pat` finds the *bindings* being done by the pattern and captures the relevant parts
- `ExprUseVisitor::maybe_read_scrutinee` determines whether matching against the pattern will at any point require inspecting a discriminant, and if so, captures *the entire scrutinee*. It also has some weird logic around bindings, deciding to also capture the entire scrutinee if *pretty much any binding exists in the pattern*, with some weird behavior like rust-lang#137553.

Nevertheless, something like `|| let (a, _) = x;` will only capture `x.0`, because `maybe_read_scrutinee` does not run for irrefutable patterns at all. This causes issues like rust-lang#137467, where the closure wouldn't be capturing enough, because an irrefutable or-pattern can still require inspecting a discriminant, and the match lowering would then panic, because it couldn't find an appropriate upvar in the closure.

My thesis is that this is not a reasonable implementation. To that end, I intend to merge the functionality of both these parts into `walk_pat`, which will bring upvar inference closer to what the MIR lowering actually needs – both in making sure that necessary variables get captured, fixing rust-lang#137467, and in reducing the cases where redundant variables do – fixing rust-lang#137553.

This PR introduces the necessary logic into `walk_pat`, fixing rust-lang#137467. A subsequent PR will remove `maybe_read_scrutinee` entirely, which should now be redundant, fixing rust-lang#137553. The latter is still pending, as my current revision doesn't handle opaque types correctly for some reason I haven't looked into yet.

## The breaking change

The following example, adapted from the testsuite, compiles on current stable, but will not compile with this PR:

```rust
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Void {}

pub fn main() {
    let mut r = Result::<Void, (u32, u32)>::Err((0, 0));
    let mut f = || {
        let Err((ref mut a, _)) = r;
        *a = 1;
    };
    let mut g = || {
    //~^ ERROR: cannot borrow `r` as mutable more than once at a time
        let Err((_, ref mut b)) = r;
        *b = 2;
    };
    f();
    g();
    assert_eq!(r, Err((1, 2)));
}
```

The issue is that, to determine that matching against `Err` here doesn't require inspecting the discriminant, we need to query the `InhabitedPredicate` of the types involved. However, as upvar inference is done during typechecking, the relevant type might not yet be fully inferred. Because of this, performing such a check hits this assertion:

https://github.com/rust-lang/rust/blob/43f0014ef0f242418674f49052ed39b70f73bc1c/compiler/rustc_middle/src/ty/inhabitedness/mod.rs#L121

The code used to compile fine, but only because the compiler incorrectly assumed that patterns used within a `let` cannot possibly be inspecting any discriminants.

## Is the breaking change necessary?

One other option would be to double down, and introduce a deliberate semantics difference between `let $pat = $expr;` and `match $expr { $pat => ... }`, that syntactically determines whether the pattern is in an irrefutable position, instead of querying the types.

**This would not eliminate the breaking change,** but it would limit it to more contrived examples, such as

```rust
let ((true, Err((ref mut a, _, _))) | (false, Err((_, ref mut a, _)))) = x;
```

The cost here, would be the complexity added with very little benefit.

## Other notes

- I performed various cleanups while working on this. The last commit of the PR is the interesting one.
- Due to the temporary duplication of logic between `maybe_read_scrutinee` and `walk_pat`, some of the `#[rustc_capture_analysis]` tests report duplicate messages before deduplication. This is harmless.
@meithecatte meithecatte changed the title ExprUseVisitor: murder maybe_read_scrutinee in cold blood ExprUseVisitor: get rid of maybe_read_scrutinee Mar 26, 2025
@meithecatte
Copy link
Contributor Author

could we give this a less weird pr title pls 💀

Sure thing. I also updated the PR description to describe both changes. I want to add a section on what exactly the insta-stable behavior will be, but I realized that I haven't added a test for that. Should I hold off on pushing that to not break the bors try and crater?

@compiler-errors
Copy link
Member

Once bors is done with the try build then you can push, no need to wait until crater is done.

@rust-log-analyzer

This comment has been minimized.

@traviscross
Copy link
Contributor

traviscross commented Oct 15, 2025

In reviewing the Reference PR rust-lang/reference#1837, @ehuss and I found that this PR seems to change whether the pointee or the pointer is captured in the following program that matches a slice against a slice pattern:

#![feature(rustc_attrs, stmt_expr_attributes)]
#![allow(internal_features)]

fn main() {
    let x: &mut [u8] = &mut [];
    let c = #[rustc_capture_analysis] || match x {
        [] => (),
        _ => (),
    };
    c();
}

On nightly, the analysis returned is:

error: First Pass analysis includes:
 --> src/main.rs:6:39
  |
6 |       let c = #[rustc_capture_analysis] || match x {
  |  _______________________________________^
7 | |         [] => (),
8 | |         _ => (),
9 | |     };
  | |_____^
  |
note: Capturing x[] -> Immutable
 --> src/main.rs:6:48
  |
6 |     let c = #[rustc_capture_analysis] || match x {
  |                                                ^

error: Min Capture analysis includes:
 --> src/main.rs:6:39
  |
6 |       let c = #[rustc_capture_analysis] || match x {
  |  _______________________________________^
7 | |         [] => (),
8 | |         _ => (),
9 | |     };
  | |_____^
  |
note: Min Capture x[] -> Immutable
 --> src/main.rs:6:48
  |
6 |     let c = #[rustc_capture_analysis] || match x {
  |                                                ^

That is, it seems to capture the pointer (analysis of the produce MIR also confirms this).

On the PR branch (rebased to master), the analysis returned is:

error: First Pass analysis includes:
 --> src/main.rs:6:39
  |
6 |       let c = #[rustc_capture_analysis] || match x {
  |  _______________________________________^
7 | |         [] => (),
8 | |         _ => (),
9 | |     };
  | |_____^
  |
note: Capturing x[Deref] -> Immutable
 --> src/main.rs:6:48
  |
6 |     let c = #[rustc_capture_analysis] || match x {
  |                                                ^

error: Min Capture analysis includes:
 --> src/main.rs:6:39
  |
6 |       let c = #[rustc_capture_analysis] || match x {
  |  _______________________________________^
7 | |         [] => (),
8 | |         _ => (),
9 | |     };
  | |_____^
  |
note: Min Capture x[Deref] -> Immutable
 --> src/main.rs:6:48
  |
6 |     let c = #[rustc_capture_analysis] || match x {
  |                                                ^

That is, it seems to capture the pointee (again, this is also seen in the MIR).

Over on rust-lang/reference#1837, it was mentioned that this might have something to do with truncate_capture_for_optimization, but in building rustc with logging, @ehuss reports not seeing that function hit in this case (either on nightly or on the branch).

What's the story on this? (And, e.g., what code in the diff affects this?)

From a language perspective, it seems maybe a bit more intuitive to capture the wide pointer rather than the pointee, as that's where the read is happening to get the length. Is this difference observable in any way in the language?

@traviscross
Copy link
Contributor

traviscross commented Oct 15, 2025

For the question above...

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Oct 15, 2025
@rustbot
Copy link
Collaborator

rustbot commented Oct 15, 2025

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@traviscross
Copy link
Contributor

This branch rebases cleanly. Pushed the rebase. It's been awhile; let's recheck the crater run to be sure we won't be surprised in the beta crater runs.

@bors2 try

@rust-bors

This comment has been minimized.

rust-bors bot added a commit that referenced this pull request Oct 15, 2025
Make closure capturing have consistent and correct behaviour around patterns
@rust-log-analyzer

This comment has been minimized.

@rust-bors
Copy link

rust-bors bot commented Oct 15, 2025

💔 Test for d2a1455 failed: CI

@rust-log-analyzer

This comment has been minimized.

@traviscross
Copy link
Contributor

The problem with clippy here seems real. On the branch, for this program,

fn issue15102() {
    let values = [None, Some(3)];
    let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some();
}

fn main() {}

clippy produces:

warning: called `is_some()` after searching an `Iterator` with `find`
 --> src/main.rs:3:34
  |
3 |     let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some();
  |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| matches!(v, *v if x % 2 == 0))`
  |
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
  = note: `#[warn(clippy::search_is_some)]` on by default

(Scroll right and look at the suggestion.)

@traviscross
Copy link
Contributor

I'm going to go ahead and file a concern here, not because I'm particularly concerned, but just as a reminder of the state that we're in here so we don't lose it as GH starts collapsing comments. We should 1) answer the question in #138961 (comment) and 2) run this back through crater.

@rfcbot concern todos

@rust-rfcbot rust-rfcbot added the proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. label Oct 16, 2025
@traviscross traviscross removed the finished-final-comment-period The final comment period is finished for this PR / Issue. label Oct 16, 2025
@meithecatte
Copy link
Contributor Author

meithecatte commented Oct 18, 2025

In reviewing the Reference PR rust-lang/reference#1837, @ehuss and I found that this PR seems to change whether the pointee or the pointer is captured in the following program that matches a slice against a slice pattern:

Note that current nightly already captures x[Deref] in this case:

#![feature(rustc_attrs, stmt_expr_attributes)]
#![allow(internal_features)]

fn main() {
    let x: &mut [u8] = &mut [];
    let c = #[rustc_capture_analysis] || {
        x.len();
    };
    c();
}

@meithecatte
Copy link
Contributor Author

To expand on the above, the change in how slice lengths get captured is consistent with the entire premise of the PR:

  • current nightly handles the pattern matching in such a way, that any discriminant-like things being inspected result in the whole scrutinee of the match expression being captured.
  • to make things work in a sensible way, and to fix the edgecases that are definitely bugs, this PR introduces more precision (into matches. for lets, the precision is already there, in a way. this weird distinction between matches and lets is, IIRC, going away as well)

To illustrate, consider the following example:

#![feature(rustc_attrs, stmt_expr_attributes)]
#![allow(internal_features)]

fn main() {
    let x: (&mut [u8], u32) = (&mut [], 0);
    let c = #[rustc_capture_analysis] || match x {
        ([], _) => (),
        _ => (),
    };
    c();
}

On nightly, this results in:

error: First Pass analysis includes:
note: Capturing x[] -> Immutable
error: Min Capture analysis includes:
note: Min Capture x[] -> Immutable

while on this PR's branch, the result is:

error: First Pass analysis includes:
note: Capturing x[(0, 0),Deref] -> Immutable
error: Min Capture analysis includes:
note: Min Capture x[(0, 0),Deref] -> Immutable

Thus, the change is not in how this particular pattern gets understood by the capture analysis, but in how the already documented changes in the common codepaths handle the results.

The question, then, is whether the length part of the slice should be captured as x, or x[Deref]. I'm definitely not an expert on this. I'd even say it's not really my department. As I remarked in my previous comment, observing the current compiler behavior suggests it's semantically part of the value behind the pointer, even though, at the ABI level, it's part of the pointer metadata.

After thinking about it for a while, this makes sense – after all, consider the unsized_locals case, when there is no pointer to speak of.

Is this difference observable in any way in the language?

I was able to come up with the following example, where it does:

#![allow(unused_assignments)]
#![allow(dead_code)]

fn f(mut x: &mut [u8]) -> impl FnOnce() + '_ {
    let g = || {
        match x {
            [] => (),
            _ => (),
        }
    };
    x = &mut [];
    g
}

Nightly rejects this, my branch accepts it. I'll try to also find a similar case that doesn't involve slice patterns, and add both as a testcase.

I don't think the interactions of this PR with dereferencing were discussed much so far, should we have T-lang at least take a look at this?

@meithecatte
Copy link
Contributor Author

I've simplified my example a bit and added it to the test suite in 19b3f2e – together with one that reproduces the same thing with just a &mut bool.

I might find the time to look into the clippy issue tomorrow. Meanwhile, the question still stands: do we want to bring this case to the attention of T-lang, just to be safe?

@rust-log-analyzer
Copy link
Collaborator

The job x86_64-gnu-tools failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
tests/ui/should_impl_trait/method_list_2.rs (revision `edition2021`) ... ok
tests/ui/crashes/third-party/conf_allowlisted.rs ... ok

FAILED TEST: tests/ui/search_is_some_fixable_some.rs
command: CLIPPY_CONF_DIR="tests" RUSTC_ICE="0" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/release/clippy-driver" "--error-format=json" "--emit=metadata" "-Aunused" "-Ainternal_features" "-Zui-testing" "-Zdeduplicate-diagnostics=no" "-Dwarnings" "-Ldependency=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/release/deps" "--sysroot=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2" "--out-dir" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/ui_test/0/tests/ui" "tests/ui/search_is_some_fixable_some.rs" "--extern" "futures=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libfutures-b34113e7b63f8058.rlib" "--extern" "futures=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libfutures-b34113e7b63f8058.rmeta" "--extern" "itertools=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libitertools-c6bc8b54b3d13b27.rlib" "--extern" "itertools=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libitertools-c6bc8b54b3d13b27.rmeta" "--extern" "libc=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/liblibc-b0a92f585872a7c2.rlib" "--extern" "libc=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/liblibc-b0a92f585872a7c2.rmeta" "--extern" "parking_lot=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libparking_lot-ffad002e20b85d9f.rlib" "--extern" "parking_lot=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libparking_lot-ffad002e20b85d9f.rmeta" "--extern" "quote=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libquote-0601c30eafe7aaad.rlib" "--extern" "quote=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libquote-0601c30eafe7aaad.rmeta" "--extern" "regex=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libregex-4df0a0268e5fe475.rlib" "--extern" "regex=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libregex-4df0a0268e5fe475.rmeta" "--extern" "serde=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libserde-9e2077ea9420effd.rlib" "--extern" "serde=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libserde-9e2077ea9420effd.rmeta" "--extern" "syn=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libsyn-9dd939f44b16796f.rlib" "--extern" "syn=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libsyn-9dd939f44b16796f.rmeta" "--extern" "tokio=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libtokio-d892880ac2e52d67.rlib" "--extern" "tokio=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps/libtokio-d892880ac2e52d67.rmeta" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/debug/deps" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/debug/deps" "--edition" "2024"

error: actual output differed from expected
Execute `./x test src/tools/clippy --bless` to update `tests/ui/search_is_some_fixable_some.stderr` to the actual output
--- tests/ui/search_is_some_fixable_some.stderr
+++ <stderr output>
 error: called `is_some()` after searching an `Iterator` with `find`
   --> tests/ui/search_is_some_fixable_some.rs:9:22
... 291 lines skipped ...
    |
 LL |     let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some();
-   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| matches!(&v, Some(x) if x % 2 == 0))`
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| matches!(v, *v if x % 2 == 0))`
 
 error: aborting due to 47 previous errors
 

Full unnormalized output:
error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:9:22
   |
LL |     let _ = v.iter().find(|&x| *x < 0).is_some();
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| *x < 0)`
   |
   = note: `-D clippy::search-is-some` implied by `-D warnings`
   = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:11:20
   |
LL |     let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| **y == x)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:14:20
   |
LL |     let _ = (0..1).find(|x| *x == 0).is_some();
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| x == 0)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:16:22
   |
LL |     let _ = v.iter().find(|x| **x == 0).is_some();
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| *x == 0)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:18:20
   |
LL |     let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some();
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| x == 1 || x == 3 || x == 5)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:20:20
   |
LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some();
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| [1, 2, 3].contains(&x))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:22:20
   |
LL |     let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some();
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| x == 0 || [1, 2, 3].contains(&x))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:24:20
   |
LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some();
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| [1, 2, 3].contains(&x) || x == 0)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:27:10
   |
LL |           .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
   |  __________^
LL | |
LL | |         .is_some();
   | |__________________^ help: consider using: `any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)`

error: called `is_some()` after searching an `Iterator` with `position`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:32:22
   |
LL |     let _ = v.iter().position(|&x| x < 0).is_some();
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|&x| x < 0)`

error: called `is_some()` after searching an `Iterator` with `rposition`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:36:22
   |
LL |     let _ = v.iter().rposition(|&x| x < 0).is_some();
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|&x| x < 0)`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:42:27
   |
LL |     let _ = "hello world".find("world").is_some();
   |                           ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:44:27
   |
LL |     let _ = "hello world".find(&s2).is_some();
   |                           ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:46:27
   |
LL |     let _ = "hello world".find(&s2[2..]).is_some();
   |                           ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:49:16
   |
LL |     let _ = s1.find("world").is_some();
   |                ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:51:16
   |
LL |     let _ = s1.find(&s2).is_some();
   |                ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:53:16
   |
LL |     let _ = s1.find(&s2[2..]).is_some();
   |                ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:56:21
   |
LL |     let _ = s1[2..].find("world").is_some();
   |                     ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:58:21
   |
LL |     let _ = s1[2..].find(&s2).is_some();
   |                     ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:60:21
   |
LL |     let _ = s1[2..].find(&s2[2..]).is_some();
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:77:44
   |
LL |             .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some())
   |                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|cc| c == &cc)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:94:49
   |
LL |             .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some())
   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|cc| c == cc)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:106:29
   |
LL |         let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some();
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.foo == 1 && v.bar == 2)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:112:14
   |
LL |               .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
   |  ______________^
LL | |
LL | |             .is_some();
   | |______________________^ help: consider using: `any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:119:29
   |
LL |         let _ = vfoo.iter().find(|a| a[0] == 42).is_some();
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|a| a[0] == 42)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:126:29
   |
LL |         let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some();
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|sub| sub[1..4].len() == 3)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:145:30
   |
LL |         let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some();
   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|ppp_x: &&u32| please(ppp_x))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:147:50
   |
LL |         let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some();
   |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| s.len() == 2)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:151:26
   |
LL |         let _ = v.iter().find(|x| deref_enough(**x)).is_some();
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| deref_enough(*x))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:153:26
   |
LL |         let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some();
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| deref_enough(*x))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:157:26
   |
LL |         let _ = v.iter().find(|x| arg_no_deref(x)).is_some();
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| arg_no_deref(&x))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:160:26
   |
LL |         let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some();
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref(&x))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:183:14
   |
LL |               .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
   |  ______________^
LL | |
LL | |             .is_some();
   | |______________________^ help: consider using: `any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:198:29
   |
LL |         let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some();
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.inner[0].bar == 2)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:204:29
   |
LL |         let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some();
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| (**x)[0] == 9)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:218:29
   |
LL |         let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some();
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.by_ref(&v.bar))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:225:14
   |
LL |               .find(|&&&(&x, ref y)| x == *y)
   |  ______________^
LL | |
LL | |             .is_some();
   | |______________________^ help: consider using: `any(|&&(&x, ref y)| x == *y)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:246:26
   |
LL |         let _ = v.iter().find(|s| s[0].is_empty()).is_some();
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| s[0].is_empty())`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:248:26
   |
LL |         let _ = v.iter().find(|s| test_string_1(&s[0])).is_some();
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| test_string_1(&s[0]))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:258:26
   |
LL |         let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some();
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| fp.field.is_power_of_two())`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:260:26
   |
LL |         let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some();
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_1(fp.field))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:262:26
   |
LL |         let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_2(*fp.field))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:278:18
   |
LL |         v.iter().find(|x: &&u32| func(x)).is_some()
   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| func(&x))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:288:26
   |
LL |         let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some();
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_impl(&x))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:292:26
   |
LL |         let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some();
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_dyn(&x))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:296:26
   |
LL |         let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some();
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| (*arg_no_deref_dyn)(&x))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:303:34
   |
LL |     let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some();
   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| matches!(v, *v if x % 2 == 0))`

error: aborting due to 47 previous errors



full stderr:
error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:9:22
   |
LL |     let _ = v.iter().find(|&x| *x < 0).is_some();
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| *x < 0)`
   |
   = note: `-D clippy::search-is-some` implied by `-D warnings`
   = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:11:20
   |
LL |     let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| **y == x)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:14:20
   |
LL |     let _ = (0..1).find(|x| *x == 0).is_some();
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| x == 0)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:16:22
   |
LL |     let _ = v.iter().find(|x| **x == 0).is_some();
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| *x == 0)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:18:20
   |
LL |     let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some();
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| x == 1 || x == 3 || x == 5)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:20:20
   |
LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some();
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| [1, 2, 3].contains(&x))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:22:20
   |
LL |     let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some();
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| x == 0 || [1, 2, 3].contains(&x))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:24:20
   |
LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some();
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| [1, 2, 3].contains(&x) || x == 0)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:27:10
   |
LL |           .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
   |  __________^
LL | |
LL | |         .is_some();
   | |__________________^ help: consider using: `any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)`

error: called `is_some()` after searching an `Iterator` with `position`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:32:22
   |
LL |     let _ = v.iter().position(|&x| x < 0).is_some();
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|&x| x < 0)`

error: called `is_some()` after searching an `Iterator` with `rposition`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:36:22
   |
LL |     let _ = v.iter().rposition(|&x| x < 0).is_some();
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|&x| x < 0)`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:42:27
   |
LL |     let _ = "hello world".find("world").is_some();
   |                           ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:44:27
   |
LL |     let _ = "hello world".find(&s2).is_some();
   |                           ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:46:27
   |
LL |     let _ = "hello world".find(&s2[2..]).is_some();
   |                           ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:49:16
   |
LL |     let _ = s1.find("world").is_some();
   |                ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:51:16
   |
LL |     let _ = s1.find(&s2).is_some();
   |                ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:53:16
   |
LL |     let _ = s1.find(&s2[2..]).is_some();
   |                ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:56:21
   |
LL |     let _ = s1[2..].find("world").is_some();
   |                     ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:58:21
   |
LL |     let _ = s1[2..].find(&s2).is_some();
   |                     ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)`

error: called `is_some()` after calling `find()` on a string
##[error]  --> tests/ui/search_is_some_fixable_some.rs:60:21
   |
LL |     let _ = s1[2..].find(&s2[2..]).is_some();
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:77:44
   |
LL |             .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some())
   |                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|cc| c == &cc)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:94:49
   |
LL |             .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some())
   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|cc| c == cc)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:106:29
   |
LL |         let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some();
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.foo == 1 && v.bar == 2)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:112:14
   |
LL |               .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
   |  ______________^
LL | |
LL | |             .is_some();
   | |______________________^ help: consider using: `any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:119:29
   |
LL |         let _ = vfoo.iter().find(|a| a[0] == 42).is_some();
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|a| a[0] == 42)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:126:29
   |
LL |         let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some();
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|sub| sub[1..4].len() == 3)`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:145:30
   |
LL |         let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some();
   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|ppp_x: &&u32| please(ppp_x))`

error: called `is_some()` after searching an `Iterator` with `find`
##[error]  --> tests/ui/search_is_some_fixable_some.rs:147:50
   |
LL |         let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some();
   |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| s.len() == 2)`

@traviscross
Copy link
Contributor

traviscross commented Oct 19, 2025

Thanks for those details and that analysis. In particular, it leads me to consider this variant of the example,

fn f<'l: 's, 's>(x: &'s mut &'l [u8]) -> impl Fn() + 'l {
    || match *x {
        &[] => (),
        _ => (),
    }
}

which is an error on nightly but works with this PR. That makes it more clear to me why capturing **x -- i.e. treating the read of the length semantically as a read of the pointee -- is likely correct. Notably, e.g., this works today:

fn f<'l: 's, 's>(x: &'s mut &'l [u8]) -> impl Fn() + 'l {
    || match **x {
        [] => (),
        _ => (),
    }
}

It's rather surprising these would be different. It's good that this PR, then, will make these consistent.

I don't think the interactions of this PR with dereferencing were discussed much so far, should we have T-lang at least take a look at this?

This is why I was asking, was to see whether there was an aspect to this stabilization that we hadn't reviewed. Though I think it'll be an easy call for us, this is indeed such an aspect, so we'll discuss for visibility.

@rustbot labels +I-lang-nominated +I-lang-easy-decision

I'll be curious to hear from @nikomatsakis, in particular, more about the history on this.

@rustbot rustbot added I-lang-easy-decision Issue: The decision needed by the team is conjectured to be easy; this does not imply nomination I-lang-nominated Nominated for discussion during a lang team meeting. P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang labels Oct 19, 2025
@traviscross traviscross added the I-lang-radar Items that are on lang's radar and will need eventual work or consideration. label Oct 19, 2025
@meithecatte
Copy link
Contributor Author

The clippy CI failure is due to the fact that the code in question (clippy_utils::sugg::DerefDelegate) is assuming that the PlaceWithHirId provided by the ExprUseVisitor will always refer to the HirId of an expression. However, in this particular case, it is a pattern.

A similar issue can already be reproduced on nightly:

fn main() {
    let values = [None, Some(3)];
    let has_even = values.iter().find(|v| { let &&v = v; true }).is_some();
    println!("{has_even}");
}

(with clippy suggesting the malformed any(|v| { let &*v = v; true }))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. I-lang-easy-decision Issue: The decision needed by the team is conjectured to be easy; this does not imply nomination I-lang-nominated Nominated for discussion during a lang team meeting. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. needs-crater This change needs a crater run to check for possible breakage in the ecosystem. needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-lang Relevant to the language team

Projects

None yet