about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-03-28 02:41:28 +0000
committerbors <bors@rust-lang.org>2018-03-28 02:41:28 +0000
commit59ec9bfc66b86f04b50f00bb32839315f59252ec (patch)
tree19b13ac2494f1e1b347ee8aae9c48150677434f3
parent9c9424de51da41fd3d1077ac7810276f8dc746fa (diff)
parent605ea7c31f7341995c2d1ae12b4b33fe6bd908b5 (diff)
Auto merge of #49406 - kennytm:rollup, r=kennytm
Rollup of 11 pull requests

- Successful merges: #48639, #49223, #49333, #49369, #49381, #49395, #49399, #49401, #49417, #49202, #49426
- Failed merges:
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--RELEASES.md14
-rw-r--r--src/Cargo.lock12
-rw-r--r--src/liballoc/benches/lib.rs1
-rw-r--r--src/liballoc/benches/slice.rs15
-rw-r--r--src/liballoc/fmt.rs15
-rw-r--r--src/liballoc/slice.rs88
-rw-r--r--src/liballoc/str.rs42
-rw-r--r--src/liballoc/tests/lib.rs1
-rw-r--r--src/liballoc/tests/slice.rs19
m---------src/libcompiler_builtins0
-rw-r--r--src/librustc/hir/print.rs8
-rw-r--r--src/librustc_typeck/check/method/suggest.rs17
-rw-r--r--src/librustdoc/clean/mod.rs14
-rw-r--r--src/librustdoc/test.rs2
-rw-r--r--src/libsyntax/parse/mod.rs1
-rw-r--r--src/libsyntax/parse/obsolete.rs67
-rw-r--r--src/libsyntax/parse/parser.rs6
-rw-r--r--src/libsyntax/parse/token.rs9
-rw-r--r--src/libsyntax/print/pprust.rs6
-rw-r--r--src/test/run-pass/rfc-2151-raw-identifiers/attr.rs2
-rw-r--r--src/test/run-pass/rfc-2151-raw-identifiers/basic.rs2
-rw-r--r--src/test/run-pass/rfc-2151-raw-identifiers/items.rs2
-rw-r--r--src/test/run-pass/rfc-2151-raw-identifiers/macros.rs2
-rw-r--r--src/test/rustdoc/link-assoc-const.rs26
-rw-r--r--src/test/ui/issue-23217.stderr2
-rw-r--r--src/test/ui/issue-28971.stderr2
27 files changed, 252 insertions, 125 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2b389888e51..7a62405f059 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -594,7 +594,7 @@ If you're looking for somewhere to start, check out the [E-easy][eeasy] tag.
 [inom]: https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Aissue+label%3AI-nominated
 [eeasy]: https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy
 [lru]: https://github.com/rust-lang/rust/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc
-[rfcbot]: https://github.com/dikaiosune/rust-dashboard/blob/master/RFCBOT.md
+[rfcbot]: https://github.com/anp/rfcbot-rs/
 
 ## Out-of-tree Contributions
 [out-of-tree-contributions]: #out-of-tree-contributions
diff --git a/RELEASES.md b/RELEASES.md
index 51c36c99858..100de005990 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -98,6 +98,20 @@ Compatibility Notes
 [`ptr::NonNull`]: https://doc.rust-lang.org/std/ptr/struct.NonNull.html
 
 
+Version 1.24.1 (2018-03-01)
+==========================
+
+ - [Do not abort when unwinding through FFI][48251]
+ - [Emit UTF-16 files for linker arguments on Windows][48318]
+ - [Make the error index generator work again][48308]
+ - [Cargo will warn on Windows 7 if an update is needed][cargo/5069].
+
+[48251]: https://github.com/rust-lang/rust/issues/48251
+[48308]: https://github.com/rust-lang/rust/issues/48308
+[48318]: https://github.com/rust-lang/rust/issues/48318
+[cargo/5069]: https://github.com/rust-lang/cargo/pull/5069
+
+
 Version 1.24.0 (2018-02-15)
 ==========================
 
diff --git a/src/Cargo.lock b/src/Cargo.lock
index c4707707be0..ad745d29e15 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -202,7 +202,7 @@ dependencies = [
  "shell-escape 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "tar 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "termcolor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -558,7 +558,7 @@ dependencies = [
  "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "termcolor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1543,7 +1543,7 @@ dependencies = [
  "rustc-ap-serialize 67.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-ap-syntax_pos 67.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "termcolor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -1721,7 +1721,7 @@ dependencies = [
  "rustc_data_structures 0.0.0",
  "serialize 0.0.0",
  "syntax_pos 0.0.0",
- "termcolor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -2326,7 +2326,7 @@ dependencies = [
 
 [[package]]
 name = "termcolor"
-version = "0.3.5"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2765,7 +2765,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
 "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1"
 "checksum term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6b677dd1e8214ea1ef4297f85dbcbed8e8cdddb561040cc998ca2551c37561"
-"checksum termcolor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "56c456352e44f9f91f774ddeeed27c1ec60a2455ed66d692059acfb1d731bda1"
+"checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83"
 "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
 "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
 "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
diff --git a/src/liballoc/benches/lib.rs b/src/liballoc/benches/lib.rs
index 09685d1bb40..a43aadfe9a2 100644
--- a/src/liballoc/benches/lib.rs
+++ b/src/liballoc/benches/lib.rs
@@ -13,6 +13,7 @@
 #![cfg_attr(stage0, feature(i128_type))]
 #![feature(rand)]
 #![feature(repr_simd)]
+#![feature(slice_sort_by_cached_key)]
 #![feature(test)]
 
 extern crate rand;
diff --git a/src/liballoc/benches/slice.rs b/src/liballoc/benches/slice.rs
index ee5182a1d46..a699ff9c0a7 100644
--- a/src/liballoc/benches/slice.rs
+++ b/src/liballoc/benches/slice.rs
@@ -284,6 +284,17 @@ macro_rules! sort_expensive {
     }
 }
 
+macro_rules! sort_lexicographic {
+    ($f:ident, $name:ident, $gen:expr, $len:expr) => {
+        #[bench]
+        fn $name(b: &mut Bencher) {
+            let v = $gen($len);
+            b.iter(|| v.clone().$f(|x| x.to_string()));
+            b.bytes = $len * mem::size_of_val(&$gen(1)[0]) as u64;
+        }
+    }
+}
+
 sort!(sort, sort_small_ascending, gen_ascending, 10);
 sort!(sort, sort_small_descending, gen_descending, 10);
 sort!(sort, sort_small_random, gen_random, 10);
@@ -312,6 +323,10 @@ sort!(sort_unstable, sort_unstable_large_big, gen_big_random, 10000);
 sort_strings!(sort_unstable, sort_unstable_large_strings, gen_strings, 10000);
 sort_expensive!(sort_unstable_by, sort_unstable_large_expensive, gen_random, 10000);
 
+sort_lexicographic!(sort_by_key, sort_by_key_lexicographic, gen_random, 10000);
+sort_lexicographic!(sort_unstable_by_key, sort_unstable_by_key_lexicographic, gen_random, 10000);
+sort_lexicographic!(sort_by_cached_key, sort_by_cached_key_lexicographic, gen_random, 10000);
+
 macro_rules! reverse {
     ($name:ident, $ty:ty, $f:expr) => {
         #[bench]
diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs
index 90043e1c716..b2c4582e840 100644
--- a/src/liballoc/fmt.rs
+++ b/src/liballoc/fmt.rs
@@ -326,7 +326,7 @@
 //! sign := '+' | '-'
 //! width := count
 //! precision := count | '*'
-//! type := identifier | ''
+//! type := identifier | '?' | ''
 //! count := parameter | integer
 //! parameter := argument '$'
 //! ```
@@ -516,17 +516,17 @@ pub use core::fmt::rt;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::fmt::{Formatter, Result, Write};
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use core::fmt::{Octal, Binary};
+pub use core::fmt::{Binary, Octal};
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use core::fmt::{Display, Debug};
+pub use core::fmt::{Debug, Display};
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use core::fmt::{LowerHex, UpperHex, Pointer};
+pub use core::fmt::{LowerHex, Pointer, UpperHex};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::fmt::{LowerExp, UpperExp};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::fmt::Error;
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use core::fmt::{ArgumentV1, Arguments, write};
+pub use core::fmt::{write, ArgumentV1, Arguments};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
 
@@ -563,7 +563,8 @@ use string;
 pub fn format(args: Arguments) -> string::String {
     let capacity = args.estimated_capacity();
     let mut output = string::String::with_capacity(capacity);
-    output.write_fmt(args)
-          .expect("a formatting trait implementation returned an error");
+    output
+        .write_fmt(args)
+        .expect("a formatting trait implementation returned an error");
     output
 }
diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs
index dc40062ef13..68f2313843c 100644
--- a/src/liballoc/slice.rs
+++ b/src/liballoc/slice.rs
@@ -102,6 +102,7 @@ use core::mem::size_of;
 use core::mem;
 use core::ptr;
 use core::slice as core_slice;
+use core::{u8, u16, u32};
 
 use borrow::{Borrow, BorrowMut, ToOwned};
 use boxed::Box;
@@ -1302,7 +1303,8 @@ impl<T> [T] {
 
     /// Sorts the slice with a key extraction function.
     ///
-    /// This sort is stable (i.e. does not reorder equal elements) and `O(n log n)` worst-case.
+    /// This sort is stable (i.e. does not reorder equal elements) and `O(m n log(m n))`
+    /// worst-case, where the key function is `O(m)`.
     ///
     /// When applicable, unstable sorting is preferred because it is generally faster than stable
     /// sorting and it doesn't allocate auxiliary memory.
@@ -1328,12 +1330,82 @@ impl<T> [T] {
     /// ```
     #[stable(feature = "slice_sort_by_key", since = "1.7.0")]
     #[inline]
-    pub fn sort_by_key<B, F>(&mut self, mut f: F)
-        where F: FnMut(&T) -> B, B: Ord
+    pub fn sort_by_key<K, F>(&mut self, mut f: F)
+        where F: FnMut(&T) -> K, K: Ord
     {
         merge_sort(self, |a, b| f(a).lt(&f(b)));
     }
 
+    /// Sorts the slice with a key extraction function.
+    ///
+    /// During sorting, the key function is called only once per element.
+    ///
+    /// This sort is stable (i.e. does not reorder equal elements) and `O(m n + n log n)`
+    /// worst-case, where the key function is `O(m)`.
+    ///
+    /// For simple key functions (e.g. functions that are property accesses or
+    /// basic operations), [`sort_by_key`](#method.sort_by_key) is likely to be
+    /// faster.
+    ///
+    /// # Current implementation
+    ///
+    /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters,
+    /// which combines the fast average case of randomized quicksort with the fast worst case of
+    /// heapsort, while achieving linear time on slices with certain patterns. It uses some
+    /// randomization to avoid degenerate cases, but with a fixed seed to always provide
+    /// deterministic behavior.
+    ///
+    /// In the worst case, the algorithm allocates temporary storage in a `Vec<(K, usize)>` the
+    /// length of the slice.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(slice_sort_by_cached_key)]
+    /// let mut v = [-5i32, 4, 32, -3, 2];
+    ///
+    /// v.sort_by_cached_key(|k| k.to_string());
+    /// assert!(v == [-3, -5, 2, 32, 4]);
+    /// ```
+    ///
+    /// [pdqsort]: https://github.com/orlp/pdqsort
+    #[unstable(feature = "slice_sort_by_cached_key", issue = "34447")]
+    #[inline]
+    pub fn sort_by_cached_key<K, F>(&mut self, f: F)
+        where F: FnMut(&T) -> K, K: Ord
+    {
+        // Helper macro for indexing our vector by the smallest possible type, to reduce allocation.
+        macro_rules! sort_by_key {
+            ($t:ty, $slice:ident, $f:ident) => ({
+                let mut indices: Vec<_> =
+                    $slice.iter().map($f).enumerate().map(|(i, k)| (k, i as $t)).collect();
+                // The elements of `indices` are unique, as they are indexed, so any sort will be
+                // stable with respect to the original slice. We use `sort_unstable` here because
+                // it requires less memory allocation.
+                indices.sort_unstable();
+                for i in 0..$slice.len() {
+                    let mut index = indices[i].1;
+                    while (index as usize) < i {
+                        index = indices[index as usize].1;
+                    }
+                    indices[i].1 = index;
+                    $slice.swap(i, index as usize);
+                }
+            })
+        }
+
+        let sz_u8    = mem::size_of::<(K, u8)>();
+        let sz_u16   = mem::size_of::<(K, u16)>();
+        let sz_u32   = mem::size_of::<(K, u32)>();
+        let sz_usize = mem::size_of::<(K, usize)>();
+
+        let len = self.len();
+        if sz_u8  < sz_u16   && len <= ( u8::MAX as usize) { return sort_by_key!( u8, self, f) }
+        if sz_u16 < sz_u32   && len <= (u16::MAX as usize) { return sort_by_key!(u16, self, f) }
+        if sz_u32 < sz_usize && len <= (u32::MAX as usize) { return sort_by_key!(u32, self, f) }
+        sort_by_key!(usize, self, f)
+    }
+
     /// Sorts the slice, but may not preserve the order of equal elements.
     ///
     /// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate),
@@ -1410,7 +1482,7 @@ impl<T> [T] {
     /// elements.
     ///
     /// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate),
-    /// and `O(n log n)` worst-case.
+    /// and `O(m n log(m n))` worst-case, where the key function is `O(m)`.
     ///
     /// # Current implementation
     ///
@@ -1420,9 +1492,6 @@ impl<T> [T] {
     /// randomization to avoid degenerate cases, but with a fixed seed to always provide
     /// deterministic behavior.
     ///
-    /// It is typically faster than stable sorting, except in a few special cases, e.g. when the
-    /// slice consists of several concatenated sorted sequences.
-    ///
     /// # Examples
     ///
     /// ```
@@ -1435,9 +1504,8 @@ impl<T> [T] {
     /// [pdqsort]: https://github.com/orlp/pdqsort
     #[stable(feature = "sort_unstable", since = "1.20.0")]
     #[inline]
-    pub fn sort_unstable_by_key<B, F>(&mut self, f: F)
-        where F: FnMut(&T) -> B,
-              B: Ord
+    pub fn sort_unstable_by_key<K, F>(&mut self, f: F)
+        where F: FnMut(&T) -> K, K: Ord
     {
         core_slice::SliceExt::sort_unstable_by_key(self, f);
     }
diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs
index 14d5e96d2e7..d5ef41df0d8 100644
--- a/src/liballoc/str.rs
+++ b/src/liballoc/str.rs
@@ -2122,6 +2122,48 @@ impl str {
         unsafe { String::from_utf8_unchecked(buf) }
     }
 
+    /// Returns true if this `str` is entirely whitespace, and false otherwise.
+    ///
+    /// 'Whitespace' is defined according to the terms of the Unicode Derived Core
+    /// Property `White_Space`.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// assert!("    \t ".is_whitespace());
+    ///
+    /// // a non-breaking space
+    /// assert!("\u{A0}".is_whitespace());
+    ///
+    /// assert!(!"   越".is_whitespace());
+    /// ```
+    #[stable(feature = "unicode_methods_on_intrinsics", since = "1.27.0")]
+    #[inline]
+    pub fn is_whitespace(&self) -> bool {
+        UnicodeStr::is_whitespace(self)
+    }
+
+    /// Returns true if this `str` is entirely alphanumeric, and false otherwise.
+    ///
+    /// 'Alphanumeric'-ness is defined in terms of the Unicode General Categories
+    /// 'Nd', 'Nl', 'No' and the Derived Core Property 'Alphabetic'.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// assert!("٣7৬Kو藏".is_alphanumeric());
+    /// assert!(!"¾①".is_alphanumeric());
+    /// ```
+    #[stable(feature = "unicode_methods_on_intrinsics", since = "1.27.0")]
+    #[inline]
+    pub fn is_alphanumeric(&self) -> bool {
+        UnicodeStr::is_alphanumeric(self)
+    }
+
     /// Checks if all characters in this string are within the ASCII range.
     ///
     /// # Examples
diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs
index bcd2ef27605..0a7e9a8be94 100644
--- a/src/liballoc/tests/lib.rs
+++ b/src/liballoc/tests/lib.rs
@@ -23,6 +23,7 @@
 #![feature(pattern)]
 #![feature(placement_in_syntax)]
 #![feature(rand)]
+#![feature(slice_sort_by_cached_key)]
 #![feature(splice)]
 #![feature(str_escape)]
 #![feature(string_retain)]
diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs
index 3f679d81f08..99d9c51efc7 100644
--- a/src/liballoc/tests/slice.rs
+++ b/src/liballoc/tests/slice.rs
@@ -425,6 +425,14 @@ fn test_sort() {
                 v.sort_by(|a, b| b.cmp(a));
                 assert!(v.windows(2).all(|w| w[0] >= w[1]));
 
+                // Sort in lexicographic order.
+                let mut v1 = orig.clone();
+                let mut v2 = orig.clone();
+                v1.sort_by_key(|x| x.to_string());
+                v2.sort_by_cached_key(|x| x.to_string());
+                assert!(v1.windows(2).all(|w| w[0].to_string() <= w[1].to_string()));
+                assert!(v1 == v2);
+
                 // Sort with many pre-sorted runs.
                 let mut v = orig.clone();
                 v.sort();
@@ -477,7 +485,7 @@ fn test_sort_stability() {
             // the second item represents which occurrence of that
             // number this element is, i.e. the second elements
             // will occur in sorted order.
-            let mut v: Vec<_> = (0..len)
+            let mut orig: Vec<_> = (0..len)
                 .map(|_| {
                     let n = thread_rng().gen::<usize>() % 10;
                     counts[n] += 1;
@@ -485,16 +493,21 @@ fn test_sort_stability() {
                 })
                 .collect();
 
-            // only sort on the first element, so an unstable sort
+            let mut v = orig.clone();
+            // Only sort on the first element, so an unstable sort
             // may mix up the counts.
             v.sort_by(|&(a, _), &(b, _)| a.cmp(&b));
 
-            // this comparison includes the count (the second item
+            // This comparison includes the count (the second item
             // of the tuple), so elements with equal first items
             // will need to be ordered with increasing
             // counts... i.e. exactly asserting that this sort is
             // stable.
             assert!(v.windows(2).all(|w| w[0] <= w[1]));
+
+            let mut v = orig.clone();
+            v.sort_by_cached_key(|&(x, _)| x);
+            assert!(v.windows(2).all(|w| w[0] <= w[1]));
         }
     }
 }
diff --git a/src/libcompiler_builtins b/src/libcompiler_builtins
-Subproject 263a703b10351d8930e48045b4fd09768991b86
+Subproject 2a2f6d96c8dc578d2474742f14c9bab0b36b040
diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs
index 3d38c0c8ed9..ff501f30c89 100644
--- a/src/librustc/hir/print.rs
+++ b/src/librustc/hir/print.rs
@@ -13,7 +13,7 @@ pub use self::AnnNode::*;
 use syntax::abi::Abi;
 use syntax::ast;
 use syntax::codemap::{CodeMap, Spanned};
-use syntax::parse::ParseSess;
+use syntax::parse::{token, ParseSess};
 use syntax::parse::lexer::comments;
 use syntax::print::pp::{self, Breaks};
 use syntax::print::pp::Breaks::{Consistent, Inconsistent};
@@ -1561,7 +1561,11 @@ impl<'a> State<'a> {
     }
 
     pub fn print_name(&mut self, name: ast::Name) -> io::Result<()> {
-        self.s.word(&name.as_str())?;
+        if token::is_raw_guess(ast::Ident::with_empty_ctxt(name)) {
+            self.s.word(&format!("r#{}", name))?;
+        } else {
+            self.s.word(&name.as_str())?;
+        }
         self.ann.post(self, NodeName(&name))
     }
 
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 61afac97d64..06bec8f6ff6 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -22,12 +22,14 @@ use rustc::traits::{Obligation, SelectionContext};
 use util::nodemap::FxHashSet;
 
 use syntax::ast;
+use syntax::util::lev_distance::find_best_match_for_name;
 use errors::DiagnosticBuilder;
 use syntax_pos::Span;
 
 use rustc::hir;
 use rustc::hir::print;
 use rustc::infer::type_variable::TypeVariableOrigin;
+use rustc::ty::TyAdt;
 
 use std::cell;
 use std::cmp::Ordering;
@@ -179,9 +181,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 let actual = self.resolve_type_vars_if_possible(&rcvr_ty);
                 let ty_string = self.ty_to_string(actual);
                 let is_method = mode == Mode::MethodCall;
+                let mut suggestion = None;
                 let type_str = if is_method {
                     "method"
                 } else if actual.is_enum() {
+                    if let TyAdt(ref adt_def, _) = actual.sty {
+                        let names = adt_def.variants.iter().map(|s| &s.name);
+                        suggestion = find_best_match_for_name(names,
+                                                              &item_name.as_str(),
+                                                              None);
+                    }
                     "variant"
                 } else {
                     match (item_name.as_str().chars().next(), actual.is_fresh_ty()) {
@@ -256,7 +265,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                         err.emit();
                         return;
                     } else {
-                        struct_span_err!(
+                        let mut err = struct_span_err!(
                             tcx.sess,
                             span,
                             E0599,
@@ -264,7 +273,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                             type_str,
                             item_name,
                             ty_string
-                        )
+                        );
+                        if let Some(suggestion) = suggestion {
+                            err.note(&format!("did you mean `{}::{}`?", type_str, suggestion));
+                        }
+                        err
                     }
                 } else {
                     tcx.sess.diagnostic().struct_dummy()
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 1a42b02140c..73b3ae32cef 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1022,11 +1022,12 @@ fn resolve(cx: &DocContext, path_str: &str, is_val: bool) -> Result<(Def, Option
                                  .flat_map(|imp| cx.tcx.associated_items(*imp))
                                  .find(|item| item.name == item_name);
                 if let Some(item) = item {
-                    if item.kind == ty::AssociatedKind::Method && is_val {
-                        Ok((ty.def, Some(format!("method.{}", item_name))))
-                    } else {
-                        Err(())
-                    }
+                    let out = match item.kind {
+                        ty::AssociatedKind::Method if is_val => "method",
+                        ty::AssociatedKind::Const if is_val => "associatedconstant",
+                        _ => return Err(())
+                    };
+                    Ok((ty.def, Some(format!("{}.{}", out, item_name))))
                 } else {
                     Err(())
                 }
@@ -1139,9 +1140,6 @@ impl Clean<Attributes> for [ast::Attribute] {
                         &link[..]
                     }.trim();
 
-                    // avoid resolving things (i.e. regular links) which aren't like paths
-                    // FIXME(Manishearth) given that most links have slashes in them might be worth
-                    // doing a check for slashes first
                     if path_str.contains(|ch: char| !(ch.is_alphanumeric() ||
                                                       ch == ':' || ch == '_')) {
                         continue;
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index 3ce8bd4ebb4..8ab9ca45187 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -406,8 +406,6 @@ pub fn make_test(s: &str,
 
 // FIXME(aburka): use a real parser to deal with multiline attributes
 fn partition_source(s: &str) -> (String, String) {
-    use std_unicode::str::UnicodeStr;
-
     let mut after_header = false;
     let mut before = String::new();
     let mut after = String::new();
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index 7b39db16ac2..1483691a1ea 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -40,7 +40,6 @@ pub mod attr;
 
 pub mod common;
 pub mod classify;
-pub mod obsolete;
 
 /// Info about a parsing session.
 pub struct ParseSess {
diff --git a/src/libsyntax/parse/obsolete.rs b/src/libsyntax/parse/obsolete.rs
deleted file mode 100644
index 49a697edf41..00000000000
--- a/src/libsyntax/parse/obsolete.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2012-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.
-
-//! Support for parsing unsupported, old syntaxes, for the purpose of reporting errors. Parsing of
-//! these syntaxes is tested by compile-test/obsolete-syntax.rs.
-//!
-//! Obsolete syntax that becomes too hard to parse can be removed.
-
-use syntax_pos::Span;
-use parse::parser;
-
-/// The specific types of unsupported syntax
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub enum ObsoleteSyntax {
-    // Nothing here at the moment
-}
-
-pub trait ParserObsoleteMethods {
-    /// Reports an obsolete syntax non-fatal error.
-    fn obsolete(&mut self, sp: Span, kind: ObsoleteSyntax);
-    fn report(&mut self,
-              sp: Span,
-              kind: ObsoleteSyntax,
-              kind_str: &str,
-              desc: &str,
-              error: bool);
-}
-
-impl<'a> ParserObsoleteMethods for parser::Parser<'a> {
-    /// Reports an obsolete syntax non-fatal error.
-    #[allow(unused_variables)]
-    #[allow(unreachable_code)]
-    fn obsolete(&mut self, sp: Span, kind: ObsoleteSyntax) {
-        let (kind_str, desc, error) = match kind {
-            // Nothing here at the moment
-        };
-
-        self.report(sp, kind, kind_str, desc, error);
-    }
-
-    fn report(&mut self,
-              sp: Span,
-              kind: ObsoleteSyntax,
-              kind_str: &str,
-              desc: &str,
-              error: bool) {
-        let mut err = if error {
-            self.diagnostic().struct_span_err(sp, &format!("obsolete syntax: {}", kind_str))
-        } else {
-            self.diagnostic().struct_span_warn(sp, &format!("obsolete syntax: {}", kind_str))
-        };
-
-        if !self.obsolete_set.contains(&kind) &&
-            (error || self.sess.span_diagnostic.flags.can_emit_warnings) {
-            err.note(desc);
-            self.obsolete_set.insert(kind);
-        }
-        err.emit();
-    }
-}
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 98e2528d30f..b4b21285d3b 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -48,7 +48,6 @@ use parse::{self, classify, token};
 use parse::common::SeqSep;
 use parse::lexer::TokenAndSpan;
 use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
-use parse::obsolete::ObsoleteSyntax;
 use parse::{new_sub_parser_from_file, ParseSess, Directory, DirectoryOwnership};
 use util::parser::{AssocOp, Fixity};
 use print::pprust;
@@ -59,7 +58,6 @@ use symbol::{Symbol, keywords};
 use util::ThinVec;
 
 use std::cmp;
-use std::collections::HashSet;
 use std::mem;
 use std::path::{self, Path, PathBuf};
 use std::slice;
@@ -229,9 +227,6 @@ pub struct Parser<'a> {
     /// the previous token kind
     prev_token_kind: PrevTokenKind,
     pub restrictions: Restrictions,
-    /// The set of seen errors about obsolete syntax. Used to suppress
-    /// extra detail when the same error is seen twice
-    pub obsolete_set: HashSet<ObsoleteSyntax>,
     /// Used to determine the path to externally loaded source files
     pub directory: Directory,
     /// Whether to parse sub-modules in other files.
@@ -555,7 +550,6 @@ impl<'a> Parser<'a> {
             meta_var_span: None,
             prev_token_kind: PrevTokenKind::Other,
             restrictions: Restrictions::empty(),
-            obsolete_set: HashSet::new(),
             recurse_into_file_modules,
             directory: Directory {
                 path: PathBuf::new(),
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index 7798a7a77ee..e2dfca5d10a 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -142,6 +142,13 @@ pub fn is_path_segment_keyword(id: ast::Ident) -> bool {
     id.name == keywords::DollarCrate.name()
 }
 
+// We see this identifier in a normal identifier position, like variable name or a type.
+// How was it written originally? Did it use the raw form? Let's try to guess.
+pub fn is_raw_guess(ident: ast::Ident) -> bool {
+    ident.name != keywords::Invalid.name() &&
+    is_reserved_ident(ident) && !is_path_segment_keyword(ident)
+}
+
 // Returns true for reserved identifiers used internally for elided lifetimes,
 // unnamed method parameters, crate root module, error recovery etc.
 pub fn is_special_ident(id: ast::Ident) -> bool {
@@ -236,7 +243,7 @@ impl Token {
 
     /// Recovers a `Token` from an `ast::Ident`. This creates a raw identifier if necessary.
     pub fn from_ast_ident(ident: ast::Ident) -> Token {
-        Ident(ident, is_reserved_ident(ident) && !is_path_segment_keyword(ident))
+        Ident(ident, is_raw_guess(ident))
     }
 
     /// Returns `true` if the token starts with '>'.
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 50577a26abf..ae045fc095a 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -2373,7 +2373,11 @@ impl<'a> State<'a> {
     }
 
     pub fn print_ident(&mut self, ident: ast::Ident) -> io::Result<()> {
-        self.s.word(&ident.name.as_str())?;
+        if token::is_raw_guess(ident) {
+            self.s.word(&format!("r#{}", ident))?;
+        } else {
+            self.s.word(&ident.name.as_str())?;
+        }
         self.ann.post(self, NodeIdent(&ident))
     }
 
diff --git a/src/test/run-pass/rfc-2151-raw-identifiers/attr.rs b/src/test/run-pass/rfc-2151-raw-identifiers/attr.rs
index 3566babaf4c..6cea75cf1d1 100644
--- a/src/test/run-pass/rfc-2151-raw-identifiers/attr.rs
+++ b/src/test/run-pass/rfc-2151-raw-identifiers/attr.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// ignore-pretty
-
 #![feature(raw_identifiers)]
 
 use std::mem;
diff --git a/src/test/run-pass/rfc-2151-raw-identifiers/basic.rs b/src/test/run-pass/rfc-2151-raw-identifiers/basic.rs
index bd1f52a9b24..5d495c4e9e5 100644
--- a/src/test/run-pass/rfc-2151-raw-identifiers/basic.rs
+++ b/src/test/run-pass/rfc-2151-raw-identifiers/basic.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// ignore-pretty
-
 #![feature(raw_identifiers)]
 
 fn r#fn(r#match: u32) -> u32 {
diff --git a/src/test/run-pass/rfc-2151-raw-identifiers/items.rs b/src/test/run-pass/rfc-2151-raw-identifiers/items.rs
index 5fdc13df8dc..256bd263d38 100644
--- a/src/test/run-pass/rfc-2151-raw-identifiers/items.rs
+++ b/src/test/run-pass/rfc-2151-raw-identifiers/items.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// ignore-pretty
-
 #![feature(raw_identifiers)]
 
 #[derive(Debug, PartialEq, Eq)]
diff --git a/src/test/run-pass/rfc-2151-raw-identifiers/macros.rs b/src/test/run-pass/rfc-2151-raw-identifiers/macros.rs
index 82d44c57e18..4bd16ded52f 100644
--- a/src/test/run-pass/rfc-2151-raw-identifiers/macros.rs
+++ b/src/test/run-pass/rfc-2151-raw-identifiers/macros.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// ignore-pretty
-
 #![feature(decl_macro)]
 #![feature(raw_identifiers)]
 
diff --git a/src/test/rustdoc/link-assoc-const.rs b/src/test/rustdoc/link-assoc-const.rs
new file mode 100644
index 00000000000..aa7ef07d5c0
--- /dev/null
+++ b/src/test/rustdoc/link-assoc-const.rs
@@ -0,0 +1,26 @@
+// Copyright 2018 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.
+
+#![crate_name = "foo"]
+
+// @has foo/index.html '//a[@href="../foo/foo/constant.FIRSTCONST.html"]' 'foo::FIRSTCONST'
+// @has foo/index.html '//a[@href="../foo/struct.Bar.html#associatedconstant.CONST"]' 'Bar::CONST'
+
+//! We have here [`foo::FIRSTCONST`] and [`Bar::CONST`].
+
+pub mod foo {
+    pub const FIRSTCONST: u32 = 42;
+}
+
+pub struct Bar;
+
+impl Bar {
+    pub const CONST: u32 = 42;
+}
diff --git a/src/test/ui/issue-23217.stderr b/src/test/ui/issue-23217.stderr
index be9ec9d73c2..d542a10e9b6 100644
--- a/src/test/ui/issue-23217.stderr
+++ b/src/test/ui/issue-23217.stderr
@@ -5,6 +5,8 @@ LL | pub enum SomeEnum {
    | ----------------- variant `A` not found here
 LL |     B = SomeEnum::A,
    |         ^^^^^^^^^^^ variant not found in `SomeEnum`
+   |
+   = note: did you mean `variant::B`?
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issue-28971.stderr b/src/test/ui/issue-28971.stderr
index 81d8d97963b..df114351ff5 100644
--- a/src/test/ui/issue-28971.stderr
+++ b/src/test/ui/issue-28971.stderr
@@ -6,6 +6,8 @@ LL | enum Foo {
 ...
 LL |             Foo::Baz(..) => (),
    |             ^^^^^^^^^^^^ variant not found in `Foo`
+   |
+   = note: did you mean `variant::Bar`?
 
 error: aborting due to previous error