Files
rust/compiler/rustc_mir_transform
Jonathan Brouwer fb6d93917e Rollup merge of #146832 - Natural-selection1:not-in-chains, r=petrochenkov
Not linting irrefutable_let_patterns on let chains

*[View all comments](https://triagebot.infra.rust-lang.org/gh-comments/rust-lang/rust/pull/146832)*

# Description

this PR makes the lint `irrefutable_let_patterns` not check for `let chains`,
only check for single `if let`, `while let`, and `if let guard`.

# Motivation

Since `let chains` were stabilized, the following code has become common:

```rust
fn max() -> usize { 42 }

fn main() {
    if let mx = max() && mx < usize::MAX { /* */ }
}
```

This code naturally expresses "please call that function and then do something if the return value satisfies a condition".
Putting the let binding outside the if would be bad as then it remains in scope after the if, which is not the intent.

Current Output:

```bash
warning: leading irrefutable pattern in let chain
 --> src/main.rs:7:8
  |
7 |     if let mx = max() && mx < usize::MAX {
  |        ^^^^^^^^^^^^^^
  |
  = note: this pattern will always match
  = help: consider moving it outside of the construct
  = note: `#[warn(irrefutable_let_patterns)]` on by default
```

Another common case is progressively destructuring a struct with enum fields, or an enum with struct variants:

```rust
struct NameOfOuterStruct {
    middle: NameOfMiddleEnum,
    other: (),
}
enum NameOfMiddleEnum {
    Inner(NameOfInnerStruct),
    Other(()),
}
struct NameOfInnerStruct {
    id: u32,
}

fn test(outer: NameOfOuterStruct) {
    if let NameOfOuterStruct { middle, .. } = outer
        && let NameOfMiddleEnum::Inner(inner) = middle
        && let NameOfInnerStruct { id } = inner
    {
        /* */
    }
}
```

Current Output:

```bash
warning: leading irrefutable pattern in let chain
  --> src\main.rs:17:8
   |
17 |     if let NameOfOuterStruct { middle, .. } = outer
   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: this pattern will always match
   = help: consider moving it outside of the construct
   = note: `#[warn(irrefutable_let_patterns)]` on by default

warning: trailing irrefutable pattern in let chain
  --> src\main.rs:19:12
   |
19 |         && let NameOfInnerStruct { id } = inner
   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: this pattern will always match
   = help: consider moving it into the body
```

To avoid the warning, the readability would be much worse:

```rust
fn test(outer: NameOfOuterStruct) {
    if let NameOfOuterStruct {
        middle: NameOfMiddleEnum::Inner(NameOfInnerStruct { id }),
        ..
    } = outer
    {
        /* */
    }
}
```

# related issue

* rust-lang/rust#139369

# possible questions

1. Moving the irrefutable pattern at the head of the chain out of it would cause a variable that was intended to be temporary to remain in scope, so we remove it.
   However, should we keep the check for moving the irrefutable pattern at the tail into the body?

2. Should we still lint `entire chain is made up of irrefutable let`?

---

This is my first time contributing non-documentation code to Rust. If there are any irregularities, please feel free to point them out.
: )
2026-02-20 22:00:54 +01:00
..