Skip to content

Commit a45726d

Browse files
authored
Merge pull request #19546 from roife/branch-value-highlights
feat: highlighting of return values while the cursor is on `match` / `if` / `=>`
2 parents 3e5c46f + dc9e9fd commit a45726d

File tree

6 files changed

+824
-10
lines changed

6 files changed

+824
-10
lines changed

src/tools/rust-analyzer/crates/ide/src/goto_definition.rs

Lines changed: 238 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,13 +291,14 @@ fn handle_control_flow_keywords(
291291
token: &SyntaxToken,
292292
) -> Option<Vec<NavigationTarget>> {
293293
match token.kind() {
294-
// For `fn` / `loop` / `while` / `for` / `async`, return the keyword it self,
294+
// For `fn` / `loop` / `while` / `for` / `async` / `match`, return the keyword it self,
295295
// so that VSCode will find the references when using `ctrl + click`
296296
T![fn] | T![async] | T![try] | T![return] => nav_for_exit_points(sema, token),
297297
T![loop] | T![while] | T![break] | T![continue] => nav_for_break_points(sema, token),
298298
T![for] if token.parent().and_then(ast::ForExpr::cast).is_some() => {
299299
nav_for_break_points(sema, token)
300300
}
301+
T![match] | T![=>] | T![if] => nav_for_branch_exit_points(sema, token),
301302
_ => None,
302303
}
303304
}
@@ -407,6 +408,91 @@ fn nav_for_exit_points(
407408
Some(navs)
408409
}
409410

411+
pub(crate) fn find_branch_root(
412+
sema: &Semantics<'_, RootDatabase>,
413+
token: &SyntaxToken,
414+
) -> Vec<SyntaxNode> {
415+
let find_nodes = |node_filter: fn(SyntaxNode) -> Option<SyntaxNode>| {
416+
sema.descend_into_macros(token.clone())
417+
.into_iter()
418+
.filter_map(|token| node_filter(token.parent()?))
419+
.collect_vec()
420+
};
421+
422+
match token.kind() {
423+
T![match] => find_nodes(|node| Some(ast::MatchExpr::cast(node)?.syntax().clone())),
424+
T![=>] => find_nodes(|node| Some(ast::MatchArm::cast(node)?.syntax().clone())),
425+
T![if] => find_nodes(|node| {
426+
let if_expr = ast::IfExpr::cast(node)?;
427+
428+
let root_if = iter::successors(Some(if_expr.clone()), |if_expr| {
429+
let parent_if = if_expr.syntax().parent().and_then(ast::IfExpr::cast)?;
430+
let ast::ElseBranch::IfExpr(else_branch) = parent_if.else_branch()? else {
431+
return None;
432+
};
433+
434+
(else_branch.syntax() == if_expr.syntax()).then_some(parent_if)
435+
})
436+
.last()?;
437+
438+
Some(root_if.syntax().clone())
439+
}),
440+
_ => vec![],
441+
}
442+
}
443+
444+
fn nav_for_branch_exit_points(
445+
sema: &Semantics<'_, RootDatabase>,
446+
token: &SyntaxToken,
447+
) -> Option<Vec<NavigationTarget>> {
448+
let db = sema.db;
449+
450+
let navs = match token.kind() {
451+
T![match] => find_branch_root(sema, token)
452+
.into_iter()
453+
.filter_map(|node| {
454+
let file_id = sema.hir_file_for(&node);
455+
let match_expr = ast::MatchExpr::cast(node)?;
456+
let focus_range = match_expr.match_token()?.text_range();
457+
let match_expr_in_file = InFile::new(file_id, match_expr.into());
458+
Some(expr_to_nav(db, match_expr_in_file, Some(focus_range)))
459+
})
460+
.flatten()
461+
.collect_vec(),
462+
463+
T![=>] => find_branch_root(sema, token)
464+
.into_iter()
465+
.filter_map(|node| {
466+
let match_arm = ast::MatchArm::cast(node)?;
467+
let match_expr = sema
468+
.ancestors_with_macros(match_arm.syntax().clone())
469+
.find_map(ast::MatchExpr::cast)?;
470+
let file_id = sema.hir_file_for(match_expr.syntax());
471+
let focus_range = match_arm.fat_arrow_token()?.text_range();
472+
let match_expr_in_file = InFile::new(file_id, match_expr.into());
473+
Some(expr_to_nav(db, match_expr_in_file, Some(focus_range)))
474+
})
475+
.flatten()
476+
.collect_vec(),
477+
478+
T![if] => find_branch_root(sema, token)
479+
.into_iter()
480+
.filter_map(|node| {
481+
let file_id = sema.hir_file_for(&node);
482+
let if_expr = ast::IfExpr::cast(node)?;
483+
let focus_range = if_expr.if_token()?.text_range();
484+
let if_expr_in_file = InFile::new(file_id, if_expr.into());
485+
Some(expr_to_nav(db, if_expr_in_file, Some(focus_range)))
486+
})
487+
.flatten()
488+
.collect_vec(),
489+
490+
_ => return Some(Vec::new()),
491+
};
492+
493+
Some(navs)
494+
}
495+
410496
pub(crate) fn find_loops(
411497
sema: &Semantics<'_, RootDatabase>,
412498
token: &SyntaxToken,
@@ -3614,4 +3700,155 @@ fn foo() {
36143700
"#,
36153701
);
36163702
}
3703+
3704+
#[test]
3705+
fn goto_def_for_match_keyword() {
3706+
check(
3707+
r#"
3708+
fn main() {
3709+
match$0 0 {
3710+
// ^^^^^
3711+
0 => {},
3712+
_ => {},
3713+
}
3714+
}
3715+
"#,
3716+
);
3717+
}
3718+
3719+
#[test]
3720+
fn goto_def_for_match_arm_fat_arrow() {
3721+
check(
3722+
r#"
3723+
fn main() {
3724+
match 0 {
3725+
0 =>$0 {},
3726+
// ^^
3727+
_ => {},
3728+
}
3729+
}
3730+
"#,
3731+
);
3732+
}
3733+
3734+
#[test]
3735+
fn goto_def_for_if_keyword() {
3736+
check(
3737+
r#"
3738+
fn main() {
3739+
if$0 true {
3740+
// ^^
3741+
()
3742+
}
3743+
}
3744+
"#,
3745+
);
3746+
}
3747+
3748+
#[test]
3749+
fn goto_def_for_match_nested_in_if() {
3750+
check(
3751+
r#"
3752+
fn main() {
3753+
if true {
3754+
match$0 0 {
3755+
// ^^^^^
3756+
0 => {},
3757+
_ => {},
3758+
}
3759+
}
3760+
}
3761+
"#,
3762+
);
3763+
}
3764+
3765+
#[test]
3766+
fn goto_def_for_multiple_match_expressions() {
3767+
check(
3768+
r#"
3769+
fn main() {
3770+
match 0 {
3771+
0 => {},
3772+
_ => {},
3773+
};
3774+
3775+
match$0 1 {
3776+
// ^^^^^
3777+
1 => {},
3778+
_ => {},
3779+
}
3780+
}
3781+
"#,
3782+
);
3783+
}
3784+
3785+
#[test]
3786+
fn goto_def_for_nested_match_expressions() {
3787+
check(
3788+
r#"
3789+
fn main() {
3790+
match 0 {
3791+
0 => match$0 1 {
3792+
// ^^^^^
3793+
1 => {},
3794+
_ => {},
3795+
},
3796+
_ => {},
3797+
}
3798+
}
3799+
"#,
3800+
);
3801+
}
3802+
3803+
#[test]
3804+
fn goto_def_for_if_else_chains() {
3805+
check(
3806+
r#"
3807+
fn main() {
3808+
if true {
3809+
// ^^
3810+
()
3811+
} else if$0 false {
3812+
()
3813+
} else {
3814+
()
3815+
}
3816+
}
3817+
"#,
3818+
);
3819+
}
3820+
3821+
#[test]
3822+
fn goto_def_for_match_with_guards() {
3823+
check(
3824+
r#"
3825+
fn main() {
3826+
match 42 {
3827+
x if x > 0 =>$0 {},
3828+
// ^^
3829+
_ => {},
3830+
}
3831+
}
3832+
"#,
3833+
);
3834+
}
3835+
3836+
#[test]
3837+
fn goto_def_for_match_with_macro_arm() {
3838+
check(
3839+
r#"
3840+
macro_rules! arm {
3841+
() => { 0 => {} };
3842+
}
3843+
3844+
fn main() {
3845+
match$0 0 {
3846+
// ^^^^^
3847+
arm!(),
3848+
_ => {},
3849+
}
3850+
}
3851+
"#,
3852+
);
3853+
}
36173854
}

0 commit comments

Comments
 (0)