11use rustc_session:: lint:: builtin:: NON_EXHAUSTIVE_OMITTED_PATTERNS ;
22use rustc_span:: ErrorGuaranteed ;
33
4+ use crate :: constructor:: Constructor ;
45use 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 ;
939
9410/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned
9511/// in a given column.
9612#[ instrument( level = "debug" , skip( cx) , ret) ]
9713fn 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 > > ,
10016) -> Result < Vec < WitnessPat < ' p , ' tcx > > , ErrorGuaranteed > {
101- let Some ( ty) = column. head_ty ( ) else {
17+ let Some ( & ty) = column. head_ty ( ) else {
10218 return Ok ( Vec :: new ( ) ) ;
10319 } ;
104- let pcx = & PlaceCtxt :: new_dummy ( cx, & ty) ;
10520
106- let set = column. analyze_ctors ( pcx ) ?;
21+ let set = column. analyze_ctors ( cx , & ty ) ?;
10722 if set. present . is_empty ( ) {
10823 // We can't consistently handle the case where no constructors are present (since this would
10924 // 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>(
11227 }
11328
11429 let mut witnesses = Vec :: new ( ) ;
115- if cx. tycx . is_foreign_non_exhaustive_enum ( ty) {
30+ if cx. is_foreign_non_exhaustive_enum ( ty) {
11631 witnesses. extend (
11732 set. missing
11833 . into_iter ( )
11934 // This will list missing visible variants.
12035 . 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 ) ) ,
12237 )
12338 }
12439
12540 // Recurse into the fields.
12641 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 ) ;
12944 for ( i, col_i) in specialized_columns. iter ( ) . enumerate ( ) {
13045 // Compute witnesses for each column.
13146 let wits_for_col_i = collect_nonexhaustive_missing_variants ( cx, col_i) ?;
@@ -141,18 +56,17 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
14156 Ok ( witnesses)
14257}
14358
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 > > ,
14863 scrut_ty : RevealedTy < ' tcx > ,
14964) -> Result < ( ) , ErrorGuaranteed > {
150- let rcx: & RustcMatchCheckCtxt < ' _ , ' _ > = cx. tycx ;
15165 if !matches ! (
15266 rcx. tcx. lint_level_at_node( NON_EXHAUSTIVE_OMITTED_PATTERNS , rcx. match_lint_level) . 0 ,
15367 rustc_session:: lint:: Level :: Allow
15468 ) {
155- let witnesses = collect_nonexhaustive_missing_variants ( cx , pat_column) ?;
69+ let witnesses = collect_nonexhaustive_missing_variants ( rcx , pat_column) ?;
15670 if !witnesses. is_empty ( ) {
15771 // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
15872 // is not exhaustive enough.
0 commit comments