Add support for `..=` syntax

Add ..= to the parser

Add ..= to libproc_macro

Add ..= to ICH

Highlight ..= in rustdoc

Update impl Debug for RangeInclusive to ..=

Replace `...` to `..=` in range docs

Make the dotdoteq warning point to the ...

Add warning for ... in expressions

Updated more tests to the ..= syntax

Updated even more tests to the ..= syntax

Updated the inclusive_range entry in unstable book
This commit is contained in:
Alex Burka 2017-09-19 05:40:04 +00:00 committed by Badel2
parent 3eb19bf9b1
commit e64efc91f4
33 changed files with 244 additions and 182 deletions

View File

@ -7,13 +7,13 @@ The tracking issue for this feature is: [#28237]
------------------------
To get a range that goes from 0 to 10 and includes the value 10, you
can write `0...10`:
can write `0..=10`:
```rust
#![feature(inclusive_range_syntax)]
fn main() {
for i in 0...10 {
for i in 0..=10 {
println!("{}", i);
}
}

View File

@ -182,7 +182,7 @@ fn test_range_small() {
fn test_range_inclusive() {
let size = 500;
let map: BTreeMap<_, _> = (0...size).map(|i| (i, i)).collect();
let map: BTreeMap<_, _> = (0..=size).map(|i| (i, i)).collect();
fn check<'a, L, R>(lhs: L, rhs: R)
where L: IntoIterator<Item=(&'a i32, &'a i32)>,
@ -193,18 +193,18 @@ fn test_range_inclusive() {
assert_eq!(lhs, rhs);
}
check(map.range(size + 1...size + 1), vec![]);
check(map.range(size...size), vec![(&size, &size)]);
check(map.range(size...size + 1), vec![(&size, &size)]);
check(map.range(0...0), vec![(&0, &0)]);
check(map.range(0...size - 1), map.range(..size));
check(map.range(-1...-1), vec![]);
check(map.range(-1...size), map.range(..));
check(map.range(...size), map.range(..));
check(map.range(...200), map.range(..201));
check(map.range(5...8), vec![(&5, &5), (&6, &6), (&7, &7), (&8, &8)]);
check(map.range(-1...0), vec![(&0, &0)]);
check(map.range(-1...2), vec![(&0, &0), (&1, &1), (&2, &2)]);
check(map.range(size + 1..=size + 1), vec![]);
check(map.range(size..=size), vec![(&size, &size)]);
check(map.range(size..=size + 1), vec![(&size, &size)]);
check(map.range(0..=0), vec![(&0, &0)]);
check(map.range(0..=size - 1), map.range(..size));
check(map.range(-1..=-1), vec![]);
check(map.range(-1..=size), map.range(..));
check(map.range(..=size), map.range(..));
check(map.range(..=200), map.range(..201));
check(map.range(5..=8), vec![(&5, &5), (&6, &6), (&7, &7), (&8, &8)]);
check(map.range(-1..=0), vec![(&0, &0)]);
check(map.range(-1..=2), vec![(&0, &0), (&1, &1), (&2, &2)]);
}
#[test]
@ -212,7 +212,7 @@ fn test_range_inclusive_max_value() {
let max = ::std::usize::MAX;
let map: BTreeMap<_, _> = vec![(max, 0)].into_iter().collect();
assert_eq!(map.range(max...max).collect::<Vec<_>>(), &[(&max, &0)]);
assert_eq!(map.range(max..=max).collect::<Vec<_>>(), &[(&max, &0)]);
}
#[test]

View File

@ -361,13 +361,13 @@ fn test_slice_fail() {
#[test]
#[should_panic]
fn test_str_slice_rangetoinclusive_max_panics() {
&"hello"[...usize::max_value()];
&"hello"[..=usize::max_value()];
}
#[test]
#[should_panic]
fn test_str_slice_rangeinclusive_max_panics() {
&"hello"[1...usize::max_value()];
&"hello"[1..=usize::max_value()];
}
#[test]
@ -375,7 +375,7 @@ fn test_str_slice_rangeinclusive_max_panics() {
fn test_str_slicemut_rangetoinclusive_max_panics() {
let mut s = "hello".to_owned();
let s: &mut str = &mut s;
&mut s[...usize::max_value()];
&mut s[..=usize::max_value()];
}
#[test]
@ -383,7 +383,7 @@ fn test_str_slicemut_rangetoinclusive_max_panics() {
fn test_str_slicemut_rangeinclusive_max_panics() {
let mut s = "hello".to_owned();
let s: &mut str = &mut s;
&mut s[1...usize::max_value()];
&mut s[1..=usize::max_value()];
}
#[test]
@ -391,13 +391,13 @@ fn test_str_get_maxinclusive() {
let mut s = "hello".to_owned();
{
let s: &str = &s;
assert_eq!(s.get(...usize::max_value()), None);
assert_eq!(s.get(1...usize::max_value()), None);
assert_eq!(s.get(..=usize::max_value()), None);
assert_eq!(s.get(1..=usize::max_value()), None);
}
{
let s: &mut str = &mut s;
assert_eq!(s.get(...usize::max_value()), None);
assert_eq!(s.get(1...usize::max_value()), None);
assert_eq!(s.get(..=usize::max_value()), None);
assert_eq!(s.get(1..=usize::max_value()), None);
}
}

View File

@ -456,9 +456,9 @@ fn test_splice_char_boundary() {
#[test]
fn test_splice_inclusive_range() {
let mut v = String::from("12345");
v.splice(2...3, "789");
v.splice(2..=3, "789");
assert_eq!(v, "127895");
v.splice(1...2, "A");
v.splice(1..=2, "A");
assert_eq!(v, "1A895");
}
@ -473,7 +473,7 @@ fn test_splice_out_of_bounds() {
#[should_panic]
fn test_splice_inclusive_out_of_bounds() {
let mut s = String::from("12345");
s.splice(5...5, "789");
s.splice(5..=5, "789");
}
#[test]

View File

@ -537,27 +537,27 @@ fn test_drain_range() {
#[test]
fn test_drain_inclusive_range() {
let mut v = vec!['a', 'b', 'c', 'd', 'e'];
for _ in v.drain(1...3) {
for _ in v.drain(1..=3) {
}
assert_eq!(v, &['a', 'e']);
let mut v: Vec<_> = (0...5).map(|x| x.to_string()).collect();
for _ in v.drain(1...5) {
let mut v: Vec<_> = (0..=5).map(|x| x.to_string()).collect();
for _ in v.drain(1..=5) {
}
assert_eq!(v, &["0".to_string()]);
let mut v: Vec<String> = (0...5).map(|x| x.to_string()).collect();
for _ in v.drain(0...5) {
let mut v: Vec<String> = (0..=5).map(|x| x.to_string()).collect();
for _ in v.drain(0..=5) {
}
assert_eq!(v, Vec::<String>::new());
let mut v: Vec<_> = (0...5).map(|x| x.to_string()).collect();
for _ in v.drain(0...3) {
let mut v: Vec<_> = (0..=5).map(|x| x.to_string()).collect();
for _ in v.drain(0..=3) {
}
assert_eq!(v, &["4".to_string(), "5".to_string()]);
let mut v: Vec<_> = (0...1).map(|x| x.to_string()).collect();
for _ in v.drain(...0) {
let mut v: Vec<_> = (0..=1).map(|x| x.to_string()).collect();
for _ in v.drain(..=0) {
}
assert_eq!(v, &["1".to_string()]);
}
@ -572,7 +572,7 @@ fn test_drain_max_vec_size() {
let mut v = Vec::<()>::with_capacity(usize::max_value());
unsafe { v.set_len(usize::max_value()); }
for _ in v.drain(usize::max_value() - 1...usize::max_value() - 1) {
for _ in v.drain(usize::max_value() - 1..=usize::max_value() - 1) {
}
assert_eq!(v.len(), usize::max_value() - 1);
}
@ -581,7 +581,7 @@ fn test_drain_max_vec_size() {
#[should_panic]
fn test_drain_inclusive_out_of_bounds() {
let mut v = vec![1, 2, 3, 4, 5];
v.drain(5...5);
v.drain(5..=5);
}
#[test]
@ -598,10 +598,10 @@ fn test_splice() {
fn test_splice_inclusive_range() {
let mut v = vec![1, 2, 3, 4, 5];
let a = [10, 11, 12];
let t1: Vec<_> = v.splice(2...3, a.iter().cloned()).collect();
let t1: Vec<_> = v.splice(2..=3, a.iter().cloned()).collect();
assert_eq!(v, &[1, 2, 10, 11, 12, 5]);
assert_eq!(t1, &[3, 4]);
let t2: Vec<_> = v.splice(1...2, Some(20)).collect();
let t2: Vec<_> = v.splice(1..=2, Some(20)).collect();
assert_eq!(v, &[1, 20, 11, 12, 5]);
assert_eq!(t2, &[2, 10]);
}
@ -619,7 +619,7 @@ fn test_splice_out_of_bounds() {
fn test_splice_inclusive_out_of_bounds() {
let mut v = vec![1, 2, 3, 4, 5];
let a = [10, 11, 12];
v.splice(5...5, a.iter().cloned());
v.splice(5..=5, a.iter().cloned());
}
#[test]

View File

@ -241,9 +241,9 @@ impl<Idx: PartialOrd<Idx>> RangeTo<Idx> {
}
}
/// An range bounded inclusively below and above (`start...end`).
/// An range bounded inclusively below and above (`start..=end`).
///
/// The `RangeInclusive` `start...end` contains all values with `x >= start`
/// The `RangeInclusive` `start..=end` contains all values with `x >= start`
/// and `x <= end`.
///
/// # Examples
@ -251,12 +251,12 @@ impl<Idx: PartialOrd<Idx>> RangeTo<Idx> {
/// ```
/// #![feature(inclusive_range,inclusive_range_syntax)]
///
/// assert_eq!((3...5), std::ops::RangeInclusive { start: 3, end: 5 });
/// assert_eq!(3 + 4 + 5, (3...5).sum());
/// assert_eq!((3..=5), std::ops::RangeInclusive { start: 3, end: 5 });
/// assert_eq!(3 + 4 + 5, (3..=5).sum());
///
/// let arr = [0, 1, 2, 3];
/// assert_eq!(arr[ ...2], [0,1,2 ]);
/// assert_eq!(arr[1...2], [ 1,2 ]); // RangeInclusive
/// assert_eq!(arr[ ..=2], [0,1,2 ]);
/// assert_eq!(arr[1..=2], [ 1,2 ]); // RangeInclusive
/// ```
#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
@ -276,7 +276,7 @@ pub struct RangeInclusive<Idx> {
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
impl<Idx: fmt::Debug> fmt::Debug for RangeInclusive<Idx> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{:?}...{:?}", self.start, self.end)
write!(fmt, "{:?}..={:?}", self.start, self.end)
}
}
@ -289,32 +289,32 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
/// ```
/// #![feature(range_contains,inclusive_range_syntax)]
///
/// assert!(!(3...5).contains(2));
/// assert!( (3...5).contains(3));
/// assert!( (3...5).contains(4));
/// assert!( (3...5).contains(5));
/// assert!(!(3...5).contains(6));
/// assert!(!(3..=5).contains(2));
/// assert!( (3..=5).contains(3));
/// assert!( (3..=5).contains(4));
/// assert!( (3..=5).contains(5));
/// assert!(!(3..=5).contains(6));
///
/// assert!( (3...3).contains(3));
/// assert!(!(3...2).contains(3));
/// assert!( (3..=3).contains(3));
/// assert!(!(3..=2).contains(3));
/// ```
pub fn contains(&self, item: Idx) -> bool {
self.start <= item && item <= self.end
}
}
/// A range only bounded inclusively above (`...end`).
/// A range only bounded inclusively above (`..=end`).
///
/// The `RangeToInclusive` `...end` contains all values with `x <= end`.
/// The `RangeToInclusive` `..=end` contains all values with `x <= end`.
/// It cannot serve as an [`Iterator`] because it doesn't have a starting point.
///
/// # Examples
///
/// The `...end` syntax is a `RangeToInclusive`:
/// The `..=end` syntax is a `RangeToInclusive`:
///
/// ```
/// #![feature(inclusive_range,inclusive_range_syntax)]
/// assert_eq!((...5), std::ops::RangeToInclusive{ end: 5 });
/// assert_eq!((..=5), std::ops::RangeToInclusive{ end: 5 });
/// ```
///
/// It does not have an [`IntoIterator`] implementation, so you can't use it in a
@ -325,7 +325,7 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
///
/// // error[E0277]: the trait bound `std::ops::RangeToInclusive<{integer}>:
/// // std::iter::Iterator` is not satisfied
/// for i in ...5 {
/// for i in ..=5 {
/// // ...
/// }
/// ```
@ -337,8 +337,8 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
/// #![feature(inclusive_range_syntax)]
///
/// let arr = [0, 1, 2, 3];
/// assert_eq!(arr[ ...2], [0,1,2 ]); // RangeToInclusive
/// assert_eq!(arr[1...2], [ 1,2 ]);
/// assert_eq!(arr[ ..=2], [0,1,2 ]); // RangeToInclusive
/// assert_eq!(arr[1..=2], [ 1,2 ]);
/// ```
///
/// [`IntoIterator`]: ../iter/trait.Iterator.html
@ -357,7 +357,7 @@ pub struct RangeToInclusive<Idx> {
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
impl<Idx: fmt::Debug> fmt::Debug for RangeToInclusive<Idx> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "...{:?}", self.end)
write!(fmt, "..={:?}", self.end)
}
}
@ -370,9 +370,9 @@ impl<Idx: PartialOrd<Idx>> RangeToInclusive<Idx> {
/// ```
/// #![feature(range_contains,inclusive_range_syntax)]
///
/// assert!( (...5).contains(-1_000_000_000));
/// assert!( (...5).contains(5));
/// assert!(!(...5).contains(6));
/// assert!( (..=5).contains(-1_000_000_000));
/// assert!( (..=5).contains(5));
/// assert!(!(..=5).contains(6));
/// ```
pub fn contains(&self, item: Idx) -> bool {
(item <= self.end)

View File

@ -16,6 +16,10 @@
#![stable(feature = "rust1", since = "1.0.0")]
// FIXME: replace remaining ... by ..= after next stage0
// Silence warning: "... is being replaced by ..="
#![cfg_attr(not(stage0), allow(warnings))]
// How this module is organized.
//
// The library infrastructure for slices is fairly messy. There's

View File

@ -1094,21 +1094,21 @@ fn test_range() {
#[test]
fn test_range_inclusive_exhaustion() {
let mut r = 10...10;
let mut r = 10..=10;
assert_eq!(r.next(), Some(10));
assert_eq!(r, 1...0);
assert_eq!(r, 1..=0);
let mut r = 10...10;
let mut r = 10..=10;
assert_eq!(r.next_back(), Some(10));
assert_eq!(r, 1...0);
assert_eq!(r, 1..=0);
let mut r = 10...12;
let mut r = 10..=12;
assert_eq!(r.nth(2), Some(12));
assert_eq!(r, 1...0);
assert_eq!(r, 1..=0);
let mut r = 10...12;
let mut r = 10..=12;
assert_eq!(r.nth(5), None);
assert_eq!(r, 1...0);
assert_eq!(r, 1..=0);
}
@ -1145,20 +1145,20 @@ fn test_range_from_nth() {
#[test]
fn test_range_inclusive_nth() {
assert_eq!((10...15).nth(0), Some(10));
assert_eq!((10...15).nth(1), Some(11));
assert_eq!((10...15).nth(5), Some(15));
assert_eq!((10...15).nth(6), None);
assert_eq!((10..=15).nth(0), Some(10));
assert_eq!((10..=15).nth(1), Some(11));
assert_eq!((10..=15).nth(5), Some(15));
assert_eq!((10..=15).nth(6), None);
let mut r = 10_u8...20;
let mut r = 10_u8..=20;
assert_eq!(r.nth(2), Some(12));
assert_eq!(r, 13...20);
assert_eq!(r, 13..=20);
assert_eq!(r.nth(2), Some(15));
assert_eq!(r, 16...20);
assert_eq!(r, 16..=20);
assert_eq!(r.is_empty(), false);
assert_eq!(r.nth(10), None);
assert_eq!(r.is_empty(), true);
assert_eq!(r, 1...0); // We may not want to document/promise this detail
assert_eq!(r, 1..=0); // We may not want to document/promise this detail
}
#[test]

View File

@ -509,6 +509,7 @@ impl TokenTree {
Dot => op!('.'),
DotDot => joint!('.', Dot),
DotDotDot => joint!('.', DotDot),
DotDotEq => joint!('.', DotEq),
Comma => op!(','),
Semi => op!(';'),
Colon => op!(':'),
@ -531,6 +532,7 @@ impl TokenTree {
})
}
DotEq => unreachable!(),
OpenDelim(..) | CloseDelim(..) => unreachable!(),
Whitespace | Comment | Shebang(..) | Eof => unreachable!(),
};

View File

@ -202,8 +202,8 @@ impl Quote for Token {
gen_match! {
Eq, Lt, Le, EqEq, Ne, Ge, Gt, AndAnd, OrOr, Not, Tilde, At, Dot, DotDot, DotDotDot,
Comma, Semi, Colon, ModSep, RArrow, LArrow, FatArrow, Pound, Dollar, Question,
Underscore;
DotDotEq, Comma, Semi, Colon, ModSep, RArrow, LArrow, FatArrow, Pound, Dollar,
Question, Underscore;
Token::OpenDelim(delim) => quote!(rt::token::OpenDelim((quote delim))),
Token::CloseDelim(delim) => quote!(rt::token::CloseDelim((quote delim))),

View File

@ -272,6 +272,8 @@ fn hash_token<'gcx, W: StableHasherResult>(token: &token::Token,
token::Token::Dot |
token::Token::DotDot |
token::Token::DotDotDot |
token::Token::DotDotEq |
token::Token::DotEq |
token::Token::Comma |
token::Token::Semi |
token::Token::Colon |

View File

@ -249,8 +249,8 @@ impl<'a> Classifier<'a> {
token::BinOpEq(..) | token::FatArrow => Class::Op,
// Miscellaneous, no highlighting.
token::Dot | token::DotDot | token::DotDotDot | token::Comma | token::Semi |
token::Colon | token::ModSep | token::LArrow | token::OpenDelim(_) |
token::Dot | token::DotDot | token::DotDotDot | token::DotDotEq | token::Comma |
token::Semi | token::Colon | token::ModSep | token::LArrow | token::OpenDelim(_) |
token::CloseDelim(token::Brace) | token::CloseDelim(token::Paren) |
token::CloseDelim(token::NoDelim) => Class::None,
@ -353,7 +353,7 @@ impl<'a> Classifier<'a> {
token::Lifetime(..) => Class::Lifetime,
token::Underscore | token::Eof | token::Interpolated(..) |
token::Tilde | token::At => Class::None,
token::Tilde | token::At | token::DotEq => Class::None,
};
// Anything that didn't return above is the simple case where we the

View File

@ -291,7 +291,7 @@ Erroneous code example:
fn main() {
let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1];
let x = &tmp[1...]; // error: inclusive range was used with no end
let x = &tmp[1..=]; // error: inclusive range was used with no end
}
```
@ -312,7 +312,7 @@ Or put an end to your inclusive range:
fn main() {
let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1];
let x = &tmp[1...3]; // ok!
let x = &tmp[1..=3]; // ok!
}
```
"##,

View File

@ -686,7 +686,9 @@ fn expr_mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> P<ast::Expr> {
token::At => "At",
token::Dot => "Dot",
token::DotDot => "DotDot",
token::DotEq => "DotEq",
token::DotDotDot => "DotDotDot",
token::DotDotEq => "DotDotEq",
token::Comma => "Comma",
token::Semi => "Semi",
token::Colon => "Colon",

View File

@ -261,7 +261,7 @@ declare_features! (
// rustc internal
(active, abi_vectorcall, "1.7.0", None),
// a...b and ...b
// a..=b and ..=b
(active, inclusive_range_syntax, "1.7.0", Some(28237)),
// X..Y patterns

View File

@ -1131,6 +1131,9 @@ impl<'a> StringReader<'a> {
if self.ch_is('.') {
self.bump();
Ok(token::DotDotDot)
} else if self.ch_is('=') {
self.bump();
Ok(token::DotDotEq)
} else {
Ok(token::DotDot)
}

View File

@ -432,7 +432,7 @@ impl Error {
Error::InclusiveRangeWithNoEnd => {
let mut err = struct_span_err!(handler, sp, E0586,
"inclusive range with no end");
err.help("inclusive ranges must be bounded at the end (`...b` or `a...b`)");
err.help("inclusive ranges must be bounded at the end (`..=b` or `a..=b`)");
err
}
}
@ -2710,7 +2710,7 @@ impl<'a> Parser<'a> {
LhsExpr::AttributesParsed(attrs) => Some(attrs),
_ => None,
};
if self.token == token::DotDot || self.token == token::DotDotDot {
if [token::DotDot, token::DotDotDot, token::DotDotEq].contains(&self.token) {
return self.parse_prefix_range_expr(attrs);
} else {
self.parse_prefix_expr(attrs)?
@ -2744,6 +2744,10 @@ impl<'a> Parser<'a> {
if op.precedence() < min_prec {
break;
}
// Warn about deprecated ... syntax (until SNAP)
if self.token == token::DotDotDot {
self.warn_dotdoteq(self.span);
}
self.bump();
if op.is_comparison() {
self.check_no_chained_comparison(&lhs, &op);
@ -2770,12 +2774,13 @@ impl<'a> Parser<'a> {
}
};
continue
} else if op == AssocOp::DotDot || op == AssocOp::DotDotDot {
// If we didnt have to handle `x..`/`x...`, it would be pretty easy to
} else if op == AssocOp::DotDot || op == AssocOp::DotDotEq {
// If we didnt have to handle `x..`/`x..=`, it would be pretty easy to
// generalise it to the Fixity::None code.
//
// We have 2 alternatives here: `x..y`/`x...y` and `x..`/`x...` The other
// We have 2 alternatives here: `x..y`/`x..=y` and `x..`/`x..=` The other
// two variants are handled with `parse_prefix_range_expr` call above.
// (and `x...y`/`x...` until SNAP)
let rhs = if self.is_at_start_of_range_notation_rhs() {
Some(self.parse_assoc_expr_with(op.precedence() + 1,
LhsExpr::NotYetParsed)?)
@ -2852,8 +2857,8 @@ impl<'a> Parser<'a> {
let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs);
self.mk_expr(span, aopexpr, ThinVec::new())
}
AssocOp::As | AssocOp::Colon | AssocOp::DotDot | AssocOp::DotDotDot => {
self.bug("As, Colon, DotDot or DotDotDot branch reached")
AssocOp::As | AssocOp::Colon | AssocOp::DotDot | AssocOp::DotDotEq => {
self.bug("AssocOp should have been handled by special case")
}
};
@ -2949,17 +2954,22 @@ impl<'a> Parser<'a> {
}
}
/// Parse prefix-forms of range notation: `..expr`, `..`, `...expr`
/// Parse prefix-forms of range notation: `..expr`, `..`, `..=expr` (and `...expr` until SNAP)
fn parse_prefix_range_expr(&mut self,
already_parsed_attrs: Option<ThinVec<Attribute>>)
-> PResult<'a, P<Expr>> {
debug_assert!(self.token == token::DotDot || self.token == token::DotDotDot,
"parse_prefix_range_expr: token {:?} is not DotDot or DotDotDot",
// SNAP remove DotDotDot
debug_assert!([token::DotDot, token::DotDotDot, token::DotDotEq].contains(&self.token),
"parse_prefix_range_expr: token {:?} is not DotDot/DotDotDot/DotDotEq",
self.token);
let tok = self.token.clone();
let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?;
let lo = self.span;
let mut hi = self.span;
// Warn about deprecated ... syntax (until SNAP)
if tok == token::DotDotDot {
self.warn_dotdoteq(self.span);
}
self.bump();
let opt_end = if self.is_at_start_of_range_notation_rhs() {
// RHS must be parsed with more associativity than the dots.
@ -3450,7 +3460,7 @@ impl<'a> Parser<'a> {
fn parse_as_ident(&mut self) -> bool {
self.look_ahead(1, |t| match *t {
token::OpenDelim(token::Paren) | token::OpenDelim(token::Brace) |
token::DotDotDot | token::ModSep | token::Not => Some(false),
token::DotDotDot | token::DotDotEq | token::ModSep | token::Not => Some(false),
// ensure slice patterns [a, b.., c] and [a, b, c..] don't go into the
// range pattern branch
token::DotDot => None,
@ -3544,11 +3554,12 @@ impl<'a> Parser<'a> {
let mac = respan(lo.to(self.prev_span), Mac_ { path: path, tts: tts });
pat = PatKind::Mac(mac);
}
token::DotDotDot | token::DotDot => {
token::DotDotDot | token::DotDotEq | token::DotDot => {
let end_kind = match self.token {
token::DotDot => RangeEnd::Excluded,
token::DotDotDot => RangeEnd::Included,
_ => panic!("can only parse `..` or `...` for ranges (checked above)"),
token::DotDotDot | token::DotDotEq => RangeEnd::Included,
_ => panic!("can only parse `..`/`...`/`..=` for ranges \
(checked above)"),
};
// Parse range
let span = lo.to(self.prev_span);
@ -3590,6 +3601,9 @@ impl<'a> Parser<'a> {
if self.eat(&token::DotDotDot) {
let end = self.parse_pat_range_end()?;
pat = PatKind::Range(begin, end, RangeEnd::Included);
} else if self.eat(&token::DotDotEq) {
let end = self.parse_pat_range_end()?;
pat = PatKind::Range(begin, end, RangeEnd::Included);
} else if self.eat(&token::DotDot) {
let end = self.parse_pat_range_end()?;
pat = PatKind::Range(begin, end, RangeEnd::Excluded);
@ -3973,7 +3987,7 @@ impl<'a> Parser<'a> {
token::BinOp(token::Minus) | token::BinOp(token::Star) |
token::BinOp(token::And) | token::BinOp(token::Or) |
token::AndAnd | token::OrOr |
token::DotDot | token::DotDotDot => false,
token::DotDot | token::DotDotDot | token::DotDotEq => false,
_ => true,
} {
self.warn_missing_semicolon();
@ -4195,6 +4209,12 @@ impl<'a> Parser<'a> {
}).emit();
}
fn warn_dotdoteq(&self, span: Span) {
self.diagnostic().struct_span_warn(span, {
"`...` is being replaced by `..=`"
}).emit();
}
// Parse bounds of a type parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
// BOUND = TY_BOUND | LT_BOUND
// LT_BOUND = LIFETIME (e.g. `'a`)

View File

@ -152,6 +152,8 @@ pub enum Token {
Dot,
DotDot,
DotDotDot,
DotDotEq,
DotEq, // HACK(durka42) never produced by the parser, only used for libproc_macro
Comma,
Semi,
Colon,
@ -212,18 +214,19 @@ impl Token {
pub fn can_begin_expr(&self) -> bool {
match *self {
Ident(ident) => ident_can_begin_expr(ident), // value name or keyword
OpenDelim(..) | // tuple, array or block
Literal(..) | // literal
Not | // operator not
BinOp(Minus) | // unary minus
BinOp(Star) | // dereference
BinOp(Or) | OrOr | // closure
BinOp(And) | // reference
AndAnd | // double reference
DotDot | DotDotDot | // range notation
Lt | BinOp(Shl) | // associated path
ModSep | // global path
Pound => true, // expression attributes
OpenDelim(..) | // tuple, array or block
Literal(..) | // literal
Not | // operator not
BinOp(Minus) | // unary minus
BinOp(Star) | // dereference
BinOp(Or) | OrOr | // closure
BinOp(And) | // reference
AndAnd | // double reference
DotDot | DotDotDot | DotDotEq | // range notation
// SNAP remove DotDotDot
Lt | BinOp(Shl) | // associated path
ModSep | // global path
Pound => true, // expression attributes
Interpolated(ref nt) => match nt.0 {
NtIdent(..) | NtExpr(..) | NtBlock(..) | NtPath(..) => true,
_ => false,
@ -402,10 +405,12 @@ impl Token {
Dot => match joint {
Dot => DotDot,
DotDot => DotDotDot,
DotEq => DotDotEq,
_ => return None,
},
DotDot => match joint {
Dot => DotDotDot,
Eq => DotDotEq,
_ => return None,
},
Colon => match joint {
@ -413,9 +418,9 @@ impl Token {
_ => return None,
},
Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot | Comma |
Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar | Question |
OpenDelim(..) | CloseDelim(..) | Underscore => return None,
Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot | DotEq |
DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar |
Question | OpenDelim(..) | CloseDelim(..) | Underscore => return None,
Literal(..) | Ident(..) | Lifetime(..) | Interpolated(..) | DocComment(..) |
Whitespace | Comment | Shebang(..) | Eof => return None,

View File

@ -203,6 +203,8 @@ pub fn token_to_string(tok: &Token) -> String {
token::Dot => ".".to_string(),
token::DotDot => "..".to_string(),
token::DotDotDot => "...".to_string(),
token::DotDotEq => "..=".to_string(),
token::DotEq => ".=".to_string(),
token::Comma => ",".to_string(),
token::Semi => ";".to_string(),
token::Colon => ":".to_string(),

View File

@ -62,8 +62,8 @@ pub enum AssocOp {
As,
/// `..` range
DotDot,
/// `...` range
DotDotDot,
/// `..=` range
DotDotEq,
/// `:`
Colon,
}
@ -105,7 +105,8 @@ impl AssocOp {
Token::AndAnd => Some(LAnd),
Token::OrOr => Some(LOr),
Token::DotDot => Some(DotDot),
Token::DotDotDot => Some(DotDotDot),
Token::DotDotEq => Some(DotDotEq),
Token::DotDotDot => Some(DotDotEq), // remove this after SNAP
Token::Colon => Some(Colon),
_ if t.is_keyword(keywords::As) => Some(As),
_ => None
@ -151,7 +152,7 @@ impl AssocOp {
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7,
LAnd => 6,
LOr => 5,
DotDot | DotDotDot => 4,
DotDot | DotDotEq => 4,
Inplace => 3,
Assign | AssignOp(_) => 2,
}
@ -166,7 +167,7 @@ impl AssocOp {
As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd |
BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual |
LAnd | LOr | Colon => Fixity::Left,
DotDot | DotDotDot => Fixity::None
DotDot | DotDotEq => Fixity::None
}
}
@ -176,7 +177,7 @@ impl AssocOp {
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
Inplace | Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract |
ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr |
DotDot | DotDotDot | Colon => false
DotDot | DotDotEq | Colon => false
}
}
@ -186,7 +187,7 @@ impl AssocOp {
Assign | AssignOp(_) | Inplace => true,
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply | Divide |
Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd |
LOr | DotDot | DotDotDot | Colon => false
LOr | DotDot | DotDotEq | Colon => false
}
}
@ -211,7 +212,7 @@ impl AssocOp {
BitOr => Some(BinOpKind::BitOr),
LAnd => Some(BinOpKind::And),
LOr => Some(BinOpKind::Or),
Inplace | Assign | AssignOp(_) | As | DotDot | DotDotDot | Colon => None
Inplace | Assign | AssignOp(_) | As | DotDot | DotDotEq | Colon => None
}
}
}

View File

@ -10,5 +10,5 @@
fn main() {
let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1];
let x = &tmp[1...]; //~ ERROR E0586
let x = &tmp[1..=]; //~ ERROR E0586
}

View File

@ -18,12 +18,12 @@ pub fn main() {
..1;
0..1;
...; //~ERROR inclusive range with no end
..=; //~ERROR inclusive range with no end
//~^HELP bounded at the end
0...; //~ERROR inclusive range with no end
0..=; //~ERROR inclusive range with no end
//~^HELP bounded at the end
...1;
0...1;
..=1;
0..=1;
}

View File

@ -14,7 +14,7 @@
// #![feature(inclusive_range)]
pub fn main() {
let _: std::ops::RangeInclusive<_> = { use std::intrinsics; 1 } ... { use std::intrinsics; 2 };
let _: std::ops::RangeInclusive<_> = { use std::intrinsics; 1 } ..= { use std::intrinsics; 2 };
//~^ ERROR use of unstable library feature 'inclusive_range'
//~| ERROR core_intrinsics
//~| ERROR core_intrinsics

View File

@ -153,5 +153,5 @@ fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] {
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] {
&slice[3...7]
&slice[3..=7]
}

View File

@ -11,5 +11,5 @@
// Parsing of range patterns
fn main() {
let macropus!() ... 11 = 12; //~ error: expected one of `:`, `;`, or `=`, found `...`
let macropus!() ..= 11 = 12; //~ error: expected one of `:`, `;`, or `=`, found `..=`
}

View File

@ -11,5 +11,5 @@
// Parsing of range patterns
fn main() {
let 10 ... makropulos!() = 12; //~ error: expected one of `::`, `:`, `;`, or `=`, found `!`
let 10 ..= makropulos!() = 12; //~ error: expected one of `::`, `:`, `;`, or `=`, found `!`
}

View File

@ -11,5 +11,5 @@
// Parsing of range patterns
fn main() {
let 10 ... 10 + 3 = 12; //~ expected one of `:`, `;`, or `=`, found `+`
let 10 ..= 10 + 3 = 12; //~ expected one of `:`, `;`, or `=`, found `+`
}

View File

@ -11,5 +11,6 @@
// Parsing of range patterns
fn main() {
let 10 - 3 ... 10 = 8; //~ error: expected one of `...`, `..`, `:`, `;`, or `=`, found `-`
let 10 - 3 ..= 10 = 8;
//~^ error: expected one of `...`, `..=`, `..`, `:`, `;`, or `=`, found `-`
}

View File

@ -13,7 +13,7 @@
#![feature(inclusive_range_syntax, inclusive_range)]
pub fn main() {
for _ in 1... {} //~ERROR inclusive range with no end
for _ in 1..= {} //~ERROR inclusive range with no end
//~^HELP bounded at the end
}

View File

@ -15,21 +15,21 @@
// #![feature(inclusive_range_syntax, inclusive_range)]
macro_rules! m {
() => { for _ in 1...10 {} } //~ ERROR inclusive range syntax is experimental
() => { for _ in 1..=10 {} } //~ ERROR inclusive range syntax is experimental
}
#[cfg(nope)]
fn f() {}
#[cfg(not(nope))]
fn f() {
for _ in 1...10 {} //~ ERROR inclusive range syntax is experimental
for _ in 1..=10 {} //~ ERROR inclusive range syntax is experimental
}
#[cfg(nope)]
macro_rules! n { () => {} }
#[cfg(not(nope))]
macro_rules! n {
() => { for _ in 1...10 {} } //~ ERROR inclusive range syntax is experimental
() => { for _ in 1..=10 {} } //~ ERROR inclusive range syntax is experimental
}
macro_rules! o {
@ -38,7 +38,7 @@ macro_rules! o {
fn g() {}
#[cfg(not(nope))]
fn g() {
for _ in 1...10 {} //~ ERROR inclusive range syntax is experimental
for _ in 1..=10 {} //~ ERROR inclusive range syntax is experimental
}
g();
@ -54,7 +54,7 @@ macro_rules! p {
fn h() {}
#[cfg(not(nope))]
fn h() {
for _ in 1...10 {} //~ ERROR inclusive range syntax is experimental
for _ in 1..=10 {} //~ ERROR inclusive range syntax is experimental
}
h();
@ -62,8 +62,8 @@ macro_rules! p {
}
pub fn main() {
for _ in 1...10 {} //~ ERROR inclusive range syntax is experimental
for _ in ...10 {} //~ ERROR inclusive range syntax is experimental
for _ in 1..=10 {} //~ ERROR inclusive range syntax is experimental
for _ in ..=10 {} //~ ERROR inclusive range syntax is experimental
f(); // not allowed in cfg'ed functions

View File

@ -0,0 +1,20 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test old and new syntax for inclusive range patterns.
fn main() {
assert!(match 42 { 0 ... 100 => true, _ => false });
assert!(match 42 { 0 ..= 100 => true, _ => false });
assert!(match 'x' { 'a' ... 'z' => true, _ => false });
assert!(match 'x' { 'a' ..= 'z' => true, _ => false });
}

View File

@ -17,18 +17,18 @@ use std::ops::{RangeInclusive, RangeToInclusive};
fn foo() -> isize { 42 }
// Test that range syntax works in return statements
fn return_range_to() -> RangeToInclusive<i32> { return ...1; }
fn return_range_to() -> RangeToInclusive<i32> { return ..=1; }
pub fn main() {
let mut count = 0;
for i in 0_usize...10 {
for i in 0_usize..=10 {
assert!(i >= 0 && i <= 10);
count += i;
}
assert_eq!(count, 55);
let mut count = 0;
let mut range = 0_usize...10;
let mut range = 0_usize..=10;
for i in range {
assert!(i >= 0 && i <= 10);
count += i;
@ -36,53 +36,53 @@ pub fn main() {
assert_eq!(count, 55);
let mut count = 0;
for i in (0_usize...10).step_by(2) {
for i in (0_usize..=10).step_by(2) {
assert!(i >= 0 && i <= 10 && i % 2 == 0);
count += i;
}
assert_eq!(count, 30);
let _ = 0_usize...4+4-3;
let _ = 0...foo();
let _ = 0_usize..=4+4-3;
let _ = 0..=foo();
let _ = { &42...&100 }; // references to literals are OK
let _ = ...42_usize;
let _ = { &42..=&100 }; // references to literals are OK
let _ = ..=42_usize;
// Test we can use two different types with a common supertype.
let x = &42;
{
let y = 42;
let _ = x...&y;
let _ = x..=&y;
}
// test collection indexing
let vec = (0...10).collect::<Vec<_>>();
let vec = (0..=10).collect::<Vec<_>>();
let slice: &[_] = &*vec;
let string = String::from("hello world");
let stir = "hello world";
assert_eq!(&vec[3...6], &[3, 4, 5, 6]);
assert_eq!(&vec[ ...6], &[0, 1, 2, 3, 4, 5, 6]);
assert_eq!(&vec[3..=6], &[3, 4, 5, 6]);
assert_eq!(&vec[ ..=6], &[0, 1, 2, 3, 4, 5, 6]);
assert_eq!(&slice[3...6], &[3, 4, 5, 6]);
assert_eq!(&slice[ ...6], &[0, 1, 2, 3, 4, 5, 6]);
assert_eq!(&slice[3..=6], &[3, 4, 5, 6]);
assert_eq!(&slice[ ..=6], &[0, 1, 2, 3, 4, 5, 6]);
assert_eq!(&string[3...6], "lo w");
assert_eq!(&string[ ...6], "hello w");
assert_eq!(&string[3..=6], "lo w");
assert_eq!(&string[ ..=6], "hello w");
assert_eq!(&stir[3...6], "lo w");
assert_eq!(&stir[ ...6], "hello w");
assert_eq!(&stir[3..=6], "lo w");
assert_eq!(&stir[ ..=6], "hello w");
// test the size hints and emptying
let mut long = 0...255u8;
let mut short = 42...42u8;
let mut long = 0..=255u8;
let mut short = 42..=42u8;
assert_eq!(long.size_hint(), (256, Some(256)));
assert_eq!(short.size_hint(), (1, Some(1)));
long.next();
short.next();
assert_eq!(long.size_hint(), (255, Some(255)));
assert_eq!(short.size_hint(), (0, Some(0)));
assert_eq!(short, 1...0);
assert_eq!(short, 1..=0);
assert_eq!(long.len(), 255);
assert_eq!(short.len(), 0);
@ -94,31 +94,31 @@ pub fn main() {
assert_eq!(long.next(), Some(1));
assert_eq!(long.next(), Some(2));
assert_eq!(long.next_back(), Some(252));
for i in 3...251 {
for i in 3..=251 {
assert_eq!(long.next(), Some(i));
}
assert_eq!(long, 1...0);
assert_eq!(long, 1..=0);
// check underflow
let mut narrow = 1...0;
let mut narrow = 1..=0;
assert_eq!(narrow.next_back(), None);
assert_eq!(narrow, 1...0);
let mut zero = 0u8...0;
assert_eq!(narrow, 1..=0);
let mut zero = 0u8..=0;
assert_eq!(zero.next_back(), Some(0));
assert_eq!(zero.next_back(), None);
assert_eq!(zero, 1...0);
let mut high = 255u8...255;
assert_eq!(zero, 1..=0);
let mut high = 255u8..=255;
assert_eq!(high.next_back(), Some(255));
assert_eq!(high.next_back(), None);
assert_eq!(high, 1...0);
assert_eq!(high, 1..=0);
// what happens if you have a nonsense range?
let mut nonsense = 10...5;
let mut nonsense = 10..=5;
assert_eq!(nonsense.next(), None);
assert_eq!(nonsense, 10...5);
assert_eq!(nonsense, 10..=5);
// output
assert_eq!(format!("{:?}", 0...10), "0...10");
assert_eq!(format!("{:?}", ...10), "...10");
assert_eq!(format!("{:?}", long), "1...0");
assert_eq!(format!("{:?}", 0..=10), "0..=10");
assert_eq!(format!("{:?}", ..=10), "..=10");
assert_eq!(format!("{:?}", long), "1..=0");
}

View File

@ -14,7 +14,7 @@
fn main() {
let mut count = 0;
for i in 0_usize...10 {
for i in 0_usize..=10 {
assert!(i >= 0 && i <= 10);
count += i;
}