diff options
Diffstat (limited to 'src/librustdoc/clean')
-rw-r--r-- | src/librustdoc/clean/inline.rs | 23 | ||||
-rw-r--r-- | src/librustdoc/clean/mod.rs | 211 | ||||
-rw-r--r-- | src/librustdoc/clean/types.rs | 121 | ||||
-rw-r--r-- | src/librustdoc/clean/utils.rs | 116 |
4 files changed, 265 insertions, 206 deletions
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index cac211307..fcbcfbf5c 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -20,7 +20,8 @@ use rustc_span::symbol::{kw, sym, Symbol}; use crate::clean::{ self, clean_fn_decl_from_did_and_sig, clean_generics, clean_impl_item, clean_middle_assoc_item, clean_middle_field, clean_middle_ty, clean_trait_ref_with_bindings, clean_ty, - clean_ty_generics, clean_variant_def, utils, Attributes, AttributesExt, ImplKind, ItemId, Type, + clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes, + AttributesExt, ImplKind, ItemId, Type, }; use crate::core::DocContext; use crate::formats::item_type::ItemType; @@ -79,10 +80,10 @@ pub(crate) fn try_inline( build_impls(cx, did, attrs_without_docs, &mut ret); clean::UnionItem(build_union(cx, did)) } - Res::Def(DefKind::TyAlias { .. }, did) => { - record_extern_fqn(cx, did, ItemType::Typedef); + Res::Def(DefKind::TyAlias, did) => { + record_extern_fqn(cx, did, ItemType::TypeAlias); build_impls(cx, did, attrs_without_docs, &mut ret); - clean::TypedefItem(build_type_alias(cx, did)) + clean::TypeAliasItem(build_type_alias(cx, did)) } Res::Def(DefKind::Enum, did) => { record_extern_fqn(cx, did, ItemType::Enum); @@ -287,18 +288,16 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union { clean::Union { generics, fields } } -fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::Typedef> { +fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::TypeAlias> { let predicates = cx.tcx.explicit_predicates_of(did); - let type_ = clean_middle_ty( - ty::Binder::dummy(cx.tcx.type_of(did).instantiate_identity()), - cx, - Some(did), - None, - ); + let ty = cx.tcx.type_of(did).instantiate_identity(); + let type_ = clean_middle_ty(ty::Binder::dummy(ty), cx, Some(did), None); + let inner_type = clean_ty_alias_inner_type(ty, cx); - Box::new(clean::Typedef { + Box::new(clean::TypeAlias { type_, generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates), + inner_type, item_type: None, }) } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c9a05460b..0caa92e44 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -24,6 +24,7 @@ use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; use rustc_middle::metadata::Reexport; use rustc_middle::middle::resolve_bound_vars as rbv; use rustc_middle::ty::fold::TypeFolder; +use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, AdtKind, EarlyBinder, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -541,7 +542,7 @@ fn clean_generic_param_def<'tcx>( }, ) } - ty::GenericParamDefKind::Const { has_default } => ( + ty::GenericParamDefKind::Const { has_default, is_host_effect } => ( def.name, GenericParamDefKind::Const { ty: Box::new(clean_middle_ty( @@ -561,6 +562,7 @@ fn clean_generic_param_def<'tcx>( )), false => None, }, + is_host_effect, }, ), }; @@ -617,6 +619,7 @@ fn clean_generic_param<'tcx>( 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: cx.tcx.has_attr(param.def_id, sym::rustc_host), }, ), }; @@ -792,6 +795,7 @@ fn clean_ty_generics<'tcx>( } Some(clean_generic_param_def(param, cx)) } + ty::GenericParamDefKind::Const { is_host_effect: true, .. } => None, ty::GenericParamDefKind::Const { .. } => Some(clean_generic_param_def(param, cx)), }) .collect::<ThinVec<GenericParamDef>>(); @@ -955,6 +959,43 @@ fn clean_ty_generics<'tcx>( } } +fn clean_ty_alias_inner_type<'tcx>( + ty: Ty<'tcx>, + cx: &mut DocContext<'tcx>, +) -> Option<TypeAliasInnerType> { + let ty::Adt(adt_def, args) = ty.kind() else { + return None; + }; + + Some(if adt_def.is_enum() { + let variants: rustc_index::IndexVec<_, _> = adt_def + .variants() + .iter() + .map(|variant| clean_variant_def_with_args(variant, args, cx)) + .collect(); + + TypeAliasInnerType::Enum { + variants, + is_non_exhaustive: adt_def.is_variant_list_non_exhaustive(), + } + } else { + let variant = adt_def + .variants() + .iter() + .next() + .unwrap_or_else(|| bug!("a struct or union should always have one variant def")); + + let fields: Vec<_> = + clean_variant_def_with_args(variant, args, cx).kind.inner_items().cloned().collect(); + + if adt_def.is_struct() { + TypeAliasInnerType::Struct { ctor_kind: variant.ctor_kind(), fields } + } else { + TypeAliasInnerType::Union { fields } + } + }) +} + fn clean_proc_macro<'tcx>( item: &hir::Item<'tcx>, name: &mut Symbol, @@ -1069,10 +1110,7 @@ fn clean_function<'tcx>( clean_args_from_types_and_names(cx, sig.decl.inputs, names) } }; - let mut decl = clean_fn_decl_with_args(cx, sig.decl, args); - if sig.header.is_async() { - decl.output = decl.sugared_async_return_type(); - } + let decl = clean_fn_decl_with_args(cx, sig.decl, Some(&sig.header), args); (generics, decl) }); Box::new(Function { decl, generics }) @@ -1123,12 +1161,16 @@ fn clean_args_from_types_and_body_id<'tcx>( fn clean_fn_decl_with_args<'tcx>( cx: &mut DocContext<'tcx>, decl: &hir::FnDecl<'tcx>, + header: Option<&hir::FnHeader>, args: Arguments, ) -> FnDecl { - let output = match decl.output { + 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 } } @@ -1141,7 +1183,17 @@ fn clean_fn_decl_from_did_and_sig<'tcx>( // We assume all empty tuples are default return type. This theoretically can discard `-> ()`, // but shouldn't change any code meaning. - let output = clean_middle_ty(sig.output(), cx, None, None); + let mut output = clean_middle_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 + // all which gives us a noticeable performance boost. + if let Some(did) = did + && let Type::ImplTrait(_) = output + && cx.tcx.asyncness(did).is_async() + { + output = output.sugared_async_return_type(); + } FnDecl { output, @@ -1219,9 +1271,10 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext None, ); AssocTypeItem( - Box::new(Typedef { + Box::new(TypeAlias { type_: clean_ty(default, cx), generics, + inner_type: None, item_type: Some(item_type), }), bounds, @@ -1264,7 +1317,12 @@ pub(crate) fn clean_impl_item<'tcx>( None, ); AssocTypeItem( - Box::new(Typedef { type_, generics, item_type: Some(item_type) }), + Box::new(TypeAlias { + type_, + generics, + inner_type: None, + item_type: Some(item_type), + }), Vec::new(), ) } @@ -1461,7 +1519,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( if tcx.defaultness(assoc_item.def_id).has_value() { AssocTypeItem( - Box::new(Typedef { + Box::new(TypeAlias { type_: clean_middle_ty( ty::Binder::dummy( tcx.type_of(assoc_item.def_id).instantiate_identity(), @@ -1471,6 +1529,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( None, ), generics, + inner_type: None, item_type: None, }), bounds, @@ -1480,7 +1539,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( } } else { AssocTypeItem( - Box::new(Typedef { + Box::new(TypeAlias { type_: clean_middle_ty( ty::Binder::dummy( tcx.type_of(assoc_item.def_id).instantiate_identity(), @@ -1490,6 +1549,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( None, ), generics, + inner_type: None, item_type: None, }), // Associated types inside trait or inherent impls are not allowed to have @@ -1706,7 +1766,7 @@ fn maybe_expand_private_type_alias<'tcx>( cx: &mut DocContext<'tcx>, path: &hir::Path<'tcx>, ) -> Option<Type> { - let Res::Def(DefKind::TyAlias { .. }, def_id) = path.res else { return None }; + 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()) @@ -1817,7 +1877,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T // 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.eval(cx.tcx, param_env)) + print_const(cx, ct.normalize(cx.tcx, param_env)) } }; @@ -1975,7 +2035,7 @@ impl<'tcx> ContainerTy<'tcx> { let (DefKind::Struct | DefKind::Union | DefKind::Enum - | DefKind::TyAlias { .. } + | DefKind::TyAlias | DefKind::Trait) = tcx.def_kind(container) else { return ObjectLifetimeDefault::Empty; @@ -2036,7 +2096,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Str => Primitive(PrimitiveType::Str), ty::Slice(ty) => Slice(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None))), ty::Array(ty, mut n) => { - n = n.eval(cx.tcx, ty::ParamEnv::reveal_all()); + 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()) } @@ -2241,7 +2301,6 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Bound(..) => panic!("Bound"), ty::Placeholder(..) => panic!("Placeholder"), ty::GeneratorWitness(..) => panic!("GeneratorWitness"), - ty::GeneratorWitnessMIR(..) => panic!("GeneratorWitnessMIR"), ty::Infer(..) => panic!("Infer"), ty::Error(_) => rustc_errors::FatalError.raise(), } @@ -2363,6 +2422,83 @@ pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocCont ) } +pub(crate) fn clean_variant_def_with_args<'tcx>( + variant: &ty::VariantDef, + args: &GenericArgsRef<'tcx>, + cx: &mut DocContext<'tcx>, +) -> Item { + let discriminant = match variant.discr { + ty::VariantDiscr::Explicit(def_id) => Some(Discriminant { expr: None, value: def_id }), + ty::VariantDiscr::Relative(_) => None, + }; + + use rustc_middle::traits::ObligationCause; + use rustc_trait_selection::infer::TyCtxtInferExt; + use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; + + let infcx = cx.tcx.infer_ctxt().build(); + let kind = match variant.ctor_kind() { + Some(CtorKind::Const) => VariantKind::CLike, + Some(CtorKind::Fn) => VariantKind::Tuple( + variant + .fields + .iter() + .map(|field| { + let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args); + + // normalize the type to only show concrete types + // note: we do not use try_normalize_erasing_regions since we + // do care about showing the regions + let ty = infcx + .at(&ObligationCause::dummy(), cx.param_env) + .query_normalize(ty) + .map(|normalized| normalized.value) + .unwrap_or(ty); + + clean_field_with_def_id( + field.did, + field.name, + clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None), + cx, + ) + }) + .collect(), + ), + None => VariantKind::Struct(VariantStruct { + fields: variant + .fields + .iter() + .map(|field| { + let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args); + + // normalize the type to only show concrete types + // note: we do not use try_normalize_erasing_regions since we + // do care about showing the regions + let ty = infcx + .at(&ObligationCause::dummy(), cx.param_env) + .query_normalize(ty) + .map(|normalized| normalized.value) + .unwrap_or(ty); + + clean_field_with_def_id( + field.did, + field.name, + clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None), + cx, + ) + }) + .collect(), + }), + }; + + Item::from_def_id_and_parts( + variant.def_id, + Some(variant.name), + VariantItem(Variant { kind, discriminant }), + cx, + ) +} + fn clean_variant_data<'tcx>( variant: &hir::VariantData<'tcx>, disr_expr: &Option<hir::AnonConst>, @@ -2406,14 +2542,22 @@ fn clean_generic_args<'tcx>( let args = generic_args .args .iter() - .map(|arg| 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)), - hir::GenericArg::Const(ct) => GenericArg::Const(Box::new(clean_const(ct, cx))), - hir::GenericArg::Infer(_inf) => GenericArg::Infer, + .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)), + // FIXME(effects): This will still emit `<true>` for non-const impls of const traits + hir::GenericArg::Const(ct) + if cx.tcx.has_attr(ct.value.def_id, sym::rustc_host) => + { + return None; + } + hir::GenericArg::Const(ct) => GenericArg::Const(Box::new(clean_const(ct, cx))), + hir::GenericArg::Infer(_inf) => GenericArg::Infer, + }) }) .collect::<Vec<_>>() .into(); @@ -2443,7 +2587,7 @@ fn clean_bare_fn_ty<'tcx>( .map(|x| clean_generic_param(cx, None, x)) .collect(); let args = clean_args_from_types_and_names(cx, bare_fn.decl.inputs, bare_fn.param_names); - let decl = clean_fn_decl_with_args(cx, bare_fn.decl, args); + let decl = clean_fn_decl_with_args(cx, bare_fn.decl, None, args); (generic_params, decl) }); BareFunctionDecl { unsafety: bare_fn.unsafety, abi: bare_fn.abi, decl, generic_params } @@ -2617,7 +2761,7 @@ fn clean_maybe_renamed_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 ty = clean_middle_ty( + let type_ = clean_middle_ty( ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), cx, None, @@ -2630,7 +2774,16 @@ fn clean_maybe_renamed_item<'tcx>( cx.current_type_aliases.remove(&def_id); } } - TypedefItem(Box::new(Typedef { type_: rustdoc_ty, generics, item_type: Some(ty) })) + + let ty = cx.tcx.type_of(def_id).instantiate_identity(); + let inner_type = clean_ty_alias_inner_type(ty, cx); + + TypeAliasItem(Box::new(TypeAlias { + generics, + inner_type, + type_: rustdoc_ty, + item_type: Some(type_), + })) } ItemKind::Enum(ref def, generics) => EnumItem(Enum { variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(), @@ -2722,7 +2875,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(clean_middle_ty( + DefKind::TyAlias => Some(clean_middle_ty( ty::Binder::dummy(tcx.type_of(def_id).instantiate_identity()), cx, Some(def_id.to_def_id()), @@ -2945,7 +3098,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>( // 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, args); + let decl = clean_fn_decl_with_args(cx, decl, None, args); (generics, decl) }); ForeignFunctionItem(Box::new(Function { decl, generics })) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 49bde1d31..4256e7b51 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -22,20 +22,23 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{BodyId, Mutability}; use rustc_hir_analysis::check::intrinsic::intrinsic_operation_unsafety; use rustc_index::IndexVec; +use rustc_metadata::rendered_const; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt, Visibility}; -use rustc_resolve::rustdoc::{add_doc_fragment, attrs_to_doc_fragments, inner_docs, DocFragment}; +use rustc_resolve::rustdoc::{ + add_doc_fragment, attrs_to_doc_fragments, inner_docs, span_of_fragments, DocFragment, +}; use rustc_session::Session; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{self, FileName, Loc}; +use rustc_span::{self, FileName, Loc, DUMMY_SP}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; use crate::clean::cfg::Cfg; use crate::clean::external_path; use crate::clean::inline::{self, print_inlined_const}; -use crate::clean::utils::{is_literal_expr, print_const_expr, print_evaluated_const}; +use crate::clean::utils::{is_literal_expr, print_evaluated_const}; use crate::core::DocContext; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; @@ -396,7 +399,7 @@ impl Item { } pub(crate) fn attr_span(&self, tcx: TyCtxt<'_>) -> rustc_span::Span { - crate::passes::span_of_attrs(&self.attrs) + span_of_fragments(&self.attrs.doc_strings) .unwrap_or_else(|| self.span(tcx).map_or(rustc_span::DUMMY_SP, |span| span.inner())) } @@ -530,8 +533,8 @@ impl Item { pub(crate) fn is_ty_method(&self) -> bool { self.type_() == ItemType::TyMethod } - pub(crate) fn is_typedef(&self) -> bool { - self.type_() == ItemType::Typedef + pub(crate) fn is_type_alias(&self) -> bool { + self.type_() == ItemType::TypeAlias } pub(crate) fn is_primitive(&self) -> bool { self.type_() == ItemType::Primitive @@ -619,7 +622,7 @@ impl Item { fn build_fn_header( def_id: DefId, tcx: TyCtxt<'_>, - asyncness: hir::IsAsync, + asyncness: ty::Asyncness, ) -> hir::FnHeader { let sig = tcx.fn_sig(def_id).skip_binder(); let constness = @@ -628,6 +631,10 @@ impl Item { } else { hir::Constness::NotConst }; + let asyncness = match asyncness { + ty::Asyncness::Yes => hir::IsAsync::Async(DUMMY_SP), + ty::Asyncness::No => hir::IsAsync::NotAsync, + }; hir::FnHeader { unsafety: sig.unsafety(), abi: sig.abi(), constness, asyncness } } let header = match *self.kind { @@ -799,7 +806,7 @@ pub(crate) enum ItemKind { EnumItem(Enum), FunctionItem(Box<Function>), ModuleItem(Module), - TypedefItem(Box<Typedef>), + TypeAliasItem(Box<TypeAlias>), OpaqueTyItem(OpaqueTy), StaticItem(Static), ConstantItem(Constant), @@ -832,7 +839,7 @@ pub(crate) enum ItemKind { /// The bounds may be non-empty if there is a `where` clause. TyAssocTypeItem(Generics, Vec<GenericBound>), /// An associated type in a trait impl or a provided one in a trait declaration. - AssocTypeItem(Box<Typedef>, Vec<GenericBound>), + AssocTypeItem(Box<TypeAlias>, Vec<GenericBound>), /// An item that has been stripped by a rustdoc pass StrippedItem(Box<ItemKind>), KeywordItem, @@ -857,7 +864,7 @@ impl ItemKind { ExternCrateItem { .. } | ImportItem(_) | FunctionItem(_) - | TypedefItem(_) + | TypeAliasItem(_) | OpaqueTyItem(_) | StaticItem(_) | ConstantItem(_) @@ -891,7 +898,7 @@ impl ItemKind { | ModuleItem(_) | ExternCrateItem { .. } | FunctionItem(_) - | TypedefItem(_) + | TypeAliasItem(_) | OpaqueTyItem(_) | StaticItem(_) | ConstantItem(_) @@ -1308,7 +1315,7 @@ impl WherePredicate { pub(crate) enum GenericParamDefKind { Lifetime { outlives: Vec<Lifetime> }, Type { did: DefId, bounds: Vec<GenericBound>, default: Option<Box<Type>>, synthetic: bool }, - Const { ty: Box<Type>, default: Option<Box<String>> }, + Const { ty: Box<Type>, default: Option<Box<String>>, is_host_effect: bool }, } impl GenericParamDefKind { @@ -1328,9 +1335,10 @@ impl GenericParamDef { Self { name, kind: GenericParamDefKind::Lifetime { outlives: Vec::new() } } } - pub(crate) fn is_synthetic_type_param(&self) -> bool { + pub(crate) fn is_synthetic_param(&self) -> bool { match self.kind { - GenericParamDefKind::Lifetime { .. } | GenericParamDefKind::Const { .. } => false, + GenericParamDefKind::Lifetime { .. } => false, + GenericParamDefKind::Const { is_host_effect, .. } => is_host_effect, GenericParamDefKind::Type { synthetic, .. } => synthetic, } } @@ -1377,28 +1385,6 @@ impl FnDecl { pub(crate) fn self_type(&self) -> Option<SelfTy> { self.inputs.values.get(0).and_then(|v| v.to_self()) } - - /// Returns the sugared return type for an async function. - /// - /// For example, if the return type is `impl std::future::Future<Output = i32>`, this function - /// will return `i32`. - /// - /// # Panics - /// - /// This function will panic if the return type does not match the expected sugaring for async - /// functions. - pub(crate) fn sugared_async_return_type(&self) -> Type { - if let Type::ImplTrait(v) = &self.output && - let [GenericBound::TraitBound(PolyTrait { trait_, .. }, _ )] = &v[..] - { - let bindings = trait_.bindings().unwrap(); - let ret_ty = bindings[0].term(); - let ty = ret_ty.ty().expect("Unexpected constant return term"); - ty.clone() - } else { - panic!("unexpected desugaring of async function") - } - } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] @@ -1610,6 +1596,30 @@ impl Type { } } + /// Returns the sugared return type for an async function. + /// + /// For example, if the return type is `impl std::future::Future<Output = i32>`, this function + /// will return `i32`. + /// + /// # Panics + /// + /// This function will panic if the return type does not match the expected sugaring for async + /// functions. + pub(crate) fn sugared_async_return_type(self) -> Type { + if let Type::ImplTrait(mut v) = self + && let Some(GenericBound::TraitBound(PolyTrait { mut trait_, .. }, _ )) = v.pop() + && let Some(segment) = trait_.segments.pop() + && let GenericArgs::AngleBracketed { mut bindings, .. } = segment.args + && let Some(binding) = bindings.pop() + && let TypeBindingKind::Equality { term } = binding.kind + && let Term::Type(ty) = term + { + ty + } else { + panic!("unexpected async fn return type") + } + } + /// Checks if this is a `T::Name` path for an associated type. pub(crate) fn is_assoc_ty(&self) -> bool { match self { @@ -1636,10 +1646,6 @@ impl Type { matches!(self, Type::Generic(_)) } - pub(crate) fn is_impl_trait(&self) -> bool { - matches!(self, Type::ImplTrait(_)) - } - pub(crate) fn is_unit(&self) -> bool { matches!(self, Type::Tuple(v) if v.is_empty()) } @@ -2086,7 +2092,7 @@ impl Discriminant { /// Will be `None` in the case of cross-crate reexports, and may be /// simplified pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> Option<String> { - self.expr.map(|body| print_const_expr(tcx, body)) + self.expr.map(|body| rendered_const(tcx, body)) } /// Will always be a machine readable number, without underscores or suffixes. pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> String { @@ -2186,16 +2192,6 @@ impl Path { } }) } - - pub(crate) fn bindings(&self) -> Option<&[TypeBinding]> { - self.segments.last().and_then(|seg| { - if let GenericArgs::AngleBracketed { ref bindings, .. } = seg.args { - Some(&**bindings) - } else { - None - } - }) - } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] @@ -2230,9 +2226,19 @@ pub(crate) struct PathSegment { } #[derive(Clone, Debug)] -pub(crate) struct Typedef { +pub(crate) enum TypeAliasInnerType { + Enum { variants: IndexVec<VariantIdx, Item>, is_non_exhaustive: bool }, + Union { fields: Vec<Item> }, + Struct { ctor_kind: Option<CtorKind>, fields: Vec<Item> }, +} + +#[derive(Clone, Debug)] +pub(crate) struct TypeAlias { pub(crate) type_: Type, pub(crate) generics: Generics, + /// Inner `AdtDef` type, ie `type TyKind = IrTyKind<Adt, Ty>`, + /// to be shown directly on the typedef page. + pub(crate) inner_type: Option<TypeAliasInnerType>, /// `type_` can come from either the HIR or from metadata. If it comes from HIR, it may be a type /// alias instead of the final type. This will always have the final type, regardless of whether /// `type_` came from HIR or from metadata. @@ -2326,7 +2332,7 @@ impl ConstantKind { ConstantKind::TyConst { ref expr } => expr.to_string(), ConstantKind::Extern { def_id } => print_inlined_const(tcx, def_id), ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => { - print_const_expr(tcx, body) + rendered_const(tcx, body) } } } @@ -2465,15 +2471,6 @@ pub(crate) enum TypeBindingKind { Constraint { bounds: Vec<GenericBound> }, } -impl TypeBinding { - pub(crate) fn term(&self) -> &Term { - match self.kind { - TypeBindingKind::Equality { ref term } => term, - _ => panic!("expected equality type binding for parenthesized generic args"), - } - } -} - /// The type, lifetime, or constant that a private type alias's parameter should be /// replaced with when expanding a use of that type alias. /// diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index a86bbcc76..61e653423 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -14,8 +14,8 @@ use rustc_ast::tokenstream::TokenTree; use rustc_hir as hir; 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::mir::interpret::ConstValue; use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, TyCtxt}; use rustc_span::symbol::{kw, sym, Symbol}; use std::fmt::Write as _; @@ -104,6 +104,10 @@ pub(crate) fn ty_args_to_args<'tcx>( arg: index, }), ))), + // FIXME(effects): this relies on the host effect being called `host`, which users could also name + // their const generics. + // FIXME(effects): this causes `host = true` and `host = false` generics to also be emitted. + GenericArgKind::Const(ct) if let ty::ConstKind::Param(p) = ct.kind() && p.name == sym::host => None, GenericArgKind::Const(ct) => { Some(GenericArg::Const(Box::new(clean_middle_const(kind.rebind(ct), cx)))) } @@ -153,7 +157,7 @@ pub(super) fn external_path<'tcx>( args: ty::Binder<'tcx, GenericArgsRef<'tcx>>, ) -> Path { let def_kind = cx.tcx.def_kind(did); - let name = cx.tcx.item_name(did); + let name = cx.tcx.opt_item_name(did).unwrap_or(kw::Empty); Path { res: Res::Def(def_kind, did), segments: thin_vec![PathSegment { @@ -255,7 +259,7 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String { match n.kind() { ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args: _ }) => { let s = if let Some(def) = def.as_local() { - print_const_expr(cx.tcx, cx.tcx.hir().body_owned_by(def)) + rendered_const(cx.tcx, cx.tcx.hir().body_owned_by(def)) } else { inline::print_inlined_const(cx.tcx, def) }; @@ -281,9 +285,9 @@ pub(crate) fn print_evaluated_const( let ty = tcx.type_of(def_id).instantiate_identity(); match (val, ty.kind()) { (_, &ty::Ref(..)) => None, - (ConstValue::Scalar(_), &ty::Adt(_, _)) => None, - (ConstValue::Scalar(_), _) => { - let const_ = mir::ConstantKind::from_value(val, ty); + (mir::ConstValue::Scalar(_), &ty::Adt(_, _)) => None, + (mir::ConstValue::Scalar(_), _) => { + let const_ = mir::Const::from_value(val, ty); Some(print_const_with_custom_print_scalar(tcx, const_, underscores_and_type)) } _ => None, @@ -319,20 +323,20 @@ fn format_integer_with_underscore_sep(num: &str) -> String { fn print_const_with_custom_print_scalar<'tcx>( tcx: TyCtxt<'tcx>, - ct: mir::ConstantKind<'tcx>, + ct: mir::Const<'tcx>, underscores_and_type: bool, ) -> String { // Use a slightly different format for integer types which always shows the actual value. // For all other types, fallback to the original `pretty_print_const`. match (ct, ct.ty().kind()) { - (mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Uint(ui)) => { + (mir::Const::Val(mir::ConstValue::Scalar(int), _), ty::Uint(ui)) => { if underscores_and_type { format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str()) } else { int.to_string() } } - (mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Int(i)) => { + (mir::Const::Val(mir::ConstValue::Scalar(int), _), ty::Int(i)) => { let ty = ct.ty(); let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size; let data = int.assert_bits(size); @@ -367,100 +371,6 @@ pub(crate) fn is_literal_expr(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { false } -/// Build a textual representation of an unevaluated constant expression. -/// -/// If the const expression is too complex, an underscore `_` is returned. -/// For const arguments, it's `{ _ }` to be precise. -/// This means that the output is not necessarily valid Rust code. -/// -/// Currently, only -/// -/// * literals (optionally with a leading `-`) -/// * unit `()` -/// * blocks (`{ … }`) around simple expressions and -/// * paths without arguments -/// -/// are considered simple enough. Simple blocks are included since they are -/// necessary to disambiguate unit from the unit type. -/// This list might get extended in the future. -/// -/// Without this censoring, in a lot of cases the output would get too large -/// and verbose. Consider `match` expressions, blocks and deeply nested ADTs. -/// Further, private and `doc(hidden)` fields of structs would get leaked -/// since HIR datatypes like the `body` parameter do not contain enough -/// semantic information for this function to be able to hide them – -/// at least not without significant performance overhead. -/// -/// Whenever possible, prefer to evaluate the constant first and try to -/// use a different method for pretty-printing. Ideally this function -/// should only ever be used as a fallback. -pub(crate) fn print_const_expr(tcx: TyCtxt<'_>, body: hir::BodyId) -> String { - let hir = tcx.hir(); - let value = &hir.body(body).value; - - #[derive(PartialEq, Eq)] - enum Classification { - Literal, - Simple, - Complex, - } - - use Classification::*; - - fn classify(expr: &hir::Expr<'_>) -> Classification { - match &expr.kind { - hir::ExprKind::Unary(hir::UnOp::Neg, expr) => { - if matches!(expr.kind, hir::ExprKind::Lit(_)) { Literal } else { Complex } - } - hir::ExprKind::Lit(_) => Literal, - hir::ExprKind::Tup([]) => Simple, - hir::ExprKind::Block(hir::Block { stmts: [], expr: Some(expr), .. }, _) => { - if classify(expr) == Complex { Complex } else { Simple } - } - // Paths with a self-type or arguments are too “complex” following our measure since - // they may leak private fields of structs (with feature `adt_const_params`). - // Consider: `<Self as Trait<{ Struct { private: () } }>>::CONSTANT`. - // Paths without arguments are definitely harmless though. - hir::ExprKind::Path(hir::QPath::Resolved(_, hir::Path { segments, .. })) => { - if segments.iter().all(|segment| segment.args.is_none()) { Simple } else { Complex } - } - // FIXME: Claiming that those kinds of QPaths are simple is probably not true if the Ty - // contains const arguments. Is there a *concise* way to check for this? - hir::ExprKind::Path(hir::QPath::TypeRelative(..)) => Simple, - // FIXME: Can they contain const arguments and thus leak private struct fields? - hir::ExprKind::Path(hir::QPath::LangItem(..)) => Simple, - _ => Complex, - } - } - - let classification = classify(value); - - if classification == Literal - && !value.span.from_expansion() - && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(value.span) { - // For literals, we avoid invoking the pretty-printer and use the source snippet instead to - // preserve certain stylistic choices the user likely made for the sake legibility like - // - // * hexadecimal notation - // * underscores - // * character escapes - // - // FIXME: This passes through `-/*spacer*/0` verbatim. - snippet - } else if classification == Simple { - // Otherwise we prefer pretty-printing to get rid of extraneous whitespace, comments and - // other formatting artifacts. - rustc_hir_pretty::id_to_string(&hir, body.hir_id) - } else if tcx.def_kind(hir.body_owner_def_id(body).to_def_id()) == DefKind::AnonConst { - // FIXME: Omit the curly braces if the enclosing expression is an array literal - // with a repeated element (an `ExprKind::Repeat`) as in such case it - // would not actually need any disambiguation. - "{ _ }".to_owned() - } else { - "_".to_owned() - } -} - /// Given a type Path, resolve it to a Type using the TyCtxt pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type { debug!("resolve_type({path:?})"); |