From 20431706a863f92cb37dc512fef6e48d192aaf2c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:11:38 +0200 Subject: Merging upstream version 1.66.0+dfsg1. Signed-off-by: Daniel Baumann --- src/librustdoc/clean/auto_trait.rs | 15 +- src/librustdoc/clean/blanket_impl.rs | 209 +++---- src/librustdoc/clean/inline.rs | 53 +- src/librustdoc/clean/mod.rs | 339 +++++----- src/librustdoc/clean/simplify.rs | 60 +- src/librustdoc/clean/types.rs | 56 +- src/librustdoc/clean/utils.rs | 19 +- src/librustdoc/config.rs | 80 +-- src/librustdoc/core.rs | 48 +- src/librustdoc/doctest.rs | 18 +- src/librustdoc/fold.rs | 2 +- src/librustdoc/formats/cache.rs | 29 +- src/librustdoc/formats/renderer.rs | 4 +- src/librustdoc/html/format.rs | 33 +- src/librustdoc/html/highlight.rs | 13 +- src/librustdoc/html/markdown.rs | 34 +- src/librustdoc/html/markdown/tests.rs | 48 +- src/librustdoc/html/render/context.rs | 65 +- src/librustdoc/html/render/mod.rs | 210 ++++--- src/librustdoc/html/render/print_item.rs | 46 +- src/librustdoc/html/render/write_shared.rs | 6 +- src/librustdoc/html/sources.rs | 2 +- src/librustdoc/html/static/css/noscript.css | 6 +- src/librustdoc/html/static/css/rustdoc.css | 683 +++++++++------------ src/librustdoc/html/static/css/settings.css | 22 +- src/librustdoc/html/static/css/themes/ayu.css | 117 +--- src/librustdoc/html/static/css/themes/dark.css | 116 +--- src/librustdoc/html/static/css/themes/light.css | 109 +--- src/librustdoc/html/static/js/main.js | 264 +++++--- src/librustdoc/html/static/js/scrape-examples.js | 2 +- src/librustdoc/html/static/js/settings.js | 11 +- src/librustdoc/html/static/js/source-script.js | 37 +- src/librustdoc/html/static/js/storage.js | 4 +- src/librustdoc/html/templates/page.html | 54 +- src/librustdoc/html/templates/print_item.html | 24 +- src/librustdoc/json/conversions.rs | 12 +- src/librustdoc/json/mod.rs | 8 +- src/librustdoc/lib.rs | 87 ++- src/librustdoc/lint.rs | 2 +- src/librustdoc/markdown.rs | 7 +- src/librustdoc/passes/bare_urls.rs | 6 +- src/librustdoc/passes/check_code_block_syntax.rs | 95 +-- src/librustdoc/passes/check_doc_test_visibility.rs | 14 +- src/librustdoc/passes/collect_intra_doc_links.rs | 28 +- .../passes/collect_intra_doc_links/early.rs | 44 +- src/librustdoc/passes/html_tags.rs | 72 ++- src/librustdoc/passes/propagate_doc_cfg.rs | 2 +- src/librustdoc/passes/strip_private.rs | 2 +- src/librustdoc/passes/stripper.rs | 44 +- src/librustdoc/scrape_examples.rs | 11 +- src/librustdoc/visit.rs | 2 +- src/librustdoc/visit_ast.rs | 20 +- src/librustdoc/visit_lib.rs | 32 +- 53 files changed, 1701 insertions(+), 1625 deletions(-) (limited to 'src/librustdoc') diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 175472797..764a6d3aa 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -148,7 +148,7 @@ where }) .collect(); // We are only interested in case the type *doesn't* implement the Sized trait. - if !ty.is_sized(tcx.at(rustc_span::DUMMY_SP), param_env) { + if !ty.is_sized(tcx, param_env) { // In case `#![no_core]` is used, `sized_trait` returns nothing. if let Some(item) = tcx.lang_items().sized_trait().and_then(|sized_trait_did| { self.generate_for_trait(ty, sized_trait_did, param_env, item_def_id, &f, true) @@ -475,6 +475,12 @@ where let mut ty_to_fn: FxHashMap)> = Default::default(); + // FIXME: This code shares much of the logic found in `clean_ty_generics` and + // `simplify::where_clause`. Consider deduplicating it to avoid diverging + // implementations. + // Further, the code below does not merge (partially re-sugared) bounds like + // `Tr` & `Tr` and it does not render higher-ranked parameters + // originating from equality predicates. for p in clean_where_predicates { let (orig_p, p) = (p, clean_predicate(p, self.cx)); if p.is_none() { @@ -549,8 +555,8 @@ where WherePredicate::RegionPredicate { lifetime, bounds } => { lifetime_to_bounds.entry(lifetime).or_default().extend(bounds); } - WherePredicate::EqPredicate { lhs, rhs } => { - match lhs { + WherePredicate::EqPredicate { lhs, rhs, bound_params } => { + match *lhs { Type::QPath(box QPathData { ref assoc, ref self_type, ref trait_, .. }) => { @@ -585,13 +591,14 @@ where GenericArgs::AngleBracketed { ref mut bindings, .. } => { bindings.push(TypeBinding { assoc: assoc.clone(), - kind: TypeBindingKind::Equality { term: rhs }, + kind: TypeBindingKind::Equality { term: *rhs }, }); } GenericArgs::Parenthesized { .. } => { existing_predicates.push(WherePredicate::EqPredicate { lhs: lhs.clone(), rhs, + bound_params, }); continue; // If something other than a Fn ends up // with parentheses, leave it alone diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index cc734389e..8b63c3db3 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -2,7 +2,6 @@ use crate::rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtE use rustc_hir as hir; use rustc_infer::infer::{InferOk, TyCtxtInferExt}; use rustc_infer::traits; -use rustc_middle::ty::subst::Subst; use rustc_middle::ty::ToPredicate; use rustc_span::DUMMY_SP; @@ -14,122 +13,124 @@ pub(crate) struct BlanketImplFinder<'a, 'tcx> { impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { pub(crate) fn get_blanket_impls(&mut self, item_def_id: DefId) -> Vec { - let param_env = self.cx.tcx.param_env(item_def_id); - let ty = self.cx.tcx.bound_type_of(item_def_id); + let cx = &mut self.cx; + let param_env = cx.tcx.param_env(item_def_id); + let ty = cx.tcx.bound_type_of(item_def_id); trace!("get_blanket_impls({:?})", ty); let mut impls = Vec::new(); - self.cx.with_all_traits(|cx, all_traits| { - for &trait_def_id in all_traits { - if !cx.cache.access_levels.is_public(trait_def_id) - || cx.generated_synthetics.get(&(ty.0, trait_def_id)).is_some() - { + for trait_def_id in cx.tcx.all_traits() { + if !cx.cache.effective_visibilities.is_directly_public(trait_def_id) + || cx.generated_synthetics.get(&(ty.0, trait_def_id)).is_some() + { + continue; + } + // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls + let trait_impls = cx.tcx.trait_impls_of(trait_def_id); + 'blanket_impls: for &impl_def_id in trait_impls.blanket_impls() { + trace!( + "get_blanket_impls: Considering impl for trait '{:?}' {:?}", + trait_def_id, + impl_def_id + ); + let trait_ref = cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap(); + if !matches!(trait_ref.0.self_ty().kind(), ty::Param(_)) { continue; } - // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls - let trait_impls = cx.tcx.trait_impls_of(trait_def_id); - for &impl_def_id in trait_impls.blanket_impls() { - trace!( - "get_blanket_impls: Considering impl for trait '{:?}' {:?}", - trait_def_id, - impl_def_id - ); - let trait_ref = cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap(); - let is_param = matches!(trait_ref.0.self_ty().kind(), ty::Param(_)); - let may_apply = is_param && cx.tcx.infer_ctxt().enter(|infcx| { - let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id); - let ty = ty.subst(infcx.tcx, substs); - let param_env = EarlyBinder(param_env).subst(infcx.tcx, substs); + let infcx = cx.tcx.infer_ctxt().build(); + let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id); + let impl_ty = ty.subst(infcx.tcx, substs); + let param_env = EarlyBinder(param_env).subst(infcx.tcx, substs); - let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); - let trait_ref = trait_ref.subst(infcx.tcx, impl_substs); + let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = trait_ref.subst(infcx.tcx, impl_substs); - // Require the type the impl is implemented on to match - // our type, and ignore the impl if there was a mismatch. - let cause = traits::ObligationCause::dummy(); - let eq_result = infcx.at(&cause, param_env).eq(trait_ref.self_ty(), ty); - if let Ok(InferOk { value: (), obligations }) = eq_result { - // FIXME(eddyb) ignoring `obligations` might cause false positives. - drop(obligations); + // Require the type the impl is implemented on to match + // our type, and ignore the impl if there was a mismatch. + let cause = traits::ObligationCause::dummy(); + let Ok(eq_result) = infcx.at(&cause, param_env).eq(impl_trait_ref.self_ty(), impl_ty) else { + continue + }; + let InferOk { value: (), obligations } = eq_result; + // FIXME(eddyb) ignoring `obligations` might cause false positives. + drop(obligations); - trace!( - "invoking predicate_may_hold: param_env={:?}, trait_ref={:?}, ty={:?}", - param_env, - trait_ref, - ty - ); - let predicates = cx - .tcx - .predicates_of(impl_def_id) - .instantiate(cx.tcx, impl_substs) - .predicates - .into_iter() - .chain(Some( - ty::Binder::dummy(trait_ref) - .to_poly_trait_predicate() - .map_bound(ty::PredicateKind::Trait) - .to_predicate(infcx.tcx), - )); - for predicate in predicates { - debug!("testing predicate {:?}", predicate); - let obligation = traits::Obligation::new( - traits::ObligationCause::dummy(), - param_env, - predicate, - ); - match infcx.evaluate_obligation(&obligation) { - Ok(eval_result) if eval_result.may_apply() => {} - Err(traits::OverflowError::Canonical) => {} - Err(traits::OverflowError::ErrorReporting) => {} - _ => { - return false; - } - } - } - true - } else { - false - } - }); - debug!( - "get_blanket_impls: found applicable impl: {} for trait_ref={:?}, ty={:?}", - may_apply, trait_ref, ty + trace!( + "invoking predicate_may_hold: param_env={:?}, impl_trait_ref={:?}, impl_ty={:?}", + param_env, + impl_trait_ref, + impl_ty + ); + let predicates = cx + .tcx + .predicates_of(impl_def_id) + .instantiate(cx.tcx, impl_substs) + .predicates + .into_iter() + .chain(Some( + ty::Binder::dummy(impl_trait_ref) + .to_poly_trait_predicate() + .map_bound(ty::PredicateKind::Trait) + .to_predicate(infcx.tcx), + )); + for predicate in predicates { + debug!("testing predicate {:?}", predicate); + let obligation = traits::Obligation::new( + traits::ObligationCause::dummy(), + param_env, + predicate, ); - if !may_apply { - continue; + match infcx.evaluate_obligation(&obligation) { + Ok(eval_result) if eval_result.may_apply() => {} + Err(traits::OverflowError::Canonical) => {} + Err(traits::OverflowError::ErrorReporting) => {} + _ => continue 'blanket_impls, } + } + debug!( + "get_blanket_impls: found applicable impl for trait_ref={:?}, ty={:?}", + trait_ref, ty + ); - cx.generated_synthetics.insert((ty.0, trait_def_id)); + cx.generated_synthetics.insert((ty.0, trait_def_id)); - impls.push(Item { - name: None, - attrs: Default::default(), - visibility: Inherited, - item_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, - kind: Box::new(ImplItem(Box::new(Impl { - unsafety: hir::Unsafety::Normal, - generics: clean_ty_generics( - cx, - cx.tcx.generics_of(impl_def_id), - cx.tcx.explicit_predicates_of(impl_def_id), - ), - // FIXME(eddyb) compute both `trait_` and `for_` from - // the post-inference `trait_ref`, as it's more accurate. - trait_: Some(clean_trait_ref_with_bindings(cx, trait_ref.0, ThinVec::new())), - for_: clean_middle_ty(ty.0, cx, None), - items: cx.tcx - .associated_items(impl_def_id) - .in_definition_order() - .map(|x| clean_middle_assoc_item(x, cx)) - .collect::>(), - polarity: ty::ImplPolarity::Positive, - kind: ImplKind::Blanket(Box::new(clean_middle_ty(trait_ref.0.self_ty(), cx, None))), - }))), - cfg: None, - }); - } + impls.push(Item { + name: None, + attrs: Default::default(), + visibility: Inherited, + item_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, + kind: Box::new(ImplItem(Box::new(Impl { + unsafety: hir::Unsafety::Normal, + generics: clean_ty_generics( + cx, + cx.tcx.generics_of(impl_def_id), + cx.tcx.explicit_predicates_of(impl_def_id), + ), + // FIXME(eddyb) compute both `trait_` and `for_` from + // the post-inference `trait_ref`, as it's more accurate. + trait_: Some(clean_trait_ref_with_bindings( + cx, + trait_ref.0, + ThinVec::new(), + )), + for_: clean_middle_ty(ty.0, cx, None), + items: cx + .tcx + .associated_items(impl_def_id) + .in_definition_order() + .map(|x| clean_middle_assoc_item(x, cx)) + .collect::>(), + polarity: ty::ImplPolarity::Positive, + kind: ImplKind::Blanket(Box::new(clean_middle_ty( + trait_ref.0.self_ty(), + cx, + None, + ))), + }))), + cfg: None, + }); } - }); + } impls } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index df0e9f7cc..4e2031a91 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -55,12 +55,39 @@ pub(crate) fn try_inline( let mut ret = Vec::new(); debug!("attrs={:?}", attrs); - let attrs_clone = attrs; + + let attrs_without_docs = attrs.map(|attrs| { + attrs.into_iter().filter(|a| a.doc_str().is_none()).cloned().collect::>() + }); + // We need this ugly code because: + // + // ``` + // attrs_without_docs.map(|a| a.as_slice()) + // ``` + // + // will fail because it returns a temporary slice and: + // + // ``` + // attrs_without_docs.map(|s| { + // vec = s.as_slice(); + // vec + // }) + // ``` + // + // will fail because we're moving an uninitialized variable into a closure. + let vec; + let attrs_without_docs = match attrs_without_docs { + Some(s) => { + vec = s; + Some(vec.as_slice()) + } + None => None, + }; let kind = match res { Res::Def(DefKind::Trait, did) => { record_extern_fqn(cx, did, ItemType::Trait); - build_impls(cx, Some(parent_module), did, attrs, &mut ret); + build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); clean::TraitItem(Box::new(build_external_trait(cx, did))) } Res::Def(DefKind::Fn, did) => { @@ -69,27 +96,27 @@ pub(crate) fn try_inline( } Res::Def(DefKind::Struct, did) => { record_extern_fqn(cx, did, ItemType::Struct); - build_impls(cx, Some(parent_module), did, attrs, &mut ret); + build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); clean::StructItem(build_struct(cx, did)) } Res::Def(DefKind::Union, did) => { record_extern_fqn(cx, did, ItemType::Union); - build_impls(cx, Some(parent_module), did, attrs, &mut ret); + build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); clean::UnionItem(build_union(cx, did)) } Res::Def(DefKind::TyAlias, did) => { record_extern_fqn(cx, did, ItemType::Typedef); - build_impls(cx, Some(parent_module), did, attrs, &mut ret); + build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); clean::TypedefItem(build_type_alias(cx, did)) } Res::Def(DefKind::Enum, did) => { record_extern_fqn(cx, did, ItemType::Enum); - build_impls(cx, Some(parent_module), did, attrs, &mut ret); + build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); clean::EnumItem(build_enum(cx, did)) } Res::Def(DefKind::ForeignTy, did) => { record_extern_fqn(cx, did, ItemType::ForeignType); - build_impls(cx, Some(parent_module), did, attrs, &mut ret); + build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); clean::ForeignTypeItem } // Never inline enum variants but leave them shown as re-exports. @@ -123,7 +150,7 @@ pub(crate) fn try_inline( _ => return None, }; - let (attrs, cfg) = merge_attrs(cx, Some(parent_module), load_attrs(cx, did), attrs_clone); + let (attrs, cfg) = merge_attrs(cx, Some(parent_module), load_attrs(cx, did), attrs); cx.inlined.insert(did.into()); let mut item = clean::Item::from_def_id_and_attrs_and_parts( did, @@ -362,7 +389,7 @@ pub(crate) fn build_impl( if !did.is_local() { if let Some(traitref) = associated_trait { let did = traitref.def_id; - if !cx.cache.access_levels.is_public(did) { + if !cx.cache.effective_visibilities.is_directly_public(did) { return; } @@ -391,7 +418,7 @@ pub(crate) fn build_impl( // reachable in rustdoc generated documentation if !did.is_local() { if let Some(did) = for_.def_id(&cx.cache) { - if !cx.cache.access_levels.is_public(did) { + if !cx.cache.effective_visibilities.is_directly_public(did) { return; } @@ -425,7 +452,7 @@ pub(crate) fn build_impl( let assoc_kind = match item.kind { hir::ImplItemKind::Const(..) => ty::AssocKind::Const, hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn, - hir::ImplItemKind::TyAlias(..) => ty::AssocKind::Type, + hir::ImplItemKind::Type(..) => ty::AssocKind::Type, }; let trait_item = tcx .associated_items(associated_trait.def_id) @@ -733,10 +760,6 @@ pub(crate) fn record_extern_trait(cx: &mut DocContext<'_>, did: DefId) { debug!("record_extern_trait: {:?}", did); let trait_ = build_external_trait(cx, did); - let trait_ = clean::TraitWithExtraInfo { - trait_, - is_notable: clean::utils::has_doc_flag(cx.tcx, did, sym::notable_trait), - }; cx.external_traits.borrow_mut().insert(did, trait_); cx.active_extern_traits.remove(&did); } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c8875c272..64a18757b 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -17,16 +17,16 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::PredicateOrigin; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; use rustc_middle::middle::resolve_lifetime as rl; use rustc_middle::ty::fold::TypeFolder; -use rustc_middle::ty::subst::{InternalSubsts, Subst}; -use rustc_middle::ty::{self, AdtKind, DefIdTree, EarlyBinder, Lift, Ty, TyCtxt}; +use rustc_middle::ty::InternalSubsts; +use rustc_middle::ty::{self, AdtKind, DefIdTree, EarlyBinder, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{AstPass, MacroKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{self, ExpnKind}; -use rustc_typeck::hir_ty_to_ty; use std::assert_matches::assert_matches; use std::collections::hash_map::Entry; @@ -176,8 +176,6 @@ fn clean_poly_trait_ref_with_bindings<'tcx>( poly_trait_ref: ty::PolyTraitRef<'tcx>, bindings: ThinVec, ) -> GenericBound { - let poly_trait_ref = poly_trait_ref.lift_to_tcx(cx.tcx).unwrap(); - // collect any late bound regions let late_bound_regions: Vec<_> = cx .tcx @@ -292,8 +290,9 @@ fn clean_where_predicate<'tcx>( }, hir::WherePredicate::EqPredicate(ref wrp) => WherePredicate::EqPredicate { - lhs: clean_ty(wrp.lhs_ty, cx), - rhs: clean_ty(wrp.rhs_ty, cx).into(), + lhs: Box::new(clean_ty(wrp.lhs_ty, cx)), + rhs: Box::new(clean_ty(wrp.rhs_ty, cx).into()), + bound_params: Vec::new(), }, }) } @@ -309,7 +308,9 @@ pub(crate) fn clean_predicate<'tcx>( } ty::PredicateKind::RegionOutlives(pred) => clean_region_outlives_predicate(pred), ty::PredicateKind::TypeOutlives(pred) => clean_type_outlives_predicate(pred, cx), - ty::PredicateKind::Projection(pred) => Some(clean_projection_predicate(pred, cx)), + ty::PredicateKind::Projection(pred) => { + Some(clean_projection_predicate(bound_predicate.rebind(pred), cx)) + } ty::PredicateKind::ConstEvaluatable(..) => None, ty::PredicateKind::WellFormed(..) => None, @@ -387,13 +388,25 @@ fn clean_hir_term<'tcx>(term: &hir::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Te } fn clean_projection_predicate<'tcx>( - pred: ty::ProjectionPredicate<'tcx>, + pred: ty::Binder<'tcx, ty::ProjectionPredicate<'tcx>>, cx: &mut DocContext<'tcx>, ) -> WherePredicate { - let ty::ProjectionPredicate { projection_ty, term } = pred; + let late_bound_regions = cx + .tcx + .collect_referenced_late_bound_regions(&pred) + .into_iter() + .filter_map(|br| match br { + ty::BrNamed(_, name) if name != kw::UnderscoreLifetime => Some(Lifetime(name)), + _ => None, + }) + .collect(); + + let ty::ProjectionPredicate { projection_ty, term } = pred.skip_binder(); + WherePredicate::EqPredicate { - lhs: clean_projection(projection_ty, cx, None), - rhs: clean_middle_term(term, cx), + lhs: Box::new(clean_projection(projection_ty, cx, None)), + rhs: Box::new(clean_middle_term(term, cx)), + bound_params: late_bound_regions, } } @@ -402,8 +415,17 @@ fn clean_projection<'tcx>( cx: &mut DocContext<'tcx>, def_id: Option, ) -> Type { - let lifted = ty.lift_to_tcx(cx.tcx).unwrap(); - let trait_ = clean_trait_ref_with_bindings(cx, lifted.trait_ref(cx.tcx), ThinVec::new()); + if cx.tcx.def_kind(ty.item_def_id) == DefKind::ImplTraitPlaceholder { + let bounds = cx + .tcx + .explicit_item_bounds(ty.item_def_id) + .iter() + .map(|(bound, _)| EarlyBinder(*bound).subst(cx.tcx, ty.substs)) + .collect::>(); + return clean_middle_opaque_bounds(cx, bounds); + } + + let trait_ = clean_trait_ref_with_bindings(cx, ty.trait_ref(cx.tcx), ThinVec::new()); let self_type = clean_middle_ty(ty.self_ty(), cx, None); let self_def_id = if let Some(def_id) = def_id { cx.tcx.opt_parent(def_id).or(Some(def_id)) @@ -632,7 +654,7 @@ fn clean_ty_generics<'tcx>( let mut impl_trait = BTreeMap::>::default(); // Bounds in the type_params and lifetimes fields are repeated in the - // predicates field (see rustc_typeck::collect::ty_generics), so remove + // predicates field (see rustc_hir_analysis::collect::ty_generics), so remove // them. let stripped_params = gens .params @@ -655,8 +677,9 @@ fn clean_ty_generics<'tcx>( }) .collect::>(); - // param index -> [(DefId of trait, associated type name and generics, type)] - let mut impl_trait_proj = FxHashMap::)>>::default(); + // param index -> [(trait DefId, associated type name & generics, type, higher-ranked params)] + let mut impl_trait_proj = + FxHashMap::, Vec)>>::default(); let where_predicates = preds .predicates @@ -715,6 +738,14 @@ fn clean_ty_generics<'tcx>( trait_did, name, rhs.ty().unwrap(), + p.get_bound_params() + .into_iter() + .flatten() + .map(|param| GenericParamDef { + name: param.0, + kind: GenericParamDefKind::Lifetime { outlives: Vec::new() }, + }) + .collect(), )); } @@ -730,15 +761,19 @@ fn clean_ty_generics<'tcx>( // Move trait bounds to the front. bounds.sort_by_key(|b| !matches!(b, GenericBound::TraitBound(..))); - if let crate::core::ImplTraitParam::ParamIndex(idx) = param { - if let Some(proj) = impl_trait_proj.remove(&idx) { - for (trait_did, name, rhs) in proj { - let rhs = clean_middle_ty(rhs, cx, None); - simplify::merge_bounds(cx, &mut bounds, trait_did, name, &Term::Type(rhs)); - } + let crate::core::ImplTraitParam::ParamIndex(idx) = param else { unreachable!() }; + if let Some(proj) = impl_trait_proj.remove(&idx) { + for (trait_did, name, rhs, bound_params) in proj { + let rhs = clean_middle_ty(rhs, cx, None); + simplify::merge_bounds( + cx, + &mut bounds, + bound_params, + trait_did, + name, + &Term::Type(rhs), + ); } - } else { - unreachable!(); } cx.impl_trait_bounds.insert(param, bounds); @@ -749,31 +784,36 @@ fn clean_ty_generics<'tcx>( let mut where_predicates = where_predicates.into_iter().flat_map(|p| clean_predicate(*p, cx)).collect::>(); - // Type parameters have a Sized bound by default unless removed with - // ?Sized. Scan through the predicates and mark any type parameter with - // a Sized bound, removing the bounds as we find them. + // In the surface language, all type parameters except `Self` have an + // implicit `Sized` bound unless removed with `?Sized`. + // However, in the list of where-predicates below, `Sized` appears like a + // normal bound: It's either present (the type is sized) or + // absent (the type is unsized) but never *maybe* (i.e. `?Sized`). + // + // This is unsuitable for rendering. + // Thus, as a first step remove all `Sized` bounds that should be implicit. // - // Note that associated types also have a sized bound by default, but we + // Note that associated types also have an implicit `Sized` bound but we // don't actually know the set of associated types right here so that's - // handled in cleaning associated types + // handled when cleaning associated types. let mut sized_params = FxHashSet::default(); - where_predicates.retain(|pred| match *pred { - WherePredicate::BoundPredicate { ty: Generic(ref g), ref bounds, .. } => { - if bounds.iter().any(|b| b.is_sized_bound(cx)) { - sized_params.insert(*g); - false - } else { - true - } + where_predicates.retain(|pred| { + if let WherePredicate::BoundPredicate { ty: Generic(g), bounds, .. } = pred + && *g != kw::SelfUpper + && bounds.iter().any(|b| b.is_sized_bound(cx)) + { + sized_params.insert(*g); + false + } else { + true } - _ => true, }); - // Run through the type parameters again and insert a ?Sized - // unbound for any we didn't find to be Sized. + // As a final step, go through the type parameters again and insert a + // `?Sized` bound for each one we didn't find to be `Sized`. for tp in &stripped_params { - if matches!(tp.kind, types::GenericParamDefKind::Type { .. }) - && !sized_params.contains(&tp.name) + if let types::GenericParamDefKind::Type { .. } = tp.kind + && !sized_params.contains(&tp.name) { where_predicates.push(WherePredicate::BoundPredicate { ty: Type::Generic(tp.name), @@ -1002,7 +1042,7 @@ fn clean_poly_trait_ref<'tcx>( } fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext<'tcx>) -> Item { - let local_did = trait_item.def_id.to_def_id(); + let local_did = trait_item.owner_id.to_def_id(); cx.with_param_env(local_did, |cx| { let inner = match trait_item.kind { hir::TraitItemKind::Const(ty, Some(default)) => AssocConstItem( @@ -1054,7 +1094,7 @@ pub(crate) fn clean_impl_item<'tcx>( impl_: &hir::ImplItem<'tcx>, cx: &mut DocContext<'tcx>, ) -> Item { - let local_did = impl_.def_id.to_def_id(); + let local_did = impl_.owner_id.to_def_id(); cx.with_param_env(local_did, |cx| { let inner = match impl_.kind { hir::ImplItemKind::Const(ty, expr) => { @@ -1063,10 +1103,10 @@ pub(crate) fn clean_impl_item<'tcx>( } hir::ImplItemKind::Fn(ref sig, body) => { let m = clean_function(cx, sig, impl_.generics, body); - let defaultness = cx.tcx.impl_defaultness(impl_.def_id); + let defaultness = cx.tcx.impl_defaultness(impl_.owner_id); MethodItem(m, Some(defaultness)) } - hir::ImplItemKind::TyAlias(hir_ty) => { + 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); @@ -1080,7 +1120,7 @@ pub(crate) fn clean_impl_item<'tcx>( let mut what_rustc_thinks = Item::from_def_id_and_parts(local_did, Some(impl_.ident.name), inner, cx); - let impl_ref = cx.tcx.impl_trait_ref(cx.tcx.local_parent(impl_.def_id)); + let impl_ref = cx.tcx.impl_trait_ref(cx.tcx.local_parent(impl_.owner_id.def_id)); // Trait impl items always inherit the impl's visibility -- // we don't want to show `pub`. @@ -1177,11 +1217,18 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( if let ty::TraitContainer = assoc_item.container { let bounds = tcx.explicit_item_bounds(assoc_item.def_id); - let predicates = ty::GenericPredicates { parent: None, predicates: bounds }; - let mut generics = - clean_ty_generics(cx, tcx.generics_of(assoc_item.def_id), predicates); - // Filter out the bounds that are (likely?) directly attached to the associated type, - // as opposed to being located in the where clause. + let predicates = tcx.explicit_predicates_of(assoc_item.def_id).predicates; + let predicates = + tcx.arena.alloc_from_iter(bounds.into_iter().chain(predicates).copied()); + let mut generics = clean_ty_generics( + cx, + tcx.generics_of(assoc_item.def_id), + ty::GenericPredicates { parent: None, predicates }, + ); + // Move bounds that are (likely) directly attached to the associated type + // from the where clause to the associated type. + // There is no guarantee that this is what the user actually wrote but we have + // no way of knowing. let mut bounds = generics .where_predicates .drain_filter(|pred| match *pred { @@ -1239,6 +1286,24 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( } None => bounds.push(GenericBound::maybe_sized(cx)), } + // Move bounds that are (likely) directly attached to the parameters of the + // (generic) associated type from the where clause to the respective parameter. + // There is no guarantee that this is what the user actually wrote but we have + // no way of knowing. + let mut where_predicates = Vec::new(); + for mut pred in generics.where_predicates { + if let WherePredicate::BoundPredicate { ty: Generic(arg), bounds, .. } = &mut pred + && let Some(GenericParamDef { + kind: GenericParamDefKind::Type { bounds: param_bounds, .. }, + .. + }) = generics.params.iter_mut().find(|param| ¶m.name == arg) + { + param_bounds.extend(mem::take(bounds)); + } else { + where_predicates.push(pred); + } + } + generics.where_predicates = where_predicates; if tcx.impl_defaultness(assoc_item.def_id).has_value() { AssocTypeItem( @@ -1325,7 +1390,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type segments: trait_segments.iter().map(|x| clean_path_segment(x, cx)).collect(), }; register_res(cx, trait_.res); - let self_def_id = DefId::local(qself.hir_id.owner.local_def_index); + let self_def_id = DefId::local(qself.hir_id.owner.def_id.local_def_index); let self_type = clean_ty(qself, cx); let should_show_cast = compute_should_show_cast(Some(self_def_id), &trait_, &self_type); Type::QPath(Box::new(QPathData { @@ -1366,7 +1431,7 @@ fn maybe_expand_private_type_alias<'tcx>( let Res::Def(DefKind::TyAlias, def_id) = path.res else { return None }; // Substitute private type aliases let def_id = def_id.as_local()?; - let alias = if !cx.cache.access_levels.is_exported(def_id.to_def_id()) { + let alias = if !cx.cache.effective_visibilities.is_exported(def_id.to_def_id()) { &cx.tcx.hir().expect_item(def_id).kind } else { return None; @@ -1480,7 +1545,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T // as we currently do not supply the parent generics to anonymous constants // but do allow `ConstKind::Param`. // - // `const_eval_poly` tries to to first substitute generic parameters which + // `const_eval_poly` tries to first substitute generic parameters which // results in an ICE while manually constructing the constant and using `eval` // does nothing for `ConstKind::Param`. let ct = ty::Const::from_anon_const(cx.tcx, def_id); @@ -1509,13 +1574,12 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T } TyKind::BareFn(barefn) => BareFunction(Box::new(clean_bare_fn_ty(barefn, cx))), // Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s. - TyKind::Infer | TyKind::Err => Infer, - TyKind::Typeof(..) => panic!("unimplemented type {:?}", ty.kind), + TyKind::Infer | TyKind::Err | TyKind::Typeof(..) => Infer, } } /// Returns `None` if the type could not be normalized -fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'_>) -> Option> { +fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'tcx>) -> Option> { // 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; @@ -1526,13 +1590,11 @@ fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'_>) -> Option> { use rustc_middle::traits::ObligationCause; // Try to normalize `::T` to a type - let lifted = ty.lift_to_tcx(cx.tcx).unwrap(); - let normalized = cx.tcx.infer_ctxt().enter(|infcx| { - infcx - .at(&ObligationCause::dummy(), cx.param_env) - .normalize(lifted) - .map(|resolved| infcx.resolve_vars_if_possible(resolved.value)) - }); + let infcx = cx.tcx.infer_ctxt().build(); + let normalized = infcx + .at(&ObligationCause::dummy(), cx.param_env) + .normalize(ty) + .map(|resolved| infcx.resolve_vars_if_possible(resolved.value)); match normalized { Ok(normalized_value) => { debug!("normalized {:?} to {:?}", ty, normalized_value); @@ -1546,12 +1608,12 @@ fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'_>) -> Option> { } pub(crate) fn clean_middle_ty<'tcx>( - this: Ty<'tcx>, + ty: Ty<'tcx>, cx: &mut DocContext<'tcx>, def_id: Option, ) -> Type { - trace!("cleaning type: {:?}", this); - let ty = normalize(cx, this).unwrap_or(this); + trace!("cleaning type: {:?}", ty); + let ty = normalize(cx, ty).unwrap_or(ty); match *ty.kind() { ty::Never => Primitive(PrimitiveType::Never), ty::Bool => Primitive(PrimitiveType::Bool), @@ -1561,8 +1623,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Float(float_ty) => Primitive(float_ty.into()), ty::Str => Primitive(PrimitiveType::Str), ty::Slice(ty) => Slice(Box::new(clean_middle_ty(ty, cx, None))), - ty::Array(ty, n) => { - let mut n = cx.tcx.lift(n).expect("array lift failed"); + ty::Array(ty, mut n) => { n = n.eval(cx.tcx, ty::ParamEnv::reveal_all()); let n = print_const(cx, n); Array(Box::new(clean_middle_ty(ty, cx, None)), n) @@ -1574,7 +1635,6 @@ pub(crate) fn clean_middle_ty<'tcx>( type_: Box::new(clean_middle_ty(ty, cx, None)), }, ty::FnDef(..) | ty::FnPtr(_) => { - let ty = cx.tcx.lift(this).expect("FnPtr lift failed"); let sig = ty.fn_sig(cx.tcx); let decl = clean_fn_decl_from_did_and_sig(cx, None, sig); BareFunction(Box::new(BareFunctionDecl { @@ -1608,7 +1668,7 @@ 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?", this)); + .unwrap_or_else(|| panic!("found trait object `{:?}` with no traits?", ty)); let substs = match obj.principal() { Some(principal) => principal.skip_binder().substs, // marker traits have no substs. @@ -1632,8 +1692,6 @@ pub(crate) fn clean_middle_ty<'tcx>( .map(|pb| TypeBinding { assoc: projection_to_path_segment( pb.skip_binder() - .lift_to_tcx(cx.tcx) - .unwrap() // HACK(compiler-errors): Doesn't actually matter what self // type we put here, because we're only using the GAT's substs. .with_self_ty(cx.tcx, cx.tcx.types.self_param) @@ -1666,66 +1724,13 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Opaque(def_id, substs) => { // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, // by looking up the bounds associated with the def_id. - let substs = cx.tcx.lift(substs).expect("Opaque lift failed"); let bounds = cx .tcx .explicit_item_bounds(def_id) .iter() .map(|(bound, _)| EarlyBinder(*bound).subst(cx.tcx, substs)) .collect::>(); - let mut regions = vec![]; - let mut has_sized = false; - let mut bounds = bounds - .iter() - .filter_map(|bound| { - let bound_predicate = bound.kind(); - let trait_ref = match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(tr) => bound_predicate.rebind(tr.trait_ref), - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { - if let Some(r) = clean_middle_region(reg) { - regions.push(GenericBound::Outlives(r)); - } - return None; - } - _ => return None, - }; - - if let Some(sized) = cx.tcx.lang_items().sized_trait() { - if trait_ref.def_id() == sized { - has_sized = true; - return None; - } - } - - let bindings: ThinVec<_> = bounds - .iter() - .filter_map(|bound| { - if let ty::PredicateKind::Projection(proj) = bound.kind().skip_binder() - { - if proj.projection_ty.trait_ref(cx.tcx) == trait_ref.skip_binder() { - Some(TypeBinding { - assoc: projection_to_path_segment(proj.projection_ty, cx), - kind: TypeBindingKind::Equality { - term: clean_middle_term(proj.term, cx), - }, - }) - } else { - None - } - } else { - None - } - }) - .collect(); - - Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, bindings)) - }) - .collect::>(); - bounds.extend(regions); - if !has_sized && !bounds.is_empty() { - bounds.insert(0, GenericBound::maybe_sized(cx)); - } - ImplTrait(bounds) + clean_middle_opaque_bounds(cx, bounds) } ty::Closure(..) => panic!("Closure"), @@ -1738,6 +1743,64 @@ pub(crate) fn clean_middle_ty<'tcx>( } } +fn clean_middle_opaque_bounds<'tcx>( + cx: &mut DocContext<'tcx>, + bounds: Vec>, +) -> Type { + let mut regions = vec![]; + let mut has_sized = false; + let mut bounds = bounds + .iter() + .filter_map(|bound| { + let bound_predicate = bound.kind(); + let trait_ref = match bound_predicate.skip_binder() { + ty::PredicateKind::Trait(tr) => bound_predicate.rebind(tr.trait_ref), + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { + if let Some(r) = clean_middle_region(reg) { + regions.push(GenericBound::Outlives(r)); + } + return None; + } + _ => return None, + }; + + if let Some(sized) = cx.tcx.lang_items().sized_trait() { + if trait_ref.def_id() == sized { + has_sized = true; + return None; + } + } + + let bindings: ThinVec<_> = bounds + .iter() + .filter_map(|bound| { + if let ty::PredicateKind::Projection(proj) = bound.kind().skip_binder() { + if proj.projection_ty.trait_ref(cx.tcx) == trait_ref.skip_binder() { + Some(TypeBinding { + assoc: projection_to_path_segment(proj.projection_ty, cx), + kind: TypeBindingKind::Equality { + term: clean_middle_term(proj.term, cx), + }, + }) + } else { + None + } + } else { + None + } + }) + .collect(); + + Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, bindings)) + }) + .collect::>(); + bounds.extend(regions); + if !has_sized && !bounds.is_empty() { + bounds.insert(0, GenericBound::maybe_sized(cx)); + } + ImplTrait(bounds) +} + pub(crate) fn clean_field<'tcx>(field: &hir::FieldDef<'tcx>, cx: &mut DocContext<'tcx>) -> Item { let def_id = cx.tcx.hir().local_def_id(field.hir_id).to_def_id(); clean_field_with_def_id(def_id, field.ident.name, clean_ty(field.ty, cx), cx) @@ -1895,7 +1958,7 @@ fn clean_maybe_renamed_item<'tcx>( ) -> Vec { use hir::ItemKind; - let def_id = item.def_id.to_def_id(); + let def_id = item.owner_id.to_def_id(); let mut name = renamed.unwrap_or_else(|| cx.tcx.hir().name(item.hir_id())); cx.with_param_env(def_id, |cx| { let kind = match item.kind { @@ -2037,11 +2100,11 @@ fn clean_extern_crate<'tcx>( cx: &mut DocContext<'tcx>, ) -> Vec { // this is the ID of the `extern crate` statement - let cnum = cx.tcx.extern_mod_stmt_cnum(krate.def_id).unwrap_or(LOCAL_CRATE); + let cnum = cx.tcx.extern_mod_stmt_cnum(krate.owner_id.def_id).unwrap_or(LOCAL_CRATE); // this is the ID of the crate itself let crate_def_id = cnum.as_def_id(); let attrs = cx.tcx.hir().attrs(krate.hir_id()); - let ty_vis = cx.tcx.visibility(krate.def_id); + let ty_vis = cx.tcx.visibility(krate.owner_id); let please_inline = ty_vis.is_public() && attrs.iter().any(|a| { a.has_name(sym::doc) @@ -2059,7 +2122,7 @@ fn clean_extern_crate<'tcx>( if let Some(items) = inline::try_inline( cx, cx.tcx.parent_module(krate.hir_id()).to_def_id(), - Some(krate.def_id.to_def_id()), + Some(krate.owner_id.to_def_id()), res, name, Some(attrs), @@ -2095,11 +2158,11 @@ fn clean_use_statement<'tcx>( return Vec::new(); } - let visibility = cx.tcx.visibility(import.def_id); + let visibility = cx.tcx.visibility(import.owner_id); let attrs = cx.tcx.hir().attrs(import.hir_id()); let inline_attr = attrs.lists(sym::doc).get_word_attr(sym::inline); let pub_underscore = visibility.is_public() && name == kw::Underscore; - let current_mod = cx.tcx.parent_module_from_def_id(import.def_id); + let current_mod = cx.tcx.parent_module_from_def_id(import.owner_id.def_id); // The parent of the module in which this import resides. This // is the same as `current_mod` if that's already the top @@ -2170,7 +2233,7 @@ fn clean_use_statement<'tcx>( } if !denied { let mut visited = FxHashSet::default(); - let import_def_id = import.def_id.to_def_id(); + let import_def_id = import.owner_id.to_def_id(); if let Some(mut items) = inline::try_inline( cx, @@ -2193,7 +2256,7 @@ fn clean_use_statement<'tcx>( Import::new_simple(name, resolve_use_source(cx, path), true) }; - vec![Item::from_def_id_and_parts(import.def_id.to_def_id(), None, ImportItem(inner), cx)] + vec![Item::from_def_id_and_parts(import.owner_id.to_def_id(), None, ImportItem(inner), cx)] } fn clean_maybe_renamed_foreign_item<'tcx>( @@ -2201,7 +2264,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>( item: &hir::ForeignItem<'tcx>, renamed: Option, ) -> Item { - let def_id = item.def_id.to_def_id(); + let def_id = item.owner_id.to_def_id(); cx.with_param_env(def_id, |cx| { let kind = match item.kind { hir::ForeignItemKind::Fn(decl, names, generics) => { diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index af7813a77..1bcb9fcd5 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -14,7 +14,6 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_hir::def_id::DefId; use rustc_middle::ty; -use rustc_span::Symbol; use crate::clean; use crate::clean::GenericArgs as PP; @@ -26,38 +25,37 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec) -> Vec { // // We use `FxIndexMap` so that the insertion order is preserved to prevent messing up to // the order of the generated bounds. - let mut params: FxIndexMap, Vec<_>)> = FxIndexMap::default(); + let mut tybounds = FxIndexMap::default(); let mut lifetimes = Vec::new(); let mut equalities = Vec::new(); - let mut tybounds = Vec::new(); for clause in clauses { match clause { - WP::BoundPredicate { ty, bounds, bound_params } => match ty { - clean::Generic(s) => { - let (b, p) = params.entry(s).or_default(); - b.extend(bounds); - p.extend(bound_params); - } - t => tybounds.push((t, (bounds, bound_params))), - }, + WP::BoundPredicate { ty, bounds, bound_params } => { + let (b, p): &mut (Vec<_>, Vec<_>) = tybounds.entry(ty).or_default(); + b.extend(bounds); + p.extend(bound_params); + } WP::RegionPredicate { lifetime, bounds } => { lifetimes.push((lifetime, bounds)); } - WP::EqPredicate { lhs, rhs } => equalities.push((lhs, rhs)), + WP::EqPredicate { lhs, rhs, bound_params } => equalities.push((lhs, rhs, bound_params)), } } // Look for equality predicates on associated types that can be merged into - // general bound predicates - equalities.retain(|&(ref lhs, ref rhs)| { - let Some((self_, trait_did, name)) = lhs.projection() else { - return true; - }; - let clean::Generic(generic) = self_ else { return true }; - let Some((bounds, _)) = params.get_mut(generic) else { return true }; - - merge_bounds(cx, bounds, trait_did, name, rhs) + // general bound predicates. + equalities.retain(|&(ref lhs, ref rhs, ref bound_params)| { + let Some((ty, trait_did, name)) = lhs.projection() else { return true; }; + let Some((bounds, _)) = tybounds.get_mut(ty) else { return true }; + let bound_params = bound_params + .into_iter() + .map(|param| clean::GenericParamDef { + name: param.0, + kind: clean::GenericParamDefKind::Lifetime { outlives: Vec::new() }, + }) + .collect(); + merge_bounds(cx, bounds, bound_params, trait_did, name, rhs) }); // And finally, let's reassemble everything @@ -65,23 +63,23 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec) -> Vec { clauses.extend( lifetimes.into_iter().map(|(lt, bounds)| WP::RegionPredicate { lifetime: lt, bounds }), ); - clauses.extend(params.into_iter().map(|(k, (bounds, params))| WP::BoundPredicate { - ty: clean::Generic(k), - bounds, - bound_params: params, - })); clauses.extend(tybounds.into_iter().map(|(ty, (bounds, bound_params))| WP::BoundPredicate { ty, bounds, bound_params, })); - clauses.extend(equalities.into_iter().map(|(lhs, rhs)| WP::EqPredicate { lhs, rhs })); + clauses.extend(equalities.into_iter().map(|(lhs, rhs, bound_params)| WP::EqPredicate { + lhs, + rhs, + bound_params, + })); clauses } pub(crate) fn merge_bounds( cx: &clean::DocContext<'_>, bounds: &mut Vec, + mut bound_params: Vec, trait_did: DefId, assoc: clean::PathSegment, rhs: &clean::Term, @@ -98,6 +96,14 @@ pub(crate) fn merge_bounds( return false; } let last = trait_ref.trait_.segments.last_mut().expect("segments were empty"); + + trait_ref.generic_params.append(&mut bound_params); + // Since the parameters (probably) originate from `tcx.collect_*_late_bound_regions` which + // returns a hash set, sort them alphabetically to guarantee a stable and deterministic + // output (and to fully deduplicate them). + trait_ref.generic_params.sort_unstable_by(|p, q| p.name.as_str().cmp(q.name.as_str())); + trait_ref.generic_params.dedup_by_key(|p| p.name); + match last.args { PP::AngleBracketed { ref mut bindings, .. } => { bindings.push(clean::TypeBinding { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index f973fd088..cd1f972dc 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -21,6 +21,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_hir::{BodyId, Mutability}; +use rustc_hir_analysis::check::intrinsic::intrinsic_operation_unsafety; use rustc_index::vec::IndexVec; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt}; @@ -31,7 +32,6 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{self, FileName, Loc}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; -use rustc_typeck::check::intrinsic::intrinsic_operation_unsafety; use crate::clean::cfg::Cfg; use crate::clean::clean_visibility; @@ -119,7 +119,7 @@ pub(crate) struct Crate { pub(crate) module: Item, pub(crate) primitives: ThinVec<(DefId, PrimitiveType)>, /// Only here so that they can be filtered through the rustdoc passes. - pub(crate) external_traits: Rc>>, + pub(crate) external_traits: Rc>>, } impl Crate { @@ -132,13 +132,6 @@ impl Crate { } } -/// This struct is used to wrap additional information added by rustdoc on a `trait` item. -#[derive(Clone, Debug)] -pub(crate) struct TraitWithExtraInfo { - pub(crate) trait_: Trait, - pub(crate) is_notable: bool, -} - #[derive(Copy, Clone, Debug)] pub(crate) struct ExternalCrate { pub(crate) crate_num: CrateNum, @@ -247,13 +240,13 @@ impl ExternalCrate { let item = tcx.hir().item(id); match item.kind { hir::ItemKind::Mod(_) => { - as_keyword(Res::Def(DefKind::Mod, id.def_id.to_def_id())) + as_keyword(Res::Def(DefKind::Mod, id.owner_id.to_def_id())) } hir::ItemKind::Use(path, hir::UseKind::Single) - if tcx.visibility(id.def_id).is_public() => + if tcx.visibility(id.owner_id).is_public() => { as_keyword(path.res.expect_non_local()) - .map(|(_, prim)| (id.def_id.to_def_id(), prim)) + .map(|(_, prim)| (id.owner_id.to_def_id(), prim)) } _ => None, } @@ -315,14 +308,14 @@ impl ExternalCrate { let item = tcx.hir().item(id); match item.kind { hir::ItemKind::Mod(_) => { - as_primitive(Res::Def(DefKind::Mod, id.def_id.to_def_id())) + as_primitive(Res::Def(DefKind::Mod, id.owner_id.to_def_id())) } hir::ItemKind::Use(path, hir::UseKind::Single) - if tcx.visibility(id.def_id).is_public() => + if tcx.visibility(id.owner_id).is_public() => { as_primitive(path.res.expect_non_local()).map(|(_, prim)| { // Pretend the primitive is local. - (id.def_id.to_def_id(), prim) + (id.owner_id.to_def_id(), prim) }) } _ => None, @@ -689,7 +682,7 @@ impl Item { let abi = tcx.fn_sig(self.item_id.as_def_id().unwrap()).abi(); hir::FnHeader { unsafety: if abi == Abi::RustIntrinsic { - intrinsic_operation_unsafety(self.name.unwrap()) + intrinsic_operation_unsafety(tcx, self.item_id.as_def_id().unwrap()) } else { hir::Unsafety::Unsafe }, @@ -1357,7 +1350,7 @@ impl Lifetime { pub(crate) enum WherePredicate { BoundPredicate { ty: Type, bounds: Vec, bound_params: Vec }, RegionPredicate { lifetime: Lifetime, bounds: Vec }, - EqPredicate { lhs: Type, rhs: Term }, + EqPredicate { lhs: Box, rhs: Box, bound_params: Vec }, } impl WherePredicate { @@ -1368,6 +1361,15 @@ impl WherePredicate { _ => None, } } + + pub(crate) fn get_bound_params(&self) -> Option<&[Lifetime]> { + match self { + Self::BoundPredicate { bound_params, .. } | Self::EqPredicate { bound_params, .. } => { + Some(bound_params) + } + _ => None, + } + } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] @@ -1530,6 +1532,9 @@ impl Trait { pub(crate) fn is_auto(&self, tcx: TyCtxt<'_>) -> bool { tcx.trait_is_auto(self.def_id) } + pub(crate) fn is_notable_trait(&self, tcx: TyCtxt<'_>) -> bool { + tcx.is_doc_notable_trait(self.def_id) + } pub(crate) fn unsafety(&self, tcx: TyCtxt<'_>) -> hir::Unsafety { tcx.trait_def(self.def_id).unsafety } @@ -2201,8 +2206,11 @@ impl Path { /// Checks if this is a `T::Name` path for an associated type. pub(crate) fn is_assoc_ty(&self) -> bool { match self.res { - Res::SelfTy { .. } if self.segments.len() != 1 => true, - Res::Def(DefKind::TyParam, _) if self.segments.len() != 1 => true, + Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } | Res::Def(DefKind::TyParam, _) + if self.segments.len() != 1 => + { + true + } Res::Def(DefKind::AssocTy, _) => true, _ => false, } @@ -2532,15 +2540,15 @@ impl SubstParam { mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(Crate, 72); // frequently moved by-value static_assert_size!(DocFragment, 32); - #[cfg(not(bootstrap))] - static_assert_size!(GenericArg, 56); + static_assert_size!(GenericArg, 48); static_assert_size!(GenericArgs, 32); static_assert_size!(GenericParamDef, 56); static_assert_size!(Item, 56); - static_assert_size!(ItemKind, 96); + static_assert_size!(ItemKind, 88); static_assert_size!(PathSegment, 40); - static_assert_size!(Type, 56); + static_assert_size!(Type, 48); + // tidy-alphabetical-end } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 3eaedaf10..58767d3a4 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -7,7 +7,6 @@ use crate::clean::{ PathSegment, Primitive, PrimitiveType, Type, TypeBinding, Visibility, }; use crate::core::DocContext; -use crate::formats::item_type::ItemType; use crate::visit_lib::LibEmbargoVisitor; use rustc_ast as ast; @@ -234,8 +233,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String { match n.kind() { - ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted }) => { - assert_eq!(promoted, ()); + ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs: _ }) => { let s = if let Some(def) = def.as_local() { print_const_expr(cx.tcx, cx.tcx.hir().body_owned_by(def.did)) } else { @@ -305,9 +303,9 @@ fn format_integer_with_underscore_sep(num: &str) -> String { .collect() } -fn print_const_with_custom_print_scalar( - tcx: TyCtxt<'_>, - ct: mir::ConstantKind<'_>, +fn print_const_with_custom_print_scalar<'tcx>( + tcx: TyCtxt<'tcx>, + ct: mir::ConstantKind<'tcx>, underscores_and_type: bool, ) -> String { // Use a slightly different format for integer types which always shows the actual value. @@ -321,7 +319,7 @@ fn print_const_with_custom_print_scalar( } } (mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Int(i)) => { - let ty = tcx.lift(ct.ty()).unwrap(); + let ty = ct.ty(); let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size; let data = int.assert_bits(size); let sign_extended_data = size.sign_extend(data) as i128; @@ -455,7 +453,9 @@ pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type { match path.res { Res::PrimTy(p) => Primitive(PrimitiveType::from(p)), - Res::SelfTy { .. } if path.segments.len() == 1 => Generic(kw::SelfUpper), + Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } if path.segments.len() == 1 => { + Generic(kw::SelfUpper) + } Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => Generic(path.segments[0].name), _ => { let _ = register_res(cx, path.res); @@ -503,9 +503,6 @@ pub(crate) fn register_res(cx: &mut DocContext<'_>, res: Res) -> DefId { return did; } inline::record_extern_fqn(cx, did, kind); - if let ItemType::Trait = kind { - inline::record_extern_trait(cx, did); - } did } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 932533db0..67ea39fb9 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -142,8 +142,6 @@ pub(crate) struct Options { // Options that alter generated documentation pages /// Crate version to note on the sidebar of generated docs. pub(crate) crate_version: Option, - /// Collected options specific to outputting final pages. - pub(crate) render_options: RenderOptions, /// The format that we output when rendering. /// /// Currently used only for the `--show-coverage` option. @@ -159,6 +157,10 @@ pub(crate) struct Options { /// Configuration for scraping examples from the current crate. If this option is Some(..) then /// the compiler will scrape examples and not generate documentation. pub(crate) scrape_examples_options: Option, + + /// Note: this field is duplicated in `RenderOptions` because it's useful + /// to have it in both places. + pub(crate) unstable_features: rustc_feature::UnstableFeatures, } impl fmt::Debug for Options { @@ -194,7 +196,6 @@ impl fmt::Debug for Options { .field("persist_doctests", &self.persist_doctests) .field("show_coverage", &self.show_coverage) .field("crate_version", &self.crate_version) - .field("render_options", &self.render_options) .field("runtool", &self.runtool) .field("runtool_args", &self.runtool_args) .field("enable-per-target-ignores", &self.enable_per_target_ignores) @@ -202,6 +203,7 @@ impl fmt::Debug for Options { .field("no_run", &self.no_run) .field("nocapture", &self.nocapture) .field("scrape_examples_options", &self.scrape_examples_options) + .field("unstable_features", &self.unstable_features) .finish() } } @@ -267,6 +269,8 @@ pub(crate) struct RenderOptions { pub(crate) generate_redirect_map: bool, /// Show the memory layout of types in the docs. pub(crate) show_type_layout: bool, + /// Note: this field is duplicated in `Options` because it's useful to have + /// it in both places. pub(crate) unstable_features: rustc_feature::UnstableFeatures, pub(crate) emit: Vec, /// If `true`, HTML source pages will generate links for items to their definition. @@ -316,7 +320,7 @@ impl Options { pub(crate) fn from_matches( matches: &getopts::Matches, args: Vec, - ) -> Result { + ) -> Result<(Options, RenderOptions), i32> { let args = &args[1..]; // Check for unstable options. nightly_options::check_nightly_options(matches, &opts()); @@ -710,7 +714,9 @@ impl Options { let with_examples = matches.opt_strs("with-examples"); let call_locations = crate::scrape_examples::load_call_locations(with_examples, &diag)?; - Ok(Options { + let unstable_features = + rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref()); + let options = Options { input, proc_macro_crate, error_format, @@ -744,42 +750,42 @@ impl Options { run_check, no_run, nocapture, - render_options: RenderOptions { - output, - external_html, - id_map, - playground_url, - module_sorting, - themes, - extension_css, - extern_html_root_urls, - extern_html_root_takes_precedence, - default_settings, - resource_suffix, - enable_minification, - enable_index_page, - index_page, - static_root_path, - markdown_no_toc, - markdown_css, - markdown_playground_url, - document_private, - document_hidden, - generate_redirect_map, - show_type_layout, - unstable_features: rustc_feature::UnstableFeatures::from_environment( - crate_name.as_deref(), - ), - emit, - generate_link_to_definition, - call_locations, - no_emit_shared: false, - }, crate_name, output_format, json_unused_externs, scrape_examples_options, - }) + unstable_features, + }; + let render_options = RenderOptions { + output, + external_html, + id_map, + playground_url, + module_sorting, + themes, + extension_css, + extern_html_root_urls, + extern_html_root_takes_precedence, + default_settings, + resource_suffix, + enable_minification, + enable_index_page, + index_page, + static_root_path, + markdown_no_toc, + markdown_css, + markdown_playground_url, + document_private, + document_hidden, + generate_redirect_map, + show_type_layout, + unstable_features, + emit, + generate_link_to_definition, + call_locations, + no_emit_shared: false, + }; + Ok((options, render_options)) } /// Returns `true` if the file given as `self.input` is a Markdown file. diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index c48b25aea..3e5f42b7a 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -1,6 +1,7 @@ use rustc_ast::NodeId; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{self, Lrc}; +use rustc_data_structures::unord::UnordSet; use rustc_errors::emitter::{Emitter, EmitterWriter}; use rustc_errors::json::JsonEmitter; use rustc_feature::UnstableFeatures; @@ -10,12 +11,10 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{HirId, Path, TraitCandidate}; use rustc_interface::interface; use rustc_middle::hir::nested_filter; -use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; use rustc_resolve as resolve; use rustc_session::config::{self, CrateType, ErrorOutputType}; use rustc_session::lint; -use rustc_session::DiagnosticOutput; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::{source_map, Span, Symbol}; @@ -26,7 +25,7 @@ use std::rc::Rc; use std::sync::LazyLock; use crate::clean::inline::build_external_trait; -use crate::clean::{self, ItemId, TraitWithExtraInfo}; +use crate::clean::{self, ItemId}; use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions}; use crate::formats::cache::Cache; use crate::passes::collect_intra_doc_links::PreprocessedMarkdownLink; @@ -40,7 +39,6 @@ pub(crate) struct ResolverCaches { /// Traits in scope for a given module. /// See `collect_intra_doc_links::traits_implemented_by` for more details. pub(crate) traits_in_scope: DefIdMap>, - pub(crate) all_traits: Option>, pub(crate) all_trait_impls: Option>, pub(crate) all_macro_rules: FxHashMap>, } @@ -59,7 +57,7 @@ pub(crate) struct DocContext<'tcx> { /// Most of this logic is copied from rustc_lint::late. pub(crate) param_env: ParamEnv<'tcx>, /// Later on moved through `clean::Crate` into `cache` - pub(crate) external_traits: Rc>>, + pub(crate) external_traits: Rc>>, /// Used while populating `external_traits` to ensure we don't process the same trait twice at /// the same time. pub(crate) active_extern_traits: FxHashSet, @@ -136,12 +134,6 @@ impl<'tcx> DocContext<'tcx> { } } - pub(crate) fn with_all_traits(&mut self, f: impl FnOnce(&mut Self, &[DefId])) { - let all_traits = self.resolver_caches.all_traits.take(); - f(self, all_traits.as_ref().expect("`all_traits` are already borrowed")); - self.resolver_caches.all_traits = all_traits; - } - pub(crate) fn with_all_trait_impls(&mut self, f: impl FnOnce(&mut Self, &[DefId])) { let all_trait_impls = self.resolver_caches.all_trait_impls.take(); f(self, all_trait_impls.as_ref().expect("`all_trait_impls` are already borrowed")); @@ -287,19 +279,17 @@ pub(crate) fn create_config( output_file: None, output_dir: None, file_loader: None, - diagnostic_output: DiagnosticOutput::Default, lint_caps, parse_sess_created: None, register_lints: Some(Box::new(crate::lint::register_lints)), override_queries: Some(|_sess, providers, _external_providers| { // Most lints will require typechecking, so just don't run them. providers.lint_mod = |_, _| {}; - // Prevent `rustc_typeck::check_crate` from calling `typeck` on all bodies. + // Prevent `rustc_hir_analysis::check_crate` from calling `typeck` on all bodies. providers.typeck_item_bodies = |_, _| {}; // hack so that `used_trait_imports` won't try to call typeck providers.used_trait_imports = |_, _| { - static EMPTY_SET: LazyLock> = - LazyLock::new(FxHashSet::default); + static EMPTY_SET: LazyLock> = LazyLock::new(UnordSet::default); &EMPTY_SET }; // In case typeck does end up being called, don't ICE in case there were name resolution errors @@ -356,17 +346,9 @@ pub(crate) fn run_global_ctxt( }); rustc_passes::stability::check_unused_or_stable_features(tcx); - let auto_traits = resolver_caches - .all_traits - .as_ref() - .expect("`all_traits` are already borrowed") - .iter() - .copied() - .filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id)) - .collect(); - let access_levels = AccessLevels { - map: tcx.privacy_access_levels(()).map.iter().map(|(k, v)| (k.to_def_id(), *v)).collect(), - }; + let auto_traits = + tcx.all_traits().filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id)).collect(); + let effective_visibilities = tcx.effective_visibilities(()).map_id(Into::into); let mut ctxt = DocContext { tcx, @@ -379,7 +361,7 @@ pub(crate) fn run_global_ctxt( impl_trait_bounds: Default::default(), generated_synthetics: Default::default(), auto_traits, - cache: Cache::new(access_levels, render_options.document_private), + cache: Cache::new(effective_visibilities, render_options.document_private), inlined: FxHashSet::default(), output_format, render_options, @@ -391,9 +373,7 @@ pub(crate) fn run_global_ctxt( // Note that in case of `#![no_core]`, the trait is not available. if let Some(sized_trait_did) = ctxt.tcx.lang_items().sized_trait() { let sized_trait = build_external_trait(&mut ctxt, sized_trait_did); - ctxt.external_traits - .borrow_mut() - .insert(sized_trait_did, TraitWithExtraInfo { trait_: sized_trait, is_notable: false }); + ctxt.external_traits.borrow_mut().insert(sized_trait_did, sized_trait); } debug!("crate: {:?}", tcx.hir().krate()); @@ -409,12 +389,8 @@ pub(crate) fn run_global_ctxt( tcx.struct_lint_node( crate::lint::MISSING_CRATE_LEVEL_DOCS, DocContext::as_local_hir_id(tcx, krate.module.item_id).unwrap(), - |lint| { - let mut diag = - lint.build("no documentation found for this crate's top-level module"); - diag.help(&help); - diag.emit(); - }, + "no documentation found for this crate's top-level module", + |lint| lint.help(help), ); } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 20ae102bc..db70029f6 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -14,11 +14,10 @@ use rustc_parse::maybe_new_parser_from_source_str; use rustc_parse::parser::attr::InnerAttrPolicy; use rustc_session::config::{self, CrateType, ErrorOutputType}; use rustc_session::parse::ParseSess; -use rustc_session::{lint, DiagnosticOutput, Session}; +use rustc_session::{lint, Session}; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; -use rustc_span::Symbol; use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; use rustc_target::spec::TargetTriple; use tempfile::Builder as TempFileBuilder; @@ -80,7 +79,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { lint_cap: Some(options.lint_cap.unwrap_or(lint::Forbid)), cg: options.codegen_options.clone(), externs: options.externs.clone(), - unstable_features: options.render_options.unstable_features, + unstable_features: options.unstable_features, actually_rustdoc: true, edition: options.edition, target_triple: options.target.clone(), @@ -100,7 +99,6 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { output_file: None, output_dir: None, file_loader: None, - diagnostic_output: DiagnosticOutput::Default, lint_caps, parse_sess_created: None, register_lints: Some(Box::new(crate::lint::register_lints)), @@ -125,7 +123,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { let opts = scrape_test_config(crate_attrs); let enable_per_target_ignores = options.enable_per_target_ignores; let mut collector = Collector::new( - tcx.crate_name(LOCAL_CRATE), + tcx.crate_name(LOCAL_CRATE).to_string(), options, false, opts, @@ -210,12 +208,13 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { pub(crate) fn run_tests( mut test_args: Vec, nocapture: bool, - tests: Vec, + mut tests: Vec, ) { test_args.insert(0, "rustdoctest".to_string()); if nocapture { test_args.push("--nocapture".to_string()); } + tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice())); test::test_main(&test_args, tests, None); } @@ -909,7 +908,7 @@ pub(crate) struct Collector { rustdoc_options: RustdocOptions, use_headers: bool, enable_per_target_ignores: bool, - crate_name: Symbol, + crate_name: String, opts: GlobalTestOptions, position: Span, source_map: Option>, @@ -921,7 +920,7 @@ pub(crate) struct Collector { impl Collector { pub(crate) fn new( - crate_name: Symbol, + crate_name: String, rustdoc_options: RustdocOptions, use_headers: bool, opts: GlobalTestOptions, @@ -984,7 +983,7 @@ impl Tester for Collector { fn add_test(&mut self, test: String, config: LangString, line: usize) { let filename = self.get_filename(); let name = self.generate_name(line, &filename); - let crate_name = self.crate_name.to_string(); + let crate_name = self.crate_name.clone(); let opts = self.opts.clone(); let edition = config.edition.unwrap_or(self.rustdoc_options.edition); let rustdoc_options = self.rustdoc_options.clone(); @@ -1134,6 +1133,7 @@ impl Tester for Collector { panic::resume_unwind(Box::new(())); } + Ok(()) })), }); } diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index ed702f5c4..c6f1f9de5 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -94,7 +94,7 @@ pub(crate) trait DocFolder: Sized { let external_traits = { std::mem::take(&mut *c.external_traits.borrow_mut()) }; for (k, mut v) in external_traits { - v.trait_.items = v.trait_.items.into_iter().filter_map(|i| self.fold_item(i)).collect(); + v.items = v.items.into_iter().filter_map(|i| self.fold_item(i)).collect(); c.external_traits.borrow_mut().insert(k, v); } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 86392610d..afe2264e8 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -2,9 +2,9 @@ use std::mem; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_middle::middle::privacy::AccessLevels; +use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::{sym, Symbol}; +use rustc_span::Symbol; use crate::clean::{self, types::ExternalLocation, ExternalCrate, ItemId, PrimitiveType}; use crate::core::DocContext; @@ -62,7 +62,7 @@ pub(crate) struct Cache { /// Implementations of a crate should inherit the documentation of the /// parent trait if no extra documentation is specified, and default methods /// should show up in documentation about trait implementations. - pub(crate) traits: FxHashMap, + pub(crate) traits: FxHashMap, /// When rendering traits, it's often useful to be able to list all /// implementors of the trait, and this mapping is exactly, that: a mapping @@ -77,8 +77,8 @@ pub(crate) struct Cache { // Note that external items for which `doc(hidden)` applies to are shown as // non-reachable while local items aren't. This is because we're reusing - // the access levels from the privacy check pass. - pub(crate) access_levels: AccessLevels, + // the effective visibilities from the privacy check pass. + pub(crate) effective_visibilities: EffectiveVisibilities, /// The version of the crate being documented, if given from the `--crate-version` flag. pub(crate) crate_version: Option, @@ -132,8 +132,11 @@ struct CacheBuilder<'a, 'tcx> { } impl Cache { - pub(crate) fn new(access_levels: AccessLevels, document_private: bool) -> Self { - Cache { access_levels, document_private, ..Cache::default() } + pub(crate) fn new( + effective_visibilities: EffectiveVisibilities, + document_private: bool, + ) -> Self { + Cache { effective_visibilities, document_private, ..Cache::default() } } /// Populates the `Cache` with more data. The returned `Crate` will be missing some data that was @@ -225,12 +228,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { // Propagate a trait method's documentation to all implementors of the // trait. if let clean::TraitItem(ref t) = *item.kind { - self.cache.traits.entry(item.item_id.expect_def_id()).or_insert_with(|| { - clean::TraitWithExtraInfo { - trait_: *t.clone(), - is_notable: item.attrs.has_doc_flag(sym::notable_trait), - } - }); + self.cache.traits.entry(item.item_id.expect_def_id()).or_insert_with(|| (**t).clone()); } // Collect all the implementors of traits. @@ -386,7 +384,10 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { // paths map if there was already an entry present and we're // not a public item. if !self.cache.paths.contains_key(&item.item_id.expect_def_id()) - || self.cache.access_levels.is_public(item.item_id.expect_def_id()) + || self + .cache + .effective_visibilities + .is_directly_public(item.item_id.expect_def_id()) { self.cache.paths.insert( item.item_id.expect_def_id(), diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs index 62ba984ac..6f9cc0266 100644 --- a/src/librustdoc/formats/renderer.rs +++ b/src/librustdoc/formats/renderer.rs @@ -58,7 +58,7 @@ pub(crate) fn run_format<'tcx, T: FormatRenderer<'tcx>>( let emit_crate = options.should_emit_crate(); let (mut format_renderer, krate) = prof - .extra_verbose_generic_activity("create_renderer", T::descr()) + .verbose_generic_activity_with_arg("create_renderer", T::descr()) .run(|| T::init(krate, options, cache, tcx))?; if !emit_crate { @@ -92,6 +92,6 @@ pub(crate) fn run_format<'tcx, T: FormatRenderer<'tcx>>( .run(|| cx.item(item))?; } } - prof.extra_verbose_generic_activity("renderer_after_krate", T::descr()) + prof.verbose_generic_activity_with_arg("renderer_after_krate", T::descr()) .run(|| format_renderer.after_krate()) } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index b499e186c..92e7f2739 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -331,7 +331,8 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>( bounds_display.truncate(bounds_display.len() - " + ".len()); write!(f, "{}: {bounds_display}", lifetime.print()) } - clean::WherePredicate::EqPredicate { lhs, rhs } => { + // FIXME(fmease): Render bound params. + clean::WherePredicate::EqPredicate { lhs, rhs, bound_params: _ } => { if f.alternate() { write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx)) } else { @@ -611,7 +612,7 @@ fn generate_macro_def_id_path( }; if path.len() < 2 { // The minimum we can have is the crate name followed by the macro name. If shorter, then - // it means that that `relative` was empty, which is an error. + // it means that `relative` was empty, which is an error. debug!("macro path cannot be empty!"); return Err(HrefError::NotInExternalCache); } @@ -658,7 +659,7 @@ pub(crate) fn href_with_root_path( } if !did.is_local() - && !cache.access_levels.is_public(did) + && !cache.effective_visibilities.is_directly_public(did) && !cache.document_private && !cache.primitive_locations.values().any(|&id| id == did) { @@ -1010,15 +1011,25 @@ fn fmt_type<'cx>( write!(f, "]") } }, - clean::Array(ref t, ref n) => { - primitive_link(f, PrimitiveType::Array, "[", cx)?; - fmt::Display::fmt(&t.print(cx), f)?; - if f.alternate() { - primitive_link(f, PrimitiveType::Array, &format!("; {}]", n), cx) - } else { - primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), cx) + clean::Array(ref t, ref n) => match **t { + clean::Generic(name) if !f.alternate() => primitive_link( + f, + PrimitiveType::Array, + &format!("[{name}; {n}]", n = Escape(n)), + cx, + ), + _ => { + write!(f, "[")?; + fmt::Display::fmt(&t.print(cx), f)?; + if f.alternate() { + write!(f, "; {n}")?; + } else { + write!(f, "; ")?; + primitive_link(f, PrimitiveType::Array, &format!("{n}", n = Escape(n)), cx)?; + } + write!(f, "]") } - } + }, clean::RawPointer(m, ref t) => { let m = match m { hir::Mutability::Mut => "mut", diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 8922bf377..5e28204b2 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -7,19 +7,18 @@ use crate::clean::PrimitiveType; use crate::html::escape::Escape; -use crate::html::render::Context; +use crate::html::render::{Context, LinkFromSrc}; use std::collections::VecDeque; use std::fmt::{Display, Write}; use rustc_data_structures::fx::FxHashMap; -use rustc_lexer::{LiteralKind, TokenKind}; +use rustc_lexer::{Cursor, LiteralKind, TokenKind}; use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; use rustc_span::{BytePos, Span, DUMMY_SP}; use super::format::{self, Buffer}; -use super::render::LinkFromSrc; /// 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> { @@ -408,15 +407,16 @@ enum Highlight<'a> { struct TokenIter<'a> { src: &'a str, + cursor: Cursor<'a>, } impl<'a> Iterator for TokenIter<'a> { type Item = (TokenKind, &'a str); fn next(&mut self) -> Option<(TokenKind, &'a str)> { - if self.src.is_empty() { + let token = self.cursor.advance_token(); + if token.kind == TokenKind::Eof { return None; } - let token = rustc_lexer::first_token(self.src); let (text, rest) = self.src.split_at(token.len as usize); self.src = rest; Some((token.kind, text)) @@ -525,7 +525,7 @@ impl<'a> Classifier<'a> { /// 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) -> Classifier<'_> { - let tokens = PeekIter::new(TokenIter { src }); + let tokens = PeekIter::new(TokenIter { src, cursor: Cursor::new(src) }); let decorations = decoration_info.map(Decorations::new); Classifier { tokens, @@ -850,6 +850,7 @@ impl<'a> Classifier<'a> { Class::Ident(self.new_span(before, text)) } TokenKind::Lifetime { .. } => Class::Lifetime, + TokenKind::Eof => panic!("Eof in advance"), }; // Anything that didn't return above is the simple case where we the // class just spans a single token, so we can use the `string` method. diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 43d07d4a5..1e1c657b0 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -111,14 +111,9 @@ pub(crate) struct MarkdownWithToc<'a>( pub(crate) Edition, pub(crate) &'a Option, ); -/// A tuple struct like `Markdown` that renders the markdown escaping HTML tags. -pub(crate) struct MarkdownHtml<'a>( - pub(crate) &'a str, - pub(crate) &'a mut IdMap, - pub(crate) ErrorCodes, - pub(crate) Edition, - pub(crate) &'a Option, -); +/// A tuple struct like `Markdown` that renders the markdown escaping HTML tags +/// and includes no paragraph tags. +pub(crate) struct MarkdownItemInfo<'a>(pub(crate) &'a str, pub(crate) &'a mut IdMap); /// A tuple struct like `Markdown` that renders only the first paragraph. pub(crate) struct MarkdownSummaryLine<'a>(pub &'a str, pub &'a [RenderedLink]); @@ -251,8 +246,6 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { _ => {} } } - let lines = origtext.lines().filter_map(|l| map_line(l).for_html()); - let text = lines.intersperse("\n".into()).collect::(); let parse_result = match kind { CodeBlockKind::Fenced(ref lang) => { @@ -265,7 +258,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> {
{}
\ ", lang, - Escape(&text), + Escape(&origtext), ) .into(), )); @@ -275,6 +268,9 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { CodeBlockKind::Indented => Default::default(), }; + let lines = origtext.lines().filter_map(|l| map_line(l).for_html()); + let text = lines.intersperse("\n".into()).collect::(); + compile_fail = parse_result.compile_fail; should_panic = parse_result.should_panic; ignore = parse_result.ignore; @@ -818,11 +814,8 @@ impl<'tcx> ExtraInfo<'tcx> { crate::lint::INVALID_CODEBLOCK_ATTRIBUTES, hir_id, self.sp, - |lint| { - let mut diag = lint.build(msg); - diag.help(help); - diag.emit(); - }, + msg, + |lint| lint.help(help), ); } } @@ -1072,9 +1065,9 @@ impl MarkdownWithToc<'_> { } } -impl MarkdownHtml<'_> { +impl MarkdownItemInfo<'_> { pub(crate) fn into_string(self) -> String { - let MarkdownHtml(md, ids, codes, edition, playground) = self; + let MarkdownItemInfo(md, ids) = self; // This is actually common enough to special-case if md.is_empty() { @@ -1093,7 +1086,9 @@ impl MarkdownHtml<'_> { let p = HeadingLinks::new(p, None, ids, HeadingOffset::H1); let p = Footnotes::new(p); let p = TableWrapper::new(p.map(|(ev, _)| ev)); - let p = CodeBlocks::new(p, codes, edition, playground); + let p = p.filter(|event| { + !matches!(event, Event::Start(Tag::Paragraph) | Event::End(Tag::Paragraph)) + }); html::push_html(&mut s, p); s @@ -1439,6 +1434,7 @@ static DEFAULT_ID_MAP: Lazy, usize>> = Lazy::new(|| fn init_id_map() -> FxHashMap, usize> { let mut map = FxHashMap::default(); // This is the list of IDs used in Javascript. + map.insert("help".into(), 1); map.insert("settings".into(), 1); map.insert("not-displayed".into(), 1); map.insert("alternative-display".into(), 1); diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index 5c0bf0ed9..68b31a6ee 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -1,5 +1,5 @@ use super::{find_testable_code, plain_text_summary, short_markdown_summary}; -use super::{ErrorCodes, HeadingOffset, IdMap, Ignore, LangString, Markdown, MarkdownHtml}; +use super::{ErrorCodes, HeadingOffset, IdMap, Ignore, LangString, Markdown, MarkdownItemInfo}; use rustc_span::edition::{Edition, DEFAULT_EDITION}; #[test] @@ -279,14 +279,13 @@ fn test_plain_text_summary() { fn test_markdown_html_escape() { fn t(input: &str, expect: &str) { let mut idmap = IdMap::new(); - let output = - MarkdownHtml(input, &mut idmap, ErrorCodes::Yes, DEFAULT_EDITION, &None).into_string(); + let output = MarkdownItemInfo(input, &mut idmap).into_string(); assert_eq!(output, expect, "original: {}", input); } - t("`Struct<'a, T>`", "

Struct<'a, T>

\n"); - t("Struct<'a, T>", "

Struct<’a, T>

\n"); - t("Struct
", "

Struct<br>

\n"); + t("`Struct<'a, T>`", "Struct<'a, T>"); + t("Struct<'a, T>", "Struct<’a, T>"); + t("Struct
", "Struct<br>"); } #[test] @@ -310,3 +309,40 @@ fn test_find_testable_code_line() { t("```rust\n```\n```rust\n```", &[1, 3]); t("```rust\n```\n ```rust\n```", &[1, 3]); } + +#[test] +fn test_ascii_with_prepending_hashtag() { + fn t(input: &str, expect: &str) { + let mut map = IdMap::new(); + let output = Markdown { + content: input, + links: &[], + ids: &mut map, + error_codes: ErrorCodes::Yes, + edition: DEFAULT_EDITION, + playground: &None, + heading_offset: HeadingOffset::H2, + } + .into_string(); + assert_eq!(output, expect, "original: {}", input); + } + + t( + r#"```ascii +#..#.####.#....#.....##.. +#..#.#....#....#....#..#. +####.###..#....#....#..#. +#..#.#....#....#....#..#. +#..#.#....#....#....#..#. +#..#.####.####.####..##.. +```"#, + "
\
+#..#.####.#....#.....##..
+#..#.#....#....#....#..#.
+####.###..#....#....#..#.
+#..#.#....#....#....#..#.
+#..#.#....#....#....#..#.
+#..#.####.####.####..##..
+
", + ); +} diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 62def4a94..5733d1f9c 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -17,8 +17,8 @@ use super::print_item::{full_path, item_path, print_item}; use super::search_index::build_index; use super::write_shared::write_shared; use super::{ - collect_spans_and_sources, print_sidebar, scrape_examples_help, AllTypes, LinkFromSrc, NameDoc, - StylePath, BASIC_KEYWORDS, + collect_spans_and_sources, print_sidebar, scrape_examples_help, sidebar_module_like, AllTypes, + LinkFromSrc, NameDoc, StylePath, BASIC_KEYWORDS, }; use crate::clean::{self, types::ExternalLocation, ExternalCrate}; @@ -430,7 +430,6 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { extension_css, resource_suffix, static_root_path, - unstable_features, generate_redirect_map, show_type_layout, generate_link_to_definition, @@ -511,7 +510,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { resource_suffix, static_root_path, fs: DocFS::new(sender), - codes: ErrorCodes::from(unstable_features.is_nightly_build()), + codes: ErrorCodes::from(options.unstable_features.is_nightly_build()), playground, all: RefCell::new(AllTypes::new()), errors: receiver, @@ -581,6 +580,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { let crate_name = self.tcx().crate_name(LOCAL_CRATE); let final_file = self.dst.join(crate_name.as_str()).join("all.html"); let settings_file = self.dst.join("settings.html"); + let help_file = self.dst.join("help.html"); let scrape_examples_help_file = self.dst.join("scrape-examples-help.html"); let mut root_path = self.dst.to_str().expect("invalid path").to_owned(); @@ -597,16 +597,24 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { keywords: BASIC_KEYWORDS, resource_suffix: &shared.resource_suffix, }; - let sidebar = if shared.cache.crate_version.is_some() { - format!("

Crate {}

", crate_name) - } else { - String::new() - }; let all = shared.all.replace(AllTypes::new()); + let mut sidebar = Buffer::html(); + if shared.cache.crate_version.is_some() { + write!(sidebar, "

Crate {}

", crate_name) + }; + + let mut items = Buffer::html(); + sidebar_module_like(&mut items, all.item_sections()); + if !items.is_empty() { + sidebar.push_str("
"); + sidebar.push_buffer(items); + sidebar.push_str("
"); + } + let v = layout::render( &shared.layout, &page, - sidebar, + sidebar.into_inner(), |buf: &mut Buffer| all.print(buf), &shared.style_files, ); @@ -626,9 +634,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { write!( buf, "
\ -

\ - Rustdoc settings\ -

\ +

Rustdoc settings

\ \
\ Back\ @@ -651,6 +657,39 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { ); shared.fs.write(settings_file, v)?; + // Generating help page. + page.title = "Rustdoc help"; + page.description = "Documentation for Rustdoc"; + page.root_path = "./"; + + let sidebar = "

Help

"; + let v = layout::render( + &shared.layout, + &page, + sidebar, + |buf: &mut Buffer| { + write!( + buf, + "
\ + ", + ) + }, + &shared.style_files, + ); + shared.fs.write(help_file, v)?; + if shared.layout.scrape_examples_extension { page.title = "About scraped examples"; page.description = "How the scraped examples feature works in Rustdoc"; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 1e6f20d2b..96c57c8c8 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -74,7 +74,9 @@ use crate::html::format::{ PrintWithSpace, }; use crate::html::highlight; -use crate::html::markdown::{HeadingOffset, IdMap, Markdown, MarkdownHtml, MarkdownSummaryLine}; +use crate::html::markdown::{ + HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine, +}; use crate::html::sources; use crate::html::static_files::SCRAPE_EXAMPLES_HELP_MD; use crate::scrape_examples::{CallData, CallLocation}; @@ -239,8 +241,8 @@ struct AllTypes { opaque_tys: FxHashSet, statics: FxHashSet, constants: FxHashSet, - attributes: FxHashSet, - derives: FxHashSet, + attribute_macros: FxHashSet, + derive_macros: FxHashSet, trait_aliases: FxHashSet, } @@ -259,8 +261,8 @@ impl AllTypes { opaque_tys: new_set(100), statics: new_set(100), constants: new_set(100), - attributes: new_set(100), - derives: new_set(100), + attribute_macros: new_set(100), + derive_macros: new_set(100), trait_aliases: new_set(100), } } @@ -283,27 +285,75 @@ impl AllTypes { ItemType::OpaqueTy => self.opaque_tys.insert(ItemEntry::new(new_url, name)), ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)), ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)), - ItemType::ProcAttribute => self.attributes.insert(ItemEntry::new(new_url, name)), - ItemType::ProcDerive => self.derives.insert(ItemEntry::new(new_url, name)), + ItemType::ProcAttribute => { + self.attribute_macros.insert(ItemEntry::new(new_url, name)) + } + ItemType::ProcDerive => self.derive_macros.insert(ItemEntry::new(new_url, name)), ItemType::TraitAlias => self.trait_aliases.insert(ItemEntry::new(new_url, name)), _ => true, }; } } -} -impl AllTypes { + fn item_sections(&self) -> FxHashSet { + let mut sections = FxHashSet::default(); + + if !self.structs.is_empty() { + sections.insert(ItemSection::Structs); + } + if !self.enums.is_empty() { + sections.insert(ItemSection::Enums); + } + if !self.unions.is_empty() { + sections.insert(ItemSection::Unions); + } + if !self.primitives.is_empty() { + sections.insert(ItemSection::PrimitiveTypes); + } + if !self.traits.is_empty() { + sections.insert(ItemSection::Traits); + } + if !self.macros.is_empty() { + sections.insert(ItemSection::Macros); + } + if !self.functions.is_empty() { + sections.insert(ItemSection::Functions); + } + if !self.typedefs.is_empty() { + sections.insert(ItemSection::TypeDefinitions); + } + if !self.opaque_tys.is_empty() { + sections.insert(ItemSection::OpaqueTypes); + } + if !self.statics.is_empty() { + sections.insert(ItemSection::Statics); + } + if !self.constants.is_empty() { + sections.insert(ItemSection::Constants); + } + if !self.attribute_macros.is_empty() { + sections.insert(ItemSection::AttributeMacros); + } + if !self.derive_macros.is_empty() { + sections.insert(ItemSection::DeriveMacros); + } + if !self.trait_aliases.is_empty() { + sections.insert(ItemSection::TraitAliases); + } + + sections + } + fn print(self, f: &mut Buffer) { - fn print_entries(f: &mut Buffer, e: &FxHashSet, title: &str, class: &str) { + fn print_entries(f: &mut Buffer, e: &FxHashSet, kind: ItemSection) { if !e.is_empty() { let mut e: Vec<&ItemEntry> = e.iter().collect(); e.sort(); write!( f, - "

{}

    ", - title.replace(' ', "-"), // IDs cannot contain whitespaces. - title, - class + "

    {title}

      ", + id = kind.id(), + title = kind.name(), ); for s in e.iter() { @@ -314,27 +364,23 @@ impl AllTypes { } } - f.write_str( - "

      \ - List of all items\ -

      ", - ); + f.write_str("

      List of all items

      "); // 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, "Structs", "structs"); - print_entries(f, &self.enums, "Enums", "enums"); - print_entries(f, &self.unions, "Unions", "unions"); - print_entries(f, &self.primitives, "Primitives", "primitives"); - print_entries(f, &self.traits, "Traits", "traits"); - print_entries(f, &self.macros, "Macros", "macros"); - print_entries(f, &self.attributes, "Attribute Macros", "attributes"); - print_entries(f, &self.derives, "Derive Macros", "derives"); - print_entries(f, &self.functions, "Functions", "functions"); - print_entries(f, &self.typedefs, "Typedefs", "typedefs"); - print_entries(f, &self.trait_aliases, "Trait Aliases", "trait-aliases"); - print_entries(f, &self.opaque_tys, "Opaque Types", "opaque-types"); - print_entries(f, &self.statics, "Statics", "statics"); - print_entries(f, &self.constants, "Constants", "constants") + print_entries(f, &self.structs, ItemSection::Structs); + print_entries(f, &self.enums, ItemSection::Enums); + print_entries(f, &self.unions, ItemSection::Unions); + print_entries(f, &self.primitives, ItemSection::PrimitiveTypes); + print_entries(f, &self.traits, ItemSection::Traits); + print_entries(f, &self.macros, ItemSection::Macros); + print_entries(f, &self.attribute_macros, ItemSection::AttributeMacros); + print_entries(f, &self.derive_macros, ItemSection::DeriveMacros); + print_entries(f, &self.functions, ItemSection::Functions); + print_entries(f, &self.typedefs, ItemSection::TypeDefinitions); + print_entries(f, &self.trait_aliases, ItemSection::TraitAliases); + print_entries(f, &self.opaque_tys, ItemSection::OpaqueTypes); + print_entries(f, &self.statics, ItemSection::Statics); + print_entries(f, &self.constants, ItemSection::Constants); } } @@ -348,9 +394,7 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String { let mut ids = IdMap::default(); format!( "
      \ -

      \ - About scraped examples\ -

      \ +

      About scraped examples

      \
      \
      {}
      ", Markdown { @@ -536,7 +580,6 @@ fn short_item_info( parent: Option<&clean::Item>, ) -> Vec { let mut extra_info = vec![]; - let error_codes = cx.shared.codes; if let Some(depr @ Deprecation { note, since, is_since_rustc_version: _, suggestion: _ }) = item.deprecation(cx.tcx()) @@ -560,13 +603,7 @@ fn short_item_info( if let Some(note) = note { let note = note.as_str(); - let html = MarkdownHtml( - note, - &mut cx.id_map, - error_codes, - cx.shared.edition(), - &cx.shared.playground, - ); + let html = MarkdownItemInfo(note, &mut cx.id_map); message.push_str(&format!(": {}", html.into_string())); } extra_info.push(format!( @@ -1239,6 +1276,15 @@ fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String { if let Some((did, ty)) = decl.output.as_return().and_then(|t| Some((t.def_id(cx.cache())?, t))) { + // Box has pass-through impls for Read, Write, Iterator, and Future when the + // boxed type implements one of those. We don't want to treat every Box return + // as being notably an Iterator (etc), though, so we exempt it. Pin has the same + // issue, with a pass-through impl for Future. + if Some(did) == cx.tcx().lang_items().owned_box() + || Some(did) == cx.tcx().lang_items().pin_type() + { + return "".to_string(); + } if let Some(impls) = cx.cache().impls.get(&did) { for i in impls { let impl_ = i.inner_impl(); @@ -1251,7 +1297,12 @@ fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String { if let Some(trait_) = &impl_.trait_ { let trait_did = trait_.def_id(); - if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable) { + if cx + .cache() + .traits + .get(&trait_did) + .map_or(false, |t| t.is_notable_trait(cx.tcx())) + { if out.is_empty() { write!( &mut out, @@ -1555,7 +1606,7 @@ fn render_impl( link, render_mode, false, - trait_.map(|t| &t.trait_), + trait_, rendering_params, ); } @@ -1615,7 +1666,7 @@ fn render_impl( &mut default_impl_items, &mut impl_items, cx, - &t.trait_, + t, i.inner_impl(), &i.impl_item, parent, @@ -1747,7 +1798,7 @@ pub(crate) fn render_impl_summary( write!(w, "
      ", id, aliases); render_rightside(w, cx, &i.impl_item, containing_item, RenderMode::Normal); write!(w, "", id); - write!(w, "

      "); + write!(w, "

      "); if let Some(use_absolute) = use_absolute { write!(w, "{}", inner_impl.print(use_absolute, cx)); @@ -1811,12 +1862,12 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { buffer.write_str("
      "); if it.is_crate() { - write!(buffer, "
        "); + write!(buffer, "
          "); if let Some(ref version) = cx.cache().crate_version { write!(buffer, "
        • Version {}
        • ", Escape(version)); } write!(buffer, "
        • All Items
        • "); - buffer.write_str("
      "); + buffer.write_str("

    "); } match *it.kind { @@ -1842,7 +1893,7 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { if !it.is_mod() { let path: String = cx.current.iter().map(|s| s.as_str()).intersperse("::").collect(); - write!(buffer, "

    In {}

    ", path); + write!(buffer, "

    In {}

    ", path); } // Closes sidebar-elems div. @@ -2216,21 +2267,8 @@ fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String } } -/// Don't call this function directly!!! Use `print_sidebar_title` or `print_sidebar_block` instead! -fn print_sidebar_title_inner(buf: &mut Buffer, id: &str, title: &str) { - write!( - buf, - "

    \ - {}\ -

    ", - id, title - ); -} - fn print_sidebar_title(buf: &mut Buffer, id: &str, title: &str) { - buf.push_str("
    "); - print_sidebar_title_inner(buf, id, title); - buf.push_str("
    "); + write!(buf, "

    {}

    ", id, title); } fn print_sidebar_block( @@ -2239,13 +2277,12 @@ fn print_sidebar_block( title: &str, items: impl Iterator, ) { - buf.push_str("
    "); - print_sidebar_title_inner(buf, id, title); - buf.push_str("
      "); + print_sidebar_title(buf, id, title); + buf.push_str("
        "); for item in items { write!(buf, "
      • {}
      • ", item); } - buf.push_str("
    "); + buf.push_str("
"); } fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { @@ -2469,7 +2506,7 @@ fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean: } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -enum ItemSection { +pub(crate) enum ItemSection { Reexports, PrimitiveTypes, Modules, @@ -2621,11 +2658,27 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection { } } -fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { +pub(crate) fn sidebar_module_like(buf: &mut Buffer, item_sections_in_use: FxHashSet) { use std::fmt::Write as _; let mut sidebar = String::new(); + for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) { + let _ = write!(sidebar, "
  • {}
  • ", sec.id(), sec.name()); + } + + if !sidebar.is_empty() { + write!( + buf, + "
    \ +
      {}
    \ +
    ", + sidebar + ); + } +} + +fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { let item_sections_in_use: FxHashSet<_> = items .iter() .filter(|it| { @@ -2640,21 +2693,8 @@ fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { }) .map(|it| item_ty_to_section(it.type_())) .collect(); - for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) { - let _ = write!(sidebar, "
  • {}
  • ", sec.id(), sec.name()); - } - if !sidebar.is_empty() { - write!( - buf, - "
    \ -
    \ -
      {}
    \ -
    \ -
    ", - sidebar - ); - } + sidebar_module_like(buf, item_sections_in_use); } fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index cfa450942..632781736 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -400,7 +400,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: if myitem.fn_header(cx.tcx()).unwrap().unsafety == hir::Unsafety::Unsafe => { - "" + "" } _ => "", }; @@ -514,7 +514,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle + name.as_str().len() + generics_len; - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "fn", |w| { render_attributes_in_pre(w, it, ""); w.reserve(header_len); @@ -553,7 +553,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: cx.tcx().trait_def(t.def_id).must_implement_one_of.clone(); // Output the trait definition - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "trait", |w| { render_attributes_in_pre(w, it, ""); write!( @@ -716,9 +716,9 @@ 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, "
    "); + write!(w, "
    "); } - write!(w, "
    ", id); + write!(w, "
    ", id); render_rightside(w, cx, m, t, RenderMode::Normal); write!(w, "

    "); render_assoc_item( @@ -730,7 +730,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: RenderMode::Normal, ); w.write_str("

    "); - w.write_str("
    "); + w.write_str(""); if toggled { write!(w, "
    "); w.push_buffer(content); @@ -890,7 +890,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: w, "implementors", "Implementors", - "
    ", + "
    ", ); for implementor in concrete { render_implementor(cx, implementor, it, w, &implementor_dups, &[]); @@ -902,7 +902,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: w, "synthetic-implementors", "Auto implementors", - "
    ", + "
    ", ); for implementor in synthetic { render_implementor( @@ -923,7 +923,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: w, "implementors", "Implementors", - "
    ", + "
    ", ); if t.is_auto(cx.tcx()) { @@ -931,7 +931,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: w, "synthetic-implementors", "Auto implementors", - "
    ", + "
    ", ); } } @@ -1033,7 +1033,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_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "trait-alias", |w| { render_attributes_in_pre(w, it, ""); write!( @@ -1057,7 +1057,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_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "opaque", |w| { render_attributes_in_pre(w, it, ""); write!( @@ -1096,7 +1096,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea }); } - wrap_into_docblock(w, |w| write_content(w, cx, it, t)); + wrap_into_item_decl(w, |w| write_content(w, cx, it, t)); document(w, cx, it, None, HeadingOffset::H2); @@ -1110,7 +1110,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_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "union", |w| { render_attributes_in_pre(w, it, ""); render_union(w, it, Some(&s.generics), &s.fields, "", cx); @@ -1174,7 +1174,7 @@ fn print_tuple_struct_fields(w: &mut Buffer, cx: &Context<'_>, s: &[clean::Item] fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) { let count_variants = e.variants().count(); - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "enum", |w| { render_attributes_in_pre(w, it, ""); write!( @@ -1333,14 +1333,14 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: } fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) { - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { highlight::render_macro_with_highlighting(&t.source, w); }); document(w, cx, it, None, HeadingOffset::H2) } fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &clean::ProcMacro) { - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { let name = it.name.expect("proc-macros always have names"); match m.kind { MacroKind::Bang => { @@ -1387,7 +1387,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_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "const", |w| { render_attributes_in_code(w, it); @@ -1436,7 +1436,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_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "struct", |w| { render_attributes_in_code(w, it); render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx); @@ -1489,7 +1489,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_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "static", |w| { render_attributes_in_code(w, it); write!( @@ -1506,7 +1506,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_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "foreigntype", |w| { w.write_str("extern {\n"); render_attributes_in_code(w, it); @@ -1595,11 +1595,11 @@ fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) bounds } -fn wrap_into_docblock(w: &mut Buffer, f: F) +fn wrap_into_item_decl(w: &mut Buffer, f: F) where F: FnOnce(&mut Buffer), { - w.write_str("
    "); + w.write_str("
    "); f(w); w.write_str("
    ") } diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index fc4d46fe6..85f63c985 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -517,14 +517,12 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; }; let content = format!( - "

    \ - List of all crates\ -

      {}
    ", + "

    List of all crates

      {}
    ", krates .iter() .map(|s| { format!( - "
  • {}
  • ", + "
  • {}
  • ", ensure_trailing_slash(s), s ) diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 2e2bee78b..7ab65bff3 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -274,7 +274,7 @@ pub(crate) fn print_src( ) { let lines = s.lines().count(); let mut line_numbers = Buffer::empty_from(buf); - line_numbers.write_str("
    ");
    +    line_numbers.write_str("
    ");
         match source_context {
             SourceContext::Standalone => {
                 for line in 1..=lines {
    diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css
    index 0a19a99ab..301f03a16 100644
    --- a/src/librustdoc/html/static/css/noscript.css
    +++ b/src/librustdoc/html/static/css/noscript.css
    @@ -14,7 +14,11 @@ rules.
     	display: none;
     }
     
    -.sub {
    +nav.sub {
     	/* The search bar and related controls don't work without JS */
     	display: none;
     }
    +
    +.source .sidebar {
    +	display: none;
    +}
    diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
    index bb35970eb..1cc954a98 100644
    --- a/src/librustdoc/html/static/css/rustdoc.css
    +++ b/src/librustdoc/html/static/css/rustdoc.css
    @@ -132,19 +132,29 @@ h1, h2, h3, h4, h5, h6 {
     	font-weight: 500;
     }
     h1, h2, h3, h4 {
    -	margin: 20px 0 15px 0;
    +	margin: 25px 0 15px 0;
     	padding-bottom: 6px;
     }
     .docblock h3, .docblock h4, h5, h6 {
     	margin: 15px 0 5px 0;
     }
    +.docblock > h2:first-child,
    +.docblock > h3:first-child,
    +.docblock > h4:first-child,
    +.docblock > h5:first-child,
    +.docblock > h6:first-child {
    +	margin-top: 0;
    +}
     h1.fqn {
     	margin: 0;
     	padding: 0;
    -	border-bottom-color: var(--headings-border-bottom-color);
    -}
    -h2, h3, h4 {
    -	border-bottom-color: var(--headings-border-bottom-color);
    +	flex-grow: 1;
    +	/* We use overflow-wrap: break-word for Safari, which doesn't recognize
    +	   `anywhere`: https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-wrap */
    +	overflow-wrap: break-word;
    +	/* Then override it with `anywhere`, which is required to make non-Safari browsers break
    +	   more aggressively when we want them to. */
    +	overflow-wrap: anywhere;
     }
     .main-heading {
     	display: flex;
    @@ -153,9 +163,6 @@ h2, h3, h4 {
     	padding-bottom: 6px;
     	margin-bottom: 15px;
     }
    -.main-heading a:hover {
    -	text-decoration: underline;
    -}
     #toggle-all-docs {
     	text-decoration: none;
     }
    @@ -164,7 +171,7 @@ h2, h3, h4 {
     	 Rustdoc-generated h2 section headings (e.g. "Implementations", "Required Methods", etc)
     	Underlines elsewhere in the documentation break up visual flow and tend to invert
     	section hierarchies. */
    -h2,
    +.content h2,
     .top-doc .docblock > h3,
     .top-doc .docblock > h4 {
     	border-bottom: 1px solid var(--headings-border-bottom-color);
    @@ -177,48 +184,28 @@ h4.code-header {
     }
     .code-header {
     	font-weight: 600;
    -	border-bottom-style: none;
     	margin: 0;
     	padding: 0;
    -	margin-top: 0.6em;
    -	margin-bottom: 0.4em;
    -}
    -.impl,
    -.impl-items .method,
    -.methods .method,
    -.impl-items .type,
    -.methods .type,
    -.impl-items .associatedconstant,
    -.methods .associatedconstant,
    -.impl-items .associatedtype,
    -.methods .associatedtype {
    -	flex-basis: 100%;
    -	font-weight: 600;
    -	position: relative;
     }
     
    +#crate-search,
     h1, h2, h3, h4, h5, h6,
     .sidebar,
     .mobile-topbar,
    -a.source,
     .search-input,
     .search-results .result-name,
     .item-left > a,
     .out-of-band,
     span.since,
    -details.rustdoc-toggle > summary::before,
    -.content ul.crate a.crate,
     a.srclink,
    -#help-button > button,
    +#help-button > a,
     details.rustdoc-toggle.top-doc > summary,
    -details.rustdoc-toggle.top-doc > summary::before,
     details.rustdoc-toggle.non-exhaustive > summary,
    -details.rustdoc-toggle.non-exhaustive > summary::before,
     .scraped-example-title,
     .more-examples-toggle summary, .more-examples-toggle .hide-more,
     .example-links a,
     /* This selector is for the items listed in the "all items" page. */
    -#main-content > ul.docblock > li > a {
    +ul.all-items {
     	font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif;
     }
     
    @@ -230,14 +217,14 @@ pre.rust a,
     .sidebar h2 a,
     .sidebar h3 a,
     .mobile-topbar h2 a,
    -.in-band a,
    +h1 a,
     .search-results a,
     .module-item .stab,
     .import-item .stab,
     .result-name .primitive > i, .result-name .keyword > i,
    -.content .method .where,
    -.content .fn .where,
    -.content .where.fmt-newline {
    +.method .where,
    +.fn .where,
    +.where.fmt-newline {
     	color: var(--main-color);
     }
     
    @@ -274,7 +261,7 @@ pre.rust a,
     	color: var(--macro-link-color);
     }
     
    -.content span.mod, .content a.mod, .block a.current.mod {
    +.content span.mod, .content a.mod {
     	color: var(--mod-link-color);
     }
     
    @@ -299,32 +286,14 @@ p {
     	   https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html */
     	margin: 0 0 .75em 0;
     }
    -
    -summary {
    -	outline: none;
    +/* For the last child of a div, the margin will be taken care of
    +	by the margin-top of the next item. */
    +p:last-child {
    +	margin: 0;
     }
     
     /* Fix some style changes due to normalize.css 8 */
     
    -td,
    -th {
    -	padding: 0;
    -}
    -
    -table {
    -	border-collapse: collapse;
    -}
    -
    -button,
    -input,
    -optgroup,
    -select,
    -textarea {
    -	color: inherit;
    -	font: inherit;
    -	margin: 0;
    -}
    -
     button {
     	/* Buttons on Safari have different default padding than other platforms. Make them the same. */
     	padding: 1px 6px;
    @@ -375,9 +344,6 @@ code, pre, a.test-arrow, .code-header {
     pre {
     	padding: 14px;
     }
    -.docblock.item-decl {
    -	margin-left: 0;
    -}
     .item-decl pre {
     	overflow-x: auto;
     }
    @@ -394,22 +360,11 @@ img {
     	overflow: visible;
     }
     
    -.sub-container {
    -	display: flex;
    -	flex-direction: row;
    -	flex-wrap: nowrap;
    -}
    -
     .sub-logo-container {
    -	display: none;
    -	margin-right: 20px;
    +	line-height: 0;
     }
     
    -.source .sub-logo-container {
    -	display: block;
    -}
    -
    -.source .sub-logo-container > img {
    +.sub-logo-container > img {
     	height: 60px;
     	width: 60px;
     	object-fit: contain;
    @@ -421,7 +376,7 @@ img {
     
     .sidebar {
     	font-size: 0.875rem;
    -	width: 250px;
    +	width: 200px;
     	min-width: 200px;
     	overflow-y: scroll;
     	position: sticky;
    @@ -430,15 +385,6 @@ img {
     	left: 0;
     }
     
    -.sidebar-elems,
    -.sidebar > .location {
    -	padding-left: 24px;
    -}
    -
    -.sidebar .location {
    -	overflow-wrap: anywhere;
    -}
    -
     .rustdoc.source .sidebar {
     	width: 50px;
     	min-width: 0px;
    @@ -452,10 +398,6 @@ img {
     	overflow-y: hidden;
     }
     
    -.rustdoc.source .sidebar .sidebar-logo {
    -	display: none;
    -}
    -
     .source .sidebar, #sidebar-toggle, #source-sidebar {
     	background-color: var(--sidebar-background-color);
     }
    @@ -465,16 +407,15 @@ img {
     }
     
     .source .sidebar > *:not(#sidebar-toggle) {
    -	opacity: 0;
     	visibility: hidden;
     }
     
     .source-sidebar-expanded .source .sidebar {
     	overflow-y: auto;
    +	width: 300px;
     }
     
     .source-sidebar-expanded .source .sidebar > *:not(#sidebar-toggle) {
    -	opacity: 1;
     	visibility: visible;
     }
     
    @@ -532,22 +473,15 @@ img {
     	width: 100px;
     }
     
    -.location:empty {
    -	border: none;
    -}
    -
    -.location a:first-of-type {
    -	font-weight: 500;
    -}
    -
    -.block ul, .block li {
    +ul.block, .block li {
     	padding: 0;
     	margin: 0;
     	list-style: none;
     }
     
     .block a,
    -h2.location a {
    +.sidebar h2 a,
    +.sidebar h3 a {
     	display: block;
     	padding: 0.25rem;
     	margin-left: -0.25rem;
    @@ -557,8 +491,7 @@ h2.location a {
     }
     
     .sidebar h2 {
    -	border-bottom: none;
    -	font-weight: 500;
    +	overflow-wrap: anywhere;
     	padding: 0;
     	margin: 0;
     	margin-top: 0.7rem;
    @@ -567,11 +500,23 @@ h2.location a {
     
     .sidebar h3 {
     	font-size: 1.125rem; /* 18px */
    -	font-weight: 500;
     	padding: 0;
     	margin: 0;
     }
     
    +.sidebar-elems,
    +.sidebar > h2 {
    +	padding-left: 24px;
    +}
    +
    +.sidebar a, .sidebar .current {
    +	color: var(--sidebar-link-color);
    +}
    +.sidebar .current,
    +.sidebar a:hover {
    +	background-color: var(--sidebar-current-link-background-color);
    +}
    +
     .sidebar-elems .block {
     	margin-bottom: 2em;
     }
    @@ -585,66 +530,61 @@ h2.location a {
     }
     
     .source .content pre.rust {
    -	white-space: pre;
     	overflow: auto;
     	padding-left: 0;
     }
     
     .rustdoc .example-wrap {
    -	display: inline-flex;
    +	display: flex;
    +	position: relative;
     	margin-bottom: 10px;
     }
    +/* For the last child of a div, the margin will be taken care of
    +	by the margin-top of the next item. */
    +.rustdoc .example-wrap:last-child {
    +	margin-bottom: 0px;
    +}
     
    -.example-wrap {
    -	position: relative;
    -	width: 100%;
    +.rustdoc .example-wrap > pre {
    +	margin: 0;
    +	flex-grow: 1;
    +	overflow-x: auto;
     }
     
    -.example-wrap > pre.line-number {
    +.rustdoc .example-wrap > pre.example-line-numbers,
    +.rustdoc .example-wrap > pre.src-line-numbers {
    +	flex-grow: 0;
     	overflow: initial;
    +	text-align: right;
    +	-webkit-user-select: none;
    +	-moz-user-select: none;
    +	-ms-user-select: none;
    +	user-select: none;
    +}
    +
    +.example-line-numbers {
     	border: 1px solid;
     	padding: 13px 8px;
    -	text-align: right;
     	border-top-left-radius: 5px;
     	border-bottom-left-radius: 5px;
    +	border-color: var(--example-line-numbers-border-color);
     }
     
    -.example-wrap > pre.rust a:hover {
    -	text-decoration: underline;
    -}
    -
    -.line-numbers {
    -	text-align: right;
    -}
    -.rustdoc:not(.source) .example-wrap > pre:not(.line-number) {
    -	width: 100%;
    -	overflow-x: auto;
    +.src-line-numbers span {
    +	cursor: pointer;
    +	color: var(--src-line-numbers-span-color);
     }
    -
    -.rustdoc:not(.source) .example-wrap > pre.line-numbers {
    -	width: auto;
    -	overflow-x: visible;
    +.src-line-numbers .line-highlighted {
    +	background-color: var(--src-line-number-highlighted-background-color);
     }
    -
    -.rustdoc .example-wrap > pre {
    -	margin: 0;
    +.src-line-numbers :target {
    +	background-color: transparent;
     }
     
     .search-loading {
     	text-align: center;
     }
     
    -.content > .example-wrap pre.line-numbers {
    -	position: relative;
    -	-webkit-user-select: none;
    -	-moz-user-select: none;
    -	-ms-user-select: none;
    -	user-select: none;
    -}
    -.line-numbers span {
    -	cursor: pointer;
    -}
    -
     .docblock-short {
     	overflow-wrap: break-word;
     	overflow-wrap: anywhere;
    @@ -669,9 +609,6 @@ h2.location a {
     
     .docblock h5 { font-size: 1rem; }
     .docblock h6 { font-size: 0.875rem; }
    -.docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5, .docblock h6 {
    -	border-bottom-color: var(--headings-border-bottom-color);
    -}
     
     .docblock {
     	margin-left: 24px;
    @@ -683,33 +620,9 @@ h2.location a {
     	overflow-x: auto;
     }
     
    -.content .out-of-band {
    +.out-of-band {
     	flex-grow: 0;
     	font-size: 1.125rem;
    -	font-weight: normal;
    -	float: right;
    -}
    -
    -.method > .code-header, .trait-impl > .code-header {
    -	max-width: calc(100% - 41px);
    -	display: block;
    -}
    -
    -.content .in-band {
    -	flex-grow: 1;
    -	margin: 0px;
    -	padding: 0px;
    -	/* We use overflow-wrap: break-word for Safari, which doesn't recognize
    -	   `anywhere`: https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-wrap */
    -	overflow-wrap: break-word;
    -	/* Then override it with `anywhere`, which is required to make non-Safari browsers break
    -	   more aggressively when we want them to. */
    -	overflow-wrap: anywhere;
    -	background-color: var(--main-background-color);
    -}
    -
    -.in-band > code, .in-band > .code-header {
    -	display: inline-block;
     }
     
     .docblock code, .docblock-short code,
    @@ -726,6 +639,7 @@ pre, .rustdoc.source .example-wrap {
     	width: calc(100% - 2px);
     	overflow-x: auto;
     	display: block;
    +	border-collapse: collapse;
     }
     
     .docblock table td {
    @@ -740,38 +654,21 @@ pre, .rustdoc.source .example-wrap {
     	border: 1px solid var(--border-color);
     }
     
    -.content .item-list {
    -	list-style-type: none;
    -	padding: 0;
    -}
    -
    -.content > .methods > .method {
    -	font-size: 1rem;
    -	position: relative;
    -}
     /* Shift "where ..." part of method or fn definition down a line */
    -.content .method .where,
    -.content .fn .where,
    -.content .where.fmt-newline {
    +.method .where,
    +.fn .where,
    +.where.fmt-newline {
     	display: block;
     	font-size: 0.875rem;
     }
     
     .item-info {
     	display: block;
    -}
    -
    -.content .item-info code {
    -	font-size: 0.875rem;
    -}
    -
    -.content .item-info {
    -	position: relative;
     	margin-left: 24px;
     }
     
    -.content .impl-items .docblock, .content .impl-items .item-info {
    -	margin-bottom: .6em;
    +.item-info code {
    +	font-size: 0.875rem;
     }
     
     #main-content > .item-info {
    @@ -780,16 +677,24 @@ pre, .rustdoc.source .example-wrap {
     }
     
     nav.sub {
    +	flex-grow: 1;
    +	flex-flow: row nowrap;
    +	margin: 4px 0 25px 0;
    +	display: flex;
    +	align-items: center;
    +}
    +.search-form {
     	position: relative;
    -	font-size: 1rem;
    +	display: flex;
    +	height: 34px;
     	flex-grow: 1;
    -	margin-bottom: 25px;
     }
     .source nav.sub {
    +	margin: 0 0 15px 0;
    +}
    +.source .search-form {
     	margin-left: 32px;
     }
    -nav.sum { text-align: right; }
    -nav.sub form { display: inline; }
     
     a {
     	text-decoration: none;
    @@ -805,9 +710,7 @@ a {
     	display: initial;
     }
     
    -.in-band:hover > .anchor, .impl:hover > .anchor, .method.trait-impl:hover > .anchor,
    -.type.trait-impl:hover > .anchor, .associatedconstant.trait-impl:hover > .anchor,
    -.associatedtype.trait-impl:hover > .anchor {
    +.impl:hover > .anchor, .trait-impl:hover > .anchor {
     	display: inline-block;
     	position: absolute;
     }
    @@ -831,12 +734,16 @@ h2.small-section-header > .anchor {
     	content: '§';
     }
     
    -.docblock a:not(.srclink):not(.test-arrow):not(.scrape-help):hover,
    -.docblock-short a:not(.srclink):not(.test-arrow):not(.scrape-help):hover, .item-info a {
    +.main-heading a:hover,
    +.example-wrap > pre.rust a:hover,
    +.all-items a:hover,
    +.docblock a:not(.test-arrow):not(.scrape-help):hover,
    +.docblock-short a:not(.test-arrow):not(.scrape-help):hover,
    +.item-info a {
     	text-decoration: underline;
     }
     
    -.block a.current.crate { font-weight: 500; }
    +.crate.block a.current { font-weight: 500; }
     
     /*  In most contexts we use `overflow-wrap: anywhere` to ensure that we can wrap
     	as much as needed on mobile (see
    @@ -876,15 +783,6 @@ table,
     	padding-right: 1.25rem;
     }
     
    -.search-container {
    -	position: relative;
    -	display: flex;
    -	height: 34px;
    -	margin-top: 4px;
    -}
    -.search-container > * {
    -	height: 100%;
    -}
     .search-results-title {
     	margin-top: 0;
     	white-space: nowrap;
    @@ -922,6 +820,9 @@ table,
     	/* Removes default arrow from firefox */
     	text-indent: 0.01px;
     	background-color: var(--main-background-color);
    +	color: inherit;
    +	line-height: 1.5;
    +	font-weight: 500;
     }
     /* cancel stylistic differences in padding in firefox
     for "appearance: none"-style (or equivalent)  {#- -#}
    -                            
    {#- -#} - {#- -#} -
    {#- -#} -
    {#- -#} - {#- -#} - Change settings {#- -#} - {#- -#} -
    {#- -#} -
    {#- -#} - {#- -#} - {#- -#} -
    {#- -#} + {%- endif -%} +
    {#- -#} + {#- This empty span is a hacky fix for Safari - See #93184 -#} + {#- -#} +
    {#- -#} + ? {#- -#} +
    {#- -#} +
    {#- -#} + {#- -#} + Change settings {#- -#} + {#- -#} +
    {#- -#} +
    {#- -#} + {#- -#}
    {{- content|safe -}}
    {#- -#}
    {#- -#} {#- -#} diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html index c755157d2..b6ce3ea3d 100644 --- a/src/librustdoc/html/templates/print_item.html +++ b/src/librustdoc/html/templates/print_item.html @@ -1,18 +1,16 @@
    {#- -#}

    {#- -#} - {#- -#} - {{-typ-}} - {#- The breadcrumbs of the item path, like std::string -#} - {%- for component in path_components -%} - {{component.name}}:: - {%- endfor -%} - {{name}} {#- -#} - {#- -#} - {#- -#} + {{-typ-}} + {#- The breadcrumbs of the item path, like std::string -#} + {%- for component in path_components -%} + {{component.name}}:: + {%- endfor -%} + {{name}} {#- -#} + {#- -#}

    {#- -#} {#- -#} {% if !stability_since_raw.is_empty() %} diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 49a31f5f1..cdf59cdd3 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -272,7 +272,12 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum { ConstantItem(c) => ItemEnum::Constant(c.into_tcx(tcx)), MacroItem(m) => ItemEnum::Macro(m.source), ProcMacroItem(m) => ItemEnum::ProcMacro(m.into_tcx(tcx)), - PrimitiveItem(p) => ItemEnum::PrimitiveType(p.as_sym().to_string()), + PrimitiveItem(p) => { + ItemEnum::Primitive(Primitive { + name: p.as_sym().to_string(), + impls: Vec::new(), // Added in JsonRenderer::item + }) + } TyAssocConstItem(ty) => ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: None }, AssocConstItem(ty, default) => { ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: Some(default.expr(tcx)) } @@ -427,8 +432,9 @@ impl FromWithTcx for WherePredicate { lifetime: convert_lifetime(lifetime), bounds: bounds.into_tcx(tcx), }, - EqPredicate { lhs, rhs } => { - WherePredicate::EqPredicate { lhs: lhs.into_tcx(tcx), rhs: rhs.into_tcx(tcx) } + // FIXME(fmease): Convert bound parameters as well. + EqPredicate { lhs, rhs, bound_params: _ } => { + WherePredicate::EqPredicate { lhs: (*lhs).into_tcx(tcx), rhs: (*rhs).into_tcx(tcx) } } } } diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 5e8f5f6fe..d13efe6c1 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -108,7 +108,6 @@ impl<'tcx> JsonRenderer<'tcx> { .filter_map(|(&id, trait_item)| { // only need to synthesize items for external traits if !id.is_local() { - let trait_item = &trait_item.trait_; for item in &trait_item.items { trace!("Adding subitem to {id:?}: {:?}", item.item_id); self.item(item.clone()).unwrap(); @@ -219,12 +218,15 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { u.impls = self.get_impls(item_id.expect_def_id()); false } + types::ItemEnum::Primitive(ref mut p) => { + p.impls = self.get_impls(item_id.expect_def_id()); + false + } types::ItemEnum::Method(_) | types::ItemEnum::Module(_) | types::ItemEnum::AssocConst { .. } - | types::ItemEnum::AssocType { .. } - | types::ItemEnum::PrimitiveType(_) => true, + | types::ItemEnum::AssocType { .. } => true, types::ItemEnum::ExternCrate { .. } | types::ItemEnum::Import(_) | types::ItemEnum::StructField(_) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 14d695582..4cf9435d9 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -9,14 +9,12 @@ #![feature(control_flow_enum)] #![feature(drain_filter)] #![feature(let_chains)] -#![cfg_attr(bootstrap, feature(let_else))] #![feature(test)] #![feature(never_type)] #![feature(once_cell)] #![feature(type_ascription)] #![feature(iter_intersperse)] #![feature(type_alias_impl_trait)] -#![cfg_attr(bootstrap, feature(generic_associated_types))] #![recursion_limit = "256"] #![warn(rustc::internal)] #![allow(clippy::collapsible_if, clippy::collapsible_else_if)] @@ -43,6 +41,7 @@ extern crate rustc_errors; extern crate rustc_expand; extern crate rustc_feature; extern crate rustc_hir; +extern crate rustc_hir_analysis; extern crate rustc_hir_pretty; extern crate rustc_index; extern crate rustc_infer; @@ -61,7 +60,6 @@ extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; extern crate rustc_trait_selection; -extern crate rustc_typeck; extern crate test; // See docs in https://github.com/rust-lang/rust/blob/master/compiler/rustc/src/main.rs @@ -156,7 +154,6 @@ pub fn main() { } } - rustc_driver::set_sigpipe_handler(); rustc_driver::install_ice_hook(); // When using CI artifacts (with `download_stage1 = true`), tracing is unconditionally built @@ -461,7 +458,7 @@ fn opts() -> Vec { "human|json|short", ) }), - unstable("diagnostic-width", |o| { + stable("diagnostic-width", |o| { o.optopt( "", "diagnostic-width", @@ -676,39 +673,6 @@ fn usage(argv0: &str) { /// A result type used by several functions under `main()`. type MainResult = Result<(), ErrorGuaranteed>; -fn main_args(at_args: &[String]) -> MainResult { - let args = rustc_driver::args::arg_expand_all(at_args); - - let mut options = getopts::Options::new(); - for option in opts() { - (option.apply)(&mut options); - } - let matches = match options.parse(&args[1..]) { - Ok(m) => m, - Err(err) => { - early_error(ErrorOutputType::default(), &err.to_string()); - } - }; - - // Note that we discard any distinction between different non-zero exit - // codes from `from_matches` here. - let options = match config::Options::from_matches(&matches, args) { - Ok(opts) => opts, - Err(code) => { - return if code == 0 { - Ok(()) - } else { - Err(ErrorGuaranteed::unchecked_claim_error_was_emitted()) - }; - } - }; - rustc_interface::util::run_in_thread_pool_with_globals( - options.edition, - 1, // this runs single-threaded, even in a parallel compiler - move || main_options(options), - ) -} - fn wrap_return(diag: &rustc_errors::Handler, res: Result<(), String>) -> MainResult { match res { Ok(()) => Ok(()), @@ -739,7 +703,33 @@ fn run_renderer<'tcx, T: formats::FormatRenderer<'tcx>>( } } -fn main_options(options: config::Options) -> MainResult { +fn main_args(at_args: &[String]) -> MainResult { + let args = rustc_driver::args::arg_expand_all(at_args); + + let mut options = getopts::Options::new(); + for option in opts() { + (option.apply)(&mut options); + } + let matches = match options.parse(&args[1..]) { + Ok(m) => m, + Err(err) => { + early_error(ErrorOutputType::default(), &err.to_string()); + } + }; + + // Note that we discard any distinction between different non-zero exit + // codes from `from_matches` here. + let (options, render_options) = match config::Options::from_matches(&matches, args) { + Ok(opts) => opts, + Err(code) => { + return if code == 0 { + Ok(()) + } else { + Err(ErrorGuaranteed::unchecked_claim_error_was_emitted()) + }; + } + }; + let diag = core::new_handler( options.error_format, None, @@ -751,9 +741,18 @@ fn main_options(options: config::Options) -> MainResult { (true, true) => return wrap_return(&diag, markdown::test(options)), (true, false) => return doctest::run(options), (false, true) => { + let input = options.input.clone(); + let edition = options.edition; + let config = core::create_config(options); + + // `markdown::render` can invoke `doctest::make_test`, which + // requires session globals and a thread pool, so we use + // `run_compiler`. return wrap_return( &diag, - markdown::render(&options.input, options.render_options, options.edition), + interface::run_compiler(config, |_compiler| { + markdown::render(&input, render_options, edition) + }), ); } (false, false) => {} @@ -774,14 +773,12 @@ fn main_options(options: config::Options) -> MainResult { let crate_version = options.crate_version.clone(); let output_format = options.output_format; - // FIXME: fix this clone (especially render_options) let externs = options.externs.clone(); - let render_options = options.render_options.clone(); let scrape_examples_options = options.scrape_examples_options.clone(); - let document_private = options.render_options.document_private; + let config = core::create_config(options); - interface::create_compiler_and_run(config, |compiler| { + interface::run_compiler(config, |compiler| { let sess = compiler.session(); if sess.opts.describe_lints { @@ -813,7 +810,7 @@ fn main_options(options: config::Options) -> MainResult { sess, krate, externs, - document_private, + render_options.document_private, ) }); (resolver.clone(), resolver_caches) diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index e76c19a61..3aad97bc2 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -148,7 +148,7 @@ declare_rustdoc_lint! { /// /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_html_tags INVALID_HTML_TAGS, - Allow, + Warn, "detects invalid HTML tags in doc comments" } diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 0b557ef24..044e05144 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -5,7 +5,6 @@ use std::path::Path; use rustc_span::edition::Edition; use rustc_span::source_map::DUMMY_SP; -use rustc_span::Symbol; use crate::config::{Options, RenderOptions}; use crate::doctest::{Collector, GlobalTestOptions}; @@ -36,6 +35,8 @@ fn extract_leading_metadata(s: &str) -> (Vec<&str>, &str) { /// Render `input` (e.g., "foo.md") into an HTML file in `output` /// (e.g., output = "bar" => "bar/foo.html"). +/// +/// Requires session globals to be available, for symbol interning. pub(crate) fn render>( input: P, options: RenderOptions, @@ -133,7 +134,7 @@ pub(crate) fn test(options: Options) -> Result<(), String> { let mut opts = GlobalTestOptions::default(); opts.no_crate_inject = true; let mut collector = Collector::new( - Symbol::intern(&options.input.display().to_string()), + options.input.display().to_string(), options.clone(), true, opts, @@ -142,7 +143,7 @@ pub(crate) fn test(options: Options) -> Result<(), String> { options.enable_per_target_ignores, ); collector.set_position(DUMMY_SP); - let codes = ErrorCodes::from(options.render_options.unstable_features.is_nightly_build()); + let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None); diff --git a/src/librustdoc/passes/bare_urls.rs b/src/librustdoc/passes/bare_urls.rs index 392e26ea6..7ff3ccef9 100644 --- a/src/librustdoc/passes/bare_urls.rs +++ b/src/librustdoc/passes/bare_urls.rs @@ -71,16 +71,14 @@ impl<'a, 'tcx> DocVisitor for BareUrlsLinter<'a, 'tcx> { let report_diag = |cx: &DocContext<'_>, msg: &str, url: &str, range: Range| { let sp = super::source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs) .unwrap_or_else(|| item.attr_span(cx.tcx)); - cx.tcx.struct_span_lint_hir(crate::lint::BARE_URLS, hir_id, sp, |lint| { - lint.build(msg) - .note("bare URLs are not automatically turned into clickable links") + cx.tcx.struct_span_lint_hir(crate::lint::BARE_URLS, hir_id, sp, msg, |lint| { + lint.note("bare URLs are not automatically turned into clickable links") .span_suggestion( sp, "use an automatic link instead", format!("<{}>", url), Applicability::MachineApplicable, ) - .emit(); }); }; diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 381ac7a5d..2e651b538 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -1,8 +1,9 @@ //! Validates syntax inside Rust code blocks (\`\`\`rust). use rustc_data_structures::sync::{Lock, Lrc}; use rustc_errors::{ - emitter::Emitter, translation::Translate, Applicability, Diagnostic, Handler, - LazyFallbackBundle, LintDiagnosticBuilder, + emitter::Emitter, + translation::{to_fluent_args, Translate}, + Applicability, Diagnostic, Handler, LazyFallbackBundle, }; use rustc_parse::parse_stream_from_source_str; use rustc_session::parse::ParseSess; @@ -97,48 +98,10 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { None => (item.attr_span(self.cx.tcx), false), }; - // lambda that will use the lint to start a new diagnostic and add - // a suggestion to it when needed. - let diag_builder = |lint: LintDiagnosticBuilder<'_, ()>| { - let explanation = if is_ignore { - "`ignore` code blocks require valid Rust code for syntax highlighting; \ - mark blocks that do not contain Rust code as text" - } else { - "mark blocks that do not contain Rust code as text" - }; - let msg = if buffer.has_errors { - "could not parse code block as Rust code" - } else { - "Rust code block is empty" - }; - let mut diag = lint.build(msg); - - if precise_span { - if is_ignore { - // giving an accurate suggestion is hard because `ignore` might not have come first in the list. - // just give a `help` instead. - diag.span_help( - sp.from_inner(InnerSpan::new(0, 3)), - &format!("{}: ```text", explanation), - ); - } else if empty_block { - diag.span_suggestion( - sp.from_inner(InnerSpan::new(0, 3)).shrink_to_hi(), - explanation, - "text", - Applicability::MachineApplicable, - ); - } - } else if empty_block || is_ignore { - diag.help(&format!("{}: ```text", explanation)); - } - - // FIXME(#67563): Provide more context for these errors by displaying the spans inline. - for message in buffer.messages.iter() { - diag.note(message); - } - - diag.emit(); + let msg = if buffer.has_errors { + "could not parse code block as Rust code" + } else { + "Rust code block is empty" }; // Finally build and emit the completed diagnostic. @@ -148,7 +111,42 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { crate::lint::INVALID_RUST_CODEBLOCKS, hir_id, sp, - diag_builder, + msg, + |lint| { + let explanation = if is_ignore { + "`ignore` code blocks require valid Rust code for syntax highlighting; \ + mark blocks that do not contain Rust code as text" + } else { + "mark blocks that do not contain Rust code as text" + }; + + if precise_span { + if is_ignore { + // giving an accurate suggestion is hard because `ignore` might not have come first in the list. + // just give a `help` instead. + lint.span_help( + sp.from_inner(InnerSpan::new(0, 3)), + &format!("{}: ```text", explanation), + ); + } else if empty_block { + lint.span_suggestion( + sp.from_inner(InnerSpan::new(0, 3)).shrink_to_hi(), + explanation, + "text", + Applicability::MachineApplicable, + ); + } + } else if empty_block || is_ignore { + lint.help(&format!("{}: ```text", explanation)); + } + + // FIXME(#67563): Provide more context for these errors by displaying the spans inline. + for message in buffer.messages.iter() { + lint.note(message); + } + + lint + }, ); } } @@ -195,8 +193,11 @@ impl Translate for BufferEmitter { impl Emitter for BufferEmitter { fn emit_diagnostic(&mut self, diag: &Diagnostic) { let mut buffer = self.buffer.borrow_mut(); - // FIXME(davidtwco): need to support translation here eventually - buffer.messages.push(format!("error from rustc: {}", diag.message[0].0.expect_str())); + + let fluent_args = to_fluent_args(diag.args()); + let translated_main_message = self.translate_message(&diag.message[0].0, &fluent_args); + + buffer.messages.push(format!("error from rustc: {}", translated_main_message)); if diag.is_error() { buffer.has_errors = true; } diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index 55d5f303d..7740c6d5b 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -56,7 +56,7 @@ impl crate::doctest::Tester for Tests { } pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool { - if !cx.cache.access_levels.is_public(item.item_id.expect_def_id()) + if !cx.cache.effective_visibilities.is_directly_public(item.item_id.expect_def_id()) || matches!( *item.kind, clean::StructFieldItem(_) @@ -125,21 +125,19 @@ pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item crate::lint::MISSING_DOC_CODE_EXAMPLES, hir_id, sp, - |lint| { - lint.build("missing code example in this documentation").emit(); - }, + "missing code example in this documentation", + |lint| lint, ); } } else if tests.found_tests > 0 - && !cx.cache.access_levels.is_exported(item.item_id.expect_def_id()) + && !cx.cache.effective_visibilities.is_exported(item.item_id.expect_def_id()) { cx.tcx.struct_span_lint_hir( crate::lint::PRIVATE_DOC_TESTS, hir_id, item.attr_span(cx.tcx), - |lint| { - lint.build("documentation test in private item").emit(); - }, + "documentation test in private item", + |lint| lint, ); } } diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index b2a41bfa4..8aa0abd36 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -80,10 +80,10 @@ impl Res { } } - fn def_id(self, tcx: TyCtxt<'_>) -> DefId { + fn def_id(self, tcx: TyCtxt<'_>) -> Option { match self { - Res::Def(_, id) => id, - Res::Primitive(prim) => *PrimitiveType::primitive_locations(tcx).get(&prim).unwrap(), + Res::Def(_, id) => Some(id), + Res::Primitive(prim) => PrimitiveType::primitive_locations(tcx).get(&prim).copied(), } } @@ -1127,10 +1127,10 @@ impl LinkCollector<'_, '_> { } } - Some(ItemLink { + res.def_id(self.cx.tcx).map(|page_id| ItemLink { link: ori_link.link.clone(), link_text: link_text.clone(), - page_id: res.def_id(self.cx.tcx), + page_id, fragment, }) } @@ -1202,8 +1202,8 @@ impl LinkCollector<'_, '_> { item.item_id.expect_def_id().as_local().map(|src_id| (src_id, dst_id)) }) { - if self.cx.tcx.privacy_access_levels(()).is_exported(src_id) - && !self.cx.tcx.privacy_access_levels(()).is_exported(dst_id) + if self.cx.tcx.effective_visibilities(()).is_exported(src_id) + && !self.cx.tcx.effective_visibilities(()).is_exported(dst_id) { privacy_error(self.cx, diag_info, path_str); } @@ -1609,9 +1609,7 @@ fn report_diagnostic( let sp = item.attr_span(tcx); - tcx.struct_span_lint_hir(lint, hir_id, sp, |lint| { - let mut diag = lint.build(msg); - + tcx.struct_span_lint_hir(lint, hir_id, sp, msg, |lint| { let span = super::source_span_for_markdown_range(tcx, dox, link_range, &item.attrs).map(|sp| { if dox.as_bytes().get(link_range.start) == Some(&b'`') @@ -1624,7 +1622,7 @@ fn report_diagnostic( }); if let Some(sp) = span { - diag.set_span(sp); + lint.set_span(sp); } else { // blah blah blah\nblah\nblah [blah] blah blah\nblah blah // ^ ~~~~ @@ -1634,7 +1632,7 @@ fn report_diagnostic( let line = dox[last_new_line_offset..].lines().next().unwrap_or(""); // Print the line containing the `link_range` and manually mark it with '^'s. - diag.note(&format!( + lint.note(&format!( "the link appears in this line:\n\n{line}\n\ {indicator: { markdown_links: FxHashMap>, doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option>>, traits_in_scope: DefIdMap>, - all_traits: Vec, all_trait_impls: Vec, all_macro_rules: FxHashMap>, document_private_items: bool, @@ -121,8 +119,6 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { loop { let crates = Vec::from_iter(self.resolver.cstore().crates_untracked()); for &cnum in &crates[start_cnum..] { - let all_traits = - Vec::from_iter(self.resolver.cstore().traits_in_crate_untracked(cnum)); let all_trait_impls = Vec::from_iter(self.resolver.cstore().trait_impls_in_crate_untracked(cnum)); let all_inherent_impls = @@ -131,20 +127,18 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { self.resolver.cstore().incoherent_impls_in_crate_untracked(cnum), ); - // Querying traits in scope is expensive so we try to prune the impl and traits lists - // using privacy, private traits and impls from other crates are never documented in + // Querying traits in scope is expensive so we try to prune the impl lists using + // privacy, private traits and impls from other crates are never documented in // the current crate, and links in their doc comments are not resolved. - for &def_id in &all_traits { - if self.resolver.cstore().visibility_untracked(def_id).is_public() { - self.resolve_doc_links_extern_impl(def_id, false); - } - } for &(trait_def_id, impl_def_id, simplified_self_ty) in &all_trait_impls { if self.resolver.cstore().visibility_untracked(trait_def_id).is_public() && simplified_self_ty.and_then(|ty| ty.def()).map_or(true, |ty_def_id| { self.resolver.cstore().visibility_untracked(ty_def_id).is_public() }) { + if self.visited_mods.insert(trait_def_id) { + self.resolve_doc_links_extern_impl(trait_def_id, false); + } self.resolve_doc_links_extern_impl(impl_def_id, false); } } @@ -157,7 +151,6 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { self.resolve_doc_links_extern_impl(impl_def_id, true); } - self.all_traits.extend(all_traits); self.all_trait_impls .extend(all_trait_impls.into_iter().map(|(_, def_id, _)| def_id)); } @@ -306,15 +299,20 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { { if let Some(def_id) = child.res.opt_def_id() && !def_id.is_local() { let scope_id = match child.res { - Res::Def(DefKind::Variant, ..) => self.resolver.parent(def_id), + Res::Def( + DefKind::Variant + | DefKind::AssocTy + | DefKind::AssocFn + | DefKind::AssocConst, + .., + ) => self.resolver.parent(def_id), _ => def_id, }; self.resolve_doc_links_extern_outer(def_id, scope_id); // Outer attribute scope if let Res::Def(DefKind::Mod, ..) = child.res { self.resolve_doc_links_extern_inner(def_id); // Inner attribute scope } - // `DefKind::Trait`s are processed in `process_extern_impls`. - if let Res::Def(DefKind::Mod | DefKind::Enum, ..) = child.res { + if let Res::Def(DefKind::Mod | DefKind::Enum | DefKind::Trait, ..) = child.res { self.process_module_children_or_reexports(def_id); } if let Res::Def(DefKind::Struct | DefKind::Union | DefKind::Variant, _) = @@ -356,10 +354,14 @@ impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> { self.parent_scope.module = old_module; } else { match &item.kind { - ItemKind::Trait(..) => { - self.all_traits.push(self.resolver.local_def_id(item.id).to_def_id()); - } - ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => { + ItemKind::Impl(box ast::Impl { of_trait: Some(trait_ref), .. }) => { + if let Some(partial_res) = self.resolver.get_partial_res(trait_ref.ref_id) + && let Some(res) = partial_res.full_res() + && let Some(trait_def_id) = res.opt_def_id() + && !trait_def_id.is_local() + && self.visited_mods.insert(trait_def_id) { + self.resolve_doc_links_extern_impl(trait_def_id, false); + } self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id()); } ItemKind::MacroDef(macro_def) if macro_def.macro_rules => { diff --git a/src/librustdoc/passes/html_tags.rs b/src/librustdoc/passes/html_tags.rs index 885dadb32..a89ed7c7e 100644 --- a/src/librustdoc/passes/html_tags.rs +++ b/src/librustdoc/passes/html_tags.rs @@ -22,10 +22,8 @@ struct InvalidHtmlTagsLinter<'a, 'tcx> { } pub(crate) fn check_invalid_html_tags(krate: Crate, cx: &mut DocContext<'_>) -> Crate { - if cx.tcx.sess.is_nightly_build() { - let mut coll = InvalidHtmlTagsLinter { cx }; - coll.visit_crate(&krate); - } + let mut coll = InvalidHtmlTagsLinter { cx }; + coll.visit_crate(&krate); krate } @@ -186,7 +184,60 @@ fn extract_html_tag( } drop_tag(tags, tag_name, r, f); } else { - tags.push((tag_name, r)); + let mut is_self_closing = false; + let mut quote_pos = None; + if c != '>' { + let mut quote = None; + let mut after_eq = false; + for (i, c) in text[pos..].char_indices() { + if !c.is_whitespace() { + if let Some(q) = quote { + if c == q { + quote = None; + quote_pos = None; + after_eq = false; + } + } else if c == '>' { + break; + } else if c == '/' && !after_eq { + is_self_closing = true; + } else { + if is_self_closing { + is_self_closing = false; + } + if (c == '"' || c == '\'') && after_eq { + quote = Some(c); + quote_pos = Some(pos + i); + } else if c == '=' { + after_eq = true; + } + } + } else if quote.is_none() { + after_eq = false; + } + } + } + if let Some(quote_pos) = quote_pos { + let qr = Range { start: quote_pos, end: quote_pos }; + f( + &format!("unclosed quoted HTML attribute on tag `{}`", tag_name), + &qr, + false, + ); + } + if is_self_closing { + // https://html.spec.whatwg.org/#parse-error-non-void-html-element-start-tag-with-trailing-solidus + let valid = ALLOWED_UNCLOSED.contains(&&tag_name[..]) + || tags.iter().take(pos + 1).any(|(at, _)| { + let at = at.to_lowercase(); + at == "svg" || at == "math" + }); + if !valid { + f(&format!("invalid self-closing HTML tag `{}`", tag_name), &r, false); + } + } else { + tags.push((tag_name, r)); + } } } break; @@ -240,9 +291,8 @@ impl<'a, 'tcx> DocVisitor for InvalidHtmlTagsLinter<'a, 'tcx> { Some(sp) => sp, None => item.attr_span(tcx), }; - tcx.struct_span_lint_hir(crate::lint::INVALID_HTML_TAGS, hir_id, sp, |lint| { + tcx.struct_span_lint_hir(crate::lint::INVALID_HTML_TAGS, hir_id, sp, msg, |lint| { use rustc_lint_defs::Applicability; - let mut diag = lint.build(msg); // If a tag looks like ``, it might actually be a generic. // We don't try to detect stuff `` because that's not valid HTML, // and we don't try to detect stuff `` because that's not valid Rust. @@ -305,11 +355,10 @@ impl<'a, 'tcx> DocVisitor for InvalidHtmlTagsLinter<'a, 'tcx> { if (generics_start > 0 && dox.as_bytes()[generics_start - 1] == b'<') || (generics_end < dox.len() && dox.as_bytes()[generics_end] == b'>') { - diag.emit(); - return; + return lint; } // multipart form is chosen here because ``Vec`` would be confusing. - diag.multipart_suggestion( + lint.multipart_suggestion( "try marking as source code", vec![ (generics_sp.shrink_to_lo(), String::from("`")), @@ -318,7 +367,8 @@ impl<'a, 'tcx> DocVisitor for InvalidHtmlTagsLinter<'a, 'tcx> { Applicability::MaybeIncorrect, ); } - diag.emit() + + lint }); }; diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 765f7c61b..de3a4b339 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -48,7 +48,7 @@ impl<'a, 'tcx> CfgPropagator<'a, 'tcx> { let expected_parent = hir.get_parent_item(hir_id); // If parents are different, it means that `item` is a reexport and we need // to compute the actual `cfg` by iterating through its "real" parents. - if self.parent == Some(expected_parent) { + if self.parent == Some(expected_parent.def_id) { return; } } diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs index f3aa3c7ce..450f69e15 100644 --- a/src/librustdoc/passes/strip_private.rs +++ b/src/librustdoc/passes/strip_private.rs @@ -23,7 +23,7 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> { let mut stripper = Stripper { retained: &mut retained, - access_levels: &cx.cache.access_levels, + effective_visibilities: &cx.cache.effective_visibilities, update_retained: true, is_json_output, }; diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index a9d768f01..0089ce63d 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -1,15 +1,17 @@ //! A collection of utility functions for the `strip_*` passes. use rustc_hir::def_id::DefId; -use rustc_middle::middle::privacy::AccessLevels; +use rustc_middle::middle::privacy::EffectiveVisibilities; +use rustc_span::symbol::sym; + use std::mem; -use crate::clean::{self, Item, ItemId, ItemIdSet}; +use crate::clean::{self, Item, ItemId, ItemIdSet, NestedAttributesExt}; use crate::fold::{strip_item, DocFolder}; use crate::formats::cache::Cache; pub(crate) struct Stripper<'a> { pub(crate) retained: &'a mut ItemIdSet, - pub(crate) access_levels: &'a AccessLevels, + pub(crate) effective_visibilities: &'a EffectiveVisibilities, pub(crate) update_retained: bool, pub(crate) is_json_output: bool, } @@ -20,13 +22,13 @@ pub(crate) struct Stripper<'a> { #[inline] fn is_item_reachable( is_json_output: bool, - access_levels: &AccessLevels, + effective_visibilities: &EffectiveVisibilities, item_id: ItemId, ) -> bool { if is_json_output { - access_levels.is_reachable(item_id.expect_def_id()) + effective_visibilities.is_reachable(item_id.expect_def_id()) } else { - access_levels.is_exported(item_id.expect_def_id()) + effective_visibilities.is_exported(item_id.expect_def_id()) } } @@ -64,7 +66,7 @@ impl<'a> DocFolder for Stripper<'a> { | clean::ForeignTypeItem => { let item_id = i.item_id; if item_id.is_local() - && !is_item_reachable(self.is_json_output, self.access_levels, item_id) + && !is_item_reachable(self.is_json_output, self.effective_visibilities, item_id) { debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name); return None; @@ -151,6 +153,22 @@ pub(crate) struct ImplStripper<'a> { pub(crate) document_private: bool, } +impl<'a> ImplStripper<'a> { + #[inline] + fn should_keep_impl(&self, item: &Item, for_def_id: DefId) -> bool { + if !for_def_id.is_local() || self.retained.contains(&for_def_id.into()) { + true + } else if self.is_json_output { + // If the "for" item is exported and the impl block isn't `#[doc(hidden)]`, then we + // need to keep it. + self.cache.effective_visibilities.is_exported(for_def_id) + && !item.attrs.lists(sym::doc).has_word(sym::hidden) + } else { + false + } + } +} + impl<'a> DocFolder for ImplStripper<'a> { fn fold_item(&mut self, i: Item) -> Option { if let clean::ImplItem(ref imp) = *i.kind { @@ -168,7 +186,7 @@ impl<'a> DocFolder for ImplStripper<'a> { item_id.is_local() && !is_item_reachable( self.is_json_output, - &self.cache.access_levels, + &self.cache.effective_visibilities, item_id, ) }) @@ -178,15 +196,17 @@ impl<'a> DocFolder for ImplStripper<'a> { return None; } } + // Because we don't inline in `maybe_inline_local` if the output format is JSON, + // we need to make a special check for JSON output: we want to keep it unless it has + // a `#[doc(hidden)]` attribute if the `for_` type is exported. if let Some(did) = imp.for_.def_id(self.cache) { - if did.is_local() && !imp.for_.is_assoc_ty() && !self.retained.contains(&did.into()) - { + if !imp.for_.is_assoc_ty() && !self.should_keep_impl(&i, did) { debug!("ImplStripper: impl item for stripped type; removing"); return None; } } if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id()) { - if did.is_local() && !self.retained.contains(&did.into()) { + if !self.should_keep_impl(&i, did) { debug!("ImplStripper: impl item for stripped trait; removing"); return None; } @@ -194,7 +214,7 @@ impl<'a> DocFolder for ImplStripper<'a> { if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) { for typaram in generics { if let Some(did) = typaram.def_id(self.cache) { - if did.is_local() && !self.retained.contains(&did.into()) { + if !self.should_keep_impl(&i, did) { debug!( "ImplStripper: stripped item in trait's generics; removing impl" ); diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index ca86ac89e..dfa6ba38b 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -143,14 +143,14 @@ where // then we need to exit before calling typeck (which will panic). See // test/run-make/rustdoc-scrape-examples-invalid-expr for an example. let hir = tcx.hir(); - if hir.maybe_body_owned_by(ex.hir_id.owner).is_none() { + if hir.maybe_body_owned_by(ex.hir_id.owner.def_id).is_none() { return; } // Get type of function if expression is a function call let (ty, call_span, ident_span) = match ex.kind { hir::ExprKind::Call(f, _) => { - let types = tcx.typeck(ex.hir_id.owner); + let types = tcx.typeck(ex.hir_id.owner.def_id); if let Some(ty) = types.node_type_opt(f.hir_id) { (ty, ex.span, f.span) @@ -160,7 +160,7 @@ where } } hir::ExprKind::MethodCall(path, _, _, call_span) => { - let types = tcx.typeck(ex.hir_id.owner); + let types = tcx.typeck(ex.hir_id.owner.def_id); let Some(def_id) = types.type_dependent_def_id(ex.hir_id) else { trace!("type_dependent_def_id({}) = None", ex.hir_id); return; @@ -183,9 +183,8 @@ where // If the enclosing item has a span coming from a proc macro, then we also don't want to include // the example. - let enclosing_item_span = tcx - .hir() - .span_with_body(tcx.hir().local_def_id_to_hir_id(tcx.hir().get_parent_item(ex.hir_id))); + let enclosing_item_span = + tcx.hir().span_with_body(tcx.hir().get_parent_item(ex.hir_id).into()); if enclosing_item_span.from_expansion() { trace!("Rejecting expr ({call_span:?}) from macro item: {enclosing_item_span:?}"); return; diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs index c40274394..d29ceead4 100644 --- a/src/librustdoc/visit.rs +++ b/src/librustdoc/visit.rs @@ -65,7 +65,7 @@ pub(crate) trait DocVisitor: Sized { // FIXME: make this a simple by-ref for loop once external_traits is cleaned up let external_traits = { std::mem::take(&mut *c.external_traits.borrow_mut()) }; for (k, v) in external_traits { - v.trait_.items.iter().for_each(|i| self.visit_item(i)); + v.items.iter().for_each(|i| self.visit_item(i)); c.external_traits.borrow_mut().insert(k, v); } } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index c27ac0ac4..06dffce55 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -7,8 +7,8 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::Node; use rustc_hir::CRATE_HIR_ID; -use rustc_middle::middle::privacy::AccessLevel; -use rustc_middle::ty::TyCtxt; +use rustc_middle::middle::privacy::Level; +use rustc_middle::ty::{TyCtxt, Visibility}; use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; @@ -230,7 +230,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } else { // All items need to be handled here in case someone wishes to link // to them with intra-doc links - self.cx.cache.access_levels.map.insert(did, AccessLevel::Public); + self.cx.cache.effective_visibilities.set_public_at_level( + did, + || Visibility::Restricted(CRATE_DEF_ID), + Level::Direct, + ); } } } @@ -242,7 +246,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { None => return false, }; - let is_private = !self.cx.cache.access_levels.is_public(res_did); + let is_private = !self.cx.cache.effective_visibilities.is_directly_public(res_did); let is_hidden = inherits_doc_hidden(self.cx.tcx, res_hir_id); // Only inline if requested or if the item would otherwise be stripped. @@ -291,11 +295,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { debug!("visiting item {:?}", item); let name = renamed.unwrap_or(item.ident.name); - let def_id = item.def_id.to_def_id(); + let def_id = item.owner_id.to_def_id(); let is_pub = self.cx.tcx.visibility(def_id).is_public(); if is_pub { - self.store_path(item.def_id.to_def_id()); + self.store_path(item.owner_id.to_def_id()); } match item.kind { @@ -356,7 +360,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // 3. We're inlining, since a reexport where inlining has been requested // should be inlined even if it is also documented at the top level. - let def_id = item.def_id.to_def_id(); + let def_id = item.owner_id.to_def_id(); let is_macro_2_0 = !macro_def.macro_rules; let nonexported = !self.cx.tcx.has_attr(def_id, sym::macro_export); @@ -401,7 +405,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { om: &mut Module<'tcx>, ) { // If inlining we only want to include public functions. - if !self.inlining || self.cx.tcx.visibility(item.def_id).is_public() { + if !self.inlining || self.cx.tcx.visibility(item.owner_id).is_public() { om.foreigns.push((item, renamed)); } } diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs index f01ec3866..70214e2ad 100644 --- a/src/librustdoc/visit_lib.rs +++ b/src/librustdoc/visit_lib.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_middle::middle::privacy::{AccessLevel, AccessLevels}; -use rustc_middle::ty::TyCtxt; +use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_ID}; +use rustc_middle::middle::privacy::{EffectiveVisibilities, Level}; +use rustc_middle::ty::{TyCtxt, Visibility}; // FIXME: this may not be exhaustive, but is sufficient for rustdocs current uses @@ -10,10 +10,10 @@ use rustc_middle::ty::TyCtxt; /// specific rustdoc annotations into account (i.e., `doc(hidden)`) pub(crate) struct LibEmbargoVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, - // Accessibility levels for reachable nodes - access_levels: &'a mut AccessLevels, - // Previous accessibility level, None means unreachable - prev_level: Option, + // Effective visibilities for reachable nodes + effective_visibilities: &'a mut EffectiveVisibilities, + // Previous level, None means unreachable + prev_level: Option, // Keeps track of already visited modules, in case a module re-exports its parent visited_mods: FxHashSet, } @@ -22,26 +22,30 @@ impl<'a, 'tcx> LibEmbargoVisitor<'a, 'tcx> { pub(crate) fn new(cx: &'a mut crate::core::DocContext<'tcx>) -> LibEmbargoVisitor<'a, 'tcx> { LibEmbargoVisitor { tcx: cx.tcx, - access_levels: &mut cx.cache.access_levels, - prev_level: Some(AccessLevel::Public), + effective_visibilities: &mut cx.cache.effective_visibilities, + prev_level: Some(Level::Direct), visited_mods: FxHashSet::default(), } } pub(crate) fn visit_lib(&mut self, cnum: CrateNum) { let did = cnum.as_def_id(); - self.update(did, Some(AccessLevel::Public)); + self.update(did, Some(Level::Direct)); self.visit_mod(did); } // Updates node level and returns the updated level - fn update(&mut self, did: DefId, level: Option) -> Option { + fn update(&mut self, did: DefId, level: Option) -> Option { let is_hidden = self.tcx.is_doc_hidden(did); - let old_level = self.access_levels.map.get(&did).cloned(); - // Accessibility levels can only grow + let old_level = self.effective_visibilities.public_at_level(did); + // Visibility levels can only grow if level > old_level && !is_hidden { - self.access_levels.map.insert(did, level.unwrap()); + self.effective_visibilities.set_public_at_level( + did, + || Visibility::Restricted(CRATE_DEF_ID), + level.unwrap(), + ); level } else { old_level -- cgit v1.2.3