make GATs print properly in traits
This commit is contained in:
parent
8a0c2c4e83
commit
ca6e06efba
9 changed files with 99 additions and 49 deletions
|
@ -985,9 +985,10 @@ impl Clean<Item> for hir::TraitItem<'_> {
|
||||||
TyMethodItem(t)
|
TyMethodItem(t)
|
||||||
}
|
}
|
||||||
hir::TraitItemKind::Type(bounds, ref default) => {
|
hir::TraitItemKind::Type(bounds, ref default) => {
|
||||||
|
let generics = self.generics.clean(cx);
|
||||||
let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect();
|
let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect();
|
||||||
let default = default.map(|t| t.clean(cx));
|
let default = default.map(|t| t.clean(cx));
|
||||||
AssocTypeItem(bounds, default)
|
AssocTypeItem(Box::new(generics), bounds, default)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let what_rustc_thinks =
|
let what_rustc_thinks =
|
||||||
|
@ -1138,32 +1139,55 @@ impl Clean<Item> for ty::AssocItem {
|
||||||
if let ty::TraitContainer(_) = self.container {
|
if let ty::TraitContainer(_) = self.container {
|
||||||
let bounds = tcx.explicit_item_bounds(self.def_id);
|
let bounds = tcx.explicit_item_bounds(self.def_id);
|
||||||
let predicates = ty::GenericPredicates { parent: None, predicates: bounds };
|
let predicates = ty::GenericPredicates { parent: None, predicates: bounds };
|
||||||
let generics = clean_ty_generics(cx, tcx.generics_of(self.def_id), predicates);
|
let mut generics =
|
||||||
|
clean_ty_generics(cx, tcx.generics_of(self.def_id), predicates);
|
||||||
|
// Filter out the bounds that are (likely?) directly attached to the associated type,
|
||||||
|
// as opposed to being located in the where clause.
|
||||||
let mut bounds = generics
|
let mut bounds = generics
|
||||||
.where_predicates
|
.where_predicates
|
||||||
.iter()
|
.drain_filter(|pred| match *pred {
|
||||||
.filter_map(|pred| {
|
WherePredicate::BoundPredicate {
|
||||||
let (name, self_type, trait_, bounds) = match *pred {
|
ty: QPath { name, ref self_type, ref trait_, .. },
|
||||||
WherePredicate::BoundPredicate {
|
..
|
||||||
ty: QPath { ref name, ref self_type, ref trait_, .. },
|
} => {
|
||||||
ref bounds,
|
if name != my_name {
|
||||||
..
|
return false;
|
||||||
} => (name, self_type, trait_, bounds),
|
}
|
||||||
_ => return None,
|
if trait_.def_id() != self.container.id() {
|
||||||
};
|
return false;
|
||||||
if *name != my_name {
|
}
|
||||||
return None;
|
match **self_type {
|
||||||
|
Generic(ref s) if *s == kw::SelfUpper => {}
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
match &assoc.args {
|
||||||
|
GenericArgs::AngleBracketed { args, bindings } => {
|
||||||
|
if !bindings.is_empty()
|
||||||
|
|| generics
|
||||||
|
.params
|
||||||
|
.iter()
|
||||||
|
.zip(args)
|
||||||
|
.any(|(param, arg)| !param_eq_arg(param, arg))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GenericArgs::Parenthesized { .. } => {
|
||||||
|
// The only time this happens is if we're inside the rustdoc for Fn(),
|
||||||
|
// which only has one associated type, which is not a GAT, so whatever.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.flat_map(|pred| {
|
||||||
|
if let WherePredicate::BoundPredicate { bounds, .. } = pred {
|
||||||
|
bounds
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
}
|
}
|
||||||
if trait_.def_id() != self.container.id() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
match **self_type {
|
|
||||||
Generic(ref s) if *s == kw::SelfUpper => {}
|
|
||||||
_ => return None,
|
|
||||||
}
|
|
||||||
Some(bounds)
|
|
||||||
})
|
})
|
||||||
.flat_map(|i| i.iter().cloned())
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
// Our Sized/?Sized bound didn't get handled when creating the generics
|
// Our Sized/?Sized bound didn't get handled when creating the generics
|
||||||
// because we didn't actually get our whole set of bounds until just now
|
// because we didn't actually get our whole set of bounds until just now
|
||||||
|
@ -1183,7 +1207,7 @@ impl Clean<Item> for ty::AssocItem {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
AssocTypeItem(bounds, ty.map(|t| t.clean(cx)))
|
AssocTypeItem(Box::new(generics), bounds, ty.map(|t| t.clean(cx)))
|
||||||
} else {
|
} else {
|
||||||
// FIXME: when could this happen? Associated items in inherent impls?
|
// FIXME: when could this happen? Associated items in inherent impls?
|
||||||
let type_ = tcx.type_of(self.def_id).clean(cx);
|
let type_ = tcx.type_of(self.def_id).clean(cx);
|
||||||
|
|
|
@ -683,7 +683,7 @@ crate enum ItemKind {
|
||||||
///
|
///
|
||||||
/// The bounds may be non-empty if there is a `where` clause.
|
/// The bounds may be non-empty if there is a `where` clause.
|
||||||
/// The `Option<Type>` is the default concrete type (e.g. `trait Trait { type Target = usize; }`)
|
/// The `Option<Type>` is the default concrete type (e.g. `trait Trait { type Target = usize; }`)
|
||||||
AssocTypeItem(Vec<GenericBound>, Option<Type>),
|
AssocTypeItem(Box<Generics>, Vec<GenericBound>, Option<Type>),
|
||||||
/// An item that has been stripped by a rustdoc pass
|
/// An item that has been stripped by a rustdoc pass
|
||||||
StrippedItem(Box<ItemKind>),
|
StrippedItem(Box<ItemKind>),
|
||||||
KeywordItem(Symbol),
|
KeywordItem(Symbol),
|
||||||
|
@ -721,7 +721,7 @@ impl ItemKind {
|
||||||
| ProcMacroItem(_)
|
| ProcMacroItem(_)
|
||||||
| PrimitiveItem(_)
|
| PrimitiveItem(_)
|
||||||
| AssocConstItem(_, _)
|
| AssocConstItem(_, _)
|
||||||
| AssocTypeItem(_, _)
|
| AssocTypeItem(..)
|
||||||
| StrippedItem(_)
|
| StrippedItem(_)
|
||||||
| KeywordItem(_) => [].iter(),
|
| KeywordItem(_) => [].iter(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ crate trait DocFolder: Sized {
|
||||||
| ProcMacroItem(_)
|
| ProcMacroItem(_)
|
||||||
| PrimitiveItem(_)
|
| PrimitiveItem(_)
|
||||||
| AssocConstItem(_, _)
|
| AssocConstItem(_, _)
|
||||||
| AssocTypeItem(_, _)
|
| AssocTypeItem(..)
|
||||||
| KeywordItem(_) => kind,
|
| KeywordItem(_) => kind,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -773,22 +773,25 @@ fn assoc_const(
|
||||||
fn assoc_type(
|
fn assoc_type(
|
||||||
w: &mut Buffer,
|
w: &mut Buffer,
|
||||||
it: &clean::Item,
|
it: &clean::Item,
|
||||||
|
generics: &clean::Generics,
|
||||||
bounds: &[clean::GenericBound],
|
bounds: &[clean::GenericBound],
|
||||||
default: Option<&clean::Type>,
|
default: Option<&clean::Type>,
|
||||||
link: AssocItemLink<'_>,
|
link: AssocItemLink<'_>,
|
||||||
extra: &str,
|
indent: usize,
|
||||||
cx: &Context<'_>,
|
cx: &Context<'_>,
|
||||||
) {
|
) {
|
||||||
write!(
|
write!(
|
||||||
w,
|
w,
|
||||||
"{}type <a href=\"{}\" class=\"associatedtype\">{}</a>",
|
"{indent}type <a href=\"{href}\" class=\"associatedtype\">{name}</a>{generics}",
|
||||||
extra,
|
indent = " ".repeat(indent),
|
||||||
naive_assoc_href(it, link, cx),
|
href = naive_assoc_href(it, link, cx),
|
||||||
it.name.as_ref().unwrap()
|
name = it.name.as_ref().unwrap(),
|
||||||
|
generics = generics.print(cx),
|
||||||
);
|
);
|
||||||
if !bounds.is_empty() {
|
if !bounds.is_empty() {
|
||||||
write!(w, ": {}", print_generic_bounds(bounds, cx))
|
write!(w, ": {}", print_generic_bounds(bounds, cx))
|
||||||
}
|
}
|
||||||
|
write!(w, "{}", print_where_clause(generics, cx, indent, false));
|
||||||
if let Some(default) = default {
|
if let Some(default) = default {
|
||||||
write!(w, " = {}", default.print(cx))
|
write!(w, " = {}", default.print(cx))
|
||||||
}
|
}
|
||||||
|
@ -812,11 +815,8 @@ fn assoc_method(
|
||||||
AssocItemLink::GotoSource(did, provided_methods) => {
|
AssocItemLink::GotoSource(did, provided_methods) => {
|
||||||
// We're creating a link from an impl-item to the corresponding
|
// We're creating a link from an impl-item to the corresponding
|
||||||
// trait-item and need to map the anchored type accordingly.
|
// trait-item and need to map the anchored type accordingly.
|
||||||
let ty = if provided_methods.contains(name) {
|
let ty =
|
||||||
ItemType::Method
|
if provided_methods.contains(name) { ItemType::Method } else { ItemType::TyMethod };
|
||||||
} else {
|
|
||||||
ItemType::TyMethod
|
|
||||||
};
|
|
||||||
|
|
||||||
match (href(did.expect_def_id(), cx), ty) {
|
match (href(did.expect_def_id(), cx), ty) {
|
||||||
(Ok(p), ty) => Some(format!("{}#{}.{}", p.0, ty, name)),
|
(Ok(p), ty) => Some(format!("{}#{}.{}", p.0, ty, name)),
|
||||||
|
@ -974,13 +974,14 @@ fn render_assoc_item(
|
||||||
clean::AssocConstItem(ref ty, _) => {
|
clean::AssocConstItem(ref ty, _) => {
|
||||||
assoc_const(w, item, ty, link, if parent == ItemType::Trait { " " } else { "" }, cx)
|
assoc_const(w, item, ty, link, if parent == ItemType::Trait { " " } else { "" }, cx)
|
||||||
}
|
}
|
||||||
clean::AssocTypeItem(ref bounds, ref default) => assoc_type(
|
clean::AssocTypeItem(ref generics, ref bounds, ref default) => assoc_type(
|
||||||
w,
|
w,
|
||||||
item,
|
item,
|
||||||
|
generics,
|
||||||
bounds,
|
bounds,
|
||||||
default.as_ref(),
|
default.as_ref(),
|
||||||
link,
|
link,
|
||||||
if parent == ItemType::Trait { " " } else { "" },
|
if parent == ItemType::Trait { 4 } else { 0 },
|
||||||
cx,
|
cx,
|
||||||
),
|
),
|
||||||
_ => panic!("render_assoc_item called on non-associated-item"),
|
_ => panic!("render_assoc_item called on non-associated-item"),
|
||||||
|
@ -1284,7 +1285,16 @@ fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String {
|
||||||
let empty_set = FxHashSet::default();
|
let empty_set = FxHashSet::default();
|
||||||
let src_link =
|
let src_link =
|
||||||
AssocItemLink::GotoSource(trait_did.into(), &empty_set);
|
AssocItemLink::GotoSource(trait_did.into(), &empty_set);
|
||||||
assoc_type(&mut out, it, &[], Some(&tydef.type_), src_link, "", cx);
|
assoc_type(
|
||||||
|
&mut out,
|
||||||
|
it,
|
||||||
|
&tydef.generics,
|
||||||
|
&[],
|
||||||
|
Some(&tydef.type_),
|
||||||
|
src_link,
|
||||||
|
0,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
out.push_str(";</span>");
|
out.push_str(";</span>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1463,10 +1473,11 @@ fn render_impl(
|
||||||
assoc_type(
|
assoc_type(
|
||||||
w,
|
w,
|
||||||
item,
|
item,
|
||||||
&Vec::new(),
|
&tydef.generics,
|
||||||
|
&[],
|
||||||
Some(&tydef.type_),
|
Some(&tydef.type_),
|
||||||
link.anchor(if trait_.is_some() { &source_id } else { &id }),
|
link.anchor(if trait_.is_some() { &source_id } else { &id }),
|
||||||
"",
|
0,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
w.write_str("</h4>");
|
w.write_str("</h4>");
|
||||||
|
@ -1494,7 +1505,7 @@ fn render_impl(
|
||||||
w.write_str("</h4>");
|
w.write_str("</h4>");
|
||||||
w.write_str("</section>");
|
w.write_str("</section>");
|
||||||
}
|
}
|
||||||
clean::AssocTypeItem(ref bounds, ref default) => {
|
clean::AssocTypeItem(ref generics, ref bounds, ref default) => {
|
||||||
let source_id = format!("{}.{}", item_type, name);
|
let source_id = format!("{}.{}", item_type, name);
|
||||||
let id = cx.derive_id(source_id.clone());
|
let id = cx.derive_id(source_id.clone());
|
||||||
write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class,);
|
write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class,);
|
||||||
|
@ -1503,10 +1514,11 @@ fn render_impl(
|
||||||
assoc_type(
|
assoc_type(
|
||||||
w,
|
w,
|
||||||
item,
|
item,
|
||||||
|
generics,
|
||||||
bounds,
|
bounds,
|
||||||
default.as_ref(),
|
default.as_ref(),
|
||||||
link.anchor(if trait_.is_some() { &source_id } else { &id }),
|
link.anchor(if trait_.is_some() { &source_id } else { &id }),
|
||||||
"",
|
0,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
w.write_str("</h4>");
|
w.write_str("</h4>");
|
||||||
|
@ -1727,7 +1739,16 @@ pub(crate) fn render_impl_summary(
|
||||||
for it in &i.inner_impl().items {
|
for it in &i.inner_impl().items {
|
||||||
if let clean::TypedefItem(ref tydef, _) = *it.kind {
|
if let clean::TypedefItem(ref tydef, _) = *it.kind {
|
||||||
w.write_str("<span class=\"where fmt-newline\"> ");
|
w.write_str("<span class=\"where fmt-newline\"> ");
|
||||||
assoc_type(w, it, &[], Some(&tydef.type_), AssocItemLink::Anchor(None), "", cx);
|
assoc_type(
|
||||||
|
w,
|
||||||
|
it,
|
||||||
|
&tydef.generics,
|
||||||
|
&[],
|
||||||
|
Some(&tydef.type_),
|
||||||
|
AssocItemLink::Anchor(None),
|
||||||
|
0,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
w.write_str(";</span>");
|
w.write_str(";</span>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -222,8 +222,9 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
|
||||||
AssocConstItem(ty, default) => {
|
AssocConstItem(ty, default) => {
|
||||||
ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: default.map(|c| c.expr(tcx)) }
|
ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: default.map(|c| c.expr(tcx)) }
|
||||||
}
|
}
|
||||||
AssocTypeItem(g, t) => ItemEnum::AssocType {
|
AssocTypeItem(g, b, t) => ItemEnum::AssocType {
|
||||||
bounds: g.into_iter().map(|x| x.into_tcx(tcx)).collect(),
|
generics: (*g).into_tcx(tcx),
|
||||||
|
bounds: b.into_iter().map(|x| x.into_tcx(tcx)).collect(),
|
||||||
default: t.map(|x| x.into_tcx(tcx)),
|
default: t.map(|x| x.into_tcx(tcx)),
|
||||||
},
|
},
|
||||||
// `convert_item` early returns `None` for striped items
|
// `convert_item` early returns `None` for striped items
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#![feature(box_patterns)]
|
#![feature(box_patterns)]
|
||||||
#![feature(control_flow_enum)]
|
#![feature(control_flow_enum)]
|
||||||
#![feature(box_syntax)]
|
#![feature(box_syntax)]
|
||||||
|
#![feature(drain_filter)]
|
||||||
#![feature(let_else)]
|
#![feature(let_else)]
|
||||||
#![feature(nll)]
|
#![feature(nll)]
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
|
|
|
@ -62,7 +62,7 @@ crate fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> boo
|
||||||
clean::StructFieldItem(_)
|
clean::StructFieldItem(_)
|
||||||
| clean::VariantItem(_)
|
| clean::VariantItem(_)
|
||||||
| clean::AssocConstItem(_, _)
|
| clean::AssocConstItem(_, _)
|
||||||
| clean::AssocTypeItem(_, _)
|
| clean::AssocTypeItem(..)
|
||||||
| clean::TypedefItem(_, _)
|
| clean::TypedefItem(_, _)
|
||||||
| clean::StaticItem(_)
|
| clean::StaticItem(_)
|
||||||
| clean::ConstantItem(_)
|
| clean::ConstantItem(_)
|
||||||
|
|
|
@ -41,7 +41,7 @@ crate trait DocVisitor: Sized {
|
||||||
| ProcMacroItem(_)
|
| ProcMacroItem(_)
|
||||||
| PrimitiveItem(_)
|
| PrimitiveItem(_)
|
||||||
| AssocConstItem(_, _)
|
| AssocConstItem(_, _)
|
||||||
| AssocTypeItem(_, _)
|
| AssocTypeItem(..)
|
||||||
| KeywordItem(_) => {}
|
| KeywordItem(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,6 +233,9 @@ pub enum ItemEnum {
|
||||||
default: Option<String>,
|
default: Option<String>,
|
||||||
},
|
},
|
||||||
AssocType {
|
AssocType {
|
||||||
|
/// generics and `where` clause
|
||||||
|
generics: Generics,
|
||||||
|
/// e.g. `: Sized`
|
||||||
bounds: Vec<GenericBound>,
|
bounds: Vec<GenericBound>,
|
||||||
/// e.g. `type X = usize;`
|
/// e.g. `type X = usize;`
|
||||||
default: Option<Type>,
|
default: Option<Type>,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue