ide_assists/handlers/
add_missing_match_arms.rs

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