Skip to content

Commit c67db1b

Browse files
Merge #10543
10543: Narrow add_missing_match_arms assist range r=Veykril a=antonfirsov Contributes to #10220 with logic borrowed from #10267. Note: if anyone has recommendations for further analyzers to check, I'm happy to (hard to do it on my own, I'm completely new to the language). Co-authored-by: Anton Firszov <[email protected]>
2 parents 0ff5e0a + 3d9ce6b commit c67db1b

File tree

1 file changed

+118
-8
lines changed

1 file changed

+118
-8
lines changed

crates/ide_assists/src/handlers/add_missing_match_arms.rs

Lines changed: 118 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use hir::{Adt, HasSource, ModuleDef, Semantics};
55
use ide_db::helpers::{mod_path_to_ast, FamousDefs};
66
use ide_db::RootDatabase;
77
use itertools::Itertools;
8-
use syntax::ast::{self, make, AstNode, HasName, MatchArm, Pat};
8+
use syntax::ast::{self, make, AstNode, HasName, MatchArm, MatchArmList, MatchExpr, Pat};
9+
use syntax::TextRange;
910

1011
use crate::{
1112
utils::{self, render_snippet, Cursor},
@@ -39,6 +40,22 @@ use crate::{
3940
pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
4041
let match_expr = ctx.find_node_at_offset_with_descend::<ast::MatchExpr>()?;
4142
let match_arm_list = match_expr.match_arm_list()?;
43+
let target_range: TextRange;
44+
45+
if let None = cursor_at_trivial_match_arm_list(&ctx, &match_expr, &match_arm_list) {
46+
target_range = TextRange::new(
47+
ctx.sema.original_range(match_expr.syntax()).range.start(),
48+
ctx.sema.original_range(match_arm_list.syntax()).range.start(),
49+
);
50+
51+
let cursor_in_range = target_range.contains_range(ctx.selection_trimmed());
52+
if !cursor_in_range {
53+
cov_mark::hit!(not_applicable_outside_of_range_right);
54+
return None;
55+
}
56+
} else {
57+
target_range = ctx.sema.original_range(match_expr.syntax()).range;
58+
}
4259

4360
let expr = match_expr.expr()?;
4461

@@ -121,11 +138,10 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext) ->
121138
return None;
122139
}
123140

124-
let target = ctx.sema.original_range(match_expr.syntax()).range;
125141
acc.add(
126142
AssistId("add_missing_match_arms", AssistKind::QuickFix),
127143
"Fill match arms",
128-
target,
144+
target_range,
129145
|builder| {
130146
let new_match_arm_list = match_arm_list.clone_for_update();
131147
let missing_arms = missing_pats
@@ -177,6 +193,29 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext) ->
177193
)
178194
}
179195

196+
fn cursor_at_trivial_match_arm_list(
197+
ctx: &AssistContext,
198+
match_expr: &MatchExpr,
199+
match_arm_list: &MatchArmList,
200+
) -> Option<()> {
201+
// match x { $0 }
202+
if match_arm_list.arms().next() == None {
203+
cov_mark::hit!(add_missing_match_arms_empty_body);
204+
return Some(());
205+
}
206+
207+
// match { _$0 => {...} }
208+
let wild_pat = ctx.find_node_at_offset_with_descend::<ast::WildcardPat>()?;
209+
let arm = wild_pat.syntax().parent().and_then(ast::MatchArm::cast)?;
210+
let arm_match_expr = arm.syntax().ancestors().nth(2).and_then(ast::MatchExpr::cast)?;
211+
if arm_match_expr == *match_expr {
212+
cov_mark::hit!(add_missing_match_arms_trivial_arm);
213+
return Some(());
214+
}
215+
216+
None
217+
}
218+
180219
fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool {
181220
!existing_pats.iter().any(|pat| does_pat_match_variant(pat, var))
182221
}
@@ -306,6 +345,39 @@ fn main() {
306345
);
307346
}
308347

348+
#[test]
349+
fn not_applicable_outside_of_range_left() {
350+
check_assist_not_applicable(
351+
add_missing_match_arms,
352+
r#"
353+
enum A { X, Y }
354+
355+
fn foo(a: A) {
356+
$0 match a {
357+
A::X => { }
358+
}
359+
}
360+
"#,
361+
);
362+
}
363+
364+
#[test]
365+
fn not_applicable_outside_of_range_right() {
366+
cov_mark::check!(not_applicable_outside_of_range_right);
367+
check_assist_not_applicable(
368+
add_missing_match_arms,
369+
r#"
370+
enum A { X, Y }
371+
372+
fn foo(a: A) {
373+
match a {$0
374+
A::X => { }
375+
}
376+
}
377+
"#,
378+
);
379+
}
380+
309381
#[test]
310382
fn all_boolean_match_arms_provided() {
311383
check_assist_not_applicable(
@@ -583,14 +655,15 @@ fn main() {
583655

584656
#[test]
585657
fn add_missing_match_arms_empty_body() {
658+
cov_mark::check!(add_missing_match_arms_empty_body);
586659
check_assist(
587660
add_missing_match_arms,
588661
r#"
589662
enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
590663
591664
fn main() {
592665
let a = A::As;
593-
match a$0 {}
666+
match a {$0}
594667
}
595668
"#,
596669
r#"
@@ -853,7 +926,7 @@ fn foo(a: &mut A) {
853926
}
854927

855928
#[test]
856-
fn add_missing_match_arms_target() {
929+
fn add_missing_match_arms_target_simple() {
857930
check_assist_target(
858931
add_missing_match_arms,
859932
r#"
@@ -867,8 +940,26 @@ fn main() {
867940
);
868941
}
869942

943+
#[test]
944+
fn add_missing_match_arms_target_complex() {
945+
check_assist_target(
946+
add_missing_match_arms,
947+
r#"
948+
enum E { X, Y }
949+
950+
fn main() {
951+
match E::X$0 {
952+
E::X => {}
953+
}
954+
}
955+
"#,
956+
"match E::X ",
957+
);
958+
}
959+
870960
#[test]
871961
fn add_missing_match_arms_trivial_arm() {
962+
cov_mark::check!(add_missing_match_arms_trivial_arm);
872963
check_assist(
873964
add_missing_match_arms,
874965
r#"
@@ -893,6 +984,25 @@ fn main() {
893984
);
894985
}
895986

987+
#[test]
988+
fn wildcard_inside_expression_not_applicable() {
989+
check_assist_not_applicable(
990+
add_missing_match_arms,
991+
r#"
992+
enum E { X, Y }
993+
994+
fn foo(e : E) {
995+
match e {
996+
_ => {
997+
println!("1");$0
998+
println!("2");
999+
}
1000+
}
1001+
}
1002+
"#,
1003+
);
1004+
}
1005+
8961006
#[test]
8971007
fn add_missing_match_arms_qualifies_path() {
8981008
check_assist(
@@ -928,8 +1038,8 @@ fn main() {
9281038
r#"
9291039
enum A { One, Two }
9301040
fn foo(a: A) {
931-
match a {
932-
// foo bar baz$0
1041+
match a $0 {
1042+
// foo bar baz
9331043
A::One => {}
9341044
// This is where the rest should be
9351045
}
@@ -938,7 +1048,7 @@ fn foo(a: A) {
9381048
r#"
9391049
enum A { One, Two }
9401050
fn foo(a: A) {
941-
match a {
1051+
match a {
9421052
// foo bar baz
9431053
A::One => {}
9441054
$0A::Two => todo!(),

0 commit comments

Comments
 (0)