11use clippy_utils:: diagnostics:: span_lint_and_then;
22use clippy_utils:: source:: snippet;
33use clippy_utils:: { path_to_local, search_same, SpanlessEq , SpanlessHash } ;
4+ use core:: cmp:: Ordering ;
45use core:: iter;
6+ use core:: slice;
57use rustc_arena:: DroplessArena ;
68use rustc_ast:: ast:: LitKind ;
79use rustc_errors:: Applicability ;
@@ -36,7 +38,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
3638 normalized_pats[ i + 1 ..]
3739 . iter ( )
3840 . 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) )
4042 . unwrap_or ( normalized_pats. len ( ) )
4143 } )
4244 . collect ( ) ;
@@ -52,7 +54,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
5254 . rev ( )
5355 . zip ( forwards_blocking_idxs[ ..i] . iter ( ) . copied ( ) . rev ( ) )
5456 . 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+ } )
5660 . unwrap_or ( 0 )
5761 } )
5862 . collect ( ) ;
@@ -159,6 +163,10 @@ enum NormalizedPat<'a> {
159163 LitInt ( u128 ) ,
160164 LitBool ( bool ) ,
161165 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.
162170 Slice ( & ' a [ Self ] , Option < & ' a [ Self ] > ) ,
163171}
164172
@@ -178,23 +186,43 @@ impl PatRange {
178186 }
179187
180188 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+ } )
190198 }
199+ }
191200
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+ }
196223 }
197224 }
225+ Iter ( left. iter ( ) , right. iter ( ) )
198226}
199227
200228#[ allow( clippy:: similar_names) ]
@@ -259,6 +287,7 @@ impl<'a> NormalizedPat<'a> {
259287 Self :: Tuple ( None , pats)
260288 } ,
261289 PatKind :: Lit ( e) => match & e. kind {
290+ // TODO: Handle negative integers. They're currently treated as a wild match.
262291 ExprKind :: Lit ( lit) => match lit. node {
263292 LitKind :: Str ( sym, _) => Self :: LitStr ( sym) ,
264293 LitKind :: ByteStr ( ref bytes) => Self :: LitBytes ( & * * bytes) ,
@@ -271,6 +300,7 @@ impl<'a> NormalizedPat<'a> {
271300 _ => Self :: Wild ,
272301 } ,
273302 PatKind :: Range ( start, end, bounds) => {
303+ // TODO: Handle negative integers. They're currently treated as a wild match.
274304 let start = match start {
275305 None => 0 ,
276306 Some ( e) => match & e. kind {
@@ -306,42 +336,17 @@ impl<'a> NormalizedPat<'a> {
306336
307337 /// Checks if two patterns overlap in the values they can match assuming they are for the same
308338 /// type.
309- fn can_also_match ( & self , other : & Self ) -> bool {
339+ fn has_overlapping_values ( & self , other : & Self ) -> bool {
310340 match ( * self , * other) {
311341 ( Self :: Wild , _) | ( _, Self :: Wild ) => true ,
312342 ( 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) )
314344 } ,
315345 ( Self :: Struct ( lpath, lfields) , Self :: Struct ( rpath, rfields) ) => {
316346 if lpath != rpath {
317347 return false ;
318348 }
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) )
345350 } ,
346351 ( Self :: Tuple ( lpath, lpats) , Self :: Tuple ( rpath, rpats) ) => {
347352 if lpath != rpath {
@@ -350,7 +355,7 @@ impl<'a> NormalizedPat<'a> {
350355 lpats
351356 . iter ( )
352357 . zip ( rpats. iter ( ) )
353- . all ( |( lpat, rpat) | lpat. can_also_match ( rpat) )
358+ . all ( |( lpat, rpat) | lpat. has_overlapping_values ( rpat) )
354359 } ,
355360 ( Self :: Path ( x) , Self :: Path ( y) ) => x == y,
356361 ( Self :: LitStr ( x) , Self :: LitStr ( y) ) => x == y,
@@ -360,26 +365,38 @@ impl<'a> NormalizedPat<'a> {
360365 ( Self :: Range ( ref x) , Self :: Range ( ref y) ) => x. overlaps ( y) ,
361366 ( Self :: Range ( ref range) , Self :: LitInt ( x) ) | ( Self :: LitInt ( x) , Self :: Range ( ref range) ) => range. contains ( x) ,
362367 ( 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) )
364369 } ,
365370 ( Self :: Slice ( pats, None ) , Self :: Slice ( front, Some ( back) ) )
366371 | ( 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`.
367374 if pats. len ( ) < front. len ( ) + back. len ( ) {
368375 return false ;
369376 }
370377 pats[ ..front. len ( ) ]
371378 . iter ( )
372379 . zip ( front. iter ( ) )
373380 . 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) )
375382 } ,
376383 ( Self :: Slice ( lfront, Some ( lback) ) , Self :: Slice ( rfront, Some ( rback) ) ) => lfront
377384 . iter ( )
378385 . zip ( rfront. iter ( ) )
379386 . 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+ } ,
381398
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
383400 _ => true ,
384401 }
385402 }
0 commit comments