1 use super::_match
::Usefulness
::*;
2 use super::_match
::WitnessPreference
::*;
3 use super::_match
::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack}
;
5 use super::{PatCtxt, PatKind, PatternError}
;
8 use rustc
::session
::Session
;
9 use rustc
::ty
::subst
::{InternalSubsts, SubstsRef}
;
10 use rustc
::ty
::{self, Ty, TyCtxt}
;
11 use rustc_errors
::{Applicability, DiagnosticBuilder}
;
13 use rustc
::hir
::def
::*;
14 use rustc
::hir
::def_id
::DefId
;
15 use rustc
::hir
::intravisit
::{self, NestedVisitorMap, Visitor}
;
16 use rustc
::hir
::HirId
;
17 use rustc
::hir
::{self, Pat}
;
21 use syntax_pos
::{MultiSpan, Span, DUMMY_SP}
;
23 crate fn check_match(tcx
: TyCtxt
<'_
>, def_id
: DefId
) {
24 let body_id
= match tcx
.hir().as_local_hir_id(def_id
) {
26 Some(id
) => tcx
.hir().body_owned_by(id
),
29 let mut visitor
= MatchVisitor
{
31 tables
: tcx
.body_tables(body_id
),
32 param_env
: tcx
.param_env(def_id
),
33 identity_substs
: InternalSubsts
::identity_for_item(tcx
, def_id
),
35 visitor
.visit_body(tcx
.hir().body(body_id
));
38 fn create_e0004(sess
: &Session
, sp
: Span
, error_message
: String
) -> DiagnosticBuilder
<'_
> {
39 struct_span_err
!(sess
, sp
, E0004
, "{}", &error_message
)
42 struct MatchVisitor
<'a
, 'tcx
> {
44 tables
: &'a ty
::TypeckTables
<'tcx
>,
45 param_env
: ty
::ParamEnv
<'tcx
>,
46 identity_substs
: SubstsRef
<'tcx
>,
49 impl<'tcx
> Visitor
<'tcx
> for MatchVisitor
<'_
, 'tcx
> {
50 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
51 NestedVisitorMap
::None
54 fn visit_expr(&mut self, ex
: &'tcx hir
::Expr
) {
55 intravisit
::walk_expr(self, ex
);
57 if let hir
::ExprKind
::Match(ref scrut
, ref arms
, source
) = ex
.kind
{
58 self.check_match(scrut
, arms
, source
);
62 fn visit_local(&mut self, loc
: &'tcx hir
::Local
) {
63 intravisit
::walk_local(self, loc
);
65 let (msg
, sp
) = match loc
.source
{
66 hir
::LocalSource
::Normal
=> ("local binding", Some(loc
.span
)),
67 hir
::LocalSource
::ForLoopDesugar
=> ("`for` loop binding", None
),
68 hir
::LocalSource
::AsyncFn
=> ("async fn binding", None
),
69 hir
::LocalSource
::AwaitDesugar
=> ("`await` future binding", None
),
71 self.check_irrefutable(&loc
.pat
, msg
, sp
);
73 // Check legality of move bindings and `@` patterns.
74 self.check_patterns(false, &loc
.pat
);
77 fn visit_body(&mut self, body
: &'tcx hir
::Body
) {
78 intravisit
::walk_body(self, body
);
80 for param
in &body
.params
{
81 self.check_irrefutable(¶m
.pat
, "function argument", None
);
82 self.check_patterns(false, ¶m
.pat
);
87 impl PatCtxt
<'_
, '_
> {
88 fn report_inlining_errors(&self, pat_span
: Span
) {
89 for error
in &self.errors
{
91 PatternError
::StaticInPattern(span
) => {
92 self.span_e0158(span
, "statics cannot be referenced in patterns")
94 PatternError
::AssocConstInPattern(span
) => {
95 self.span_e0158(span
, "associated consts cannot be referenced in patterns")
97 PatternError
::FloatBug
=> {
98 // FIXME(#31407) this is only necessary because float parsing is buggy
99 ::rustc
::mir
::interpret
::struct_error(
100 self.tcx
.at(pat_span
),
101 "could not evaluate float literal (see issue #31407)",
105 PatternError
::NonConstPath(span
) => {
106 ::rustc
::mir
::interpret
::struct_error(
108 "runtime values cannot be referenced in patterns",
116 fn span_e0158(&self, span
: Span
, text
: &str) {
117 span_err
!(self.tcx
.sess
, span
, E0158
, "{}", text
)
121 impl<'tcx
> MatchVisitor
<'_
, 'tcx
> {
122 fn check_patterns(&mut self, has_guard
: bool
, pat
: &Pat
) {
123 check_legality_of_move_bindings(self, has_guard
, pat
);
124 check_legality_of_bindings_in_at_patterns(self, pat
);
127 fn check_match(&mut self, scrut
: &hir
::Expr
, arms
: &'tcx
[hir
::Arm
], source
: hir
::MatchSource
) {
129 // First, check legality of move bindings.
130 self.check_patterns(arm
.guard
.is_some(), &arm
.pat
);
132 // Second, perform some lints.
133 check_for_bindings_named_same_as_variants(self, &arm
.pat
);
136 let module
= self.tcx
.hir().get_module_parent(scrut
.hir_id
);
137 MatchCheckCtxt
::create_and_enter(self.tcx
, self.param_env
, module
, |ref mut cx
| {
138 let mut have_errors
= false;
140 let inlined_arms
: Vec
<(Vec
<_
>, _
)> = arms
144 // HACK(or_patterns; Centril | dlrobertson): Remove this and
145 // correctly handle exhaustiveness checking for nested or-patterns.
146 match &arm
.pat
.kind
{
147 hir
::PatKind
::Or(pats
) => pats
,
148 _
=> std
::slice
::from_ref(&arm
.pat
),
152 let mut patcx
= PatCtxt
::new(
154 self.param_env
.and(self.identity_substs
),
157 patcx
.include_lint_checks();
160 .alloc(expand_pattern(cx
, patcx
.lower_pattern(&pat
)))
162 if !patcx
.errors
.is_empty() {
163 patcx
.report_inlining_errors(pat
.span
);
169 arm
.guard
.as_ref().map(|g
| match g
{
170 hir
::Guard
::If(ref e
) => &**e
,
176 // Bail out early if inlining failed.
181 // Fourth, check for unreachable arms.
182 check_arms(cx
, &inlined_arms
, source
);
184 // Then, if the match has no arms, check whether the scrutinee
186 let pat_ty
= self.tables
.node_type(scrut
.hir_id
);
187 let module
= self.tcx
.hir().get_module_parent(scrut
.hir_id
);
188 let mut def_span
= None
;
189 let mut missing_variants
= vec
![];
190 if inlined_arms
.is_empty() {
191 let scrutinee_is_uninhabited
= if self.tcx
.features().exhaustive_patterns
{
192 self.tcx
.is_ty_uninhabited_from(module
, pat_ty
)
197 def_span
= self.tcx
.hir().span_if_local(def
.did
);
198 if def
.variants
.len() < 4 && !def
.variants
.is_empty() {
199 // keep around to point at the definition of non-covered variants
201 def
.variants
.iter().map(|variant
| variant
.ident
).collect();
204 let is_non_exhaustive_and_non_local
=
205 def
.is_variant_list_non_exhaustive() && !def
.did
.is_local();
207 !(is_non_exhaustive_and_non_local
) && def
.variants
.is_empty()
212 if !scrutinee_is_uninhabited
{
213 // We know the type is inhabited, so this must be wrong
214 let mut err
= create_e0004(
218 "non-exhaustive patterns: {}",
219 match missing_variants
.len() {
220 0 => format
!("type `{}` is non-empty", pat_ty
),
222 "pattern `{}` of type `{}` is not handled",
223 missing_variants
[0].name
, pat_ty
,
226 "multiple patterns of type `{}` are not handled",
233 "ensure that all possible cases are being handled, \
234 possibly by adding wildcards or more match arms",
236 if let Some(sp
) = def_span
{
237 err
.span_label(sp
, format
!("`{}` defined here", pat_ty
));
239 // point at the definition of non-covered enum variants
240 for variant
in &missing_variants
{
241 err
.span_label(variant
.span
, "variant not covered");
245 // If the type *is* uninhabited, it's vacuously exhaustive
249 let matrix
: Matrix
<'_
, '_
> = inlined_arms
251 .filter(|&&(_
, guard
)| guard
.is_none())
252 .flat_map(|arm
| &arm
.0)
253 .map(|pat
| PatStack
::from_pattern(pat
.0))
255 let scrut_ty
= self.tables
.node_type(scrut
.hir_id
);
256 check_exhaustive(cx
, scrut_ty
, scrut
.span
, &matrix
, scrut
.hir_id
);
260 fn check_irrefutable(&self, pat
: &'tcx Pat
, origin
: &str, sp
: Option
<Span
>) {
261 let module
= self.tcx
.hir().get_module_parent(pat
.hir_id
);
262 MatchCheckCtxt
::create_and_enter(self.tcx
, self.param_env
, module
, |ref mut cx
| {
264 PatCtxt
::new(self.tcx
, self.param_env
.and(self.identity_substs
), self.tables
);
265 patcx
.include_lint_checks();
266 let pattern
= patcx
.lower_pattern(pat
);
267 let pattern_ty
= pattern
.ty
;
268 let pattern
= expand_pattern(cx
, pattern
);
269 let pats
: Matrix
<'_
, '_
> = vec
![PatStack
::from_pattern(&pattern
)].into_iter().collect();
271 let witnesses
= match check_not_useful(cx
, pattern_ty
, &pats
, pat
.hir_id
) {
276 let joined_patterns
= joined_uncovered_patterns(&witnesses
);
277 let mut err
= struct_span_err
!(
281 "refutable pattern in {}: {} not covered",
285 let suggest_if_let
= match &pat
.kind
{
286 hir
::PatKind
::Path(hir
::QPath
::Resolved(None
, path
))
287 if path
.segments
.len() == 1 && path
.segments
[0].args
.is_none() =>
289 const_not_var(&mut err
, cx
.tcx
, pat
, path
);
295 pattern_not_covered_label(&witnesses
, &joined_patterns
),
301 if let (Some(span
), true) = (sp
, suggest_if_let
) {
303 "`let` bindings require an \"irrefutable pattern\", like a `struct` or \
304 an `enum` with only one variant",
306 if let Ok(snippet
) = self.tcx
.sess
.source_map().span_to_snippet(span
) {
309 "you might want to use `if let` to ignore the variant that isn't matched",
310 format
!("if {} {{ /* */ }}", &snippet
[..snippet
.len() - 1]),
311 Applicability
::HasPlaceholders
,
315 "for more information, visit \
316 https://doc.rust-lang.org/book/ch18-02-refutability.html",
320 adt_defined_here(cx
, &mut err
, pattern_ty
, &witnesses
);
326 /// A path pattern was interpreted as a constant, not a new variable.
327 /// This caused an irrefutable match failure in e.g. `let`.
328 fn const_not_var(err
: &mut DiagnosticBuilder
<'_
>, tcx
: TyCtxt
<'_
>, pat
: &Pat
, path
: &hir
::Path
) {
329 let descr
= path
.res
.descr();
332 format
!("interpreted as {} {} pattern, not a new variable", path
.res
.article(), descr
,),
337 "introduce a variable instead",
338 format
!("{}_var", path
.segments
[0].ident
).to_lowercase(),
339 // Cannot use `MachineApplicable` as it's not really *always* correct
340 // because there may be such an identifier in scope or the user maybe
341 // really wanted to match against the constant. This is quite unlikely however.
342 Applicability
::MaybeIncorrect
,
345 if let Some(span
) = tcx
.hir().res_span(path
.res
) {
346 err
.span_label(span
, format
!("{} defined here", descr
));
350 fn check_for_bindings_named_same_as_variants(cx
: &MatchVisitor
<'_
, '_
>, pat
: &Pat
) {
352 if let hir
::PatKind
::Binding(_
, _
, ident
, None
) = p
.kind
{
353 if let Some(&bm
) = cx
.tables
.pat_binding_modes().get(p
.hir_id
) {
354 if bm
!= ty
::BindByValue(hir
::MutImmutable
) {
358 let pat_ty
= cx
.tables
.pat_ty(p
);
359 if let ty
::Adt(edef
, _
) = pat_ty
.kind
{
361 && edef
.variants
.iter().any(|variant
| {
362 variant
.ident
== ident
&& variant
.ctor_kind
== CtorKind
::Const
365 let ty_path
= cx
.tcx
.def_path_str(edef
.did
);
366 let mut err
= struct_span_warn
!(
370 "pattern binding `{}` is named the same as one \
371 of the variants of the type `{}`",
377 "to match on the variant, qualify the path",
378 format
!("{}::{}", ty_path
, ident
),
379 Applicability
::MachineApplicable
,
385 cx
.tcx
.sess
.delay_span_bug(p
.span
, "missing binding mode");
392 /// Checks for common cases of "catchall" patterns that may not be intended as such.
393 fn pat_is_catchall(pat
: &Pat
) -> bool
{
395 hir
::PatKind
::Binding(.., None
) => true,
396 hir
::PatKind
::Binding(.., Some(ref s
)) => pat_is_catchall(s
),
397 hir
::PatKind
::Ref(ref s
, _
) => pat_is_catchall(s
),
398 hir
::PatKind
::Tuple(ref v
, _
) => v
.iter().all(|p
| pat_is_catchall(&p
)),
403 // Check for unreachable patterns
405 cx
: &mut MatchCheckCtxt
<'_
, 'tcx
>,
406 arms
: &[(Vec
<(&super::Pat
<'tcx
>, &hir
::Pat
)>, Option
<&hir
::Expr
>)],
407 source
: hir
::MatchSource
,
409 let mut seen
= Matrix
::empty();
410 let mut catchall
= None
;
411 for (arm_index
, &(ref pats
, guard
)) in arms
.iter().enumerate() {
412 for &(pat
, hir_pat
) in pats
{
413 let v
= PatStack
::from_pattern(pat
);
415 match is_useful(cx
, &seen
, &v
, LeaveOutWitness
, hir_pat
.hir_id
) {
418 hir
::MatchSource
::IfDesugar { .. }
| hir
::MatchSource
::WhileDesugar
=> {
421 hir
::MatchSource
::IfLetDesugar { .. }
=> {
423 lint
::builtin
::IRREFUTABLE_LET_PATTERNS
,
426 "irrefutable if-let pattern",
430 hir
::MatchSource
::WhileLetDesugar
=> {
431 // check which arm we're on.
433 // The arm with the user-specified pattern.
436 lint
::builtin
::UNREACHABLE_PATTERNS
,
439 "unreachable pattern",
442 // The arm with the wildcard pattern.
445 lint
::builtin
::IRREFUTABLE_LET_PATTERNS
,
448 "irrefutable while-let pattern",
455 hir
::MatchSource
::ForLoopDesugar
| hir
::MatchSource
::Normal
=> {
456 let mut err
= cx
.tcx
.struct_span_lint_hir(
457 lint
::builtin
::UNREACHABLE_PATTERNS
,
460 "unreachable pattern",
462 // if we had a catchall pattern, hint at that
463 if let Some(catchall
) = catchall
{
464 err
.span_label(pat
.span
, "unreachable pattern");
465 err
.span_label(catchall
, "matches any value");
470 // Unreachable patterns in try and await expressions occur when one of
471 // the arms are an uninhabited type. Which is OK.
472 hir
::MatchSource
::AwaitDesugar
| hir
::MatchSource
::TryDesugar
=> {}
476 UsefulWithWitness(_
) => bug
!(),
480 if catchall
.is_none() && pat_is_catchall(hir_pat
) {
481 catchall
= Some(pat
.span
);
489 cx
: &mut MatchCheckCtxt
<'_
, 'tcx
>,
491 matrix
: &Matrix
<'_
, 'tcx
>,
493 ) -> Result
<(), Vec
<super::Pat
<'tcx
>>> {
494 let wild_pattern
= super::Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild }
;
495 match is_useful(cx
, matrix
, &PatStack
::from_pattern(&wild_pattern
), ConstructWitness
, hir_id
) {
496 NotUseful
=> Ok(()), // This is good, wildcard pattern isn't reachable.
497 UsefulWithWitness(pats
) => Err(if pats
.is_empty() {
500 pats
.into_iter().map(|w
| w
.single_pattern()).collect()
506 fn check_exhaustive
<'tcx
>(
507 cx
: &mut MatchCheckCtxt
<'_
, 'tcx
>,
510 matrix
: &Matrix
<'_
, 'tcx
>,
513 let witnesses
= match check_not_useful(cx
, scrut_ty
, matrix
, hir_id
) {
518 let joined_patterns
= joined_uncovered_patterns(&witnesses
);
519 let mut err
= create_e0004(
522 format
!("non-exhaustive patterns: {} not covered", joined_patterns
),
524 err
.span_label(sp
, pattern_not_covered_label(&witnesses
, &joined_patterns
));
525 adt_defined_here(cx
, &mut err
, scrut_ty
, &witnesses
);
527 "ensure that all possible cases are being handled, \
528 possibly by adding wildcards or more match arms",
533 fn joined_uncovered_patterns(witnesses
: &[super::Pat
<'_
>]) -> String
{
534 const LIMIT
: usize = 3;
537 [witness
] => format
!("`{}`", witness
),
538 [head @
.., tail
] if head
.len() < LIMIT
=> {
539 let head
: Vec
<_
> = head
.iter().map(<_
>::to_string
).collect();
540 format
!("`{}` and `{}`", head
.join("`, `"), tail
)
543 let (head
, tail
) = witnesses
.split_at(LIMIT
);
544 let head
: Vec
<_
> = head
.iter().map(<_
>::to_string
).collect();
545 format
!("`{}` and {} more", head
.join("`, `"), tail
.len())
550 fn pattern_not_covered_label(witnesses
: &[super::Pat
<'_
>], joined_patterns
: &str) -> String
{
551 format
!("pattern{} {} not covered", rustc_errors
::pluralise
!(witnesses
.len()), joined_patterns
)
554 /// Point at the definition of non-covered `enum` variants.
556 cx
: &MatchCheckCtxt
<'_
, '_
>,
557 err
: &mut DiagnosticBuilder
<'_
>,
559 witnesses
: &[super::Pat
<'_
>],
561 let ty
= ty
.peel_refs();
562 if let ty
::Adt(def
, _
) = ty
.kind
{
563 if let Some(sp
) = cx
.tcx
.hir().span_if_local(def
.did
) {
564 err
.span_label(sp
, format
!("`{}` defined here", ty
));
567 if witnesses
.len() < 4 {
568 for sp
in maybe_point_at_variant(ty
, &witnesses
) {
569 err
.span_label(sp
, "not covered");
575 fn maybe_point_at_variant(ty
: Ty
<'_
>, patterns
: &[super::Pat
<'_
>]) -> Vec
<Span
> {
576 let mut covered
= vec
![];
577 if let ty
::Adt(def
, _
) = ty
.kind
{
578 // Don't point at variants that have already been covered due to other patterns to avoid
580 for pattern
in patterns
{
581 use PatKind
::{AscribeUserType, Deref, Leaf, Or, Variant}
;
582 match &*pattern
.kind
{
583 AscribeUserType { subpattern, .. }
| Deref { subpattern }
=> {
584 covered
.extend(maybe_point_at_variant(ty
, slice
::from_ref(&subpattern
)));
586 Variant { adt_def, variant_index, subpatterns, .. }
if adt_def
.did
== def
.did
=> {
587 let sp
= def
.variants
[*variant_index
].ident
.span
;
588 if covered
.contains(&sp
) {
593 let pats
= subpatterns
595 .map(|field_pattern
| field_pattern
.pattern
.clone())
596 .collect
::<Box
<[_
]>>();
597 covered
.extend(maybe_point_at_variant(ty
, &pats
));
599 Leaf { subpatterns }
=> {
600 let pats
= subpatterns
602 .map(|field_pattern
| field_pattern
.pattern
.clone())
603 .collect
::<Box
<[_
]>>();
604 covered
.extend(maybe_point_at_variant(ty
, &pats
));
607 let pats
= pats
.iter().cloned().collect
::<Box
<[_
]>>();
608 covered
.extend(maybe_point_at_variant(ty
, &pats
));
617 // Check the legality of legality of by-move bindings.
618 fn check_legality_of_move_bindings(cx
: &mut MatchVisitor
<'_
, '_
>, has_guard
: bool
, pat
: &Pat
) {
619 let mut by_ref_span
= None
;
620 pat
.each_binding(|_
, hir_id
, span
, _
| {
621 if let Some(&bm
) = cx
.tables
.pat_binding_modes().get(hir_id
) {
622 if let ty
::BindByReference(..) = bm
{
623 by_ref_span
= Some(span
);
626 cx
.tcx
.sess
.delay_span_bug(pat
.span
, "missing binding mode");
630 let span_vec
= &mut Vec
::new();
631 let mut check_move
= |p
: &Pat
, sub
: Option
<&Pat
>| {
632 // Check legality of moving out of the enum.
634 // `x @ Foo(..)` is legal, but `x @ Foo(y)` isn't.
635 if sub
.map_or(false, |p
| p
.contains_bindings()) {
636 struct_span_err
!(cx
.tcx
.sess
, p
.span
, E0007
, "cannot bind by-move with sub-bindings")
637 .span_label(p
.span
, "binds an already bound by-move value by moving it")
639 } else if !has_guard
&& by_ref_span
.is_some() {
640 span_vec
.push(p
.span
);
645 if let hir
::PatKind
::Binding(.., sub
) = &p
.kind
{
646 if let Some(&bm
) = cx
.tables
.pat_binding_modes().get(p
.hir_id
) {
647 if let ty
::BindByValue(..) = bm
{
648 let pat_ty
= cx
.tables
.node_type(p
.hir_id
);
649 if !pat_ty
.is_copy_modulo_regions(cx
.tcx
, cx
.param_env
, pat
.span
) {
650 check_move(p
, sub
.as_deref());
654 cx
.tcx
.sess
.delay_span_bug(pat
.span
, "missing binding mode");
660 if !span_vec
.is_empty() {
661 let mut err
= struct_span_err
!(
663 MultiSpan
::from_spans(span_vec
.clone()),
665 "cannot bind by-move and by-ref in the same pattern",
667 if let Some(by_ref_span
) = by_ref_span
{
668 err
.span_label(by_ref_span
, "both by-ref and by-move used");
670 for span
in span_vec
.iter() {
671 err
.span_label(*span
, "by-move pattern here");
677 /// Forbids bindings in `@` patterns. This is necessary for memory safety,
678 /// because of the way rvalues are handled in the borrow check. (See issue
680 fn check_legality_of_bindings_in_at_patterns(cx
: &MatchVisitor
<'_
, '_
>, pat
: &Pat
) {
681 AtBindingPatternVisitor { cx, bindings_allowed: true }
.visit_pat(pat
);
684 struct AtBindingPatternVisitor
<'a
, 'b
, 'tcx
> {
685 cx
: &'a MatchVisitor
<'b
, 'tcx
>,
686 bindings_allowed
: bool
,
689 impl<'v
> Visitor
<'v
> for AtBindingPatternVisitor
<'_
, '_
, '_
> {
690 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'v
> {
691 NestedVisitorMap
::None
694 fn visit_pat(&mut self, pat
: &Pat
) {
696 hir
::PatKind
::Binding(.., ref subpat
) => {
697 if !self.bindings_allowed
{
702 "pattern bindings are not allowed after an `@`"
704 .span_label(pat
.span
, "not allowed after `@`")
708 if subpat
.is_some() {
709 let bindings_were_allowed
= self.bindings_allowed
;
710 self.bindings_allowed
= false;
711 intravisit
::walk_pat(self, pat
);
712 self.bindings_allowed
= bindings_were_allowed
;
715 _
=> intravisit
::walk_pat(self, pat
),