rust-lang / rust-lang/rust-clippy

`collapsible_if` should handle multiple nested if statements

Open
#15,042 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

Summary

The collapsible_if lint can now detect opportunities for let-chains. Unfortunately, when there is more than one nested if in the chain, this is presented as N separate warnings, instead of one "if chain" that can be improved. Since let-chains bring the most value when the chains are long (and the nesting is deep), this hides the potential benefit.

Reproducer

I tried this code:

pub fn inner_type(ty: &Type) -> &Type {
    if let Type::Path(TypePath { path, .. }) = ty {
        if let Some(segment) = path.segments.last() {
            if let PathArguments::AngleBracketed(args) = &segment.arguments {
                if let Some(GenericArgument::Type(ty)) = args.args.first() {
                    return ty;
                }
            }
        }
    }
    panic!("Expected inner type");
}

I expected to see this happen:

Clippy should suggest collapsing the entire chain of if statements:

    if let Type::Path(TypePath { path, .. }) = ty
        && let Some(segment) = path.segments.last()
        && let PathArguments::AngleBracketed(args) = &segment.arguments
        && let Some(GenericArgument::Type(ty)) = args.args.first()
    {
        return ty;
    }

Instead, this happened:

It prints a separate warning for each of the 3 possible nested ifs. This seems needlessly verbose, and I find it much harder to comprehend:

warning: this `if` statement can be collapsed
  --> src/lib.rs:4:5
   |
4  | /     if let Type::Path(TypePath { path, .. }) = ty {
5  | |         if let Some(segment) = path.segments.last() {
6  | |             if let PathArguments::AngleBracketed(args) = &segment.arguments {
7  | |                 if let Some(GenericArgument::Type(ty)) = args.args.first() {
...  |
12 | |     }
   | |_____^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
   = note: `#[warn(clippy::collapsible_if)]` on by default
help: collapse nested if block
   |
4  ~     if let Type::Path(TypePath { path, .. }) = ty
5  ~         && let Some(segment) = path.segments.last() {
6  |             if let PathArguments::AngleBracketed(args) = &segment.arguments {
...
10 |             }
11 ~         }
   |

warning: this `if` statement can be collapsed
  --> src/lib.rs:5:9
   |
5  | /         if let Some(segment) = path.segments.last() {
6  | |             if let PathArguments::AngleBracketed(args) = &segment.arguments {
7  | |                 if let Some(GenericArgument::Type(ty)) = args.args.first() {
8  | |                     return ty;
...  |
11 | |         }
   | |_________^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
help: collapse nested if block
   |
5  ~         if let Some(segment) = path.segments.last()
6  ~             && let PathArguments::AngleBracketed(args) = &segment.arguments {
7  |                 if let Some(GenericArgument::Type(ty)) = args.args.first() {
8  |                     return ty;
9  |                 }
10 ~             }
   |

warning: this `if` statement can be collapsed
  --> src/lib.rs:6:13
   |
6  | /             if let PathArguments::AngleBracketed(args) = &segment.arguments {
7  | |                 if let Some(GenericArgument::Type(ty)) = args.args.first() {
8  | |                     return ty;
9  | |                 }
10 | |             }
   | |_____________^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
help: collapse nested if block
   |
6  ~             if let PathArguments::AngleBracketed(args) = &segment.arguments
7  ~                 && let Some(GenericArgument::Type(ty)) = args.args.first() {
8  |                     return ty;
9  ~                 }
   |

Note this does produce the exact same output, but it's hard to tell that that will be the case because it's spread out among the three different help blocks.

Version
rustc 1.89.0-nightly (e703dff8f 2025-06-11)
binary: rustc
commit-hash: e703dff8fe220b78195c53478e83fb2f68d8499c
commit-date: 2025-06-11
host: x86_64-unknown-linux-gnu
release: 1.89.0-nightly
LLVM version: 20.1.5
Additional Labels

No response

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.

Research direction

Start at the collapsible_if lint entry point and reproduce the supplied nested-Rust example. Trace how nested opportunities are collected and warnings are emitted, then add a regression case that expects one suggestion for the full chain and run the relevant Clippy tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.