From 3e33ef4c42c5e4c4400a8cd470ac851a4dff0789 Mon Sep 17 00:00:00 2001 From: mitaa Date: Thu, 24 Mar 2016 06:10:52 +0100 Subject: [PATCH] Correct anchor for links to associated trait items --- src/librustdoc/clean/inline.rs | 17 ++++-- src/librustdoc/clean/mod.rs | 63 +++++++++++++--------- src/librustdoc/html/render.rs | 92 +++++++++++++++++---------------- src/librustdoc/passes.rs | 13 ++--- src/test/rustdoc/issue-28478.rs | 39 ++++++++++++++ 5 files changed, 143 insertions(+), 81 deletions(-) create mode 100644 src/test/rustdoc/issue-28478.rs diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index e9c55c56a9d..265b43ca16b 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -26,7 +26,7 @@ use rustc::middle::const_eval; use core::DocContext; use doctree; -use clean::{self, Attributes}; +use clean::{self, Attributes, GetDefId}; use super::{Clean, ToSource}; @@ -414,15 +414,22 @@ pub fn build_impl(cx: &DocContext, clean::RegionBound(..) => unreachable!(), } }); - if let Some(clean::ResolvedPath { did, .. }) = trait_ { - if Some(did) == cx.deref_trait_did.get() { - super::build_deref_target_impls(cx, &trait_items, ret); - } + if trait_.def_id() == cx.deref_trait_did.get() { + super::build_deref_target_impls(cx, &trait_items, ret); } + + let provided = trait_.def_id().map(|did| { + cx.tcx().provided_trait_methods(did) + .into_iter() + .map(|meth| meth.name.to_string()) + .collect() + }).unwrap_or(HashSet::new()); + ret.push(clean::Item { inner: clean::ImplItem(clean::Impl { unsafety: hir::Unsafety::Normal, // FIXME: this should be decoded derived: clean::detect_derived(&attrs), + provided_trait_methods: provided, trait_: trait_, for_: ty.ty.clean(cx), generics: (&ty.generics, &predicates, subst::TypeSpace).clean(cx), diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index cca027ca17a..bec3ae799ca 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -44,7 +44,7 @@ use rustc::middle::stability; use rustc_front::hir; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::path::PathBuf; use std::rc::Rc; use std::u32; @@ -559,15 +559,9 @@ impl TyParamBound { fn is_sized_bound(&self, cx: &DocContext) -> bool { use rustc_front::hir::TraitBoundModifier as TBM; if let Some(tcx) = cx.tcx_opt() { - let sized_did = match tcx.lang_items.sized_trait() { - Some(did) => did, - None => return false - }; - if let TyParamBound::TraitBound(PolyTrait { - trait_: Type::ResolvedPath { did, .. }, .. - }, TBM::None) = *self { - if did == sized_did { - return true + if let TyParamBound::TraitBound(PolyTrait { ref trait_, .. }, TBM::None) = *self { + if trait_.def_id() == tcx.lang_items.sized_trait() { + return true; } } } @@ -724,15 +718,18 @@ impl<'tcx> Clean for ty::TraitRef<'tcx> { } } - TraitBound(PolyTrait { - trait_: ResolvedPath { - path: path, - typarams: None, - did: self.def_id, - is_generic: false, + TraitBound( + PolyTrait { + trait_: ResolvedPath { + path: path, + typarams: None, + did: self.def_id, + is_generic: false, + }, + lifetimes: late_bounds, }, - lifetimes: late_bounds - }, hir::TraitBoundModifier::None) + hir::TraitBoundModifier::None + ) } } @@ -932,7 +929,6 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics<'tcx>, &'a ty::GenericPredicates<'tcx>, subst::ParamSpace) { fn clean(&self, cx: &DocContext) -> Generics { - use std::collections::HashSet; use self::WherePredicate as WP; let (gens, preds, space) = *self; @@ -1486,6 +1482,16 @@ pub enum TypeKind { TypeTypedef, } +pub trait GetDefId { + fn def_id(&self) -> Option; +} + +impl GetDefId for Option { + fn def_id(&self) -> Option { + self.as_ref().and_then(|d| d.def_id()) + } +} + impl Type { pub fn primitive_type(&self) -> Option { match *self { @@ -1499,7 +1505,9 @@ impl Type { _ => None, } } +} +impl GetDefId for Type { fn def_id(&self) -> Option { match *self { ResolvedPath { did, .. } => Some(did), @@ -2208,6 +2216,7 @@ impl Clean for hir::ImplPolarity { pub struct Impl { pub unsafety: hir::Unsafety, pub generics: Generics, + pub provided_trait_methods: HashSet, pub trait_: Option, pub for_: Type, pub items: Vec, @@ -2227,12 +2236,19 @@ impl Clean> for doctree::Impl { // If this impl block is an implementation of the Deref trait, then we // need to try inlining the target's inherent impl blocks as well. - if let Some(ResolvedPath { did, .. }) = trait_ { - if Some(did) == cx.deref_trait_did.get() { - build_deref_target_impls(cx, &items, &mut ret); - } + if trait_.def_id() == cx.deref_trait_did.get() { + build_deref_target_impls(cx, &items, &mut ret); } + let provided = trait_.def_id().and_then(|did| { + cx.tcx_opt().map(|tcx| { + tcx.provided_trait_methods(did) + .into_iter() + .map(|meth| meth.name.to_string()) + .collect() + }) + }).unwrap_or(HashSet::new()); + ret.push(Item { name: None, attrs: self.attrs.clean(cx), @@ -2244,6 +2260,7 @@ impl Clean> for doctree::Impl { inner: ImplItem(Impl { unsafety: self.unsafety, generics: self.generics.clean(cx), + provided_trait_methods: provided, trait_: trait_, for_: self.for_.clean(cx), items: items, diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 071e3dd6bd0..51e069c6668 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -62,7 +62,7 @@ use rustc::middle::stability; use rustc::session::config::get_unstable_features_setting; use rustc_front::hir; -use clean::{self, SelfTy, Attributes}; +use clean::{self, SelfTy, Attributes, GetDefId}; use doctree; use fold::DocFolder; use html::escape::Escape; @@ -144,9 +144,7 @@ pub struct Impl { impl Impl { fn trait_did(&self) -> Option { - self.impl_.trait_.as_ref().and_then(|tr| { - if let clean::ResolvedPath { did, .. } = *tr {Some(did)} else {None} - }) + self.impl_.trait_.def_id() } } @@ -967,7 +965,7 @@ impl DocFolder for Cache { // Collect all the implementors of traits. if let clean::ImplItem(ref i) = item.inner { - if let Some(clean::ResolvedPath{ did, .. }) = i.trait_ { + if let Some(did) = i.trait_.def_id() { self.implementors.entry(did).or_insert(vec![]).push(Implementor { def_id: item.def_id, stability: item.stability.clone(), @@ -2066,10 +2064,11 @@ fn render_stability_since(w: &mut fmt::Formatter, render_stability_since_raw(w, item.stable_since(), containing_item.stable_since()) } -fn render_assoc_item(w: &mut fmt::Formatter, meth: &clean::Item, +fn render_assoc_item(w: &mut fmt::Formatter, + item: &clean::Item, link: AssocItemLink) -> fmt::Result { fn method(w: &mut fmt::Formatter, - it: &clean::Item, + meth: &clean::Item, unsafety: hir::Unsafety, constness: hir::Constness, abi: abi::Abi, @@ -2080,12 +2079,20 @@ fn render_assoc_item(w: &mut fmt::Formatter, meth: &clean::Item, -> fmt::Result { use syntax::abi::Abi; - let name = it.name.as_ref().unwrap(); - let anchor = format!("#{}.{}", shortty(it), name); + let name = meth.name.as_ref().unwrap(); + let anchor = format!("#{}.{}", shortty(meth), name); let href = match link { AssocItemLink::Anchor => anchor, - AssocItemLink::GotoSource(did) => { - href(did).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor) + AssocItemLink::GotoSource(did, provided_methods) => { + // We're creating a link from an impl-item to the corresponding + // trait-item and need to map the anchored type accordingly. + let ty = if provided_methods.contains(name) { + ItemType::Method + } else { + ItemType::TyMethod + }; + + href(did).map(|p| format!("{}#{}.{}", p.0, ty, name)).unwrap_or(anchor) } }; let vis_constness = match get_unstable_features_setting() { @@ -2106,21 +2113,21 @@ fn render_assoc_item(w: &mut fmt::Formatter, meth: &clean::Item, decl = Method(selfty, d), where_clause = WhereClause(g)) } - match meth.inner { + match item.inner { clean::TyMethodItem(ref m) => { - method(w, meth, m.unsafety, hir::Constness::NotConst, + method(w, item, m.unsafety, hir::Constness::NotConst, m.abi, &m.generics, &m.self_, &m.decl, link) } clean::MethodItem(ref m) => { - method(w, meth, m.unsafety, m.constness, + method(w, item, m.unsafety, m.constness, m.abi, &m.generics, &m.self_, &m.decl, link) } clean::AssociatedConstItem(ref ty, ref default) => { - assoc_const(w, meth, ty, default.as_ref()) + assoc_const(w, item, ty, default.as_ref()) } clean::AssociatedTypeItem(ref bounds, ref default) => { - assoc_type(w, meth, bounds, default) + assoc_type(w, item, bounds, default) } _ => panic!("render_assoc_item called on non-associated-item") } @@ -2338,9 +2345,9 @@ fn render_struct(w: &mut fmt::Formatter, it: &clean::Item, } #[derive(Copy, Clone)] -enum AssocItemLink { +enum AssocItemLink<'a> { Anchor, - GotoSource(DefId), + GotoSource(DefId, &'a HashSet), } enum AssocItemRender<'a> { @@ -2383,12 +2390,7 @@ fn render_assoc_items(w: &mut fmt::Formatter, } if !traits.is_empty() { let deref_impl = traits.iter().find(|t| { - match *t.impl_.trait_.as_ref().unwrap() { - clean::ResolvedPath { did, .. } => { - Some(did) == c.deref_trait_did - } - _ => false - } + t.impl_.trait_.def_id() == c.deref_trait_did }); if let Some(impl_) = deref_impl { render_deref_methods(w, cx, impl_, containing_item)?; @@ -2400,8 +2402,8 @@ fn render_assoc_items(w: &mut fmt::Formatter, }); for i in &manual { let did = i.trait_did().unwrap(); - render_impl(w, cx, i, AssocItemLink::GotoSource(did), true, - containing_item.stable_since())?; + let assoc_link = AssocItemLink::GotoSource(did, &i.impl_.provided_trait_methods); + render_impl(w, cx, i, assoc_link, true, containing_item.stable_since())?; } if !derived.is_empty() { write!(w, "

\ @@ -2409,8 +2411,8 @@ fn render_assoc_items(w: &mut fmt::Formatter,

")?; for i in &derived { let did = i.trait_did().unwrap(); - render_impl(w, cx, i, AssocItemLink::GotoSource(did), true, - containing_item.stable_since())?; + let assoc_link = AssocItemLink::GotoSource(did, &i.impl_.provided_trait_methods); + render_impl(w, cx, i, assoc_link, true, containing_item.stable_since())?; } } } @@ -2427,17 +2429,16 @@ fn render_deref_methods(w: &mut fmt::Formatter, cx: &Context, impl_: &Impl, } }).next().expect("Expected associated type binding"); let what = AssocItemRender::DerefFor { trait_: deref_type, type_: target }; - match *target { - clean::ResolvedPath { did, .. } => render_assoc_items(w, cx, container_item, did, what), - _ => { - if let Some(prim) = target.primitive_type() { - if let Some(c) = cache().primitive_locations.get(&prim) { - let did = DefId { krate: *c, index: prim.to_def_index() }; - render_assoc_items(w, cx, container_item, did, what)?; - } + if let Some(did) = target.def_id() { + render_assoc_items(w, cx, container_item, did, what) + } else { + if let Some(prim) = target.primitive_type() { + if let Some(c) = cache().primitive_locations.get(&prim) { + let did = DefId { krate: *c, index: prim.to_def_index() }; + render_assoc_items(w, cx, container_item, did, what)?; } - Ok(()) } + Ok(()) } } @@ -2521,18 +2522,19 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi fn render_default_items(w: &mut fmt::Formatter, cx: &Context, - did: DefId, t: &clean::Trait, - i: &clean::Impl, - render_static: bool, - outer_version: Option<&str>) -> fmt::Result { + i: &clean::Impl, + render_static: bool, + outer_version: Option<&str>) -> fmt::Result { for trait_item in &t.items { let n = trait_item.name.clone(); - if i.items.iter().find(|m| { m.name == n }).is_some() { + if i.items.iter().find(|m| m.name == n).is_some() { continue; } + let did = i.trait_.as_ref().unwrap().def_id().unwrap(); + let assoc_link = AssocItemLink::GotoSource(did, &i.provided_trait_methods); - doctraititem(w, cx, trait_item, AssocItemLink::GotoSource(did), render_static, + doctraititem(w, cx, trait_item, assoc_link, render_static, outer_version)?; } Ok(()) @@ -2542,9 +2544,9 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi // default methods which weren't overridden in the implementation block. // FIXME: this also needs to be done for associated types, whenever defaults // for them work. - if let Some(clean::ResolvedPath { did, .. }) = i.impl_.trait_ { + if let Some(did) = i.trait_did() { if let Some(t) = cache().traits.get(&did) { - render_default_items(w, cx, did, t, &i.impl_, render_header, outer_version)?; + render_default_items(w, cx, t, &i.impl_, render_header, outer_version)?; } } write!(w, "")?; diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs index 154b812cdff..88cb20991d6 100644 --- a/src/librustdoc/passes.rs +++ b/src/librustdoc/passes.rs @@ -16,7 +16,7 @@ use std::string::String; use std::usize; use rustc_front::hir; -use clean::{self, Attributes}; +use clean::{self, Attributes, GetDefId}; use clean::Item; use plugins; use fold; @@ -74,7 +74,7 @@ pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult { return None; } // Impls of stripped traits also don't need to exist - if let Some(clean::ResolvedPath { did, .. }) = *trait_ { + if let Some(did) = trait_.def_id() { if self.stripped.contains(&did) { return None; } @@ -223,13 +223,10 @@ struct ImplStripper<'a>(&'a DefIdSet); impl<'a> fold::DocFolder for ImplStripper<'a> { fn fold_item(&mut self, i: Item) -> Option { if let clean::ImplItem(ref imp) = i.inner { - match imp.trait_ { - Some(clean::ResolvedPath{ did, .. }) => { - if did.is_local() && !self.0.contains(&did) { - return None; - } + if let Some(did) = imp.trait_.def_id() { + if did.is_local() && !self.0.contains(&did) { + return None; } - Some(..) | None => {} } } self.fold_item_recur(i) diff --git a/src/test/rustdoc/issue-28478.rs b/src/test/rustdoc/issue-28478.rs new file mode 100644 index 00000000000..9d3433fb399 --- /dev/null +++ b/src/test/rustdoc/issue-28478.rs @@ -0,0 +1,39 @@ +// 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. + +#![feature(associated_type_defaults)] +#![feature(associated_consts)] + +// @has issue_28478/trait.Bar.html +pub trait Bar { + // @has - '//*[@id="associatedtype.Bar"]' 'type Bar = ()' + type Bar = (); + + // @has - '//*[@id="associatedconstant.Baz"]' 'const Baz: usize = 7' + const Baz: usize = 7; + // @has - '//*[@id="tymethod.bar"]' 'fn bar' + fn bar(); + // @has - '//*[@id="method.baz"]' 'fn baz' + fn baz() { } +} + +// @has issue_28478/struct.Foo.html +pub struct Foo; + +impl Foo { + // @has - '//*[@href="#method.foo"]' 'foo' + pub fn foo() {} +} + +impl Bar for Foo { + // @has - '//*[@href="../issue_28478/trait.Bar.html#tymethod.bar"]' 'bar' + fn bar() {} + // @has - '//*[@href="../issue_28478/trait.Bar.html#method.baz"]' 'baz' +}