about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-03-31 16:04:18 +0000
committerbors <bors@rust-lang.org>2017-03-31 16:04:18 +0000
commit40feadb966f825de7aa54a3138416c906b60f54a (patch)
tree782773130a3e3273426a8ef3380f5802a333d458
parenta9329d3aa3974cfe7fda57a67b3898434f410131 (diff)
parentc34f533a85529b483083c9082b5cb9ae0b711bcf (diff)
downloadrust-40feadb966f825de7aa54a3138416c906b60f54a.tar.gz
rust-40feadb966f825de7aa54a3138416c906b60f54a.zip
Auto merge of #40950 - frewsxcv:rollup, r=frewsxcv
Rollup of 10 pull requests

- Successful merges: #40694, #40842, #40869, #40888, #40898, #40904, #40925, #40928, #40929, #40934
- Failed merges:
-rw-r--r--src/Cargo.lock3
-rw-r--r--src/doc/unstable-book/src/SUMMARY.md4
-rw-r--r--src/doc/unstable-book/src/pub-restricted.md7
-rw-r--r--src/doc/unstable-book/src/reverse-cmp-key.md7
-rw-r--r--src/doc/unstable-book/src/rustdoc.md7
-rw-r--r--src/doc/unstable-book/src/specialization.md2
-rw-r--r--src/doc/unstable-book/src/str-checked-slicing.md7
-rw-r--r--src/libcore/cmp.rs9
-rw-r--r--src/libcore/macros.rs54
-rw-r--r--src/librustc/middle/expr_use_visitor.rs2
-rw-r--r--src/librustdoc/html/layout.rs4
-rw-r--r--src/librustdoc/html/static/rustdoc.css6
-rw-r--r--src/librustdoc/html/static/styles/main.css4
-rw-r--r--src/libstd/os/linux/fs.rs19
-rw-r--r--src/libstd/process.rs14
-rw-r--r--src/libstd/sys/unix/ext/io.rs15
-rw-r--r--src/tools/error_index_generator/main.rs2
-rw-r--r--src/tools/tidy/Cargo.toml1
-rw-r--r--src/tools/tidy/src/features.rs150
-rw-r--r--src/tools/tidy/src/main.rs4
-rw-r--r--src/tools/tidy/src/unstable_book.rs138
21 files changed, 330 insertions, 129 deletions
diff --git a/src/Cargo.lock b/src/Cargo.lock
index a9021dc34e2..1fa256197ce 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -926,6 +926,9 @@ dependencies = [
 [[package]]
 name = "tidy"
 version = "0.1.0"
+dependencies = [
+ "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
 
 [[package]]
 name = "toml"
diff --git a/src/doc/unstable-book/src/SUMMARY.md b/src/doc/unstable-book/src/SUMMARY.md
index fe491d7f901..292f5a1ec81 100644
--- a/src/doc/unstable-book/src/SUMMARY.md
+++ b/src/doc/unstable-book/src/SUMMARY.md
@@ -146,7 +146,6 @@
 - [proc_macro](proc-macro.md)
 - [proc_macro_internals](proc-macro-internals.md)
 - [process_try_wait](process-try-wait.md)
-- [pub_restricted](pub-restricted.md)
 - [question_mark_carrier](question-mark-carrier.md)
 - [quote](quote.md)
 - [rand](rand.md)
@@ -156,11 +155,11 @@
 - [relaxed_adts](relaxed-adts.md)
 - [repr_simd](repr-simd.md)
 - [retain_hash_collection](retain-hash-collection.md)
+- [reverse_cmp_key](reverse-cmp-key.md)
 - [rt](rt.md)
 - [rustc_attrs](rustc-attrs.md)
 - [rustc_diagnostic_macros](rustc-diagnostic-macros.md)
 - [rustc_private](rustc-private.md)
-- [rustdoc](rustdoc.md)
 - [rvalue_static_promotion](rvalue-static-promotion.md)
 - [sanitizer_runtime](sanitizer-runtime.md)
 - [sanitizer_runtime_lib](sanitizer-runtime-lib.md)
@@ -181,6 +180,7 @@
 - [step_by](step-by.md)
 - [step_trait](step-trait.md)
 - [stmt_expr_attributes](stmt-expr-attributes.md)
+- [str_checked_slicing](str-checked-slicing.md)
 - [str_escape](str-escape.md)
 - [str_internals](str-internals.md)
 - [struct_field_attributes](struct-field-attributes.md)
diff --git a/src/doc/unstable-book/src/pub-restricted.md b/src/doc/unstable-book/src/pub-restricted.md
deleted file mode 100644
index 6b1e88b8603..00000000000
--- a/src/doc/unstable-book/src/pub-restricted.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# `pub_restricted`
-
-The tracking issue for this feature is: [#32409]
-
-[#38356]: https://github.com/rust-lang/rust/issues/32409
-
-------------------------
diff --git a/src/doc/unstable-book/src/reverse-cmp-key.md b/src/doc/unstable-book/src/reverse-cmp-key.md
new file mode 100644
index 00000000000..a1a851d6ed6
--- /dev/null
+++ b/src/doc/unstable-book/src/reverse-cmp-key.md
@@ -0,0 +1,7 @@
+# `reverse_cmp_key`
+
+The tracking issue for this feature is: [#40893]
+
+[#40893]: https://github.com/rust-lang/rust/issues/40893
+
+------------------------
diff --git a/src/doc/unstable-book/src/rustdoc.md b/src/doc/unstable-book/src/rustdoc.md
deleted file mode 100644
index c7491ab034b..00000000000
--- a/src/doc/unstable-book/src/rustdoc.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# `rustdoc`
-
-The tracking issue for this feature is: [#27812]
-
-[#27812]: https://github.com/rust-lang/rust/issues/27812
-
-------------------------
diff --git a/src/doc/unstable-book/src/specialization.md b/src/doc/unstable-book/src/specialization.md
index 59f27343b66..efc380df6e1 100644
--- a/src/doc/unstable-book/src/specialization.md
+++ b/src/doc/unstable-book/src/specialization.md
@@ -2,6 +2,8 @@
 
 The tracking issue for this feature is: [#31844]
 
+[#31844]: https://github.com/rust-lang/rust/issues/31844
+
 ------------------------
 
 
diff --git a/src/doc/unstable-book/src/str-checked-slicing.md b/src/doc/unstable-book/src/str-checked-slicing.md
new file mode 100644
index 00000000000..d390139a6be
--- /dev/null
+++ b/src/doc/unstable-book/src/str-checked-slicing.md
@@ -0,0 +1,7 @@
+# `str_checked_slicing`
+
+The tracking issue for this feature is: [#39932]
+
+[#39932]: https://github.com/rust-lang/rust/issues/39932
+
+------------------------
diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs
index dc2398b22ac..74ded948b18 100644
--- a/src/libcore/cmp.rs
+++ b/src/libcore/cmp.rs
@@ -347,6 +347,15 @@ impl<T: PartialOrd> PartialOrd for Reverse<T> {
     fn partial_cmp(&self, other: &Reverse<T>) -> Option<Ordering> {
         other.0.partial_cmp(&self.0)
     }
+
+    #[inline]
+    fn lt(&self, other: &Self) -> bool { other.0 < self.0 }
+    #[inline]
+    fn le(&self, other: &Self) -> bool { other.0 <= self.0 }
+    #[inline]
+    fn ge(&self, other: &Self) -> bool { other.0 >= self.0 }
+    #[inline]
+    fn gt(&self, other: &Self) -> bool { other.0 > self.0 }
 }
 
 #[unstable(feature = "reverse_cmp_key", issue = "40893")]
diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs
index 021079f85f6..2a28d108df7 100644
--- a/src/libcore/macros.rs
+++ b/src/libcore/macros.rs
@@ -337,27 +337,20 @@ macro_rules! try {
 
 /// Write formatted data into a buffer
 ///
-/// This macro accepts a 'writer' (any value with a `write_fmt` method), a format string, and a
-/// list of arguments to format.
+/// This macro accepts a format string, a list of arguments, and a 'writer'. Arguments will be
+/// formatted according to the specified format string and the result will be passed to the writer.
+/// The writer may be any value with a `write_fmt` method; generally this comes from an
+/// implementation of either the [`std::fmt::Write`] or the [`std::io::Write`] trait. The macro
+/// returns whatever the 'write_fmt' method returns; commonly a [`std::fmt::Result`], or an
+/// [`io::Result`].
 ///
-/// The `write_fmt` method usually comes from an implementation of [`std::fmt::Write`][fmt_write]
-/// or [`std::io::Write`][io_write] traits. The term 'writer' refers to an implementation of one of
-/// these two traits.
+/// See [`std::fmt`] for more information on the format string syntax.
 ///
-/// Passed arguments will be formatted according to the specified format string and the resulting
-/// string will be passed to the writer.
-///
-/// See [`std::fmt`][fmt] for more information on format syntax.
-///
-/// `write!` returns whatever the 'write_fmt' method returns.
-///
-/// Common return values include: [`fmt::Result`][fmt_result], [`io::Result`][io_result]
-///
-/// [fmt]: ../std/fmt/index.html
-/// [fmt_write]: ../std/fmt/trait.Write.html
-/// [io_write]: ../std/io/trait.Write.html
-/// [fmt_result]: ../std/fmt/type.Result.html
-/// [io_result]: ../std/io/type.Result.html
+/// [`std::fmt`]: ../std/fmt/index.html
+/// [`std::fmt::Write`]: ../std/fmt/trait.Write.html
+/// [`std::io::Write`]: ../std/io/trait.Write.html
+/// [`std::fmt::Result`]: ../std/fmt/type.Result.html
+/// [`io::Result`]: ../std/io/type.Result.html
 ///
 /// # Examples
 ///
@@ -396,27 +389,12 @@ macro_rules! write {
 /// On all platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone
 /// (no additional CARRIAGE RETURN (`\r`/`U+000D`).
 ///
-/// This macro accepts a 'writer' (any value with a `write_fmt` method), a format string, and a
-/// list of arguments to format.
-///
-/// The `write_fmt` method usually comes from an implementation of [`std::fmt::Write`][fmt_write]
-/// or [`std::io::Write`][io_write] traits. The term 'writer' refers to an implementation of one of
-/// these two traits.
-///
-/// Passed arguments will be formatted according to the specified format string and the resulting
-/// string will be passed to the writer, along with the appended newline.
-///
-/// See [`std::fmt`][fmt] for more information on format syntax.
-///
-/// `write!` returns whatever the 'write_fmt' method returns.
+/// For more information, see [`write!`]. For information on the format string syntax, see
+/// [`std::fmt`].
 ///
-/// Common return values include: [`fmt::Result`][fmt_result], [`io::Result`][io_result]
+/// [`write!`]: macro.write.html
+/// [`std::fmt`]: ../std/fmt/index.html
 ///
-/// [fmt]: ../std/fmt/index.html
-/// [fmt_write]: ../std/fmt/trait.Write.html
-/// [io_write]: ../std/io/trait.Write.html
-/// [fmt_result]: ../std/fmt/type.Result.html
-/// [io_result]: ../std/io/type.Result.html
 ///
 /// # Examples
 ///
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index c7cf4a35a4b..a10f52e2d4c 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -288,6 +288,8 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
     }
 
     pub fn consume_body(&mut self, body: &hir::Body) {
+        debug!("consume_body(body={:?})", body);
+
         for arg in &body.arguments {
             let arg_ty = return_if_err!(self.mc.infcx.node_ty(arg.pat.id));
 
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 06a81641296..d6033a69da7 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -54,7 +54,7 @@ r##"<!DOCTYPE html>
     {favicon}
     {in_header}
 </head>
-<body class="rustdoc">
+<body class="rustdoc {css_class}">
     <!--[if lte IE 8]>
     <div class="warning">
         This old browser is unsupported and will most likely display funky
@@ -80,7 +80,7 @@ r##"<!DOCTYPE html>
         </form>
     </nav>
 
-    <section id='main' class="content {css_class}">{content}</section>
+    <section id='main' class="content">{content}</section>
     <section id='search' class="content hidden"></section>
 
     <section class="footer"></section>
diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css
index 3df120eece9..4edf6309346 100644
--- a/src/librustdoc/html/static/rustdoc.css
+++ b/src/librustdoc/html/static/rustdoc.css
@@ -141,7 +141,7 @@ pre {
 	padding: 14px;
 }
 
-.source pre {
+.source .content pre {
 	padding: 20px;
 }
 
@@ -149,7 +149,7 @@ img {
 	max-width: 100%;
 }
 
-.content.source {
+.source .content {
 	margin-top: 50px;
 	max-width: none;
 	overflow: visible;
@@ -231,7 +231,7 @@ nav.sub {
 	padding: 15px 0;
 }
 
-.content.source pre.rust {
+.source .content pre.rust {
 	white-space: pre;
 	overflow: auto;
 	padding-left: 0;
diff --git a/src/librustdoc/html/static/styles/main.css b/src/librustdoc/html/static/styles/main.css
index 74ec3691b38..c0310199088 100644
--- a/src/librustdoc/html/static/styles/main.css
+++ b/src/librustdoc/html/static/styles/main.css
@@ -45,6 +45,10 @@ pre {
 	background-color: #fff;
 }
 
+.source .sidebar {
+	background-color: #fff;
+}
+
 .sidebar .location {
 	border-color: #000;
 	background-color: #fff;
diff --git a/src/libstd/os/linux/fs.rs b/src/libstd/os/linux/fs.rs
index 11c41816cec..7ebda5ed744 100644
--- a/src/libstd/os/linux/fs.rs
+++ b/src/libstd/os/linux/fs.rs
@@ -34,36 +34,55 @@ pub trait MetadataExt {
     #[allow(deprecated)]
     fn as_raw_stat(&self) -> &raw::stat;
 
+    /// Returns the device ID on which this file resides.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_dev(&self) -> u64;
+    /// Returns the inode number.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_ino(&self) -> u64;
+    /// Returns the file type and mode.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_mode(&self) -> u32;
+    /// Returns the number of hard links to file.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_nlink(&self) -> u64;
+    /// Returns the user ID of the file owner.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_uid(&self) -> u32;
+    /// Returns the group ID of the file owner.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_gid(&self) -> u32;
+    /// Returns the device ID that this file represents. Only relevant for special file.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_rdev(&self) -> u64;
+    /// Returns the size of the file (if it is a regular file or a symbolic link) in bytes.
+    ///
+    /// The size of a symbolic link is the length of the pathname it contains,
+    /// without a terminating null byte.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_size(&self) -> u64;
+    /// Returns the last access time.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_atime(&self) -> i64;
+    /// Returns the last access time, nano seconds part.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_atime_nsec(&self) -> i64;
+    /// Returns the last modification time.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_mtime(&self) -> i64;
+    /// Returns the last modification time, nano seconds part.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_mtime_nsec(&self) -> i64;
+    /// Returns the last status change time.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_ctime(&self) -> i64;
+    /// Returns the last status change time, nano seconds part.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_ctime_nsec(&self) -> i64;
+    /// Returns the "preferred" blocksize for efficient filesystem I/O.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_blksize(&self) -> u64;
+    /// Returns the number of blocks allocated to the file, 512-byte units.
     #[stable(feature = "metadata_ext2", since = "1.8.0")]
     fn st_blocks(&self) -> u64;
 }
diff --git a/src/libstd/process.rs b/src/libstd/process.rs
index d46cf7a26da..7f1a00c707c 100644
--- a/src/libstd/process.rs
+++ b/src/libstd/process.rs
@@ -1056,6 +1056,20 @@ pub fn exit(code: i32) -> ! {
 /// will be run. If a clean shutdown is needed it is recommended to only call
 /// this function at a known point where there are no more destructors left
 /// to run.
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::process;
+///
+/// fn main() {
+///     println!("aborting");
+///
+///     process::abort();
+///
+///     // execution never gets here
+/// }
+/// ```
 #[stable(feature = "process_abort", since = "1.17.0")]
 pub fn abort() -> ! {
     unsafe { ::sys::abort_internal() };
diff --git a/src/libstd/sys/unix/ext/io.rs b/src/libstd/sys/unix/ext/io.rs
index 296235e173d..75aa72e3cff 100644
--- a/src/libstd/sys/unix/ext/io.rs
+++ b/src/libstd/sys/unix/ext/io.rs
@@ -73,6 +73,13 @@ pub trait IntoRawFd {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
+impl AsRawFd for RawFd {
+    fn as_raw_fd(&self) -> RawFd {
+        *self
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
 impl AsRawFd for fs::File {
     fn as_raw_fd(&self) -> RawFd {
         self.as_inner().fd().raw()
@@ -84,6 +91,14 @@ impl FromRawFd for fs::File {
         fs::File::from_inner(sys::fs::File::from_inner(fd))
     }
 }
+
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for RawFd {
+    fn into_raw_fd(self) -> RawFd {
+        self
+    }
+}
+
 #[stable(feature = "into_raw_os", since = "1.4.0")]
 impl IntoRawFd for fs::File {
     fn into_raw_fd(self) -> RawFd {
diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs
index 1d4f2c60d54..5db2ad83a0a 100644
--- a/src/tools/error_index_generator/main.rs
+++ b/src/tools/error_index_generator/main.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(rustc_private, rustdoc)]
+#![feature(rustc_private)]
 
 extern crate syntax;
 extern crate rustdoc;
diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml
index e900bd47fb7..371922c9e6b 100644
--- a/src/tools/tidy/Cargo.toml
+++ b/src/tools/tidy/Cargo.toml
@@ -4,3 +4,4 @@ version = "0.1.0"
 authors = ["Alex Crichton <alex@alexcrichton.com>"]
 
 [dependencies]
+regex = "0.2"
\ No newline at end of file
diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs
index 9b323c95fc3..e1fdc19c27d 100644
--- a/src/tools/tidy/src/features.rs
+++ b/src/tools/tidy/src/features.rs
@@ -24,8 +24,8 @@ use std::fs::File;
 use std::io::prelude::*;
 use std::path::Path;
 
-#[derive(PartialEq)]
-enum Status {
+#[derive(Debug, PartialEq)]
+pub enum Status {
     Stable,
     Removed,
     Unstable,
@@ -42,78 +42,21 @@ impl fmt::Display for Status {
     }
 }
 
-struct Feature {
-    level: Status,
-    since: String,
-    has_gate_test: bool,
+#[derive(Debug)]
+pub struct Feature {
+    pub level: Status,
+    pub since: String,
+    pub has_gate_test: bool,
 }
 
 pub fn check(path: &Path, bad: &mut bool) {
-    let mut features = collect_lang_features(&path.join("libsyntax/feature_gate.rs"));
+    let mut features = collect_lang_features(path);
     assert!(!features.is_empty());
-    let mut lib_features = HashMap::<String, Feature>::new();
-
-    let mut contents = String::new();
-    super::walk(path,
-                &mut |path| super::filter_dirs(path) || path.ends_with("src/test"),
-                &mut |file| {
-        let filename = file.file_name().unwrap().to_string_lossy();
-        if !filename.ends_with(".rs") || filename == "features.rs" ||
-           filename == "diagnostic_list.rs" {
-            return;
-        }
-
-        contents.truncate(0);
-        t!(t!(File::open(&file), &file).read_to_string(&mut contents));
 
-        for (i, line) in contents.lines().enumerate() {
-            let mut err = |msg: &str| {
-                println!("{}:{}: {}", file.display(), i + 1, msg);
-                *bad = true;
-            };
-            let level = if line.contains("[unstable(") {
-                Status::Unstable
-            } else if line.contains("[stable(") {
-                Status::Stable
-            } else {
-                continue;
-            };
-            let feature_name = match find_attr_val(line, "feature") {
-                Some(name) => name,
-                None => {
-                    err("malformed stability attribute");
-                    continue;
-                }
-            };
-            let since = match find_attr_val(line, "since") {
-                Some(name) => name,
-                None if level == Status::Stable => {
-                    err("malformed stability attribute");
-                    continue;
-                }
-                None => "None",
-            };
+    let lib_features = collect_lib_features(path, bad, &features);
+    assert!(!lib_features.is_empty());
 
-            if features.contains_key(feature_name) {
-                err("duplicating a lang feature");
-            }
-            if let Some(ref s) = lib_features.get(feature_name) {
-                if s.level != level {
-                    err("different stability level than before");
-                }
-                if s.since != since {
-                    err("different `since` than before");
-                }
-                continue;
-            }
-            lib_features.insert(feature_name.to_owned(),
-                                Feature {
-                                    level: level,
-                                    since: since.to_owned(),
-                                    has_gate_test: false,
-                                });
-        }
-    });
+    let mut contents = String::new();
 
     super::walk_many(&[&path.join("test/compile-fail"),
                        &path.join("test/compile-fail-fulldeps"),
@@ -233,8 +176,9 @@ fn test_filen_gate(filen_underscore: &str,
     return false;
 }
 
-fn collect_lang_features(path: &Path) -> HashMap<String, Feature> {
+pub fn collect_lang_features(base_src_path: &Path) -> HashMap<String, Feature> {
     let mut contents = String::new();
+    let path = base_src_path.join("libsyntax/feature_gate.rs");
     t!(t!(File::open(path)).read_to_string(&mut contents));
 
     contents.lines()
@@ -257,3 +201,71 @@ fn collect_lang_features(path: &Path) -> HashMap<String, Feature> {
         })
         .collect()
 }
+
+pub fn collect_lib_features(base_src_path: &Path,
+                            bad: &mut bool,
+                            features: &HashMap<String, Feature>) -> HashMap<String, Feature> {
+    let mut lib_features = HashMap::<String, Feature>::new();
+    let mut contents = String::new();
+    super::walk(base_src_path,
+                &mut |path| super::filter_dirs(path) || path.ends_with("src/test"),
+                &mut |file| {
+        let filename = file.file_name().unwrap().to_string_lossy();
+        if !filename.ends_with(".rs") || filename == "features.rs" ||
+           filename == "diagnostic_list.rs" {
+            return;
+        }
+
+        contents.truncate(0);
+        t!(t!(File::open(&file), &file).read_to_string(&mut contents));
+
+        for (i, line) in contents.lines().enumerate() {
+            let mut err = |msg: &str| {
+                println!("{}:{}: {}", file.display(), i + 1, msg);
+                *bad = true;
+            };
+            let level = if line.contains("[unstable(") {
+                Status::Unstable
+            } else if line.contains("[stable(") {
+                Status::Stable
+            } else {
+                continue;
+            };
+            let feature_name = match find_attr_val(line, "feature") {
+                Some(name) => name,
+                None => {
+                    err("malformed stability attribute");
+                    continue;
+                }
+            };
+            let since = match find_attr_val(line, "since") {
+                Some(name) => name,
+                None if level == Status::Stable => {
+                    err("malformed stability attribute");
+                    continue;
+                }
+                None => "None",
+            };
+
+            if features.contains_key(feature_name) {
+                err("duplicating a lang feature");
+            }
+            if let Some(ref s) = lib_features.get(feature_name) {
+                if s.level != level {
+                    err("different stability level than before");
+                }
+                if s.since != since {
+                    err("different `since` than before");
+                }
+                continue;
+            }
+            lib_features.insert(feature_name.to_owned(),
+                                Feature {
+                                    level: level,
+                                    since: since.to_owned(),
+                                    has_gate_test: false,
+                                });
+        }
+    });
+    lib_features
+}
\ No newline at end of file
diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index 2af891b5b85..501e35e03e8 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -14,6 +14,8 @@
 //! etc. This is run by default on `make check` and as part of the auto
 //! builders.
 
+extern crate regex;
+
 use std::fs;
 use std::path::{PathBuf, Path};
 use std::env;
@@ -37,6 +39,7 @@ mod features;
 mod cargo;
 mod pal;
 mod deps;
+mod unstable_book;
 
 fn main() {
     let path = env::args_os().skip(1).next().expect("need an argument");
@@ -51,6 +54,7 @@ fn main() {
     cargo::check(&path, &mut bad);
     features::check(&path, &mut bad);
     pal::check(&path, &mut bad);
+    unstable_book::check(&path, &mut bad);
     if !args.iter().any(|s| *s == "--no-vendor") {
         deps::check(&path, &mut bad);
     }
diff --git a/src/tools/tidy/src/unstable_book.rs b/src/tools/tidy/src/unstable_book.rs
new file mode 100644
index 00000000000..c10e3107794
--- /dev/null
+++ b/src/tools/tidy/src/unstable_book.rs
@@ -0,0 +1,138 @@
+// 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.
+
+use std::collections::HashSet;
+use std::fs;
+use std::io::{self, BufRead, Write};
+use std::path;
+use features::{collect_lang_features, collect_lib_features, Status};
+
+const PATH_STR: &'static str = "doc/unstable-book/src";
+
+const SUMMARY_FILE_NAME: &'static str = "SUMMARY.md";
+
+static EXCLUDE: &'static [&'static str; 2] = &[SUMMARY_FILE_NAME, "the-unstable-book.md"];
+
+/// Build the path to the Unstable Book source directory from the Rust 'src' directory
+fn unstable_book_path(base_src_path: &path::Path) -> path::PathBuf {
+    base_src_path.join(PATH_STR)
+}
+
+/// Build the path to the Unstable Book SUMMARY file from the Rust 'src' directory
+fn unstable_book_summary_path(base_src_path: &path::Path) -> path::PathBuf {
+    unstable_book_path(base_src_path).join(SUMMARY_FILE_NAME)
+}
+
+/// Open the Unstable Book SUMMARY file
+fn open_unstable_book_summary_file(base_src_path: &path::Path) -> fs::File {
+    fs::File::open(unstable_book_summary_path(base_src_path))
+        .expect("could not open Unstable Book SUMMARY.md")
+}
+
+/// Test to determine if DirEntry is a file
+fn dir_entry_is_file(dir_entry: &fs::DirEntry) -> bool {
+    dir_entry.file_type().expect("could not determine file type of directory entry").is_file()
+}
+
+/// Retrieve names of all lang-related unstable features
+fn collect_unstable_lang_feature_names(base_src_path: &path::Path) -> HashSet<String> {
+    collect_lang_features(base_src_path)
+        .into_iter()
+        .filter(|&(_, ref f)| f.level == Status::Unstable)
+        .map(|(ref name, _)| name.to_owned())
+        .collect()
+}
+
+/// Retrieve names of all lib-related unstable features
+fn collect_unstable_lib_feature_names(base_src_path: &path::Path) -> HashSet<String> {
+    let mut bad = true;
+    let lang_features = collect_lang_features(base_src_path);
+    collect_lib_features(base_src_path, &mut bad, &lang_features)
+        .into_iter()
+        .filter(|&(_, ref f)| f.level == Status::Unstable)
+        .map(|(ref name, _)| name.to_owned())
+        .collect()
+}
+
+/// Retrieve names of all unstable features
+fn collect_unstable_feature_names(base_src_path: &path::Path) -> HashSet<String> {
+    collect_unstable_lib_feature_names(base_src_path)
+        .union(&collect_unstable_lang_feature_names(base_src_path))
+        .map(|n| n.to_owned())
+        .collect::<HashSet<_, _>>()
+}
+
+/// Retrieve file names of all sections in the Unstable Book with:
+///
+/// * hyphens replaced by underscores
+/// * the markdown suffix ('.md') removed
+fn collect_unstable_book_section_file_names(base_src_path: &path::Path) -> HashSet<String> {
+    fs::read_dir(unstable_book_path(base_src_path))
+        .expect("could not read directory")
+        .into_iter()
+        .map(|entry| entry.expect("could not read directory entry"))
+        .filter(dir_entry_is_file)
+        .map(|entry| entry.file_name().into_string().unwrap())
+        .filter(|n| EXCLUDE.iter().all(|e| n != e))
+        .map(|n| n.trim_right_matches(".md").replace('-', "_"))
+        .collect()
+}
+
+/// Retrieve unstable feature names that are in the Unstable Book SUMMARY file
+fn collect_unstable_book_summary_links(base_src_path: &path::Path) -> HashSet<String> {
+    let summary_link_regex =
+        ::regex::Regex::new(r"^- \[(\S+)\]\(\S+\.md\)").expect("invalid regex");
+    io::BufReader::new(open_unstable_book_summary_file(base_src_path))
+        .lines()
+        .map(|l| l.expect("could not read line from file"))
+        .filter_map(|line| {
+            summary_link_regex.captures(&line).map(|c| {
+                                                       c.get(1)
+                                                           .unwrap()
+                                                           .as_str()
+                                                           .to_owned()
+                                                   })
+        })
+        .collect()
+}
+
+pub fn check(path: &path::Path, bad: &mut bool) {
+    let unstable_feature_names = collect_unstable_feature_names(path);
+    let unstable_book_section_file_names = collect_unstable_book_section_file_names(path);
+    let unstable_book_links = collect_unstable_book_summary_links(path);
+
+    // Check for Unstable Book section names with no corresponding SUMMARY.md link
+    for feature_name in &unstable_book_section_file_names - &unstable_book_links {
+        *bad = true;
+        writeln!(io::stderr(),
+                 "The Unstable Book section '{}' needs to have a link in SUMMARY.md",
+                 feature_name)
+                .expect("could not write to stderr")
+    }
+
+    // Check for unstable features that don't have Unstable Book sections
+    for feature_name in &unstable_feature_names - &unstable_book_section_file_names {
+        *bad = true;
+        writeln!(io::stderr(),
+                 "Unstable feature '{}' needs to have a section in The Unstable Book",
+                 feature_name)
+                .expect("could not write to stderr")
+    }
+
+    // Check for Unstable Book sections that don't have a corresponding unstable feature
+    for feature_name in &unstable_book_section_file_names - &unstable_feature_names {
+        *bad = true;
+        writeln!(io::stderr(),
+                 "The Unstable Book has a section '{}' which doesn't correspond \
+                  to an unstable feature",
+                 feature_name)
+                .expect("could not write to stderr")
+    }
+}