diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustdoc/clean/mod.rs | 57 | ||||
| -rw-r--r-- | src/test/rustdoc/auxiliary/normalize-assoc-item.rs | 12 | ||||
| -rw-r--r-- | src/test/rustdoc/normalize-assoc-item.rs | 63 |
3 files changed, 120 insertions, 12 deletions
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1667a92d757..b89fa1f7a01 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1290,6 +1290,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &DocContext<'_>) -> Type { hir::TyKind::Path(qpath) => qpath, _ => unreachable!(), }; + match qpath { hir::QPath::Resolved(None, ref path) => { if let Res::Def(DefKind::TyParam, did) = path.res { @@ -1393,6 +1394,12 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &DocContext<'_>) -> Type { resolve_type(cx, path.clean(cx), hir_id) } hir::QPath::Resolved(Some(ref qself), ref p) => { + // Try to normalize `<X as Y>::T` to a type + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + if let Some(normalized_value) = normalize(cx.tcx, ty) { + return normalized_value.clean(cx); + } + let segments = if p.is_global() { &p.segments[1..] } else { &p.segments }; let trait_segments = &segments[..segments.len() - 1]; let trait_path = self::Path { @@ -1410,18 +1417,12 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &DocContext<'_>) -> Type { } } hir::QPath::TypeRelative(ref qself, ref segment) => { - let mut res = Res::Err; - /* - let hir_ty = hir::Ty { - kind: hir::TyKind::Path((*qpath).clone()), - hir_id, - span, - }; - */ let ty = hir_ty_to_ty(cx.tcx, hir_ty); - if let ty::Projection(proj) = ty.kind() { - res = Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id); - } + let res = if let ty::Projection(proj) = ty.kind() { + Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id) + } else { + Res::Err + }; let trait_path = hir::Path { span, res, segments: &[] }; Type::QPath { name: segment.ident.name.clean(cx), @@ -1496,10 +1497,42 @@ impl Clean<Type> for hir::Ty<'_> { } } +/// Returns `None` if the type could not be normalized +fn normalize(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { + use crate::rustc_trait_selection::infer::TyCtxtInferExt; + use crate::rustc_trait_selection::traits::query::normalize::AtExt; + use rustc_middle::traits::ObligationCause; + use rustc_middle::ty::ParamEnv; + + // Try to normalize `<X as Y>::T` to a type + // FIXME: rustdoc won't be able to perform 'partial' normalization + // until this param env is actually correct + // 'partial': `<Vec<T> as IntoIterator>::IntoIter>` -> `vec::IntoIter<T>` + let param_env = ParamEnv::empty(); + let lifted = ty.lift_to_tcx(tcx).unwrap(); + let normalized = tcx.infer_ctxt().enter(|infcx| { + infcx + .at(&ObligationCause::dummy(), param_env) + .normalize(lifted) + .map(|resolved| infcx.resolve_vars_if_possible(resolved.value)) + }); + match normalized { + Ok(normalized_value) => { + debug!("resolved {:?} to {:?}", ty, normalized_value); + Some(normalized_value) + } + Err(err) => { + debug!("failed to resolve {:?}: {:?}", ty, err); + None + } + } +} + impl<'tcx> Clean<Type> for Ty<'tcx> { fn clean(&self, cx: &DocContext<'_>) -> Type { debug!("cleaning type: {:?}", self); - match *self.kind() { + let ty = normalize(cx.tcx, self.lift_to_tcx(cx.tcx).unwrap()).unwrap_or(self); + match *ty.kind() { ty::Never => Never, ty::Bool => Primitive(PrimitiveType::Bool), ty::Char => Primitive(PrimitiveType::Char), diff --git a/src/test/rustdoc/auxiliary/normalize-assoc-item.rs b/src/test/rustdoc/auxiliary/normalize-assoc-item.rs new file mode 100644 index 00000000000..fbd111c3035 --- /dev/null +++ b/src/test/rustdoc/auxiliary/normalize-assoc-item.rs @@ -0,0 +1,12 @@ +#![crate_name = "inner"] +pub trait MyTrait { + type Y; +} + +impl MyTrait for u32 { + type Y = i32; +} + +pub fn foo() -> <u32 as MyTrait>::Y { + 0 +} diff --git a/src/test/rustdoc/normalize-assoc-item.rs b/src/test/rustdoc/normalize-assoc-item.rs new file mode 100644 index 00000000000..829f446b7cc --- /dev/null +++ b/src/test/rustdoc/normalize-assoc-item.rs @@ -0,0 +1,63 @@ +// ignore-tidy-linelength +// aux-build:normalize-assoc-item.rs +// build-aux-docs + +pub trait Trait { + type X; +} + +impl Trait for usize { + type X = isize; +} + +// @has 'normalize_assoc_item/fn.f.html' '//pre[@class="rust fn"]' 'pub fn f() -> isize' +pub fn f() -> <usize as Trait>::X { + 0 +} + +pub struct S { + // @has 'normalize_assoc_item/struct.S.html' '//span[@id="structfield.box_me_up"]' 'box_me_up: Box<S>' + pub box_me_up: <S as Trait>::X, + // @has 'normalize_assoc_item/struct.S.html' '//span[@id="structfield.generic"]' 'generic: (usize, isize)' + pub generic: <Generic<usize> as Trait>::X, +} + +impl Trait for S { + type X = Box<S>; +} + +pub struct Generic<Inner>(Inner); + +impl<Inner: Trait> Trait for Generic<Inner> { + type X = (Inner, Inner::X); +} + +// These can't be normalized because they depend on a generic parameter. +// However the user can choose whether the text should be displayed as `Inner::X` or `<Inner as Trait>::X`. + +// @has 'normalize_assoc_item/struct.Unknown.html' '//pre[@class="rust struct"]' 'pub struct Unknown<Inner: Trait>(pub <Inner as Trait>::X);' +pub struct Unknown<Inner: Trait>(pub <Inner as Trait>::X); + +// @has 'normalize_assoc_item/struct.Unknown2.html' '//pre[@class="rust struct"]' 'pub struct Unknown2<Inner: Trait>(pub Inner::X);' +pub struct Unknown2<Inner: Trait>(pub Inner::X); + +trait Lifetimes<'a> { + type Y; +} + +impl<'a> Lifetimes<'a> for usize { + type Y = &'a isize; +} + +// @has 'normalize_assoc_item/fn.g.html' '//pre[@class="rust fn"]' "pub fn g() -> &isize" +pub fn g() -> <usize as Lifetimes<'static>>::Y { + &0 +} + +// @has 'normalize_assoc_item/constant.A.html' '//pre[@class="rust const"]' "pub const A: &isize" +pub const A: <usize as Lifetimes<'static>>::Y = &0; + +// test cross-crate re-exports +extern crate inner; +// @has 'normalize_assoc_item/fn.foo.html' '//pre[@class="rust fn"]' "pub fn foo() -> i32" +pub use inner::foo; |
