about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authormitaa <mitaa.ceb@gmail.com>2016-03-24 06:10:52 +0100
committermitaa <mitaa.ceb@gmail.com>2016-03-27 00:20:55 +0100
commit3e33ef4c42c5e4c4400a8cd470ac851a4dff0789 (patch)
treeb09877d208e79d8dbd5d0330b7bfaf9145015084 /src
parentdc1f6831eb0d0e5cca16395f14b7406ff85c4c3d (diff)
downloadrust-3e33ef4c42c5e4c4400a8cd470ac851a4dff0789.tar.gz
rust-3e33ef4c42c5e4c4400a8cd470ac851a4dff0789.zip
Correct anchor for links to associated trait items
Diffstat (limited to 'src')
-rw-r--r--src/librustdoc/clean/inline.rs17
-rw-r--r--src/librustdoc/clean/mod.rs63
-rw-r--r--src/librustdoc/html/render.rs92
-rw-r--r--src/librustdoc/passes.rs13
-rw-r--r--src/test/rustdoc/issue-28478.rs39
5 files changed, 143 insertions, 81 deletions
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<TyParamBound> 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<Generics> 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<DefId>;
+}
+
+impl<T: GetDefId> GetDefId for Option<T> {
+    fn def_id(&self) -> Option<DefId> {
+        self.as_ref().and_then(|d| d.def_id())
+    }
+}
+
 impl Type {
     pub fn primitive_type(&self) -> Option<PrimitiveType> {
         match *self {
@@ -1499,7 +1505,9 @@ impl Type {
             _ => None,
         }
     }
+}
 
+impl GetDefId for Type {
     fn def_id(&self) -> Option<DefId> {
         match *self {
             ResolvedPath { did, .. } => Some(did),
@@ -2208,6 +2216,7 @@ impl Clean<ImplPolarity> for hir::ImplPolarity {
 pub struct Impl {
     pub unsafety: hir::Unsafety,
     pub generics: Generics,
+    pub provided_trait_methods: HashSet<String>,
     pub trait_: Option<Type>,
     pub for_: Type,
     pub items: Vec<Item>,
@@ -2227,12 +2236,19 @@ impl Clean<Vec<Item>> 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<Vec<Item>> 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<DefId> {
-        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<String>),
 }
 
 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, "<h3 id='derived_implementations'>\
@@ -2409,8 +2411,8 @@ fn render_assoc_items(w: &mut fmt::Formatter,
                        </h3>")?;
             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, "</div>")?;
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<Item> {
         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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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'
+}