about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-09-27 16:04:31 +0000
committerbors <bors@rust-lang.org>2017-09-27 16:04:31 +0000
commit0e6f4cf51cd3b799fb057956f8e733d16605d09b (patch)
tree56ff7787ca8fa8b1282782c5b14546d7a27870ef
parent1fd3a42c624faf91e9402942419ec409699fb94a (diff)
parent5102309b1ff63660309dead2e39ff9a9b554799a (diff)
downloadrust-0e6f4cf51cd3b799fb057956f8e733d16605d09b.tar.gz
rust-0e6f4cf51cd3b799fb057956f8e733d16605d09b.zip
Auto merge of #44709 - Badel2:inclusive-range-dotdoteq, r=petrochenkov
Initial support for `..=` syntax

#28237

This PR adds `..=` as a synonym for `...` in patterns and expressions.
Since `...` in expressions was never stable, we now issue a warning.

cc @durka
r? @aturon
-rw-r--r--src/Cargo.lock4
-rw-r--r--src/doc/unstable-book/src/language-features/inclusive-range-syntax.md4
-rw-r--r--src/liballoc/tests/btree/map.rs28
-rw-r--r--src/liballoc/tests/str.rs16
-rw-r--r--src/liballoc/tests/string.rs6
-rw-r--r--src/liballoc/tests/vec.rs28
-rw-r--r--src/libcore/ops/range.rs50
-rw-r--r--src/libcore/slice/mod.rs15
-rw-r--r--src/libcore/tests/iter.rs32
-rw-r--r--src/libproc_macro/lib.rs2
-rw-r--r--src/libproc_macro/quote.rs4
-rw-r--r--src/librustc/hir/lowering.rs2
-rw-r--r--src/librustc/ich/impls_syntax.rs2
-rw-r--r--src/librustdoc/html/highlight.rs6
-rw-r--r--src/libsyntax/ast.rs10
-rw-r--r--src/libsyntax/diagnostic_list.rs4
-rw-r--r--src/libsyntax/ext/quote.rs2
-rw-r--r--src/libsyntax/feature_gate.rs11
-rw-r--r--src/libsyntax/parse/lexer/mod.rs3
-rw-r--r--src/libsyntax/parse/parser.rs57
-rw-r--r--src/libsyntax/parse/token.rs35
-rw-r--r--src/libsyntax/print/pprust.rs7
-rw-r--r--src/libsyntax/util/parser.rs17
-rw-r--r--src/test/compile-fail/E0586.rs2
-rw-r--r--src/test/compile-fail/feature-gate-dotdoteq_in_patterns.rs16
-rw-r--r--src/test/compile-fail/impossible_range.rs8
-rw-r--r--src/test/compile-fail/range_inclusive_gate.rs2
-rw-r--r--src/test/incremental/hashes/indexing_expressions.rs2
-rw-r--r--src/test/parse-fail/pat-ranges-1.rs2
-rw-r--r--src/test/parse-fail/pat-ranges-2.rs2
-rw-r--r--src/test/parse-fail/pat-ranges-3.rs2
-rw-r--r--src/test/parse-fail/pat-ranges-4.rs3
-rw-r--r--src/test/parse-fail/range_inclusive.rs2
-rw-r--r--src/test/parse-fail/range_inclusive_gate.rs14
-rw-r--r--src/test/run-pass/inc-range-pat.rs22
-rw-r--r--src/test/run-pass/range_inclusive.rs68
-rw-r--r--src/test/run-pass/range_inclusive_gate.rs2
m---------src/tools/rustfmt14
38 files changed, 301 insertions, 205 deletions
diff --git a/src/Cargo.lock b/src/Cargo.lock
index e9a20a7c79c..c930c2073eb 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -1356,7 +1356,7 @@ dependencies = [
  "rls-rustc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rls-vfs 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustfmt-nightly 0.2.5",
+ "rustfmt-nightly 0.2.7",
  "serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1837,7 +1837,7 @@ dependencies = [
 
 [[package]]
 name = "rustfmt-nightly"
-version = "0.2.5"
+version = "0.2.7"
 dependencies = [
  "diff 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/src/doc/unstable-book/src/language-features/inclusive-range-syntax.md b/src/doc/unstable-book/src/language-features/inclusive-range-syntax.md
index 255445c318d..56f58803150 100644
--- a/src/doc/unstable-book/src/language-features/inclusive-range-syntax.md
+++ b/src/doc/unstable-book/src/language-features/inclusive-range-syntax.md
@@ -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);
     }
 }
diff --git a/src/liballoc/tests/btree/map.rs b/src/liballoc/tests/btree/map.rs
index 2c899d96940..2393101040d 100644
--- a/src/liballoc/tests/btree/map.rs
+++ b/src/liballoc/tests/btree/map.rs
@@ -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]
diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs
index 9d8ca38b20e..b3178064505 100644
--- a/src/liballoc/tests/str.rs
+++ b/src/liballoc/tests/str.rs
@@ -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);
     }
 }
 
diff --git a/src/liballoc/tests/string.rs b/src/liballoc/tests/string.rs
index 6aba18ddf49..ef6f5e10a72 100644
--- a/src/liballoc/tests/string.rs
+++ b/src/liballoc/tests/string.rs
@@ -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]
diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs
index 670ea8089fc..0e25da5bd30 100644
--- a/src/liballoc/tests/vec.rs
+++ b/src/liballoc/tests/vec.rs
@@ -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]
diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs
index 463a50491a8..3f573f7c7eb 100644
--- a/src/libcore/ops/range.rs
+++ b/src/libcore/ops/range.rs
@@ -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)
diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs
index dacc014955a..ae243f3f246 100644
--- a/src/libcore/slice/mod.rs
+++ b/src/libcore/slice/mod.rs
@@ -16,6 +16,9 @@
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
+// FIXME: after next stage0, change RangeInclusive { ... } back to ..=
+use ops::RangeInclusive;
+
 // How this module is organized.
 //
 // The library infrastructure for slices is fairly messy. There's
@@ -1044,32 +1047,32 @@ impl<T> SliceIndex<[T]> for ops::RangeToInclusive<usize> {
 
     #[inline]
     fn get(self, slice: &[T]) -> Option<&[T]> {
-        (0...self.end).get(slice)
+        (RangeInclusive { start: 0, end: self.end }).get(slice)
     }
 
     #[inline]
     fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> {
-        (0...self.end).get_mut(slice)
+        (RangeInclusive { start: 0, end: self.end }).get_mut(slice)
     }
 
     #[inline]
     unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
-        (0...self.end).get_unchecked(slice)
+        (RangeInclusive { start: 0, end: self.end }).get_unchecked(slice)
     }
 
     #[inline]
     unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
-        (0...self.end).get_unchecked_mut(slice)
+        (RangeInclusive { start: 0, end: self.end }).get_unchecked_mut(slice)
     }
 
     #[inline]
     fn index(self, slice: &[T]) -> &[T] {
-        (0...self.end).index(slice)
+        (RangeInclusive { start: 0, end: self.end }).index(slice)
     }
 
     #[inline]
     fn index_mut(self, slice: &mut [T]) -> &mut [T] {
-        (0...self.end).index_mut(slice)
+        (RangeInclusive { start: 0, end: self.end }).index_mut(slice)
     }
 }
 
diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs
index 59ae30de452..3c4ea974fc9 100644
--- a/src/libcore/tests/iter.rs
+++ b/src/libcore/tests/iter.rs
@@ -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]
diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs
index 07e933985a0..2c540c8de8f 100644
--- a/src/libproc_macro/lib.rs
+++ b/src/libproc_macro/lib.rs
@@ -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!(),
         };
diff --git a/src/libproc_macro/quote.rs b/src/libproc_macro/quote.rs
index 0db2b86b15f..8c1f6bfc11a 100644
--- a/src/libproc_macro/quote.rs
+++ b/src/libproc_macro/quote.rs
@@ -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))),
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 95b8e49d60c..411cdde3190 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -1864,7 +1864,7 @@ impl<'a> LoweringContext<'a> {
 
     fn lower_range_end(&mut self, e: &RangeEnd) -> hir::RangeEnd {
         match *e {
-            RangeEnd::Included => hir::RangeEnd::Included,
+            RangeEnd::Included(_) => hir::RangeEnd::Included,
             RangeEnd::Excluded => hir::RangeEnd::Excluded,
         }
     }
diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs
index 56ec6a65eb6..669e1ba773e 100644
--- a/src/librustc/ich/impls_syntax.rs
+++ b/src/librustc/ich/impls_syntax.rs
@@ -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 |
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 081f950e40d..98863b229b5 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -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
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 496be8b3eb2..0504e889ea1 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -538,11 +538,17 @@ pub enum BindingMode {
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub enum RangeEnd {
-    Included,
+    Included(RangeSyntax),
     Excluded,
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
+pub enum RangeSyntax {
+    DotDotDot,
+    DotDotEq,
+}
+
+#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub enum PatKind {
     /// Represents a wildcard pattern (`_`)
     Wild,
@@ -578,7 +584,7 @@ pub enum PatKind {
     Ref(P<Pat>, Mutability),
     /// A literal
     Lit(P<Expr>),
-    /// A range pattern, e.g. `1...2` or `1..2`
+    /// A range pattern, e.g. `1...2`, `1..=2` or `1..2`
     Range(P<Expr>, P<Expr>, RangeEnd),
     /// `[a, b, ..i, y, z]` is represented as:
     ///     `PatKind::Slice(box [a, b], Some(i), box [y, z])`
diff --git a/src/libsyntax/diagnostic_list.rs b/src/libsyntax/diagnostic_list.rs
index b29883670bd..c3cf4747835 100644
--- a/src/libsyntax/diagnostic_list.rs
+++ b/src/libsyntax/diagnostic_list.rs
@@ -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!
 }
 ```
 "##,
diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs
index c3f3a59c302..bd8c9a0ed40 100644
--- a/src/libsyntax/ext/quote.rs
+++ b/src/libsyntax/ext/quote.rs
@@ -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",
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 5c730aaa8d0..4da0df5b0de 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -26,7 +26,7 @@ use self::AttributeType::*;
 use self::AttributeGate::*;
 
 use abi::Abi;
-use ast::{self, NodeId, PatKind, RangeEnd};
+use ast::{self, NodeId, PatKind, RangeEnd, RangeSyntax};
 use attr;
 use codemap::Spanned;
 use syntax_pos::Span;
@@ -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
@@ -392,6 +392,9 @@ declare_features! (
 
     // allow `'_` placeholder lifetimes
     (active, underscore_lifetimes, "1.22.0", Some(44524)),
+
+    // allow `..=` in patterns (RFC 1192)
+    (active, dotdoteq_in_patterns, "1.22.0", Some(28237)),
 );
 
 declare_features! (
@@ -1491,6 +1494,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 gate_feature_post!(&self, exclusive_range_pattern, pattern.span,
                                    "exclusive range pattern syntax is experimental");
             }
+            PatKind::Range(_, _, RangeEnd::Included(RangeSyntax::DotDotEq)) => {
+                gate_feature_post!(&self, dotdoteq_in_patterns, pattern.span,
+                                   "`..=` syntax in patterns is experimental");
+            }
             _ => {}
         }
         visit::walk_pat(self, pattern)
diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs
index ce3f16d2ba1..1cb7b0eca58 100644
--- a/src/libsyntax/parse/lexer/mod.rs
+++ b/src/libsyntax/parse/lexer/mod.rs
@@ -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)
                     }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 3829be40a1e..d5ba4b54d90 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -38,7 +38,7 @@ use ast::{Ty, TyKind, TypeBinding, TyParam, TyParamBounds};
 use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
 use ast::{Visibility, WhereClause};
 use ast::{BinOpKind, UnOp};
-use ast::RangeEnd;
+use ast::{RangeEnd, RangeSyntax};
 use {ast, attr};
 use codemap::{self, CodeMap, Spanned, respan};
 use syntax_pos::{self, Span, BytePos};
@@ -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 didn’t have to handle `x..`/`x...`, it would be pretty easy to
+            } else if op == AssocOp::DotDot || op == AssocOp::DotDotEq {
+                // If we didn’t 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,13 @@ 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 => RangeEnd::Included(RangeSyntax::DotDotDot),
+                            token::DotDotEq => RangeEnd::Included(RangeSyntax::DotDotEq),
+                            _ => panic!("can only parse `..`/`...`/`..=` for ranges \
+                                         (checked above)"),
                         };
                         // Parse range
                         let span = lo.to(self.prev_span);
@@ -3589,7 +3601,12 @@ impl<'a> Parser<'a> {
                     Ok(begin) => {
                         if self.eat(&token::DotDotDot) {
                             let end = self.parse_pat_range_end()?;
-                            pat = PatKind::Range(begin, end, RangeEnd::Included);
+                            pat = PatKind::Range(begin, end,
+                                    RangeEnd::Included(RangeSyntax::DotDotDot));
+                        } else if self.eat(&token::DotDotEq) {
+                            let end = self.parse_pat_range_end()?;
+                            pat = PatKind::Range(begin, end,
+                                    RangeEnd::Included(RangeSyntax::DotDotEq));
                         } else if self.eat(&token::DotDot) {
                             let end = self.parse_pat_range_end()?;
                             pat = PatKind::Range(begin, end, RangeEnd::Excluded);
@@ -3973,7 +3990,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 +4212,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`)
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index a316733bdb5..4888654fac9 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -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,
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 9903dc50f36..959dd4ef30f 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -11,7 +11,7 @@
 pub use self::AnnNode::*;
 
 use abi::{self, Abi};
-use ast::{self, BlockCheckMode, PatKind, RangeEnd};
+use ast::{self, BlockCheckMode, PatKind, RangeEnd, RangeSyntax};
 use ast::{SelfKind, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
 use ast::Attribute;
 use util::parser::{self, AssocOp, Fixity};
@@ -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(),
@@ -2588,7 +2590,8 @@ impl<'a> State<'a> {
                 self.print_expr(begin)?;
                 self.s.space()?;
                 match *end_kind {
-                    RangeEnd::Included => self.s.word("...")?,
+                    RangeEnd::Included(RangeSyntax::DotDotDot) => self.s.word("...")?,
+                    RangeEnd::Included(RangeSyntax::DotDotEq) => self.s.word("..=")?,
                     RangeEnd::Excluded => self.s.word("..")?,
                 }
                 self.print_expr(end)?;
diff --git a/src/libsyntax/util/parser.rs b/src/libsyntax/util/parser.rs
index a4f06cb1b45..590874806d7 100644
--- a/src/libsyntax/util/parser.rs
+++ b/src/libsyntax/util/parser.rs
@@ -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
         }
     }
 }
diff --git a/src/test/compile-fail/E0586.rs b/src/test/compile-fail/E0586.rs
index 0b063569abc..c1bfc5c73a1 100644
--- a/src/test/compile-fail/E0586.rs
+++ b/src/test/compile-fail/E0586.rs
@@ -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
 }
diff --git a/src/test/compile-fail/feature-gate-dotdoteq_in_patterns.rs b/src/test/compile-fail/feature-gate-dotdoteq_in_patterns.rs
new file mode 100644
index 00000000000..1fb139bf07f
--- /dev/null
+++ b/src/test/compile-fail/feature-gate-dotdoteq_in_patterns.rs
@@ -0,0 +1,16 @@
+// Copyright 2014 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.
+
+pub fn main() {
+    match 22 {
+        0 ..= 3 => {} //~ ERROR `..=` syntax in patterns is experimental
+        _ => {}
+    }
+}
diff --git a/src/test/compile-fail/impossible_range.rs b/src/test/compile-fail/impossible_range.rs
index 94e048fed65..e4465e9f6b6 100644
--- a/src/test/compile-fail/impossible_range.rs
+++ b/src/test/compile-fail/impossible_range.rs
@@ -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;
 }
 
 
diff --git a/src/test/compile-fail/range_inclusive_gate.rs b/src/test/compile-fail/range_inclusive_gate.rs
index 1d1153e951b..69b9a4c67ad 100644
--- a/src/test/compile-fail/range_inclusive_gate.rs
+++ b/src/test/compile-fail/range_inclusive_gate.rs
@@ -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
diff --git a/src/test/incremental/hashes/indexing_expressions.rs b/src/test/incremental/hashes/indexing_expressions.rs
index bb31982d93f..a12624d0832 100644
--- a/src/test/incremental/hashes/indexing_expressions.rs
+++ b/src/test/incremental/hashes/indexing_expressions.rs
@@ -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]
 }
diff --git a/src/test/parse-fail/pat-ranges-1.rs b/src/test/parse-fail/pat-ranges-1.rs
index e1cbb961b1b..857a3924aec 100644
--- a/src/test/parse-fail/pat-ranges-1.rs
+++ b/src/test/parse-fail/pat-ranges-1.rs
@@ -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 `..=`
 }
diff --git a/src/test/parse-fail/pat-ranges-2.rs b/src/test/parse-fail/pat-ranges-2.rs
index 04ad5ff083b..64c749333cf 100644
--- a/src/test/parse-fail/pat-ranges-2.rs
+++ b/src/test/parse-fail/pat-ranges-2.rs
@@ -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 `!`
 }
diff --git a/src/test/parse-fail/pat-ranges-3.rs b/src/test/parse-fail/pat-ranges-3.rs
index 5f7aac71d29..1327a9fab36 100644
--- a/src/test/parse-fail/pat-ranges-3.rs
+++ b/src/test/parse-fail/pat-ranges-3.rs
@@ -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 `+`
 }
diff --git a/src/test/parse-fail/pat-ranges-4.rs b/src/test/parse-fail/pat-ranges-4.rs
index 4bbf387d1c0..c159c770250 100644
--- a/src/test/parse-fail/pat-ranges-4.rs
+++ b/src/test/parse-fail/pat-ranges-4.rs
@@ -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 `-`
 }
diff --git a/src/test/parse-fail/range_inclusive.rs b/src/test/parse-fail/range_inclusive.rs
index ce97372c668..cc32b9903b5 100644
--- a/src/test/parse-fail/range_inclusive.rs
+++ b/src/test/parse-fail/range_inclusive.rs
@@ -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
 }
 
diff --git a/src/test/parse-fail/range_inclusive_gate.rs b/src/test/parse-fail/range_inclusive_gate.rs
index 30dc6fc5b20..de690c3fea3 100644
--- a/src/test/parse-fail/range_inclusive_gate.rs
+++ b/src/test/parse-fail/range_inclusive_gate.rs
@@ -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
 
diff --git a/src/test/run-pass/inc-range-pat.rs b/src/test/run-pass/inc-range-pat.rs
new file mode 100644
index 00000000000..5faf36eddaf
--- /dev/null
+++ b/src/test/run-pass/inc-range-pat.rs
@@ -0,0 +1,22 @@
+// 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.
+#![feature(dotdoteq_in_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 });
+}
+
diff --git a/src/test/run-pass/range_inclusive.rs b/src/test/run-pass/range_inclusive.rs
index f6119e70999..71e11804052 100644
--- a/src/test/run-pass/range_inclusive.rs
+++ b/src/test/run-pass/range_inclusive.rs
@@ -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");
 }
diff --git a/src/test/run-pass/range_inclusive_gate.rs b/src/test/run-pass/range_inclusive_gate.rs
index 5e0ec19d6b3..570087aedbb 100644
--- a/src/test/run-pass/range_inclusive_gate.rs
+++ b/src/test/run-pass/range_inclusive_gate.rs
@@ -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;
     }
diff --git a/src/tools/rustfmt b/src/tools/rustfmt
-Subproject a1fd68da464fc51585f351c81fc2b867211c197
+Subproject 22eb5241c0ee5bb7eaf95e270a2b1500e82bf76