about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs40
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs41
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs149
5 files changed, 220 insertions, 17 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
index 1972f166134..2c7d9e97c44 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
@@ -61,6 +61,7 @@ pub(crate) fn complete_expr_path(
         after_if_expr,
         in_condition,
         incomplete_let,
+        after_incomplete_let,
         in_value,
         ref ref_expr_parent,
         after_amp,
@@ -385,8 +386,11 @@ pub(crate) fn complete_expr_path(
                         add_keyword("let", "let $1 = $0;");
                     }
 
-                    if after_if_expr {
+                    if after_if_expr || after_incomplete_let {
                         add_keyword("else", "else {\n    $0\n}");
+                    }
+
+                    if after_if_expr {
                         add_keyword("else if", "else if $1 {\n    $0\n}");
                     }
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs
index aea4e119f20..b3d770997ab 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs
@@ -248,6 +248,46 @@ fn main() {
         );
 
         check_edit(
+            "else",
+            r#"
+fn main() {
+    let x = if true {
+        ()
+    } $0
+    let y = 92;
+}
+"#,
+            r#"
+fn main() {
+    let x = if true {
+        ()
+    } else {
+    $0
+};
+    let y = 92;
+}
+"#,
+        );
+
+        check_edit(
+            "else",
+            r#"
+fn main() {
+    let x = 2 $0
+    let y = 92;
+}
+"#,
+            r#"
+fn main() {
+    let x = 2 else {
+    $0
+};
+    let y = 92;
+}
+"#,
+        );
+
+        check_edit(
             "loop",
             r#"
 fn main() {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
index 265462d2201..007475688d2 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -147,6 +147,7 @@ pub(crate) struct PathExprCtx<'db> {
     /// Whether this expression is the direct condition of an if or while expression
     pub(crate) in_condition: bool,
     pub(crate) incomplete_let: bool,
+    pub(crate) after_incomplete_let: bool,
     pub(crate) in_value: bool,
     pub(crate) ref_expr_parent: Option<ast::RefExpr>,
     pub(crate) after_amp: bool,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index 17978b4c107..33b98a33cab 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -947,25 +947,29 @@ fn classify_name_ref<'db>(
             None
         }
     };
-    let after_if_expr = |node: SyntaxNode| {
-        let prev_expr = (|| {
-            let node = match node.parent().and_then(ast::ExprStmt::cast) {
-                Some(stmt) => stmt.syntax().clone(),
-                None => node,
-            };
-            let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
+    let prev_expr = |node: SyntaxNode| {
+        let node = match node.parent().and_then(ast::ExprStmt::cast) {
+            Some(stmt) => stmt.syntax().clone(),
+            None => node,
+        };
+        let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
 
-            match_ast! {
-                match prev_sibling {
-                    ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()),
-                    ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()),
-                    ast::Expr(expr) => Some(expr),
-                    _ => None,
-                }
+        match_ast! {
+            match prev_sibling {
+                ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()),
+                ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()),
+                ast::Expr(expr) => Some(expr),
+                _ => None,
             }
-        })();
+        }
+    };
+    let after_if_expr = |node: SyntaxNode| {
+        let prev_expr = prev_expr(node);
         matches!(prev_expr, Some(ast::Expr::IfExpr(_)))
     };
+    let after_incomplete_let = |node: SyntaxNode| {
+        prev_expr(node).and_then(|it| it.syntax().parent()).and_then(ast::LetStmt::cast)
+    };
 
     // We do not want to generate path completions when we are sandwiched between an item decl signature and its body.
     // ex. trait Foo $0 {}
@@ -1265,10 +1269,14 @@ fn classify_name_ref<'db>(
         };
         let is_func_update = func_update_record(it);
         let in_condition = is_in_condition(&expr);
+        let after_incomplete_let = after_incomplete_let(it.clone()).is_some();
+        let incomplete_expr_stmt =
+            it.parent().and_then(ast::ExprStmt::cast).map(|it| it.semicolon_token().is_none());
         let incomplete_let = it
             .parent()
             .and_then(ast::LetStmt::cast)
-            .is_some_and(|it| it.semicolon_token().is_none());
+            .is_some_and(|it| it.semicolon_token().is_none())
+            || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true);
         let in_value = it.parent().and_then(Either::<ast::LetStmt, ast::ArgList>::cast).is_some();
         let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax());
 
@@ -1292,6 +1300,7 @@ fn classify_name_ref<'db>(
                 self_param,
                 in_value,
                 incomplete_let,
+                after_incomplete_let,
                 impl_,
                 in_match_guard,
             },
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
index af6ef34761e..79137104b56 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
@@ -451,6 +451,155 @@ fn completes_in_let_initializer() {
 }
 
 #[test]
+fn completes_let_else() {
+    check(
+        r#"fn main() { let _ = 2 $0 }"#,
+        expect![[r#"
+            fn main() fn()
+            bt u32     u32
+            kw async
+            kw const
+            kw crate::
+            kw else
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw impl for
+            kw let
+            kw letm
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+
+    check(
+        r#"fn main() { let _ = 2 el$0 }"#,
+        expect![[r#"
+            fn main() fn()
+            bt u32     u32
+            kw async
+            kw const
+            kw crate::
+            kw else
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw impl for
+            kw let
+            kw letm
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+
+    check_edit(
+        "else",
+        r#"
+fn main() {
+    let _ = 2 $0
+}
+"#,
+        r#"
+fn main() {
+    let _ = 2 else {
+    $0
+};
+}
+"#,
+    );
+
+    check_edit(
+        "else",
+        r#"
+fn main() {
+    let _ = 2 el$0
+}
+"#,
+        r#"
+fn main() {
+    let _ = 2 else {
+    $0
+};
+}
+"#,
+    );
+
+    check_edit(
+        "else",
+        r#"
+fn main() {
+    let _ = 2 $0;
+}
+"#,
+        r#"
+fn main() {
+    let _ = 2 else {
+    $0
+};
+}
+"#,
+    );
+
+    check_edit(
+        "else",
+        r#"
+fn main() {
+    let _ = 2 el$0;
+}
+"#,
+        r#"
+fn main() {
+    let _ = 2 else {
+    $0
+};
+}
+"#,
+    );
+}
+
+#[test]
 fn completes_after_ref_expr() {
     check(
         r#"fn main() { let _ = &$0 }"#,