1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
2
use clippy_utils:: source:: snippet;
3
3
use clippy_utils:: { path_to_local, search_same, SpanlessEq , SpanlessHash } ;
4
+ use core:: cmp:: Ordering ;
4
5
use core:: iter;
6
+ use core:: slice;
5
7
use rustc_arena:: DroplessArena ;
6
8
use rustc_ast:: ast:: LitKind ;
7
9
use rustc_errors:: Applicability ;
@@ -36,7 +38,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
36
38
normalized_pats[ i + 1 ..]
37
39
. iter ( )
38
40
. enumerate ( )
39
- . find_map ( |( j, other) | pat. can_also_match ( other) . then ( || i + 1 + j) )
41
+ . find_map ( |( j, other) | pat. has_overlapping_values ( other) . then ( || i + 1 + j) )
40
42
. unwrap_or ( normalized_pats. len ( ) )
41
43
} )
42
44
. collect ( ) ;
@@ -52,7 +54,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
52
54
. rev ( )
53
55
. zip ( forwards_blocking_idxs[ ..i] . iter ( ) . copied ( ) . rev ( ) )
54
56
. skip_while ( |& ( _, forward_block) | forward_block > i)
55
- . find_map ( |( ( j, other) , forward_block) | ( forward_block == i || pat. can_also_match ( other) ) . then ( || j) )
57
+ . find_map ( |( ( j, other) , forward_block) | {
58
+ ( forward_block == i || pat. has_overlapping_values ( other) ) . then ( || j)
59
+ } )
56
60
. unwrap_or ( 0 )
57
61
} )
58
62
. collect ( ) ;
@@ -159,6 +163,10 @@ enum NormalizedPat<'a> {
159
163
LitInt ( u128 ) ,
160
164
LitBool ( bool ) ,
161
165
Range ( PatRange ) ,
166
+ /// A slice pattern. If the second value is `None`, then this matches an exact size. Otherwise
167
+ /// the first value contains everything before the `..` wildcard pattern, and the second value
168
+ /// contains everything afterwards. Note that either side, or both sides, may contain zero
169
+ /// patterns.
162
170
Slice ( & ' a [ Self ] , Option < & ' a [ Self ] > ) ,
163
171
}
164
172
@@ -178,23 +186,43 @@ impl PatRange {
178
186
}
179
187
180
188
fn overlaps ( & self , other : & Self ) -> bool {
181
- ! ( self . is_empty ( ) || other . is_empty ( ) )
182
- && match self . bounds {
183
- RangeEnd :: Included => self . end >= other . start ,
184
- RangeEnd :: Excluded => self . end > other. start ,
185
- }
186
- && match other. bounds {
187
- RangeEnd :: Included => self . start <= other. end ,
188
- RangeEnd :: Excluded => self . start < other. end ,
189
- }
189
+ // Note: Empty ranges are impossible, so this is correct even though it would return true if an
190
+ // empty exclusive range were to reside within an inclusive range.
191
+ ( match self . bounds {
192
+ RangeEnd :: Included => self . end >= other. start ,
193
+ RangeEnd :: Excluded => self . end > other . start ,
194
+ } && match other. bounds {
195
+ RangeEnd :: Included => self . start <= other. end ,
196
+ RangeEnd :: Excluded => self . start < other. end ,
197
+ } )
190
198
}
199
+ }
191
200
192
- fn is_empty ( & self ) -> bool {
193
- match self . bounds {
194
- RangeEnd :: Included => false ,
195
- RangeEnd :: Excluded => self . start == self . end ,
201
+ /// Iterates over the pairs of fields with matching names.
202
+ fn iter_matching_struct_fields < ' a > (
203
+ left : & ' a [ ( Symbol , NormalizedPat < ' a > ) ] ,
204
+ right : & ' a [ ( Symbol , NormalizedPat < ' a > ) ] ,
205
+ ) -> impl Iterator < Item = ( & ' a NormalizedPat < ' a > , & ' a NormalizedPat < ' a > ) > + ' a {
206
+ struct Iter < ' a > (
207
+ slice:: Iter < ' a , ( Symbol , NormalizedPat < ' a > ) > ,
208
+ slice:: Iter < ' a , ( Symbol , NormalizedPat < ' a > ) > ,
209
+ ) ;
210
+ impl < ' a > Iterator for Iter < ' a > {
211
+ type Item = ( & ' a NormalizedPat < ' a > , & ' a NormalizedPat < ' a > ) ;
212
+ fn next ( & mut self ) -> Option < Self :: Item > {
213
+ // Note: all the fields in each slice are sorted by symbol value.
214
+ let mut left = self . 0 . next ( ) ?;
215
+ let mut right = self . 1 . next ( ) ?;
216
+ loop {
217
+ match left. 0 . cmp ( & right. 0 ) {
218
+ Ordering :: Equal => return Some ( ( & left. 1 , & right. 1 ) ) ,
219
+ Ordering :: Less => left = self . 0 . next ( ) ?,
220
+ Ordering :: Greater => right = self . 1 . next ( ) ?,
221
+ }
222
+ }
196
223
}
197
224
}
225
+ Iter ( left. iter ( ) , right. iter ( ) )
198
226
}
199
227
200
228
#[ allow( clippy:: similar_names) ]
@@ -259,6 +287,7 @@ impl<'a> NormalizedPat<'a> {
259
287
Self :: Tuple ( None , pats)
260
288
} ,
261
289
PatKind :: Lit ( e) => match & e. kind {
290
+ // TODO: Handle negative integers. They're currently treated as a wild match.
262
291
ExprKind :: Lit ( lit) => match lit. node {
263
292
LitKind :: Str ( sym, _) => Self :: LitStr ( sym) ,
264
293
LitKind :: ByteStr ( ref bytes) => Self :: LitBytes ( & * * bytes) ,
@@ -271,6 +300,7 @@ impl<'a> NormalizedPat<'a> {
271
300
_ => Self :: Wild ,
272
301
} ,
273
302
PatKind :: Range ( start, end, bounds) => {
303
+ // TODO: Handle negative integers. They're currently treated as a wild match.
274
304
let start = match start {
275
305
None => 0 ,
276
306
Some ( e) => match & e. kind {
@@ -306,42 +336,17 @@ impl<'a> NormalizedPat<'a> {
306
336
307
337
/// Checks if two patterns overlap in the values they can match assuming they are for the same
308
338
/// type.
309
- fn can_also_match ( & self , other : & Self ) -> bool {
339
+ fn has_overlapping_values ( & self , other : & Self ) -> bool {
310
340
match ( * self , * other) {
311
341
( Self :: Wild , _) | ( _, Self :: Wild ) => true ,
312
342
( Self :: Or ( pats) , ref other) | ( ref other, Self :: Or ( pats) ) => {
313
- pats. iter ( ) . any ( |pat| pat. can_also_match ( other) )
343
+ pats. iter ( ) . any ( |pat| pat. has_overlapping_values ( other) )
314
344
} ,
315
345
( Self :: Struct ( lpath, lfields) , Self :: Struct ( rpath, rfields) ) => {
316
346
if lpath != rpath {
317
347
return false ;
318
348
}
319
- let mut rfields = rfields. iter ( ) ;
320
- let mut rfield = match rfields. next ( ) {
321
- Some ( x) => x,
322
- None => return true ,
323
- } ;
324
- ' outer: for lfield in lfields {
325
- loop {
326
- if lfield. 0 < rfield. 0 {
327
- continue ' outer;
328
- } else if lfield. 0 > rfield. 0 {
329
- rfield = match rfields. next ( ) {
330
- Some ( x) => x,
331
- None => return true ,
332
- } ;
333
- } else if !lfield. 1 . can_also_match ( & rfield. 1 ) {
334
- return false ;
335
- } else {
336
- rfield = match rfields. next ( ) {
337
- Some ( x) => x,
338
- None => return true ,
339
- } ;
340
- continue ' outer;
341
- }
342
- }
343
- }
344
- true
349
+ iter_matching_struct_fields ( lfields, rfields) . all ( |( lpat, rpat) | lpat. has_overlapping_values ( rpat) )
345
350
} ,
346
351
( Self :: Tuple ( lpath, lpats) , Self :: Tuple ( rpath, rpats) ) => {
347
352
if lpath != rpath {
@@ -350,7 +355,7 @@ impl<'a> NormalizedPat<'a> {
350
355
lpats
351
356
. iter ( )
352
357
. zip ( rpats. iter ( ) )
353
- . all ( |( lpat, rpat) | lpat. can_also_match ( rpat) )
358
+ . all ( |( lpat, rpat) | lpat. has_overlapping_values ( rpat) )
354
359
} ,
355
360
( Self :: Path ( x) , Self :: Path ( y) ) => x == y,
356
361
( Self :: LitStr ( x) , Self :: LitStr ( y) ) => x == y,
@@ -360,26 +365,38 @@ impl<'a> NormalizedPat<'a> {
360
365
( Self :: Range ( ref x) , Self :: Range ( ref y) ) => x. overlaps ( y) ,
361
366
( Self :: Range ( ref range) , Self :: LitInt ( x) ) | ( Self :: LitInt ( x) , Self :: Range ( ref range) ) => range. contains ( x) ,
362
367
( Self :: Slice ( lpats, None ) , Self :: Slice ( rpats, None ) ) => {
363
- lpats. len ( ) == rpats. len ( ) && lpats. iter ( ) . zip ( rpats. iter ( ) ) . all ( |( x, y) | x. can_also_match ( y) )
368
+ lpats. len ( ) == rpats. len ( ) && lpats. iter ( ) . zip ( rpats. iter ( ) ) . all ( |( x, y) | x. has_overlapping_values ( y) )
364
369
} ,
365
370
( Self :: Slice ( pats, None ) , Self :: Slice ( front, Some ( back) ) )
366
371
| ( Self :: Slice ( front, Some ( back) ) , Self :: Slice ( pats, None ) ) => {
372
+ // Here `pats` is an exact size match. If the combined lengths of `front` and `back` are greater
373
+ // then the minium length required will be greater than the length of `pats`.
367
374
if pats. len ( ) < front. len ( ) + back. len ( ) {
368
375
return false ;
369
376
}
370
377
pats[ ..front. len ( ) ]
371
378
. iter ( )
372
379
. zip ( front. iter ( ) )
373
380
. chain ( pats[ pats. len ( ) - back. len ( ) ..] . iter ( ) . zip ( back. iter ( ) ) )
374
- . all ( |( x, y) | x. can_also_match ( y) )
381
+ . all ( |( x, y) | x. has_overlapping_values ( y) )
375
382
} ,
376
383
( Self :: Slice ( lfront, Some ( lback) ) , Self :: Slice ( rfront, Some ( rback) ) ) => lfront
377
384
. iter ( )
378
385
. zip ( rfront. iter ( ) )
379
386
. chain ( lback. iter ( ) . rev ( ) . zip ( rback. iter ( ) . rev ( ) ) )
380
- . all ( |( x, y) | x. can_also_match ( y) ) ,
387
+ . all ( |( x, y) | x. has_overlapping_values ( y) ) ,
388
+
389
+ // Enums can mix unit variants with tuple/struct variants. These can never overlap.
390
+ ( Self :: Path ( _) , Self :: Tuple ( ..) | Self :: Struct ( ..) )
391
+ | ( Self :: Tuple ( ..) | Self :: Struct ( ..) , Self :: Path ( _) ) => false ,
392
+
393
+ // Tuples can be matched like a struct.
394
+ ( Self :: Tuple ( x, _) , Self :: Struct ( y, _) ) | ( Self :: Struct ( x, _) , Self :: Tuple ( y, _) ) => {
395
+ // TODO: check fields here.
396
+ x == y
397
+ } ,
381
398
382
- // Todo : Lit* with Path, Range with Path, LitBytes with Slice, Slice with Slice
399
+ // TODO : Lit* with Path, Range with Path, LitBytes with Slice
383
400
_ => true ,
384
401
}
385
402
}
0 commit comments