From 20bd7f000fa788b9d38c5d664bb19b1ba38af850 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 21 Nov 2016 23:26:31 +0100 Subject: [PATCH 001/208] utf8 validation: Compute block end upfront Simplify the conditional used for ensuring that the whole word loop is only used if there are at least two whole words left to read. This makes the function slightly smaller and simpler, a 0-5% reduction in runtime for various test cases. --- src/libcore/str/mod.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 7f91da531424..48c0f0b27087 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1217,6 +1217,11 @@ fn contains_nonascii(x: usize) -> bool { fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { let mut offset = 0; let len = v.len(); + + let usize_bytes = mem::size_of::(); + let ascii_block_size = 2 * usize_bytes; + let blocks_end = if len >= ascii_block_size { len - ascii_block_size + 1 } else { 0 }; + while offset < len { let old_offset = offset; macro_rules! err { () => {{ @@ -1282,26 +1287,22 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { // Ascii case, try to skip forward quickly. // When the pointer is aligned, read 2 words of data per iteration // until we find a word containing a non-ascii byte. - let usize_bytes = mem::size_of::(); - let bytes_per_iteration = 2 * usize_bytes; let ptr = v.as_ptr(); let align = (ptr as usize + offset) & (usize_bytes - 1); if align == 0 { - if len >= bytes_per_iteration { - while offset <= len - bytes_per_iteration { - unsafe { - let u = *(ptr.offset(offset as isize) as *const usize); - let v = *(ptr.offset((offset + usize_bytes) as isize) as *const usize); + while offset < blocks_end { + unsafe { + let u = *(ptr.offset(offset as isize) as *const usize); + let v = *(ptr.offset((offset + usize_bytes) as isize) as *const usize); - // break if there is a nonascii byte - let zu = contains_nonascii(u); - let zv = contains_nonascii(v); - if zu || zv { - break; - } + // break if there is a nonascii byte + let zu = contains_nonascii(u); + let zv = contains_nonascii(v); + if zu || zv { + break; } - offset += bytes_per_iteration; } + offset += ascii_block_size; } // step from the point where the wordwise loop stopped while offset < len && v[offset] < 128 { From 4a8b04eda0505948bae2f1be367b10bd8ba082e5 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 22 Nov 2016 13:47:45 +0100 Subject: [PATCH 002/208] utf8 validation: Cleanup code in the ascii fast path --- src/libcore/str/mod.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 48c0f0b27087..7cb70bce7b86 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1292,13 +1292,11 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { if align == 0 { while offset < blocks_end { unsafe { - let u = *(ptr.offset(offset as isize) as *const usize); - let v = *(ptr.offset((offset + usize_bytes) as isize) as *const usize); - + let block = ptr.offset(offset as isize) as *const usize; // break if there is a nonascii byte - let zu = contains_nonascii(u); - let zv = contains_nonascii(v); - if zu || zv { + let zu = contains_nonascii(*block); + let zv = contains_nonascii(*block.offset(1)); + if zu | zv { break; } } From 0dffc1e193f11392ba49b033a0d1c21ab4863b3d Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 22 Nov 2016 13:47:45 +0100 Subject: [PATCH 003/208] utf8 validation: Cleanup code by renaming index variable --- src/libcore/str/mod.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 7cb70bce7b86..c334ac488727 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1215,15 +1215,15 @@ fn contains_nonascii(x: usize) -> bool { /// invalid sequence. #[inline(always)] fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { - let mut offset = 0; + let mut index = 0; let len = v.len(); let usize_bytes = mem::size_of::(); let ascii_block_size = 2 * usize_bytes; let blocks_end = if len >= ascii_block_size { len - ascii_block_size + 1 } else { 0 }; - while offset < len { - let old_offset = offset; + while index < len { + let old_offset = index; macro_rules! err { () => {{ return Err(Utf8Error { valid_up_to: old_offset @@ -1231,15 +1231,15 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { }}} macro_rules! next { () => {{ - offset += 1; + index += 1; // we needed data, but there was none: error! - if offset >= len { + if index >= len { err!() } - v[offset] + v[index] }}} - let first = v[offset]; + let first = v[index]; if first >= 128 { let w = UTF8_CHAR_WIDTH[first as usize]; let second = next!(); @@ -1282,17 +1282,17 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { } _ => err!() } - offset += 1; + index += 1; } else { // Ascii case, try to skip forward quickly. // When the pointer is aligned, read 2 words of data per iteration // until we find a word containing a non-ascii byte. let ptr = v.as_ptr(); - let align = (ptr as usize + offset) & (usize_bytes - 1); + let align = (ptr as usize + index) & (usize_bytes - 1); if align == 0 { - while offset < blocks_end { + while index < blocks_end { unsafe { - let block = ptr.offset(offset as isize) as *const usize; + let block = ptr.offset(index as isize) as *const usize; // break if there is a nonascii byte let zu = contains_nonascii(*block); let zv = contains_nonascii(*block.offset(1)); @@ -1300,14 +1300,14 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { break; } } - offset += ascii_block_size; + index += ascii_block_size; } // step from the point where the wordwise loop stopped - while offset < len && v[offset] < 128 { - offset += 1; + while index < len && v[index] < 128 { + index += 1; } } else { - offset += 1; + index += 1; } } } From d83fff3b3b9dd0fd6eef862e97f883d171367041 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 29 Nov 2016 04:11:12 +0100 Subject: [PATCH 004/208] Use more specific panic message for &str slicing errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Separate out of bounds errors from character boundary errors, and print more details for character boundary errors. Example: &"abcαβγ"[..4] thread 'str::test_slice_fail_boundary_1' panicked at 'byte index 4 is not a char boundary; it is inside `α` (bytes 3..5) of `abcαβγ`' --- src/doc/book/strings.md | 4 ++-- src/libcollectionstest/str.rs | 16 ++++++++++++++-- src/libcore/str/mod.rs | 26 ++++++++++++++++++++++---- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/doc/book/strings.md b/src/doc/book/strings.md index 6af15d876836..a2146b669e3c 100644 --- a/src/doc/book/strings.md +++ b/src/doc/book/strings.md @@ -163,8 +163,8 @@ let hachi = &dog[0..2]; with this error: ```text -thread 'main' panicked at 'index 0 and/or 2 in `忠犬ハチ公` do not lie on -character boundary' +thread 'main' panicked at 'byte index 2 is not a char boundary; it is inside '忠' +(bytes 0..3) of `忠犬ハチ公`' ``` ## Concatenation diff --git a/src/libcollectionstest/str.rs b/src/libcollectionstest/str.rs index 14a0819d381b..9c3c3740aa96 100644 --- a/src/libcollectionstest/str.rs +++ b/src/libcollectionstest/str.rs @@ -383,17 +383,29 @@ tempus vel, gravida nec quam."; // check the panic includes the prefix of the sliced string #[test] -#[should_panic(expected="Lorem ipsum dolor sit amet")] +#[should_panic(expected="byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet")] fn test_slice_fail_truncated_1() { &LOREM_PARAGRAPH[..1024]; } // check the truncation in the panic message #[test] -#[should_panic(expected="luctus, im`[...] do not lie on character boundary")] +#[should_panic(expected="luctus, im`[...]")] fn test_slice_fail_truncated_2() { &LOREM_PARAGRAPH[..1024]; } +#[test] +#[should_panic(expected="byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of")] +fn test_slice_fail_boundary_1() { + &"abcαβγ"[4..]; +} + +#[test] +#[should_panic(expected="byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of")] +fn test_slice_fail_boundary_2() { + &"abcαβγ"[2..6]; +} + #[test] fn test_slice_from() { assert_eq!(&"abcd"[0..], "abcd"); diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index b4cd52e59f65..7081c3ebf43a 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1741,13 +1741,31 @@ fn truncate_to_char_boundary(s: &str, mut max: usize) -> (bool, &str) { #[cold] fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { const MAX_DISPLAY_LENGTH: usize = 256; - let (truncated, s) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH); + let (truncated, s_trunc) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH); let ellipsis = if truncated { "[...]" } else { "" }; + // 1. out of bounds + if begin > s.len() || end > s.len() { + let oob_index = if begin > s.len() { begin } else { end }; + panic!("byte index {} is out of bounds of `{}`{}", oob_index, s_trunc, ellipsis); + } + + // 2. begin <= end assert!(begin <= end, "begin <= end ({} <= {}) when slicing `{}`{}", - begin, end, s, ellipsis); - panic!("index {} and/or {} in `{}`{} do not lie on character boundary", - begin, end, s, ellipsis); + begin, end, s_trunc, ellipsis); + + // 3. character boundary + let index = if !s.is_char_boundary(begin) { begin } else { end }; + // find the character + let mut char_start = index; + while !s.is_char_boundary(char_start) { + char_start -= 1; + } + // `char_start` must be less than len and a char boundary + let ch = s[char_start..].chars().next().unwrap(); + let char_range = char_start .. char_start + ch.len_utf8(); + panic!("byte index {} is not a char boundary; it is inside {:?} (bytes {:?}) of `{}`{}", + index, ch, char_range, s_trunc, ellipsis); } #[stable(feature = "core", since = "1.6.0")] From 1ae2245a4e89d90f8bacef1d1d05fcf7461596fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 8 Dec 2016 10:14:35 -0800 Subject: [PATCH 005/208] rustdoc: escape the deprecated and unstable reason text --- src/liballoc/boxed.rs | 8 ++++---- src/librustdoc/html/render.rs | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index addb056f5342..5409ade29236 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -587,7 +587,7 @@ impl FusedIterator for Box {} /// ``` #[rustc_paren_sugar] #[unstable(feature = "fnbox", - reason = "will be deprecated if and when Box becomes usable", issue = "28796")] + reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] pub trait FnBox { type Output; @@ -595,7 +595,7 @@ pub trait FnBox { } #[unstable(feature = "fnbox", - reason = "will be deprecated if and when Box becomes usable", issue = "28796")] + reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] impl FnBox for F where F: FnOnce { @@ -607,7 +607,7 @@ impl FnBox for F } #[unstable(feature = "fnbox", - reason = "will be deprecated if and when Box becomes usable", issue = "28796")] + reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] impl<'a, A, R> FnOnce for Box + 'a> { type Output = R; @@ -617,7 +617,7 @@ impl<'a, A, R> FnOnce for Box + 'a> { } #[unstable(feature = "fnbox", - reason = "will be deprecated if and when Box becomes usable", issue = "28796")] + reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] impl<'a, A, R> FnOnce for Box + Send + 'a> { type Output = R; diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index e721b66779ff..29781888fdb4 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1844,7 +1844,7 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec Vec Vec Date: Mon, 12 Dec 2016 15:18:22 -0800 Subject: [PATCH 006/208] Add `MarkdownHmtl` escape struct `MarkdownHtml` structs escape HTML tags from its text. --- src/librustdoc/html/markdown.rs | 24 ++++++++++++++++++++---- src/librustdoc/html/render.rs | 14 +++++++------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 67cf12f4f4a6..b4f86c1ae77a 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -49,6 +49,8 @@ pub struct Markdown<'a>(pub &'a str); /// A unit struct like `Markdown`, that renders the markdown with a /// table of contents. pub struct MarkdownWithToc<'a>(pub &'a str); +/// A unit struct like `Markdown`, that renders the markdown escaping HTML tags. +pub struct MarkdownHtml<'a>(pub &'a str); const DEF_OUNIT: libc::size_t = 64; const HOEDOWN_EXT_NO_INTRA_EMPHASIS: libc::c_uint = 1 << 11; @@ -58,6 +60,7 @@ const HOEDOWN_EXT_AUTOLINK: libc::c_uint = 1 << 3; const HOEDOWN_EXT_STRIKETHROUGH: libc::c_uint = 1 << 4; const HOEDOWN_EXT_SUPERSCRIPT: libc::c_uint = 1 << 8; const HOEDOWN_EXT_FOOTNOTES: libc::c_uint = 1 << 2; +const HOEDOWN_HTML_ESCAPE: libc::c_uint = 1 << 1; const HOEDOWN_EXTENSIONS: libc::c_uint = HOEDOWN_EXT_NO_INTRA_EMPHASIS | HOEDOWN_EXT_TABLES | @@ -220,7 +223,11 @@ thread_local!(pub static PLAYGROUND: RefCell, String)>> = RefCell::new(None) }); -pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result { + +pub fn render(w: &mut fmt::Formatter, + s: &str, + print_toc: bool, + html_flags: libc::c_uint) -> fmt::Result { extern fn block(ob: *mut hoedown_buffer, orig_text: *const hoedown_buffer, lang: *const hoedown_buffer, data: *const hoedown_renderer_data) { unsafe { @@ -383,7 +390,7 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result { unsafe { let ob = hoedown_buffer_new(DEF_OUNIT); - let renderer = hoedown_html_renderer_new(0, 0); + let renderer = hoedown_html_renderer_new(html_flags, 0); let mut opaque = MyOpaque { dfltblk: (*renderer).blockcode.unwrap(), toc_builder: if print_toc {Some(TocBuilder::new())} else {None} @@ -553,14 +560,23 @@ impl<'a> fmt::Display for Markdown<'a> { let Markdown(md) = *self; // This is actually common enough to special-case if md.is_empty() { return Ok(()) } - render(fmt, md, false) + render(fmt, md, false, 0) } } impl<'a> fmt::Display for MarkdownWithToc<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let MarkdownWithToc(md) = *self; - render(fmt, md, true) + render(fmt, md, true, 0) + } +} + +impl<'a> fmt::Display for MarkdownHtml<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let MarkdownHtml(md) = *self; + // This is actually common enough to special-case + if md.is_empty() { return Ok(()) } + render(fmt, md, false, HOEDOWN_HTML_ESCAPE) } } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 29781888fdb4..e21898499a3c 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -71,7 +71,7 @@ use html::format::{TyParamBounds, WhereClause, href, AbiSpace}; use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace}; use html::format::fmt_impl_for_trait_page; use html::item_type::ItemType; -use html::markdown::{self, Markdown}; +use html::markdown::{self, Markdown, MarkdownHtml}; use html::{highlight, layout}; /// A pair of name and its optional document. @@ -1844,7 +1844,7 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec Vec{}", text)) }; @@ -1875,16 +1875,16 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec{}", text)) }; } else if let Some(depr) = item.deprecation.as_ref() { let note = if show_reason && !depr.note.is_empty() { - format!(": {}", Escape(&depr.note)) + format!(": {}", depr.note) } else { String::new() }; @@ -1894,7 +1894,7 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec{}", text)) } From a99f70b1e424595dbdcbad4a415f549a6d6686bc Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sun, 11 Dec 2016 13:36:03 -0800 Subject: [PATCH 007/208] Clarify zero-value behavior of `ctlz`/`cttz` intrinsics. Fixes https://github.com/rust-lang/rust/issues/34381. --- src/libcore/intrinsics.rs | 52 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 3726eee9a93c..31a0cc688418 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1156,10 +1156,58 @@ extern "rust-intrinsic" { /// Returns the number of bits set in an integer type `T` pub fn ctpop(x: T) -> T; - /// Returns the number of leading bits unset in an integer type `T` + /// Returns the number of leading unset bits (zeroes) in an integer type `T`. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_intrinsics)] + /// + /// use std::intrinsics::ctlz; + /// + /// let x = 0b0001_1100_u8; + /// let num_leading = unsafe { ctlz(x) }; + /// assert_eq!(num_leading, 3); + /// ``` + /// + /// An `x` with value `0` will return the bit width of `T`. + /// + /// ``` + /// #![feature(core_intrinsics)] + /// + /// use std::intrinsics::ctlz; + /// + /// let x = 0u16; + /// let num_leading = unsafe { ctlz(x) }; + /// assert_eq!(num_leading, 16); + /// ``` pub fn ctlz(x: T) -> T; - /// Returns the number of trailing bits unset in an integer type `T` + /// Returns the number of trailing unset bits (zeroes) in an integer type `T`. + /// + /// # Examples + /// + /// ``` + /// #![feature(core_intrinsics)] + /// + /// use std::intrinsics::cttz; + /// + /// let x = 0b0011_1000_u8; + /// let num_trailing = unsafe { cttz(x) }; + /// assert_eq!(num_trailing, 3); + /// ``` + /// + /// An `x` with value `0` will return the bit width of `T`: + /// + /// ``` + /// #![feature(core_intrinsics)] + /// + /// use std::intrinsics::cttz; + /// + /// let x = 0u16; + /// let num_trailing = unsafe { cttz(x) }; + /// assert_eq!(num_trailing, 16); + /// ``` pub fn cttz(x: T) -> T; /// Reverses the bytes in an integer type `T`. From 2841bf3bc79bb866980c5aea55a96a3d1cdea2f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 15 Dec 2016 23:13:00 -0800 Subject: [PATCH 008/208] Rustdoc: disambiguate Implementors when the type name is not unique --- src/librustdoc/html/format.rs | 354 ++++++++++++++++++---------------- src/librustdoc/html/render.rs | 18 +- 2 files changed, 201 insertions(+), 171 deletions(-) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 6dc6e80dae0b..6808752b1fe0 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -433,7 +433,7 @@ pub fn href(did: DefId) -> Option<(String, ItemType, Vec)> { /// Used when rendering a `ResolvedPath` structure. This invokes the `path` /// rendering function with the necessary arguments for linking to a local path. fn resolved_path(w: &mut fmt::Formatter, did: DefId, path: &clean::Path, - print_all: bool) -> fmt::Result { + print_all: bool, use_absolute: bool) -> fmt::Result { let last = path.segments.last().unwrap(); let rel_root = match &*path.segments[0].name { "self" => Some("./".to_string()), @@ -467,7 +467,17 @@ fn resolved_path(w: &mut fmt::Formatter, did: DefId, path: &clean::Path, if w.alternate() { write!(w, "{:#}{:#}", HRef::new(did, &last.name), last.params)?; } else { - write!(w, "{}{}", HRef::new(did, &last.name), last.params)?; + let path = if use_absolute { + match href(did) { + Some((_, _, fqp)) => format!("{}::{}", + fqp[..fqp.len()-1].join("::"), + HRef::new(did, fqp.last().unwrap())), + None => format!("{}", HRef::new(did, &last.name)), + } + } else { + format!("{}", HRef::new(did, &last.name)) + }; + write!(w, "{}{}", path, last.params)?; } Ok(()) } @@ -551,194 +561,198 @@ impl<'a> fmt::Display for HRef<'a> { } } -impl fmt::Display for clean::Type { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - clean::Generic(ref name) => { - f.write_str(name) +fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, full_path: bool) -> fmt::Result { + match *t { + clean::Generic(ref name) => { + f.write_str(name) + } + clean::ResolvedPath{ did, ref typarams, ref path, is_generic } => { + // Paths like T::Output and Self::Output should be rendered with all segments + resolved_path(f, did, path, is_generic, full_path)?; + tybounds(f, typarams) + } + clean::Infer => write!(f, "_"), + clean::Primitive(prim) => primitive_link(f, prim, prim.as_str()), + clean::BareFunction(ref decl) => { + if f.alternate() { + write!(f, "{}{}fn{:#}{:#}", + UnsafetySpace(decl.unsafety), + AbiSpace(decl.abi), + decl.generics, + decl.decl) + } else { + write!(f, "{}{}fn{}{}", + UnsafetySpace(decl.unsafety), + AbiSpace(decl.abi), + decl.generics, + decl.decl) } - clean::ResolvedPath{ did, ref typarams, ref path, is_generic } => { - // Paths like T::Output and Self::Output should be rendered with all segments - resolved_path(f, did, path, is_generic)?; - tybounds(f, typarams) - } - clean::Infer => write!(f, "_"), - clean::Primitive(prim) => primitive_link(f, prim, prim.as_str()), - clean::BareFunction(ref decl) => { - if f.alternate() { - write!(f, "{}{}fn{:#}{:#}", - UnsafetySpace(decl.unsafety), - AbiSpace(decl.abi), - decl.generics, - decl.decl) - } else { - write!(f, "{}{}fn{}{}", - UnsafetySpace(decl.unsafety), - AbiSpace(decl.abi), - decl.generics, - decl.decl) + } + clean::Tuple(ref typs) => { + match &typs[..] { + &[] => primitive_link(f, PrimitiveType::Tuple, "()"), + &[ref one] => { + primitive_link(f, PrimitiveType::Tuple, "(")?; + //carry f.alternate() into this display w/o branching manually + fmt::Display::fmt(one, f)?; + primitive_link(f, PrimitiveType::Tuple, ",)") + } + many => { + primitive_link(f, PrimitiveType::Tuple, "(")?; + fmt::Display::fmt(&CommaSep(&many), f)?; + primitive_link(f, PrimitiveType::Tuple, ")") } } - clean::Tuple(ref typs) => { - match &typs[..] { - &[] => primitive_link(f, PrimitiveType::Tuple, "()"), - &[ref one] => { - primitive_link(f, PrimitiveType::Tuple, "(")?; - //carry f.alternate() into this display w/o branching manually - fmt::Display::fmt(one, f)?; - primitive_link(f, PrimitiveType::Tuple, ",)") - } - many => { - primitive_link(f, PrimitiveType::Tuple, "(")?; - fmt::Display::fmt(&CommaSep(&many), f)?; - primitive_link(f, PrimitiveType::Tuple, ")") - } - } + } + clean::Vector(ref t) => { + primitive_link(f, PrimitiveType::Slice, &format!("["))?; + fmt::Display::fmt(t, f)?; + primitive_link(f, PrimitiveType::Slice, &format!("]")) + } + clean::FixedVector(ref t, ref s) => { + primitive_link(f, PrimitiveType::Array, "[")?; + fmt::Display::fmt(t, f)?; + if f.alternate() { + primitive_link(f, PrimitiveType::Array, + &format!("; {}]", s)) + } else { + primitive_link(f, PrimitiveType::Array, + &format!("; {}]", Escape(s))) } - clean::Vector(ref t) => { - primitive_link(f, PrimitiveType::Slice, &format!("["))?; - fmt::Display::fmt(t, f)?; - primitive_link(f, PrimitiveType::Slice, &format!("]")) - } - clean::FixedVector(ref t, ref s) => { - primitive_link(f, PrimitiveType::Array, "[")?; - fmt::Display::fmt(t, f)?; - if f.alternate() { - primitive_link(f, PrimitiveType::Array, - &format!("; {}]", s)) - } else { - primitive_link(f, PrimitiveType::Array, - &format!("; {}]", Escape(s))) - } - } - clean::Never => f.write_str("!"), - clean::RawPointer(m, ref t) => { - match **t { - clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => { - if f.alternate() { - primitive_link(f, clean::PrimitiveType::RawPointer, - &format!("*{}{:#}", RawMutableSpace(m), t)) - } else { - primitive_link(f, clean::PrimitiveType::RawPointer, - &format!("*{}{}", RawMutableSpace(m), t)) - } - } - _ => { + } + clean::Never => f.write_str("!"), + clean::RawPointer(m, ref t) => { + match **t { + clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => { + if f.alternate() { primitive_link(f, clean::PrimitiveType::RawPointer, - &format!("*{}", RawMutableSpace(m)))?; - fmt::Display::fmt(t, f) + &format!("*{}{:#}", RawMutableSpace(m), t)) + } else { + primitive_link(f, clean::PrimitiveType::RawPointer, + &format!("*{}{}", RawMutableSpace(m), t)) } } + _ => { + primitive_link(f, clean::PrimitiveType::RawPointer, + &format!("*{}", RawMutableSpace(m)))?; + fmt::Display::fmt(t, f) + } } - clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => { - let lt = match *l { - Some(ref l) => format!("{} ", *l), - _ => "".to_string(), - }; - let m = MutableSpace(mutability); - match **ty { - clean::Vector(ref bt) => { // BorrowedRef{ ... Vector(T) } is &[T] - match **bt { - clean::Generic(_) => - if f.alternate() { - primitive_link(f, PrimitiveType::Slice, - &format!("&{}{}[{:#}]", lt, m, **bt)) - } else { - primitive_link(f, PrimitiveType::Slice, - &format!("&{}{}[{}]", lt, m, **bt)) - }, - _ => { - if f.alternate() { - primitive_link(f, PrimitiveType::Slice, - &format!("&{}{}[", lt, m))?; - write!(f, "{:#}", **bt)?; - } else { - primitive_link(f, PrimitiveType::Slice, - &format!("&{}{}[", lt, m))?; - write!(f, "{}", **bt)?; - } - primitive_link(f, PrimitiveType::Slice, "]") + } + clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => { + let lt = match *l { + Some(ref l) => format!("{} ", *l), + _ => "".to_string(), + }; + let m = MutableSpace(mutability); + match **ty { + clean::Vector(ref bt) => { // BorrowedRef{ ... Vector(T) } is &[T] + match **bt { + clean::Generic(_) => + if f.alternate() { + primitive_link(f, PrimitiveType::Slice, + &format!("&{}{}[{:#}]", lt, m, **bt)) + } else { + primitive_link(f, PrimitiveType::Slice, + &format!("&{}{}[{}]", lt, m, **bt)) + }, + _ => { + if f.alternate() { + primitive_link(f, PrimitiveType::Slice, + &format!("&{}{}[", lt, m))?; + write!(f, "{:#}", **bt)?; + } else { + primitive_link(f, PrimitiveType::Slice, + &format!("&{}{}[", lt, m))?; + write!(f, "{}", **bt)?; } - } - } - _ => { - if f.alternate() { - write!(f, "&{}{}{:#}", lt, m, **ty) - } else { - write!(f, "&{}{}{}", lt, m, **ty) + primitive_link(f, PrimitiveType::Slice, "]") } } } - } - clean::PolyTraitRef(ref bounds) => { - for (i, bound) in bounds.iter().enumerate() { - if i != 0 { - write!(f, " + ")?; - } + _ => { if f.alternate() { - write!(f, "{:#}", *bound)?; + write!(f, "&{}{}{:#}", lt, m, **ty) } else { - write!(f, "{}", *bound)?; + write!(f, "&{}{}{}", lt, m, **ty) } } - Ok(()) } - clean::ImplTrait(ref bounds) => { - write!(f, "impl ")?; - for (i, bound) in bounds.iter().enumerate() { - if i != 0 { - write!(f, " + ")?; - } - if f.alternate() { - write!(f, "{:#}", *bound)?; - } else { - write!(f, "{}", *bound)?; - } + } + clean::PolyTraitRef(ref bounds) => { + for (i, bound) in bounds.iter().enumerate() { + if i != 0 { + write!(f, " + ")?; } - Ok(()) - } - // It's pretty unsightly to look at `::C` in output, and - // we've got hyperlinking on our side, so try to avoid longer - // notation as much as possible by making `C` a hyperlink to trait - // `B` to disambiguate. - // - // FIXME: this is still a lossy conversion and there should probably - // be a better way of representing this in general? Most of - // the ugliness comes from inlining across crates where - // everything comes in as a fully resolved QPath (hard to - // look at). - clean::QPath { - ref name, - ref self_type, - trait_: box clean::ResolvedPath { did, ref typarams, .. }, - } => { if f.alternate() { - write!(f, "{:#}::", self_type)?; + write!(f, "{:#}", *bound)?; } else { - write!(f, "{}::", self_type)?; + write!(f, "{}", *bound)?; } - let path = clean::Path::singleton(name.clone()); - resolved_path(f, did, &path, false)?; + } + Ok(()) + } + clean::ImplTrait(ref bounds) => { + write!(f, "impl ")?; + for (i, bound) in bounds.iter().enumerate() { + if i != 0 { + write!(f, " + ")?; + } + if f.alternate() { + write!(f, "{:#}", *bound)?; + } else { + write!(f, "{}", *bound)?; + } + } + Ok(()) + } + // It's pretty unsightly to look at `::C` in output, and + // we've got hyperlinking on our side, so try to avoid longer + // notation as much as possible by making `C` a hyperlink to trait + // `B` to disambiguate. + // + // FIXME: this is still a lossy conversion and there should probably + // be a better way of representing this in general? Most of + // the ugliness comes from inlining across crates where + // everything comes in as a fully resolved QPath (hard to + // look at). + clean::QPath { + ref name, + ref self_type, + trait_: box clean::ResolvedPath { did, ref typarams, .. }, + } => { + if f.alternate() { + write!(f, "{:#}::", self_type)?; + } else { + write!(f, "{}::", self_type)?; + } + let path = clean::Path::singleton(name.clone()); + resolved_path(f, did, &path, true, full_path)?; - // FIXME: `typarams` are not rendered, and this seems bad? - drop(typarams); - Ok(()) - } - clean::QPath { ref name, ref self_type, ref trait_ } => { - if f.alternate() { - write!(f, "<{:#} as {:#}>::{}", self_type, trait_, name) - } else { - write!(f, "<{} as {}>::{}", self_type, trait_, name) - } - } - clean::Unique(..) => { - panic!("should have been cleaned") + // FIXME: `typarams` are not rendered, and this seems bad? + drop(typarams); + Ok(()) + } + clean::QPath { ref name, ref self_type, ref trait_ } => { + if f.alternate() { + write!(f, "<{:#} as {:#}>::{}", self_type, trait_, name) + } else { + write!(f, "<{} as {}>::{}", self_type, trait_, name) } } + clean::Unique(..) => { + panic!("should have been cleaned") + } } } -fn fmt_impl(i: &clean::Impl, f: &mut fmt::Formatter, link_trait: bool) -> fmt::Result { +impl fmt::Display for clean::Type { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt_type(self, f, false) + } +} + +fn fmt_impl(i: &clean::Impl, f: &mut fmt::Formatter, link_trait: bool, full: bool) -> fmt::Result { let mut plain = String::new(); if f.alternate() { @@ -759,7 +773,7 @@ fn fmt_impl(i: &clean::Impl, f: &mut fmt::Formatter, link_trait: bool) -> fmt::R plain.push_str(&format!("{:#}", ty)); } else { match *ty { - clean::ResolvedPath{ typarams: None, ref path, is_generic: false, .. } => { + clean::ResolvedPath { typarams: None, ref path, is_generic: false, .. } => { let last = path.segments.last().unwrap(); fmt::Display::fmt(&last.name, f)?; fmt::Display::fmt(&last.params, f)?; @@ -772,7 +786,7 @@ fn fmt_impl(i: &clean::Impl, f: &mut fmt::Formatter, link_trait: bool) -> fmt::R plain.push_str(" for "); } - fmt::Display::fmt(&i.for_, f)?; + fmt_type(&i.for_, f, full)?; plain.push_str(&format!("{:#}", i.for_)); fmt::Display::fmt(&WhereClause(&i.generics, plain.len() + 1), f)?; @@ -781,13 +795,15 @@ fn fmt_impl(i: &clean::Impl, f: &mut fmt::Formatter, link_trait: bool) -> fmt::R impl fmt::Display for clean::Impl { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt_impl(self, f, true) + fmt_impl(self, f, true, false) } } // The difference from above is that trait is not hyperlinked. -pub fn fmt_impl_for_trait_page(i: &clean::Impl, f: &mut fmt::Formatter) -> fmt::Result { - fmt_impl(i, f, false) +pub fn fmt_impl_for_trait_page(i: &clean::Impl, + f: &mut fmt::Formatter, + disambiguate: bool) -> fmt::Result { + fmt_impl(i, f, false, disambiguate) } impl fmt::Display for clean::Arguments { @@ -978,7 +994,7 @@ impl fmt::Display for clean::Import { impl fmt::Display for clean::ImportSource { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.did { - Some(did) => resolved_path(f, did, &self.path, true), + Some(did) => resolved_path(f, did, &self.path, true, false), _ => { for (i, seg) in self.path.segments.iter().enumerate() { if i > 0 { diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index e721b66779ff..c7a258145537 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -2111,9 +2111,23 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,