From 35f39a88a5cefb930c4adf78814201b69f7c9ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 28 Jun 2024 01:10:13 +0200 Subject: [PATCH 1/4] Move HIR cleaners to separate module called `local` --- src/librustdoc/clean/inline.rs | 14 +- src/librustdoc/clean/local.rs | 1467 ++++++++++++++++++++++++++++++++ src/librustdoc/clean/mod.rs | 1460 +------------------------------ 3 files changed, 1497 insertions(+), 1444 deletions(-) create mode 100644 src/librustdoc/clean/local.rs diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 0024e246ef007..16b953e8dcaff 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -19,10 +19,10 @@ use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Symbol}; use crate::clean::{ - self, clean_bound_vars, clean_generics, clean_impl_item, clean_middle_assoc_item, - clean_middle_field, clean_middle_ty, clean_poly_fn_sig, clean_trait_ref_with_constraints, - clean_ty, clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes, - AttributesExt, ImplKind, ItemId, Type, + self, clean_bound_vars, clean_middle_assoc_item, clean_middle_field, clean_middle_ty, + clean_poly_fn_sig, clean_trait_ref_with_constraints, clean_ty_alias_inner_type, + clean_ty_generics, clean_variant_def, local, utils, Attributes, AttributesExt, ImplKind, + ItemId, Type, }; use crate::core::DocContext; use crate::formats::item_type::ItemType; @@ -477,7 +477,7 @@ pub(crate) fn build_impl( }; let for_ = match &impl_item { - Some(impl_) => clean_ty(impl_.self_ty, cx), + Some(impl_) => local::clean_ty(impl_.self_ty, cx), None => clean_middle_ty( ty::Binder::dummy(tcx.type_of(did).instantiate_identity()), cx, @@ -533,9 +533,9 @@ pub(crate) fn build_impl( true } }) - .map(|item| clean_impl_item(item, cx)) + .map(|item| local::clean_impl_item(item, cx)) .collect::>(), - clean_generics(impl_.generics, cx), + local::clean_generics(impl_.generics, cx), ), None => ( tcx.associated_items(did) diff --git a/src/librustdoc/clean/local.rs b/src/librustdoc/clean/local.rs new file mode 100644 index 0000000000000..9a90d5e8a9ec8 --- /dev/null +++ b/src/librustdoc/clean/local.rs @@ -0,0 +1,1467 @@ +use rustc_ast as ast; +use rustc_attr as attr; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry}; +use rustc_errors::{codes::*, struct_span_code_err}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE}; +use rustc_hir::PredicateOrigin; +use rustc_hir_analysis::lower_ty; +use rustc_middle::bug; +use rustc_middle::middle::resolve_bound_vars as rbv; +use rustc_middle::ty; +use rustc_middle::ty::TypeVisitableExt; +use rustc_span::hygiene::{AstPass, MacroKind}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::ExpnKind; + +use thin_vec::ThinVec; + +use crate::core::DocContext; +use crate::formats::item_type::ItemType; + +use super::{ + inline, + types::*, + utils::{self, *}, +}; + +fn clean_args_from_types_and_body_id<'tcx>( + cx: &mut DocContext<'tcx>, + types: &[hir::Ty<'tcx>], + body_id: hir::BodyId, +) -> Arguments { + let body = cx.tcx.hir().body(body_id); + + Arguments { + values: types + .iter() + .enumerate() + .map(|(i, ty)| Argument { + name: name_from_pat(body.params[i].pat), + type_: clean_ty(ty, cx), + is_const: false, + }) + .collect(), + } +} + +fn clean_args_from_types_and_names<'tcx>( + cx: &mut DocContext<'tcx>, + types: &[hir::Ty<'tcx>], + names: &[Ident], +) -> Arguments { + Arguments { + values: types + .iter() + .enumerate() + .map(|(i, ty)| Argument { + type_: clean_ty(ty, cx), + name: names + .get(i) + .map(|ident| ident.name) + .filter(|ident| !ident.is_empty()) + .unwrap_or(kw::Underscore), + is_const: false, + }) + .collect(), + } +} + +fn clean_assoc_item_constraint<'tcx>( + constraint: &hir::AssocItemConstraint<'tcx>, + cx: &mut DocContext<'tcx>, +) -> AssocItemConstraint { + AssocItemConstraint { + assoc: PathSegment { + name: constraint.ident.name, + args: clean_generic_args(constraint.gen_args, cx), + }, + kind: match constraint.kind { + hir::AssocItemConstraintKind::Equality { ref term } => { + AssocItemConstraintKind::Equality { term: clean_hir_term(term, cx) } + } + hir::AssocItemConstraintKind::Bound { bounds } => AssocItemConstraintKind::Bound { + bounds: bounds.iter().filter_map(|b| clean_generic_bound(b, cx)).collect(), + }, + }, + } +} + +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, None, args); + (generic_params, decl) + }); + BareFunctionDecl { safety: bare_fn.safety, abi: bare_fn.abi, decl, generic_params } +} + +fn clean_const<'tcx>(constant: &hir::ConstArg<'_>, _cx: &mut DocContext<'tcx>) -> Constant { + Constant { kind: ConstantKind::Anonymous { body: constant.value.body } } +} + +fn clean_extern_crate<'tcx>( + krate: &hir::Item<'tcx>, + name: Symbol, + orig_name: Option, + cx: &mut DocContext<'tcx>, +) -> Vec { + // this is the ID of the `extern crate` statement + 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.owner_id); + let please_inline = ty_vis.is_public() + && attrs.iter().any(|a| { + a.has_name(sym::doc) + && match a.meta_item_list() { + Some(l) => attr::list_contains_name(&l, sym::inline), + None => false, + } + }) + && !cx.output_format.is_json(); + + let krate_owner_def_id = krate.owner_id.to_def_id(); + if please_inline { + if let Some(items) = inline::try_inline( + cx, + Res::Def(DefKind::Mod, crate_def_id), + name, + Some((attrs, Some(krate_owner_def_id))), + &mut Default::default(), + ) { + return items; + } + } + + vec![Item::from_def_id_and_parts( + krate_owner_def_id, + Some(name), + ExternCrateItem { src: orig_name }, + cx, + )] +} + +fn clean_field<'tcx>(field: &hir::FieldDef<'tcx>, cx: &mut DocContext<'tcx>) -> Item { + super::clean_field_with_def_id( + field.def_id.to_def_id(), + field.ident.name, + clean_ty(field.ty, cx), + cx, + ) +} + +/// This is needed to make it more "readable" when documenting functions using +/// `rustc_legacy_const_generics`. More information in +/// . +fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[ast::Attribute]) { + for meta_item_list in attrs + .iter() + .filter(|a| a.has_name(sym::rustc_legacy_const_generics)) + .filter_map(|a| a.meta_item_list()) + { + for (pos, literal) in meta_item_list.iter().filter_map(|meta| meta.lit()).enumerate() { + match literal.kind { + ast::LitKind::Int(a, _) => { + let gen = func.generics.params.remove(0); + if let GenericParamDef { + name, + kind: GenericParamDefKind::Const { ty, .. }, + .. + } = gen + { + func.decl + .inputs + .values + .insert(a.get() as _, Argument { name, type_: *ty, is_const: true }); + } else { + panic!("unexpected non const in position {pos}"); + } + } + _ => panic!("invalid arg index"), + } + } + } +} + +fn clean_fn_decl_with_args<'tcx>( + cx: &mut DocContext<'tcx>, + decl: &hir::FnDecl<'tcx>, + header: Option<&hir::FnHeader>, + args: Arguments, +) -> FnDecl { + let mut output = match decl.output { + hir::FnRetTy::Return(typ) => clean_ty(typ, cx), + hir::FnRetTy::DefaultReturn(..) => Type::Tuple(Vec::new()), + }; + if let Some(header) = header + && header.is_async() + { + output = output.sugared_async_return_type(); + } + FnDecl { inputs: args, output, c_variadic: decl.c_variadic } +} + +fn clean_fn_or_proc_macro<'tcx>( + item: &hir::Item<'tcx>, + sig: &hir::FnSig<'tcx>, + generics: &hir::Generics<'tcx>, + body_id: hir::BodyId, + name: &mut Symbol, + cx: &mut DocContext<'tcx>, +) -> ItemKind { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let macro_kind = attrs.iter().find_map(|a| { + if a.has_name(sym::proc_macro) { + Some(MacroKind::Bang) + } else if a.has_name(sym::proc_macro_derive) { + Some(MacroKind::Derive) + } else if a.has_name(sym::proc_macro_attribute) { + Some(MacroKind::Attr) + } else { + None + } + }); + match macro_kind { + Some(kind) => clean_proc_macro(item, name, kind, cx), + None => { + let mut func = clean_function(cx, sig, generics, FunctionArgs::Body(body_id)); + clean_fn_decl_legacy_const_generics(&mut func, attrs); + FunctionItem(func) + } + } +} + +// FIXME: rename +fn clean_function<'tcx>( + cx: &mut DocContext<'tcx>, + sig: &hir::FnSig<'tcx>, + generics: &hir::Generics<'tcx>, + args: FunctionArgs<'tcx>, +) -> Box { + let (generics, decl) = enter_impl_trait(cx, |cx| { + // NOTE: generics must be cleaned before args + let generics = clean_generics(generics, cx); + let args = match args { + FunctionArgs::Body(body_id) => { + clean_args_from_types_and_body_id(cx, sig.decl.inputs, body_id) + } + FunctionArgs::Names(names) => { + clean_args_from_types_and_names(cx, sig.decl.inputs, names) + } + }; + let decl = clean_fn_decl_with_args(cx, sig.decl, Some(&sig.header), args); + (generics, decl) + }); + Box::new(Function { decl, generics }) +} + +fn clean_generic_args<'tcx>( + generic_args: &hir::GenericArgs<'tcx>, + cx: &mut DocContext<'tcx>, +) -> GenericArgs { + // FIXME(return_type_notation): Fix RTN parens rendering + if let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() { + let inputs = inputs.iter().map(|x| clean_ty(x, cx)).collect::>().into(); + let output = match output.kind { + hir::TyKind::Tup(&[]) => None, + _ => Some(Box::new(clean_ty(output, cx))), + }; + GenericArgs::Parenthesized { inputs, output } + } else { + let args = generic_args + .args + .iter() + .filter_map(|arg| { + Some(match arg { + hir::GenericArg::Lifetime(lt) if !lt.is_anonymous() => { + GenericArg::Lifetime(clean_lifetime(*lt, cx)) + } + hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()), + hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty, cx)), + // Checking for `is_desugared_from_effects` on the `AnonConst` not only accounts for the case + // where the argument is `host` but for all possible cases (e.g., `true`, `false`). + hir::GenericArg::Const(hir::ConstArg { + is_desugared_from_effects: true, + .. + }) => { + return None; + } + hir::GenericArg::Const(ct) => GenericArg::Const(Box::new(clean_const(ct, cx))), + hir::GenericArg::Infer(_inf) => GenericArg::Infer, + }) + }) + .collect::>() + .into(); + let constraints = generic_args + .constraints + .iter() + .map(|c| clean_assoc_item_constraint(c, cx)) + .collect::>(); + GenericArgs::AngleBracketed { args, constraints } + } +} + +fn clean_generic_bound<'tcx>( + bound: &hir::GenericBound<'tcx>, + cx: &mut DocContext<'tcx>, +) -> Option { + Some(match *bound { + hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)), + 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(clean_poly_trait_ref(t, cx), modifier) + } + // FIXME(precise_capturing): Implement rustdoc support + hir::GenericBound::Use(..) => return None, + }) +} + +fn clean_generic_param<'tcx>( + cx: &mut DocContext<'tcx>, + generics: Option<&hir::Generics<'tcx>>, + param: &hir::GenericParam<'tcx>, +) -> GenericParamDef { + let (name, kind) = match param.kind { + hir::GenericParamKind::Lifetime { .. } => { + let outlives = if let Some(generics) = generics { + generics + .outlives_for_param(param.def_id) + .filter(|bp| !bp.in_where_clause) + .flat_map(|bp| bp.bounds) + .map(|bound| match bound { + hir::GenericBound::Outlives(lt) => clean_lifetime(lt, cx), + _ => panic!(), + }) + .collect() + } else { + ThinVec::new() + }; + (param.name.ident().name, GenericParamDefKind::Lifetime { outlives }) + } + hir::GenericParamKind::Type { ref default, synthetic } => { + let bounds = if let Some(generics) = generics { + generics + .bounds_for_param(param.def_id) + .filter(|bp| bp.origin != PredicateOrigin::WhereClause) + .flat_map(|bp| bp.bounds) + .filter_map(|x| clean_generic_bound(x, cx)) + .collect() + } else { + ThinVec::new() + }; + ( + param.name.ident().name, + GenericParamDefKind::Type { + bounds, + default: default.map(|t| clean_ty(t, cx)).map(Box::new), + synthetic, + }, + ) + } + hir::GenericParamKind::Const { ty, default, is_host_effect } => ( + param.name.ident().name, + GenericParamDefKind::Const { + ty: Box::new(clean_ty(ty, cx)), + default: default + .map(|ct| Box::new(ty::Const::from_anon_const(cx.tcx, ct.def_id).to_string())), + is_host_effect, + }, + ), + }; + + GenericParamDef { name, def_id: param.def_id.to_def_id(), kind } +} + +pub(super) 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 { ref bounds, .. } => { + cx.impl_trait_bounds.insert(param.def_id.into(), bounds.to_vec()); + } + GenericParamDefKind::Const { .. } => unreachable!(), + } + param + }) + .collect::>(); + + let mut bound_predicates = FxIndexMap::default(); + let mut region_predicates = FxIndexMap::default(); + let mut eq_predicates = ThinVec::default(); + for pred in gens.predicates.iter().filter_map(|x| clean_where_predicate(x, cx)) { + match pred { + WherePredicate::BoundPredicate { ty, bounds, bound_params } => { + match bound_predicates.entry(ty) { + IndexEntry::Vacant(v) => { + v.insert((bounds, bound_params)); + } + IndexEntry::Occupied(mut o) => { + // we merge both bounds. + for bound in bounds { + if !o.get().0.contains(&bound) { + o.get_mut().0.push(bound); + } + } + for bound_param in bound_params { + if !o.get().1.contains(&bound_param) { + o.get_mut().1.push(bound_param); + } + } + } + } + } + WherePredicate::RegionPredicate { lifetime, bounds } => { + match region_predicates.entry(lifetime) { + IndexEntry::Vacant(v) => { + v.insert(bounds); + } + IndexEntry::Occupied(mut o) => { + // we merge both bounds. + for bound in bounds { + if !o.get().contains(&bound) { + o.get_mut().push(bound); + } + } + } + } + } + WherePredicate::EqPredicate { lhs, rhs } => { + eq_predicates.push(WherePredicate::EqPredicate { lhs, rhs }); + } + } + } + + let mut params = ThinVec::with_capacity(gens.params.len()); + // In this loop, we gather the generic parameters (`<'a, B: 'a>`) and check if they have + // bounds in the where predicates. If so, we move their bounds into the where predicates + // while also preventing duplicates. + for p in gens.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) { + let mut p = clean_generic_param(cx, Some(gens), p); + match &mut p.kind { + GenericParamDefKind::Lifetime { ref mut outlives } => { + if let Some(region_pred) = region_predicates.get_mut(&Lifetime(p.name)) { + // We merge bounds in the `where` clause. + for outlive in outlives.drain(..) { + let outlive = GenericBound::Outlives(outlive); + if !region_pred.contains(&outlive) { + region_pred.push(outlive); + } + } + } + } + GenericParamDefKind::Type { bounds, synthetic: false, .. } => { + if let Some(bound_pred) = bound_predicates.get_mut(&Type::Generic(p.name)) { + // We merge bounds in the `where` clause. + for bound in bounds.drain(..) { + if !bound_pred.0.contains(&bound) { + bound_pred.0.push(bound); + } + } + } + } + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { + // nothing to do here. + } + } + params.push(p); + } + params.extend(impl_trait_params); + + Generics { + params, + where_predicates: bound_predicates + .into_iter() + .map(|(ty, (bounds, bound_params))| WherePredicate::BoundPredicate { + ty, + bounds, + bound_params, + }) + .chain( + region_predicates + .into_iter() + .map(|(lifetime, bounds)| WherePredicate::RegionPredicate { lifetime, bounds }), + ) + .chain(eq_predicates) + .collect(), + } +} + +// FIXME: rename +fn clean_hir_term<'tcx>(term: &hir::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Term { + match term { + hir::Term::Ty(ty) => Term::Type(clean_ty(ty, cx)), + hir::Term::Const(c) => Term::Constant(super::clean_middle_const( + ty::Binder::dummy(ty::Const::from_anon_const(cx.tcx, c.def_id)), + cx, + )), + } +} + +fn clean_impl<'tcx>( + impl_: &hir::Impl<'tcx>, + def_id: LocalDefId, + cx: &mut DocContext<'tcx>, +) -> Vec { + 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| clean_impl_item(tcx.hir().impl_item(ii.id), cx)) + .collect::>(); + + // If this impl block is an implementation of the Deref trait, then we + // need to try inlining the target's inherent impl blocks as well. + if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() { + build_deref_target_impls(cx, &items, &mut ret); + } + + let for_ = clean_ty(impl_.self_ty, cx); + let type_alias = + for_.def_id(&cx.cache).and_then(|alias_def_id: DefId| match tcx.def_kind(alias_def_id) { + DefKind::TyAlias => Some(super::clean_middle_ty( + ty::Binder::dummy(tcx.type_of(def_id).instantiate_identity()), + cx, + Some(def_id.to_def_id()), + None, + )), + _ => None, + }); + let mut make_item = |trait_: Option, for_: Type, items: Vec| { + let kind = ImplItem(Box::new(Impl { + safety: impl_.safety, + generics: clean_generics(impl_.generics, cx), + trait_, + for_, + items, + polarity: tcx.impl_polarity(def_id), + kind: if utils::has_doc_flag(tcx, def_id.to_def_id(), sym::fake_variadic) { + ImplKind::FakeVariadic + } else { + ImplKind::Normal + }, + })); + Item::from_def_id_and_parts(def_id.to_def_id(), None, kind, cx) + }; + if let Some(type_alias) = type_alias { + ret.push(make_item(trait_.clone(), type_alias, items.clone())); + } + ret.push(make_item(trait_, for_, items)); + ret +} + +pub(super) 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 generics = clean_generics(impl_.generics, cx); + let default = ConstantKind::Local { def_id: local_did, body: expr }; + AssocConstItem(generics, Box::new(clean_ty(ty, cx)), default) + } + hir::ImplItemKind::Fn(ref sig, body) => { + let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body)); + let defaultness = cx.tcx.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 = super::clean_middle_ty( + ty::Binder::dummy(lower_ty(cx.tcx, hir_ty)), + cx, + None, + None, + ); + AssocTypeItem( + Box::new(TypeAlias { + type_, + generics, + inner_type: None, + item_type: Some(item_type), + }), + Vec::new(), + ) + } + }; + + Item::from_def_id_and_parts(local_did, Some(impl_.ident.name), inner, cx) + }) +} + +fn clean_lifetime<'tcx>(lifetime: &hir::Lifetime, cx: &mut DocContext<'tcx>) -> Lifetime { + if let Some( + rbv::ResolvedArg::EarlyBound(did) + | rbv::ResolvedArg::LateBound(_, _, did) + | rbv::ResolvedArg::Free(_, did), + ) = cx.tcx.named_bound_var(lifetime.hir_id) + && let Some(lt) = cx.args.get(&did).and_then(|arg| arg.as_lt()) + { + return lt.clone(); + } + Lifetime(lifetime.ident.name) +} + +// FIXME: rename +pub(super) fn clean_maybe_renamed_foreign_item<'tcx>( + cx: &mut DocContext<'tcx>, + item: &hir::ForeignItem<'tcx>, + renamed: Option, +) -> Item { + 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, safety) => { + let (generics, decl) = enter_impl_trait(cx, |cx| { + // NOTE: generics must be cleaned before args + 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, None, args); + (generics, decl) + }); + ForeignFunctionItem(Box::new(Function { decl, generics }), safety) + } + hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem( + Static { type_: clean_ty(ty, cx), mutability, expr: None }, + safety, + ), + hir::ForeignItemKind::Type => ForeignTypeItem, + }; + + Item::from_def_id_and_parts( + item.owner_id.def_id.to_def_id(), + Some(renamed.unwrap_or(item.ident.name)), + kind, + cx, + ) + }) +} + +// FIXME: rename +pub(super) fn clean_maybe_renamed_item<'tcx>( + cx: &mut DocContext<'tcx>, + item: &hir::Item<'tcx>, + renamed: Option, + import_id: Option, +) -> Vec { + use hir::ItemKind; + + 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 { + ItemKind::Static(ty, mutability, body_id) => { + StaticItem(Static { type_: clean_ty(ty, cx), mutability, expr: Some(body_id) }) + } + ItemKind::Const(ty, generics, body_id) => ConstantItem( + clean_generics(generics, cx), + Box::new(clean_ty(ty, cx)), + Constant { kind: ConstantKind::Local { body: body_id, def_id } }, + ), + ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy { + bounds: ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), + generics: clean_generics(ty.generics, cx), + }), + ItemKind::TyAlias(hir_ty, generics) => { + *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; + let rustdoc_ty = clean_ty(hir_ty, cx); + let type_ = super::clean_middle_ty( + ty::Binder::dummy(lower_ty(cx.tcx, hir_ty)), + cx, + None, + None, + ); + let generics = clean_generics(generics, cx); + if let Some(count) = cx.current_type_aliases.get_mut(&def_id) { + *count -= 1; + if *count == 0 { + cx.current_type_aliases.remove(&def_id); + } + } + + let ty = cx.tcx.type_of(def_id).instantiate_identity(); + + let mut ret = Vec::new(); + let inner_type = super::clean_ty_alias_inner_type(ty, cx, &mut ret); + + ret.push(super::generate_item_with_correct_attrs( + cx, + TypeAliasItem(Box::new(TypeAlias { + generics, + inner_type, + type_: rustdoc_ty, + item_type: Some(type_), + })), + item.owner_id.def_id.to_def_id(), + name, + import_id, + renamed, + )); + return ret; + } + ItemKind::Enum(ref def, generics) => EnumItem(Enum { + variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(), + generics: clean_generics(generics, cx), + }), + ItemKind::TraitAlias(generics, bounds) => TraitAliasItem(TraitAlias { + 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: clean_generics(generics, cx), + fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), + }), + ItemKind::Struct(ref variant_data, generics) => StructItem(Struct { + ctor_kind: variant_data.ctor_kind(), + 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.owner_id.def_id, cx), + ItemKind::Macro(ref macro_def, MacroKind::Bang) => { + let ty_vis = cx.tcx.visibility(def_id); + MacroItem(Macro { + // FIXME this shouldn't be false + source: display_macro_source(cx, name, macro_def, def_id, ty_vis, false), + }) + } + ItemKind::Macro(_, macro_kind) => clean_proc_macro(item, &mut name, macro_kind, cx), + // proc macros can have a name set by attributes + ItemKind::Fn(ref sig, generics, body_id) => { + clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx) + } + ItemKind::Trait(_, _, generics, bounds, item_ids) => { + let items = item_ids + .iter() + .map(|ti| clean_trait_item(cx.tcx.hir().trait_item(ti.id), cx)) + .collect(); + + TraitItem(Box::new(Trait { + def_id, + items, + 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); + } + ItemKind::Use(path, kind) => { + return clean_use_statement(item, name, path, kind, cx, &mut FxHashSet::default()); + } + _ => unreachable!("not yet converted"), + }; + + vec![super::generate_item_with_correct_attrs( + cx, + kind, + item.owner_id.def_id.to_def_id(), + name, + import_id, + renamed, + )] + }) +} + +fn clean_path<'tcx>(path: &hir::Path<'tcx>, cx: &mut DocContext<'tcx>) -> Path { + Path { + res: path.res, + segments: path.segments.iter().map(|x| clean_path_segment(x, cx)).collect(), + } +} + +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) } +} + +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(), + } +} + +fn clean_proc_macro<'tcx>( + item: &hir::Item<'tcx>, + name: &mut Symbol, + kind: MacroKind, + cx: &mut DocContext<'tcx>, +) -> ItemKind { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + if kind == MacroKind::Derive + && let Some(derive_name) = attrs.lists(sym::proc_macro_derive).find_map(|mi| mi.ident()) + { + *name = derive_name.name; + } + + let mut helpers = Vec::new(); + for mi in attrs.lists(sym::proc_macro_derive) { + if !mi.has_name(sym::attributes) { + continue; + } + + if let Some(list) = mi.meta_item_list() { + for inner_mi in list { + if let Some(ident) = inner_mi.ident() { + helpers.push(ident.name); + } + } + } + } + ProcMacroItem(ProcMacro { kind, helpers }) +} + +fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type { + let hir::Ty { hir_id, span, ref kind } = *hir_ty; + let hir::TyKind::Path(qpath) = kind else { unreachable!() }; + + match qpath { + hir::QPath::Resolved(None, path) => { + if let Res::Def(DefKind::TyParam, did) = path.res { + if let Some(new_ty) = cx.args.get(&did).and_then(|p| p.as_ty()).cloned() { + return new_ty; + } + if let Some(bounds) = cx.impl_trait_bounds.remove(&did.into()) { + return ImplTrait(bounds); + } + } + + if let Some(expanded) = maybe_expand_private_type_alias(cx, path) { + expanded + } else { + // First we check if it's a private re-export. + let path = if let Some(path) = first_non_private(cx, hir_id, &path) { + path + } else { + clean_path(path, cx) + }; + resolve_type(cx, path) + } + } + hir::QPath::Resolved(Some(qself), p) => { + // Try to normalize `::T` to a type + let ty = lower_ty(cx.tcx, hir_ty); + // `hir_to_ty` can return projection types with escaping vars for GATs, e.g. `<() as Trait>::Gat<'_>` + if !ty.has_escaping_bound_vars() + && let Some(normalized_value) = super::normalize(cx, ty::Binder::dummy(ty)) + { + return super::clean_middle_ty(normalized_value, cx, None, None); + } + + let trait_segments = &p.segments[..p.segments.len() - 1]; + 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| clean_path_segment(x, cx)).collect(), + }; + register_res(cx, trait_.res); + 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 = + super::compute_should_show_cast(Some(self_def_id), &trait_, &self_type); + Type::QPath(Box::new(QPathData { + assoc: clean_path_segment(p.segments.last().expect("segments were empty"), cx), + should_show_cast, + self_type, + trait_: Some(trait_), + })) + } + hir::QPath::TypeRelative(qself, segment) => { + let ty = lower_ty(cx.tcx, hir_ty); + let self_type = clean_ty(qself, cx); + + let (trait_, should_show_cast) = match ty.kind() { + ty::Alias(ty::Projection, proj) => { + let res = Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id); + let trait_ = clean_path(&hir::Path { span, res, segments: &[] }, cx); + register_res(cx, trait_.res); + let self_def_id = res.opt_def_id(); + let should_show_cast = + super::compute_should_show_cast(self_def_id, &trait_, &self_type); + + (Some(trait_), should_show_cast) + } + ty::Alias(ty::Inherent, _) => (None, false), + // Rustdoc handles `ty::Error`s by turning them into `Type::Infer`s. + ty::Error(_) => return Type::Infer, + _ => bug!("clean: expected associated type, found `{ty:?}`"), + }; + + Type::QPath(Box::new(QPathData { + assoc: clean_path_segment(segment, cx), + should_show_cast, + self_type, + trait_, + })) + } + hir::QPath::LangItem(..) => bug!("clean: requiring documentation of lang item"), + } +} + +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)) => { + let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); + AssocConstItem( + generics, + Box::new(clean_ty(ty, cx)), + ConstantKind::Local { def_id: local_did, body: default }, + ) + } + hir::TraitItemKind::Const(ty, None) => { + let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); + TyAssocConstItem(generics, Box::new(clean_ty(ty, cx))) + } + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { + let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Body(body)); + MethodItem(m, None) + } + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(names)) => { + let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Names(names)); + TyMethodItem(m) + } + 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 = super::clean_middle_ty( + ty::Binder::dummy(lower_ty(cx.tcx, default)), + cx, + None, + None, + ); + AssocTypeItem( + Box::new(TypeAlias { + type_: clean_ty(default, cx), + generics, + inner_type: None, + 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(generics, bounds) + } + }; + Item::from_def_id_and_parts(local_did, Some(trait_item.ident.name), inner, cx) + }) +} + +fn clean_trait_ref<'tcx>(trait_ref: &hir::TraitRef<'tcx>, cx: &mut DocContext<'tcx>) -> Path { + let path = clean_path(trait_ref.path, cx); + register_res(cx, path.res); + path +} + +pub(super) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type { + use rustc_hir::*; + + match ty.kind { + TyKind::Never => Primitive(PrimitiveType::Never), + TyKind::Ptr(ref m) => RawPointer(m.mutbl, Box::new(clean_ty(m.ty, cx))), + TyKind::Ref(ref l, ref m) => { + let lifetime = if l.is_anonymous() { None } else { Some(clean_lifetime(*l, cx)) }; + BorrowedRef { lifetime, mutability: m.mutbl, type_: Box::new(clean_ty(m.ty, cx)) } + } + TyKind::Slice(ty) => Slice(Box::new(clean_ty(ty, cx))), + TyKind::Pat(ty, pat) => Type::Pat(Box::new(clean_ty(ty, cx)), format!("{pat:?}").into()), + TyKind::Array(ty, ref length) => { + let length = match length { + hir::ArrayLen::Infer(..) => "_".to_string(), + hir::ArrayLen::Body(anon_const) => { + // NOTE(min_const_generics): We can't use `const_eval_poly` for constants + // as we currently do not supply the parent generics to anonymous constants + // but do allow `ConstKind::Param`. + // + // `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, anon_const.def_id); + let param_env = cx.tcx.param_env(anon_const.def_id); + print_const(cx, ct.normalize(cx.tcx, param_env)) + } + }; + + Array(Box::new(clean_ty(ty, cx)), length.into()) + } + TyKind::Tup(tys) => Tuple(tys.iter().map(|ty| clean_ty(ty, cx)).collect()), + 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| 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| 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(clean_bare_fn_ty(barefn, cx))), + // Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s. + TyKind::Infer | TyKind::Err(_) | TyKind::Typeof(..) | TyKind::InferDelegation(..) => Infer, + TyKind::AnonAdt(..) => { + unimplemented!("Anonymous structs or unions are not supported yet") + } + } +} + +pub(super) fn clean_use_statement<'tcx>( + import: &hir::Item<'tcx>, + name: Symbol, + path: &hir::UsePath<'tcx>, + kind: hir::UseKind, + cx: &mut DocContext<'tcx>, + inlined_names: &mut FxHashSet<(ItemType, Symbol)>, +) -> Vec { + let mut items = Vec::new(); + let hir::UsePath { segments, ref res, span } = *path; + for &res in res { + let path = hir::Path { segments, res, span }; + items.append(&mut clean_use_statement_inner(import, name, &path, kind, cx, inlined_names)); + } + items +} + +pub(super) fn clean_use_statement_inner<'tcx>( + import: &hir::Item<'tcx>, + name: Symbol, + path: &hir::Path<'tcx>, + kind: hir::UseKind, + cx: &mut DocContext<'tcx>, + inlined_names: &mut FxHashSet<(ItemType, Symbol)>, +) -> Vec { + if should_ignore_res(path.res) { + return Vec::new(); + } + // We need this comparison because some imports (for std types for example) + // are "inserted" as well but directly by the compiler and they should not be + // taken into account. + if import.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::StdImports) { + return Vec::new(); + } + + 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.owner_id.def_id); + let import_def_id = import.owner_id.def_id.to_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 + // level module. + let parent_mod = cx.tcx.parent_module_from_def_id(current_mod.to_local_def_id()); + + // This checks if the import can be seen from a higher level module. + // In other words, it checks if the visibility is the equivalent of + // `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, cx.tcx) && !current_mod.is_top_level_module(); + + if pub_underscore && let Some(ref inline) = inline_attr { + struct_span_code_err!( + cx.tcx.dcx(), + inline.span(), + E0780, + "anonymous imports cannot be inlined" + ) + .with_span_label(import.span, "anonymous import") + .emit(); + } + + // We consider inlining the documentation of `pub use` statements, but we + // forcefully don't inline if this is not public or if the + // #[doc(no_inline)] attribute is present. + // Don't inline doc(hidden) imports so they can be stripped at a later stage. + let mut denied = cx.output_format.is_json() + || !(visibility.is_public() + || (cx.render_options.document_private && is_visible_from_parent_mod)) + || pub_underscore + || attrs.iter().any(|a| { + a.has_name(sym::doc) + && match a.meta_item_list() { + Some(l) => { + attr::list_contains_name(&l, sym::no_inline) + || attr::list_contains_name(&l, sym::hidden) + } + None => false, + } + }); + + // Also check whether imports were asked to be inlined, in case we're trying to re-export a + // crate in Rust 2018+ + let path = clean_path(path, cx); + let inner = if kind == hir::UseKind::Glob { + if !denied { + let mut visited = DefIdSet::default(); + if let Some(items) = inline::try_inline_glob( + cx, + path.res, + current_mod, + &mut visited, + inlined_names, + import, + ) { + return items; + } + } + Import::new_glob(resolve_use_source(cx, path), true) + } else { + if inline_attr.is_none() + && let Res::Def(DefKind::Mod, did) = path.res + && !did.is_local() + && did.is_crate_root() + { + // if we're `pub use`ing an extern crate root, don't inline it unless we + // were specifically asked for it + denied = true; + } + if !denied + && let Some(mut items) = inline::try_inline( + cx, + path.res, + name, + Some((attrs, Some(import_def_id))), + &mut Default::default(), + ) + { + items.push(Item::from_def_id_and_parts( + import_def_id, + None, + ImportItem(Import::new_simple(name, resolve_use_source(cx, path), false)), + cx, + )); + return items; + } + Import::new_simple(name, resolve_use_source(cx, path), true) + }; + + vec![Item::from_def_id_and_parts(import_def_id, None, ImportItem(inner), cx)] +} + +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)); + Item::from_def_id_and_parts(variant.def_id.to_def_id(), Some(variant.ident.name), kind, cx) +} + +fn clean_variant_data<'tcx>( + variant: &hir::VariantData<'tcx>, + disr_expr: &Option<&hir::AnonConst>, + cx: &mut DocContext<'tcx>, +) -> Variant { + let discriminant = disr_expr + .map(|disr| Discriminant { expr: Some(disr.body), value: disr.def_id.to_def_id() }); + + let kind = match variant { + hir::VariantData::Struct { fields, .. } => VariantKind::Struct(VariantStruct { + fields: fields.iter().map(|x| clean_field(x, cx)).collect(), + }), + hir::VariantData::Tuple(..) => { + VariantKind::Tuple(variant.fields().iter().map(|x| clean_field(x, cx)).collect()) + } + hir::VariantData::Unit(..) => VariantKind::CLike, + }; + + Variant { discriminant, kind } +} + +fn clean_where_predicate<'tcx>( + predicate: &hir::WherePredicate<'tcx>, + cx: &mut DocContext<'tcx>, +) -> Option { + 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| clean_generic_param(cx, None, param)) + .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| 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(), + }, + }) +} + +/// The goal of this function is to return the first `Path` which is not private (ie not private +/// or `doc(hidden)`). If it's not possible, it'll return the "end type". +/// +/// If the path is not a re-export or is public, it'll return `None`. +fn first_non_private<'tcx>( + cx: &mut DocContext<'tcx>, + hir_id: hir::HirId, + path: &hir::Path<'tcx>, +) -> Option { + let target_def_id = path.res.opt_def_id()?; + let (parent_def_id, ident) = match &path.segments { + [] => return None, + // Relative paths are available in the same scope as the owner. + [leaf] => (cx.tcx.local_parent(hir_id.owner.def_id), leaf.ident), + // So are self paths. + [parent, leaf] if parent.ident.name == kw::SelfLower => { + (cx.tcx.local_parent(hir_id.owner.def_id), leaf.ident) + } + // Crate paths are not. We start from the crate root. + [parent, leaf] if matches!(parent.ident.name, kw::Crate | kw::PathRoot) => { + (LOCAL_CRATE.as_def_id().as_local()?, leaf.ident) + } + [parent, leaf] if parent.ident.name == kw::Super => { + let parent_mod = cx.tcx.parent_module(hir_id); + if let Some(super_parent) = cx.tcx.opt_local_parent(parent_mod.to_local_def_id()) { + (super_parent, leaf.ident) + } else { + // If we can't find the parent of the parent, then the parent is already the crate. + (LOCAL_CRATE.as_def_id().as_local()?, leaf.ident) + } + } + // Absolute paths are not. We start from the parent of the item. + [.., parent, leaf] => (parent.res.opt_def_id()?.as_local()?, leaf.ident), + }; + // First we try to get the `DefId` of the item. + for child in + cx.tcx.module_children_local(parent_def_id).iter().filter(move |c| c.ident == ident) + { + if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = child.res { + continue; + } + + if let Some(def_id) = child.res.opt_def_id() + && target_def_id == def_id + { + let mut last_path_res = None; + 'reexps: for reexp in child.reexport_chain.iter() { + if let Some(use_def_id) = reexp.id() + && let Some(local_use_def_id) = use_def_id.as_local() + && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(local_use_def_id) + && !item.ident.name.is_empty() + && let hir::ItemKind::Use(path, _) = item.kind + { + for res in &path.res { + if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { + continue; + } + if (cx.render_options.document_hidden || + !cx.tcx.is_doc_hidden(use_def_id)) && + // We never check for "cx.render_options.document_private" + // because if a re-export is not fully public, it's never + // documented. + cx.tcx.local_visibility(local_use_def_id).is_public() + { + break 'reexps; + } + last_path_res = Some((path, res)); + continue 'reexps; + } + } + } + if !child.reexport_chain.is_empty() { + // So in here, we use the data we gathered from iterating the reexports. If + // `last_path_res` is set, it can mean two things: + // + // 1. We found a public reexport. + // 2. We didn't find a public reexport so it's the "end type" path. + if let Some((new_path, _)) = last_path_res { + return Some(first_non_private_clean_path( + cx, + path, + new_path.segments, + new_path.span, + )); + } + // If `last_path_res` is `None`, it can mean two things: + // + // 1. The re-export is public, no need to change anything, just use the path as is. + // 2. Nothing was found, so let's just return the original path. + return None; + } + } + } + None +} + +fn first_non_private_clean_path<'tcx>( + cx: &mut DocContext<'tcx>, + path: &hir::Path<'tcx>, + new_path_segments: &'tcx [hir::PathSegment<'tcx>], + new_path_span: rustc_span::Span, +) -> Path { + let new_hir_path = + hir::Path { segments: new_path_segments, res: path.res, span: new_path_span }; + let mut new_clean_path = clean_path(&new_hir_path, cx); + // In here we need to play with the path data one last time to provide it the + // missing `args` and `res` of the final `Path` we get, which, since it comes + // from a re-export, doesn't have the generics that were originally there, so + // we add them by hand. + if let Some(path_last) = path.segments.last().as_ref() + && let Some(new_path_last) = new_clean_path.segments[..].last_mut() + && let Some(path_last_args) = path_last.args.as_ref() + && path_last.args.is_some() + { + assert!(new_path_last.args.is_empty()); + new_path_last.args = clean_generic_args(path_last_args, cx); + } + new_clean_path +} + +/// This can happen for `async fn`, e.g. `async fn f<'_>(&'_ self)`. +/// +/// See `lifetime_to_generic_param` in `rustc_ast_lowering` for more information. +fn is_elided_lifetime(param: &hir::GenericParam<'_>) -> bool { + matches!( + param.kind, + hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided(_) } + ) +} + +/// Synthetic type-parameters are inserted after normal ones. +/// In order for normal parameters to be able to refer to synthetic ones, +/// scans them first. +fn is_impl_trait(param: &hir::GenericParam<'_>) -> bool { + match param.kind { + hir::GenericParamKind::Type { synthetic, .. } => synthetic, + _ => false, + } +} + +fn maybe_expand_private_type_alias<'tcx>( + cx: &mut DocContext<'tcx>, + path: &hir::Path<'tcx>, +) -> Option { + 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.effective_visibilities.is_exported(cx.tcx, def_id.to_def_id()) + && !cx.current_type_aliases.contains_key(&def_id.to_def_id()) + { + &cx.tcx.hir().expect_item(def_id).kind + } else { + return None; + }; + let hir::ItemKind::TyAlias(ty, generics) = alias else { return None }; + + let provided_params = &path.segments.last().expect("segments were empty"); + let mut args = DefIdMap::default(); + let generic_args = provided_params.args(); + + let mut indices: hir::GenericParamCount = Default::default(); + for param in generics.params.iter() { + match param.kind { + hir::GenericParamKind::Lifetime { .. } => { + let mut j = 0; + let lifetime = generic_args.args.iter().find_map(|arg| match arg { + hir::GenericArg::Lifetime(lt) => { + if indices.lifetimes == j { + return Some(lt); + } + j += 1; + None + } + _ => None, + }); + if let Some(lt) = lifetime { + let lt = if !lt.is_anonymous() { + clean_lifetime(lt, cx) + } else { + Lifetime::elided() + }; + args.insert(param.def_id.to_def_id(), GenericArg::Lifetime(lt)); + } + indices.lifetimes += 1; + } + hir::GenericParamKind::Type { ref default, .. } => { + let mut j = 0; + let type_ = generic_args.args.iter().find_map(|arg| match arg { + hir::GenericArg::Type(ty) => { + if indices.types == j { + return Some(*ty); + } + j += 1; + None + } + _ => None, + }); + if let Some(ty) = type_.or(*default) { + args.insert(param.def_id.to_def_id(), GenericArg::Type(clean_ty(ty, cx))); + } + indices.types += 1; + } + // FIXME(#82852): Instantiate const parameters. + hir::GenericParamKind::Const { .. } => {} + } + } + + Some(cx.enter_alias(args, def_id.to_def_id(), |cx| { + cx.with_param_env(def_id.to_def_id(), |cx| clean_ty(&ty, cx)) + })) +} + +enum FunctionArgs<'tcx> { + Body(hir::BodyId), + Names(&'tcx [Ident]), +} diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 22565ea402803..6b8ba18f3009b 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -6,7 +6,7 @@ //! There are two kinds of transformation — *cleaning* — procedures: //! //! 1. Cleans [HIR][hir] types. Used for user-written code and inlined local re-exports -//! both found in the local crate. +//! both found in the local crate. These are defined in module [`local`]. //! 2. Cleans [`rustc_middle::ty`] types. Used for inlined cross-crate re-exports and anything //! output by the trait solver (e.g., when synthesizing blanket and auto-trait impls). //! They usually have `ty` or `middle` in their name. @@ -25,6 +25,7 @@ mod auto_trait; mod blanket_impl; pub(crate) mod cfg; pub(crate) mod inline; +pub(crate) mod local; mod render_macro_matchers; mod simplify; pub(crate) mod types; @@ -33,23 +34,17 @@ pub(crate) mod utils; use rustc_ast as ast; use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; -use rustc_attr as attr; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, IndexEntry}; -use rustc_errors::{codes::*, struct_span_code_err, FatalError}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_errors::FatalError; use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE}; -use rustc_hir::PredicateOrigin; -use rustc_hir_analysis::lower_ty; +use rustc_hir::def::{CtorKind, DefKind}; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::metadata::Reexport; use rustc_middle::middle::resolve_bound_vars as rbv; use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, AdtKind, 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::ExpnKind; +use rustc_span::symbol::{kw, sym, Symbol}; use rustc_trait_selection::traits::wf::object_region_bounds; use std::borrow::Cow; @@ -66,11 +61,12 @@ use utils::*; pub(crate) use self::types::*; pub(crate) use self::utils::{krate, register_res, synthesize_auto_trait_and_blanket_impls}; +// FIXME: move into local? pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<'tcx>) -> Item { let mut items: Vec = vec![]; let mut inserted = FxHashSet::default(); items.extend(doc.foreigns.iter().map(|(item, renamed)| { - let item = clean_maybe_renamed_foreign_item(cx, item, *renamed); + let item = local::clean_maybe_renamed_foreign_item(cx, item, *renamed); if let Some(name) = item.name && (cx.render_options.document_hidden || !item.is_doc_hidden()) { @@ -102,7 +98,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) { return Vec::new(); } - let v = clean_maybe_renamed_item(cx, item, *renamed, *import_id); + let v = local::clean_maybe_renamed_item(cx, item, *renamed, *import_id); for item in &v { if let Some(name) = item.name && (cx.render_options.document_hidden || !item.is_doc_hidden()) @@ -120,7 +116,14 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< hir::ItemKind::Use(path, kind) => { let hir::UsePath { segments, span, .. } = *path; let path = hir::Path { segments, res: *res, span }; - clean_use_statement_inner(import, name, &path, kind, cx, &mut Default::default()) + local::clean_use_statement_inner( + import, + name, + &path, + kind, + cx, + &mut Default::default(), + ) } _ => unreachable!(), } @@ -129,7 +132,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< // 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) + local::clean_use_statement(item, name, path, hir::UseKind::Glob, cx, &mut inserted) } else { // skip everything else Vec::new() @@ -212,27 +215,6 @@ fn generate_item_with_correct_attrs( item } -fn clean_generic_bound<'tcx>( - bound: &hir::GenericBound<'tcx>, - cx: &mut DocContext<'tcx>, -) -> Option { - Some(match *bound { - hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)), - 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(clean_poly_trait_ref(t, cx), modifier) - } - // FIXME(precise_capturing): Implement rustdoc support - hir::GenericBound::Use(..) => return None, - }) -} - pub(crate) fn clean_trait_ref_with_constraints<'tcx>( cx: &mut DocContext<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, @@ -270,26 +252,6 @@ fn clean_poly_trait_ref_with_constraints<'tcx>( ) } -fn clean_lifetime<'tcx>(lifetime: &hir::Lifetime, cx: &mut DocContext<'tcx>) -> Lifetime { - if let Some( - rbv::ResolvedArg::EarlyBound(did) - | rbv::ResolvedArg::LateBound(_, _, did) - | rbv::ResolvedArg::Free(_, did), - ) = cx.tcx.named_bound_var(lifetime.hir_id) - && let Some(lt) = cx.args.get(&did).and_then(|arg| arg.as_lt()) - { - return lt.clone(); - } - Lifetime(lifetime.ident.name) -} - -pub(crate) fn clean_const<'tcx>( - constant: &hir::ConstArg<'_>, - _cx: &mut DocContext<'tcx>, -) -> Constant { - Constant { kind: ConstantKind::Anonymous { body: constant.value.body } } -} - pub(crate) fn clean_middle_const<'tcx>( constant: ty::Binder<'tcx, ty::Const<'tcx>>, _cx: &mut DocContext<'tcx>, @@ -316,39 +278,6 @@ pub(crate) fn clean_middle_region<'tcx>(region: ty::Region<'tcx>) -> Option( - predicate: &hir::WherePredicate<'tcx>, - cx: &mut DocContext<'tcx>, -) -> Option { - 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| clean_generic_param(cx, None, param)) - .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| 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(), - }, - }) -} - pub(crate) fn clean_predicate<'tcx>( predicate: ty::Clause<'tcx>, cx: &mut DocContext<'tcx>, @@ -426,16 +355,6 @@ fn clean_middle_term<'tcx>( } } -fn clean_hir_term<'tcx>(term: &hir::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Term { - match term { - hir::Term::Ty(ty) => Term::Type(clean_ty(ty, cx)), - hir::Term::Const(c) => Term::Constant(clean_middle_const( - ty::Binder::dummy(ty::Const::from_anon_const(cx.tcx, c.def_id)), - cx, - )), - } -} - fn clean_projection_predicate<'tcx>( pred: ty::Binder<'tcx, ty::ProjectionPredicate<'tcx>>, cx: &mut DocContext<'tcx>, @@ -489,6 +408,7 @@ fn clean_projection<'tcx>( })) } +// FIXME: maybe move to utils? fn compute_should_show_cast(self_def_id: Option, trait_: &Path, self_type: &Type) -> bool { !trait_.segments.is_empty() && self_def_id @@ -586,204 +506,6 @@ enum ParamDefaults { No, } -fn clean_generic_param<'tcx>( - cx: &mut DocContext<'tcx>, - generics: Option<&hir::Generics<'tcx>>, - param: &hir::GenericParam<'tcx>, -) -> GenericParamDef { - let (name, kind) = match param.kind { - hir::GenericParamKind::Lifetime { .. } => { - let outlives = if let Some(generics) = generics { - generics - .outlives_for_param(param.def_id) - .filter(|bp| !bp.in_where_clause) - .flat_map(|bp| bp.bounds) - .map(|bound| match bound { - hir::GenericBound::Outlives(lt) => clean_lifetime(lt, cx), - _ => panic!(), - }) - .collect() - } else { - ThinVec::new() - }; - (param.name.ident().name, GenericParamDefKind::Lifetime { outlives }) - } - hir::GenericParamKind::Type { ref default, synthetic } => { - let bounds = if let Some(generics) = generics { - generics - .bounds_for_param(param.def_id) - .filter(|bp| bp.origin != PredicateOrigin::WhereClause) - .flat_map(|bp| bp.bounds) - .filter_map(|x| clean_generic_bound(x, cx)) - .collect() - } else { - ThinVec::new() - }; - ( - param.name.ident().name, - GenericParamDefKind::Type { - bounds, - default: default.map(|t| clean_ty(t, cx)).map(Box::new), - synthetic, - }, - ) - } - hir::GenericParamKind::Const { ty, default, is_host_effect } => ( - param.name.ident().name, - GenericParamDefKind::Const { - ty: Box::new(clean_ty(ty, cx)), - default: default - .map(|ct| Box::new(ty::Const::from_anon_const(cx.tcx, ct.def_id).to_string())), - is_host_effect, - }, - ), - }; - - GenericParamDef { name, def_id: param.def_id.to_def_id(), kind } -} - -/// Synthetic type-parameters are inserted after normal ones. -/// In order for normal parameters to be able to refer to synthetic ones, -/// scans them first. -fn is_impl_trait(param: &hir::GenericParam<'_>) -> bool { - match param.kind { - hir::GenericParamKind::Type { synthetic, .. } => synthetic, - _ => false, - } -} - -/// This can happen for `async fn`, e.g. `async fn f<'_>(&'_ self)`. -/// -/// See `lifetime_to_generic_param` in `rustc_ast_lowering` for more information. -fn is_elided_lifetime(param: &hir::GenericParam<'_>) -> bool { - matches!( - param.kind, - hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided(_) } - ) -} - -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 { ref bounds, .. } => { - cx.impl_trait_bounds.insert(param.def_id.into(), bounds.to_vec()); - } - GenericParamDefKind::Const { .. } => unreachable!(), - } - param - }) - .collect::>(); - - let mut bound_predicates = FxIndexMap::default(); - let mut region_predicates = FxIndexMap::default(); - let mut eq_predicates = ThinVec::default(); - for pred in gens.predicates.iter().filter_map(|x| clean_where_predicate(x, cx)) { - match pred { - WherePredicate::BoundPredicate { ty, bounds, bound_params } => { - match bound_predicates.entry(ty) { - IndexEntry::Vacant(v) => { - v.insert((bounds, bound_params)); - } - IndexEntry::Occupied(mut o) => { - // we merge both bounds. - for bound in bounds { - if !o.get().0.contains(&bound) { - o.get_mut().0.push(bound); - } - } - for bound_param in bound_params { - if !o.get().1.contains(&bound_param) { - o.get_mut().1.push(bound_param); - } - } - } - } - } - WherePredicate::RegionPredicate { lifetime, bounds } => { - match region_predicates.entry(lifetime) { - IndexEntry::Vacant(v) => { - v.insert(bounds); - } - IndexEntry::Occupied(mut o) => { - // we merge both bounds. - for bound in bounds { - if !o.get().contains(&bound) { - o.get_mut().push(bound); - } - } - } - } - } - WherePredicate::EqPredicate { lhs, rhs } => { - eq_predicates.push(WherePredicate::EqPredicate { lhs, rhs }); - } - } - } - - let mut params = ThinVec::with_capacity(gens.params.len()); - // In this loop, we gather the generic parameters (`<'a, B: 'a>`) and check if they have - // bounds in the where predicates. If so, we move their bounds into the where predicates - // while also preventing duplicates. - for p in gens.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) { - let mut p = clean_generic_param(cx, Some(gens), p); - match &mut p.kind { - GenericParamDefKind::Lifetime { ref mut outlives } => { - if let Some(region_pred) = region_predicates.get_mut(&Lifetime(p.name)) { - // We merge bounds in the `where` clause. - for outlive in outlives.drain(..) { - let outlive = GenericBound::Outlives(outlive); - if !region_pred.contains(&outlive) { - region_pred.push(outlive); - } - } - } - } - GenericParamDefKind::Type { bounds, synthetic: false, .. } => { - if let Some(bound_pred) = bound_predicates.get_mut(&Type::Generic(p.name)) { - // We merge bounds in the `where` clause. - for bound in bounds.drain(..) { - if !bound_pred.0.contains(&bound) { - bound_pred.0.push(bound); - } - } - } - } - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { - // nothing to do here. - } - } - params.push(p); - } - params.extend(impl_trait_params); - - Generics { - params, - where_predicates: bound_predicates - .into_iter() - .map(|(ty, (bounds, bound_params))| WherePredicate::BoundPredicate { - ty, - bounds, - bound_params, - }) - .chain( - region_predicates - .into_iter() - .map(|(lifetime, bounds)| WherePredicate::RegionPredicate { lifetime, bounds }), - ) - .chain(eq_predicates) - .collect(), - } -} - fn clean_ty_generics<'tcx>( cx: &mut DocContext<'tcx>, gens: &ty::Generics, @@ -925,6 +647,7 @@ fn clean_ty_generics<'tcx>( generics } +// FIXME: maybe move to utils? fn clean_ty_alias_inner_type<'tcx>( ty: Ty<'tcx>, cx: &mut DocContext<'tcx>, @@ -979,187 +702,6 @@ fn clean_ty_alias_inner_type<'tcx>( }) } -fn clean_proc_macro<'tcx>( - item: &hir::Item<'tcx>, - name: &mut Symbol, - kind: MacroKind, - cx: &mut DocContext<'tcx>, -) -> ItemKind { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - if kind == MacroKind::Derive - && let Some(derive_name) = attrs.lists(sym::proc_macro_derive).find_map(|mi| mi.ident()) - { - *name = derive_name.name; - } - - let mut helpers = Vec::new(); - for mi in attrs.lists(sym::proc_macro_derive) { - if !mi.has_name(sym::attributes) { - continue; - } - - if let Some(list) = mi.meta_item_list() { - for inner_mi in list { - if let Some(ident) = inner_mi.ident() { - helpers.push(ident.name); - } - } - } - } - ProcMacroItem(ProcMacro { kind, helpers }) -} - -fn clean_fn_or_proc_macro<'tcx>( - item: &hir::Item<'tcx>, - sig: &hir::FnSig<'tcx>, - generics: &hir::Generics<'tcx>, - body_id: hir::BodyId, - name: &mut Symbol, - cx: &mut DocContext<'tcx>, -) -> ItemKind { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - let macro_kind = attrs.iter().find_map(|a| { - if a.has_name(sym::proc_macro) { - Some(MacroKind::Bang) - } else if a.has_name(sym::proc_macro_derive) { - Some(MacroKind::Derive) - } else if a.has_name(sym::proc_macro_attribute) { - Some(MacroKind::Attr) - } else { - None - } - }); - match macro_kind { - Some(kind) => clean_proc_macro(item, name, kind, cx), - None => { - let mut func = clean_function(cx, sig, generics, FunctionArgs::Body(body_id)); - clean_fn_decl_legacy_const_generics(&mut func, attrs); - FunctionItem(func) - } - } -} - -/// This is needed to make it more "readable" when documenting functions using -/// `rustc_legacy_const_generics`. More information in -/// . -fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[ast::Attribute]) { - for meta_item_list in attrs - .iter() - .filter(|a| a.has_name(sym::rustc_legacy_const_generics)) - .filter_map(|a| a.meta_item_list()) - { - for (pos, literal) in meta_item_list.iter().filter_map(|meta| meta.lit()).enumerate() { - match literal.kind { - ast::LitKind::Int(a, _) => { - let gen = func.generics.params.remove(0); - if let GenericParamDef { - name, - kind: GenericParamDefKind::Const { ty, .. }, - .. - } = gen - { - func.decl - .inputs - .values - .insert(a.get() as _, Argument { name, type_: *ty, is_const: true }); - } else { - panic!("unexpected non const in position {pos}"); - } - } - _ => panic!("invalid arg index"), - } - } - } -} - -enum FunctionArgs<'tcx> { - Body(hir::BodyId), - Names(&'tcx [Ident]), -} - -fn clean_function<'tcx>( - cx: &mut DocContext<'tcx>, - sig: &hir::FnSig<'tcx>, - generics: &hir::Generics<'tcx>, - args: FunctionArgs<'tcx>, -) -> Box { - let (generics, decl) = enter_impl_trait(cx, |cx| { - // NOTE: generics must be cleaned before args - let generics = clean_generics(generics, cx); - let args = match args { - FunctionArgs::Body(body_id) => { - clean_args_from_types_and_body_id(cx, sig.decl.inputs, body_id) - } - FunctionArgs::Names(names) => { - clean_args_from_types_and_names(cx, sig.decl.inputs, names) - } - }; - let decl = clean_fn_decl_with_args(cx, sig.decl, Some(&sig.header), args); - (generics, decl) - }); - Box::new(Function { decl, generics }) -} - -fn clean_args_from_types_and_names<'tcx>( - cx: &mut DocContext<'tcx>, - types: &[hir::Ty<'tcx>], - names: &[Ident], -) -> Arguments { - Arguments { - values: types - .iter() - .enumerate() - .map(|(i, ty)| Argument { - type_: clean_ty(ty, cx), - name: names - .get(i) - .map(|ident| ident.name) - .filter(|ident| !ident.is_empty()) - .unwrap_or(kw::Underscore), - is_const: false, - }) - .collect(), - } -} - -fn clean_args_from_types_and_body_id<'tcx>( - cx: &mut DocContext<'tcx>, - types: &[hir::Ty<'tcx>], - body_id: hir::BodyId, -) -> Arguments { - let body = cx.tcx.hir().body(body_id); - - Arguments { - values: types - .iter() - .enumerate() - .map(|(i, ty)| Argument { - name: name_from_pat(body.params[i].pat), - type_: clean_ty(ty, cx), - is_const: false, - }) - .collect(), - } -} - -fn clean_fn_decl_with_args<'tcx>( - cx: &mut DocContext<'tcx>, - decl: &hir::FnDecl<'tcx>, - header: Option<&hir::FnHeader>, - args: Arguments, -) -> FnDecl { - let mut output = match decl.output { - hir::FnRetTy::Return(typ) => clean_ty(typ, cx), - hir::FnRetTy::DefaultReturn(..) => Type::Tuple(Vec::new()), - }; - if let Some(header) = header - && header.is_async() - { - output = output.sugared_async_return_type(); - } - FnDecl { inputs: args, output, c_variadic: decl.c_variadic } -} - fn clean_poly_fn_sig<'tcx>( cx: &mut DocContext<'tcx>, did: Option, @@ -1202,114 +744,6 @@ fn clean_poly_fn_sig<'tcx>( } } -fn clean_trait_ref<'tcx>(trait_ref: &hir::TraitRef<'tcx>, cx: &mut DocContext<'tcx>) -> Path { - let path = clean_path(trait_ref.path, cx); - register_res(cx, path.res); - path -} - -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(), - } -} - -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)) => { - let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); - AssocConstItem( - generics, - Box::new(clean_ty(ty, cx)), - ConstantKind::Local { def_id: local_did, body: default }, - ) - } - hir::TraitItemKind::Const(ty, None) => { - let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); - TyAssocConstItem(generics, Box::new(clean_ty(ty, cx))) - } - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Body(body)); - MethodItem(m, None) - } - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(names)) => { - let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Names(names)); - TyMethodItem(m) - } - 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(ty::Binder::dummy(lower_ty(cx.tcx, default)), cx, None, None); - AssocTypeItem( - Box::new(TypeAlias { - type_: clean_ty(default, cx), - generics, - inner_type: None, - 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(generics, bounds) - } - }; - Item::from_def_id_and_parts(local_did, Some(trait_item.ident.name), inner, cx) - }) -} - -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 generics = clean_generics(impl_.generics, cx); - let default = ConstantKind::Local { def_id: local_did, body: expr }; - AssocConstItem(generics, Box::new(clean_ty(ty, cx)), default) - } - hir::ImplItemKind::Fn(ref sig, body) => { - let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body)); - let defaultness = cx.tcx.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(ty::Binder::dummy(lower_ty(cx.tcx, hir_ty)), cx, None, None); - AssocTypeItem( - Box::new(TypeAlias { - type_, - generics, - inner_type: None, - item_type: Some(item_type), - }), - Vec::new(), - ) - } - }; - - Item::from_def_id_and_parts(local_did, Some(impl_.ident.name), inner, cx) - }) -} - pub(crate) fn clean_middle_assoc_item<'tcx>( assoc_item: &ty::AssocItem, cx: &mut DocContext<'tcx>, @@ -1523,341 +957,8 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name), kind, cx) } -fn first_non_private_clean_path<'tcx>( - cx: &mut DocContext<'tcx>, - path: &hir::Path<'tcx>, - new_path_segments: &'tcx [hir::PathSegment<'tcx>], - new_path_span: rustc_span::Span, -) -> Path { - let new_hir_path = - hir::Path { segments: new_path_segments, res: path.res, span: new_path_span }; - let mut new_clean_path = clean_path(&new_hir_path, cx); - // In here we need to play with the path data one last time to provide it the - // missing `args` and `res` of the final `Path` we get, which, since it comes - // from a re-export, doesn't have the generics that were originally there, so - // we add them by hand. - if let Some(path_last) = path.segments.last().as_ref() - && let Some(new_path_last) = new_clean_path.segments[..].last_mut() - && let Some(path_last_args) = path_last.args.as_ref() - && path_last.args.is_some() - { - assert!(new_path_last.args.is_empty()); - new_path_last.args = clean_generic_args(path_last_args, cx); - } - new_clean_path -} - -/// The goal of this function is to return the first `Path` which is not private (ie not private -/// or `doc(hidden)`). If it's not possible, it'll return the "end type". -/// -/// If the path is not a re-export or is public, it'll return `None`. -fn first_non_private<'tcx>( - cx: &mut DocContext<'tcx>, - hir_id: hir::HirId, - path: &hir::Path<'tcx>, -) -> Option { - let target_def_id = path.res.opt_def_id()?; - let (parent_def_id, ident) = match &path.segments { - [] => return None, - // Relative paths are available in the same scope as the owner. - [leaf] => (cx.tcx.local_parent(hir_id.owner.def_id), leaf.ident), - // So are self paths. - [parent, leaf] if parent.ident.name == kw::SelfLower => { - (cx.tcx.local_parent(hir_id.owner.def_id), leaf.ident) - } - // Crate paths are not. We start from the crate root. - [parent, leaf] if matches!(parent.ident.name, kw::Crate | kw::PathRoot) => { - (LOCAL_CRATE.as_def_id().as_local()?, leaf.ident) - } - [parent, leaf] if parent.ident.name == kw::Super => { - let parent_mod = cx.tcx.parent_module(hir_id); - if let Some(super_parent) = cx.tcx.opt_local_parent(parent_mod.to_local_def_id()) { - (super_parent, leaf.ident) - } else { - // If we can't find the parent of the parent, then the parent is already the crate. - (LOCAL_CRATE.as_def_id().as_local()?, leaf.ident) - } - } - // Absolute paths are not. We start from the parent of the item. - [.., parent, leaf] => (parent.res.opt_def_id()?.as_local()?, leaf.ident), - }; - // First we try to get the `DefId` of the item. - for child in - cx.tcx.module_children_local(parent_def_id).iter().filter(move |c| c.ident == ident) - { - if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = child.res { - continue; - } - - if let Some(def_id) = child.res.opt_def_id() - && target_def_id == def_id - { - let mut last_path_res = None; - 'reexps: for reexp in child.reexport_chain.iter() { - if let Some(use_def_id) = reexp.id() - && let Some(local_use_def_id) = use_def_id.as_local() - && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(local_use_def_id) - && !item.ident.name.is_empty() - && let hir::ItemKind::Use(path, _) = item.kind - { - for res in &path.res { - if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { - continue; - } - if (cx.render_options.document_hidden || - !cx.tcx.is_doc_hidden(use_def_id)) && - // We never check for "cx.render_options.document_private" - // because if a re-export is not fully public, it's never - // documented. - cx.tcx.local_visibility(local_use_def_id).is_public() - { - break 'reexps; - } - last_path_res = Some((path, res)); - continue 'reexps; - } - } - } - if !child.reexport_chain.is_empty() { - // So in here, we use the data we gathered from iterating the reexports. If - // `last_path_res` is set, it can mean two things: - // - // 1. We found a public reexport. - // 2. We didn't find a public reexport so it's the "end type" path. - if let Some((new_path, _)) = last_path_res { - return Some(first_non_private_clean_path( - cx, - path, - new_path.segments, - new_path.span, - )); - } - // If `last_path_res` is `None`, it can mean two things: - // - // 1. The re-export is public, no need to change anything, just use the path as is. - // 2. Nothing was found, so let's just return the original path. - return None; - } - } - } - None -} - -fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type { - let hir::Ty { hir_id, span, ref kind } = *hir_ty; - let hir::TyKind::Path(qpath) = kind else { unreachable!() }; - - match qpath { - hir::QPath::Resolved(None, path) => { - if let Res::Def(DefKind::TyParam, did) = path.res { - if let Some(new_ty) = cx.args.get(&did).and_then(|p| p.as_ty()).cloned() { - return new_ty; - } - if let Some(bounds) = cx.impl_trait_bounds.remove(&did.into()) { - return ImplTrait(bounds); - } - } - - if let Some(expanded) = maybe_expand_private_type_alias(cx, path) { - expanded - } else { - // First we check if it's a private re-export. - let path = if let Some(path) = first_non_private(cx, hir_id, &path) { - path - } else { - clean_path(path, cx) - }; - resolve_type(cx, path) - } - } - hir::QPath::Resolved(Some(qself), p) => { - // Try to normalize `::T` to a type - let ty = lower_ty(cx.tcx, hir_ty); - // `hir_to_ty` can return projection types with escaping vars for GATs, e.g. `<() as Trait>::Gat<'_>` - if !ty.has_escaping_bound_vars() - && let Some(normalized_value) = normalize(cx, ty::Binder::dummy(ty)) - { - return clean_middle_ty(normalized_value, cx, None, None); - } - - let trait_segments = &p.segments[..p.segments.len() - 1]; - 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| clean_path_segment(x, cx)).collect(), - }; - register_res(cx, trait_.res); - 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(Box::new(QPathData { - assoc: clean_path_segment(p.segments.last().expect("segments were empty"), cx), - should_show_cast, - self_type, - trait_: Some(trait_), - })) - } - hir::QPath::TypeRelative(qself, segment) => { - let ty = lower_ty(cx.tcx, hir_ty); - let self_type = clean_ty(qself, cx); - - let (trait_, should_show_cast) = match ty.kind() { - ty::Alias(ty::Projection, proj) => { - let res = Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id); - let trait_ = clean_path(&hir::Path { span, res, segments: &[] }, cx); - register_res(cx, trait_.res); - let self_def_id = res.opt_def_id(); - let should_show_cast = - compute_should_show_cast(self_def_id, &trait_, &self_type); - - (Some(trait_), should_show_cast) - } - ty::Alias(ty::Inherent, _) => (None, false), - // Rustdoc handles `ty::Error`s by turning them into `Type::Infer`s. - ty::Error(_) => return Type::Infer, - _ => bug!("clean: expected associated type, found `{ty:?}`"), - }; - - Type::QPath(Box::new(QPathData { - assoc: clean_path_segment(segment, cx), - should_show_cast, - self_type, - trait_, - })) - } - hir::QPath::LangItem(..) => bug!("clean: requiring documentation of lang item"), - } -} - -fn maybe_expand_private_type_alias<'tcx>( - cx: &mut DocContext<'tcx>, - path: &hir::Path<'tcx>, -) -> Option { - 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.effective_visibilities.is_exported(cx.tcx, def_id.to_def_id()) - && !cx.current_type_aliases.contains_key(&def_id.to_def_id()) - { - &cx.tcx.hir().expect_item(def_id).kind - } else { - return None; - }; - let hir::ItemKind::TyAlias(ty, generics) = alias else { return None }; - - let provided_params = &path.segments.last().expect("segments were empty"); - let mut args = DefIdMap::default(); - let generic_args = provided_params.args(); - - let mut indices: hir::GenericParamCount = Default::default(); - for param in generics.params.iter() { - match param.kind { - hir::GenericParamKind::Lifetime { .. } => { - let mut j = 0; - let lifetime = generic_args.args.iter().find_map(|arg| match arg { - hir::GenericArg::Lifetime(lt) => { - if indices.lifetimes == j { - return Some(lt); - } - j += 1; - None - } - _ => None, - }); - if let Some(lt) = lifetime { - let lt = if !lt.is_anonymous() { - clean_lifetime(lt, cx) - } else { - Lifetime::elided() - }; - args.insert(param.def_id.to_def_id(), GenericArg::Lifetime(lt)); - } - indices.lifetimes += 1; - } - hir::GenericParamKind::Type { ref default, .. } => { - let mut j = 0; - let type_ = generic_args.args.iter().find_map(|arg| match arg { - hir::GenericArg::Type(ty) => { - if indices.types == j { - return Some(*ty); - } - j += 1; - None - } - _ => None, - }); - if let Some(ty) = type_.or(*default) { - args.insert(param.def_id.to_def_id(), GenericArg::Type(clean_ty(ty, cx))); - } - indices.types += 1; - } - // FIXME(#82852): Instantiate const parameters. - hir::GenericParamKind::Const { .. } => {} - } - } - - Some(cx.enter_alias(args, def_id.to_def_id(), |cx| { - cx.with_param_env(def_id.to_def_id(), |cx| clean_ty(&ty, cx)) - })) -} - -pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type { - use rustc_hir::*; - - match ty.kind { - TyKind::Never => Primitive(PrimitiveType::Never), - TyKind::Ptr(ref m) => RawPointer(m.mutbl, Box::new(clean_ty(m.ty, cx))), - TyKind::Ref(ref l, ref m) => { - let lifetime = if l.is_anonymous() { None } else { Some(clean_lifetime(*l, cx)) }; - BorrowedRef { lifetime, mutability: m.mutbl, type_: Box::new(clean_ty(m.ty, cx)) } - } - TyKind::Slice(ty) => Slice(Box::new(clean_ty(ty, cx))), - TyKind::Pat(ty, pat) => Type::Pat(Box::new(clean_ty(ty, cx)), format!("{pat:?}").into()), - TyKind::Array(ty, ref length) => { - let length = match length { - hir::ArrayLen::Infer(..) => "_".to_string(), - hir::ArrayLen::Body(anon_const) => { - // NOTE(min_const_generics): We can't use `const_eval_poly` for constants - // as we currently do not supply the parent generics to anonymous constants - // but do allow `ConstKind::Param`. - // - // `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, anon_const.def_id); - let param_env = cx.tcx.param_env(anon_const.def_id); - print_const(cx, ct.normalize(cx.tcx, param_env)) - } - }; - - Array(Box::new(clean_ty(ty, cx)), length.into()) - } - TyKind::Tup(tys) => Tuple(tys.iter().map(|ty| clean_ty(ty, cx)).collect()), - 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| 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| 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(clean_bare_fn_ty(barefn, cx))), - // Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s. - TyKind::Infer | TyKind::Err(_) | TyKind::Typeof(..) | TyKind::InferDelegation(..) => Infer, - TyKind::AnonAdt(..) => { - unimplemented!("Anonymous structs or unions are not supported yet") - } - } -} - /// Returns `None` if the type could not be normalized +// FIXME: maybe move to util? fn normalize<'tcx>( cx: &mut DocContext<'tcx>, ty: ty::Binder<'tcx, Ty<'tcx>>, @@ -2335,10 +1436,6 @@ fn clean_middle_opaque_bounds<'tcx>( ImplTrait(bounds) } -pub(crate) fn clean_field<'tcx>(field: &hir::FieldDef<'tcx>, cx: &mut DocContext<'tcx>) -> Item { - clean_field_with_def_id(field.def_id.to_def_id(), field.ident.name, clean_ty(field.ty, cx), cx) -} - pub(crate) fn clean_middle_field<'tcx>(field: &ty::FieldDef, cx: &mut DocContext<'tcx>) -> Item { clean_field_with_def_id( field.did, @@ -2353,6 +1450,7 @@ pub(crate) fn clean_middle_field<'tcx>(field: &ty::FieldDef, cx: &mut DocContext ) } +// FIXME: maybe move to utils? pub(crate) fn clean_field_with_def_id( def_id: DefId, name: Symbol, @@ -2463,106 +1561,6 @@ pub(crate) fn clean_variant_def_with_args<'tcx>( ) } -fn clean_variant_data<'tcx>( - variant: &hir::VariantData<'tcx>, - disr_expr: &Option<&hir::AnonConst>, - cx: &mut DocContext<'tcx>, -) -> Variant { - let discriminant = disr_expr - .map(|disr| Discriminant { expr: Some(disr.body), value: disr.def_id.to_def_id() }); - - let kind = match variant { - hir::VariantData::Struct { fields, .. } => VariantKind::Struct(VariantStruct { - fields: fields.iter().map(|x| clean_field(x, cx)).collect(), - }), - hir::VariantData::Tuple(..) => { - VariantKind::Tuple(variant.fields().iter().map(|x| clean_field(x, cx)).collect()) - } - hir::VariantData::Unit(..) => VariantKind::CLike, - }; - - Variant { discriminant, kind } -} - -fn clean_path<'tcx>(path: &hir::Path<'tcx>, cx: &mut DocContext<'tcx>) -> Path { - Path { - res: path.res, - segments: path.segments.iter().map(|x| clean_path_segment(x, cx)).collect(), - } -} - -fn clean_generic_args<'tcx>( - generic_args: &hir::GenericArgs<'tcx>, - cx: &mut DocContext<'tcx>, -) -> GenericArgs { - // FIXME(return_type_notation): Fix RTN parens rendering - if let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() { - let inputs = inputs.iter().map(|x| clean_ty(x, cx)).collect::>().into(); - let output = match output.kind { - hir::TyKind::Tup(&[]) => None, - _ => Some(Box::new(clean_ty(output, cx))), - }; - GenericArgs::Parenthesized { inputs, output } - } else { - let args = generic_args - .args - .iter() - .filter_map(|arg| { - Some(match arg { - hir::GenericArg::Lifetime(lt) if !lt.is_anonymous() => { - GenericArg::Lifetime(clean_lifetime(*lt, cx)) - } - hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()), - hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty, cx)), - // Checking for `is_desugared_from_effects` on the `AnonConst` not only accounts for the case - // where the argument is `host` but for all possible cases (e.g., `true`, `false`). - hir::GenericArg::Const(hir::ConstArg { - is_desugared_from_effects: true, - .. - }) => { - return None; - } - hir::GenericArg::Const(ct) => GenericArg::Const(Box::new(clean_const(ct, cx))), - hir::GenericArg::Infer(_inf) => GenericArg::Infer, - }) - }) - .collect::>() - .into(); - let constraints = generic_args - .constraints - .iter() - .map(|c| clean_assoc_item_constraint(c, cx)) - .collect::>(); - GenericArgs::AngleBracketed { args, constraints } - } -} - -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) } -} - -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, None, args); - (generic_params, decl) - }); - BareFunctionDecl { safety: bare_fn.safety, abi: bare_fn.abi, decl, generic_params } -} - pub(crate) fn reexport_chain<'tcx>( tcx: TyCtxt<'tcx>, import_def_id: LocalDefId, @@ -2711,418 +1709,6 @@ fn add_without_unwanted_attributes<'hir>( } } -fn clean_maybe_renamed_item<'tcx>( - cx: &mut DocContext<'tcx>, - item: &hir::Item<'tcx>, - renamed: Option, - import_id: Option, -) -> Vec { - use hir::ItemKind; - - 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 { - ItemKind::Static(ty, mutability, body_id) => { - StaticItem(Static { type_: clean_ty(ty, cx), mutability, expr: Some(body_id) }) - } - ItemKind::Const(ty, generics, body_id) => ConstantItem( - clean_generics(generics, cx), - Box::new(clean_ty(ty, cx)), - Constant { kind: ConstantKind::Local { body: body_id, def_id } }, - ), - ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy { - bounds: ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), - generics: clean_generics(ty.generics, cx), - }), - ItemKind::TyAlias(hir_ty, generics) => { - *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; - let rustdoc_ty = clean_ty(hir_ty, cx); - let type_ = - clean_middle_ty(ty::Binder::dummy(lower_ty(cx.tcx, hir_ty)), cx, None, None); - let generics = clean_generics(generics, cx); - if let Some(count) = cx.current_type_aliases.get_mut(&def_id) { - *count -= 1; - if *count == 0 { - cx.current_type_aliases.remove(&def_id); - } - } - - let ty = cx.tcx.type_of(def_id).instantiate_identity(); - - let mut ret = Vec::new(); - let inner_type = clean_ty_alias_inner_type(ty, cx, &mut ret); - - ret.push(generate_item_with_correct_attrs( - cx, - TypeAliasItem(Box::new(TypeAlias { - generics, - inner_type, - type_: rustdoc_ty, - item_type: Some(type_), - })), - item.owner_id.def_id.to_def_id(), - name, - import_id, - renamed, - )); - return ret; - } - ItemKind::Enum(ref def, generics) => EnumItem(Enum { - variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(), - generics: clean_generics(generics, cx), - }), - ItemKind::TraitAlias(generics, bounds) => TraitAliasItem(TraitAlias { - 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: clean_generics(generics, cx), - fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), - }), - ItemKind::Struct(ref variant_data, generics) => StructItem(Struct { - ctor_kind: variant_data.ctor_kind(), - 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.owner_id.def_id, cx), - ItemKind::Macro(ref macro_def, MacroKind::Bang) => { - let ty_vis = cx.tcx.visibility(def_id); - MacroItem(Macro { - // FIXME this shouldn't be false - source: display_macro_source(cx, name, macro_def, def_id, ty_vis, false), - }) - } - ItemKind::Macro(_, macro_kind) => clean_proc_macro(item, &mut name, macro_kind, cx), - // proc macros can have a name set by attributes - ItemKind::Fn(ref sig, generics, body_id) => { - clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx) - } - ItemKind::Trait(_, _, generics, bounds, item_ids) => { - let items = item_ids - .iter() - .map(|ti| clean_trait_item(cx.tcx.hir().trait_item(ti.id), cx)) - .collect(); - - TraitItem(Box::new(Trait { - def_id, - items, - 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); - } - ItemKind::Use(path, kind) => { - return clean_use_statement(item, name, path, kind, cx, &mut FxHashSet::default()); - } - _ => unreachable!("not yet converted"), - }; - - vec![generate_item_with_correct_attrs( - cx, - kind, - item.owner_id.def_id.to_def_id(), - name, - import_id, - renamed, - )] - }) -} - -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)); - Item::from_def_id_and_parts(variant.def_id.to_def_id(), Some(variant.ident.name), kind, cx) -} - -fn clean_impl<'tcx>( - impl_: &hir::Impl<'tcx>, - def_id: LocalDefId, - cx: &mut DocContext<'tcx>, -) -> Vec { - 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| clean_impl_item(tcx.hir().impl_item(ii.id), cx)) - .collect::>(); - - // If this impl block is an implementation of the Deref trait, then we - // need to try inlining the target's inherent impl blocks as well. - if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() { - build_deref_target_impls(cx, &items, &mut ret); - } - - let for_ = clean_ty(impl_.self_ty, cx); - let type_alias = - for_.def_id(&cx.cache).and_then(|alias_def_id: DefId| match tcx.def_kind(alias_def_id) { - DefKind::TyAlias => Some(clean_middle_ty( - ty::Binder::dummy(tcx.type_of(def_id).instantiate_identity()), - cx, - Some(def_id.to_def_id()), - None, - )), - _ => None, - }); - let mut make_item = |trait_: Option, for_: Type, items: Vec| { - let kind = ImplItem(Box::new(Impl { - safety: impl_.safety, - generics: clean_generics(impl_.generics, cx), - trait_, - for_, - items, - polarity: tcx.impl_polarity(def_id), - kind: if utils::has_doc_flag(tcx, def_id.to_def_id(), sym::fake_variadic) { - ImplKind::FakeVariadic - } else { - ImplKind::Normal - }, - })); - Item::from_def_id_and_parts(def_id.to_def_id(), None, kind, cx) - }; - if let Some(type_alias) = type_alias { - ret.push(make_item(trait_.clone(), type_alias, items.clone())); - } - ret.push(make_item(trait_, for_, items)); - ret -} - -fn clean_extern_crate<'tcx>( - krate: &hir::Item<'tcx>, - name: Symbol, - orig_name: Option, - cx: &mut DocContext<'tcx>, -) -> Vec { - // this is the ID of the `extern crate` statement - 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.owner_id); - let please_inline = ty_vis.is_public() - && attrs.iter().any(|a| { - a.has_name(sym::doc) - && match a.meta_item_list() { - Some(l) => attr::list_contains_name(&l, sym::inline), - None => false, - } - }) - && !cx.output_format.is_json(); - - let krate_owner_def_id = krate.owner_id.to_def_id(); - if please_inline { - if let Some(items) = inline::try_inline( - cx, - Res::Def(DefKind::Mod, crate_def_id), - name, - Some((attrs, Some(krate_owner_def_id))), - &mut Default::default(), - ) { - return items; - } - } - - vec![Item::from_def_id_and_parts( - krate_owner_def_id, - Some(name), - ExternCrateItem { src: orig_name }, - cx, - )] -} - -fn clean_use_statement<'tcx>( - import: &hir::Item<'tcx>, - name: Symbol, - path: &hir::UsePath<'tcx>, - kind: hir::UseKind, - cx: &mut DocContext<'tcx>, - inlined_names: &mut FxHashSet<(ItemType, Symbol)>, -) -> Vec { - let mut items = Vec::new(); - let hir::UsePath { segments, ref res, span } = *path; - for &res in res { - let path = hir::Path { segments, res, span }; - items.append(&mut clean_use_statement_inner(import, name, &path, kind, cx, inlined_names)); - } - items -} - -fn clean_use_statement_inner<'tcx>( - import: &hir::Item<'tcx>, - name: Symbol, - path: &hir::Path<'tcx>, - kind: hir::UseKind, - cx: &mut DocContext<'tcx>, - inlined_names: &mut FxHashSet<(ItemType, Symbol)>, -) -> Vec { - if should_ignore_res(path.res) { - return Vec::new(); - } - // We need this comparison because some imports (for std types for example) - // are "inserted" as well but directly by the compiler and they should not be - // taken into account. - if import.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::StdImports) { - return Vec::new(); - } - - 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.owner_id.def_id); - let import_def_id = import.owner_id.def_id.to_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 - // level module. - let parent_mod = cx.tcx.parent_module_from_def_id(current_mod.to_local_def_id()); - - // This checks if the import can be seen from a higher level module. - // In other words, it checks if the visibility is the equivalent of - // `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, cx.tcx) && !current_mod.is_top_level_module(); - - if pub_underscore && let Some(ref inline) = inline_attr { - struct_span_code_err!( - cx.tcx.dcx(), - inline.span(), - E0780, - "anonymous imports cannot be inlined" - ) - .with_span_label(import.span, "anonymous import") - .emit(); - } - - // We consider inlining the documentation of `pub use` statements, but we - // forcefully don't inline if this is not public or if the - // #[doc(no_inline)] attribute is present. - // Don't inline doc(hidden) imports so they can be stripped at a later stage. - let mut denied = cx.output_format.is_json() - || !(visibility.is_public() - || (cx.render_options.document_private && is_visible_from_parent_mod)) - || pub_underscore - || attrs.iter().any(|a| { - a.has_name(sym::doc) - && match a.meta_item_list() { - Some(l) => { - attr::list_contains_name(&l, sym::no_inline) - || attr::list_contains_name(&l, sym::hidden) - } - None => false, - } - }); - - // Also check whether imports were asked to be inlined, in case we're trying to re-export a - // crate in Rust 2018+ - let path = clean_path(path, cx); - let inner = if kind == hir::UseKind::Glob { - if !denied { - let mut visited = DefIdSet::default(); - if let Some(items) = inline::try_inline_glob( - cx, - path.res, - current_mod, - &mut visited, - inlined_names, - import, - ) { - return items; - } - } - Import::new_glob(resolve_use_source(cx, path), true) - } else { - if inline_attr.is_none() - && let Res::Def(DefKind::Mod, did) = path.res - && !did.is_local() - && did.is_crate_root() - { - // if we're `pub use`ing an extern crate root, don't inline it unless we - // were specifically asked for it - denied = true; - } - if !denied - && let Some(mut items) = inline::try_inline( - cx, - path.res, - name, - Some((attrs, Some(import_def_id))), - &mut Default::default(), - ) - { - items.push(Item::from_def_id_and_parts( - import_def_id, - None, - ImportItem(Import::new_simple(name, resolve_use_source(cx, path), false)), - cx, - )); - return items; - } - Import::new_simple(name, resolve_use_source(cx, path), true) - }; - - vec![Item::from_def_id_and_parts(import_def_id, None, ImportItem(inner), cx)] -} - -fn clean_maybe_renamed_foreign_item<'tcx>( - cx: &mut DocContext<'tcx>, - item: &hir::ForeignItem<'tcx>, - renamed: Option, -) -> Item { - 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, safety) => { - let (generics, decl) = enter_impl_trait(cx, |cx| { - // NOTE: generics must be cleaned before args - 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, None, args); - (generics, decl) - }); - ForeignFunctionItem(Box::new(Function { decl, generics }), safety) - } - hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem( - Static { type_: clean_ty(ty, cx), mutability, expr: None }, - safety, - ), - hir::ForeignItemKind::Type => ForeignTypeItem, - }; - - Item::from_def_id_and_parts( - item.owner_id.def_id.to_def_id(), - Some(renamed.unwrap_or(item.ident.name)), - kind, - cx, - ) - }) -} - -fn clean_assoc_item_constraint<'tcx>( - constraint: &hir::AssocItemConstraint<'tcx>, - cx: &mut DocContext<'tcx>, -) -> AssocItemConstraint { - AssocItemConstraint { - assoc: PathSegment { - name: constraint.ident.name, - args: clean_generic_args(constraint.gen_args, cx), - }, - kind: match constraint.kind { - hir::AssocItemConstraintKind::Equality { ref term } => { - AssocItemConstraintKind::Equality { term: clean_hir_term(term, cx) } - } - hir::AssocItemConstraintKind::Bound { bounds } => AssocItemConstraintKind::Bound { - bounds: bounds.iter().filter_map(|b| clean_generic_bound(b, cx)).collect(), - }, - }, - } -} - fn clean_bound_vars<'tcx>( bound_vars: &'tcx ty::List, ) -> Vec { From e43b7503437668a1afafb752c8c2c8b7f2885a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 28 Jun 2024 01:19:17 +0200 Subject: [PATCH 2/4] Rename some HIR cleaners --- src/librustdoc/clean/local.rs | 206 +++++++++++++++++----------------- src/librustdoc/clean/mod.rs | 4 +- 2 files changed, 104 insertions(+), 106 deletions(-) diff --git a/src/librustdoc/clean/local.rs b/src/librustdoc/clean/local.rs index 9a90d5e8a9ec8..c036fc13e1d7e 100644 --- a/src/librustdoc/clean/local.rs +++ b/src/librustdoc/clean/local.rs @@ -79,7 +79,7 @@ fn clean_assoc_item_constraint<'tcx>( }, kind: match constraint.kind { hir::AssocItemConstraintKind::Equality { ref term } => { - AssocItemConstraintKind::Equality { term: clean_hir_term(term, cx) } + AssocItemConstraintKind::Equality { term: clean_term(term, cx) } } hir::AssocItemConstraintKind::Bound { bounds } => AssocItemConstraintKind::Bound { bounds: bounds.iter().filter_map(|b| clean_generic_bound(b, cx)).collect(), @@ -107,7 +107,7 @@ fn clean_bare_fn_ty<'tcx>( BareFunctionDecl { safety: bare_fn.safety, abi: bare_fn.abi, decl, generic_params } } -fn clean_const<'tcx>(constant: &hir::ConstArg<'_>, _cx: &mut DocContext<'tcx>) -> Constant { +fn clean_const_arg<'tcx>(constant: &hir::ConstArg<'_>, _cx: &mut DocContext<'tcx>) -> Constant { Constant { kind: ConstantKind::Anonymous { body: constant.value.body } } } @@ -163,39 +163,6 @@ fn clean_field<'tcx>(field: &hir::FieldDef<'tcx>, cx: &mut DocContext<'tcx>) -> ) } -/// This is needed to make it more "readable" when documenting functions using -/// `rustc_legacy_const_generics`. More information in -/// . -fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[ast::Attribute]) { - for meta_item_list in attrs - .iter() - .filter(|a| a.has_name(sym::rustc_legacy_const_generics)) - .filter_map(|a| a.meta_item_list()) - { - for (pos, literal) in meta_item_list.iter().filter_map(|meta| meta.lit()).enumerate() { - match literal.kind { - ast::LitKind::Int(a, _) => { - let gen = func.generics.params.remove(0); - if let GenericParamDef { - name, - kind: GenericParamDefKind::Const { ty, .. }, - .. - } = gen - { - func.decl - .inputs - .values - .insert(a.get() as _, Argument { name, type_: *ty, is_const: true }); - } else { - panic!("unexpected non const in position {pos}"); - } - } - _ => panic!("invalid arg index"), - } - } - } -} - fn clean_fn_decl_with_args<'tcx>( cx: &mut DocContext<'tcx>, decl: &hir::FnDecl<'tcx>, @@ -237,15 +204,14 @@ fn clean_fn_or_proc_macro<'tcx>( match macro_kind { Some(kind) => clean_proc_macro(item, name, kind, cx), None => { - let mut func = clean_function(cx, sig, generics, FunctionArgs::Body(body_id)); - clean_fn_decl_legacy_const_generics(&mut func, attrs); + let mut func = clean_fn(cx, sig, generics, FunctionArgs::Body(body_id)); + clean_legacy_const_generics(&mut func, attrs); FunctionItem(func) } } } -// FIXME: rename -fn clean_function<'tcx>( +fn clean_fn<'tcx>( cx: &mut DocContext<'tcx>, sig: &hir::FnSig<'tcx>, generics: &hir::Generics<'tcx>, @@ -268,6 +234,40 @@ fn clean_function<'tcx>( Box::new(Function { decl, generics }) } +pub(super) fn clean_foreign_item<'tcx>( + cx: &mut DocContext<'tcx>, + item: &hir::ForeignItem<'tcx>, + renamed: Option, +) -> Item { + 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, safety) => { + let (generics, decl) = enter_impl_trait(cx, |cx| { + // NOTE: generics must be cleaned before args + 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, None, args); + (generics, decl) + }); + ForeignFunctionItem(Box::new(Function { decl, generics }), safety) + } + hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem( + Static { type_: clean_ty(ty, cx), mutability, expr: None }, + safety, + ), + hir::ForeignItemKind::Type => ForeignTypeItem, + }; + + Item::from_def_id_and_parts( + item.owner_id.def_id.to_def_id(), + Some(renamed.unwrap_or(item.ident.name)), + kind, + cx, + ) + }) +} + fn clean_generic_args<'tcx>( generic_args: &hir::GenericArgs<'tcx>, cx: &mut DocContext<'tcx>, @@ -299,7 +299,9 @@ fn clean_generic_args<'tcx>( }) => { return None; } - hir::GenericArg::Const(ct) => GenericArg::Const(Box::new(clean_const(ct, cx))), + hir::GenericArg::Const(ct) => { + GenericArg::Const(Box::new(clean_const_arg(ct, cx))) + } hir::GenericArg::Infer(_inf) => GenericArg::Infer, }) }) @@ -513,17 +515,6 @@ pub(super) fn clean_generics<'tcx>( } } -// FIXME: rename -fn clean_hir_term<'tcx>(term: &hir::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Term { - match term { - hir::Term::Ty(ty) => Term::Type(clean_ty(ty, cx)), - hir::Term::Const(c) => Term::Constant(super::clean_middle_const( - ty::Binder::dummy(ty::Const::from_anon_const(cx.tcx, c.def_id)), - cx, - )), - } -} - fn clean_impl<'tcx>( impl_: &hir::Impl<'tcx>, def_id: LocalDefId, @@ -591,7 +582,7 @@ pub(super) fn clean_impl_item<'tcx>( AssocConstItem(generics, Box::new(clean_ty(ty, cx)), default) } hir::ImplItemKind::Fn(ref sig, body) => { - let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body)); + let m = clean_fn(cx, sig, impl_.generics, FunctionArgs::Body(body)); let defaultness = cx.tcx.defaultness(impl_.owner_id); MethodItem(m, Some(defaultness)) } @@ -620,56 +611,7 @@ pub(super) fn clean_impl_item<'tcx>( }) } -fn clean_lifetime<'tcx>(lifetime: &hir::Lifetime, cx: &mut DocContext<'tcx>) -> Lifetime { - if let Some( - rbv::ResolvedArg::EarlyBound(did) - | rbv::ResolvedArg::LateBound(_, _, did) - | rbv::ResolvedArg::Free(_, did), - ) = cx.tcx.named_bound_var(lifetime.hir_id) - && let Some(lt) = cx.args.get(&did).and_then(|arg| arg.as_lt()) - { - return lt.clone(); - } - Lifetime(lifetime.ident.name) -} - -// FIXME: rename -pub(super) fn clean_maybe_renamed_foreign_item<'tcx>( - cx: &mut DocContext<'tcx>, - item: &hir::ForeignItem<'tcx>, - renamed: Option, -) -> Item { - 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, safety) => { - let (generics, decl) = enter_impl_trait(cx, |cx| { - // NOTE: generics must be cleaned before args - 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, None, args); - (generics, decl) - }); - ForeignFunctionItem(Box::new(Function { decl, generics }), safety) - } - hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem( - Static { type_: clean_ty(ty, cx), mutability, expr: None }, - safety, - ), - hir::ForeignItemKind::Type => ForeignTypeItem, - }; - - Item::from_def_id_and_parts( - item.owner_id.def_id.to_def_id(), - Some(renamed.unwrap_or(item.ident.name)), - kind, - cx, - ) - }) -} - -// FIXME: rename -pub(super) fn clean_maybe_renamed_item<'tcx>( +pub(super) fn clean_item<'tcx>( cx: &mut DocContext<'tcx>, item: &hir::Item<'tcx>, renamed: Option, @@ -793,6 +735,52 @@ pub(super) fn clean_maybe_renamed_item<'tcx>( }) } +/// This is needed to make it more "readable" when documenting functions using +/// `rustc_legacy_const_generics`. More information in +/// . +fn clean_legacy_const_generics(func: &mut Function, attrs: &[ast::Attribute]) { + for meta_item_list in attrs + .iter() + .filter(|a| a.has_name(sym::rustc_legacy_const_generics)) + .filter_map(|a| a.meta_item_list()) + { + for (pos, literal) in meta_item_list.iter().filter_map(|meta| meta.lit()).enumerate() { + match literal.kind { + ast::LitKind::Int(a, _) => { + let gen = func.generics.params.remove(0); + if let GenericParamDef { + name, + kind: GenericParamDefKind::Const { ty, .. }, + .. + } = gen + { + func.decl + .inputs + .values + .insert(a.get() as _, Argument { name, type_: *ty, is_const: true }); + } else { + panic!("unexpected non const in position {pos}"); + } + } + _ => panic!("invalid arg index"), + } + } + } +} + +fn clean_lifetime<'tcx>(lifetime: &hir::Lifetime, cx: &mut DocContext<'tcx>) -> Lifetime { + if let Some( + rbv::ResolvedArg::EarlyBound(did) + | rbv::ResolvedArg::LateBound(_, _, did) + | rbv::ResolvedArg::Free(_, did), + ) = cx.tcx.named_bound_var(lifetime.hir_id) + && let Some(lt) = cx.args.get(&did).and_then(|arg| arg.as_lt()) + { + return lt.clone(); + } + Lifetime(lifetime.ident.name) +} + fn clean_path<'tcx>(path: &hir::Path<'tcx>, cx: &mut DocContext<'tcx>) -> Path { Path { res: path.res, @@ -939,6 +927,16 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type } } +fn clean_term<'tcx>(term: &hir::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Term { + match term { + hir::Term::Ty(ty) => Term::Type(clean_ty(ty, cx)), + hir::Term::Const(c) => Term::Constant(super::clean_middle_const( + ty::Binder::dummy(ty::Const::from_anon_const(cx.tcx, c.def_id)), + cx, + )), + } +} + 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| { @@ -956,11 +954,11 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext TyAssocConstItem(generics, Box::new(clean_ty(ty, cx))) } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Body(body)); + let m = clean_fn(cx, sig, trait_item.generics, FunctionArgs::Body(body)); MethodItem(m, None) } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(names)) => { - let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Names(names)); + let m = clean_fn(cx, sig, trait_item.generics, FunctionArgs::Names(names)); TyMethodItem(m) } hir::TraitItemKind::Type(bounds, Some(default)) => { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 6b8ba18f3009b..3d2686e3fa037 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -66,7 +66,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< let mut items: Vec = vec![]; let mut inserted = FxHashSet::default(); items.extend(doc.foreigns.iter().map(|(item, renamed)| { - let item = local::clean_maybe_renamed_foreign_item(cx, item, *renamed); + let item = local::clean_foreign_item(cx, item, *renamed); if let Some(name) = item.name && (cx.render_options.document_hidden || !item.is_doc_hidden()) { @@ -98,7 +98,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) { return Vec::new(); } - let v = local::clean_maybe_renamed_item(cx, item, *renamed, *import_id); + let v = local::clean_item(cx, item, *renamed, *import_id); for item in &v { if let Some(name) = item.name && (cx.render_options.document_hidden || !item.is_doc_hidden()) From a4d986ecd869c384b03d3ea8e65ca8537ea6204e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 28 Jun 2024 01:29:49 +0200 Subject: [PATCH 3/4] Rename rustc_middle::ty cleaners and move some around --- src/librustdoc/clean/auto_trait.rs | 13 +- src/librustdoc/clean/blanket_impl.rs | 17 +- src/librustdoc/clean/inline.rs | 47 ++-- src/librustdoc/clean/local.rs | 30 +-- src/librustdoc/clean/mod.rs | 331 ++++++++++++++++++++------- src/librustdoc/clean/types.rs | 4 +- src/librustdoc/clean/utils.rs | 188 +-------------- 7 files changed, 286 insertions(+), 344 deletions(-) diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index ac927e9a19426..ffcab89e32108 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -11,8 +11,8 @@ use thin_vec::ThinVec; use crate::clean::{self, simplify, Lifetime}; use crate::clean::{ - clean_generic_param_def, clean_middle_ty, clean_predicate, clean_trait_ref_with_constraints, - clean_ty_generics, + clean_generic_param_def, clean_generics, clean_predicate, clean_trait_ref_with_constraints, + clean_ty, }; use crate::core::DocContext; @@ -102,11 +102,8 @@ fn synthesize_auto_trait_impl<'tcx>( // Instead, we generate `impl !Send for Foo`, which better // expresses the fact that `Foo` never implements `Send`, // regardless of the choice of `T`. - let mut generics = clean_ty_generics( - cx, - tcx.generics_of(item_def_id), - ty::GenericPredicates::default(), - ); + let mut generics = + clean_generics(cx, tcx.generics_of(item_def_id), ty::GenericPredicates::default()); generics.where_predicates.clear(); (generics, ty::ImplPolarity::Negative) @@ -122,7 +119,7 @@ fn synthesize_auto_trait_impl<'tcx>( safety: hir::Safety::Safe, generics, trait_: Some(clean_trait_ref_with_constraints(cx, trait_ref, ThinVec::new())), - for_: clean_middle_ty(ty::Binder::dummy(ty), cx, None, None), + for_: clean_ty(ty::Binder::dummy(ty), cx, None, None), items: Vec::new(), polarity, kind: clean::ImplKind::Auto, diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 4c7a2ecdb53f5..4e97b68d70ee7 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -9,9 +9,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use thin_vec::ThinVec; use crate::clean; -use crate::clean::{ - clean_middle_assoc_item, clean_middle_ty, clean_trait_ref_with_constraints, clean_ty_generics, -}; +use crate::clean::{clean_assoc_item, clean_generics, clean_trait_ref_with_constraints, clean_ty}; use crate::core::DocContext; #[instrument(level = "debug", skip(cx))] @@ -88,7 +86,7 @@ pub(crate) fn synthesize_blanket_impls( item_id: clean::ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, kind: Box::new(clean::ImplItem(Box::new(clean::Impl { safety: hir::Safety::Safe, - generics: clean_ty_generics( + generics: clean_generics( cx, tcx.generics_of(impl_def_id), tcx.explicit_predicates_of(impl_def_id), @@ -100,20 +98,15 @@ pub(crate) fn synthesize_blanket_impls( ty::Binder::dummy(trait_ref.instantiate_identity()), ThinVec::new(), )), - for_: clean_middle_ty( - ty::Binder::dummy(ty.instantiate_identity()), - cx, - None, - None, - ), + for_: clean_ty(ty::Binder::dummy(ty.instantiate_identity()), cx, None, None), items: tcx .associated_items(impl_def_id) .in_definition_order() .filter(|item| !item.is_impl_trait_in_trait()) - .map(|item| clean_middle_assoc_item(item, cx)) + .map(|item| clean_assoc_item(item, cx)) .collect(), polarity: ty::ImplPolarity::Positive, - kind: clean::ImplKind::Blanket(Box::new(clean_middle_ty( + kind: clean::ImplKind::Blanket(Box::new(clean_ty( ty::Binder::dummy(trait_ref.instantiate_identity().self_ty()), cx, None, diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 16b953e8dcaff..8a856a931b4d8 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -19,10 +19,9 @@ use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Symbol}; use crate::clean::{ - self, clean_bound_vars, clean_middle_assoc_item, clean_middle_field, clean_middle_ty, - clean_poly_fn_sig, clean_trait_ref_with_constraints, clean_ty_alias_inner_type, - clean_ty_generics, clean_variant_def, local, utils, Attributes, AttributesExt, ImplKind, - ItemId, Type, + self, clean_assoc_item, clean_bound_vars, clean_field, clean_generics, clean_poly_fn_sig, + clean_trait_ref_with_constraints, clean_ty, clean_ty_alias_inner_type, clean_variant_def, + local, utils, Attributes, AttributesExt, ImplKind, ItemId, Type, }; use crate::core::DocContext; use crate::formats::item_type::ItemType; @@ -281,11 +280,11 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean .associated_items(did) .in_definition_order() .filter(|item| !item.is_impl_trait_in_trait()) - .map(|item| clean_middle_assoc_item(item, cx)) + .map(|item| clean_assoc_item(item, cx)) .collect(); let predicates = cx.tcx.predicates_of(did); - let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates); + let generics = clean_generics(cx, cx.tcx.generics_of(did), predicates); let generics = filter_non_trait_generics(did, generics); let (generics, supertrait_bounds) = separate_supertrait_bounds(generics); clean::Trait { def_id: did, generics, items: trait_items, bounds: supertrait_bounds } @@ -298,7 +297,7 @@ pub(crate) fn build_function<'tcx>( let sig = cx.tcx.fn_sig(def_id).instantiate_identity(); // The generics need to be cleaned before the signature. let mut generics = - clean_ty_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id)); + clean_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id)); let bound_vars = clean_bound_vars(sig.bound_vars()); // At the time of writing early & late-bound params are stored separately in rustc, @@ -330,7 +329,7 @@ fn build_enum(cx: &mut DocContext<'_>, did: DefId) -> clean::Enum { let predicates = cx.tcx.explicit_predicates_of(did); clean::Enum { - generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates), + generics: clean_generics(cx, cx.tcx.generics_of(did), predicates), variants: cx.tcx.adt_def(did).variants().iter().map(|v| clean_variant_def(v, cx)).collect(), } } @@ -341,8 +340,8 @@ fn build_struct(cx: &mut DocContext<'_>, did: DefId) -> clean::Struct { clean::Struct { ctor_kind: variant.ctor_kind(), - generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates), - fields: variant.fields.iter().map(|x| clean_middle_field(x, cx)).collect(), + generics: clean_generics(cx, cx.tcx.generics_of(did), predicates), + fields: variant.fields.iter().map(|x| clean_field(x, cx)).collect(), } } @@ -350,8 +349,8 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union { let predicates = cx.tcx.explicit_predicates_of(did); let variant = cx.tcx.adt_def(did).non_enum_variant(); - let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates); - let fields = variant.fields.iter().map(|x| clean_middle_field(x, cx)).collect(); + let generics = clean_generics(cx, cx.tcx.generics_of(did), predicates); + let fields = variant.fields.iter().map(|x| clean_field(x, cx)).collect(); clean::Union { generics, fields } } @@ -362,12 +361,12 @@ fn build_type_alias( ) -> Box { let predicates = cx.tcx.explicit_predicates_of(did); let ty = cx.tcx.type_of(did).instantiate_identity(); - let type_ = clean_middle_ty(ty::Binder::dummy(ty), cx, Some(did), None); + let type_ = clean_ty(ty::Binder::dummy(ty), cx, Some(did), None); let inner_type = clean_ty_alias_inner_type(ty, cx, ret); Box::new(clean::TypeAlias { type_, - generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates), + generics: clean_generics(cx, cx.tcx.generics_of(did), predicates), inner_type, item_type: None, }) @@ -478,7 +477,7 @@ pub(crate) fn build_impl( let for_ = match &impl_item { Some(impl_) => local::clean_ty(impl_.self_ty, cx), - None => clean_middle_ty( + None => clean_ty( ty::Binder::dummy(tcx.type_of(did).instantiate_identity()), cx, Some(did), @@ -560,11 +559,9 @@ pub(crate) fn build_impl( item.visibility(tcx).is_public() } }) - .map(|item| clean_middle_assoc_item(item, cx)) + .map(|item| clean_assoc_item(item, cx)) .collect::>(), - clean::enter_impl_trait(cx, |cx| { - clean_ty_generics(cx, tcx.generics_of(did), predicates) - }), + clean::enter_impl_trait(cx, |cx| clean_generics(cx, tcx.generics_of(did), predicates)), ), }; let polarity = tcx.impl_polarity(did); @@ -725,20 +722,16 @@ fn build_const_item( def_id: DefId, ) -> (clean::Generics, clean::Type, clean::Constant) { let mut generics = - clean_ty_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id)); + clean_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id)); clean::simplify::move_bounds_to_generic_parameters(&mut generics); - let ty = clean_middle_ty( - ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()), - cx, - None, - None, - ); + let ty = + clean_ty(ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()), cx, None, None); (generics, ty, clean::Constant { kind: clean::ConstantKind::Extern { def_id } }) } fn build_static(cx: &mut DocContext<'_>, did: DefId, mutable: bool) -> clean::Static { clean::Static { - type_: clean_middle_ty( + type_: clean_ty( ty::Binder::dummy(cx.tcx.type_of(did).instantiate_identity()), cx, Some(did), diff --git a/src/librustdoc/clean/local.rs b/src/librustdoc/clean/local.rs index c036fc13e1d7e..d9505f822c1dc 100644 --- a/src/librustdoc/clean/local.rs +++ b/src/librustdoc/clean/local.rs @@ -538,7 +538,7 @@ fn clean_impl<'tcx>( let for_ = clean_ty(impl_.self_ty, cx); let type_alias = for_.def_id(&cx.cache).and_then(|alias_def_id: DefId| match tcx.def_kind(alias_def_id) { - DefKind::TyAlias => Some(super::clean_middle_ty( + DefKind::TyAlias => Some(super::clean_ty( ty::Binder::dummy(tcx.type_of(def_id).instantiate_identity()), cx, Some(def_id.to_def_id()), @@ -589,12 +589,8 @@ pub(super) fn clean_impl_item<'tcx>( hir::ImplItemKind::Type(hir_ty) => { let type_ = clean_ty(hir_ty, cx); let generics = clean_generics(impl_.generics, cx); - let item_type = super::clean_middle_ty( - ty::Binder::dummy(lower_ty(cx.tcx, hir_ty)), - cx, - None, - None, - ); + let item_type = + super::clean_ty(ty::Binder::dummy(lower_ty(cx.tcx, hir_ty)), cx, None, None); AssocTypeItem( Box::new(TypeAlias { type_, @@ -638,12 +634,8 @@ pub(super) fn clean_item<'tcx>( ItemKind::TyAlias(hir_ty, generics) => { *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; let rustdoc_ty = clean_ty(hir_ty, cx); - let type_ = super::clean_middle_ty( - ty::Binder::dummy(lower_ty(cx.tcx, hir_ty)), - cx, - None, - None, - ); + let type_ = + super::clean_ty(ty::Binder::dummy(lower_ty(cx.tcx, hir_ty)), cx, None, None); let generics = clean_generics(generics, cx); if let Some(count) = cx.current_type_aliases.get_mut(&def_id) { *count -= 1; @@ -874,7 +866,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type if !ty.has_escaping_bound_vars() && let Some(normalized_value) = super::normalize(cx, ty::Binder::dummy(ty)) { - return super::clean_middle_ty(normalized_value, cx, None, None); + return super::clean_ty(normalized_value, cx, None, None); } let trait_segments = &p.segments[..p.segments.len() - 1]; @@ -930,7 +922,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type fn clean_term<'tcx>(term: &hir::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Term { match term { hir::Term::Ty(ty) => Term::Type(clean_ty(ty, cx)), - hir::Term::Const(c) => Term::Constant(super::clean_middle_const( + hir::Term::Const(c) => Term::Constant(super::clean_const( ty::Binder::dummy(ty::Const::from_anon_const(cx.tcx, c.def_id)), cx, )), @@ -964,12 +956,8 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext 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 = super::clean_middle_ty( - ty::Binder::dummy(lower_ty(cx.tcx, default)), - cx, - None, - None, - ); + let item_type = + super::clean_ty(ty::Binder::dummy(lower_ty(cx.tcx, default)), cx, None, None); AssocTypeItem( Box::new(TypeAlias { type_: clean_ty(default, cx), diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 3d2686e3fa037..0c0b79b62b2a3 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -9,7 +9,7 @@ //! both found in the local crate. These are defined in module [`local`]. //! 2. Cleans [`rustc_middle::ty`] types. Used for inlined cross-crate re-exports and anything //! output by the trait solver (e.g., when synthesizing blanket and auto-trait impls). -//! They usually have `ty` or `middle` in their name. +//! They are defined in this module. //! //! Their name is prefixed by `clean_`. //! @@ -37,20 +37,22 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_errors::FatalError; use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind}; +use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::metadata::Reexport; use rustc_middle::middle::resolve_bound_vars as rbv; use rustc_middle::ty::GenericArgsRef; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, AdtKind, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_trait_selection::traits::wf::object_region_bounds; +use std::assert_matches::debug_assert_matches; use std::borrow::Cow; use std::collections::BTreeMap; use std::mem; -use thin_vec::ThinVec; +use thin_vec::{thin_vec, ThinVec}; use crate::core::DocContext; use crate::formats::item_type::ItemType; @@ -225,13 +227,8 @@ pub(crate) fn clean_trait_ref_with_constraints<'tcx>( 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 = clean_middle_path( - cx, - trait_ref.def_id(), - true, - constraints, - trait_ref.map_bound(|tr| tr.args), - ); + let path = + clean_path(cx, trait_ref.def_id(), true, constraints, trait_ref.map_bound(|tr| tr.args)); debug!(?trait_ref); @@ -252,7 +249,7 @@ fn clean_poly_trait_ref_with_constraints<'tcx>( ) } -pub(crate) fn clean_middle_const<'tcx>( +pub(crate) fn clean_const<'tcx>( constant: ty::Binder<'tcx, ty::Const<'tcx>>, _cx: &mut DocContext<'tcx>, ) -> Constant { @@ -260,7 +257,7 @@ pub(crate) fn clean_middle_const<'tcx>( Constant { kind: ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() } } } -pub(crate) fn clean_middle_region<'tcx>(region: ty::Region<'tcx>) -> Option { +pub(crate) fn clean_region<'tcx>(region: ty::Region<'tcx>) -> Option { match *region { ty::ReStatic => Some(Lifetime::statik()), _ if !region.has_name() => None, @@ -311,7 +308,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.self_ty(), cx, None, None), + ty: clean_ty(poly_trait_ref.self_ty(), cx, None, None), bounds: vec![clean_poly_trait_ref_with_constraints(cx, poly_trait_ref, ThinVec::new())], bound_params: Vec::new(), }) @@ -323,10 +320,8 @@ fn clean_region_outlives_predicate<'tcx>( let ty::OutlivesPredicate(a, b) = pred; Some(WherePredicate::RegionPredicate { - lifetime: clean_middle_region(a).expect("failed to clean lifetime"), - bounds: vec![GenericBound::Outlives( - clean_middle_region(b).expect("failed to clean bounds"), - )], + lifetime: clean_region(a).expect("failed to clean lifetime"), + bounds: vec![GenericBound::Outlives(clean_region(b).expect("failed to clean bounds"))], }) } @@ -337,21 +332,16 @@ fn clean_type_outlives_predicate<'tcx>( let ty::OutlivesPredicate(ty, lt) = pred.skip_binder(); Some(WherePredicate::BoundPredicate { - ty: clean_middle_ty(pred.rebind(ty), cx, None, None), - bounds: vec![GenericBound::Outlives( - clean_middle_region(lt).expect("failed to clean lifetimes"), - )], + ty: clean_ty(pred.rebind(ty), cx, None, None), + bounds: vec![GenericBound::Outlives(clean_region(lt).expect("failed to clean lifetimes"))], bound_params: Vec::new(), }) } -fn clean_middle_term<'tcx>( - term: ty::Binder<'tcx, ty::Term<'tcx>>, - cx: &mut DocContext<'tcx>, -) -> Term { +fn clean_term<'tcx>(term: ty::Binder<'tcx, ty::Term<'tcx>>, cx: &mut DocContext<'tcx>) -> Term { match term.skip_binder().unpack() { - ty::TermKind::Ty(ty) => Term::Type(clean_middle_ty(term.rebind(ty), cx, None, None)), - ty::TermKind::Const(c) => Term::Constant(clean_middle_const(term.rebind(c), cx)), + ty::TermKind::Ty(ty) => Term::Type(clean_ty(term.rebind(ty), cx, None, None)), + ty::TermKind::Const(c) => Term::Constant(clean_const(term.rebind(c), cx)), } } @@ -369,7 +359,7 @@ fn clean_projection_predicate<'tcx>( cx, None, ), - rhs: clean_middle_term(pred.map_bound(|p| p.term), cx), + rhs: clean_term(pred.map_bound(|p| p.term), cx), } } @@ -385,7 +375,7 @@ fn clean_projection<'tcx>( .iter_instantiated_copied(cx.tcx, ty.skip_binder().args) .map(|(pred, _)| pred) .collect::>(); - return clean_middle_opaque_bounds(cx, bounds); + return clean_opaque_bounds(cx, bounds); } let trait_ = clean_trait_ref_with_constraints( @@ -393,7 +383,7 @@ fn clean_projection<'tcx>( ty.map_bound(|ty| ty.trait_ref(cx.tcx)), ThinVec::new(), ); - let self_type = clean_middle_ty(ty.map_bound(|ty| ty.self_ty()), cx, None, None); + let self_type = clean_ty(ty.map_bound(|ty| ty.self_ty()), cx, None, None); let self_def_id = if let Some(def_id) = def_id { cx.tcx.opt_parent(def_id).or(Some(def_id)) } else { @@ -426,7 +416,7 @@ fn projection_to_path_segment<'tcx>( PathSegment { name: item.name, args: GenericArgs::AngleBracketed { - args: clean_middle_generic_args( + args: clean_generic_args( cx, ty.map_bound(|ty| &ty.args[generics.parent_count..]), false, @@ -451,7 +441,7 @@ fn clean_generic_param_def<'tcx>( let default = if let ParamDefaults::Yes = defaults && has_default { - Some(clean_middle_ty( + Some(clean_ty( ty::Binder::dummy(cx.tcx.type_of(def.def_id).instantiate_identity()), cx, Some(def.def_id), @@ -472,7 +462,7 @@ fn clean_generic_param_def<'tcx>( ty::GenericParamDefKind::Const { has_default, is_host_effect } => ( def.name, GenericParamDefKind::Const { - ty: Box::new(clean_middle_ty( + ty: Box::new(clean_ty( ty::Binder::dummy( cx.tcx .type_of(def.def_id) @@ -506,7 +496,7 @@ enum ParamDefaults { No, } -fn clean_ty_generics<'tcx>( +fn clean_generics<'tcx>( cx: &mut DocContext<'tcx>, gens: &ty::Generics, preds: ty::GenericPredicates<'tcx>, @@ -628,7 +618,7 @@ fn clean_ty_generics<'tcx>( if let Some(proj) = impl_trait_proj.remove(&idx) { for (trait_did, name, rhs) in proj { - let rhs = clean_middle_term(rhs, cx); + let rhs = clean_term(rhs, cx); simplify::merge_bounds(cx, &mut bounds, trait_did, name, &rhs); } } @@ -711,7 +701,7 @@ fn clean_poly_fn_sig<'tcx>( // We assume all empty tuples are default return type. This theoretically can discard `-> ()`, // but shouldn't change any code meaning. - let mut output = clean_middle_ty(sig.output(), cx, None, None); + let mut output = clean_ty(sig.output(), cx, None, None); // If the return type isn't an `impl Trait`, we can safely assume that this // function isn't async without needing to execute the query `asyncness` at @@ -731,7 +721,7 @@ fn clean_poly_fn_sig<'tcx>( .inputs() .iter() .map(|t| Argument { - type_: clean_middle_ty(t.map_bound(|t| *t), cx, None, None), + type_: clean_ty(t.map_bound(|t| *t), cx, None, None), name: names .next() .map(|i| i.name) @@ -744,21 +734,21 @@ fn clean_poly_fn_sig<'tcx>( } } -pub(crate) fn clean_middle_assoc_item<'tcx>( +pub(crate) fn clean_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 = Box::new(clean_middle_ty( + let ty = Box::new(clean_ty( ty::Binder::dummy(tcx.type_of(assoc_item.def_id).instantiate_identity()), cx, Some(assoc_item.def_id), None, )); - let mut generics = clean_ty_generics( + let mut generics = clean_generics( cx, tcx.generics_of(assoc_item.def_id), tcx.explicit_predicates_of(assoc_item.def_id), @@ -842,7 +832,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( tcx.explicit_item_bounds(assoc_item.def_id).instantiate_identity_iter_copied(); predicates = tcx.arena.alloc_from_iter(bounds.chain(predicates.iter().copied())); } - let mut generics = clean_ty_generics( + let mut generics = clean_generics( cx, tcx.generics_of(assoc_item.def_id), ty::GenericPredicates { parent: None, predicates }, @@ -914,7 +904,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( if tcx.defaultness(assoc_item.def_id).has_value() { AssocTypeItem( Box::new(TypeAlias { - type_: clean_middle_ty( + type_: clean_ty( ty::Binder::dummy( tcx.type_of(assoc_item.def_id).instantiate_identity(), ), @@ -934,7 +924,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( } else { AssocTypeItem( Box::new(TypeAlias { - type_: clean_middle_ty( + type_: clean_ty( ty::Binder::dummy( tcx.type_of(assoc_item.def_id).instantiate_identity(), ), @@ -1002,7 +992,7 @@ fn clean_trait_object_lifetime_bound<'tcx>( // Since there is a semantic difference between an implicitly elided (i.e. "defaulted") object // lifetime and an explicitly elided object lifetime (`'_`), we intentionally don't hide the - // latter contrary to `clean_middle_region`. + // latter contrary to `clean_region`. match *region { ty::ReStatic => Some(Lifetime::statik()), ty::ReEarlyParam(region) if region.name != kw::Empty => Some(Lifetime(region.name)), @@ -1124,7 +1114,7 @@ pub(crate) enum ObjectLifetimeDefault<'tcx> { } #[instrument(level = "trace", skip(cx), ret)] -pub(crate) fn clean_middle_ty<'tcx>( +pub(crate) fn clean_ty<'tcx>( bound_ty: ty::Binder<'tcx, Ty<'tcx>>, cx: &mut DocContext<'tcx>, parent_def_id: Option, @@ -1139,28 +1129,23 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Uint(uint_ty) => Primitive(uint_ty.into()), ty::Float(float_ty) => Primitive(float_ty.into()), ty::Str => Primitive(PrimitiveType::Str), - ty::Slice(ty) => Slice(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None))), + ty::Slice(ty) => Slice(Box::new(clean_ty(bound_ty.rebind(ty), cx, None, None))), ty::Pat(ty, pat) => Type::Pat( - Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), + Box::new(clean_ty(bound_ty.rebind(ty), cx, None, None)), format!("{pat:?}").into_boxed_str(), ), ty::Array(ty, mut n) => { n = n.normalize(cx.tcx, ty::ParamEnv::reveal_all()); let n = print_const(cx, n); - Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), n.into()) + Array(Box::new(clean_ty(bound_ty.rebind(ty), cx, None, None)), n.into()) } ty::RawPtr(ty, mutbl) => { - RawPointer(mutbl, Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None))) + RawPointer(mutbl, Box::new(clean_ty(bound_ty.rebind(ty), cx, None, None))) } ty::Ref(r, ty, mutbl) => BorrowedRef { - lifetime: clean_middle_region(r), + lifetime: clean_region(r), mutability: mutbl, - type_: Box::new(clean_middle_ty( - bound_ty.rebind(ty), - cx, - None, - Some(ContainerTy::Ref(r)), - )), + type_: Box::new(clean_ty(bound_ty.rebind(ty), cx, None, Some(ContainerTy::Ref(r)))), }, ty::FnDef(..) | ty::FnPtr(_) => { // FIXME: should we merge the outer and inner binders somehow? @@ -1183,12 +1168,12 @@ pub(crate) fn clean_middle_ty<'tcx>( AdtKind::Enum => ItemType::Enum, }; inline::record_extern_fqn(cx, did, kind); - let path = clean_middle_path(cx, did, false, ThinVec::new(), bound_ty.rebind(args)); + let path = clean_path(cx, did, false, ThinVec::new(), bound_ty.rebind(args)); Type::Path { path } } ty::Foreign(did) => { inline::record_extern_fqn(cx, did, ItemType::ForeignType); - let path = clean_middle_path( + let path = clean_path( cx, did, false, @@ -1219,7 +1204,7 @@ pub(crate) fn clean_middle_ty<'tcx>( let mut bounds = dids .map(|did| { let empty = ty::Binder::dummy(ty::GenericArgs::empty()); - let path = clean_middle_path(cx, did, false, ThinVec::new(), empty); + let path = clean_path(cx, did, false, ThinVec::new(), empty); inline::record_extern_fqn(cx, did, ItemType::Trait); PolyTrait { trait_: path, generic_params: Vec::new() } }) @@ -1242,7 +1227,7 @@ pub(crate) fn clean_middle_ty<'tcx>( cx, ), kind: AssocItemConstraintKind::Equality { - term: clean_middle_term(pb.map_bound(|pb| pb.term), cx), + term: clean_term(pb.map_bound(|pb| pb.term), cx), }, }) .collect(); @@ -1261,13 +1246,13 @@ pub(crate) fn clean_middle_ty<'tcx>( .collect(); let late_bound_regions = late_bound_regions.into_iter().collect(); - let path = clean_middle_path(cx, did, false, bindings, args); + let path = clean_path(cx, did, false, bindings, args); bounds.insert(0, PolyTrait { trait_: path, generic_params: late_bound_regions }); DynTrait(bounds, lifetime) } ty::Tuple(t) => { - Tuple(t.iter().map(|t| clean_middle_ty(bound_ty.rebind(t), cx, None, None)).collect()) + Tuple(t.iter().map(|t| clean_ty(bound_ty.rebind(t), cx, None, None)).collect()) } ty::Alias(ty::Projection, data) => { @@ -1277,13 +1262,13 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Alias(ty::Inherent, alias_ty) => { let def_id = alias_ty.def_id; let alias_ty = bound_ty.rebind(alias_ty); - let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None, None); + let self_type = clean_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None, None); Type::QPath(Box::new(QPathData { assoc: PathSegment { name: cx.tcx.associated_item(def_id).name, args: GenericArgs::AngleBracketed { - args: clean_middle_generic_args( + args: clean_generic_args( cx, alias_ty.map_bound(|ty| ty.args.as_slice()), true, @@ -1303,17 +1288,12 @@ pub(crate) fn clean_middle_ty<'tcx>( if cx.tcx.features().lazy_type_alias { // Weak type alias `data` represents the `type X` in `type X = Y`. If we need `Y`, // we need to use `type_of`. - let path = clean_middle_path( - cx, - data.def_id, - false, - ThinVec::new(), - bound_ty.rebind(data.args), - ); + let path = + clean_path(cx, data.def_id, false, ThinVec::new(), bound_ty.rebind(data.args)); Type::Path { path } } else { let ty = cx.tcx.type_of(data.def_id).instantiate(cx.tcx, data.args); - clean_middle_ty(bound_ty.rebind(ty), cx, None, None) + clean_ty(bound_ty.rebind(ty), cx, None, None) } } @@ -1333,8 +1313,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { // If it's already in the same alias, don't get an infinite loop. if cx.current_type_aliases.contains_key(&def_id) { - let path = - clean_middle_path(cx, def_id, false, ThinVec::new(), bound_ty.rebind(args)); + let path = clean_path(cx, def_id, false, ThinVec::new(), bound_ty.rebind(args)); Type::Path { path } } else { *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; @@ -1346,7 +1325,7 @@ pub(crate) fn clean_middle_ty<'tcx>( .iter_instantiated_copied(cx.tcx, args) .map(|(bound, _)| bound) .collect::>(); - let ty = clean_middle_opaque_bounds(cx, bounds); + let ty = clean_opaque_bounds(cx, bounds); if let Some(count) = cx.current_type_aliases.get_mut(&def_id) { *count -= 1; if *count == 0 { @@ -1367,10 +1346,7 @@ pub(crate) fn clean_middle_ty<'tcx>( } } -fn clean_middle_opaque_bounds<'tcx>( - cx: &mut DocContext<'tcx>, - bounds: Vec>, -) -> Type { +fn clean_opaque_bounds<'tcx>(cx: &mut DocContext<'tcx>, bounds: Vec>) -> Type { let mut has_sized = false; let mut bounds = bounds .iter() @@ -1379,7 +1355,7 @@ fn clean_middle_opaque_bounds<'tcx>( let trait_ref = match bound_predicate.skip_binder() { ty::ClauseKind::Trait(tr) => bound_predicate.rebind(tr.trait_ref), ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { - return clean_middle_region(reg).map(GenericBound::Outlives); + return clean_region(reg).map(GenericBound::Outlives); } _ => return None, }; @@ -1404,7 +1380,7 @@ fn clean_middle_opaque_bounds<'tcx>( cx, ), kind: AssocItemConstraintKind::Equality { - term: clean_middle_term(bound.kind().rebind(proj.term), cx), + term: clean_term(bound.kind().rebind(proj.term), cx), }, }) } else { @@ -1436,11 +1412,11 @@ fn clean_middle_opaque_bounds<'tcx>( ImplTrait(bounds) } -pub(crate) fn clean_middle_field<'tcx>(field: &ty::FieldDef, cx: &mut DocContext<'tcx>) -> Item { +pub(crate) fn clean_field<'tcx>(field: &ty::FieldDef, cx: &mut DocContext<'tcx>) -> Item { clean_field_with_def_id( field.did, field.name, - clean_middle_ty( + clean_ty( ty::Binder::dummy(cx.tcx.type_of(field.did).instantiate_identity()), cx, Some(field.did), @@ -1468,11 +1444,11 @@ pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocCont let kind = match variant.ctor_kind() { Some(CtorKind::Const) => VariantKind::CLike, - Some(CtorKind::Fn) => VariantKind::Tuple( - variant.fields.iter().map(|field| clean_middle_field(field, cx)).collect(), - ), + Some(CtorKind::Fn) => { + VariantKind::Tuple(variant.fields.iter().map(|field| clean_field(field, cx)).collect()) + } None => VariantKind::Struct(VariantStruct { - fields: variant.fields.iter().map(|field| clean_middle_field(field, cx)).collect(), + fields: variant.fields.iter().map(|field| clean_field(field, cx)).collect(), }), }; @@ -1520,7 +1496,7 @@ pub(crate) fn clean_variant_def_with_args<'tcx>( clean_field_with_def_id( field.did, field.name, - clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None), + clean_ty(ty::Binder::dummy(ty), cx, Some(field.did), None), cx, ) }) @@ -1545,7 +1521,7 @@ pub(crate) fn clean_variant_def_with_args<'tcx>( clean_field_with_def_id( field.did, field.name, - clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None), + clean_ty(ty::Binder::dummy(ty), cx, Some(field.did), None), cx, ) }) @@ -1709,6 +1685,183 @@ fn add_without_unwanted_attributes<'hir>( } } +fn clean_path<'tcx>( + cx: &mut DocContext<'tcx>, + did: DefId, + has_self: bool, + constraints: ThinVec, + args: ty::Binder<'tcx, GenericArgsRef<'tcx>>, +) -> Path { + let def_kind = cx.tcx.def_kind(did); + let name = cx.tcx.opt_item_name(did).unwrap_or(kw::Empty); + Path { + res: Res::Def(def_kind, did), + segments: thin_vec![PathSegment { + name, + args: clean_generic_args_with_constraints(cx, did, has_self, constraints, args), + }], + } +} + +fn clean_generic_args<'tcx>( + cx: &mut DocContext<'tcx>, + args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, + mut has_self: bool, + owner: DefId, +) -> Vec { + let (args, bound_vars) = (args.skip_binder(), args.bound_vars()); + if args.is_empty() { + // Fast path which avoids executing the query `generics_of`. + return Vec::new(); + } + + // If the container is a trait object type, the arguments won't contain the self type but the + // generics of the corresponding trait will. In such a case, prepend a dummy self type in order + // to align the arguments and parameters for the iteration below and to enable us to correctly + // instantiate the generic parameter default later. + let generics = cx.tcx.generics_of(owner); + let args = if !has_self && generics.parent.is_none() && generics.has_self { + has_self = true; + [cx.tcx.types.trait_object_dummy_self.into()] + .into_iter() + .chain(args.iter().copied()) + .collect::>() + .into() + } else { + std::borrow::Cow::from(args) + }; + + let mut elision_has_failed_once_before = false; + let clean_arg = |(index, &arg): (usize, &ty::GenericArg<'tcx>)| { + // Elide the self type. + if has_self && index == 0 { + return None; + } + + // Elide internal host effect args. + let param = generics.param_at(index, cx.tcx); + if param.is_host_effect() { + return None; + } + + let arg = ty::Binder::bind_with_vars(arg, bound_vars); + + // Elide arguments that coincide with their default. + if !elision_has_failed_once_before && let Some(default) = param.default_value(cx.tcx) { + let default = default.instantiate(cx.tcx, args.as_ref()); + if can_elide_generic_arg(arg, arg.rebind(default)) { + return None; + } + elision_has_failed_once_before = true; + } + + match arg.skip_binder().unpack() { + ty::GenericArgKind::Lifetime(lt) => { + Some(GenericArg::Lifetime(clean_region(lt).unwrap_or(Lifetime::elided()))) + } + ty::GenericArgKind::Type(ty) => Some(GenericArg::Type(clean_ty( + arg.rebind(ty), + cx, + None, + Some(crate::clean::ContainerTy::Regular { + ty: owner, + args: arg.rebind(args.as_ref()), + arg: index, + }), + ))), + ty::GenericArgKind::Const(ct) => { + Some(GenericArg::Const(Box::new(clean_const(arg.rebind(ct), cx)))) + } + } + }; + + let offset = if has_self { 1 } else { 0 }; + let mut clean_args = Vec::with_capacity(args.len().saturating_sub(offset)); + clean_args.extend(args.iter().enumerate().rev().filter_map(clean_arg)); + clean_args.reverse(); + clean_args +} + +/// Check if the generic argument `actual` coincides with the `default` and can therefore be elided. +/// +/// This uses a very conservative approach for performance and correctness reasons, meaning for +/// several classes of terms it claims that they cannot be elided even if they theoretically could. +/// This is absolutely fine since it mostly concerns edge cases. +fn can_elide_generic_arg<'tcx>( + actual: ty::Binder<'tcx, ty::GenericArg<'tcx>>, + default: ty::Binder<'tcx, ty::GenericArg<'tcx>>, +) -> bool { + debug_assert_matches!( + (actual.skip_binder().unpack(), default.skip_binder().unpack()), + (ty::GenericArgKind::Lifetime(_), ty::GenericArgKind::Lifetime(_)) + | (ty::GenericArgKind::Type(_), ty::GenericArgKind::Type(_)) + | (ty::GenericArgKind::Const(_), ty::GenericArgKind::Const(_)) + ); + + // In practice, we shouldn't have any inference variables at this point. + // However to be safe, we bail out if we do happen to stumble upon them. + if actual.has_infer() || default.has_infer() { + return false; + } + + // Since we don't properly keep track of bound variables in rustdoc (yet), we don't attempt to + // make any sense out of escaping bound variables. We simply don't have enough context and it + // would be incorrect to try to do so anyway. + if actual.has_escaping_bound_vars() || default.has_escaping_bound_vars() { + return false; + } + + // Theoretically we could now check if either term contains (non-escaping) late-bound regions or + // projections, relate the two using an `InferCtxt` and check if the resulting obligations hold. + // Having projections means that the terms can potentially be further normalized thereby possibly + // revealing that they are equal after all. Regarding late-bound regions, they could to be + // liberated allowing us to consider more types to be equal by ignoring the names of binders + // (e.g., `for<'a> TYPE<'a>` and `for<'b> TYPE<'b>`). + // + // However, we are mostly interested in “reeliding” generic args, i.e., eliding generic args that + // were originally elided by the user and later filled in by the compiler contrary to eliding + // arbitrary generic arguments if they happen to semantically coincide with the default (of course, + // we cannot possibly distinguish these two cases). Therefore and for performance reasons, it + // suffices to only perform a syntactic / structural check by comparing the memory addresses of + // the interned arguments. + actual.skip_binder() == default.skip_binder() +} + +fn clean_generic_args_with_constraints<'tcx>( + cx: &mut DocContext<'tcx>, + did: DefId, + has_self: bool, + constraints: ThinVec, + ty_args: ty::Binder<'tcx, GenericArgsRef<'tcx>>, +) -> GenericArgs { + let args = clean_generic_args(cx, ty_args.map_bound(|args| &args[..]), has_self, did); + + if cx.tcx.fn_trait_kind_from_def_id(did).is_some() { + let ty = ty_args + .iter() + .nth(if has_self { 1 } else { 0 }) + .unwrap() + .map_bound(|arg| arg.expect_ty()); + let inputs = + // The trait's first substitution is the one after self, if there is one. + match ty.skip_binder().kind() { + ty::Tuple(tys) => tys.iter().map(|t| clean_ty(ty.rebind(t), cx, None, None)).collect::>().into(), + _ => return GenericArgs::AngleBracketed { args: args.into(), constraints }, + }; + let output = constraints.into_iter().next().and_then(|binding| match binding.kind { + AssocItemConstraintKind::Equality { term: Term::Type(ty) } + if ty != Type::Tuple(Vec::new()) => + { + Some(Box::new(ty)) + } + _ => None, + }); + GenericArgs::Parenthesized { inputs, output } + } else { + GenericArgs::AngleBracketed { args: args.into(), constraints } + } +} + fn clean_bound_vars<'tcx>( bound_vars: &'tcx ty::List, ) -> Vec { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index c4020f2a450bc..677980cdca727 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -37,7 +37,7 @@ use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; use crate::clean::cfg::Cfg; -use crate::clean::clean_middle_path; +use crate::clean::clean_path; use crate::clean::inline::{self, print_inlined_const}; use crate::clean::utils::{is_literal_expr, print_evaluated_const}; use crate::core::DocContext; @@ -1258,7 +1258,7 @@ impl GenericBound { fn sized_with(cx: &mut DocContext<'_>, modifier: hir::TraitBoundModifier) -> GenericBound { let did = cx.tcx.require_lang_item(LangItem::Sized, None); let empty = ty::Binder::dummy(ty::GenericArgs::empty()); - let path = clean_middle_path(cx, did, false, ThinVec::new(), empty); + let path = clean_path(cx, did, false, ThinVec::new(), empty); inline::record_extern_fqn(cx, did, ItemType::Trait); GenericBound::TraitBound(PolyTrait { trait_: path, generic_params: Vec::new() }, modifier) } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 7c83d4387193f..9e28dcd4742ba 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -2,10 +2,8 @@ use crate::clean::auto_trait::synthesize_auto_trait_impls; use crate::clean::blanket_impl::synthesize_blanket_impls; use crate::clean::render_macro_matchers::render_macro_matcher; use crate::clean::{ - clean_doc_module, clean_middle_const, clean_middle_region, clean_middle_ty, inline, - AssocItemConstraint, AssocItemConstraintKind, Crate, ExternalCrate, Generic, GenericArg, - GenericArgs, ImportSource, Item, ItemKind, Lifetime, Path, PathSegment, Primitive, - PrimitiveType, Term, Type, + clean_doc_module, inline, Crate, ExternalCrate, Generic, ImportSource, Item, ItemKind, Path, + Primitive, PrimitiveType, Type, }; use crate::core::DocContext; use crate::html::format::visibility_to_src_with_space; @@ -17,14 +15,11 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_metadata::rendered_const; use rustc_middle::mir; -use rustc_middle::ty::TypeVisitableExt; -use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{kw, sym, Symbol}; -use std::assert_matches::debug_assert_matches; use std::fmt::Write as _; use std::mem; use std::sync::LazyLock as Lazy; -use thin_vec::{thin_vec, ThinVec}; #[cfg(test)] mod tests; @@ -77,183 +72,6 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { Crate { module, external_traits: cx.external_traits.clone() } } -pub(crate) fn clean_middle_generic_args<'tcx>( - cx: &mut DocContext<'tcx>, - args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, - mut has_self: bool, - owner: DefId, -) -> Vec { - let (args, bound_vars) = (args.skip_binder(), args.bound_vars()); - if args.is_empty() { - // Fast path which avoids executing the query `generics_of`. - return Vec::new(); - } - - // If the container is a trait object type, the arguments won't contain the self type but the - // generics of the corresponding trait will. In such a case, prepend a dummy self type in order - // to align the arguments and parameters for the iteration below and to enable us to correctly - // instantiate the generic parameter default later. - let generics = cx.tcx.generics_of(owner); - let args = if !has_self && generics.parent.is_none() && generics.has_self { - has_self = true; - [cx.tcx.types.trait_object_dummy_self.into()] - .into_iter() - .chain(args.iter().copied()) - .collect::>() - .into() - } else { - std::borrow::Cow::from(args) - }; - - let mut elision_has_failed_once_before = false; - let clean_arg = |(index, &arg): (usize, &ty::GenericArg<'tcx>)| { - // Elide the self type. - if has_self && index == 0 { - return None; - } - - // Elide internal host effect args. - let param = generics.param_at(index, cx.tcx); - if param.is_host_effect() { - return None; - } - - let arg = ty::Binder::bind_with_vars(arg, bound_vars); - - // Elide arguments that coincide with their default. - if !elision_has_failed_once_before && let Some(default) = param.default_value(cx.tcx) { - let default = default.instantiate(cx.tcx, args.as_ref()); - if can_elide_generic_arg(arg, arg.rebind(default)) { - return None; - } - elision_has_failed_once_before = true; - } - - match arg.skip_binder().unpack() { - GenericArgKind::Lifetime(lt) => { - Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided()))) - } - GenericArgKind::Type(ty) => Some(GenericArg::Type(clean_middle_ty( - arg.rebind(ty), - cx, - None, - Some(crate::clean::ContainerTy::Regular { - ty: owner, - args: arg.rebind(args.as_ref()), - arg: index, - }), - ))), - GenericArgKind::Const(ct) => { - Some(GenericArg::Const(Box::new(clean_middle_const(arg.rebind(ct), cx)))) - } - } - }; - - let offset = if has_self { 1 } else { 0 }; - let mut clean_args = Vec::with_capacity(args.len().saturating_sub(offset)); - clean_args.extend(args.iter().enumerate().rev().filter_map(clean_arg)); - clean_args.reverse(); - clean_args -} - -/// Check if the generic argument `actual` coincides with the `default` and can therefore be elided. -/// -/// This uses a very conservative approach for performance and correctness reasons, meaning for -/// several classes of terms it claims that they cannot be elided even if they theoretically could. -/// This is absolutely fine since it mostly concerns edge cases. -fn can_elide_generic_arg<'tcx>( - actual: ty::Binder<'tcx, ty::GenericArg<'tcx>>, - default: ty::Binder<'tcx, ty::GenericArg<'tcx>>, -) -> bool { - debug_assert_matches!( - (actual.skip_binder().unpack(), default.skip_binder().unpack()), - (ty::GenericArgKind::Lifetime(_), ty::GenericArgKind::Lifetime(_)) - | (ty::GenericArgKind::Type(_), ty::GenericArgKind::Type(_)) - | (ty::GenericArgKind::Const(_), ty::GenericArgKind::Const(_)) - ); - - // In practice, we shouldn't have any inference variables at this point. - // However to be safe, we bail out if we do happen to stumble upon them. - if actual.has_infer() || default.has_infer() { - return false; - } - - // Since we don't properly keep track of bound variables in rustdoc (yet), we don't attempt to - // make any sense out of escaping bound variables. We simply don't have enough context and it - // would be incorrect to try to do so anyway. - if actual.has_escaping_bound_vars() || default.has_escaping_bound_vars() { - return false; - } - - // Theoretically we could now check if either term contains (non-escaping) late-bound regions or - // projections, relate the two using an `InferCtxt` and check if the resulting obligations hold. - // Having projections means that the terms can potentially be further normalized thereby possibly - // revealing that they are equal after all. Regarding late-bound regions, they could to be - // liberated allowing us to consider more types to be equal by ignoring the names of binders - // (e.g., `for<'a> TYPE<'a>` and `for<'b> TYPE<'b>`). - // - // However, we are mostly interested in “reeliding” generic args, i.e., eliding generic args that - // were originally elided by the user and later filled in by the compiler contrary to eliding - // arbitrary generic arguments if they happen to semantically coincide with the default (of course, - // we cannot possibly distinguish these two cases). Therefore and for performance reasons, it - // suffices to only perform a syntactic / structural check by comparing the memory addresses of - // the interned arguments. - actual.skip_binder() == default.skip_binder() -} - -fn clean_middle_generic_args_with_constraints<'tcx>( - cx: &mut DocContext<'tcx>, - did: DefId, - has_self: bool, - constraints: ThinVec, - ty_args: ty::Binder<'tcx, GenericArgsRef<'tcx>>, -) -> GenericArgs { - let args = clean_middle_generic_args(cx, ty_args.map_bound(|args| &args[..]), has_self, did); - - if cx.tcx.fn_trait_kind_from_def_id(did).is_some() { - let ty = ty_args - .iter() - .nth(if has_self { 1 } else { 0 }) - .unwrap() - .map_bound(|arg| arg.expect_ty()); - let inputs = - // The trait's first substitution is the one after self, if there is one. - match ty.skip_binder().kind() { - ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(ty.rebind(t), cx, None, None)).collect::>().into(), - _ => return GenericArgs::AngleBracketed { args: args.into(), constraints }, - }; - let output = constraints.into_iter().next().and_then(|binding| match binding.kind { - AssocItemConstraintKind::Equality { term: Term::Type(ty) } - if ty != Type::Tuple(Vec::new()) => - { - Some(Box::new(ty)) - } - _ => None, - }); - GenericArgs::Parenthesized { inputs, output } - } else { - GenericArgs::AngleBracketed { args: args.into(), constraints } - } -} - -pub(super) fn clean_middle_path<'tcx>( - cx: &mut DocContext<'tcx>, - did: DefId, - has_self: bool, - constraints: ThinVec, - args: ty::Binder<'tcx, GenericArgsRef<'tcx>>, -) -> Path { - let def_kind = cx.tcx.def_kind(did); - let name = cx.tcx.opt_item_name(did).unwrap_or(kw::Empty); - Path { - res: Res::Def(def_kind, did), - segments: thin_vec![PathSegment { - name, - args: clean_middle_generic_args_with_constraints(cx, did, has_self, constraints, args), - }], - } -} - pub(crate) fn qpath_to_string(p: &hir::QPath<'_>) -> String { let segments = match *p { hir::QPath::Resolved(_, path) => &path.segments, From 5c489cba6e75b9f550378d5e0fd97c03a4eb3d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 28 Jun 2024 01:32:52 +0200 Subject: [PATCH 4/4] [wip] HIR cleaners: move `cx` param to the end --- src/librustdoc/clean/local.rs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/librustdoc/clean/local.rs b/src/librustdoc/clean/local.rs index d9505f822c1dc..56493519c0f6b 100644 --- a/src/librustdoc/clean/local.rs +++ b/src/librustdoc/clean/local.rs @@ -27,9 +27,9 @@ use super::{ }; fn clean_args_from_types_and_body_id<'tcx>( - cx: &mut DocContext<'tcx>, types: &[hir::Ty<'tcx>], body_id: hir::BodyId, + cx: &mut DocContext<'tcx>, ) -> Arguments { let body = cx.tcx.hir().body(body_id); @@ -47,9 +47,9 @@ fn clean_args_from_types_and_body_id<'tcx>( } fn clean_args_from_types_and_names<'tcx>( - cx: &mut DocContext<'tcx>, types: &[hir::Ty<'tcx>], names: &[Ident], + cx: &mut DocContext<'tcx>, ) -> Arguments { Arguments { values: types @@ -107,7 +107,7 @@ fn clean_bare_fn_ty<'tcx>( BareFunctionDecl { safety: bare_fn.safety, abi: bare_fn.abi, decl, generic_params } } -fn clean_const_arg<'tcx>(constant: &hir::ConstArg<'_>, _cx: &mut DocContext<'tcx>) -> Constant { +fn clean_const_arg<'tcx>(constant: &hir::ConstArg<'_>) -> Constant { Constant { kind: ConstantKind::Anonymous { body: constant.value.body } } } @@ -164,10 +164,10 @@ fn clean_field<'tcx>(field: &hir::FieldDef<'tcx>, cx: &mut DocContext<'tcx>) -> } fn clean_fn_decl_with_args<'tcx>( - cx: &mut DocContext<'tcx>, decl: &hir::FnDecl<'tcx>, header: Option<&hir::FnHeader>, args: Arguments, + cx: &mut DocContext<'tcx>, ) -> FnDecl { let mut output = match decl.output { hir::FnRetTy::Return(typ) => clean_ty(typ, cx), @@ -212,10 +212,10 @@ fn clean_fn_or_proc_macro<'tcx>( } fn clean_fn<'tcx>( - cx: &mut DocContext<'tcx>, sig: &hir::FnSig<'tcx>, generics: &hir::Generics<'tcx>, args: FunctionArgs<'tcx>, + cx: &mut DocContext<'tcx>, ) -> Box { let (generics, decl) = enter_impl_trait(cx, |cx| { // NOTE: generics must be cleaned before args @@ -235,9 +235,9 @@ fn clean_fn<'tcx>( } pub(super) fn clean_foreign_item<'tcx>( - cx: &mut DocContext<'tcx>, item: &hir::ForeignItem<'tcx>, renamed: Option, + cx: &mut DocContext<'tcx>, ) -> Item { let def_id = item.owner_id.to_def_id(); cx.with_param_env(def_id, |cx| { @@ -299,9 +299,7 @@ fn clean_generic_args<'tcx>( }) => { return None; } - hir::GenericArg::Const(ct) => { - GenericArg::Const(Box::new(clean_const_arg(ct, cx))) - } + hir::GenericArg::Const(ct) => GenericArg::Const(Box::new(clean_const_arg(ct))), hir::GenericArg::Infer(_inf) => GenericArg::Infer, }) }) @@ -338,9 +336,9 @@ fn clean_generic_bound<'tcx>( } fn clean_generic_param<'tcx>( - cx: &mut DocContext<'tcx>, - generics: Option<&hir::Generics<'tcx>>, param: &hir::GenericParam<'tcx>, + generics: Option<&hir::Generics<'tcx>>, + cx: &mut DocContext<'tcx>, ) -> GenericParamDef { let (name, kind) = match param.kind { hir::GenericParamKind::Lifetime { .. } => { @@ -608,10 +606,10 @@ pub(super) fn clean_impl_item<'tcx>( } pub(super) fn clean_item<'tcx>( - cx: &mut DocContext<'tcx>, item: &hir::Item<'tcx>, renamed: Option, import_id: Option, + cx: &mut DocContext<'tcx>, ) -> Vec { use hir::ItemKind; @@ -1045,8 +1043,8 @@ pub(super) fn clean_use_statement<'tcx>( name: Symbol, path: &hir::UsePath<'tcx>, kind: hir::UseKind, - cx: &mut DocContext<'tcx>, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, + cx: &mut DocContext<'tcx>, ) -> Vec { let mut items = Vec::new(); let hir::UsePath { segments, ref res, span } = *path; @@ -1062,8 +1060,8 @@ pub(super) fn clean_use_statement_inner<'tcx>( name: Symbol, path: &hir::Path<'tcx>, kind: hir::UseKind, - cx: &mut DocContext<'tcx>, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, + cx: &mut DocContext<'tcx>, ) -> Vec { if should_ignore_res(path.res) { return Vec::new(); @@ -1240,9 +1238,9 @@ fn clean_where_predicate<'tcx>( /// /// If the path is not a re-export or is public, it'll return `None`. fn first_non_private<'tcx>( - cx: &mut DocContext<'tcx>, hir_id: hir::HirId, path: &hir::Path<'tcx>, + cx: &mut DocContext<'tcx>, ) -> Option { let target_def_id = path.res.opt_def_id()?; let (parent_def_id, ident) = match &path.segments { @@ -1332,10 +1330,10 @@ fn first_non_private<'tcx>( } fn first_non_private_clean_path<'tcx>( - cx: &mut DocContext<'tcx>, path: &hir::Path<'tcx>, new_path_segments: &'tcx [hir::PathSegment<'tcx>], new_path_span: rustc_span::Span, + cx: &mut DocContext<'tcx>, ) -> Path { let new_hir_path = hir::Path { segments: new_path_segments, res: path.res, span: new_path_span }; @@ -1376,8 +1374,8 @@ fn is_impl_trait(param: &hir::GenericParam<'_>) -> bool { } fn maybe_expand_private_type_alias<'tcx>( - cx: &mut DocContext<'tcx>, path: &hir::Path<'tcx>, + cx: &mut DocContext<'tcx>, ) -> Option { let Res::Def(DefKind::TyAlias, def_id) = path.res else { return None }; // Substitute private type aliases