about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustdoc/clean/mod.rs57
-rw-r--r--src/test/rustdoc/auxiliary/normalize-assoc-item.rs12
-rw-r--r--src/test/rustdoc/normalize-assoc-item.rs63
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;