From 7f60ee0ccd90d41a978eb3c1c0cc83bceb330bc8 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 2 Oct 2020 15:10:49 -0400 Subject: [PATCH 1/4] Refactor clean_qpath into a separate function --- src/librustdoc/clean/mod.rs | 290 +++++++++++++++++++----------------- 1 file changed, 151 insertions(+), 139 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 8356e50f6374a..1667a92d75726 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1283,6 +1283,156 @@ impl Clean for ty::AssocItem { } } +fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &DocContext<'_>) -> Type { + use rustc_hir::GenericParamCount; + let hir::Ty { hir_id, span, ref kind } = *hir_ty; + let qpath = match kind { + hir::TyKind::Path(qpath) => qpath, + _ => unreachable!(), + }; + match qpath { + hir::QPath::Resolved(None, ref path) => { + if let Res::Def(DefKind::TyParam, did) = path.res { + if let Some(new_ty) = cx.ty_substs.borrow().get(&did).cloned() { + return new_ty; + } + if let Some(bounds) = cx.impl_trait_bounds.borrow_mut().remove(&did.into()) { + return ImplTrait(bounds); + } + } + + let mut alias = None; + if let Res::Def(DefKind::TyAlias, def_id) = path.res { + // Substitute private type aliases + if let Some(def_id) = def_id.as_local() { + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id); + if !cx.renderinfo.borrow().access_levels.is_exported(def_id.to_def_id()) { + alias = Some(&cx.tcx.hir().expect_item(hir_id).kind); + } + } + }; + + if let Some(&hir::ItemKind::TyAlias(ref ty, ref generics)) = alias { + let provided_params = &path.segments.last().expect("segments were empty"); + let mut ty_substs = FxHashMap::default(); + let mut lt_substs = FxHashMap::default(); + let mut ct_substs = FxHashMap::default(); + let generic_args = provided_params.generic_args(); + { + let mut indices: GenericParamCount = Default::default(); + for param in generics.params.iter() { + match param.kind { + hir::GenericParamKind::Lifetime { .. } => { + let mut j = 0; + let lifetime = generic_args.args.iter().find_map(|arg| match arg { + hir::GenericArg::Lifetime(lt) => { + if indices.lifetimes == j { + return Some(lt); + } + j += 1; + None + } + _ => None, + }); + if let Some(lt) = lifetime.cloned() { + let lt_def_id = cx.tcx.hir().local_def_id(param.hir_id); + let cleaned = if !lt.is_elided() { + lt.clean(cx) + } else { + self::types::Lifetime::elided() + }; + lt_substs.insert(lt_def_id.to_def_id(), cleaned); + } + indices.lifetimes += 1; + } + hir::GenericParamKind::Type { ref default, .. } => { + let ty_param_def_id = cx.tcx.hir().local_def_id(param.hir_id); + let mut j = 0; + let type_ = generic_args.args.iter().find_map(|arg| match arg { + hir::GenericArg::Type(ty) => { + if indices.types == j { + return Some(ty); + } + j += 1; + None + } + _ => None, + }); + if let Some(ty) = type_ { + ty_substs.insert(ty_param_def_id.to_def_id(), ty.clean(cx)); + } else if let Some(default) = *default { + ty_substs + .insert(ty_param_def_id.to_def_id(), default.clean(cx)); + } + indices.types += 1; + } + hir::GenericParamKind::Const { .. } => { + let const_param_def_id = cx.tcx.hir().local_def_id(param.hir_id); + let mut j = 0; + let const_ = generic_args.args.iter().find_map(|arg| match arg { + hir::GenericArg::Const(ct) => { + if indices.consts == j { + return Some(ct); + } + j += 1; + None + } + _ => None, + }); + if let Some(ct) = const_ { + ct_substs.insert(const_param_def_id.to_def_id(), ct.clean(cx)); + } + // FIXME(const_generics:defaults) + indices.consts += 1; + } + } + } + } + return cx.enter_alias(ty_substs, lt_substs, ct_substs, || ty.clean(cx)); + } + resolve_type(cx, path.clean(cx), hir_id) + } + hir::QPath::Resolved(Some(ref qself), ref p) => { + let segments = if p.is_global() { &p.segments[1..] } else { &p.segments }; + let trait_segments = &segments[..segments.len() - 1]; + let trait_path = self::Path { + global: p.is_global(), + res: Res::Def( + DefKind::Trait, + cx.tcx.associated_item(p.res.def_id()).container.id(), + ), + segments: trait_segments.clean(cx), + }; + Type::QPath { + name: p.segments.last().expect("segments were empty").ident.name.clean(cx), + self_type: box qself.clean(cx), + trait_: box resolve_type(cx, trait_path, hir_id), + } + } + 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 trait_path = hir::Path { span, res, segments: &[] }; + Type::QPath { + name: segment.ident.name.clean(cx), + self_type: box qself.clean(cx), + trait_: box resolve_type(cx, trait_path.clean(cx), hir_id), + } + } + hir::QPath::LangItem(..) => bug!("clean: requiring documentation of lang item"), + } +} + impl Clean for hir::Ty<'_> { fn clean(&self, cx: &DocContext<'_>) -> Type { use rustc_hir::*; @@ -1318,145 +1468,7 @@ impl Clean for hir::Ty<'_> { unreachable!() } } - TyKind::Path(hir::QPath::Resolved(None, ref path)) => { - if let Res::Def(DefKind::TyParam, did) = path.res { - if let Some(new_ty) = cx.ty_substs.borrow().get(&did).cloned() { - return new_ty; - } - if let Some(bounds) = cx.impl_trait_bounds.borrow_mut().remove(&did.into()) { - return ImplTrait(bounds); - } - } - - let mut alias = None; - if let Res::Def(DefKind::TyAlias, def_id) = path.res { - // Substitute private type aliases - if let Some(def_id) = def_id.as_local() { - let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id); - if !cx.renderinfo.borrow().access_levels.is_exported(def_id.to_def_id()) { - alias = Some(&cx.tcx.hir().expect_item(hir_id).kind); - } - } - }; - - if let Some(&hir::ItemKind::TyAlias(ref ty, ref generics)) = alias { - let provided_params = &path.segments.last().expect("segments were empty"); - let mut ty_substs = FxHashMap::default(); - let mut lt_substs = FxHashMap::default(); - let mut ct_substs = FxHashMap::default(); - let generic_args = provided_params.generic_args(); - { - let mut indices: GenericParamCount = Default::default(); - for param in generics.params.iter() { - match param.kind { - hir::GenericParamKind::Lifetime { .. } => { - let mut j = 0; - let lifetime = - generic_args.args.iter().find_map(|arg| match arg { - hir::GenericArg::Lifetime(lt) => { - if indices.lifetimes == j { - return Some(lt); - } - j += 1; - None - } - _ => None, - }); - if let Some(lt) = lifetime.cloned() { - let lt_def_id = cx.tcx.hir().local_def_id(param.hir_id); - let cleaned = if !lt.is_elided() { - lt.clean(cx) - } else { - self::types::Lifetime::elided() - }; - lt_substs.insert(lt_def_id.to_def_id(), cleaned); - } - indices.lifetimes += 1; - } - hir::GenericParamKind::Type { ref default, .. } => { - let ty_param_def_id = cx.tcx.hir().local_def_id(param.hir_id); - let mut j = 0; - let type_ = - generic_args.args.iter().find_map(|arg| match arg { - hir::GenericArg::Type(ty) => { - if indices.types == j { - return Some(ty); - } - j += 1; - None - } - _ => None, - }); - if let Some(ty) = type_ { - ty_substs.insert(ty_param_def_id.to_def_id(), ty.clean(cx)); - } else if let Some(default) = *default { - ty_substs - .insert(ty_param_def_id.to_def_id(), default.clean(cx)); - } - indices.types += 1; - } - hir::GenericParamKind::Const { .. } => { - let const_param_def_id = - cx.tcx.hir().local_def_id(param.hir_id); - let mut j = 0; - let const_ = - generic_args.args.iter().find_map(|arg| match arg { - hir::GenericArg::Const(ct) => { - if indices.consts == j { - return Some(ct); - } - j += 1; - None - } - _ => None, - }); - if let Some(ct) = const_ { - ct_substs - .insert(const_param_def_id.to_def_id(), ct.clean(cx)); - } - // FIXME(const_generics:defaults) - indices.consts += 1; - } - } - } - } - return cx.enter_alias(ty_substs, lt_substs, ct_substs, || ty.clean(cx)); - } - resolve_type(cx, path.clean(cx), self.hir_id) - } - TyKind::Path(hir::QPath::Resolved(Some(ref qself), ref p)) => { - let segments = if p.is_global() { &p.segments[1..] } else { &p.segments }; - let trait_segments = &segments[..segments.len() - 1]; - let trait_path = self::Path { - global: p.is_global(), - res: Res::Def( - DefKind::Trait, - cx.tcx.associated_item(p.res.def_id()).container.id(), - ), - segments: trait_segments.clean(cx), - }; - Type::QPath { - name: p.segments.last().expect("segments were empty").ident.name.clean(cx), - self_type: box qself.clean(cx), - trait_: box resolve_type(cx, trait_path, self.hir_id), - } - } - TyKind::Path(hir::QPath::TypeRelative(ref qself, ref segment)) => { - let mut res = Res::Err; - let ty = hir_ty_to_ty(cx.tcx, self); - if let ty::Projection(proj) = ty.kind() { - res = Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id); - } - let trait_path = hir::Path { span: self.span, res, segments: &[] }; - Type::QPath { - name: segment.ident.name.clean(cx), - self_type: box qself.clean(cx), - trait_: box resolve_type(cx, trait_path.clean(cx), self.hir_id), - } - } - TyKind::Path(hir::QPath::LangItem(..)) => { - bug!("clean: requiring documentation of lang item") - } + TyKind::Path(_) => clean_qpath(&self, cx), TyKind::TraitObject(ref bounds, ref lifetime) => { match bounds[0].clean(cx).trait_ { ResolvedPath { path, param_names: None, did, is_generic } => { From a192e5d9c2e90798ffcd0bc79527cdc4ad52d949 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 2 Oct 2020 16:21:43 -0400 Subject: [PATCH 2/4] Normalize `::T` for rustdoc - Only run for `QPath::Resolved` with `Some` self parameter (`::T`) - Fall back to the previous behavior if the path can't be resolved - Show what the behavior is if the type can't be normalized - Run `resolve_vars_if_possible` It's not clear whether or not this is necessary. See https://github.com/rust-lang/rust/pull/77616 for more context. - Add a test for cross-crate re-exports - Use the same code for both `hir::Ty` and `Ty` --- src/librustdoc/clean/mod.rs | 57 +++++++++++++---- .../rustdoc/auxiliary/normalize-assoc-item.rs | 12 ++++ src/test/rustdoc/normalize-assoc-item.rs | 63 +++++++++++++++++++ 3 files changed, 120 insertions(+), 12 deletions(-) create mode 100644 src/test/rustdoc/auxiliary/normalize-assoc-item.rs create mode 100644 src/test/rustdoc/normalize-assoc-item.rs diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1667a92d75726..b89fa1f7a0187 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 `::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 for hir::Ty<'_> { } } +/// Returns `None` if the type could not be normalized +fn normalize(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option> { + 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 `::T` to a type + // FIXME: rustdoc won't be able to perform 'partial' normalization + // until this param env is actually correct + // 'partial': ` as IntoIterator>::IntoIter>` -> `vec::IntoIter` + 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 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 0000000000000..fbd111c303566 --- /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() -> ::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 0000000000000..829f446b7cc6d --- /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() -> ::X { + 0 +} + +pub struct S { + // @has 'normalize_assoc_item/struct.S.html' '//span[@id="structfield.box_me_up"]' 'box_me_up: Box' + pub box_me_up: ::X, + // @has 'normalize_assoc_item/struct.S.html' '//span[@id="structfield.generic"]' 'generic: (usize, isize)' + pub generic: as Trait>::X, +} + +impl Trait for S { + type X = Box; +} + +pub struct Generic(Inner); + +impl Trait for Generic { + 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 `::X`. + +// @has 'normalize_assoc_item/struct.Unknown.html' '//pre[@class="rust struct"]' 'pub struct Unknown(pub ::X);' +pub struct Unknown(pub ::X); + +// @has 'normalize_assoc_item/struct.Unknown2.html' '//pre[@class="rust struct"]' 'pub struct Unknown2(pub Inner::X);' +pub struct Unknown2(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() -> >::Y { + &0 +} + +// @has 'normalize_assoc_item/constant.A.html' '//pre[@class="rust const"]' "pub const A: &isize" +pub const A: >::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; From 6278daac540ad4ef69ca0828a1a660671da539b9 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sun, 18 Oct 2020 11:27:16 -0400 Subject: [PATCH 3/4] Track `ParamEnv`s properly This uses the same `with_param_env` pattern that late lints use. Thanks to all the doctree refactors, this was very easy to add. --- src/librustdoc/clean/mod.rs | 318 ++++++++++++----------- src/librustdoc/core.rs | 16 +- src/test/rustdoc/normalize-assoc-item.rs | 7 +- 3 files changed, 186 insertions(+), 155 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b89fa1f7a0187..250922e5aa907 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1067,63 +1067,68 @@ impl Clean for hir::def::DefKind { impl Clean for hir::TraitItem<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { let local_did = cx.tcx.hir().local_def_id(self.hir_id).to_def_id(); - let inner = match self.kind { - hir::TraitItemKind::Const(ref ty, default) => { - AssocConstItem(ty.clean(cx), default.map(|e| print_const_expr(cx, e))) - } - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - let mut m = (sig, &self.generics, body).clean(cx); - if m.header.constness == hir::Constness::Const - && is_unstable_const_fn(cx.tcx, local_did).is_some() - { - m.header.constness = hir::Constness::NotConst; + cx.with_param_env(local_did, || { + let inner = match self.kind { + hir::TraitItemKind::Const(ref ty, default) => { + AssocConstItem(ty.clean(cx), default.map(|e| print_const_expr(cx, e))) } - MethodItem(m, None) - } - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(ref names)) => { - let (generics, decl) = enter_impl_trait(cx, || { - (self.generics.clean(cx), (&*sig.decl, &names[..]).clean(cx)) - }); - let (all_types, ret_types) = get_all_types(&generics, &decl, cx); - let mut t = Function { header: sig.header, decl, generics, all_types, ret_types }; - if t.header.constness == hir::Constness::Const - && is_unstable_const_fn(cx.tcx, local_did).is_some() - { - t.header.constness = hir::Constness::NotConst; + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { + let mut m = (sig, &self.generics, body).clean(cx); + if m.header.constness == hir::Constness::Const + && is_unstable_const_fn(cx.tcx, local_did).is_some() + { + m.header.constness = hir::Constness::NotConst; + } + MethodItem(m, None) } - TyMethodItem(t) - } - hir::TraitItemKind::Type(ref bounds, ref default) => { - AssocTypeItem(bounds.clean(cx), default.clean(cx)) - } - }; - Item::from_def_id_and_parts(local_did, Some(self.ident.name), inner, cx) + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(ref names)) => { + let (generics, decl) = enter_impl_trait(cx, || { + (self.generics.clean(cx), (&*sig.decl, &names[..]).clean(cx)) + }); + let (all_types, ret_types) = get_all_types(&generics, &decl, cx); + let mut t = + Function { header: sig.header, decl, generics, all_types, ret_types }; + if t.header.constness == hir::Constness::Const + && is_unstable_const_fn(cx.tcx, local_did).is_some() + { + t.header.constness = hir::Constness::NotConst; + } + TyMethodItem(t) + } + hir::TraitItemKind::Type(ref bounds, ref default) => { + AssocTypeItem(bounds.clean(cx), default.clean(cx)) + } + }; + Item::from_def_id_and_parts(local_did, Some(self.ident.name), inner, cx) + }) } } impl Clean for hir::ImplItem<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { let local_did = cx.tcx.hir().local_def_id(self.hir_id).to_def_id(); - let inner = match self.kind { - hir::ImplItemKind::Const(ref ty, expr) => { - AssocConstItem(ty.clean(cx), Some(print_const_expr(cx, expr))) - } - hir::ImplItemKind::Fn(ref sig, body) => { - let mut m = (sig, &self.generics, body).clean(cx); - if m.header.constness == hir::Constness::Const - && is_unstable_const_fn(cx.tcx, local_did).is_some() - { - m.header.constness = hir::Constness::NotConst; + cx.with_param_env(local_did, || { + let inner = match self.kind { + hir::ImplItemKind::Const(ref ty, expr) => { + AssocConstItem(ty.clean(cx), Some(print_const_expr(cx, expr))) } - MethodItem(m, Some(self.defaultness)) - } - hir::ImplItemKind::TyAlias(ref ty) => { - let type_ = ty.clean(cx); - let item_type = type_.def_id().and_then(|did| inline::build_ty(cx, did)); - TypedefItem(Typedef { type_, generics: Generics::default(), item_type }, true) - } - }; - Item::from_def_id_and_parts(local_did, Some(self.ident.name), inner, cx) + hir::ImplItemKind::Fn(ref sig, body) => { + let mut m = (sig, &self.generics, body).clean(cx); + if m.header.constness == hir::Constness::Const + && is_unstable_const_fn(cx.tcx, local_did).is_some() + { + m.header.constness = hir::Constness::NotConst; + } + MethodItem(m, Some(self.defaultness)) + } + hir::ImplItemKind::TyAlias(ref ty) => { + let type_ = ty.clean(cx); + let item_type = type_.def_id().and_then(|did| inline::build_ty(cx, did)); + TypedefItem(Typedef { type_, generics: Generics::default(), item_type }, true) + } + }; + Item::from_def_id_and_parts(local_did, Some(self.ident.name), inner, cx) + }) } } @@ -1396,7 +1401,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &DocContext<'_>) -> Type { hir::QPath::Resolved(Some(ref qself), ref p) => { // Try to normalize `::T` to a type let ty = hir_ty_to_ty(cx.tcx, hir_ty); - if let Some(normalized_value) = normalize(cx.tcx, ty) { + if let Some(normalized_value) = normalize(cx, ty) { return normalized_value.clean(cx); } @@ -1498,21 +1503,16 @@ impl Clean for hir::Ty<'_> { } /// Returns `None` if the type could not be normalized -fn normalize(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option> { +fn normalize(cx: &DocContext<'tcx>, ty: Ty<'tcx>) -> Option> { 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 `::T` to a type - // FIXME: rustdoc won't be able to perform 'partial' normalization - // until this param env is actually correct - // 'partial': ` as IntoIterator>::IntoIter>` -> `vec::IntoIter` - let param_env = ParamEnv::empty(); - let lifted = ty.lift_to_tcx(tcx).unwrap(); - let normalized = tcx.infer_ctxt().enter(|infcx| { + let lifted = ty.lift_to_tcx(cx.tcx).unwrap(); + let normalized = cx.tcx.infer_ctxt().enter(|infcx| { infcx - .at(&ObligationCause::dummy(), param_env) + .at(&ObligationCause::dummy(), cx.param_env.get()) .normalize(lifted) .map(|resolved| infcx.resolve_vars_if_possible(resolved.value)) }); @@ -1531,7 +1531,7 @@ fn normalize(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option> { impl<'tcx> Clean for Ty<'tcx> { fn clean(&self, cx: &DocContext<'_>) -> Type { debug!("cleaning type: {:?}", self); - let ty = normalize(cx.tcx, self.lift_to_tcx(cx.tcx).unwrap()).unwrap_or(self); + let ty = normalize(cx, self.lift_to_tcx(cx.tcx).unwrap()).unwrap_or(self); match *ty.kind() { ty::Never => Never, ty::Bool => Primitive(PrimitiveType::Bool), @@ -1984,77 +1984,81 @@ impl Clean> for (&hir::Item<'_>, Option) { Some(ident) => ident.name, None => cx.tcx.hir().name(item.hir_id), }; - let kind = match item.kind { - ItemKind::Static(ty, mutability, body_id) => StaticItem(Static { - type_: ty.clean(cx), - mutability, - expr: print_const_expr(cx, body_id), - }), - ItemKind::Const(ty, body_id) => ConstantItem(Constant { - type_: ty.clean(cx), - expr: print_const_expr(cx, body_id), - value: print_evaluated_const(cx, def_id), - is_literal: is_literal_expr(cx, body_id.hir_id), - }), - ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy { - bounds: ty.bounds.clean(cx), - generics: ty.generics.clean(cx), - }), - ItemKind::TyAlias(ty, ref generics) => { - let rustdoc_ty = ty.clean(cx); - let item_type = rustdoc_ty.def_id().and_then(|did| inline::build_ty(cx, did)); - TypedefItem( - Typedef { type_: rustdoc_ty, generics: generics.clean(cx), item_type }, - false, - ) - } - ItemKind::Enum(ref def, ref generics) => EnumItem(Enum { - variants: def.variants.iter().map(|v| v.clean(cx)).collect(), - generics: generics.clean(cx), - variants_stripped: false, - }), - ItemKind::TraitAlias(ref generics, bounds) => TraitAliasItem(TraitAlias { - generics: generics.clean(cx), - bounds: bounds.clean(cx), - }), - ItemKind::Union(ref variant_data, ref generics) => UnionItem(Union { - struct_type: doctree::struct_type_from_def(&variant_data), - generics: generics.clean(cx), - fields: variant_data.fields().clean(cx), - fields_stripped: false, - }), - ItemKind::Struct(ref variant_data, ref generics) => StructItem(Struct { - struct_type: doctree::struct_type_from_def(&variant_data), - generics: generics.clean(cx), - fields: variant_data.fields().clean(cx), - fields_stripped: false, - }), - ItemKind::Impl { .. } => return clean_impl(item, cx), - // proc macros can have a name set by attributes - ItemKind::Fn(ref sig, ref generics, body_id) => { - clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx) - } - hir::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref item_ids) => { - let items = - item_ids.iter().map(|ti| cx.tcx.hir().trait_item(ti.id).clean(cx)).collect(); - let attrs = item.attrs.clean(cx); - let is_spotlight = attrs.has_doc_flag(sym::spotlight); - TraitItem(Trait { - unsafety, - items, + cx.with_param_env(def_id, || { + let kind = match item.kind { + ItemKind::Static(ty, mutability, body_id) => StaticItem(Static { + type_: ty.clean(cx), + mutability, + expr: print_const_expr(cx, body_id), + }), + ItemKind::Const(ty, body_id) => ConstantItem(Constant { + type_: ty.clean(cx), + expr: print_const_expr(cx, body_id), + value: print_evaluated_const(cx, def_id), + is_literal: is_literal_expr(cx, body_id.hir_id), + }), + ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy { + bounds: ty.bounds.clean(cx), + generics: ty.generics.clean(cx), + }), + ItemKind::TyAlias(ty, ref generics) => { + let rustdoc_ty = ty.clean(cx); + let item_type = rustdoc_ty.def_id().and_then(|did| inline::build_ty(cx, did)); + TypedefItem( + Typedef { type_: rustdoc_ty, generics: generics.clean(cx), item_type }, + false, + ) + } + ItemKind::Enum(ref def, ref generics) => EnumItem(Enum { + variants: def.variants.iter().map(|v| v.clean(cx)).collect(), + generics: generics.clean(cx), + variants_stripped: false, + }), + ItemKind::TraitAlias(ref generics, bounds) => TraitAliasItem(TraitAlias { generics: generics.clean(cx), bounds: bounds.clean(cx), - is_spotlight, - is_auto: is_auto.clean(cx), - }) - } - ItemKind::ExternCrate(orig_name) => { - return clean_extern_crate(item, name, orig_name, cx); - } - _ => unreachable!("not yet converted"), - }; + }), + ItemKind::Union(ref variant_data, ref generics) => UnionItem(Union { + struct_type: doctree::struct_type_from_def(&variant_data), + generics: generics.clean(cx), + fields: variant_data.fields().clean(cx), + fields_stripped: false, + }), + ItemKind::Struct(ref variant_data, ref generics) => StructItem(Struct { + struct_type: doctree::struct_type_from_def(&variant_data), + generics: generics.clean(cx), + fields: variant_data.fields().clean(cx), + fields_stripped: false, + }), + ItemKind::Impl { .. } => return clean_impl(item, cx), + // proc macros can have a name set by attributes + ItemKind::Fn(ref sig, ref generics, body_id) => { + clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx) + } + hir::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref item_ids) => { + let items = item_ids + .iter() + .map(|ti| cx.tcx.hir().trait_item(ti.id).clean(cx)) + .collect(); + let attrs = item.attrs.clean(cx); + let is_spotlight = attrs.has_doc_flag(sym::spotlight); + TraitItem(Trait { + unsafety, + items, + generics: generics.clean(cx), + bounds: bounds.clean(cx), + is_spotlight, + is_auto: is_auto.clean(cx), + }) + } + ItemKind::ExternCrate(orig_name) => { + return clean_extern_crate(item, name, orig_name, cx); + } + _ => unreachable!("not yet converted"), + }; - vec![Item::from_def_id_and_parts(def_id, Some(name), kind, cx)] + vec![Item::from_def_id_and_parts(def_id, Some(name), kind, cx)] + }) } } @@ -2272,32 +2276,42 @@ impl Clean> for doctree::Import<'_> { impl Clean for (&hir::ForeignItem<'_>, Option) { fn clean(&self, cx: &DocContext<'_>) -> Item { let (item, renamed) = self; - let kind = match item.kind { - hir::ForeignItemKind::Fn(ref decl, ref names, ref generics) => { - let abi = cx.tcx.hir().get_foreign_abi(item.hir_id); - let (generics, decl) = - enter_impl_trait(cx, || (generics.clean(cx), (&**decl, &names[..]).clean(cx))); - let (all_types, ret_types) = get_all_types(&generics, &decl, cx); - ForeignFunctionItem(Function { - decl, - generics, - header: hir::FnHeader { - unsafety: hir::Unsafety::Unsafe, - abi, - constness: hir::Constness::NotConst, - asyncness: hir::IsAsync::NotAsync, - }, - all_types, - ret_types, - }) - } - hir::ForeignItemKind::Static(ref ty, mutability) => { - ForeignStaticItem(Static { type_: ty.clean(cx), mutability, expr: String::new() }) - } - hir::ForeignItemKind::Type => ForeignTypeItem, - }; + cx.with_param_env(cx.tcx.hir().local_def_id(item.hir_id).to_def_id(), || { + let kind = match item.kind { + hir::ForeignItemKind::Fn(ref decl, ref names, ref generics) => { + let abi = cx.tcx.hir().get_foreign_abi(item.hir_id); + let (generics, decl) = enter_impl_trait(cx, || { + (generics.clean(cx), (&**decl, &names[..]).clean(cx)) + }); + let (all_types, ret_types) = get_all_types(&generics, &decl, cx); + ForeignFunctionItem(Function { + decl, + generics, + header: hir::FnHeader { + unsafety: hir::Unsafety::Unsafe, + abi, + constness: hir::Constness::NotConst, + asyncness: hir::IsAsync::NotAsync, + }, + all_types, + ret_types, + }) + } + hir::ForeignItemKind::Static(ref ty, mutability) => ForeignStaticItem(Static { + type_: ty.clean(cx), + mutability, + expr: String::new(), + }), + hir::ForeignItemKind::Type => ForeignTypeItem, + }; - Item::from_hir_id_and_parts(item.hir_id, Some(renamed.unwrap_or(item.ident).name), kind, cx) + Item::from_hir_id_and_parts( + item.hir_id, + Some(renamed.unwrap_or(item.ident).name), + kind, + cx, + ) + }) } } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index b7cc0f1945911..b63acdc114e89 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -15,7 +15,7 @@ use rustc_interface::interface; use rustc_middle::hir::map::Map; use rustc_middle::middle::cstore::CrateStore; use rustc_middle::middle::privacy::AccessLevels; -use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; use rustc_resolve as resolve; use rustc_session::config::{self, CrateType, ErrorOutputType}; use rustc_session::lint; @@ -25,7 +25,7 @@ use rustc_span::source_map; use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::mem; use std::rc::Rc; @@ -42,6 +42,10 @@ crate type ExternalPaths = FxHashMap, clean::TypeKind)>; crate struct DocContext<'tcx> { crate tcx: TyCtxt<'tcx>, crate resolver: Rc>, + /// Used for normalization. + /// + /// Most of this logic is copied from rustc_lint::late. + crate param_env: Cell>, /// Later on moved into `CACHE_KEY` crate renderinfo: RefCell, /// Later on moved through `clean::Crate` into `CACHE_KEY` @@ -79,6 +83,13 @@ impl<'tcx> DocContext<'tcx> { &self.tcx.sess } + crate fn with_param_env T>(&self, def_id: DefId, f: F) -> T { + let old_param_env = self.param_env.replace(self.tcx.param_env(def_id)); + let ret = f(); + self.param_env.set(old_param_env); + ret + } + crate fn enter_resolver(&self, f: F) -> R where F: FnOnce(&mut resolve::Resolver<'_>) -> R, @@ -524,6 +535,7 @@ fn run_global_ctxt( let mut ctxt = DocContext { tcx, resolver, + param_env: Cell::new(ParamEnv::empty()), external_traits: Default::default(), active_extern_traits: Default::default(), renderinfo: RefCell::new(renderinfo), diff --git a/src/test/rustdoc/normalize-assoc-item.rs b/src/test/rustdoc/normalize-assoc-item.rs index 829f446b7cc6d..137fd354a8743 100644 --- a/src/test/rustdoc/normalize-assoc-item.rs +++ b/src/test/rustdoc/normalize-assoc-item.rs @@ -16,7 +16,7 @@ pub fn f() -> ::X { } pub struct S { - // @has 'normalize_assoc_item/struct.S.html' '//span[@id="structfield.box_me_up"]' 'box_me_up: Box' + // @has 'normalize_assoc_item/struct.S.html' '//span[@id="structfield.box_me_up"]' 'box_me_up: Box' pub box_me_up: ::X, // @has 'normalize_assoc_item/struct.S.html' '//span[@id="structfield.generic"]' 'generic: (usize, isize)' pub generic: as Trait>::X, @@ -61,3 +61,8 @@ pub const A: >::Y = &0; extern crate inner; // @has 'normalize_assoc_item/fn.foo.html' '//pre[@class="rust fn"]' "pub fn foo() -> i32" pub use inner::foo; + +// @has 'normalize_assoc_item/fn.h.html' '//pre[@class="rust fn"]' "pub fn h() -> IntoIter" +pub fn h() -> as IntoIterator>::IntoIter { + vec![].into_iter() +} From 277bdbc0ed0f1f87e8d340233d7f485fbbe8cc66 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 26 Nov 2020 07:59:51 -0500 Subject: [PATCH 4/4] Remove redundant `lift_to_tcx` ... and fix some fuzzy wording in the debug logging. --- src/librustdoc/clean/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 250922e5aa907..ea34085823f6e 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1503,7 +1503,7 @@ impl Clean for hir::Ty<'_> { } /// Returns `None` if the type could not be normalized -fn normalize(cx: &DocContext<'tcx>, ty: Ty<'tcx>) -> Option> { +fn normalize(cx: &DocContext<'tcx>, ty: Ty<'_>) -> Option> { use crate::rustc_trait_selection::infer::TyCtxtInferExt; use crate::rustc_trait_selection::traits::query::normalize::AtExt; use rustc_middle::traits::ObligationCause; @@ -1518,11 +1518,11 @@ fn normalize(cx: &DocContext<'tcx>, ty: Ty<'tcx>) -> Option> { }); match normalized { Ok(normalized_value) => { - debug!("resolved {:?} to {:?}", ty, normalized_value); + debug!("normalized {:?} to {:?}", ty, normalized_value); Some(normalized_value) } Err(err) => { - debug!("failed to resolve {:?}: {:?}", ty, err); + debug!("failed to normalize {:?}: {:?}", ty, err); None } } @@ -1531,7 +1531,7 @@ fn normalize(cx: &DocContext<'tcx>, ty: Ty<'tcx>) -> Option> { impl<'tcx> Clean for Ty<'tcx> { fn clean(&self, cx: &DocContext<'_>) -> Type { debug!("cleaning type: {:?}", self); - let ty = normalize(cx, self.lift_to_tcx(cx.tcx).unwrap()).unwrap_or(self); + let ty = normalize(cx, self).unwrap_or(self); match *ty.kind() { ty::Never => Never, ty::Bool => Primitive(PrimitiveType::Bool),