rustdoc: Improve handling inlined associated types
* All bounds are now discovered through the trait to be inlined. * The `?Sized` bound now renders correctly for inlined associated types. * All `QPath`s (`<A as B>::C`) instances are rendered as `A::C` where `C` is a hyperlink to the trait `B`. This should improve at least how the docs look at least. * Supertrait bounds are now separated and display as the source lists them. Closes #20727 Closes #21145
This commit is contained in:
parent
11f26f9995
commit
75ef0832ae
9 changed files with 382 additions and 69 deletions
|
@ -1264,7 +1264,8 @@ fn encode_info_for_item(ecx: &EncodeContext,
|
|||
encode_paren_sugar(rbml_w, trait_def.paren_sugar);
|
||||
encode_defaulted(rbml_w, ty::trait_has_default_impl(tcx, def_id));
|
||||
encode_associated_type_names(rbml_w, &trait_def.associated_type_names);
|
||||
encode_generics(rbml_w, ecx, &trait_def.generics, &trait_predicates, tag_item_generics);
|
||||
encode_generics(rbml_w, ecx, &trait_def.generics, &trait_predicates,
|
||||
tag_item_generics);
|
||||
encode_predicates(rbml_w, ecx, &ty::lookup_super_predicates(tcx, def_id),
|
||||
tag_item_super_predicates);
|
||||
encode_trait_ref(rbml_w, ecx, &*trait_def.trait_ref, tag_item_trait_ref);
|
||||
|
|
|
@ -150,11 +150,14 @@ pub fn build_external_trait(cx: &DocContext, tcx: &ty::ctxt,
|
|||
let def = ty::lookup_trait_def(tcx, did);
|
||||
let trait_items = ty::trait_items(tcx, did).clean(cx);
|
||||
let predicates = ty::lookup_predicates(tcx, did);
|
||||
let generics = (&def.generics, &predicates, subst::TypeSpace).clean(cx);
|
||||
let generics = filter_non_trait_generics(did, generics);
|
||||
let (generics, supertrait_bounds) = separate_supertrait_bounds(generics);
|
||||
clean::Trait {
|
||||
unsafety: def.unsafety,
|
||||
generics: (&def.generics, &predicates, subst::TypeSpace).clean(cx),
|
||||
generics: generics,
|
||||
items: trait_items,
|
||||
bounds: vec![], // supertraits can be found in the list of predicates
|
||||
bounds: supertrait_bounds,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -447,3 +450,48 @@ fn build_static(cx: &DocContext, tcx: &ty::ctxt,
|
|||
expr: "\n\n\n".to_string(), // trigger the "[definition]" links
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait's generics clause actually contains all of the predicates for all of
|
||||
/// its associated types as well. We specifically move these clauses to the
|
||||
/// associated types instead when displaying, so when we're genering the
|
||||
/// generics for the trait itself we need to be sure to remove them.
|
||||
///
|
||||
/// The inverse of this filtering logic can be found in the `Clean`
|
||||
/// implementation for `AssociatedType`
|
||||
fn filter_non_trait_generics(trait_did: ast::DefId, mut g: clean::Generics)
|
||||
-> clean::Generics {
|
||||
g.where_predicates.retain(|pred| {
|
||||
match *pred {
|
||||
clean::WherePredicate::BoundPredicate {
|
||||
ty: clean::QPath {
|
||||
self_type: box clean::Generic(ref s),
|
||||
trait_: box clean::ResolvedPath { did, .. },
|
||||
name: ref _name,
|
||||
}, ..
|
||||
} => *s != "Self" || did != trait_did,
|
||||
_ => true,
|
||||
}
|
||||
});
|
||||
return g;
|
||||
}
|
||||
|
||||
/// Supertrait bounds for a trait are also listed in the generics coming from
|
||||
/// the metadata for a crate, so we want to separate those out and create a new
|
||||
/// list of explicit supertrait bounds to render nicely.
|
||||
fn separate_supertrait_bounds(mut g: clean::Generics)
|
||||
-> (clean::Generics, Vec<clean::TyParamBound>) {
|
||||
let mut ty_bounds = Vec::new();
|
||||
g.where_predicates.retain(|pred| {
|
||||
match *pred {
|
||||
clean::WherePredicate::BoundPredicate {
|
||||
ty: clean::Generic(ref s),
|
||||
ref bounds
|
||||
} if *s == "Self" => {
|
||||
ty_bounds.extend(bounds.iter().cloned());
|
||||
false
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
});
|
||||
(g, ty_bounds)
|
||||
}
|
||||
|
|
|
@ -498,6 +498,35 @@ pub enum TyParamBound {
|
|||
TraitBound(PolyTrait, ast::TraitBoundModifier)
|
||||
}
|
||||
|
||||
impl TyParamBound {
|
||||
fn maybe_sized(cx: &DocContext) -> TyParamBound {
|
||||
use syntax::ast::TraitBoundModifier as TBM;
|
||||
let mut sized_bound = ty::BuiltinBound::BoundSized.clean(cx);
|
||||
if let TyParamBound::TraitBound(_, ref mut tbm) = sized_bound {
|
||||
*tbm = TBM::Maybe
|
||||
};
|
||||
sized_bound
|
||||
}
|
||||
|
||||
fn is_sized_bound(&self, cx: &DocContext) -> bool {
|
||||
use syntax::ast::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
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Clean<TyParamBound> for ast::TyParamBound {
|
||||
fn clean(&self, cx: &DocContext) -> TyParamBound {
|
||||
match *self {
|
||||
|
@ -835,7 +864,9 @@ impl<'tcx> Clean<Type> for ty::ProjectionTy<'tcx> {
|
|||
fn clean(&self, cx: &DocContext) -> Type {
|
||||
let trait_ = match self.trait_ref.clean(cx) {
|
||||
TyParamBound::TraitBound(t, _) => t.trait_,
|
||||
TyParamBound::RegionBound(_) => panic!("cleaning a trait got a region??"),
|
||||
TyParamBound::RegionBound(_) => {
|
||||
panic!("cleaning a trait got a region")
|
||||
}
|
||||
};
|
||||
Type::QPath {
|
||||
name: self.item_name.clean(cx),
|
||||
|
@ -868,28 +899,8 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics<'tcx>,
|
|||
subst::ParamSpace) {
|
||||
fn clean(&self, cx: &DocContext) -> Generics {
|
||||
use std::collections::HashSet;
|
||||
use syntax::ast::TraitBoundModifier as TBM;
|
||||
use self::WherePredicate as WP;
|
||||
|
||||
fn has_sized_bound(bounds: &[TyParamBound], cx: &DocContext) -> bool {
|
||||
if let Some(tcx) = cx.tcx_opt() {
|
||||
let sized_did = match tcx.lang_items.sized_trait() {
|
||||
Some(did) => did,
|
||||
None => return false
|
||||
};
|
||||
for bound in bounds {
|
||||
if let TyParamBound::TraitBound(PolyTrait {
|
||||
trait_: Type::ResolvedPath { did, .. }, ..
|
||||
}, TBM::None) = *bound {
|
||||
if did == sized_did {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
let (gens, preds, space) = *self;
|
||||
|
||||
// Bounds in the type_params and lifetimes fields are repeated in the
|
||||
|
@ -904,34 +915,38 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics<'tcx>,
|
|||
srp.clean(cx)
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
let where_predicates = preds.predicates.get_slice(space)
|
||||
.to_vec().clean(cx);
|
||||
let mut where_predicates = preds.predicates.get_slice(space)
|
||||
.to_vec().clean(cx);
|
||||
|
||||
// Type parameters have a Sized bound by default unless removed with
|
||||
// Type parameters and have a Sized bound by default unless removed with
|
||||
// ?Sized. Scan through the predicates and mark any type parameter with
|
||||
// a Sized bound, removing the bounds as we find them.
|
||||
//
|
||||
// Note that associated types also have a sized bound by default, but we
|
||||
// don't actually konw the set of associated types right here so that's
|
||||
// handled in cleaning associated types
|
||||
let mut sized_params = HashSet::new();
|
||||
let mut where_predicates = where_predicates.into_iter().filter_map(|pred| {
|
||||
if let WP::BoundPredicate { ty: Type::Generic(ref g), ref bounds } = pred {
|
||||
if has_sized_bound(&**bounds, cx) {
|
||||
sized_params.insert(g.clone());
|
||||
return None
|
||||
where_predicates.retain(|pred| {
|
||||
match *pred {
|
||||
WP::BoundPredicate { ty: Generic(ref g), ref bounds } => {
|
||||
if bounds.iter().any(|b| b.is_sized_bound(cx)) {
|
||||
sized_params.insert(g.clone());
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
Some(pred)
|
||||
}).collect::<Vec<_>>();
|
||||
});
|
||||
|
||||
// Finally, run through the type parameters again and insert a ?Sized
|
||||
// Run through the type parameters again and insert a ?Sized
|
||||
// unbound for any we didn't find to be Sized.
|
||||
for tp in &stripped_typarams {
|
||||
if !sized_params.contains(&tp.name) {
|
||||
let mut sized_bound = ty::BuiltinBound::BoundSized.clean(cx);
|
||||
if let TyParamBound::TraitBound(_, ref mut tbm) = sized_bound {
|
||||
*tbm = TBM::Maybe
|
||||
};
|
||||
where_predicates.push(WP::BoundPredicate {
|
||||
ty: Type::Generic(tp.name.clone()),
|
||||
bounds: vec![sized_bound]
|
||||
bounds: vec![TyParamBound::maybe_sized(cx)],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1597,17 +1612,7 @@ impl<'tcx> Clean<Type> for ty::Ty<'tcx> {
|
|||
}
|
||||
ty::ty_tup(ref t) => Tuple(t.clean(cx)),
|
||||
|
||||
ty::ty_projection(ref data) => {
|
||||
let trait_ref = match data.trait_ref.clean(cx) {
|
||||
TyParamBound::TraitBound(t, _) => t.trait_,
|
||||
TyParamBound::RegionBound(_) => panic!("cleaning a trait got a region??"),
|
||||
};
|
||||
Type::QPath {
|
||||
name: data.item_name.clean(cx),
|
||||
self_type: box data.trait_ref.self_ty().clean(cx),
|
||||
trait_: box trait_ref,
|
||||
}
|
||||
}
|
||||
ty::ty_projection(ref data) => data.clean(cx),
|
||||
|
||||
ty::ty_param(ref p) => Generic(token::get_name(p.name).to_string()),
|
||||
|
||||
|
@ -1881,6 +1886,22 @@ pub struct Path {
|
|||
pub segments: Vec<PathSegment>,
|
||||
}
|
||||
|
||||
impl Path {
|
||||
pub fn singleton(name: String) -> Path {
|
||||
Path {
|
||||
global: false,
|
||||
segments: vec![PathSegment {
|
||||
name: name,
|
||||
params: PathParameters::AngleBracketed {
|
||||
lifetimes: Vec::new(),
|
||||
types: Vec::new(),
|
||||
bindings: Vec::new()
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clean<Path> for ast::Path {
|
||||
fn clean(&self, cx: &DocContext) -> Path {
|
||||
Path {
|
||||
|
@ -2516,21 +2537,66 @@ impl Clean<Stability> for attr::Stability {
|
|||
|
||||
impl Clean<Item> for ty::AssociatedType {
|
||||
fn clean(&self, cx: &DocContext) -> Item {
|
||||
// When loading a cross-crate associated type, the bounds for this type
|
||||
// are actually located on the trait/impl itself, so we need to load
|
||||
// all of the generics from there and then look for bounds that are
|
||||
// applied to this associated type in question.
|
||||
let predicates = ty::lookup_predicates(cx.tcx(), self.container.id());
|
||||
let generics = match self.container {
|
||||
ty::TraitContainer(did) => {
|
||||
let def = ty::lookup_trait_def(cx.tcx(), did);
|
||||
(&def.generics, &predicates, subst::TypeSpace).clean(cx)
|
||||
}
|
||||
ty::ImplContainer(did) => {
|
||||
let ty = ty::lookup_item_type(cx.tcx(), did);
|
||||
(&ty.generics, &predicates, subst::TypeSpace).clean(cx)
|
||||
}
|
||||
};
|
||||
let my_name = self.name.clean(cx);
|
||||
let mut bounds = generics.where_predicates.iter().filter_map(|pred| {
|
||||
let (name, self_type, trait_, bounds) = match *pred {
|
||||
WherePredicate::BoundPredicate {
|
||||
ty: QPath { ref name, ref self_type, ref trait_ },
|
||||
ref bounds
|
||||
} => (name, self_type, trait_, bounds),
|
||||
_ => return None,
|
||||
};
|
||||
if *name != my_name { return None }
|
||||
match **trait_ {
|
||||
ResolvedPath { did, .. } if did == self.container.id() => {}
|
||||
_ => return None,
|
||||
}
|
||||
match **self_type {
|
||||
Generic(ref s) if *s == "Self" => {}
|
||||
_ => return None,
|
||||
}
|
||||
Some(bounds)
|
||||
}).flat_map(|i| i.iter().cloned()).collect::<Vec<_>>();
|
||||
|
||||
// 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
|
||||
// (some of them may have come from the trait). If we do have a sized
|
||||
// bound, we remove it, and if we don't then we add the `?Sized` bound
|
||||
// at the end.
|
||||
match bounds.iter().position(|b| b.is_sized_bound(cx)) {
|
||||
Some(i) => { bounds.remove(i); }
|
||||
None => bounds.push(TyParamBound::maybe_sized(cx)),
|
||||
}
|
||||
|
||||
Item {
|
||||
source: DUMMY_SP.clean(cx),
|
||||
name: Some(self.name.clean(cx)),
|
||||
attrs: Vec::new(),
|
||||
// FIXME(#20727): bounds are missing and need to be filled in from the
|
||||
// predicates on the trait itself
|
||||
inner: AssociatedTypeItem(vec![], None),
|
||||
visibility: None,
|
||||
attrs: inline::load_attrs(cx, cx.tcx(), self.def_id),
|
||||
inner: AssociatedTypeItem(bounds, None),
|
||||
visibility: self.vis.clean(cx),
|
||||
def_id: self.def_id,
|
||||
stability: None,
|
||||
stability: stability::lookup(cx.tcx(), self.def_id).clean(cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Clean<Typedef> for (ty::TypeScheme<'a>, ty::GenericPredicates<'a>, ParamSpace) {
|
||||
impl<'a> Clean<Typedef> for (ty::TypeScheme<'a>, ty::GenericPredicates<'a>,
|
||||
ParamSpace) {
|
||||
fn clean(&self, cx: &DocContext) -> Typedef {
|
||||
let (ref ty_scheme, ref predicates, ps) = *self;
|
||||
Typedef {
|
||||
|
|
|
@ -502,6 +502,29 @@ impl fmt::Display for clean::Type {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
// It's pretty unsightly to look at `<A as B>::C` in output, and
|
||||
// we've got hyperlinking on our side, so try to avoid longer
|
||||
// notation as much as possible by making `C` a hyperlink to trait
|
||||
// `B` to disambiguate.
|
||||
//
|
||||
// FIXME: this is still a lossy conversion and there should probably
|
||||
// be a better way of representing this in general? Most of
|
||||
// the ugliness comes from inlining across crates where
|
||||
// everything comes in as a fully resolved QPath (hard to
|
||||
// look at).
|
||||
clean::QPath {
|
||||
ref name,
|
||||
ref self_type,
|
||||
trait_: box clean::ResolvedPath { did, ref typarams, .. },
|
||||
} => {
|
||||
try!(write!(f, "{}::", self_type));
|
||||
let path = clean::Path::singleton(name.clone());
|
||||
try!(resolved_path(f, did, &path, false));
|
||||
|
||||
// FIXME: `typarams` are not rendered, and this seems bad?
|
||||
drop(typarams);
|
||||
Ok(())
|
||||
}
|
||||
clean::QPath { ref name, ref self_type, ref trait_ } => {
|
||||
write!(f, "<{} as {}>::{}", self_type, trait_, name)
|
||||
}
|
||||
|
@ -636,17 +659,7 @@ impl fmt::Display for clean::ViewListIdent {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.source {
|
||||
Some(did) => {
|
||||
let path = clean::Path {
|
||||
global: false,
|
||||
segments: vec!(clean::PathSegment {
|
||||
name: self.name.clone(),
|
||||
params: clean::PathParameters::AngleBracketed {
|
||||
lifetimes: Vec::new(),
|
||||
types: Vec::new(),
|
||||
bindings: Vec::new()
|
||||
}
|
||||
})
|
||||
};
|
||||
let path = clean::Path::singleton(self.name.clone());
|
||||
resolved_path(f, did, &path, false)
|
||||
}
|
||||
_ => write!(f, "{}", self.name),
|
||||
|
|
38
src/test/auxiliary/issue-20727.rs
Normal file
38
src/test/auxiliary/issue-20727.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
pub trait Deref {
|
||||
type Target: ?Sized;
|
||||
|
||||
fn deref<'a>(&'a self) -> &'a Self::Target;
|
||||
}
|
||||
|
||||
pub trait Add<RHS = Self> {
|
||||
type Output;
|
||||
|
||||
fn add(self, rhs: RHS) -> Self::Output;
|
||||
}
|
||||
|
||||
|
||||
pub trait Bar {}
|
||||
pub trait Deref2 {
|
||||
type Target: Bar;
|
||||
|
||||
fn deref(&self) -> Self::Target;
|
||||
}
|
||||
|
||||
pub trait Index<Idx: ?Sized> {
|
||||
type Output: ?Sized;
|
||||
fn index(&self, index: Idx) -> &Self::Output;
|
||||
}
|
||||
|
||||
pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
|
||||
fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
|
||||
}
|
32
src/test/rustdoc/issue-20727-2.rs
Normal file
32
src/test/rustdoc/issue-20727-2.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// aux-build:issue-20727.rs
|
||||
|
||||
extern crate issue_20727;
|
||||
|
||||
// @has issue_20727_2/trait.Add.html
|
||||
pub trait Add<RHS = Self> {
|
||||
// @has - '//*[@class="rust trait"]' 'trait Add<RHS = Self> {'
|
||||
// @has - '//*[@class="rust trait"]' 'type Output;'
|
||||
type Output;
|
||||
|
||||
// @has - '//*[@class="rust trait"]' 'fn add(self, rhs: RHS) -> Self::Output;'
|
||||
fn add(self, rhs: RHS) -> Self::Output;
|
||||
}
|
||||
|
||||
// @has issue_20727_2/reexport/trait.Add.html
|
||||
pub mod reexport {
|
||||
// @has - '//*[@class="rust trait"]' 'trait Add<RHS = Self> {'
|
||||
// @has - '//*[@class="rust trait"]' 'type Output;'
|
||||
// @has - '//*[@class="rust trait"]' 'fn add(self, rhs: RHS) -> Self::Output;'
|
||||
pub use issue_20727::Add;
|
||||
}
|
||||
|
33
src/test/rustdoc/issue-20727-3.rs
Normal file
33
src/test/rustdoc/issue-20727-3.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// aux-build:issue-20727.rs
|
||||
|
||||
extern crate issue_20727;
|
||||
|
||||
pub trait Bar {}
|
||||
|
||||
// @has issue_20727_3/trait.Deref2.html
|
||||
pub trait Deref2 {
|
||||
// @has - '//*[@class="rust trait"]' 'trait Deref2 {'
|
||||
// @has - '//*[@class="rust trait"]' 'type Target: Bar;'
|
||||
type Target: Bar;
|
||||
|
||||
// @has - '//*[@class="rust trait"]' 'fn deref(&self) -> Self::Target;'
|
||||
fn deref(&self) -> Self::Target;
|
||||
}
|
||||
|
||||
// @has issue_20727_3/reexport/trait.Deref2.html
|
||||
pub mod reexport {
|
||||
// @has - '//*[@class="rust trait"]' 'trait Deref2 {'
|
||||
// @has - '//*[@class="rust trait"]' 'type Target: Bar;'
|
||||
// @has - '//*[@class="rust trait"]' 'fn deref(&self) -> Self::Target;'
|
||||
pub use issue_20727::Deref2;
|
||||
}
|
49
src/test/rustdoc/issue-20727-4.rs
Normal file
49
src/test/rustdoc/issue-20727-4.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// aux-build:issue-20727.rs
|
||||
|
||||
extern crate issue_20727;
|
||||
|
||||
// @has issue_20727_4/trait.Index.html
|
||||
pub trait Index<Idx: ?Sized> {
|
||||
// @has - '//*[@class="rust trait"]' 'trait Index<Idx: ?Sized> {'
|
||||
// @has - '//*[@class="rust trait"]' 'type Output: ?Sized'
|
||||
type Output: ?Sized;
|
||||
|
||||
// @has - '//*[@class="rust trait"]' \
|
||||
// 'fn index(&self, index: Idx) -> &Self::Output'
|
||||
fn index(&self, index: Idx) -> &Self::Output;
|
||||
}
|
||||
|
||||
// @has issue_20727_4/trait.IndexMut.html
|
||||
pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
|
||||
// @has - '//*[@class="rust trait"]' \
|
||||
// 'trait IndexMut<Idx: ?Sized>: Index<Idx> {'
|
||||
// @has - '//*[@class="rust trait"]' \
|
||||
// 'fn index_mut(&mut self, index: Idx) -> &mut Self::Output;'
|
||||
fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
|
||||
}
|
||||
|
||||
pub mod reexport {
|
||||
// @has issue_20727_4/reexport/trait.Index.html
|
||||
// @has - '//*[@class="rust trait"]' 'trait Index<Idx> where Idx: ?Sized {'
|
||||
// @has - '//*[@class="rust trait"]' 'type Output: ?Sized'
|
||||
// @has - '//*[@class="rust trait"]' \
|
||||
// 'fn index(&self, index: Idx) -> &Self::Output'
|
||||
pub use issue_20727::Index;
|
||||
|
||||
// @has issue_20727_4/reexport/trait.IndexMut.html
|
||||
// @has - '//*[@class="rust trait"]' \
|
||||
// 'trait IndexMut<Idx>: Index<Idx> where Idx: ?Sized {'
|
||||
// @has - '//*[@class="rust trait"]' \
|
||||
// 'fn index_mut(&mut self, index: Idx) -> &mut Self::Output;'
|
||||
pub use issue_20727::IndexMut;
|
||||
}
|
33
src/test/rustdoc/issue-20727.rs
Normal file
33
src/test/rustdoc/issue-20727.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// aux-build:issue-20727.rs
|
||||
|
||||
extern crate issue_20727;
|
||||
|
||||
// @has issue_20727/trait.Deref.html
|
||||
pub trait Deref {
|
||||
// @has - '//*[@class="rust trait"]' 'trait Deref {'
|
||||
// @has - '//*[@class="rust trait"]' 'type Target: ?Sized;'
|
||||
type Target: ?Sized;
|
||||
|
||||
// @has - '//*[@class="rust trait"]' \
|
||||
// "fn deref<'a>(&'a self) -> &'a Self::Target;"
|
||||
fn deref<'a>(&'a self) -> &'a Self::Target;
|
||||
}
|
||||
|
||||
// @has issue_20727/reexport/trait.Deref.html
|
||||
pub mod reexport {
|
||||
// @has - '//*[@class="rust trait"]' 'trait Deref {'
|
||||
// @has - '//*[@class="rust trait"]' 'type Target: ?Sized;'
|
||||
// @has - '//*[@class="rust trait"]' \
|
||||
// "fn deref(&'a self) -> &'a Self::Target;"
|
||||
pub use issue_20727::Deref;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue