1
Fork 0

Rollup merge of #58243 - GuillaumeGomez:trait-alias-docs, r=Manishearth

Add trait alias support in rustdoc

Fixes #57595.

r? @QuietMisdreavus
This commit is contained in:
Guillaume Gomez 2019-02-10 21:45:13 +01:00 committed by GitHub
commit adf516b94e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 153 additions and 47 deletions

View file

@ -517,6 +517,7 @@ pub enum ItemEnum {
StaticItem(Static), StaticItem(Static),
ConstantItem(Constant), ConstantItem(Constant),
TraitItem(Trait), TraitItem(Trait),
TraitAliasItem(TraitAlias),
ImplItem(Impl), ImplItem(Impl),
/// A method signature only. Used for required methods in traits (ie, /// A method signature only. Used for required methods in traits (ie,
/// non-default-methods). /// non-default-methods).
@ -554,6 +555,7 @@ impl ItemEnum {
ItemEnum::TyMethodItem(ref i) => &i.generics, ItemEnum::TyMethodItem(ref i) => &i.generics,
ItemEnum::MethodItem(ref i) => &i.generics, ItemEnum::MethodItem(ref i) => &i.generics,
ItemEnum::ForeignFunctionItem(ref f) => &f.generics, ItemEnum::ForeignFunctionItem(ref f) => &f.generics,
ItemEnum::TraitAliasItem(ref ta) => &ta.generics,
_ => return None, _ => return None,
}) })
} }
@ -603,6 +605,7 @@ impl Clean<Item> for doctree::Module {
items.extend(self.impls.iter().flat_map(|x| x.clean(cx))); items.extend(self.impls.iter().flat_map(|x| x.clean(cx)));
items.extend(self.macros.iter().map(|x| x.clean(cx))); items.extend(self.macros.iter().map(|x| x.clean(cx)));
items.extend(self.proc_macros.iter().map(|x| x.clean(cx))); items.extend(self.proc_macros.iter().map(|x| x.clean(cx)));
items.extend(self.trait_aliases.iter().map(|x| x.clean(cx)));
// determine if we should display the inner contents or // determine if we should display the inner contents or
// the outer `mod` item for the source code. // the outer `mod` item for the source code.
@ -1909,13 +1912,38 @@ impl Clean<Item> for doctree::Trait {
items: self.items.clean(cx), items: self.items.clean(cx),
generics: self.generics.clean(cx), generics: self.generics.clean(cx),
bounds: self.bounds.clean(cx), bounds: self.bounds.clean(cx),
is_spotlight: is_spotlight, is_spotlight,
is_auto: self.is_auto.clean(cx), is_auto: self.is_auto.clean(cx),
}), }),
} }
} }
} }
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct TraitAlias {
pub generics: Generics,
pub bounds: Vec<GenericBound>,
}
impl Clean<Item> for doctree::TraitAlias {
fn clean(&self, cx: &DocContext) -> Item {
let attrs = self.attrs.clean(cx);
Item {
name: Some(self.name.clean(cx)),
attrs,
source: self.whence.clean(cx),
def_id: cx.tcx.hir().local_def_id(self.id),
visibility: self.vis.clean(cx),
stability: self.stab.clean(cx),
deprecation: self.depr.clean(cx),
inner: TraitAliasItem(TraitAlias {
generics: self.generics.clean(cx),
bounds: self.bounds.clean(cx),
}),
}
}
}
impl Clean<bool> for hir::IsAuto { impl Clean<bool> for hir::IsAuto {
fn clean(&self, _: &DocContext) -> bool { fn clean(&self, _: &DocContext) -> bool {
match *self { match *self {
@ -2247,6 +2275,7 @@ pub enum TypeKind {
Macro, Macro,
Attr, Attr,
Derive, Derive,
TraitAlias,
} }
pub trait GetDefId { pub trait GetDefId {
@ -3858,10 +3887,9 @@ pub fn register_def(cx: &DocContext, def: Def) -> DefId {
MacroKind::Derive => (i, TypeKind::Derive), MacroKind::Derive => (i, TypeKind::Derive),
MacroKind::ProcMacroStub => unreachable!(), MacroKind::ProcMacroStub => unreachable!(),
}, },
Def::TraitAlias(i) => (i, TypeKind::TraitAlias),
Def::SelfTy(Some(def_id), _) => (def_id, TypeKind::Trait), Def::SelfTy(Some(def_id), _) => (def_id, TypeKind::Trait),
Def::SelfTy(_, Some(impl_def_id)) => { Def::SelfTy(_, Some(impl_def_id)) => return impl_def_id,
return impl_def_id
}
_ => return def.def_id() _ => return def.def_id()
}; };
if did.is_local() { return did } if did.is_local() { return did }

View file

@ -38,6 +38,7 @@ pub struct Module {
pub foreigns: Vec<hir::ForeignMod>, pub foreigns: Vec<hir::ForeignMod>,
pub macros: Vec<Macro>, pub macros: Vec<Macro>,
pub proc_macros: Vec<ProcMacro>, pub proc_macros: Vec<ProcMacro>,
pub trait_aliases: Vec<TraitAlias>,
pub is_crate: bool, pub is_crate: bool,
} }
@ -53,21 +54,22 @@ impl Module {
where_inner: syntax_pos::DUMMY_SP, where_inner: syntax_pos::DUMMY_SP,
attrs : hir::HirVec::new(), attrs : hir::HirVec::new(),
extern_crates: Vec::new(), extern_crates: Vec::new(),
imports : Vec::new(), imports : Vec::new(),
structs : Vec::new(), structs : Vec::new(),
unions : Vec::new(), unions : Vec::new(),
enums : Vec::new(), enums : Vec::new(),
fns : Vec::new(), fns : Vec::new(),
mods : Vec::new(), mods : Vec::new(),
typedefs : Vec::new(), typedefs : Vec::new(),
existentials: Vec::new(), existentials: Vec::new(),
statics : Vec::new(), statics : Vec::new(),
constants : Vec::new(), constants : Vec::new(),
traits : Vec::new(), traits : Vec::new(),
impls : Vec::new(), impls : Vec::new(),
foreigns : Vec::new(), foreigns : Vec::new(),
macros : Vec::new(), macros : Vec::new(),
proc_macros: Vec::new(), proc_macros: Vec::new(),
trait_aliases: Vec::new(),
is_crate : false, is_crate : false,
} }
} }
@ -208,6 +210,18 @@ pub struct Trait {
pub depr: Option<attr::Deprecation>, pub depr: Option<attr::Deprecation>,
} }
pub struct TraitAlias {
pub name: Name,
pub generics: hir::Generics,
pub bounds: hir::HirVec<hir::GenericBound>,
pub attrs: hir::HirVec<ast::Attribute>,
pub id: ast::NodeId,
pub whence: Span,
pub vis: hir::Visibility,
pub stab: Option<attr::Stability>,
pub depr: Option<attr::Deprecation>,
}
#[derive(Debug)] #[derive(Debug)]
pub struct Impl { pub struct Impl {
pub unsafety: hir::Unsafety, pub unsafety: hir::Unsafety,

View file

@ -42,6 +42,7 @@ pub enum ItemType {
Existential = 22, Existential = 22,
ProcAttribute = 23, ProcAttribute = 23,
ProcDerive = 24, ProcDerive = 24,
TraitAlias = 25,
} }
@ -86,6 +87,7 @@ impl<'a> From<&'a clean::Item> for ItemType {
clean::AssociatedTypeItem(..) => ItemType::AssociatedType, clean::AssociatedTypeItem(..) => ItemType::AssociatedType,
clean::ForeignTypeItem => ItemType::ForeignType, clean::ForeignTypeItem => ItemType::ForeignType,
clean::KeywordItem(..) => ItemType::Keyword, clean::KeywordItem(..) => ItemType::Keyword,
clean::TraitAliasItem(..) => ItemType::TraitAlias,
clean::ProcMacroItem(ref mac) => match mac.kind { clean::ProcMacroItem(ref mac) => match mac.kind {
MacroKind::Bang => ItemType::Macro, MacroKind::Bang => ItemType::Macro,
MacroKind::Attr => ItemType::ProcAttribute, MacroKind::Attr => ItemType::ProcAttribute,
@ -100,20 +102,21 @@ impl<'a> From<&'a clean::Item> for ItemType {
impl From<clean::TypeKind> for ItemType { impl From<clean::TypeKind> for ItemType {
fn from(kind: clean::TypeKind) -> ItemType { fn from(kind: clean::TypeKind) -> ItemType {
match kind { match kind {
clean::TypeKind::Struct => ItemType::Struct, clean::TypeKind::Struct => ItemType::Struct,
clean::TypeKind::Union => ItemType::Union, clean::TypeKind::Union => ItemType::Union,
clean::TypeKind::Enum => ItemType::Enum, clean::TypeKind::Enum => ItemType::Enum,
clean::TypeKind::Function => ItemType::Function, clean::TypeKind::Function => ItemType::Function,
clean::TypeKind::Trait => ItemType::Trait, clean::TypeKind::Trait => ItemType::Trait,
clean::TypeKind::Module => ItemType::Module, clean::TypeKind::Module => ItemType::Module,
clean::TypeKind::Static => ItemType::Static, clean::TypeKind::Static => ItemType::Static,
clean::TypeKind::Const => ItemType::Constant, clean::TypeKind::Const => ItemType::Constant,
clean::TypeKind::Variant => ItemType::Variant, clean::TypeKind::Variant => ItemType::Variant,
clean::TypeKind::Typedef => ItemType::Typedef, clean::TypeKind::Typedef => ItemType::Typedef,
clean::TypeKind::Foreign => ItemType::ForeignType, clean::TypeKind::Foreign => ItemType::ForeignType,
clean::TypeKind::Macro => ItemType::Macro, clean::TypeKind::Macro => ItemType::Macro,
clean::TypeKind::Attr => ItemType::ProcAttribute, clean::TypeKind::Attr => ItemType::ProcAttribute,
clean::TypeKind::Derive => ItemType::ProcDerive, clean::TypeKind::Derive => ItemType::ProcDerive,
clean::TypeKind::TraitAlias => ItemType::TraitAlias,
} }
} }
} }
@ -146,6 +149,7 @@ impl ItemType {
ItemType::Existential => "existential", ItemType::Existential => "existential",
ItemType::ProcAttribute => "attr", ItemType::ProcAttribute => "attr",
ItemType::ProcDerive => "derive", ItemType::ProcDerive => "derive",
ItemType::TraitAlias => "traitalias",
} }
} }
@ -160,6 +164,7 @@ impl ItemType {
ItemType::Primitive | ItemType::Primitive |
ItemType::AssociatedType | ItemType::AssociatedType |
ItemType::Existential | ItemType::Existential |
ItemType::TraitAlias |
ItemType::ForeignType => NameSpace::Type, ItemType::ForeignType => NameSpace::Type,
ItemType::ExternCrate | ItemType::ExternCrate |

View file

@ -1836,6 +1836,7 @@ struct AllTypes {
keywords: FxHashSet<ItemEntry>, keywords: FxHashSet<ItemEntry>,
attributes: FxHashSet<ItemEntry>, attributes: FxHashSet<ItemEntry>,
derives: FxHashSet<ItemEntry>, derives: FxHashSet<ItemEntry>,
trait_aliases: FxHashSet<ItemEntry>,
} }
impl AllTypes { impl AllTypes {
@ -1856,6 +1857,7 @@ impl AllTypes {
keywords: new_set(100), keywords: new_set(100),
attributes: new_set(100), attributes: new_set(100),
derives: new_set(100), derives: new_set(100),
trait_aliases: new_set(100),
} }
} }
@ -1879,6 +1881,7 @@ impl AllTypes {
ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)), ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)),
ItemType::ProcAttribute => self.attributes.insert(ItemEntry::new(new_url, name)), ItemType::ProcAttribute => self.attributes.insert(ItemEntry::new(new_url, name)),
ItemType::ProcDerive => self.derives.insert(ItemEntry::new(new_url, name)), ItemType::ProcDerive => self.derives.insert(ItemEntry::new(new_url, name)),
ItemType::TraitAlias => self.trait_aliases.insert(ItemEntry::new(new_url, name)),
_ => true, _ => true,
}; };
} }
@ -1922,6 +1925,7 @@ impl fmt::Display for AllTypes {
print_entries(f, &self.derives, "Derive Macros", "derives")?; print_entries(f, &self.derives, "Derive Macros", "derives")?;
print_entries(f, &self.functions, "Functions", "functions")?; print_entries(f, &self.functions, "Functions", "functions")?;
print_entries(f, &self.typedefs, "Typedefs", "typedefs")?; print_entries(f, &self.typedefs, "Typedefs", "typedefs")?;
print_entries(f, &self.trait_aliases, "Trait Aliases", "trait-aliases")?;
print_entries(f, &self.existentials, "Existentials", "existentials")?; print_entries(f, &self.existentials, "Existentials", "existentials")?;
print_entries(f, &self.statics, "Statics", "statics")?; print_entries(f, &self.statics, "Statics", "statics")?;
print_entries(f, &self.constants, "Constants", "constants") print_entries(f, &self.constants, "Constants", "constants")
@ -2419,6 +2423,7 @@ impl<'a> fmt::Display for Item<'a> {
clean::ForeignTypeItem => write!(fmt, "Foreign Type ")?, clean::ForeignTypeItem => write!(fmt, "Foreign Type ")?,
clean::KeywordItem(..) => write!(fmt, "Keyword ")?, clean::KeywordItem(..) => write!(fmt, "Keyword ")?,
clean::ExistentialItem(..) => write!(fmt, "Existential Type ")?, clean::ExistentialItem(..) => write!(fmt, "Existential Type ")?,
clean::TraitAliasItem(..) => write!(fmt, "Trait Alias ")?,
_ => { _ => {
// We don't generate pages for any other type. // We don't generate pages for any other type.
unreachable!(); unreachable!();
@ -2457,6 +2462,7 @@ impl<'a> fmt::Display for Item<'a> {
clean::ForeignTypeItem => item_foreign_type(fmt, self.cx, self.item), clean::ForeignTypeItem => item_foreign_type(fmt, self.cx, self.item),
clean::KeywordItem(ref k) => item_keyword(fmt, self.cx, self.item, k), clean::KeywordItem(ref k) => item_keyword(fmt, self.cx, self.item, k),
clean::ExistentialItem(ref e, _) => item_existential(fmt, self.cx, self.item, e), clean::ExistentialItem(ref e, _) => item_existential(fmt, self.cx, self.item, e),
clean::TraitAliasItem(ref ta) => item_trait_alias(fmt, self.cx, self.item, ta),
_ => { _ => {
// We don't generate pages for any other type. // We don't generate pages for any other type.
unreachable!(); unreachable!();
@ -3015,23 +3021,17 @@ fn render_impls(cx: &Context, w: &mut fmt::Formatter,
Ok(()) Ok(())
} }
fn bounds(t_bounds: &[clean::GenericBound]) -> String { fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool) -> String {
let mut bounds = String::new(); let mut bounds = String::new();
let mut bounds_plain = String::new();
if !t_bounds.is_empty() { if !t_bounds.is_empty() {
if !bounds.is_empty() { if !trait_alias {
bounds.push(' '); bounds.push_str(": ");
bounds_plain.push(' ');
} }
bounds.push_str(": ");
bounds_plain.push_str(": ");
for (i, p) in t_bounds.iter().enumerate() { for (i, p) in t_bounds.iter().enumerate() {
if i > 0 { if i > 0 {
bounds.push_str(" + "); bounds.push_str(" + ");
bounds_plain.push_str(" + ");
} }
bounds.push_str(&(*p).to_string()); bounds.push_str(&(*p).to_string());
bounds_plain.push_str(&format!("{:#}", *p));
} }
} }
bounds bounds
@ -3051,7 +3051,7 @@ fn item_trait(
it: &clean::Item, it: &clean::Item,
t: &clean::Trait, t: &clean::Trait,
) -> fmt::Result { ) -> fmt::Result {
let bounds = bounds(&t.bounds); let bounds = bounds(&t.bounds, false);
let types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>(); let types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>();
let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>(); let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>();
let required = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>(); let required = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>();
@ -4282,7 +4282,26 @@ fn item_existential(
it.name.as_ref().unwrap(), it.name.as_ref().unwrap(),
t.generics, t.generics,
where_clause = WhereClause { gens: &t.generics, indent: 0, end_newline: true }, where_clause = WhereClause { gens: &t.generics, indent: 0, end_newline: true },
bounds = bounds(&t.bounds))?; bounds = bounds(&t.bounds, false))?;
document(w, cx, it)?;
// Render any items associated directly to this alias, as otherwise they
// won't be visible anywhere in the docs. It would be nice to also show
// associated items from the aliased type (see discussion in #32077), but
// we need #14072 to make sense of the generics.
render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
}
fn item_trait_alias(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
t: &clean::TraitAlias) -> fmt::Result {
write!(w, "<pre class='rust trait-alias'>")?;
render_attributes(w, it)?;
write!(w, "trait {}{}{} = {};</pre>",
it.name.as_ref().unwrap(),
t.generics,
WhereClause { gens: &t.generics, indent: 0, end_newline: true },
bounds(&t.bounds, true))?;
document(w, cx, it)?; document(w, cx, it)?;
@ -4846,6 +4865,7 @@ fn item_ty_to_strs(ty: &ItemType) -> (&'static str, &'static str) {
ItemType::Existential => ("existentials", "Existentials"), ItemType::Existential => ("existentials", "Existentials"),
ItemType::ProcAttribute => ("attributes", "Attribute Macros"), ItemType::ProcAttribute => ("attributes", "Attribute Macros"),
ItemType::ProcDerive => ("derives", "Derive Macros"), ItemType::ProcDerive => ("derives", "Derive Macros"),
ItemType::TraitAlias => ("trait-aliases", "Trait aliases"),
} }
} }

View file

@ -68,7 +68,8 @@ if (!DOMTokenList.prototype.remove) {
"keyword", "keyword",
"existential", "existential",
"attr", "attr",
"derive"]; "derive",
"traitalias"];
var search_input = document.getElementsByClassName("search-input")[0]; var search_input = document.getElementsByClassName("search-input")[0];
@ -1786,6 +1787,7 @@ if (!DOMTokenList.prototype.remove) {
block("type", "Type Definitions"); block("type", "Type Definitions");
block("foreigntype", "Foreign Types"); block("foreigntype", "Foreign Types");
block("keyword", "Keywords"); block("keyword", "Keywords");
block("traitalias", "Trait Aliases");
} }
window.initSidebarItems = initSidebarItems; window.initSidebarItems = initSidebarItems;

View file

@ -94,6 +94,7 @@ pre {
} }
.content .highlighted a, .content .highlighted span { color: #eee !important; } .content .highlighted a, .content .highlighted span { color: #eee !important; }
.content .highlighted.trait { background-color: #013191; } .content .highlighted.trait { background-color: #013191; }
.content .highlighted.traitalias { background-color: #013191; }
.content .highlighted.mod, .content .highlighted.mod,
.content .highlighted.externcrate { background-color: #afc6e4; } .content .highlighted.externcrate { background-color: #afc6e4; }
.content .highlighted.mod { background-color: #803a1b; } .content .highlighted.mod { background-color: #803a1b; }
@ -128,6 +129,7 @@ pre {
.content span.externcrate, .content span.externcrate,
.content span.mod, .content a.mod, .block a.current.mod { color: #bda000; } .content span.mod, .content a.mod, .block a.current.mod { color: #bda000; }
.content span.trait, .content a.trait, .block a.current.trait { color: #b78cf2; } .content span.trait, .content a.trait, .block a.current.trait { color: #b78cf2; }
.content span.traitalias, .content a.traitalias, .block a.current.traitalias { color: #b397da; }
.content span.fn, .content a.fn, .block a.current.fn, .content span.fn, .content a.fn, .block a.current.fn,
.content span.method, .content a.method, .block a.current.method, .content span.method, .content a.method, .block a.current.method,
.content span.tymethod, .content a.tymethod, .block a.current.tymethod, .content span.tymethod, .content a.tymethod, .block a.current.tymethod,

View file

@ -96,6 +96,7 @@ pre {
} }
.content .highlighted a, .content .highlighted span { color: #000 !important; } .content .highlighted a, .content .highlighted span { color: #000 !important; }
.content .highlighted.trait { background-color: #c7b6ff; } .content .highlighted.trait { background-color: #c7b6ff; }
.content .highlighted.traitalias { background-color: #c7b6ff; }
.content .highlighted.mod, .content .highlighted.mod,
.content .highlighted.externcrate { background-color: #afc6e4; } .content .highlighted.externcrate { background-color: #afc6e4; }
.content .highlighted.enum { background-color: #b4d1b9; } .content .highlighted.enum { background-color: #b4d1b9; }
@ -128,6 +129,7 @@ pre {
.content span.externcrate, .content span.externcrate,
.content span.mod, .content a.mod, .block a.current.mod { color: #4d76ae; } .content span.mod, .content a.mod, .block a.current.mod { color: #4d76ae; }
.content span.trait, .content a.trait, .block a.current.trait { color: #7c5af3; } .content span.trait, .content a.trait, .block a.current.trait { color: #7c5af3; }
.content span.traitalias, .content a.traitalias, .block a.current.traitalias { color: #6841f1; }
.content span.fn, .content a.fn, .block a.current.fn, .content span.fn, .content a.fn, .block a.current.fn,
.content span.method, .content a.method, .block a.current.method, .content span.method, .content a.method, .block a.current.method,
.content span.tymethod, .content a.tymethod, .block a.current.tymethod, .content span.tymethod, .content a.tymethod, .block a.current.tymethod,

View file

@ -224,6 +224,7 @@ impl<'a> fold::DocFolder for Stripper<'a> {
| clean::ConstantItem(..) | clean::ConstantItem(..)
| clean::UnionItem(..) | clean::UnionItem(..)
| clean::AssociatedConstItem(..) | clean::AssociatedConstItem(..)
| clean::TraitAliasItem(..)
| clean::ForeignTypeItem => { | clean::ForeignTypeItem => {
if i.def_id.is_local() { if i.def_id.is_local() {
if !self.access_levels.is_exported(i.def_id) { if !self.access_levels.is_exported(i.def_id) {

View file

@ -547,8 +547,19 @@ impl<'a, 'tcx, 'rcx> RustdocVisitor<'a, 'tcx, 'rcx> {
}; };
om.traits.push(t); om.traits.push(t);
}, },
hir::ItemKind::TraitAlias(..) => { hir::ItemKind::TraitAlias(ref gen, ref b) => {
unimplemented!("trait objects are not yet implemented") let t = TraitAlias {
name: ident.name,
generics: gen.clone(),
bounds: b.iter().cloned().collect(),
id: item.id,
attrs: item.attrs.clone(),
whence: item.span,
vis: item.vis.clone(),
stab: self.stability(item.id),
depr: self.deprecation(item.id),
};
om.trait_aliases.push(t);
}, },
hir::ItemKind::Impl(unsafety, hir::ItemKind::Impl(unsafety,

View file

@ -0,0 +1,21 @@
#![feature(trait_alias)]
#![crate_name = "foo"]
use std::fmt::Debug;
// @has foo/all.html '//a[@href="traitalias.CopyAlias.html"]' 'CopyAlias'
// @has foo/all.html '//a[@href="traitalias.Alias2.html"]' 'Alias2'
// @has foo/all.html '//a[@href="traitalias.Foo.html"]' 'Foo'
// @has foo/index.html '//h2[@id="trait-aliases"]' 'Trait aliases'
// @has foo/index.html '//a[@class="traitalias"]' 'CopyAlias'
// @has foo/index.html '//a[@class="traitalias"]' 'Alias2'
// @has foo/index.html '//a[@class="traitalias"]' 'Foo'
// @has foo/traitalias.CopyAlias.html '//section[@id="main"]/pre' 'trait CopyAlias = Copy;'
pub trait CopyAlias = Copy;
// @has foo/traitalias.Alias2.html '//section[@id="main"]/pre' 'trait Alias2 = Copy + Debug;'
pub trait Alias2 = Copy + Debug;
// @has foo/traitalias.Foo.html '//section[@id="main"]/pre' 'trait Foo<T> = Into<T> + Debug;'
pub trait Foo<T> = Into<T> + Debug;