Add guard support for replace_if_let_with_match

- Fix loses comments
- Fix bad indentation

Example
---
```rust
fn main() {
    if $0let true = true
        && true
        && false
    {
        code()
    }
}
```

**Before this PR**

Assist not applicable

**After this PR**

```rust
fn main() {
    match true {
        true if true
            && false => code(),
        _ => (),
    }
}
```

---

```rust
pub fn foo(foo: i32) {
    $0if let 1 = foo {
        // some comment
        self.foo();
    } else if let 2 = foo {
        // some comment 2
        self.bar()
    }
}
```

**Before this PR**

```rust
pub fn foo(foo: i32) {
    match foo {
        1 => {
            self.foo();
        }
        2 => self.bar(),
        _ => (),
    }
}
```

**After this PR**

```rust
pub fn foo(foo: i32) {
    match foo {
        1 => {
            // some comment
            self.foo();
        }
        2 => {
            // some comment 2
            self.bar()
        },
        _ => (),
    }
}
```
diff --git a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
index 3b815a4..b7e5344 100644
--- a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
@@ -1,15 +1,10 @@
 use std::iter::successors;
 
-use either::Either;
-use ide_db::{
-    RootDatabase,
-    defs::NameClass,
-    syntax_helpers::node_ext::{is_pattern_cond, single_let},
-    ty_filter::TryEnum,
-};
+use ide_db::{RootDatabase, defs::NameClass, ty_filter::TryEnum};
 use syntax::{
-    AstNode, Edition, T, TextRange,
+    AstNode, Edition, SyntaxKind, T, TextRange,
     ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory},
+    syntax_editor::SyntaxEditor,
 };
 
 use crate::{
@@ -54,42 +49,46 @@
         return None;
     }
     let mut else_block = None;
+    let indent = if_expr.indent_level();
     let if_exprs = successors(Some(if_expr.clone()), |expr| match expr.else_branch()? {
         ast::ElseBranch::IfExpr(expr) => Some(expr),
         ast::ElseBranch::Block(block) => {
+            let block = unwrap_trivial_block(block).clone_for_update();
+            block.reindent_to(IndentLevel(1));
             else_block = Some(block);
             None
         }
     });
     let scrutinee_to_be_expr = if_expr.condition()?;
-    let scrutinee_to_be_expr = match single_let(scrutinee_to_be_expr.clone()) {
-        Some(cond) => cond.expr()?,
-        None => scrutinee_to_be_expr,
+    let scrutinee_to_be_expr = match let_and_guard(&scrutinee_to_be_expr) {
+        (Some(let_expr), _) => let_expr.expr()?,
+        (None, cond) => cond?,
     };
 
     let mut pat_seen = false;
     let mut cond_bodies = Vec::new();
     for if_expr in if_exprs {
         let cond = if_expr.condition()?;
-        let cond = match single_let(cond.clone()) {
-            Some(let_) => {
+        let (cond, guard) = match let_and_guard(&cond) {
+            (None, guard) => (None, Some(guard?)),
+            (Some(let_), guard) => {
                 let pat = let_.pat()?;
                 let expr = let_.expr()?;
-                // FIXME: If one `let` is wrapped in parentheses and the second is not,
-                // we'll exit here.
                 if scrutinee_to_be_expr.syntax().text() != expr.syntax().text() {
                     // Only if all condition expressions are equal we can merge them into a match
                     return None;
                 }
                 pat_seen = true;
-                Either::Left(pat)
+                (Some(pat), guard)
             }
-            // Multiple `let`, unsupported.
-            None if is_pattern_cond(cond.clone()) => return None,
-            None => Either::Right(cond),
         };
-        let body = if_expr.then_branch()?;
-        cond_bodies.push((cond, body));
+        if let Some(guard) = &guard {
+            guard.dedent(indent);
+            guard.indent(IndentLevel(1));
+        }
+        let body = if_expr.then_branch()?.clone_for_update();
+        body.indent(IndentLevel(1));
+        cond_bodies.push((cond, guard, body));
     }
 
     if !pat_seen && cond_bodies.len() != 1 {
@@ -106,27 +105,25 @@
         available_range,
         move |builder| {
             let make = SyntaxFactory::with_mappings();
-            let match_expr = {
+            let match_expr: ast::Expr = {
                 let else_arm = make_else_arm(ctx, &make, else_block, &cond_bodies);
-                let make_match_arm = |(pat, body): (_, ast::BlockExpr)| {
-                    let body = make.block_expr(body.statements(), body.tail_expr());
-                    body.indent(IndentLevel::from(1));
-                    let body = unwrap_trivial_block(body);
-                    match pat {
-                        Either::Left(pat) => make.match_arm(pat, None, body),
-                        Either::Right(_) if !pat_seen => {
-                            make.match_arm(make.literal_pat("true").into(), None, body)
+                let make_match_arm =
+                    |(pat, guard, body): (_, Option<ast::Expr>, ast::BlockExpr)| {
+                        body.reindent_to(IndentLevel::single());
+                        let body = unwrap_trivial_block(body);
+                        match (pat, guard.map(|it| make.match_guard(it))) {
+                            (Some(pat), guard) => make.match_arm(pat, guard, body),
+                            (None, _) if !pat_seen => {
+                                make.match_arm(make.literal_pat("true").into(), None, body)
+                            }
+                            (None, guard) => {
+                                make.match_arm(make.wildcard_pat().into(), guard, body)
+                            }
                         }
-                        Either::Right(expr) => make.match_arm(
-                            make.wildcard_pat().into(),
-                            Some(make.match_guard(expr)),
-                            body,
-                        ),
-                    }
-                };
+                    };
                 let arms = cond_bodies.into_iter().map(make_match_arm).chain([else_arm]);
                 let match_expr = make.expr_match(scrutinee_to_be_expr, make.match_arm_list(arms));
-                match_expr.indent(IndentLevel::from_node(if_expr.syntax()));
+                match_expr.indent(indent);
                 match_expr.into()
             };
 
@@ -134,7 +131,11 @@
                 if_expr.syntax().parent().is_some_and(|it| ast::IfExpr::can_cast(it.kind()));
             let expr = if has_preceding_if_expr {
                 // make sure we replace the `else if let ...` with a block so we don't end up with `else expr`
-                make.block_expr([], Some(match_expr)).into()
+                match_expr.dedent(indent);
+                match_expr.indent(IndentLevel(1));
+                let block_expr = make.block_expr([], Some(match_expr));
+                block_expr.indent(indent);
+                block_expr.into()
             } else {
                 match_expr
             };
@@ -150,13 +151,13 @@
 fn make_else_arm(
     ctx: &AssistContext<'_>,
     make: &SyntaxFactory,
-    else_block: Option<ast::BlockExpr>,
-    conditionals: &[(Either<ast::Pat, ast::Expr>, ast::BlockExpr)],
+    else_expr: Option<ast::Expr>,
+    conditionals: &[(Option<ast::Pat>, Option<ast::Expr>, ast::BlockExpr)],
 ) -> ast::MatchArm {
-    let (pattern, expr) = if let Some(else_block) = else_block {
+    let (pattern, expr) = if let Some(else_expr) = else_expr {
         let pattern = match conditionals {
-            [(Either::Right(_), _)] => make.literal_pat("false").into(),
-            [(Either::Left(pat), _)] => match ctx
+            [(None, Some(_), _)] => make.literal_pat("false").into(),
+            [(Some(pat), _, _)] => match ctx
                 .sema
                 .type_of_pat(pat)
                 .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted()))
@@ -174,10 +175,10 @@
             },
             _ => make.wildcard_pat().into(),
         };
-        (pattern, unwrap_trivial_block(else_block))
+        (pattern, else_expr)
     } else {
         let pattern = match conditionals {
-            [(Either::Right(_), _)] => make.literal_pat("false").into(),
+            [(None, Some(_), _)] => make.literal_pat("false").into(),
             _ => make.wildcard_pat().into(),
         };
         (pattern, make.expr_unit())
@@ -266,7 +267,10 @@
                 // wrap them in another BlockExpr.
                 match expr {
                     ast::Expr::BlockExpr(block) if block.modifier().is_none() => block,
-                    expr => make.block_expr([], Some(expr)),
+                    expr => {
+                        expr.indent(IndentLevel(1));
+                        make.block_expr([], Some(expr))
+                    }
                 }
             };
 
@@ -289,7 +293,9 @@
                 condition
             };
             let then_expr = then_expr.clone_for_update();
+            let else_expr = else_expr.clone_for_update();
             then_expr.reindent_to(IndentLevel::single());
+            else_expr.reindent_to(IndentLevel::single());
             let then_block = make_block_expr(then_expr);
             let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) };
             let if_let_expr = make.expr_if(
@@ -382,6 +388,48 @@
         .is_some_and(|it| does_pat_match_variant(pat, &it.sad_pattern()))
 }
 
+fn let_and_guard(cond: &ast::Expr) -> (Option<ast::LetExpr>, Option<ast::Expr>) {
+    if let ast::Expr::ParenExpr(expr) = cond
+        && let Some(sub_expr) = expr.expr()
+    {
+        let_and_guard(&sub_expr)
+    } else if let ast::Expr::LetExpr(let_expr) = cond {
+        (Some(let_expr.clone()), None)
+    } else if let ast::Expr::BinExpr(bin_expr) = cond
+        && let Some(ast::Expr::LetExpr(let_expr)) = and_bin_expr_left(bin_expr).lhs()
+    {
+        let new_expr = bin_expr.clone_subtree();
+        let mut edit = SyntaxEditor::new(new_expr.syntax().clone());
+
+        let left_bin = and_bin_expr_left(&new_expr);
+        if let Some(rhs) = left_bin.rhs() {
+            edit.replace(left_bin.syntax(), rhs.syntax());
+        } else {
+            if let Some(next) = left_bin.syntax().next_sibling_or_token()
+                && next.kind() == SyntaxKind::WHITESPACE
+            {
+                edit.delete(next);
+            }
+            edit.delete(left_bin.syntax());
+        }
+
+        let new_expr = edit.finish().new_root().clone();
+        (Some(let_expr), ast::Expr::cast(new_expr))
+    } else {
+        (None, Some(cond.clone()))
+    }
+}
+
+fn and_bin_expr_left(expr: &ast::BinExpr) -> ast::BinExpr {
+    if expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And))
+        && let Some(ast::Expr::BinExpr(left)) = expr.lhs()
+    {
+        and_bin_expr_left(&left)
+    } else {
+        expr.clone()
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -453,6 +501,45 @@
     }
 
     #[test]
+    fn test_if_with_match_comments() {
+        check_assist(
+            replace_if_let_with_match,
+            r#"
+pub fn foo(foo: i32) {
+    $0if let 1 = foo {
+        // some comment
+        self.foo();
+    } else if let 2 = foo {
+        // some comment 2
+        self.bar()
+    } else {
+        // some comment 3
+        self.baz();
+    }
+}
+"#,
+            r#"
+pub fn foo(foo: i32) {
+    match foo {
+        1 => {
+            // some comment
+            self.foo();
+        }
+        2 => {
+            // some comment 2
+            self.bar()
+        }
+        _ => {
+            // some comment 3
+            self.baz();
+        }
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
     fn test_if_let_with_match_no_else() {
         check_assist(
             replace_if_let_with_match,
@@ -514,14 +601,151 @@
 
     #[test]
     fn test_if_let_with_match_let_chain() {
-        check_assist_not_applicable(
+        check_assist(
             replace_if_let_with_match,
             r#"
+#![feature(if_let_guard)]
+fn main() {
+    if $0let true = true && let Some(1) = None {} else { other() }
+}
+"#,
+            r#"
+#![feature(if_let_guard)]
+fn main() {
+    match true {
+        true if let Some(1) = None => {}
+        _ => other(),
+    }
+}
+"#,
+        );
+
+        check_assist(
+            replace_if_let_with_match,
+            r#"
+#![feature(if_let_guard)]
+fn main() {
+    if true {
+        $0if let ParenExpr(expr) = cond
+            && let Some(sub_expr) = expr.expr()
+        {
+            branch1(
+                "..."
+            )
+        } else if let LetExpr(let_expr) = cond {
+            branch2(
+                "..."
+            )
+        } else if let BinExpr(bin_expr) = cond
+            && let Some(kind) = bin_expr.op_kind()
+            && let Some(LetExpr(let_expr)) = foo(bin_expr)
+        {
+            branch3()
+        } else {
+            branch4(
+                "..."
+            )
+        }
+    }
+}
+"#,
+            r#"
+#![feature(if_let_guard)]
+fn main() {
+    if true {
+        match cond {
+            ParenExpr(expr) if let Some(sub_expr) = expr.expr() => {
+                branch1(
+                    "..."
+                )
+            }
+            LetExpr(let_expr) => {
+                branch2(
+                    "..."
+                )
+            }
+            BinExpr(bin_expr) if let Some(kind) = bin_expr.op_kind()
+                && let Some(LetExpr(let_expr)) = foo(bin_expr) => branch3(),
+            _ => {
+                branch4(
+                    "..."
+                )
+            }
+        }
+    }
+}
+"#,
+        );
+
+        check_assist(
+            replace_if_let_with_match,
+            r#"
+fn main() {
+    if $0let true = true
+        && true
+        && false
+    {
+        code()
+    } else {
+        other()
+    }
+}
+"#,
+            r#"
+fn main() {
+    match true {
+        true if true
+            && false => code(),
+        _ => other(),
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_if_let_with_match_let_chain_no_else() {
+        check_assist(
+            replace_if_let_with_match,
+            r#"
+#![feature(if_let_guard)]
 fn main() {
     if $0let true = true && let Some(1) = None {}
 }
 "#,
-        )
+            r#"
+#![feature(if_let_guard)]
+fn main() {
+    match true {
+        true if let Some(1) = None => {}
+        _ => (),
+    }
+}
+"#,
+        );
+
+        check_assist(
+            replace_if_let_with_match,
+            r#"
+fn main() {
+    if $0let true = true
+        && true
+        && false
+    {
+        code()
+    }
+}
+"#,
+            r#"
+fn main() {
+    match true {
+        true if true
+            && false => code(),
+        _ => (),
+    }
+}
+"#,
+        );
     }
 
     #[test]
@@ -553,10 +777,10 @@
             VariantData::Tuple(..) => false,
             _ if cond() => true,
             _ => {
-                    bar(
-                        123
-                    )
-                }
+                bar(
+                    123
+                )
+            }
         }
     }
 }
@@ -587,11 +811,11 @@
         if let VariantData::Struct(..) = *self {
             true
         } else {
-    match *self {
-            VariantData::Tuple(..) => false,
-            _ => false,
+            match *self {
+                VariantData::Tuple(..) => false,
+                _ => false,
+            }
         }
-}
     }
 }
 "#,
@@ -706,9 +930,12 @@
 fn main() {
     if true {
         $0if let Ok(rel_path) = path.strip_prefix(root_path) {
-            let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
+            let rel_path = RelativePathBuf::from_path(rel_path)
+                .ok()?;
             Some((*id, rel_path))
         } else {
+            let _ = some_code()
+                .clone();
             None
         }
     }
@@ -719,10 +946,52 @@
     if true {
         match path.strip_prefix(root_path) {
             Ok(rel_path) => {
-                let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
+                let rel_path = RelativePathBuf::from_path(rel_path)
+                    .ok()?;
                 Some((*id, rel_path))
             }
-            _ => None,
+            _ => {
+                let _ = some_code()
+                    .clone();
+                None
+            }
+        }
+    }
+}
+"#,
+        );
+
+        check_assist(
+            replace_if_let_with_match,
+            r#"
+fn main() {
+    if true {
+        $0if let Ok(rel_path) = path.strip_prefix(root_path) {
+            Foo {
+                x: 1
+            }
+        } else {
+            Foo {
+                x: 2
+            }
+        }
+    }
+}
+"#,
+            r#"
+fn main() {
+    if true {
+        match path.strip_prefix(root_path) {
+            Ok(rel_path) => {
+                Foo {
+                    x: 1
+                }
+            }
+            _ => {
+                Foo {
+                    x: 2
+                }
+            }
         }
     }
 }
@@ -1583,11 +1852,12 @@
 fn main() {
     if true {
         $0match path.strip_prefix(root_path) {
-            Ok(rel_path) => {
-                let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
-                Some((*id, rel_path))
+            Ok(rel_path) => Foo {
+                x: 2
             }
-            _ => None,
+            _ => Foo {
+                x: 3
+            },
         }
     }
 }
@@ -1596,15 +1866,55 @@
 fn main() {
     if true {
         if let Ok(rel_path) = path.strip_prefix(root_path) {
-            let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
+            Foo {
+                x: 2
+            }
+        } else {
+            Foo {
+                x: 3
+            }
+        }
+    }
+}
+"#,
+        );
+
+        check_assist(
+            replace_match_with_if_let,
+            r#"
+fn main() {
+    if true {
+        $0match path.strip_prefix(root_path) {
+            Ok(rel_path) => {
+                let rel_path = RelativePathBuf::from_path(rel_path)
+                    .ok()?;
+                Some((*id, rel_path))
+            }
+            _ => {
+                let _ = some_code()
+                    .clone();
+                None
+            },
+        }
+    }
+}
+"#,
+            r#"
+fn main() {
+    if true {
+        if let Ok(rel_path) = path.strip_prefix(root_path) {
+            let rel_path = RelativePathBuf::from_path(rel_path)
+                .ok()?;
             Some((*id, rel_path))
         } else {
+            let _ = some_code()
+                .clone();
             None
         }
     }
 }
 "#,
-        )
+        );
     }
 
     #[test]
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index e43516f..fbdd066 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -57,6 +57,14 @@
             });
         non_trivial_children.next().is_some()
     };
+    if stmt_list
+        .syntax()
+        .children_with_tokens()
+        .filter_map(NodeOrToken::into_token)
+        .any(|token| token.kind() == syntax::SyntaxKind::COMMENT)
+    {
+        return None;
+    }
 
     if let Some(expr) = stmt_list.tail_expr() {
         if has_anything_else(expr.syntax()) {