From 538689ddc7fe9a7176d6722b6c37e2e082e5fd08 Mon Sep 17 00:00:00 2001 From: mitaa Date: Thu, 3 Dec 2015 00:06:26 +0100 Subject: [PATCH 1/5] Move ID generator to a more suited location --- src/librustdoc/html/markdown.rs | 49 ++++++++------------------------- src/librustdoc/html/render.rs | 31 ++++++++++++++++++++- src/librustdoc/markdown.rs | 5 ++-- 3 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index f68e82501e91e..9496b7e2a6fbe 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -14,7 +14,7 @@ //! (bundled into the rust runtime). This module self-contains the C bindings //! and necessary legwork to render markdown, and exposes all of the //! functionality through a unit-struct, `Markdown`, which has an implementation -//! of `fmt::String`. Example usage: +//! of `fmt::Display`. Example usage: //! //! ```rust,ignore //! use rustdoc::html::markdown::Markdown; @@ -29,19 +29,19 @@ use libc; use std::ascii::AsciiExt; use std::cell::RefCell; -use std::collections::HashMap; use std::default::Default; use std::ffi::CString; use std::fmt; use std::slice; use std::str; +use html::render::{with_unique_id, reset_ids}; use html::toc::TocBuilder; use html::highlight; use html::escape::Escape; use test; -/// A unit struct which has the `fmt::String` trait implemented. When +/// A unit struct which has the `fmt::Display` trait implemented. When /// formatted, this struct will emit the HTML corresponding to the rendered /// version of the contained markdown string. pub struct Markdown<'a>(pub &'a str); @@ -210,10 +210,6 @@ fn collapse_whitespace(s: &str) -> String { s.split_whitespace().collect::>().join(" ") } -thread_local!(static USED_HEADER_MAP: RefCell> = { - RefCell::new(HashMap::new()) -}); - thread_local!(pub static PLAYGROUND_KRATE: RefCell>> = { RefCell::new(None) }); @@ -311,31 +307,22 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result { let opaque = unsafe { (*data).opaque as *mut hoedown_html_renderer_state }; let opaque = unsafe { &mut *((*opaque).opaque as *mut MyOpaque) }; - // Make sure our hyphenated ID is unique for this page - let id = USED_HEADER_MAP.with(|map| { - let id = match map.borrow_mut().get_mut(&id) { - None => id, - Some(a) => { *a += 1; format!("{}-{}", id, *a - 1) } - }; - map.borrow_mut().insert(id.clone(), 1); - id - }); - + let text = with_unique_id(id, |id| { + let sec = opaque.toc_builder.as_mut().map_or("".to_owned(), |builder| { + format!("{} ", builder.push(level as u32, s.clone(), id.to_owned())) + }); - let sec = opaque.toc_builder.as_mut().map_or("".to_owned(), |builder| { - format!("{} ", builder.push(level as u32, s.clone(), id.clone())) + // Render the HTML + format!("\ + {sec}{}", + s, lvl = level, id = id, sec = sec) }); - // Render the HTML - let text = format!("\ - {sec}{}", - s, lvl = level, id = id, sec = sec); - let text = CString::new(text).unwrap(); unsafe { hoedown_buffer_puts(ob, text.as_ptr()) } } - reset_headers(); + reset_ids(); extern fn codespan( ob: *mut hoedown_buffer, @@ -500,18 +487,6 @@ impl LangString { } } -/// By default this markdown renderer generates anchors for each header in the -/// rendered document. The anchor name is the contents of the header separated -/// by hyphens, and a thread-local map is used to disambiguate among duplicate -/// headers (numbers are appended). -/// -/// This method will reset the local table for these headers. This is typically -/// used at the beginning of rendering an entire HTML page to reset from the -/// previous state (if any). -pub fn reset_headers() { - USED_HEADER_MAP.with(|s| s.borrow_mut().clear()); -} - impl<'a> fmt::Display for Markdown<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let Markdown(md) = *self; diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 574b9b599f5f8..c4fb9d5d9e7d5 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -342,6 +342,35 @@ impl fmt::Display for IndexItemFunctionType { thread_local!(static CACHE_KEY: RefCell> = Default::default()); thread_local!(pub static CURRENT_LOCATION_KEY: RefCell> = RefCell::new(Vec::new())); +thread_local!(static USED_ID_MAP: RefCell> = + RefCell::new(HashMap::new())); + +/// This method resets the local table of used ID attributes. This is typically +/// used at the beginning of rendering an entire HTML page to reset from the +/// previous state (if any). +pub fn reset_ids() { + USED_ID_MAP.with(|s| s.borrow_mut().clear()); +} + +pub fn with_unique_id T>(candidate: String, f: F) -> T { + USED_ID_MAP.with(|map| { + let (id, ret) = match map.borrow_mut().get_mut(&candidate) { + None => { + let ret = f(&candidate); + (candidate, ret) + }, + Some(a) => { + let id = format!("{}-{}", candidate, *a); + let ret = f(&id); + *a += 1; + (id, ret) + } + }; + + map.borrow_mut().insert(id, 1); + ret + }) +} /// Generates the documentation for `crate` into the directory `dst` pub fn run(mut krate: clean::Crate, @@ -1274,7 +1303,7 @@ impl Context { keywords: &keywords, }; - markdown::reset_headers(); + reset_ids(); // We have a huge number of calls to write, so try to alleviate some // of the pain by using a buffered writer instead of invoking the diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index a311b938e9609..ac64fd3bec0e7 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -21,9 +21,10 @@ use rustc::session::search_paths::SearchPaths; use externalfiles::ExternalHtml; +use html::render::reset_ids; use html::escape::Escape; use html::markdown; -use html::markdown::{Markdown, MarkdownWithToc, find_testable_code, reset_headers}; +use html::markdown::{Markdown, MarkdownWithToc, find_testable_code}; use test::{TestOptions, Collector}; /// Separate any lines at the start of the file that begin with `%`. @@ -82,7 +83,7 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches, } let title = metadata[0]; - reset_headers(); + reset_ids(); let rendered = if include_toc { format!("{}", MarkdownWithToc(text)) From fa0269dd35eef80d7898de3843f9ed327c6f3a0c Mon Sep 17 00:00:00 2001 From: mitaa Date: Wed, 2 Dec 2015 23:49:18 +0100 Subject: [PATCH 2/5] Generate unique IDs for each rustdoc HTML page --- src/librustdoc/html/markdown.rs | 6 +-- src/librustdoc/html/render.rs | 70 ++++++++++++++++----------------- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 9496b7e2a6fbe..eccdd08db8a75 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -35,7 +35,7 @@ use std::fmt; use std::slice; use std::str; -use html::render::{with_unique_id, reset_ids}; +use html::render::with_unique_id; use html::toc::TocBuilder; use html::highlight; use html::escape::Escape; @@ -322,8 +322,6 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result { unsafe { hoedown_buffer_puts(ob, text.as_ptr()) } } - reset_ids(); - extern fn codespan( ob: *mut hoedown_buffer, text: *const hoedown_buffer, @@ -554,6 +552,7 @@ pub fn plain_summary_line(md: &str) -> String { mod tests { use super::{LangString, Markdown}; use super::plain_summary_line; + use html::render::reset_ids; #[test] fn test_lang_string_parse() { @@ -593,6 +592,7 @@ mod tests { fn t(input: &str, expect: &str) { let output = format!("{}", Markdown(input)); assert_eq!(output, expect); + reset_ids(); } t("# Foo bar", "\n

\ diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index c4fb9d5d9e7d5..e7fe4de8d04a3 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1725,10 +1725,10 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, ItemType::AssociatedType => ("associated-types", "Associated Types"), ItemType::AssociatedConst => ("associated-consts", "Associated Constants"), }; - try!(write!(w, - "

\ - {name}

\n", - id = short, name = name)); + try!(with_unique_id(short.to_owned(), |id| + write!(w, "

\ + {name}

\n
", + id = id, name = name))); } match myitem.inner { @@ -1949,10 +1949,11 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, fn trait_item(w: &mut fmt::Formatter, cx: &Context, m: &clean::Item) -> fmt::Result { - try!(write!(w, "

", - ty = shortty(m), - name = *m.name.as_ref().unwrap(), - stab = m.stability_class())); + let name = m.name.as_ref().unwrap(); + try!(with_unique_id(format!("{}.{}", shortty(m), name), |id| + write!(w, "

", + id = id, + stab = m.stability_class()))); try!(render_assoc_item(w, m, AssocItemLink::Anchor)); try!(write!(w, "

")); try!(document(w, cx, m)); @@ -2141,11 +2142,12 @@ fn item_struct(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, if fields.peek().is_some() { try!(write!(w, "

Fields

\n

")); for field in fields { - try!(write!(w, " - ")); } @@ -2212,8 +2214,9 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, if !e.variants.is_empty() { try!(write!(w, "

Variants

\n
\ - {name}", - stab = field.stability_class(), - name = field.name.as_ref().unwrap())); + let name = field.name.as_ref().unwrap(); + try!(with_unique_id(format!("structfield.{}", name), |id| + write!(w, "
{}", + field.stability_class(), + id, + name))); try!(document(w, cx, field)); try!(write!(w, "
")); for variant in &e.variants { - try!(write!(w, "
{name}", - name = variant.name.as_ref().unwrap())); + let name = variant.name.as_ref().unwrap(); + try!(with_unique_id(format!("variant.{}", name), |id| + write!(w, "
{}", id, name))); try!(document(w, cx, variant)); match variant.inner { clean::VariantItem(ref var) => { @@ -2231,11 +2234,10 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, try!(write!(w, "

Fields

\n ")); for field in fields { - try!(write!(w, "")); } @@ -2447,44 +2449,38 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi fn doctraititem(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item, link: AssocItemLink, render_static: bool) -> fmt::Result { + let name = item.name.as_ref().unwrap(); match item.inner { clean::MethodItem(..) | clean::TyMethodItem(..) => { // Only render when the method is not static or we allow static methods if !is_static_method(item) || render_static { - try!(write!(w, "

", - *item.name.as_ref().unwrap(), - shortty(item))); + try!(with_unique_id(format!("method.{}", name), |id| + write!(w, "

", id, shortty(item)))); try!(render_assoc_item(w, item, link)); try!(write!(w, "

\n")); } } clean::TypedefItem(ref tydef, _) => { - let name = item.name.as_ref().unwrap(); - try!(write!(w, "

", - *name, - shortty(item))); + try!(with_unique_id(format!("assoc_type.{}", name), |id| + write!(w, "

", id, shortty(item)))); try!(write!(w, "type {} = {}", name, tydef.type_)); try!(write!(w, "

\n")); } clean::AssociatedConstItem(ref ty, ref default) => { - let name = item.name.as_ref().unwrap(); - try!(write!(w, "

", - *name, shortty(item))); + try!(with_unique_id(format!("assoc_const.{}", name), |id| + write!(w, "

", id, shortty(item)))); try!(assoc_const(w, item, ty, default.as_ref())); try!(write!(w, "

\n")); } clean::ConstantItem(ref c) => { - let name = item.name.as_ref().unwrap(); - try!(write!(w, "

", - *name, shortty(item))); + try!(with_unique_id(format!("assoc_const.{}", name), |id| + write!(w, "

", id, shortty(item)))); try!(assoc_const(w, item, &c.type_, Some(&c.expr))); try!(write!(w, "

\n")); } clean::AssociatedTypeItem(ref bounds, ref default) => { - let name = item.name.as_ref().unwrap(); - try!(write!(w, "

", - *name, - shortty(item))); + try!(with_unique_id(format!("assoc_type.{}", name), |id| + write!(w, "

", id, shortty(item)))); try!(assoc_type(w, item, bounds, default)); try!(write!(w, "

\n")); } From 5c01cf485f6ebe38cbb638800684c88423acaadb Mon Sep 17 00:00:00 2001 From: mitaa Date: Thu, 3 Dec 2015 02:19:23 +0100 Subject: [PATCH 3/5] Initialize the ID map with known values --- src/librustdoc/html/render.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index e7fe4de8d04a3..3372c9d4807ad 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -343,13 +343,33 @@ thread_local!(static CACHE_KEY: RefCell> = Default::default()); thread_local!(pub static CURRENT_LOCATION_KEY: RefCell> = RefCell::new(Vec::new())); thread_local!(static USED_ID_MAP: RefCell> = - RefCell::new(HashMap::new())); + RefCell::new(init_ids())); + +fn init_ids() -> HashMap { + [ + "main", + "search", + "help", + "TOC", + "render-detail", + "associated-types", + "associated-const", + "required-methods", + "provided-methods", + "implementors", + "implementors-list", + "methods", + "deref-methods", + "implementations", + "derived_implementations" + ].into_iter().map(|id| (String::from(*id), 1)).collect::>() +} /// This method resets the local table of used ID attributes. This is typically /// used at the beginning of rendering an entire HTML page to reset from the /// previous state (if any). pub fn reset_ids() { - USED_ID_MAP.with(|s| s.borrow_mut().clear()); + USED_ID_MAP.with(|s| *s.borrow_mut() = init_ids()); } pub fn with_unique_id T>(candidate: String, f: F) -> T { From 72d5675fef11c85b481b0b139c59fb27e1d327b6 Mon Sep 17 00:00:00 2001 From: mitaa Date: Fri, 4 Dec 2015 00:48:59 +0100 Subject: [PATCH 4/5] Address review comments --- src/librustdoc/html/markdown.rs | 18 ++++---- src/librustdoc/html/render.rs | 74 +++++++++++++++------------------ 2 files changed, 43 insertions(+), 49 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index eccdd08db8a75..a6d6802c0e10e 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -35,7 +35,7 @@ use std::fmt; use std::slice; use std::str; -use html::render::with_unique_id; +use html::render::derive_id; use html::toc::TocBuilder; use html::highlight; use html::escape::Escape; @@ -307,17 +307,17 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result { let opaque = unsafe { (*data).opaque as *mut hoedown_html_renderer_state }; let opaque = unsafe { &mut *((*opaque).opaque as *mut MyOpaque) }; - let text = with_unique_id(id, |id| { - let sec = opaque.toc_builder.as_mut().map_or("".to_owned(), |builder| { - format!("{} ", builder.push(level as u32, s.clone(), id.to_owned())) - }); + let id = derive_id(id); - // Render the HTML - format!("\ - {sec}{}", - s, lvl = level, id = id, sec = sec) + let sec = opaque.toc_builder.as_mut().map_or("".to_owned(), |builder| { + format!("{} ", builder.push(level as u32, s.clone(), id.clone())) }); + // Render the HTML + let text = format!("\ + {sec}{}", + s, lvl = level, id = id, sec = sec); + let text = CString::new(text).unwrap(); unsafe { hoedown_buffer_puts(ob, text.as_ptr()) } } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 3372c9d4807ad..aac2a52984a14 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -372,23 +372,19 @@ pub fn reset_ids() { USED_ID_MAP.with(|s| *s.borrow_mut() = init_ids()); } -pub fn with_unique_id T>(candidate: String, f: F) -> T { +pub fn derive_id(candidate: String) -> String { USED_ID_MAP.with(|map| { - let (id, ret) = match map.borrow_mut().get_mut(&candidate) { - None => { - let ret = f(&candidate); - (candidate, ret) - }, + let id = match map.borrow_mut().get_mut(&candidate) { + None => candidate, Some(a) => { let id = format!("{}-{}", candidate, *a); - let ret = f(&id); *a += 1; - (id, ret) + id } }; - map.borrow_mut().insert(id, 1); - ret + map.borrow_mut().insert(id.clone(), 1); + id }) } @@ -1745,10 +1741,9 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, ItemType::AssociatedType => ("associated-types", "Associated Types"), ItemType::AssociatedConst => ("associated-consts", "Associated Constants"), }; - try!(with_unique_id(short.to_owned(), |id| - write!(w, "

\ - {name}

\n

\ - {f}", - v = variant.name.as_ref().unwrap(), - f = field.name.as_ref().unwrap())); + let v = variant.name.as_ref().unwrap(); + let f = field.name.as_ref().unwrap(); + try!(with_unique_id(format!("variant.{}.field.{}", v, f), |id| + write!(w, "
{}", id, f))); try!(document(w, cx, field)); try!(write!(w, "
", - id = id, name = name))); + try!(write!(w, "

\ + {name}

\n
", + id = derive_id(short.to_owned()), name = name)); } match myitem.inner { @@ -1970,10 +1965,10 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, fn trait_item(w: &mut fmt::Formatter, cx: &Context, m: &clean::Item) -> fmt::Result { let name = m.name.as_ref().unwrap(); - try!(with_unique_id(format!("{}.{}", shortty(m), name), |id| - write!(w, "

", + let id = derive_id(format!("{}.{}", shortty(m), name)); + try!(write!(w, "

", id = id, - stab = m.stability_class()))); + stab = m.stability_class())); try!(render_assoc_item(w, m, AssocItemLink::Anchor)); try!(write!(w, "

")); try!(document(w, cx, m)); @@ -2162,12 +2157,11 @@ fn item_struct(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, if fields.peek().is_some() { try!(write!(w, "

Fields

\n

")); for field in fields { - let name = field.name.as_ref().unwrap(); - try!(with_unique_id(format!("structfield.{}", name), |id| - write!(w, " + ")); } @@ -2234,9 +2228,8 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, if !e.variants.is_empty() { try!(write!(w, "

Variants

\n
{}", - field.stability_class(), - id, - name))); + try!(write!(w, "
\ + {name}", + stab = field.stability_class(), + name = field.name.as_ref().unwrap())); try!(document(w, cx, field)); try!(write!(w, "
")); for variant in &e.variants { - let name = variant.name.as_ref().unwrap(); - try!(with_unique_id(format!("variant.{}", name), |id| - write!(w, "
{}", id, name))); + try!(write!(w, "
{name}", + name = variant.name.as_ref().unwrap())); try!(document(w, cx, variant)); match variant.inner { clean::VariantItem(ref var) => { @@ -2254,10 +2247,11 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, try!(write!(w, "

Fields

\n ")); for field in fields { - let v = variant.name.as_ref().unwrap(); - let f = field.name.as_ref().unwrap(); - try!(with_unique_id(format!("variant.{}.field.{}", v, f), |id| - write!(w, "")); } @@ -2474,33 +2468,33 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi clean::MethodItem(..) | clean::TyMethodItem(..) => { // Only render when the method is not static or we allow static methods if !is_static_method(item) || render_static { - try!(with_unique_id(format!("method.{}", name), |id| - write!(w, "

", id, shortty(item)))); + let id = derive_id(format!("method.{}", name)); + try!(write!(w, "

", id, shortty(item))); try!(render_assoc_item(w, item, link)); try!(write!(w, "

\n")); } } clean::TypedefItem(ref tydef, _) => { - try!(with_unique_id(format!("assoc_type.{}", name), |id| - write!(w, "

", id, shortty(item)))); + let id = derive_id(format!("assoc_type.{}", name)); + try!(write!(w, "

", id, shortty(item))); try!(write!(w, "type {} = {}", name, tydef.type_)); try!(write!(w, "

\n")); } clean::AssociatedConstItem(ref ty, ref default) => { - try!(with_unique_id(format!("assoc_const.{}", name), |id| - write!(w, "

", id, shortty(item)))); + let id = derive_id(format!("assoc_const.{}", name)); + try!(write!(w, "

", id, shortty(item))); try!(assoc_const(w, item, ty, default.as_ref())); try!(write!(w, "

\n")); } clean::ConstantItem(ref c) => { - try!(with_unique_id(format!("assoc_const.{}", name), |id| - write!(w, "

", id, shortty(item)))); + let id = derive_id(format!("assoc_const.{}", name)); + try!(write!(w, "

", id, shortty(item))); try!(assoc_const(w, item, &c.type_, Some(&c.expr))); try!(write!(w, "

\n")); } clean::AssociatedTypeItem(ref bounds, ref default) => { - try!(with_unique_id(format!("assoc_type.{}", name), |id| - write!(w, "

", id, shortty(item)))); + let id = derive_id(format!("assoc_type.{}", name)); + try!(write!(w, "

", id, shortty(item))); try!(assoc_type(w, item, bounds, default)); try!(write!(w, "

\n")); } From fb7008c0a08264f42d44f010786edf15492c9bce Mon Sep 17 00:00:00 2001 From: mitaa Date: Sat, 5 Dec 2015 23:09:20 +0100 Subject: [PATCH 5/5] Add tests --- src/librustdoc/html/markdown.rs | 27 +++++++++++++++++ src/librustdoc/html/render.rs | 19 ++++++++++++ src/test/rustdoc/issue-25001.rs | 53 +++++++++++++++++++++++++++++++++ src/test/rustdoc/issue-29449.rs | 30 +++++++++++++++++++ 4 files changed, 129 insertions(+) create mode 100644 src/test/rustdoc/issue-25001.rs create mode 100644 src/test/rustdoc/issue-29449.rs diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index a6d6802c0e10e..a5436886a7e8c 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -585,6 +585,7 @@ mod tests { fn issue_17736() { let markdown = "# title"; format!("{}", Markdown(markdown)); + reset_ids(); } #[test] @@ -609,6 +610,32 @@ mod tests { baz ❤ #qux

"); } + #[test] + fn test_header_ids_multiple_blocks() { + fn t(input: &str, expect: &str) { + let output = format!("{}", Markdown(input)); + assert_eq!(output, expect); + } + + let test = || { + t("# Example", "\n

\ + Example

"); + t("# Panics", "\n

\ + Panics

"); + t("# Example", "\n

\ + Example

"); + t("# Main", "\n

\ + Main

"); + t("# Example", "\n

\ + Example

"); + t("# Panics", "\n

\ + Panics

"); + }; + test(); + reset_ids(); + test(); + } + #[test] fn test_plain_summary_line() { fn t(input: &str, expect: &str) { diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index aac2a52984a14..58d7a36aac8a6 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -2708,3 +2708,22 @@ fn get_index_type_name(clean_type: &clean::Type) -> Option { pub fn cache() -> Arc { CACHE_KEY.with(|c| c.borrow().clone()) } + +#[cfg(test)] +#[test] +fn test_unique_id() { + let input = ["foo", "examples", "examples", "method.into_iter","examples", + "method.into_iter", "foo", "main", "search", "methods", + "examples", "method.into_iter", "assoc_type.Item", "assoc_type.Item"]; + let expected = ["foo", "examples", "examples-1", "method.into_iter", "examples-2", + "method.into_iter-1", "foo-1", "main-1", "search-1", "methods-1", + "examples-3", "method.into_iter-2", "assoc_type.Item", "assoc_type.Item-1"]; + + let test = || { + let actual: Vec = input.iter().map(|s| derive_id(s.to_string())).collect(); + assert_eq!(&actual[..], expected); + }; + test(); + reset_ids(); + test(); +} diff --git a/src/test/rustdoc/issue-25001.rs b/src/test/rustdoc/issue-25001.rs new file mode 100644 index 0000000000000..e4d97828d50a0 --- /dev/null +++ b/src/test/rustdoc/issue-25001.rs @@ -0,0 +1,53 @@ +// Copyright 2015 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. + +// @has issue_25001/struct.Foo.html +pub struct Foo(T); + +pub trait Bar { + type Item; + + fn quux(self); +} + +impl Foo { + // @has - '//*[@id="method.pass"]//code' 'fn pass()' + pub fn pass() {} +} +impl Foo { + // @has - '//*[@id="method.pass-1"]//code' 'fn pass() -> usize' + pub fn pass() -> usize { 42 } +} +impl Foo { + // @has - '//*[@id="method.pass-2"]//code' 'fn pass() -> isize' + pub fn pass() -> isize { 42 } +} + +impl Bar for Foo { + // @has - '//*[@id="assoc_type.Item"]//code' 'type Item = T' + type Item=T; + + // @has - '//*[@id="method.quux"]//code' 'fn quux(self)' + fn quux(self) {} +} +impl<'a, T> Bar for &'a Foo { + // @has - '//*[@id="assoc_type.Item-1"]//code' "type Item = &'a T" + type Item=&'a T; + + // @has - '//*[@id="method.quux-1"]//code' 'fn quux(self)' + fn quux(self) {} +} +impl<'a, T> Bar for &'a mut Foo { + // @has - '//*[@id="assoc_type.Item-2"]//code' "type Item = &'a mut T" + type Item=&'a mut T; + + // @has - '//*[@id="method.quux-2"]//code' 'fn quux(self)' + fn quux(self) {} +} diff --git a/src/test/rustdoc/issue-29449.rs b/src/test/rustdoc/issue-29449.rs new file mode 100644 index 0000000000000..f296048e30b54 --- /dev/null +++ b/src/test/rustdoc/issue-29449.rs @@ -0,0 +1,30 @@ +// Copyright 2015 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. + +// @has issue_29449/struct.Foo.html +pub struct Foo; + +impl Foo { + // @has - '//*[@id="examples"]//a' 'Examples' + // @has - '//*[@id="panics"]//a' 'Panics' + /// # Examples + /// # Panics + pub fn bar() {} + + // @has - '//*[@id="examples-1"]//a' 'Examples' + /// # Examples + pub fn bar_1() {} + + // @has - '//*[@id="examples-2"]//a' 'Examples' + // @has - '//*[@id="panics-1"]//a' 'Panics' + /// # Examples + /// # Panics + pub fn bar_2() {} +}

{}", id, f))); + try!(write!(w, "
\ + {f}", + v = variant.name.as_ref().unwrap(), + f = field.name.as_ref().unwrap())); try!(document(w, cx, field)); try!(write!(w, "