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}
;
23 use rustc_error_codes
::*;
25 crate fn check_match(tcx
: TyCtxt
<'_
>, def_id
: DefId
) {
26 let body_id
= match tcx
.hir().as_local_hir_id(def_id
) {
28 Some(id
) => tcx
.hir().body_owned_by(id
),
31 let mut visitor
= MatchVisitor
{
33 tables
: tcx
.body_tables(body_id
),
34 param_env
: tcx
.param_env(def_id
),
35 identity_substs
: InternalSubsts
::identity_for_item(tcx
, def_id
),
37 visitor
.visit_body(tcx
.hir().body(body_id
));
40 fn create_e0004(sess
: &Session
, sp
: Span
, error_message
: String
) -> DiagnosticBuilder
<'_
> {
41 struct_span_err
!(sess
, sp
, E0004
, "{}", &error_message
)
44 struct MatchVisitor
<'a
, 'tcx
> {
46 tables
: &'a ty
::TypeckTables
<'tcx
>,
47 param_env
: ty
::ParamEnv
<'tcx
>,
48 identity_substs
: SubstsRef
<'tcx
>,
51 impl<'tcx
> Visitor
<'tcx
> for MatchVisitor
<'_
, 'tcx
> {
52 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
53 NestedVisitorMap
::None
56 fn visit_expr(&mut self, ex
: &'tcx hir
::Expr
) {
57 intravisit
::walk_expr(self, ex
);
59 if let hir
::ExprKind
::Match(ref scrut
, ref arms
, source
) = ex
.kind
{
60 self.check_match(scrut
, arms
, source
);
64 fn visit_local(&mut self, loc
: &'tcx hir
::Local
) {
65 intravisit
::walk_local(self, loc
);
67 let (msg
, sp
) = match loc
.source
{
68 hir
::LocalSource
::Normal
=> ("local binding", Some(loc
.span
)),
69 hir
::LocalSource
::ForLoopDesugar
=> ("`for` loop binding", None
),
70 hir
::LocalSource
::AsyncFn
=> ("async fn binding", None
),
71 hir
::LocalSource
::AwaitDesugar
=> ("`await` future binding", None
),
73 self.check_irrefutable(&loc
.pat
, msg
, sp
);
75 // Check legality of move bindings and `@` patterns.
76 self.check_patterns(false, &loc
.pat
);
79 fn visit_body(&mut self, body
: &'tcx hir
::Body
) {
80 intravisit
::walk_body(self, body
);
82 for param
in &body
.params
{
83 self.check_irrefutable(¶m
.pat
, "function argument", None
);
84 self.check_patterns(false, ¶m
.pat
);
89 impl PatCtxt
<'_
, '_
> {
90 fn report_inlining_errors(&self, pat_span
: Span
) {
91 for error
in &self.errors
{
93 PatternError
::StaticInPattern(span
) => {
94 self.span_e0158(span
, "statics cannot be referenced in patterns")
96 PatternError
::AssocConstInPattern(span
) => {
97 self.span_e0158(span
, "associated consts cannot be referenced in patterns")
99 PatternError
::FloatBug
=> {
100 // FIXME(#31407) this is only necessary because float parsing is buggy
101 ::rustc
::mir
::interpret
::struct_error(
102 self.tcx
.at(pat_span
),
103 "could not evaluate float literal (see issue #31407)",
107 PatternError
::NonConstPath(span
) => {
108 ::rustc
::mir
::interpret
::struct_error(
110 "runtime values cannot be referenced in patterns",
118 fn span_e0158(&self, span
: Span
, text
: &str) {
119 span_err
!(self.tcx
.sess
, span
, E0158
, "{}", text
)
123 impl<'tcx
> MatchVisitor
<'_
, 'tcx
> {
124 fn check_patterns(&mut self, has_guard
: bool
, pat
: &Pat
) {
125 check_legality_of_move_bindings(self, has_guard
, pat
);
126 check_legality_of_bindings_in_at_patterns(self, pat
);
129 fn check_match(&mut self, scrut
: &hir
::Expr
, arms
: &'tcx
[hir
::Arm
], source
: hir
::MatchSource
) {
131 // First, check legality of move bindings.
132 self.check_patterns(arm
.guard
.is_some(), &arm
.pat
);
134 // Second, perform some lints.
135 check_for_bindings_named_same_as_variants(self, &arm
.pat
);
138 let module
= self.tcx
.hir().get_module_parent(scrut
.hir_id
);
139 MatchCheckCtxt
::create_and_enter(self.tcx
, self.param_env
, module
, |ref mut cx
| {
140 let mut have_errors
= false;
142 let inlined_arms
: Vec
<_
> = arms
145 let mut patcx
= PatCtxt
::new(
147 self.param_env
.and(self.identity_substs
),
150 patcx
.include_lint_checks();
151 let pattern
= patcx
.lower_pattern(&arm
.pat
);
152 let pattern
: &_
= cx
.pattern_arena
.alloc(expand_pattern(cx
, pattern
));
153 if !patcx
.errors
.is_empty() {
154 patcx
.report_inlining_errors(arm
.pat
.span
);
157 (pattern
, &*arm
.pat
, arm
.guard
.is_some())
161 // Bail out early if inlining failed.
166 // Fourth, check for unreachable arms.
167 let matrix
= check_arms(cx
, &inlined_arms
, source
);
169 // Fifth, check if the match is exhaustive.
170 let scrut_ty
= self.tables
.node_type(scrut
.hir_id
);
171 // Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
172 // since an empty matrix can occur when there are arms, if those arms all have guards.
173 let is_empty_match
= inlined_arms
.is_empty();
174 check_exhaustive(cx
, scrut_ty
, scrut
.span
, &matrix
, scrut
.hir_id
, is_empty_match
);
178 fn check_irrefutable(&self, pat
: &'tcx Pat
, origin
: &str, sp
: Option
<Span
>) {
179 let module
= self.tcx
.hir().get_module_parent(pat
.hir_id
);
180 MatchCheckCtxt
::create_and_enter(self.tcx
, self.param_env
, module
, |ref mut cx
| {
182 PatCtxt
::new(self.tcx
, self.param_env
.and(self.identity_substs
), self.tables
);
183 patcx
.include_lint_checks();
184 let pattern
= patcx
.lower_pattern(pat
);
185 let pattern_ty
= pattern
.ty
;
186 let pattern
= cx
.pattern_arena
.alloc(expand_pattern(cx
, pattern
));
187 let pats
: Matrix
<'_
, '_
> = vec
![PatStack
::from_pattern(pattern
)].into_iter().collect();
189 let witnesses
= match check_not_useful(cx
, pattern_ty
, &pats
, pat
.hir_id
) {
194 let joined_patterns
= joined_uncovered_patterns(&witnesses
);
195 let mut err
= struct_span_err
!(
199 "refutable pattern in {}: {} not covered",
203 let suggest_if_let
= match &pat
.kind
{
204 hir
::PatKind
::Path(hir
::QPath
::Resolved(None
, path
))
205 if path
.segments
.len() == 1 && path
.segments
[0].args
.is_none() =>
207 const_not_var(&mut err
, cx
.tcx
, pat
, path
);
213 pattern_not_covered_label(&witnesses
, &joined_patterns
),
219 if let (Some(span
), true) = (sp
, suggest_if_let
) {
221 "`let` bindings require an \"irrefutable pattern\", like a `struct` or \
222 an `enum` with only one variant",
224 if let Ok(snippet
) = self.tcx
.sess
.source_map().span_to_snippet(span
) {
227 "you might want to use `if let` to ignore the variant that isn't matched",
228 format
!("if {} {{ /* */ }}", &snippet
[..snippet
.len() - 1]),
229 Applicability
::HasPlaceholders
,
233 "for more information, visit \
234 https://doc.rust-lang.org/book/ch18-02-refutability.html",
238 adt_defined_here(cx
, &mut err
, pattern_ty
, &witnesses
);
244 /// A path pattern was interpreted as a constant, not a new variable.
245 /// This caused an irrefutable match failure in e.g. `let`.
246 fn const_not_var(err
: &mut DiagnosticBuilder
<'_
>, tcx
: TyCtxt
<'_
>, pat
: &Pat
, path
: &hir
::Path
) {
247 let descr
= path
.res
.descr();
250 format
!("interpreted as {} {} pattern, not a new variable", path
.res
.article(), descr
,),
255 "introduce a variable instead",
256 format
!("{}_var", path
.segments
[0].ident
).to_lowercase(),
257 // Cannot use `MachineApplicable` as it's not really *always* correct
258 // because there may be such an identifier in scope or the user maybe
259 // really wanted to match against the constant. This is quite unlikely however.
260 Applicability
::MaybeIncorrect
,
263 if let Some(span
) = tcx
.hir().res_span(path
.res
) {
264 err
.span_label(span
, format
!("{} defined here", descr
));
268 fn check_for_bindings_named_same_as_variants(cx
: &MatchVisitor
<'_
, '_
>, pat
: &Pat
) {
270 if let hir
::PatKind
::Binding(_
, _
, ident
, None
) = p
.kind
{
271 if let Some(&bm
) = cx
.tables
.pat_binding_modes().get(p
.hir_id
) {
272 if bm
!= ty
::BindByValue(hir
::Mutability
::Immutable
) {
276 let pat_ty
= cx
.tables
.pat_ty(p
);
277 if let ty
::Adt(edef
, _
) = pat_ty
.kind
{
279 && edef
.variants
.iter().any(|variant
| {
280 variant
.ident
== ident
&& variant
.ctor_kind
== CtorKind
::Const
283 let ty_path
= cx
.tcx
.def_path_str(edef
.did
);
284 let mut err
= struct_span_warn
!(
288 "pattern binding `{}` is named the same as one \
289 of the variants of the type `{}`",
295 "to match on the variant, qualify the path",
296 format
!("{}::{}", ty_path
, ident
),
297 Applicability
::MachineApplicable
,
303 cx
.tcx
.sess
.delay_span_bug(p
.span
, "missing binding mode");
310 /// Checks for common cases of "catchall" patterns that may not be intended as such.
311 fn pat_is_catchall(pat
: &Pat
) -> bool
{
313 hir
::PatKind
::Binding(.., None
) => true,
314 hir
::PatKind
::Binding(.., Some(ref s
)) => pat_is_catchall(s
),
315 hir
::PatKind
::Ref(ref s
, _
) => pat_is_catchall(s
),
316 hir
::PatKind
::Tuple(ref v
, _
) => v
.iter().all(|p
| pat_is_catchall(&p
)),
321 // Check for unreachable patterns
322 fn check_arms
<'p
, 'tcx
>(
323 cx
: &mut MatchCheckCtxt
<'p
, 'tcx
>,
324 arms
: &[(&'p
super::Pat
<'tcx
>, &hir
::Pat
, bool
)],
325 source
: hir
::MatchSource
,
326 ) -> Matrix
<'p
, 'tcx
> {
327 let mut seen
= Matrix
::empty();
328 let mut catchall
= None
;
329 for (arm_index
, (pat
, hir_pat
, has_guard
)) in arms
.iter().enumerate() {
330 let v
= PatStack
::from_pattern(pat
);
332 match is_useful(cx
, &seen
, &v
, LeaveOutWitness
, hir_pat
.hir_id
, true) {
335 hir
::MatchSource
::IfDesugar { .. }
| hir
::MatchSource
::WhileDesugar
=> bug
!(),
337 hir
::MatchSource
::IfLetDesugar { .. }
| hir
::MatchSource
::WhileLetDesugar
=> {
338 // check which arm we're on.
340 // The arm with the user-specified pattern.
343 lint
::builtin
::UNREACHABLE_PATTERNS
,
346 "unreachable pattern",
349 // The arm with the wildcard pattern.
351 let msg
= match source
{
352 hir
::MatchSource
::IfLetDesugar { .. }
=> {
353 "irrefutable if-let pattern"
355 hir
::MatchSource
::WhileLetDesugar
=> {
356 "irrefutable while-let pattern"
361 lint
::builtin
::IRREFUTABLE_LET_PATTERNS
,
371 hir
::MatchSource
::ForLoopDesugar
| hir
::MatchSource
::Normal
=> {
372 let mut err
= cx
.tcx
.struct_span_lint_hir(
373 lint
::builtin
::UNREACHABLE_PATTERNS
,
376 "unreachable pattern",
378 // if we had a catchall pattern, hint at that
379 if let Some(catchall
) = catchall
{
380 err
.span_label(pat
.span
, "unreachable pattern");
381 err
.span_label(catchall
, "matches any value");
386 // Unreachable patterns in try and await expressions occur when one of
387 // the arms are an uninhabited type. Which is OK.
388 hir
::MatchSource
::AwaitDesugar
| hir
::MatchSource
::TryDesugar
=> {}
391 Useful(unreachable_subpatterns
) => {
392 for pat
in unreachable_subpatterns
{
394 lint
::builtin
::UNREACHABLE_PATTERNS
,
397 "unreachable pattern",
401 UsefulWithWitness(_
) => bug
!(),
405 if catchall
.is_none() && pat_is_catchall(hir_pat
) {
406 catchall
= Some(pat
.span
);
413 fn check_not_useful
<'p
, 'tcx
>(
414 cx
: &mut MatchCheckCtxt
<'p
, 'tcx
>,
416 matrix
: &Matrix
<'p
, 'tcx
>,
418 ) -> Result
<(), Vec
<super::Pat
<'tcx
>>> {
419 let wild_pattern
= cx
.pattern_arena
.alloc(super::Pat
::wildcard_from_ty(ty
));
420 let v
= PatStack
::from_pattern(wild_pattern
);
421 match is_useful(cx
, matrix
, &v
, ConstructWitness
, hir_id
, true) {
422 NotUseful
=> Ok(()), // This is good, wildcard pattern isn't reachable.
423 UsefulWithWitness(pats
) => Err(if pats
.is_empty() {
424 bug
!("Exhaustiveness check returned no witnesses")
426 pats
.into_iter().map(|w
| w
.single_pattern()).collect()
432 fn check_exhaustive
<'p
, 'tcx
>(
433 cx
: &mut MatchCheckCtxt
<'p
, 'tcx
>,
436 matrix
: &Matrix
<'p
, 'tcx
>,
438 is_empty_match
: bool
,
440 // In the absence of the `exhaustive_patterns` feature, empty matches are not detected by
441 // `is_useful` to exhaustively match uninhabited types, so we manually check here.
442 if is_empty_match
&& !cx
.tcx
.features().exhaustive_patterns
{
443 let scrutinee_is_visibly_uninhabited
= match scrut_ty
.kind
{
447 && def
.variants
.is_empty()
448 && !cx
.is_foreign_non_exhaustive_enum(scrut_ty
)
452 if scrutinee_is_visibly_uninhabited
{
453 // If the type *is* uninhabited, an empty match is vacuously exhaustive.
458 let witnesses
= match check_not_useful(cx
, scrut_ty
, matrix
, hir_id
) {
463 let non_empty_enum
= match scrut_ty
.kind
{
464 ty
::Adt(def
, _
) => def
.is_enum() && !def
.variants
.is_empty(),
467 // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
470 if is_empty_match
&& !non_empty_enum
{
474 format
!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty
),
477 let joined_patterns
= joined_uncovered_patterns(&witnesses
);
481 format
!("non-exhaustive patterns: {} not covered", joined_patterns
),
483 err
.span_label(sp
, pattern_not_covered_label(&witnesses
, &joined_patterns
));
486 adt_defined_here(cx
, &mut err
, scrut_ty
, &witnesses
);
488 "ensure that all possible cases are being handled, \
489 possibly by adding wildcards or more match arms",
494 fn joined_uncovered_patterns(witnesses
: &[super::Pat
<'_
>]) -> String
{
495 const LIMIT
: usize = 3;
498 [witness
] => format
!("`{}`", witness
),
499 [head @
.., tail
] if head
.len() < LIMIT
=> {
500 let head
: Vec
<_
> = head
.iter().map(<_
>::to_string
).collect();
501 format
!("`{}` and `{}`", head
.join("`, `"), tail
)
504 let (head
, tail
) = witnesses
.split_at(LIMIT
);
505 let head
: Vec
<_
> = head
.iter().map(<_
>::to_string
).collect();
506 format
!("`{}` and {} more", head
.join("`, `"), tail
.len())
511 fn pattern_not_covered_label(witnesses
: &[super::Pat
<'_
>], joined_patterns
: &str) -> String
{
512 format
!("pattern{} {} not covered", rustc_errors
::pluralize
!(witnesses
.len()), joined_patterns
)
515 /// Point at the definition of non-covered `enum` variants.
517 cx
: &MatchCheckCtxt
<'_
, '_
>,
518 err
: &mut DiagnosticBuilder
<'_
>,
520 witnesses
: &[super::Pat
<'_
>],
522 let ty
= ty
.peel_refs();
523 if let ty
::Adt(def
, _
) = ty
.kind
{
524 if let Some(sp
) = cx
.tcx
.hir().span_if_local(def
.did
) {
525 err
.span_label(sp
, format
!("`{}` defined here", ty
));
528 if witnesses
.len() < 4 {
529 for sp
in maybe_point_at_variant(ty
, &witnesses
) {
530 err
.span_label(sp
, "not covered");
536 fn maybe_point_at_variant(ty
: Ty
<'_
>, patterns
: &[super::Pat
<'_
>]) -> Vec
<Span
> {
537 let mut covered
= vec
![];
538 if let ty
::Adt(def
, _
) = ty
.kind
{
539 // Don't point at variants that have already been covered due to other patterns to avoid
541 for pattern
in patterns
{
542 use PatKind
::{AscribeUserType, Deref, Leaf, Or, Variant}
;
543 match &*pattern
.kind
{
544 AscribeUserType { subpattern, .. }
| Deref { subpattern }
=> {
545 covered
.extend(maybe_point_at_variant(ty
, slice
::from_ref(&subpattern
)));
547 Variant { adt_def, variant_index, subpatterns, .. }
if adt_def
.did
== def
.did
=> {
548 let sp
= def
.variants
[*variant_index
].ident
.span
;
549 if covered
.contains(&sp
) {
554 let pats
= subpatterns
556 .map(|field_pattern
| field_pattern
.pattern
.clone())
557 .collect
::<Box
<[_
]>>();
558 covered
.extend(maybe_point_at_variant(ty
, &pats
));
560 Leaf { subpatterns }
=> {
561 let pats
= subpatterns
563 .map(|field_pattern
| field_pattern
.pattern
.clone())
564 .collect
::<Box
<[_
]>>();
565 covered
.extend(maybe_point_at_variant(ty
, &pats
));
568 let pats
= pats
.iter().cloned().collect
::<Box
<[_
]>>();
569 covered
.extend(maybe_point_at_variant(ty
, &pats
));
578 // Check the legality of legality of by-move bindings.
579 fn check_legality_of_move_bindings(cx
: &mut MatchVisitor
<'_
, '_
>, has_guard
: bool
, pat
: &Pat
) {
580 let mut by_ref_span
= None
;
581 pat
.each_binding(|_
, hir_id
, span
, _
| {
582 if let Some(&bm
) = cx
.tables
.pat_binding_modes().get(hir_id
) {
583 if let ty
::BindByReference(..) = bm
{
584 by_ref_span
= Some(span
);
587 cx
.tcx
.sess
.delay_span_bug(pat
.span
, "missing binding mode");
591 let span_vec
= &mut Vec
::new();
592 let mut check_move
= |p
: &Pat
, sub
: Option
<&Pat
>| {
593 // Check legality of moving out of the enum.
595 // `x @ Foo(..)` is legal, but `x @ Foo(y)` isn't.
596 if sub
.map_or(false, |p
| p
.contains_bindings()) {
597 struct_span_err
!(cx
.tcx
.sess
, p
.span
, E0007
, "cannot bind by-move with sub-bindings")
598 .span_label(p
.span
, "binds an already bound by-move value by moving it")
600 } else if !has_guard
&& by_ref_span
.is_some() {
601 span_vec
.push(p
.span
);
606 if let hir
::PatKind
::Binding(.., sub
) = &p
.kind
{
607 if let Some(&bm
) = cx
.tables
.pat_binding_modes().get(p
.hir_id
) {
608 if let ty
::BindByValue(..) = bm
{
609 let pat_ty
= cx
.tables
.node_type(p
.hir_id
);
610 if !pat_ty
.is_copy_modulo_regions(cx
.tcx
, cx
.param_env
, pat
.span
) {
611 check_move(p
, sub
.as_deref());
615 cx
.tcx
.sess
.delay_span_bug(pat
.span
, "missing binding mode");
621 if !span_vec
.is_empty() {
622 let mut err
= struct_span_err
!(
624 MultiSpan
::from_spans(span_vec
.clone()),
626 "cannot bind by-move and by-ref in the same pattern",
628 if let Some(by_ref_span
) = by_ref_span
{
629 err
.span_label(by_ref_span
, "both by-ref and by-move used");
631 for span
in span_vec
.iter() {
632 err
.span_label(*span
, "by-move pattern here");
638 /// Forbids bindings in `@` patterns. This is necessary for memory safety,
639 /// because of the way rvalues are handled in the borrow check. (See issue
641 fn check_legality_of_bindings_in_at_patterns(cx
: &MatchVisitor
<'_
, '_
>, pat
: &Pat
) {
642 AtBindingPatternVisitor { cx, bindings_allowed: true }
.visit_pat(pat
);
645 struct AtBindingPatternVisitor
<'a
, 'b
, 'tcx
> {
646 cx
: &'a MatchVisitor
<'b
, 'tcx
>,
647 bindings_allowed
: bool
,
650 impl<'v
> Visitor
<'v
> for AtBindingPatternVisitor
<'_
, '_
, '_
> {
651 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'v
> {
652 NestedVisitorMap
::None
655 fn visit_pat(&mut self, pat
: &Pat
) {
657 hir
::PatKind
::Binding(.., ref subpat
) => {
658 if !self.bindings_allowed
{
663 "pattern bindings are not allowed after an `@`"
665 .span_label(pat
.span
, "not allowed after `@`")
669 if subpat
.is_some() {
670 let bindings_were_allowed
= self.bindings_allowed
;
671 self.bindings_allowed
= false;
672 intravisit
::walk_pat(self, pat
);
673 self.bindings_allowed
= bindings_were_allowed
;
676 _
=> intravisit
::walk_pat(self, pat
),