Skip to content

Commit b147cf5

Browse files
committed
Auto merge of rust-lang#118879 - Nadrieril:lint-range-gap, r=<try>
WIP: Lint small gaps between ranges In the discussion to stabilize exclusive range patterns (rust-lang#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](rust-lang#37854 (comment)) a lint to catch the complementary mistake. This PR adds a new `small_gaps_between_ranges` lint that catches likely off-by-one errors with 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 a non-inclusive range 11..20 => ..., // this seems 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 don't lint `lo..T::MAX` but we could. WARNING: the first 3 commits come from rust-lang#119233, ignore those. r? ghost
2 parents e87ccb8 + 6049acb commit b147cf5

File tree

21 files changed

+612
-217
lines changed

21 files changed

+612
-217
lines changed

compiler/rustc_lint_defs/src/builtin.rs

+31
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ declare_lint_pass! {
8787
RUST_2021_PRELUDE_COLLISIONS,
8888
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
8989
SINGLE_USE_LIFETIMES,
90+
SMALL_GAPS_BETWEEN_RANGES,
9091
SOFT_UNSTABLE,
9192
STABLE_FEATURES,
9293
SUSPICIOUS_AUTO_TRAIT_IMPLS,
@@ -835,6 +836,36 @@ declare_lint! {
835836
"detects range patterns with overlapping endpoints"
836837
}
837838

839+
declare_lint! {
840+
/// The `small_gaps_between_ranges` lint detects `match` expressions that use [range patterns]
841+
/// that skip over a single number.
842+
///
843+
/// [range patterns]: https://doc.rust-lang.org/nightly/reference/patterns.html#range-patterns
844+
///
845+
/// ### Example
846+
///
847+
/// ```rust
848+
/// # #![feature(exclusive_range_pattern)]
849+
/// let x = 123u32;
850+
/// match x {
851+
/// 0..100 => { println!("small"); }
852+
/// 101..1000 => { println!("large"); }
853+
/// _ => { println!("larger"); }
854+
/// }
855+
/// ```
856+
///
857+
/// {{produces}}
858+
///
859+
/// ### Explanation
860+
///
861+
/// It is likely a mistake to have range patterns in a match expression that miss out a single
862+
/// number. Check that the beginning and end values are what you expect, and keep in mind that
863+
/// with `..=` the right bound is inclusive, and with `..` it is exclusive.
864+
pub SMALL_GAPS_BETWEEN_RANGES,
865+
Warn,
866+
"detects range patterns separated by a single number"
867+
}
868+
838869
declare_lint! {
839870
/// The `bindings_with_variant_name` lint detects pattern bindings with
840871
/// the same name as one of the matched variants.

compiler/rustc_middle/src/thir/visit.rs

+27-12
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,26 @@ use super::{
33
PatKind, Stmt, StmtKind, Thir,
44
};
55

6-
pub trait Visitor<'a, 'tcx: 'a>: Sized {
7-
fn thir(&self) -> &'a Thir<'tcx>;
6+
pub trait Visitor<'thir, 'tcx: 'thir>: Sized {
7+
fn thir(&self) -> &'thir Thir<'tcx>;
88

9-
fn visit_expr(&mut self, expr: &Expr<'tcx>) {
9+
fn visit_expr(&mut self, expr: &'thir Expr<'tcx>) {
1010
walk_expr(self, expr);
1111
}
1212

13-
fn visit_stmt(&mut self, stmt: &Stmt<'tcx>) {
13+
fn visit_stmt(&mut self, stmt: &'thir Stmt<'tcx>) {
1414
walk_stmt(self, stmt);
1515
}
1616

17-
fn visit_block(&mut self, block: &Block) {
17+
fn visit_block(&mut self, block: &'thir Block) {
1818
walk_block(self, block);
1919
}
2020

21-
fn visit_arm(&mut self, arm: &Arm<'tcx>) {
21+
fn visit_arm(&mut self, arm: &'thir Arm<'tcx>) {
2222
walk_arm(self, arm);
2323
}
2424

25-
fn visit_pat(&mut self, pat: &Pat<'tcx>) {
25+
fn visit_pat(&mut self, pat: &'thir Pat<'tcx>) {
2626
walk_pat(self, pat);
2727
}
2828

@@ -36,7 +36,10 @@ pub trait Visitor<'a, 'tcx: 'a>: Sized {
3636
// other `visit*` functions.
3737
}
3838

39-
pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Expr<'tcx>) {
39+
pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
40+
visitor: &mut V,
41+
expr: &'thir Expr<'tcx>,
42+
) {
4043
use ExprKind::*;
4144
match expr.kind {
4245
Scope { value, region_scope: _, lint_level: _ } => {
@@ -168,7 +171,10 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp
168171
}
169172
}
170173

171-
pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stmt<'tcx>) {
174+
pub fn walk_stmt<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
175+
visitor: &mut V,
176+
stmt: &'thir Stmt<'tcx>,
177+
) {
172178
match &stmt.kind {
173179
StmtKind::Expr { expr, scope: _ } => visitor.visit_expr(&visitor.thir()[*expr]),
174180
StmtKind::Let {
@@ -191,7 +197,10 @@ pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stm
191197
}
192198
}
193199

194-
pub fn walk_block<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, block: &Block) {
200+
pub fn walk_block<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
201+
visitor: &mut V,
202+
block: &'thir Block,
203+
) {
195204
for &stmt in &*block.stmts {
196205
visitor.visit_stmt(&visitor.thir()[stmt]);
197206
}
@@ -200,7 +209,10 @@ pub fn walk_block<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, block: &B
200209
}
201210
}
202211

203-
pub fn walk_arm<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, arm: &Arm<'tcx>) {
212+
pub fn walk_arm<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
213+
visitor: &mut V,
214+
arm: &'thir Arm<'tcx>,
215+
) {
204216
match arm.guard {
205217
Some(Guard::If(expr)) => visitor.visit_expr(&visitor.thir()[expr]),
206218
Some(Guard::IfLet(ref pat, expr)) => {
@@ -213,7 +225,10 @@ pub fn walk_arm<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, arm: &Arm<'
213225
visitor.visit_expr(&visitor.thir()[arm.body]);
214226
}
215227

216-
pub fn walk_pat<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, pat: &Pat<'tcx>) {
228+
pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
229+
visitor: &mut V,
230+
pat: &'thir Pat<'tcx>,
231+
) {
217232
use PatKind::*;
218233
match &pat.kind {
219234
AscribeUserType { subpattern, ascription: _ }

compiler/rustc_mir_build/src/check_unsafety.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for LayoutConstrainedPlaceVisitor<'a, 'tcx> {
175175
self.thir
176176
}
177177

178-
fn visit_expr(&mut self, expr: &Expr<'tcx>) {
178+
fn visit_expr(&mut self, expr: &'a Expr<'tcx>) {
179179
match expr.kind {
180180
ExprKind::Field { lhs, .. } => {
181181
if let ty::Adt(adt_def, _) = self.thir[lhs].ty.kind() {
@@ -206,7 +206,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
206206
self.thir
207207
}
208208

209-
fn visit_block(&mut self, block: &Block) {
209+
fn visit_block(&mut self, block: &'a Block) {
210210
match block.safety_mode {
211211
// compiler-generated unsafe code should not count towards the usefulness of
212212
// an outer unsafe block
@@ -234,7 +234,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
234234
}
235235
}
236236

237-
fn visit_pat(&mut self, pat: &Pat<'tcx>) {
237+
fn visit_pat(&mut self, pat: &'a Pat<'tcx>) {
238238
if self.in_union_destructure {
239239
match pat.kind {
240240
// binding to a variable allows getting stuff out of variable
@@ -319,7 +319,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
319319
}
320320
}
321321

322-
fn visit_expr(&mut self, expr: &Expr<'tcx>) {
322+
fn visit_expr(&mut self, expr: &'a Expr<'tcx>) {
323323
// could we be in the LHS of an assignment to a field?
324324
match expr.kind {
325325
ExprKind::Field { .. }

compiler/rustc_mir_build/src/errors.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -453,14 +453,14 @@ pub enum UnusedUnsafeEnclosing {
453453
},
454454
}
455455

456-
pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
457-
pub cx: &'m RustcMatchCheckCtxt<'p, 'tcx>,
456+
pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'thir, 'tcx, 'm> {
457+
pub cx: &'m RustcMatchCheckCtxt<'p, 'thir, 'tcx>,
458458
pub expr_span: Span,
459459
pub span: Span,
460460
pub ty: Ty<'tcx>,
461461
}
462462

463-
impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
463+
impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_, '_> {
464464
fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'_> {
465465
let mut diag = DiagnosticBuilder::new(
466466
dcx,

0 commit comments

Comments
 (0)