Skip to content

Commit 5d8a648

Browse files
committed
correctly check exclusive range patterns for overlap
1 parent d825f19 commit 5d8a648

File tree

4 files changed

+72
-19
lines changed

4 files changed

+72
-19
lines changed

clippy_lints/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#![feature(stmt_expr_attributes)]
1111
#![feature(repeat_str)]
1212
#![feature(conservative_impl_trait)]
13+
#![feature(collections_bound)]
1314

1415
#![allow(indexing_slicing, shadow_reuse, unknown_lints, missing_docs_in_private_items)]
1516
#![allow(needless_lifetimes)]

clippy_lints/src/matches.rs

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use rustc_const_eval::EvalHint::ExprTypeChecked;
66
use rustc_const_eval::ConstContext;
77
use rustc_const_math::ConstInt;
88
use std::cmp::Ordering;
9+
use std::collections::Bound;
910
use syntax::ast::LitKind;
1011
use syntax::codemap::Span;
1112
use utils::paths;
@@ -361,18 +362,22 @@ fn all_ranges(cx: &LateContext, arms: &[Arm]) -> Vec<SpannedRange<ConstVal>> {
361362
}
362363
.filter_map(|pat| {
363364
if_let_chain! {[
364-
let PatKind::Range(ref lhs, ref rhs, _) = pat.node,
365+
let PatKind::Range(ref lhs, ref rhs, ref range_end) = pat.node,
365366
let Ok(lhs) = constcx.eval(lhs, ExprTypeChecked),
366367
let Ok(rhs) = constcx.eval(rhs, ExprTypeChecked)
367368
], {
369+
let rhs = match *range_end {
370+
RangeEnd::Included => Bound::Included(rhs),
371+
RangeEnd::Excluded => Bound::Excluded(rhs),
372+
};
368373
return Some(SpannedRange { span: pat.span, node: (lhs, rhs) });
369374
}}
370375

371376
if_let_chain! {[
372377
let PatKind::Lit(ref value) = pat.node,
373378
let Ok(value) = constcx.eval(value, ExprTypeChecked)
374379
], {
375-
return Some(SpannedRange { span: pat.span, node: (value.clone(), value) });
380+
return Some(SpannedRange { span: pat.span, node: (value.clone(), Bound::Included(value)) });
376381
}}
377382

378383
None
@@ -384,7 +389,7 @@ fn all_ranges(cx: &LateContext, arms: &[Arm]) -> Vec<SpannedRange<ConstVal>> {
384389
#[derive(Debug, Eq, PartialEq)]
385390
pub struct SpannedRange<T> {
386391
pub span: Span,
387-
pub node: (T, T),
392+
pub node: (T, Bound<T>),
388393
}
389394

390395
type TypedRanges = Vec<SpannedRange<ConstInt>>;
@@ -393,13 +398,20 @@ type TypedRanges = Vec<SpannedRange<ConstInt>>;
393398
/// `Uint` and `Int` probably don't make sense.
394399
fn type_ranges(ranges: &[SpannedRange<ConstVal>]) -> TypedRanges {
395400
ranges.iter()
396-
.filter_map(|range| if let (ConstVal::Integral(start), ConstVal::Integral(end)) = range.node {
397-
Some(SpannedRange {
401+
.filter_map(|range| match range.node {
402+
(ConstVal::Integral(start), Bound::Included(ConstVal::Integral(end))) => Some(SpannedRange {
398403
span: range.span,
399-
node: (start, end),
400-
})
401-
} else {
402-
None
404+
node: (start, Bound::Included(end)),
405+
}),
406+
(ConstVal::Integral(start), Bound::Excluded(ConstVal::Integral(end))) => Some(SpannedRange {
407+
span: range.span,
408+
node: (start, Bound::Excluded(end)),
409+
}),
410+
(ConstVal::Integral(start), Bound::Unbounded) => Some(SpannedRange {
411+
span: range.span,
412+
node: (start, Bound::Unbounded),
413+
}),
414+
_ => None,
403415
})
404416
.collect()
405417
}
@@ -443,7 +455,7 @@ pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &
443455
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
444456
enum Kind<'a, T: 'a> {
445457
Start(T, &'a SpannedRange<T>),
446-
End(T, &'a SpannedRange<T>),
458+
End(Bound<T>, &'a SpannedRange<T>),
447459
}
448460

449461
impl<'a, T: Copy> Kind<'a, T> {
@@ -454,9 +466,9 @@ pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &
454466
}
455467
}
456468

457-
fn value(self) -> T {
469+
fn value(self) -> Bound<T> {
458470
match self {
459-
Kind::Start(t, _) |
471+
Kind::Start(t, _) => Bound::Included(t),
460472
Kind::End(t, _) => t,
461473
}
462474
}
@@ -470,7 +482,20 @@ pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &
470482

471483
impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
472484
fn cmp(&self, other: &Self) -> Ordering {
473-
self.value().cmp(&other.value())
485+
match (self.value(), other.value()) {
486+
(Bound::Included(a), Bound::Included(b)) |
487+
(Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
488+
(Bound::Unbounded, _) |
489+
(_, Bound::Unbounded) => unimplemented!(),
490+
(Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
491+
Ordering::Equal => Ordering::Greater,
492+
other => other,
493+
},
494+
(Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
495+
Ordering::Equal => Ordering::Less,
496+
other => other,
497+
},
498+
}
474499
}
475500
}
476501

@@ -490,7 +515,7 @@ pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &
490515
return Some((ra, rb));
491516
}
492517
},
493-
(&Kind::End(a, _), &Kind::Start(b, _)) if a != b => (),
518+
(&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
494519
_ => return Some((a.range(), b.range())),
495520
}
496521
}

tests/compile-fail/matches.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![feature(plugin)]
2+
#![feature(exclusive_range_pattern)]
23

34
#![plugin(clippy)]
45
#![deny(clippy)]
@@ -245,12 +246,36 @@ fn overlapping() {
245246
_ => (),
246247
}
247248

249+
match 42 {
250+
2 => println!("2"), //~NOTE overlaps with this
251+
0 ... 2 => println!("0 ... 5"), //~ERROR: some ranges overlap
252+
_ => (),
253+
}
254+
248255
match 42 {
249256
0 ... 10 => println!("0 ... 10"),
250257
11 ... 50 => println!("0 ... 10"),
251258
_ => (),
252259
}
253260

261+
match 42 {
262+
2 => println!("2"),
263+
0 .. 2 => println!("0 .. 2"),
264+
_ => (),
265+
}
266+
267+
match 42 {
268+
0 .. 10 => println!("0 ... 10"),
269+
10 .. 50 => println!("0 ... 10"),
270+
_ => (),
271+
}
272+
273+
match 42 {
274+
0 .. 11 => println!("0 ... 10"), //~ERROR: some ranges overlap
275+
0 ... 11 => println!("0 ... 10"), //~NOTE overlaps with this
276+
_ => (),
277+
}
278+
254279
if let None = Some(42) {
255280
// nothing
256281
} else if let None = Some(42) {

tests/matches.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#![feature(rustc_private)]
2+
#![feature(collections_bound)]
23

34
extern crate clippy_lints;
45
extern crate syntax;
6+
use std::collections::Bound;
57

68
#[test]
79
fn test_overlapping() {
@@ -16,9 +18,9 @@ fn test_overlapping() {
1618
};
1719

1820
assert_eq!(None, overlapping::<u8>(&[]));
19-
assert_eq!(None, overlapping(&[sp(1, 4)]));
20-
assert_eq!(None, overlapping(&[sp(1, 4), sp(5, 6)]));
21-
assert_eq!(None, overlapping(&[sp(1, 4), sp(5, 6), sp(10, 11)]));
22-
assert_eq!(Some((&sp(1, 4), &sp(3, 6))), overlapping(&[sp(1, 4), sp(3, 6)]));
23-
assert_eq!(Some((&sp(5, 6), &sp(6, 11))), overlapping(&[sp(1, 4), sp(5, 6), sp(6, 11)]));
21+
assert_eq!(None, overlapping(&[sp(1, Bound::Included(4))]));
22+
assert_eq!(None, overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6))]));
23+
assert_eq!(None, overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6)), sp(10, Bound::Included(11))]));
24+
assert_eq!(Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))), overlapping(&[sp(1, Bound::Included(4)), sp(3, Bound::Included(6))]));
25+
assert_eq!(Some((&sp(5, Bound::Included(6)), &sp(6, Bound::Included(11)))), overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6)), sp(6, Bound::Included(11))]));
2426
}

0 commit comments

Comments
 (0)