Not linting irrefutable_let_patterns on let chains by Natural-selection1 · Pull Request #146832 · rust-lang/rust (original) (raw)

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:

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:

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:

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:

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:

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

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.
: )