diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:03 +0000 |
commit | 64d98f8ee037282c35007b64c2649055c56af1db (patch) | |
tree | 5492bcf97fce41ee1c0b1cc2add283f3e66cdab0 /src/librustdoc | |
parent | Adding debian version 1.67.1+dfsg1-1. (diff) | |
download | rustc-64d98f8ee037282c35007b64c2649055c56af1db.tar.xz rustc-64d98f8ee037282c35007b64c2649055c56af1db.zip |
Merging upstream version 1.68.2+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/librustdoc')
48 files changed, 1035 insertions, 943 deletions
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 953f4aa8a..a302750aa 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -44,7 +44,7 @@ where discard_positive_impl: bool, ) -> Option<Item> { let tcx = self.cx.tcx; - let trait_ref = tcx.mk_trait_ref(trait_def_id, [ty]); + let trait_ref = ty::Binder::dummy(tcx.mk_trait_ref(trait_def_id, [ty])); if !self.cx.generated_synthetics.insert((ty, trait_def_id)) { debug!("get_auto_trait_impl_for({:?}): already generated, aborting", trait_ref); return None; @@ -124,7 +124,7 @@ where unsafety: hir::Unsafety::Normal, generics: new_generics, trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, ThinVec::new())), - for_: clean_middle_ty(ty, self.cx, None), + for_: clean_middle_ty(ty::Binder::dummy(ty), self.cx, None), items: Vec::new(), polarity, kind: ImplKind::Auto, @@ -402,15 +402,13 @@ where bound_params: Vec::new(), }) }) - .chain( - lifetime_to_bounds.into_iter().filter(|&(_, ref bounds)| !bounds.is_empty()).map( - |(lifetime, bounds)| { - let mut bounds_vec = bounds.into_iter().collect(); - self.sort_where_bounds(&mut bounds_vec); - WherePredicate::RegionPredicate { lifetime, bounds: bounds_vec } - }, - ), - ) + .chain(lifetime_to_bounds.into_iter().filter(|(_, bounds)| !bounds.is_empty()).map( + |(lifetime, bounds)| { + let mut bounds_vec = bounds.into_iter().collect(); + self.sort_where_bounds(&mut bounds_vec); + WherePredicate::RegionPredicate { lifetime, bounds: bounds_vec } + }, + )) .collect() } diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index a1145b90d..e6b2b2349 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -33,7 +33,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { trait_def_id, impl_def_id ); - let trait_ref = cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap(); + let trait_ref = cx.tcx.impl_trait_ref(impl_def_id).unwrap(); if !matches!(trait_ref.0.self_ty().kind(), ty::Param(_)) { continue; } @@ -105,10 +105,10 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { // the post-inference `trait_ref`, as it's more accurate. trait_: Some(clean_trait_ref_with_bindings( cx, - trait_ref.0, + ty::Binder::dummy(trait_ref.0), ThinVec::new(), )), - for_: clean_middle_ty(ty.0, cx, None), + for_: clean_middle_ty(ty::Binder::dummy(ty.0), cx, None), items: cx .tcx .associated_items(impl_def_id) @@ -117,7 +117,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { .collect::<Vec<_>>(), polarity: ty::ImplPolarity::Positive, kind: ImplKind::Blanket(Box::new(clean_middle_ty( - trait_ref.0.self_ty(), + ty::Binder::dummy(trait_ref.0.self_ty()), cx, None, ))), diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 1843a2120..f1853f369 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -507,7 +507,9 @@ impl<'a> fmt::Display for Display<'a> { "openbsd" => "OpenBSD", "redox" => "Redox", "solaris" => "Solaris", + "tvos" => "tvOS", "wasi" => "WASI", + "watchos" => "watchOS", "windows" => "Windows", _ => "", }, diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index 7f72d5d39..81f676724 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -1,9 +1,8 @@ use super::*; -use rustc_ast::attr; -use rustc_ast::Path; +use rustc_ast::{LitKind, MetaItemLit, Path, StrStyle}; use rustc_span::create_default_session_globals_then; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::DUMMY_SP; fn word_cfg(s: &str) -> Cfg { @@ -22,6 +21,15 @@ fn dummy_meta_item_word(name: &str) -> MetaItem { } } +fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> MetaItem { + let lit = MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP }; + MetaItem { + path: Path::from_ident(Ident::from_str(name)), + kind: MetaItemKind::NameValue(lit), + span: DUMMY_SP, + } +} + macro_rules! dummy_meta_item_list { ($name:ident, [$($list:ident),* $(,)?]) => { MetaItem { @@ -242,8 +250,8 @@ fn test_parse_ok() { let mi = dummy_meta_item_word("all"); assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all"))); - let mi = - attr::mk_name_value_item_str(Ident::from_str("all"), Symbol::intern("done"), DUMMY_SP); + let done = Symbol::intern("done"); + let mi = dummy_meta_item_name_value("all", done, LitKind::Str(done, StrStyle::Cooked)); assert_eq!(Cfg::parse(&mi), Ok(name_value_cfg("all", "done"))); let mi = dummy_meta_item_list!(all, [a, b]); @@ -272,7 +280,7 @@ fn test_parse_ok() { #[test] fn test_parse_err() { create_default_session_globals_then(|| { - let mi = attr::mk_name_value_item(Ident::from_str("foo"), LitKind::Bool(false), DUMMY_SP); + let mi = dummy_meta_item_name_value("foo", kw::False, LitKind::Bool(false)); assert!(Cfg::parse(&mi).is_err()); let mi = dummy_meta_item_list!(not, [a, b]); diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index e7c3e5a45..b3b093312 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -9,7 +9,7 @@ use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::Mutability; use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_middle::ty::{self, TyCtxt}; @@ -162,6 +162,7 @@ pub(crate) fn try_inline( pub(crate) fn try_inline_glob( cx: &mut DocContext<'_>, res: Res, + current_mod: LocalDefId, visited: &mut FxHashSet<DefId>, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, ) -> Option<Vec<clean::Item>> { @@ -172,7 +173,16 @@ pub(crate) fn try_inline_glob( match res { Res::Def(DefKind::Mod, did) => { - let mut items = build_module_items(cx, did, visited, inlined_names); + // Use the set of module reexports to filter away names that are not actually + // reexported by the glob, e.g. because they are shadowed by something else. + let reexports = cx + .tcx + .module_reexports(current_mod) + .unwrap_or_default() + .iter() + .filter_map(|child| child.res.opt_def_id()) + .collect(); + let mut items = build_module_items(cx, did, visited, inlined_names, Some(&reexports)); items.drain_filter(|item| { if let Some(name) = item.name { // If an item with the same type and name already exists, @@ -293,7 +303,7 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union { fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::Typedef> { let predicates = cx.tcx.explicit_predicates_of(did); - let type_ = clean_middle_ty(cx.tcx.type_of(did), cx, Some(did)); + let type_ = clean_middle_ty(ty::Binder::dummy(cx.tcx.type_of(did)), cx, Some(did)); Box::new(clean::Typedef { type_, @@ -325,7 +335,7 @@ pub(crate) fn build_impls( // * https://github.com/rust-lang/rust/pull/99917 — where the feature got used // * https://github.com/rust-lang/rust/issues/53487 — overall tracking issue for Error if tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) { - use rustc_middle::ty::fast_reject::SimplifiedTypeGen::*; + use rustc_middle::ty::fast_reject::SimplifiedType::*; let type_ = if tcx.is_trait(did) { TraitSimplifiedType(did) } else { AdtSimplifiedType(did) }; for &did in tcx.incoherent_impls(type_) { @@ -376,7 +386,7 @@ pub(crate) fn build_impl( let _prof_timer = cx.tcx.sess.prof.generic_activity("build_impl"); let tcx = cx.tcx; - let associated_trait = tcx.impl_trait_ref(did); + let associated_trait = tcx.impl_trait_ref(did).map(ty::EarlyBinder::skip_binder); // Only inline impl if the implemented trait is // reachable in rustdoc generated documentation @@ -405,7 +415,7 @@ pub(crate) fn build_impl( let for_ = match &impl_item { Some(impl_) => clean_ty(impl_.self_ty, cx), - None => clean_middle_ty(tcx.type_of(did), cx, Some(did)), + None => clean_middle_ty(ty::Binder::dummy(tcx.type_of(did)), cx, Some(did)), }; // Only inline impl if the implementing type is @@ -496,7 +506,8 @@ pub(crate) fn build_impl( ), }; let polarity = tcx.impl_polarity(did); - let trait_ = associated_trait.map(|t| clean_trait_ref_with_bindings(cx, t, ThinVec::new())); + let trait_ = associated_trait + .map(|t| clean_trait_ref_with_bindings(cx, ty::Binder::dummy(t), ThinVec::new())); if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() { super::build_deref_target_impls(cx, &trait_items, ret); } @@ -562,7 +573,7 @@ fn build_module( did: DefId, visited: &mut FxHashSet<DefId>, ) -> clean::Module { - let items = build_module_items(cx, did, visited, &mut FxHashSet::default()); + let items = build_module_items(cx, did, visited, &mut FxHashSet::default(), None); let span = clean::Span::new(cx.tcx.def_span(did)); clean::Module { items, span } @@ -573,6 +584,7 @@ fn build_module_items( did: DefId, visited: &mut FxHashSet<DefId>, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, + allowed_def_ids: Option<&FxHashSet<DefId>>, ) -> Vec<clean::Item> { let mut items = Vec::new(); @@ -582,6 +594,11 @@ fn build_module_items( for &item in cx.tcx.module_children(did).iter() { if item.vis.is_public() { let res = item.res.expect_non_local(); + if let Some(def_id) = res.opt_def_id() + && let Some(allowed_def_ids) = allowed_def_ids + && !allowed_def_ids.contains(&def_id) { + continue; + } if let Some(def_id) = res.mod_def_id() { // If we're inlining a glob import, it's possible to have // two distinct modules with the same name. We don't want to @@ -599,7 +616,9 @@ fn build_module_items( items.push(clean::Item { name: None, attrs: Box::new(clean::Attributes::default()), - item_id: ItemId::Primitive(prim_ty, did.krate), + // We can use the item's `DefId` directly since the only information ever used + // from it is `DefId.krate`. + item_id: ItemId::DefId(did), kind: Box::new(clean::ImportItem(clean::Import::new_simple( item.ident.name, clean::ImportSource { @@ -640,14 +659,14 @@ pub(crate) fn print_inlined_const(tcx: TyCtxt<'_>, did: DefId) -> String { fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant { clean::Constant { - type_: clean_middle_ty(cx.tcx.type_of(def_id), cx, Some(def_id)), + type_: clean_middle_ty(ty::Binder::dummy(cx.tcx.type_of(def_id)), cx, Some(def_id)), kind: clean::ConstantKind::Extern { def_id }, } } fn build_static(cx: &mut DocContext<'_>, did: DefId, mutable: bool) -> clean::Static { clean::Static { - type_: clean_middle_ty(cx.tcx.type_of(did), cx, Some(did)), + type_: clean_middle_ty(ty::Binder::dummy(cx.tcx.type_of(did)), cx, Some(did)), mutability: if mutable { Mutability::Mut } else { Mutability::Not }, expr: None, } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2a2a9470d..0f0e16265 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -22,6 +22,7 @@ use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; use rustc_middle::middle::resolve_lifetime as rl; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::InternalSubsts; +use rustc_middle::ty::TypeVisitable; use rustc_middle::ty::{self, AdtKind, DefIdTree, EarlyBinder, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{AstPass, MacroKind}; @@ -127,7 +128,7 @@ fn clean_generic_bound<'tcx>( hir::GenericBound::LangItemTrait(lang_item, span, _, generic_args) => { let def_id = cx.tcx.require_lang_item(lang_item, Some(span)); - let trait_ref = ty::TraitRef::identity(cx.tcx, def_id).skip_binder(); + let trait_ref = ty::TraitRef::identity(cx.tcx, def_id); let generic_args = clean_generic_args(generic_args, cx); let GenericArgs::AngleBracketed { bindings, .. } = generic_args @@ -156,17 +157,18 @@ fn clean_generic_bound<'tcx>( pub(crate) fn clean_trait_ref_with_bindings<'tcx>( cx: &mut DocContext<'tcx>, - trait_ref: ty::TraitRef<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, bindings: ThinVec<TypeBinding>, ) -> Path { - let kind = cx.tcx.def_kind(trait_ref.def_id).into(); + let kind = cx.tcx.def_kind(trait_ref.def_id()).into(); if !matches!(kind, ItemType::Trait | ItemType::TraitAlias) { - span_bug!(cx.tcx.def_span(trait_ref.def_id), "`TraitRef` had unexpected kind {:?}", kind); + span_bug!(cx.tcx.def_span(trait_ref.def_id()), "`TraitRef` had unexpected kind {:?}", kind); } - inline::record_extern_fqn(cx, trait_ref.def_id, kind); - let path = external_path(cx, trait_ref.def_id, true, bindings, trait_ref.substs); + inline::record_extern_fqn(cx, trait_ref.def_id(), kind); + let path = + external_path(cx, trait_ref.def_id(), true, bindings, trait_ref.map_bound(|tr| tr.substs)); - debug!("ty::TraitRef\n subst: {:?}\n", trait_ref.substs); + debug!(?trait_ref); path } @@ -187,7 +189,7 @@ fn clean_poly_trait_ref_with_bindings<'tcx>( }) .collect(); - let trait_ = clean_trait_ref_with_bindings(cx, poly_trait_ref.skip_binder(), bindings); + let trait_ = clean_trait_ref_with_bindings(cx, poly_trait_ref, bindings); GenericBound::TraitBound( PolyTrait { trait_, generic_params: late_bound_regions }, hir::TraitBoundModifier::None, @@ -212,19 +214,19 @@ fn clean_lifetime<'tcx>(lifetime: &hir::Lifetime, cx: &mut DocContext<'tcx>) -> pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg, cx: &mut DocContext<'tcx>) -> Constant { let def_id = cx.tcx.hir().body_owner_def_id(constant.value.body).to_def_id(); Constant { - type_: clean_middle_ty(cx.tcx.type_of(def_id), cx, Some(def_id)), + type_: clean_middle_ty(ty::Binder::dummy(cx.tcx.type_of(def_id)), cx, Some(def_id)), kind: ConstantKind::Anonymous { body: constant.value.body }, } } pub(crate) fn clean_middle_const<'tcx>( - constant: ty::Const<'tcx>, + constant: ty::Binder<'tcx, ty::Const<'tcx>>, cx: &mut DocContext<'tcx>, ) -> Constant { // FIXME: instead of storing the stringified expression, store `self` directly instead. Constant { - type_: clean_middle_ty(constant.ty(), cx, None), - kind: ConstantKind::TyConst { expr: constant.to_string().into() }, + type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None), + kind: ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() }, } } @@ -333,7 +335,7 @@ fn clean_poly_trait_predicate<'tcx>( let poly_trait_ref = pred.map_bound(|pred| pred.trait_ref); Some(WherePredicate::BoundPredicate { - ty: clean_middle_ty(poly_trait_ref.skip_binder().self_ty(), cx, None), + ty: clean_middle_ty(poly_trait_ref.self_ty(), cx, None), bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, ThinVec::new())], bound_params: Vec::new(), }) @@ -359,7 +361,7 @@ fn clean_type_outlives_predicate<'tcx>( let ty::OutlivesPredicate(ty, lt) = pred; Some(WherePredicate::BoundPredicate { - ty: clean_middle_ty(ty, cx, None), + ty: clean_middle_ty(ty::Binder::dummy(ty), cx, None), bounds: vec![GenericBound::Outlives( clean_middle_region(lt).expect("failed to clean lifetimes"), )], @@ -367,10 +369,13 @@ fn clean_type_outlives_predicate<'tcx>( }) } -fn clean_middle_term<'tcx>(term: ty::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Term { - match term.unpack() { - ty::TermKind::Ty(ty) => Term::Type(clean_middle_ty(ty, cx, None)), - ty::TermKind::Const(c) => Term::Constant(clean_middle_const(c, cx)), +fn clean_middle_term<'tcx>( + term: ty::Binder<'tcx, ty::Term<'tcx>>, + cx: &mut DocContext<'tcx>, +) -> Term { + match term.skip_binder().unpack() { + ty::TermKind::Ty(ty) => Term::Type(clean_middle_ty(term.rebind(ty), cx, None)), + ty::TermKind::Const(c) => Term::Constant(clean_middle_const(term.rebind(c), cx)), } } @@ -379,7 +384,10 @@ fn clean_hir_term<'tcx>(term: &hir::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Te hir::Term::Ty(ty) => Term::Type(clean_ty(ty, cx)), hir::Term::Const(c) => { let def_id = cx.tcx.hir().local_def_id(c.hir_id); - Term::Constant(clean_middle_const(ty::Const::from_anon_const(cx.tcx, def_id), cx)) + Term::Constant(clean_middle_const( + ty::Binder::dummy(ty::Const::from_anon_const(cx.tcx, def_id)), + cx, + )) } } } @@ -398,32 +406,31 @@ fn clean_projection_predicate<'tcx>( }) .collect(); - let ty::ProjectionPredicate { projection_ty, term } = pred.skip_binder(); - WherePredicate::EqPredicate { - lhs: Box::new(clean_projection(projection_ty, cx, None)), - rhs: Box::new(clean_middle_term(term, cx)), + lhs: Box::new(clean_projection(pred.map_bound(|p| p.projection_ty), cx, None)), + rhs: Box::new(clean_middle_term(pred.map_bound(|p| p.term), cx)), bound_params: late_bound_regions, } } fn clean_projection<'tcx>( - ty: ty::ProjectionTy<'tcx>, + ty: ty::Binder<'tcx, ty::AliasTy<'tcx>>, cx: &mut DocContext<'tcx>, def_id: Option<DefId>, ) -> Type { - if cx.tcx.def_kind(ty.item_def_id) == DefKind::ImplTraitPlaceholder { + if cx.tcx.def_kind(ty.skip_binder().def_id) == DefKind::ImplTraitPlaceholder { let bounds = cx .tcx - .explicit_item_bounds(ty.item_def_id) + .explicit_item_bounds(ty.skip_binder().def_id) .iter() - .map(|(bound, _)| EarlyBinder(*bound).subst(cx.tcx, ty.substs)) + .map(|(bound, _)| EarlyBinder(*bound).subst(cx.tcx, ty.skip_binder().substs)) .collect::<Vec<_>>(); return clean_middle_opaque_bounds(cx, bounds); } - let trait_ = clean_trait_ref_with_bindings(cx, ty.trait_ref(cx.tcx), ThinVec::new()); - let self_type = clean_middle_ty(ty.self_ty(), cx, None); + let trait_ = + clean_trait_ref_with_bindings(cx, ty.map_bound(|ty| ty.trait_ref(cx.tcx)), ThinVec::new()); + let self_type = clean_middle_ty(ty.map_bound(|ty| ty.self_ty()), cx, None); let self_def_id = if let Some(def_id) = def_id { cx.tcx.opt_parent(def_id).or(Some(def_id)) } else { @@ -446,15 +453,16 @@ fn compute_should_show_cast(self_def_id: Option<DefId>, trait_: &Path, self_type } fn projection_to_path_segment<'tcx>( - ty: ty::ProjectionTy<'tcx>, + ty: ty::Binder<'tcx, ty::AliasTy<'tcx>>, cx: &mut DocContext<'tcx>, ) -> PathSegment { - let item = cx.tcx.associated_item(ty.item_def_id); - let generics = cx.tcx.generics_of(ty.item_def_id); + let item = cx.tcx.associated_item(ty.skip_binder().def_id); + let generics = cx.tcx.generics_of(ty.skip_binder().def_id); PathSegment { name: item.name, args: GenericArgs::AngleBracketed { - args: substs_to_args(cx, &ty.substs[generics.parent_count..], false).into(), + args: substs_to_args(cx, ty.map_bound(|ty| &ty.substs[generics.parent_count..]), false) + .into(), bindings: Default::default(), }, } @@ -470,7 +478,11 @@ fn clean_generic_param_def<'tcx>( } ty::GenericParamDefKind::Type { has_default, synthetic, .. } => { let default = if has_default { - Some(clean_middle_ty(cx.tcx.type_of(def.def_id), cx, Some(def.def_id))) + Some(clean_middle_ty( + ty::Binder::dummy(cx.tcx.type_of(def.def_id)), + cx, + Some(def.def_id), + )) } else { None }; @@ -488,9 +500,15 @@ fn clean_generic_param_def<'tcx>( def.name, GenericParamDefKind::Const { did: def.def_id, - ty: Box::new(clean_middle_ty(cx.tcx.type_of(def.def_id), cx, Some(def.def_id))), + ty: Box::new(clean_middle_ty( + ty::Binder::dummy(cx.tcx.type_of(def.def_id)), + cx, + Some(def.def_id), + )), default: match has_default { - true => Some(Box::new(cx.tcx.const_param_default(def.def_id).to_string())), + true => Some(Box::new( + cx.tcx.const_param_default(def.def_id).subst_identity().to_string(), + )), false => None, }, }, @@ -733,8 +751,10 @@ fn clean_ty_generics<'tcx>( .collect::<ThinVec<GenericParamDef>>(); // param index -> [(trait DefId, associated type name & generics, type, higher-ranked params)] - let mut impl_trait_proj = - FxHashMap::<u32, Vec<(DefId, PathSegment, Ty<'_>, Vec<GenericParamDef>)>>::default(); + let mut impl_trait_proj = FxHashMap::< + u32, + Vec<(DefId, PathSegment, ty::Binder<'_, Ty<'_>>, Vec<GenericParamDef>)>, + >::default(); let where_predicates = preds .predicates @@ -783,8 +803,8 @@ fn clean_ty_generics<'tcx>( let proj = projection.map(|p| { ( - clean_projection(p.skip_binder().projection_ty, cx, None), - p.skip_binder().term, + clean_projection(p.map_bound(|p| p.projection_ty), cx, None), + p.map_bound(|p| p.term), ) }); if let Some(((_, trait_did, name), rhs)) = proj @@ -795,7 +815,7 @@ fn clean_ty_generics<'tcx>( impl_trait_proj.entry(param_idx).or_default().push(( trait_did, name, - rhs.ty().unwrap(), + rhs.map_bound(|rhs| rhs.ty().unwrap()), p.get_bound_params() .into_iter() .flatten() @@ -1066,7 +1086,7 @@ 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 = match clean_middle_ty(sig.skip_binder().output(), cx, None) { + let output = match clean_middle_ty(sig.output(), cx, None) { Type::Tuple(inner) if inner.is_empty() => DefaultReturn, ty => Return(ty), }; @@ -1076,11 +1096,10 @@ fn clean_fn_decl_from_did_and_sig<'tcx>( c_variadic: sig.skip_binder().c_variadic, inputs: Arguments { values: sig - .skip_binder() .inputs() .iter() .map(|t| Argument { - type_: clean_middle_ty(*t, cx, None), + type_: clean_middle_ty(t.map_bound(|t| *t), cx, None), name: names .next() .map(|i| i.name) @@ -1134,7 +1153,8 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext hir::TraitItemKind::Type(bounds, Some(default)) => { let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(); - let item_type = clean_middle_ty(hir_ty_to_ty(cx.tcx, default), cx, None); + let item_type = + clean_middle_ty(ty::Binder::dummy(hir_ty_to_ty(cx.tcx, default)), cx, None); AssocTypeItem( Box::new(Typedef { type_: clean_ty(default, cx), @@ -1173,7 +1193,8 @@ pub(crate) fn clean_impl_item<'tcx>( hir::ImplItemKind::Type(hir_ty) => { let type_ = clean_ty(hir_ty, cx); let generics = clean_generics(impl_.generics, cx); - let item_type = clean_middle_ty(hir_ty_to_ty(cx.tcx, hir_ty), cx, None); + let item_type = + clean_middle_ty(ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), cx, None); AssocTypeItem( Box::new(Typedef { type_, generics, item_type: Some(item_type) }), Vec::new(), @@ -1192,7 +1213,11 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( let tcx = cx.tcx; let kind = match assoc_item.kind { ty::AssocKind::Const => { - let ty = clean_middle_ty(tcx.type_of(assoc_item.def_id), cx, Some(assoc_item.def_id)); + let ty = clean_middle_ty( + ty::Binder::dummy(tcx.type_of(assoc_item.def_id)), + cx, + Some(assoc_item.def_id), + ); let provided = match assoc_item.container { ty::ImplContainer => true, @@ -1375,7 +1400,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( AssocTypeItem( Box::new(Typedef { type_: clean_middle_ty( - tcx.type_of(assoc_item.def_id), + ty::Binder::dummy(tcx.type_of(assoc_item.def_id)), cx, Some(assoc_item.def_id), ), @@ -1393,7 +1418,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( AssocTypeItem( Box::new(Typedef { type_: clean_middle_ty( - tcx.type_of(assoc_item.def_id), + ty::Binder::dummy(tcx.type_of(assoc_item.def_id)), cx, Some(assoc_item.def_id), ), @@ -1437,8 +1462,11 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type hir::QPath::Resolved(Some(qself), p) => { // Try to normalize `<X as Y>::T` to a type let ty = hir_ty_to_ty(cx.tcx, hir_ty); - if let Some(normalized_value) = normalize(cx, ty) { - return clean_middle_ty(normalized_value, cx, None); + // `hir_to_ty` can return projection types with escaping vars for GATs, e.g. `<() as Trait>::Gat<'_>` + if !ty.has_escaping_bound_vars() { + if let Some(normalized_value) = normalize(cx, ty::Binder::dummy(ty)) { + return clean_middle_ty(normalized_value, cx, None); + } } let trait_segments = &p.segments[..p.segments.len() - 1]; @@ -1461,11 +1489,13 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type hir::QPath::TypeRelative(qself, segment) => { let ty = hir_ty_to_ty(cx.tcx, hir_ty); let res = match ty.kind() { - ty::Projection(proj) => Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id), + ty::Alias(ty::Projection, proj) => { + Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id) + } // Rustdoc handles `ty::Error`s by turning them into `Type::Infer`s. ty::Error(_) => return Type::Infer, // Otherwise, this is an inherent associated type. - _ => return clean_middle_ty(ty, cx, None), + _ => return clean_middle_ty(ty::Binder::dummy(ty), cx, None), }; let trait_ = clean_path(&hir::Path { span, res, segments: &[] }, cx); register_res(cx, trait_.res); @@ -1584,7 +1614,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T match ty.kind { TyKind::Never => Primitive(PrimitiveType::Never), TyKind::Ptr(ref m) => RawPointer(m.mutbl, Box::new(clean_ty(m.ty, cx))), - TyKind::Rptr(ref l, ref m) => { + TyKind::Ref(ref l, ref m) => { let lifetime = if l.is_anonymous() { None } else { Some(clean_lifetime(*l, cx)) }; BorrowedRef { lifetime, mutability: m.mutbl, type_: Box::new(clean_ty(m.ty, cx)) } } @@ -1632,7 +1662,10 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T } /// Returns `None` if the type could not be normalized -fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { +fn normalize<'tcx>( + cx: &mut DocContext<'tcx>, + ty: ty::Binder<'tcx, Ty<'tcx>>, +) -> Option<ty::Binder<'tcx, Ty<'tcx>>> { // HACK: low-churn fix for #79459 while we wait for a trait normalization fix if !cx.tcx.sess.opts.unstable_opts.normalize_docs { return None; @@ -1660,14 +1693,14 @@ fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> } } +#[instrument(level = "trace", skip(cx), ret)] pub(crate) fn clean_middle_ty<'tcx>( - ty: Ty<'tcx>, + bound_ty: ty::Binder<'tcx, Ty<'tcx>>, cx: &mut DocContext<'tcx>, def_id: Option<DefId>, ) -> Type { - trace!("cleaning type: {:?}", ty); - let ty = normalize(cx, ty).unwrap_or(ty); - match *ty.kind() { + let bound_ty = normalize(cx, bound_ty).unwrap_or(bound_ty); + match *bound_ty.skip_binder().kind() { ty::Never => Primitive(PrimitiveType::Never), ty::Bool => Primitive(PrimitiveType::Bool), ty::Char => Primitive(PrimitiveType::Char), @@ -1675,20 +1708,23 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Uint(uint_ty) => Primitive(uint_ty.into()), ty::Float(float_ty) => Primitive(float_ty.into()), ty::Str => Primitive(PrimitiveType::Str), - ty::Slice(ty) => Slice(Box::new(clean_middle_ty(ty, cx, None))), + ty::Slice(ty) => Slice(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None))), ty::Array(ty, mut n) => { n = n.eval(cx.tcx, ty::ParamEnv::reveal_all()); let n = print_const(cx, n); - Array(Box::new(clean_middle_ty(ty, cx, None)), n.into()) + Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None)), n.into()) + } + ty::RawPtr(mt) => { + RawPointer(mt.mutbl, Box::new(clean_middle_ty(bound_ty.rebind(mt.ty), cx, None))) } - ty::RawPtr(mt) => RawPointer(mt.mutbl, Box::new(clean_middle_ty(mt.ty, cx, None))), ty::Ref(r, ty, mutbl) => BorrowedRef { lifetime: clean_middle_region(r), mutability: mutbl, - type_: Box::new(clean_middle_ty(ty, cx, None)), + type_: Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None)), }, ty::FnDef(..) | ty::FnPtr(_) => { - let sig = ty.fn_sig(cx.tcx); + // FIXME: should we merge the outer and inner binders somehow? + let sig = bound_ty.skip_binder().fn_sig(cx.tcx); let decl = clean_fn_decl_from_did_and_sig(cx, None, sig); BareFunction(Box::new(BareFunctionDecl { unsafety: sig.unsafety(), @@ -1705,12 +1741,18 @@ pub(crate) fn clean_middle_ty<'tcx>( AdtKind::Enum => ItemType::Enum, }; inline::record_extern_fqn(cx, did, kind); - let path = external_path(cx, did, false, ThinVec::new(), substs); + let path = external_path(cx, did, false, ThinVec::new(), bound_ty.rebind(substs)); Type::Path { path } } ty::Foreign(did) => { inline::record_extern_fqn(cx, did, ItemType::ForeignType); - let path = external_path(cx, did, false, ThinVec::new(), InternalSubsts::empty()); + let path = external_path( + cx, + did, + false, + ThinVec::new(), + ty::Binder::dummy(InternalSubsts::empty()), + ); Type::Path { path } } ty::Dynamic(obj, ref reg, _) => { @@ -1721,11 +1763,11 @@ pub(crate) fn clean_middle_ty<'tcx>( let did = obj .principal_def_id() .or_else(|| dids.next()) - .unwrap_or_else(|| panic!("found trait object `{:?}` with no traits?", ty)); + .unwrap_or_else(|| panic!("found trait object `{bound_ty:?}` with no traits?")); let substs = match obj.principal() { - Some(principal) => principal.skip_binder().substs, + Some(principal) => principal.map_bound(|p| p.substs), // marker traits have no substs. - _ => cx.tcx.intern_substs(&[]), + _ => ty::Binder::dummy(InternalSubsts::empty()), }; inline::record_extern_fqn(cx, did, ItemType::Trait); @@ -1736,7 +1778,7 @@ pub(crate) fn clean_middle_ty<'tcx>( let lifetime = clean_middle_region(*reg); let mut bounds = dids .map(|did| { - let empty = cx.tcx.intern_substs(&[]); + let empty = ty::Binder::dummy(InternalSubsts::empty()); let path = external_path(cx, did, false, ThinVec::new(), empty); inline::record_extern_fqn(cx, did, ItemType::Trait); PolyTrait { trait_: path, generic_params: Vec::new() } @@ -1747,15 +1789,17 @@ pub(crate) fn clean_middle_ty<'tcx>( .projection_bounds() .map(|pb| TypeBinding { assoc: projection_to_path_segment( - pb.skip_binder() - // HACK(compiler-errors): Doesn't actually matter what self - // type we put here, because we're only using the GAT's substs. - .with_self_ty(cx.tcx, cx.tcx.types.self_param) - .projection_ty, + pb.map_bound(|pb| { + pb + // HACK(compiler-errors): Doesn't actually matter what self + // type we put here, because we're only using the GAT's substs. + .with_self_ty(cx.tcx, cx.tcx.types.self_param) + .projection_ty + }), cx, ), kind: TypeBindingKind::Equality { - term: clean_middle_term(pb.skip_binder().term, cx), + term: clean_middle_term(pb.map_bound(|pb| pb.term), cx), }, }) .collect(); @@ -1779,9 +1823,11 @@ pub(crate) fn clean_middle_ty<'tcx>( DynTrait(bounds, lifetime) } - ty::Tuple(t) => Tuple(t.iter().map(|t| clean_middle_ty(t, cx, None)).collect()), + ty::Tuple(t) => { + Tuple(t.iter().map(|t| clean_middle_ty(bound_ty.rebind(t), cx, None)).collect()) + } - ty::Projection(ref data) => clean_projection(*data, cx, def_id), + ty::Alias(ty::Projection, ref data) => clean_projection(bound_ty.rebind(*data), cx, def_id), ty::Param(ref p) => { if let Some(bounds) = cx.impl_trait_bounds.remove(&p.index.into()) { @@ -1791,7 +1837,7 @@ pub(crate) fn clean_middle_ty<'tcx>( } } - ty::Opaque(def_id, substs) => { + ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => { // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, // by looking up the bounds associated with the def_id. let bounds = cx @@ -1809,7 +1855,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Placeholder(..) => panic!("Placeholder"), ty::GeneratorWitness(..) => panic!("GeneratorWitness"), ty::Infer(..) => panic!("Infer"), - ty::Error(_) => panic!("Error"), + ty::Error(_) => rustc_errors::FatalError.raise(), } } @@ -1854,9 +1900,12 @@ fn clean_middle_opaque_bounds<'tcx>( { if proj.projection_ty.trait_ref(cx.tcx) == trait_ref.skip_binder() { Some(TypeBinding { - assoc: projection_to_path_segment(proj.projection_ty, cx), + assoc: projection_to_path_segment( + bound.kind().rebind(proj.projection_ty), + cx, + ), kind: TypeBindingKind::Equality { - term: clean_middle_term(proj.term, cx), + term: clean_middle_term(bound.kind().rebind(proj.term), cx), }, }) } else { @@ -1887,7 +1936,7 @@ pub(crate) fn clean_middle_field<'tcx>(field: &ty::FieldDef, cx: &mut DocContext clean_field_with_def_id( field.did, field.name, - clean_middle_ty(cx.tcx.type_of(field.did), cx, Some(field.did)), + clean_middle_ty(ty::Binder::dummy(cx.tcx.type_of(field.did)), cx, Some(field.did)), cx, ) } @@ -1902,20 +1951,27 @@ pub(crate) fn clean_field_with_def_id( } pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, 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, + }; + let kind = match variant.ctor_kind() { - Some(CtorKind::Const) => Variant::CLike(match variant.discr { - ty::VariantDiscr::Explicit(def_id) => Some(Discriminant { expr: None, value: def_id }), - ty::VariantDiscr::Relative(_) => None, - }), - Some(CtorKind::Fn) => Variant::Tuple( + Some(CtorKind::Const) => VariantKind::CLike, + Some(CtorKind::Fn) => VariantKind::Tuple( variant.fields.iter().map(|field| clean_middle_field(field, cx)).collect(), ), - None => Variant::Struct(VariantStruct { - ctor_kind: None, + None => VariantKind::Struct(VariantStruct { fields: variant.fields.iter().map(|field| clean_middle_field(field, cx)).collect(), }), }; - Item::from_def_id_and_parts(variant.def_id, Some(variant.name), VariantItem(kind), cx) + + Item::from_def_id_and_parts( + variant.def_id, + Some(variant.name), + VariantItem(Variant { kind, discriminant }), + cx, + ) } fn clean_variant_data<'tcx>( @@ -1923,19 +1979,22 @@ fn clean_variant_data<'tcx>( disr_expr: &Option<hir::AnonConst>, cx: &mut DocContext<'tcx>, ) -> Variant { - match variant { - hir::VariantData::Struct(..) => Variant::Struct(VariantStruct { - ctor_kind: None, + let discriminant = disr_expr.map(|disr| Discriminant { + expr: Some(disr.body), + value: cx.tcx.hir().local_def_id(disr.hir_id).to_def_id(), + }); + + let kind = match variant { + hir::VariantData::Struct(..) => VariantKind::Struct(VariantStruct { fields: variant.fields().iter().map(|x| clean_field(x, cx)).collect(), }), hir::VariantData::Tuple(..) => { - Variant::Tuple(variant.fields().iter().map(|x| clean_field(x, cx)).collect()) + VariantKind::Tuple(variant.fields().iter().map(|x| clean_field(x, cx)).collect()) } - hir::VariantData::Unit(..) => Variant::CLike(disr_expr.map(|disr| Discriminant { - expr: Some(disr.body), - value: cx.tcx.hir().local_def_id(disr.hir_id).to_def_id(), - })), - } + hir::VariantData::Unit(..) => VariantKind::CLike, + }; + + Variant { discriminant, kind } } fn clean_path<'tcx>(path: &hir::Path<'tcx>, cx: &mut DocContext<'tcx>) -> Path { @@ -2052,10 +2111,12 @@ fn get_all_import_attributes<'hir>( ) { let hir_map = tcx.hir(); let mut visitor = OneLevelVisitor::new(hir_map, target_hir_id); + let mut visited = FxHashSet::default(); // If the item is an import and has at least a path with two parts, we go into it. while let hir::ItemKind::Use(path, _) = item.kind && path.segments.len() > 1 && - let hir::def::Res::Def(_, def_id) = path.segments[path.segments.len() - 2].res + let hir::def::Res::Def(_, def_id) = path.segments[path.segments.len() - 2].res && + visited.insert(def_id) { if let Some(hir::Node::Item(parent_item)) = hir_map.get_if_local(def_id) { // We add the attributes from this import into the list. @@ -2100,7 +2161,7 @@ fn clean_maybe_renamed_item<'tcx>( }), ItemKind::TyAlias(hir_ty, generics) => { let rustdoc_ty = clean_ty(hir_ty, cx); - let ty = clean_middle_ty(hir_ty_to_ty(cx.tcx, hir_ty), cx, None); + let ty = clean_middle_ty(ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), cx, None); TypedefItem(Box::new(Typedef { type_: rustdoc_ty, generics: clean_generics(generics, cx), @@ -2211,7 +2272,9 @@ fn clean_impl<'tcx>( let for_ = clean_ty(impl_.self_ty, cx); let type_alias = for_.def_id(&cx.cache).and_then(|did| match tcx.def_kind(did) { - DefKind::TyAlias => Some(clean_middle_ty(tcx.type_of(did), cx, Some(did))), + DefKind::TyAlias => { + Some(clean_middle_ty(ty::Binder::dummy(tcx.type_of(did)), cx, Some(did))) + } _ => None, }); let mut make_item = |trait_: Option<Path>, for_: Type, items: Vec<Item>| { @@ -2380,7 +2443,8 @@ fn clean_use_statement_inner<'tcx>( let inner = if kind == hir::UseKind::Glob { if !denied { let mut visited = FxHashSet::default(); - if let Some(items) = inline::try_inline_glob(cx, path.res, &mut visited, inlined_names) + if let Some(items) = + inline::try_inline_glob(cx, path.res, current_mod, &mut visited, inlined_names) { return items; } diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index e96a9bab7..dbbc25739 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -46,7 +46,7 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> ThinVec<WP // Look for equality predicates on associated types that can be merged into // general bound predicates. - equalities.retain(|&(ref lhs, ref rhs, ref bound_params)| { + equalities.retain(|(lhs, rhs, bound_params)| { let Some((ty, trait_did, name)) = lhs.projection() else { return true; }; let Some((bounds, _)) = tybounds.get_mut(ty) else { return true }; let bound_params = bound_params diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 2590bb0df..87de41fde 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -10,7 +10,6 @@ use std::{cmp, fmt, iter}; use arrayvec::ArrayVec; use thin_vec::ThinVec; -use rustc_ast::attr; use rustc_ast::util::comments::beautify_doc_string; use rustc_ast::{self as ast, AttrStyle}; use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel}; @@ -27,7 +26,6 @@ use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, DefIdTree, TyCtxt, Visibility}; use rustc_session::Session; use rustc_span::hygiene::MacroKind; -use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{self, FileName, Loc}; use rustc_target::abi::VariantIdx; @@ -64,8 +62,6 @@ pub(crate) enum ItemId { Auto { trait_: DefId, for_: DefId }, /// Identifier that is used for blanket implementations. Blanket { impl_id: DefId, for_: DefId }, - /// Identifier for primitive types. - Primitive(PrimitiveType, CrateNum), } impl ItemId { @@ -75,7 +71,6 @@ impl ItemId { ItemId::Auto { for_: id, .. } | ItemId::Blanket { for_: id, .. } | ItemId::DefId(id) => id.is_local(), - ItemId::Primitive(_, krate) => krate == LOCAL_CRATE, } } @@ -100,7 +95,6 @@ impl ItemId { ItemId::Auto { for_: id, .. } | ItemId::Blanket { for_: id, .. } | ItemId::DefId(id) => id.krate, - ItemId::Primitive(_, krate) => krate, } } } @@ -682,7 +676,8 @@ impl Item { } let header = match *self.kind { ItemKind::ForeignFunctionItem(_) => { - let abi = tcx.fn_sig(self.item_id.as_def_id().unwrap()).abi(); + let def_id = self.item_id.as_def_id().unwrap(); + let abi = tcx.fn_sig(def_id).abi(); hir::FnHeader { unsafety: if abi == Abi::RustIntrinsic { intrinsic_operation_unsafety(tcx, self.item_id.as_def_id().unwrap()) @@ -690,7 +685,14 @@ impl Item { hir::Unsafety::Unsafe }, abi, - constness: hir::Constness::NotConst, + constness: if abi == Abi::RustIntrinsic + && tcx.is_const_fn(def_id) + && is_unstable_const_fn(tcx, def_id).is_none() + { + hir::Constness::Const + } else { + hir::Constness::NotConst + }, asyncness: hir::IsAsync::NotAsync, } } @@ -709,15 +711,13 @@ impl Item { let def_id = match self.item_id { // Anything but DefId *shouldn't* matter, but return a reasonable value anyway. ItemId::Auto { .. } | ItemId::Blanket { .. } => return None, - // Primitives and Keywords are written in the source code as private modules. - // The modules need to be private so that nobody actually uses them, but the - // keywords and primitives that they are documenting are public. - ItemId::Primitive(..) => return Some(Visibility::Public), ItemId::DefId(def_id) => def_id, }; match *self.kind { - // Explication on `ItemId::Primitive` just above. + // Primitives and Keywords are written in the source code as private modules. + // The modules need to be private so that nobody actually uses them, but the + // keywords and primitives that they are documenting are public. ItemKind::KeywordItem | ItemKind::PrimitiveItem(_) => return Some(Visibility::Public), // Variant fields inherit their enum's visibility. StructFieldItem(..) if is_field_vis_inherited(tcx, def_id) => { @@ -809,8 +809,11 @@ impl ItemKind { match self { StructItem(s) => s.fields.iter(), UnionItem(u) => u.fields.iter(), - VariantItem(Variant::Struct(v)) => v.fields.iter(), - VariantItem(Variant::Tuple(v)) => v.iter(), + VariantItem(v) => match &v.kind { + VariantKind::CLike => [].iter(), + VariantKind::Tuple(t) => t.iter(), + VariantKind::Struct(s) => s.fields.iter(), + }, EnumItem(e) => e.variants.iter(), TraitItem(t) => t.items.iter(), ImplItem(i) => i.items.iter(), @@ -826,7 +829,6 @@ impl ItemKind { | TyMethodItem(_) | MethodItem(_, _) | StructFieldItem(_) - | VariantItem(_) | ForeignFunctionItem(_) | ForeignStaticItem(_) | ForeignTypeItem @@ -982,12 +984,12 @@ impl AttributesExt for [ast::Attribute] { // #[doc(cfg(target_feature = "feat"))] attributes as well for attr in self.lists(sym::target_feature) { if attr.has_name(sym::enable) { - if let Some(feat) = attr.value_str() { - let meta = attr::mk_name_value_item_str( - Ident::with_dummy_span(sym::target_feature), - feat, - DUMMY_SP, - ); + if attr.value_str().is_some() { + // Clone `enable = "feat"`, change to `target_feature = "feat"`. + // Unwrap is safe because `value_str` succeeded above. + let mut meta = attr.meta_item().unwrap().clone(); + meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature)); + if let Ok(feat_cfg) = Cfg::parse(&meta) { cfg &= feat_cfg; } @@ -1345,7 +1347,7 @@ pub(crate) enum GenericBound { impl GenericBound { pub(crate) fn maybe_sized(cx: &mut DocContext<'_>) -> GenericBound { let did = cx.tcx.require_lang_item(LangItem::Sized, None); - let empty = cx.tcx.intern_substs(&[]); + let empty = ty::Binder::dummy(ty::InternalSubsts::empty()); let path = external_path(cx, did, false, ThinVec::new(), empty); inline::record_extern_fqn(cx, did, ItemType::Trait); GenericBound::TraitBound( @@ -1742,7 +1744,7 @@ impl Type { fn inner_def_id(&self, cache: Option<&Cache>) -> Option<DefId> { let t: PrimitiveType = match *self { Type::Path { ref path } => return Some(path.def_id()), - DynTrait(ref bounds, _) => return Some(bounds[0].trait_.def_id()), + DynTrait(ref bounds, _) => return bounds.get(0).map(|b| b.trait_.def_id()), Primitive(p) => return cache.and_then(|c| c.primitive_locations.get(&p).cloned()), BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference, BorrowedRef { ref type_, .. } => return type_.inner_def_id(cache), @@ -1872,7 +1874,7 @@ impl PrimitiveType { } pub(crate) fn simplified_types() -> &'static SimplifiedTypes { - use ty::fast_reject::SimplifiedTypeGen::*; + use ty::fast_reject::SimplifiedType::*; use ty::{FloatTy, IntTy, UintTy}; use PrimitiveType::*; static CELL: OnceCell<SimplifiedTypes> = OnceCell::new(); @@ -2111,7 +2113,6 @@ impl Union { /// only as a variant in an enum. #[derive(Clone, Debug)] pub(crate) struct VariantStruct { - pub(crate) ctor_kind: Option<CtorKind>, pub(crate) fields: Vec<Item>, } @@ -2138,17 +2139,23 @@ impl Enum { } #[derive(Clone, Debug)] -pub(crate) enum Variant { - CLike(Option<Discriminant>), +pub(crate) struct Variant { + pub kind: VariantKind, + pub discriminant: Option<Discriminant>, +} + +#[derive(Clone, Debug)] +pub(crate) enum VariantKind { + CLike, Tuple(Vec<Item>), Struct(VariantStruct), } impl Variant { pub(crate) fn has_stripped_entries(&self) -> Option<bool> { - match *self { - Self::Struct(ref struct_) => Some(struct_.has_stripped_entries()), - Self::CLike(..) | Self::Tuple(_) => None, + match &self.kind { + VariantKind::Struct(struct_) => Some(struct_.has_stripped_entries()), + VariantKind::CLike | VariantKind::Tuple(_) => None, } } } @@ -2489,6 +2496,17 @@ impl Import { pub(crate) fn new_glob(source: ImportSource, should_be_displayed: bool) -> Self { Self { kind: ImportKind::Glob, source, should_be_displayed } } + + pub(crate) fn imported_item_is_doc_hidden(&self, tcx: TyCtxt<'_>) -> bool { + match self.source.did { + Some(did) => tcx + .get_attrs(did, sym::doc) + .filter_map(ast::Attribute::meta_item_list) + .flatten() + .has_word(sym::hidden), + None => false, + } + } } #[derive(Clone, Debug)] diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 246560bad..a12f764fa 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -78,12 +78,16 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { pub(crate) fn substs_to_args<'tcx>( cx: &mut DocContext<'tcx>, - substs: &[ty::subst::GenericArg<'tcx>], + substs: ty::Binder<'tcx, &[ty::subst::GenericArg<'tcx>]>, mut skip_first: bool, ) -> Vec<GenericArg> { let mut ret_val = - Vec::with_capacity(substs.len().saturating_sub(if skip_first { 1 } else { 0 })); - ret_val.extend(substs.iter().filter_map(|kind| match kind.unpack() { + Vec::with_capacity(substs.skip_binder().len().saturating_sub(if skip_first { + 1 + } else { + 0 + })); + ret_val.extend(substs.iter().filter_map(|kind| match kind.skip_binder().unpack() { GenericArgKind::Lifetime(lt) => { Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided()))) } @@ -91,8 +95,12 @@ pub(crate) fn substs_to_args<'tcx>( skip_first = false; None } - GenericArgKind::Type(ty) => Some(GenericArg::Type(clean_middle_ty(ty, cx, None))), - GenericArgKind::Const(ct) => Some(GenericArg::Const(Box::new(clean_middle_const(ct, cx)))), + GenericArgKind::Type(ty) => { + Some(GenericArg::Type(clean_middle_ty(kind.rebind(ty), cx, None))) + } + GenericArgKind::Const(ct) => { + Some(GenericArg::Const(Box::new(clean_middle_const(kind.rebind(ct), cx)))) + } })); ret_val } @@ -102,15 +110,20 @@ fn external_generic_args<'tcx>( did: DefId, has_self: bool, bindings: ThinVec<TypeBinding>, - substs: SubstsRef<'tcx>, + substs: ty::Binder<'tcx, SubstsRef<'tcx>>, ) -> GenericArgs { - let args = substs_to_args(cx, substs, has_self); + let args = substs_to_args(cx, substs.map_bound(|substs| &substs[..]), has_self); if cx.tcx.fn_trait_kind_from_def_id(did).is_some() { + let ty = substs + .iter() + .nth(if has_self { 1 } else { 0 }) + .unwrap() + .map_bound(|arg| arg.expect_ty()); let inputs = // The trait's first substitution is the one after self, if there is one. - match substs.iter().nth(if has_self { 1 } else { 0 }).unwrap().expect_ty().kind() { - ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(t, cx, None)).collect::<Vec<_>>().into(), + match ty.skip_binder().kind() { + ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(ty.rebind(t), cx, None)).collect::<Vec<_>>().into(), _ => return GenericArgs::AngleBracketed { args: args.into(), bindings }, }; let output = bindings.into_iter().next().and_then(|binding| match binding.kind { @@ -130,7 +143,7 @@ pub(super) fn external_path<'tcx>( did: DefId, has_self: bool, bindings: ThinVec<TypeBinding>, - substs: SubstsRef<'tcx>, + substs: ty::Binder<'tcx, SubstsRef<'tcx>>, ) -> Path { let def_kind = cx.tcx.def_kind(did); let name = cx.tcx.item_name(did); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index da0df596c..2153e7d8c 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -225,7 +225,6 @@ pub(crate) fn create_config( // Add the doc cfg into the doc build. cfgs.push("doc".to_string()); - let cpath = Some(input.clone()); let input = Input::File(input); // By default, rustdoc ignores all lints. @@ -277,7 +276,6 @@ pub(crate) fn create_config( crate_cfg: interface::parse_cfgspecs(cfgs), crate_check_cfg: interface::parse_check_cfg(check_cfgs), input, - input_path: cpath, output_file: None, output_dir: None, file_loader: None, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 81d9c4644..c1a652c75 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -95,7 +95,6 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { crate_cfg: interface::parse_cfgspecs(cfgs), crate_check_cfg: interface::parse_check_cfg(options.check_cfgs.clone()), input, - input_path: None, output_file: None, output_dir: None, file_loader: None, @@ -115,9 +114,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { let (tests, unused_extern_reports, compiling_test_count) = interface::run_compiler(config, |compiler| { compiler.enter(|queries| { - let mut global_ctxt = queries.global_ctxt()?.take(); - - let collector = global_ctxt.enter(|tcx| { + let collector = queries.global_ctxt()?.enter(|tcx| { let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID); let opts = scrape_test_config(crate_attrs); @@ -156,9 +153,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { let unused_extern_reports = collector.unused_extern_reports.clone(); let compiling_test_count = collector.compiling_test_count.load(Ordering::SeqCst); - let ret: Result<_, ErrorGuaranteed> = - Ok((collector.tests, unused_extern_reports, compiling_test_count)); - ret + Ok((collector.tests, unused_extern_reports, compiling_test_count)) }) })?; @@ -1225,7 +1220,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { ) { let ast_attrs = self.tcx.hir().attrs(hir_id); if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) { - if !cfg.matches(&self.sess.parse_sess, Some(self.sess.features_untracked())) { + if !cfg.matches(&self.sess.parse_sess, Some(self.tcx.features())) { return; } } diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index c6f1f9de5..656aeefb0 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -37,17 +37,21 @@ pub(crate) trait DocFolder: Sized { i.items = i.items.into_iter().filter_map(|x| self.fold_item(x)).collect(); ImplItem(i) } - VariantItem(i) => match i { - Variant::Struct(mut j) => { - j.fields = j.fields.into_iter().filter_map(|x| self.fold_item(x)).collect(); - VariantItem(Variant::Struct(j)) - } - Variant::Tuple(fields) => { - let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect(); - VariantItem(Variant::Tuple(fields)) - } - Variant::CLike(disr) => VariantItem(Variant::CLike(disr)), - }, + VariantItem(Variant { kind, discriminant }) => { + let kind = match kind { + VariantKind::Struct(mut j) => { + j.fields = j.fields.into_iter().filter_map(|x| self.fold_item(x)).collect(); + VariantKind::Struct(j) + } + VariantKind::Tuple(fields) => { + let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect(); + VariantKind::Tuple(fields) + } + VariantKind::CLike => VariantKind::CLike, + }; + + VariantItem(Variant { kind, discriminant }) + } ExternCrateItem { src: _ } | ImportItem(_) | FunctionItem(_) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index d027fb6e8..1c78c5b8d 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -242,7 +242,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { } // Index this method for searching later on. - if let Some(ref s) = item.name.or_else(|| { + if let Some(s) = item.name.or_else(|| { if item.is_stripped() { None } else if let clean::ImportItem(ref i) = *item.kind && @@ -296,7 +296,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { // for where the type was defined. On the other // hand, `paths` always has the right // information if present. - Some(&(ref fqp, _)) => Some(&fqp[..fqp.len() - 1]), + Some((fqp, _)) => Some(&fqp[..fqp.len() - 1]), None => None, }; ((did, path), true) @@ -317,14 +317,15 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { short_markdown_summary(x.as_str(), &item.link_names(self.cache)) }); let ty = item.type_(); - let name = s.to_string(); - if ty != ItemType::StructField || u16::from_str_radix(&name, 10).is_err() { + if ty != ItemType::StructField + || u16::from_str_radix(s.as_str(), 10).is_err() + { // In case this is a field from a tuple struct, we don't add it into // the search index because its name is something like "0", which is // not useful for rustdoc search. self.cache.search_index.push(IndexItem { ty, - name, + name: s, path: join_with_double_colon(path), desc, parent, diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index f21e60a64..2f1f4cbf3 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -177,6 +177,9 @@ impl ItemType { ItemType::TraitAlias => "traitalias", } } + pub(crate) fn is_method(&self) -> bool { + matches!(*self, ItemType::Method | ItemType::TyMethod) + } } impl fmt::Display for ItemType { diff --git a/src/librustdoc/formats/mod.rs b/src/librustdoc/formats/mod.rs index b236bd7be..e607a16ad 100644 --- a/src/librustdoc/formats/mod.rs +++ b/src/librustdoc/formats/mod.rs @@ -53,12 +53,6 @@ impl Impl { ItemId::Blanket { impl_id, .. } => impl_id, ItemId::Auto { trait_, .. } => trait_, ItemId::DefId(def_id) => def_id, - ItemId::Primitive(_, _) => { - panic!( - "Unexpected ItemId::Primitive in expect_def_id: {:?}", - self.impl_item.item_id - ) - } } } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 39e2a9022..d3dc4065d 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -569,7 +569,7 @@ fn generate_macro_def_id_path( root_path: Option<&str>, ) -> Result<(String, ItemType, Vec<Symbol>), HrefError> { let tcx = cx.shared.tcx; - let crate_name = tcx.crate_name(def_id.krate).to_string(); + let crate_name = tcx.crate_name(def_id.krate); let cache = cx.cache(); let fqp: Vec<Symbol> = tcx @@ -584,7 +584,7 @@ fn generate_macro_def_id_path( } }) .collect(); - let mut relative = fqp.iter().map(|elem| elem.to_string()); + let mut relative = fqp.iter().copied(); let cstore = CStore::from_tcx(tcx); // We need this to prevent a `panic` when this function is used from intra doc links... if !cstore.has_crate_data(def_id.krate) { @@ -602,9 +602,9 @@ fn generate_macro_def_id_path( }; let mut path = if is_macro_2 { - once(crate_name.clone()).chain(relative).collect() + once(crate_name).chain(relative).collect() } else { - vec![crate_name.clone(), relative.next_back().unwrap()] + vec![crate_name, relative.next_back().unwrap()] }; if path.len() < 2 { // The minimum we can have is the crate name followed by the macro name. If shorter, then @@ -614,17 +614,22 @@ fn generate_macro_def_id_path( } if let Some(last) = path.last_mut() { - *last = format!("macro.{}.html", last); + *last = Symbol::intern(&format!("macro.{}.html", last.as_str())); } let url = match cache.extern_locations[&def_id.krate] { ExternalLocation::Remote(ref s) => { // `ExternalLocation::Remote` always end with a `/`. - format!("{}{}", s, path.join("/")) + format!("{}{}", s, path.iter().map(|p| p.as_str()).join("/")) } ExternalLocation::Local => { // `root_path` always end with a `/`. - format!("{}{}/{}", root_path.unwrap_or(""), crate_name, path.join("/")) + format!( + "{}{}/{}", + root_path.unwrap_or(""), + crate_name, + path.iter().map(|p| p.as_str()).join("/") + ) } ExternalLocation::Unknown => { debug!("crate {} not in cache when linkifying macros", crate_name); @@ -957,7 +962,7 @@ fn fmt_type<'cx>( clean::Tuple(ref typs) => { match &typs[..] { &[] => primitive_link(f, PrimitiveType::Unit, "()", cx), - &[ref one] => { + [one] => { if let clean::Generic(name) = one { primitive_link(f, PrimitiveType::Tuple, &format!("({name},)"), cx) } else { @@ -1050,7 +1055,7 @@ fn fmt_type<'cx>( _ => String::new(), }; let m = mutability.print_with_space(); - let amp = if f.alternate() { "&".to_string() } else { "&".to_string() }; + let amp = if f.alternate() { "&" } else { "&" }; match **ty { clean::DynTrait(ref bounds, ref trait_lt) if bounds.len() > 1 || trait_lt.is_some() => @@ -1655,10 +1660,10 @@ impl clean::types::Term { &'a self, cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { - match self { - clean::types::Term::Type(ty) => ty.print(cx), - _ => todo!(), - } + display_fn(move |f| match self { + clean::types::Term::Type(ty) => fmt::Display::fmt(&ty.print(cx), f), + clean::types::Term::Constant(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f), + }) } } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index cd8c8c463..8a9e6caf6 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -21,15 +21,15 @@ use rustc_span::{BytePos, Span, DUMMY_SP}; use super::format::{self, Buffer}; /// This type is needed in case we want to render links on items to allow to go to their definition. -pub(crate) struct HrefContext<'a, 'b, 'c> { - pub(crate) context: &'a Context<'b>, +pub(crate) struct HrefContext<'a, 'tcx> { + pub(crate) context: &'a Context<'tcx>, /// This span contains the current file we're going through. pub(crate) file_span: Span, /// This field is used to know "how far" from the top of the directory we are to link to either /// documentation pages or other source pages. - pub(crate) root_path: &'c str, + pub(crate) root_path: &'a str, /// This field is used to calculate precise local URLs. - pub(crate) current_href: &'c str, + pub(crate) current_href: String, } /// Decorations are represented as a map from CSS class to vector of character ranges. @@ -70,7 +70,7 @@ pub(crate) fn render_source_with_highlighting( src: &str, out: &mut Buffer, line_numbers: Buffer, - href_context: HrefContext<'_, '_, '_>, + href_context: HrefContext<'_, '_>, decoration_info: DecorationInfo, extra: Option<&str>, ) { @@ -137,7 +137,7 @@ fn can_merge(class1: Option<Class>, class2: Option<Class>, text: &str) -> bool { /// This type is used as a conveniency to prevent having to pass all its fields as arguments into /// the various functions (which became its methods). -struct TokenHandler<'a, 'b, 'c, 'd, 'e> { +struct TokenHandler<'a, 'tcx> { out: &'a mut Buffer, /// It contains the closing tag and the associated `Class`. closing_tags: Vec<(&'static str, Class)>, @@ -149,11 +149,11 @@ struct TokenHandler<'a, 'b, 'c, 'd, 'e> { current_class: Option<Class>, /// We need to keep the `Class` for each element because it could contain a `Span` which is /// used to generate links. - pending_elems: Vec<(&'b str, Option<Class>)>, - href_context: Option<HrefContext<'c, 'd, 'e>>, + pending_elems: Vec<(&'a str, Option<Class>)>, + href_context: Option<HrefContext<'a, 'tcx>>, } -impl<'a, 'b, 'c, 'd, 'e> TokenHandler<'a, 'b, 'c, 'd, 'e> { +impl<'a, 'tcx> TokenHandler<'a, 'tcx> { fn handle_exit_span(&mut self) { // We can't get the last `closing_tags` element using `pop()` because `closing_tags` is // being used in `write_pending_elems`. @@ -205,7 +205,7 @@ impl<'a, 'b, 'c, 'd, 'e> TokenHandler<'a, 'b, 'c, 'd, 'e> { } } -impl<'a, 'b, 'c, 'd, 'e> Drop for TokenHandler<'a, 'b, 'c, 'd, 'e> { +impl<'a, 'tcx> Drop for TokenHandler<'a, 'tcx> { /// When leaving, we need to flush all pending data to not have missing content. fn drop(&mut self) { if self.pending_exit_span.is_some() { @@ -230,7 +230,7 @@ impl<'a, 'b, 'c, 'd, 'e> Drop for TokenHandler<'a, 'b, 'c, 'd, 'e> { fn write_code( out: &mut Buffer, src: &str, - href_context: Option<HrefContext<'_, '_, '_>>, + href_context: Option<HrefContext<'_, '_>>, decoration_info: Option<DecorationInfo>, ) { // This replace allows to fix how the code source with DOS backline characters is displayed. @@ -514,18 +514,18 @@ impl Decorations { /// Processes program tokens, classifying strings of text by highlighting /// category (`Class`). -struct Classifier<'a> { - tokens: PeekIter<'a>, +struct Classifier<'src> { + tokens: PeekIter<'src>, in_attribute: bool, in_macro: bool, in_macro_nonterminal: bool, byte_pos: u32, file_span: Span, - src: &'a str, + src: &'src str, decorations: Option<Decorations>, } -impl<'a> Classifier<'a> { +impl<'src> Classifier<'src> { /// Takes as argument the source code to HTML-ify, the rust edition to use and the source code /// file span which will be used later on by the `span_correspondance_map`. fn new(src: &str, file_span: Span, decoration_info: Option<DecorationInfo>) -> Classifier<'_> { @@ -603,7 +603,7 @@ impl<'a> Classifier<'a> { /// /// It returns the token's kind, the token as a string and its byte position in the source /// string. - fn next(&mut self) -> Option<(TokenKind, &'a str, u32)> { + fn next(&mut self) -> Option<(TokenKind, &'src str, u32)> { if let Some((kind, text)) = self.tokens.next() { let before = self.byte_pos; self.byte_pos += text.len() as u32; @@ -618,7 +618,7 @@ impl<'a> Classifier<'a> { /// The general structure for this method is to iterate over each token, /// possibly giving it an HTML span with a class specifying what flavor of /// token is used. - fn highlight(mut self, sink: &mut dyn FnMut(Highlight<'a>)) { + fn highlight(mut self, sink: &mut dyn FnMut(Highlight<'src>)) { loop { if let Some(decs) = self.decorations.as_mut() { let byte_pos = self.byte_pos; @@ -666,8 +666,8 @@ impl<'a> Classifier<'a> { fn advance( &mut self, token: TokenKind, - text: &'a str, - sink: &mut dyn FnMut(Highlight<'a>), + text: &'src str, + sink: &mut dyn FnMut(Highlight<'src>), before: u32, ) { let lookahead = self.peek(); @@ -881,7 +881,7 @@ impl<'a> Classifier<'a> { fn enter_span( out: &mut Buffer, klass: Class, - href_context: &Option<HrefContext<'_, '_, '_>>, + href_context: &Option<HrefContext<'_, '_>>, ) -> &'static str { string_without_closing_tag(out, "", Some(klass), href_context, true).expect( "internal error: enter_span was called with Some(klass) but did not return a \ @@ -914,7 +914,7 @@ fn string<T: Display>( out: &mut Buffer, text: T, klass: Option<Class>, - href_context: &Option<HrefContext<'_, '_, '_>>, + href_context: &Option<HrefContext<'_, '_>>, open_tag: bool, ) { if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context, open_tag) @@ -936,7 +936,7 @@ fn string_without_closing_tag<T: Display>( out: &mut Buffer, text: T, klass: Option<Class>, - href_context: &Option<HrefContext<'_, '_, '_>>, + href_context: &Option<HrefContext<'_, '_>>, open_tag: bool, ) -> Option<&'static str> { let Some(klass) = klass @@ -985,7 +985,7 @@ fn string_without_closing_tag<T: Display>( // https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338 match href { LinkFromSrc::Local(span) => { - context.href_from_span_relative(*span, href_context.current_href) + context.href_from_span_relative(*span, &href_context.current_href) } LinkFromSrc::External(def_id) => { format::href_with_root_path(*def_id, context, Some(href_context.root_path)) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 1e1c657b0..4ff67fe15 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -30,7 +30,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::HirId; use rustc_middle::ty::TyCtxt; use rustc_span::edition::Edition; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use once_cell::sync::Lazy; use std::borrow::Cow; @@ -198,7 +198,7 @@ fn slugify(c: char) -> Option<char> { #[derive(Clone, Debug)] pub struct Playground { - pub crate_name: Option<String>, + pub crate_name: Option<Symbol>, pub url: String, } @@ -236,12 +236,12 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { return event; }; - let mut origtext = String::new(); + let mut original_text = String::new(); for event in &mut self.inner { match event { Event::End(Tag::CodeBlock(..)) => break, Event::Text(ref s) => { - origtext.push_str(s); + original_text.push_str(s); } _ => {} } @@ -258,7 +258,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { <pre class=\"language-{}\"><code>{}</code></pre>\ </div>", lang, - Escape(&origtext), + Escape(&original_text), ) .into(), )); @@ -268,7 +268,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { CodeBlockKind::Indented => Default::default(), }; - let lines = origtext.lines().filter_map(|l| map_line(l).for_html()); + let lines = original_text.lines().filter_map(|l| map_line(l).for_html()); let text = lines.intersperse("\n".into()).collect::<String>(); compile_fail = parse_result.compile_fail; @@ -285,12 +285,12 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { if url.is_empty() { return None; } - let test = origtext + let test = original_text .lines() .map(|l| map_line(l).for_code()) .intersperse("\n".into()) .collect::<String>(); - let krate = krate.as_ref().map(|s| &**s); + let krate = krate.as_ref().map(|s| s.as_str()); let (test, _, _) = doctest::make_test(&test, krate, false, &Default::default(), edition, None); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; @@ -567,11 +567,12 @@ struct SummaryLine<'a, I: Iterator<Item = Event<'a>>> { inner: I, started: bool, depth: u32, + skipped_tags: u32, } impl<'a, I: Iterator<Item = Event<'a>>> SummaryLine<'a, I> { fn new(iter: I) -> Self { - SummaryLine { inner: iter, started: false, depth: 0 } + SummaryLine { inner: iter, started: false, depth: 0, skipped_tags: 0 } } } @@ -601,6 +602,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> { let is_allowed_tag = match event { Event::Start(ref c) => { if is_forbidden_tag(c) { + self.skipped_tags += 1; return None; } self.depth += 1; @@ -608,6 +610,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> { } Event::End(ref c) => { if is_forbidden_tag(c) { + self.skipped_tags += 1; return None; } self.depth -= 1; @@ -616,6 +619,9 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> { } _ => true, }; + if !is_allowed_tag { + self.skipped_tags += 1; + } return if !is_allowed_tag { if is_start { Some(Event::Start(Tag::Paragraph)) @@ -1096,11 +1102,11 @@ impl MarkdownItemInfo<'_> { } impl MarkdownSummaryLine<'_> { - pub(crate) fn into_string(self) -> String { + pub(crate) fn into_string_with_has_more_content(self) -> (String, bool) { let MarkdownSummaryLine(md, links) = self; // This is actually common enough to special-case if md.is_empty() { - return String::new(); + return (String::new(), false); } let mut replacer = |broken_link: BrokenLink<'_>| { @@ -1110,17 +1116,26 @@ impl MarkdownSummaryLine<'_> { .map(|link| (link.href.as_str().into(), link.new_text.as_str().into())) }; - let p = Parser::new_with_broken_link_callback(md, summary_opts(), Some(&mut replacer)); + let p = Parser::new_with_broken_link_callback(md, summary_opts(), Some(&mut replacer)) + .peekable(); + let mut summary = SummaryLine::new(p); let mut s = String::new(); - let without_paragraphs = LinkReplacer::new(SummaryLine::new(p), links).filter(|event| { + let without_paragraphs = LinkReplacer::new(&mut summary, links).filter(|event| { !matches!(event, Event::Start(Tag::Paragraph) | Event::End(Tag::Paragraph)) }); html::push_html(&mut s, without_paragraphs); - s + let has_more_content = + matches!(summary.inner.peek(), Some(Event::Start(_))) || summary.skipped_tags > 0; + + (s, has_more_content) + } + + pub(crate) fn into_string(self) -> String { + self.into_string_with_has_more_content().0 } } diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index 68b31a6ee..5878c5826 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -345,4 +345,12 @@ fn test_ascii_with_prepending_hashtag() { #..#.####.####.####..##.. </code></pre></div>", ); + t( + r#"```markdown +# hello +```"#, + "<div class=\"example-wrap\"><pre class=\"language-markdown\"><code>\ +# hello +</code></pre></div>", + ); } diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 73690c86f..5cefe9475 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -309,7 +309,7 @@ impl<'tcx> Context<'tcx> { pub(crate) fn href_from_span(&self, span: clean::Span, with_lines: bool) -> Option<String> { let mut root = self.root_path(); - let mut path = String::new(); + let mut path: String; let cnum = span.cnum(self.sess()); // We can safely ignore synthetic `SourceFile`s. @@ -340,10 +340,24 @@ impl<'tcx> Context<'tcx> { ExternalLocation::Unknown => return None, }; - sources::clean_path(&src_root, file, false, |component| { - path.push_str(&component.to_string_lossy()); + let href = RefCell::new(PathBuf::new()); + sources::clean_path( + &src_root, + file, + |component| { + href.borrow_mut().push(component); + }, + || { + href.borrow_mut().pop(); + }, + ); + + path = href.into_inner().to_string_lossy().to_string(); + + if let Some(c) = path.as_bytes().last() && *c != b'/' { path.push('/'); - }); + } + let mut fname = file.file_name().expect("source has no filename").to_os_string(); fname.push(".html"); path.push_str(&fname.to_string_lossy()); @@ -450,8 +464,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { // If user passed in `--playground-url` arg, we fill in crate name here let mut playground = None; if let Some(url) = playground_url { - playground = - Some(markdown::Playground { crate_name: Some(krate.name(tcx).to_string()), url }); + playground = Some(markdown::Playground { crate_name: Some(krate.name(tcx)), url }); } let mut layout = layout::Layout { logo: String::new(), @@ -477,7 +490,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { } (sym::html_playground_url, Some(s)) => { playground = Some(markdown::Playground { - crate_name: Some(krate.name(tcx).to_string()), + crate_name: Some(krate.name(tcx)), url: s.to_string(), }); } @@ -625,7 +638,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { write!( buf, "<div class=\"main-heading\">\ - <h1 class=\"fqn\">Rustdoc settings</h1>\ + <h1>Rustdoc settings</h1>\ <span class=\"out-of-band\">\ <a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\ Back\ @@ -637,7 +650,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { You need to enable Javascript be able to update your settings.\ </section>\ </noscript>\ - <link rel=\"stylesheet\" type=\"text/css\" \ + <link rel=\"stylesheet\" \ href=\"{static_root_path}{settings_css}\">\ <script defer src=\"{static_root_path}{settings_js}\"></script>", static_root_path = page.get_static_root_path(), @@ -663,7 +676,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { write!( buf, "<div class=\"main-heading\">\ - <h1 class=\"fqn\">Rustdoc help</h1>\ + <h1>Rustdoc help</h1>\ <span class=\"out-of-band\">\ <a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\ Back\ diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 36d15ec3b..4fa33e890 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -100,7 +100,7 @@ pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { #[derive(Debug)] pub(crate) struct IndexItem { pub(crate) ty: ItemType, - pub(crate) name: String, + pub(crate) name: Symbol, pub(crate) path: String, pub(crate) desc: String, pub(crate) parent: Option<DefId>, @@ -364,7 +364,7 @@ impl AllTypes { } } - f.write_str("<h1 class=\"fqn\">List of all items</h1>"); + 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(f, &self.structs, ItemSection::Structs); @@ -394,7 +394,7 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String { let mut ids = IdMap::default(); format!( "<div class=\"main-heading\">\ - <h1 class=\"fqn\">About scraped examples</h1>\ + <h1>About scraped examples</h1>\ </div>\ <div>{}</div>", Markdown { @@ -467,9 +467,10 @@ fn document_short( return; } if let Some(s) = item.doc_value() { - let mut summary_html = MarkdownSummaryLine(&s, &item.links(cx)).into_string(); + let (mut summary_html, has_more_content) = + MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content(); - if s.contains('\n') { + if has_more_content { let link = format!(r#" <a{}>Read more</a>"#, assoc_href_attr(item, link, cx)); if let Some(idx) = summary_html.rfind("</p>") { @@ -512,7 +513,7 @@ fn document_full_inner( debug!("Doc block: =====\n{}\n=====", s); if is_collapsible { w.write_str( - "<details class=\"rustdoc-toggle top-doc\" open>\ + "<details class=\"toggle top-doc\" open>\ <summary class=\"hideme\">\ <span>Expand description</span>\ </summary>", @@ -1342,7 +1343,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { write!( &mut out, "<h3>Notable traits for <code>{}</code></h3>\ - <pre class=\"content\"><code>", + <pre><code>", impl_.for_.print(cx) ); } @@ -1512,9 +1513,8 @@ fn render_impl( let toggled = !doc_buffer.is_empty(); if toggled { - let method_toggle_class = - if item_type == ItemType::Method { " method-toggle" } else { "" }; - write!(w, "<details class=\"rustdoc-toggle{}\" open><summary>", method_toggle_class); + let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" }; + write!(w, "<details class=\"toggle{}\" open><summary>", method_toggle_class); } match &*item.kind { clean::MethodItem(..) | clean::TyMethodItem(_) => { @@ -1730,7 +1730,7 @@ fn render_impl( close_tags.insert_str(0, "</details>"); write!( w, - "<details class=\"rustdoc-toggle implementors-toggle\"{}>", + "<details class=\"toggle implementors-toggle\"{}>", if rendering_params.toggle_open_by_default { " open" } else { "" } ); write!(w, "<summary>") @@ -2769,8 +2769,8 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> { let mut work = VecDeque::new(); let mut process_path = |did: DefId| { - let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone()); - let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern); + let get_extern = || cache.external_paths.get(&did).map(|s| &s.0); + let fqp = cache.exact_paths.get(&did).or_else(get_extern); if let Some(path) = fqp { out.push(join_with_double_colon(&path)); @@ -2921,7 +2921,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite // Look for the example file in the source map if it exists, otherwise return a dummy span let file_span = (|| { let source_map = tcx.sess.source_map(); - let crate_src = tcx.sess.local_crate_source_file.as_ref()?; + let crate_src = tcx.sess.local_crate_source_file()?; let abs_crate_src = crate_src.canonicalize().ok()?; let crate_root = abs_crate_src.parent()?.parent()?; let rel_path = path.strip_prefix(crate_root).ok()?; @@ -2999,14 +2999,13 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite if it.peek().is_some() { write!( w, - "<details class=\"rustdoc-toggle more-examples-toggle\">\ + "<details class=\"toggle more-examples-toggle\">\ <summary class=\"hideme\">\ <span>More examples</span>\ </summary>\ <div class=\"hide-more\">Hide additional examples</div>\ <div class=\"more-scraped-examples\">\ - <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>\ - <div class=\"more-scraped-examples-inner\">" + <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>" ); // Only generate inline code for MAX_FULL_EXAMPLES number of examples. Otherwise we could @@ -3030,7 +3029,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite write!(w, "</ul></div>"); } - write!(w, "</div></div></details>"); + write!(w, "</div></details>"); } write!(w, "</div>"); diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index acbe3f228..f824c9e3a 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -204,7 +204,7 @@ fn should_hide_fields(n_fields: usize) -> bool { fn toggle_open(w: &mut Buffer, text: impl fmt::Display) { write!( w, - "<details class=\"rustdoc-toggle type-contents-toggle\">\ + "<details class=\"toggle type-contents-toggle\">\ <summary class=\"hideme\">\ <span>Show {}</span>\ </summary>", @@ -531,7 +531,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle f.decl.output.as_return().and_then(|output| notable_traits_button(output, cx)); wrap_into_item_decl(w, |w| { - wrap_item(w, "fn", |w| { + wrap_item(w, |w| { render_attributes_in_pre(w, it, ""); w.reserve(header_len); write!( @@ -570,7 +570,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: // Output the trait definition wrap_into_item_decl(w, |w| { - wrap_item(w, "trait", |w| { + wrap_item(w, |w| { render_attributes_in_pre(w, it, ""); write!( w, @@ -732,7 +732,8 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: document(&mut content, cx, m, Some(t), HeadingOffset::H5); let toggled = !content.is_empty(); if toggled { - write!(w, "<details class=\"rustdoc-toggle method-toggle\" open><summary>"); + let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" }; + write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>"); } write!(w, "<section id=\"{}\" class=\"method has-srclink\">", id); render_rightside(w, cx, m, t, RenderMode::Normal); @@ -1026,8 +1027,8 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: .chain(std::iter::once("implementors")) .collect(); if let Some(did) = it.item_id.as_def_id() && - let get_extern = { || cache.external_paths.get(&did).map(|s| s.0.clone()) } && - let Some(fqp) = cache.exact_paths.get(&did).cloned().or_else(get_extern) { + let get_extern = { || cache.external_paths.get(&did).map(|s| &s.0) } && + let Some(fqp) = cache.exact_paths.get(&did).or_else(get_extern) { js_src_path.extend(fqp[..fqp.len() - 1].iter().copied()); js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), fqp.last().unwrap())); } else { @@ -1050,7 +1051,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TraitAlias) { wrap_into_item_decl(w, |w| { - wrap_item(w, "trait-alias", |w| { + wrap_item(w, |w| { render_attributes_in_pre(w, it, ""); write!( w, @@ -1074,7 +1075,7 @@ fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: & fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { wrap_into_item_decl(w, |w| { - wrap_item(w, "opaque", |w| { + wrap_item(w, |w| { render_attributes_in_pre(w, it, ""); write!( w, @@ -1098,7 +1099,7 @@ fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &cl fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) { fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) { - wrap_item(w, "typedef", |w| { + wrap_item(w, |w| { render_attributes_in_pre(w, it, ""); write!(w, "{}", visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx)); write!( @@ -1127,7 +1128,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) { wrap_into_item_decl(w, |w| { - wrap_item(w, "union", |w| { + wrap_item(w, |w| { render_attributes_in_pre(w, it, ""); render_union(w, it, Some(&s.generics), &s.fields, "", cx); }); @@ -1192,7 +1193,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: let tcx = cx.tcx(); let count_variants = e.variants().count(); wrap_into_item_decl(w, |w| { - wrap_item(w, "enum", |w| { + wrap_item(w, |w| { render_attributes_in_pre(w, it, ""); write!( w, @@ -1219,25 +1220,16 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: w.write_str(" "); let name = v.name.unwrap(); match *v.kind { - clean::VariantItem(ref var) => match var { - // FIXME(#101337): Show discriminant - clean::Variant::CLike(..) => write!(w, "{}", name), - clean::Variant::Tuple(ref s) => { + // FIXME(#101337): Show discriminant + clean::VariantItem(ref var) => match var.kind { + clean::VariantKind::CLike => write!(w, "{}", name), + clean::VariantKind::Tuple(ref s) => { write!(w, "{}(", name); print_tuple_struct_fields(w, cx, s); w.write_str(")"); } - clean::Variant::Struct(ref s) => { - render_struct( - w, - v, - None, - s.ctor_kind, - &s.fields, - " ", - false, - cx, - ); + clean::VariantKind::Struct(ref s) => { + render_struct(w, v, None, None, &s.fields, " ", false, cx); } }, _ => unreachable!(), @@ -1285,25 +1277,28 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: " rightside", ); write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap()); - if let clean::VariantItem(clean::Variant::Tuple(ref s)) = *variant.kind { + + let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() }; + + if let clean::VariantKind::Tuple(ref s) = variant_data.kind { w.write_str("("); print_tuple_struct_fields(w, cx, s); w.write_str(")"); } w.write_str("</h3></section>"); - use crate::clean::Variant; - - let heading_and_fields = match &*variant.kind { - clean::VariantItem(Variant::Struct(s)) => Some(("Fields", &s.fields)), - // Documentation on tuple variant fields is rare, so to reduce noise we only emit - // the section if at least one field is documented. - clean::VariantItem(Variant::Tuple(fields)) - if fields.iter().any(|f| f.doc_value().is_some()) => - { - Some(("Tuple Fields", fields)) + let heading_and_fields = match &variant_data.kind { + clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)), + clean::VariantKind::Tuple(fields) => { + // Documentation on tuple variant fields is rare, so to reduce noise we only emit + // the section if at least one field is documented. + if fields.iter().any(|f| f.doc_value().is_some()) { + Some(("Tuple Fields", fields)) + } else { + None + } } - _ => None, + clean::VariantKind::CLike => None, }; if let Some((heading, fields)) = heading_and_fields { @@ -1362,17 +1357,17 @@ fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &c let name = it.name.expect("proc-macros always have names"); match m.kind { MacroKind::Bang => { - wrap_item(w, "macro", |w| { + wrap_item(w, |w| { write!(w, "{}!() {{ /* proc-macro */ }}", name); }); } MacroKind::Attr => { - wrap_item(w, "attr", |w| { + wrap_item(w, |w| { write!(w, "#[{}]", name); }); } MacroKind::Derive => { - wrap_item(w, "derive", |w| { + wrap_item(w, |w| { write!(w, "#[derive({})]", name); if !m.helpers.is_empty() { w.push_str("\n{\n"); @@ -1406,7 +1401,7 @@ fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) { wrap_into_item_decl(w, |w| { - wrap_item(w, "const", |w| { + wrap_item(w, |w| { let tcx = cx.tcx(); render_attributes_in_code(w, it); @@ -1456,7 +1451,7 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) { wrap_into_item_decl(w, |w| { - wrap_item(w, "struct", |w| { + wrap_item(w, |w| { render_attributes_in_code(w, it); render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx); }); @@ -1509,7 +1504,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) { wrap_into_item_decl(w, |w| { - wrap_item(w, "static", |w| { + wrap_item(w, |w| { render_attributes_in_code(w, it); write!( w, @@ -1526,7 +1521,7 @@ fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { wrap_into_item_decl(w, |w| { - wrap_item(w, "foreigntype", |w| { + wrap_item(w, |w| { w.write_str("extern {\n"); render_attributes_in_code(w, it); write!( @@ -1623,11 +1618,11 @@ where w.write_str("</div>") } -fn wrap_item<F>(w: &mut Buffer, item_name: &str, f: F) +fn wrap_item<F>(w: &mut Buffer, f: F) where F: FnOnce(&mut Buffer), { - w.write_fmt(format_args!("<pre class=\"rust {}\"><code>", item_name)); + w.write_str(r#"<pre class="rust"><code>"#); f(w); w.write_str("</code></pre>"); } @@ -1845,7 +1840,7 @@ fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) { if item.is_non_exhaustive() { write!( w, - "<details class=\"rustdoc-toggle non-exhaustive\">\ + "<details class=\"toggle non-exhaustive\">\ <summary class=\"hideme\"><span>{}</span></summary>\ <div class=\"docblock\">", { diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 8eb9c07f8..5b0caac09 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -3,7 +3,6 @@ use std::collections::BTreeMap; use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::TyCtxt; -use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::Symbol; use serde::ser::{Serialize, SerializeStruct, Serializer}; @@ -24,18 +23,19 @@ pub(crate) fn build_index<'tcx>( tcx: TyCtxt<'tcx>, ) -> String { let mut itemid_to_pathid = FxHashMap::default(); + let mut primitives = FxHashMap::default(); let mut crate_paths = vec![]; // Attach all orphan items to the type's definition if the type // has since been learned. for &OrphanImplItem { parent, ref item, ref impl_generics } in &cache.orphan_impl_items { - if let Some(&(ref fqp, _)) = cache.paths.get(&parent) { + if let Some((fqp, _)) = cache.paths.get(&parent) { let desc = item .doc_value() .map_or_else(String::new, |s| short_markdown_summary(&s, &item.link_names(cache))); cache.search_index.push(IndexItem { ty: item.type_(), - name: item.name.unwrap().to_string(), + name: item.name.unwrap(), path: join_with_double_colon(&fqp[..fqp.len() - 1]), desc, parent: Some(parent), @@ -58,8 +58,8 @@ pub(crate) fn build_index<'tcx>( // Sort search index items. This improves the compressibility of the search index. cache.search_index.sort_unstable_by(|k1, k2| { // `sort_unstable_by_key` produces lifetime errors - let k1 = (&k1.path, &k1.name, &k1.ty, &k1.parent); - let k2 = (&k2.path, &k2.name, &k2.ty, &k2.parent); + let k1 = (&k1.path, k1.name.as_str(), &k1.ty, &k1.parent); + let k2 = (&k2.path, k2.name.as_str(), &k2.ty, &k2.parent); std::cmp::Ord::cmp(&k1, &k2) }); @@ -78,16 +78,45 @@ pub(crate) fn build_index<'tcx>( // First, on function signatures let mut search_index = std::mem::replace(&mut cache.search_index, Vec::new()); for item in search_index.iter_mut() { + fn insert_into_map<F: std::hash::Hash + Eq>( + ty: &mut RenderType, + map: &mut FxHashMap<F, usize>, + itemid: F, + lastpathid: &mut usize, + crate_paths: &mut Vec<(ItemType, Symbol)>, + item_type: ItemType, + path: Symbol, + ) { + match map.entry(itemid) { + Entry::Occupied(entry) => ty.id = Some(RenderTypeId::Index(*entry.get())), + Entry::Vacant(entry) => { + let pathid = *lastpathid; + entry.insert(pathid); + *lastpathid += 1; + crate_paths.push((item_type, path)); + ty.id = Some(RenderTypeId::Index(pathid)); + } + } + } + fn convert_render_type( ty: &mut RenderType, cache: &mut Cache, itemid_to_pathid: &mut FxHashMap<ItemId, usize>, + primitives: &mut FxHashMap<Symbol, usize>, lastpathid: &mut usize, crate_paths: &mut Vec<(ItemType, Symbol)>, ) { if let Some(generics) = &mut ty.generics { for item in generics { - convert_render_type(item, cache, itemid_to_pathid, lastpathid, crate_paths); + convert_render_type( + item, + cache, + itemid_to_pathid, + primitives, + lastpathid, + crate_paths, + ); } } let Cache { ref paths, ref external_paths, .. } = *cache; @@ -95,33 +124,37 @@ pub(crate) fn build_index<'tcx>( assert!(ty.generics.is_some()); return; }; - let (itemid, path, item_type) = match id { + match id { RenderTypeId::DefId(defid) => { if let Some(&(ref fqp, item_type)) = paths.get(&defid).or_else(|| external_paths.get(&defid)) { - (ItemId::DefId(defid), *fqp.last().unwrap(), item_type) + insert_into_map( + ty, + itemid_to_pathid, + ItemId::DefId(defid), + lastpathid, + crate_paths, + item_type, + *fqp.last().unwrap(), + ); } else { ty.id = None; - return; } } - RenderTypeId::Primitive(primitive) => ( - ItemId::Primitive(primitive, LOCAL_CRATE), - primitive.as_sym(), - ItemType::Primitive, - ), - RenderTypeId::Index(_) => return, - }; - match itemid_to_pathid.entry(itemid) { - Entry::Occupied(entry) => ty.id = Some(RenderTypeId::Index(*entry.get())), - Entry::Vacant(entry) => { - let pathid = *lastpathid; - entry.insert(pathid); - *lastpathid += 1; - crate_paths.push((item_type, path)); - ty.id = Some(RenderTypeId::Index(pathid)); + RenderTypeId::Primitive(primitive) => { + let sym = primitive.as_sym(); + insert_into_map( + ty, + primitives, + sym, + lastpathid, + crate_paths, + ItemType::Primitive, + sym, + ); } + RenderTypeId::Index(_) => {} } } if let Some(search_type) = &mut item.search_type { @@ -130,6 +163,7 @@ pub(crate) fn build_index<'tcx>( item, cache, &mut itemid_to_pathid, + &mut primitives, &mut lastpathid, &mut crate_paths, ); @@ -139,6 +173,7 @@ pub(crate) fn build_index<'tcx>( item, cache, &mut itemid_to_pathid, + &mut primitives, &mut lastpathid, &mut crate_paths, ); @@ -205,7 +240,7 @@ pub(crate) fn build_index<'tcx>( )?; crate_data.serialize_field( "n", - &self.items.iter().map(|item| &item.name).collect::<Vec<_>>(), + &self.items.iter().map(|item| item.name.as_str()).collect::<Vec<_>>(), )?; crate_data.serialize_field( "q", @@ -264,7 +299,7 @@ pub(crate) fn build_index<'tcx>( )?; crate_data.serialize_field( "p", - &self.paths.iter().map(|(it, s)| (it, s.to_string())).collect::<Vec<_>>(), + &self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::<Vec<_>>(), )?; if has_aliases { crate_data.serialize_field("a", &self.aliases)?; @@ -322,8 +357,7 @@ fn get_index_type_id(clean_type: &clean::Type) -> Option<RenderTypeId> { match *clean_type { clean::Type::Path { ref path, .. } => Some(RenderTypeId::DefId(path.def_id())), clean::DynTrait(ref bounds, _) => { - let path = &bounds[0].trait_; - Some(RenderTypeId::DefId(path.def_id())) + bounds.get(0).map(|b| RenderTypeId::DefId(b.trait_.def_id())) } clean::Primitive(p) => Some(RenderTypeId::Primitive(p)), clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => { @@ -539,7 +573,7 @@ fn get_fn_inputs_and_outputs<'tcx>( let decl = &func.decl; let combined_generics; - let (self_, generics) = if let Some(&(ref impl_self, ref impl_generics)) = impl_generics { + let (self_, generics) = if let Some((impl_self, impl_generics)) = impl_generics { match (impl_generics.is_empty(), func.generics.is_empty()) { (true, _) => (Some(impl_self), &func.generics), (_, true) => (Some(impl_self), impl_generics), diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 94d8a9fec..bc8badad3 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -1,8 +1,9 @@ +use std::cell::RefCell; use std::fs::{self, File}; use std::io::prelude::*; use std::io::{self, BufReader}; use std::path::{Component, Path}; -use std::rc::Rc; +use std::rc::{Rc, Weak}; use itertools::Itertools; use rustc_data_structures::flock; @@ -184,23 +185,26 @@ pub(super) fn write_shared( use std::ffi::OsString; - #[derive(Debug)] + #[derive(Debug, Default)] struct Hierarchy { + parent: Weak<Self>, elem: OsString, - children: FxHashMap<OsString, Hierarchy>, - elems: FxHashSet<OsString>, + children: RefCell<FxHashMap<OsString, Rc<Self>>>, + elems: RefCell<FxHashSet<OsString>>, } impl Hierarchy { - fn new(elem: OsString) -> Hierarchy { - Hierarchy { elem, children: FxHashMap::default(), elems: FxHashSet::default() } + fn with_parent(elem: OsString, parent: &Rc<Self>) -> Self { + Self { elem, parent: Rc::downgrade(parent), ..Self::default() } } fn to_json_string(&self) -> String { - let mut subs: Vec<&Hierarchy> = self.children.values().collect(); + let borrow = self.children.borrow(); + let mut subs: Vec<_> = borrow.values().collect(); subs.sort_unstable_by(|a, b| a.elem.cmp(&b.elem)); let mut files = self .elems + .borrow() .iter() .map(|s| format!("\"{}\"", s.to_str().expect("invalid osstring conversion"))) .collect::<Vec<_>>(); @@ -220,36 +224,52 @@ pub(super) fn write_shared( files = files ) } - } - if cx.include_sources { - let mut hierarchy = Hierarchy::new(OsString::new()); - for source in cx - .shared - .local_sources - .iter() - .filter_map(|p| p.0.strip_prefix(&cx.shared.src_root).ok()) - { - let mut h = &mut hierarchy; - let mut elems = source + fn add_path(self: &Rc<Self>, path: &Path) { + let mut h = Rc::clone(&self); + let mut elems = path .components() .filter_map(|s| match s { Component::Normal(s) => Some(s.to_owned()), + Component::ParentDir => Some(OsString::from("..")), _ => None, }) .peekable(); loop { let cur_elem = elems.next().expect("empty file path"); + if cur_elem == ".." { + if let Some(parent) = h.parent.upgrade() { + h = parent; + } + continue; + } if elems.peek().is_none() { - h.elems.insert(cur_elem); + h.elems.borrow_mut().insert(cur_elem); break; } else { - let e = cur_elem.clone(); - h = h.children.entry(cur_elem.clone()).or_insert_with(|| Hierarchy::new(e)); + let entry = Rc::clone( + h.children + .borrow_mut() + .entry(cur_elem.clone()) + .or_insert_with(|| Rc::new(Self::with_parent(cur_elem, &h))), + ); + h = entry; } } } + } + if cx.include_sources { + let hierarchy = Rc::new(Hierarchy::default()); + for source in cx + .shared + .local_sources + .iter() + .filter_map(|p| p.0.strip_prefix(&cx.shared.src_root).ok()) + { + hierarchy.add_path(source); + } + let hierarchy = Rc::try_unwrap(hierarchy).unwrap(); let dst = cx.dst.join(&format!("source-files{}.js", cx.shared.resource_suffix)); let make_sources = || { let (mut all_sources, _krates) = @@ -325,7 +345,7 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; }; let content = format!( - "<h1 class=\"fqn\">List of all crates</h1><ul class=\"all-items\">{}</ul>", + "<h1>List of all crates</h1><ul class=\"all-items\">{}</ul>", krates .iter() .map(|s| { diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 54e296959..799c497d1 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -13,6 +13,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::source_map::FileName; +use std::cell::RefCell; use std::ffi::OsStr; use std::fs; use std::path::{Component, Path, PathBuf}; @@ -72,12 +73,22 @@ impl LocalSourcesCollector<'_, '_> { return; } - let mut href = String::new(); - clean_path(self.src_root, &p, false, |component| { - href.push_str(&component.to_string_lossy()); - href.push('/'); - }); + let href = RefCell::new(PathBuf::new()); + clean_path( + &self.src_root, + &p, + |component| { + href.borrow_mut().push(component); + }, + || { + href.borrow_mut().pop(); + }, + ); + let mut href = href.into_inner().to_string_lossy().to_string(); + if let Some(c) = href.as_bytes().last() && *c != b'/' { + href.push('/'); + } let mut src_fname = p.file_name().expect("source has no filename").to_os_string(); src_fname.push(".html"); href.push_str(&src_fname.to_string_lossy()); @@ -180,13 +191,28 @@ impl SourceCollector<'_, '_> { let shared = Rc::clone(&self.cx.shared); // Create the intermediate directories - let mut cur = self.dst.clone(); - let mut root_path = String::from("../../"); - clean_path(&shared.src_root, &p, false, |component| { - cur.push(component); - root_path.push_str("../"); - }); + let cur = RefCell::new(PathBuf::new()); + let root_path = RefCell::new(PathBuf::new()); + + clean_path( + &shared.src_root, + &p, + |component| { + cur.borrow_mut().push(component); + root_path.borrow_mut().push(".."); + }, + || { + cur.borrow_mut().pop(); + root_path.borrow_mut().pop(); + }, + ); + let root_path = PathBuf::from("../../").join(root_path.into_inner()); + let mut root_path = root_path.to_string_lossy(); + if let Some(c) = root_path.as_bytes().last() && *c != b'/' { + root_path += "/"; + } + let mut cur = self.dst.join(cur.into_inner()); shared.ensure_dir(&cur)?; let src_fname = p.file_name().expect("source has no filename").to_os_string(); @@ -232,11 +258,13 @@ impl SourceCollector<'_, '_> { /// Takes a path to a source file and cleans the path to it. This canonicalizes /// things like ".." to components which preserve the "top down" hierarchy of a /// static HTML tree. Each component in the cleaned path will be passed as an -/// argument to `f`. The very last component of the path (ie the file name) will -/// be passed to `f` if `keep_filename` is true, and ignored otherwise. -pub(crate) fn clean_path<F>(src_root: &Path, p: &Path, keep_filename: bool, mut f: F) +/// argument to `f`. The very last component of the path (ie the file name) is ignored. +/// If a `..` is encountered, the `parent` closure will be called to allow the callee to +/// handle it. +pub(crate) fn clean_path<F, P>(src_root: &Path, p: &Path, mut f: F, mut parent: P) where F: FnMut(&OsStr), + P: FnMut(), { // make it relative, if possible let p = p.strip_prefix(src_root).unwrap_or(p); @@ -244,12 +272,12 @@ where let mut iter = p.components().peekable(); while let Some(c) = iter.next() { - if !keep_filename && iter.peek().is_none() { + if iter.peek().is_none() { break; } match c { - Component::ParentDir => f("up".as_ref()), + Component::ParentDir => parent(), Component::Normal(c) => f(c), _ => continue, } @@ -276,7 +304,7 @@ pub(crate) fn print_src( let mut line_numbers = Buffer::empty_from(buf); let extra; line_numbers.write_str("<pre class=\"src-line-numbers\">"); - let current_href = &context + let current_href = context .href_from_span(clean::Span::new(file_span), false) .expect("only local crates should have sources emitted"); match source_context { diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index afcb40224..a93f60da2 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -76,8 +76,6 @@ } * { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; box-sizing: border-box; } @@ -110,11 +108,7 @@ body { /* Then override it with `anywhere`, which is required to make non-Safari browsers break more aggressively when we want them to. */ overflow-wrap: anywhere; - - -webkit-font-feature-settings: "kern", "liga"; - -moz-font-feature-settings: "kern", "liga"; font-feature-settings: "kern", "liga"; - background-color: var(--main-background-color); color: var(--main-color); } @@ -145,7 +139,7 @@ h1, h2, h3, h4 { .docblock > h6:first-child { margin-top: 0; } -h1.fqn { +.main-heading h1 { margin: 0; padding: 0; flex-grow: 1; @@ -212,7 +206,7 @@ pre.rust a, .mobile-topbar h2 a, h1 a, .search-results a, -.item-left .stab, +.stab, .result-name .primitive > i, .result-name .keyword > i { color: var(--main-color); } @@ -259,6 +253,7 @@ h1 a, a { color: var(--link-color); + text-decoration: none; } ol, ul { @@ -322,11 +317,7 @@ main { margin-right: auto; } -.source .width-limiter { - max-width: unset; -} - -details:not(.rustdoc-toggle) summary { +details:not(.toggle) summary { margin-bottom: .6em; } @@ -342,26 +333,35 @@ code, pre, a.test-arrow, .code-header { } pre { padding: 14px; + line-height: 1.5; /* https://github.com/rust-lang/rust/issues/105906 */ } .item-decl pre { overflow-x: auto; } +/* This rule allows to have scrolling on the X axis. */ +.item-decl .type-contents-toggle { + contain: initial; +} .source .content pre { padding: 20px; } +.rustdoc.source .example-wrap > pre.src-line-numbers { + padding: 20px 0 20px 4px; +} img { max-width: 100%; } -.source .content { - overflow: visible; -} - .sub-logo-container, .logo-container { /* zero text boxes so that computed line height = image height exactly */ line-height: 0; + display: block; +} + +.sub-logo-container { + margin-right: 32px; } .sub-logo-container > img { @@ -374,10 +374,6 @@ img { filter: var(--rust-logo-filter); } -.sidebar, .mobile-topbar, .sidebar-menu-toggle { - background-color: var(--sidebar-background-color); -} - .sidebar { font-size: 0.875rem; flex: 0 0 200px; @@ -396,15 +392,16 @@ img { overflow-y: hidden; } -.source .sidebar, #sidebar-toggle, #source-sidebar { +.sidebar, .mobile-topbar, .sidebar-menu-toggle, +#src-sidebar-toggle, #source-sidebar { background-color: var(--sidebar-background-color); } -#sidebar-toggle > button:hover, #sidebar-toggle > button:focus { +#src-sidebar-toggle > button:hover, #src-sidebar-toggle > button:focus { background-color: var(--sidebar-background-color-hover); } -.source .sidebar > *:not(#sidebar-toggle) { +.source .sidebar > *:not(#src-sidebar-toggle) { visibility: hidden; } @@ -413,7 +410,7 @@ img { flex-basis: 300px; } -.source-sidebar-expanded .source .sidebar > *:not(#sidebar-toggle) { +.source-sidebar-expanded .source .sidebar > *:not(#src-sidebar-toggle) { visibility: visible; } @@ -500,11 +497,11 @@ ul.block, .block li { padding-left: 24px; } -.sidebar a, .sidebar .current { +.sidebar a { color: var(--sidebar-link-color); } .sidebar .current, -.sidebar a:hover { +.sidebar a:hover:not(.logo-container) { background-color: var(--sidebar-current-link-background-color); } @@ -522,11 +519,6 @@ ul.block, .block li { display: none; } -.source .content pre.rust { - overflow: auto; - padding-left: 0; -} - .rustdoc .example-wrap { display: flex; position: relative; @@ -547,29 +539,26 @@ ul.block, .block li { .rustdoc .example-wrap > pre.example-line-numbers, .rustdoc .example-wrap > pre.src-line-numbers { flex-grow: 0; + min-width: fit-content; /* prevent collapsing into nothing in truncated scraped examples */ overflow: initial; text-align: right; -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; user-select: none; + padding: 14px 8px; + color: var(--src-line-numbers-span-color); } -.example-line-numbers { - border: 1px solid; - padding: 13px 8px; - border-top-left-radius: 5px; - border-bottom-left-radius: 5px; - border-color: var(--example-line-numbers-border-color); +.rustdoc .example-wrap > pre.src-line-numbers { + padding: 14px 0; } - .src-line-numbers a, .src-line-numbers span { color: var(--src-line-numbers-span-color); + padding: 0 8px; } .src-line-numbers :target { background-color: transparent; border-right: none; - padding-right: 0; + padding: 0 8px; } .src-line-numbers .line-highlighted { background-color: var(--src-line-number-highlighted-background-color); @@ -582,12 +571,10 @@ ul.block, .block li { .docblock-short { overflow-wrap: break-word; overflow-wrap: anywhere; - overflow: hidden; - text-overflow: ellipsis; } /* Wrap non-pre code blocks (`text`) but not (```text```). */ -.docblock > :not(pre) > code, -.docblock-short > code { +.docblock :not(pre) > code, +.docblock-short code { white-space: pre-wrap; } @@ -679,13 +666,6 @@ nav.sub { .source nav.sub { margin: 0 0 15px 0; } -.source .search-form { - margin-left: 32px; -} - -a { - text-decoration: none; -} .small-section-header { /* fields use <span> tags, but should get their own lines */ @@ -693,14 +673,10 @@ a { position: relative; } -.small-section-header:hover > .anchor { +.small-section-header:hover > .anchor, .impl:hover > .anchor, +.trait-impl:hover > .anchor, .variant:hover > .anchor { display: initial; } - -.impl:hover > .anchor, .trait-impl:hover > .anchor, .variant:hover > .anchor { - display: inline-block; - position: absolute; -} .anchor { display: none; position: absolute; @@ -731,7 +707,7 @@ h2.small-section-header > .anchor { /* In most contexts we use `overflow-wrap: anywhere` to ensure that we can wrap as much as needed on mobile (see - src/test/rustdoc-gui/type-declaration-overflow.goml for an example of why + tests/rustdoc-gui/type-declaration-overflow.goml for an example of why this matters). The `anywhere` value means: "Soft wrap opportunities introduced by the word break are considered when @@ -903,10 +879,11 @@ so that we can apply CSS-filters to change the arrow color in themes */ top: 100%; right: 0; z-index: 2; - display: block; margin-top: 7px; border-radius: 3px; border: 1px solid var(--border-color); + background-color: var(--main-background-color); + color: var(--main-color); --popover-arrow-offset: 11px; } @@ -917,16 +894,12 @@ so that we can apply CSS-filters to change the arrow color in themes */ right: var(--popover-arrow-offset); border: solid var(--border-color); border-width: 1px 1px 0 0; + background-color: var(--main-background-color); padding: 4px; transform: rotate(-45deg); top: -5px; } -.popover, .popover::before { - background-color: var(--main-background-color); - color: var(--main-color); -} - /* use larger max-width for help popover, but not for help.html */ #help.popover { max-width: 600px; @@ -960,22 +933,29 @@ so that we can apply CSS-filters to change the arrow color in themes */ } .item-info .stab { - width: fit-content; /* This min-height is needed to unify the height of the stab elements because some of them have emojis. */ min-height: 36px; display: flex; - align-items: center; - white-space: pre-wrap; -} -.stab { padding: 3px; margin-bottom: 5px; +} +.item-left .stab { + margin-left: 0.3125em; +} +.stab { + padding: 0 2px; font-size: 0.875rem; font-weight: normal; color: var(--main-color); background-color: var(--stab-background-color); + width: fit-content; + align-items: center; + white-space: pre-wrap; + border-radius: 3px; + display: inline-flex; + vertical-align: text-bottom; } .stab.portability > code { @@ -988,12 +968,6 @@ so that we can apply CSS-filters to change the arrow color in themes */ margin-right: 0.3rem; } -/* This is to prevent the `.stab` elements to overflow the .docblock elements. */ -.docblock .stab { - padding: 0 0.125em; - margin-bottom: 0; -} - /* Black one-pixel outline around emoji shapes */ .emoji { text-shadow: @@ -1003,16 +977,6 @@ so that we can apply CSS-filters to change the arrow color in themes */ 0 -1px 0 black; } -.item-left .stab { - border-radius: 3px; - display: inline-block; - line-height: 1.2; - margin-bottom: 0; - margin-left: 0.3125em; - padding: 2px; - vertical-align: text-bottom; -} - .module-item.unstable, .import-item.unstable { opacity: 0.65; @@ -1084,6 +1048,10 @@ pre.rust .doccomment { color: var(--code-highlight-doc-comment-color); } +.rustdoc.source .example-wrap pre.rust a { + background: var(--codeblock-link-background); +} + .example-wrap.compile_fail, .example-wrap.should_panic { border-left: 2px solid var(--codeblock-error-color); @@ -1127,9 +1095,7 @@ pre.rust .doccomment { top: 5px; } -.example-wrap .tooltip::after { - display: none; - text-align: center; +.example-wrap .tooltip:hover::after { padding: 5px 3px 3px 3px; border-radius: 6px; margin-left: 5px; @@ -1143,35 +1109,30 @@ pre.rust .doccomment { color: var(--tooltip-color); } -.example-wrap .tooltip::before { +.example-wrap .tooltip:hover::before { content: " "; position: absolute; top: 50%; left: 16px; margin-top: -5px; - display: none; z-index: 1; border: 5px solid transparent; border-right-color: var(--tooltip-background-color); } -.example-wrap.ignore .tooltip::after { +.example-wrap.ignore .tooltip:hover::after { content: "This example is not tested"; } -.example-wrap.compile_fail .tooltip::after { +.example-wrap.compile_fail .tooltip:hover::after { content: "This example deliberately fails to compile"; } -.example-wrap.should_panic .tooltip::after { +.example-wrap.should_panic .tooltip:hover::after { content: "This example panics"; } -.example-wrap.edition .tooltip::after { +.example-wrap.edition .tooltip:hover::after { content: "This code runs with edition " attr(data-edition); } -.example-wrap .tooltip:hover::before, .example-wrap .tooltip:hover::after { - display: inline; -} - .example-wrap.compile_fail .tooltip, .example-wrap.should_panic .tooltip, .example-wrap.ignore .tooltip { @@ -1253,11 +1214,11 @@ a.test-arrow:hover { content: "\00a0"; } -.notable .docblock { +.notable .content { margin: 0.25em 0.5em; } -.notable .docblock pre, .notable .docblock code { +.notable .content pre, .notable .content code { background: transparent; margin: 0; padding: 0; @@ -1265,6 +1226,10 @@ a.test-arrow:hover { white-space: pre-wrap; } +.notable .content > h3:first-child { + margin: 0 0 5px 0; +} + .search-failed { text-align: center; margin-top: 20px; @@ -1282,14 +1247,14 @@ a.test-arrow:hover { margin-right: auto; } -#titles { +#search-tabs { display: flex; flex-direction: row; gap: 1px; margin-bottom: 4px; } -#titles > button { +#search-tabs button { text-align: center; font-size: 1.125rem; border: 0; @@ -1299,12 +1264,12 @@ a.test-arrow:hover { color: inherit; } -#titles > button > div.count { - display: inline-block; +#search-tabs .count { font-size: 1rem; + color: var(--search-tab-title-count-color); } -#sidebar-toggle { +#src-sidebar-toggle { position: sticky; top: 0; left: 0; @@ -1333,7 +1298,7 @@ a.test-arrow:hover { #source-sidebar div.files > a.selected { background-color: var(--source-sidebar-background-selected); } -#sidebar-toggle > button { +#src-sidebar-toggle > button { font-size: inherit; font-weight: bold; background: none; @@ -1346,15 +1311,11 @@ a.test-arrow:hover { -webkit-appearance: none; opacity: 1; } + #settings-menu, #help-button { margin-left: 4px; display: flex; } - -#settings-menu > a, #help-button > a, #copy-path { - width: 33px; -} - #settings-menu > a, #help-button > a { display: flex; align-items: center; @@ -1366,6 +1327,7 @@ a.test-arrow:hover { /* Rare exception to specifying font sizes in rem. Since this is acting as an icon, it's okay to specify their sizes in pixels. */ font-size: 20px; + width: 33px; } #settings-menu > a:hover, #settings-menu > a:focus, @@ -1381,6 +1343,7 @@ a.test-arrow:hover { padding: 0; padding-left: 2px; border: 0; + width: 33px; } #copy-path > img { filter: var(--copy-path-img-filter); @@ -1409,8 +1372,7 @@ kbd { vertical-align: middle; border: solid 1px var(--border-color); border-radius: 3px; - cursor: default; - color: var(--kbd--color); + color: var(--kbd-color); background-color: var(--kbd-background); box-shadow: inset 0 -1px 0 var(--kbd-box-shadow-color); } @@ -1423,31 +1385,10 @@ details.dir-entry { padding-left: 4px; } -details.dir-entry > summary::after { - content: " ►"; - position: absolute; - left: -15px; - top: 0px; - font-size: 80%; - padding: 2px 0px; - /* set width to cover gap between arrow and text */ - width: 25px; -} - -details[open].dir-entry > summary::after { - content: " ▼"; -} - -details.dir-entry > summary::-webkit-details-marker, -details.dir-entry > summary::marker { - display: none; -} - details.dir-entry > summary { - margin: 0 0 0 13px; - list-style: none; + margin: 0 0 0 -4px; + padding: 0 0 0 4px; cursor: pointer; - position: relative; } details.dir-entry div.folders, details.dir-entry div.files { @@ -1464,7 +1405,7 @@ details.dir-entry a { Unfortunately we can't yet specify contain: content or contain: strict because the [-]/[+] toggles extend past the boundaries of the <details> https://developer.mozilla.org/en-US/docs/Web/CSS/contain */ -details.rustdoc-toggle { +details.toggle { contain: layout; position: relative; } @@ -1472,26 +1413,26 @@ details.rustdoc-toggle { /* The hideme class is used on summary tags that contain a span with placeholder text shown only when the toggle is closed. For instance, "Expand description" or "Show methods". */ -details.rustdoc-toggle > summary.hideme { +details.toggle > summary.hideme { cursor: pointer; font-size: 1rem; } -details.rustdoc-toggle > summary { +details.toggle > summary { list-style: none; /* focus outline is shown on `::before` instead of this */ outline: none; } -details.rustdoc-toggle > summary::-webkit-details-marker, -details.rustdoc-toggle > summary::marker { +details.toggle > summary::-webkit-details-marker, +details.toggle > summary::marker { display: none; } -details.rustdoc-toggle > summary.hideme > span { +details.toggle > summary.hideme > span { margin-left: 9px; } -details.rustdoc-toggle > summary::before { +details.toggle > summary::before { background: url("toggle-plus-1092eb4930d581b0.svg") no-repeat top left; content: ""; cursor: pointer; @@ -1503,14 +1444,14 @@ details.rustdoc-toggle > summary::before { filter: var(--toggle-filter); } -details.rustdoc-toggle > summary.hideme > span, +details.toggle > summary.hideme > span, .more-examples-toggle summary, .more-examples-toggle .hide-more { color: var(--toggles-color); } /* Screen readers see the text version at the end the line. Visual readers see the icon at the start of the line, but small and transparent. */ -details.rustdoc-toggle > summary::after { +details.toggle > summary::after { content: "Expand"; overflow: hidden; width: 0; @@ -1518,17 +1459,17 @@ details.rustdoc-toggle > summary::after { position: absolute; } -details.rustdoc-toggle > summary.hideme::after { +details.toggle > summary.hideme::after { /* "hideme" toggles already have a description when they're contracted */ content: ""; } -details.rustdoc-toggle > summary:focus::before, -details.rustdoc-toggle > summary:hover::before { +details.toggle > summary:focus::before, +details.toggle > summary:hover::before { opacity: 1; } -details.rustdoc-toggle > summary:focus-visible::before { +details.toggle > summary:focus-visible::before { /* The SVG is black, and gets turned white using a filter in the dark themes. Do the same with the outline. The dotted 1px style is copied from Firefox's focus ring style. @@ -1541,17 +1482,17 @@ details.non-exhaustive { margin-bottom: 8px; } -details.rustdoc-toggle > summary.hideme::before { +details.toggle > summary.hideme::before { position: relative; } -details.rustdoc-toggle > summary:not(.hideme)::before { +details.toggle > summary:not(.hideme)::before { position: absolute; left: -24px; top: 4px; } -.impl-items > details.rustdoc-toggle > summary:not(.hideme)::before { +.impl-items > details.toggle > summary:not(.hideme)::before { position: absolute; left: -24px; } @@ -1561,19 +1502,19 @@ details.rustdoc-toggle > summary:not(.hideme)::before { affect the layout of the items to its right. To do that, we use absolute positioning. Note that we also set position: relative on the parent <details> to make this work properly. */ -details.rustdoc-toggle[open] > summary.hideme { +details.toggle[open] > summary.hideme { position: absolute; } -details.rustdoc-toggle[open] > summary.hideme > span { +details.toggle[open] > summary.hideme > span { display: none; } -details.rustdoc-toggle[open] > summary::before { +details.toggle[open] > summary::before { background: url("toggle-minus-31bbd6e4c77f5c96.svg") no-repeat top left; } -details.rustdoc-toggle[open] > summary::after { +details.toggle[open] > summary::after { content: "Collapse"; } @@ -1633,7 +1574,7 @@ in storage.js /* Hide the logo and item name from the sidebar. Those are displayed in the mobile-topbar instead. */ - .sidebar .sidebar-logo, + .sidebar .logo-container, .sidebar .location { display: none; } @@ -1662,14 +1603,10 @@ in storage.js .sidebar.shown, .source-sidebar-expanded .source .sidebar, - .sidebar:focus-within { + .rustdoc:not(.source) .sidebar:focus-within { left: 0; } - .rustdoc.source > .sidebar { - width: 0; - } - .mobile-topbar h2 { padding-bottom: 0; margin: auto 0.5em auto auto; @@ -1719,30 +1656,25 @@ in storage.js margin-top: 1em; } - .content { - margin-left: 0px; - } - .anchor { display: none !important; } - #titles > button > div.count { + #search-tabs .count { display: block; } - #main-content > details.rustdoc-toggle > summary::before, - #main-content > div > details.rustdoc-toggle > summary::before { + #main-content > details.toggle > summary::before, + #main-content > div > details.toggle > summary::before { left: -11px; } - #sidebar-toggle { + #src-sidebar-toggle { position: fixed; left: 1px; top: 100px; width: 30px; font-size: 1.5rem; - text-align: center; padding: 0; z-index: 10; border-top-right-radius: 3px; @@ -1751,7 +1683,7 @@ in storage.js border-left: 0; } - .source-sidebar-expanded #sidebar-toggle { + .source-sidebar-expanded #src-sidebar-toggle { left: unset; top: unset; width: unset; @@ -1787,12 +1719,12 @@ in storage.js } /* Position of the "[-]" element. */ - details.rustdoc-toggle:not(.top-doc) > summary { + details.toggle:not(.top-doc) > summary { margin-left: 10px; } - .impl-items > details.rustdoc-toggle > summary:not(.hideme)::before, - #main-content > details.rustdoc-toggle:not(.top-doc) > summary::before, - #main-content > div > details.rustdoc-toggle > summary::before { + .impl-items > details.toggle > summary:not(.hideme)::before, + #main-content > details.toggle:not(.top-doc) > summary::before, + #main-content > div > details.toggle > summary::before { left: -11px; } @@ -1825,8 +1757,8 @@ in storage.js @media print { nav.sidebar, nav.sub, .out-of-band, a.srclink, #copy-path, - details.rustdoc-toggle[open] > summary::before, details.rustdoc-toggle > summary::before, - details.rustdoc-toggle.top-doc > summary { + details.toggle[open] > summary::before, details.toggle > summary::before, + details.toggle.top-doc > summary { display: none; } @@ -1861,13 +1793,6 @@ in storage.js height: 35px; width: 35px; } - - #sidebar-toggle { - top: 10px; - } - .source-sidebar-expanded #sidebar-toggle { - top: unset; - } } .variant, @@ -1875,23 +1800,24 @@ in storage.js .impl, #implementors-list > .docblock, .impl-items > section, -.impl-items > .rustdoc-toggle > summary, +.impl-items > .toggle > summary, .methods > section, -.methods > .rustdoc-toggle > summary +.methods > .toggle > summary { margin-bottom: 0.75em; } .variants > .docblock, -.impl-items > .rustdoc-toggle[open]:not(:last-child), -.methods > .rustdoc-toggle[open]:not(:last-child), +.implementors-toggle > .docblock, +.impl-items > .toggle[open]:not(:last-child), +.methods > .toggle[open]:not(:last-child), .implementors-toggle[open]:not(:last-child) { margin-bottom: 2em; } -#trait-implementations-list .impl-items > .rustdoc-toggle:not(:last-child), -#synthetic-implementations-list .impl-items > .rustdoc-toggle:not(:last-child), -#blanket-implementations-list .impl-items > .rustdoc-toggle:not(:last-child) { +#trait-implementations-list .impl-items > .toggle:not(:last-child), +#synthetic-implementations-list .impl-items > .toggle:not(:last-child), +#blanket-implementations-list .impl-items > .toggle:not(:last-child) { margin-bottom: 1em; } @@ -1904,9 +1830,13 @@ in storage.js font-size: 12px; position: relative; bottom: 1px; - border-width: 1px; - border-style: solid; + border: 1px solid var(--scrape-example-help-border-color); border-radius: 50px; + color: var(--scrape-example-help-color); +} +.scraped-example-list .scrape-help:hover { + border-color: var(--scrape-example-help-hover-border-color); + color: var(--scrape-example-help-hover-color); } .scraped-example { @@ -1982,21 +1912,8 @@ in storage.js bottom: 0; } -.scraped-example .code-wrapper .src-line-numbers { - margin: 0; - padding: 14px 0; -} - -.scraped-example .code-wrapper .src-line-numbers a, -.scraped-example .code-wrapper .src-line-numbers span { - padding: 0 14px; -} - .scraped-example .code-wrapper .example-wrap { - display: grid; - grid-template-columns: max-content auto; width: 100%; - overflow-x: auto; overflow-y: hidden; margin-bottom: 0; } @@ -2005,12 +1922,12 @@ in storage.js overflow-x: hidden; } -.scraped-example .code-wrapper .example-wrap pre.rust { - overflow-x: inherit; - width: inherit; - overflow-y: hidden; +.scraped-example .example-wrap .rust span.highlight { + background: var(--scrape-example-code-line-highlight); +} +.scraped-example .example-wrap .rust span.highlight.focus { + background: var(--scrape-example-code-line-highlight-focus); } - .more-examples-toggle { max-width: calc(100% + 25px); @@ -2020,25 +1937,19 @@ in storage.js .more-examples-toggle .hide-more { margin-left: 25px; - margin-bottom: 5px; cursor: pointer; } .more-scraped-examples { - margin-left: 5px; - display: flex; - flex-direction: row; -} - -.more-scraped-examples-inner { - /* 20px is width of toggle-line + toggle-line-inner */ - width: calc(100% - 20px); + margin-left: 25px; + position: relative; } .toggle-line { - align-self: stretch; - margin-right: 10px; - margin-top: 5px; + position: absolute; + top: 5px; + bottom: 0; + right: calc(100% + 10px); padding: 0 4px; cursor: pointer; } @@ -2046,18 +1957,19 @@ in storage.js .toggle-line-inner { min-width: 2px; height: 100%; + background: var(--scrape-example-toggle-line-background); } -.more-scraped-examples .scraped-example { - margin-bottom: 20px; +.toggle-line:hover .toggle-line-inner { + background: var(--scrape-example-toggle-line-hover-background); } -.more-scraped-examples .scraped-example:last-child { - margin-bottom: 0; +.more-scraped-examples .scraped-example, .example-links { + margin-top: 20px; } -.example-links a { - margin-top: 20px; +.more-scraped-examples .scraped-example:first-child { + margin-top: 5px; } .example-links ul { diff --git a/src/librustdoc/html/static/css/settings.css b/src/librustdoc/html/static/css/settings.css index 1f6fb961e..4e9803fe2 100644 --- a/src/librustdoc/html/static/css/settings.css +++ b/src/librustdoc/html/static/css/settings.css @@ -1,15 +1,10 @@ .setting-line { - margin: 0.6em 0 0.6em 0.3em; + margin: 1.2em 0.6em; position: relative; } -.setting-line .choices { - display: flex; - flex-wrap: wrap; -} - .setting-line .radio-line input, -.setting-line .toggle input { +.setting-line .settings-toggle input { margin-right: 0.3em; height: 1.2rem; width: 1.2rem; @@ -22,27 +17,23 @@ .setting-line .radio-line input { border-radius: 50%; } -.setting-line .toggle input:checked { +.setting-line .settings-toggle input:checked { content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\ <path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\ <path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>'); } .setting-line .radio-line input + span, -.setting-line .toggle span { +.setting-line .settings-toggle span { padding-bottom: 1px; } -.radio-line .setting-name { - width: 100%; -} - .radio-line .choice { margin-top: 0.1em; margin-bottom: 0.1em; min-width: 3.8em; padding: 0.3em; - display: flex; + display: inline-flex; align-items: center; cursor: pointer; } @@ -50,7 +41,7 @@ margin-left: 0.5em; } -.toggle { +.settings-toggle { position: relative; width: 100%; margin-right: 20px; @@ -59,25 +50,15 @@ cursor: pointer; } -.setting-line > .sub-settings { - padding-left: 42px; - width: 100%; - display: block; -} - -#settings .setting-line { - margin: 1.2em 0.6em; -} - .setting-line .radio-line input:checked { box-shadow: inset 0 0 0 3px var(--main-background-color); background-color: var(--settings-input-color); } -.setting-line .toggle input:checked { +.setting-line .settings-toggle input:checked { background-color: var(--settings-input-color); } .setting-line .radio-line input:focus, -.setting-line .toggle input:focus { +.setting-line .settings-toggle input:focus { box-shadow: 0 0 1px 1px var(--settings-input-color); } /* In here we combine both `:focus` and `:checked` properties. */ @@ -86,6 +67,6 @@ 0 0 2px 2px var(--settings-input-color); } .setting-line .radio-line input:hover, -.setting-line .toggle input:hover { +.setting-line .settings-toggle input:hover { border-color: var(--settings-input-color) !important; } diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index 0436fe013..979e7e0f9 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -45,6 +45,7 @@ Original by Dempfi (https://github.com/dempfi/ayu) --search-color: #fff; --search-results-alias-color: #c5c5c5; --search-results-grey-color: #999; + --search-tab-title-count-color: #888; --stab-background-color: #314559; --stab-code-color: #e6e1cf; --code-highlight-kw-color: #ff7733; @@ -61,7 +62,6 @@ Original by Dempfi (https://github.com/dempfi/ayu) --code-highlight-question-mark-color: #ff9011; --code-highlight-comment-color: #788797; --code-highlight-doc-comment-color: #a1ac88; - --example-line-numbers-border-color: none; --src-line-numbers-span-color: #5c6773; --src-line-number-highlighted-background-color: rgba(255, 236, 164, 0.06); --test-arrow-color: #788797; @@ -88,6 +88,15 @@ Original by Dempfi (https://github.com/dempfi/ayu) --source-sidebar-background-selected: #14191f; --source-sidebar-background-hover: #14191f; --table-alt-row-background-color: #191f26; + --codeblock-link-background: #333; + --scrape-example-toggle-line-background: #999; + --scrape-example-toggle-line-hover-background: #c5c5c5; + --scrape-example-code-line-highlight: rgb(91, 59, 1); + --scrape-example-code-line-highlight-focus: rgb(124, 75, 15); + --scrape-example-help-border-color: #aaa; + --scrape-example-help-color: #eee; + --scrape-example-help-hover-border-color: #fff; + --scrape-example-help-hover-color: #fff; } h1, h2, h3, h4 { @@ -130,7 +139,7 @@ pre, .rustdoc.source .example-wrap { .src-line-numbers .line-highlighted { color: #708090; - padding-right: 4px; + padding-right: 7px; border-right: 1px solid #ffb44c; } @@ -150,55 +159,37 @@ pre, .rustdoc.source .example-wrap { color: #c5c5c5; } -.content .item-info::before { color: #ccc; } - .sidebar h2 a, .sidebar h3 a { color: white; } -body.source .example-wrap pre.rust a { - background: #333; -} - -.module-item .stab, -.import-item .stab { - color: #000; -} .result-name .primitive > i, .result-name .keyword > i { color: #788797; } -#titles > button.selected { +#search-tabs > button.selected { background-color: #141920 !important; border-bottom: 1px solid #ffb44c !important; border-top: none; } -#titles > button:not(.selected) { +#search-tabs > button:not(.selected) { background-color: transparent !important; border: none; } -#titles > button:hover { +#search-tabs > button:hover { border-bottom: 1px solid rgba(242, 151, 24, 0.3); } -#titles > button > div.count { - color: #888; -} - /* rules that this theme does not need to set, here to satisfy the rule checker */ /* note that a lot of these are partially set in some way (meaning they are set individually rather than as a group) */ /* FIXME: these rules should be at the bottom of the file but currently must be above the `@media (max-width: 700px)` rules due to a bug in the css checker */ /* see https://github.com/rust-lang/rust/pull/71237#issuecomment-618170143 */ -pre.rust .lifetime {} -pre.rust .kw {} -#titles > button:hover, #titles > button.selected {} -pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val, pre.rust .attribute {} -pre.rust .kw-2, pre.rust .prelude-ty {} +#search-tabs > button:hover, #search-tabs > button.selected {} #settings-menu > a img { filter: invert(100); @@ -213,29 +204,9 @@ pre.rust .kw-2, pre.rust .prelude-ty {} color: #ffb44c; } -.scraped-example-list .scrape-help { - border-color: #aaa; - color: #eee; -} -.scraped-example-list .scrape-help:hover { - border-color: white; - color: white; -} -.scraped-example .example-wrap .rust span.highlight { - background: rgb(91, 59, 1); -} -.scraped-example .example-wrap .rust span.highlight.focus { - background: rgb(124, 75, 15); -} -.scraped-example:not(.expanded) .code-wrapper:before { +.scraped-example:not(.expanded) .code-wrapper::before { background: linear-gradient(to bottom, rgba(15, 20, 25, 1), rgba(15, 20, 25, 0)); } -.scraped-example:not(.expanded) .code-wrapper:after { +.scraped-example:not(.expanded) .code-wrapper::after { background: linear-gradient(to top, rgba(15, 20, 25, 1), rgba(15, 20, 25, 0)); } -.toggle-line-inner { - background: #999; -} -.toggle-line:hover .toggle-line-inner { - background: #c5c5c5; -} diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index d945e956c..fb15863b0 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -40,6 +40,7 @@ --search-color: #111; --search-results-alias-color: #fff; --search-results-grey-color: #ccc; + --search-tab-title-count-color: #888; --stab-background-color: #314559; --stab-code-color: #e6e1cf; --code-highlight-kw-color: #ab8ac1; @@ -56,7 +57,6 @@ --code-highlight-question-mark-color: #ff9011; --code-highlight-comment-color: #8d8d8b; --code-highlight-doc-comment-color: #8ca375; - --example-line-numbers-border-color: #4a4949; --src-line-numbers-span-color: #3b91e2; --src-line-number-highlighted-background-color: #0a042f; --test-arrow-color: #dedede; @@ -83,51 +83,30 @@ --source-sidebar-background-selected: #333; --source-sidebar-background-hover: #444; --table-alt-row-background-color: #2A2A2A; + --codeblock-link-background: #333; + --scrape-example-toggle-line-background: #999; + --scrape-example-toggle-line-hover-background: #c5c5c5; + --scrape-example-code-line-highlight: rgb(91, 59, 1); + --scrape-example-code-line-highlight-focus: rgb(124, 75, 15); + --scrape-example-help-border-color: #aaa; + --scrape-example-help-color: #eee; + --scrape-example-help-hover-border-color: #fff; + --scrape-example-help-hover-color: #fff; } -.content .item-info::before { color: #ccc; } - -body.source .example-wrap pre.rust a { - background: #333; -} - -#titles > button:not(.selected) { +#search-tabs > button:not(.selected) { background-color: #252525; border-top-color: #252525; } -#titles > button:hover, #titles > button.selected { +#search-tabs > button:hover, #search-tabs > button.selected { border-top-color: #0089ff; background-color: #353535; } -#titles > button > div.count { - color: #888; -} - -.scraped-example-list .scrape-help { - border-color: #aaa; - color: #eee; -} -.scraped-example-list .scrape-help:hover { - border-color: white; - color: white; -} -.scraped-example .example-wrap .rust span.highlight { - background: rgb(91, 59, 1); -} -.scraped-example .example-wrap .rust span.highlight.focus { - background: rgb(124, 75, 15); -} -.scraped-example:not(.expanded) .code-wrapper:before { +.scraped-example:not(.expanded) .code-wrapper::before { background: linear-gradient(to bottom, rgba(53, 53, 53, 1), rgba(53, 53, 53, 0)); } -.scraped-example:not(.expanded) .code-wrapper:after { +.scraped-example:not(.expanded) .code-wrapper::after { background: linear-gradient(to top, rgba(53, 53, 53, 1), rgba(53, 53, 53, 0)); } -.toggle-line-inner { - background: #999; -} -.toggle-line:hover .toggle-line-inner { - background: #c5c5c5; -} diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index 58955a793..053fa78d1 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -40,6 +40,7 @@ --search-color: #000; --search-results-alias-color: #000; --search-results-grey-color: #999; + --search-tab-title-count-color: #888; --stab-background-color: #fff5d6; --stab-code-color: #000; --code-highlight-kw-color: #8959a8; @@ -56,7 +57,6 @@ --code-highlight-question-mark-color: #ff9011; --code-highlight-comment-color: #8e908c; --code-highlight-doc-comment-color: #4d4d4c; - --example-line-numbers-border-color: #c7c7c7; --src-line-numbers-span-color: #c67e2d; --src-line-number-highlighted-background-color: #fdffd3; --test-arrow-color: #f5f5f5; @@ -80,51 +80,30 @@ --source-sidebar-background-selected: #fff; --source-sidebar-background-hover: #e0e0e0; --table-alt-row-background-color: #F5F5F5; + --codeblock-link-background: #eee; + --scrape-example-toggle-line-background: #ccc; + --scrape-example-toggle-line-hover-background: #999; + --scrape-example-code-line-highlight: #fcffd6; + --scrape-example-code-line-highlight-focus: #f6fdb0; + --scrape-example-help-border-color: #555; + --scrape-example-help-color: #333; + --scrape-example-help-hover-border-color: #000; + --scrape-example-help-hover-color: #000; } -.content .item-info::before { color: #ccc; } - -body.source .example-wrap pre.rust a { - background: #eee; -} - -#titles > button:not(.selected) { +#search-tabs > button:not(.selected) { background-color: #e6e6e6; border-top-color: #e6e6e6; } -#titles > button:hover, #titles > button.selected { +#search-tabs > button:hover, #search-tabs > button.selected { background-color: #ffffff; border-top-color: #0089ff; } -#titles > button > div.count { - color: #888; -} - -.scraped-example-list .scrape-help { - border-color: #555; - color: #333; -} -.scraped-example-list .scrape-help:hover { - border-color: black; - color: black; -} -.scraped-example .example-wrap .rust span.highlight { - background: #fcffd6; -} -.scraped-example .example-wrap .rust span.highlight.focus { - background: #f6fdb0; -} -.scraped-example:not(.expanded) .code-wrapper:before { +.scraped-example:not(.expanded) .code-wrapper::before { background: linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)); } -.scraped-example:not(.expanded) .code-wrapper:after { +.scraped-example:not(.expanded) .code-wrapper::after { background: linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)); } -.toggle-line-inner { - background: #ccc; -} -.toggle-line:hover .toggle-line-inner { - background: #999; -} diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 152116089..604ab147f 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -184,7 +184,6 @@ function browserSupportsHistoryApi() { function loadCss(cssUrl) { const link = document.createElement("link"); link.href = cssUrl; - link.type = "text/css"; link.rel = "stylesheet"; document.getElementsByTagName("head")[0].appendChild(link); } @@ -391,7 +390,8 @@ function loadCss(cssUrl) { } if (document.activeElement.tagName === "INPUT" && - document.activeElement.type !== "checkbox") { + document.activeElement.type !== "checkbox" && + document.activeElement.type !== "radio") { switch (getVirtualKey(ev)) { case "Escape": handleEscape(ev); @@ -527,7 +527,7 @@ function loadCss(cssUrl) { } let currentNbImpls = implementors.getElementsByClassName("impl").length; - const traitName = document.querySelector("h1.fqn > .trait").textContent; + const traitName = document.querySelector(".main-heading h1 > .trait").textContent; const baseIdName = "impl-" + traitName + "-"; const libs = Object.getOwnPropertyNames(imp); // We don't want to include impls from this JS file, when the HTML already has them. @@ -564,7 +564,7 @@ function loadCss(cssUrl) { onEachLazy(code.getElementsByTagName("a"), elem => { const href = elem.getAttribute("href"); - if (href && href.indexOf("http") !== 0) { + if (href && !/^(?:[a-z+]+:)?\/\//.test(href)) { elem.setAttribute("href", window.rootPath + href); } }); @@ -621,7 +621,7 @@ function loadCss(cssUrl) { function expandAllDocs() { const innerToggle = document.getElementById(toggleAllDocsId); removeClass(innerToggle, "will-expand"); - onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => { + onEachLazy(document.getElementsByClassName("toggle"), e => { if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) { e.open = true; } @@ -633,7 +633,7 @@ function loadCss(cssUrl) { function collapseAllDocs() { const innerToggle = document.getElementById(toggleAllDocsId); addClass(innerToggle, "will-expand"); - onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => { + onEachLazy(document.getElementsByClassName("toggle"), e => { if (e.parentNode.id !== "implementations-list" || (!hasClass(e, "implementors-toggle") && !hasClass(e, "type-contents-toggle")) @@ -681,7 +681,7 @@ function loadCss(cssUrl) { setImplementorsTogglesOpen("blanket-implementations-list", false); } - onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => { + onEachLazy(document.getElementsByClassName("toggle"), e => { if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) { e.open = true; } @@ -804,29 +804,22 @@ function loadCss(cssUrl) { } }); - function handleClick(id, f) { - const elem = document.getElementById(id); - if (elem) { - elem.addEventListener("click", f); - } + const mainElem = document.getElementById(MAIN_ID); + if (mainElem) { + mainElem.addEventListener("click", hideSidebar); } - handleClick(MAIN_ID, () => { - hideSidebar(); - }); - onEachLazy(document.getElementsByTagName("a"), el => { + onEachLazy(document.querySelectorAll("a[href^='#']"), el => { // For clicks on internal links (<A> tags with a hash property), we expand the section we're // jumping to *before* jumping there. We can't do this in onHashChange, because it changes // the height of the document so we wind up scrolled to the wrong place. - if (el.hash) { - el.addEventListener("click", () => { - expandSection(el.hash.slice(1)); - hideSidebar(); - }); - } + el.addEventListener("click", () => { + expandSection(el.hash.slice(1)); + hideSidebar(); + }); }); - onEachLazy(document.querySelectorAll(".rustdoc-toggle > summary:not(.hideme)"), el => { + onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"), el => { el.addEventListener("click", e => { if (e.target.tagName !== "SUMMARY" && e.target.tagName !== "A") { e.preventDefault(); @@ -850,7 +843,7 @@ function loadCss(cssUrl) { window.hideAllModals(false); const ty = e.getAttribute("data-ty"); const wrapper = document.createElement("div"); - wrapper.innerHTML = "<div class=\"docblock\">" + window.NOTABLE_TRAITS[ty] + "</div>"; + wrapper.innerHTML = "<div class=\"content\">" + window.NOTABLE_TRAITS[ty] + "</div>"; wrapper.className = "notable popover"; const focusCatcher = document.createElement("div"); focusCatcher.setAttribute("tabindex", "0"); @@ -948,7 +941,7 @@ function loadCss(cssUrl) { return; } if (!this.NOTABLE_FORCE_VISIBLE && - !elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT)) { + !elemIsInParent(ev.relatedTarget, window.CURRENT_NOTABLE_ELEMENT)) { hideNotable(true); } }; @@ -1043,9 +1036,6 @@ function loadCss(cssUrl) { help_button.appendChild(container); container.onblur = helpBlurHandler; - container.onclick = event => { - event.preventDefault(); - }; help_button.onblur = helpBlurHandler; help_button.children[0].onblur = helpBlurHandler; } @@ -1093,6 +1083,9 @@ function loadCss(cssUrl) { * Show the help popup menu. */ function showHelp() { + // Prevent `blur` events from being dispatched as a result of closing + // other modals. + getHelpButton().querySelector("a").focus(); const menu = getHelpMenu(true); if (menu.style.display === "none") { window.hideAllModals(); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 23ae4e970..88592fa0c 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -49,7 +49,7 @@ function printTab(nb) { let iter = 0; let foundCurrentTab = false; let foundCurrentResultSet = false; - onEachLazy(document.getElementById("titles").childNodes, elem => { + onEachLazy(document.getElementById("search-tabs").childNodes, elem => { if (nb === iter) { addClass(elem, "selected"); foundCurrentTab = true; @@ -781,7 +781,29 @@ function initSearch(rawSearchIndex) { return a - b; } - // Sort by non levenshtein results and then levenshtein results by the distance + // sort by index of keyword in item name (no literal occurrence goes later) + a = (aaa.index < 0); + b = (bbb.index < 0); + if (a !== b) { + return a - b; + } + + // Sort by distance in the path part, if specified + // (less changes required to match means higher rankings) + a = aaa.path_lev; + b = bbb.path_lev; + if (a !== b) { + return a - b; + } + + // (later literal occurrence, if any, goes later) + a = aaa.index; + b = bbb.index; + if (a !== b) { + return a - b; + } + + // Sort by distance in the name part, the last part of the path // (less changes required to match means higher rankings) a = (aaa.lev); b = (bbb.lev); @@ -810,19 +832,6 @@ function initSearch(rawSearchIndex) { return (a > b ? +1 : -1); } - // sort by index of keyword in item name (no literal occurrence goes later) - a = (aaa.index < 0); - b = (bbb.index < 0); - if (a !== b) { - return a - b; - } - // (later literal occurrence, if any, goes later) - a = aaa.index; - b = bbb.index; - if (a !== b) { - return a - b; - } - // special precedence for primitive and keyword pages if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) || (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) { @@ -1230,15 +1239,19 @@ function initSearch(rawSearchIndex) { * * `id` is the index in both `searchWords` and `searchIndex` arrays for this element. * * `index` is an `integer`` used to sort by the position of the word in the item's name. * * `lev` is the main metric used to sort the search results. + * * `path_lev` is zero if a single-component search query is used, otherwise it's the + * distance computed for everything other than the last path component. * * @param {Results} results * @param {string} fullId * @param {integer} id * @param {integer} index * @param {integer} lev + * @param {integer} path_lev */ - function addIntoResults(results, fullId, id, index, lev) { - if (lev === 0 || (!parsedQuery.literalSearch && lev <= MAX_LEV_DISTANCE)) { + function addIntoResults(results, fullId, id, index, lev, path_lev) { + const inBounds = lev <= MAX_LEV_DISTANCE || index !== -1; + if (lev === 0 || (!parsedQuery.literalSearch && inBounds)) { if (results[fullId] !== undefined) { const result = results[fullId]; if (result.dontValidate || result.lev <= lev) { @@ -1250,6 +1263,7 @@ function initSearch(rawSearchIndex) { index: index, dontValidate: parsedQuery.literalSearch, lev: lev, + path_lev: path_lev, }; } } @@ -1280,68 +1294,68 @@ function initSearch(rawSearchIndex) { if (!row || (filterCrates !== null && row.crate !== filterCrates)) { return; } - let lev, lev_add = 0, index = -1; + let lev, index = -1, path_lev = 0; const fullId = row.id; + const searchWord = searchWords[pos]; const in_args = findArg(row, elem, parsedQuery.typeFilter); const returned = checkReturned(row, elem, parsedQuery.typeFilter); - addIntoResults(results_in_args, fullId, pos, index, in_args); - addIntoResults(results_returned, fullId, pos, index, returned); + // path_lev is 0 because no parent path information is currently stored + // in the search index + addIntoResults(results_in_args, fullId, pos, -1, in_args, 0); + addIntoResults(results_returned, fullId, pos, -1, returned, 0); if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) { return; } - const searchWord = searchWords[pos]; - if (parsedQuery.literalSearch) { - if (searchWord === elem.name) { - addIntoResults(results_others, fullId, pos, -1, 0); - } - return; + const row_index = row.normalizedName.indexOf(elem.pathLast); + const word_index = searchWord.indexOf(elem.pathLast); + + // lower indexes are "better" matches + // rank based on the "best" match + if (row_index === -1) { + index = word_index; + } else if (word_index === -1) { + index = row_index; + } else if (word_index < row_index) { + index = word_index; + } else { + index = row_index; } // No need to check anything else if it's a "pure" generics search. if (elem.name.length === 0) { if (row.type !== null) { lev = checkGenerics(row.type, elem, MAX_LEV_DISTANCE + 1); - addIntoResults(results_others, fullId, pos, index, lev); + // path_lev is 0 because we know it's empty + addIntoResults(results_others, fullId, pos, index, lev, 0); } return; } if (elem.fullPath.length > 1) { - lev = checkPath(elem.pathWithoutLast, row); - if (lev > MAX_LEV_DISTANCE || (parsedQuery.literalSearch && lev !== 0)) { + path_lev = checkPath(elem.pathWithoutLast, row); + if (path_lev > MAX_LEV_DISTANCE) { return; - } else if (lev > 0) { - lev_add = lev / 10; } } - if (searchWord.indexOf(elem.pathLast) > -1 || - row.normalizedName.indexOf(elem.pathLast) > -1 - ) { - index = row.normalizedName.indexOf(elem.pathLast); - } - lev = levenshtein(searchWord, elem.pathLast); - if (lev > 0 && elem.pathLast.length > 2 && searchWord.indexOf(elem.pathLast) > -1) { - if (elem.pathLast.length < 6) { - lev = 1; - } else { - lev = 0; + if (parsedQuery.literalSearch) { + if (searchWord === elem.name) { + addIntoResults(results_others, fullId, pos, index, 0, path_lev); } - } - lev += lev_add; - if (lev > MAX_LEV_DISTANCE) { return; - } else if (index !== -1 && elem.fullPath.length < 2) { - lev -= 1; } - if (lev < 0) { - lev = 0; + + lev = levenshtein(searchWord, elem.pathLast); + + if (index === -1 && lev + path_lev > MAX_LEV_DISTANCE) { + return; } - addIntoResults(results_others, fullId, pos, index, lev); + + addIntoResults(results_others, fullId, pos, index, lev, path_lev); } /** @@ -1386,7 +1400,7 @@ function initSearch(rawSearchIndex) { return; } const lev = Math.round(totalLev / nbLev); - addIntoResults(results, row.id, pos, 0, lev); + addIntoResults(results, row.id, pos, 0, lev, 0); } function innerRunQuery() { @@ -1490,7 +1504,7 @@ function initSearch(rawSearchIndex) { function focusSearchResult() { const target = searchState.focusedByTab[searchState.currentTab] || document.querySelectorAll(".search-results.active a").item(0) || - document.querySelectorAll("#titles > button").item(searchState.currentTab); + document.querySelectorAll("#search-tabs button").item(searchState.currentTab); searchState.focusedByTab[searchState.currentTab] = null; if (target) { target.focus(); @@ -1645,9 +1659,9 @@ function initSearch(rawSearchIndex) { function makeTabHeader(tabNb, text, nbElems) { if (searchState.currentTab === tabNb) { return "<button class=\"selected\">" + text + - " <div class=\"count\">(" + nbElems + ")</div></button>"; + " <span class=\"count\">(" + nbElems + ")</span></button>"; } - return "<button>" + text + " <div class=\"count\">(" + nbElems + ")</div></button>"; + return "<button>" + text + " <span class=\"count\">(" + nbElems + ")</span></button>"; } /** @@ -1712,12 +1726,12 @@ function initSearch(rawSearchIndex) { let output = `<h1 class="search-results-title">Results${crates}</h1>`; if (results.query.error !== null) { output += `<h3>Query parser error: "${results.query.error}".</h3>`; - output += "<div id=\"titles\">" + + output += "<div id=\"search-tabs\">" + makeTabHeader(0, "In Names", ret_others[1]) + "</div>"; currentTab = 0; } else if (results.query.foundElems <= 1 && results.query.returned.length === 0) { - output += "<div id=\"titles\">" + + output += "<div id=\"search-tabs\">" + makeTabHeader(0, "In Names", ret_others[1]) + makeTabHeader(1, "In Parameters", ret_in_args[1]) + makeTabHeader(2, "In Return Types", ret_returned[1]) + @@ -1727,7 +1741,7 @@ function initSearch(rawSearchIndex) { results.query.elems.length === 0 ? "In Function Return Types" : results.query.returned.length === 0 ? "In Function Parameters" : "In Function Signatures"; - output += "<div id=\"titles\">" + + output += "<div id=\"search-tabs\">" + makeTabHeader(0, signatureTabTitle, ret_others[1]) + "</div>"; currentTab = 0; @@ -1747,7 +1761,7 @@ function initSearch(rawSearchIndex) { search.appendChild(resultsElem); // Reset focused elements. searchState.showResults(search); - const elems = document.getElementById("titles").childNodes; + const elems = document.getElementById("search-tabs").childNodes; searchState.focusedByTab = []; let i = 0; for (const elem of elems) { diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 589bfc793..84df1b7d3 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -135,7 +135,7 @@ // This is a select setting. output += `\ <div class="radio-line" id="${js_data_name}"> - <span class="setting-name">${setting_name}</span> + <div class="setting-name">${setting_name}</div> <div class="choices">`; onEach(setting["options"], option => { const checked = option === setting["default"] ? " checked" : ""; @@ -150,10 +150,10 @@ }); output += "</div></div>"; } else { - // This is a toggle. + // This is a checkbox toggle. const checked = setting["default"] === true ? " checked" : ""; output += `\ -<label class="toggle">\ +<label class="settings-toggle">\ <input type="checkbox" id="${js_data_name}"${checked}>\ <span class="label">${setting_name}</span>\ </label>`; diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js index 5db768c1c..0e1c864e6 100644 --- a/src/librustdoc/html/static/js/source-script.js +++ b/src/librustdoc/html/static/js/source-script.js @@ -83,7 +83,7 @@ function toggleSidebar() { function createSidebarToggle() { const sidebarToggle = document.createElement("div"); - sidebarToggle.id = "sidebar-toggle"; + sidebarToggle.id = "src-sidebar-toggle"; const inner = document.createElement("button"); diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index aa3bf827d..fddda293b 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -72,34 +72,30 @@ {%- if page.css_class != "source" -%} <nav class="mobile-topbar"> {#- -#} <button class="sidebar-menu-toggle">☰</button> {#- -#} - <a class="sidebar-logo" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {#- -#} - <div class="logo-container"> {#- -#} - {%- if !layout.logo.is_empty() -%} - <img src="{{layout.logo}}" alt="logo"> {#- -#} - {%- else -%} - <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {#- -#} - {%- endif -%} - </div> {#- -#} + <a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {#- -#} + {%- if !layout.logo.is_empty() -%} + <img src="{{layout.logo}}" alt="logo"> {#- -#} + {%- else -%} + <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {#- -#} + {%- endif -%} </a> {#- -#} <h2></h2> {#- -#} </nav> {#- -#} {%- endif -%} <nav class="sidebar"> {#- -#} {%- if page.css_class != "source" -%} - <a class="sidebar-logo" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {#- -#} - <div class="logo-container"> {#- -#} - {%- if !layout.logo.is_empty() %} - <img src="{{layout.logo}}" alt="logo"> {#- -#} - {%- else -%} - <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {#- -#} - {%- endif -%} - </div> {#- -#} + <a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {#- -#} + {%- if !layout.logo.is_empty() %} + <img src="{{layout.logo}}" alt="logo"> {#- -#} + {%- else -%} + <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {#- -#} + {%- endif -%} </a> {#- -#} {%- endif -%} {{- sidebar|safe -}} </nav> {#- -#} <main> {#- -#} - <div class="width-limiter"> {#- -#} + {%- if page.css_class != "source" -%}<div class="width-limiter">{%- endif -%} <nav class="sub"> {#- -#} {%- if page.css_class == "source" -%} <a class="sub-logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {#- -#} @@ -132,7 +128,7 @@ </form> {#- -#} </nav> {#- -#} <section id="main-content" class="content">{{- content|safe -}}</section> {#- -#} - </div> {#- -#} + {%- if page.css_class != "source" -%}</div>{%- endif -%} </main> {#- -#} {{- layout.external_html.after_content|safe -}} <div id="rustdoc-vars" {# -#} diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html index 611d124d4..ee2880bf6 100644 --- a/src/librustdoc/html/templates/print_item.html +++ b/src/librustdoc/html/templates/print_item.html @@ -1,5 +1,5 @@ <div class="main-heading"> {#- -#} - <h1 class="fqn"> {#- -#} + <h1> {#- -#} {{-typ-}} {#- The breadcrumbs of the item path, like std::string -#} {%- for component in path_components -%} diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index d7184053c..bd95ec186 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -8,8 +8,9 @@ use std::convert::From; use std::fmt; use rustc_ast::ast; -use rustc_hir::{def::CtorKind, def_id::DefId}; +use rustc_hir::{def::CtorKind, def::DefKind, def_id::DefId}; use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::symbol::sym; use rustc_span::{Pos, Symbol}; use rustc_target::spec::abi::Abi as RustcAbi; @@ -217,13 +218,27 @@ pub(crate) fn from_item_id_with_name(item_id: ItemId, tcx: TyCtxt<'_>, name: Opt impl<'a> fmt::Display for DisplayDefId<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let name = match self.2 { + let DisplayDefId(def_id, tcx, name) = self; + let name = match name { Some(name) => format!(":{}", name.as_u32()), - None => self - .1 - .opt_item_name(self.0) - .map(|n| format!(":{}", n.as_u32())) - .unwrap_or_default(), + None => { + // We need this workaround because primitive types' DefId actually refers to + // their parent module, which isn't present in the output JSON items. So + // instead, we directly get the primitive symbol and convert it to u32 to + // generate the ID. + if matches!(tcx.def_kind(def_id), DefKind::Mod) && + let Some(prim) = tcx.get_attrs(*def_id, sym::doc) + .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) + .filter(|attr| attr.has_name(sym::primitive)) + .find_map(|attr| attr.value_str()) { + format!(":{}", prim.as_u32()) + } else { + tcx + .opt_item_name(*def_id) + .map(|n| format!(":{}", n.as_u32())) + .unwrap_or_default() + } + } }; write!(f, "{}:{}{}", self.0.krate.as_u32(), u32::from(self.0.index), name) } @@ -237,7 +252,6 @@ pub(crate) fn from_item_id_with_name(item_id: ItemId, tcx: TyCtxt<'_>, name: Opt ItemId::Auto { for_, trait_ } => { Id(format!("a:{}-{}", DisplayDefId(trait_, tcx, None), DisplayDefId(for_, tcx, name))) } - ItemId::Primitive(ty, krate) => Id(format!("p:{}:{}", krate.as_u32(), ty.as_sym())), } } @@ -646,15 +660,20 @@ impl FromWithTcx<clean::Enum> for Enum { impl FromWithTcx<clean::Variant> for Variant { fn from_tcx(variant: clean::Variant, tcx: TyCtxt<'_>) -> Self { - use clean::Variant::*; - match variant { - CLike(disr) => Variant::Plain(disr.map(|disr| disr.into_tcx(tcx))), - Tuple(fields) => Variant::Tuple(ids_keeping_stripped(fields, tcx)), - Struct(s) => Variant::Struct { + use clean::VariantKind::*; + + let discriminant = variant.discriminant.map(|d| d.into_tcx(tcx)); + + let kind = match variant.kind { + CLike => VariantKind::Plain, + Tuple(fields) => VariantKind::Tuple(ids_keeping_stripped(fields, tcx)), + Struct(s) => VariantKind::Struct { fields_stripped: s.has_stripped_entries(), fields: ids(s.fields, tcx), }, - } + }; + + Variant { kind, discriminant } } } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index ef1d7da5a..86454e1f2 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -772,7 +772,6 @@ fn main_args(at_args: &[String]) -> MainResult { let crate_version = options.crate_version.clone(); let output_format = options.output_format; - let externs = options.externs.clone(); let scrape_examples_options = options.scrape_examples_options.clone(); let bin_crate = options.bin_crate; @@ -800,13 +799,12 @@ fn main_args(at_args: &[String]) -> MainResult { // FIXME(#83761): Resolver cloning can lead to inconsistencies between data in the // two copies because one of the copies can be modified after `TyCtxt` construction. let (resolver, resolver_caches) = { - let (krate, resolver, _) = &*abort_on_err(queries.expansion(), sess).peek(); + let expansion = abort_on_err(queries.expansion(), sess); + let (krate, resolver, _) = &*expansion.borrow(); let resolver_caches = resolver.borrow_mut().access(|resolver| { collect_intra_doc_links::early_resolve_intra_doc_links( resolver, - sess, krate, - externs, render_options.document_private, ) }); @@ -817,7 +815,7 @@ fn main_args(at_args: &[String]) -> MainResult { sess.fatal("Compilation failed, aborting rustdoc"); } - let mut global_ctxt = abort_on_err(queries.global_ctxt(), sess).peek_mut(); + let global_ctxt = abort_on_err(queries.global_ctxt(), sess); global_ctxt.enter(|tcx| { let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || { diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 044e05144..5f4ad6d2a 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -53,7 +53,7 @@ pub(crate) fn render<P: AsRef<Path>>( let mut css = String::new(); for name in &options.markdown_css { - write!(css, r#"<link rel="stylesheet" type="text/css" href="{name}">"#) + write!(css, r#"<link rel="stylesheet" href="{name}">"#) .expect("Writing to a String can't fail"); } diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index 057d2fdd9..6aa2dda98 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -82,7 +82,7 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) - let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.item_id.expect_def_id().expect_local()); // check if parent is trait impl - if let Some(parent_hir_id) = cx.tcx.hir().find_parent_node(hir_id) { + if let Some(parent_hir_id) = cx.tcx.hir().opt_parent_id(hir_id) { if let Some(parent_node) = cx.tcx.hir().find(parent_hir_id) { if matches!( parent_node, diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 37a28b6b7..075951312 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -538,11 +538,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did, .. }, _)), _) | ty::Foreign(did) => { Res::from_def_id(self.cx.tcx, did) } - ty::Projection(_) + ty::Alias(..) | ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(_) - | ty::Opaque(..) | ty::Dynamic(..) | ty::Param(_) | ty::Bound(..) @@ -787,7 +786,7 @@ fn trait_impls_for<'a>( tcx.find_map_relevant_impl(trait_, ty, |impl_| { let trait_ref = tcx.impl_trait_ref(impl_).expect("this is not an inherent impl"); // Check if these are the same type. - let impl_type = trait_ref.self_ty(); + let impl_type = trait_ref.skip_binder().self_ty(); trace!( "comparing type {} with kind {:?} against type {:?}", impl_type, diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs index 1b373cfe5..42677bd84 100644 --- a/src/librustdoc/passes/collect_intra_doc_links/early.rs +++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs @@ -12,8 +12,6 @@ use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, CRATE_DEF_ID}; use rustc_hir::TraitCandidate; use rustc_middle::ty::{DefIdTree, Visibility}; use rustc_resolve::{ParentScope, Resolver}; -use rustc_session::config::Externs; -use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::{Symbol, SyntaxContext}; @@ -22,16 +20,13 @@ use std::mem; pub(crate) fn early_resolve_intra_doc_links( resolver: &mut Resolver<'_>, - sess: &Session, krate: &ast::Crate, - externs: Externs, document_private_items: bool, ) -> ResolverCaches { let parent_scope = ParentScope::module(resolver.expect_module(CRATE_DEF_ID.to_def_id()), resolver); let mut link_resolver = EarlyDocLinkResolver { resolver, - sess, parent_scope, visited_mods: Default::default(), markdown_links: Default::default(), @@ -52,7 +47,9 @@ pub(crate) fn early_resolve_intra_doc_links( // the known necessary crates. Load them all unconditionally until we find a way to fix this. // DO NOT REMOVE THIS without first testing on the reproducer in // https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb - for (extern_name, _) in externs.iter().filter(|(_, entry)| entry.add_prelude) { + for (extern_name, _) in + link_resolver.resolver.sess().opts.externs.iter().filter(|(_, entry)| entry.add_prelude) + { link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, parent_scope); } @@ -73,7 +70,6 @@ fn doc_attrs<'a>(attrs: impl Iterator<Item = &'a ast::Attribute>) -> Attributes struct EarlyDocLinkResolver<'r, 'ra> { resolver: &'r mut Resolver<'ra>, - sess: &'r Session, parent_scope: ParentScope<'ra>, visited_mods: DefIdSet, markdown_links: FxHashMap<String, Vec<PreprocessedMarkdownLink>>, @@ -166,7 +162,7 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { fn resolve_doc_links_extern_impl(&mut self, def_id: DefId, is_inherent: bool) { self.resolve_doc_links_extern_outer_fixme(def_id, def_id); let assoc_item_def_ids = Vec::from_iter( - self.resolver.cstore().associated_item_def_ids_untracked(def_id, self.sess), + self.resolver.cstore().associated_item_def_ids_untracked(def_id, self.resolver.sess()), ); for assoc_def_id in assoc_item_def_ids { if !is_inherent || self.resolver.cstore().visibility_untracked(assoc_def_id).is_public() @@ -191,7 +187,9 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { if !self.resolver.cstore().may_have_doc_links_untracked(def_id) { return; } - let attrs = Vec::from_iter(self.resolver.cstore().item_attrs_untracked(def_id, self.sess)); + let attrs = Vec::from_iter( + self.resolver.cstore().item_attrs_untracked(def_id, self.resolver.sess()), + ); let parent_scope = ParentScope::module( self.resolver.get_nearest_non_block_module( self.resolver.opt_parent(scope_id).unwrap_or(scope_id), @@ -205,7 +203,9 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { if !self.resolver.cstore().may_have_doc_links_untracked(def_id) { return; } - let attrs = Vec::from_iter(self.resolver.cstore().item_attrs_untracked(def_id, self.sess)); + let attrs = Vec::from_iter( + self.resolver.cstore().item_attrs_untracked(def_id, self.resolver.sess()), + ); let parent_scope = ParentScope::module(self.resolver.expect_module(def_id), self.resolver); self.resolve_doc_links(doc_attrs(attrs.iter()), parent_scope); } @@ -321,7 +321,7 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { let field_def_ids = Vec::from_iter( self.resolver .cstore() - .associated_item_def_ids_untracked(def_id, self.sess), + .associated_item_def_ids_untracked(def_id, self.resolver.sess()), ); for field_def_id in field_def_ids { self.resolve_doc_links_extern_outer(field_def_id, scope_id); diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index d57f981d5..79db3c6c3 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -19,6 +19,12 @@ pub(crate) const COLLECT_TRAIT_IMPLS: Pass = Pass { }; pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate { + // We need to check if there are errors before running this pass because it would crash when + // we try to get auto and blanket implementations. + if cx.tcx.sess.diagnostic().has_errors_or_lint_errors().is_some() { + return krate; + } + let synth_impls = cx.sess().time("collect_synthetic_impls", || { let mut synth = SyntheticImplCollector { cx, impls: Vec::new() }; synth.visit_crate(&krate); diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs index 5aa4f238b..7158355ff 100644 --- a/src/librustdoc/passes/lint/check_code_block_syntax.rs +++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs @@ -156,7 +156,9 @@ impl Emitter for BufferEmitter { let mut buffer = self.buffer.borrow_mut(); let fluent_args = to_fluent_args(diag.args()); - let translated_main_message = self.translate_message(&diag.message[0].0, &fluent_args); + let translated_main_message = self + .translate_message(&diag.message[0].0, &fluent_args) + .unwrap_or_else(|e| panic!("{e}")); buffer.messages.push(format!("error from rustc: {}", translated_main_message)); if diag.is_error() { diff --git a/src/librustdoc/passes/strip_priv_imports.rs b/src/librustdoc/passes/strip_priv_imports.rs index 3bac5a8e5..4c992e948 100644 --- a/src/librustdoc/passes/strip_priv_imports.rs +++ b/src/librustdoc/passes/strip_priv_imports.rs @@ -12,5 +12,6 @@ pub(crate) const STRIP_PRIV_IMPORTS: Pass = Pass { }; pub(crate) fn strip_priv_imports(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { - ImportStripper { tcx: cx.tcx }.fold_crate(krate) + let is_json_output = cx.output_format.is_json() && !cx.show_coverage; + ImportStripper { tcx: cx.tcx, is_json_output }.fold_crate(krate) } diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs index 8fc42462d..bb6dccb7c 100644 --- a/src/librustdoc/passes/strip_private.rs +++ b/src/librustdoc/passes/strip_private.rs @@ -28,7 +28,8 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> is_json_output, tcx: cx.tcx, }; - krate = ImportStripper { tcx: cx.tcx }.fold_crate(stripper.fold_crate(krate)); + krate = + ImportStripper { tcx: cx.tcx, is_json_output }.fold_crate(stripper.fold_crate(krate)); } // strip all impls referencing private items diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index 995fb5dcc..048ed2646 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -97,17 +97,7 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> { } // handled in the `strip-priv-imports` pass - clean::ExternCrateItem { .. } => {} - clean::ImportItem(ref imp) => { - // Because json doesn't inline imports from private modules, we need to mark - // the imported item as retained so it's impls won't be stripped. - // - // FIXME: Is it necessary to check for json output here: See - // https://github.com/rust-lang/rust/pull/100325#discussion_r941495215 - if let Some(did) = imp.source.did && self.is_json_output { - self.retained.insert(did.into()); - } - } + clean::ExternCrateItem { .. } | clean::ImportItem(_) => {} clean::ImplItem(..) => {} @@ -132,7 +122,10 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> { // implementations of traits are always public. clean::ImplItem(ref imp) if imp.trait_.is_some() => true, // Variant fields have inherited visibility - clean::VariantItem(clean::Variant::Struct(..) | clean::Variant::Tuple(..)) => true, + clean::VariantItem(clean::Variant { + kind: clean::VariantKind::Struct(..) | clean::VariantKind::Tuple(..), + .. + }) => true, _ => false, }; @@ -240,11 +233,25 @@ impl<'a> DocFolder for ImplStripper<'a, '_> { /// This stripper discards all private import statements (`use`, `extern crate`) pub(crate) struct ImportStripper<'tcx> { pub(crate) tcx: TyCtxt<'tcx>, + pub(crate) is_json_output: bool, +} + +impl<'tcx> ImportStripper<'tcx> { + fn import_should_be_hidden(&self, i: &Item, imp: &clean::Import) -> bool { + if self.is_json_output { + // FIXME: This should be handled the same way as for HTML output. + imp.imported_item_is_doc_hidden(self.tcx) + } else { + i.attrs.lists(sym::doc).has_word(sym::hidden) + } + } } impl<'tcx> DocFolder for ImportStripper<'tcx> { fn fold_item(&mut self, i: Item) -> Option<Item> { match *i.kind { + clean::ImportItem(imp) if self.import_should_be_hidden(&i, &imp) => None, + clean::ImportItem(_) if i.attrs.lists(sym::doc).has_word(sym::hidden) => None, clean::ExternCrateItem { .. } | clean::ImportItem(..) if i.visibility(self.tcx) != Some(Visibility::Public) => { diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs index d29ceead4..390b94361 100644 --- a/src/librustdoc/visit.rs +++ b/src/librustdoc/visit.rs @@ -17,10 +17,10 @@ pub(crate) trait DocVisitor: Sized { EnumItem(i) => i.variants.iter().for_each(|x| self.visit_item(x)), TraitItem(i) => i.items.iter().for_each(|x| self.visit_item(x)), ImplItem(i) => i.items.iter().for_each(|x| self.visit_item(x)), - VariantItem(i) => match i { - Variant::Struct(j) => j.fields.iter().for_each(|x| self.visit_item(x)), - Variant::Tuple(fields) => fields.iter().for_each(|x| self.visit_item(x)), - Variant::CLike(_) => {} + VariantItem(i) => match &i.kind { + VariantKind::Struct(j) => j.fields.iter().for_each(|x| self.visit_item(x)), + VariantKind::Tuple(fields) => fields.iter().for_each(|x| self.visit_item(x)), + VariantKind::CLike => {} }, ExternCrateItem { src: _ } | ImportItem(_) |