Skip to main content

ide_assists/handlers/
add_missing_match_arms.rs

1use std::iter;
2
3use either::Either;
4use hir::{Adt, AsAssocItem, Crate, FindPathConfig, HasAttrs, ModuleDef, Semantics};
5use ide_db::RootDatabase;
6use ide_db::syntax_helpers::suggest_name;
7use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast_with_factory};
8use itertools::Itertools;
9use syntax::ast::edit::IndentLevel;
10use syntax::ast::syntax_factory::SyntaxFactory;
11use syntax::ast::{self, AstNode, MatchArmList, MatchExpr, Pat};
12use syntax::syntax_editor::{Position, SyntaxEditor};
13use syntax::{SyntaxKind, SyntaxNode, ToSmolStr};
14
15use crate::{AssistContext, AssistId, Assists, utils};
16
17// Assist: add_missing_match_arms
18//
19// Adds missing clauses to a `match` expression.
20//
21// ```
22// enum Action { Move { distance: u32 }, Stop }
23//
24// fn handle(action: Action) {
25//     match action {
26//         $0
27//     }
28// }
29// ```
30// ->
31// ```
32// enum Action { Move { distance: u32 }, Stop }
33//
34// fn handle(action: Action) {
35//     match action {
36//         Action::Move { distance } => ${1:todo!()},
37//         Action::Stop => ${2:todo!()},$0
38//     }
39// }
40// ```
41pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
42    let match_expr = ctx.find_node_at_offset_with_descend::<ast::MatchExpr>()?;
43    let match_arm_list = match_expr.match_arm_list()?;
44    let arm_list_range = ctx.sema.original_range_opt(match_arm_list.syntax())?;
45
46    if cursor_at_trivial_match_arm_list(ctx, &match_expr, &match_arm_list).is_none() {
47        let cursor_in_range = arm_list_range.range.contains_range(ctx.selection_trimmed());
48        if cursor_in_range {
49            cov_mark::hit!(not_applicable_outside_of_range_right);
50            return None;
51        }
52    }
53
54    let expr = match_expr.expr()?;
55
56    let mut has_catch_all_arm = false;
57
58    let top_lvl_pats: Vec<_> = match_arm_list
59        .arms()
60        .filter_map(|arm| Some((arm.pat()?, arm.guard().is_some())))
61        .flat_map(|(pat, has_guard)| {
62            match pat {
63                // Special case OrPat as separate top-level pats
64                Pat::OrPat(or_pat) => Either::Left(or_pat.pats()),
65                _ => Either::Right(iter::once(pat)),
66            }
67            .map(move |pat| (pat, has_guard))
68        })
69        .filter_map(|(pat, has_guard)| {
70            has_catch_all_arm |= !has_guard && matches!(pat, Pat::WildcardPat(_));
71            (!has_guard).then_some(pat)
72        })
73        // Exclude top level wildcards so that they are expanded by this assist, retains status quo in #8129.
74        .filter(|pat| !matches!(pat, Pat::WildcardPat(_)))
75        .collect();
76
77    let make = SyntaxFactory::without_mappings();
78
79    let scope = ctx.sema.scope(expr.syntax())?;
80    let module = scope.module();
81    let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(scope.krate()));
82    let self_ty = if ctx.config.prefer_self_ty {
83        scope.expression_store_owner().and_then(|def| {
84            match def {
85                hir::ExpressionStoreOwner::Body(def_with_body) => {
86                    def_with_body.as_assoc_item(ctx.db())
87                }
88                hir::ExpressionStoreOwner::Signature(def) => def.as_assoc_item(ctx.db()),
89                hir::ExpressionStoreOwner::VariantFields(_) => None,
90            }?
91            .implementing_ty(ctx.db())
92        })
93    } else {
94        None
95    };
96    let (missing_pats, is_non_exhaustive, has_hidden_variants): (
97        Vec<(ast::Pat, bool)>,
98        bool,
99        bool,
100    ) = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr, self_ty.as_ref()) {
101        let is_non_exhaustive = enum_def.is_non_exhaustive(ctx.db(), module.krate(ctx.db()));
102
103        let variants = enum_def.variants(ctx.db());
104
105        let has_hidden_variants = variants
106            .iter()
107            .any(|variant| variant.should_be_hidden(ctx.db(), module.krate(ctx.db())));
108
109        let missing_pats = variants
110            .into_iter()
111            .filter_map(|variant| {
112                Some((
113                    build_pat(ctx, &make, module, variant, cfg)?,
114                    variant.should_be_hidden(ctx.db(), module.krate(ctx.db())),
115                ))
116            })
117            .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
118
119        let option_enum = FamousDefs(&ctx.sema, module.krate(ctx.db())).core_option_Option();
120        let missing_pats: Vec<_> = if matches!(enum_def, ExtendedEnum::Enum { enum_: e, .. } if Some(e) == option_enum)
121        {
122            // Match `Some` variant first.
123            cov_mark::hit!(option_order);
124            missing_pats.rev().collect()
125        } else {
126            missing_pats.collect()
127        };
128        (missing_pats, is_non_exhaustive, has_hidden_variants)
129    } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr, self_ty.as_ref()) {
130        let is_non_exhaustive = enum_defs
131            .iter()
132            .any(|enum_def| enum_def.is_non_exhaustive(ctx.db(), module.krate(ctx.db())));
133
134        let mut n_arms = 1;
135        let variants_of_enums: Vec<Vec<ExtendedVariant>> = enum_defs
136            .into_iter()
137            .map(|enum_def| enum_def.variants(ctx.db()))
138            .inspect(|variants| n_arms *= variants.len())
139            .collect();
140
141        // When calculating the match arms for a tuple of enums, we want
142        // to create a match arm for each possible combination of enum
143        // values. The `multi_cartesian_product` method transforms
144        // Vec<Vec<EnumVariant>> into Vec<(EnumVariant, .., EnumVariant)>
145        // where each tuple represents a proposed match arm.
146
147        // A number of arms grows very fast on even a small tuple of large enums.
148        // We skip the assist beyond an arbitrary threshold.
149        if n_arms > 256 {
150            return None;
151        }
152
153        let has_hidden_variants = variants_of_enums
154            .iter()
155            .flatten()
156            .any(|variant| variant.should_be_hidden(ctx.db(), module.krate(ctx.db())));
157
158        let missing_pats = variants_of_enums
159            .into_iter()
160            .multi_cartesian_product()
161            .inspect(|_| cov_mark::hit!(add_missing_match_arms_lazy_computation))
162            .map(|variants| {
163                let is_hidden = variants
164                    .iter()
165                    .any(|variant| variant.should_be_hidden(ctx.db(), module.krate(ctx.db())));
166                let patterns = variants
167                    .into_iter()
168                    .filter_map(|variant| build_pat(ctx, &make, module, variant, cfg));
169
170                (ast::Pat::from(make.tuple_pat(patterns)), is_hidden)
171            })
172            .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat))
173            .collect();
174        (missing_pats, is_non_exhaustive, has_hidden_variants)
175    } else if let Some((enum_def, len)) =
176        resolve_array_of_enum_def(&ctx.sema, &expr, self_ty.as_ref())
177    {
178        let is_non_exhaustive = enum_def.is_non_exhaustive(ctx.db(), module.krate(ctx.db()));
179        let variants = enum_def.variants(ctx.db());
180
181        if len.pow(variants.len() as u32) > 256 {
182            return None;
183        }
184
185        let has_hidden_variants = variants
186            .iter()
187            .any(|variant| variant.should_be_hidden(ctx.db(), module.krate(ctx.db())));
188
189        let variants_of_enums = vec![variants; len];
190
191        let missing_pats = variants_of_enums
192            .into_iter()
193            .multi_cartesian_product()
194            .inspect(|_| cov_mark::hit!(add_missing_match_arms_lazy_computation))
195            .map(|variants| {
196                let is_hidden = variants
197                    .iter()
198                    .any(|variant| variant.should_be_hidden(ctx.db(), module.krate(ctx.db())));
199                let patterns = variants
200                    .into_iter()
201                    .filter_map(|variant| build_pat(ctx, &make, module, variant, cfg));
202
203                (ast::Pat::from(make.slice_pat(patterns)), is_hidden)
204            })
205            .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat))
206            .collect();
207        (missing_pats, is_non_exhaustive, has_hidden_variants)
208    } else {
209        return None;
210    };
211
212    let mut needs_catch_all_arm = is_non_exhaustive && !has_catch_all_arm;
213
214    if !needs_catch_all_arm
215        && ((has_hidden_variants && has_catch_all_arm) || missing_pats.is_empty())
216    {
217        return None;
218    }
219
220    let visible_count = missing_pats.iter().filter(|(_, hidden)| !hidden).count();
221    let label = if visible_count == 0 {
222        "Add missing catch-all match arm `_`".to_owned()
223    } else if visible_count == 1 {
224        let pat = &missing_pats.iter().find(|(_, hidden)| !hidden).unwrap().0;
225        format!("Add missing match arm `{pat}`")
226    } else {
227        format!("Add {visible_count} missing match arms")
228    };
229
230    acc.add(
231        AssistId::quick_fix("add_missing_match_arms"),
232        label,
233        ctx.sema.original_range(match_expr.syntax()).range,
234        |builder| {
235            // having any hidden variants means that we need a catch-all arm
236            needs_catch_all_arm |= has_hidden_variants;
237
238            let mut missing_arms = missing_pats
239                .into_iter()
240                .filter(|(_, hidden)| {
241                    // filter out hidden patterns because they're handled by the catch-all arm
242                    !hidden
243                })
244                .map(|(pat, _)| make.match_arm(pat, None, utils::expr_fill_default(ctx.config)))
245                .collect::<Vec<_>>();
246
247            if needs_catch_all_arm && !has_catch_all_arm {
248                cov_mark::hit!(added_wildcard_pattern);
249                let arm = make.match_arm(
250                    make.wildcard_pat().into(),
251                    None,
252                    utils::expr_fill_default(ctx.config),
253                );
254                missing_arms.push(arm);
255            }
256
257            // FIXME: Hack for syntax trees not having great support for macros
258            // Just edit the element that the original range came from
259            let old_place = {
260                // Find the original element
261                let file = ctx.sema.parse(arm_list_range.file_id);
262                let old_place = file.syntax().covering_element(arm_list_range.range);
263
264                match old_place {
265                    syntax::SyntaxElement::Node(it) => it,
266                    syntax::SyntaxElement::Token(it) => {
267                        // If a token is found, it is '{' or '}'
268                        // The parent is `{ ... }`
269                        it.parent().expect("Token must have a parent.")
270                    }
271                }
272            };
273
274            let editor = builder.make_editor(&old_place);
275            let mut arms_edit = ArmsEdit { match_arm_list, place: old_place, last_arm: None };
276
277            arms_edit.remove_wildcard_arms(ctx, &editor);
278            arms_edit.add_comma_after_last_arm(ctx, &make, &editor);
279            arms_edit.append_arms(&missing_arms, &make, &editor);
280
281            if let Some(cap) = ctx.config.snippet_cap {
282                if let Some(it) = missing_arms
283                    .first()
284                    .and_then(|arm| arm.syntax().descendants().find_map(ast::WildcardPat::cast))
285                {
286                    editor.add_annotation(it.syntax(), builder.make_placeholder_snippet(cap));
287                }
288
289                for arm in &missing_arms {
290                    if let Some(expr) = arm.expr() {
291                        editor.add_annotation(expr.syntax(), builder.make_placeholder_snippet(cap));
292                    }
293                }
294
295                if let Some(arm) = missing_arms.last() {
296                    editor.add_annotation(arm.syntax(), builder.make_tabstop_after(cap));
297                }
298            }
299
300            builder.add_file_edits(ctx.vfs_file_id(), editor);
301        },
302    )
303}
304
305fn cursor_at_trivial_match_arm_list(
306    ctx: &AssistContext<'_, '_>,
307    match_expr: &MatchExpr,
308    match_arm_list: &MatchArmList,
309) -> Option<()> {
310    // match x { $0 }
311    if match_arm_list.arms().next().is_none() {
312        cov_mark::hit!(add_missing_match_arms_empty_body);
313        return Some(());
314    }
315
316    // match x {
317    //     bar => baz,
318    //     $0
319    // }
320    if let Some(last_arm) = match_arm_list.arms().last() {
321        let last_node = match last_arm.expr() {
322            Some(expr) => expr.syntax().clone(),
323            None => last_arm.syntax().clone(),
324        };
325        let last_node_range = ctx.sema.original_range_opt(&last_node)?.range;
326        let match_expr_range = ctx.sema.original_range_opt(match_expr.syntax())?.range;
327        if last_node_range.end() <= ctx.offset() && ctx.offset() < match_expr_range.end() {
328            cov_mark::hit!(add_missing_match_arms_end_of_last_arm);
329            return Some(());
330        }
331
332        if ast::Expr::cast(last_node.clone()).is_some_and(is_empty_expr)
333            && last_node_range.contains(ctx.offset())
334            && !last_node.text().contains_char('\n')
335        {
336            cov_mark::hit!(add_missing_match_arms_end_of_last_empty_arm);
337            return Some(());
338        }
339    }
340
341    // match { _$0 => {...} }
342    let wild_pat = ctx.find_node_at_offset_with_descend::<ast::WildcardPat>()?;
343    let arm = wild_pat.syntax().parent().and_then(ast::MatchArm::cast)?;
344    let arm_match_expr = arm.syntax().ancestors().nth(2).and_then(ast::MatchExpr::cast)?;
345    if arm_match_expr == *match_expr {
346        cov_mark::hit!(add_missing_match_arms_trivial_arm);
347        return Some(());
348    }
349
350    None
351}
352
353struct ArmsEdit {
354    match_arm_list: MatchArmList,
355    place: SyntaxNode,
356    last_arm: Option<ast::MatchArm>,
357}
358
359impl ArmsEdit {
360    fn remove_wildcard_arms(&mut self, ctx: &AssistContext<'_, '_>, editor: &SyntaxEditor) {
361        for arm in self.match_arm_list.arms() {
362            if !matches!(arm.pat(), Some(Pat::WildcardPat(_))) {
363                self.last_arm = Some(arm);
364                continue;
365            }
366            if !arm.expr().is_none_or(is_empty_expr) {
367                cov_mark::hit!(add_missing_match_arms_empty_expr);
368                self.last_arm = Some(arm);
369                continue;
370            }
371            let Some(range) = self.cover_edit_range(ctx, &arm) else { continue };
372
373            let prev = match range.start() {
374                syntax::NodeOrToken::Node(node) => {
375                    node.first_token().and_then(|it| it.prev_token())
376                }
377                syntax::NodeOrToken::Token(tok) => tok.prev_token(),
378            };
379            if let Some(prev) = prev
380                && prev.kind() == SyntaxKind::WHITESPACE
381            {
382                editor.delete(prev);
383            }
384
385            editor.delete_all(range);
386        }
387    }
388
389    fn append_arms(&self, arms: &[ast::MatchArm], make: &SyntaxFactory, editor: &SyntaxEditor) {
390        let Some(mut before) = self.place.last_token() else {
391            stdx::never!("match arm list not contain any token");
392            return;
393        };
394        if let Some(prev) = before.prev_token()
395            && prev.kind() == SyntaxKind::WHITESPACE
396        {
397            before = prev;
398        }
399        let open_curly =
400            !self.place.text().contains_char('\n') || before.kind() == SyntaxKind::WHITESPACE;
401        let indent = IndentLevel::from_node(&self.place);
402        let arm_indent = indent + 1;
403        let indent = make.whitespace(&format!("\n{indent}"));
404        let arm_indent = make.whitespace(&format!("\n{arm_indent}"));
405        let elements = arms
406            .iter()
407            .flat_map(|arm| [arm_indent.clone().into(), arm.syntax().clone().into()])
408            .chain(open_curly.then(|| indent.clone().into()))
409            .collect();
410
411        if before.kind() == SyntaxKind::WHITESPACE {
412            editor.replace_with_many(before, elements);
413        } else {
414            editor.insert_all(Position::before(before), elements);
415        }
416    }
417
418    fn add_comma_after_last_arm(
419        &self,
420        ctx: &AssistContext<'_, '_>,
421        make: &SyntaxFactory,
422        editor: &SyntaxEditor,
423    ) {
424        if let Some(last_arm) = &self.last_arm
425            && last_arm.comma_token().is_none()
426            && last_arm.expr().is_none_or(|it| !it.is_block_like())
427            && let Some(range) = self.cover_edit_range(ctx, last_arm)
428        {
429            editor.insert(Position::after(range.end()), make.token(syntax::T![,]));
430        }
431    }
432
433    fn cover_edit_range(
434        &self,
435        ctx: &AssistContext<'_, '_>,
436        node: &impl AstNode,
437    ) -> Option<std::ops::RangeInclusive<syntax::SyntaxElement>> {
438        let range = ctx.sema.original_range_opt(node.syntax())?;
439
440        if !self.place.text_range().contains_range(range.range) {
441            return None;
442        }
443
444        Some(utils::cover_edit_range(&self.place, range.range))
445    }
446}
447
448fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool {
449    !existing_pats.iter().any(|pat| does_pat_match_variant(pat, var))
450}
451
452fn is_empty_expr(e: ast::Expr) -> bool {
453    match e {
454        ast::Expr::BlockExpr(b) => b.statements().next().is_none() && b.tail_expr().is_none(),
455        ast::Expr::TupleExpr(t) => t.fields().next().is_none(),
456        _ => false,
457    }
458}
459
460// Fixme: this is still somewhat limited, use hir_ty::diagnostics::match_check?
461fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
462    match (pat, var) {
463        (Pat::WildcardPat(_), _) => true,
464        (Pat::SlicePat(spat), Pat::SlicePat(svar)) => {
465            spat.pats().zip(svar.pats()).all(|(p, v)| does_pat_match_variant(&p, &v))
466        }
467        (Pat::TuplePat(tpat), Pat::TuplePat(tvar)) => {
468            tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v))
469        }
470        (Pat::OrPat(opat), _) => opat.pats().any(|p| does_pat_match_variant(&p, var)),
471        _ => utils::does_pat_match_variant(pat, var),
472    }
473}
474
475#[derive(Eq, PartialEq, Clone)]
476enum ExtendedEnum {
477    Bool,
478    Enum { enum_: hir::Enum, use_self: bool },
479}
480
481#[derive(Eq, PartialEq, Clone, Copy, Debug)]
482enum ExtendedVariant {
483    True,
484    False,
485    Variant { variant: hir::EnumVariant, use_self: bool },
486}
487
488impl ExtendedVariant {
489    fn should_be_hidden(self, db: &RootDatabase, krate: Crate) -> bool {
490        match self {
491            ExtendedVariant::Variant { variant: var, .. } => {
492                var.attrs(db).is_doc_hidden() && var.module(db).krate(db) != krate
493            }
494            _ => false,
495        }
496    }
497}
498
499impl ExtendedEnum {
500    fn enum_(
501        db: &RootDatabase,
502        enum_: hir::Enum,
503        enum_ty: &hir::Type<'_>,
504        self_ty: Option<&hir::Type<'_>>,
505    ) -> Self {
506        ExtendedEnum::Enum {
507            enum_,
508            use_self: self_ty.is_some_and(|self_ty| self_ty.could_unify_with_deeply(db, enum_ty)),
509        }
510    }
511
512    fn is_non_exhaustive(&self, db: &RootDatabase, krate: Crate) -> bool {
513        match self {
514            ExtendedEnum::Enum { enum_: e, .. } => {
515                e.attrs(db).is_non_exhaustive() && e.module(db).krate(db) != krate
516            }
517            _ => false,
518        }
519    }
520
521    fn variants(&self, db: &RootDatabase) -> Vec<ExtendedVariant> {
522        match *self {
523            ExtendedEnum::Enum { enum_: e, use_self } => e
524                .variants(db)
525                .into_iter()
526                .map(|variant| ExtendedVariant::Variant { variant, use_self })
527                .collect::<Vec<_>>(),
528            ExtendedEnum::Bool => {
529                Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False])
530            }
531        }
532    }
533}
534
535fn resolve_enum_def(
536    sema: &Semantics<'_, RootDatabase>,
537    expr: &ast::Expr,
538    self_ty: Option<&hir::Type<'_>>,
539) -> Option<ExtendedEnum> {
540    sema.type_of_expr(expr)?.adjusted().autoderef(sema.db).find_map(|ty| match ty.as_adt() {
541        Some(Adt::Enum(e)) => Some(ExtendedEnum::enum_(sema.db, e, &ty, self_ty)),
542        _ => ty.is_bool().then_some(ExtendedEnum::Bool),
543    })
544}
545
546fn resolve_tuple_of_enum_def(
547    sema: &Semantics<'_, RootDatabase>,
548    expr: &ast::Expr,
549    self_ty: Option<&hir::Type<'_>>,
550) -> Option<Vec<ExtendedEnum>> {
551    sema.type_of_expr(expr)?
552        .adjusted()
553        .tuple_fields(sema.db)
554        .iter()
555        .map(|ty| {
556            ty.autoderef(sema.db).find_map(|ty| {
557                match ty.as_adt() {
558                    Some(Adt::Enum(e)) => Some(ExtendedEnum::enum_(sema.db, e, &ty, self_ty)),
559                    // For now we only handle expansion for a tuple of enums. Here
560                    // we map non-enum items to None and rely on `collect` to
561                    // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
562                    _ => ty.is_bool().then_some(ExtendedEnum::Bool),
563                }
564            })
565        })
566        .collect::<Option<Vec<ExtendedEnum>>>()
567        .and_then(|list| if list.is_empty() { None } else { Some(list) })
568}
569
570fn resolve_array_of_enum_def(
571    sema: &Semantics<'_, RootDatabase>,
572    expr: &ast::Expr,
573    self_ty: Option<&hir::Type<'_>>,
574) -> Option<(ExtendedEnum, usize)> {
575    sema.type_of_expr(expr)?.adjusted().as_array(sema.db).and_then(|(ty, len)| {
576        ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
577            Some(Adt::Enum(e)) => Some((ExtendedEnum::enum_(sema.db, e, &ty, self_ty), len)),
578            _ => ty.is_bool().then_some((ExtendedEnum::Bool, len)),
579        })
580    })
581}
582
583fn build_pat(
584    ctx: &AssistContext<'_, '_>,
585    make: &SyntaxFactory,
586    module: hir::Module,
587    var: ExtendedVariant,
588    cfg: FindPathConfig,
589) -> Option<ast::Pat> {
590    let db = ctx.db();
591    match var {
592        ExtendedVariant::Variant { variant: var, use_self } => {
593            let edition = module.krate(db).edition(db);
594            let path = if use_self {
595                make.path_from_segments(
596                    [
597                        make.path_segment(make.name_ref_self_ty()),
598                        make.path_segment(
599                            make.name_ref(&var.name(db).display(db, edition).to_smolstr()),
600                        ),
601                    ],
602                    false,
603                )
604            } else {
605                mod_path_to_ast_with_factory(
606                    make,
607                    &module.find_path(db, ModuleDef::from(var), cfg)?,
608                    edition,
609                )
610            };
611            let fields = var.fields(db);
612            let pat: ast::Pat = match var.kind(db) {
613                hir::StructKind::Tuple => {
614                    let mut name_generator = suggest_name::NameGenerator::default();
615                    let pats = fields.into_iter().map(|f| {
616                        let name = name_generator.for_type(&f.ty(db).to_type(db), db, edition);
617                        match name {
618                            Some(name) => make.ident_pat(false, false, make.name(&name)).into(),
619                            None => make.wildcard_pat().into(),
620                        }
621                    });
622                    make.tuple_struct_pat(path, pats).into()
623                }
624                hir::StructKind::Record => {
625                    let fields = fields
626                        .into_iter()
627                        .map(|f| make.ident_pat(false, false, make.name(f.name(db).as_str())))
628                        .map(|ident| make.record_pat_field_shorthand(ident.into()));
629                    let fields = make.record_pat_field_list(fields, None);
630                    make.record_pat_with_fields(path, fields).into()
631                }
632                hir::StructKind::Unit => make.path_pat(path),
633            };
634            Some(pat)
635        }
636        ExtendedVariant::True => Some(ast::Pat::from(make.literal_pat("true"))),
637        ExtendedVariant::False => Some(ast::Pat::from(make.literal_pat("false"))),
638    }
639}
640
641#[cfg(test)]
642mod tests {
643    use crate::AssistConfig;
644    use crate::tests::{
645        TEST_CONFIG, check_assist, check_assist_not_applicable, check_assist_target,
646        check_assist_unresolved, check_assist_with_config, check_assist_with_label,
647    };
648
649    use super::add_missing_match_arms;
650
651    #[test]
652    fn all_match_arms_provided() {
653        check_assist_not_applicable(
654            add_missing_match_arms,
655            r#"
656enum A {
657    As,
658    Bs{x:i32, y:Option<i32>},
659    Cs(i32, Option<i32>),
660}
661fn main() {
662    match A::As$0 {
663        A::As,
664        A::Bs{x,y:Some(_)} => {}
665        A::Cs(_, Some(_)) => {}
666    }
667}
668            "#,
669        );
670    }
671
672    #[test]
673    fn not_applicable_outside_of_range_left() {
674        check_assist_not_applicable(
675            add_missing_match_arms,
676            r#"
677enum A { X, Y }
678
679fn foo(a: A) {
680    $0 match a {
681        A::X => { }
682    }
683}
684        "#,
685        );
686    }
687
688    #[test]
689    fn not_applicable_outside_of_range_right() {
690        cov_mark::check!(not_applicable_outside_of_range_right);
691        check_assist_not_applicable(
692            add_missing_match_arms,
693            r#"
694enum A { X, Y }
695
696fn foo(a: A) {
697    match a {$0
698        A::X => { }
699    }
700}
701        "#,
702        );
703    }
704
705    #[test]
706    fn all_boolean_match_arms_provided() {
707        check_assist_not_applicable(
708            add_missing_match_arms,
709            r#"
710fn foo(a: bool) {
711    match a$0 {
712        true => {}
713        false => {}
714    }
715}
716"#,
717        )
718    }
719
720    #[test]
721    fn tuple_of_non_enum() {
722        // for now this case is not handled, although it potentially could be
723        // in the future
724        check_assist_not_applicable(
725            add_missing_match_arms,
726            r#"
727fn main() {
728    match (0, false)$0 {
729    }
730}
731"#,
732        );
733    }
734
735    #[test]
736    fn add_missing_match_arms_boolean() {
737        check_assist(
738            add_missing_match_arms,
739            r#"
740fn foo(a: bool) {
741    match a$0 {
742    }
743}
744"#,
745            r#"
746fn foo(a: bool) {
747    match a {
748        true => ${1:todo!()},
749        false => ${2:todo!()},$0
750    }
751}
752"#,
753        )
754    }
755
756    #[test]
757    fn partial_fill_boolean() {
758        check_assist(
759            add_missing_match_arms,
760            r#"
761fn foo(a: bool) {
762    match a$0 {
763        true => {}
764    }
765}
766"#,
767            r#"
768fn foo(a: bool) {
769    match a {
770        true => {}
771        false => ${1:todo!()},$0
772    }
773}
774"#,
775        )
776    }
777
778    #[test]
779    fn all_boolean_tuple_arms_provided() {
780        check_assist_not_applicable(
781            add_missing_match_arms,
782            r#"
783fn foo(a: bool) {
784    match (a, a)$0 {
785        (true | false, true) => {}
786        (true, false) => {}
787        (false, false) => {}
788    }
789}
790"#,
791        );
792
793        check_assist_not_applicable(
794            add_missing_match_arms,
795            r#"
796fn foo(a: bool) {
797    match (a, a)$0 {
798        (true, true) => {}
799        (true, false) => {}
800        (false, true) => {}
801        (false, false) => {}
802    }
803}
804"#,
805        )
806    }
807
808    #[test]
809    fn fill_boolean_tuple() {
810        check_assist(
811            add_missing_match_arms,
812            r#"
813fn foo(a: bool) {
814    match (a, a)$0 {
815    }
816}
817"#,
818            r#"
819fn foo(a: bool) {
820    match (a, a) {
821        (true, true) => ${1:todo!()},
822        (true, false) => ${2:todo!()},
823        (false, true) => ${3:todo!()},
824        (false, false) => ${4:todo!()},$0
825    }
826}
827"#,
828        )
829    }
830
831    #[test]
832    fn fill_boolean_array() {
833        check_assist(
834            add_missing_match_arms,
835            r#"
836fn foo(a: bool) {
837    match [a]$0 {
838    }
839}
840"#,
841            r#"
842fn foo(a: bool) {
843    match [a] {
844        [true] => ${1:todo!()},
845        [false] => ${2:todo!()},$0
846    }
847}
848"#,
849        );
850
851        check_assist(
852            add_missing_match_arms,
853            r#"
854fn foo(a: bool) {
855    match [a,]$0 {
856    }
857}
858"#,
859            r#"
860fn foo(a: bool) {
861    match [a,] {
862        [true] => ${1:todo!()},
863        [false] => ${2:todo!()},$0
864    }
865}
866"#,
867        );
868
869        check_assist(
870            add_missing_match_arms,
871            r#"
872fn foo(a: bool) {
873    match [a, a]$0 {
874        [true, true] => todo!(),
875    }
876}
877"#,
878            r#"
879fn foo(a: bool) {
880    match [a, a] {
881        [true, true] => todo!(),
882        [true, false] => ${1:todo!()},
883        [false, true] => ${2:todo!()},
884        [false, false] => ${3:todo!()},$0
885    }
886}
887"#,
888        );
889
890        check_assist(
891            add_missing_match_arms,
892            r#"
893fn foo(a: bool) {
894    match [a, a]$0 {
895    }
896}
897"#,
898            r#"
899fn foo(a: bool) {
900    match [a, a] {
901        [true, true] => ${1:todo!()},
902        [true, false] => ${2:todo!()},
903        [false, true] => ${3:todo!()},
904        [false, false] => ${4:todo!()},$0
905    }
906}
907"#,
908        )
909    }
910
911    #[test]
912    fn partial_fill_boolean_tuple() {
913        check_assist(
914            add_missing_match_arms,
915            r#"
916fn foo(a: bool) {
917    match (a, a)$0 {
918        (true | false, true) => {}
919    }
920}
921"#,
922            r#"
923fn foo(a: bool) {
924    match (a, a) {
925        (true | false, true) => {}
926        (true, false) => ${1:todo!()},
927        (false, false) => ${2:todo!()},$0
928    }
929}
930"#,
931        );
932
933        check_assist(
934            add_missing_match_arms,
935            r#"
936fn foo(a: bool) {
937    match (a, a)$0 {
938        (false, true) => {}
939    }
940}
941"#,
942            r#"
943fn foo(a: bool) {
944    match (a, a) {
945        (false, true) => {}
946        (true, true) => ${1:todo!()},
947        (true, false) => ${2:todo!()},
948        (false, false) => ${3:todo!()},$0
949    }
950}
951"#,
952        )
953    }
954
955    #[test]
956    fn partial_fill_record_tuple() {
957        check_assist(
958            add_missing_match_arms,
959            r#"
960enum A {
961    As,
962    Bs { x: i32, y: Option<i32> },
963    Cs(i32, Option<i32>),
964}
965fn main() {
966    match A::As$0 {
967        A::Bs { x, y: Some(_) } => {}
968        A::Cs(_, Some(_)) => {}
969    }
970}
971"#,
972            r#"
973enum A {
974    As,
975    Bs { x: i32, y: Option<i32> },
976    Cs(i32, Option<i32>),
977}
978fn main() {
979    match A::As {
980        A::Bs { x, y: Some(_) } => {}
981        A::Cs(_, Some(_)) => {}
982        A::As => ${1:todo!()},$0
983    }
984}
985"#,
986        );
987    }
988
989    #[test]
990    fn partial_fill_option() {
991        check_assist(
992            add_missing_match_arms,
993            r#"
994//- minicore: option
995fn main() {
996    match None$0 {
997        None => {}
998    }
999}
1000"#,
1001            r#"
1002fn main() {
1003    match None {
1004        None => {}
1005        Some(${1:_}) => ${2:todo!()},$0
1006    }
1007}
1008"#,
1009        );
1010    }
1011
1012    #[test]
1013    fn partial_fill_option_with_indentation() {
1014        check_assist(
1015            add_missing_match_arms,
1016            r#"
1017//- minicore: option
1018fn main() {
1019    match None$0 {
1020        None => {
1021            foo(
1022                "foo",
1023                "bar",
1024            );
1025        }
1026    }
1027}
1028"#,
1029            r#"
1030fn main() {
1031    match None {
1032        None => {
1033            foo(
1034                "foo",
1035                "bar",
1036            );
1037        }
1038        Some(${1:_}) => ${2:todo!()},$0
1039    }
1040}
1041"#,
1042        );
1043    }
1044
1045    #[test]
1046    fn partial_fill_or_pat() {
1047        check_assist(
1048            add_missing_match_arms,
1049            r#"
1050enum A { As, Bs, Cs(Option<i32>) }
1051fn main() {
1052    match A::As$0 {
1053        A::Cs(_) | A::Bs => {}
1054    }
1055}
1056"#,
1057            r#"
1058enum A { As, Bs, Cs(Option<i32>) }
1059fn main() {
1060    match A::As {
1061        A::Cs(_) | A::Bs => {}
1062        A::As => ${1:todo!()},$0
1063    }
1064}
1065"#,
1066        );
1067    }
1068
1069    #[test]
1070    fn partial_fill() {
1071        check_assist(
1072            add_missing_match_arms,
1073            r#"
1074enum A { As, Bs, Cs, Ds(String), Es(B) }
1075enum B { Xs, Ys }
1076fn main() {
1077    match A::As$0 {
1078        A::Bs if 0 < 1 => {}
1079        A::Ds(_value) => { let x = 1; }
1080        A::Es(B::Xs) => (),
1081    }
1082}
1083"#,
1084            r#"
1085enum A { As, Bs, Cs, Ds(String), Es(B) }
1086enum B { Xs, Ys }
1087fn main() {
1088    match A::As {
1089        A::Bs if 0 < 1 => {}
1090        A::Ds(_value) => { let x = 1; }
1091        A::Es(B::Xs) => (),
1092        A::As => ${1:todo!()},
1093        A::Bs => ${2:todo!()},
1094        A::Cs => ${3:todo!()},$0
1095    }
1096}
1097"#,
1098        );
1099    }
1100
1101    #[test]
1102    fn partial_fill_bind_pat() {
1103        check_assist(
1104            add_missing_match_arms,
1105            r#"
1106enum A { As, Bs, Cs(Option<i32>) }
1107fn main() {
1108    match A::As$0 {
1109        A::As(_) => {}
1110        a @ A::Bs(_) => {}
1111    }
1112}
1113"#,
1114            r#"
1115enum A { As, Bs, Cs(Option<i32>) }
1116fn main() {
1117    match A::As {
1118        A::As(_) => {}
1119        a @ A::Bs(_) => {}
1120        A::Cs(${1:_}) => ${2:todo!()},$0
1121    }
1122}
1123"#,
1124        );
1125    }
1126
1127    #[test]
1128    fn add_missing_match_arms_empty_body() {
1129        cov_mark::check!(add_missing_match_arms_empty_body);
1130        check_assist(
1131            add_missing_match_arms,
1132            r#"
1133enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
1134
1135fn main() {
1136    let a = A::As;
1137    match a {$0}
1138}
1139"#,
1140            r#"
1141enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
1142
1143fn main() {
1144    let a = A::As;
1145    match a {
1146        A::As => ${1:todo!()},
1147        A::Bs => ${2:todo!()},
1148        A::Cs(_) => ${3:todo!()},
1149        A::Ds(_, _) => ${4:todo!()},
1150        A::Es { x, y } => ${5:todo!()},$0
1151    }
1152}
1153"#,
1154        );
1155    }
1156
1157    #[test]
1158    fn add_missing_match_arms_end_of_last_arm() {
1159        cov_mark::check_count!(add_missing_match_arms_end_of_last_arm, 2);
1160        check_assist(
1161            add_missing_match_arms,
1162            r#"
1163enum A { One, Two }
1164enum B { One, Two }
1165
1166fn main() {
1167    let a = A::One;
1168    let b = B::One;
1169    match (a, b) {
1170        (A::Two, B::One) => {},$0
1171    }
1172}
1173"#,
1174            r#"
1175enum A { One, Two }
1176enum B { One, Two }
1177
1178fn main() {
1179    let a = A::One;
1180    let b = B::One;
1181    match (a, b) {
1182        (A::Two, B::One) => {},
1183        (A::One, B::One) => ${1:todo!()},
1184        (A::One, B::Two) => ${2:todo!()},
1185        (A::Two, B::Two) => ${3:todo!()},$0
1186    }
1187}
1188"#,
1189        );
1190
1191        check_assist(
1192            add_missing_match_arms,
1193            r#"
1194enum A { One, Two }
1195enum B { One, Two }
1196
1197fn main() {
1198    let a = A::One;
1199    let b = B::One;
1200    match (a, b) {
1201        (A::Two, B::One) => 2$0,
1202    }
1203}
1204"#,
1205            r#"
1206enum A { One, Two }
1207enum B { One, Two }
1208
1209fn main() {
1210    let a = A::One;
1211    let b = B::One;
1212    match (a, b) {
1213        (A::Two, B::One) => 2,
1214        (A::One, B::One) => ${1:todo!()},
1215        (A::One, B::Two) => ${2:todo!()},
1216        (A::Two, B::Two) => ${3:todo!()},$0
1217    }
1218}
1219"#,
1220        );
1221    }
1222
1223    #[test]
1224    fn add_missing_match_arms_end_of_last_empty_arm() {
1225        cov_mark::check_count!(add_missing_match_arms_end_of_last_empty_arm, 2);
1226        check_assist(
1227            add_missing_match_arms,
1228            r#"
1229enum A { One, Two }
1230enum B { One, Two }
1231
1232fn main() {
1233    let a = A::One;
1234    let b = B::One;
1235    match (a, b) {
1236        (A::Two, B::One) => {$0}
1237    }
1238}
1239"#,
1240            r#"
1241enum A { One, Two }
1242enum B { One, Two }
1243
1244fn main() {
1245    let a = A::One;
1246    let b = B::One;
1247    match (a, b) {
1248        (A::Two, B::One) => {}
1249        (A::One, B::One) => ${1:todo!()},
1250        (A::One, B::Two) => ${2:todo!()},
1251        (A::Two, B::Two) => ${3:todo!()},$0
1252    }
1253}
1254"#,
1255        );
1256
1257        check_assist(
1258            add_missing_match_arms,
1259            r#"
1260enum A { One, Two }
1261enum B { One, Two }
1262
1263fn main() {
1264    let a = A::One;
1265    let b = B::One;
1266    match (a, b) {
1267        (A::Two, B::One) => ($0)
1268    }
1269}
1270"#,
1271            r#"
1272enum A { One, Two }
1273enum B { One, Two }
1274
1275fn main() {
1276    let a = A::One;
1277    let b = B::One;
1278    match (a, b) {
1279        (A::Two, B::One) => (),
1280        (A::One, B::One) => ${1:todo!()},
1281        (A::One, B::Two) => ${2:todo!()},
1282        (A::Two, B::Two) => ${3:todo!()},$0
1283    }
1284}
1285"#,
1286        );
1287    }
1288
1289    #[test]
1290    fn add_missing_match_arms_tuple_of_enum() {
1291        check_assist(
1292            add_missing_match_arms,
1293            r#"
1294enum A { One, Two }
1295enum B { One, Two }
1296
1297fn main() {
1298    let a = A::One;
1299    let b = B::One;
1300    match (a$0, b) {}
1301}
1302"#,
1303            r#"
1304enum A { One, Two }
1305enum B { One, Two }
1306
1307fn main() {
1308    let a = A::One;
1309    let b = B::One;
1310    match (a, b) {
1311        (A::One, B::One) => ${1:todo!()},
1312        (A::One, B::Two) => ${2:todo!()},
1313        (A::Two, B::One) => ${3:todo!()},
1314        (A::Two, B::Two) => ${4:todo!()},$0
1315    }
1316}
1317"#,
1318        );
1319    }
1320
1321    #[test]
1322    fn add_missing_match_arms_tuple_of_enum_ref() {
1323        check_assist(
1324            add_missing_match_arms,
1325            r#"
1326enum A { One, Two }
1327enum B { One, Two }
1328
1329fn main() {
1330    let a = A::One;
1331    let b = B::One;
1332    match (&a$0, &b) {}
1333}
1334"#,
1335            r#"
1336enum A { One, Two }
1337enum B { One, Two }
1338
1339fn main() {
1340    let a = A::One;
1341    let b = B::One;
1342    match (&a, &b) {
1343        (A::One, B::One) => ${1:todo!()},
1344        (A::One, B::Two) => ${2:todo!()},
1345        (A::Two, B::One) => ${3:todo!()},
1346        (A::Two, B::Two) => ${4:todo!()},$0
1347    }
1348}
1349"#,
1350        );
1351    }
1352
1353    #[test]
1354    fn add_missing_match_arms_tuple_of_enum_partial() {
1355        check_assist(
1356            add_missing_match_arms,
1357            r#"
1358enum A { One, Two }
1359enum B { One, Two }
1360
1361fn main() {
1362    let a = A::One;
1363    let b = B::One;
1364    match (a$0, b) {
1365        (A::Two, B::One) => {}
1366    }
1367}
1368"#,
1369            r#"
1370enum A { One, Two }
1371enum B { One, Two }
1372
1373fn main() {
1374    let a = A::One;
1375    let b = B::One;
1376    match (a, b) {
1377        (A::Two, B::One) => {}
1378        (A::One, B::One) => ${1:todo!()},
1379        (A::One, B::Two) => ${2:todo!()},
1380        (A::Two, B::Two) => ${3:todo!()},$0
1381    }
1382}
1383"#,
1384        );
1385
1386        check_assist(
1387            add_missing_match_arms,
1388            r#"
1389enum E { A, B, C }
1390fn main() {
1391    use E::*;
1392    match (A, B, C)$0 {
1393        (A | B , A, A | B | C) => (),
1394        (A | B | C , B | C, A | B | C) => (),
1395    }
1396}
1397"#,
1398            r#"
1399enum E { A, B, C }
1400fn main() {
1401    use E::*;
1402    match (A, B, C) {
1403        (A | B , A, A | B | C) => (),
1404        (A | B | C , B | C, A | B | C) => (),
1405        (C, A, A) => ${1:todo!()},
1406        (C, A, B) => ${2:todo!()},
1407        (C, A, C) => ${3:todo!()},$0
1408    }
1409}
1410"#,
1411        )
1412    }
1413
1414    #[test]
1415    fn add_missing_match_arms_tuple_of_enum_partial_with_wildcards() {
1416        check_assist(
1417            add_missing_match_arms,
1418            r#"
1419//- minicore: option
1420fn main() {
1421    let a = Some(1);
1422    let b = Some(());
1423    match (a$0, b) {
1424        (Some(_), _) => {}
1425        (None, Some(_)) => {}
1426    }
1427}
1428"#,
1429            r#"
1430fn main() {
1431    let a = Some(1);
1432    let b = Some(());
1433    match (a, b) {
1434        (Some(_), _) => {}
1435        (None, Some(_)) => {}
1436        (None, None) => ${1:todo!()},$0
1437    }
1438}
1439"#,
1440        );
1441    }
1442
1443    #[test]
1444    fn add_missing_match_arms_partial_with_deep_pattern() {
1445        // Fixme: cannot handle deep patterns
1446        check_assist_not_applicable(
1447            add_missing_match_arms,
1448            r#"
1449//- minicore: option
1450fn main() {
1451    match $0Some(true) {
1452        Some(true) => {}
1453        None => {}
1454    }
1455}
1456"#,
1457        );
1458    }
1459
1460    #[test]
1461    fn add_missing_match_arms_tuple_of_enum_not_applicable() {
1462        check_assist_not_applicable(
1463            add_missing_match_arms,
1464            r#"
1465enum A { One, Two }
1466enum B { One, Two }
1467
1468fn main() {
1469    let a = A::One;
1470    let b = B::One;
1471    match (a$0, b) {
1472        (A::Two, B::One) => {}
1473        (A::One, B::One) => {}
1474        (A::One, B::Two) => {}
1475        (A::Two, B::Two) => {}
1476    }
1477}
1478"#,
1479        );
1480    }
1481
1482    #[test]
1483    fn add_missing_match_arms_single_element_tuple_of_enum() {
1484        check_assist(
1485            add_missing_match_arms,
1486            r#"
1487enum A { One, Two }
1488
1489fn main() {
1490    let a = A::One;
1491    match (a$0, ) {
1492    }
1493}
1494"#,
1495            r#"
1496enum A { One, Two }
1497
1498fn main() {
1499    let a = A::One;
1500    match (a, ) {
1501        (A::One,) => ${1:todo!()},
1502        (A::Two,) => ${2:todo!()},$0
1503    }
1504}
1505"#,
1506        );
1507    }
1508
1509    #[test]
1510    fn test_fill_match_arm_refs() {
1511        check_assist(
1512            add_missing_match_arms,
1513            r#"
1514enum A { As }
1515
1516fn foo(a: &A) {
1517    match a$0 {
1518    }
1519}
1520"#,
1521            r#"
1522enum A { As }
1523
1524fn foo(a: &A) {
1525    match a {
1526        A::As => ${1:todo!()},$0
1527    }
1528}
1529"#,
1530        );
1531
1532        check_assist(
1533            add_missing_match_arms,
1534            r#"
1535enum A {
1536    Es { x: usize, y: usize }
1537}
1538
1539fn foo(a: &mut A) {
1540    match a$0 {
1541    }
1542}
1543"#,
1544            r#"
1545enum A {
1546    Es { x: usize, y: usize }
1547}
1548
1549fn foo(a: &mut A) {
1550    match a {
1551        A::Es { x, y } => ${1:todo!()},$0
1552    }
1553}
1554"#,
1555        );
1556    }
1557
1558    #[test]
1559    fn add_missing_match_arms_target_simple() {
1560        check_assist_target(
1561            add_missing_match_arms,
1562            r#"
1563enum E { X, Y }
1564
1565fn main() {
1566    match E::X$0 {}
1567}
1568"#,
1569            "match E::X {}",
1570        );
1571    }
1572
1573    #[test]
1574    fn add_missing_match_arms_target_complex() {
1575        check_assist_target(
1576            add_missing_match_arms,
1577            r#"
1578enum E { X, Y }
1579
1580fn main() {
1581    match E::X$0 {
1582        E::X => {}
1583    }
1584}
1585"#,
1586            "match E::X {
1587        E::X => {}
1588    }",
1589        );
1590    }
1591
1592    #[test]
1593    fn add_missing_match_arms_trivial_arm() {
1594        cov_mark::check!(add_missing_match_arms_trivial_arm);
1595        check_assist(
1596            add_missing_match_arms,
1597            r#"
1598enum E { X, Y }
1599
1600fn main() {
1601    match E::X {
1602        $0_ => {}
1603    }
1604}
1605"#,
1606            r#"
1607enum E { X, Y }
1608
1609fn main() {
1610    match E::X {
1611        E::X => ${1:todo!()},
1612        E::Y => ${2:todo!()},$0
1613    }
1614}
1615"#,
1616        );
1617    }
1618
1619    #[test]
1620    fn wildcard_inside_expression_not_applicable() {
1621        check_assist_not_applicable(
1622            add_missing_match_arms,
1623            r#"
1624enum E { X, Y }
1625
1626fn foo(e : E) {
1627    match e {
1628        _ => {
1629            println!("1");$0
1630            println!("2");
1631        }
1632    }
1633}
1634"#,
1635        );
1636    }
1637
1638    #[test]
1639    fn add_missing_match_arms_qualifies_path() {
1640        check_assist(
1641            add_missing_match_arms,
1642            r#"
1643mod foo { pub enum E { X, Y } }
1644use foo::E::X;
1645
1646fn main() {
1647    match X {
1648        $0
1649    }
1650}
1651"#,
1652            r#"
1653mod foo { pub enum E { X, Y } }
1654use foo::E::X;
1655
1656fn main() {
1657    match X {
1658        X => ${1:todo!()},
1659        foo::E::Y => ${2:todo!()},$0
1660    }
1661}
1662"#,
1663        );
1664    }
1665
1666    // FIXME: Preserving comments is quite hard in the current transitional syntax editing model.
1667    // Once we migrate to new trivia model addressed in #6854, remove the ignore attribute.
1668    #[ignore]
1669    #[test]
1670    fn add_missing_match_arms_preserves_comments() {
1671        check_assist(
1672            add_missing_match_arms,
1673            r#"
1674enum A { One, Two }
1675fn foo(a: A) {
1676    match a $0 {
1677        // foo bar baz
1678        A::One => {}
1679        // This is where the rest should be
1680    }
1681}
1682"#,
1683            r#"
1684enum A { One, Two }
1685fn foo(a: A) {
1686    match a  {
1687        // foo bar baz
1688        A::One => {}
1689        A::Two => ${1:todo!()},$0
1690        // This is where the rest should be
1691    }
1692}
1693"#,
1694        );
1695    }
1696
1697    // FIXME: Preserving comments is quite hard in the current transitional syntax editing model.
1698    // Once we migrate to new trivia model addressed in #6854, remove the ignore attribute.
1699    #[ignore]
1700    #[test]
1701    fn add_missing_match_arms_preserves_comments_empty() {
1702        check_assist(
1703            add_missing_match_arms,
1704            r#"
1705enum A { One, Two }
1706fn foo(a: A) {
1707    match a {
1708        // foo bar baz$0
1709    }
1710}
1711"#,
1712            r#"
1713enum A { One, Two }
1714fn foo(a: A) {
1715    match a {
1716        A::One => ${1:todo!()},
1717        A::Two => ${2:todo!()},$0
1718        // foo bar baz
1719    }
1720}
1721"#,
1722        );
1723    }
1724
1725    #[test]
1726    fn add_missing_match_arms_placeholder() {
1727        check_assist(
1728            add_missing_match_arms,
1729            r#"
1730enum A { One, Two, }
1731fn foo(a: A) {
1732    match a$0 {
1733        _ => (),
1734    }
1735}
1736"#,
1737            r#"
1738enum A { One, Two, }
1739fn foo(a: A) {
1740    match a {
1741        A::One => ${1:todo!()},
1742        A::Two => ${2:todo!()},$0
1743    }
1744}
1745"#,
1746        );
1747    }
1748
1749    #[test]
1750    fn option_order() {
1751        cov_mark::check!(option_order);
1752        check_assist(
1753            add_missing_match_arms,
1754            r#"
1755//- minicore: option
1756fn foo(opt: Option<i32>) {
1757    match opt$0 {
1758    }
1759}
1760"#,
1761            r#"
1762fn foo(opt: Option<i32>) {
1763    match opt {
1764        Some(${1:_}) => ${2:todo!()},
1765        None => ${3:todo!()},$0
1766    }
1767}
1768"#,
1769        );
1770    }
1771
1772    #[test]
1773    fn works_inside_macro_call() {
1774        check_assist(
1775            add_missing_match_arms,
1776            r#"
1777macro_rules! m { ($expr:expr) => {$expr}}
1778enum Test {
1779    A,
1780    B,
1781    C,
1782}
1783
1784fn foo(t: Test) {
1785    m!(match t$0 {});
1786}"#,
1787            r#"
1788macro_rules! m { ($expr:expr) => {$expr}}
1789enum Test {
1790    A,
1791    B,
1792    C,
1793}
1794
1795fn foo(t: Test) {
1796    m!(match t {
1797        Test::A => ${1:todo!()},
1798        Test::B => ${2:todo!()},
1799        Test::C => ${3:todo!()},$0
1800    });
1801}"#,
1802        );
1803
1804        check_assist(
1805            add_missing_match_arms,
1806            r#"
1807macro_rules! m { ($expr:expr) => {$expr}}
1808enum Test {
1809    A,
1810    B,
1811    C,
1812}
1813
1814fn foo(t: Test) {
1815    m!(match t {
1816        Test::A => (),
1817    $0});
1818}"#,
1819            r#"
1820macro_rules! m { ($expr:expr) => {$expr}}
1821enum Test {
1822    A,
1823    B,
1824    C,
1825}
1826
1827fn foo(t: Test) {
1828    m!(match t {
1829        Test::A => (),
1830        Test::B => ${1:todo!()},
1831        Test::C => ${2:todo!()},$0
1832    });
1833}"#,
1834        );
1835    }
1836
1837    #[test]
1838    fn lazy_computation() {
1839        // We now collect all missing arms eagerly, so we can show the count
1840        // of missing arms.
1841        cov_mark::check_count!(add_missing_match_arms_lazy_computation, 4);
1842
1843        check_assist_unresolved(
1844            add_missing_match_arms,
1845            r#"
1846enum A { One, Two, }
1847fn foo(tuple: (A, A)) {
1848    match $0tuple {};
1849}
1850"#,
1851        );
1852    }
1853
1854    #[test]
1855    fn label_single_missing_arm() {
1856        check_assist_with_label(
1857            add_missing_match_arms,
1858            r#"
1859enum A { One, Two }
1860fn foo(a: A) {
1861    match $0a {
1862        A::One => {}
1863    }
1864}
1865"#,
1866            "Add missing match arm `A::Two`",
1867        );
1868    }
1869
1870    #[test]
1871    fn label_multiple_missing_arms() {
1872        check_assist_with_label(
1873            add_missing_match_arms,
1874            r#"
1875enum A { One, Two, Three }
1876fn foo(a: A) {
1877    match $0a {}
1878}
1879"#,
1880            "Add 3 missing match arms",
1881        );
1882    }
1883
1884    #[test]
1885    fn label_catch_all_only() {
1886        check_assist_with_label(
1887            add_missing_match_arms,
1888            r#"
1889//- /main.rs crate:main deps:e
1890fn foo(t: ::e::E) {
1891    match $0t {
1892        e::E::A => {}
1893    }
1894}
1895//- /e.rs crate:e
1896pub enum E { A, #[doc(hidden)] B, }
1897"#,
1898            "Add missing catch-all match arm `_`",
1899        );
1900    }
1901
1902    #[test]
1903    fn adds_comma_before_new_arms() {
1904        check_assist(
1905            add_missing_match_arms,
1906            r#"
1907fn foo(t: bool) {
1908    match $0t {
1909        true => 1 + 2
1910    }
1911}"#,
1912            r#"
1913fn foo(t: bool) {
1914    match t {
1915        true => 1 + 2,
1916        false => ${1:todo!()},$0
1917    }
1918}"#,
1919        );
1920    }
1921
1922    #[test]
1923    fn does_not_add_extra_comma() {
1924        check_assist(
1925            add_missing_match_arms,
1926            r#"
1927fn foo(t: bool) {
1928    match $0t {
1929        true => 1 + 2,
1930    }
1931}"#,
1932            r#"
1933fn foo(t: bool) {
1934    match t {
1935        true => 1 + 2,
1936        false => ${1:todo!()},$0
1937    }
1938}"#,
1939        );
1940    }
1941
1942    #[test]
1943    fn does_not_remove_catch_all_with_non_empty_expr() {
1944        cov_mark::check!(add_missing_match_arms_empty_expr);
1945        check_assist(
1946            add_missing_match_arms,
1947            r#"
1948fn foo(t: bool) {
1949    match $0t {
1950        _ => 1 + 2,
1951    }
1952}"#,
1953            r#"
1954fn foo(t: bool) {
1955    match t {
1956        _ => 1 + 2,
1957        true => ${1:todo!()},
1958        false => ${2:todo!()},$0
1959    }
1960}"#,
1961        );
1962    }
1963
1964    #[test]
1965    fn does_not_fill_hidden_variants() {
1966        cov_mark::check!(added_wildcard_pattern);
1967        check_assist(
1968            add_missing_match_arms,
1969            r#"
1970//- /main.rs crate:main deps:e
1971fn foo(t: ::e::E) {
1972    match $0t {
1973    }
1974}
1975//- /e.rs crate:e
1976pub enum E { A, #[doc(hidden)] B, }
1977"#,
1978            r#"
1979fn foo(t: ::e::E) {
1980    match t {
1981        e::E::A => ${1:todo!()},
1982        _ => ${2:todo!()},$0
1983    }
1984}
1985"#,
1986        );
1987    }
1988
1989    #[test]
1990    fn does_not_fill_hidden_variants_tuple() {
1991        cov_mark::check!(added_wildcard_pattern);
1992        check_assist(
1993            add_missing_match_arms,
1994            r#"
1995//- /main.rs crate:main deps:e
1996fn foo(t: (bool, ::e::E)) {
1997    match $0t {
1998    }
1999}
2000//- /e.rs crate:e
2001pub enum E { A, #[doc(hidden)] B, }
2002"#,
2003            r#"
2004fn foo(t: (bool, ::e::E)) {
2005    match t {
2006        (true, e::E::A) => ${1:todo!()},
2007        (false, e::E::A) => ${2:todo!()},
2008        _ => ${3:todo!()},$0
2009    }
2010}
2011"#,
2012        );
2013    }
2014
2015    #[test]
2016    fn fills_wildcard_with_only_hidden_variants() {
2017        cov_mark::check!(added_wildcard_pattern);
2018        check_assist(
2019            add_missing_match_arms,
2020            r#"
2021//- /main.rs crate:main deps:e
2022fn foo(t: ::e::E) {
2023    match $0t {
2024    }
2025}
2026//- /e.rs crate:e
2027pub enum E { #[doc(hidden)] A, }
2028"#,
2029            r#"
2030fn foo(t: ::e::E) {
2031    match t {
2032        ${1:_} => ${2:todo!()},$0
2033    }
2034}
2035"#,
2036        );
2037    }
2038
2039    #[test]
2040    fn does_not_fill_wildcard_when_hidden_variants_are_explicit() {
2041        check_assist_not_applicable(
2042            add_missing_match_arms,
2043            r#"
2044//- /main.rs crate:main deps:e
2045fn foo(t: ::e::E) {
2046    match $0t {
2047        e::E::A => todo!(),
2048    }
2049}
2050//- /e.rs crate:e
2051pub enum E { #[doc(hidden)] A, }
2052"#,
2053        );
2054    }
2055
2056    #[test]
2057    fn does_not_fill_wildcard_with_wildcard() {
2058        check_assist_not_applicable(
2059            add_missing_match_arms,
2060            r#"
2061//- /main.rs crate:main deps:e
2062fn foo(t: ::e::E) {
2063    match $0t {
2064        _ => todo!(),
2065    }
2066}
2067//- /e.rs crate:e
2068pub enum E { #[doc(hidden)] A, }
2069"#,
2070        );
2071    }
2072
2073    #[test]
2074    fn fills_wildcard_on_non_exhaustive_with_explicit_matches() {
2075        cov_mark::check!(added_wildcard_pattern);
2076        check_assist(
2077            add_missing_match_arms,
2078            r#"
2079//- /main.rs crate:main deps:e
2080fn foo(t: ::e::E) {
2081    match $0t {
2082        e::E::A => todo!(),
2083    }
2084}
2085//- /e.rs crate:e
2086#[non_exhaustive]
2087pub enum E { A, }
2088"#,
2089            r#"
2090fn foo(t: ::e::E) {
2091    match t {
2092        e::E::A => todo!(),
2093        ${1:_} => ${2:todo!()},$0
2094    }
2095}
2096"#,
2097        );
2098    }
2099
2100    #[test]
2101    fn fills_wildcard_on_non_exhaustive_without_matches() {
2102        cov_mark::check!(added_wildcard_pattern);
2103        check_assist(
2104            add_missing_match_arms,
2105            r#"
2106//- /main.rs crate:main deps:e
2107fn foo(t: ::e::E) {
2108    match $0t {
2109    }
2110}
2111//- /e.rs crate:e
2112#[non_exhaustive]
2113pub enum E { A, }
2114"#,
2115            r#"
2116fn foo(t: ::e::E) {
2117    match t {
2118        e::E::A => ${1:todo!()},
2119        _ => ${2:todo!()},$0
2120    }
2121}
2122"#,
2123        );
2124    }
2125
2126    #[test]
2127    fn fills_wildcard_on_non_exhaustive_with_doc_hidden() {
2128        cov_mark::check!(added_wildcard_pattern);
2129        check_assist(
2130            add_missing_match_arms,
2131            r#"
2132//- /main.rs crate:main deps:e
2133fn foo(t: ::e::E) {
2134    match $0t {
2135    }
2136}
2137//- /e.rs crate:e
2138#[non_exhaustive]
2139pub enum E { A, #[doc(hidden)] B }"#,
2140            r#"
2141fn foo(t: ::e::E) {
2142    match t {
2143        e::E::A => ${1:todo!()},
2144        _ => ${2:todo!()},$0
2145    }
2146}
2147"#,
2148        );
2149    }
2150
2151    #[test]
2152    fn fills_wildcard_on_non_exhaustive_with_doc_hidden_with_explicit_arms() {
2153        cov_mark::check!(added_wildcard_pattern);
2154        check_assist(
2155            add_missing_match_arms,
2156            r#"
2157//- /main.rs crate:main deps:e
2158fn foo(t: ::e::E) {
2159    match $0t {
2160        e::E::A => todo!(),
2161    }
2162}
2163//- /e.rs crate:e
2164#[non_exhaustive]
2165pub enum E { A, #[doc(hidden)] B }"#,
2166            r#"
2167fn foo(t: ::e::E) {
2168    match t {
2169        e::E::A => todo!(),
2170        ${1:_} => ${2:todo!()},$0
2171    }
2172}
2173"#,
2174        );
2175    }
2176
2177    #[test]
2178    fn fill_wildcard_with_partial_wildcard() {
2179        cov_mark::check!(added_wildcard_pattern);
2180        check_assist(
2181            add_missing_match_arms,
2182            r#"
2183//- /main.rs crate:main deps:e
2184fn foo(t: ::e::E, b: bool) {
2185    match $0t {
2186        _ if b => todo!(),
2187    }
2188}
2189//- /e.rs crate:e
2190pub enum E { #[doc(hidden)] A, }"#,
2191            r#"
2192fn foo(t: ::e::E, b: bool) {
2193    match t {
2194        _ if b => todo!(),
2195        ${1:_} => ${2:todo!()},$0
2196    }
2197}
2198"#,
2199        );
2200    }
2201
2202    #[test]
2203    fn does_not_fill_wildcard_with_partial_wildcard_and_wildcard() {
2204        check_assist_not_applicable(
2205            add_missing_match_arms,
2206            r#"
2207//- /main.rs crate:main deps:e
2208fn foo(t: ::e::E, b: bool) {
2209    match $0t {
2210        _ if b => todo!(),
2211        _ => todo!(),
2212    }
2213}
2214//- /e.rs crate:e
2215pub enum E { #[doc(hidden)] A, }"#,
2216        );
2217    }
2218
2219    #[test]
2220    fn non_exhaustive_doc_hidden_tuple_fills_wildcard() {
2221        cov_mark::check!(added_wildcard_pattern);
2222        check_assist(
2223            add_missing_match_arms,
2224            r#"
2225//- /main.rs crate:main deps:e
2226fn foo(t: ::e::E) {
2227    match $0t {
2228    }
2229}
2230//- /e.rs crate:e
2231#[non_exhaustive]
2232pub enum E { A, #[doc(hidden)] B, }"#,
2233            r#"
2234fn foo(t: ::e::E) {
2235    match t {
2236        e::E::A => ${1:todo!()},
2237        _ => ${2:todo!()},$0
2238    }
2239}
2240"#,
2241        );
2242    }
2243
2244    #[test]
2245    fn ignores_doc_hidden_for_crate_local_enums() {
2246        check_assist(
2247            add_missing_match_arms,
2248            r#"
2249enum E { A, #[doc(hidden)] B, }
2250
2251fn foo(t: E) {
2252    match $0t {
2253    }
2254}"#,
2255            r#"
2256enum E { A, #[doc(hidden)] B, }
2257
2258fn foo(t: E) {
2259    match t {
2260        E::A => ${1:todo!()},
2261        E::B => ${2:todo!()},$0
2262    }
2263}"#,
2264        );
2265    }
2266
2267    #[test]
2268    fn ignores_non_exhaustive_for_crate_local_enums() {
2269        check_assist(
2270            add_missing_match_arms,
2271            r#"
2272#[non_exhaustive]
2273enum E { A, B, }
2274
2275fn foo(t: E) {
2276    match $0t {
2277    }
2278}"#,
2279            r#"
2280#[non_exhaustive]
2281enum E { A, B, }
2282
2283fn foo(t: E) {
2284    match t {
2285        E::A => ${1:todo!()},
2286        E::B => ${2:todo!()},$0
2287    }
2288}"#,
2289        );
2290    }
2291
2292    #[test]
2293    fn ignores_doc_hidden_and_non_exhaustive_for_crate_local_enums() {
2294        check_assist(
2295            add_missing_match_arms,
2296            r#"
2297#[non_exhaustive]
2298enum E { A, #[doc(hidden)] B, }
2299
2300fn foo(t: E) {
2301    match $0t {
2302    }
2303}"#,
2304            r#"
2305#[non_exhaustive]
2306enum E { A, #[doc(hidden)] B, }
2307
2308fn foo(t: E) {
2309    match t {
2310        E::A => ${1:todo!()},
2311        E::B => ${2:todo!()},$0
2312    }
2313}"#,
2314        );
2315    }
2316
2317    #[test]
2318    fn keep_comments() {
2319        check_assist(
2320            add_missing_match_arms,
2321            r#"
2322enum E { A, B, C }
2323
2324fn foo(t: E) -> i32 {
2325    match $0t {
2326        // variant a
2327        E::A => 2
2328        // comment on end
2329    }
2330}"#,
2331            r#"
2332enum E { A, B, C }
2333
2334fn foo(t: E) -> i32 {
2335    match t {
2336        // variant a
2337        E::A => 2,
2338        // comment on end
2339        E::B => ${1:todo!()},
2340        E::C => ${2:todo!()},$0
2341    }
2342}"#,
2343        );
2344    }
2345
2346    #[test]
2347    fn not_applicable_when_match_arm_list_cannot_be_upmapped() {
2348        check_assist_not_applicable(
2349            add_missing_match_arms,
2350            r#"
2351macro_rules! foo {
2352    ($($t:tt)*) => {
2353        $($t)* {}
2354    }
2355}
2356
2357enum E { A }
2358
2359fn main() {
2360    foo!(match E::A$0);
2361}
2362"#,
2363        );
2364    }
2365
2366    /// See [`discussion`](https://github.com/rust-lang/rust-analyzer/pull/15594#discussion_r1322960614)
2367    #[test]
2368    fn missing_field_name() {
2369        check_assist(
2370            add_missing_match_arms,
2371            r#"
2372enum A {
2373    A,
2374    Missing { a: u32, : u32, c: u32 }
2375}
2376
2377fn a() {
2378    let b = A::A;
2379    match b$0 {}
2380}"#,
2381            r#"
2382enum A {
2383    A,
2384    Missing { a: u32, : u32, c: u32 }
2385}
2386
2387fn a() {
2388    let b = A::A;
2389    match b {
2390        A::A => ${1:todo!()},
2391        A::Missing { a, u32, c } => ${2:todo!()},$0
2392    }
2393}"#,
2394        )
2395    }
2396
2397    #[test]
2398    fn suggest_name_for_tuple_struct_patterns() {
2399        // single tuple struct
2400        check_assist(
2401            add_missing_match_arms,
2402            r#"
2403struct S;
2404
2405pub enum E {
2406    A
2407    B(S),
2408}
2409
2410fn f() {
2411    let value = E::A;
2412    match value {
2413        $0
2414    }
2415}
2416"#,
2417            r#"
2418struct S;
2419
2420pub enum E {
2421    A
2422    B(S),
2423}
2424
2425fn f() {
2426    let value = E::A;
2427    match value {
2428        E::A => ${1:todo!()},
2429        E::B(s) => ${2:todo!()},$0
2430    }
2431}
2432"#,
2433        );
2434
2435        // multiple tuple struct patterns
2436        check_assist(
2437            add_missing_match_arms,
2438            r#"
2439struct S1;
2440struct S2;
2441
2442pub enum E {
2443    A
2444    B(S1, S2),
2445}
2446
2447fn f() {
2448    let value = E::A;
2449    match value {
2450        $0
2451    }
2452}
2453"#,
2454            r#"
2455struct S1;
2456struct S2;
2457
2458pub enum E {
2459    A
2460    B(S1, S2),
2461}
2462
2463fn f() {
2464    let value = E::A;
2465    match value {
2466        E::A => ${1:todo!()},
2467        E::B(s1, s2) => ${2:todo!()},$0
2468    }
2469}
2470"#,
2471        );
2472    }
2473
2474    #[test]
2475    fn prefer_self() {
2476        check_assist_with_config(
2477            add_missing_match_arms,
2478            AssistConfig { prefer_self_ty: true, ..TEST_CONFIG },
2479            r#"
2480enum Foo {
2481    Bar,
2482    Baz,
2483}
2484
2485impl Foo {
2486    fn qux(&self) {
2487        match self {
2488            $0_ => {}
2489        }
2490    }
2491}
2492            "#,
2493            r#"
2494enum Foo {
2495    Bar,
2496    Baz,
2497}
2498
2499impl Foo {
2500    fn qux(&self) {
2501        match self {
2502            Self::Bar => ${1:todo!()},
2503            Self::Baz => ${2:todo!()},$0
2504        }
2505    }
2506}
2507            "#,
2508        );
2509    }
2510
2511    #[test]
2512    fn prefer_self_with_generics() {
2513        check_assist_with_config(
2514            add_missing_match_arms,
2515            AssistConfig { prefer_self_ty: true, ..TEST_CONFIG },
2516            r#"
2517enum Foo<T> {
2518    Bar(T),
2519    Baz,
2520}
2521
2522impl<T> Foo<T> {
2523    fn qux(&self) {
2524        match self {
2525            $0_ => {}
2526        }
2527    }
2528}
2529            "#,
2530            r#"
2531enum Foo<T> {
2532    Bar(T),
2533    Baz,
2534}
2535
2536impl<T> Foo<T> {
2537    fn qux(&self) {
2538        match self {
2539            Self::Bar(${1:_}) => ${2:todo!()},
2540            Self::Baz => ${3:todo!()},$0
2541        }
2542    }
2543}
2544            "#,
2545        );
2546        check_assist_with_config(
2547            add_missing_match_arms,
2548            AssistConfig { prefer_self_ty: true, ..TEST_CONFIG },
2549            r#"
2550enum Foo<T> {
2551    Bar(T),
2552    Baz,
2553}
2554
2555impl<T> Foo<T> {
2556    fn qux(v: Foo<i32>) {
2557        match v {
2558            $0_ => {}
2559        }
2560    }
2561}
2562            "#,
2563            r#"
2564enum Foo<T> {
2565    Bar(T),
2566    Baz,
2567}
2568
2569impl<T> Foo<T> {
2570    fn qux(v: Foo<i32>) {
2571        match v {
2572            Foo::Bar(${1:_}) => ${2:todo!()},
2573            Foo::Baz => ${3:todo!()},$0
2574        }
2575    }
2576}
2577            "#,
2578        );
2579    }
2580}