1 //! FIXME: write short doc here
5 use hir::{Adt, HasSource, Semantics};
6 use ra_ide_db::RootDatabase;
8 use crate::{Assist, AssistCtx, AssistId};
9 use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner};
11 use ast::{MatchArm, Pat};
13 // Assist: fill_match_arms
15 // Adds missing clauses to a `match` expression.
18 // enum Action { Move { distance: u32 }, Stop }
20 // fn handle(action: Action) {
28 // enum Action { Move { distance: u32 }, Stop }
30 // fn handle(action: Action) {
32 // Action::Move { distance } => (),
33 // Action::Stop => (),
37 pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
38 let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?;
39 let match_arm_list = match_expr.match_arm_list()?;
41 let expr = match_expr.expr()?;
42 let enum_def = resolve_enum_def(&ctx.sema, &expr)?;
43 let module = ctx.sema.scope(expr.syntax()).module()?;
45 let variants = enum_def.variants(ctx.db);
46 if variants.is_empty() {
50 let mut arms: Vec<MatchArm> = match_arm_list.arms().collect();
52 if let Some(Pat::PlaceholderPat(..)) = arms[0].pat() {
58 let missing_arms: Vec<MatchArm> = variants
60 .filter_map(|variant| build_pat(db, module, variant))
61 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
62 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit()))
65 if missing_arms.is_empty() {
69 ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| {
70 arms.extend(missing_arms);
72 let indent_level = IndentLevel::from_node(match_arm_list.syntax());
73 let new_arm_list = indent_level.increase_indent(make::match_arm_list(arms));
75 edit.target(match_expr.syntax().text_range());
76 edit.set_cursor(expr.syntax().text_range().start());
77 edit.replace_ast(match_arm_list, new_arm_list);
81 fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
82 existing_arms.iter().filter_map(|arm| arm.pat()).all(|pat| {
83 // Special casee OrPat as separate top-level pats
84 let top_level_pats: Vec<Pat> = match pat {
85 Pat::OrPat(pats) => pats.pats().collect::<Vec<_>>(),
89 !top_level_pats.iter().any(|pat| does_pat_match_variant(pat, var))
93 fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
94 let pat_head = pat.syntax().first_child().map(|node| node.text());
95 let var_head = var.syntax().first_child().map(|node| node.text());
100 fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> {
101 sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
102 Some(Adt::Enum(e)) => Some(e),
107 fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> {
108 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?);
110 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
111 let pat: ast::Pat = match var.source(db).value.kind() {
112 ast::StructKind::Tuple(field_list) => {
114 iter::repeat(make::placeholder_pat().into()).take(field_list.fields().count());
115 make::tuple_struct_pat(path, pats).into()
117 ast::StructKind::Record(field_list) => {
118 let pats = field_list.fields().map(|f| make::bind_pat(f.name().unwrap()).into());
119 make::record_pat(path, pats).into()
121 ast::StructKind::Unit => make::path_pat(path),
129 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
131 use super::fill_match_arms;
134 fn all_match_arms_provided() {
135 check_assist_not_applicable(
140 Bs{x:i32, y:Option<i32>},
141 Cs(i32, Option<i32>),
146 A::Bs{x,y:Some(_)} => (),
147 A::Cs(_, Some(_)) => (),
155 fn partial_fill_record_tuple() {
161 Bs{x:i32, y:Option<i32>},
162 Cs(i32, Option<i32>),
166 A::Bs{x,y:Some(_)} => (),
167 A::Cs(_, Some(_)) => (),
174 Bs{x:i32, y:Option<i32>},
175 Cs(i32, Option<i32>),
179 A::Bs{x,y:Some(_)} => (),
180 A::Cs(_, Some(_)) => (),
189 fn partial_fill_or_pat() {
200 A::Cs(_) | A::Bs => (),
212 A::Cs(_) | A::Bs => (),
238 A::Bs if 0 < 1 => (),
258 A::Bs if 0 < 1 => (),
270 fn fill_match_arms_empty_body() {
279 Es{ x: usize, y: usize }
293 Es{ x: usize, y: usize }
303 A::Es { x, y } => (),
311 fn test_fill_match_arm_refs() {
341 Es{ x: usize, y: usize }
351 Es{ x: usize, y: usize }
356 A::Es { x, y } => (),
364 fn fill_match_arms_target() {
379 fn fill_match_arms_trivial_arm() {
405 fn fill_match_arms_qualifies_path() {
409 mod foo { pub enum E { X, Y } }
419 mod foo { pub enum E { X, Y } }