Merge pull request #1490 from Manishearth/excl_range_pat_overlap

correctly check exclusive range patterns for overlap
This commit is contained in:
Oliver Schneider 2017-01-31 12:40:21 +01:00 committed by GitHub
commit 909ef37f08
4 changed files with 90 additions and 23 deletions

View File

@ -10,6 +10,7 @@
#![feature(stmt_expr_attributes)] #![feature(stmt_expr_attributes)]
#![feature(repeat_str)] #![feature(repeat_str)]
#![feature(conservative_impl_trait)] #![feature(conservative_impl_trait)]
#![feature(collections_bound)]
#![allow(indexing_slicing, shadow_reuse, unknown_lints, missing_docs_in_private_items)] #![allow(indexing_slicing, shadow_reuse, unknown_lints, missing_docs_in_private_items)]
#![allow(needless_lifetimes)] #![allow(needless_lifetimes)]

View File

@ -6,6 +6,7 @@
use rustc_const_eval::ConstContext; use rustc_const_eval::ConstContext;
use rustc_const_math::ConstInt; use rustc_const_math::ConstInt;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::Bound;
use syntax::ast::LitKind; use syntax::ast::LitKind;
use syntax::codemap::Span; use syntax::codemap::Span;
use utils::paths; use utils::paths;
@ -361,10 +362,14 @@ fn all_ranges(cx: &LateContext, arms: &[Arm]) -> Vec<SpannedRange<ConstVal>> {
} }
.filter_map(|pat| { .filter_map(|pat| {
if_let_chain! {[ if_let_chain! {[
let PatKind::Range(ref lhs, ref rhs, _) = pat.node, let PatKind::Range(ref lhs, ref rhs, ref range_end) = pat.node,
let Ok(lhs) = constcx.eval(lhs, ExprTypeChecked), let Ok(lhs) = constcx.eval(lhs, ExprTypeChecked),
let Ok(rhs) = constcx.eval(rhs, ExprTypeChecked) let Ok(rhs) = constcx.eval(rhs, ExprTypeChecked)
], { ], {
let rhs = match *range_end {
RangeEnd::Included => Bound::Included(rhs),
RangeEnd::Excluded => Bound::Excluded(rhs),
};
return Some(SpannedRange { span: pat.span, node: (lhs, rhs) }); return Some(SpannedRange { span: pat.span, node: (lhs, rhs) });
}} }}
@ -372,7 +377,7 @@ fn all_ranges(cx: &LateContext, arms: &[Arm]) -> Vec<SpannedRange<ConstVal>> {
let PatKind::Lit(ref value) = pat.node, let PatKind::Lit(ref value) = pat.node,
let Ok(value) = constcx.eval(value, ExprTypeChecked) let Ok(value) = constcx.eval(value, ExprTypeChecked)
], { ], {
return Some(SpannedRange { span: pat.span, node: (value.clone(), value) }); return Some(SpannedRange { span: pat.span, node: (value.clone(), Bound::Included(value)) });
}} }}
None None
@ -384,7 +389,7 @@ fn all_ranges(cx: &LateContext, arms: &[Arm]) -> Vec<SpannedRange<ConstVal>> {
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub struct SpannedRange<T> { pub struct SpannedRange<T> {
pub span: Span, pub span: Span,
pub node: (T, T), pub node: (T, Bound<T>),
} }
type TypedRanges = Vec<SpannedRange<ConstInt>>; type TypedRanges = Vec<SpannedRange<ConstInt>>;
@ -393,13 +398,26 @@ pub struct SpannedRange<T> {
/// `Uint` and `Int` probably don't make sense. /// `Uint` and `Int` probably don't make sense.
fn type_ranges(ranges: &[SpannedRange<ConstVal>]) -> TypedRanges { fn type_ranges(ranges: &[SpannedRange<ConstVal>]) -> TypedRanges {
ranges.iter() ranges.iter()
.filter_map(|range| if let (ConstVal::Integral(start), ConstVal::Integral(end)) = range.node { .filter_map(|range| match range.node {
Some(SpannedRange { (ConstVal::Integral(start), Bound::Included(ConstVal::Integral(end))) => {
span: range.span, Some(SpannedRange {
node: (start, end), span: range.span,
}) node: (start, Bound::Included(end)),
} else { })
None },
(ConstVal::Integral(start), Bound::Excluded(ConstVal::Integral(end))) => {
Some(SpannedRange {
span: range.span,
node: (start, Bound::Excluded(end)),
})
},
(ConstVal::Integral(start), Bound::Unbounded) => {
Some(SpannedRange {
span: range.span,
node: (start, Bound::Unbounded),
})
},
_ => None,
}) })
.collect() .collect()
} }
@ -443,7 +461,7 @@ pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum Kind<'a, T: 'a> { enum Kind<'a, T: 'a> {
Start(T, &'a SpannedRange<T>), Start(T, &'a SpannedRange<T>),
End(T, &'a SpannedRange<T>), End(Bound<T>, &'a SpannedRange<T>),
} }
impl<'a, T: Copy> Kind<'a, T> { impl<'a, T: Copy> Kind<'a, T> {
@ -454,9 +472,9 @@ fn range(&self) -> &'a SpannedRange<T> {
} }
} }
fn value(self) -> T { fn value(self) -> Bound<T> {
match self { match self {
Kind::Start(t, _) | Kind::Start(t, _) => Bound::Included(t),
Kind::End(t, _) => t, Kind::End(t, _) => t,
} }
} }
@ -470,7 +488,25 @@ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
impl<'a, T: Copy + Ord> Ord for Kind<'a, T> { impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
self.value().cmp(&other.value()) match (self.value(), other.value()) {
(Bound::Included(a), Bound::Included(b)) |
(Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
// Range patterns cannot be unbounded (yet)
(Bound::Unbounded, _) |
(_, Bound::Unbounded) => unimplemented!(),
(Bound::Included(a), Bound::Excluded(b)) => {
match a.cmp(&b) {
Ordering::Equal => Ordering::Greater,
other => other,
}
},
(Bound::Excluded(a), Bound::Included(b)) => {
match a.cmp(&b) {
Ordering::Equal => Ordering::Less,
other => other,
}
},
}
} }
} }
@ -490,7 +526,7 @@ fn cmp(&self, other: &Self) -> Ordering {
return Some((ra, rb)); return Some((ra, rb));
} }
}, },
(&Kind::End(a, _), &Kind::Start(b, _)) if a != b => (), (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
_ => return Some((a.range(), b.range())), _ => return Some((a.range(), b.range())),
} }
} }

View File

@ -1,4 +1,5 @@
#![feature(plugin)] #![feature(plugin)]
#![feature(exclusive_range_pattern)]
#![plugin(clippy)] #![plugin(clippy)]
#![deny(clippy)] #![deny(clippy)]
@ -228,14 +229,14 @@ fn overlapping() {
match 42 { match 42 {
0 ... 10 => println!("0 ... 10"), //~ERROR: some ranges overlap 0 ... 10 => println!("0 ... 10"), //~ERROR: some ranges overlap
0 ... 11 => println!("0 ... 10"), //~NOTE overlaps with this 0 ... 11 => println!("0 ... 11"), //~NOTE overlaps with this
_ => (), _ => (),
} }
match 42 { match 42 {
0 ... 5 => println!("0 ... 5"), //~ERROR: some ranges overlap 0 ... 5 => println!("0 ... 5"), //~ERROR: some ranges overlap
6 ... 7 => println!("6 ... 7"), 6 ... 7 => println!("6 ... 7"),
FOO ... 11 => println!("0 ... 10"), //~NOTE overlaps with this FOO ... 11 => println!("0 ... 11"), //~NOTE overlaps with this
_ => (), _ => (),
} }
@ -245,9 +246,33 @@ fn overlapping() {
_ => (), _ => (),
} }
match 42 {
2 => println!("2"), //~NOTE overlaps with this
0 ... 2 => println!("0 ... 2"), //~ERROR: some ranges overlap
_ => (),
}
match 42 { match 42 {
0 ... 10 => println!("0 ... 10"), 0 ... 10 => println!("0 ... 10"),
11 ... 50 => println!("0 ... 10"), 11 ... 50 => println!("11 ... 50"),
_ => (),
}
match 42 {
2 => println!("2"),
0 .. 2 => println!("0 .. 2"),
_ => (),
}
match 42 {
0 .. 10 => println!("0 .. 10"),
10 .. 50 => println!("10 .. 50"),
_ => (),
}
match 42 {
0 .. 11 => println!("0 .. 11"), //~ERROR: some ranges overlap
0 ... 11 => println!("0 ... 11"), //~NOTE overlaps with this
_ => (), _ => (),
} }

View File

@ -1,7 +1,9 @@
#![feature(rustc_private)] #![feature(rustc_private)]
#![feature(collections_bound)]
extern crate clippy_lints; extern crate clippy_lints;
extern crate syntax; extern crate syntax;
use std::collections::Bound;
#[test] #[test]
fn test_overlapping() { fn test_overlapping() {
@ -16,9 +18,12 @@ fn test_overlapping() {
}; };
assert_eq!(None, overlapping::<u8>(&[])); assert_eq!(None, overlapping::<u8>(&[]));
assert_eq!(None, overlapping(&[sp(1, 4)])); assert_eq!(None, overlapping(&[sp(1, Bound::Included(4))]));
assert_eq!(None, overlapping(&[sp(1, 4), sp(5, 6)])); assert_eq!(None, overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6))]));
assert_eq!(None, overlapping(&[sp(1, 4), sp(5, 6), sp(10, 11)])); assert_eq!(None,
assert_eq!(Some((&sp(1, 4), &sp(3, 6))), overlapping(&[sp(1, 4), sp(3, 6)])); overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6)), sp(10, Bound::Included(11))]));
assert_eq!(Some((&sp(5, 6), &sp(6, 11))), overlapping(&[sp(1, 4), sp(5, 6), sp(6, 11)])); assert_eq!(Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))),
overlapping(&[sp(1, Bound::Included(4)), sp(3, Bound::Included(6))]));
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))]));
} }