Skip to content

Conversation

Natural-selection1
Copy link
Contributor

@Natural-selection1 Natural-selection1 commented Sep 21, 2025

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
    {
        /* */
    }
}

related issue

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

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 21, 2025
@rustbot

This comment was marked as off-topic.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@traviscross traviscross added T-lang Relevant to the language team needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. labels Sep 21, 2025
@rust-log-analyzer

This comment has been minimized.

@Natural-selection1 Natural-selection1 force-pushed the not-in-chains branch 2 times, most recently from ce66065 to e77a3c4 Compare September 21, 2025 11:02
@Natural-selection1 Natural-selection1 marked this pull request as ready for review September 21, 2025 11:07
@rustbot
Copy link
Collaborator

rustbot commented Sep 21, 2025

Some changes occurred in match checking

cc @Nadrieril

The Miri subtree was changed

cc @rust-lang/miri

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

It seems that only modifying tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.rs is needed to cover this change. Do I still need to add an additional new test?

Comment on lines 572 to 568
// for let chains, don't emit IRREFUTABLE_LET_PATTERNS
if chain_refutabilities.len() > 1 {
return;
}

if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, Irrefutable)))) {
// The entire chain is made up of irrefutable `let` statements
report_irrefutable_let_patterns(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems the check you're adding should cause a reworking of the logic that follows.

@traviscross
Copy link
Contributor

It seems that only modifying tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.rs is needed to cover this change. Do I still need to add an additional new test?

All the lints seem to be removed in that test, but you'll want to ensure there are tests that cover the edges of what is still linted here.

@rustbot author

@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 21, 2025
@rustbot
Copy link
Collaborator

rustbot commented Sep 21, 2025

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@rustbot rustbot added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Sep 21, 2025
Comment on lines 171 to 173
// For let chains, don't lint.
if let [Some((_, refutability))] = chain_refutabilities[..] {
self.check_single_let(refutability, ex.span);
Copy link
Contributor Author

@Natural-selection1 Natural-selection1 Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but you'll want to ensure there are tests that cover the edges of what is still linted here.

match chain_refutabilities.len() {
    0   => "`assert!(self.let_source != LetSource::None)` ensure this case doesn't exist"
    2.. => "`if let [Some((_, refutability))] = chain_refutabilities[..]` ensure not checked"  // The purpose of this PR: don't check let chains
    1 if chain_refutabilities[0].is_none() 
        => "A boolean expression at the start of `while let` | `if let` | `else if let`, this case cannot pass syntax analysis"
    1 if chain_refutabilities[0].is_some() 
        => "There is exactly one let binding in `while let` | `if let` | `else if let` statement, proceed to check"
}

@rustbot
Copy link
Collaborator

rustbot commented Sep 22, 2025

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

@rust-log-analyzer

This comment has been minimized.

@Natural-selection1 Natural-selection1 force-pushed the not-in-chains branch 2 times, most recently from 282a823 to 075e491 Compare September 22, 2025 09:54
@Natural-selection1
Copy link
Contributor Author

Natural-selection1 commented Sep 22, 2025

I tried adding a new test file, but I don't know why ./x test tests/ui/binding/irrefutable-in-let-chains.rs --bless is not generating the .stderr file. I'm not sure what I did incorrectly.
: (

edit: adding //@ check-pass can fix it : )

@rust-log-analyzer

This comment has been minimized.

@Natural-selection1
Copy link
Contributor Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 22, 2025
Comment on lines 1 to 20
//@ edition: 2024
//@ check-pass

#![feature(if_let_guard)]

use std::ops::Range;

fn main() {
let opt = Some(None..Some(1));

if let first = &opt {}
//~^ WARN irrefutable `if let` pattern

if let first = &opt
&& let Some(second) = first
{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment at the top of this test describing the purpose of this test overall and comments throughout describing the nature of the cases that we're testing.

// If at least one of the operands is a `let ... = ...`.
if chain_refutabilities.iter().any(|x| x.is_some()) {
self.check_let_chain(chain_refutabilities, ex.span);
// For let chains, don't lint.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a good comment in this position. What follows is something that will lint in some cases, and that's what should be described.

@traviscross
Copy link
Contributor

@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 Sep 22, 2025
@Natural-selection1 Natural-selection1 force-pushed the not-in-chains branch 2 times, most recently from 7200237 to fddc34f Compare September 23, 2025 01:29
@Natural-selection1
Copy link
Contributor Author

@rustbot ready

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

This will need an FCP by the lang team. For that, this needs a description of what we'll be stabilizing exactly and the reasons for it. Have a look at other PRs that are labeled T-lang and finished-final-comment-period to see examples of this.

@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 Sep 24, 2025
@Natural-selection1
Copy link
Contributor Author

this needs a description of what we'll be stabilizing exactly and the reasons for it. Have a look at other PRs that are labeled T-lang and finished-final-comment-period to see examples of this.

Is there anything I can do to move this forward?

@RalfJung
Copy link
Member

Yes, you can write such a description. :)

@Natural-selection1
Copy link
Contributor Author

I have updated the top comment of this PR. Does it now have the proper format for a FCP? If so, what are the next steps to get it started?

@traviscross traviscross added I-lang-nominated Nominated for discussion during a lang team meeting. P-lang-drag-2 Lang team prioritization drag level 2.https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang. labels Sep 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. P-lang-drag-2 Lang team prioritization drag level 2.https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants