Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix deref impl typedef #68093

Merged
merged 9 commits into from
Jan 18, 2020
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,22 @@ fn build_type_alias(cx: &DocContext<'_>, did: DefId) -> clean::Typedef {
clean::Typedef {
type_: cx.tcx.type_of(did).clean(cx),
generics: (cx.tcx.generics_of(did), predicates).clean(cx),
item_type: build_type_alias_type(cx, did),
}
}

fn build_type_alias_type(cx: &DocContext<'_>, did: DefId) -> Option<clean::Type> {
let type_ = cx.tcx.type_of(did).clean(cx);
type_.def_id().and_then(|did| build_ty(cx, did))
}

pub fn build_ty(cx: &DocContext, did: DefId) -> Option<clean::Type> {
match cx.tcx.def_kind(did)? {
DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::Const | DefKind::Static => {
Some(cx.tcx.type_of(did).clean(cx))
}
DefKind::TyAlias => build_type_alias_type(cx, did),
_ => None,
}
}

Expand Down
47 changes: 39 additions & 8 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1122,7 +1122,9 @@ impl Clean<Item> for hir::ImplItem<'_> {
MethodItem((sig, &self.generics, body, Some(self.defaultness)).clean(cx))
}
hir::ImplItemKind::TyAlias(ref ty) => {
TypedefItem(Typedef { type_: ty.clean(cx), generics: Generics::default() }, true)
let type_ = ty.clean(cx);
let item_type = type_.def_id().and_then(|did| inline::build_ty(cx, did));
TypedefItem(Typedef { type_, generics: Generics::default(), item_type }, true)
}
hir::ImplItemKind::OpaqueTy(ref bounds) => OpaqueTyItem(
OpaqueTy { bounds: bounds.clean(cx), generics: Generics::default() },
Expand Down Expand Up @@ -1282,10 +1284,13 @@ impl Clean<Item> for ty::AssocItem {

AssocTypeItem(bounds, ty.clean(cx))
} else {
let type_ = cx.tcx.type_of(self.def_id).clean(cx);
let item_type = type_.def_id().and_then(|did| inline::build_ty(cx, did));
TypedefItem(
Typedef {
type_: cx.tcx.type_of(self.def_id).clean(cx),
type_,
generics: Generics { params: Vec::new(), where_predicates: Vec::new() },
item_type,
},
true,
)
Expand Down Expand Up @@ -1989,6 +1994,8 @@ impl Clean<String> for ast::Name {

impl Clean<Item> for doctree::Typedef<'_> {
fn clean(&self, cx: &DocContext<'_>) -> Item {
let type_ = self.ty.clean(cx);
let item_type = type_.def_id().and_then(|did| inline::build_ty(cx, did));
Item {
name: Some(self.name.clean(cx)),
attrs: self.attrs.clean(cx),
Expand All @@ -1997,10 +2004,7 @@ impl Clean<Item> for doctree::Typedef<'_> {
visibility: self.vis.clean(cx),
stability: cx.stability(self.id).clean(cx),
deprecation: cx.deprecation(self.id).clean(cx),
inner: TypedefItem(
Typedef { type_: self.ty.clean(cx), generics: self.gen.clean(cx) },
false,
),
inner: TypedefItem(Typedef { type_, generics: self.gen.clean(cx), item_type }, false),
}
}
}
Expand Down Expand Up @@ -2101,7 +2105,7 @@ impl Clean<Vec<Item>> for doctree::Impl<'_> {
build_deref_target_impls(cx, &items, &mut ret);
}

let provided = trait_
let provided: FxHashSet<String> = trait_
.def_id()
.map(|did| {
cx.tcx
Expand All @@ -2112,6 +2116,33 @@ impl Clean<Vec<Item>> for doctree::Impl<'_> {
})
.unwrap_or_default();

let for_ = self.for_.clean(cx);
let type_alias = for_.def_id().and_then(|did| match cx.tcx.def_kind(did) {
Some(DefKind::TyAlias) => Some(cx.tcx.type_of(did).clean(cx)),
_ => None,
});
if let Some(type_alias) = type_alias {
Copy link
Contributor

@oli-obk oli-obk Jan 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you try deduplicating the ret.push with the one afterwards? Maybe for for_ in type_alias.into_iter().chain(self.for_.clean(cx)) {

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do it this way, we'll have to clone trait_ and items one extra time. I'll write something else instead to avoid the duplication of code.

ret.push(Item {
name: None,
attrs: self.attrs.clean(cx),
source: self.whence.clean(cx),
def_id,
visibility: self.vis.clean(cx),
stability: cx.stability(self.id).clean(cx),
deprecation: cx.deprecation(self.id).clean(cx),
inner: ImplItem(Impl {
unsafety: self.unsafety,
generics: self.generics.clean(cx),
provided_trait_methods: provided.clone(),
trait_: trait_.clone(),
for_: type_alias,
items: items.clone(),
polarity: Some(cx.tcx.impl_polarity(def_id).clean(cx)),
synthetic: false,
blanket_impl: None,
}),
});
}
ret.push(Item {
name: None,
attrs: self.attrs.clean(cx),
Expand All @@ -2125,7 +2156,7 @@ impl Clean<Vec<Item>> for doctree::Impl<'_> {
generics: self.generics.clean(cx),
provided_trait_methods: provided,
trait_,
for_: self.for_.clean(cx),
for_,
items,
polarity: Some(cx.tcx.impl_polarity(def_id).clean(cx)),
synthetic: false,
Expand Down
8 changes: 8 additions & 0 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1406,6 +1406,14 @@ pub struct PathSegment {
pub struct Typedef {
pub type_: Type,
pub generics: Generics,
// Type of target item.
pub item_type: Option<Type>,
}

impl GetDefId for Typedef {
fn def_id(&self) -> Option<DefId> {
self.type_.def_id()
}
}

#[derive(Clone, Debug)]
Expand Down
20 changes: 13 additions & 7 deletions src/librustdoc/html/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3469,20 +3469,23 @@ fn render_deref_methods(
deref_mut: bool,
) {
let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
let target = impl_
let (target, real_target) = impl_
.inner_impl()
.items
.iter()
.filter_map(|item| match item.inner {
clean::TypedefItem(ref t, true) => Some(&t.type_),
clean::TypedefItem(ref t, true) => Some(match *t {
clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
_ => (&t.type_, &t.type_),
}),
_ => None,
})
.next()
.expect("Expected associated type binding");
let what =
AssocItemRender::DerefFor { trait_: deref_type, type_: target, deref_mut_: deref_mut };
AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
if let Some(did) = target.def_id() {
render_assoc_items(w, cx, container_item, did, what)
render_assoc_items(w, cx, container_item, did, what);
} else {
if let Some(prim) = target.primitive_type() {
if let Some(&did) = cx.cache.primitive_locations.get(&prim) {
Expand Down Expand Up @@ -4123,12 +4126,15 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
.filter(|i| i.inner_impl().trait_.is_some())
.find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did)
{
if let Some(target) = impl_
if let Some((target, real_target)) = impl_
.inner_impl()
.items
.iter()
.filter_map(|item| match item.inner {
clean::TypedefItem(ref t, true) => Some(&t.type_),
clean::TypedefItem(ref t, true) => Some(match *t {
clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
_ => (&t.type_, &t.type_),
}),
_ => None,
})
.next()
Expand All @@ -4147,7 +4153,7 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
"{:#}",
impl_.inner_impl().trait_.as_ref().unwrap().print()
)),
Escape(&format!("{:#}", target.print()))
Escape(&format!("{:#}", real_target.print()))
));
out.push_str("</a>");
let mut ret = impls
Expand Down
14 changes: 7 additions & 7 deletions src/librustdoc/html/render/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ impl DocFolder for Cache {
| clean::StructFieldItem(..)
| clean::VariantItem(..) => (
(
Some(*self.parent_stack.last().unwrap()),
Some(*self.parent_stack.last().expect("parent_stack is empty")),
Some(&self.stack[..self.stack.len() - 1]),
),
false,
Expand All @@ -286,7 +286,7 @@ impl DocFolder for Cache {
if self.parent_stack.is_empty() {
((None, None), false)
} else {
let last = self.parent_stack.last().unwrap();
let last = self.parent_stack.last().expect("parent_stack is empty 2");
let did = *last;
let path = match self.paths.get(&did) {
// The current stack not necessarily has correlation
Expand Down Expand Up @@ -468,7 +468,7 @@ impl DocFolder for Cache {
self.impls.entry(did).or_insert(vec![]).push(impl_item.clone());
}
} else {
let trait_did = impl_item.trait_did().unwrap();
let trait_did = impl_item.trait_did().expect("no trait did");
self.orphan_trait_impls.push((trait_did, dids, impl_item));
}
None
Expand All @@ -478,10 +478,10 @@ impl DocFolder for Cache {
});

if pushed {
self.stack.pop().unwrap();
self.stack.pop().expect("stack already empty");
}
if parent_pushed {
self.parent_stack.pop().unwrap();
self.parent_stack.pop().expect("parent stack already empty");
}
self.stripped_mod = orig_stripped_mod;
self.parent_is_trait_impl = orig_parent_is_trait_impl;
Expand Down Expand Up @@ -594,7 +594,7 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
for item in search_index {
item.parent_idx = item.parent.map(|nodeid| {
if nodeid_to_pathid.contains_key(&nodeid) {
*nodeid_to_pathid.get(&nodeid).unwrap()
*nodeid_to_pathid.get(&nodeid).expect("no pathid")
} else {
let pathid = lastpathid;
nodeid_to_pathid.insert(nodeid, pathid);
Expand Down Expand Up @@ -639,7 +639,7 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
items: crate_items,
paths: crate_paths,
})
.unwrap()
.expect("failed serde conversion")
)
}

Expand Down
33 changes: 33 additions & 0 deletions src/test/rustdoc/deref-typedef.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#![crate_name = "foo"]

// @has 'foo/struct.Bar.html'
// @has '-' '//*[@id="deref-methods"]' 'Methods from Deref<Target = FooC>'
// @has '-' '//*[@class="impl-items"]//*[@id="method.foo_a"]' 'pub fn foo_a(&self)'
// @has '-' '//*[@class="impl-items"]//*[@id="method.foo_b"]' 'pub fn foo_b(&self)'
// @has '-' '//*[@class="impl-items"]//*[@id="method.foo_c"]' 'pub fn foo_c(&self)'
// @has '-' '//*[@class="sidebar-title"]' 'Methods from Deref<Target=FooC>'
// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.foo_a"]' 'foo_a'
// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.foo_b"]' 'foo_b'
// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.foo_c"]' 'foo_c'

pub struct FooA;
pub type FooB = FooA;
pub type FooC = FooB;

impl FooA {
pub fn foo_a(&self) {}
}

impl FooB {
pub fn foo_b(&self) {}
}

impl FooC {
pub fn foo_c(&self) {}
}

pub struct Bar;
impl std::ops::Deref for Bar {
type Target = FooC;
fn deref(&self) -> &Self::Target { unimplemented!() }
}