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