-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Recursively document methods via Deref
traits
#80653
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
Changes from all commits
61c8aae
fd0ad03
06ce97c
8eaf68f
ea94607
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -115,6 +115,9 @@ crate struct Context<'tcx> { | |
crate render_redirect_pages: bool, | ||
/// The map used to ensure all generated 'id=' attributes are unique. | ||
id_map: Rc<RefCell<IdMap>>, | ||
/// Tracks section IDs for `Deref` targets so they match in both the main | ||
/// body and the sidebar. | ||
deref_id_map: Rc<RefCell<FxHashMap<DefId, String>>>, | ||
crate shared: Arc<SharedContext<'tcx>>, | ||
all: Rc<RefCell<AllTypes>>, | ||
/// Storage for the errors produced while generating documentation so they | ||
|
@@ -372,7 +375,6 @@ crate fn initial_ids() -> Vec<String> { | |
"implementors-list", | ||
"synthetic-implementors-list", | ||
"methods", | ||
"deref-methods", | ||
"implementations", | ||
] | ||
.iter() | ||
|
@@ -506,6 +508,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { | |
dst, | ||
render_redirect_pages: false, | ||
id_map: Rc::new(RefCell::new(id_map)), | ||
deref_id_map: Rc::new(RefCell::new(FxHashMap::default())), | ||
shared: Arc::new(scx), | ||
all: Rc::new(RefCell::new(AllTypes::new())), | ||
errors: Rc::new(receiver), | ||
|
@@ -3517,14 +3520,18 @@ fn render_assoc_items( | |
RenderMode::Normal | ||
} | ||
AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { | ||
let id = | ||
cx.derive_id(small_url_encode(&format!("deref-methods-{:#}", type_.print()))); | ||
cx.deref_id_map.borrow_mut().insert(type_.def_id().unwrap(), id.clone()); | ||
write!( | ||
w, | ||
"<h2 id=\"deref-methods\" class=\"small-section-header\">\ | ||
Methods from {}<Target = {}>\ | ||
<a href=\"#deref-methods\" class=\"anchor\"></a>\ | ||
"<h2 id=\"{id}\" class=\"small-section-header\">\ | ||
Methods from {trait_}<Target = {type_}>\ | ||
<a href=\"#{id}\" class=\"anchor\"></a>\ | ||
</h2>", | ||
trait_.print(), | ||
type_.print() | ||
id = id, | ||
trait_ = trait_.print(), | ||
type_ = type_.print(), | ||
); | ||
RenderMode::ForDeref { mut_: deref_mut_ } | ||
} | ||
|
@@ -3548,9 +3555,6 @@ fn render_assoc_items( | |
); | ||
} | ||
} | ||
if let AssocItemRender::DerefFor { .. } = what { | ||
return; | ||
} | ||
jyn514 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if !traits.is_empty() { | ||
let deref_impl = | ||
traits.iter().find(|t| t.inner_impl().trait_.def_id() == cache.deref_trait_did); | ||
|
@@ -3560,6 +3564,12 @@ fn render_assoc_items( | |
render_deref_methods(w, cx, impl_, containing_item, has_deref_mut, cache); | ||
} | ||
|
||
// If we were already one level into rendering deref methods, we don't want to render | ||
// anything after recursing into any further deref methods above. | ||
if let AssocItemRender::DerefFor { .. } = what { | ||
return; | ||
} | ||
Comment on lines
+3569
to
+3571
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The impls were already documented by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are two concepts at play here simultaneously which is a bit confusing, so I'll try to explain and then we can work out whatever comments or assertions might be helpful. Let's consider a two level pub struct Foo(Bar);
pub struct Bar(Baz);
pub struct Baz;
impl Deref for Foo {
type Target = Bar;
fn deref(&self) -> &Bar { &self.0 }
}
impl Deref for Bar {
type Target = Baz;
fn deref(&self) -> &Baz { &self.0 }
} The goal of The goal of Does that help at all? 😅 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that helps me understand why these are different, thanks :) I still have my original question, although maybe I didn't word it very well. This There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At the moment, this change is preserving the previous behaviour: trait, synthetic, and blanket impls are all skipped for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it, thanks - I missed that this is only skipping trait impls, not inherent impls. I'm not sure that behavior is correct, but it doesn't need to be fixed here. |
||
|
||
let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) = | ||
traits.iter().partition(|t| t.inner_impl().synthetic); | ||
let (blanket_impl, concrete): (Vec<&&Impl>, _) = | ||
|
@@ -3631,6 +3641,13 @@ fn render_deref_methods( | |
let what = | ||
AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut }; | ||
if let Some(did) = target.def_id() { | ||
if let Some(type_did) = impl_.inner_impl().for_.def_id() { | ||
// `impl Deref<Target = S> for S` | ||
if did == type_did { | ||
jryans marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Avoid infinite cycles | ||
return; | ||
} | ||
} | ||
render_assoc_items(w, cx, container_item, did, what, cache); | ||
} else { | ||
if let Some(prim) = target.primitive_type() { | ||
|
@@ -4165,14 +4182,14 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer, cache: | |
); | ||
} | ||
match *it.kind { | ||
clean::StructItem(ref s) => sidebar_struct(buffer, it, s), | ||
clean::TraitItem(ref t) => sidebar_trait(buffer, it, t), | ||
clean::PrimitiveItem(_) => sidebar_primitive(buffer, it), | ||
clean::UnionItem(ref u) => sidebar_union(buffer, it, u), | ||
clean::EnumItem(ref e) => sidebar_enum(buffer, it, e), | ||
clean::TypedefItem(_, _) => sidebar_typedef(buffer, it), | ||
clean::StructItem(ref s) => sidebar_struct(cx, buffer, it, s), | ||
clean::TraitItem(ref t) => sidebar_trait(cx, buffer, it, t), | ||
clean::PrimitiveItem(_) => sidebar_primitive(cx, buffer, it), | ||
clean::UnionItem(ref u) => sidebar_union(cx, buffer, it, u), | ||
clean::EnumItem(ref e) => sidebar_enum(cx, buffer, it, e), | ||
clean::TypedefItem(_, _) => sidebar_typedef(cx, buffer, it), | ||
clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items), | ||
clean::ForeignTypeItem => sidebar_foreign_type(buffer, it), | ||
clean::ForeignTypeItem => sidebar_foreign_type(cx, buffer, it), | ||
_ => (), | ||
} | ||
|
||
|
@@ -4273,7 +4290,7 @@ fn small_url_encode(s: &str) -> String { | |
.replace("\"", "%22") | ||
} | ||
|
||
fn sidebar_assoc_items(it: &clean::Item) -> String { | ||
fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String { | ||
let mut out = String::new(); | ||
let c = cache(); | ||
if let Some(v) = c.impls.get(&it.def_id) { | ||
|
@@ -4303,58 +4320,7 @@ 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) | ||
{ | ||
debug!("found Deref: {:?}", impl_); | ||
if let Some((target, real_target)) = | ||
impl_.inner_impl().items.iter().find_map(|item| match *item.kind { | ||
clean::TypedefItem(ref t, true) => Some(match *t { | ||
clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), | ||
_ => (&t.type_, &t.type_), | ||
}), | ||
_ => None, | ||
}) | ||
{ | ||
debug!("found target, real_target: {:?} {:?}", target, real_target); | ||
let deref_mut = v | ||
.iter() | ||
.filter(|i| i.inner_impl().trait_.is_some()) | ||
.any(|i| i.inner_impl().trait_.def_id() == c.deref_mut_trait_did); | ||
let inner_impl = target | ||
.def_id() | ||
.or_else(|| { | ||
target | ||
.primitive_type() | ||
.and_then(|prim| c.primitive_locations.get(&prim).cloned()) | ||
}) | ||
.and_then(|did| c.impls.get(&did)); | ||
if let Some(impls) = inner_impl { | ||
debug!("found inner_impl: {:?}", impls); | ||
out.push_str("<a class=\"sidebar-title\" href=\"#deref-methods\">"); | ||
out.push_str(&format!( | ||
"Methods from {}<Target={}>", | ||
Escape(&format!( | ||
"{:#}", | ||
impl_.inner_impl().trait_.as_ref().unwrap().print() | ||
)), | ||
Escape(&format!("{:#}", real_target.print())) | ||
)); | ||
out.push_str("</a>"); | ||
let mut ret = impls | ||
.iter() | ||
.filter(|i| i.inner_impl().trait_.is_none()) | ||
.flat_map(|i| { | ||
get_methods(i.inner_impl(), true, &mut used_links, deref_mut) | ||
}) | ||
.collect::<Vec<_>>(); | ||
// We want links' order to be reproducible so we don't use unstable sort. | ||
ret.sort(); | ||
if !ret.is_empty() { | ||
out.push_str(&format!( | ||
"<div class=\"sidebar-links\">{}</div>", | ||
ret.join("") | ||
)); | ||
} | ||
} | ||
} | ||
out.push_str(&sidebar_deref_methods(cx, impl_, v)); | ||
} | ||
let format_impls = |impls: Vec<&Impl>| { | ||
let mut links = FxHashSet::default(); | ||
|
@@ -4422,7 +4388,81 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { | |
out | ||
} | ||
|
||
fn sidebar_struct(buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) { | ||
fn sidebar_deref_methods(cx: &Context<'_>, impl_: &Impl, v: &Vec<Impl>) -> String { | ||
let mut out = String::new(); | ||
let c = cache(); | ||
|
||
debug!("found Deref: {:?}", impl_); | ||
if let Some((target, real_target)) = | ||
impl_.inner_impl().items.iter().find_map(|item| match *item.kind { | ||
clean::TypedefItem(ref t, true) => Some(match *t { | ||
clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), | ||
_ => (&t.type_, &t.type_), | ||
}), | ||
_ => None, | ||
}) | ||
{ | ||
debug!("found target, real_target: {:?} {:?}", target, real_target); | ||
let deref_mut = v | ||
.iter() | ||
.filter(|i| i.inner_impl().trait_.is_some()) | ||
.any(|i| i.inner_impl().trait_.def_id() == c.deref_mut_trait_did); | ||
let inner_impl = target | ||
.def_id() | ||
.or_else(|| { | ||
target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned()) | ||
}) | ||
.and_then(|did| c.impls.get(&did)); | ||
if let Some(impls) = inner_impl { | ||
debug!("found inner_impl: {:?}", impls); | ||
let mut used_links = FxHashSet::default(); | ||
let mut ret = impls | ||
.iter() | ||
.filter(|i| i.inner_impl().trait_.is_none()) | ||
.flat_map(|i| get_methods(i.inner_impl(), true, &mut used_links, deref_mut)) | ||
.collect::<Vec<_>>(); | ||
if !ret.is_empty() { | ||
let deref_id_map = cx.deref_id_map.borrow(); | ||
let id = deref_id_map | ||
.get(&real_target.def_id().unwrap()) | ||
.expect("Deref section without derived id"); | ||
out.push_str(&format!( | ||
"<a class=\"sidebar-title\" href=\"#{}\">Methods from {}<Target={}></a>", | ||
id, | ||
Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print())), | ||
Escape(&format!("{:#}", real_target.print())), | ||
)); | ||
// We want links' order to be reproducible so we don't use unstable sort. | ||
ret.sort(); | ||
out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", ret.join(""))); | ||
} | ||
} | ||
|
||
// Recurse into any further impls that might exist for `target` | ||
if let Some(target_did) = target.def_id() { | ||
if let Some(target_impls) = c.impls.get(&target_did) { | ||
if let Some(target_deref_impl) = target_impls | ||
.iter() | ||
.filter(|i| i.inner_impl().trait_.is_some()) | ||
.find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did) | ||
{ | ||
if let Some(type_did) = impl_.inner_impl().for_.def_id() { | ||
// `impl Deref<Target = S> for S` | ||
if target_did == type_did { | ||
// Avoid infinite cycles | ||
return out; | ||
} | ||
} | ||
out.push_str(&sidebar_deref_methods(cx, target_deref_impl, target_impls)); | ||
} | ||
} | ||
} | ||
} | ||
|
||
out | ||
} | ||
|
||
fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) { | ||
let mut sidebar = String::new(); | ||
let fields = get_struct_fields_name(&s.fields); | ||
|
||
|
@@ -4436,7 +4476,7 @@ fn sidebar_struct(buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) { | |
} | ||
} | ||
|
||
sidebar.push_str(&sidebar_assoc_items(it)); | ||
sidebar.push_str(&sidebar_assoc_items(cx, it)); | ||
|
||
if !sidebar.is_empty() { | ||
write!(buf, "<div class=\"block items\">{}</div>", sidebar); | ||
|
@@ -4467,7 +4507,7 @@ fn is_negative_impl(i: &clean::Impl) -> bool { | |
i.polarity == Some(clean::ImplPolarity::Negative) | ||
} | ||
|
||
fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { | ||
fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { | ||
let mut sidebar = String::new(); | ||
|
||
let mut types = t | ||
|
@@ -4567,7 +4607,7 @@ fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { | |
} | ||
} | ||
|
||
sidebar.push_str(&sidebar_assoc_items(it)); | ||
sidebar.push_str(&sidebar_assoc_items(cx, it)); | ||
|
||
sidebar.push_str("<a class=\"sidebar-title\" href=\"#implementors\">Implementors</a>"); | ||
if t.is_auto { | ||
|
@@ -4580,16 +4620,16 @@ fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { | |
write!(buf, "<div class=\"block items\">{}</div>", sidebar) | ||
} | ||
|
||
fn sidebar_primitive(buf: &mut Buffer, it: &clean::Item) { | ||
let sidebar = sidebar_assoc_items(it); | ||
fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { | ||
let sidebar = sidebar_assoc_items(cx, it); | ||
|
||
if !sidebar.is_empty() { | ||
write!(buf, "<div class=\"block items\">{}</div>", sidebar); | ||
} | ||
} | ||
|
||
fn sidebar_typedef(buf: &mut Buffer, it: &clean::Item) { | ||
let sidebar = sidebar_assoc_items(it); | ||
fn sidebar_typedef(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { | ||
let sidebar = sidebar_assoc_items(cx, it); | ||
|
||
if !sidebar.is_empty() { | ||
write!(buf, "<div class=\"block items\">{}</div>", sidebar); | ||
|
@@ -4611,7 +4651,7 @@ fn get_struct_fields_name(fields: &[clean::Item]) -> String { | |
fields.join("") | ||
} | ||
|
||
fn sidebar_union(buf: &mut Buffer, it: &clean::Item, u: &clean::Union) { | ||
fn sidebar_union(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, u: &clean::Union) { | ||
let mut sidebar = String::new(); | ||
let fields = get_struct_fields_name(&u.fields); | ||
|
||
|
@@ -4623,14 +4663,14 @@ fn sidebar_union(buf: &mut Buffer, it: &clean::Item, u: &clean::Union) { | |
)); | ||
} | ||
|
||
sidebar.push_str(&sidebar_assoc_items(it)); | ||
sidebar.push_str(&sidebar_assoc_items(cx, it)); | ||
|
||
if !sidebar.is_empty() { | ||
write!(buf, "<div class=\"block items\">{}</div>", sidebar); | ||
} | ||
} | ||
|
||
fn sidebar_enum(buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) { | ||
fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) { | ||
let mut sidebar = String::new(); | ||
|
||
let mut variants = e | ||
|
@@ -4650,7 +4690,7 @@ fn sidebar_enum(buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) { | |
)); | ||
} | ||
|
||
sidebar.push_str(&sidebar_assoc_items(it)); | ||
sidebar.push_str(&sidebar_assoc_items(cx, it)); | ||
|
||
if !sidebar.is_empty() { | ||
write!(buf, "<div class=\"block items\">{}</div>", sidebar); | ||
|
@@ -4739,8 +4779,8 @@ fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { | |
} | ||
} | ||
|
||
fn sidebar_foreign_type(buf: &mut Buffer, it: &clean::Item) { | ||
let sidebar = sidebar_assoc_items(it); | ||
fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { | ||
let sidebar = sidebar_assoc_items(cx, it); | ||
if !sidebar.is_empty() { | ||
write!(buf, "<div class=\"block items\">{}</div>", sidebar); | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.