From ba60bceace120580360ab84413767c6f46ccce44 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 12 Dec 2023 16:58:31 +0100 Subject: [PATCH] Lint small gaps between ranges --- compiler/rustc_lint_defs/src/builtin.rs | 30 ++++ compiler/rustc_pattern_analysis/messages.ftl | 4 + .../rustc_pattern_analysis/src/constructor.rs | 36 +++-- compiler/rustc_pattern_analysis/src/errors.rs | 30 ++++ compiler/rustc_pattern_analysis/src/lib.rs | 32 ++-- compiler/rustc_pattern_analysis/src/lints.rs | 81 +++++++--- compiler/rustc_pattern_analysis/src/rustc.rs | 6 +- .../clippy/tests/ui/manual_range_patterns.rs | 1 + tests/ui/match/issue-18060.rs | 2 + tests/ui/mir/mir_match_test.rs | 1 + .../integer-ranges/gap_between_ranges.rs | 65 ++++++++ .../integer-ranges/gap_between_ranges.stderr | 141 ++++++++++++++++++ .../usefulness/integer-ranges/reachability.rs | 1 + .../integer-ranges/reachability.stderr | 52 +++---- 14 files changed, 400 insertions(+), 82 deletions(-) create mode 100644 tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.rs create mode 100644 tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.stderr diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index f9b66239bf9a4..4de746ebab81c 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -87,6 +87,7 @@ declare_lint_pass! { RUST_2021_PRELUDE_COLLISIONS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, SINGLE_USE_LIFETIMES, + SMALL_GAPS_BETWEEN_RANGES, SOFT_UNSTABLE, STABLE_FEATURES, SUSPICIOUS_AUTO_TRAIT_IMPLS, @@ -835,6 +836,35 @@ declare_lint! { "detects range patterns with overlapping endpoints" } +declare_lint! { + /// The `small_gaps_between_ranges` lint detects `match` expressions that use [range patterns] + /// that skip over a single number. + /// + /// [range patterns]: https://doc.rust-lang.org/nightly/reference/patterns.html#range-patterns + /// + /// ### Example + /// + /// ```rust + /// let x = 123u32; + /// match x { + /// 0..100 => { println!("small"); } + /// 101..1000 => { println!("large"); } + /// _ => { println!("larger"); } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is likely a mistake to have range patterns in a match expression that miss out a single + /// number. Check that the beginning and end values are what you expect, and keep in mind that + /// with `..=` the right bound is inclusive, and with `..` it is exclusive. + pub SMALL_GAPS_BETWEEN_RANGES, + Warn, + "detects range patterns separated by a single number" +} + declare_lint! { /// The `bindings_with_variant_name` lint detects pattern bindings with /// the same name as one of the matched variants. diff --git a/compiler/rustc_pattern_analysis/messages.ftl b/compiler/rustc_pattern_analysis/messages.ftl index 827928f97d7cb..8fd185b1d30e0 100644 --- a/compiler/rustc_pattern_analysis/messages.ftl +++ b/compiler/rustc_pattern_analysis/messages.ftl @@ -11,6 +11,10 @@ pattern_analysis_overlapping_range_endpoints = multiple patterns overlap on thei .label = ... with this range .note = you likely meant to write mutually exclusive ranges +pattern_analysis_small_gap_between_ranges = multiple ranges are one apart + .label = ... with this range + .note = you likely meant to match `{$range}` too + pattern_analysis_uncovered = {$count -> [1] pattern `{$witness_1}` [2] patterns `{$witness_1}` and `{$witness_2}` diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index af0a7497a34f9..d430e96dcaf78 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -193,7 +193,7 @@ impl fmt::Display for RangeEnd { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum MaybeInfiniteInt { NegInfinity, - /// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite`. + /// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite_{int,uint}`. #[non_exhaustive] Finite(u128), /// The integer after `u128::MAX`. We need it to represent `x..=u128::MAX` as an exclusive range. @@ -230,25 +230,22 @@ impl MaybeInfiniteInt { } /// Note: this will not turn a finite value into an infinite one or vice-versa. - pub fn minus_one(self) -> Self { + pub fn minus_one(self) -> Option { match self { - Finite(n) => match n.checked_sub(1) { - Some(m) => Finite(m), - None => panic!("Called `MaybeInfiniteInt::minus_one` on 0"), - }, - JustAfterMax => Finite(u128::MAX), - x => x, + Finite(n) => n.checked_sub(1).map(Finite), + JustAfterMax => Some(Finite(u128::MAX)), + x => Some(x), } } /// Note: this will not turn a finite value into an infinite one or vice-versa. - pub fn plus_one(self) -> Self { + pub fn plus_one(self) -> Option { match self { Finite(n) => match n.checked_add(1) { - Some(m) => Finite(m), - None => JustAfterMax, + Some(m) => Some(Finite(m)), + None => Some(JustAfterMax), }, - JustAfterMax => panic!("Called `MaybeInfiniteInt::plus_one` on u128::MAX+1"), - x => x, + JustAfterMax => None, + x => Some(x), } } } @@ -269,18 +266,25 @@ impl IntRange { pub fn is_singleton(&self) -> bool { // Since `lo` and `hi` can't be the same `Infinity` and `plus_one` never changes from finite // to infinite, this correctly only detects ranges that contain exacly one `Finite(x)`. - self.lo.plus_one() == self.hi + // `unwrap()` is ok since `self.lo` will never be `JustAfterMax`. + self.lo.plus_one().unwrap() == self.hi } + /// Construct a singleton range. + /// `x` be a `Finite(_)` value. #[inline] pub fn from_singleton(x: MaybeInfiniteInt) -> IntRange { - IntRange { lo: x, hi: x.plus_one() } + // `unwrap()` is ok on a finite value + IntRange { lo: x, hi: x.plus_one().unwrap() } } + /// Construct a range with these boundaries. + /// `lo` must not be `PosInfinity` or `JustAfterMax`. `hi` must not be `NegInfinity`. + /// If `end` is `Included`, `hi` must also not be `JustAfterMax`. #[inline] pub fn from_range(lo: MaybeInfiniteInt, mut hi: MaybeInfiniteInt, end: RangeEnd) -> IntRange { if end == RangeEnd::Included { - hi = hi.plus_one(); + hi = hi.plus_one().unwrap(); } if lo >= hi { // This should have been caught earlier by E0030. diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index 88770b0c43b37..ad00c94e69719 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -72,6 +72,36 @@ impl<'tcx> AddToDiagnostic for Overlap<'tcx> { } } +#[derive(LintDiagnostic)] +#[diag(pattern_analysis_small_gap_between_ranges)] +#[note] +pub struct SmallGapBetweenRanges<'tcx> { + #[label] + pub first_range: Span, + pub range: Pat<'tcx>, + #[subdiagnostic] + pub gap_with: Vec>, +} + +pub struct GappedRange<'tcx> { + pub span: Span, + pub range: Pat<'tcx>, +} + +impl<'tcx> AddToDiagnostic for GappedRange<'tcx> { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + let GappedRange { span, range } = self; + + // FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]` + // does not support `#[subdiagnostic(eager)]`... + let message = format!("this range has a small gap on `{range}`..."); + diag.span_label(span, message); + } +} + #[derive(LintDiagnostic)] #[diag(pattern_analysis_non_exhaustive_omitted_pattern)] #[help] diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 785a60e997842..83b968f1bf059 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -26,15 +26,7 @@ use rustc_index::Idx; use rustc_middle::ty::Ty; use crate::constructor::{Constructor, ConstructorSet}; -#[cfg(feature = "rustc")] -use crate::lints::{ - lint_nonexhaustive_missing_variants, lint_overlapping_range_endpoints, PatternColumn, -}; use crate::pat::DeconstructedPat; -#[cfg(feature = "rustc")] -use crate::rustc::RustcMatchCheckCtxt; -#[cfg(feature = "rustc")] -use crate::usefulness::{compute_match_usefulness, ValidityConstraint}; // It's not possible to only enable the `typed_arena` dependency when the `rustc` feature is off, so // we use another feature instead. The crate won't compile if one of these isn't enabled. @@ -109,26 +101,32 @@ impl<'p, Cx: TypeCx> Copy for MatchArm<'p, Cx> {} /// useful, and runs some lints. #[cfg(feature = "rustc")] pub fn analyze_match<'p, 'tcx>( - tycx: &RustcMatchCheckCtxt<'p, 'tcx>, + tycx: &rustc::RustcMatchCheckCtxt<'p, 'tcx>, arms: &[rustc::MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>, ) -> rustc::UsefulnessReport<'p, 'tcx> { + use crate::lints::PatternColumn; + use crate::usefulness::ValidityConstraint; + // Arena to store the extra wildcards we construct during analysis. let wildcard_arena = tycx.pattern_arena; let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee); let cx = MatchCtxt { tycx, wildcard_arena }; - let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity); + let report = usefulness::compute_match_usefulness(cx, arms, scrut_ty, scrut_validity); - let pat_column = PatternColumn::new(arms); + // Only run the lints if the match is exhaustive. + if report.non_exhaustiveness_witnesses.is_empty() { + let pat_column = PatternColumn::new(arms); - // Lint on ranges that overlap on their endpoints, which is likely a mistake. - lint_overlapping_range_endpoints(cx, &pat_column); + // Detect possible range-related mistakes. + lints::lint_likely_range_mistakes(cx, &pat_column); - // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting - // `if let`s. Only run if the match is exhaustive otherwise the error is redundant. - if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() { - lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty) + // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid + // hitting `if let`s. + if tycx.refutable { + lints::lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty) + } } report diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 072ef4836a84e..cabae16cd3450 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -7,10 +7,7 @@ use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_span::Span; use crate::constructor::{IntRange, MaybeInfiniteInt}; -use crate::errors::{ - NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Overlap, - OverlappingRangeEndpoints, Uncovered, -}; +use crate::errors; use crate::rustc::{ Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RustcMatchCheckCtxt, SplitConstructorSet, WitnessPat, @@ -189,9 +186,9 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level, rcx.scrut_span, - NonExhaustiveOmittedPattern { + errors::NonExhaustiveOmittedPattern { scrut_ty, - uncovered: Uncovered::new(rcx.scrut_span, rcx, witnesses), + uncovered: errors::Uncovered::new(rcx.scrut_span, rcx, witnesses), }, ); } @@ -203,7 +200,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( let (lint_level, lint_level_source) = rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data); if !matches!(lint_level, rustc_session::lint::Level::Allow) { - let decorator = NonExhaustiveOmittedPatternLintOnArm { + let decorator = errors::NonExhaustiveOmittedPatternLintOnArm { lint_span: lint_level_source.span(), suggest_lint_on_match: rcx.whole_match_span.map(|span| span.shrink_to_lo()), lint_level: lint_level.as_str(), @@ -220,9 +217,10 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( } } -/// Traverse the patterns to warn the user about ranges that overlap on their endpoints. +/// Traverse the patterns to warn the user about ranges that overlap on their endpoints or are +/// distant by one. #[instrument(level = "debug", skip(cx))] -pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( +pub(crate) fn lint_likely_range_mistakes<'a, 'p, 'tcx>( cx: MatchCtxt<'a, 'p, 'tcx>, column: &PatternColumn<'a, 'p, 'tcx>, ) { @@ -235,24 +233,24 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( let set = column.analyze_ctors(pcx); if matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_)) { - let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| { + let emit_overlap_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| { let overlap_as_pat = rcx.hoist_pat_range(overlap, ty); let overlaps: Vec<_> = overlapped_spans .iter() .copied() - .map(|span| Overlap { range: overlap_as_pat.clone(), span }) + .map(|span| errors::Overlap { range: overlap_as_pat.clone(), span }) .collect(); rcx.tcx.emit_spanned_lint( lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, rcx.match_lint_level, this_span, - OverlappingRangeEndpoints { overlap: overlaps, range: this_span }, + errors::OverlappingRangeEndpoints { overlap: overlaps, range: this_span }, ); }; - // If two ranges overlapped, the split set will contain their intersection as a singleton. - let split_int_ranges = set.present.iter().filter_map(|c| c.as_int_range()); - for overlap_range in split_int_ranges.clone() { + // The two cases we are interested in will show up as a singleton after range splitting. + let present_int_ranges = set.present.iter().filter_map(|c| c.as_int_range()); + for overlap_range in present_int_ranges { if overlap_range.is_singleton() { let overlap: MaybeInfiniteInt = overlap_range.lo; // Ranges that look like `lo..=overlap`. @@ -267,29 +265,72 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( // Don't lint when one of the ranges is a singleton. continue; } - if this_range.lo == overlap { + if overlap == this_range.lo { // `this_range` looks like `overlap..=this_range.hi`; it overlaps with any // ranges that look like `lo..=overlap`. if !prefixes.is_empty() { - emit_lint(overlap_range, this_span, &prefixes); + emit_overlap_lint(overlap_range, this_span, &prefixes); } suffixes.push(this_span) - } else if this_range.hi == overlap.plus_one() { + } else if overlap.plus_one() == Some(this_range.hi) { // `this_range` looks like `this_range.lo..=overlap`; it overlaps with any // ranges that look like `overlap..=hi`. if !suffixes.is_empty() { - emit_lint(overlap_range, this_span, &suffixes); + emit_overlap_lint(overlap_range, this_span, &suffixes); } prefixes.push(this_span) } } } } + + let missing_int_ranges = set.missing.iter().filter_map(|c| c.as_int_range()); + for point_range in missing_int_ranges { + if point_range.is_singleton() { + let point: MaybeInfiniteInt = point_range.lo; + // Ranges that look like `lo..point`. + let mut onebefore: SmallVec<[_; 1]> = Default::default(); + // Ranges that look like `point+1..=hi`. + let mut oneafter: SmallVec<[_; 1]> = Default::default(); + for pat in column.iter() { + let this_span = *pat.data(); + let Constructor::IntRange(this_range) = pat.ctor() else { continue }; + + if point == this_range.hi && !this_range.is_singleton() { + onebefore.push(this_span) + } else if point.plus_one() == Some(this_range.lo) { + oneafter.push(this_span) + } + } + + if !onebefore.is_empty() && !oneafter.is_empty() { + // We have some `lo..point` and some `point+1..hi` but no `point`. + let point_as_pat = rcx.hoist_pat_range(point_range, ty); + for span_after in oneafter { + let spans_before: Vec<_> = onebefore + .iter() + .copied() + .map(|span| errors::GappedRange { range: point_as_pat.clone(), span }) + .collect(); + rcx.tcx.emit_spanned_lint( + lint::builtin::SMALL_GAPS_BETWEEN_RANGES, + rcx.match_lint_level, + span_after, + errors::SmallGapBetweenRanges { + range: point_as_pat.clone(), + first_range: span_after, + gap_with: spans_before, + }, + ); + } + } + } + } } else { // Recurse into the fields. for ctor in set.present { for col in column.specialize(pcx, &ctor) { - lint_overlapping_range_endpoints(cx, &col); + lint_likely_range_mistakes(cx, &col); } } } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 65c90aa9f1d23..9d754750b658c 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -659,12 +659,12 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { let value = mir::Const::from_ty_const(c, cx.tcx); lo = PatRangeBoundary::Finite(value); } - let hi = if matches!(range.hi, Finite(0)) { + let hi = if let Some(hi) = range.hi.minus_one() { + hi + } else { // The range encodes `..ty::MIN`, so we can't convert it to an inclusive range. end = rustc_hir::RangeEnd::Excluded; range.hi - } else { - range.hi.minus_one() }; let hi = cx.hoist_pat_range_bdy(hi, ty); PatKind::Range(Box::new(PatRange { lo, hi, end, ty })) diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.rs b/src/tools/clippy/tests/ui/manual_range_patterns.rs index a0750f54b73f1..555a2f5c0ebf7 100644 --- a/src/tools/clippy/tests/ui/manual_range_patterns.rs +++ b/src/tools/clippy/tests/ui/manual_range_patterns.rs @@ -1,4 +1,5 @@ #![allow(unused)] +#![allow(small_gaps_between_ranges)] #![warn(clippy::manual_range_patterns)] #![feature(exclusive_range_pattern)] diff --git a/tests/ui/match/issue-18060.rs b/tests/ui/match/issue-18060.rs index b5f3d0f74bc9a..d8d6531016ecf 100644 --- a/tests/ui/match/issue-18060.rs +++ b/tests/ui/match/issue-18060.rs @@ -1,6 +1,8 @@ // run-pass // Regression test for #18060: match arms were matching in the wrong order. +#[allow(overlapping_range_endpoints)] +#[allow(small_gaps_between_ranges)] fn main() { assert_eq!(2, match (1, 3) { (0, 2..=5) => 1, (1, 3) => 2, (_, 2..=5) => 3, (_, _) => 4 }); assert_eq!(2, match (1, 3) { (1, 3) => 2, (_, 2..=5) => 3, (_, _) => 4 }); diff --git a/tests/ui/mir/mir_match_test.rs b/tests/ui/mir/mir_match_test.rs index d41a7f4a1db4a..c29012af9533e 100644 --- a/tests/ui/mir/mir_match_test.rs +++ b/tests/ui/mir/mir_match_test.rs @@ -1,5 +1,6 @@ #![feature(exclusive_range_pattern)] #![allow(overlapping_range_endpoints)] +#![allow(small_gaps_between_ranges)] // run-pass diff --git a/tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.rs b/tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.rs new file mode 100644 index 0000000000000..8e38dc2678fc4 --- /dev/null +++ b/tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.rs @@ -0,0 +1,65 @@ +#![feature(exclusive_range_pattern)] +#![deny(small_gaps_between_ranges)] + +macro_rules! m { + ($s:expr, $t1:pat, $t2:pat) => { + match $s { + $t1 => {} + $t2 => {} + _ => {} + } + }; +} + +fn main() { + m!(0u8, 20..30, 31..=40); //~ ERROR multiple ranges are one apart + m!(0u8, 31..=40, 20..30); //~ ERROR multiple ranges are one apart + m!(0u8, 20..30, 29..=40); //~ WARN multiple patterns overlap on their endpoints + m!(0u8, 20..30, 30..=40); + m!(0u8, 20..30, 31..=40); //~ ERROR multiple ranges are one apart + m!(0u8, 20..30, 32..=40); + m!(0u8, 20..30, 31); //~ ERROR multiple ranges are one apart + m!(0u8, 20..30, 31..=32); //~ ERROR multiple ranges are one apart + // Don't lint if the lower one is a singleton. + m!(0u8, 30, 32..=40); + // Don't lint two singletons. + m!(0u8, 30, 32); + + // Don't lint if the gap is caught by another range. + match 0u8 { + 0..10 => {} + 11..20 => {} + 10 => {} + _ => {} + } + match 0u8 { + 0..10 => {} + 11..20 => {} + 5..15 => {} + _ => {} + } + + match 0u8 { + 0..10 => {} + 21..30 => {} //~ ERROR multiple ranges are one apart + 11..20 => {} //~ ERROR multiple ranges are one apart + _ => {} + } + match (0u8, true) { + (0..10, true) => {} + (11..20, true) => {} //~ ERROR multiple ranges are one apart + (11..20, false) => {} //~ ERROR multiple ranges are one apart + _ => {} + } + match (true, 0u8) { + (true, 0..10) => {} + (true, 11..20) => {} //~ ERROR multiple ranges are one apart + (false, 11..20) => {} //~ ERROR multiple ranges are one apart + _ => {} + } + match Some(0u8) { + Some(0..10) => {} + Some(11..20) => {} //~ ERROR multiple ranges are one apart + _ => {} + } +} diff --git a/tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.stderr b/tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.stderr new file mode 100644 index 0000000000000..55a6007e1e023 --- /dev/null +++ b/tests/ui/pattern/usefulness/integer-ranges/gap_between_ranges.stderr @@ -0,0 +1,141 @@ +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:15:21 + | +LL | m!(0u8, 20..30, 31..=40); + | ------ ^^^^^^^ ... with this range + | | + | this range has a small gap on `30_u8`... + | + = note: you likely meant to match `30_u8` too +note: the lint level is defined here + --> $DIR/gap_between_ranges.rs:2:9 + | +LL | #![deny(small_gaps_between_ranges)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:16:13 + | +LL | m!(0u8, 31..=40, 20..30); + | ^^^^^^^ ------ this range has a small gap on `30_u8`... + | | + | ... with this range + | + = note: you likely meant to match `30_u8` too + +warning: multiple patterns overlap on their endpoints + --> $DIR/gap_between_ranges.rs:17:21 + | +LL | m!(0u8, 20..30, 29..=40); + | ------ ^^^^^^^ ... with this range + | | + | this range overlaps on `29_u8`... + | + = note: you likely meant to write mutually exclusive ranges + = note: `#[warn(overlapping_range_endpoints)]` on by default + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:19:21 + | +LL | m!(0u8, 20..30, 31..=40); + | ------ ^^^^^^^ ... with this range + | | + | this range has a small gap on `30_u8`... + | + = note: you likely meant to match `30_u8` too + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:21:21 + | +LL | m!(0u8, 20..30, 31); + | ------ ^^ ... with this range + | | + | this range has a small gap on `30_u8`... + | + = note: you likely meant to match `30_u8` too + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:22:21 + | +LL | m!(0u8, 20..30, 31..=32); + | ------ ^^^^^^^ ... with this range + | | + | this range has a small gap on `30_u8`... + | + = note: you likely meant to match `30_u8` too + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:45:9 + | +LL | 0..10 => {} + | ----- this range has a small gap on `10_u8`... +LL | 21..30 => {} +LL | 11..20 => {} + | ^^^^^^ ... with this range + | + = note: you likely meant to match `10_u8` too + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:44:9 + | +LL | 21..30 => {} + | ^^^^^^ ... with this range +LL | 11..20 => {} + | ------ this range has a small gap on `20_u8`... + | + = note: you likely meant to match `20_u8` too + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:50:10 + | +LL | (0..10, true) => {} + | ----- this range has a small gap on `10_u8`... +LL | (11..20, true) => {} + | ^^^^^^ ... with this range + | + = note: you likely meant to match `10_u8` too + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:51:10 + | +LL | (0..10, true) => {} + | ----- this range has a small gap on `10_u8`... +LL | (11..20, true) => {} +LL | (11..20, false) => {} + | ^^^^^^ ... with this range + | + = note: you likely meant to match `10_u8` too + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:56:16 + | +LL | (true, 0..10) => {} + | ----- this range has a small gap on `10_u8`... +LL | (true, 11..20) => {} + | ^^^^^^ ... with this range + | + = note: you likely meant to match `10_u8` too + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:57:17 + | +LL | (true, 0..10) => {} + | ----- this range has a small gap on `10_u8`... +LL | (true, 11..20) => {} +LL | (false, 11..20) => {} + | ^^^^^^ ... with this range + | + = note: you likely meant to match `10_u8` too + +error: multiple ranges are one apart + --> $DIR/gap_between_ranges.rs:62:14 + | +LL | Some(0..10) => {} + | ----- this range has a small gap on `10_u8`... +LL | Some(11..20) => {} + | ^^^^^^ ... with this range + | + = note: you likely meant to match `10_u8` too + +error: aborting due to 12 previous errors; 1 warning emitted + diff --git a/tests/ui/pattern/usefulness/integer-ranges/reachability.rs b/tests/ui/pattern/usefulness/integer-ranges/reachability.rs index 247fdd91572cd..2d27575e26374 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/reachability.rs +++ b/tests/ui/pattern/usefulness/integer-ranges/reachability.rs @@ -1,5 +1,6 @@ #![feature(exclusive_range_pattern)] #![allow(overlapping_range_endpoints)] +#![allow(small_gaps_between_ranges)] #![deny(unreachable_patterns)] macro_rules! m { diff --git a/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr b/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr index c5b028d2038c3..0f52dfd83b9cb 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr +++ b/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr @@ -1,137 +1,137 @@ error: unreachable pattern - --> $DIR/reachability.rs:18:17 + --> $DIR/reachability.rs:19:17 | LL | m!(0u8, 42, 42); | ^^ | note: the lint level is defined here - --> $DIR/reachability.rs:3:9 + --> $DIR/reachability.rs:4:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:22:22 + --> $DIR/reachability.rs:23:22 | LL | m!(0u8, 20..=30, 20); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:23:22 + --> $DIR/reachability.rs:24:22 | LL | m!(0u8, 20..=30, 21); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:24:22 + --> $DIR/reachability.rs:25:22 | LL | m!(0u8, 20..=30, 25); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:25:22 + --> $DIR/reachability.rs:26:22 | LL | m!(0u8, 20..=30, 29); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:26:22 + --> $DIR/reachability.rs:27:22 | LL | m!(0u8, 20..=30, 30); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:29:21 + --> $DIR/reachability.rs:30:21 | LL | m!(0u8, 20..30, 20); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:30:21 + --> $DIR/reachability.rs:31:21 | LL | m!(0u8, 20..30, 21); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:31:21 + --> $DIR/reachability.rs:32:21 | LL | m!(0u8, 20..30, 25); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:32:21 + --> $DIR/reachability.rs:33:21 | LL | m!(0u8, 20..30, 29); | ^^ error: unreachable pattern - --> $DIR/reachability.rs:36:22 + --> $DIR/reachability.rs:37:22 | LL | m!(0u8, 20..=30, 20..=30); | ^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:37:22 + --> $DIR/reachability.rs:38:22 | LL | m!(0u8, 20.. 30, 20.. 30); | ^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:38:22 + --> $DIR/reachability.rs:39:22 | LL | m!(0u8, 20..=30, 20.. 30); | ^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:40:22 + --> $DIR/reachability.rs:41:22 | LL | m!(0u8, 20..=30, 21..=30); | ^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:41:22 + --> $DIR/reachability.rs:42:22 | LL | m!(0u8, 20..=30, 20..=29); | ^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:43:24 + --> $DIR/reachability.rs:44:24 | LL | m!('a', 'A'..='z', 'a'..='z'); | ^^^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:50:9 + --> $DIR/reachability.rs:51:9 | LL | 5..=8 => {}, | ^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:56:9 + --> $DIR/reachability.rs:57:9 | LL | 5..15 => {}, | ^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:63:9 + --> $DIR/reachability.rs:64:9 | LL | 5..25 => {}, | ^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:71:9 + --> $DIR/reachability.rs:72:9 | LL | 5..25 => {}, | ^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:77:9 + --> $DIR/reachability.rs:78:9 | LL | 5..15 => {}, | ^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:84:9 + --> $DIR/reachability.rs:85:9 | LL | _ => {}, | - matches any value @@ -139,19 +139,19 @@ LL | '\u{D7FF}'..='\u{E000}' => {}, | ^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern error: unreachable pattern - --> $DIR/reachability.rs:89:9 + --> $DIR/reachability.rs:90:9 | LL | '\u{D7FF}'..='\u{E000}' => {}, | ^^^^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/reachability.rs:105:9 + --> $DIR/reachability.rs:106:9 | LL | &FOO => {} | ^^^^ error: unreachable pattern - --> $DIR/reachability.rs:106:9 + --> $DIR/reachability.rs:107:9 | LL | BAR => {} | ^^^