about summary refs log tree commit diff
path: root/compiler/rustc_pattern_analysis/src/lib.rs
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-03-09 03:49:01 +0000
committerbors <bors@rust-lang.org>2024-03-09 03:49:01 +0000
commit1b427b3bf79c2cd48c75915301be3b009b82dea3 (patch)
tree3567f55ab86ee59c0a67343c4d1749c01d5b0226 /compiler/rustc_pattern_analysis/src/lib.rs
parent4d4bb491b65c300835442f6cb4f34fc9a5685c26 (diff)
parent8ac9a04257f73d9861625816d4c741096dd69c67 (diff)
downloadrust-1b427b3bf79c2cd48c75915301be3b009b82dea3.tar.gz
rust-1b427b3bf79c2cd48c75915301be3b009b82dea3.zip
Auto merge of #118879 - Nadrieril:lint-range-gap, r=estebank
Lint singleton gaps after exclusive ranges

In the discussion to stabilize exclusive range patterns (https://github.com/rust-lang/rust/issues/37854), it has often come up that they're likely to cause off-by-one mistakes. We already have the `overlapping_range_endpoints` lint, so I [proposed](https://github.com/rust-lang/rust/issues/37854#issuecomment-1845580712) a lint to catch the complementary mistake.

This PR adds a new `non_contiguous_range_endpoints` lint that catches likely off-by-one errors with exclusive range patterns. Here's the idea (see the test file for more examples):
```rust
match x {
    0..10 => ..., // WARN: this range doesn't match `10_u8` because `..` is an exclusive range
    11..20 => ..., // this could appear to continue range `0_u8..10_u8`, but `10_u8` isn't matched by either of them
    _ => ...,
}
// help: use an inclusive range instead: `0_u8..=10_u8`
```

More precisely: for any exclusive range `lo..hi`, if `hi+1` is matched by another range but `hi` isn't, we suggest writing an inclusive range `lo..=hi` instead. We also catch `lo..T::MAX`.
Diffstat (limited to 'compiler/rustc_pattern_analysis/src/lib.rs')
-rw-r--r--compiler/rustc_pattern_analysis/src/lib.rs23
1 files changed, 16 insertions, 7 deletions
diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs
index 4b0955699fc..f632eaf7ea4 100644
--- a/compiler/rustc_pattern_analysis/src/lib.rs
+++ b/compiler/rustc_pattern_analysis/src/lib.rs
@@ -70,14 +70,8 @@ use rustc_middle::ty::Ty;
 use rustc_span::ErrorGuaranteed;
 
 use crate::constructor::{Constructor, ConstructorSet, IntRange};
-#[cfg(feature = "rustc")]
-use crate::lints::lint_nonexhaustive_missing_variants;
 use crate::pat::DeconstructedPat;
 use crate::pat_column::PatternColumn;
-#[cfg(feature = "rustc")]
-use crate::rustc::RustcMatchCheckCtxt;
-#[cfg(feature = "rustc")]
-use crate::usefulness::{compute_match_usefulness, ValidityConstraint};
 
 pub trait Captures<'a> {}
 impl<'a, T: ?Sized> Captures<'a> for T {}
@@ -145,6 +139,18 @@ pub trait TypeCx: Sized + fmt::Debug {
 
     /// The maximum pattern complexity limit was reached.
     fn complexity_exceeded(&self) -> Result<(), Self::Error>;
+
+    /// Lint that there is a gap `gap` between `pat` and all of `gapped_with` such that the gap is
+    /// not matched by another range. If `gapped_with` is empty, then `gap` is `T::MAX`. We only
+    /// detect singleton gaps.
+    /// The default implementation does nothing.
+    fn lint_non_contiguous_range_endpoints(
+        &self,
+        _pat: &DeconstructedPat<Self>,
+        _gap: IntRange,
+        _gapped_with: &[&DeconstructedPat<Self>],
+    ) {
+    }
 }
 
 /// The arm of a match expression.
@@ -167,11 +173,14 @@ 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>,
     pattern_complexity_limit: Option<usize>,
 ) -> Result<rustc::UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
+    use lints::lint_nonexhaustive_missing_variants;
+    use usefulness::{compute_match_usefulness, ValidityConstraint};
+
     let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
     let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee);
     let report =