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