diff options
Diffstat (limited to 'crates/ide_diagnostics/src/handlers/missing_match_arms.rs')
-rw-r--r-- | crates/ide_diagnostics/src/handlers/missing_match_arms.rs | 929 |
1 files changed, 929 insertions, 0 deletions
diff --git a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs new file mode 100644 index 000000000..9ea533d74 --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs | |||
@@ -0,0 +1,929 @@ | |||
1 | use hir::InFile; | ||
2 | |||
3 | use crate::{Diagnostic, DiagnosticsContext}; | ||
4 | |||
5 | // Diagnostic: missing-match-arm | ||
6 | // | ||
7 | // This diagnostic is triggered if `match` block is missing one or more match arms. | ||
8 | pub(crate) fn missing_match_arms( | ||
9 | ctx: &DiagnosticsContext<'_>, | ||
10 | d: &hir::MissingMatchArms, | ||
11 | ) -> Diagnostic { | ||
12 | Diagnostic::new( | ||
13 | "missing-match-arm", | ||
14 | "missing match arm", | ||
15 | ctx.sema.diagnostics_display_range(InFile::new(d.file, d.match_expr.clone().into())).range, | ||
16 | ) | ||
17 | } | ||
18 | |||
19 | #[cfg(test)] | ||
20 | mod tests { | ||
21 | use crate::tests::check_diagnostics; | ||
22 | |||
23 | fn check_diagnostics_no_bails(ra_fixture: &str) { | ||
24 | cov_mark::check_count!(validate_match_bailed_out, 0); | ||
25 | crate::tests::check_diagnostics(ra_fixture) | ||
26 | } | ||
27 | |||
28 | #[test] | ||
29 | fn empty_tuple() { | ||
30 | check_diagnostics_no_bails( | ||
31 | r#" | ||
32 | fn main() { | ||
33 | match () { } | ||
34 | //^^ missing match arm | ||
35 | match (()) { } | ||
36 | //^^^^ missing match arm | ||
37 | |||
38 | match () { _ => (), } | ||
39 | match () { () => (), } | ||
40 | match (()) { (()) => (), } | ||
41 | } | ||
42 | "#, | ||
43 | ); | ||
44 | } | ||
45 | |||
46 | #[test] | ||
47 | fn tuple_of_two_empty_tuple() { | ||
48 | check_diagnostics_no_bails( | ||
49 | r#" | ||
50 | fn main() { | ||
51 | match ((), ()) { } | ||
52 | //^^^^^^^^ missing match arm | ||
53 | |||
54 | match ((), ()) { ((), ()) => (), } | ||
55 | } | ||
56 | "#, | ||
57 | ); | ||
58 | } | ||
59 | |||
60 | #[test] | ||
61 | fn boolean() { | ||
62 | check_diagnostics_no_bails( | ||
63 | r#" | ||
64 | fn test_main() { | ||
65 | match false { } | ||
66 | //^^^^^ missing match arm | ||
67 | match false { true => (), } | ||
68 | //^^^^^ missing match arm | ||
69 | match (false, true) {} | ||
70 | //^^^^^^^^^^^^^ missing match arm | ||
71 | match (false, true) { (true, true) => (), } | ||
72 | //^^^^^^^^^^^^^ missing match arm | ||
73 | match (false, true) { | ||
74 | //^^^^^^^^^^^^^ missing match arm | ||
75 | (false, true) => (), | ||
76 | (false, false) => (), | ||
77 | (true, false) => (), | ||
78 | } | ||
79 | match (false, true) { (true, _x) => (), } | ||
80 | //^^^^^^^^^^^^^ missing match arm | ||
81 | |||
82 | match false { true => (), false => (), } | ||
83 | match (false, true) { | ||
84 | (false, _) => (), | ||
85 | (true, false) => (), | ||
86 | (_, true) => (), | ||
87 | } | ||
88 | match (false, true) { | ||
89 | (true, true) => (), | ||
90 | (true, false) => (), | ||
91 | (false, true) => (), | ||
92 | (false, false) => (), | ||
93 | } | ||
94 | match (false, true) { | ||
95 | (true, _x) => (), | ||
96 | (false, true) => (), | ||
97 | (false, false) => (), | ||
98 | } | ||
99 | match (false, true, false) { | ||
100 | (false, ..) => (), | ||
101 | (true, ..) => (), | ||
102 | } | ||
103 | match (false, true, false) { | ||
104 | (.., false) => (), | ||
105 | (.., true) => (), | ||
106 | } | ||
107 | match (false, true, false) { (..) => (), } | ||
108 | } | ||
109 | "#, | ||
110 | ); | ||
111 | } | ||
112 | |||
113 | #[test] | ||
114 | fn tuple_of_tuple_and_bools() { | ||
115 | check_diagnostics_no_bails( | ||
116 | r#" | ||
117 | fn main() { | ||
118 | match (false, ((), false)) {} | ||
119 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
120 | match (false, ((), false)) { (true, ((), true)) => (), } | ||
121 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
122 | match (false, ((), false)) { (true, _) => (), } | ||
123 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
124 | |||
125 | match (false, ((), false)) { | ||
126 | (true, ((), true)) => (), | ||
127 | (true, ((), false)) => (), | ||
128 | (false, ((), true)) => (), | ||
129 | (false, ((), false)) => (), | ||
130 | } | ||
131 | match (false, ((), false)) { | ||
132 | (true, ((), true)) => (), | ||
133 | (true, ((), false)) => (), | ||
134 | (false, _) => (), | ||
135 | } | ||
136 | } | ||
137 | "#, | ||
138 | ); | ||
139 | } | ||
140 | |||
141 | #[test] | ||
142 | fn enums() { | ||
143 | check_diagnostics_no_bails( | ||
144 | r#" | ||
145 | enum Either { A, B, } | ||
146 | |||
147 | fn main() { | ||
148 | match Either::A { } | ||
149 | //^^^^^^^^^ missing match arm | ||
150 | match Either::B { Either::A => (), } | ||
151 | //^^^^^^^^^ missing match arm | ||
152 | |||
153 | match &Either::B { | ||
154 | //^^^^^^^^^^ missing match arm | ||
155 | Either::A => (), | ||
156 | } | ||
157 | |||
158 | match Either::B { | ||
159 | Either::A => (), Either::B => (), | ||
160 | } | ||
161 | match &Either::B { | ||
162 | Either::A => (), Either::B => (), | ||
163 | } | ||
164 | } | ||
165 | "#, | ||
166 | ); | ||
167 | } | ||
168 | |||
169 | #[test] | ||
170 | fn enum_containing_bool() { | ||
171 | check_diagnostics_no_bails( | ||
172 | r#" | ||
173 | enum Either { A(bool), B } | ||
174 | |||
175 | fn main() { | ||
176 | match Either::B { } | ||
177 | //^^^^^^^^^ missing match arm | ||
178 | match Either::B { | ||
179 | //^^^^^^^^^ missing match arm | ||
180 | Either::A(true) => (), Either::B => () | ||
181 | } | ||
182 | |||
183 | match Either::B { | ||
184 | Either::A(true) => (), | ||
185 | Either::A(false) => (), | ||
186 | Either::B => (), | ||
187 | } | ||
188 | match Either::B { | ||
189 | Either::B => (), | ||
190 | _ => (), | ||
191 | } | ||
192 | match Either::B { | ||
193 | Either::A(_) => (), | ||
194 | Either::B => (), | ||
195 | } | ||
196 | |||
197 | } | ||
198 | "#, | ||
199 | ); | ||
200 | } | ||
201 | |||
202 | #[test] | ||
203 | fn enum_different_sizes() { | ||
204 | check_diagnostics_no_bails( | ||
205 | r#" | ||
206 | enum Either { A(bool), B(bool, bool) } | ||
207 | |||
208 | fn main() { | ||
209 | match Either::A(false) { | ||
210 | //^^^^^^^^^^^^^^^^ missing match arm | ||
211 | Either::A(_) => (), | ||
212 | Either::B(false, _) => (), | ||
213 | } | ||
214 | |||
215 | match Either::A(false) { | ||
216 | Either::A(_) => (), | ||
217 | Either::B(true, _) => (), | ||
218 | Either::B(false, _) => (), | ||
219 | } | ||
220 | match Either::A(false) { | ||
221 | Either::A(true) | Either::A(false) => (), | ||
222 | Either::B(true, _) => (), | ||
223 | Either::B(false, _) => (), | ||
224 | } | ||
225 | } | ||
226 | "#, | ||
227 | ); | ||
228 | } | ||
229 | |||
230 | #[test] | ||
231 | fn tuple_of_enum_no_diagnostic() { | ||
232 | check_diagnostics_no_bails( | ||
233 | r#" | ||
234 | enum Either { A(bool), B(bool, bool) } | ||
235 | enum Either2 { C, D } | ||
236 | |||
237 | fn main() { | ||
238 | match (Either::A(false), Either2::C) { | ||
239 | (Either::A(true), _) | (Either::A(false), _) => (), | ||
240 | (Either::B(true, _), Either2::C) => (), | ||
241 | (Either::B(false, _), Either2::C) => (), | ||
242 | (Either::B(_, _), Either2::D) => (), | ||
243 | } | ||
244 | } | ||
245 | "#, | ||
246 | ); | ||
247 | } | ||
248 | |||
249 | #[test] | ||
250 | fn or_pattern_no_diagnostic() { | ||
251 | check_diagnostics_no_bails( | ||
252 | r#" | ||
253 | enum Either {A, B} | ||
254 | |||
255 | fn main() { | ||
256 | match (Either::A, Either::B) { | ||
257 | (Either::A | Either::B, _) => (), | ||
258 | } | ||
259 | }"#, | ||
260 | ) | ||
261 | } | ||
262 | |||
263 | #[test] | ||
264 | fn mismatched_types() { | ||
265 | cov_mark::check_count!(validate_match_bailed_out, 4); | ||
266 | // Match statements with arms that don't match the | ||
267 | // expression pattern do not fire this diagnostic. | ||
268 | check_diagnostics( | ||
269 | r#" | ||
270 | enum Either { A, B } | ||
271 | enum Either2 { C, D } | ||
272 | |||
273 | fn main() { | ||
274 | match Either::A { | ||
275 | Either2::C => (), | ||
276 | Either2::D => (), | ||
277 | } | ||
278 | match (true, false) { | ||
279 | (true, false, true) => (), | ||
280 | (true) => (), | ||
281 | } | ||
282 | match (true, false) { (true,) => {} } | ||
283 | match (0) { () => () } | ||
284 | match Unresolved::Bar { Unresolved::Baz => () } | ||
285 | } | ||
286 | "#, | ||
287 | ); | ||
288 | } | ||
289 | |||
290 | #[test] | ||
291 | fn mismatched_types_in_or_patterns() { | ||
292 | cov_mark::check_count!(validate_match_bailed_out, 2); | ||
293 | check_diagnostics( | ||
294 | r#" | ||
295 | fn main() { | ||
296 | match false { true | () => {} } | ||
297 | match (false,) { (true | (),) => {} } | ||
298 | } | ||
299 | "#, | ||
300 | ); | ||
301 | } | ||
302 | |||
303 | #[test] | ||
304 | fn malformed_match_arm_tuple_enum_missing_pattern() { | ||
305 | // We are testing to be sure we don't panic here when the match | ||
306 | // arm `Either::B` is missing its pattern. | ||
307 | check_diagnostics_no_bails( | ||
308 | r#" | ||
309 | enum Either { A, B(u32) } | ||
310 | |||
311 | fn main() { | ||
312 | match Either::A { | ||
313 | Either::A => (), | ||
314 | Either::B() => (), | ||
315 | } | ||
316 | } | ||
317 | "#, | ||
318 | ); | ||
319 | } | ||
320 | |||
321 | #[test] | ||
322 | fn malformed_match_arm_extra_fields() { | ||
323 | cov_mark::check_count!(validate_match_bailed_out, 2); | ||
324 | check_diagnostics( | ||
325 | r#" | ||
326 | enum A { B(isize, isize), C } | ||
327 | fn main() { | ||
328 | match A::B(1, 2) { | ||
329 | A::B(_, _, _) => (), | ||
330 | } | ||
331 | match A::B(1, 2) { | ||
332 | A::C(_) => (), | ||
333 | } | ||
334 | } | ||
335 | "#, | ||
336 | ); | ||
337 | } | ||
338 | |||
339 | #[test] | ||
340 | fn expr_diverges() { | ||
341 | cov_mark::check_count!(validate_match_bailed_out, 2); | ||
342 | check_diagnostics( | ||
343 | r#" | ||
344 | enum Either { A, B } | ||
345 | |||
346 | fn main() { | ||
347 | match loop {} { | ||
348 | Either::A => (), | ||
349 | Either::B => (), | ||
350 | } | ||
351 | match loop {} { | ||
352 | Either::A => (), | ||
353 | } | ||
354 | match loop { break Foo::A } { | ||
355 | //^^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
356 | Either::A => (), | ||
357 | } | ||
358 | match loop { break Foo::A } { | ||
359 | Either::A => (), | ||
360 | Either::B => (), | ||
361 | } | ||
362 | } | ||
363 | "#, | ||
364 | ); | ||
365 | } | ||
366 | |||
367 | #[test] | ||
368 | fn expr_partially_diverges() { | ||
369 | check_diagnostics_no_bails( | ||
370 | r#" | ||
371 | enum Either<T> { A(T), B } | ||
372 | |||
373 | fn foo() -> Either<!> { Either::B } | ||
374 | fn main() -> u32 { | ||
375 | match foo() { | ||
376 | Either::A(val) => val, | ||
377 | Either::B => 0, | ||
378 | } | ||
379 | } | ||
380 | "#, | ||
381 | ); | ||
382 | } | ||
383 | |||
384 | #[test] | ||
385 | fn enum_record() { | ||
386 | check_diagnostics_no_bails( | ||
387 | r#" | ||
388 | enum Either { A { foo: bool }, B } | ||
389 | |||
390 | fn main() { | ||
391 | let a = Either::A { foo: true }; | ||
392 | match a { } | ||
393 | //^ missing match arm | ||
394 | match a { Either::A { foo: true } => () } | ||
395 | //^ missing match arm | ||
396 | match a { | ||
397 | Either::A { } => (), | ||
398 | //^^^^^^^^^ Missing structure fields: | ||
399 | // | - foo | ||
400 | Either::B => (), | ||
401 | } | ||
402 | match a { | ||
403 | //^ missing match arm | ||
404 | Either::A { } => (), | ||
405 | } //^^^^^^^^^ Missing structure fields: | ||
406 | // | - foo | ||
407 | |||
408 | match a { | ||
409 | Either::A { foo: true } => (), | ||
410 | Either::A { foo: false } => (), | ||
411 | Either::B => (), | ||
412 | } | ||
413 | match a { | ||
414 | Either::A { foo: _ } => (), | ||
415 | Either::B => (), | ||
416 | } | ||
417 | } | ||
418 | "#, | ||
419 | ); | ||
420 | } | ||
421 | |||
422 | #[test] | ||
423 | fn enum_record_fields_out_of_order() { | ||
424 | check_diagnostics_no_bails( | ||
425 | r#" | ||
426 | enum Either { | ||
427 | A { foo: bool, bar: () }, | ||
428 | B, | ||
429 | } | ||
430 | |||
431 | fn main() { | ||
432 | let a = Either::A { foo: true, bar: () }; | ||
433 | match a { | ||
434 | //^ missing match arm | ||
435 | Either::A { bar: (), foo: false } => (), | ||
436 | Either::A { foo: true, bar: () } => (), | ||
437 | } | ||
438 | |||
439 | match a { | ||
440 | Either::A { bar: (), foo: false } => (), | ||
441 | Either::A { foo: true, bar: () } => (), | ||
442 | Either::B => (), | ||
443 | } | ||
444 | } | ||
445 | "#, | ||
446 | ); | ||
447 | } | ||
448 | |||
449 | #[test] | ||
450 | fn enum_record_ellipsis() { | ||
451 | check_diagnostics_no_bails( | ||
452 | r#" | ||
453 | enum Either { | ||
454 | A { foo: bool, bar: bool }, | ||
455 | B, | ||
456 | } | ||
457 | |||
458 | fn main() { | ||
459 | let a = Either::B; | ||
460 | match a { | ||
461 | //^ missing match arm | ||
462 | Either::A { foo: true, .. } => (), | ||
463 | Either::B => (), | ||
464 | } | ||
465 | match a { | ||
466 | //^ missing match arm | ||
467 | Either::A { .. } => (), | ||
468 | } | ||
469 | |||
470 | match a { | ||
471 | Either::A { foo: true, .. } => (), | ||
472 | Either::A { foo: false, .. } => (), | ||
473 | Either::B => (), | ||
474 | } | ||
475 | |||
476 | match a { | ||
477 | Either::A { .. } => (), | ||
478 | Either::B => (), | ||
479 | } | ||
480 | } | ||
481 | "#, | ||
482 | ); | ||
483 | } | ||
484 | |||
485 | #[test] | ||
486 | fn enum_tuple_partial_ellipsis() { | ||
487 | check_diagnostics_no_bails( | ||
488 | r#" | ||
489 | enum Either { | ||
490 | A(bool, bool, bool, bool), | ||
491 | B, | ||
492 | } | ||
493 | |||
494 | fn main() { | ||
495 | match Either::B { | ||
496 | //^^^^^^^^^ missing match arm | ||
497 | Either::A(true, .., true) => (), | ||
498 | Either::A(true, .., false) => (), | ||
499 | Either::A(false, .., false) => (), | ||
500 | Either::B => (), | ||
501 | } | ||
502 | match Either::B { | ||
503 | //^^^^^^^^^ missing match arm | ||
504 | Either::A(true, .., true) => (), | ||
505 | Either::A(true, .., false) => (), | ||
506 | Either::A(.., true) => (), | ||
507 | Either::B => (), | ||
508 | } | ||
509 | |||
510 | match Either::B { | ||
511 | Either::A(true, .., true) => (), | ||
512 | Either::A(true, .., false) => (), | ||
513 | Either::A(false, .., true) => (), | ||
514 | Either::A(false, .., false) => (), | ||
515 | Either::B => (), | ||
516 | } | ||
517 | match Either::B { | ||
518 | Either::A(true, .., true) => (), | ||
519 | Either::A(true, .., false) => (), | ||
520 | Either::A(.., true) => (), | ||
521 | Either::A(.., false) => (), | ||
522 | Either::B => (), | ||
523 | } | ||
524 | } | ||
525 | "#, | ||
526 | ); | ||
527 | } | ||
528 | |||
529 | #[test] | ||
530 | fn never() { | ||
531 | check_diagnostics_no_bails( | ||
532 | r#" | ||
533 | enum Never {} | ||
534 | |||
535 | fn enum_(never: Never) { | ||
536 | match never {} | ||
537 | } | ||
538 | fn enum_ref(never: &Never) { | ||
539 | match never {} | ||
540 | //^^^^^ missing match arm | ||
541 | } | ||
542 | fn bang(never: !) { | ||
543 | match never {} | ||
544 | } | ||
545 | "#, | ||
546 | ); | ||
547 | } | ||
548 | |||
549 | #[test] | ||
550 | fn unknown_type() { | ||
551 | cov_mark::check_count!(validate_match_bailed_out, 1); | ||
552 | |||
553 | check_diagnostics( | ||
554 | r#" | ||
555 | enum Option<T> { Some(T), None } | ||
556 | |||
557 | fn main() { | ||
558 | // `Never` is deliberately not defined so that it's an uninferred type. | ||
559 | match Option::<Never>::None { | ||
560 | None => (), | ||
561 | Some(never) => match never {}, | ||
562 | } | ||
563 | match Option::<Never>::None { | ||
564 | //^^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
565 | Option::Some(_never) => {}, | ||
566 | } | ||
567 | } | ||
568 | "#, | ||
569 | ); | ||
570 | } | ||
571 | |||
572 | #[test] | ||
573 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | ||
574 | check_diagnostics_no_bails( | ||
575 | r#" | ||
576 | fn main() { | ||
577 | match (false, true, false) { | ||
578 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
579 | (false, ..) => (), | ||
580 | } | ||
581 | }"#, | ||
582 | ); | ||
583 | } | ||
584 | |||
585 | #[test] | ||
586 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | ||
587 | check_diagnostics_no_bails( | ||
588 | r#" | ||
589 | fn main() { | ||
590 | match (false, true, false) { | ||
591 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
592 | (.., false) => (), | ||
593 | } | ||
594 | }"#, | ||
595 | ); | ||
596 | } | ||
597 | |||
598 | #[test] | ||
599 | fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() { | ||
600 | check_diagnostics_no_bails( | ||
601 | r#" | ||
602 | fn main() { | ||
603 | match (false, true, false) { | ||
604 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
605 | (true, .., false) => (), | ||
606 | } | ||
607 | }"#, | ||
608 | ); | ||
609 | } | ||
610 | |||
611 | #[test] | ||
612 | fn record_struct() { | ||
613 | check_diagnostics_no_bails( | ||
614 | r#"struct Foo { a: bool } | ||
615 | fn main(f: Foo) { | ||
616 | match f {} | ||
617 | //^ missing match arm | ||
618 | match f { Foo { a: true } => () } | ||
619 | //^ missing match arm | ||
620 | match &f { Foo { a: true } => () } | ||
621 | //^^ missing match arm | ||
622 | match f { Foo { a: _ } => () } | ||
623 | match f { | ||
624 | Foo { a: true } => (), | ||
625 | Foo { a: false } => (), | ||
626 | } | ||
627 | match &f { | ||
628 | Foo { a: true } => (), | ||
629 | Foo { a: false } => (), | ||
630 | } | ||
631 | } | ||
632 | "#, | ||
633 | ); | ||
634 | } | ||
635 | |||
636 | #[test] | ||
637 | fn tuple_struct() { | ||
638 | check_diagnostics_no_bails( | ||
639 | r#"struct Foo(bool); | ||
640 | fn main(f: Foo) { | ||
641 | match f {} | ||
642 | //^ missing match arm | ||
643 | match f { Foo(true) => () } | ||
644 | //^ missing match arm | ||
645 | match f { | ||
646 | Foo(true) => (), | ||
647 | Foo(false) => (), | ||
648 | } | ||
649 | } | ||
650 | "#, | ||
651 | ); | ||
652 | } | ||
653 | |||
654 | #[test] | ||
655 | fn unit_struct() { | ||
656 | check_diagnostics_no_bails( | ||
657 | r#"struct Foo; | ||
658 | fn main(f: Foo) { | ||
659 | match f {} | ||
660 | //^ missing match arm | ||
661 | match f { Foo => () } | ||
662 | } | ||
663 | "#, | ||
664 | ); | ||
665 | } | ||
666 | |||
667 | #[test] | ||
668 | fn record_struct_ellipsis() { | ||
669 | check_diagnostics_no_bails( | ||
670 | r#"struct Foo { foo: bool, bar: bool } | ||
671 | fn main(f: Foo) { | ||
672 | match f { Foo { foo: true, .. } => () } | ||
673 | //^ missing match arm | ||
674 | match f { | ||
675 | //^ missing match arm | ||
676 | Foo { foo: true, .. } => (), | ||
677 | Foo { bar: false, .. } => () | ||
678 | } | ||
679 | match f { Foo { .. } => () } | ||
680 | match f { | ||
681 | Foo { foo: true, .. } => (), | ||
682 | Foo { foo: false, .. } => () | ||
683 | } | ||
684 | } | ||
685 | "#, | ||
686 | ); | ||
687 | } | ||
688 | |||
689 | #[test] | ||
690 | fn internal_or() { | ||
691 | check_diagnostics_no_bails( | ||
692 | r#" | ||
693 | fn main() { | ||
694 | enum Either { A(bool), B } | ||
695 | match Either::B { | ||
696 | //^^^^^^^^^ missing match arm | ||
697 | Either::A(true | false) => (), | ||
698 | } | ||
699 | } | ||
700 | "#, | ||
701 | ); | ||
702 | } | ||
703 | |||
704 | #[test] | ||
705 | fn no_panic_at_unimplemented_subpattern_type() { | ||
706 | cov_mark::check_count!(validate_match_bailed_out, 1); | ||
707 | |||
708 | check_diagnostics( | ||
709 | r#" | ||
710 | struct S { a: char} | ||
711 | fn main(v: S) { | ||
712 | match v { S{ a } => {} } | ||
713 | match v { S{ a: _x } => {} } | ||
714 | match v { S{ a: 'a' } => {} } | ||
715 | match v { S{..} => {} } | ||
716 | match v { _ => {} } | ||
717 | match v { } | ||
718 | //^ missing match arm | ||
719 | } | ||
720 | "#, | ||
721 | ); | ||
722 | } | ||
723 | |||
724 | #[test] | ||
725 | fn binding() { | ||
726 | check_diagnostics_no_bails( | ||
727 | r#" | ||
728 | fn main() { | ||
729 | match true { | ||
730 | _x @ true => {} | ||
731 | false => {} | ||
732 | } | ||
733 | match true { _x @ true => {} } | ||
734 | //^^^^ missing match arm | ||
735 | } | ||
736 | "#, | ||
737 | ); | ||
738 | } | ||
739 | |||
740 | #[test] | ||
741 | fn binding_ref_has_correct_type() { | ||
742 | cov_mark::check_count!(validate_match_bailed_out, 1); | ||
743 | |||
744 | // Asserts `PatKind::Binding(ref _x): bool`, not &bool. | ||
745 | // If that's not true match checking will panic with "incompatible constructors" | ||
746 | // FIXME: make facilities to test this directly like `tests::check_infer(..)` | ||
747 | check_diagnostics( | ||
748 | r#" | ||
749 | enum Foo { A } | ||
750 | fn main() { | ||
751 | // FIXME: this should not bail out but current behavior is such as the old algorithm. | ||
752 | // ExprValidator::validate_match(..) checks types of top level patterns incorrecly. | ||
753 | match Foo::A { | ||
754 | ref _x => {} | ||
755 | Foo::A => {} | ||
756 | } | ||
757 | match (true,) { | ||
758 | (ref _x,) => {} | ||
759 | (true,) => {} | ||
760 | } | ||
761 | } | ||
762 | "#, | ||
763 | ); | ||
764 | } | ||
765 | |||
766 | #[test] | ||
767 | fn enum_non_exhaustive() { | ||
768 | check_diagnostics_no_bails( | ||
769 | r#" | ||
770 | //- /lib.rs crate:lib | ||
771 | #[non_exhaustive] | ||
772 | pub enum E { A, B } | ||
773 | fn _local() { | ||
774 | match E::A { _ => {} } | ||
775 | match E::A { | ||
776 | E::A => {} | ||
777 | E::B => {} | ||
778 | } | ||
779 | match E::A { | ||
780 | E::A | E::B => {} | ||
781 | } | ||
782 | } | ||
783 | |||
784 | //- /main.rs crate:main deps:lib | ||
785 | use lib::E; | ||
786 | fn main() { | ||
787 | match E::A { _ => {} } | ||
788 | match E::A { | ||
789 | //^^^^ missing match arm | ||
790 | E::A => {} | ||
791 | E::B => {} | ||
792 | } | ||
793 | match E::A { | ||
794 | //^^^^ missing match arm | ||
795 | E::A | E::B => {} | ||
796 | } | ||
797 | } | ||
798 | "#, | ||
799 | ); | ||
800 | } | ||
801 | |||
802 | #[test] | ||
803 | fn match_guard() { | ||
804 | check_diagnostics_no_bails( | ||
805 | r#" | ||
806 | fn main() { | ||
807 | match true { | ||
808 | true if false => {} | ||
809 | true => {} | ||
810 | false => {} | ||
811 | } | ||
812 | match true { | ||
813 | //^^^^ missing match arm | ||
814 | true if false => {} | ||
815 | false => {} | ||
816 | } | ||
817 | } | ||
818 | "#, | ||
819 | ); | ||
820 | } | ||
821 | |||
822 | #[test] | ||
823 | fn pattern_type_is_of_substitution() { | ||
824 | cov_mark::check!(match_check_wildcard_expanded_to_substitutions); | ||
825 | check_diagnostics_no_bails( | ||
826 | r#" | ||
827 | struct Foo<T>(T); | ||
828 | struct Bar; | ||
829 | fn main() { | ||
830 | match Foo(Bar) { | ||
831 | _ | Foo(Bar) => {} | ||
832 | } | ||
833 | } | ||
834 | "#, | ||
835 | ); | ||
836 | } | ||
837 | |||
838 | #[test] | ||
839 | fn record_struct_no_such_field() { | ||
840 | cov_mark::check_count!(validate_match_bailed_out, 1); | ||
841 | |||
842 | check_diagnostics( | ||
843 | r#" | ||
844 | struct Foo { } | ||
845 | fn main(f: Foo) { | ||
846 | match f { Foo { bar } => () } | ||
847 | } | ||
848 | "#, | ||
849 | ); | ||
850 | } | ||
851 | |||
852 | #[test] | ||
853 | fn match_ergonomics_issue_9095() { | ||
854 | check_diagnostics_no_bails( | ||
855 | r#" | ||
856 | enum Foo<T> { A(T) } | ||
857 | fn main() { | ||
858 | match &Foo::A(true) { | ||
859 | _ => {} | ||
860 | Foo::A(_) => {} | ||
861 | } | ||
862 | } | ||
863 | "#, | ||
864 | ); | ||
865 | } | ||
866 | |||
867 | mod false_negatives { | ||
868 | //! The implementation of match checking here is a work in progress. As we roll this out, we | ||
869 | //! prefer false negatives to false positives (ideally there would be no false positives). This | ||
870 | //! test module should document known false negatives. Eventually we will have a complete | ||
871 | //! implementation of match checking and this module will be empty. | ||
872 | //! | ||
873 | //! The reasons for documenting known false negatives: | ||
874 | //! | ||
875 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. | ||
876 | //! 2. It ensures the code doesn't panic when handling these cases. | ||
877 | use super::*; | ||
878 | |||
879 | #[test] | ||
880 | fn integers() { | ||
881 | cov_mark::check_count!(validate_match_bailed_out, 1); | ||
882 | |||
883 | // We don't currently check integer exhaustiveness. | ||
884 | check_diagnostics( | ||
885 | r#" | ||
886 | fn main() { | ||
887 | match 5 { | ||
888 | 10 => (), | ||
889 | 11..20 => (), | ||
890 | } | ||
891 | } | ||
892 | "#, | ||
893 | ); | ||
894 | } | ||
895 | |||
896 | #[test] | ||
897 | fn reference_patterns_at_top_level() { | ||
898 | cov_mark::check_count!(validate_match_bailed_out, 1); | ||
899 | |||
900 | check_diagnostics( | ||
901 | r#" | ||
902 | fn main() { | ||
903 | match &false { | ||
904 | &true => {} | ||
905 | } | ||
906 | } | ||
907 | "#, | ||
908 | ); | ||
909 | } | ||
910 | |||
911 | #[test] | ||
912 | fn reference_patterns_in_fields() { | ||
913 | cov_mark::check_count!(validate_match_bailed_out, 2); | ||
914 | |||
915 | check_diagnostics( | ||
916 | r#" | ||
917 | fn main() { | ||
918 | match (&false,) { | ||
919 | (true,) => {} | ||
920 | } | ||
921 | match (&false,) { | ||
922 | (&true,) => {} | ||
923 | } | ||
924 | } | ||
925 | "#, | ||
926 | ); | ||
927 | } | ||
928 | } | ||
929 | } | ||