Skip to content

Commit 07cf8cb

Browse files
committed
Add break value completion support
```rust fn foo() -> i32 { loop { $0 } } ``` **Before this PR**: ```rust fn foo() -> i32 { loop { break; } } ``` **After this PR**: ```rust fn foo() -> i32 { loop { break $0; } } ```
1 parent 8192c63 commit 07cf8cb

File tree

4 files changed

+65
-12
lines changed

4 files changed

+65
-12
lines changed

crates/ide-completion/src/completions/expr.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use syntax::ast;
99
use crate::{
1010
CompletionContext, Completions,
1111
completions::record::add_default_update,
12-
context::{BreakableKind, PathCompletionCtx, PathExprCtx, Qualified},
12+
context::{PathCompletionCtx, PathExprCtx, Qualified},
1313
};
1414

1515
struct PathCallback<'a, F> {
@@ -57,7 +57,6 @@ pub(crate) fn complete_expr_path(
5757

5858
let &PathExprCtx {
5959
in_block_expr,
60-
in_breakable,
6160
after_if_expr,
6261
in_condition,
6362
incomplete_let,
@@ -67,6 +66,7 @@ pub(crate) fn complete_expr_path(
6766
after_amp,
6867
ref is_func_update,
6968
ref innermost_ret_ty,
69+
ref innermost_breakable_ty,
7070
ref impl_,
7171
in_match_guard,
7272
..
@@ -404,14 +404,21 @@ pub(crate) fn complete_expr_path(
404404
add_keyword("mut", "mut ");
405405
}
406406

407-
if in_breakable != BreakableKind::None {
407+
if let Some(loop_ty) = innermost_breakable_ty {
408408
if in_block_expr {
409409
add_keyword("continue", "continue;");
410-
add_keyword("break", "break;");
411410
} else {
412411
add_keyword("continue", "continue");
413-
add_keyword("break", "break");
414412
}
413+
add_keyword(
414+
"break",
415+
match (loop_ty.is_unit(), in_block_expr) {
416+
(true, true) => "break;",
417+
(true, false) => "break",
418+
(false, true) => "break $0;",
419+
(false, false) => "break $0",
420+
},
421+
);
415422
}
416423

417424
if let Some(ret_ty) = innermost_ret_ty {

crates/ide-completion/src/context.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ pub(crate) struct PathExprCtx<'db> {
155155
pub(crate) is_func_update: Option<ast::RecordExpr>,
156156
pub(crate) self_param: Option<hir::SelfParam>,
157157
pub(crate) innermost_ret_ty: Option<hir::Type<'db>>,
158+
pub(crate) innermost_breakable_ty: Option<hir::Type<'db>>,
158159
pub(crate) impl_: Option<ast::Impl>,
159160
/// Whether this expression occurs in match arm guard position: before the
160161
/// fat arrow token

crates/ide-completion/src/context/analysis.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,7 @@ fn classify_name_ref<'db>(
902902
receiver_ty,
903903
kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal },
904904
receiver,
905-
ctx: DotAccessExprCtx { in_block_expr: is_in_block(field.syntax()), in_breakable: is_in_breakable(field.syntax()) }
905+
ctx: DotAccessExprCtx { in_block_expr: is_in_block(field.syntax()), in_breakable: is_in_breakable(field.syntax()).0 }
906906
});
907907
return Some(make_res(kind));
908908
},
@@ -917,7 +917,7 @@ fn classify_name_ref<'db>(
917917
receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
918918
kind: DotAccessKind::Method { has_parens },
919919
receiver,
920-
ctx: DotAccessExprCtx { in_block_expr: is_in_block(method.syntax()), in_breakable: is_in_breakable(method.syntax()) }
920+
ctx: DotAccessExprCtx { in_block_expr: is_in_block(method.syntax()), in_breakable: is_in_breakable(method.syntax()).0 }
921921
});
922922
return Some(make_res(kind));
923923
},
@@ -1223,7 +1223,7 @@ fn classify_name_ref<'db>(
12231223
let make_path_kind_expr = |expr: ast::Expr| {
12241224
let it = expr.syntax();
12251225
let in_block_expr = is_in_block(it);
1226-
let in_loop_body = is_in_breakable(it);
1226+
let (in_loop_body, innermost_breakable) = is_in_breakable(it);
12271227
let after_if_expr = after_if_expr(it.clone());
12281228
let ref_expr_parent =
12291229
path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast);
@@ -1277,6 +1277,11 @@ fn classify_name_ref<'db>(
12771277
None => (None, None),
12781278
}
12791279
};
1280+
let innermost_breakable_ty = innermost_breakable
1281+
.and_then(ast::Expr::cast)
1282+
.and_then(|expr| find_node_in_file_compensated(sema, original_file, &expr))
1283+
.and_then(|expr| sema.type_of_expr(&expr))
1284+
.map(|ty| if ty.original.is_never() { ty.adjusted() } else { ty.original() });
12801285
let is_func_update = func_update_record(it);
12811286
let in_condition = is_in_condition(&expr);
12821287
let after_incomplete_let = after_incomplete_let(it.clone()).is_some();
@@ -1307,6 +1312,7 @@ fn classify_name_ref<'db>(
13071312
after_amp,
13081313
is_func_update,
13091314
innermost_ret_ty,
1315+
innermost_breakable_ty,
13101316
self_param,
13111317
in_value,
13121318
incomplete_let,
@@ -1854,7 +1860,7 @@ fn is_in_token_of_for_loop(path: &ast::Path) -> bool {
18541860
.unwrap_or(false)
18551861
}
18561862

1857-
fn is_in_breakable(node: &SyntaxNode) -> BreakableKind {
1863+
fn is_in_breakable(node: &SyntaxNode) -> (BreakableKind, Option<SyntaxNode>) {
18581864
node.ancestors()
18591865
.take_while(|it| it.kind() != SyntaxKind::FN && it.kind() != SyntaxKind::CLOSURE_EXPR)
18601866
.find_map(|it| {
@@ -1863,15 +1869,15 @@ fn is_in_breakable(node: &SyntaxNode) -> BreakableKind {
18631869
ast::ForExpr(it) => (BreakableKind::For, it.loop_body()),
18641870
ast::WhileExpr(it) => (BreakableKind::While, it.loop_body()),
18651871
ast::LoopExpr(it) => (BreakableKind::Loop, it.loop_body()),
1866-
ast::BlockExpr(it) => return it.label().map(|_| BreakableKind::Block),
1872+
ast::BlockExpr(it) => return it.label().map(|_| (BreakableKind::Block, Some(it.syntax().clone()))),
18671873
_ => return None,
18681874
}
18691875
};
18701876
loop_body
18711877
.filter(|it| it.syntax().text_range().contains_range(node.text_range()))
1872-
.map(|_| breakable)
1878+
.map(|_| (breakable, Some(it)))
18731879
})
1874-
.unwrap_or(BreakableKind::None)
1880+
.unwrap_or((BreakableKind::None, None))
18751881
}
18761882

18771883
fn is_in_block(node: &SyntaxNode) -> bool {

crates/ide-completion/src/tests/expression.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,45 @@ fn return_value_no_block() {
10921092
);
10931093
}
10941094

1095+
#[test]
1096+
fn break_unit_block() {
1097+
check_edit("break", r#"fn f() { loop { break; $0 } }"#, r#"fn f() { loop { break; break; } }"#);
1098+
check_edit("break", r#"fn f() { loop { $0 } }"#, r#"fn f() { loop { break; } }"#);
1099+
}
1100+
1101+
#[test]
1102+
fn break_unit_no_block() {
1103+
check_edit(
1104+
"break",
1105+
r#"fn f() { loop { break; match () { () => $0 } } }"#,
1106+
r#"fn f() { loop { break; match () { () => break } } }"#,
1107+
);
1108+
1109+
check_edit(
1110+
"break",
1111+
r#"fn f() { loop { match () { () => $0 } } }"#,
1112+
r#"fn f() { loop { match () { () => break } } }"#,
1113+
);
1114+
}
1115+
1116+
#[test]
1117+
fn break_value_block() {
1118+
check_edit(
1119+
"break",
1120+
r#"fn f() -> i32 { loop { $0 } }"#,
1121+
r#"fn f() -> i32 { loop { break $0; } }"#,
1122+
);
1123+
}
1124+
1125+
#[test]
1126+
fn break_value_no_block() {
1127+
check_edit(
1128+
"break",
1129+
r#"fn f() -> i32 { loop { match () { () => $0 } } }"#,
1130+
r#"fn f() -> i32 { loop { match () { () => break $0 } } }"#,
1131+
);
1132+
}
1133+
10951134
#[test]
10961135
fn else_completion_after_if() {
10971136
check(

0 commit comments

Comments
 (0)