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
17pub(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 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 .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 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 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 needs_catch_all_arm |= has_hidden_variants;
227
228 let missing_arms = missing_pats
229 .filter(|(_, hidden)| {
230 !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 let old_place = {
291 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 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 if match_arm_list.arms().next().is_none() {
342 cov_mark::hit!(add_missing_match_arms_empty_body);
343 return Some(());
344 }
345
346 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 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
375fn 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 _ => 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 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 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 #[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 #[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 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 #[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 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 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}