From 6278daac540ad4ef69ca0828a1a660671da539b9 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sun, 18 Oct 2020 11:27:16 -0400 Subject: [PATCH] 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() +}