diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 8aad4d301ee9..2de5ddf243f4 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -37,7 +37,7 @@ pub use self::ExternalLocation::*; use std::ascii::AsciiExt; use std::cell::RefCell; use std::cmp::Ordering; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashSet}; use std::default::Default; use std::error; use std::fmt::{self, Display, Formatter, Write as FmtWrite}; @@ -3206,12 +3206,37 @@ fn render_deref_methods(w: &mut fmt::Formatter, cx: &Context, impl_: &Impl, } } +fn should_render_item(item: &clean::Item, deref_mut_: bool) -> bool { + let self_type_opt = match item.inner { + clean::MethodItem(ref method) => method.decl.self_type(), + clean::TyMethodItem(ref method) => method.decl.self_type(), + _ => None + }; + + if let Some(self_ty) = self_type_opt { + let (by_mut_ref, by_box) = match self_ty { + SelfTy::SelfBorrowed(_, mutability) | + SelfTy::SelfExplicit(clean::BorrowedRef { mutability, .. }) => { + (mutability == Mutability::Mutable, false) + }, + SelfTy::SelfExplicit(clean::ResolvedPath { did, .. }) => { + (false, Some(did) == cache().owned_box_did) + }, + _ => (false, false), + }; + + (deref_mut_ || !by_mut_ref) && !by_box + } else { + false + } +} + fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLink, render_mode: RenderMode, outer_version: Option<&str>, show_def_docs: bool) -> fmt::Result { if render_mode == RenderMode::Normal { let id = derive_id(match i.inner_impl().trait_ { - Some(ref t) => format!("impl-{}", Escape(&format!("{:#}", t))), + Some(ref t) => format!("impl-{}", small_url_encode(&format!("{:#}", t))), None => "impl".to_string(), }); write!(w, "

{}", @@ -3243,30 +3268,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi let render_method_item: bool = match render_mode { RenderMode::Normal => true, - RenderMode::ForDeref { mut_: deref_mut_ } => { - let self_type_opt = match item.inner { - clean::MethodItem(ref method) => method.decl.self_type(), - clean::TyMethodItem(ref method) => method.decl.self_type(), - _ => None - }; - - if let Some(self_ty) = self_type_opt { - let (by_mut_ref, by_box) = match self_ty { - SelfTy::SelfBorrowed(_, mutability) | - SelfTy::SelfExplicit(clean::BorrowedRef { mutability, .. }) => { - (mutability == Mutability::Mutable, false) - }, - SelfTy::SelfExplicit(clean::ResolvedPath { did, .. }) => { - (false, Some(did) == cache().owned_box_did) - }, - _ => (false, false), - }; - - (deref_mut_ || !by_mut_ref) && !by_box - } else { - false - } - }, + RenderMode::ForDeref { mut_: deref_mut_ } => should_render_item(&item, deref_mut_), }; match item.inner { @@ -3513,12 +3515,16 @@ impl<'a> fmt::Display for Sidebar<'a> { } } -fn get_methods(i: &clean::Impl) -> Vec { +fn get_methods(i: &clean::Impl, for_deref: bool) -> Vec { i.items.iter().filter_map(|item| { match item.name { // Maybe check with clean::Visibility::Public as well? Some(ref name) if !name.is_empty() && item.visibility.is_some() && item.is_method() => { - Some(format!("{name}", name = name)) + if !for_deref || should_render_item(item, false) { + Some(format!("{name}", name = name)) + } else { + None + } } _ => None, } @@ -3546,7 +3552,7 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { if let Some(v) = c.impls.get(&it.def_id) { let ret = v.iter() .filter(|i| i.inner_impl().trait_.is_none()) - .flat_map(|i| get_methods(i.inner_impl())) + .flat_map(|i| get_methods(i.inner_impl(), false)) .collect::(); if !ret.is_empty() { out.push_str(&format!("Methods\ @@ -3574,17 +3580,23 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { out.push_str(""); let ret = impls.iter() .filter(|i| i.inner_impl().trait_.is_none()) - .flat_map(|i| get_methods(i.inner_impl())) + .flat_map(|i| get_methods(i.inner_impl(), true)) .collect::(); out.push_str(&format!("
{}
", ret)); } } } + let mut links = HashSet::new(); let ret = v.iter() .filter_map(|i| if let Some(ref i) = i.inner_impl().trait_ { let out = format!("{:#}", i).replace("<", "<").replace(">", ">"); let encoded = small_url_encode(&format!("{:#}", i)); - Some(format!("{}", encoded, out)) + let generated = format!("{}", encoded, out); + if !links.contains(&generated) && links.insert(generated.clone()) { + Some(generated) + } else { + None + } } else { None }) diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 3ea2e6313af4..e0153e1e6f67 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -69,15 +69,32 @@ struct FileEntry { type Cache = HashMap; +fn small_url_encode(s: &str) -> String { + s.replace("<", "%3C") + .replace(">", "%3E") + .replace(" ", "%20") + .replace("?", "%3F") + .replace("'", "%27") + .replace("&", "%26") + .replace(",", "%2C") + .replace(":", "%3A") + .replace(";", "%3B") + .replace("[", "%5B") + .replace("]", "%5D") +} + impl FileEntry { fn parse_ids(&mut self, file: &Path, contents: &str, errors: &mut bool) { if self.ids.is_empty() { with_attrs_in_source(contents, " id", |fragment, i, _| { let frag = fragment.trim_left_matches("#").to_owned(); + let encoded = small_url_encode(&frag); if !self.ids.insert(frag) { *errors = true; println!("{}:{}: id is not unique: `{}`", file.display(), i, fragment); } + // Just in case, we also add the encoded id. + self.ids.insert(encoded); }); } }