Skip to content

Commit

Permalink
support Self in match pattern rust-lang#4650
Browse files Browse the repository at this point in the history
Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
  • Loading branch information
bnjjj committed May 30, 2020
1 parent 07060b3 commit ca3b679
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 3 deletions.
62 changes: 59 additions & 3 deletions crates/ra_assists/src/handlers/fill_match_arms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,35 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
let variants = enum_def.variants(ctx.db);

let self_pattern = arms
.first()
.and_then(|arm| arm.pat())
.and_then(|pat| pat.syntax().first_child())
.and_then(|node| {
String::from(node.text()).split("::").map(|elt| elt.to_string()).next()
})
.map(|first_path_seg| first_path_seg.as_str() == "Self")
.unwrap_or_default();

let mut variants = variants
.into_iter()
.filter_map(|variant| build_pat(ctx.db, module, variant))
.filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
.map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
.map(|pat| {
let pat = if self_pattern {
let pat = pat.syntax().first_child().map(|node| {
String::from(node.text())
.split("::")
.map(|elt| elt.to_string())
.skip(1)
.collect::<Vec<String>>()
});
make::path_pat_from_string(format!("Self::{}", pat.unwrap().join("::")))
} else {
pat
};
make::match_arm(iter::once(pat), make::expr_empty_block())
})
.collect::<Vec<_>>();
if Some(enum_def) == FamousDefs(&ctx.sema, module.krate()).core_option_Option() {
// Match `Some` variant first.
Expand Down Expand Up @@ -136,8 +160,12 @@ fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
}

fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
let pat_head = pat.syntax().first_child().map(|node| node.text());
let var_head = var.syntax().first_child().map(|node| node.text());
let pat_head = pat.syntax().first_child().map(|node| {
String::from(node.text()).split("::").map(|elt| elt.to_string()).skip(1).join("::")
});
let var_head = var.syntax().first_child().map(|node| {
String::from(node.text()).split("::").map(|elt| elt.to_string()).skip(1).join("::")
});

pat_head == var_head
}
Expand Down Expand Up @@ -686,6 +714,34 @@ mod tests {
);
}

#[test]
fn fill_match_arms_with_self() {
check_assist(
fill_match_arms,
r#"
enum A { One, Two }
impl A {
fn foo(self) {
match self {
Self::One => {},<|>
}
}
}
"#,
r#"
enum A { One, Two }
impl A {
fn foo(self) {
match self {
Self::One => {},
$0Self::Two => {}
}
}
}
"#,
);
}

#[test]
fn fill_match_arms_placeholder() {
check_assist(
Expand Down
7 changes: 7 additions & 0 deletions crates/ra_syntax/src/ast/make.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,13 @@ pub fn path_pat(path: ast::Path) -> ast::Pat {
}
}

pub fn path_pat_from_string(path: String) -> ast::Pat {
return from_text(&path);
fn from_text(text: &str) -> ast::Pat {
ast_from_text(&format!("fn f({}: ())", text))
}
}

pub fn match_arm(pats: impl IntoIterator<Item = ast::Pat>, expr: ast::Expr) -> ast::MatchArm {
let pats_str = pats.into_iter().join(" | ");
return from_text(&format!("{} => {}", pats_str, expr));
Expand Down

0 comments on commit ca3b679

Please sign in to comment.