diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:11:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:11:28 +0000 |
commit | 94a0819fe3a0d679c3042a77bfe6a2afc505daea (patch) | |
tree | 2b827afe6a05f3538db3f7803a88c4587fe85648 /src/librustdoc/clean | |
parent | Adding upstream version 1.64.0+dfsg1. (diff) | |
download | rustc-94a0819fe3a0d679c3042a77bfe6a2afc505daea.tar.xz rustc-94a0819fe3a0d679c3042a77bfe6a2afc505daea.zip |
Adding upstream version 1.66.0+dfsg1.upstream/1.66.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/librustdoc/clean')
-rw-r--r-- | src/librustdoc/clean/auto_trait.rs | 35 | ||||
-rw-r--r-- | src/librustdoc/clean/blanket_impl.rs | 209 | ||||
-rw-r--r-- | src/librustdoc/clean/inline.rs | 108 | ||||
-rw-r--r-- | src/librustdoc/clean/mod.rs | 1553 | ||||
-rw-r--r-- | src/librustdoc/clean/simplify.rs | 60 | ||||
-rw-r--r-- | src/librustdoc/clean/types.rs | 190 | ||||
-rw-r--r-- | src/librustdoc/clean/utils.rs | 94 |
7 files changed, 1208 insertions, 1041 deletions
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index af33c1a6a..764a6d3aa 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -123,7 +123,7 @@ where kind: Box::new(ImplItem(Box::new(Impl { unsafety: hir::Unsafety::Normal, generics: new_generics, - trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, &[])), + trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, ThinVec::new())), for_: clean_middle_ty(ty, self.cx, None), items: Vec::new(), polarity, @@ -148,7 +148,7 @@ where }) .collect(); // We are only interested in case the type *doesn't* implement the Sized trait. - if !ty.is_sized(tcx.at(rustc_span::DUMMY_SP), param_env) { + if !ty.is_sized(tcx, param_env) { // In case `#![no_core]` is used, `sized_trait` returns nothing. if let Some(item) = tcx.lang_items().sized_trait().and_then(|sized_trait_did| { self.generate_for_trait(ty, sized_trait_did, param_env, item_def_id, &f, true) @@ -475,8 +475,14 @@ where let mut ty_to_fn: FxHashMap<Type, (PolyTrait, Option<Type>)> = Default::default(); + // FIXME: This code shares much of the logic found in `clean_ty_generics` and + // `simplify::where_clause`. Consider deduplicating it to avoid diverging + // implementations. + // Further, the code below does not merge (partially re-sugared) bounds like + // `Tr<A = T>` & `Tr<B = U>` and it does not render higher-ranked parameters + // originating from equality predicates. for p in clean_where_predicates { - let (orig_p, p) = (p, p.clean(self.cx)); + let (orig_p, p) = (p, clean_predicate(p, self.cx)); if p.is_none() { continue; } @@ -525,8 +531,8 @@ where GenericBound::TraitBound(ref mut p, _) => { // Insert regions into the for_generics hash map first, to ensure // that we don't end up with duplicate bounds (e.g., for<'b, 'b>) - for_generics.extend(p.generic_params.clone()); - p.generic_params = for_generics.into_iter().collect(); + for_generics.extend(p.generic_params.drain(..)); + p.generic_params.extend(for_generics); self.is_fn_trait(&p.trait_) } _ => false, @@ -549,15 +555,17 @@ where WherePredicate::RegionPredicate { lifetime, bounds } => { lifetime_to_bounds.entry(lifetime).or_default().extend(bounds); } - WherePredicate::EqPredicate { lhs, rhs } => { - match lhs { - Type::QPath { ref assoc, ref self_type, ref trait_, .. } => { + WherePredicate::EqPredicate { lhs, rhs, bound_params } => { + match *lhs { + Type::QPath(box QPathData { + ref assoc, ref self_type, ref trait_, .. + }) => { let ty = &*self_type; let mut new_trait = trait_.clone(); if self.is_fn_trait(trait_) && assoc.name == sym::Output { ty_to_fn - .entry(*ty.clone()) + .entry(ty.clone()) .and_modify(|e| { *e = (e.0.clone(), Some(rhs.ty().unwrap().clone())) }) @@ -582,21 +590,22 @@ where // to 'T: Iterator<Item=u8>' GenericArgs::AngleBracketed { ref mut bindings, .. } => { bindings.push(TypeBinding { - assoc: *assoc.clone(), - kind: TypeBindingKind::Equality { term: rhs }, + assoc: assoc.clone(), + kind: TypeBindingKind::Equality { term: *rhs }, }); } GenericArgs::Parenthesized { .. } => { existing_predicates.push(WherePredicate::EqPredicate { lhs: lhs.clone(), rhs, + bound_params, }); continue; // If something other than a Fn ends up // with parentheses, leave it alone } } - let bounds = ty_to_bounds.entry(*ty.clone()).or_default(); + let bounds = ty_to_bounds.entry(ty.clone()).or_default(); bounds.insert(GenericBound::TraitBound( PolyTrait { trait_: new_trait, generic_params: Vec::new() }, @@ -613,7 +622,7 @@ where )); // Avoid creating any new duplicate bounds later in the outer // loop - ty_to_traits.entry(*ty.clone()).or_default().insert(trait_.clone()); + ty_to_traits.entry(ty.clone()).or_default().insert(trait_.clone()); } _ => panic!("Unexpected LHS {:?} for {:?}", lhs, item_def_id), } diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 01dd95e6e..8b63c3db3 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -2,7 +2,6 @@ use crate::rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtE use rustc_hir as hir; use rustc_infer::infer::{InferOk, TyCtxtInferExt}; use rustc_infer::traits; -use rustc_middle::ty::subst::Subst; use rustc_middle::ty::ToPredicate; use rustc_span::DUMMY_SP; @@ -14,122 +13,124 @@ pub(crate) struct BlanketImplFinder<'a, 'tcx> { impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { pub(crate) fn get_blanket_impls(&mut self, item_def_id: DefId) -> Vec<Item> { - let param_env = self.cx.tcx.param_env(item_def_id); - let ty = self.cx.tcx.bound_type_of(item_def_id); + let cx = &mut self.cx; + let param_env = cx.tcx.param_env(item_def_id); + let ty = cx.tcx.bound_type_of(item_def_id); trace!("get_blanket_impls({:?})", ty); let mut impls = Vec::new(); - self.cx.with_all_traits(|cx, all_traits| { - for &trait_def_id in all_traits { - if !cx.cache.access_levels.is_public(trait_def_id) - || cx.generated_synthetics.get(&(ty.0, trait_def_id)).is_some() - { + for trait_def_id in cx.tcx.all_traits() { + if !cx.cache.effective_visibilities.is_directly_public(trait_def_id) + || cx.generated_synthetics.get(&(ty.0, trait_def_id)).is_some() + { + continue; + } + // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls + let trait_impls = cx.tcx.trait_impls_of(trait_def_id); + 'blanket_impls: for &impl_def_id in trait_impls.blanket_impls() { + trace!( + "get_blanket_impls: Considering impl for trait '{:?}' {:?}", + trait_def_id, + impl_def_id + ); + let trait_ref = cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap(); + if !matches!(trait_ref.0.self_ty().kind(), ty::Param(_)) { continue; } - // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls - let trait_impls = cx.tcx.trait_impls_of(trait_def_id); - for &impl_def_id in trait_impls.blanket_impls() { - trace!( - "get_blanket_impls: Considering impl for trait '{:?}' {:?}", - trait_def_id, - impl_def_id - ); - let trait_ref = cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap(); - let is_param = matches!(trait_ref.0.self_ty().kind(), ty::Param(_)); - let may_apply = is_param && cx.tcx.infer_ctxt().enter(|infcx| { - let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id); - let ty = ty.subst(infcx.tcx, substs); - let param_env = EarlyBinder(param_env).subst(infcx.tcx, substs); + let infcx = cx.tcx.infer_ctxt().build(); + let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id); + let impl_ty = ty.subst(infcx.tcx, substs); + let param_env = EarlyBinder(param_env).subst(infcx.tcx, substs); - let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); - let trait_ref = trait_ref.subst(infcx.tcx, impl_substs); + let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = trait_ref.subst(infcx.tcx, impl_substs); - // Require the type the impl is implemented on to match - // our type, and ignore the impl if there was a mismatch. - let cause = traits::ObligationCause::dummy(); - let eq_result = infcx.at(&cause, param_env).eq(trait_ref.self_ty(), ty); - if let Ok(InferOk { value: (), obligations }) = eq_result { - // FIXME(eddyb) ignoring `obligations` might cause false positives. - drop(obligations); + // Require the type the impl is implemented on to match + // our type, and ignore the impl if there was a mismatch. + let cause = traits::ObligationCause::dummy(); + let Ok(eq_result) = infcx.at(&cause, param_env).eq(impl_trait_ref.self_ty(), impl_ty) else { + continue + }; + let InferOk { value: (), obligations } = eq_result; + // FIXME(eddyb) ignoring `obligations` might cause false positives. + drop(obligations); - trace!( - "invoking predicate_may_hold: param_env={:?}, trait_ref={:?}, ty={:?}", - param_env, - trait_ref, - ty - ); - let predicates = cx - .tcx - .predicates_of(impl_def_id) - .instantiate(cx.tcx, impl_substs) - .predicates - .into_iter() - .chain(Some( - ty::Binder::dummy(trait_ref) - .to_poly_trait_predicate() - .map_bound(ty::PredicateKind::Trait) - .to_predicate(infcx.tcx), - )); - for predicate in predicates { - debug!("testing predicate {:?}", predicate); - let obligation = traits::Obligation::new( - traits::ObligationCause::dummy(), - param_env, - predicate, - ); - match infcx.evaluate_obligation(&obligation) { - Ok(eval_result) if eval_result.may_apply() => {} - Err(traits::OverflowError::Canonical) => {} - Err(traits::OverflowError::ErrorReporting) => {} - _ => { - return false; - } - } - } - true - } else { - false - } - }); - debug!( - "get_blanket_impls: found applicable impl: {} for trait_ref={:?}, ty={:?}", - may_apply, trait_ref, ty + trace!( + "invoking predicate_may_hold: param_env={:?}, impl_trait_ref={:?}, impl_ty={:?}", + param_env, + impl_trait_ref, + impl_ty + ); + let predicates = cx + .tcx + .predicates_of(impl_def_id) + .instantiate(cx.tcx, impl_substs) + .predicates + .into_iter() + .chain(Some( + ty::Binder::dummy(impl_trait_ref) + .to_poly_trait_predicate() + .map_bound(ty::PredicateKind::Trait) + .to_predicate(infcx.tcx), + )); + for predicate in predicates { + debug!("testing predicate {:?}", predicate); + let obligation = traits::Obligation::new( + traits::ObligationCause::dummy(), + param_env, + predicate, ); - if !may_apply { - continue; + match infcx.evaluate_obligation(&obligation) { + Ok(eval_result) if eval_result.may_apply() => {} + Err(traits::OverflowError::Canonical) => {} + Err(traits::OverflowError::ErrorReporting) => {} + _ => continue 'blanket_impls, } + } + debug!( + "get_blanket_impls: found applicable impl for trait_ref={:?}, ty={:?}", + trait_ref, ty + ); - cx.generated_synthetics.insert((ty.0, trait_def_id)); + cx.generated_synthetics.insert((ty.0, trait_def_id)); - impls.push(Item { - name: None, - attrs: Default::default(), - visibility: Inherited, - item_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, - kind: Box::new(ImplItem(Box::new(Impl { - unsafety: hir::Unsafety::Normal, - generics: clean_ty_generics( - cx, - cx.tcx.generics_of(impl_def_id), - cx.tcx.explicit_predicates_of(impl_def_id), - ), - // FIXME(eddyb) compute both `trait_` and `for_` from - // the post-inference `trait_ref`, as it's more accurate. - trait_: Some(clean_trait_ref_with_bindings(cx, trait_ref.0, &[])), - for_: clean_middle_ty(ty.0, cx, None), - items: cx.tcx - .associated_items(impl_def_id) - .in_definition_order() - .map(|x| x.clean(cx)) - .collect::<Vec<_>>(), - polarity: ty::ImplPolarity::Positive, - kind: ImplKind::Blanket(Box::new(clean_middle_ty(trait_ref.0.self_ty(), cx, None))), - }))), - cfg: None, - }); - } + impls.push(Item { + name: None, + attrs: Default::default(), + visibility: Inherited, + item_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, + kind: Box::new(ImplItem(Box::new(Impl { + unsafety: hir::Unsafety::Normal, + generics: clean_ty_generics( + cx, + cx.tcx.generics_of(impl_def_id), + cx.tcx.explicit_predicates_of(impl_def_id), + ), + // FIXME(eddyb) compute both `trait_` and `for_` from + // the post-inference `trait_ref`, as it's more accurate. + trait_: Some(clean_trait_ref_with_bindings( + cx, + trait_ref.0, + ThinVec::new(), + )), + for_: clean_middle_ty(ty.0, cx, None), + items: cx + .tcx + .associated_items(impl_def_id) + .in_definition_order() + .map(|x| clean_middle_assoc_item(x, cx)) + .collect::<Vec<_>>(), + polarity: ty::ImplPolarity::Positive, + kind: ImplKind::Blanket(Box::new(clean_middle_ty( + trait_ref.0.self_ty(), + cx, + None, + ))), + }))), + cfg: None, + }); } - }); + } impls } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 58d0aedb0..4e2031a91 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -3,9 +3,10 @@ use std::iter::once; use std::sync::Arc; +use thin_vec::ThinVec; + use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -16,15 +17,14 @@ use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Symbol}; use crate::clean::{ - self, clean_fn_decl_from_did_and_sig, clean_middle_field, clean_middle_ty, - clean_trait_ref_with_bindings, clean_ty, clean_ty_generics, clean_variant_def, - clean_visibility, utils, Attributes, AttributesExt, Clean, ImplKind, ItemId, Type, Visibility, + self, clean_fn_decl_from_did_and_sig, clean_generics, clean_impl_item, clean_middle_assoc_item, + clean_middle_field, clean_middle_ty, clean_trait_ref_with_bindings, clean_ty, + clean_ty_generics, clean_variant_def, clean_visibility, utils, Attributes, AttributesExt, + ImplKind, ItemId, Type, Visibility, }; use crate::core::DocContext; use crate::formats::item_type::ItemType; -type Attrs<'hir> = &'hir [ast::Attribute]; - /// Attempt to inline a definition into this AST. /// /// This function will fetch the definition specified, and if it is @@ -45,7 +45,7 @@ pub(crate) fn try_inline( import_def_id: Option<DefId>, res: Res, name: Symbol, - attrs: Option<Attrs<'_>>, + attrs: Option<&[ast::Attribute]>, visited: &mut FxHashSet<DefId>, ) -> Option<Vec<clean::Item>> { let did = res.opt_def_id()?; @@ -55,13 +55,40 @@ pub(crate) fn try_inline( let mut ret = Vec::new(); debug!("attrs={:?}", attrs); - let attrs_clone = attrs; + + let attrs_without_docs = attrs.map(|attrs| { + attrs.into_iter().filter(|a| a.doc_str().is_none()).cloned().collect::<Vec<_>>() + }); + // We need this ugly code because: + // + // ``` + // attrs_without_docs.map(|a| a.as_slice()) + // ``` + // + // will fail because it returns a temporary slice and: + // + // ``` + // attrs_without_docs.map(|s| { + // vec = s.as_slice(); + // vec + // }) + // ``` + // + // will fail because we're moving an uninitialized variable into a closure. + let vec; + let attrs_without_docs = match attrs_without_docs { + Some(s) => { + vec = s; + Some(vec.as_slice()) + } + None => None, + }; let kind = match res { Res::Def(DefKind::Trait, did) => { record_extern_fqn(cx, did, ItemType::Trait); - build_impls(cx, Some(parent_module), did, attrs, &mut ret); - clean::TraitItem(build_external_trait(cx, did)) + build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); + clean::TraitItem(Box::new(build_external_trait(cx, did))) } Res::Def(DefKind::Fn, did) => { record_extern_fqn(cx, did, ItemType::Function); @@ -69,27 +96,27 @@ pub(crate) fn try_inline( } Res::Def(DefKind::Struct, did) => { record_extern_fqn(cx, did, ItemType::Struct); - build_impls(cx, Some(parent_module), did, attrs, &mut ret); + build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); clean::StructItem(build_struct(cx, did)) } Res::Def(DefKind::Union, did) => { record_extern_fqn(cx, did, ItemType::Union); - build_impls(cx, Some(parent_module), did, attrs, &mut ret); + build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); clean::UnionItem(build_union(cx, did)) } Res::Def(DefKind::TyAlias, did) => { record_extern_fqn(cx, did, ItemType::Typedef); - build_impls(cx, Some(parent_module), did, attrs, &mut ret); + build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); clean::TypedefItem(build_type_alias(cx, did)) } Res::Def(DefKind::Enum, did) => { record_extern_fqn(cx, did, ItemType::Enum); - build_impls(cx, Some(parent_module), did, attrs, &mut ret); + build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); clean::EnumItem(build_enum(cx, did)) } Res::Def(DefKind::ForeignTy, did) => { record_extern_fqn(cx, did, ItemType::ForeignType); - build_impls(cx, Some(parent_module), did, attrs, &mut ret); + build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); clean::ForeignTypeItem } // Never inline enum variants but leave them shown as re-exports. @@ -123,7 +150,7 @@ pub(crate) fn try_inline( _ => return None, }; - let (attrs, cfg) = merge_attrs(cx, Some(parent_module), load_attrs(cx, did), attrs_clone); + let (attrs, cfg) = merge_attrs(cx, Some(parent_module), load_attrs(cx, did), attrs); cx.inlined.insert(did.into()); let mut item = clean::Item::from_def_id_and_attrs_and_parts( did, @@ -171,7 +198,7 @@ pub(crate) fn try_inline_glob( } } -pub(crate) fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> { +pub(crate) fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> &'hir [ast::Attribute] { cx.tcx.get_attrs_unchecked(did) } @@ -217,7 +244,7 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean // which causes methods to have a `pub` prefix, which is invalid since items in traits // can not have a visibility prefix. Thus we override the visibility here manually. // See https://github.com/rust-lang/rust/issues/81274 - clean::Item { visibility: Visibility::Inherited, ..item.clean(cx) } + clean::Item { visibility: Visibility::Inherited, ..clean_middle_assoc_item(item, cx) } }) .collect(); @@ -286,7 +313,7 @@ pub(crate) fn build_impls( cx: &mut DocContext<'_>, parent_module: Option<DefId>, did: DefId, - attrs: Option<Attrs<'_>>, + attrs: Option<&[ast::Attribute]>, ret: &mut Vec<clean::Item>, ) { let _prof_timer = cx.tcx.sess.prof.generic_activity("build_inherent_impls"); @@ -296,14 +323,29 @@ pub(crate) fn build_impls( for &did in tcx.inherent_impls(did).iter() { build_impl(cx, parent_module, did, attrs, ret); } + + // This pretty much exists expressly for `dyn Error` traits that exist in the `alloc` crate. + // See also: + // + // * https://github.com/rust-lang/rust/issues/103170 — where it didn't used to get documented + // * https://github.com/rust-lang/rust/pull/99917 — where the feature got used + // * https://github.com/rust-lang/rust/issues/53487 — overall tracking issue for Error + if tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) { + use rustc_middle::ty::fast_reject::SimplifiedTypeGen::*; + let type_ = + if tcx.is_trait(did) { TraitSimplifiedType(did) } else { AdtSimplifiedType(did) }; + for &did in tcx.incoherent_impls(type_) { + build_impl(cx, parent_module, did, attrs, ret); + } + } } /// `parent_module` refers to the parent of the re-export, not the original item -fn merge_attrs( +pub(crate) fn merge_attrs( cx: &mut DocContext<'_>, parent_module: Option<DefId>, - old_attrs: Attrs<'_>, - new_attrs: Option<Attrs<'_>>, + old_attrs: &[ast::Attribute], + new_attrs: Option<&[ast::Attribute]>, ) -> (clean::Attributes, Option<Arc<clean::cfg::Cfg>>) { // NOTE: If we have additional attributes (from a re-export), // always insert them first. This ensure that re-export @@ -330,7 +372,7 @@ pub(crate) fn build_impl( cx: &mut DocContext<'_>, parent_module: Option<DefId>, did: DefId, - attrs: Option<Attrs<'_>>, + attrs: Option<&[ast::Attribute]>, ret: &mut Vec<clean::Item>, ) { if !cx.inlined.insert(did.into()) { @@ -347,7 +389,7 @@ pub(crate) fn build_impl( if !did.is_local() { if let Some(traitref) = associated_trait { let did = traitref.def_id; - if !cx.cache.access_levels.is_public(did) { + if !cx.cache.effective_visibilities.is_directly_public(did) { return; } @@ -376,7 +418,7 @@ pub(crate) fn build_impl( // reachable in rustdoc generated documentation if !did.is_local() { if let Some(did) = for_.def_id(&cx.cache) { - if !cx.cache.access_levels.is_public(did) { + if !cx.cache.effective_visibilities.is_directly_public(did) { return; } @@ -410,7 +452,7 @@ pub(crate) fn build_impl( let assoc_kind = match item.kind { hir::ImplItemKind::Const(..) => ty::AssocKind::Const, hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn, - hir::ImplItemKind::TyAlias(..) => ty::AssocKind::Type, + hir::ImplItemKind::Type(..) => ty::AssocKind::Type, }; let trait_item = tcx .associated_items(associated_trait.def_id) @@ -426,9 +468,9 @@ pub(crate) fn build_impl( true } }) - .map(|item| item.clean(cx)) + .map(|item| clean_impl_item(item, cx)) .collect::<Vec<_>>(), - impl_.generics.clean(cx), + clean_generics(impl_.generics, cx), ), None => ( tcx.associated_items(did) @@ -452,7 +494,7 @@ pub(crate) fn build_impl( item.visibility(tcx).is_public() } }) - .map(|item| item.clean(cx)) + .map(|item| clean_middle_assoc_item(item, cx)) .collect::<Vec<_>>(), clean::enter_impl_trait(cx, |cx| { clean_ty_generics(cx, tcx.generics_of(did), predicates) @@ -460,7 +502,7 @@ pub(crate) fn build_impl( ), }; let polarity = tcx.impl_polarity(did); - let trait_ = associated_trait.map(|t| clean_trait_ref_with_bindings(cx, t, &[])); + let trait_ = associated_trait.map(|t| clean_trait_ref_with_bindings(cx, t, ThinVec::new())); if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() { super::build_deref_target_impls(cx, &trait_items, ret); } @@ -671,7 +713,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean: g.where_predicates.retain(|pred| match pred { clean::WherePredicate::BoundPredicate { - ty: clean::QPath { self_type: box clean::Generic(ref s), trait_, .. }, + ty: clean::QPath(box clean::QPathData { self_type: clean::Generic(ref s), trait_, .. }), bounds, .. } => !(bounds.is_empty() || *s == kw::SelfUpper && trait_.def_id() == trait_did), @@ -718,10 +760,6 @@ pub(crate) fn record_extern_trait(cx: &mut DocContext<'_>, did: DefId) { debug!("record_extern_trait: {:?}", did); let trait_ = build_external_trait(cx, did); - let trait_ = clean::TraitWithExtraInfo { - trait_, - is_notable: clean::utils::has_doc_flag(cx.tcx, did, sym::notable_trait), - }; cx.external_traits.borrow_mut().insert(did, trait_); cx.active_extern_traits.remove(&did); } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 929f5f89b..64a18757b 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -17,23 +17,24 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::PredicateOrigin; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; use rustc_middle::middle::resolve_lifetime as rl; use rustc_middle::ty::fold::TypeFolder; -use rustc_middle::ty::subst::{InternalSubsts, Subst}; -use rustc_middle::ty::{self, AdtKind, DefIdTree, EarlyBinder, Lift, Ty, TyCtxt}; +use rustc_middle::ty::InternalSubsts; +use rustc_middle::ty::{self, AdtKind, DefIdTree, EarlyBinder, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{AstPass, MacroKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{self, ExpnKind}; -use rustc_typeck::hir_ty_to_ty; use std::assert_matches::assert_matches; use std::collections::hash_map::Entry; use std::collections::BTreeMap; use std::default::Default; use std::hash::Hash; -use std::{mem, vec}; +use std::mem; +use thin_vec::ThinVec; use crate::core::{self, DocContext, ImplTraitParam}; use crate::formats::item_type::ItemType; @@ -44,128 +45,126 @@ use utils::*; pub(crate) use self::types::*; pub(crate) use self::utils::{get_auto_trait_and_blanket_impls, krate, register_res}; -pub(crate) trait Clean<'tcx, T> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> T; -} +pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<'tcx>) -> Item { + let mut items: Vec<Item> = vec![]; + let mut inserted = FxHashSet::default(); + items.extend(doc.foreigns.iter().map(|(item, renamed)| { + let item = clean_maybe_renamed_foreign_item(cx, item, *renamed); + if let Some(name) = item.name && !item.attrs.lists(sym::doc).has_word(sym::hidden) { + inserted.insert((item.type_(), name)); + } + item + })); + items.extend(doc.mods.iter().filter_map(|x| { + if !inserted.insert((ItemType::Module, x.name)) { + return None; + } + let item = clean_doc_module(x, cx); + if item.attrs.lists(sym::doc).has_word(sym::hidden) { + // Hidden modules are stripped at a later stage. + // If a hidden module has the same name as a visible one, we want + // to keep both of them around. + inserted.remove(&(ItemType::Module, x.name)); + } + Some(item) + })); -impl<'tcx> Clean<'tcx, Item> for DocModule<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> Item { - let mut items: Vec<Item> = vec![]; - let mut inserted = FxHashSet::default(); - items.extend(self.foreigns.iter().map(|(item, renamed)| { - let item = clean_maybe_renamed_foreign_item(cx, item, *renamed); - if let Some(name) = item.name { + // Split up imports from all other items. + // + // This covers the case where somebody does an import which should pull in an item, + // but there's already an item with the same namespace and same name. Rust gives + // priority to the not-imported one, so we should, too. + items.extend(doc.items.iter().flat_map(|(item, renamed)| { + // First, lower everything other than imports. + if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) { + return Vec::new(); + } + let v = clean_maybe_renamed_item(cx, item, *renamed); + for item in &v { + if let Some(name) = item.name && !item.attrs.lists(sym::doc).has_word(sym::hidden) { inserted.insert((item.type_(), name)); } - item - })); - items.extend(self.mods.iter().map(|x| { - inserted.insert((ItemType::Module, x.name)); - x.clean(cx) - })); - - // Split up imports from all other items. - // - // This covers the case where somebody does an import which should pull in an item, - // but there's already an item with the same namespace and same name. Rust gives - // priority to the not-imported one, so we should, too. - items.extend(self.items.iter().flat_map(|(item, renamed)| { - // First, lower everything other than imports. - if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) { - return Vec::new(); - } - let v = clean_maybe_renamed_item(cx, item, *renamed); - for item in &v { - if let Some(name) = item.name { - inserted.insert((item.type_(), name)); - } - } - v - })); - items.extend(self.items.iter().flat_map(|(item, renamed)| { - // Now we actually lower the imports, skipping everything else. - if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind { - let name = renamed.unwrap_or_else(|| cx.tcx.hir().name(item.hir_id())); - clean_use_statement(item, name, path, hir::UseKind::Glob, cx, &mut inserted) - } else { - // skip everything else - Vec::new() - } - })); - - // determine if we should display the inner contents or - // the outer `mod` item for the source code. - - let span = Span::new({ - let where_outer = self.where_outer(cx.tcx); - let sm = cx.sess().source_map(); - let outer = sm.lookup_char_pos(where_outer.lo()); - let inner = sm.lookup_char_pos(self.where_inner.lo()); - if outer.file.start_pos == inner.file.start_pos { - // mod foo { ... } - where_outer - } else { - // mod foo; (and a separate SourceFile for the contents) - self.where_inner - } - }); + } + v + })); + items.extend(doc.items.iter().flat_map(|(item, renamed)| { + // Now we actually lower the imports, skipping everything else. + if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind { + let name = renamed.unwrap_or_else(|| cx.tcx.hir().name(item.hir_id())); + clean_use_statement(item, name, path, hir::UseKind::Glob, cx, &mut inserted) + } else { + // skip everything else + Vec::new() + } + })); + + // determine if we should display the inner contents or + // the outer `mod` item for the source code. + + let span = Span::new({ + let where_outer = doc.where_outer(cx.tcx); + let sm = cx.sess().source_map(); + let outer = sm.lookup_char_pos(where_outer.lo()); + let inner = sm.lookup_char_pos(doc.where_inner.lo()); + if outer.file.start_pos == inner.file.start_pos { + // mod foo { ... } + where_outer + } else { + // mod foo; (and a separate SourceFile for the contents) + doc.where_inner + } + }); - Item::from_hir_id_and_parts( - self.id, - Some(self.name), - ModuleItem(Module { items, span }), - cx, - ) - } + Item::from_hir_id_and_parts(doc.id, Some(doc.name), ModuleItem(Module { items, span }), cx) } -impl<'tcx> Clean<'tcx, Option<GenericBound>> for hir::GenericBound<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> Option<GenericBound> { - Some(match *self { - hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)), - hir::GenericBound::LangItemTrait(lang_item, span, _, generic_args) => { - let def_id = cx.tcx.require_lang_item(lang_item, Some(span)); - - let trait_ref = ty::TraitRef::identity(cx.tcx, def_id).skip_binder(); - - let generic_args = generic_args.clean(cx); - let GenericArgs::AngleBracketed { bindings, .. } = generic_args - else { - bug!("clean: parenthesized `GenericBound::LangItemTrait`"); - }; +fn clean_generic_bound<'tcx>( + bound: &hir::GenericBound<'tcx>, + cx: &mut DocContext<'tcx>, +) -> Option<GenericBound> { + Some(match *bound { + hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)), + hir::GenericBound::LangItemTrait(lang_item, span, _, generic_args) => { + let def_id = cx.tcx.require_lang_item(lang_item, Some(span)); + + let trait_ref = ty::TraitRef::identity(cx.tcx, def_id).skip_binder(); + + let generic_args = clean_generic_args(generic_args, cx); + let GenericArgs::AngleBracketed { bindings, .. } = generic_args + else { + bug!("clean: parenthesized `GenericBound::LangItemTrait`"); + }; - let trait_ = clean_trait_ref_with_bindings(cx, trait_ref, &bindings); - GenericBound::TraitBound( - PolyTrait { trait_, generic_params: vec![] }, - hir::TraitBoundModifier::None, - ) + let trait_ = clean_trait_ref_with_bindings(cx, trait_ref, bindings); + GenericBound::TraitBound( + PolyTrait { trait_, generic_params: vec![] }, + hir::TraitBoundModifier::None, + ) + } + hir::GenericBound::Trait(ref t, modifier) => { + // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op. + if modifier == hir::TraitBoundModifier::MaybeConst + && cx.tcx.lang_items().destruct_trait() == Some(t.trait_ref.trait_def_id().unwrap()) + { + return None; } - hir::GenericBound::Trait(ref t, modifier) => { - // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op. - if modifier == hir::TraitBoundModifier::MaybeConst - && cx.tcx.lang_items().destruct_trait() - == Some(t.trait_ref.trait_def_id().unwrap()) - { - return None; - } - GenericBound::TraitBound(t.clean(cx), modifier) - } - }) - } + GenericBound::TraitBound(clean_poly_trait_ref(t, cx), modifier) + } + }) } pub(crate) fn clean_trait_ref_with_bindings<'tcx>( cx: &mut DocContext<'tcx>, trait_ref: ty::TraitRef<'tcx>, - bindings: &[TypeBinding], + bindings: ThinVec<TypeBinding>, ) -> Path { let kind = cx.tcx.def_kind(trait_ref.def_id).into(); if !matches!(kind, ItemType::Trait | ItemType::TraitAlias) { span_bug!(cx.tcx.def_span(trait_ref.def_id), "`TraitRef` had unexpected kind {:?}", kind); } inline::record_extern_fqn(cx, trait_ref.def_id, kind); - let path = external_path(cx, trait_ref.def_id, true, bindings.to_vec(), trait_ref.substs); + let path = external_path(cx, trait_ref.def_id, true, bindings, trait_ref.substs); debug!("ty::TraitRef\n subst: {:?}\n", trait_ref.substs); @@ -175,10 +174,8 @@ pub(crate) fn clean_trait_ref_with_bindings<'tcx>( fn clean_poly_trait_ref_with_bindings<'tcx>( cx: &mut DocContext<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, - bindings: &[TypeBinding], + bindings: ThinVec<TypeBinding>, ) -> GenericBound { - let poly_trait_ref = poly_trait_ref.lift_to_tcx(cx.tcx).unwrap(); - // collect any late bound regions let late_bound_regions: Vec<_> = cx .tcx @@ -200,16 +197,10 @@ fn clean_poly_trait_ref_with_bindings<'tcx>( ) } -impl<'tcx> Clean<'tcx, GenericBound> for ty::PolyTraitRef<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> GenericBound { - clean_poly_trait_ref_with_bindings(cx, *self, &[]) - } -} - -fn clean_lifetime<'tcx>(lifetime: hir::Lifetime, cx: &mut DocContext<'tcx>) -> Lifetime { +fn clean_lifetime<'tcx>(lifetime: &hir::Lifetime, cx: &mut DocContext<'tcx>) -> Lifetime { let def = cx.tcx.named_region(lifetime.hir_id); if let Some( - rl::Region::EarlyBound(_, node_id) + rl::Region::EarlyBound(node_id) | rl::Region::LateBound(_, _, node_id) | rl::Region::Free(_, node_id), ) = def @@ -257,7 +248,6 @@ pub(crate) fn clean_middle_region<'tcx>(region: ty::Region<'tcx>) -> Option<Life | ty::ReFree(..) | ty::ReVar(..) | ty::RePlaceholder(..) - | ty::ReEmpty(_) | ty::ReErased => { debug!("cannot clean region {:?}", region); None @@ -265,66 +255,71 @@ pub(crate) fn clean_middle_region<'tcx>(region: ty::Region<'tcx>) -> Option<Life } } -impl<'tcx> Clean<'tcx, Option<WherePredicate>> for hir::WherePredicate<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> Option<WherePredicate> { - if !self.in_where_clause() { - return None; - } - Some(match *self { - hir::WherePredicate::BoundPredicate(ref wbp) => { - let bound_params = wbp - .bound_generic_params - .iter() - .map(|param| { - // Higher-ranked params must be lifetimes. - // Higher-ranked lifetimes can't have bounds. - assert_matches!( - param, - hir::GenericParam { kind: hir::GenericParamKind::Lifetime { .. }, .. } - ); - Lifetime(param.name.ident().name) - }) - .collect(); - WherePredicate::BoundPredicate { - ty: clean_ty(wbp.bounded_ty, cx), - bounds: wbp.bounds.iter().filter_map(|x| x.clean(cx)).collect(), - bound_params, - } +fn clean_where_predicate<'tcx>( + predicate: &hir::WherePredicate<'tcx>, + cx: &mut DocContext<'tcx>, +) -> Option<WherePredicate> { + if !predicate.in_where_clause() { + return None; + } + Some(match *predicate { + hir::WherePredicate::BoundPredicate(ref wbp) => { + let bound_params = wbp + .bound_generic_params + .iter() + .map(|param| { + // Higher-ranked params must be lifetimes. + // Higher-ranked lifetimes can't have bounds. + assert_matches!( + param, + hir::GenericParam { kind: hir::GenericParamKind::Lifetime { .. }, .. } + ); + Lifetime(param.name.ident().name) + }) + .collect(); + WherePredicate::BoundPredicate { + ty: clean_ty(wbp.bounded_ty, cx), + bounds: wbp.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), + bound_params, } + } - hir::WherePredicate::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate { - lifetime: clean_lifetime(wrp.lifetime, cx), - bounds: wrp.bounds.iter().filter_map(|x| x.clean(cx)).collect(), - }, + hir::WherePredicate::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate { + lifetime: clean_lifetime(wrp.lifetime, cx), + bounds: wrp.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), + }, - hir::WherePredicate::EqPredicate(ref wrp) => WherePredicate::EqPredicate { - lhs: clean_ty(wrp.lhs_ty, cx), - rhs: clean_ty(wrp.rhs_ty, cx).into(), - }, - }) - } + hir::WherePredicate::EqPredicate(ref wrp) => WherePredicate::EqPredicate { + lhs: Box::new(clean_ty(wrp.lhs_ty, cx)), + rhs: Box::new(clean_ty(wrp.rhs_ty, cx).into()), + bound_params: Vec::new(), + }, + }) } -impl<'tcx> Clean<'tcx, Option<WherePredicate>> for ty::Predicate<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> Option<WherePredicate> { - let bound_predicate = self.kind(); - match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(pred) => { - clean_poly_trait_predicate(bound_predicate.rebind(pred), cx) - } - ty::PredicateKind::RegionOutlives(pred) => clean_region_outlives_predicate(pred), - ty::PredicateKind::TypeOutlives(pred) => clean_type_outlives_predicate(pred, cx), - ty::PredicateKind::Projection(pred) => Some(clean_projection_predicate(pred, cx)), - ty::PredicateKind::ConstEvaluatable(..) => None, - ty::PredicateKind::WellFormed(..) => None, - - ty::PredicateKind::Subtype(..) - | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::TypeWellFormedFromEnv(..) => panic!("not user writable"), +pub(crate) fn clean_predicate<'tcx>( + predicate: ty::Predicate<'tcx>, + cx: &mut DocContext<'tcx>, +) -> Option<WherePredicate> { + let bound_predicate = predicate.kind(); + match bound_predicate.skip_binder() { + ty::PredicateKind::Trait(pred) => { + clean_poly_trait_predicate(bound_predicate.rebind(pred), cx) + } + ty::PredicateKind::RegionOutlives(pred) => clean_region_outlives_predicate(pred), + ty::PredicateKind::TypeOutlives(pred) => clean_type_outlives_predicate(pred, cx), + ty::PredicateKind::Projection(pred) => { + Some(clean_projection_predicate(bound_predicate.rebind(pred), cx)) } + ty::PredicateKind::ConstEvaluatable(..) => None, + ty::PredicateKind::WellFormed(..) => None, + + ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => panic!("not user writable"), } } @@ -342,7 +337,7 @@ fn clean_poly_trait_predicate<'tcx>( let poly_trait_ref = pred.map_bound(|pred| pred.trait_ref); Some(WherePredicate::BoundPredicate { ty: clean_middle_ty(poly_trait_ref.skip_binder().self_ty(), cx, None), - bounds: vec![poly_trait_ref.clean(cx)], + bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, ThinVec::new())], bound_params: Vec::new(), }) } @@ -352,10 +347,6 @@ fn clean_region_outlives_predicate<'tcx>( ) -> Option<WherePredicate> { let ty::OutlivesPredicate(a, b) = pred; - if a.is_empty() && b.is_empty() { - return None; - } - Some(WherePredicate::RegionPredicate { lifetime: clean_middle_region(a).expect("failed to clean lifetime"), bounds: vec![GenericBound::Outlives( @@ -370,10 +361,6 @@ fn clean_type_outlives_predicate<'tcx>( ) -> Option<WherePredicate> { let ty::OutlivesPredicate(ty, lt) = pred; - if lt.is_empty() { - return None; - } - Some(WherePredicate::BoundPredicate { ty: clean_middle_ty(ty, cx, None), bounds: vec![GenericBound::Outlives( @@ -384,9 +371,9 @@ fn clean_type_outlives_predicate<'tcx>( } fn clean_middle_term<'tcx>(term: ty::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Term { - match term { - ty::Term::Ty(ty) => Term::Type(clean_middle_ty(ty, cx, None)), - ty::Term::Const(c) => Term::Constant(clean_middle_const(c, cx)), + match term.unpack() { + ty::TermKind::Ty(ty) => Term::Type(clean_middle_ty(ty, cx, None)), + ty::TermKind::Const(c) => Term::Constant(clean_middle_const(c, cx)), } } @@ -401,13 +388,25 @@ fn clean_hir_term<'tcx>(term: &hir::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Te } fn clean_projection_predicate<'tcx>( - pred: ty::ProjectionPredicate<'tcx>, + pred: ty::Binder<'tcx, ty::ProjectionPredicate<'tcx>>, cx: &mut DocContext<'tcx>, ) -> WherePredicate { - let ty::ProjectionPredicate { projection_ty, term } = pred; + let late_bound_regions = cx + .tcx + .collect_referenced_late_bound_regions(&pred) + .into_iter() + .filter_map(|br| match br { + ty::BrNamed(_, name) if name != kw::UnderscoreLifetime => Some(Lifetime(name)), + _ => None, + }) + .collect(); + + let ty::ProjectionPredicate { projection_ty, term } = pred.skip_binder(); + WherePredicate::EqPredicate { - lhs: clean_projection(projection_ty, cx, None), - rhs: clean_middle_term(term, cx), + lhs: Box::new(clean_projection(projection_ty, cx, None)), + rhs: Box::new(clean_middle_term(term, cx)), + bound_params: late_bound_regions, } } @@ -416,8 +415,17 @@ fn clean_projection<'tcx>( cx: &mut DocContext<'tcx>, def_id: Option<DefId>, ) -> Type { - let lifted = ty.lift_to_tcx(cx.tcx).unwrap(); - let trait_ = clean_trait_ref_with_bindings(cx, lifted.trait_ref(cx.tcx), &[]); + if cx.tcx.def_kind(ty.item_def_id) == DefKind::ImplTraitPlaceholder { + let bounds = cx + .tcx + .explicit_item_bounds(ty.item_def_id) + .iter() + .map(|(bound, _)| EarlyBinder(*bound).subst(cx.tcx, ty.substs)) + .collect::<Vec<_>>(); + return clean_middle_opaque_bounds(cx, bounds); + } + + let trait_ = clean_trait_ref_with_bindings(cx, ty.trait_ref(cx.tcx), ThinVec::new()); let self_type = clean_middle_ty(ty.self_ty(), cx, None); let self_def_id = if let Some(def_id) = def_id { cx.tcx.opt_parent(def_id).or(Some(def_id)) @@ -425,12 +433,12 @@ fn clean_projection<'tcx>( self_type.def_id(&cx.cache) }; let should_show_cast = compute_should_show_cast(self_def_id, &trait_, &self_type); - Type::QPath { - assoc: Box::new(projection_to_path_segment(ty, cx)), + Type::QPath(Box::new(QPathData { + assoc: projection_to_path_segment(ty, cx), should_show_cast, - self_type: Box::new(self_type), + self_type, trait_, - } + })) } fn compute_should_show_cast(self_def_id: Option<DefId>, trait_: &Path, self_type: &Type) -> bool { @@ -509,7 +517,7 @@ fn clean_generic_param<'tcx>( .filter(|bp| !bp.in_where_clause) .flat_map(|bp| bp.bounds) .map(|bound| match bound { - hir::GenericBound::Outlives(lt) => clean_lifetime(*lt, cx), + hir::GenericBound::Outlives(lt) => clean_lifetime(lt, cx), _ => panic!(), }) .collect() @@ -524,7 +532,7 @@ fn clean_generic_param<'tcx>( .bounds_for_param(did) .filter(|bp| bp.origin != PredicateOrigin::WhereClause) .flat_map(|bp| bp.bounds) - .filter_map(|x| x.clean(cx)) + .filter_map(|x| clean_generic_bound(x, cx)) .collect() } else { Vec::new() @@ -572,65 +580,68 @@ fn is_elided_lifetime(param: &hir::GenericParam<'_>) -> bool { matches!(param.kind, hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided }) } -impl<'tcx> Clean<'tcx, Generics> for hir::Generics<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> Generics { - let impl_trait_params = self - .params - .iter() - .filter(|param| is_impl_trait(param)) - .map(|param| { - let param = clean_generic_param(cx, Some(self), param); - match param.kind { - GenericParamDefKind::Lifetime { .. } => unreachable!(), - GenericParamDefKind::Type { did, ref bounds, .. } => { - cx.impl_trait_bounds.insert(did.into(), bounds.clone()); - } - GenericParamDefKind::Const { .. } => unreachable!(), +pub(crate) fn clean_generics<'tcx>( + gens: &hir::Generics<'tcx>, + cx: &mut DocContext<'tcx>, +) -> Generics { + let impl_trait_params = gens + .params + .iter() + .filter(|param| is_impl_trait(param)) + .map(|param| { + let param = clean_generic_param(cx, Some(gens), param); + match param.kind { + GenericParamDefKind::Lifetime { .. } => unreachable!(), + GenericParamDefKind::Type { did, ref bounds, .. } => { + cx.impl_trait_bounds.insert(did.into(), bounds.clone()); } - param - }) - .collect::<Vec<_>>(); + GenericParamDefKind::Const { .. } => unreachable!(), + } + param + }) + .collect::<Vec<_>>(); - let mut params = Vec::with_capacity(self.params.len()); - for p in self.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) { - let p = clean_generic_param(cx, Some(self), p); - params.push(p); - } - params.extend(impl_trait_params); + let mut params = Vec::with_capacity(gens.params.len()); + for p in gens.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) { + let p = clean_generic_param(cx, Some(gens), p); + params.push(p); + } + params.extend(impl_trait_params); - let mut generics = Generics { - params, - where_predicates: self.predicates.iter().filter_map(|x| x.clean(cx)).collect(), - }; + let mut generics = Generics { + params, + where_predicates: gens + .predicates + .iter() + .filter_map(|x| clean_where_predicate(x, cx)) + .collect(), + }; - // Some duplicates are generated for ?Sized bounds between type params and where - // predicates. The point in here is to move the bounds definitions from type params - // to where predicates when such cases occur. - for where_pred in &mut generics.where_predicates { - match *where_pred { - WherePredicate::BoundPredicate { - ty: Generic(ref name), ref mut bounds, .. - } => { - if bounds.is_empty() { - for param in &mut generics.params { - match param.kind { - GenericParamDefKind::Lifetime { .. } => {} - GenericParamDefKind::Type { bounds: ref mut ty_bounds, .. } => { - if ¶m.name == name { - mem::swap(bounds, ty_bounds); - break; - } + // Some duplicates are generated for ?Sized bounds between type params and where + // predicates. The point in here is to move the bounds definitions from type params + // to where predicates when such cases occur. + for where_pred in &mut generics.where_predicates { + match *where_pred { + WherePredicate::BoundPredicate { ty: Generic(ref name), ref mut bounds, .. } => { + if bounds.is_empty() { + for param in &mut generics.params { + match param.kind { + GenericParamDefKind::Lifetime { .. } => {} + GenericParamDefKind::Type { bounds: ref mut ty_bounds, .. } => { + if ¶m.name == name { + mem::swap(bounds, ty_bounds); + break; } - GenericParamDefKind::Const { .. } => {} } + GenericParamDefKind::Const { .. } => {} } } } - _ => continue, } + _ => continue, } - generics } + generics } fn clean_ty_generics<'tcx>( @@ -643,7 +654,7 @@ fn clean_ty_generics<'tcx>( let mut impl_trait = BTreeMap::<ImplTraitParam, Vec<GenericBound>>::default(); // Bounds in the type_params and lifetimes fields are repeated in the - // predicates field (see rustc_typeck::collect::ty_generics), so remove + // predicates field (see rustc_hir_analysis::collect::ty_generics), so remove // them. let stripped_params = gens .params @@ -666,8 +677,9 @@ fn clean_ty_generics<'tcx>( }) .collect::<Vec<GenericParamDef>>(); - // param index -> [(DefId of trait, associated type name and generics, type)] - let mut impl_trait_proj = FxHashMap::<u32, Vec<(DefId, PathSegment, Ty<'_>)>>::default(); + // param index -> [(trait DefId, associated type name & generics, type, higher-ranked params)] + let mut impl_trait_proj = + FxHashMap::<u32, Vec<(DefId, PathSegment, Ty<'_>, Vec<GenericParamDef>)>>::default(); let where_predicates = preds .predicates @@ -701,7 +713,7 @@ fn clean_ty_generics<'tcx>( if let Some(param_idx) = param_idx { if let Some(b) = impl_trait.get_mut(¶m_idx.into()) { - let p: WherePredicate = p.clean(cx)?; + let p: WherePredicate = clean_predicate(*p, cx)?; b.extend( p.get_bounds() @@ -726,6 +738,14 @@ fn clean_ty_generics<'tcx>( trait_did, name, rhs.ty().unwrap(), + p.get_bound_params() + .into_iter() + .flatten() + .map(|param| GenericParamDef { + name: param.0, + kind: GenericParamDefKind::Lifetime { outlives: Vec::new() }, + }) + .collect(), )); } @@ -741,15 +761,19 @@ fn clean_ty_generics<'tcx>( // Move trait bounds to the front. bounds.sort_by_key(|b| !matches!(b, GenericBound::TraitBound(..))); - if let crate::core::ImplTraitParam::ParamIndex(idx) = param { - if let Some(proj) = impl_trait_proj.remove(&idx) { - for (trait_did, name, rhs) in proj { - let rhs = clean_middle_ty(rhs, cx, None); - simplify::merge_bounds(cx, &mut bounds, trait_did, name, &Term::Type(rhs)); - } + let crate::core::ImplTraitParam::ParamIndex(idx) = param else { unreachable!() }; + if let Some(proj) = impl_trait_proj.remove(&idx) { + for (trait_did, name, rhs, bound_params) in proj { + let rhs = clean_middle_ty(rhs, cx, None); + simplify::merge_bounds( + cx, + &mut bounds, + bound_params, + trait_did, + name, + &Term::Type(rhs), + ); } - } else { - unreachable!(); } cx.impl_trait_bounds.insert(param, bounds); @@ -758,33 +782,38 @@ fn clean_ty_generics<'tcx>( // Now that `cx.impl_trait_bounds` is populated, we can process // remaining predicates which could contain `impl Trait`. let mut where_predicates = - where_predicates.into_iter().flat_map(|p| p.clean(cx)).collect::<Vec<_>>(); + where_predicates.into_iter().flat_map(|p| clean_predicate(*p, cx)).collect::<Vec<_>>(); - // Type parameters 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. + // In the surface language, all type parameters except `Self` have an + // implicit `Sized` bound unless removed with `?Sized`. + // However, in the list of where-predicates below, `Sized` appears like a + // normal bound: It's either present (the type is sized) or + // absent (the type is unsized) but never *maybe* (i.e. `?Sized`). + // + // This is unsuitable for rendering. + // Thus, as a first step remove all `Sized` bounds that should be implicit. // - // Note that associated types also have a sized bound by default, but we + // Note that associated types also have an implicit `Sized` bound but we // don't actually know the set of associated types right here so that's - // handled in cleaning associated types + // handled when cleaning associated types. let mut sized_params = FxHashSet::default(); - where_predicates.retain(|pred| match *pred { - WherePredicate::BoundPredicate { ty: Generic(ref g), ref bounds, .. } => { - if bounds.iter().any(|b| b.is_sized_bound(cx)) { - sized_params.insert(*g); - false - } else { - true - } + where_predicates.retain(|pred| { + if let WherePredicate::BoundPredicate { ty: Generic(g), bounds, .. } = pred + && *g != kw::SelfUpper + && bounds.iter().any(|b| b.is_sized_bound(cx)) + { + sized_params.insert(*g); + false + } else { + true } - _ => true, }); - // Run through the type parameters again and insert a ?Sized - // unbound for any we didn't find to be Sized. + // As a final step, go through the type parameters again and insert a + // `?Sized` bound for each one we didn't find to be `Sized`. for tp in &stripped_params { - if matches!(tp.kind, types::GenericParamDefKind::Type { .. }) - && !sized_params.contains(&tp.name) + if let types::GenericParamDefKind::Type { .. } = tp.kind + && !sized_params.contains(&tp.name) { where_predicates.push(WherePredicate::BoundPredicate { ty: Type::Generic(tp.name), @@ -896,9 +925,12 @@ fn clean_function<'tcx>( ) -> Box<Function> { let (generics, decl) = enter_impl_trait(cx, |cx| { // NOTE: generics must be cleaned before args - let generics = generics.clean(cx); + let generics = clean_generics(generics, cx); let args = clean_args_from_types_and_body_id(cx, sig.decl.inputs, body_id); - let decl = clean_fn_decl_with_args(cx, sig.decl, args); + let mut decl = clean_fn_decl_with_args(cx, sig.decl, args); + if sig.header.is_async() { + decl.output = decl.sugared_async_return_type(); + } (generics, decl) }); Box::new(Function { decl, generics }) @@ -994,305 +1026,332 @@ fn clean_trait_ref<'tcx>(trait_ref: &hir::TraitRef<'tcx>, cx: &mut DocContext<'t path } -impl<'tcx> Clean<'tcx, PolyTrait> for hir::PolyTraitRef<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> PolyTrait { - PolyTrait { - trait_: clean_trait_ref(&self.trait_ref, cx), - generic_params: self - .bound_generic_params - .iter() - .filter(|p| !is_elided_lifetime(p)) - .map(|x| clean_generic_param(cx, None, x)) - .collect(), - } +fn clean_poly_trait_ref<'tcx>( + poly_trait_ref: &hir::PolyTraitRef<'tcx>, + cx: &mut DocContext<'tcx>, +) -> PolyTrait { + PolyTrait { + trait_: clean_trait_ref(&poly_trait_ref.trait_ref, cx), + generic_params: poly_trait_ref + .bound_generic_params + .iter() + .filter(|p| !is_elided_lifetime(p)) + .map(|x| clean_generic_param(cx, None, x)) + .collect(), } } -impl<'tcx> Clean<'tcx, Item> for hir::TraitItem<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> Item { - let local_did = self.def_id.to_def_id(); - cx.with_param_env(local_did, |cx| { - let inner = match self.kind { - hir::TraitItemKind::Const(ty, Some(default)) => AssocConstItem( - clean_ty(ty, cx), - ConstantKind::Local { def_id: local_did, body: default }, - ), - hir::TraitItemKind::Const(ty, None) => TyAssocConstItem(clean_ty(ty, cx)), - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - let m = clean_function(cx, sig, self.generics, body); - MethodItem(m, None) - } - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(names)) => { - let (generics, decl) = enter_impl_trait(cx, |cx| { - // NOTE: generics must be cleaned before args - let generics = self.generics.clean(cx); - let args = clean_args_from_types_and_names(cx, sig.decl.inputs, names); - let decl = clean_fn_decl_with_args(cx, sig.decl, args); - (generics, decl) - }); - TyMethodItem(Box::new(Function { decl, generics })) - } - hir::TraitItemKind::Type(bounds, Some(default)) => { - let generics = enter_impl_trait(cx, |cx| self.generics.clean(cx)); - let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect(); - let item_type = clean_middle_ty(hir_ty_to_ty(cx.tcx, default), cx, None); - AssocTypeItem( - Box::new(Typedef { - type_: clean_ty(default, cx), - generics, - item_type: Some(item_type), - }), - bounds, - ) - } - hir::TraitItemKind::Type(bounds, None) => { - let generics = enter_impl_trait(cx, |cx| self.generics.clean(cx)); - let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect(); - TyAssocTypeItem(Box::new(generics), bounds) - } - }; - let what_rustc_thinks = - Item::from_def_id_and_parts(local_did, Some(self.ident.name), inner, cx); - // Trait items always inherit the trait's visibility -- we don't want to show `pub`. - Item { visibility: Inherited, ..what_rustc_thinks } - }) - } +fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext<'tcx>) -> Item { + let local_did = trait_item.owner_id.to_def_id(); + cx.with_param_env(local_did, |cx| { + let inner = match trait_item.kind { + hir::TraitItemKind::Const(ty, Some(default)) => AssocConstItem( + clean_ty(ty, cx), + ConstantKind::Local { def_id: local_did, body: default }, + ), + hir::TraitItemKind::Const(ty, None) => TyAssocConstItem(clean_ty(ty, cx)), + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { + let m = clean_function(cx, sig, trait_item.generics, body); + MethodItem(m, None) + } + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(names)) => { + let (generics, decl) = enter_impl_trait(cx, |cx| { + // NOTE: generics must be cleaned before args + let generics = clean_generics(trait_item.generics, cx); + let args = clean_args_from_types_and_names(cx, sig.decl.inputs, names); + let decl = clean_fn_decl_with_args(cx, sig.decl, args); + (generics, decl) + }); + TyMethodItem(Box::new(Function { decl, generics })) + } + hir::TraitItemKind::Type(bounds, Some(default)) => { + let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); + let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(); + let item_type = clean_middle_ty(hir_ty_to_ty(cx.tcx, default), cx, None); + AssocTypeItem( + Box::new(Typedef { + type_: clean_ty(default, cx), + generics, + item_type: Some(item_type), + }), + bounds, + ) + } + hir::TraitItemKind::Type(bounds, None) => { + let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); + let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(); + TyAssocTypeItem(Box::new(generics), bounds) + } + }; + let what_rustc_thinks = + Item::from_def_id_and_parts(local_did, Some(trait_item.ident.name), inner, cx); + // Trait items always inherit the trait's visibility -- we don't want to show `pub`. + Item { visibility: Inherited, ..what_rustc_thinks } + }) } -impl<'tcx> Clean<'tcx, Item> for hir::ImplItem<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> Item { - let local_did = self.def_id.to_def_id(); - cx.with_param_env(local_did, |cx| { - let inner = match self.kind { - hir::ImplItemKind::Const(ty, expr) => { - let default = ConstantKind::Local { def_id: local_did, body: expr }; - AssocConstItem(clean_ty(ty, cx), default) - } - hir::ImplItemKind::Fn(ref sig, body) => { - let m = clean_function(cx, sig, self.generics, body); - let defaultness = cx.tcx.impl_defaultness(self.def_id); - MethodItem(m, Some(defaultness)) - } - hir::ImplItemKind::TyAlias(hir_ty) => { - let type_ = clean_ty(hir_ty, cx); - let generics = self.generics.clean(cx); - let item_type = clean_middle_ty(hir_ty_to_ty(cx.tcx, hir_ty), cx, None); - AssocTypeItem( - Box::new(Typedef { type_, generics, item_type: Some(item_type) }), - Vec::new(), - ) - } - }; +pub(crate) fn clean_impl_item<'tcx>( + impl_: &hir::ImplItem<'tcx>, + cx: &mut DocContext<'tcx>, +) -> Item { + let local_did = impl_.owner_id.to_def_id(); + cx.with_param_env(local_did, |cx| { + let inner = match impl_.kind { + hir::ImplItemKind::Const(ty, expr) => { + let default = ConstantKind::Local { def_id: local_did, body: expr }; + AssocConstItem(clean_ty(ty, cx), default) + } + hir::ImplItemKind::Fn(ref sig, body) => { + let m = clean_function(cx, sig, impl_.generics, body); + let defaultness = cx.tcx.impl_defaultness(impl_.owner_id); + MethodItem(m, Some(defaultness)) + } + hir::ImplItemKind::Type(hir_ty) => { + let type_ = clean_ty(hir_ty, cx); + let generics = clean_generics(impl_.generics, cx); + let item_type = clean_middle_ty(hir_ty_to_ty(cx.tcx, hir_ty), cx, None); + AssocTypeItem( + Box::new(Typedef { type_, generics, item_type: Some(item_type) }), + Vec::new(), + ) + } + }; - let mut what_rustc_thinks = - Item::from_def_id_and_parts(local_did, Some(self.ident.name), inner, cx); + let mut what_rustc_thinks = + Item::from_def_id_and_parts(local_did, Some(impl_.ident.name), inner, cx); - let impl_ref = cx.tcx.impl_trait_ref(cx.tcx.local_parent(self.def_id)); + let impl_ref = cx.tcx.impl_trait_ref(cx.tcx.local_parent(impl_.owner_id.def_id)); - // Trait impl items always inherit the impl's visibility -- - // we don't want to show `pub`. - if impl_ref.is_some() { - what_rustc_thinks.visibility = Inherited; - } + // Trait impl items always inherit the impl's visibility -- + // we don't want to show `pub`. + if impl_ref.is_some() { + what_rustc_thinks.visibility = Inherited; + } - what_rustc_thinks - }) - } + what_rustc_thinks + }) } -impl<'tcx> Clean<'tcx, Item> for ty::AssocItem { - fn clean(&self, cx: &mut DocContext<'tcx>) -> Item { - let tcx = cx.tcx; - let kind = match self.kind { - ty::AssocKind::Const => { - let ty = clean_middle_ty(tcx.type_of(self.def_id), cx, Some(self.def_id)); +pub(crate) fn clean_middle_assoc_item<'tcx>( + assoc_item: &ty::AssocItem, + cx: &mut DocContext<'tcx>, +) -> Item { + let tcx = cx.tcx; + let kind = match assoc_item.kind { + ty::AssocKind::Const => { + let ty = clean_middle_ty(tcx.type_of(assoc_item.def_id), cx, Some(assoc_item.def_id)); - let provided = match self.container { - ty::ImplContainer => true, - ty::TraitContainer => tcx.impl_defaultness(self.def_id).has_value(), - }; - if provided { - AssocConstItem(ty, ConstantKind::Extern { def_id: self.def_id }) - } else { - TyAssocConstItem(ty) - } + let provided = match assoc_item.container { + ty::ImplContainer => true, + ty::TraitContainer => tcx.impl_defaultness(assoc_item.def_id).has_value(), + }; + if provided { + AssocConstItem(ty, ConstantKind::Extern { def_id: assoc_item.def_id }) + } else { + TyAssocConstItem(ty) } - ty::AssocKind::Fn => { - let generics = clean_ty_generics( - cx, - tcx.generics_of(self.def_id), - tcx.explicit_predicates_of(self.def_id), - ); - let sig = tcx.fn_sig(self.def_id); - let mut decl = clean_fn_decl_from_did_and_sig(cx, Some(self.def_id), sig); - - if self.fn_has_self_parameter { - let self_ty = match self.container { - ty::ImplContainer => tcx.type_of(self.container_id(tcx)), - ty::TraitContainer => tcx.types.self_param, - }; - let self_arg_ty = sig.input(0).skip_binder(); - if self_arg_ty == self_ty { - decl.inputs.values[0].type_ = Generic(kw::SelfUpper); - } else if let ty::Ref(_, ty, _) = *self_arg_ty.kind() { - if ty == self_ty { - match decl.inputs.values[0].type_ { - BorrowedRef { ref mut type_, .. } => { - **type_ = Generic(kw::SelfUpper) - } - _ => unreachable!(), - } + } + ty::AssocKind::Fn => { + let generics = clean_ty_generics( + cx, + tcx.generics_of(assoc_item.def_id), + tcx.explicit_predicates_of(assoc_item.def_id), + ); + let sig = tcx.fn_sig(assoc_item.def_id); + let mut decl = clean_fn_decl_from_did_and_sig(cx, Some(assoc_item.def_id), sig); + + if assoc_item.fn_has_self_parameter { + let self_ty = match assoc_item.container { + ty::ImplContainer => tcx.type_of(assoc_item.container_id(tcx)), + ty::TraitContainer => tcx.types.self_param, + }; + let self_arg_ty = sig.input(0).skip_binder(); + if self_arg_ty == self_ty { + decl.inputs.values[0].type_ = Generic(kw::SelfUpper); + } else if let ty::Ref(_, ty, _) = *self_arg_ty.kind() { + if ty == self_ty { + match decl.inputs.values[0].type_ { + BorrowedRef { ref mut type_, .. } => **type_ = Generic(kw::SelfUpper), + _ => unreachable!(), } } } + } - let provided = match self.container { - ty::ImplContainer => true, - ty::TraitContainer => self.defaultness(tcx).has_value(), + let provided = match assoc_item.container { + ty::ImplContainer => true, + ty::TraitContainer => assoc_item.defaultness(tcx).has_value(), + }; + if provided { + let defaultness = match assoc_item.container { + ty::ImplContainer => Some(assoc_item.defaultness(tcx)), + ty::TraitContainer => None, }; - if provided { - let defaultness = match self.container { - ty::ImplContainer => Some(self.defaultness(tcx)), - ty::TraitContainer => None, - }; - MethodItem(Box::new(Function { generics, decl }), defaultness) - } else { - TyMethodItem(Box::new(Function { generics, decl })) - } + MethodItem(Box::new(Function { generics, decl }), defaultness) + } else { + TyMethodItem(Box::new(Function { generics, decl })) } - ty::AssocKind::Type => { - let my_name = self.name; - - fn param_eq_arg(param: &GenericParamDef, arg: &GenericArg) -> bool { - match (¶m.kind, arg) { - (GenericParamDefKind::Type { .. }, GenericArg::Type(Type::Generic(ty))) - if *ty == param.name => - { - true - } - ( - GenericParamDefKind::Lifetime { .. }, - GenericArg::Lifetime(Lifetime(lt)), - ) if *lt == param.name => true, - (GenericParamDefKind::Const { .. }, GenericArg::Const(c)) => { - match &c.kind { - ConstantKind::TyConst { expr } => expr == param.name.as_str(), - _ => false, - } - } - _ => false, + } + ty::AssocKind::Type => { + let my_name = assoc_item.name; + + fn param_eq_arg(param: &GenericParamDef, arg: &GenericArg) -> bool { + match (¶m.kind, arg) { + (GenericParamDefKind::Type { .. }, GenericArg::Type(Type::Generic(ty))) + if *ty == param.name => + { + true + } + (GenericParamDefKind::Lifetime { .. }, GenericArg::Lifetime(Lifetime(lt))) + if *lt == param.name => + { + true } + (GenericParamDefKind::Const { .. }, GenericArg::Const(c)) => match &c.kind { + ConstantKind::TyConst { expr } => expr == param.name.as_str(), + _ => false, + }, + _ => false, } + } - if let ty::TraitContainer = self.container { - let bounds = tcx.explicit_item_bounds(self.def_id); - let predicates = ty::GenericPredicates { parent: None, predicates: bounds }; - let mut generics = - clean_ty_generics(cx, tcx.generics_of(self.def_id), predicates); - // Filter out the bounds that are (likely?) directly attached to the associated type, - // as opposed to being located in the where clause. - let mut bounds = generics - .where_predicates - .drain_filter(|pred| match *pred { - WherePredicate::BoundPredicate { - ty: QPath { ref assoc, ref self_type, ref trait_, .. }, - .. - } => { - if assoc.name != my_name { - return false; - } - if trait_.def_id() != self.container_id(tcx) { - return false; - } - match **self_type { - Generic(ref s) if *s == kw::SelfUpper => {} - _ => return false, - } - match &assoc.args { - GenericArgs::AngleBracketed { args, bindings } => { - if !bindings.is_empty() - || generics - .params - .iter() - .zip(args.iter()) - .any(|(param, arg)| !param_eq_arg(param, arg)) - { - return false; - } - } - GenericArgs::Parenthesized { .. } => { - // The only time this happens is if we're inside the rustdoc for Fn(), - // which only has one associated type, which is not a GAT, so whatever. + if let ty::TraitContainer = assoc_item.container { + let bounds = tcx.explicit_item_bounds(assoc_item.def_id); + let predicates = tcx.explicit_predicates_of(assoc_item.def_id).predicates; + let predicates = + tcx.arena.alloc_from_iter(bounds.into_iter().chain(predicates).copied()); + let mut generics = clean_ty_generics( + cx, + tcx.generics_of(assoc_item.def_id), + ty::GenericPredicates { parent: None, predicates }, + ); + // Move bounds that are (likely) directly attached to the associated type + // from the where clause to the associated type. + // There is no guarantee that this is what the user actually wrote but we have + // no way of knowing. + let mut bounds = generics + .where_predicates + .drain_filter(|pred| match *pred { + WherePredicate::BoundPredicate { + ty: QPath(box QPathData { ref assoc, ref self_type, ref trait_, .. }), + .. + } => { + if assoc.name != my_name { + return false; + } + if trait_.def_id() != assoc_item.container_id(tcx) { + return false; + } + match *self_type { + Generic(ref s) if *s == kw::SelfUpper => {} + _ => return false, + } + match &assoc.args { + GenericArgs::AngleBracketed { args, bindings } => { + if !bindings.is_empty() + || generics + .params + .iter() + .zip(args.iter()) + .any(|(param, arg)| !param_eq_arg(param, arg)) + { + return false; } } - true - } - _ => false, - }) - .flat_map(|pred| { - if let WherePredicate::BoundPredicate { bounds, .. } = pred { - bounds - } else { - unreachable!() + GenericArgs::Parenthesized { .. } => { + // The only time this happens is if we're inside the rustdoc for Fn(), + // which only has one associated type, which is not a GAT, so whatever. + } } - }) - .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); + true } - None => bounds.push(GenericBound::maybe_sized(cx)), + _ => false, + }) + .flat_map(|pred| { + if let WherePredicate::BoundPredicate { bounds, .. } = pred { + bounds + } else { + unreachable!() + } + }) + .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); } - - if tcx.impl_defaultness(self.def_id).has_value() { - AssocTypeItem( - Box::new(Typedef { - type_: clean_middle_ty( - tcx.type_of(self.def_id), - cx, - Some(self.def_id), - ), - generics, - // FIXME: should we obtain the Type from HIR and pass it on here? - item_type: None, - }), - bounds, - ) + None => bounds.push(GenericBound::maybe_sized(cx)), + } + // Move bounds that are (likely) directly attached to the parameters of the + // (generic) associated type from the where clause to the respective parameter. + // There is no guarantee that this is what the user actually wrote but we have + // no way of knowing. + let mut where_predicates = Vec::new(); + for mut pred in generics.where_predicates { + if let WherePredicate::BoundPredicate { ty: Generic(arg), bounds, .. } = &mut pred + && let Some(GenericParamDef { + kind: GenericParamDefKind::Type { bounds: param_bounds, .. }, + .. + }) = generics.params.iter_mut().find(|param| ¶m.name == arg) + { + param_bounds.extend(mem::take(bounds)); } else { - TyAssocTypeItem(Box::new(generics), bounds) + where_predicates.push(pred); } - } else { - // FIXME: when could this happen? Associated items in inherent impls? + } + generics.where_predicates = where_predicates; + + if tcx.impl_defaultness(assoc_item.def_id).has_value() { AssocTypeItem( Box::new(Typedef { - type_: clean_middle_ty(tcx.type_of(self.def_id), cx, Some(self.def_id)), - generics: Generics { params: Vec::new(), where_predicates: Vec::new() }, + type_: clean_middle_ty( + tcx.type_of(assoc_item.def_id), + cx, + Some(assoc_item.def_id), + ), + generics, + // FIXME: should we obtain the Type from HIR and pass it on here? item_type: None, }), - Vec::new(), + bounds, ) + } else { + TyAssocTypeItem(Box::new(generics), bounds) } + } else { + // FIXME: when could this happen? Associated items in inherent impls? + AssocTypeItem( + Box::new(Typedef { + type_: clean_middle_ty( + tcx.type_of(assoc_item.def_id), + cx, + Some(assoc_item.def_id), + ), + generics: Generics { params: Vec::new(), where_predicates: Vec::new() }, + item_type: None, + }), + Vec::new(), + ) } - }; - - let mut what_rustc_thinks = - Item::from_def_id_and_parts(self.def_id, Some(self.name), kind, cx); + } + }; - let impl_ref = tcx.impl_trait_ref(tcx.parent(self.def_id)); + let mut what_rustc_thinks = + Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name), kind, cx); - // Trait impl items always inherit the impl's visibility -- - // we don't want to show `pub`. - if impl_ref.is_some() { - what_rustc_thinks.visibility = Visibility::Inherited; - } + let impl_ref = tcx.impl_trait_ref(tcx.parent(assoc_item.def_id)); - what_rustc_thinks + // Trait impl items always inherit the impl's visibility -- + // we don't want to show `pub`. + if impl_ref.is_some() { + what_rustc_thinks.visibility = Visibility::Inherited; } + + what_rustc_thinks } fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type { @@ -1328,18 +1387,18 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type let trait_def = cx.tcx.associated_item(p.res.def_id()).container_id(cx.tcx); let trait_ = self::Path { res: Res::Def(DefKind::Trait, trait_def), - segments: trait_segments.iter().map(|x| x.clean(cx)).collect(), + segments: trait_segments.iter().map(|x| clean_path_segment(x, cx)).collect(), }; register_res(cx, trait_.res); - let self_def_id = DefId::local(qself.hir_id.owner.local_def_index); + let self_def_id = DefId::local(qself.hir_id.owner.def_id.local_def_index); let self_type = clean_ty(qself, cx); let should_show_cast = compute_should_show_cast(Some(self_def_id), &trait_, &self_type); - Type::QPath { - assoc: Box::new(p.segments.last().expect("segments were empty").clean(cx)), + Type::QPath(Box::new(QPathData { + assoc: clean_path_segment(p.segments.last().expect("segments were empty"), cx), should_show_cast, - self_type: Box::new(self_type), + self_type, trait_, - } + })) } hir::QPath::TypeRelative(qself, segment) => { let ty = hir_ty_to_ty(cx.tcx, hir_ty); @@ -1354,12 +1413,12 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type let self_def_id = res.opt_def_id(); let self_type = clean_ty(qself, cx); let should_show_cast = compute_should_show_cast(self_def_id, &trait_, &self_type); - Type::QPath { - assoc: Box::new(segment.clean(cx)), + Type::QPath(Box::new(QPathData { + assoc: clean_path_segment(segment, cx), should_show_cast, - self_type: Box::new(self_type), + self_type, trait_, - } + })) } hir::QPath::LangItem(..) => bug!("clean: requiring documentation of lang item"), } @@ -1372,7 +1431,7 @@ fn maybe_expand_private_type_alias<'tcx>( let Res::Def(DefKind::TyAlias, def_id) = path.res else { return None }; // Substitute private type aliases let def_id = def_id.as_local()?; - let alias = if !cx.cache.access_levels.is_exported(def_id.to_def_id()) { + let alias = if !cx.cache.effective_visibilities.is_exported(def_id.to_def_id()) { &cx.tcx.hir().expect_item(def_id).kind } else { return None; @@ -1398,7 +1457,7 @@ fn maybe_expand_private_type_alias<'tcx>( } _ => None, }); - if let Some(lt) = lifetime.cloned() { + if let Some(lt) = lifetime { let lt_def_id = cx.tcx.hir().local_def_id(param.hir_id); let cleaned = if !lt.is_elided() { clean_lifetime(lt, cx) } else { Lifetime::elided() }; @@ -1486,7 +1545,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T // as we currently do not supply the parent generics to anonymous constants // but do allow `ConstKind::Param`. // - // `const_eval_poly` tries to to first substitute generic parameters which + // `const_eval_poly` tries to first substitute generic parameters which // results in an ICE while manually constructing the constant and using `eval` // does nothing for `ConstKind::Param`. let ct = ty::Const::from_anon_const(cx.tcx, def_id); @@ -1498,30 +1557,29 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T Array(Box::new(clean_ty(ty, cx)), length) } TyKind::Tup(tys) => Tuple(tys.iter().map(|ty| clean_ty(ty, cx)).collect()), - TyKind::OpaqueDef(item_id, _) => { + TyKind::OpaqueDef(item_id, _, _) => { let item = cx.tcx.hir().item(item_id); if let hir::ItemKind::OpaqueTy(ref ty) = item.kind { - ImplTrait(ty.bounds.iter().filter_map(|x| x.clean(cx)).collect()) + ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect()) } else { unreachable!() } } TyKind::Path(_) => clean_qpath(ty, cx), TyKind::TraitObject(bounds, ref lifetime, _) => { - let bounds = bounds.iter().map(|bound| bound.clean(cx)).collect(); + let bounds = bounds.iter().map(|bound| clean_poly_trait_ref(bound, cx)).collect(); let lifetime = if !lifetime.is_elided() { Some(clean_lifetime(*lifetime, cx)) } else { None }; DynTrait(bounds, lifetime) } - TyKind::BareFn(barefn) => BareFunction(Box::new(barefn.clean(cx))), + TyKind::BareFn(barefn) => BareFunction(Box::new(clean_bare_fn_ty(barefn, cx))), // Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s. - TyKind::Infer | TyKind::Err => Infer, - TyKind::Typeof(..) => panic!("unimplemented type {:?}", ty.kind), + TyKind::Infer | TyKind::Err | TyKind::Typeof(..) => Infer, } } /// Returns `None` if the type could not be normalized -fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'_>) -> Option<Ty<'tcx>> { +fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { // HACK: low-churn fix for #79459 while we wait for a trait normalization fix if !cx.tcx.sess.opts.unstable_opts.normalize_docs { return None; @@ -1532,13 +1590,11 @@ fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'_>) -> Option<Ty<'tcx>> { use rustc_middle::traits::ObligationCause; // Try to normalize `<X as Y>::T` to a type - let lifted = ty.lift_to_tcx(cx.tcx).unwrap(); - let normalized = cx.tcx.infer_ctxt().enter(|infcx| { - infcx - .at(&ObligationCause::dummy(), cx.param_env) - .normalize(lifted) - .map(|resolved| infcx.resolve_vars_if_possible(resolved.value)) - }); + let infcx = cx.tcx.infer_ctxt().build(); + let normalized = infcx + .at(&ObligationCause::dummy(), cx.param_env) + .normalize(ty) + .map(|resolved| infcx.resolve_vars_if_possible(resolved.value)); match normalized { Ok(normalized_value) => { debug!("normalized {:?} to {:?}", ty, normalized_value); @@ -1552,12 +1608,12 @@ fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'_>) -> Option<Ty<'tcx>> { } pub(crate) fn clean_middle_ty<'tcx>( - this: Ty<'tcx>, + ty: Ty<'tcx>, cx: &mut DocContext<'tcx>, def_id: Option<DefId>, ) -> Type { - trace!("cleaning type: {:?}", this); - let ty = normalize(cx, this).unwrap_or(this); + trace!("cleaning type: {:?}", ty); + let ty = normalize(cx, ty).unwrap_or(ty); match *ty.kind() { ty::Never => Primitive(PrimitiveType::Never), ty::Bool => Primitive(PrimitiveType::Bool), @@ -1567,8 +1623,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Float(float_ty) => Primitive(float_ty.into()), ty::Str => Primitive(PrimitiveType::Str), ty::Slice(ty) => Slice(Box::new(clean_middle_ty(ty, cx, None))), - ty::Array(ty, n) => { - let mut n = cx.tcx.lift(n).expect("array lift failed"); + ty::Array(ty, mut n) => { n = n.eval(cx.tcx, ty::ParamEnv::reveal_all()); let n = print_const(cx, n); Array(Box::new(clean_middle_ty(ty, cx, None)), n) @@ -1580,7 +1635,6 @@ pub(crate) fn clean_middle_ty<'tcx>( type_: Box::new(clean_middle_ty(ty, cx, None)), }, ty::FnDef(..) | ty::FnPtr(_) => { - let ty = cx.tcx.lift(this).expect("FnPtr lift failed"); let sig = ty.fn_sig(cx.tcx); let decl = clean_fn_decl_from_did_and_sig(cx, None, sig); BareFunction(Box::new(BareFunctionDecl { @@ -1598,22 +1652,23 @@ pub(crate) fn clean_middle_ty<'tcx>( AdtKind::Enum => ItemType::Enum, }; inline::record_extern_fqn(cx, did, kind); - let path = external_path(cx, did, false, vec![], substs); + let path = external_path(cx, did, false, ThinVec::new(), substs); Type::Path { path } } ty::Foreign(did) => { inline::record_extern_fqn(cx, did, ItemType::ForeignType); - let path = external_path(cx, did, false, vec![], InternalSubsts::empty()); + let path = external_path(cx, did, false, ThinVec::new(), InternalSubsts::empty()); Type::Path { path } } - ty::Dynamic(obj, ref reg) => { + ty::Dynamic(obj, ref reg, _) => { // HACK: pick the first `did` as the `did` of the trait object. Someone // might want to implement "native" support for marker-trait-only // trait objects. - let mut dids = obj.principal_def_id().into_iter().chain(obj.auto_traits()); - let did = dids - .next() - .unwrap_or_else(|| panic!("found trait object `{:?}` with no traits?", this)); + let mut dids = obj.auto_traits(); + let did = obj + .principal_def_id() + .or_else(|| dids.next()) + .unwrap_or_else(|| panic!("found trait object `{:?}` with no traits?", ty)); let substs = match obj.principal() { Some(principal) => principal.skip_binder().substs, // marker traits have no substs. @@ -1623,23 +1678,20 @@ pub(crate) fn clean_middle_ty<'tcx>( inline::record_extern_fqn(cx, did, ItemType::Trait); let lifetime = clean_middle_region(*reg); - let mut bounds = vec![]; - - for did in dids { - let empty = cx.tcx.intern_substs(&[]); - let path = external_path(cx, did, false, vec![], empty); - inline::record_extern_fqn(cx, did, ItemType::Trait); - let bound = PolyTrait { trait_: path, generic_params: Vec::new() }; - bounds.push(bound); - } + let mut bounds = dids + .map(|did| { + let empty = cx.tcx.intern_substs(&[]); + let path = external_path(cx, did, false, ThinVec::new(), empty); + inline::record_extern_fqn(cx, did, ItemType::Trait); + PolyTrait { trait_: path, generic_params: Vec::new() } + }) + .collect::<Vec<_>>(); - let mut bindings = vec![]; - for pb in obj.projection_bounds() { - bindings.push(TypeBinding { + let bindings = obj + .projection_bounds() + .map(|pb| TypeBinding { assoc: projection_to_path_segment( pb.skip_binder() - .lift_to_tcx(cx.tcx) - .unwrap() // HACK(compiler-errors): Doesn't actually matter what self // type we put here, because we're only using the GAT's substs. .with_self_ty(cx.tcx, cx.tcx.types.self_param) @@ -1649,8 +1701,8 @@ pub(crate) fn clean_middle_ty<'tcx>( kind: TypeBindingKind::Equality { term: clean_middle_term(pb.skip_binder().term, cx), }, - }); - } + }) + .collect(); let path = external_path(cx, did, false, bindings, substs); bounds.insert(0, PolyTrait { trait_: path, generic_params: Vec::new() }); @@ -1672,66 +1724,13 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Opaque(def_id, substs) => { // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, // by looking up the bounds associated with the def_id. - let substs = cx.tcx.lift(substs).expect("Opaque lift failed"); let bounds = cx .tcx .explicit_item_bounds(def_id) .iter() .map(|(bound, _)| EarlyBinder(*bound).subst(cx.tcx, substs)) .collect::<Vec<_>>(); - let mut regions = vec![]; - let mut has_sized = false; - let mut bounds = bounds - .iter() - .filter_map(|bound| { - let bound_predicate = bound.kind(); - let trait_ref = match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(tr) => bound_predicate.rebind(tr.trait_ref), - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { - if let Some(r) = clean_middle_region(reg) { - regions.push(GenericBound::Outlives(r)); - } - return None; - } - _ => return None, - }; - - if let Some(sized) = cx.tcx.lang_items().sized_trait() { - if trait_ref.def_id() == sized { - has_sized = true; - return None; - } - } - - let bindings: Vec<_> = bounds - .iter() - .filter_map(|bound| { - if let ty::PredicateKind::Projection(proj) = bound.kind().skip_binder() - { - if proj.projection_ty.trait_ref(cx.tcx) == trait_ref.skip_binder() { - Some(TypeBinding { - assoc: projection_to_path_segment(proj.projection_ty, cx), - kind: TypeBindingKind::Equality { - term: clean_middle_term(proj.term, cx), - }, - }) - } else { - None - } - } else { - None - } - }) - .collect(); - - Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, &bindings)) - }) - .collect::<Vec<_>>(); - bounds.extend(regions); - if !has_sized && !bounds.is_empty() { - bounds.insert(0, GenericBound::maybe_sized(cx)); - } - ImplTrait(bounds) + clean_middle_opaque_bounds(cx, bounds) } ty::Closure(..) => panic!("Closure"), @@ -1744,6 +1743,64 @@ pub(crate) fn clean_middle_ty<'tcx>( } } +fn clean_middle_opaque_bounds<'tcx>( + cx: &mut DocContext<'tcx>, + bounds: Vec<ty::Predicate<'tcx>>, +) -> Type { + let mut regions = vec![]; + let mut has_sized = false; + let mut bounds = bounds + .iter() + .filter_map(|bound| { + let bound_predicate = bound.kind(); + let trait_ref = match bound_predicate.skip_binder() { + ty::PredicateKind::Trait(tr) => bound_predicate.rebind(tr.trait_ref), + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { + if let Some(r) = clean_middle_region(reg) { + regions.push(GenericBound::Outlives(r)); + } + return None; + } + _ => return None, + }; + + if let Some(sized) = cx.tcx.lang_items().sized_trait() { + if trait_ref.def_id() == sized { + has_sized = true; + return None; + } + } + + let bindings: ThinVec<_> = bounds + .iter() + .filter_map(|bound| { + if let ty::PredicateKind::Projection(proj) = bound.kind().skip_binder() { + if proj.projection_ty.trait_ref(cx.tcx) == trait_ref.skip_binder() { + Some(TypeBinding { + assoc: projection_to_path_segment(proj.projection_ty, cx), + kind: TypeBindingKind::Equality { + term: clean_middle_term(proj.term, cx), + }, + }) + } else { + None + } + } else { + None + } + }) + .collect(); + + Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, bindings)) + }) + .collect::<Vec<_>>(); + bounds.extend(regions); + if !has_sized && !bounds.is_empty() { + bounds.insert(0, GenericBound::maybe_sized(cx)); + } + ImplTrait(bounds) +} + pub(crate) fn clean_field<'tcx>(field: &hir::FieldDef<'tcx>, cx: &mut DocContext<'tcx>) -> Item { let def_id = cx.tcx.hir().local_def_id(field.hir_id).to_def_id(); clean_field_with_def_id(def_id, field.ident.name, clean_ty(field.ty, cx), cx) @@ -1783,21 +1840,19 @@ fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool { } } -pub(crate) fn clean_visibility(vis: ty::Visibility) -> Visibility { +pub(crate) fn clean_visibility(vis: ty::Visibility<DefId>) -> Visibility { match vis { ty::Visibility::Public => Visibility::Public, - // NOTE: this is not quite right: `ty` uses `Invisible` to mean 'private', - // while rustdoc really does mean inherited. That means that for enum variants, such as - // `pub enum E { V }`, `V` will be marked as `Public` by `ty`, but as `Inherited` by rustdoc. - // Various parts of clean override `tcx.visibility` explicitly to make sure this distinction is captured. - ty::Visibility::Invisible => Visibility::Inherited, ty::Visibility::Restricted(module) => Visibility::Restricted(module), } } pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocContext<'tcx>) -> Item { let kind = match variant.ctor_kind { - CtorKind::Const => Variant::CLike, + CtorKind::Const => Variant::CLike(match variant.discr { + ty::VariantDiscr::Explicit(def_id) => Some(Discriminant { expr: None, value: def_id }), + ty::VariantDiscr::Relative(_) => None, + }), CtorKind::Fn => Variant::Tuple( variant.fields.iter().map(|field| clean_middle_field(field, cx)).collect(), ), @@ -1814,6 +1869,7 @@ pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocCont fn clean_variant_data<'tcx>( variant: &hir::VariantData<'tcx>, + disr_expr: &Option<hir::AnonConst>, cx: &mut DocContext<'tcx>, ) -> Variant { match variant { @@ -1824,66 +1880,75 @@ fn clean_variant_data<'tcx>( hir::VariantData::Tuple(..) => { Variant::Tuple(variant.fields().iter().map(|x| clean_field(x, cx)).collect()) } - hir::VariantData::Unit(..) => Variant::CLike, + hir::VariantData::Unit(..) => Variant::CLike(disr_expr.map(|disr| Discriminant { + expr: Some(disr.body), + value: cx.tcx.hir().local_def_id(disr.hir_id).to_def_id(), + })), } } fn clean_path<'tcx>(path: &hir::Path<'tcx>, cx: &mut DocContext<'tcx>) -> Path { - Path { res: path.res, segments: path.segments.iter().map(|x| x.clean(cx)).collect() } + Path { + res: path.res, + segments: path.segments.iter().map(|x| clean_path_segment(x, cx)).collect(), + } } -impl<'tcx> Clean<'tcx, GenericArgs> for hir::GenericArgs<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> GenericArgs { - if self.parenthesized { - let output = clean_ty(self.bindings[0].ty(), cx); - let output = - if output != Type::Tuple(Vec::new()) { Some(Box::new(output)) } else { None }; - let inputs = self.inputs().iter().map(|x| clean_ty(x, cx)).collect::<Vec<_>>().into(); - GenericArgs::Parenthesized { inputs, output } - } else { - let args = self - .args - .iter() - .map(|arg| match arg { - hir::GenericArg::Lifetime(lt) if !lt.is_elided() => { - GenericArg::Lifetime(clean_lifetime(*lt, cx)) - } - hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()), - hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty, cx)), - hir::GenericArg::Const(ct) => GenericArg::Const(Box::new(clean_const(ct, cx))), - hir::GenericArg::Infer(_inf) => GenericArg::Infer, - }) - .collect::<Vec<_>>() - .into(); - let bindings = - self.bindings.iter().map(|x| clean_type_binding(x, cx)).collect::<Vec<_>>().into(); - GenericArgs::AngleBracketed { args, bindings } - } +fn clean_generic_args<'tcx>( + generic_args: &hir::GenericArgs<'tcx>, + cx: &mut DocContext<'tcx>, +) -> GenericArgs { + if generic_args.parenthesized { + let output = clean_ty(generic_args.bindings[0].ty(), cx); + let output = if output != Type::Tuple(Vec::new()) { Some(Box::new(output)) } else { None }; + let inputs = + generic_args.inputs().iter().map(|x| clean_ty(x, cx)).collect::<Vec<_>>().into(); + GenericArgs::Parenthesized { inputs, output } + } else { + let args = generic_args + .args + .iter() + .map(|arg| match arg { + hir::GenericArg::Lifetime(lt) if !lt.is_elided() => { + GenericArg::Lifetime(clean_lifetime(*lt, cx)) + } + hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()), + hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty, cx)), + hir::GenericArg::Const(ct) => GenericArg::Const(Box::new(clean_const(ct, cx))), + hir::GenericArg::Infer(_inf) => GenericArg::Infer, + }) + .collect::<Vec<_>>() + .into(); + let bindings = + generic_args.bindings.iter().map(|x| clean_type_binding(x, cx)).collect::<ThinVec<_>>(); + GenericArgs::AngleBracketed { args, bindings } } } -impl<'tcx> Clean<'tcx, PathSegment> for hir::PathSegment<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> PathSegment { - PathSegment { name: self.ident.name, args: self.args().clean(cx) } - } +fn clean_path_segment<'tcx>( + path: &hir::PathSegment<'tcx>, + cx: &mut DocContext<'tcx>, +) -> PathSegment { + PathSegment { name: path.ident.name, args: clean_generic_args(path.args(), cx) } } -impl<'tcx> Clean<'tcx, BareFunctionDecl> for hir::BareFnTy<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> BareFunctionDecl { - let (generic_params, decl) = enter_impl_trait(cx, |cx| { - // NOTE: generics must be cleaned before args - let generic_params = self - .generic_params - .iter() - .filter(|p| !is_elided_lifetime(p)) - .map(|x| clean_generic_param(cx, None, x)) - .collect(); - let args = clean_args_from_types_and_names(cx, self.decl.inputs, self.param_names); - let decl = clean_fn_decl_with_args(cx, self.decl, args); - (generic_params, decl) - }); - BareFunctionDecl { unsafety: self.unsafety, abi: self.abi, decl, generic_params } - } +fn clean_bare_fn_ty<'tcx>( + bare_fn: &hir::BareFnTy<'tcx>, + cx: &mut DocContext<'tcx>, +) -> BareFunctionDecl { + let (generic_params, decl) = enter_impl_trait(cx, |cx| { + // NOTE: generics must be cleaned before args + let generic_params = bare_fn + .generic_params + .iter() + .filter(|p| !is_elided_lifetime(p)) + .map(|x| clean_generic_param(cx, None, x)) + .collect(); + let args = clean_args_from_types_and_names(cx, bare_fn.decl.inputs, bare_fn.param_names); + let decl = clean_fn_decl_with_args(cx, bare_fn.decl, args); + (generic_params, decl) + }); + BareFunctionDecl { unsafety: bare_fn.unsafety, abi: bare_fn.abi, decl, generic_params } } fn clean_maybe_renamed_item<'tcx>( @@ -1893,7 +1958,7 @@ fn clean_maybe_renamed_item<'tcx>( ) -> Vec<Item> { use hir::ItemKind; - let def_id = item.def_id.to_def_id(); + let def_id = item.owner_id.to_def_id(); let mut name = renamed.unwrap_or_else(|| cx.tcx.hir().name(item.hir_id())); cx.with_param_env(def_id, |cx| { let kind = match item.kind { @@ -1905,33 +1970,33 @@ fn clean_maybe_renamed_item<'tcx>( kind: ConstantKind::Local { body: body_id, def_id }, }), ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy { - bounds: ty.bounds.iter().filter_map(|x| x.clean(cx)).collect(), - generics: ty.generics.clean(cx), + bounds: ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), + generics: clean_generics(ty.generics, cx), }), ItemKind::TyAlias(hir_ty, generics) => { let rustdoc_ty = clean_ty(hir_ty, cx); let ty = clean_middle_ty(hir_ty_to_ty(cx.tcx, hir_ty), cx, None); TypedefItem(Box::new(Typedef { type_: rustdoc_ty, - generics: generics.clean(cx), + generics: clean_generics(generics, cx), item_type: Some(ty), })) } ItemKind::Enum(ref def, generics) => EnumItem(Enum { - variants: def.variants.iter().map(|v| v.clean(cx)).collect(), - generics: generics.clean(cx), + variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(), + generics: clean_generics(generics, cx), }), ItemKind::TraitAlias(generics, bounds) => TraitAliasItem(TraitAlias { - generics: generics.clean(cx), - bounds: bounds.iter().filter_map(|x| x.clean(cx)).collect(), + generics: clean_generics(generics, cx), + bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), }), ItemKind::Union(ref variant_data, generics) => UnionItem(Union { - generics: generics.clean(cx), + generics: clean_generics(generics, cx), fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), }), ItemKind::Struct(ref variant_data, generics) => StructItem(Struct { struct_type: CtorKind::from_hir(variant_data), - generics: generics.clean(cx), + generics: clean_generics(generics, cx), fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), }), ItemKind::Impl(impl_) => return clean_impl(impl_, item.hir_id(), cx), @@ -1946,15 +2011,17 @@ fn clean_maybe_renamed_item<'tcx>( }) } ItemKind::Trait(_, _, generics, bounds, item_ids) => { - let items = - item_ids.iter().map(|ti| cx.tcx.hir().trait_item(ti.id).clean(cx)).collect(); + let items = item_ids + .iter() + .map(|ti| clean_trait_item(cx.tcx.hir().trait_item(ti.id), cx)) + .collect(); - TraitItem(Trait { + TraitItem(Box::new(Trait { def_id, items, - generics: generics.clean(cx), - bounds: bounds.iter().filter_map(|x| x.clean(cx)).collect(), - }) + generics: clean_generics(generics, cx), + bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), + })) } ItemKind::ExternCrate(orig_name) => { return clean_extern_crate(item, name, orig_name, cx); @@ -1969,14 +2036,12 @@ fn clean_maybe_renamed_item<'tcx>( }) } -impl<'tcx> Clean<'tcx, Item> for hir::Variant<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> Item { - let kind = VariantItem(clean_variant_data(&self.data, cx)); - let what_rustc_thinks = - Item::from_hir_id_and_parts(self.id, Some(self.ident.name), kind, cx); - // don't show `pub` for variants, which are always public - Item { visibility: Inherited, ..what_rustc_thinks } - } +fn clean_variant<'tcx>(variant: &hir::Variant<'tcx>, cx: &mut DocContext<'tcx>) -> Item { + let kind = VariantItem(clean_variant_data(&variant.data, &variant.disr_expr, cx)); + let what_rustc_thinks = + Item::from_hir_id_and_parts(variant.id, Some(variant.ident.name), kind, cx); + // don't show `pub` for variants, which are always public + Item { visibility: Inherited, ..what_rustc_thinks } } fn clean_impl<'tcx>( @@ -1987,8 +2052,11 @@ fn clean_impl<'tcx>( let tcx = cx.tcx; let mut ret = Vec::new(); let trait_ = impl_.of_trait.as_ref().map(|t| clean_trait_ref(t, cx)); - let items = - impl_.items.iter().map(|ii| tcx.hir().impl_item(ii.id).clean(cx)).collect::<Vec<_>>(); + let items = impl_ + .items + .iter() + .map(|ii| clean_impl_item(tcx.hir().impl_item(ii.id), cx)) + .collect::<Vec<_>>(); let def_id = tcx.hir().local_def_id(hir_id); // If this impl block is an implementation of the Deref trait, then we @@ -2005,7 +2073,7 @@ fn clean_impl<'tcx>( let mut make_item = |trait_: Option<Path>, for_: Type, items: Vec<Item>| { let kind = ImplItem(Box::new(Impl { unsafety: impl_.unsafety, - generics: impl_.generics.clean(cx), + generics: clean_generics(impl_.generics, cx), trait_, for_, items, @@ -2032,11 +2100,11 @@ fn clean_extern_crate<'tcx>( cx: &mut DocContext<'tcx>, ) -> Vec<Item> { // this is the ID of the `extern crate` statement - let cnum = cx.tcx.extern_mod_stmt_cnum(krate.def_id).unwrap_or(LOCAL_CRATE); + let cnum = cx.tcx.extern_mod_stmt_cnum(krate.owner_id.def_id).unwrap_or(LOCAL_CRATE); // this is the ID of the crate itself let crate_def_id = cnum.as_def_id(); let attrs = cx.tcx.hir().attrs(krate.hir_id()); - let ty_vis = cx.tcx.visibility(krate.def_id); + let ty_vis = cx.tcx.visibility(krate.owner_id); let please_inline = ty_vis.is_public() && attrs.iter().any(|a| { a.has_name(sym::doc) @@ -2054,7 +2122,7 @@ fn clean_extern_crate<'tcx>( if let Some(items) = inline::try_inline( cx, cx.tcx.parent_module(krate.hir_id()).to_def_id(), - Some(krate.def_id.to_def_id()), + Some(krate.owner_id.to_def_id()), res, name, Some(attrs), @@ -2090,11 +2158,11 @@ fn clean_use_statement<'tcx>( return Vec::new(); } - let visibility = cx.tcx.visibility(import.def_id); + let visibility = cx.tcx.visibility(import.owner_id); let attrs = cx.tcx.hir().attrs(import.hir_id()); let inline_attr = attrs.lists(sym::doc).get_word_attr(sym::inline); let pub_underscore = visibility.is_public() && name == kw::Underscore; - let current_mod = cx.tcx.parent_module_from_def_id(import.def_id); + let current_mod = cx.tcx.parent_module_from_def_id(import.owner_id.def_id); // The parent of the module in which this import resides. This // is the same as `current_mod` if that's already the top @@ -2106,8 +2174,8 @@ fn clean_use_statement<'tcx>( // `pub(super)` or higher. If the current module is the top level // module, there isn't really a parent module, which makes the results // meaningless. In this case, we make sure the answer is `false`. - let is_visible_from_parent_mod = visibility.is_accessible_from(parent_mod.to_def_id(), cx.tcx) - && !current_mod.is_top_level_module(); + let is_visible_from_parent_mod = + visibility.is_accessible_from(parent_mod, cx.tcx) && !current_mod.is_top_level_module(); if pub_underscore { if let Some(ref inline) = inline_attr { @@ -2165,7 +2233,7 @@ fn clean_use_statement<'tcx>( } if !denied { let mut visited = FxHashSet::default(); - let import_def_id = import.def_id.to_def_id(); + let import_def_id = import.owner_id.to_def_id(); if let Some(mut items) = inline::try_inline( cx, @@ -2188,7 +2256,7 @@ fn clean_use_statement<'tcx>( Import::new_simple(name, resolve_use_source(cx, path), true) }; - vec![Item::from_def_id_and_parts(import.def_id.to_def_id(), None, ImportItem(inner), cx)] + vec![Item::from_def_id_and_parts(import.owner_id.to_def_id(), None, ImportItem(inner), cx)] } fn clean_maybe_renamed_foreign_item<'tcx>( @@ -2196,13 +2264,13 @@ fn clean_maybe_renamed_foreign_item<'tcx>( item: &hir::ForeignItem<'tcx>, renamed: Option<Symbol>, ) -> Item { - let def_id = item.def_id.to_def_id(); + let def_id = item.owner_id.to_def_id(); cx.with_param_env(def_id, |cx| { let kind = match item.kind { hir::ForeignItemKind::Fn(decl, names, generics) => { let (generics, decl) = enter_impl_trait(cx, |cx| { // NOTE: generics must be cleaned before args - let generics = generics.clean(cx); + let generics = clean_generics(generics, cx); let args = clean_args_from_types_and_names(cx, decl.inputs, names); let decl = clean_fn_decl_with_args(cx, decl, args); (generics, decl) @@ -2229,13 +2297,16 @@ fn clean_type_binding<'tcx>( cx: &mut DocContext<'tcx>, ) -> TypeBinding { TypeBinding { - assoc: PathSegment { name: type_binding.ident.name, args: type_binding.gen_args.clean(cx) }, + assoc: PathSegment { + name: type_binding.ident.name, + args: clean_generic_args(type_binding.gen_args, cx), + }, kind: match type_binding.kind { hir::TypeBindingKind::Equality { ref term } => { TypeBindingKind::Equality { term: clean_hir_term(term, cx) } } hir::TypeBindingKind::Constraint { bounds } => TypeBindingKind::Constraint { - bounds: bounds.iter().filter_map(|b| b.clean(cx)).collect(), + bounds: bounds.iter().filter_map(|b| clean_generic_bound(b, cx)).collect(), }, }, } diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index af7813a77..1bcb9fcd5 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -14,7 +14,6 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_hir::def_id::DefId; use rustc_middle::ty; -use rustc_span::Symbol; use crate::clean; use crate::clean::GenericArgs as PP; @@ -26,38 +25,37 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> { // // We use `FxIndexMap` so that the insertion order is preserved to prevent messing up to // the order of the generated bounds. - let mut params: FxIndexMap<Symbol, (Vec<_>, Vec<_>)> = FxIndexMap::default(); + let mut tybounds = FxIndexMap::default(); let mut lifetimes = Vec::new(); let mut equalities = Vec::new(); - let mut tybounds = Vec::new(); for clause in clauses { match clause { - WP::BoundPredicate { ty, bounds, bound_params } => match ty { - clean::Generic(s) => { - let (b, p) = params.entry(s).or_default(); - b.extend(bounds); - p.extend(bound_params); - } - t => tybounds.push((t, (bounds, bound_params))), - }, + WP::BoundPredicate { ty, bounds, bound_params } => { + let (b, p): &mut (Vec<_>, Vec<_>) = tybounds.entry(ty).or_default(); + b.extend(bounds); + p.extend(bound_params); + } WP::RegionPredicate { lifetime, bounds } => { lifetimes.push((lifetime, bounds)); } - WP::EqPredicate { lhs, rhs } => equalities.push((lhs, rhs)), + WP::EqPredicate { lhs, rhs, bound_params } => equalities.push((lhs, rhs, bound_params)), } } // Look for equality predicates on associated types that can be merged into - // general bound predicates - equalities.retain(|&(ref lhs, ref rhs)| { - let Some((self_, trait_did, name)) = lhs.projection() else { - return true; - }; - let clean::Generic(generic) = self_ else { return true }; - let Some((bounds, _)) = params.get_mut(generic) else { return true }; - - merge_bounds(cx, bounds, trait_did, name, rhs) + // general bound predicates. + equalities.retain(|&(ref lhs, ref rhs, ref bound_params)| { + let Some((ty, trait_did, name)) = lhs.projection() else { return true; }; + let Some((bounds, _)) = tybounds.get_mut(ty) else { return true }; + let bound_params = bound_params + .into_iter() + .map(|param| clean::GenericParamDef { + name: param.0, + kind: clean::GenericParamDefKind::Lifetime { outlives: Vec::new() }, + }) + .collect(); + merge_bounds(cx, bounds, bound_params, trait_did, name, rhs) }); // And finally, let's reassemble everything @@ -65,23 +63,23 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> { clauses.extend( lifetimes.into_iter().map(|(lt, bounds)| WP::RegionPredicate { lifetime: lt, bounds }), ); - clauses.extend(params.into_iter().map(|(k, (bounds, params))| WP::BoundPredicate { - ty: clean::Generic(k), - bounds, - bound_params: params, - })); clauses.extend(tybounds.into_iter().map(|(ty, (bounds, bound_params))| WP::BoundPredicate { ty, bounds, bound_params, })); - clauses.extend(equalities.into_iter().map(|(lhs, rhs)| WP::EqPredicate { lhs, rhs })); + clauses.extend(equalities.into_iter().map(|(lhs, rhs, bound_params)| WP::EqPredicate { + lhs, + rhs, + bound_params, + })); clauses } pub(crate) fn merge_bounds( cx: &clean::DocContext<'_>, bounds: &mut Vec<clean::GenericBound>, + mut bound_params: Vec<clean::GenericParamDef>, trait_did: DefId, assoc: clean::PathSegment, rhs: &clean::Term, @@ -98,6 +96,14 @@ pub(crate) fn merge_bounds( return false; } let last = trait_ref.trait_.segments.last_mut().expect("segments were empty"); + + trait_ref.generic_params.append(&mut bound_params); + // Since the parameters (probably) originate from `tcx.collect_*_late_bound_regions` which + // returns a hash set, sort them alphabetically to guarantee a stable and deterministic + // output (and to fully deduplicate them). + trait_ref.generic_params.sort_unstable_by(|p, q| p.name.as_str().cmp(q.name.as_str())); + trait_ref.generic_params.dedup_by_key(|p| p.name); + match last.args { PP::AngleBracketed { ref mut bindings, .. } => { bindings.push(clean::TypeBinding { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 0e6de842c..cd1f972dc 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -8,6 +8,7 @@ use std::sync::OnceLock as OnceCell; use std::{cmp, fmt, iter}; use arrayvec::ArrayVec; +use thin_vec::ThinVec; use rustc_ast::attr; use rustc_ast::util::comments::beautify_doc_string; @@ -15,12 +16,12 @@ use rustc_ast::{self as ast, AttrStyle}; use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel}; use rustc_const_eval::const_eval::is_unstable_const_fn; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_hir::{BodyId, Mutability}; +use rustc_hir_analysis::check::intrinsic::intrinsic_operation_unsafety; use rustc_index::vec::IndexVec; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt}; @@ -31,7 +32,6 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{self, FileName, Loc}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; -use rustc_typeck::check::intrinsic::intrinsic_operation_unsafety; use crate::clean::cfg::Cfg; use crate::clean::clean_visibility; @@ -119,7 +119,7 @@ pub(crate) struct Crate { pub(crate) module: Item, pub(crate) primitives: ThinVec<(DefId, PrimitiveType)>, /// Only here so that they can be filtered through the rustdoc passes. - pub(crate) external_traits: Rc<RefCell<FxHashMap<DefId, TraitWithExtraInfo>>>, + pub(crate) external_traits: Rc<RefCell<FxHashMap<DefId, Trait>>>, } impl Crate { @@ -132,13 +132,6 @@ impl Crate { } } -/// This struct is used to wrap additional information added by rustdoc on a `trait` item. -#[derive(Clone, Debug)] -pub(crate) struct TraitWithExtraInfo { - pub(crate) trait_: Trait, - pub(crate) is_notable: bool, -} - #[derive(Copy, Clone, Debug)] pub(crate) struct ExternalCrate { pub(crate) crate_num: CrateNum, @@ -247,13 +240,13 @@ impl ExternalCrate { let item = tcx.hir().item(id); match item.kind { hir::ItemKind::Mod(_) => { - as_keyword(Res::Def(DefKind::Mod, id.def_id.to_def_id())) + as_keyword(Res::Def(DefKind::Mod, id.owner_id.to_def_id())) } hir::ItemKind::Use(path, hir::UseKind::Single) - if tcx.visibility(id.def_id).is_public() => + if tcx.visibility(id.owner_id).is_public() => { as_keyword(path.res.expect_non_local()) - .map(|(_, prim)| (id.def_id.to_def_id(), prim)) + .map(|(_, prim)| (id.owner_id.to_def_id(), prim)) } _ => None, } @@ -315,14 +308,14 @@ impl ExternalCrate { let item = tcx.hir().item(id); match item.kind { hir::ItemKind::Mod(_) => { - as_primitive(Res::Def(DefKind::Mod, id.def_id.to_def_id())) + as_primitive(Res::Def(DefKind::Mod, id.owner_id.to_def_id())) } hir::ItemKind::Use(path, hir::UseKind::Single) - if tcx.visibility(id.def_id).is_public() => + if tcx.visibility(id.owner_id).is_public() => { as_primitive(path.res.expect_non_local()).map(|(_, prim)| { // Pretend the primitive is local. - (id.def_id.to_def_id(), prim) + (id.owner_id.to_def_id(), prim) }) } _ => None, @@ -415,29 +408,28 @@ impl Item { .unwrap_or(false) } - pub(crate) fn span(&self, tcx: TyCtxt<'_>) -> Span { + pub(crate) fn span(&self, tcx: TyCtxt<'_>) -> Option<Span> { let kind = match &*self.kind { ItemKind::StrippedItem(k) => k, _ => &*self.kind, }; match kind { - ItemKind::ModuleItem(Module { span, .. }) => *span, - ItemKind::ImplItem(box Impl { kind: ImplKind::Auto, .. }) => Span::dummy(), + ItemKind::ModuleItem(Module { span, .. }) => Some(*span), + ItemKind::ImplItem(box Impl { kind: ImplKind::Auto, .. }) => None, ItemKind::ImplItem(box Impl { kind: ImplKind::Blanket(_), .. }) => { if let ItemId::Blanket { impl_id, .. } = self.item_id { - rustc_span(impl_id, tcx) + Some(rustc_span(impl_id, tcx)) } else { panic!("blanket impl item has non-blanket ID") } } - _ => { - self.item_id.as_def_id().map(|did| rustc_span(did, tcx)).unwrap_or_else(Span::dummy) - } + _ => self.item_id.as_def_id().map(|did| rustc_span(did, tcx)), } } pub(crate) fn attr_span(&self, tcx: TyCtxt<'_>) -> rustc_span::Span { - crate::passes::span_of_attrs(&self.attrs).unwrap_or_else(|| self.span(tcx).inner()) + crate::passes::span_of_attrs(&self.attrs) + .unwrap_or_else(|| self.span(tcx).map_or(rustc_span::DUMMY_SP, |span| span.inner())) } /// Finds the `doc` attribute as a NameValue and returns the corresponding @@ -483,7 +475,7 @@ impl Item { cx: &mut DocContext<'_>, cfg: Option<Arc<Cfg>>, ) -> Item { - trace!("name={:?}, def_id={:?}", name, def_id); + trace!("name={:?}, def_id={:?} cfg={:?}", name, def_id, cfg); // Primitives and Keywords are written in the source code as private modules. // The modules need to be private so that nobody actually uses them, but the @@ -511,7 +503,7 @@ impl Item { .get(&self.item_id) .map_or(&[][..], |v| v.as_slice()) .iter() - .filter_map(|ItemLink { link: s, link_text, did, ref fragment }| { + .filter_map(|ItemLink { link: s, link_text, page_id: did, ref fragment }| { debug!(?did); if let Ok((mut href, ..)) = href(*did, cx) { debug!(?href); @@ -690,7 +682,7 @@ impl Item { let abi = tcx.fn_sig(self.item_id.as_def_id().unwrap()).abi(); hir::FnHeader { unsafety: if abi == Abi::RustIntrinsic { - intrinsic_operation_unsafety(self.name.unwrap()) + intrinsic_operation_unsafety(tcx, self.item_id.as_def_id().unwrap()) } else { hir::Unsafety::Unsafe }, @@ -728,7 +720,7 @@ pub(crate) enum ItemKind { OpaqueTyItem(OpaqueTy), StaticItem(Static), ConstantItem(Constant), - TraitItem(Trait), + TraitItem(Box<Trait>), TraitAliasItem(TraitAlias), ImplItem(Box<Impl>), /// A required method in a trait declaration meaning it's only a function signature. @@ -802,6 +794,31 @@ impl ItemKind { | KeywordItem => [].iter(), } } + + /// Returns `true` if this item does not appear inside an impl block. + pub(crate) fn is_non_assoc(&self) -> bool { + matches!( + self, + StructItem(_) + | UnionItem(_) + | EnumItem(_) + | TraitItem(_) + | ModuleItem(_) + | ExternCrateItem { .. } + | FunctionItem(_) + | TypedefItem(_) + | OpaqueTyItem(_) + | StaticItem(_) + | ConstantItem(_) + | TraitAliasItem(_) + | ForeignFunctionItem(_) + | ForeignStaticItem(_) + | ForeignTypeItem + | MacroItem(_) + | ProcMacroItem(_) + | PrimitiveItem(_) + ) + } } #[derive(Clone, Debug)] @@ -821,8 +838,6 @@ pub(crate) trait AttributesExt { fn inner_docs(&self) -> bool; - fn other_attrs(&self) -> Vec<ast::Attribute>; - fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>>; } @@ -849,10 +864,6 @@ impl AttributesExt for [ast::Attribute] { self.iter().find(|a| a.doc_str().is_some()).map_or(true, |a| a.style == AttrStyle::Inner) } - fn other_attrs(&self) -> Vec<ast::Attribute> { - self.iter().filter(|attr| attr.doc_str().is_none()).cloned().collect() - } - fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>> { let sess = tcx.sess; let doc_cfg_active = tcx.features().doc_cfg; @@ -1116,7 +1127,10 @@ pub(crate) struct ItemLink { /// This may not be the same as `link` if there was a disambiguator /// in an intra-doc link (e.g. \[`fn@f`\]) pub(crate) link_text: String, - pub(crate) did: DefId, + /// The `DefId` of the Item whose **HTML Page** contains the item being + /// linked to. This will be different to `item_id` on item's that don't + /// have their own page, such as struct fields and enum variants. + pub(crate) page_id: DefId, /// The url fragment to append to the link pub(crate) fragment: Option<UrlFragment>, } @@ -1137,7 +1151,7 @@ pub struct RenderedLink { #[derive(Clone, Debug, Default)] pub(crate) struct Attributes { pub(crate) doc_strings: Vec<DocFragment>, - pub(crate) other_attrs: Vec<ast::Attribute>, + pub(crate) other_attrs: ast::AttrVec, } impl Attributes { @@ -1180,7 +1194,7 @@ impl Attributes { doc_only: bool, ) -> Attributes { let mut doc_strings = Vec::new(); - let mut other_attrs = Vec::new(); + let mut other_attrs = ast::AttrVec::new(); for (attr, parent_module) in attrs { if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() { trace!("got doc_str={doc_str:?}"); @@ -1285,7 +1299,7 @@ impl GenericBound { pub(crate) fn maybe_sized(cx: &mut DocContext<'_>) -> GenericBound { let did = cx.tcx.require_lang_item(LangItem::Sized, None); let empty = cx.tcx.intern_substs(&[]); - let path = external_path(cx, did, false, vec![], empty); + let path = external_path(cx, did, false, ThinVec::new(), empty); inline::record_extern_fqn(cx, did, ItemType::Trait); GenericBound::TraitBound( PolyTrait { trait_: path, generic_params: Vec::new() }, @@ -1336,7 +1350,7 @@ impl Lifetime { pub(crate) enum WherePredicate { BoundPredicate { ty: Type, bounds: Vec<GenericBound>, bound_params: Vec<Lifetime> }, RegionPredicate { lifetime: Lifetime, bounds: Vec<GenericBound> }, - EqPredicate { lhs: Type, rhs: Term }, + EqPredicate { lhs: Box<Type>, rhs: Box<Term>, bound_params: Vec<Lifetime> }, } impl WherePredicate { @@ -1347,6 +1361,15 @@ impl WherePredicate { _ => None, } } + + pub(crate) fn get_bound_params(&self) -> Option<&[Lifetime]> { + match self { + Self::BoundPredicate { bound_params, .. } | Self::EqPredicate { bound_params, .. } => { + Some(bound_params) + } + _ => None, + } + } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] @@ -1509,6 +1532,9 @@ impl Trait { pub(crate) fn is_auto(&self, tcx: TyCtxt<'_>) -> bool { tcx.trait_is_auto(self.def_id) } + pub(crate) fn is_notable_trait(&self, tcx: TyCtxt<'_>) -> bool { + tcx.is_doc_notable_trait(self.def_id) + } pub(crate) fn unsafety(&self, tcx: TyCtxt<'_>) -> hir::Unsafety { tcx.trait_def(self.def_id).unsafety } @@ -1557,13 +1583,7 @@ pub(crate) enum Type { BorrowedRef { lifetime: Option<Lifetime>, mutability: Mutability, type_: Box<Type> }, /// A qualified path to an associated item: `<Type as Trait>::Name` - QPath { - assoc: Box<PathSegment>, - self_type: Box<Type>, - /// FIXME: compute this field on demand. - should_show_cast: bool, - trait_: Path, - }, + QPath(Box<QPathData>), /// A type that is inferred: `_` Infer, @@ -1661,8 +1681,8 @@ impl Type { } pub(crate) fn projection(&self) -> Option<(&Type, DefId, PathSegment)> { - if let QPath { self_type, trait_, assoc, .. } = self { - Some((self_type, trait_.def_id(), *assoc.clone())) + if let QPath(box QPathData { self_type, trait_, assoc, .. }) = self { + Some((self_type, trait_.def_id(), assoc.clone())) } else { None } @@ -1686,7 +1706,7 @@ impl Type { Slice(..) => PrimitiveType::Slice, Array(..) => PrimitiveType::Array, RawPointer(..) => PrimitiveType::RawPointer, - QPath { ref self_type, .. } => return self_type.inner_def_id(cache), + QPath(box QPathData { ref self_type, .. }) => return self_type.inner_def_id(cache), Generic(_) | Infer | ImplTrait(_) => return None, }; cache.and_then(|c| Primitive(t).def_id(c)) @@ -1700,6 +1720,15 @@ impl Type { } } +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub(crate) struct QPathData { + pub assoc: PathSegment, + pub self_type: Type, + /// FIXME: compute this field on demand. + pub should_show_cast: bool, + pub trait_: Path, +} + /// A primitive (aka, builtin) type. /// /// This represents things like `i32`, `str`, etc. @@ -2077,7 +2106,7 @@ impl Enum { #[derive(Clone, Debug)] pub(crate) enum Variant { - CLike, + CLike(Option<Discriminant>), Tuple(Vec<Item>), Struct(VariantStruct), } @@ -2086,11 +2115,31 @@ impl Variant { pub(crate) fn has_stripped_entries(&self) -> Option<bool> { match *self { Self::Struct(ref struct_) => Some(struct_.has_stripped_entries()), - Self::CLike | Self::Tuple(_) => None, + Self::CLike(..) | Self::Tuple(_) => None, } } } +#[derive(Clone, Debug)] +pub(crate) struct Discriminant { + // In the case of cross crate re-exports, we don't have the nessesary information + // to reconstruct the expression of the discriminant, only the value. + pub(super) expr: Option<BodyId>, + pub(super) value: DefId, +} + +impl Discriminant { + /// Will be `None` in the case of cross-crate reexports, and may be + /// simplified + pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> Option<String> { + self.expr.map(|body| print_const_expr(tcx, body)) + } + /// Will always be a machine readable number, without underscores or suffixes. + pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> String { + print_evaluated_const(tcx, self.value, false).unwrap() + } +} + /// Small wrapper around [`rustc_span::Span`] that adds helper methods /// and enforces calling [`rustc_span::Span::source_callsite()`]. #[derive(Copy, Clone, Debug)] @@ -2109,14 +2158,6 @@ impl Span { self.0 } - pub(crate) fn dummy() -> Self { - Self(rustc_span::DUMMY_SP) - } - - pub(crate) fn is_dummy(&self) -> bool { - self.0.is_dummy() - } - pub(crate) fn filename(&self, sess: &Session) -> FileName { sess.source_map().span_to_filename(self.0) } @@ -2165,8 +2206,11 @@ impl Path { /// Checks if this is a `T::Name` path for an associated type. pub(crate) fn is_assoc_ty(&self) -> bool { match self.res { - Res::SelfTy { .. } if self.segments.len() != 1 => true, - Res::Def(DefKind::TyParam, _) if self.segments.len() != 1 => true, + Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } | Res::Def(DefKind::TyParam, _) + if self.segments.len() != 1 => + { + true + } Res::Def(DefKind::AssocTy, _) => true, _ => false, } @@ -2325,7 +2369,7 @@ impl ConstantKind { match *self { ConstantKind::TyConst { .. } | ConstantKind::Anonymous { .. } => None, ConstantKind::Extern { def_id } | ConstantKind::Local { def_id, .. } => { - print_evaluated_const(tcx, def_id) + print_evaluated_const(tcx, def_id, true) } } } @@ -2495,14 +2539,16 @@ impl SubstParam { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] mod size_asserts { use super::*; - // These are in alphabetical order, which is easy to maintain. - rustc_data_structures::static_assert_size!(Crate, 72); // frequently moved by-value - rustc_data_structures::static_assert_size!(DocFragment, 32); - rustc_data_structures::static_assert_size!(GenericArg, 80); - rustc_data_structures::static_assert_size!(GenericArgs, 32); - rustc_data_structures::static_assert_size!(GenericParamDef, 56); - rustc_data_structures::static_assert_size!(Item, 56); - rustc_data_structures::static_assert_size!(ItemKind, 112); - rustc_data_structures::static_assert_size!(PathSegment, 40); - rustc_data_structures::static_assert_size!(Type, 72); + use rustc_data_structures::static_assert_size; + // tidy-alphabetical-start + static_assert_size!(Crate, 72); // frequently moved by-value + static_assert_size!(DocFragment, 32); + static_assert_size!(GenericArg, 48); + static_assert_size!(GenericArgs, 32); + static_assert_size!(GenericParamDef, 56); + static_assert_size!(Item, 56); + static_assert_size!(ItemKind, 88); + static_assert_size!(PathSegment, 40); + static_assert_size!(Type, 48); + // tidy-alphabetical-end } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 43e71e90a..58767d3a4 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -2,17 +2,15 @@ use crate::clean::auto_trait::AutoTraitFinder; use crate::clean::blanket_impl::BlanketImplFinder; use crate::clean::render_macro_matchers::render_macro_matcher; use crate::clean::{ - clean_middle_const, clean_middle_region, clean_middle_ty, inline, Clean, Crate, ExternalCrate, - Generic, GenericArg, GenericArgs, ImportSource, Item, ItemKind, Lifetime, Path, PathSegment, - Primitive, PrimitiveType, Type, TypeBinding, Visibility, + clean_doc_module, clean_middle_const, clean_middle_region, clean_middle_ty, inline, Crate, + ExternalCrate, Generic, GenericArg, GenericArgs, ImportSource, Item, ItemKind, Lifetime, Path, + PathSegment, Primitive, PrimitiveType, Type, TypeBinding, Visibility, }; use crate::core::DocContext; -use crate::formats::item_type::ItemType; use crate::visit_lib::LibEmbargoVisitor; use rustc_ast as ast; use rustc_ast::tokenstream::TokenTree; -use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; @@ -23,6 +21,7 @@ use rustc_middle::ty::{self, DefIdTree, TyCtxt}; use rustc_span::symbol::{kw, sym, Symbol}; use std::fmt::Write as _; use std::mem; +use thin_vec::ThinVec; #[cfg(test)] mod tests; @@ -37,7 +36,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { // Clean the crate, translating the entire librustc_ast AST to one that is // understood by rustdoc. - let mut module = module.clean(cx); + let mut module = clean_doc_module(&module, cx); match *module.kind { ItemKind::ModuleItem(ref module) => { @@ -102,7 +101,7 @@ fn external_generic_args<'tcx>( cx: &mut DocContext<'tcx>, did: DefId, has_self: bool, - bindings: Vec<TypeBinding>, + bindings: ThinVec<TypeBinding>, substs: SubstsRef<'tcx>, ) -> GenericArgs { let args = substs_to_args(cx, substs, has_self); @@ -112,7 +111,7 @@ fn external_generic_args<'tcx>( // The trait's first substitution is the one after self, if there is one. match substs.iter().nth(if has_self { 1 } else { 0 }).unwrap().expect_ty().kind() { ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(t, cx, None)).collect::<Vec<_>>().into(), - _ => return GenericArgs::AngleBracketed { args: args.into(), bindings: bindings.into() }, + _ => return GenericArgs::AngleBracketed { args: args.into(), bindings }, }; let output = None; // FIXME(#20299) return type comes from a projection now @@ -130,7 +129,7 @@ pub(super) fn external_path<'tcx>( cx: &mut DocContext<'tcx>, did: DefId, has_self: bool, - bindings: Vec<TypeBinding>, + bindings: ThinVec<TypeBinding>, substs: SubstsRef<'tcx>, ) -> Path { let def_kind = cx.tcx.def_kind(did); @@ -234,15 +233,13 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String { match n.kind() { - ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted }) => { - let mut s = if let Some(def) = def.as_local() { + ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs: _ }) => { + let s = if let Some(def) = def.as_local() { print_const_expr(cx.tcx, cx.tcx.hir().body_owned_by(def.did)) } else { inline::print_inlined_const(cx.tcx, def.did) }; - if let Some(promoted) = promoted { - s.push_str(&format!("::{:?}", promoted)) - } + s } _ => { @@ -261,7 +258,11 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String { } } -pub(crate) fn print_evaluated_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<String> { +pub(crate) fn print_evaluated_const( + tcx: TyCtxt<'_>, + def_id: DefId, + underscores_and_type: bool, +) -> Option<String> { tcx.const_eval_poly(def_id).ok().and_then(|val| { let ty = tcx.type_of(def_id); match (val, ty.kind()) { @@ -269,7 +270,7 @@ pub(crate) fn print_evaluated_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<St (ConstValue::Scalar(_), &ty::Adt(_, _)) => None, (ConstValue::Scalar(_), _) => { let const_ = mir::ConstantKind::from_value(val, ty); - Some(print_const_with_custom_print_scalar(tcx, const_)) + Some(print_const_with_custom_print_scalar(tcx, const_, underscores_and_type)) } _ => None, } @@ -302,23 +303,35 @@ fn format_integer_with_underscore_sep(num: &str) -> String { .collect() } -fn print_const_with_custom_print_scalar(tcx: TyCtxt<'_>, ct: mir::ConstantKind<'_>) -> String { +fn print_const_with_custom_print_scalar<'tcx>( + tcx: TyCtxt<'tcx>, + ct: mir::ConstantKind<'tcx>, + underscores_and_type: bool, +) -> String { // Use a slightly different format for integer types which always shows the actual value. // For all other types, fallback to the original `pretty_print_const`. match (ct, ct.ty().kind()) { (mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Uint(ui)) => { - format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str()) + if underscores_and_type { + format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str()) + } else { + int.to_string() + } } (mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Int(i)) => { - let ty = tcx.lift(ct.ty()).unwrap(); + let ty = ct.ty(); let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size; let data = int.assert_bits(size); let sign_extended_data = size.sign_extend(data) as i128; - format!( - "{}{}", - format_integer_with_underscore_sep(&sign_extended_data.to_string()), - i.name_str() - ) + if underscores_and_type { + format!( + "{}{}", + format_integer_with_underscore_sep(&sign_extended_data.to_string()), + i.name_str() + ) + } else { + sign_extended_data.to_string() + } } _ => ct.to_string(), } @@ -440,7 +453,9 @@ pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type { match path.res { Res::PrimTy(p) => Primitive(PrimitiveType::from(p)), - Res::SelfTy { .. } if path.segments.len() == 1 => Generic(kw::SelfUpper), + Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } if path.segments.len() == 1 => { + Generic(kw::SelfUpper) + } Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => Generic(path.segments[0].name), _ => { let _ = register_res(cx, path.res); @@ -475,38 +490,19 @@ pub(crate) fn register_res(cx: &mut DocContext<'_>, res: Res) -> DefId { use DefKind::*; debug!("register_res({:?})", res); - let (did, kind) = match res { - // These should be added to the cache using `record_extern_fqn`. + let (kind, did) = match res { Res::Def( kind @ (AssocTy | AssocFn | AssocConst | Variant | Fn | TyAlias | Enum | Trait | Struct | Union | Mod | ForeignTy | Const | Static(_) | Macro(..) | TraitAlias), - i, - ) => (i, kind.into()), - // This is part of a trait definition or trait impl; document the trait. - Res::SelfTy { trait_: Some(trait_def_id), alias_to: _ } => (trait_def_id, ItemType::Trait), - // This is an inherent impl or a type definition; it doesn't have its own page. - Res::SelfTy { trait_: None, alias_to: Some((item_def_id, _)) } => return item_def_id, - Res::SelfTy { trait_: None, alias_to: None } - | Res::PrimTy(_) - | Res::ToolMod - | Res::SelfCtor(_) - | Res::Local(_) - | Res::NonMacroAttr(_) - | Res::Err => return res.def_id(), - Res::Def( - TyParam | ConstParam | Ctor(..) | ExternCrate | Use | ForeignMod | AnonConst - | InlineConst | OpaqueTy | Field | LifetimeParam | GlobalAsm | Impl | Closure - | Generator, - id, - ) => return id, + did, + ) => (kind.into(), did), + + _ => panic!("register_res: unexpected {:?}", res), }; if did.is_local() { return did; } inline::record_extern_fqn(cx, did, kind); - if let ItemType::Trait = kind { - inline::record_extern_trait(cx, did); - } did } |