diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py index a5449b748dd5e..7e8fde2034640 100644 --- a/src/etc/htmldocck.py +++ b/src/etc/htmldocck.py @@ -29,7 +29,7 @@ In order to avoid one-off dependencies for this task, this script uses a reasonably working HTML parser and the existing XPath implementation -from Python 2's standard library. Hopefully we won't render +from Python's standard library. Hopefully we won't render non-well-formed HTML. # Commands @@ -110,11 +110,17 @@ import re import shlex from collections import namedtuple -from HTMLParser import HTMLParser +try: + from html.parser import HTMLParser +except ImportError: + from HTMLParser import HTMLParser from xml.etree import cElementTree as ET # ⇤/⇥ are not in HTML 4 but are in HTML 5 -from htmlentitydefs import entitydefs +try: + from html.entities import entitydefs +except ImportError: + from htmlentitydefs import entitydefs entitydefs['larrb'] = u'\u21e4' entitydefs['rarrb'] = u'\u21e5' entitydefs['nbsp'] = ' ' @@ -123,6 +129,11 @@ VOID_ELEMENTS = set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr']) +# Python 2 -> 3 compatibility +try: + unichr +except NameError: + unichr = chr class CustomHTMLParser(HTMLParser): """simplified HTML parser. @@ -184,12 +195,8 @@ def concat_multi_lines(f): # strip the common prefix from the current line if needed if lastline is not None: - maxprefix = 0 - for i in xrange(min(len(line), len(lastline))): - if line[i] != lastline[i]: - break - maxprefix += 1 - line = line[maxprefix:].lstrip() + common_prefix = os.path.commonprefix([line, lastline]) + line = line[len(common_prefix):].lstrip() firstlineno = firstlineno or lineno if line.endswith('\\'): @@ -213,7 +220,7 @@ def concat_multi_lines(f): def get_commands(template): - with open(template, 'rUb') as f: + with open(template, 'rU') as f: for lineno, line in concat_multi_lines(f): m = LINE_PATTERN.search(line) if not m: @@ -372,7 +379,7 @@ def check_command(c, cache): cache.get_file(c.args[0]) ret = True except FailedCheck as err: - cerr = err.message + cerr = str(err) ret = False elif len(c.args) == 2: # @has/matches = string test cerr = "`PATTERN` did not match" @@ -413,9 +420,9 @@ def check_command(c, cache): except FailedCheck as err: message = '@{}{} check failed'.format('!' if c.negated else '', c.cmd) - print_err(c.lineno, c.context, err.message, message) + print_err(c.lineno, c.context, str(err), message) except InvalidCheck as err: - print_err(c.lineno, c.context, err.message) + print_err(c.lineno, c.context, str(err)) def check(target, commands): cache = CachedFiles(target) diff --git a/src/liballoc/allocator.rs b/src/liballoc/allocator.rs index fc6585a9f951d..f14f27023249e 100644 --- a/src/liballoc/allocator.rs +++ b/src/liballoc/allocator.rs @@ -110,7 +110,7 @@ impl Layout { /// Creates a layout, bypassing all checks. /// - /// # Unsafety + /// # Safety /// /// This function is unsafe as it does not verify that `align` is /// a power-of-two that is also less than or equal to 2^31, nor @@ -485,7 +485,7 @@ pub unsafe trait Alloc { /// behavior, e.g. to ensure initialization to particular sets of /// bit patterns.) /// - /// # Unsafety + /// # Safety /// /// This function is unsafe because undefined behavior can result /// if the caller does not ensure that `layout` has non-zero size. @@ -513,7 +513,7 @@ pub unsafe trait Alloc { /// Deallocate the memory referenced by `ptr`. /// - /// # Unsafety + /// # Safety /// /// This function is unsafe because undefined behavior can result /// if the caller does not ensure all of the following: @@ -617,7 +617,7 @@ pub unsafe trait Alloc { /// behavior is well-defined (though underspecified) when this /// constraint is violated; further discussion below. /// - /// # Unsafety + /// # Safety /// /// This function is unsafe because undefined behavior can result /// if the caller does not ensure all of the following: @@ -688,7 +688,7 @@ pub unsafe trait Alloc { /// Behaves like `alloc`, but also ensures that the contents /// are set to zero before being returned. /// - /// # Unsafety + /// # Safety /// /// This function is unsafe for the same reasons that `alloc` is. /// @@ -714,7 +714,7 @@ pub unsafe trait Alloc { /// the returned block. For some `layout` inputs, like arrays, this /// may include extra storage usable for additional data. /// - /// # Unsafety + /// # Safety /// /// This function is unsafe for the same reasons that `alloc` is. /// @@ -736,7 +736,7 @@ pub unsafe trait Alloc { /// the returned block. For some `layout` inputs, like arrays, this /// may include extra storage usable for additional data. /// - /// # Unsafety + /// # Safety /// /// This function is unsafe for the same reasons that `realloc` is. /// @@ -770,7 +770,7 @@ pub unsafe trait Alloc { /// memory block referenced by `ptr` has not been transferred, and /// the contents of the memory block are unaltered. /// - /// # Unsafety + /// # Safety /// /// This function is unsafe because undefined behavior can result /// if the caller does not ensure all of the following: @@ -827,7 +827,7 @@ pub unsafe trait Alloc { /// the memory block has not been transferred, and the contents of /// the memory block are unaltered. /// - /// # Unsafety + /// # Safety /// /// This function is unsafe because undefined behavior can result /// if the caller does not ensure all of the following: @@ -920,7 +920,7 @@ pub unsafe trait Alloc { /// /// Captures a common usage pattern for allocators. /// - /// # Unsafety + /// # Safety /// /// This function is unsafe because undefined behavior can result /// if the caller does not ensure both: @@ -993,7 +993,7 @@ pub unsafe trait Alloc { /// The returned block is suitable for passing to the /// `alloc`/`realloc` methods of this allocator. /// - /// # Unsafety + /// # Safety /// /// This function is unsafe because undefined behavior can result /// if the caller does not ensure all of the following: @@ -1037,7 +1037,7 @@ pub unsafe trait Alloc { /// /// Captures a common usage pattern for allocators. /// - /// # Unsafety + /// # Safety /// /// This function is unsafe because undefined behavior can result /// if the caller does not ensure both: diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index c0d43d9c52755..82aac4dbf6334 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -633,7 +633,7 @@ impl FusedIterator for Box {} /// that `FnBox` may be deprecated in the future if `Box` /// closures become directly usable.) /// -/// ### Example +/// # Examples /// /// Here is a snippet of code which creates a hashmap full of boxed /// once closures and then removes them one by one, calling each diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index cbf242e884a70..7787ace944119 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -171,7 +171,7 @@ mod hack { impl [T] { /// Returns the number of elements in the slice. /// - /// # Example + /// # Examples /// /// ``` /// let a = [1, 2, 3]; @@ -185,7 +185,7 @@ impl [T] { /// Returns `true` if the slice has a length of 0. /// - /// # Example + /// # Examples /// /// ``` /// let a = [1, 2, 3]; @@ -523,7 +523,7 @@ impl [T] { /// Reverses the order of elements in the slice, in place. /// - /// # Example + /// # Examples /// /// ``` /// let mut v = [1, 2, 3]; @@ -580,7 +580,7 @@ impl [T] { /// /// Panics if `size` is 0. /// - /// # Example + /// # Examples /// /// ``` /// let slice = ['r', 'u', 's', 't']; @@ -613,7 +613,7 @@ impl [T] { /// /// Panics if `size` is 0. /// - /// # Example + /// # Examples /// /// ``` /// let slice = ['l', 'o', 'r', 'e', 'm']; @@ -1040,7 +1040,7 @@ impl [T] { /// `Err` is returned, containing the index where a matching /// element could be inserted while maintaining sorted order. /// - /// # Example + /// # Examples /// /// Looks up a series of four elements. The first is found, with a /// uniquely determined position; the second and third are not @@ -1074,7 +1074,7 @@ impl [T] { /// `Err` is returned, containing the index where a matching /// element could be inserted while maintaining sorted order. /// - /// # Example + /// # Examples /// /// Looks up a series of four elements. The first is found, with a /// uniquely determined position; the second and third are not @@ -1419,7 +1419,7 @@ impl [T] { /// /// This function will panic if the two slices have different lengths. /// - /// # Example + /// # Examples /// /// ``` /// let mut dst = [0, 0, 0]; @@ -1445,7 +1445,7 @@ impl [T] { /// /// This function will panic if the two slices have different lengths. /// - /// # Example + /// # Examples /// /// ``` /// let mut dst = [0, 0, 0]; diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 80317cd763b5c..79b2bbce2af7c 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -1714,7 +1714,7 @@ impl str { /// /// [`Err`]: str/trait.FromStr.html#associatedtype.Err /// - /// # Example + /// # Examples /// /// Basic usage /// diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 96bd6273c9484..b1919c7c968c9 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -82,7 +82,7 @@ use boxed::Box; /// /// # Examples /// -/// You can create a `String` from a literal string with `String::from`: +/// You can create a `String` from a literal string with [`String::from`]: /// /// ``` /// let hello = String::from("Hello, world!"); @@ -98,6 +98,7 @@ use boxed::Box; /// hello.push_str("orld!"); /// ``` /// +/// [`String::from`]: #method.from /// [`char`]: ../../std/primitive.char.html /// [`push`]: #method.push /// [`push_str`]: #method.push_str diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index dc0905e297251..e0a3b8d52f40e 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -998,7 +998,7 @@ impl<'b, T: ?Sized> Ref<'b, T> { /// A method would interfere with methods of the same name on the contents /// of a `RefCell` used through `Deref`. /// - /// # Example + /// # Examples /// /// ``` /// use std::cell::{RefCell, Ref}; @@ -1040,7 +1040,7 @@ impl<'b, T: ?Sized> RefMut<'b, T> { /// `RefMut::map(...)`. A method would interfere with methods of the same /// name on the contents of a `RefCell` used through `Deref`. /// - /// # Example + /// # Examples /// /// ``` /// use std::cell::{RefCell, RefMut}; diff --git a/src/libcore/fmt/builders.rs b/src/libcore/fmt/builders.rs index 8125097d7d105..b594c886b64f5 100644 --- a/src/libcore/fmt/builders.rs +++ b/src/libcore/fmt/builders.rs @@ -58,7 +58,7 @@ impl<'a, 'b: 'a> fmt::Write for PadAdapter<'a, 'b> { /// [`Formatter::debug_struct`](struct.Formatter.html#method.debug_struct) /// method. /// -/// # Example +/// # Examples /// /// ``` /// use std::fmt; @@ -153,7 +153,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// [`Formatter::debug_tuple`](struct.Formatter.html#method.debug_tuple) /// method. /// -/// # Example +/// # Examples /// /// ``` /// use std::fmt; @@ -290,7 +290,7 @@ impl<'a, 'b: 'a> DebugInner<'a, 'b> { /// [`Formatter::debug_set`](struct.Formatter.html#method.debug_set) /// method. /// -/// # Example +/// # Examples /// /// ``` /// use std::fmt; @@ -361,7 +361,7 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { /// [`Formatter::debug_list`](struct.Formatter.html#method.debug_list) /// method. /// -/// # Example +/// # Examples /// /// ``` /// use std::fmt; @@ -432,7 +432,7 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { /// [`Formatter::debug_map`](struct.Formatter.html#method.debug_map) /// method. /// -/// # Example +/// # Examples /// /// ``` /// use std::fmt; diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 6f7adbe1e7a0e..4b866cab1eae2 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -901,7 +901,7 @@ impl ManuallyDrop { /// Manually drops the contained value. /// - /// # Unsafety + /// # Safety /// /// This function runs the destructor of the contained value and thus the wrapped value /// now represents uninitialized data. It is up to the user of this method to ensure the diff --git a/src/librustc_back/target/haiku_base.rs b/src/librustc_back/target/haiku_base.rs index 21410dcd41264..112f424f7a8bb 100644 --- a/src/librustc_back/target/haiku_base.rs +++ b/src/librustc_back/target/haiku_base.rs @@ -20,7 +20,6 @@ pub fn opts() -> TargetOptions { target_family: Some("unix".to_string()), relro_level: RelroLevel::Full, linker_is_gnu: true, - no_integrated_as: true, .. Default::default() } } diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 195bd2acce0fe..cbc4ebe90fd09 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -160,21 +160,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { }; let mut fn_warned = false; - let maybe_def = match expr.node { - hir::ExprCall(ref callee, _) => { - match callee.node { - hir::ExprPath(ref qpath) => Some(cx.tables.qpath_def(qpath, callee.hir_id)), - _ => None - } - }, - hir::ExprMethodCall(..) => { - cx.tables.type_dependent_defs().get(expr.hir_id).cloned() - }, - _ => { None } - }; - if let Some(def) = maybe_def { - let def_id = def.def_id(); - fn_warned = check_must_use(cx, def_id, s.span, "return value of "); + if cx.tcx.sess.features.borrow().fn_must_use { + let maybe_def = match expr.node { + hir::ExprCall(ref callee, _) => { + match callee.node { + hir::ExprPath(ref qpath) => { + Some(cx.tables.qpath_def(qpath, callee.hir_id)) + }, + _ => None + } + }, + hir::ExprMethodCall(..) => { + cx.tables.type_dependent_defs().get(expr.hir_id).cloned() + }, + _ => None + }; + if let Some(def) = maybe_def { + let def_id = def.def_id(); + fn_warned = check_must_use(cx, def_id, s.span, "return value of "); + } } if !(ty_warned || fn_warned) { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 92b3180c5cbf2..7d6ad5286d111 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -323,6 +323,10 @@ impl Item { pub fn is_union(&self) -> bool { self.type_() == ItemType::Union } + pub fn is_import(&self) -> bool { + self.type_() == ItemType::Import + } + pub fn is_stripped(&self) -> bool { match self.inner { StrippedItem(..) => true, _ => false } } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 6593d6dfd6cff..5457f69cb6dab 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1764,6 +1764,37 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, } indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2)); + // This call is to remove reexport duplicates in cases such as: + // + // ``` + // pub mod foo { + // pub mod bar { + // pub trait Double { fn foo(); } + // } + // } + // + // pub use foo::bar::*; + // pub use foo::*; + // ``` + // + // `Double` will appear twice in the generated docs. + // + // FIXME: This code is quite ugly and could be improved. Small issue: DefId + // can be identical even if the elements are different (mostly in imports). + // So in case this is an import, we keep everything by adding a "unique id" + // (which is the position in the vector). + indices.dedup_by_key(|i| (items[*i].def_id, + if items[*i].name.as_ref().is_some() { + Some(full_path(cx, &items[*i]).clone()) + } else { + None + }, + items[*i].type_(), + if items[*i].is_import() { + *i + } else { + 0 + })); debug!("{:?}", indices); let mut curty = None; @@ -2925,7 +2956,13 @@ fn render_deref_methods(w: &mut fmt::Formatter, cx: &Context, impl_: &Impl, fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLink, render_mode: RenderMode, outer_version: Option<&str>) -> fmt::Result { if render_mode == RenderMode::Normal { - write!(w, "

{}", i.inner_impl())?; + let id = derive_id(match i.inner_impl().trait_ { + Some(ref t) => format!("impl-{}", Escape(&format!("{:#}", t))), + None => "impl".to_string(), + }); + write!(w, "

{}", + id, i.inner_impl())?; + write!(w, "", id)?; write!(w, "")?; let since = i.impl_item.stability.as_ref().map(|s| &s.since[..]); if let Some(l) = (Item { item: &i.impl_item, cx: cx }).src_href() { diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 26a64f6cd2245..4a3286b421ae9 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -445,6 +445,10 @@ a { .small-section-header:hover > .anchor { display: initial; } + +.in-band:hover > .anchor { + display: initial; +} .anchor { display: none; } diff --git a/src/librustdoc/html/static/styles/main.css b/src/librustdoc/html/static/styles/main.css index 08bf5a10fe9d9..c5f4272b932fc 100644 --- a/src/librustdoc/html/static/styles/main.css +++ b/src/librustdoc/html/static/styles/main.css @@ -26,6 +26,7 @@ h1.fqn { h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod) { border-bottom-color: #DDDDDD; } + .in-band { background-color: white; } @@ -83,6 +84,11 @@ pre { } :target { background: #FDFFD3; } + +:target > .in-band { + background: #FDFFD3; +} + .content .highlighted { color: #000 !important; background-color: #ccc; diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 0fff833e7d83e..074ab3ebd8fdc 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -522,7 +522,7 @@ pub trait Read { /// `Read`er - the method only takes `&self` so that it can be used through /// trait objects. /// - /// # Unsafety + /// # Safety /// /// This method is unsafe because a `Read`er could otherwise return a /// non-zeroing `Initializer` from another `Read` type without an `unsafe` @@ -903,7 +903,7 @@ impl Initializer { /// Returns a new `Initializer` which will not zero out buffers. /// - /// # Unsafety + /// # Safety /// /// This may only be called by `Read`ers which guarantee that they will not /// read from buffers passed to `Read` methods, and that the return value of diff --git a/src/libstd/memchr.rs b/src/libstd/memchr.rs index 98642f86f4dc2..240e82069ff4d 100644 --- a/src/libstd/memchr.rs +++ b/src/libstd/memchr.rs @@ -20,7 +20,7 @@ /// magnitude faster than `haystack.iter().position(|&b| b == needle)`. /// (See benchmarks.) /// -/// # Example +/// # Examples /// /// This shows how to find the first position of a byte in a byte string. /// @@ -40,7 +40,7 @@ pub fn memchr(needle: u8, haystack: &[u8]) -> Option { /// Returns the index corresponding to the last occurrence of `needle` in /// `haystack`, or `None` if one is not found. /// -/// # Example +/// # Examples /// /// This shows how to find the last position of a byte in a byte string. /// diff --git a/src/libstd/sys/redox/ext/mod.rs b/src/libstd/sys/redox/ext/mod.rs index 259cda5bcb3eb..9fd8d6c91869c 100644 --- a/src/libstd/sys/redox/ext/mod.rs +++ b/src/libstd/sys/redox/ext/mod.rs @@ -13,7 +13,7 @@ //! For now, this module is limited to extracting file descriptors, //! but its functionality will grow over time. //! -//! # Example +//! # Examples //! //! ```no_run //! use std::fs::File; diff --git a/src/libstd/sys/unix/ext/mod.rs b/src/libstd/sys/unix/ext/mod.rs index 67fe46cc9c7a2..98bc90dd4e132 100644 --- a/src/libstd/sys/unix/ext/mod.rs +++ b/src/libstd/sys/unix/ext/mod.rs @@ -13,7 +13,7 @@ //! For now, this module is limited to extracting file descriptors, //! but its functionality will grow over time. //! -//! # Example +//! # Examples //! //! ```no_run //! use std::fs::File; diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index ee103c803f542..6354e746af26a 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -807,7 +807,7 @@ pub fn park_timeout_ms(ms: u32) { /// Platforms which do not support nanosecond precision for sleeping will have /// `dur` rounded up to the nearest granularity of time they can sleep for. /// -/// # Example +/// # Examples /// /// Waiting for the complete expiration of the timeout: /// diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 801343689b728..09574d5ba129e 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -112,8 +112,8 @@ macro_rules! declare_features { // was set. This is most important for knowing when a particular feature became // stable (active). // -// NB: The featureck.py script parses this information directly out of the source -// so take care when modifying it. +// NB: tools/tidy/src/features.rs parses this information directly out of the +// source, so take care when modifying it. declare_features! ( (active, asm, "1.0.0", Some(29722)), @@ -372,6 +372,9 @@ declare_features! ( // #[doc(cfg(...))] (active, doc_cfg, "1.21.0", Some(43781)), + + // allow `#[must_use]` on functions (RFC 1940) + (active, fn_must_use, "1.21.0", Some(43302)), ); declare_features! ( @@ -915,20 +918,27 @@ struct Context<'a> { } macro_rules! gate_feature_fn { - ($cx: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{ - let (cx, has_feature, span, name, explain) = ($cx, $has_feature, $span, $name, $explain); + ($cx: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $level: expr) => {{ + let (cx, has_feature, span, + name, explain, level) = ($cx, $has_feature, $span, $name, $explain, $level); let has_feature: bool = has_feature(&$cx.features); debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature); if !has_feature && !span.allows_unstable() { - emit_feature_err(cx.parse_sess, name, span, GateIssue::Language, explain); + leveled_feature_err(cx.parse_sess, name, span, GateIssue::Language, explain, level) + .emit(); } }} } macro_rules! gate_feature { ($cx: expr, $feature: ident, $span: expr, $explain: expr) => { - gate_feature_fn!($cx, |x:&Features| x.$feature, $span, stringify!($feature), $explain) - } + gate_feature_fn!($cx, |x:&Features| x.$feature, $span, + stringify!($feature), $explain, GateStrength::Hard) + }; + ($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => { + gate_feature_fn!($cx, |x:&Features| x.$feature, $span, + stringify!($feature), $explain, $level) + }; } impl<'a> Context<'a> { @@ -938,7 +948,7 @@ impl<'a> Context<'a> { for &(n, ty, ref gateage) in BUILTIN_ATTRIBUTES { if name == n { if let Gated(_, name, desc, ref has_feature) = *gateage { - gate_feature_fn!(self, has_feature, attr.span, name, desc); + gate_feature_fn!(self, has_feature, attr.span, name, desc, GateStrength::Hard); } debug!("check_attribute: {:?} is builtin, {:?}, {:?}", attr.path, ty, gateage); return; @@ -1008,13 +1018,26 @@ pub enum GateIssue { Library(Option) } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum GateStrength { + /// A hard error. (Most feature gates should use this.) + Hard, + /// Only a warning. (Use this only as backwards-compatibility demands.) + Soft, +} + pub fn emit_feature_err(sess: &ParseSess, feature: &str, span: Span, issue: GateIssue, explain: &str) { feature_err(sess, feature, span, issue, explain).emit(); } pub fn feature_err<'a>(sess: &'a ParseSess, feature: &str, span: Span, issue: GateIssue, - explain: &str) -> DiagnosticBuilder<'a> { + explain: &str) -> DiagnosticBuilder<'a> { + leveled_feature_err(sess, feature, span, issue, explain, GateStrength::Hard) +} + +fn leveled_feature_err<'a>(sess: &'a ParseSess, feature: &str, span: Span, issue: GateIssue, + explain: &str, level: GateStrength) -> DiagnosticBuilder<'a> { let diag = &sess.span_diagnostic; let issue = match issue { @@ -1022,10 +1045,15 @@ pub fn feature_err<'a>(sess: &'a ParseSess, feature: &str, span: Span, issue: Ga GateIssue::Library(lib) => lib, }; - let mut err = if let Some(n) = issue { - diag.struct_span_err(span, &format!("{} (see issue #{})", explain, n)) + let explanation = if let Some(n) = issue { + format!("{} (see issue #{})", explain, n) } else { - diag.struct_span_err(span, explain) + explain.to_owned() + }; + + let mut err = match level { + GateStrength::Hard => diag.struct_span_err(span, &explanation), + GateStrength::Soft => diag.struct_span_warn(span, &explanation), }; // #23973: do not suggest `#![feature(...)]` if we are in beta/stable @@ -1035,7 +1063,15 @@ pub fn feature_err<'a>(sess: &'a ParseSess, feature: &str, span: Span, issue: Ga feature)); } + // If we're on stable and only emitting a "soft" warning, add a note to + // clarify that the feature isn't "on" (rather than being on but + // warning-worthy). + if !sess.unstable_features.is_nightly_build() && level == GateStrength::Soft { + err.help("a nightly build of the compiler is required to enable this feature"); + } + err + } const EXPLAIN_BOX_SYNTAX: &'static str = @@ -1092,6 +1128,12 @@ macro_rules! gate_feature_post { if !span.allows_unstable() { gate_feature!(cx.context, $feature, span, $explain) } + }}; + ($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {{ + let (cx, span) = ($cx, $span); + if !span.allows_unstable() { + gate_feature!(cx.context, $feature, span, $explain, $level) + } }} } @@ -1234,6 +1276,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { function may change over time, for now \ a top-level `fn main()` is required"); } + if attr::contains_name(&i.attrs[..], "must_use") { + gate_feature_post!(&self, fn_must_use, i.span, + "`#[must_use]` on functions is experimental", + GateStrength::Soft); + } } ast::ItemKind::Struct(..) => { @@ -1271,7 +1318,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { and possibly buggy"); } - ast::ItemKind::Impl(_, polarity, defaultness, _, _, _, _) => { + ast::ItemKind::Impl(_, polarity, defaultness, _, _, _, ref impl_items) => { if polarity == ast::ImplPolarity::Negative { gate_feature_post!(&self, optin_builtin_traits, i.span, @@ -1284,6 +1331,16 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { i.span, "specialization is unstable"); } + + for impl_item in impl_items { + if let ast::ImplItemKind::Method(..) = impl_item.node { + if attr::contains_name(&impl_item.attrs[..], "must_use") { + gate_feature_post!(&self, fn_must_use, impl_item.span, + "`#[must_use]` on methods is experimental", + GateStrength::Soft); + } + } + } } ast::ItemKind::MacroDef(ast::MacroDef { legacy: false, .. }) => { diff --git a/src/test/compile-fail/feature-gate-fn_must_use.rs b/src/test/compile-fail/feature-gate-fn_must_use.rs new file mode 100644 index 0000000000000..2dd6b90407267 --- /dev/null +++ b/src/test/compile-fail/feature-gate-fn_must_use.rs @@ -0,0 +1,31 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +struct MyStruct; + +impl MyStruct { + #[must_use] + fn need_to_use_method() -> bool { true } //~ WARN `#[must_use]` on methods is experimental +} + +#[must_use] +fn need_to_use_it() -> bool { true } //~ WARN `#[must_use]` on functions is experimental + + +// Feature gates are tidy-required to have a specially named (or +// comment-annotated) compile-fail test (which MUST fail), but for +// backwards-compatibility reasons, we want `#[must_use]` on functions to be +// compilable even if the `fn_must_use` feature is absent, thus necessitating +// the usage of `#[rustc_error]` here, pragmatically if awkwardly solving this +// dilemma until a superior solution can be devised. +#[rustc_error] +fn main() {} //~ ERROR compilation successful diff --git a/src/test/compile-fail/feature-gate/issue-43106-gating-of-builtin-attrs.rs b/src/test/compile-fail/feature-gate/issue-43106-gating-of-builtin-attrs.rs index 29a2b0609fcd2..204190d64acc1 100644 --- a/src/test/compile-fail/feature-gate/issue-43106-gating-of-builtin-attrs.rs +++ b/src/test/compile-fail/feature-gate/issue-43106-gating-of-builtin-attrs.rs @@ -680,6 +680,7 @@ mod must_use { mod inner { #![must_use="1400"] } #[must_use = "1400"] fn f() { } + //~^ WARN `#[must_use]` on functions is experimental #[must_use = "1400"] struct S; diff --git a/src/test/rustdoc/remove-duplicates.rs b/src/test/rustdoc/remove-duplicates.rs new file mode 100644 index 0000000000000..6c4f6d0700a53 --- /dev/null +++ b/src/test/rustdoc/remove-duplicates.rs @@ -0,0 +1,24 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "foo"] + +mod foo { + pub use bar::*; + pub mod bar { + pub trait Foo { + fn foo(); + } + } +} + +// @count foo/index.html '//*[@class="trait"]' 1 +pub use foo::bar::*; +pub use foo::*; diff --git a/src/test/ui/lint/fn_must_use.rs b/src/test/ui/lint/fn_must_use.rs index 5aea5f2ca0642..c549ded4db218 100644 --- a/src/test/ui/lint/fn_must_use.rs +++ b/src/test/ui/lint/fn_must_use.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(fn_must_use)] #![warn(unused_must_use)] struct MyStruct { diff --git a/src/test/ui/lint/fn_must_use.stderr b/src/test/ui/lint/fn_must_use.stderr index 20eb7452aea71..242837793a0bf 100644 --- a/src/test/ui/lint/fn_must_use.stderr +++ b/src/test/ui/lint/fn_must_use.stderr @@ -1,18 +1,18 @@ warning: unused return value of `need_to_use_this_value` which must be used: it's important - --> $DIR/fn_must_use.rs:30:5 + --> $DIR/fn_must_use.rs:31:5 | -30 | need_to_use_this_value(); +31 | need_to_use_this_value(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: lint level defined here - --> $DIR/fn_must_use.rs:11:9 + --> $DIR/fn_must_use.rs:12:9 | -11 | #![warn(unused_must_use)] +12 | #![warn(unused_must_use)] | ^^^^^^^^^^^^^^^ warning: unused return value of `MyStruct::need_to_use_this_method_value` which must be used - --> $DIR/fn_must_use.rs:33:5 + --> $DIR/fn_must_use.rs:34:5 | -33 | m.need_to_use_this_method_value(); +34 | m.need_to_use_this_method_value(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^