@@ -8,10 +8,7 @@ use rustc_span::Span;
88
99use crate :: constructor:: { Constructor , IntRange , MaybeInfiniteInt , SplitConstructorSet } ;
1010use crate :: cx:: MatchCheckCtxt ;
11- use crate :: errors:: {
12- NonExhaustiveOmittedPattern , NonExhaustiveOmittedPatternLintOnArm , Overlap ,
13- OverlappingRangeEndpoints , Uncovered ,
14- } ;
11+ use crate :: errors;
1512use crate :: pat:: { DeconstructedPat , WitnessPat } ;
1613use crate :: usefulness:: PatCtxt ;
1714use crate :: MatchArm ;
@@ -184,9 +181,9 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
184181 NON_EXHAUSTIVE_OMITTED_PATTERNS ,
185182 cx. match_lint_level ,
186183 cx. scrut_span ,
187- NonExhaustiveOmittedPattern {
184+ errors :: NonExhaustiveOmittedPattern {
188185 scrut_ty,
189- uncovered : Uncovered :: new ( cx. scrut_span , cx, witnesses) ,
186+ uncovered : errors :: Uncovered :: new ( cx. scrut_span , cx, witnesses) ,
190187 } ,
191188 ) ;
192189 }
@@ -198,7 +195,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
198195 let ( lint_level, lint_level_source) =
199196 cx. tcx . lint_level_at_node ( NON_EXHAUSTIVE_OMITTED_PATTERNS , arm. hir_id ) ;
200197 if !matches ! ( lint_level, rustc_session:: lint:: Level :: Allow ) {
201- let decorator = NonExhaustiveOmittedPatternLintOnArm {
198+ let decorator = errors :: NonExhaustiveOmittedPatternLintOnArm {
202199 lint_span : lint_level_source. span ( ) ,
203200 suggest_lint_on_match : cx. whole_match_span . map ( |span| span. shrink_to_lo ( ) ) ,
204201 lint_level : lint_level. as_str ( ) ,
@@ -215,9 +212,10 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
215212 }
216213}
217214
218- /// Traverse the patterns to warn the user about ranges that overlap on their endpoints.
215+ /// Traverse the patterns to warn the user about ranges that overlap on their endpoints or are
216+ /// distant by one.
219217#[ instrument( level = "debug" , skip( cx) ) ]
220- pub ( crate ) fn lint_overlapping_range_endpoints < ' p , ' tcx > (
218+ pub ( crate ) fn lint_likely_range_mistakes < ' p , ' tcx > (
221219 cx : & MatchCheckCtxt < ' p , ' tcx > ,
222220 column : & PatternColumn < ' p , ' tcx > ,
223221) {
@@ -229,24 +227,24 @@ pub(crate) fn lint_overlapping_range_endpoints<'p, 'tcx>(
229227 let set = column. analyze_ctors ( pcx) ;
230228
231229 if matches ! ( ty. kind( ) , ty:: Char | ty:: Int ( _) | ty:: Uint ( _) ) {
232- let emit_lint = |overlap : & IntRange , this_span : Span , overlapped_spans : & [ Span ] | {
230+ let emit_overlap_lint = |overlap : & IntRange , this_span : Span , overlapped_spans : & [ Span ] | {
233231 let overlap_as_pat = cx. hoist_pat_range ( overlap, ty) ;
234232 let overlaps: Vec < _ > = overlapped_spans
235233 . iter ( )
236234 . copied ( )
237- . map ( |span| Overlap { range : overlap_as_pat. clone ( ) , span } )
235+ . map ( |span| errors :: Overlap { range : overlap_as_pat. clone ( ) , span } )
238236 . collect ( ) ;
239237 cx. tcx . emit_spanned_lint (
240238 lint:: builtin:: OVERLAPPING_RANGE_ENDPOINTS ,
241239 cx. match_lint_level ,
242240 this_span,
243- OverlappingRangeEndpoints { overlap : overlaps, range : this_span } ,
241+ errors :: OverlappingRangeEndpoints { overlap : overlaps, range : this_span } ,
244242 ) ;
245243 } ;
246244
247- // If two ranges overlapped, the split set will contain their intersection as a singleton.
248- let split_int_ranges = set. present . iter ( ) . filter_map ( |c| c. as_int_range ( ) ) ;
249- for overlap_range in split_int_ranges . clone ( ) {
245+ // The two cases we are interested in will show up as a singleton after range splitting .
246+ let present_int_ranges = set. present . iter ( ) . filter_map ( |c| c. as_int_range ( ) ) ;
247+ for overlap_range in present_int_ranges {
250248 if overlap_range. is_singleton ( ) {
251249 let overlap: MaybeInfiniteInt = overlap_range. lo ;
252250 // Ranges that look like `lo..=overlap`.
@@ -261,29 +259,72 @@ pub(crate) fn lint_overlapping_range_endpoints<'p, 'tcx>(
261259 // Don't lint when one of the ranges is a singleton.
262260 continue ;
263261 }
264- if this_range . lo == overlap {
262+ if overlap == this_range . lo {
265263 // `this_range` looks like `overlap..=this_range.hi`; it overlaps with any
266264 // ranges that look like `lo..=overlap`.
267265 if !prefixes. is_empty ( ) {
268- emit_lint ( overlap_range, this_span, & prefixes) ;
266+ emit_overlap_lint ( overlap_range, this_span, & prefixes) ;
269267 }
270268 suffixes. push ( this_span)
271- } else if this_range . hi == overlap . plus_one ( ) {
269+ } else if overlap . plus_one ( ) == Some ( this_range . hi ) {
272270 // `this_range` looks like `this_range.lo..=overlap`; it overlaps with any
273271 // ranges that look like `overlap..=hi`.
274272 if !suffixes. is_empty ( ) {
275- emit_lint ( overlap_range, this_span, & suffixes) ;
273+ emit_overlap_lint ( overlap_range, this_span, & suffixes) ;
276274 }
277275 prefixes. push ( this_span)
278276 }
279277 }
280278 }
281279 }
280+
281+ let missing_int_ranges = set. missing . iter ( ) . filter_map ( |c| c. as_int_range ( ) ) ;
282+ for point_range in missing_int_ranges {
283+ if point_range. is_singleton ( ) {
284+ let point: MaybeInfiniteInt = point_range. lo ;
285+ // Ranges that look like `lo..point`.
286+ let mut onebefore: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
287+ // Ranges that look like `point+1..=hi`.
288+ let mut oneafter: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
289+ for pat in column. iter ( ) {
290+ let this_span = pat. span ( ) ;
291+ let Constructor :: IntRange ( this_range) = pat. ctor ( ) else { continue } ;
292+
293+ if point == this_range. hi {
294+ onebefore. push ( this_span)
295+ } else if point. plus_one ( ) == Some ( this_range. lo ) {
296+ oneafter. push ( this_span)
297+ }
298+ }
299+
300+ if !onebefore. is_empty ( ) && !oneafter. is_empty ( ) {
301+ // We have some `lo..point` and some `point+1..hi` but no `point`.
302+ let point_as_pat = cx. hoist_pat_range ( point_range, ty) ;
303+ for span_after in oneafter {
304+ let spans_before: Vec < _ > = onebefore
305+ . iter ( )
306+ . copied ( )
307+ . map ( |span| errors:: GappedRange { range : point_as_pat. clone ( ) , span } )
308+ . collect ( ) ;
309+ cx. tcx . emit_spanned_lint (
310+ lint:: builtin:: OVERLAPPING_RANGE_ENDPOINTS ,
311+ cx. match_lint_level ,
312+ span_after,
313+ errors:: SmallGapBetweenRanges {
314+ range : point_as_pat. clone ( ) ,
315+ first_range : span_after,
316+ gap_with : spans_before,
317+ } ,
318+ ) ;
319+ }
320+ }
321+ }
322+ }
282323 } else {
283324 // Recurse into the fields.
284325 for ctor in set. present {
285326 for col in column. specialize ( pcx, & ctor) {
286- lint_overlapping_range_endpoints ( cx, & col) ;
327+ lint_likely_range_mistakes ( cx, & col) ;
287328 }
288329 }
289330 }
0 commit comments