diff options
Diffstat (limited to 'src/librustdoc')
27 files changed, 589 insertions, 549 deletions
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 909b81a723b..27ae0553c60 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -9,7 +9,7 @@ path = "lib.rs" [dependencies] arrayvec = { version = "0.7", default-features = false } -rinja = { version = "0.3", default-features = false, features = ["config"] } +askama = { version = "0.13", default-features = false, features = ["alloc", "config", "derive"] } base64 = "0.21.7" itertools = "0.12" indexmap = "2" diff --git a/src/librustdoc/rinja.toml b/src/librustdoc/askama.toml index 2732c4bc61e..2732c4bc61e 100644 --- a/src/librustdoc/rinja.toml +++ b/src/librustdoc/askama.toml diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index a48f5c623cd..138ac3c97f7 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -182,7 +182,7 @@ fn clean_param_env<'tcx>( .is_some_and(|pred| tcx.lang_items().sized_trait() == Some(pred.def_id())) }) .map(|pred| { - fold_regions(tcx, pred, |r, _| match *r { + fold_regions(tcx, pred, |r, _| match r.kind() { // FIXME: Don't `unwrap_or`, I think we should panic if we encounter an infer var that // we can't map to a concrete region. However, `AutoTraitFinder` *does* leak those kinds // of `ReVar`s for some reason at the time of writing. See `rustdoc-ui/` tests. @@ -362,7 +362,7 @@ fn clean_region_outlives_constraints<'tcx>( } fn early_bound_region_name(region: Region<'_>) -> Option<Symbol> { - match *region { + match region.kind() { ty::ReEarlyParam(r) => Some(r.name), _ => None, } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index e0e09b53fc2..55a116a018a 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -67,9 +67,13 @@ pub(crate) fn try_inline( record_extern_fqn(cx, did, ItemType::Trait); cx.with_param_env(did, |cx| { build_impls(cx, did, attrs_without_docs, &mut ret); - clean::TraitItem(Box::new(build_external_trait(cx, did))) + clean::TraitItem(Box::new(build_trait(cx, did))) }) } + Res::Def(DefKind::TraitAlias, did) => { + record_extern_fqn(cx, did, ItemType::TraitAlias); + cx.with_param_env(did, |cx| clean::TraitAliasItem(build_trait_alias(cx, did))) + } Res::Def(DefKind::Fn, did) => { record_extern_fqn(cx, did, ItemType::Function); cx.with_param_env(did, |cx| { @@ -251,7 +255,7 @@ pub(crate) fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: ItemT } } -pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean::Trait { +pub(crate) fn build_trait(cx: &mut DocContext<'_>, did: DefId) -> clean::Trait { let trait_items = cx .tcx .associated_items(did) @@ -263,11 +267,18 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean let predicates = cx.tcx.predicates_of(did); let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates); let generics = filter_non_trait_generics(did, generics); - let (generics, supertrait_bounds) = separate_supertrait_bounds(generics); + let (generics, supertrait_bounds) = separate_self_bounds(generics); clean::Trait { def_id: did, generics, items: trait_items, bounds: supertrait_bounds } } -pub(crate) fn build_function(cx: &mut DocContext<'_>, def_id: DefId) -> Box<clean::Function> { +fn build_trait_alias(cx: &mut DocContext<'_>, did: DefId) -> clean::TraitAlias { + let predicates = cx.tcx.predicates_of(did); + let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates); + let (generics, bounds) = separate_self_bounds(generics); + clean::TraitAlias { generics, bounds } +} + +pub(super) fn build_function(cx: &mut DocContext<'_>, def_id: DefId) -> Box<clean::Function> { let sig = cx.tcx.fn_sig(def_id).instantiate_identity(); // The generics need to be cleaned before the signature. let mut generics = @@ -490,17 +501,17 @@ pub(crate) fn build_impl( return true; } if let Some(associated_trait) = associated_trait { - let assoc_kind = match item.kind { - hir::ImplItemKind::Const(..) => ty::AssocKind::Const, - hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn, - hir::ImplItemKind::Type(..) => ty::AssocKind::Type, + let assoc_tag = match item.kind { + hir::ImplItemKind::Const(..) => ty::AssocTag::Const, + hir::ImplItemKind::Fn(..) => ty::AssocTag::Fn, + hir::ImplItemKind::Type(..) => ty::AssocTag::Type, }; let trait_item = tcx .associated_items(associated_trait.def_id) - .find_by_name_and_kind( + .find_by_ident_and_kind( tcx, item.ident, - assoc_kind, + assoc_tag, associated_trait.def_id, ) .unwrap(); // SAFETY: For all impl items there exists trait item that has the same name. @@ -524,10 +535,10 @@ pub(crate) fn build_impl( if let Some(associated_trait) = associated_trait { let trait_item = tcx .associated_items(associated_trait.def_id) - .find_by_name_and_kind( + .find_by_ident_and_kind( tcx, item.ident(tcx), - item.kind, + item.as_tag(), associated_trait.def_id, ) .unwrap(); // corresponding associated item has to exist @@ -788,12 +799,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean: g } -/// Supertrait bounds for a trait are also listed in the generics coming from -/// the metadata for a crate, so we want to separate those out and create a new -/// list of explicit supertrait bounds to render nicely. -fn separate_supertrait_bounds( - mut g: clean::Generics, -) -> (clean::Generics, Vec<clean::GenericBound>) { +fn separate_self_bounds(mut g: clean::Generics) -> (clean::Generics, Vec<clean::GenericBound>) { let mut ty_bounds = Vec::new(); g.where_predicates.retain(|pred| match *pred { clean::WherePredicate::BoundPredicate { ty: clean::SelfTy, ref bounds, .. } => { @@ -806,22 +812,17 @@ fn separate_supertrait_bounds( } pub(crate) fn record_extern_trait(cx: &mut DocContext<'_>, did: DefId) { - if did.is_local() { - return; - } - + if did.is_local() + || cx.external_traits.contains_key(&did) + || cx.active_extern_traits.contains(&did) { - if cx.external_traits.contains_key(&did) || cx.active_extern_traits.contains(&did) { - return; - } + return; } - { - cx.active_extern_traits.insert(did); - } + cx.active_extern_traits.insert(did); debug!("record_extern_trait: {did:?}"); - let trait_ = build_external_trait(cx, did); + let trait_ = build_trait(cx, did); cx.external_traits.insert(did, trait_); cx.active_extern_traits.remove(&did); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c08ae168d69..034ecb2f6c1 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -117,7 +117,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()) + clean_use_statement_inner( + import, + Some(name), + &path, + kind, + cx, + &mut Default::default(), + ) } _ => unreachable!(), } @@ -125,8 +132,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< items.extend(doc.items.values().flat_map(|(item, renamed, _)| { // Now we actually lower the imports, skipping everything else. if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind { - let name = renamed.unwrap_or(kw::Empty); // using kw::Empty is a bit of a hack - clean_use_statement(item, name, path, hir::UseKind::Glob, cx, &mut inserted) + clean_use_statement(item, *renamed, path, hir::UseKind::Glob, cx, &mut inserted) } else { // skip everything else Vec::new() @@ -320,7 +326,7 @@ pub(crate) fn clean_middle_const<'tcx>( } pub(crate) fn clean_middle_region(region: ty::Region<'_>) -> Option<Lifetime> { - match *region { + match region.kind() { ty::ReStatic => Some(Lifetime::statik()), _ if !region.has_name() => None, ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(_, name), .. }) => { @@ -521,7 +527,7 @@ fn projection_to_path_segment<'tcx>( let item = cx.tcx.associated_item(def_id); let generics = cx.tcx.generics_of(def_id); PathSegment { - name: item.name, + name: item.name(), args: GenericArgs::AngleBracketed { args: clean_middle_generic_args( cx, @@ -1046,7 +1052,7 @@ 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)); + let mut func = clean_function(cx, sig, generics, ParamsSrc::Body(body_id)); clean_fn_decl_legacy_const_generics(&mut func, attrs); FunctionItem(func) } @@ -1065,17 +1071,12 @@ fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[hir::Attrib for (pos, literal) in meta_item_list.iter().filter_map(|meta| meta.lit()).enumerate() { match literal.kind { ast::LitKind::Int(a, _) => { - let param = func.generics.params.remove(0); - if let GenericParamDef { - name, - kind: GenericParamDefKind::Const { ty, .. }, - .. - } = param - { - func.decl - .inputs - .values - .insert(a.get() as _, Argument { name, type_: *ty, is_const: true }); + let GenericParamDef { name, kind, .. } = func.generics.params.remove(0); + if let GenericParamDefKind::Const { ty, .. } = kind { + func.decl.inputs.insert( + a.get() as _, + Parameter { name: Some(name), type_: *ty, is_const: true }, + ); } else { panic!("unexpected non const in position {pos}"); } @@ -1086,95 +1087,71 @@ fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[hir::Attrib } } -enum FunctionArgs<'tcx> { +enum ParamsSrc<'tcx> { Body(hir::BodyId), - Names(&'tcx [Option<Ident>]), + Idents(&'tcx [Option<Ident>]), } fn clean_function<'tcx>( cx: &mut DocContext<'tcx>, sig: &hir::FnSig<'tcx>, generics: &hir::Generics<'tcx>, - args: FunctionArgs<'tcx>, + params: ParamsSrc<'tcx>, ) -> Box<Function> { let (generics, decl) = enter_impl_trait(cx, |cx| { - // NOTE: generics must be cleaned before args + // NOTE: Generics must be cleaned before params. 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 params = match params { + ParamsSrc::Body(body_id) => clean_params_via_body(cx, sig.decl.inputs, body_id), + // Let's not perpetuate anon params from Rust 2015; use `_` for them. + ParamsSrc::Idents(idents) => clean_params(cx, sig.decl.inputs, idents, |ident| { + Some(ident.map_or(kw::Underscore, |ident| ident.name)) + }), }; - let decl = clean_fn_decl_with_args(cx, sig.decl, Some(&sig.header), args); + let decl = clean_fn_decl_with_params(cx, sig.decl, Some(&sig.header), params); (generics, decl) }); Box::new(Function { decl, generics }) } -fn clean_args_from_types_and_names<'tcx>( +fn clean_params<'tcx>( cx: &mut DocContext<'tcx>, types: &[hir::Ty<'tcx>], - names: &[Option<Ident>], -) -> Arguments { - fn nonempty_name(ident: &Option<Ident>) -> Option<Symbol> { - if let Some(ident) = ident - && ident.name != kw::Underscore - { - Some(ident.name) - } else { - None - } - } - - // If at least one argument has a name, use `_` as the name of unnamed - // arguments. Otherwise omit argument names. - let default_name = if names.iter().any(|ident| nonempty_name(ident).is_some()) { - kw::Underscore - } else { - kw::Empty - }; - - Arguments { - values: types - .iter() - .enumerate() - .map(|(i, ty)| Argument { - type_: clean_ty(ty, cx), - name: names.get(i).and_then(nonempty_name).unwrap_or(default_name), - is_const: false, - }) - .collect(), - } + idents: &[Option<Ident>], + postprocess: impl Fn(Option<Ident>) -> Option<Symbol>, +) -> Vec<Parameter> { + types + .iter() + .enumerate() + .map(|(i, ty)| Parameter { + name: postprocess(idents[i]), + type_: clean_ty(ty, cx), + is_const: false, + }) + .collect() } -fn clean_args_from_types_and_body_id<'tcx>( +fn clean_params_via_body<'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() - .zip(body.params) - .map(|(ty, param)| Argument { - name: name_from_pat(param.pat), - type_: clean_ty(ty, cx), - is_const: false, - }) - .collect(), - } +) -> Vec<Parameter> { + types + .iter() + .zip(cx.tcx.hir_body(body_id).params) + .map(|(ty, param)| Parameter { + name: Some(name_from_pat(param.pat)), + type_: clean_ty(ty, cx), + is_const: false, + }) + .collect() } -fn clean_fn_decl_with_args<'tcx>( +fn clean_fn_decl_with_params<'tcx>( cx: &mut DocContext<'tcx>, decl: &hir::FnDecl<'tcx>, header: Option<&hir::FnHeader>, - args: Arguments, + params: Vec<Parameter>, ) -> FnDecl { let mut output = match decl.output { hir::FnRetTy::Return(typ) => clean_ty(typ, cx), @@ -1185,7 +1162,7 @@ fn clean_fn_decl_with_args<'tcx>( { output = output.sugared_async_return_type(); } - FnDecl { inputs: args, output, c_variadic: decl.c_variadic } + FnDecl { inputs: params, output, c_variadic: decl.c_variadic } } fn clean_poly_fn_sig<'tcx>( @@ -1193,10 +1170,6 @@ fn clean_poly_fn_sig<'tcx>( did: Option<DefId>, sig: ty::PolyFnSig<'tcx>, ) -> FnDecl { - let mut names = did.map_or(&[] as &[_], |did| cx.tcx.fn_arg_names(did)).iter(); - - // 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); // If the return type isn't an `impl Trait`, we can safely assume that this @@ -1209,25 +1182,25 @@ fn clean_poly_fn_sig<'tcx>( output = output.sugared_async_return_type(); } - FnDecl { - output, - c_variadic: sig.skip_binder().c_variadic, - inputs: Arguments { - values: sig - .inputs() - .iter() - .map(|t| Argument { - type_: clean_middle_ty(t.map_bound(|t| *t), cx, None, None), - name: if let Some(Some(ident)) = names.next() { - ident.name - } else { - kw::Underscore - }, - is_const: false, - }) - .collect(), - }, - } + let mut idents = did.map(|did| cx.tcx.fn_arg_idents(did)).unwrap_or_default().iter().copied(); + + // If this comes from a fn item, let's not perpetuate anon params from Rust 2015; use `_` for them. + // If this comes from a fn ptr ty, we just keep params unnamed since it's more conventional stylistically. + // Since the param name is not part of the semantic type, these params never bear a name unlike + // in the HIR case, thus we can't peform any fancy fallback logic unlike `clean_bare_fn_ty`. + let fallback = did.map(|_| kw::Underscore); + + let params = sig + .inputs() + .iter() + .map(|ty| Parameter { + name: idents.next().flatten().map(|ident| ident.name).or(fallback), + type_: clean_middle_ty(ty.map_bound(|ty| *ty), cx, None, None), + is_const: false, + }) + .collect(); + + FnDecl { inputs: params, output, c_variadic: sig.skip_binder().c_variadic } } fn clean_trait_ref<'tcx>(trait_ref: &hir::TraitRef<'tcx>, cx: &mut DocContext<'tcx>) -> Path { @@ -1267,11 +1240,11 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext RequiredAssocConstItem(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_function(cx, sig, trait_item.generics, ParamsSrc::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)); + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(idents)) => { + let m = clean_function(cx, sig, trait_item.generics, ParamsSrc::Idents(idents)); RequiredMethodItem(m) } hir::TraitItemKind::Type(bounds, Some(default)) => { @@ -1312,7 +1285,7 @@ pub(crate) fn clean_impl_item<'tcx>( type_: clean_ty(ty, cx), })), hir::ImplItemKind::Fn(ref sig, body) => { - let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body)); + let m = clean_function(cx, sig, impl_.generics, ParamsSrc::Body(body)); let defaultness = cx.tcx.defaultness(impl_.owner_id); MethodItem(m, Some(defaultness)) } @@ -1340,7 +1313,7 @@ pub(crate) fn clean_impl_item<'tcx>( pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocContext<'_>) -> Item { let tcx = cx.tcx; let kind = match assoc_item.kind { - ty::AssocKind::Const => { + ty::AssocKind::Const { .. } => { let ty = clean_middle_ty( ty::Binder::dummy(tcx.type_of(assoc_item.def_id).instantiate_identity()), cx, @@ -1374,24 +1347,24 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo } } } - ty::AssocKind::Fn => { + ty::AssocKind::Fn { has_self, .. } => { let mut item = inline::build_function(cx, assoc_item.def_id); - if assoc_item.fn_has_self_parameter { + if has_self { let self_ty = match assoc_item.container { ty::AssocItemContainer::Impl => { tcx.type_of(assoc_item.container_id(tcx)).instantiate_identity() } ty::AssocItemContainer::Trait => tcx.types.self_param, }; - let self_arg_ty = + let self_param_ty = tcx.fn_sig(assoc_item.def_id).instantiate_identity().input(0).skip_binder(); - if self_arg_ty == self_ty { - item.decl.inputs.values[0].type_ = SelfTy; - } else if let ty::Ref(_, ty, _) = *self_arg_ty.kind() + if self_param_ty == self_ty { + item.decl.inputs[0].type_ = SelfTy; + } else if let ty::Ref(_, ty, _) = *self_param_ty.kind() && ty == self_ty { - match item.decl.inputs.values[0].type_ { + match item.decl.inputs[0].type_ { BorrowedRef { ref mut type_, .. } => **type_ = SelfTy, _ => unreachable!(), } @@ -1412,8 +1385,8 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo RequiredMethodItem(item) } } - ty::AssocKind::Type => { - let my_name = assoc_item.name; + ty::AssocKind::Type { .. } => { + let my_name = assoc_item.name(); fn param_eq_arg(param: &GenericParamDef, arg: &GenericArg) -> bool { match (¶m.kind, arg) { @@ -1554,7 +1527,7 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo } }; - Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name), kind, cx) + Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name()), kind, cx) } fn first_non_private_clean_path<'tcx>( @@ -1941,7 +1914,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`. - match *region { + match region.kind() { ty::ReStatic => Some(Lifetime::statik()), ty::ReEarlyParam(region) => Some(Lifetime(region.name)), ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(_, name), .. }) => { @@ -1972,7 +1945,7 @@ fn can_elide_trait_object_lifetime_bound<'tcx>( // > If there is a unique bound from the containing type then that is the default // If there is a default object lifetime and the given region is lexically equal to it, elide it. match default { - ObjectLifetimeDefault::Static => return *region == ty::ReStatic, + ObjectLifetimeDefault::Static => return region.kind() == ty::ReStatic, // FIXME(fmease): Don't compare lexically but respect de Bruijn indices etc. to handle shadowing correctly. ObjectLifetimeDefault::Arg(default) => return region.get_name() == default.get_name(), // > If there is more than one bound from the containing type then an explicit bound must be specified @@ -1992,7 +1965,7 @@ fn can_elide_trait_object_lifetime_bound<'tcx>( // Note however that at the time of this writing it should be fine to disregard this subtlety // as we neither render const exprs faithfully anyway (hiding them in some places or using `_` instead) // nor show the contents of fn bodies. - [] => *region == ty::ReStatic, + [] => region.kind() == ty::ReStatic, // > If the trait is defined with a single lifetime bound then that bound is used. // > If 'static is used for any lifetime bound then 'static is used. // FIXME(fmease): Don't compare lexically but respect de Bruijn indices etc. to handle shadowing correctly. @@ -2223,7 +2196,7 @@ pub(crate) fn clean_middle_ty<'tcx>( Type::QPath(Box::new(QPathData { assoc: PathSegment { - name: cx.tcx.associated_item(def_id).name, + name: cx.tcx.associated_item(def_id).name(), args: GenericArgs::AngleBracketed { args: clean_middle_generic_args( cx, @@ -2605,15 +2578,25 @@ fn clean_bare_fn_ty<'tcx>( cx: &mut DocContext<'tcx>, ) -> BareFunctionDecl { let (generic_params, decl) = enter_impl_trait(cx, |cx| { - // NOTE: generics must be cleaned before args + // NOTE: Generics must be cleaned before params. 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); + // Since it's more conventional stylistically, elide the name of all params called `_` + // unless there's at least one interestingly named param in which case don't elide any + // name since mixing named and unnamed params is less legible. + let filter = |ident: Option<Ident>| { + ident.map(|ident| ident.name).filter(|&ident| ident != kw::Underscore) + }; + let fallback = + bare_fn.param_idents.iter().copied().find_map(filter).map(|_| kw::Underscore); + let params = clean_params(cx, bare_fn.decl.inputs, bare_fn.param_idents, |ident| { + filter(ident).or(fallback) + }); + let decl = clean_fn_decl_with_params(cx, bare_fn.decl, None, params); (generic_params, decl) }); BareFunctionDecl { safety: bare_fn.safety, abi: bare_fn.abi, decl, generic_params } @@ -2623,7 +2606,6 @@ fn clean_unsafe_binder_ty<'tcx>( unsafe_binder_ty: &hir::UnsafeBinderTy<'tcx>, cx: &mut DocContext<'tcx>, ) -> UnsafeBinderTy { - // NOTE: generics must be cleaned before args let generic_params = unsafe_binder_ty .generic_params .iter() @@ -2792,10 +2774,7 @@ fn clean_maybe_renamed_item<'tcx>( use hir::ItemKind; let def_id = item.owner_id.to_def_id(); - let mut name = renamed.unwrap_or_else(|| { - // FIXME: using kw::Empty is a bit of a hack - cx.tcx.hir_opt_name(item.hir_id()).unwrap_or(kw::Empty) - }); + let mut name = if renamed.is_some() { renamed } else { cx.tcx.hir_opt_name(item.hir_id()) }; cx.with_param_env(def_id, |cx| { let kind = match item.kind { @@ -2836,7 +2815,7 @@ fn clean_maybe_renamed_item<'tcx>( item_type: Some(type_), })), item.owner_id.def_id.to_def_id(), - name, + name.unwrap(), import_id, renamed, )); @@ -2861,13 +2840,15 @@ fn clean_maybe_renamed_item<'tcx>( }), ItemKind::Impl(impl_) => return clean_impl(impl_, item.owner_id.def_id, cx), ItemKind::Macro(_, macro_def, MacroKind::Bang) => MacroItem(Macro { - source: display_macro_source(cx, name, macro_def), + source: display_macro_source(cx, name.unwrap(), macro_def), macro_rules: macro_def.macro_rules, }), - ItemKind::Macro(_, _, macro_kind) => clean_proc_macro(item, &mut name, macro_kind, cx), + ItemKind::Macro(_, _, macro_kind) => { + clean_proc_macro(item, name.as_mut().unwrap(), macro_kind, cx) + } // proc macros can have a name set by attributes ItemKind::Fn { ref sig, generics, body: body_id, .. } => { - clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx) + clean_fn_or_proc_macro(item, sig, generics, body_id, name.as_mut().unwrap(), cx) } ItemKind::Trait(_, _, _, generics, bounds, item_ids) => { let items = item_ids @@ -2883,7 +2864,7 @@ fn clean_maybe_renamed_item<'tcx>( })) } ItemKind::ExternCrate(orig_name, _) => { - return clean_extern_crate(item, name, orig_name, cx); + return clean_extern_crate(item, name.unwrap(), orig_name, cx); } ItemKind::Use(path, kind) => { return clean_use_statement(item, name, path, kind, cx, &mut FxHashSet::default()); @@ -2895,7 +2876,7 @@ fn clean_maybe_renamed_item<'tcx>( cx, kind, item.owner_id.def_id.to_def_id(), - name, + name.unwrap(), import_id, renamed, )] @@ -3006,7 +2987,7 @@ fn clean_extern_crate<'tcx>( fn clean_use_statement<'tcx>( import: &hir::Item<'tcx>, - name: Symbol, + name: Option<Symbol>, path: &hir::UsePath<'tcx>, kind: hir::UseKind, cx: &mut DocContext<'tcx>, @@ -3023,7 +3004,7 @@ fn clean_use_statement<'tcx>( fn clean_use_statement_inner<'tcx>( import: &hir::Item<'tcx>, - name: Symbol, + name: Option<Symbol>, path: &hir::Path<'tcx>, kind: hir::UseKind, cx: &mut DocContext<'tcx>, @@ -3042,7 +3023,7 @@ fn clean_use_statement_inner<'tcx>( let visibility = cx.tcx.visibility(import.owner_id); let attrs = cx.tcx.hir_attrs(import.hir_id()); let inline_attr = hir_attr_lists(attrs, sym::doc).get_word_attr(sym::inline); - let pub_underscore = visibility.is_public() && name == kw::Underscore; + let pub_underscore = visibility.is_public() && name == Some(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; @@ -3108,6 +3089,7 @@ fn clean_use_statement_inner<'tcx>( } Import::new_glob(resolve_use_source(cx, path), true) } else { + let name = name.unwrap(); if inline_attr.is_none() && let Res::Def(DefKind::Mod, did) = path.res && !did.is_local() @@ -3148,8 +3130,8 @@ fn clean_maybe_renamed_foreign_item<'tcx>( let def_id = item.owner_id.to_def_id(); cx.with_param_env(def_id, |cx| { let kind = match item.kind { - hir::ForeignItemKind::Fn(sig, names, generics) => ForeignFunctionItem( - clean_function(cx, &sig, generics, FunctionArgs::Names(names)), + hir::ForeignItemKind::Fn(sig, idents, generics) => ForeignFunctionItem( + clean_function(cx, &sig, generics, ParamsSrc::Idents(idents)), sig.header.safety(), ), hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem( diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index d3ddb77c0b3..bbe11bf56af 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -11,7 +11,6 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; 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::span_bug; @@ -518,7 +517,7 @@ impl Item { Some(RenderedLink { original_text: s.clone(), new_text: link_text.clone(), - tooltip: link_tooltip(*id, fragment, cx), + tooltip: link_tooltip(*id, fragment, cx).to_string(), href, }) } else { @@ -687,8 +686,6 @@ impl Item { hir::FnHeader { safety: if tcx.codegen_fn_attrs(def_id).safe_target_features { hir::HeaderSafety::SafeTargetFeatures - } else if abi == ExternAbi::RustIntrinsic { - intrinsic_operation_unsafety(tcx, def_id.expect_local()).into() } else { safety.into() }, @@ -791,7 +788,7 @@ impl Item { } _ => Some(rustc_hir_pretty::attribute_to_string(&tcx, attr)), } - } else if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) { + } else if attr.has_any_name(ALLOWED_ATTRIBUTES) { Some( rustc_hir_pretty::attribute_to_string(&tcx, attr) .replace("\\\n", "") @@ -1410,34 +1407,30 @@ pub(crate) struct Function { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct FnDecl { - pub(crate) inputs: Arguments, + pub(crate) inputs: Vec<Parameter>, pub(crate) output: Type, pub(crate) c_variadic: bool, } impl FnDecl { pub(crate) fn receiver_type(&self) -> Option<&Type> { - self.inputs.values.first().and_then(|v| v.to_receiver()) + self.inputs.first().and_then(|v| v.to_receiver()) } } +/// A function parameter. #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub(crate) struct Arguments { - pub(crate) values: Vec<Argument>, -} - -#[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub(crate) struct Argument { +pub(crate) struct Parameter { + pub(crate) name: Option<Symbol>, pub(crate) type_: Type, - pub(crate) name: Symbol, /// This field is used to represent "const" arguments from the `rustc_legacy_const_generics` /// feature. More information in <https://github.com/rust-lang/rust/issues/83167>. pub(crate) is_const: bool, } -impl Argument { +impl Parameter { pub(crate) fn to_receiver(&self) -> Option<&Type> { - if self.name == kw::SelfLower { Some(&self.type_) } else { None } + if self.name == Some(kw::SelfLower) { Some(&self.type_) } else { None } } } @@ -2507,7 +2500,7 @@ impl Impl { self.trait_ .as_ref() .map(|t| t.def_id()) - .map(|did| tcx.provided_trait_methods(did).map(|meth| meth.name).collect()) + .map(|did| tcx.provided_trait_methods(did).map(|meth| meth.name()).collect()) .unwrap_or_default() } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index f81db58950c..af7986d030e 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -234,7 +234,7 @@ pub(super) fn clean_middle_path<'tcx>( 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); + let name = cx.tcx.opt_item_name(did).unwrap_or(sym::dummy); Path { res: Res::Def(def_kind, did), segments: thin_vec![PathSegment { @@ -303,12 +303,12 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { debug!("trying to get a name from pattern: {p:?}"); Symbol::intern(&match &p.kind { - // FIXME(never_patterns): does this make sense? - PatKind::Wild - | PatKind::Err(_) + PatKind::Err(_) + | PatKind::Missing // Let's not perpetuate anon params from Rust 2015; use `_` for them. | PatKind::Never + | PatKind::Range(..) | PatKind::Struct(..) - | PatKind::Range(..) => { + | PatKind::Wild => { return kw::Underscore; } PatKind::Binding(_, _, ident, _) => return ident.name, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index c47e42670c9..9d1c9ff00b1 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -27,7 +27,7 @@ use rustc_span::source_map; use rustc_span::symbol::sym; use tracing::{debug, info}; -use crate::clean::inline::build_external_trait; +use crate::clean::inline::build_trait; use crate::clean::{self, ItemId}; use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions}; use crate::formats::cache::Cache; @@ -321,6 +321,7 @@ pub(crate) fn create_config( (rustc_interface::DEFAULT_QUERY_PROVIDERS.typeck)(tcx, def_id) }; }), + extra_symbols: Vec::new(), make_codegen_backend: None, registry: rustc_driver::diagnostics_registry(), ice_file: None, @@ -384,7 +385,7 @@ pub(crate) fn run_global_ctxt( // // Note that in case of `#![no_core]`, the trait is not available. if let Some(sized_trait_did) = ctxt.tcx.lang_items().sized_trait() { - let sized_trait = build_external_trait(&mut ctxt, sized_trait_did); + let sized_trait = build_trait(&mut ctxt, sized_trait_did); ctxt.external_traits.insert(sized_trait_did, sized_trait); } @@ -411,9 +412,7 @@ pub(crate) fn run_global_ctxt( // Process all of the crate attributes, extracting plugin metadata along // with the passes which we are supposed to run. for attr in krate.module.attrs.lists(sym::doc) { - let name = attr.name_or_empty(); - - if attr.is_word() && name == sym::document_private_items { + if attr.is_word() && attr.has_name(sym::document_private_items) { ctxt.render_options.document_private = true; } } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index a2808bddb3a..88eaa52c6de 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -191,6 +191,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions hash_untracked_state: None, register_lints: Some(Box::new(crate::lint::register_lints)), override_queries: None, + extra_symbols: Vec::new(), make_codegen_backend: None, registry: rustc_driver::diagnostics_registry(), ice_file: None, diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 4edd5433de6..d5c965f7053 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -345,7 +345,7 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn fn check_item(item: &ast::Item, info: &mut ParseSourceInfo, crate_name: &Option<&str>) -> bool { let mut is_extern_crate = false; if !info.has_global_allocator - && item.attrs.iter().any(|attr| attr.name_or_empty() == sym::global_allocator) + && item.attrs.iter().any(|attr| attr.has_name(sym::global_allocator)) { info.has_global_allocator = true; } @@ -377,7 +377,7 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn } let mut prev_span_hi = 0; - let not_crate_attrs = [sym::forbid, sym::allow, sym::warn, sym::deny, sym::expect]; + let not_crate_attrs = &[sym::forbid, sym::allow, sym::warn, sym::deny, sym::expect]; let parsed = parser.parse_item(rustc_parse::parser::ForceCollect::No); let result = match parsed { @@ -386,17 +386,13 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn && let Some(ref body) = fn_item.body => { for attr in &item.attrs { - let attr_name = attr.name_or_empty(); - - if attr.style == AttrStyle::Outer || not_crate_attrs.contains(&attr_name) { + if attr.style == AttrStyle::Outer || attr.has_any_name(not_crate_attrs) { // There is one exception to these attributes: // `#![allow(internal_features)]`. If this attribute is used, we need to // consider it only as a crate-level attribute. - if attr_name == sym::allow + if attr.has_name(sym::allow) && let Some(list) = attr.meta_item_list() - && list.iter().any(|sub_attr| { - sub_attr.name_or_empty().as_str() == "internal_features" - }) + && list.iter().any(|sub_attr| sub_attr.has_name(sym::internal_features)) { push_to_s(&mut info.crate_attrs, source, attr.span, &mut prev_span_hi); } else { diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 41e9a5a6651..299fd6b9adb 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -11,6 +11,7 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::fmt::{self, Display, Write}; use std::iter::{self, once}; +use std::slice; use itertools::Either; use rustc_abi::ExternAbi; @@ -650,33 +651,35 @@ pub(crate) fn href_relative_parts<'fqp>( } } -pub(crate) fn link_tooltip(did: DefId, fragment: &Option<UrlFragment>, cx: &Context<'_>) -> String { - let cache = cx.cache(); - let Some((fqp, shortty)) = cache.paths.get(&did).or_else(|| cache.external_paths.get(&did)) - else { - return String::new(); - }; - let mut buf = String::new(); - let fqp = if *shortty == ItemType::Primitive { - // primitives are documented in a crate, but not actually part of it - &fqp[fqp.len() - 1..] - } else { - fqp - }; - if let &Some(UrlFragment::Item(id)) = fragment { - write_str(&mut buf, format_args!("{} ", cx.tcx().def_descr(id))); - for component in fqp { - write_str(&mut buf, format_args!("{component}::")); - } - write_str(&mut buf, format_args!("{}", cx.tcx().item_name(id))); - } else if !fqp.is_empty() { - let mut fqp_it = fqp.iter(); - write_str(&mut buf, format_args!("{shortty} {}", fqp_it.next().unwrap())); - for component in fqp_it { - write_str(&mut buf, format_args!("::{component}")); +pub(crate) fn link_tooltip( + did: DefId, + fragment: &Option<UrlFragment>, + cx: &Context<'_>, +) -> impl fmt::Display { + fmt::from_fn(move |f| { + let cache = cx.cache(); + let Some((fqp, shortty)) = cache.paths.get(&did).or_else(|| cache.external_paths.get(&did)) + else { + return Ok(()); + }; + let fqp = if *shortty == ItemType::Primitive { + // primitives are documented in a crate, but not actually part of it + slice::from_ref(fqp.last().unwrap()) + } else { + fqp + }; + if let &Some(UrlFragment::Item(id)) = fragment { + write!(f, "{} ", cx.tcx().def_descr(id))?; + for component in fqp { + write!(f, "{component}::")?; + } + write!(f, "{}", cx.tcx().item_name(id))?; + } else if !fqp.is_empty() { + write!(f, "{shortty} ")?; + fqp.iter().joined("::", f)?; } - } - buf + Ok(()) + }) } /// Used to render a [`clean::Path`]. @@ -1183,8 +1186,8 @@ impl clean::Impl { { primitive_link(f, PrimitiveType::Array, format_args!("[{name}; N]"), cx)?; } else if let clean::BareFunction(bare_fn) = &type_ - && let [clean::Argument { type_: clean::Type::Generic(name), .. }] = - &bare_fn.decl.inputs.values[..] + && let [clean::Parameter { type_: clean::Type::Generic(name), .. }] = + &bare_fn.decl.inputs[..] && (self.kind.is_fake_variadic() || self.kind.is_auto()) { // Hardcoded anchor library/core/src/primitive_docs.rs @@ -1231,22 +1234,20 @@ impl clean::Impl { } } -impl clean::Arguments { - pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { - fmt::from_fn(move |f| { - self.values - .iter() - .map(|input| { - fmt::from_fn(|f| { - if !input.name.is_empty() { - write!(f, "{}: ", input.name)?; - } - input.type_.print(cx).fmt(f) - }) +pub(crate) fn print_params(params: &[clean::Parameter], cx: &Context<'_>) -> impl Display { + fmt::from_fn(move |f| { + params + .iter() + .map(|param| { + fmt::from_fn(|f| { + if let Some(name) = param.name { + write!(f, "{}: ", name)?; + } + param.type_.print(cx).fmt(f) }) - .joined(", ", f) - }) - } + }) + .joined(", ", f) + }) } // Implements Write but only counts the bytes "written". @@ -1278,16 +1279,16 @@ impl clean::FnDecl { if f.alternate() { write!( f, - "({args:#}{ellipsis}){arrow:#}", - args = self.inputs.print(cx), + "({params:#}{ellipsis}){arrow:#}", + params = print_params(&self.inputs, cx), ellipsis = ellipsis, arrow = self.print_output(cx) ) } else { write!( f, - "({args}{ellipsis}){arrow}", - args = self.inputs.print(cx), + "({params}{ellipsis}){arrow}", + params = print_params(&self.inputs, cx), ellipsis = ellipsis, arrow = self.print_output(cx) ) @@ -1333,14 +1334,14 @@ impl clean::FnDecl { write!(f, "(")?; if let Some(n) = line_wrapping_indent - && !self.inputs.values.is_empty() + && !self.inputs.is_empty() { write!(f, "\n{}", Indent(n + 4))?; } - let last_input_index = self.inputs.values.len().checked_sub(1); - for (i, input) in self.inputs.values.iter().enumerate() { - if let Some(selfty) = input.to_receiver() { + let last_input_index = self.inputs.len().checked_sub(1); + for (i, param) in self.inputs.iter().enumerate() { + if let Some(selfty) = param.to_receiver() { match selfty { clean::SelfTy => { write!(f, "self")?; @@ -1358,11 +1359,13 @@ impl clean::FnDecl { } } } else { - if input.is_const { + if param.is_const { write!(f, "const ")?; } - write!(f, "{}: ", input.name)?; - input.type_.print(cx).fmt(f)?; + if let Some(name) = param.name { + write!(f, "{}: ", name)?; + } + param.type_.print(cx).fmt(f)?; } match (line_wrapping_indent, last_input_index) { (_, None) => (), diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index df70df062fe..44b3be23914 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -1,7 +1,7 @@ use std::fmt::{self, Display}; use std::path::PathBuf; -use rinja::Template; +use askama::Template; use rustc_data_structures::fx::FxIndexMap; use super::static_files::{STATIC_FILES, StaticFiles}; diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 334a7a86989..44134bda5ea 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -721,7 +721,7 @@ pub(crate) fn find_codes<T: doctest::DocTestVisitor>( extra_info: Option<&ExtraInfo<'_>>, include_non_rust: bool, ) { - let mut parser = Parser::new(doc).into_offset_iter(); + let mut parser = Parser::new_ext(doc, main_body_opts()).into_offset_iter(); let mut prev_offset = 0; let mut nb_lines = 0; let mut register_header = None; diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 5f69e79f3ab..f22935df96c 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -5,7 +5,7 @@ use std::io; use std::path::{Path, PathBuf}; use std::sync::mpsc::{Receiver, channel}; -use rinja::Template; +use askama::Template; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE}; use rustc_middle::ty::TyCtxt; @@ -521,23 +521,23 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { // Crawl the crate attributes looking for attributes which control how we're // going to emit HTML for attr in krate.module.attrs.lists(sym::doc) { - match (attr.name_or_empty(), attr.value_str()) { - (sym::html_favicon_url, Some(s)) => { + match (attr.name(), attr.value_str()) { + (Some(sym::html_favicon_url), Some(s)) => { layout.favicon = s.to_string(); } - (sym::html_logo_url, Some(s)) => { + (Some(sym::html_logo_url), Some(s)) => { layout.logo = s.to_string(); } - (sym::html_playground_url, Some(s)) => { + (Some(sym::html_playground_url), Some(s)) => { playground = Some(markdown::Playground { crate_name: Some(krate.name(tcx)), url: s.to_string(), }); } - (sym::issue_tracker_base_url, Some(s)) => { + (Some(sym::issue_tracker_base_url), Some(s)) => { issue_tracker_base_url = Some(s.to_string()); } - (sym::html_no_source, None) if attr.is_word() => { + (Some(sym::html_no_source), None) if attr.is_word() => { include_sources = false; } _ => {} @@ -650,15 +650,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { bar.render_into(&mut sidebar).unwrap(); - let v = layout::render( - &shared.layout, - &page, - sidebar, - BufDisplay(|buf: &mut String| { - all.print(buf); - }), - &shared.style_files, - ); + let v = layout::render(&shared.layout, &page, sidebar, all.print(), &shared.style_files); shared.fs.write(final_file, v)?; // if to avoid writing help, settings files to doc root unless we're on the final invocation diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 2237e0f987b..7e17f09aecd 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -13,6 +13,9 @@ //! is cloned per-thread and contains information about what is currently being //! rendered. //! +//! The main entry point to the rendering system is the implementation of +//! `FormatRenderer` on `Context`. +//! //! In order to speed up rendering (mostly because of markdown rendering), the //! rendering process has been parallelized. This parallelization is only //! exposed through the `crate` method on the context, and then also from the @@ -37,13 +40,15 @@ mod span_map; mod type_layout; mod write_shared; +use std::borrow::Cow; use std::collections::VecDeque; use std::fmt::{self, Display as _, Write}; use std::iter::Peekable; use std::path::PathBuf; use std::{fs, str}; -use rinja::Template; +use askama::Template; +use itertools::Either; use rustc_attr_parsing::{ ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince, }; @@ -90,15 +95,28 @@ pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display { /// Specifies whether rendering directly implemented trait items or ones from a certain Deref /// impl. #[derive(Copy, Clone, Debug)] -pub(crate) enum AssocItemRender<'a> { +enum AssocItemRender<'a> { All, DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool }, } +impl AssocItemRender<'_> { + fn render_mode(&self) -> RenderMode { + match self { + Self::All => RenderMode::Normal, + &Self::DerefFor { deref_mut_, .. } => RenderMode::ForDeref { mut_: deref_mut_ }, + } + } + + fn class(&self) -> Option<&'static str> { + if let Self::DerefFor { .. } = self { Some("impl-items") } else { None } + } +} + /// For different handling of associated items from the Deref target of a type rather than the type /// itself. #[derive(Copy, Clone, PartialEq)] -pub(crate) enum RenderMode { +enum RenderMode { Normal, ForDeref { mut_: bool }, } @@ -126,7 +144,7 @@ pub(crate) struct IndexItem { /// A type used for the search index. #[derive(Debug, Eq, PartialEq)] -pub(crate) struct RenderType { +struct RenderType { id: Option<RenderTypeId>, generics: Option<Vec<RenderType>>, bindings: Option<Vec<(RenderTypeId, Vec<RenderType>)>>, @@ -137,7 +155,7 @@ impl RenderType { // The contents of the lists are always integers in self-terminating hex // form, handled by `RenderTypeId::write_to_string`, so no commas are // needed to separate the items. - pub fn write_to_string(&self, string: &mut String) { + fn write_to_string(&self, string: &mut String) { fn write_optional_id(id: Option<RenderTypeId>, string: &mut String) { // 0 is a sentinel, everything else is one-indexed match id { @@ -177,7 +195,7 @@ impl RenderType { } #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub(crate) enum RenderTypeId { +enum RenderTypeId { DefId(DefId), Primitive(clean::PrimitiveType), AssociatedType(Symbol), @@ -186,7 +204,7 @@ pub(crate) enum RenderTypeId { } impl RenderTypeId { - pub fn write_to_string(&self, string: &mut String) { + fn write_to_string(&self, string: &mut String) { let id: i32 = match &self { // 0 is a sentinel, everything else is one-indexed // concrete type @@ -205,11 +223,11 @@ pub(crate) struct IndexItemFunctionType { inputs: Vec<RenderType>, output: Vec<RenderType>, where_clause: Vec<Vec<RenderType>>, - param_names: Vec<Symbol>, + param_names: Vec<Option<Symbol>>, } impl IndexItemFunctionType { - pub fn write_to_string<'a>( + fn write_to_string<'a>( &'a self, string: &mut String, backref_queue: &mut VecDeque<&'a IndexItemFunctionType>, @@ -309,7 +327,7 @@ impl ItemEntry { } impl ItemEntry { - pub(crate) fn print(&self) -> impl fmt::Display { + fn print(&self) -> impl fmt::Display { fmt::from_fn(move |f| write!(f, "<a href=\"{}\">{}</a>", self.url, Escape(&self.name))) } } @@ -436,44 +454,49 @@ impl AllTypes { sections } - fn print(&self, f: &mut String) { - fn print_entries(f: &mut String, e: &FxIndexSet<ItemEntry>, kind: ItemSection) { - if !e.is_empty() { + fn print(&self) -> impl fmt::Display { + fn print_entries(e: &FxIndexSet<ItemEntry>, kind: ItemSection) -> impl fmt::Display { + fmt::from_fn(move |f| { + if e.is_empty() { + return Ok(()); + } + let mut e: Vec<&ItemEntry> = e.iter().collect(); e.sort(); - write_str( + write!( f, - format_args!( - "<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">", - id = kind.id(), - title = kind.name(), - ), - ); + "<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">", + id = kind.id(), + title = kind.name(), + )?; for s in e.iter() { - write_str(f, format_args!("<li>{}</li>", s.print())); + write!(f, "<li>{}</li>", s.print())?; } - f.push_str("</ul>"); - } + f.write_str("</ul>") + }) } - f.push_str("<h1>List of all items</h1>"); - // Note: print_entries does not escape the title, because we know the current set of titles - // doesn't require escaping. - print_entries(f, &self.structs, ItemSection::Structs); - print_entries(f, &self.enums, ItemSection::Enums); - print_entries(f, &self.unions, ItemSection::Unions); - print_entries(f, &self.primitives, ItemSection::PrimitiveTypes); - print_entries(f, &self.traits, ItemSection::Traits); - print_entries(f, &self.macros, ItemSection::Macros); - print_entries(f, &self.attribute_macros, ItemSection::AttributeMacros); - print_entries(f, &self.derive_macros, ItemSection::DeriveMacros); - print_entries(f, &self.functions, ItemSection::Functions); - print_entries(f, &self.type_aliases, ItemSection::TypeAliases); - print_entries(f, &self.trait_aliases, ItemSection::TraitAliases); - print_entries(f, &self.statics, ItemSection::Statics); - print_entries(f, &self.constants, ItemSection::Constants); + fmt::from_fn(|f| { + f.write_str("<h1>List of all items</h1>")?; + // Note: print_entries does not escape the title, because we know the current set of titles + // doesn't require escaping. + print_entries(&self.structs, ItemSection::Structs).fmt(f)?; + print_entries(&self.enums, ItemSection::Enums).fmt(f)?; + print_entries(&self.unions, ItemSection::Unions).fmt(f)?; + print_entries(&self.primitives, ItemSection::PrimitiveTypes).fmt(f)?; + print_entries(&self.traits, ItemSection::Traits).fmt(f)?; + print_entries(&self.macros, ItemSection::Macros).fmt(f)?; + print_entries(&self.attribute_macros, ItemSection::AttributeMacros).fmt(f)?; + print_entries(&self.derive_macros, ItemSection::DeriveMacros).fmt(f)?; + print_entries(&self.functions, ItemSection::Functions).fmt(f)?; + print_entries(&self.type_aliases, ItemSection::TypeAliases).fmt(f)?; + print_entries(&self.trait_aliases, ItemSection::TraitAliases).fmt(f)?; + print_entries(&self.statics, ItemSection::Statics).fmt(f)?; + print_entries(&self.constants, ItemSection::Constants).fmt(f)?; + Ok(()) + }) } } @@ -760,7 +783,7 @@ fn short_item_info( // Render the list of items inside one of the sections "Trait Implementations", // "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages). -pub(crate) fn render_impls( +fn render_impls( cx: &Context<'_>, mut w: impl Write, impls: &[&Impl], @@ -1201,8 +1224,8 @@ impl<'a> AssocItemLink<'a> { } } -pub fn write_section_heading( - title: &str, +fn write_section_heading( + title: impl fmt::Display, id: &str, extra_class: Option<&str>, extra: impl fmt::Display, @@ -1222,11 +1245,11 @@ pub fn write_section_heading( }) } -fn write_impl_section_heading(title: &str, id: &str) -> impl fmt::Display { +fn write_impl_section_heading(title: impl fmt::Display, id: &str) -> impl fmt::Display { write_section_heading(title, id, None, "") } -pub(crate) fn render_all_impls( +fn render_all_impls( mut w: impl Write, cx: &Context<'_>, containing_item: &clean::Item, @@ -1299,20 +1322,17 @@ fn render_assoc_items_inner( let (mut non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none()); if !non_trait.is_empty() { - let mut close_tags = <Vec<&str>>::with_capacity(1); - let mut tmp_buf = String::new(); - let (render_mode, id, class_html) = match what { - AssocItemRender::All => { - write_str( - &mut tmp_buf, - format_args!( - "{}", - write_impl_section_heading("Implementations", "implementations") - ), - ); - (RenderMode::Normal, "implementations-list".to_owned(), "") - } - AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { + let render_mode = what.render_mode(); + let class_html = what + .class() + .map(|class| fmt::from_fn(move |f| write!(f, r#" class="{class}""#))) + .maybe_display(); + let (section_heading, id) = match what { + AssocItemRender::All => ( + Either::Left(write_impl_section_heading("Implementations", "implementations")), + Cow::Borrowed("implementations-list"), + ), + AssocItemRender::DerefFor { trait_, type_, .. } => { let id = cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx)))); // the `impls.get` above only looks at the outermost type, @@ -1326,25 +1346,27 @@ fn render_assoc_items_inner( type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache) }); let derived_id = cx.derive_id(&id); - close_tags.push("</details>"); - write_str( - &mut tmp_buf, - format_args!( - "<details class=\"toggle big-toggle\" open><summary>{}</summary>", - write_impl_section_heading( - &format!( - "<span>Methods from {trait_}<Target = {type_}></span>", - trait_ = trait_.print(cx), - type_ = type_.print(cx), - ), - &id, - ) - ), - ); if let Some(def_id) = type_.def_id(cx.cache()) { - cx.deref_id_map.borrow_mut().insert(def_id, id); + cx.deref_id_map.borrow_mut().insert(def_id, id.clone()); } - (RenderMode::ForDeref { mut_: deref_mut_ }, derived_id, r#" class="impl-items""#) + ( + Either::Right(fmt::from_fn(move |f| { + write!( + f, + "<details class=\"toggle big-toggle\" open><summary>{}</summary>", + write_impl_section_heading( + fmt::from_fn(|f| write!( + f, + "<span>Methods from {trait_}<Target = {type_}></span>", + trait_ = trait_.print(cx), + type_ = type_.print(cx), + )), + &id, + ) + ) + })), + Cow::Owned(derived_id), + ) } }; let mut impls_buf = String::new(); @@ -1372,10 +1394,14 @@ fn render_assoc_items_inner( ); } if !impls_buf.is_empty() { - write!(w, "{tmp_buf}<div id=\"{id}\"{class_html}>{impls_buf}</div>").unwrap(); - for tag in close_tags.into_iter().rev() { - w.write_str(tag).unwrap(); - } + write!( + w, + "{section_heading}<div id=\"{id}\"{class_html}>{impls_buf}</div>{}", + matches!(what, AssocItemRender::DerefFor { .. }) + .then_some("</details>") + .maybe_display(), + ) + .unwrap(); } } @@ -1473,10 +1499,7 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> } } -pub(crate) fn notable_traits_button( - ty: &clean::Type, - cx: &Context<'_>, -) -> Option<impl fmt::Display> { +fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Option<impl fmt::Display> { if ty.is_unit() { // Very common fast path. return None; @@ -1588,10 +1611,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { (format!("{:#}", ty.print(cx)), out) } -pub(crate) fn notable_traits_json<'a>( - tys: impl Iterator<Item = &'a clean::Type>, - cx: &Context<'_>, -) -> String { +fn notable_traits_json<'a>(tys: impl Iterator<Item = &'a clean::Type>, cx: &Context<'_>) -> String { let mut mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect(); mp.sort_by(|(name1, _html1), (name2, _html2)| name1.cmp(name2)); struct NotableTraitsMap(Vec<(String, String)>); @@ -1642,8 +1662,8 @@ fn render_impl( // `containing_item` is used for rendering stability info. If the parent is a trait impl, // `containing_item` will the grandparent, since trait impls can't have stability attached. fn doc_impl_item( - boring: &mut String, - interesting: &mut String, + boring: impl fmt::Write, + interesting: impl fmt::Write, cx: &Context<'_>, item: &clean::Item, parent: &clean::Item, @@ -1652,7 +1672,7 @@ fn render_impl( is_default_item: bool, trait_: Option<&clean::Trait>, rendering_params: ImplRenderingParameters, - ) { + ) -> fmt::Result { let item_type = item.type_(); let name = item.name.as_ref().unwrap(); @@ -1727,15 +1747,16 @@ fn render_impl( ); } } - let w = if short_documented && trait_.is_some() { interesting } else { boring }; + let mut w = if short_documented && trait_.is_some() { + Either::Left(interesting) + } else { + Either::Right(boring) + }; let toggled = !doc_buffer.is_empty(); if toggled { let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" }; - write_str( - w, - format_args!("<details class=\"toggle{method_toggle_class}\" open><summary>"), - ); + write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>")?; } match &item.kind { clean::MethodItem(..) | clean::RequiredMethodItem(_) => { @@ -1750,172 +1771,151 @@ fn render_impl( .find(|item| item.name.map(|n| n == *name).unwrap_or(false)) }) .map(|item| format!("{}.{name}", item.type_())); - write_str( + write!( w, - format_args!( - "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\ + "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\ {}", - render_rightside(cx, item, render_mode) - ), - ); + render_rightside(cx, item, render_mode) + )?; if trait_.is_some() { // Anchors are only used on trait impls. - write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>")); + write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?; } - write_str( + write!( w, - format_args!( - "<h4 class=\"code-header\">{}</h4></section>", - render_assoc_item( - item, - link.anchor(source_id.as_ref().unwrap_or(&id)), - ItemType::Impl, - cx, - render_mode, - ), + "<h4 class=\"code-header\">{}</h4></section>", + render_assoc_item( + item, + link.anchor(source_id.as_ref().unwrap_or(&id)), + ItemType::Impl, + cx, + render_mode, ), - ); + )?; } } clean::RequiredAssocConstItem(generics, ty) => { let source_id = format!("{item_type}.{name}"); let id = cx.derive_id(&source_id); - write_str( + write!( w, - format_args!( - "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\ + "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\ {}", - render_rightside(cx, item, render_mode) - ), - ); + render_rightside(cx, item, render_mode) + )?; if trait_.is_some() { // Anchors are only used on trait impls. - write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>")); + write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?; } - write_str( + write!( w, - format_args!( - "<h4 class=\"code-header\">{}</h4></section>", - assoc_const( - item, - generics, - ty, - AssocConstValue::None, - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ) + "<h4 class=\"code-header\">{}</h4></section>", + assoc_const( + item, + generics, + ty, + AssocConstValue::None, + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, ), - ); + )?; } clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => { let source_id = format!("{item_type}.{name}"); let id = cx.derive_id(&source_id); - write_str( + write!( w, - format_args!( - "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\ + "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\ {}", - render_rightside(cx, item, render_mode) - ), - ); + render_rightside(cx, item, render_mode), + )?; if trait_.is_some() { // Anchors are only used on trait impls. - write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>")); + write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?; } - write_str( + write!( w, - format_args!( - "<h4 class=\"code-header\">{}</h4></section>", - assoc_const( - item, - &ci.generics, - &ci.type_, - match item.kind { - clean::ProvidedAssocConstItem(_) => - AssocConstValue::TraitDefault(&ci.kind), - clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind), - _ => unreachable!(), - }, - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ) + "<h4 class=\"code-header\">{}</h4></section>", + assoc_const( + item, + &ci.generics, + &ci.type_, + match item.kind { + clean::ProvidedAssocConstItem(_) => + AssocConstValue::TraitDefault(&ci.kind), + clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind), + _ => unreachable!(), + }, + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, ), - ); + )?; } clean::RequiredAssocTypeItem(generics, bounds) => { let source_id = format!("{item_type}.{name}"); let id = cx.derive_id(&source_id); - write_str( + write!( w, - format_args!( - "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\ + "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\ {}", - render_rightside(cx, item, render_mode) - ), - ); + render_rightside(cx, item, render_mode), + )?; if trait_.is_some() { // Anchors are only used on trait impls. - write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>")); + write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?; } - write_str( + write!( w, - format_args!( - "<h4 class=\"code-header\">{}</h4></section>", - assoc_type( - item, - generics, - bounds, - None, - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ) + "<h4 class=\"code-header\">{}</h4></section>", + assoc_type( + item, + generics, + bounds, + None, + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, ), - ); + )?; } clean::AssocTypeItem(tydef, _bounds) => { let source_id = format!("{item_type}.{name}"); let id = cx.derive_id(&source_id); - write_str( + write!( w, - format_args!( - "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\ + "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\ {}", - render_rightside(cx, item, render_mode) - ), - ); + render_rightside(cx, item, render_mode), + )?; if trait_.is_some() { // Anchors are only used on trait impls. - write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>")); + write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?; } - write_str( + write!( w, - format_args!( - "<h4 class=\"code-header\">{}</h4></section>", - assoc_type( - item, - &tydef.generics, - &[], // intentionally leaving out bounds - Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)), - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ) + "<h4 class=\"code-header\">{}</h4></section>", + assoc_type( + item, + &tydef.generics, + &[], // intentionally leaving out bounds + Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)), + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, ), - ); + )?; } - clean::StrippedItem(..) => return, + clean::StrippedItem(..) => return Ok(()), _ => panic!("can't make docs for trait item with name {:?}", item.name), } - w.push_str(&info_buffer); + w.write_str(&info_buffer)?; if toggled { - w.push_str("</summary>"); - w.push_str(&doc_buffer); - w.push_str("</details>"); + write!(w, "</summary>{doc_buffer}</details>")?; } + Ok(()) } let mut impl_items = String::new(); @@ -1958,7 +1958,7 @@ fn render_impl( false, trait_, rendering_params, - ); + )?; } _ => {} } @@ -1976,7 +1976,7 @@ fn render_impl( false, trait_, rendering_params, - ); + )?; } for method in methods { doc_impl_item( @@ -1990,20 +1990,20 @@ fn render_impl( false, trait_, rendering_params, - ); + )?; } } fn render_default_items( - boring: &mut String, - interesting: &mut String, + mut boring: impl fmt::Write, + mut interesting: impl fmt::Write, cx: &Context<'_>, t: &clean::Trait, i: &clean::Impl, parent: &clean::Item, render_mode: RenderMode, rendering_params: ImplRenderingParameters, - ) { + ) -> fmt::Result { for trait_item in &t.items { // Skip over any default trait items that are impossible to reference // (e.g. if it has a `Self: Sized` bound on an unsized type). @@ -2023,8 +2023,8 @@ fn render_impl( let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods); doc_impl_item( - boring, - interesting, + &mut boring, + &mut interesting, cx, trait_item, parent, @@ -2033,8 +2033,9 @@ fn render_impl( true, Some(t), rendering_params, - ); + )?; } + Ok(()) } // If we've implemented a trait, then also emit documentation for all @@ -2054,7 +2055,7 @@ fn render_impl( &i.impl_item, render_mode, rendering_params, - ); + )?; } } if render_mode == RenderMode::Normal { @@ -2171,7 +2172,7 @@ fn render_rightside( }) } -pub(crate) fn render_impl_summary( +fn render_impl_summary( cx: &Context<'_>, i: &Impl, parent: &clean::Item, diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 3c5c2ce1976..39a631b637b 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering; use std::fmt::{self, Display, Write as _}; use std::iter; -use rinja::Template; +use askama::Template; use rustc_abi::VariantIdx; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_hir as hir; @@ -11,7 +11,7 @@ use rustc_hir::def_id::DefId; use rustc_index::IndexVec; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::hygiene::MacroKind; -use rustc_span::symbol::{Symbol, kw, sym}; +use rustc_span::symbol::{Symbol, sym}; use tracing::{debug, info}; use super::type_layout::document_type_layout; @@ -37,7 +37,7 @@ use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine}; use crate::html::render::{document_full, document_item_info}; use crate::html::url_parts_builder::UrlPartsBuilder; -/// Generates a Rinja template struct for rendering items with common methods. +/// Generates an Askama template struct for rendering items with common methods. /// /// Usage: /// ```ignore (illustrative) @@ -301,7 +301,7 @@ fn toggle_close(mut w: impl fmt::Write) { w.write_str("</details>").unwrap(); } -trait ItemTemplate<'a, 'cx: 'a>: rinja::Template + Display { +trait ItemTemplate<'a, 'cx: 'a>: askama::Template + Display { fn item_and_cx(&self) -> (&'a clean::Item, &'a Context<'cx>); } @@ -347,9 +347,12 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i // but we actually want stable items to come first return is_stable2.cmp(&is_stable1); } - let lhs = i1.name.unwrap_or(kw::Empty); - let rhs = i2.name.unwrap_or(kw::Empty); - compare_names(lhs.as_str(), rhs.as_str()) + match (i1.name, i2.name) { + (Some(name1), Some(name2)) => compare_names(name1.as_str(), name2.as_str()), + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + (None, None) => Ordering::Equal, + } } let tcx = cx.tcx(); @@ -1229,12 +1232,13 @@ fn item_trait_alias( wrap_item(w, |w| { write!( w, - "{attrs}trait {name}{generics}{where_b} = {bounds};", + "{attrs}trait {name}{generics} = {bounds}{where_clause};", attrs = render_attributes_in_pre(it, "", cx), name = it.name.unwrap(), generics = t.generics.print(cx), - where_b = print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display(), bounds = bounds(&t.bounds, true, cx), + where_clause = + print_where_clause(&t.generics, cx, 0, Ending::NoNewline).maybe_display(), ) })?; @@ -1867,7 +1871,7 @@ fn item_proc_macro(cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) -> } } } - Ok(()) + fmt::Result::Ok(()) })?; write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) }) @@ -1944,7 +1948,7 @@ fn item_constant( } } } - Ok(()) + Ok::<(), fmt::Error>(()) })?; write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index b39701fae1d..aff8684ee3a 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -709,8 +709,11 @@ pub(crate) fn build_index( let mut result = Vec::new(); for (index, item) in self.items.iter().enumerate() { if let Some(ty) = &item.search_type - && let my = - ty.param_names.iter().map(|sym| sym.as_str()).collect::<Vec<_>>() + && let my = ty + .param_names + .iter() + .filter_map(|sym| sym.map(|sym| sym.to_string())) + .collect::<Vec<_>>() && my != prev { result.push((index, my.join(","))); @@ -1109,7 +1112,7 @@ fn simplify_fn_type<'a, 'tcx>( } Type::BareFunction(ref bf) => { let mut ty_generics = Vec::new(); - for ty in bf.decl.inputs.values.iter().map(|arg| &arg.type_) { + for ty in bf.decl.inputs.iter().map(|arg| &arg.type_) { simplify_fn_type( self_, generics, @@ -1372,7 +1375,7 @@ fn simplify_fn_constraint<'a>( /// Used to allow type-based search on constants and statics. fn make_nullary_fn( clean_type: &clean::Type, -) -> (Vec<RenderType>, Vec<RenderType>, Vec<Symbol>, Vec<Vec<RenderType>>) { +) -> (Vec<RenderType>, Vec<RenderType>, Vec<Option<Symbol>>, Vec<Vec<RenderType>>) { let mut rgen: FxIndexMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default(); let output = get_index_type(clean_type, vec![], &mut rgen); (vec![], vec![output], vec![], vec![]) @@ -1387,7 +1390,7 @@ fn get_fn_inputs_and_outputs( tcx: TyCtxt<'_>, impl_or_trait_generics: Option<&(clean::Type, clean::Generics)>, cache: &Cache, -) -> (Vec<RenderType>, Vec<RenderType>, Vec<Symbol>, Vec<Vec<RenderType>>) { +) -> (Vec<RenderType>, Vec<RenderType>, Vec<Option<Symbol>>, Vec<Vec<RenderType>>) { let decl = &func.decl; let mut rgen: FxIndexMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default(); @@ -1415,15 +1418,15 @@ fn get_fn_inputs_and_outputs( (None, &func.generics) }; - let mut arg_types = Vec::new(); - for arg in decl.inputs.values.iter() { + let mut param_types = Vec::new(); + for param in decl.inputs.iter() { simplify_fn_type( self_, generics, - &arg.type_, + ¶m.type_, tcx, 0, - &mut arg_types, + &mut param_types, &mut rgen, false, cache, @@ -1436,15 +1439,15 @@ fn get_fn_inputs_and_outputs( let mut simplified_params = rgen.into_iter().collect::<Vec<_>>(); simplified_params.sort_by_key(|(_, (idx, _))| -idx); ( - arg_types, + param_types, ret_types, simplified_params .iter() .map(|(name, (_idx, _traits))| match name { - SimplifiedParam::Symbol(name) => *name, - SimplifiedParam::Anonymous(_) => kw::Empty, + SimplifiedParam::Symbol(name) => Some(*name), + SimplifiedParam::Anonymous(_) => None, SimplifiedParam::AssociatedType(def_id, name) => { - Symbol::intern(&format!("{}::{}", tcx.item_name(*def_id), name)) + Some(Symbol::intern(&format!("{}::{}", tcx.item_name(*def_id), name))) } }) .collect(), diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 89ff61ecb03..cd0c9775f5c 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::cmp::Ordering; -use rinja::Template; +use askama::Template; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{DefIdMap, DefIdSet}; @@ -123,10 +123,10 @@ impl<'a> Link<'a> { pub(crate) mod filters { use std::fmt::{self, Display}; - use rinja::filters::Safe; + use askama::filters::Safe; use crate::html::escape::EscapeBodyTextWithWbr; - pub(crate) fn wrapped<T>(v: T) -> rinja::Result<Safe<impl Display>> + pub(crate) fn wrapped<T>(v: T) -> askama::Result<Safe<impl Display>> where T: Display, { diff --git a/src/librustdoc/html/render/tests.rs b/src/librustdoc/html/render/tests.rs index 657cd3c82aa..327a30887b1 100644 --- a/src/librustdoc/html/render/tests.rs +++ b/src/librustdoc/html/render/tests.rs @@ -47,8 +47,7 @@ fn test_all_types_prints_header_once() { // Regression test for #82477 let all_types = AllTypes::new(); - let mut buffer = String::new(); - all_types.print(&mut buffer); + let buffer = all_types.print().to_string(); assert_eq!(1, buffer.matches("List of all items").count()); } diff --git a/src/librustdoc/html/render/type_layout.rs b/src/librustdoc/html/render/type_layout.rs index a1ee5c8c548..fb1f0271c2a 100644 --- a/src/librustdoc/html/render/type_layout.rs +++ b/src/librustdoc/html/render/type_layout.rs @@ -1,6 +1,6 @@ use std::fmt; -use rinja::Template; +use askama::Template; use rustc_abi::{Primitive, TagEncoding, Variants}; use rustc_hir::def_id::DefId; use rustc_middle::span_bug; diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index cbbd4b01d83..095795c711d 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -3,7 +3,7 @@ use std::ffi::OsStr; use std::path::{Component, Path, PathBuf}; use std::{fmt, fs}; -use rinja::Template; +use askama::Template; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::ty::TyCtxt; diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index aa8df35258d..74d23b3143f 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1447,7 +1447,7 @@ so that we can apply CSS-filters to change the arrow color in themes */ cursor: pointer; } .setting-check input { - flex-shrink: 0, + flex-shrink: 0; } .setting-radio input:checked { diff --git a/src/librustdoc/html/templates/STYLE.md b/src/librustdoc/html/templates/STYLE.md index 32bacb11475..12c2553cffd 100644 --- a/src/librustdoc/html/templates/STYLE.md +++ b/src/librustdoc/html/templates/STYLE.md @@ -1,13 +1,13 @@ # Style for Templates -This directory has templates in the [Rinja templating language][rinjadoc], which is very +This directory has templates in the [Askama templating language][askamadoc], which is very similar to [Jinja2][jinjadoc]. [jinjadoc]: https://jinja.palletsprojects.com/en/3.1.x/templates/ -[rinjadoc]: https://docs.rs/rinja/latest/rinja/ +[askamadoc]: https://docs.rs/askama/latest/askama/ We want our rendered output to have as little unnecessary whitespace as -possible, so that pages load quickly. To achieve that we use Rinja's +possible, so that pages load quickly. To achieve that we use Askama's [whitespace control] features. By default, whitespace characters are removed around jinja tags (`{% %}` for example). At the end of most lines, we put an empty comment tag: `{# #}`. This causes all whitespace between the end of the @@ -18,7 +18,7 @@ remove following whitespace but not preceding. We also use the whitespace control characters in most instances of tags with control flow, for example `{% if foo %}`. -[whitespace control]: https://rinja.readthedocs.io/en/stable/configuration.html#whitespace-control +[whitespace control]: https://askama.readthedocs.io/en/stable/configuration.html#whitespace-control We want our templates to be readable, so we use indentation and newlines liberally. We indent by four spaces after opening an HTML tag _or_ a Jinja @@ -26,11 +26,11 @@ tag. In most cases an HTML tag should be followed by a newline, but if the tag has simple contents and fits with its close tag on a single line, the contents don't necessarily need a new line. -Rinja templates support quite sophisticated control flow. To keep our templates +Askama templates support quite sophisticated control flow. To keep our templates simple and understandable, we use only a subset: `if` and `for`. In particular -we avoid [assignments in the template logic][assignments] and [Rinja +we avoid [assignments in the template logic][assignments] and [Askama macros][macros]. This also may make things easier if we switch to a different Jinja-style template system in the future. -[assignments]: https://rinja.readthedocs.io/en/stable/template_syntax.html#assignments -[macros]: https://rinja.readthedocs.io/en/stable/template_syntax.html#macros +[assignments]: https://askama.readthedocs.io/en/stable/template_syntax.html#assignments +[macros]: https://askama.readthedocs.io/en/stable/template_syntax.html#macros diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 9d8eb70fbe0..dab23f8e42a 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -11,7 +11,7 @@ use rustc_hir::def::CtorKind; use rustc_hir::def_id::DefId; use rustc_metadata::rendered_const; use rustc_middle::{bug, ty}; -use rustc_span::{Pos, Symbol}; +use rustc_span::{Pos, Symbol, kw}; use rustdoc_json_types::*; use crate::clean::{self, ItemId}; @@ -609,9 +609,13 @@ impl FromClean<clean::FnDecl> for FunctionSignature { let clean::FnDecl { inputs, output, c_variadic } = decl; FunctionSignature { inputs: inputs - .values .into_iter() - .map(|arg| (arg.name.to_string(), arg.type_.into_json(renderer))) + .map(|param| { + // `_` is the most sensible name for missing param names. + let name = param.name.unwrap_or(kw::Underscore).to_string(); + let type_ = param.type_.into_json(renderer); + (name, type_) + }) .collect(), output: if output.is_unit() { None } else { Some(output.into_json(renderer)) }, is_c_variadic: c_variadic, diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index ba27eed7c11..131a12ce228 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -14,6 +14,7 @@ use std::io::{BufWriter, Write, stdout}; use std::path::PathBuf; use std::rc::Rc; +use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -123,6 +124,58 @@ impl<'tcx> JsonRenderer<'tcx> { } } +fn target(sess: &rustc_session::Session) -> types::Target { + // Build a set of which features are enabled on this target + let globally_enabled_features: FxHashSet<&str> = + sess.unstable_target_features.iter().map(|name| name.as_str()).collect(); + + // Build a map of target feature stability by feature name + use rustc_target::target_features::Stability; + let feature_stability: FxHashMap<&str, Stability> = sess + .target + .rust_target_features() + .into_iter() + .copied() + .map(|(name, stability, _)| (name, stability)) + .collect(); + + types::Target { + triple: sess.opts.target_triple.tuple().into(), + target_features: sess + .target + .rust_target_features() + .into_iter() + .copied() + .filter(|(_, stability, _)| { + // Describe only target features which the user can toggle + stability.toggle_allowed().is_ok() + }) + .map(|(name, stability, implied_features)| { + types::TargetFeature { + name: name.into(), + unstable_feature_gate: match stability { + Stability::Unstable(feature_gate) => Some(feature_gate.as_str().into()), + _ => None, + }, + implies_features: implied_features + .into_iter() + .copied() + .filter(|name| { + // Imply only target features which the user can toggle + feature_stability + .get(name) + .map(|stability| stability.toggle_allowed().is_ok()) + .unwrap_or(false) + }) + .map(String::from) + .collect(), + globally_enabled: globally_enabled_features.contains(name), + } + }) + .collect(), + } +} + impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { fn descr() -> &'static str { "json" @@ -248,6 +301,12 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { let e = ExternalCrate { crate_num: LOCAL_CRATE }; let index = (*self.index).clone().into_inner(); + // Note that tcx.rust_target_features is inappropriate here because rustdoc tries to run for + // multiple targets: https://github.com/rust-lang/rust/pull/137632 + // + // We want to describe a single target, so pass tcx.sess rather than tcx. + let target = target(self.tcx.sess); + debug!("Constructing Output"); let output_crate = types::Crate { root: self.id_from_item_default(e.def_id().into()), @@ -288,6 +347,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { ) }) .collect(), + target, format_version: types::FORMAT_VERSION, }; if let Some(ref out_dir) = self.out_dir { diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index fdbb792d25d..297597b3dea 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -60,7 +60,7 @@ fn filter_assoc_items_by_name_and_namespace( ns: Namespace, ) -> impl Iterator<Item = &ty::AssocItem> { tcx.associated_items(assoc_items_of).filter_by_name_unhygienic(ident.name).filter(move |item| { - item.kind.namespace() == ns && tcx.hygienic_eq(ident, item.ident(tcx), assoc_items_of) + item.namespace() == ns && tcx.hygienic_eq(ident, item.ident(tcx), assoc_items_of) }) } @@ -743,7 +743,7 @@ impl<'tcx> LinkCollector<'_, 'tcx> { ns, ) .map(|item| { - let res = Res::Def(item.kind.as_def_kind(), item.def_id); + let res = Res::Def(item.as_def_kind(), item.def_id); (res, item.def_id) }) .collect::<Vec<_>>(), diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index bcdca862862..692ce21d6cf 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -91,19 +91,21 @@ impl DocFolder for Stripper<'_, '_> { if let clean::ImportItem(clean::Import { source, .. }) = &i.kind && let Some(source_did) = source.did - && let Some(import_def_id) = i.def_id().and_then(|def_id| def_id.as_local()) { - let reexports = reexport_chain(self.tcx, import_def_id, source_did); + if self.tcx.is_doc_hidden(source_did) { + return None; + } else if let Some(import_def_id) = i.def_id().and_then(|def_id| def_id.as_local()) { + let reexports = reexport_chain(self.tcx, import_def_id, source_did); - // Check if any reexport in the chain has a hidden source - let has_hidden_source = reexports - .iter() - .filter_map(|reexport| reexport.id()) - .any(|reexport_did| self.tcx.is_doc_hidden(reexport_did)) - || self.tcx.is_doc_hidden(source_did); + // Check if any reexport in the chain has a hidden source + let has_hidden_source = reexports + .iter() + .filter_map(|reexport| reexport.id()) + .any(|reexport_did| self.tcx.is_doc_hidden(reexport_did)); - if has_hidden_source { - return None; + if has_hidden_source { + return None; + } } } |
