1
1
use rustc_session:: lint:: builtin:: NON_EXHAUSTIVE_OMITTED_PATTERNS ;
2
2
use rustc_span:: ErrorGuaranteed ;
3
3
4
+ use crate :: constructor:: Constructor ;
4
5
use crate :: errors:: { NonExhaustiveOmittedPattern , NonExhaustiveOmittedPatternLintOnArm , Uncovered } ;
5
- use crate :: pat:: PatOrWild ;
6
- use crate :: rustc:: {
7
- Constructor , DeconstructedPat , MatchArm , MatchCtxt , PlaceCtxt , RevealedTy , RustcMatchCheckCtxt ,
8
- SplitConstructorSet , WitnessPat ,
9
- } ;
10
-
11
- /// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
12
- /// inspect the same subvalue/place".
13
- /// This is used to traverse patterns column-by-column for lints. Despite similarities with the
14
- /// algorithm in [`crate::usefulness`], this does a different traversal. Notably this is linear in
15
- /// the depth of patterns, whereas `compute_exhaustiveness_and_usefulness` is worst-case exponential
16
- /// (exhaustiveness is NP-complete). The core difference is that we treat sub-columns separately.
17
- ///
18
- /// This must not contain an or-pattern. `expand_and_push` takes care to expand them.
19
- ///
20
- /// This is not used in the usefulness algorithm; only in lints.
21
- #[ derive( Debug ) ]
22
- pub ( crate ) struct PatternColumn < ' p , ' tcx > {
23
- patterns : Vec < & ' p DeconstructedPat < ' p , ' tcx > > ,
24
- }
25
-
26
- impl < ' p , ' tcx > PatternColumn < ' p , ' tcx > {
27
- pub ( crate ) fn new ( arms : & [ MatchArm < ' p , ' tcx > ] ) -> Self {
28
- let patterns = Vec :: with_capacity ( arms. len ( ) ) ;
29
- let mut column = PatternColumn { patterns } ;
30
- for arm in arms {
31
- column. expand_and_push ( PatOrWild :: Pat ( arm. pat ) ) ;
32
- }
33
- column
34
- }
35
- /// Pushes a pattern onto the column, expanding any or-patterns into its subpatterns.
36
- /// Internal method, prefer [`PatternColumn::new`].
37
- fn expand_and_push ( & mut self , pat : PatOrWild < ' p , RustcMatchCheckCtxt < ' p , ' tcx > > ) {
38
- // We flatten or-patterns and skip algorithm-generated wildcards.
39
- if pat. is_or_pat ( ) {
40
- self . patterns . extend (
41
- pat. flatten_or_pat ( ) . into_iter ( ) . filter_map ( |pat_or_wild| pat_or_wild. as_pat ( ) ) ,
42
- )
43
- } else if let Some ( pat) = pat. as_pat ( ) {
44
- self . patterns . push ( pat)
45
- }
46
- }
47
-
48
- fn head_ty ( & self ) -> Option < RevealedTy < ' tcx > > {
49
- self . patterns . first ( ) . map ( |pat| * pat. ty ( ) )
50
- }
51
-
52
- /// Do constructor splitting on the constructors of the column.
53
- fn analyze_ctors (
54
- & self ,
55
- pcx : & PlaceCtxt < ' _ , ' p , ' tcx > ,
56
- ) -> Result < SplitConstructorSet < ' p , ' tcx > , ErrorGuaranteed > {
57
- let column_ctors = self . patterns . iter ( ) . map ( |p| p. ctor ( ) ) ;
58
- let ctors_for_ty = & pcx. ctors_for_ty ( ) ?;
59
- Ok ( ctors_for_ty. split ( column_ctors) )
60
- }
61
-
62
- /// Does specialization: given a constructor, this takes the patterns from the column that match
63
- /// the constructor, and outputs their fields.
64
- /// This returns one column per field of the constructor. They usually all have the same length
65
- /// (the number of patterns in `self` that matched `ctor`), except that we expand or-patterns
66
- /// which may change the lengths.
67
- fn specialize (
68
- & self ,
69
- pcx : & PlaceCtxt < ' _ , ' p , ' tcx > ,
70
- ctor : & Constructor < ' p , ' tcx > ,
71
- ) -> Vec < PatternColumn < ' p , ' tcx > > {
72
- let arity = ctor. arity ( pcx) ;
73
- if arity == 0 {
74
- return Vec :: new ( ) ;
75
- }
76
-
77
- // We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These
78
- // columns may have different lengths in the presence of or-patterns (this is why we can't
79
- // reuse `Matrix`).
80
- let mut specialized_columns: Vec < _ > =
81
- ( 0 ..arity) . map ( |_| Self { patterns : Vec :: new ( ) } ) . collect ( ) ;
82
- let relevant_patterns =
83
- self . patterns . iter ( ) . filter ( |pat| ctor. is_covered_by ( pcx, pat. ctor ( ) ) ) ;
84
- for pat in relevant_patterns {
85
- let specialized = pat. specialize ( ctor, arity) ;
86
- for ( subpat, column) in specialized. into_iter ( ) . zip ( & mut specialized_columns) {
87
- column. expand_and_push ( subpat) ;
88
- }
89
- }
90
- specialized_columns
91
- }
92
- }
6
+ use crate :: pat_column:: PatternColumn ;
7
+ use crate :: rustc:: { RevealedTy , RustcMatchCheckCtxt , WitnessPat } ;
8
+ use crate :: MatchArm ;
93
9
94
10
/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned
95
11
/// in a given column.
96
12
#[ instrument( level = "debug" , skip( cx) , ret) ]
97
13
fn collect_nonexhaustive_missing_variants < ' a , ' p , ' tcx > (
98
- cx : MatchCtxt < ' a , ' p , ' tcx > ,
99
- column : & PatternColumn < ' p , ' tcx > ,
14
+ cx : & RustcMatchCheckCtxt < ' p , ' tcx > ,
15
+ column : & PatternColumn < ' p , RustcMatchCheckCtxt < ' p , ' tcx > > ,
100
16
) -> Result < Vec < WitnessPat < ' p , ' tcx > > , ErrorGuaranteed > {
101
- let Some ( ty) = column. head_ty ( ) else {
17
+ let Some ( & ty) = column. head_ty ( ) else {
102
18
return Ok ( Vec :: new ( ) ) ;
103
19
} ;
104
- let pcx = & PlaceCtxt :: new_dummy ( cx, & ty) ;
105
20
106
- let set = column. analyze_ctors ( pcx ) ?;
21
+ let set = column. analyze_ctors ( cx , & ty ) ?;
107
22
if set. present . is_empty ( ) {
108
23
// We can't consistently handle the case where no constructors are present (since this would
109
24
// require digging deep through any type in case there's a non_exhaustive enum somewhere),
@@ -112,20 +27,20 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
112
27
}
113
28
114
29
let mut witnesses = Vec :: new ( ) ;
115
- if cx. tycx . is_foreign_non_exhaustive_enum ( ty) {
30
+ if cx. is_foreign_non_exhaustive_enum ( ty) {
116
31
witnesses. extend (
117
32
set. missing
118
33
. into_iter ( )
119
34
// This will list missing visible variants.
120
35
. filter ( |c| !matches ! ( c, Constructor :: Hidden | Constructor :: NonExhaustive ) )
121
- . map ( |missing_ctor| WitnessPat :: wild_from_ctor ( pcx , missing_ctor) ) ,
36
+ . map ( |missing_ctor| WitnessPat :: wild_from_ctor ( cx , missing_ctor, ty ) ) ,
122
37
)
123
38
}
124
39
125
40
// Recurse into the fields.
126
41
for ctor in set. present {
127
- let specialized_columns = column. specialize ( pcx , & ctor) ;
128
- let wild_pat = WitnessPat :: wild_from_ctor ( pcx , ctor) ;
42
+ let specialized_columns = column. specialize ( cx , & ty , & ctor) ;
43
+ let wild_pat = WitnessPat :: wild_from_ctor ( cx , ctor, ty ) ;
129
44
for ( i, col_i) in specialized_columns. iter ( ) . enumerate ( ) {
130
45
// Compute witnesses for each column.
131
46
let wits_for_col_i = collect_nonexhaustive_missing_variants ( cx, col_i) ?;
@@ -141,18 +56,17 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
141
56
Ok ( witnesses)
142
57
}
143
58
144
- pub ( crate ) fn lint_nonexhaustive_missing_variants < ' a , ' p , ' tcx > (
145
- cx : MatchCtxt < ' a , ' p , ' tcx > ,
146
- arms : & [ MatchArm < ' p , ' tcx > ] ,
147
- pat_column : & PatternColumn < ' p , ' tcx > ,
59
+ pub ( crate ) fn lint_nonexhaustive_missing_variants < ' p , ' tcx > (
60
+ rcx : & RustcMatchCheckCtxt < ' p , ' tcx > ,
61
+ arms : & [ MatchArm < ' p , RustcMatchCheckCtxt < ' p , ' tcx > > ] ,
62
+ pat_column : & PatternColumn < ' p , RustcMatchCheckCtxt < ' p , ' tcx > > ,
148
63
scrut_ty : RevealedTy < ' tcx > ,
149
64
) -> Result < ( ) , ErrorGuaranteed > {
150
- let rcx: & RustcMatchCheckCtxt < ' _ , ' _ > = cx. tycx ;
151
65
if !matches ! (
152
66
rcx. tcx. lint_level_at_node( NON_EXHAUSTIVE_OMITTED_PATTERNS , rcx. match_lint_level) . 0 ,
153
67
rustc_session:: lint:: Level :: Allow
154
68
) {
155
- let witnesses = collect_nonexhaustive_missing_variants ( cx , pat_column) ?;
69
+ let witnesses = collect_nonexhaustive_missing_variants ( rcx , pat_column) ?;
156
70
if !witnesses. is_empty ( ) {
157
71
// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
158
72
// is not exhaustive enough.
0 commit comments