Skip to content

Commit 477352d

Browse files
committed
Add BreakExpr completion suggest
1 parent 05b7cbc commit 477352d

File tree

2 files changed

+108
-1
lines changed

2 files changed

+108
-1
lines changed

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,14 @@ fn expected_type_and_name<'db>(
736736
});
737737
(ty, None)
738738
},
739+
ast::BreakExpr(it) => {
740+
let ty = match find_breakvalueable(&node, it.lifetime(), sema) {
741+
Some(Either::Left(block_expr)) => sema.type_of_expr(&block_expr.into()),
742+
Some(Either::Right(loop_expr)) => sema.type_of_expr(&loop_expr.into()),
743+
None => None,
744+
};
745+
(ty.map(TypeInfo::original), None)
746+
},
739747
ast::ClosureExpr(it) => {
740748
let ty = sema.type_of_expr(&it.into());
741749
ty.and_then(|ty| ty.original.as_callable(sema.db))
@@ -1855,6 +1863,31 @@ fn is_in_breakable(node: &SyntaxNode) -> BreakableKind {
18551863
.unwrap_or(BreakableKind::None)
18561864
}
18571865

1866+
fn find_breakvalueable(
1867+
node: &SyntaxNode,
1868+
label: Option<ast::Lifetime>,
1869+
sema: &Semantics<'_, RootDatabase>,
1870+
) -> Option<Either<ast::BlockExpr, ast::LoopExpr>> {
1871+
let label_eq = |value: Option<ast::Label>| match (&label, &value) {
1872+
(None, _) => true,
1873+
(Some(_), None) => false,
1874+
(Some(label), Some(found)) => {
1875+
found.lifetime().is_some_and(|found| label.syntax().text() == found.syntax().text())
1876+
}
1877+
};
1878+
sema.ancestors_with_macros(node.clone()).find_map(|it| {
1879+
match_ast! {
1880+
match it {
1881+
ast::LoopExpr(it) => label_eq(it.label()).then_some(Either::Right(it)),
1882+
ast::BlockExpr(it) => it.label().and_then(|label| {
1883+
label_eq(Some(label)).then_some(Either::Left(it))
1884+
}),
1885+
_ => None,
1886+
}
1887+
}
1888+
})
1889+
}
1890+
18581891
fn is_in_block(node: &SyntaxNode) -> bool {
18591892
node.parent()
18601893
.map(|node| ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind()))
@@ -1905,7 +1938,8 @@ fn prev_special_biased_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken {
19051938
| T![^=]
19061939
| T![return]
19071940
| T![break]
1908-
| T![continue] = prev.kind()
1941+
| T![continue]
1942+
| T![lifetime_ident] = prev.kind()
19091943
{
19101944
token = prev
19111945
}

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

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,3 +525,76 @@ fn foo() {
525525
expect![[r#"ty: State, name: ?"#]],
526526
);
527527
}
528+
529+
#[test]
530+
fn expected_type_break_expr_in_loop() {
531+
check_expected_type_and_name(
532+
r#"
533+
enum State { Stop }
534+
fn foo() {
535+
let _x: State = loop {
536+
{
537+
break State::Stop;
538+
break $0;
539+
}
540+
};
541+
}
542+
"#,
543+
expect![[r#"ty: State, name: ?"#]],
544+
);
545+
546+
check_expected_type_and_name(
547+
r#"
548+
enum State { Stop }
549+
fn foo() {
550+
let _x: State = 'a: loop {
551+
{
552+
break State::Stop;
553+
break $0;
554+
}
555+
};
556+
}
557+
"#,
558+
expect![[r#"ty: State, name: ?"#]],
559+
);
560+
}
561+
562+
#[test]
563+
fn expected_type_break_expr_in_labeled_loop() {
564+
check_expected_type_and_name(
565+
r#"
566+
enum State { Stop }
567+
fn foo() {
568+
let _x: State = 'a: loop {
569+
let _y: i32 = loop {
570+
{
571+
break 'a State::Stop;
572+
break 'a $0;
573+
}
574+
};
575+
};
576+
}
577+
"#,
578+
expect![[r#"ty: State, name: ?"#]],
579+
);
580+
}
581+
582+
#[test]
583+
fn expected_type_break_expr_in_labeled_block() {
584+
check_expected_type_and_name(
585+
r#"
586+
enum State { Stop }
587+
fn foo() {
588+
let _x: State = 'a: {
589+
let _y: i32 = 'b: {
590+
{
591+
break 'a State::Stop;
592+
break 'a $0;
593+
};
594+
};
595+
};
596+
}
597+
"#,
598+
expect![[r#"ty: State, name: ?"#]],
599+
);
600+
}

0 commit comments

Comments
 (0)