summaryrefslogtreecommitdiffstats
path: root/src/librustdoc
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc')
-rw-r--r--src/librustdoc/Cargo.toml3
-rw-r--r--src/librustdoc/clean/auto_trait.rs7
-rw-r--r--src/librustdoc/clean/inline.rs37
-rw-r--r--src/librustdoc/clean/mod.rs209
-rw-r--r--src/librustdoc/clean/simplify.rs21
-rw-r--r--src/librustdoc/clean/types.rs98
-rw-r--r--src/librustdoc/clean/types/tests.rs3
-rw-r--r--src/librustdoc/clean/utils.rs164
-rw-r--r--src/librustdoc/core.rs17
-rw-r--r--src/librustdoc/doctest.rs13
-rw-r--r--src/librustdoc/formats/cache.rs86
-rw-r--r--src/librustdoc/formats/item_type.rs5
-rw-r--r--src/librustdoc/html/format.rs80
-rw-r--r--src/librustdoc/html/layout.rs37
-rw-r--r--src/librustdoc/html/markdown.rs5
-rw-r--r--src/librustdoc/html/render/context.rs75
-rw-r--r--src/librustdoc/html/render/mod.rs140
-rw-r--r--src/librustdoc/html/render/print_item.rs274
-rw-r--r--src/librustdoc/html/render/search_index.rs33
-rw-r--r--src/librustdoc/html/render/sidebar.rs105
-rw-r--r--src/librustdoc/html/render/write_shared.rs289
-rw-r--r--src/librustdoc/html/sources.rs7
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css87
-rw-r--r--src/librustdoc/html/static/js/main.js281
-rw-r--r--src/librustdoc/html/static/js/search.js21
-rw-r--r--src/librustdoc/html/templates/page.html52
-rw-r--r--src/librustdoc/html/templates/sidebar.html7
-rw-r--r--src/librustdoc/json/conversions.rs28
-rw-r--r--src/librustdoc/lib.rs15
-rw-r--r--src/librustdoc/markdown.rs2
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs6
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs20
-rw-r--r--src/librustdoc/passes/lint/html_tags.rs150
-rw-r--r--src/librustdoc/passes/lint/redundant_explicit_links.rs2
34 files changed, 1714 insertions, 665 deletions
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index 29912b957..f3917b978 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -10,7 +10,8 @@ path = "lib.rs"
arrayvec = { version = "0.7", default-features = false }
askama = { version = "0.12", default-features = false, features = ["config"] }
itertools = "0.10.1"
-minifier = "0.2.2"
+indexmap = "2"
+minifier = "0.3.0"
once_cell = "1.10.0"
regex = "1"
rustdoc-json-types = { path = "../rustdoc-json-types" }
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index a06f31a93..bdf6a0f6b 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -551,8 +551,8 @@ where
WherePredicate::RegionPredicate { lifetime, bounds } => {
lifetime_to_bounds.entry(lifetime).or_default().extend(bounds);
}
- WherePredicate::EqPredicate { lhs, rhs, bound_params } => {
- match *lhs {
+ WherePredicate::EqPredicate { lhs, rhs } => {
+ match lhs {
Type::QPath(box QPathData {
ref assoc,
ref self_type,
@@ -590,14 +590,13 @@ 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/inline.rs b/src/librustdoc/clean/inline.rs
index fcbcfbf5c..974ba1e3b 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -18,14 +18,16 @@ use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol};
use crate::clean::{
- self, clean_fn_decl_from_did_and_sig, clean_generics, clean_impl_item, clean_middle_assoc_item,
- clean_middle_field, clean_middle_ty, clean_trait_ref_with_bindings, clean_ty,
- clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes,
+ self, clean_bound_vars, clean_fn_decl_from_did_and_sig, clean_generics, clean_impl_item,
+ clean_middle_assoc_item, clean_middle_field, clean_middle_ty, clean_trait_ref_with_bindings,
+ clean_ty, clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes,
AttributesExt, ImplKind, ItemId, Type,
};
use crate::core::DocContext;
use crate::formats::item_type::ItemType;
+use super::Item;
+
/// Attempt to inline a definition into this AST.
///
/// This function will fetch the definition specified, and if it is
@@ -83,7 +85,7 @@ pub(crate) fn try_inline(
Res::Def(DefKind::TyAlias, did) => {
record_extern_fqn(cx, did, ItemType::TypeAlias);
build_impls(cx, did, attrs_without_docs, &mut ret);
- clean::TypeAliasItem(build_type_alias(cx, did))
+ clean::TypeAliasItem(build_type_alias(cx, did, &mut ret))
}
Res::Def(DefKind::Enum, did) => {
record_extern_fqn(cx, did, ItemType::Enum);
@@ -239,20 +241,13 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean
fn build_external_function<'tcx>(cx: &mut DocContext<'tcx>, did: DefId) -> Box<clean::Function> {
let sig = cx.tcx.fn_sig(did).instantiate_identity();
-
- let late_bound_regions = sig.bound_vars().into_iter().filter_map(|var| match var {
- ty::BoundVariableKind::Region(ty::BrNamed(_, name)) if name != kw::UnderscoreLifetime => {
- Some(clean::GenericParamDef::lifetime(name))
- }
- _ => None,
- });
-
let predicates = cx.tcx.explicit_predicates_of(did);
+
let (generics, decl) = clean::enter_impl_trait(cx, |cx| {
// NOTE: generics need to be cleaned before the decl!
let mut generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates);
// FIXME: This does not place parameters in source order (late-bound ones come last)
- generics.params.extend(late_bound_regions);
+ generics.params.extend(clean_bound_vars(sig.bound_vars()));
let decl = clean_fn_decl_from_did_and_sig(cx, Some(did), sig);
(generics, decl)
});
@@ -288,11 +283,15 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union {
clean::Union { generics, fields }
}
-fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::TypeAlias> {
+fn build_type_alias(
+ cx: &mut DocContext<'_>,
+ did: DefId,
+ ret: &mut Vec<Item>,
+) -> Box<clean::TypeAlias> {
let predicates = cx.tcx.explicit_predicates_of(did);
let ty = cx.tcx.type_of(did).instantiate_identity();
let type_ = clean_middle_ty(ty::Binder::dummy(ty), cx, Some(did), None);
- let inner_type = clean_ty_alias_inner_type(ty, cx);
+ let inner_type = clean_ty_alias_inner_type(ty, cx, ret);
Box::new(clean::TypeAlias {
type_,
@@ -600,7 +599,7 @@ fn build_module_items(
let prim_ty = clean::PrimitiveType::from(p);
items.push(clean::Item {
name: None,
- attrs: Box::new(clean::Attributes::default()),
+ attrs: Box::default(),
// We can use the item's `DefId` directly since the only information ever used
// from it is `DefId.krate`.
item_id: ItemId::DefId(did),
@@ -648,13 +647,13 @@ fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant {
clean::simplify::move_bounds_to_generic_parameters(&mut generics);
clean::Constant {
- type_: clean_middle_ty(
+ type_: Box::new(clean_middle_ty(
ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()),
cx,
Some(def_id),
None,
- ),
- generics: Box::new(generics),
+ )),
+ generics,
kind: clean::ConstantKind::Extern { def_id },
}
}
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 0caa92e44..1b7ca7bf7 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -232,20 +232,11 @@ fn clean_poly_trait_ref_with_bindings<'tcx>(
poly_trait_ref: ty::PolyTraitRef<'tcx>,
bindings: ThinVec<TypeBinding>,
) -> GenericBound {
- // collect any late bound regions
- let late_bound_regions: Vec<_> = cx
- .tcx
- .collect_referenced_late_bound_regions(&poly_trait_ref)
- .into_iter()
- .filter_map(|br| match br {
- ty::BrNamed(_, name) if br.is_named() => Some(GenericParamDef::lifetime(name)),
- _ => None,
- })
- .collect();
-
- let trait_ = clean_trait_ref_with_bindings(cx, poly_trait_ref, bindings);
GenericBound::TraitBound(
- PolyTrait { trait_, generic_params: late_bound_regions },
+ PolyTrait {
+ trait_: clean_trait_ref_with_bindings(cx, poly_trait_ref, bindings),
+ generic_params: clean_bound_vars(poly_trait_ref.bound_vars()),
+ },
hir::TraitBoundModifier::None,
)
}
@@ -268,13 +259,13 @@ fn clean_lifetime<'tcx>(lifetime: &hir::Lifetime, cx: &mut DocContext<'tcx>) ->
pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg, cx: &mut DocContext<'tcx>) -> Constant {
let def_id = cx.tcx.hir().body_owner_def_id(constant.value.body).to_def_id();
Constant {
- type_: clean_middle_ty(
+ type_: Box::new(clean_middle_ty(
ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()),
cx,
Some(def_id),
None,
- ),
- generics: Box::new(Generics::default()),
+ )),
+ generics: Generics::default(),
kind: ConstantKind::Anonymous { body: constant.value.body },
}
}
@@ -285,8 +276,8 @@ pub(crate) fn clean_middle_const<'tcx>(
) -> Constant {
// FIXME: instead of storing the stringified expression, store `self` directly instead.
Constant {
- type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None, None),
- generics: Box::new(Generics::default()),
+ type_: Box::new(clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None, None)),
+ generics: Generics::default(),
kind: ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() },
}
}
@@ -338,9 +329,8 @@ fn clean_where_predicate<'tcx>(
},
hir::WherePredicate::EqPredicate(ref wrp) => WherePredicate::EqPredicate {
- lhs: Box::new(clean_ty(wrp.lhs_ty, cx)),
- rhs: Box::new(clean_ty(wrp.rhs_ty, cx).into()),
- bound_params: Vec::new(),
+ lhs: clean_ty(wrp.lhs_ty, cx),
+ rhs: clean_ty(wrp.rhs_ty, cx).into(),
},
})
}
@@ -436,20 +426,9 @@ fn clean_projection_predicate<'tcx>(
pred: ty::Binder<'tcx, ty::ProjectionPredicate<'tcx>>,
cx: &mut DocContext<'tcx>,
) -> WherePredicate {
- let late_bound_regions = cx
- .tcx
- .collect_referenced_late_bound_regions(&pred)
- .into_iter()
- .filter_map(|br| match br {
- ty::BrNamed(_, name) if br.is_named() => Some(GenericParamDef::lifetime(name)),
- _ => None,
- })
- .collect();
-
WherePredicate::EqPredicate {
- lhs: Box::new(clean_projection(pred.map_bound(|p| p.projection_ty), cx, None)),
- rhs: Box::new(clean_middle_term(pred.map_bound(|p| p.term), cx)),
- bound_params: late_bound_regions,
+ lhs: clean_projection(pred.map_bound(|p| p.projection_ty), cx, None),
+ rhs: clean_middle_term(pred.map_bound(|p| p.term), cx),
}
}
@@ -496,8 +475,9 @@ fn projection_to_path_segment<'tcx>(
ty: ty::Binder<'tcx, ty::AliasTy<'tcx>>,
cx: &mut DocContext<'tcx>,
) -> PathSegment {
- let item = cx.tcx.associated_item(ty.skip_binder().def_id);
- let generics = cx.tcx.generics_of(ty.skip_binder().def_id);
+ let def_id = ty.skip_binder().def_id;
+ let item = cx.tcx.associated_item(def_id);
+ let generics = cx.tcx.generics_of(def_id);
PathSegment {
name: item.name,
args: GenericArgs::AngleBracketed {
@@ -505,7 +485,7 @@ fn projection_to_path_segment<'tcx>(
cx,
ty.map_bound(|ty| &ty.args[generics.parent_count..]),
false,
- None,
+ def_id,
)
.into(),
bindings: Default::default(),
@@ -519,7 +499,7 @@ fn clean_generic_param_def<'tcx>(
) -> GenericParamDef {
let (name, kind) = match def.kind {
ty::GenericParamDefKind::Lifetime => {
- (def.name, GenericParamDefKind::Lifetime { outlives: vec![] })
+ (def.name, GenericParamDefKind::Lifetime { outlives: ThinVec::new() })
}
ty::GenericParamDefKind::Type { has_default, synthetic, .. } => {
let default = if has_default {
@@ -536,7 +516,7 @@ fn clean_generic_param_def<'tcx>(
def.name,
GenericParamDefKind::Type {
did: def.def_id,
- bounds: vec![], // These are filled in from the where-clauses.
+ bounds: ThinVec::new(), // These are filled in from the where-clauses.
default: default.map(Box::new),
synthetic,
},
@@ -588,7 +568,7 @@ fn clean_generic_param<'tcx>(
})
.collect()
} else {
- Vec::new()
+ ThinVec::new()
};
(param.name.ident().name, GenericParamDefKind::Lifetime { outlives })
}
@@ -601,7 +581,7 @@ fn clean_generic_param<'tcx>(
.filter_map(|x| clean_generic_bound(x, cx))
.collect()
} else {
- Vec::new()
+ ThinVec::new()
};
(
param.name.ident().name,
@@ -657,7 +637,7 @@ pub(crate) fn clean_generics<'tcx>(
match param.kind {
GenericParamDefKind::Lifetime { .. } => unreachable!(),
GenericParamDefKind::Type { did, ref bounds, .. } => {
- cx.impl_trait_bounds.insert(did.into(), bounds.clone());
+ cx.impl_trait_bounds.insert(did.into(), bounds.to_vec());
}
GenericParamDefKind::Const { .. } => unreachable!(),
}
@@ -705,8 +685,8 @@ pub(crate) fn clean_generics<'tcx>(
}
}
}
- WherePredicate::EqPredicate { lhs, rhs, bound_params } => {
- eq_predicates.push(WherePredicate::EqPredicate { lhs, rhs, bound_params });
+ WherePredicate::EqPredicate { lhs, rhs } => {
+ eq_predicates.push(WherePredicate::EqPredicate { lhs, rhs });
}
}
}
@@ -800,11 +780,9 @@ fn clean_ty_generics<'tcx>(
})
.collect::<ThinVec<GenericParamDef>>();
- // param index -> [(trait DefId, associated type name & generics, term, higher-ranked params)]
- let mut impl_trait_proj = FxHashMap::<
- u32,
- Vec<(DefId, PathSegment, ty::Binder<'_, ty::Term<'_>>, Vec<GenericParamDef>)>,
- >::default();
+ // param index -> [(trait DefId, associated type name & generics, term)]
+ let mut impl_trait_proj =
+ FxHashMap::<u32, Vec<(DefId, PathSegment, ty::Binder<'_, ty::Term<'_>>)>>::default();
let where_predicates = preds
.predicates
@@ -856,11 +834,6 @@ fn clean_ty_generics<'tcx>(
trait_did,
name,
proj.map_bound(|p| p.term),
- pred.get_bound_params()
- .into_iter()
- .flatten()
- .cloned()
- .collect(),
));
}
@@ -896,9 +869,9 @@ fn clean_ty_generics<'tcx>(
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 {
+ for (trait_did, name, rhs) in proj {
let rhs = clean_middle_term(rhs, cx);
- simplify::merge_bounds(cx, &mut bounds, bound_params, trait_did, name, &rhs);
+ simplify::merge_bounds(cx, &mut bounds, trait_did, name, &rhs);
}
}
@@ -962,11 +935,16 @@ fn clean_ty_generics<'tcx>(
fn clean_ty_alias_inner_type<'tcx>(
ty: Ty<'tcx>,
cx: &mut DocContext<'tcx>,
+ ret: &mut Vec<Item>,
) -> Option<TypeAliasInnerType> {
let ty::Adt(adt_def, args) = ty.kind() else {
return None;
};
+ if !adt_def.did().is_local() {
+ inline::build_impls(cx, adt_def.did(), None, ret);
+ }
+
Some(if adt_def.is_enum() {
let variants: rustc_index::IndexVec<_, _> = adt_def
.variants()
@@ -974,6 +952,10 @@ fn clean_ty_alias_inner_type<'tcx>(
.map(|variant| clean_variant_def_with_args(variant, args, cx))
.collect();
+ if !adt_def.did().is_local() {
+ inline::record_extern_fqn(cx, adt_def.did(), ItemType::Enum);
+ }
+
TypeAliasInnerType::Enum {
variants,
is_non_exhaustive: adt_def.is_variant_list_non_exhaustive(),
@@ -989,8 +971,14 @@ fn clean_ty_alias_inner_type<'tcx>(
clean_variant_def_with_args(variant, args, cx).kind.inner_items().cloned().collect();
if adt_def.is_struct() {
+ if !adt_def.did().is_local() {
+ inline::record_extern_fqn(cx, adt_def.did(), ItemType::Struct);
+ }
TypeAliasInnerType::Struct { ctor_kind: variant.ctor_kind(), fields }
} else {
+ if !adt_def.did().is_local() {
+ inline::record_extern_fqn(cx, adt_def.did(), ItemType::Union);
+ }
TypeAliasInnerType::Union { fields }
}
})
@@ -1244,14 +1232,14 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
hir::TraitItemKind::Const(ty, Some(default)) => {
let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
AssocConstItem(
- Box::new(generics),
- clean_ty(ty, cx),
+ generics,
+ Box::new(clean_ty(ty, cx)),
ConstantKind::Local { def_id: local_did, body: default },
)
}
hir::TraitItemKind::Const(ty, None) => {
let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
- TyAssocConstItem(Box::new(generics), clean_ty(ty, cx))
+ TyAssocConstItem(generics, Box::new(clean_ty(ty, cx)))
}
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Body(body));
@@ -1300,7 +1288,7 @@ pub(crate) fn clean_impl_item<'tcx>(
hir::ImplItemKind::Const(ty, expr) => {
let generics = clean_generics(impl_.generics, cx);
let default = ConstantKind::Local { def_id: local_did, body: expr };
- AssocConstItem(Box::new(generics), clean_ty(ty, cx), default)
+ AssocConstItem(generics, Box::new(clean_ty(ty, cx)), default)
}
hir::ImplItemKind::Fn(ref sig, body) => {
let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body));
@@ -1339,18 +1327,18 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
let tcx = cx.tcx;
let kind = match assoc_item.kind {
ty::AssocKind::Const => {
- let ty = clean_middle_ty(
+ let ty = Box::new(clean_middle_ty(
ty::Binder::dummy(tcx.type_of(assoc_item.def_id).instantiate_identity()),
cx,
Some(assoc_item.def_id),
None,
- );
+ ));
- let mut generics = Box::new(clean_ty_generics(
+ let mut generics = clean_ty_generics(
cx,
tcx.generics_of(assoc_item.def_id),
tcx.explicit_predicates_of(assoc_item.def_id),
- ));
+ );
simplify::move_bounds_to_generic_parameters(&mut generics);
let provided = match assoc_item.container {
@@ -1365,23 +1353,13 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
}
ty::AssocKind::Fn => {
let sig = tcx.fn_sig(assoc_item.def_id).instantiate_identity();
-
- let late_bound_regions = sig.bound_vars().into_iter().filter_map(|var| match var {
- ty::BoundVariableKind::Region(ty::BrNamed(_, name))
- if name != kw::UnderscoreLifetime =>
- {
- Some(GenericParamDef::lifetime(name))
- }
- _ => None,
- });
-
let mut generics = clean_ty_generics(
cx,
tcx.generics_of(assoc_item.def_id),
tcx.explicit_predicates_of(assoc_item.def_id),
);
// FIXME: This does not place parameters in source order (late-bound ones come last)
- generics.params.extend(late_bound_regions);
+ generics.params.extend(clean_bound_vars(sig.bound_vars()));
let mut decl = clean_fn_decl_from_did_and_sig(cx, Some(assoc_item.def_id), sig);
@@ -2117,9 +2095,11 @@ pub(crate) fn clean_middle_ty<'tcx>(
// FIXME: should we merge the outer and inner binders somehow?
let sig = bound_ty.skip_binder().fn_sig(cx.tcx);
let decl = clean_fn_decl_from_did_and_sig(cx, None, sig);
+ let generic_params = clean_bound_vars(sig.bound_vars());
+
BareFunction(Box::new(BareFunctionDecl {
unsafety: sig.unsafety(),
- generic_params: Vec::new(),
+ generic_params,
decl,
abi: sig.abi(),
}))
@@ -2195,8 +2175,8 @@ pub(crate) fn clean_middle_ty<'tcx>(
let late_bound_regions: FxIndexSet<_> = obj
.iter()
- .flat_map(|pb| pb.bound_vars())
- .filter_map(|br| match br {
+ .flat_map(|pred| pred.bound_vars())
+ .filter_map(|var| match var {
ty::BoundVariableKind::Region(ty::BrNamed(_, name))
if name != kw::UnderscoreLifetime =>
{
@@ -2221,18 +2201,19 @@ pub(crate) fn clean_middle_ty<'tcx>(
}
ty::Alias(ty::Inherent, alias_ty) => {
+ let def_id = alias_ty.def_id;
let alias_ty = bound_ty.rebind(alias_ty);
let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None, None);
Type::QPath(Box::new(QPathData {
assoc: PathSegment {
- name: cx.tcx.associated_item(alias_ty.skip_binder().def_id).name,
+ name: cx.tcx.associated_item(def_id).name,
args: GenericArgs::AngleBracketed {
args: ty_args_to_args(
cx,
alias_ty.map_bound(|ty| ty.args.as_slice()),
true,
- None,
+ def_id,
)
.into(),
bindings: Default::default(),
@@ -2270,6 +2251,11 @@ pub(crate) fn clean_middle_ty<'tcx>(
}
}
+ ty::Bound(_, ref ty) => match ty.kind {
+ ty::BoundTyKind::Param(_, name) => Generic(name),
+ ty::BoundTyKind::Anon => panic!("unexpected anonymous bound type variable"),
+ },
+
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
// If it's already in the same alias, don't get an infinite loop.
if cx.current_type_aliases.contains_key(&def_id) {
@@ -2297,10 +2283,9 @@ pub(crate) fn clean_middle_ty<'tcx>(
}
ty::Closure(..) => panic!("Closure"),
- ty::Generator(..) => panic!("Generator"),
- ty::Bound(..) => panic!("Bound"),
+ ty::Coroutine(..) => panic!("Coroutine"),
ty::Placeholder(..) => panic!("Placeholder"),
- ty::GeneratorWitness(..) => panic!("GeneratorWitness"),
+ ty::CoroutineWitness(..) => panic!("CoroutineWitness"),
ty::Infer(..) => panic!("Infer"),
ty::Error(_) => rustc_errors::FatalError.raise(),
}
@@ -2549,7 +2534,8 @@ fn clean_generic_args<'tcx>(
}
hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()),
hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty, cx)),
- // FIXME(effects): This will still emit `<true>` for non-const impls of const traits
+ // Checking for `#[rustc_host]` on the `AnonConst` not only accounts for the case
+ // where the argument is `host` but for all possible cases (e.g., `true`, `false`).
hir::GenericArg::Const(ct)
if cx.tcx.has_attr(ct.value.def_id, sym::rustc_host) =>
{
@@ -2750,8 +2736,8 @@ fn clean_maybe_renamed_item<'tcx>(
StaticItem(Static { type_: clean_ty(ty, cx), mutability, expr: Some(body_id) })
}
ItemKind::Const(ty, generics, body_id) => ConstantItem(Constant {
- type_: clean_ty(ty, cx),
- generics: Box::new(clean_generics(generics, cx)),
+ type_: Box::new(clean_ty(ty, cx)),
+ generics: clean_generics(generics, cx),
kind: ConstantKind::Local { body: body_id, def_id },
}),
ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy {
@@ -2776,14 +2762,24 @@ fn clean_maybe_renamed_item<'tcx>(
}
let ty = cx.tcx.type_of(def_id).instantiate_identity();
- let inner_type = clean_ty_alias_inner_type(ty, cx);
- TypeAliasItem(Box::new(TypeAlias {
- generics,
- inner_type,
- type_: rustdoc_ty,
- item_type: Some(type_),
- }))
+ let mut ret = Vec::new();
+ let inner_type = clean_ty_alias_inner_type(ty, cx, &mut ret);
+
+ ret.push(generate_item_with_correct_attrs(
+ cx,
+ TypeAliasItem(Box::new(TypeAlias {
+ generics,
+ inner_type,
+ type_: rustdoc_ty,
+ item_type: Some(type_),
+ })),
+ item.owner_id.def_id.to_def_id(),
+ name,
+ import_id,
+ renamed,
+ ));
+ return ret;
}
ItemKind::Enum(ref def, generics) => EnumItem(Enum {
variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(),
@@ -3137,3 +3133,30 @@ fn clean_type_binding<'tcx>(
},
}
}
+
+fn clean_bound_vars<'tcx>(
+ bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
+) -> Vec<GenericParamDef> {
+ bound_vars
+ .into_iter()
+ .filter_map(|var| match var {
+ ty::BoundVariableKind::Region(ty::BrNamed(_, name))
+ if name != kw::UnderscoreLifetime =>
+ {
+ Some(GenericParamDef::lifetime(name))
+ }
+ ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(did, name)) => Some(GenericParamDef {
+ name,
+ kind: GenericParamDefKind::Type {
+ did,
+ bounds: ThinVec::new(),
+ default: None,
+ synthetic: false,
+ },
+ }),
+ // FIXME(non_lifetime_binders): Support higher-ranked const parameters.
+ ty::BoundVariableKind::Const => None,
+ _ => None,
+ })
+ .collect()
+}
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index 7b8f20326..627f15e67 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -40,18 +40,18 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> ThinVec<WP
WP::RegionPredicate { lifetime, bounds } => {
lifetimes.push((lifetime, bounds));
}
- WP::EqPredicate { lhs, rhs, bound_params } => equalities.push((lhs, rhs, bound_params)),
+ WP::EqPredicate { lhs, rhs } => equalities.push((lhs, rhs)),
}
}
// Look for equality predicates on associated types that can be merged into
// general bound predicates.
- equalities.retain(|(lhs, rhs, bound_params)| {
+ equalities.retain(|(lhs, rhs)| {
let Some((ty, trait_did, name)) = lhs.projection() else {
return true;
};
let Some((bounds, _)) = tybounds.get_mut(ty) else { return true };
- merge_bounds(cx, bounds, bound_params.clone(), trait_did, name, rhs)
+ merge_bounds(cx, bounds, trait_did, name, rhs)
});
// And finally, let's reassemble everything
@@ -64,18 +64,13 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> ThinVec<WP
bounds,
bound_params,
}));
- clauses.extend(equalities.into_iter().map(|(lhs, rhs, bound_params)| WP::EqPredicate {
- lhs,
- rhs,
- bound_params,
- }));
+ clauses.extend(equalities.into_iter().map(|(lhs, rhs)| WP::EqPredicate { lhs, rhs }));
clauses
}
pub(crate) fn merge_bounds(
cx: &clean::DocContext<'_>,
bounds: &mut Vec<clean::GenericBound>,
- mut bound_params: Vec<clean::GenericParamDef>,
trait_did: DefId,
assoc: clean::PathSegment,
rhs: &clean::Term,
@@ -93,12 +88,6 @@ pub(crate) fn merge_bounds(
}
let last = trait_ref.trait_.segments.last_mut().expect("segments were empty");
- trait_ref.generic_params.append(&mut bound_params);
- // Sort parameters (likely) originating from a hashset alphabetically to
- // produce predictable output (and to allow for full deduplication).
- 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 {
@@ -156,7 +145,7 @@ pub(crate) fn move_bounds_to_generic_parameters(generics: &mut clean::Generics)
..
}) = generics.params.iter_mut().find(|param| &param.name == arg)
{
- param_bounds.append(bounds);
+ param_bounds.extend(bounds.drain(..));
} else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred
&& let Some(GenericParamDef {
kind: GenericParamDefKind::Lifetime { outlives: param_bounds },
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 4256e7b51..88ee4e3a2 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -12,7 +12,7 @@ use thin_vec::ThinVec;
use rustc_ast as ast;
use rustc_ast_pretty::pprust;
-use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel};
+use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel, StableSince};
use rustc_const_eval::const_eval::is_unstable_const_fn;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
@@ -585,14 +585,14 @@ impl Item {
})
}
- pub(crate) fn stable_since(&self, tcx: TyCtxt<'_>) -> Option<Symbol> {
+ pub(crate) fn stable_since(&self, tcx: TyCtxt<'_>) -> Option<StableSince> {
match self.stability(tcx)?.level {
StabilityLevel::Stable { since, .. } => Some(since),
StabilityLevel::Unstable { .. } => None,
}
}
- pub(crate) fn const_stable_since(&self, tcx: TyCtxt<'_>) -> Option<Symbol> {
+ pub(crate) fn const_stable_since(&self, tcx: TyCtxt<'_>) -> Option<StableSince> {
match self.const_stability(tcx)?.level {
StabilityLevel::Stable { since, .. } => Some(since),
StabilityLevel::Unstable { .. } => None,
@@ -713,12 +713,16 @@ impl Item {
Some(tcx.visibility(def_id))
}
- pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, keep_as_is: bool) -> Vec<String> {
+ pub(crate) fn attributes(
+ &self,
+ tcx: TyCtxt<'_>,
+ cache: &Cache,
+ keep_as_is: bool,
+ ) -> Vec<String> {
const ALLOWED_ATTRIBUTES: &[Symbol] =
- &[sym::export_name, sym::link_section, sym::no_mangle, sym::repr, sym::non_exhaustive];
+ &[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive];
use rustc_abi::IntegerType;
- use rustc_middle::ty::ReprFlags;
let mut attrs: Vec<String> = self
.attrs
@@ -739,20 +743,38 @@ impl Item {
}
})
.collect();
- if let Some(def_id) = self.def_id() &&
- !def_id.is_local() &&
- // This check is needed because `adt_def` will panic if not a compatible type otherwise...
- matches!(self.type_(), ItemType::Struct | ItemType::Enum | ItemType::Union)
+ if !keep_as_is
+ && let Some(def_id) = self.def_id()
+ && let ItemType::Struct | ItemType::Enum | ItemType::Union = self.type_()
{
- let repr = tcx.adt_def(def_id).repr();
+ let adt = tcx.adt_def(def_id);
+ let repr = adt.repr();
let mut out = Vec::new();
- if repr.flags.contains(ReprFlags::IS_C) {
+ if repr.c() {
out.push("C");
}
- if repr.flags.contains(ReprFlags::IS_TRANSPARENT) {
- out.push("transparent");
+ if repr.transparent() {
+ // Render `repr(transparent)` iff the non-1-ZST field is public or at least one
+ // field is public in case all fields are 1-ZST fields.
+ let render_transparent = cache.document_private
+ || adt
+ .all_fields()
+ .find(|field| {
+ let ty =
+ field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did));
+ tcx.layout_of(tcx.param_env(field.did).and(ty))
+ .is_ok_and(|layout| !layout.is_1zst())
+ })
+ .map_or_else(
+ || adt.all_fields().any(|field| field.vis.is_public()),
+ |field| field.vis.is_public(),
+ );
+
+ if render_transparent {
+ out.push("transparent");
+ }
}
- if repr.flags.contains(ReprFlags::IS_SIMD) {
+ if repr.simd() {
out.push("simd");
}
let pack_s;
@@ -777,10 +799,9 @@ impl Item {
};
out.push(&int_s);
}
- if out.is_empty() {
- return Vec::new();
+ if !out.is_empty() {
+ attrs.push(format!("#[repr({})]", out.join(", ")));
}
- attrs.push(format!("#[repr({})]", out.join(", ")));
}
attrs
}
@@ -831,9 +852,9 @@ pub(crate) enum ItemKind {
ProcMacroItem(ProcMacro),
PrimitiveItem(PrimitiveType),
/// A required associated constant in a trait declaration.
- TyAssocConstItem(Box<Generics>, Type),
+ TyAssocConstItem(Generics, Box<Type>),
/// An associated constant in a trait impl or a provided one in a trait declaration.
- AssocConstItem(Box<Generics>, Type, ConstantKind),
+ AssocConstItem(Generics, Box<Type>, ConstantKind),
/// A required associated type in a trait declaration.
///
/// The bounds may be non-empty if there is a `where` clause.
@@ -1289,7 +1310,7 @@ impl Lifetime {
pub(crate) enum WherePredicate {
BoundPredicate { ty: Type, bounds: Vec<GenericBound>, bound_params: Vec<GenericParamDef> },
RegionPredicate { lifetime: Lifetime, bounds: Vec<GenericBound> },
- EqPredicate { lhs: Box<Type>, rhs: Box<Term>, bound_params: Vec<GenericParamDef> },
+ EqPredicate { lhs: Type, rhs: Term },
}
impl WherePredicate {
@@ -1300,21 +1321,13 @@ impl WherePredicate {
_ => None,
}
}
-
- pub(crate) fn get_bound_params(&self) -> Option<&[GenericParamDef]> {
- match self {
- Self::BoundPredicate { bound_params, .. } | Self::EqPredicate { bound_params, .. } => {
- Some(bound_params)
- }
- _ => None,
- }
- }
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub(crate) enum GenericParamDefKind {
- Lifetime { outlives: Vec<Lifetime> },
- Type { did: DefId, bounds: Vec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
+ Lifetime { outlives: ThinVec<Lifetime> },
+ Type { did: DefId, bounds: ThinVec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
+ // Option<Box<String>> makes this type smaller than `Option<String>` would.
Const { ty: Box<Type>, default: Option<Box<String>>, is_host_effect: bool },
}
@@ -1332,7 +1345,7 @@ pub(crate) struct GenericParamDef {
impl GenericParamDef {
pub(crate) fn lifetime(name: Symbol) -> Self {
- Self { name, kind: GenericParamDefKind::Lifetime { outlives: Vec::new() } }
+ Self { name, kind: GenericParamDefKind::Lifetime { outlives: ThinVec::new() } }
}
pub(crate) fn is_synthetic_param(&self) -> bool {
@@ -1443,6 +1456,9 @@ impl Trait {
pub(crate) fn unsafety(&self, tcx: TyCtxt<'_>) -> hir::Unsafety {
tcx.trait_def(self.def_id).unsafety
}
+ pub(crate) fn is_object_safe(&self, tcx: TyCtxt<'_>) -> bool {
+ tcx.check_is_object_safe(self.def_id)
+ }
}
#[derive(Clone, Debug)]
@@ -2094,9 +2110,8 @@ impl Discriminant {
pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> Option<String> {
self.expr.map(|body| rendered_const(tcx, body))
}
- /// Will always be a machine readable number, without underscores or suffixes.
- pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> String {
- print_evaluated_const(tcx, self.value, false).unwrap()
+ pub(crate) fn value(&self, tcx: TyCtxt<'_>, with_underscores: bool) -> String {
+ print_evaluated_const(tcx, self.value, with_underscores, false).unwrap()
}
}
@@ -2271,8 +2286,8 @@ pub(crate) struct Static {
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct Constant {
- pub(crate) type_: Type,
- pub(crate) generics: Box<Generics>,
+ pub(crate) type_: Box<Type>,
+ pub(crate) generics: Generics,
pub(crate) kind: ConstantKind,
}
@@ -2341,7 +2356,7 @@ impl ConstantKind {
match *self {
ConstantKind::TyConst { .. } | ConstantKind::Anonymous { .. } => None,
ConstantKind::Extern { def_id } | ConstantKind::Local { def_id, .. } => {
- print_evaluated_const(tcx, def_id, true)
+ print_evaluated_const(tcx, def_id, true, true)
}
}
}
@@ -2510,11 +2525,10 @@ mod size_asserts {
static_assert_size!(DocFragment, 32);
static_assert_size!(GenericArg, 32);
static_assert_size!(GenericArgs, 32);
- static_assert_size!(GenericParamDef, 56);
+ static_assert_size!(GenericParamDef, 40);
static_assert_size!(Generics, 16);
static_assert_size!(Item, 56);
- // FIXME(generic_const_items): Further reduce the size.
- static_assert_size!(ItemKind, 72);
+ static_assert_size!(ItemKind, 56);
static_assert_size!(PathSegment, 40);
static_assert_size!(Type, 32);
// tidy-alphabetical-end
diff --git a/src/librustdoc/clean/types/tests.rs b/src/librustdoc/clean/types/tests.rs
index 4907a5527..ee7c0068e 100644
--- a/src/librustdoc/clean/types/tests.rs
+++ b/src/librustdoc/clean/types/tests.rs
@@ -1,9 +1,8 @@
use super::*;
use rustc_resolve::rustdoc::{unindent_doc_fragments, DocFragment, DocFragmentKind};
-use rustc_span::create_default_session_globals_then;
-use rustc_span::source_map::DUMMY_SP;
use rustc_span::symbol::Symbol;
+use rustc_span::{create_default_session_globals_then, DUMMY_SP};
fn create_doc_fragment(s: &str) -> Vec<DocFragment> {
vec![DocFragment {
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 61e653423..9ff00c194 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -17,6 +17,7 @@ use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
use rustc_metadata::rendered_const;
use rustc_middle::mir;
use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, TyCtxt};
+use rustc_middle::ty::{TypeVisitable, TypeVisitableExt};
use rustc_span::symbol::{kw, sym, Symbol};
use std::fmt::Write as _;
use std::mem;
@@ -76,44 +77,119 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
pub(crate) fn ty_args_to_args<'tcx>(
cx: &mut DocContext<'tcx>,
- args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>,
+ ty_args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>,
has_self: bool,
- container: Option<DefId>,
+ owner: DefId,
) -> Vec<GenericArg> {
- let mut skip_first = has_self;
- let mut ret_val =
- Vec::with_capacity(args.skip_binder().len().saturating_sub(if skip_first { 1 } else { 0 }));
-
- ret_val.extend(args.iter().enumerate().filter_map(|(index, kind)| {
- match kind.skip_binder().unpack() {
- GenericArgKind::Lifetime(lt) => {
- Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided())))
- }
- GenericArgKind::Type(_) if skip_first => {
- skip_first = false;
- None
+ if ty_args.skip_binder().is_empty() {
+ // Fast path which avoids executing the query `generics_of`.
+ return Vec::new();
+ }
+
+ let params = &cx.tcx.generics_of(owner).params;
+ let mut elision_has_failed_once_before = false;
+
+ let offset = if has_self { 1 } else { 0 };
+ let mut args = Vec::with_capacity(ty_args.skip_binder().len().saturating_sub(offset));
+
+ let ty_arg_to_arg = |(index, arg): (usize, &ty::GenericArg<'tcx>)| match arg.unpack() {
+ GenericArgKind::Lifetime(lt) => {
+ Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided())))
+ }
+ GenericArgKind::Type(_) if has_self && index == 0 => None,
+ GenericArgKind::Type(ty) => {
+ if !elision_has_failed_once_before
+ && let Some(default) = params[index].default_value(cx.tcx)
+ {
+ let default =
+ ty_args.map_bound(|args| default.instantiate(cx.tcx, args).expect_ty());
+
+ if can_elide_generic_arg(ty_args.rebind(ty), default) {
+ return None;
+ }
+
+ elision_has_failed_once_before = true;
}
- GenericArgKind::Type(ty) => Some(GenericArg::Type(clean_middle_ty(
- kind.rebind(ty),
+
+ Some(GenericArg::Type(clean_middle_ty(
+ ty_args.rebind(ty),
cx,
None,
- container.map(|container| crate::clean::ContainerTy::Regular {
- ty: container,
- args,
+ Some(crate::clean::ContainerTy::Regular {
+ ty: owner,
+ args: ty_args,
has_self,
arg: index,
}),
- ))),
- // FIXME(effects): this relies on the host effect being called `host`, which users could also name
- // their const generics.
- // FIXME(effects): this causes `host = true` and `host = false` generics to also be emitted.
- GenericArgKind::Const(ct) if let ty::ConstKind::Param(p) = ct.kind() && p.name == sym::host => None,
- GenericArgKind::Const(ct) => {
- Some(GenericArg::Const(Box::new(clean_middle_const(kind.rebind(ct), cx))))
+ )))
+ }
+ GenericArgKind::Const(ct) => {
+ if let ty::GenericParamDefKind::Const { is_host_effect: true, .. } = params[index].kind
+ {
+ return None;
+ }
+
+ if !elision_has_failed_once_before
+ && let Some(default) = params[index].default_value(cx.tcx)
+ {
+ let default =
+ ty_args.map_bound(|args| default.instantiate(cx.tcx, args).expect_const());
+
+ if can_elide_generic_arg(ty_args.rebind(ct), default) {
+ return None;
+ }
+
+ elision_has_failed_once_before = true;
}
+
+ Some(GenericArg::Const(Box::new(clean_middle_const(ty_args.rebind(ct), cx))))
}
- }));
- ret_val
+ };
+
+ args.extend(ty_args.skip_binder().iter().enumerate().rev().filter_map(ty_arg_to_arg));
+ args.reverse();
+ args
+}
+
+/// Check if the generic argument `actual` coincides with the `default` and can therefore be elided.
+///
+/// This uses a very conservative approach for performance and correctness reasons, meaning for
+/// several classes of terms it claims that they cannot be elided even if they theoretically could.
+/// This is absolutely fine since it mostly concerns edge cases.
+fn can_elide_generic_arg<'tcx, Term>(
+ actual: ty::Binder<'tcx, Term>,
+ default: ty::Binder<'tcx, Term>,
+) -> bool
+where
+ Term: Eq + TypeVisitable<TyCtxt<'tcx>>,
+{
+ // In practice, we shouldn't have any inference variables at this point.
+ // However to be safe, we bail out if we do happen to stumble upon them.
+ if actual.has_infer() || default.has_infer() {
+ return false;
+ }
+
+ // Since we don't properly keep track of bound variables in rustdoc (yet), we don't attempt to
+ // make any sense out of escaping bound variables. We simply don't have enough context and it
+ // would be incorrect to try to do so anyway.
+ if actual.has_escaping_bound_vars() || default.has_escaping_bound_vars() {
+ return false;
+ }
+
+ // Theoretically we could now check if either term contains (non-escaping) late-bound regions or
+ // projections, relate the two using an `InferCtxt` and check if the resulting obligations hold.
+ // Having projections means that the terms can potentially be further normalized thereby possibly
+ // revealing that they are equal after all. Regarding late-bound regions, they could to be
+ // liberated allowing us to consider more types to be equal by ignoring the names of binders
+ // (e.g., `for<'a> TYPE<'a>` and `for<'b> TYPE<'b>`).
+ //
+ // However, we are mostly interested in “reeliding” generic args, i.e., eliding generic args that
+ // were originally elided by the user and later filled in by the compiler contrary to eliding
+ // arbitrary generic arguments if they happen to semantically coincide with the default (of course,
+ // we cannot possibly distinguish these two cases). Therefore and for performance reasons, it
+ // suffices to only perform a syntactic / structural check by comparing the memory addresses of
+ // the interned arguments.
+ actual.skip_binder() == default.skip_binder()
}
fn external_generic_args<'tcx>(
@@ -123,7 +199,7 @@ fn external_generic_args<'tcx>(
bindings: ThinVec<TypeBinding>,
ty_args: ty::Binder<'tcx, GenericArgsRef<'tcx>>,
) -> GenericArgs {
- let args = ty_args_to_args(cx, ty_args.map_bound(|args| &args[..]), has_self, Some(did));
+ let args = ty_args_to_args(cx, ty_args.map_bound(|args| &args[..]), has_self, did);
if cx.tcx.fn_trait_kind_from_def_id(did).is_some() {
let ty = ty_args
@@ -279,7 +355,8 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
pub(crate) fn print_evaluated_const(
tcx: TyCtxt<'_>,
def_id: DefId,
- underscores_and_type: bool,
+ with_underscores: bool,
+ with_type: bool,
) -> Option<String> {
tcx.const_eval_poly(def_id).ok().and_then(|val| {
let ty = tcx.type_of(def_id).instantiate_identity();
@@ -288,7 +365,7 @@ pub(crate) fn print_evaluated_const(
(mir::ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
(mir::ConstValue::Scalar(_), _) => {
let const_ = mir::Const::from_value(val, ty);
- Some(print_const_with_custom_print_scalar(tcx, const_, underscores_and_type))
+ Some(print_const_with_custom_print_scalar(tcx, const_, with_underscores, with_type))
}
_ => None,
}
@@ -324,32 +401,37 @@ fn format_integer_with_underscore_sep(num: &str) -> String {
fn print_const_with_custom_print_scalar<'tcx>(
tcx: TyCtxt<'tcx>,
ct: mir::Const<'tcx>,
- underscores_and_type: bool,
+ with_underscores: bool,
+ with_type: bool,
) -> String {
// Use a slightly different format for integer types which always shows the actual value.
// For all other types, fallback to the original `pretty_print_const`.
match (ct, ct.ty().kind()) {
(mir::Const::Val(mir::ConstValue::Scalar(int), _), ty::Uint(ui)) => {
- if underscores_and_type {
- format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
+ let mut output = if with_underscores {
+ format_integer_with_underscore_sep(&int.to_string())
} else {
int.to_string()
+ };
+ if with_type {
+ output += ui.name_str();
}
+ output
}
(mir::Const::Val(mir::ConstValue::Scalar(int), _), ty::Int(i)) => {
let ty = ct.ty();
let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size;
let data = int.assert_bits(size);
let sign_extended_data = size.sign_extend(data) as i128;
- if underscores_and_type {
- format!(
- "{}{}",
- format_integer_with_underscore_sep(&sign_extended_data.to_string()),
- i.name_str()
- )
+ let mut output = if with_underscores {
+ format_integer_with_underscore_sep(&sign_extended_data.to_string())
} else {
sign_extended_data.to_string()
+ };
+ if with_type {
+ output += i.name_str();
}
+ output
}
_ => ct.to_string(),
}
@@ -502,7 +584,7 @@ pub(crate) fn has_doc_flag(tcx: TyCtxt<'_>, did: DefId, flag: Symbol) -> bool {
/// Set by `bootstrap::Builder::doc_rust_lang_org_channel` in order to keep tests passing on beta/stable.
pub(crate) const DOC_RUST_LANG_ORG_CHANNEL: &str = env!("DOC_RUST_LANG_ORG_CHANNEL");
pub(crate) static DOC_CHANNEL: Lazy<&'static str> =
- Lazy::new(|| DOC_RUST_LANG_ORG_CHANNEL.rsplit("/").filter(|c| !c.is_empty()).next().unwrap());
+ Lazy::new(|| DOC_RUST_LANG_ORG_CHANNEL.rsplit('/').filter(|c| !c.is_empty()).next().unwrap());
/// Render a sequence of macro arms in a format suitable for displaying to the user
/// as part of an item declaration.
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 3e6066c78..6d9f8b820 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -14,8 +14,8 @@ use rustc_lint::{late_lint_mod, MissingDoc};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_session::config::{self, CrateType, ErrorOutputType, ResolveDocLinks};
+use rustc_session::lint;
use rustc_session::Session;
-use rustc_session::{lint, EarlyErrorHandler};
use rustc_span::symbol::sym;
use rustc_span::{source_map, Span};
@@ -23,6 +23,7 @@ use std::cell::RefCell;
use std::mem;
use std::rc::Rc;
use std::sync::LazyLock;
+use std::sync::{atomic::AtomicBool, Arc};
use crate::clean::inline::build_external_trait;
use crate::clean::{self, ItemId};
@@ -174,7 +175,6 @@ pub(crate) fn new_handler(
/// Parse, resolve, and typecheck the given crate.
pub(crate) fn create_config(
- handler: &EarlyErrorHandler,
RustdocOptions {
input,
crate_name,
@@ -198,6 +198,7 @@ pub(crate) fn create_config(
..
}: RustdocOptions,
RenderOptions { document_private, .. }: &RenderOptions,
+ using_internal_features: Arc<AtomicBool>,
) -> rustc_interface::Config {
// Add the doc cfg into the doc build.
cfgs.push("doc".to_string());
@@ -253,8 +254,8 @@ pub(crate) fn create_config(
interface::Config {
opts: sessopts,
- crate_cfg: interface::parse_cfgspecs(handler, cfgs),
- crate_check_cfg: interface::parse_check_cfg(handler, check_cfgs),
+ crate_cfg: cfgs,
+ crate_check_cfg: check_cfgs,
input,
output_file: None,
output_dir: None,
@@ -262,6 +263,7 @@ pub(crate) fn create_config(
locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES,
lint_caps,
parse_sess_created: None,
+ hash_untracked_state: None,
register_lints: Some(Box::new(crate::lint::register_lints)),
override_queries: Some(|_sess, providers| {
// We do not register late module lints, so this only runs `MissingDoc`.
@@ -292,6 +294,7 @@ pub(crate) fn create_config(
make_codegen_backend: None,
registry: rustc_driver::diagnostics_registry(),
ice_file: None,
+ using_internal_features,
expanded_args,
}
}
@@ -316,10 +319,14 @@ pub(crate) fn run_global_ctxt(
tcx.hir().for_each_module(|module| tcx.ensure().collect_mod_item_types(module))
});
- // NOTE: This is copy/pasted from typeck/lib.rs and should be kept in sync with those changes.
+ // NOTE: These are copy/pasted from typeck/lib.rs and should be kept in sync with those changes.
+ let _ = tcx.sess.time("wf_checking", || {
+ tcx.hir().try_par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module))
+ });
tcx.sess.time("item_types_checking", || {
tcx.hir().for_each_module(|module| tcx.ensure().check_mod_item_types(module))
});
+
tcx.sess.abort_if_errors();
tcx.sess.time("missing_docs", || rustc_lint::check_crate(tcx));
tcx.sess.time("check_mod_attrs", || {
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 741d329fb..241286580 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -13,7 +13,7 @@ use rustc_parse::parser::attr::InnerAttrPolicy;
use rustc_resolve::rustdoc::span_of_fragments;
use rustc_session::config::{self, CrateType, ErrorOutputType};
use rustc_session::parse::ParseSess;
-use rustc_session::{lint, EarlyErrorHandler, Session};
+use rustc_session::{lint, Session};
use rustc_span::edition::Edition;
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::sym;
@@ -85,18 +85,13 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
..config::Options::default()
};
- let early_error_handler = EarlyErrorHandler::new(ErrorOutputType::default());
-
let mut cfgs = options.cfgs.clone();
cfgs.push("doc".to_owned());
cfgs.push("doctest".to_owned());
let config = interface::Config {
opts: sessopts,
- crate_cfg: interface::parse_cfgspecs(&early_error_handler, cfgs),
- crate_check_cfg: interface::parse_check_cfg(
- &early_error_handler,
- options.check_cfgs.clone(),
- ),
+ crate_cfg: cfgs,
+ crate_check_cfg: options.check_cfgs.clone(),
input,
output_file: None,
output_dir: None,
@@ -104,11 +99,13 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES,
lint_caps,
parse_sess_created: None,
+ hash_untracked_state: None,
register_lints: Some(Box::new(crate::lint::register_lints)),
override_queries: None,
make_codegen_backend: None,
registry: rustc_driver::diagnostics_registry(),
ice_file: None,
+ using_internal_features: Arc::default(),
expanded_args: options.expanded_args.clone(),
};
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 4c6e7dfb9..abff77253 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -50,8 +50,8 @@ pub(crate) struct Cache {
/// Unlike 'paths', this mapping ignores any renames that occur
/// due to 'use' statements.
///
- /// This map is used when writing out the special 'implementors'
- /// javascript file. By using the exact path that the type
+ /// This map is used when writing out the `impl.trait` and `impl.type`
+ /// javascript files. By using the exact path that the type
/// is declared with, we ensure that each path will be identical
/// to the path used if the corresponding type is inlined. By
/// doing this, we can detect duplicate impls on a trait page, and only display
@@ -221,19 +221,25 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
_ => self.cache.stripped_mod,
};
+ #[inline]
+ fn is_from_private_dep(tcx: TyCtxt<'_>, cache: &Cache, def_id: DefId) -> bool {
+ let krate = def_id.krate;
+
+ cache.masked_crates.contains(&krate) || tcx.is_private_dep(krate)
+ }
+
// If the impl is from a masked crate or references something from a
// masked crate then remove it completely.
- if let clean::ImplItem(ref i) = *item.kind {
- if self.cache.masked_crates.contains(&item.item_id.krate())
+ if let clean::ImplItem(ref i) = *item.kind &&
+ (self.cache.masked_crates.contains(&item.item_id.krate())
|| i.trait_
.as_ref()
- .map_or(false, |t| self.cache.masked_crates.contains(&t.def_id().krate))
+ .map_or(false, |t| is_from_private_dep(self.tcx, self.cache, t.def_id()))
|| i.for_
.def_id(self.cache)
- .map_or(false, |d| self.cache.masked_crates.contains(&d.krate))
- {
- return None;
- }
+ .map_or(false, |d| is_from_private_dep(self.tcx, self.cache, d)))
+ {
+ return None;
}
// Propagate a trait method's documentation to all implementors of the
@@ -334,33 +340,37 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
// A crate has a module at its root, containing all items,
// which should not be indexed. The crate-item itself is
// inserted later on when serializing the search-index.
- if item.item_id.as_def_id().map_or(false, |idx| !idx.is_crate_root()) {
+ if item.item_id.as_def_id().map_or(false, |idx| !idx.is_crate_root())
+ && let ty = item.type_()
+ && (ty != ItemType::StructField
+ || u16::from_str_radix(s.as_str(), 10).is_err())
+ {
let desc =
short_markdown_summary(&item.doc_value(), &item.link_names(self.cache));
- let ty = item.type_();
- if ty != ItemType::StructField
- || u16::from_str_radix(s.as_str(), 10).is_err()
- {
- // In case this is a field from a tuple struct, we don't add it into
- // the search index because its name is something like "0", which is
- // not useful for rustdoc search.
- self.cache.search_index.push(IndexItem {
- ty,
- name: s,
- path: join_with_double_colon(path),
- desc,
- parent,
- parent_idx: None,
- search_type: get_function_type_for_search(
- &item,
- self.tcx,
- clean_impl_generics(self.cache.parent_stack.last()).as_ref(),
- self.cache,
- ),
- aliases: item.attrs.get_doc_aliases(),
- deprecation: item.deprecation(self.tcx),
- });
- }
+ // In case this is a field from a tuple struct, we don't add it into
+ // the search index because its name is something like "0", which is
+ // not useful for rustdoc search.
+ self.cache.search_index.push(IndexItem {
+ ty,
+ name: s,
+ path: join_with_double_colon(path),
+ desc,
+ parent,
+ parent_idx: None,
+ impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = self.cache.parent_stack.last() {
+ item_id.as_def_id()
+ } else {
+ None
+ },
+ search_type: get_function_type_for_search(
+ &item,
+ self.tcx,
+ clean_impl_generics(self.cache.parent_stack.last()).as_ref(),
+ self.cache,
+ ),
+ aliases: item.attrs.get_doc_aliases(),
+ deprecation: item.deprecation(self.tcx),
+ });
}
}
(Some(parent), None) if is_inherent_impl_item => {
@@ -371,6 +381,13 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
parent,
item: item.clone(),
impl_generics,
+ impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) =
+ self.cache.parent_stack.last()
+ {
+ item_id.as_def_id()
+ } else {
+ None
+ },
});
}
_ => {}
@@ -541,6 +558,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
pub(crate) struct OrphanImplItem {
pub(crate) parent: DefId,
+ pub(crate) impl_id: Option<DefId>,
pub(crate) item: clean::Item,
pub(crate) impl_generics: Option<(clean::Type, clean::Generics)>,
}
diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs
index be2ee7915..def3a90c8 100644
--- a/src/librustdoc/formats/item_type.rs
+++ b/src/librustdoc/formats/item_type.rs
@@ -141,7 +141,7 @@ impl From<DefKind> for ItemType {
| DefKind::GlobalAsm
| DefKind::Impl { .. }
| DefKind::Closure
- | DefKind::Generator => Self::ForeignType,
+ | DefKind::Coroutine => Self::ForeignType,
}
}
}
@@ -180,6 +180,9 @@ impl ItemType {
pub(crate) fn is_method(&self) -> bool {
matches!(*self, ItemType::Method | ItemType::TyMethod)
}
+ pub(crate) fn is_adt(&self) -> bool {
+ matches!(*self, ItemType::Struct | ItemType::Union | ItemType::Enum)
+ }
}
impl fmt::Display for ItemType {
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 2751b6613..29fd880af 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -325,8 +325,7 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
bounds_display.truncate(bounds_display.len() - " + ".len());
write!(f, "{}: {bounds_display}", lifetime.print())
}
- // FIXME(fmease): Render bound params.
- clean::WherePredicate::EqPredicate { lhs, rhs, bound_params: _ } => {
+ clean::WherePredicate::EqPredicate { lhs, rhs } => {
if f.alternate() {
write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx))
} else {
@@ -848,7 +847,7 @@ fn resolved_path<'cx>(
fn primitive_link(
f: &mut fmt::Formatter<'_>,
prim: clean::PrimitiveType,
- name: &str,
+ name: fmt::Arguments<'_>,
cx: &Context<'_>,
) -> fmt::Result {
primitive_link_fragment(f, prim, name, "", cx)
@@ -857,7 +856,7 @@ fn primitive_link(
fn primitive_link_fragment(
f: &mut fmt::Formatter<'_>,
prim: clean::PrimitiveType,
- name: &str,
+ name: fmt::Arguments<'_>,
fragment: &str,
cx: &Context<'_>,
) -> fmt::Result {
@@ -908,7 +907,7 @@ fn primitive_link_fragment(
None => {}
}
}
- f.write_str(name)?;
+ std::fmt::Display::fmt(&name, f)?;
if needs_termination {
write!(f, "</a>")?;
}
@@ -978,9 +977,11 @@ fn fmt_type<'cx>(
}
clean::Infer => write!(f, "_"),
clean::Primitive(clean::PrimitiveType::Never) => {
- primitive_link(f, PrimitiveType::Never, "!", cx)
+ primitive_link(f, PrimitiveType::Never, format_args!("!"), cx)
+ }
+ clean::Primitive(prim) => {
+ primitive_link(f, prim, format_args!("{}", prim.as_sym().as_str()), cx)
}
- clean::Primitive(prim) => primitive_link(f, prim, prim.as_sym().as_str(), cx),
clean::BareFunction(ref decl) => {
if f.alternate() {
write!(
@@ -999,16 +1000,16 @@ fn fmt_type<'cx>(
decl.unsafety.print_with_space(),
print_abi_with_space(decl.abi)
)?;
- primitive_link(f, PrimitiveType::Fn, "fn", cx)?;
+ primitive_link(f, PrimitiveType::Fn, format_args!("fn"), cx)?;
write!(f, "{}", decl.decl.print(cx))
}
}
clean::Tuple(ref typs) => {
match &typs[..] {
- &[] => primitive_link(f, PrimitiveType::Unit, "()", cx),
+ &[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx),
[one] => {
if let clean::Generic(name) = one {
- primitive_link(f, PrimitiveType::Tuple, &format!("({name},)"), cx)
+ primitive_link(f, PrimitiveType::Tuple, format_args!("({name},)"), cx)
} else {
write!(f, "(")?;
// Carry `f.alternate()` into this display w/o branching manually.
@@ -1029,7 +1030,10 @@ fn fmt_type<'cx>(
primitive_link(
f,
PrimitiveType::Tuple,
- &format!("({})", generic_names.iter().map(|s| s.as_str()).join(", ")),
+ format_args!(
+ "({})",
+ generic_names.iter().map(|s| s.as_str()).join(", ")
+ ),
cx,
)
} else {
@@ -1048,7 +1052,7 @@ fn fmt_type<'cx>(
}
clean::Slice(ref t) => match **t {
clean::Generic(name) => {
- primitive_link(f, PrimitiveType::Slice, &format!("[{name}]"), cx)
+ primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx)
}
_ => {
write!(f, "[")?;
@@ -1060,7 +1064,7 @@ fn fmt_type<'cx>(
clean::Generic(name) if !f.alternate() => primitive_link(
f,
PrimitiveType::Array,
- &format!("[{name}; {n}]", n = Escape(n)),
+ format_args!("[{name}; {n}]", n = Escape(n)),
cx,
),
_ => {
@@ -1070,7 +1074,12 @@ fn fmt_type<'cx>(
write!(f, "; {n}")?;
} else {
write!(f, "; ")?;
- primitive_link(f, PrimitiveType::Array, &format!("{n}", n = Escape(n)), cx)?;
+ primitive_link(
+ f,
+ PrimitiveType::Array,
+ format_args!("{n}", n = Escape(n)),
+ cx,
+ )?;
}
write!(f, "]")
}
@@ -1082,22 +1091,32 @@ fn fmt_type<'cx>(
};
if matches!(**t, clean::Generic(_)) || t.is_assoc_ty() {
- let text = if f.alternate() {
- format!("*{m} {ty:#}", ty = t.print(cx))
+ let ty = t.print(cx);
+ if f.alternate() {
+ primitive_link(
+ f,
+ clean::PrimitiveType::RawPointer,
+ format_args!("*{m} {ty:#}"),
+ cx,
+ )
} else {
- format!("*{m} {ty}", ty = t.print(cx))
- };
- primitive_link(f, clean::PrimitiveType::RawPointer, &text, cx)
+ primitive_link(
+ f,
+ clean::PrimitiveType::RawPointer,
+ format_args!("*{m} {ty}"),
+ cx,
+ )
+ }
} else {
- primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{m} "), cx)?;
+ primitive_link(f, clean::PrimitiveType::RawPointer, format_args!("*{m} "), cx)?;
fmt::Display::fmt(&t.print(cx), f)
}
}
clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => {
- let lt = match l {
- Some(l) => format!("{} ", l.print()),
- _ => String::new(),
- };
+ let lt = display_fn(|f| match l {
+ Some(l) => write!(f, "{} ", l.print()),
+ _ => Ok(()),
+ });
let m = mutability.print_with_space();
let amp = if f.alternate() { "&" } else { "&amp;" };
@@ -1105,7 +1124,7 @@ fn fmt_type<'cx>(
return primitive_link(
f,
PrimitiveType::Reference,
- &format!("{amp}{lt}{m}{name}"),
+ format_args!("{amp}{lt}{m}{name}"),
cx,
);
}
@@ -1255,7 +1274,7 @@ impl clean::Impl {
{
// Hardcoded anchor library/core/src/primitive_docs.rs
// Link should match `# Trait implementations`
- primitive_link_fragment(f, PrimitiveType::Tuple, &format!("({name}₁, {name}₂, …, {name}ₙ)"), "#trait-implementations-1", cx)?;
+ primitive_link_fragment(f, PrimitiveType::Tuple, format_args!("({name}₁, {name}₂, …, {name}ₙ)"), "#trait-implementations-1", cx)?;
} else if let clean::BareFunction(bare_fn) = &self.for_ &&
let [clean::Argument { type_: clean::Type::Generic(name), .. }] = &bare_fn.decl.inputs.values[..] &&
(self.kind.is_fake_variadic() || self.kind.is_auto())
@@ -1282,7 +1301,7 @@ impl clean::Impl {
} else {
""
};
- primitive_link_fragment(f, PrimitiveType::Tuple, &format!("fn ({name}₁, {name}₂, …, {name}ₙ{ellipsis})"), "#trait-implementations-1", cx)?;
+ primitive_link_fragment(f, PrimitiveType::Tuple, format_args!("fn ({name}₁, {name}₂, …, {name}ₙ{ellipsis})"), "#trait-implementations-1", cx)?;
// Write output.
if !bare_fn.decl.output.is_unit() {
write!(f, " -> ")?;
@@ -1666,7 +1685,12 @@ impl clean::ImportSource {
}
let name = self.path.last();
if let hir::def::Res::PrimTy(p) = self.path.res {
- primitive_link(f, PrimitiveType::from(p), name.as_str(), cx)?;
+ primitive_link(
+ f,
+ PrimitiveType::from(p),
+ format_args!("{}", name.as_str()),
+ cx,
+ )?;
} else {
f.write_str(name.as_str())?;
}
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 8c5871d91..d4b4db0f3 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -17,6 +17,7 @@ pub(crate) struct Layout {
pub(crate) external_html: ExternalHtml,
pub(crate) default_settings: FxHashMap<String, String>,
pub(crate) krate: String,
+ pub(crate) krate_version: String,
/// The given user css file which allow to customize the generated
/// documentation theme.
pub(crate) css_file_extension: Option<PathBuf>,
@@ -31,6 +32,7 @@ pub(crate) struct Page<'a> {
pub(crate) static_root_path: Option<&'a str>,
pub(crate) description: &'a str,
pub(crate) resource_suffix: &'a str,
+ pub(crate) rust_logo: bool,
}
impl<'a> Page<'a> {
@@ -54,9 +56,19 @@ struct PageLayout<'a> {
themes: Vec<String>,
sidebar: String,
content: String,
- krate_with_trailing_slash: String,
rust_channel: &'static str,
pub(crate) rustdoc_version: &'a str,
+ // same as layout.krate, except on top-level pages like
+ // Settings, Help, All Crates, and About Scraped Examples,
+ // where these things instead give Rustdoc name and version.
+ //
+ // These are separate from the variables used for the search
+ // engine, because "Rustdoc" isn't necessarily a crate in
+ // the current workspace.
+ display_krate: &'a str,
+ display_krate_with_trailing_slash: String,
+ display_krate_version_number: &'a str,
+ display_krate_version_extra: &'a str,
}
pub(crate) fn render<T: Print, S: Print>(
@@ -66,12 +78,26 @@ pub(crate) fn render<T: Print, S: Print>(
t: T,
style_files: &[StylePath],
) -> String {
+ let rustdoc_version = rustc_interface::util::version_str!().unwrap_or("unknown version");
+
+ let (display_krate, display_krate_version, display_krate_with_trailing_slash) =
+ if page.root_path == "./" {
+ // top level pages use Rust branding
+ ("Rustdoc", rustdoc_version, String::new())
+ } else {
+ let display_krate_with_trailing_slash =
+ ensure_trailing_slash(&layout.krate).to_string();
+ (&layout.krate[..], &layout.krate_version[..], display_krate_with_trailing_slash)
+ };
let static_root_path = page.get_static_root_path();
- let krate_with_trailing_slash = ensure_trailing_slash(&layout.krate).to_string();
+
+ // bootstrap passes in parts of the version separated by tabs, but other stuff might use spaces
+ let (display_krate_version_number, display_krate_version_extra) =
+ display_krate_version.split_once([' ', '\t']).unwrap_or((display_krate_version, ""));
+
let mut themes: Vec<String> = style_files.iter().map(|s| s.basename().unwrap()).collect();
themes.sort();
- let rustdoc_version = rustc_interface::util::version_str!().unwrap_or("unknown version");
let content = Buffer::html().to_display(t); // Note: This must happen before making the sidebar.
let sidebar = Buffer::html().to_display(sidebar);
PageLayout {
@@ -82,7 +108,10 @@ pub(crate) fn render<T: Print, S: Print>(
themes,
sidebar,
content,
- krate_with_trailing_slash,
+ display_krate,
+ display_krate_with_trailing_slash,
+ display_krate_version_number,
+ display_krate_version_extra,
rust_channel: *crate::clean::utils::DOC_CHANNEL,
rustdoc_version,
}
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index d24e6e5fa..2807dfed0 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -1750,7 +1750,7 @@ pub(crate) fn markdown_links<'md, R>(
}
// do not actually include braces in the span
let range = (open_brace + 1)..close_brace;
- MarkdownLinkRange::Destination(range.clone())
+ MarkdownLinkRange::Destination(range)
};
let span_for_offset_forward = |span: Range<usize>, open: u8, close: u8| {
@@ -1786,7 +1786,7 @@ pub(crate) fn markdown_links<'md, R>(
}
// do not actually include braces in the span
let range = (open_brace + 1)..close_brace;
- MarkdownLinkRange::Destination(range.clone())
+ MarkdownLinkRange::Destination(range)
};
let mut broken_link_callback = |link: BrokenLink<'md>| Some((link.reference, "".into()));
@@ -2024,6 +2024,7 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
map.insert("required-associated-consts".into(), 1);
map.insert("required-methods".into(), 1);
map.insert("provided-methods".into(), 1);
+ map.insert("object-safety".into(), 1);
map.insert("implementors".into(), 1);
map.insert("synthetic-implementors".into(), 1);
map.insert("implementations-list".into(), 1);
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 97714afaa..50777134d 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -7,13 +7,10 @@ use std::sync::mpsc::{channel, Receiver};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE};
-use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
-use rustc_span::def_id::DefId;
use rustc_span::edition::Edition;
-use rustc_span::source_map::FileName;
-use rustc_span::{sym, Symbol};
+use rustc_span::{sym, FileName, Symbol};
use super::print_item::{full_path, item_path, print_item};
use super::search_index::build_index;
@@ -24,13 +21,14 @@ use super::{
sidebar::{sidebar_module_like, Sidebar},
AllTypes, LinkFromSrc, StylePath,
};
-use crate::clean::{self, types::ExternalLocation, ExternalCrate, TypeAliasItem};
+use crate::clean::utils::has_doc_flag;
+use crate::clean::{self, types::ExternalLocation, ExternalCrate};
use crate::config::{ModuleSorting, RenderOptions};
use crate::docfs::{DocFS, PathError};
use crate::error::Error;
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
-use crate::formats::{self, FormatRenderer};
+use crate::formats::FormatRenderer;
use crate::html::escape::Escape;
use crate::html::format::{join_with_double_colon, Buffer};
use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
@@ -149,53 +147,6 @@ impl SharedContext<'_> {
pub(crate) fn edition(&self) -> Edition {
self.tcx.sess.edition()
}
-
- /// Returns a list of impls on the given type, and, if it's a type alias,
- /// other types that it aliases.
- pub(crate) fn all_impls_for_item<'a>(
- &'a self,
- it: &clean::Item,
- did: DefId,
- ) -> Vec<&'a formats::Impl> {
- let tcx = self.tcx;
- let cache = &self.cache;
- let mut saw_impls = FxHashSet::default();
- let mut v: Vec<&formats::Impl> = cache
- .impls
- .get(&did)
- .map(Vec::as_slice)
- .unwrap_or(&[])
- .iter()
- .filter(|i| saw_impls.insert(i.def_id()))
- .collect();
- if let TypeAliasItem(ait) = &*it.kind &&
- let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) &&
- let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) &&
- let Some(av) = cache.impls.get(&aliased_type_defid) &&
- let Some(alias_def_id) = it.item_id.as_def_id()
- {
- // This branch of the compiler compares types structually, but does
- // not check trait bounds. That's probably fine, since type aliases
- // don't normally constrain on them anyway.
- // https://github.com/rust-lang/rust/issues/21903
- //
- // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification.
- // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress.
- let aliased_ty = tcx.type_of(alias_def_id).skip_binder();
- let reject_cx = DeepRejectCtxt {
- treat_obligation_params: TreatParams::AsCandidateKey,
- };
- v.extend(av.iter().filter(|impl_| {
- if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() {
- reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder())
- && saw_impls.insert(impl_def_id)
- } else {
- false
- }
- }));
- }
- v
- }
}
impl<'tcx> Context<'tcx> {
@@ -277,6 +228,7 @@ impl<'tcx> Context<'tcx> {
title: &title,
description: &desc,
resource_suffix: &clone_shared.resource_suffix,
+ rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
};
let mut page_buffer = Buffer::html();
print_item(self, it, &mut page_buffer, &page);
@@ -528,12 +480,14 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
if let Some(url) = playground_url {
playground = Some(markdown::Playground { crate_name: Some(krate.name(tcx)), url });
}
+ let krate_version = cache.crate_version.as_deref().unwrap_or_default();
let mut layout = layout::Layout {
logo: String::new(),
favicon: String::new(),
external_html,
default_settings,
krate: krate.name(tcx).to_string(),
+ krate_version: krate_version.to_string(),
css_file_extension: extension_css,
scrape_examples_extension: !call_locations.is_empty(),
};
@@ -658,21 +612,22 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
let shared = Rc::clone(&self.shared);
let mut page = layout::Page {
title: "List of all items in this crate",
- css_class: "mod",
+ css_class: "mod sys",
root_path: "../",
static_root_path: shared.static_root_path.as_deref(),
description: "List of all items in this crate",
resource_suffix: &shared.resource_suffix,
+ rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
};
let all = shared.all.replace(AllTypes::new());
let mut sidebar = Buffer::html();
let blocks = sidebar_module_like(all.item_sections());
let bar = Sidebar {
- title_prefix: "Crate ",
- title: crate_name.as_str(),
+ title_prefix: "",
+ title: "",
is_crate: false,
- version: "",
+ is_mod: false,
blocks: vec![blocks],
path: String::new(),
};
@@ -689,9 +644,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
shared.fs.write(final_file, v)?;
// Generating settings page.
- page.title = "Rustdoc settings";
+ page.title = "Settings";
page.description = "Settings of Rustdoc";
page.root_path = "./";
+ page.rust_logo = true;
let sidebar = "<h2 class=\"location\">Settings</h2><div class=\"sidebar-elems\"></div>";
let v = layout::render(
@@ -739,9 +695,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
shared.fs.write(settings_file, v)?;
// Generating help page.
- page.title = "Rustdoc help";
+ page.title = "Help";
page.description = "Documentation for Rustdoc";
page.root_path = "./";
+ page.rust_logo = true;
let sidebar = "<h2 class=\"location\">Help</h2><div class=\"sidebar-elems\"></div>";
let v = layout::render(
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 3e671a64b..c52fa01bd 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -48,13 +48,13 @@ use std::str;
use std::string::ToString;
use askama::Template;
-use rustc_attr::{ConstStability, Deprecation, StabilityLevel};
+use rustc_attr::{ConstStability, DeprecatedSince, Deprecation, StabilityLevel, StableSince};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_hir::Mutability;
-use rustc_middle::middle::stability;
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_session::RustcVersion;
use rustc_span::{
symbol::{sym, Symbol},
BytePos, FileName, RealFileName,
@@ -102,6 +102,7 @@ pub(crate) struct IndexItem {
pub(crate) desc: String,
pub(crate) parent: Option<DefId>,
pub(crate) parent_idx: Option<isize>,
+ pub(crate) impl_id: Option<DefId>,
pub(crate) search_type: Option<IndexItemFunctionType>,
pub(crate) aliases: Box<[Symbol]>,
pub(crate) deprecation: Option<Deprecation>,
@@ -615,24 +616,22 @@ fn short_item_info(
) -> Vec<ShortItemInfo> {
let mut extra_info = vec![];
- if let Some(depr @ Deprecation { note, since, is_since_rustc_version: _, suggestion: _ }) =
- item.deprecation(cx.tcx())
- {
+ if let Some(depr @ Deprecation { note, since, suggestion: _ }) = item.deprecation(cx.tcx()) {
// We display deprecation messages for #[deprecated], but only display
// the future-deprecation messages for rustc versions.
- let mut message = if let Some(since) = since {
- let since = since.as_str();
- if !stability::deprecation_in_effect(&depr) {
- if since == "TBD" {
- String::from("Deprecating in a future Rust version")
+ let mut message = match since {
+ DeprecatedSince::RustcVersion(version) => {
+ if depr.is_in_effect() {
+ format!("Deprecated since {version}")
} else {
- format!("Deprecating in {}", Escape(since))
+ format!("Deprecating in {version}")
}
- } else {
- format!("Deprecated since {}", Escape(since))
}
- } else {
- String::from("Deprecated")
+ DeprecatedSince::Future => String::from("Deprecating in a future Rust version"),
+ DeprecatedSince::NonStandard(since) => {
+ format!("Deprecated since {}", Escape(since.as_str()))
+ }
+ DeprecatedSince::Unspecified | DeprecatedSince::Err => String::from("Deprecated"),
};
if let Some(note) = note {
@@ -867,10 +866,10 @@ fn assoc_method(
let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
header_len += 4;
let indent_str = " ";
- write!(w, "{}", render_attributes_in_pre(meth, indent_str, tcx));
+ write!(w, "{}", render_attributes_in_pre(meth, indent_str, cx));
(4, indent_str, Ending::NoNewline)
} else {
- render_attributes_in_code(w, meth, tcx);
+ render_attributes_in_code(w, meth, cx);
(0, "", Ending::Newline)
};
w.reserve(header_len + "<a href=\"\" class=\"fn\">{".len() + "</a>".len());
@@ -910,13 +909,17 @@ fn assoc_method(
/// consequence of the above rules.
fn render_stability_since_raw_with_extra(
w: &mut Buffer,
- ver: Option<Symbol>,
+ ver: Option<StableSince>,
const_stability: Option<ConstStability>,
- containing_ver: Option<Symbol>,
- containing_const_ver: Option<Symbol>,
+ containing_ver: Option<StableSince>,
+ containing_const_ver: Option<StableSince>,
extra_class: &str,
) -> bool {
- let stable_version = ver.filter(|inner| !inner.is_empty() && Some(*inner) != containing_ver);
+ let stable_version = if ver != containing_ver && let Some(ver) = &ver {
+ since_to_string(ver)
+ } else {
+ None
+ };
let mut title = String::new();
let mut stability = String::new();
@@ -930,7 +933,8 @@ fn render_stability_since_raw_with_extra(
Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. })
if Some(since) != containing_const_ver =>
{
- Some((format!("const since {since}"), format!("const: {since}")))
+ since_to_string(&since)
+ .map(|since| (format!("const since {since}"), format!("const: {since}")))
}
Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => {
let unstable = if let Some(n) = issue {
@@ -970,13 +974,21 @@ fn render_stability_since_raw_with_extra(
!stability.is_empty()
}
+fn since_to_string(since: &StableSince) -> Option<String> {
+ match since {
+ StableSince::Version(since) => Some(since.to_string()),
+ StableSince::Current => Some(RustcVersion::CURRENT.to_string()),
+ StableSince::Err => None,
+ }
+}
+
#[inline]
fn render_stability_since_raw(
w: &mut Buffer,
- ver: Option<Symbol>,
+ ver: Option<StableSince>,
const_stability: Option<ConstStability>,
- containing_ver: Option<Symbol>,
- containing_const_ver: Option<Symbol>,
+ containing_ver: Option<StableSince>,
+ containing_const_ver: Option<StableSince>,
) -> bool {
render_stability_since_raw_with_extra(
w,
@@ -1046,13 +1058,13 @@ fn render_assoc_item(
// When an attribute is rendered inside a `<pre>` tag, it is formatted using
// a whitespace prefix and newline.
-fn render_attributes_in_pre<'a, 'b: 'a>(
+fn render_attributes_in_pre<'a, 'tcx: 'a>(
it: &'a clean::Item,
prefix: &'a str,
- tcx: TyCtxt<'b>,
-) -> impl fmt::Display + Captures<'a> + Captures<'b> {
+ cx: &'a Context<'tcx>,
+) -> impl fmt::Display + Captures<'a> + Captures<'tcx> {
crate::html::format::display_fn(move |f| {
- for a in it.attributes(tcx, false) {
+ for a in it.attributes(cx.tcx(), cx.cache(), false) {
writeln!(f, "{prefix}{a}")?;
}
Ok(())
@@ -1061,8 +1073,8 @@ fn render_attributes_in_pre<'a, 'b: 'a>(
// When an attribute is rendered inside a <code> tag, it is formatted using
// a div to produce a newline after it.
-fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, tcx: TyCtxt<'_>) {
- for attr in it.attributes(tcx, false) {
+fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) {
+ for attr in it.attributes(cx.tcx(), cx.cache(), false) {
write!(w, "<div class=\"code-attribute\">{attr}</div>").unwrap();
}
}
@@ -1131,13 +1143,13 @@ pub(crate) fn render_all_impls(
fn render_assoc_items<'a, 'cx: 'a>(
cx: &'a mut Context<'cx>,
containing_item: &'a clean::Item,
- did: DefId,
+ it: DefId,
what: AssocItemRender<'a>,
) -> impl fmt::Display + 'a + Captures<'cx> {
let mut derefs = DefIdSet::default();
- derefs.insert(did);
+ derefs.insert(it);
display_fn(move |f| {
- render_assoc_items_inner(f, cx, containing_item, did, what, &mut derefs);
+ render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
Ok(())
})
}
@@ -1146,17 +1158,15 @@ fn render_assoc_items_inner(
mut w: &mut dyn fmt::Write,
cx: &mut Context<'_>,
containing_item: &clean::Item,
- did: DefId,
+ it: DefId,
what: AssocItemRender<'_>,
derefs: &mut DefIdSet,
) {
info!("Documenting associated items of {:?}", containing_item.name);
let shared = Rc::clone(&cx.shared);
- let v = shared.all_impls_for_item(containing_item, did);
- let v = v.as_slice();
- let (non_trait, traits): (Vec<&Impl>, _) =
- v.iter().partition(|i| i.inner_impl().trait_.is_none());
- let mut saw_impls = FxHashSet::default();
+ let cache = &shared.cache;
+ let Some(v) = cache.impls.get(&it) else { return };
+ let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
if !non_trait.is_empty() {
let mut tmp_buf = Buffer::html();
let (render_mode, id, class_html) = match what {
@@ -1185,9 +1195,6 @@ fn render_assoc_items_inner(
};
let mut impls_buf = Buffer::html();
for i in &non_trait {
- if !saw_impls.insert(i.def_id()) {
- continue;
- }
render_impl(
&mut impls_buf,
cx,
@@ -1233,10 +1240,8 @@ fn render_assoc_items_inner(
let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
- let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete
- .into_iter()
- .filter(|t| saw_impls.insert(t.def_id()))
- .partition(|t| t.inner_impl().kind.is_blanket());
+ let (blanket_impl, concrete): (Vec<&Impl>, _) =
+ concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
}
@@ -1877,7 +1882,7 @@ pub(crate) fn render_impl_summary(
aliases: &[String],
) {
let inner_impl = i.inner_impl();
- let id = cx.derive_id(get_id_for_impl(&inner_impl.for_, inner_impl.trait_.as_ref(), cx));
+ let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
let aliases = if aliases.is_empty() {
String::new()
} else {
@@ -1994,21 +1999,35 @@ pub(crate) fn small_url_encode(s: String) -> String {
}
}
-fn get_id_for_impl(for_: &clean::Type, trait_: Option<&clean::Path>, cx: &Context<'_>) -> String {
- match trait_ {
- Some(t) => small_url_encode(format!("impl-{:#}-for-{:#}", t.print(cx), for_.print(cx))),
- None => small_url_encode(format!("impl-{:#}", for_.print(cx))),
- }
+fn get_id_for_impl<'tcx>(tcx: TyCtxt<'tcx>, impl_id: ItemId) -> String {
+ use rustc_middle::ty::print::with_forced_trimmed_paths;
+ let (type_, trait_) = match impl_id {
+ ItemId::Auto { trait_, for_ } => {
+ let ty = tcx.type_of(for_).skip_binder();
+ (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
+ }
+ ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
+ match tcx.impl_subject(impl_id).skip_binder() {
+ ty::ImplSubject::Trait(trait_ref) => {
+ (trait_ref.args[0].expect_ty(), Some(trait_ref))
+ }
+ ty::ImplSubject::Inherent(ty) => (ty, None),
+ }
+ }
+ };
+ with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
+ format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
+ } else {
+ format!("impl-{type_}")
+ }))
}
fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
match *item.kind {
- clean::ItemKind::ImplItem(ref i) => {
- i.trait_.as_ref().map(|trait_| {
- // Alternative format produces no URLs,
- // so this parameter does nothing.
- (format!("{:#}", i.for_.print(cx)), get_id_for_impl(&i.for_, Some(trait_), cx))
- })
+ clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
+ // Alternative format produces no URLs,
+ // so this parameter does nothing.
+ Some((format!("{:#}", i.for_.print(cx)), get_id_for_impl(cx.tcx(), item.item_id)))
}
_ => None,
}
@@ -2079,6 +2098,7 @@ impl ItemSection {
const ALL: &'static [Self] = {
use ItemSection::*;
// NOTE: The order here affects the order in the UI.
+ // Keep this synchronized with addSidebarItems in main.js
&[
Reexports,
PrimitiveTypes,
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index c6751c958..d226701ba 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -5,10 +5,12 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::CtorKind;
use rustc_hir::def_id::DefId;
-use rustc_middle::middle::stability;
+use rustc_index::IndexVec;
+use rustc_middle::query::Key;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_target::abi::VariantIdx;
use std::cell::{RefCell, RefMut};
use std::cmp::Ordering;
use std::fmt;
@@ -117,8 +119,7 @@ macro_rules! item_template_methods {
fn render_attributes_in_pre<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
display_fn(move |f| {
let (item, cx) = self.item_and_mut_cx();
- let tcx = cx.tcx();
- let v = render_attributes_in_pre(item, "", tcx);
+ let v = render_attributes_in_pre(item, "", &cx);
write!(f, "{v}")
})
}
@@ -589,11 +590,7 @@ fn extra_info_tags<'a, 'tcx: 'a>(
// The trailing space after each tag is to space it properly against the rest of the docs.
if let Some(depr) = &item.deprecation(tcx) {
- let message = if stability::deprecation_in_effect(depr) {
- "Deprecated"
- } else {
- "Deprecation planned"
- };
+ let message = if depr.is_in_effect() { "Deprecated" } else { "Deprecation planned" };
write!(f, "{}", tag_html("deprecated", "", message))?;
}
@@ -656,7 +653,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
w,
"{attrs}{vis}{constness}{asyncness}{unsafety}{abi}fn \
{name}{generics}{decl}{notable_traits}{where_clause}",
- attrs = render_attributes_in_pre(it, "", tcx),
+ attrs = render_attributes_in_pre(it, "", cx),
vis = visibility,
constness = constness,
asyncness = asyncness,
@@ -691,7 +688,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
write!(
w,
"{attrs}{vis}{unsafety}{is_auto}trait {name}{generics}{bounds}",
- attrs = render_attributes_in_pre(it, "", tcx),
+ attrs = render_attributes_in_pre(it, "", cx),
vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
unsafety = t.unsafety(tcx).print_with_space(),
is_auto = if t.is_auto(tcx) { "auto " } else { "" },
@@ -957,6 +954,21 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
let cloned_shared = Rc::clone(&cx.shared);
let cache = &cloned_shared.cache;
let mut extern_crates = FxHashSet::default();
+
+ if !t.is_object_safe(cx.tcx()) {
+ write_small_section_header(
+ w,
+ "object-safety",
+ "Object Safety",
+ &format!(
+ "<div class=\"object-safety-info\">This trait is <b>not</b> \
+ <a href=\"{base}/reference/items/traits.html#object-safety\">\
+ object safe</a>.</div>",
+ base = crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL
+ ),
+ );
+ }
+
if let Some(implementors) = cache.implementors.get(&it.item_id.expect_def_id()) {
// The DefId is for the first Type found with that name. The bool is
// if any Types with the same name but different DefId have been found.
@@ -979,7 +991,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}
}
- let (local, foreign) =
+ let (local, mut foreign) =
implementors.iter().partition::<Vec<_>, _>(|i| i.is_on_local_type(cx));
let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) =
@@ -987,6 +999,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
synthetic.sort_by_cached_key(|i| ImplString::new(i, cx));
concrete.sort_by_cached_key(|i| ImplString::new(i, cx));
+ foreign.sort_by_cached_key(|i| ImplString::new(i, cx));
if !foreign.is_empty() {
write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", "");
@@ -1064,6 +1077,8 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}
}
+ // [RUSTDOCIMPL] trait.impl
+ //
// Include implementors in crates that depend on the current crate.
//
// This is complicated by the way rustdoc is invoked, which is basically
@@ -1099,7 +1114,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// ```
//
// Basically, we want `C::Baz` and `A::Foo` to show the same set of
- // impls, which is easier if they both treat `/implementors/A/trait.Foo.js`
+ // impls, which is easier if they both treat `/trait.impl/A/trait.Foo.js`
// as the Single Source of Truth.
//
// We also want the `impl Baz for Quux` to be written to
@@ -1108,7 +1123,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// because that'll load faster, and it's better for SEO. And we don't want
// the same impl to show up twice on the same page.
//
- // To make this work, the implementors JS file has a structure kinda
+ // To make this work, the trait.impl/A/trait.Foo.js JS file has a structure kinda
// like this:
//
// ```js
@@ -1125,7 +1140,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// So C's HTML will have something like this:
//
// ```html
- // <script src="/implementors/A/trait.Foo.js"
+ // <script src="/trait.impl/A/trait.Foo.js"
// data-ignore-extern-crates="A,B" async></script>
// ```
//
@@ -1135,7 +1150,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// [JSONP]: https://en.wikipedia.org/wiki/JSONP
let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..")
.take(cx.current.len())
- .chain(std::iter::once("implementors"))
+ .chain(std::iter::once("trait.impl"))
.collect();
if let Some(did) = it.item_id.as_def_id() &&
let get_extern = { || cache.external_paths.get(&did).map(|s| &s.0) } &&
@@ -1170,7 +1185,7 @@ fn item_trait_alias(
write!(
w,
"{attrs}trait {name}{generics}{where_b} = {bounds};",
- attrs = render_attributes_in_pre(it, "", cx.tcx()),
+ attrs = render_attributes_in_pre(it, "", cx),
name = it.name.unwrap(),
generics = t.generics.print(cx),
where_b = print_where_clause(&t.generics, cx, 0, Ending::Newline),
@@ -1198,7 +1213,7 @@ fn item_opaque_ty(
write!(
w,
"{attrs}type {name}{generics}{where_clause} = impl {bounds};",
- attrs = render_attributes_in_pre(it, "", cx.tcx()),
+ attrs = render_attributes_in_pre(it, "", cx),
name = it.name.unwrap(),
generics = t.generics.print(cx),
where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline),
@@ -1223,7 +1238,7 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
write!(
w,
"{attrs}{vis}type {name}{generics}{where_clause} = {type_};",
- attrs = render_attributes_in_pre(it, "", cx.tcx()),
+ attrs = render_attributes_in_pre(it, "", cx),
vis = visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx),
name = it.name.unwrap(),
generics = t.generics.print(cx),
@@ -1247,6 +1262,9 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
match inner_type {
clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
let variants_iter = || variants.iter().filter(|i| !i.is_stripped());
+ let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
+ let enum_def_id = ty.ty_adt_id().unwrap();
+
wrap_item(w, |w| {
let variants_len = variants.len();
let variants_count = variants_iter().count();
@@ -1257,13 +1275,14 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
w,
cx,
Some(&t.generics),
- variants_iter(),
+ &variants,
variants_count,
has_stripped_entries,
*is_non_exhaustive,
+ enum_def_id,
)
});
- item_variants(w, cx, it, variants_iter());
+ item_variants(w, cx, it, &variants, enum_def_id);
}
clean::TypeAliasInnerType::Union { fields } => {
wrap_item(w, |w| {
@@ -1313,6 +1332,102 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
// we need #14072 to make sense of the generics.
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
write!(w, "{}", document_type_layout(cx, def_id));
+
+ // [RUSTDOCIMPL] type.impl
+ //
+ // Include type definitions from the alias target type.
+ //
+ // Earlier versions of this code worked by having `render_assoc_items`
+ // include this data directly. That generates *O*`(types*impls)` of HTML
+ // text, and some real crates have a lot of types and impls.
+ //
+ // To create the same UX without generating half a gigabyte of HTML for a
+ // crate that only contains 20 megabytes of actual documentation[^115718],
+ // rustdoc stashes these type-alias-inlined docs in a [JSONP]
+ // "database-lite". The file itself is generated in `write_shared.rs`,
+ // and hooks into functions provided by `main.js`.
+ //
+ // The format of `trait.impl` and `type.impl` JS files are superficially
+ // similar. Each line, except the JSONP wrapper itself, belongs to a crate,
+ // and they are otherwise separate (rustdoc should be idempotent). The
+ // "meat" of the file is HTML strings, so the frontend code is very simple.
+ // Links are relative to the doc root, though, so the frontend needs to fix
+ // that up, and inlined docs can reuse these files.
+ //
+ // However, there are a few differences, caused by the sophisticated
+ // features that type aliases have. Consider this crate graph:
+ //
+ // ```text
+ // ---------------------------------
+ // | crate A: struct Foo<T> |
+ // | type Bar = Foo<i32> |
+ // | impl X for Foo<i8> |
+ // | impl Y for Foo<i32> |
+ // ---------------------------------
+ // |
+ // ----------------------------------
+ // | crate B: type Baz = A::Foo<i8> |
+ // | type Xyy = A::Foo<i8> |
+ // | impl Z for Xyy |
+ // ----------------------------------
+ // ```
+ //
+ // The type.impl/A/struct.Foo.js JS file has a structure kinda like this:
+ //
+ // ```js
+ // JSONP({
+ // "A": [["impl Y for Foo<i32>", "Y", "A::Bar"]],
+ // "B": [["impl X for Foo<i8>", "X", "B::Baz", "B::Xyy"], ["impl Z for Xyy", "Z", "B::Baz"]],
+ // });
+ // ```
+ //
+ // When the type.impl file is loaded, only the current crate's docs are
+ // actually used. The main reason to bundle them together is that there's
+ // enough duplication in them for DEFLATE to remove the redundancy.
+ //
+ // The contents of a crate are a list of impl blocks, themselves
+ // represented as lists. The first item in the sublist is the HTML block,
+ // the second item is the name of the trait (which goes in the sidebar),
+ // and all others are the names of type aliases that successfully match.
+ //
+ // This way:
+ //
+ // - There's no need to generate these files for types that have no aliases
+ // in the current crate. If a dependent crate makes a type alias, it'll
+ // take care of generating its own docs.
+ // - There's no need to reimplement parts of the type checker in
+ // JavaScript. The Rust backend does the checking, and includes its
+ // results in the file.
+ // - Docs defined directly on the type alias are dropped directly in the
+ // HTML by `render_assoc_items`, and are accessible without JavaScript.
+ // The JSONP file will not list impl items that are known to be part
+ // of the main HTML file already.
+ //
+ // [JSONP]: https://en.wikipedia.org/wiki/JSONP
+ // [^115718]: https://github.com/rust-lang/rust/issues/115718
+ let cloned_shared = Rc::clone(&cx.shared);
+ let cache = &cloned_shared.cache;
+ if let Some(target_did) = t.type_.def_id(cache) &&
+ let get_extern = { || cache.external_paths.get(&target_did) } &&
+ let Some(&(ref target_fqp, target_type)) = cache.paths.get(&target_did).or_else(get_extern) &&
+ target_type.is_adt() && // primitives cannot be inlined
+ let Some(self_did) = it.item_id.as_def_id() &&
+ let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) } &&
+ let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local)
+ {
+ let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..")
+ .take(cx.current.len())
+ .chain(std::iter::once("type.impl"))
+ .collect();
+ js_src_path.extend(target_fqp[..target_fqp.len() - 1].iter().copied());
+ js_src_path.push_fmt(format_args!("{target_type}.{}.js", target_fqp.last().unwrap()));
+ let self_path = self_fqp.iter().map(Symbol::as_str).collect::<Vec<&str>>().join("::");
+ write!(
+ w,
+ "<script src=\"{src}\" data-self-path=\"{self_path}\" async></script>",
+ src = js_src_path.finish(),
+ );
+ }
}
fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) {
@@ -1408,7 +1523,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
let tcx = cx.tcx();
let count_variants = e.variants().count();
wrap_item(w, |w| {
- render_attributes_in_code(w, it, tcx);
+ render_attributes_in_code(w, it, cx);
write!(
w,
"{}enum {}{}",
@@ -1416,36 +1531,90 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
it.name.unwrap(),
e.generics.print(cx),
);
+
render_enum_fields(
w,
cx,
Some(&e.generics),
- e.variants(),
+ &e.variants,
count_variants,
e.has_stripped_entries(),
it.is_non_exhaustive(),
+ it.def_id().unwrap(),
);
});
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
if count_variants != 0 {
- item_variants(w, cx, it, e.variants());
+ item_variants(w, cx, it, &e.variants, it.def_id().unwrap());
}
let def_id = it.item_id.expect_def_id();
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
write!(w, "{}", document_type_layout(cx, def_id));
}
-fn render_enum_fields<'a>(
+/// It'll return false if any variant is not a C-like variant. Otherwise it'll return true if at
+/// least one of them has an explicit discriminant or if the enum has `#[repr(C)]` or an integer
+/// `repr`.
+fn should_show_enum_discriminant(
+ cx: &Context<'_>,
+ enum_def_id: DefId,
+ variants: &IndexVec<VariantIdx, clean::Item>,
+) -> bool {
+ let mut has_variants_with_value = false;
+ for variant in variants {
+ if let clean::VariantItem(ref var) = *variant.kind &&
+ matches!(var.kind, clean::VariantKind::CLike)
+ {
+ has_variants_with_value |= var.discriminant.is_some();
+ } else {
+ return false;
+ }
+ }
+ if has_variants_with_value {
+ return true;
+ }
+ let repr = cx.tcx().adt_def(enum_def_id).repr();
+ repr.c() || repr.int.is_some()
+}
+
+fn display_c_like_variant(
+ w: &mut Buffer,
+ cx: &mut Context<'_>,
+ item: &clean::Item,
+ variant: &clean::Variant,
+ index: VariantIdx,
+ should_show_enum_discriminant: bool,
+ enum_def_id: DefId,
+) {
+ let name = item.name.unwrap();
+ if let Some(ref value) = variant.discriminant {
+ write!(w, "{} = {}", name.as_str(), value.value(cx.tcx(), true));
+ } else if should_show_enum_discriminant {
+ let adt_def = cx.tcx().adt_def(enum_def_id);
+ let discr = adt_def.discriminant_for_variant(cx.tcx(), index);
+ if discr.ty.is_signed() {
+ write!(w, "{} = {}", name.as_str(), discr.val as i128);
+ } else {
+ write!(w, "{} = {}", name.as_str(), discr.val);
+ }
+ } else {
+ w.write_str(name.as_str());
+ }
+}
+
+fn render_enum_fields(
mut w: &mut Buffer,
cx: &mut Context<'_>,
g: Option<&clean::Generics>,
- variants: impl Iterator<Item = &'a clean::Item>,
+ variants: &IndexVec<VariantIdx, clean::Item>,
count_variants: usize,
has_stripped_entries: bool,
is_non_exhaustive: bool,
+ enum_def_id: DefId,
) {
+ let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants);
if !g.is_some_and(|g| print_where_clause_and_check(w, g, cx)) {
// If there wasn't a `where` clause, we add a whitespace.
w.write_str(" ");
@@ -1461,15 +1630,24 @@ fn render_enum_fields<'a>(
toggle_open(&mut w, format_args!("{count_variants} variants"));
}
const TAB: &str = " ";
- for v in variants {
+ for (index, v) in variants.iter_enumerated() {
+ if v.is_stripped() {
+ continue;
+ }
w.write_str(TAB);
- let name = v.name.unwrap();
match *v.kind {
- // FIXME(#101337): Show discriminant
clean::VariantItem(ref var) => match var.kind {
- clean::VariantKind::CLike => w.write_str(name.as_str()),
+ clean::VariantKind::CLike => display_c_like_variant(
+ w,
+ cx,
+ v,
+ var,
+ index,
+ should_show_enum_discriminant,
+ enum_def_id,
+ ),
clean::VariantKind::Tuple(ref s) => {
- write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
+ write!(w, "{}({})", v.name.unwrap(), print_tuple_struct_fields(cx, s));
}
clean::VariantKind::Struct(ref s) => {
render_struct(w, v, None, None, &s.fields, TAB, false, cx);
@@ -1490,11 +1668,12 @@ fn render_enum_fields<'a>(
}
}
-fn item_variants<'a>(
+fn item_variants(
w: &mut Buffer,
cx: &mut Context<'_>,
it: &clean::Item,
- variants: impl Iterator<Item = &'a clean::Item>,
+ variants: &IndexVec<VariantIdx, clean::Item>,
+ enum_def_id: DefId,
) {
let tcx = cx.tcx();
write!(
@@ -1507,7 +1686,11 @@ fn item_variants<'a>(
document_non_exhaustive_header(it),
document_non_exhaustive(it)
);
- for variant in variants {
+ let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants);
+ for (index, variant) in variants.iter_enumerated() {
+ if variant.is_stripped() {
+ continue;
+ }
let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
write!(
w,
@@ -1522,7 +1705,22 @@ fn item_variants<'a>(
it.const_stable_since(tcx),
" rightside",
);
- write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
+ w.write_str("<h3 class=\"code-header\">");
+ if let clean::VariantItem(ref var) = *variant.kind &&
+ let clean::VariantKind::CLike = var.kind
+ {
+ display_c_like_variant(
+ w,
+ cx,
+ variant,
+ var,
+ index,
+ should_show_enum_discriminant,
+ enum_def_id,
+ );
+ } else {
+ w.write_str(variant.name.unwrap().as_str());
+ }
let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
@@ -1644,7 +1842,7 @@ fn item_primitive(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Ite
fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) {
wrap_item(w, |w| {
let tcx = cx.tcx();
- render_attributes_in_code(w, it, tcx);
+ render_attributes_in_code(w, it, cx);
write!(
w,
@@ -1693,7 +1891,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_item(w, |w| {
- render_attributes_in_code(w, it, cx.tcx());
+ render_attributes_in_code(w, it, cx);
render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx);
});
@@ -1753,7 +1951,7 @@ fn item_fields(
fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) {
wrap_item(w, |buffer| {
- render_attributes_in_code(buffer, it, cx.tcx());
+ render_attributes_in_code(buffer, it, cx);
write!(
buffer,
"{vis}static {mutability}{name}: {typ}",
@@ -1771,7 +1969,7 @@ fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item,
fn item_foreign_type(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item) {
wrap_item(w, |buffer| {
buffer.write_str("extern {\n").unwrap();
- render_attributes_in_code(buffer, it, cx.tcx());
+ render_attributes_in_code(buffer, it, cx);
write!(
buffer,
" {}type {};\n}}",
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index 78c443b22..af1dab594 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -12,7 +12,7 @@ use crate::formats::cache::{Cache, OrphanImplItem};
use crate::formats::item_type::ItemType;
use crate::html::format::join_with_double_colon;
use crate::html::markdown::short_markdown_summary;
-use crate::html::render::{IndexItem, IndexItemFunctionType, RenderType, RenderTypeId};
+use crate::html::render::{self, IndexItem, IndexItemFunctionType, RenderType, RenderTypeId};
/// Builds the search index from the collected metadata
pub(crate) fn build_index<'tcx>(
@@ -26,7 +26,8 @@ pub(crate) fn build_index<'tcx>(
// Attach all orphan items to the type's definition if the type
// has since been learned.
- for &OrphanImplItem { parent, ref item, ref impl_generics } in &cache.orphan_impl_items {
+ for &OrphanImplItem { impl_id, parent, ref item, ref impl_generics } in &cache.orphan_impl_items
+ {
if let Some((fqp, _)) = cache.paths.get(&parent) {
let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache));
cache.search_index.push(IndexItem {
@@ -36,6 +37,7 @@ pub(crate) fn build_index<'tcx>(
desc,
parent: Some(parent),
parent_idx: None,
+ impl_id,
search_type: get_function_type_for_search(item, tcx, impl_generics.as_ref(), cache),
aliases: item.attrs.get_doc_aliases(),
deprecation: item.deprecation(tcx),
@@ -222,6 +224,29 @@ pub(crate) fn build_index<'tcx>(
})
.collect();
+ // Find associated items that need disambiguators
+ let mut associated_item_duplicates = FxHashMap::<(isize, ItemType, Symbol), usize>::default();
+
+ for &item in &crate_items {
+ if item.impl_id.is_some() && let Some(parent_idx) = item.parent_idx {
+ let count = associated_item_duplicates
+ .entry((parent_idx, item.ty, item.name))
+ .or_insert(0);
+ *count += 1;
+ }
+ }
+
+ let associated_item_disambiguators = crate_items
+ .iter()
+ .enumerate()
+ .filter_map(|(index, item)| {
+ let impl_id = ItemId::DefId(item.impl_id?);
+ let parent_idx = item.parent_idx?;
+ let count = *associated_item_duplicates.get(&(parent_idx, item.ty, item.name))?;
+ if count > 1 { Some((index, render::get_id_for_impl(tcx, impl_id))) } else { None }
+ })
+ .collect::<Vec<_>>();
+
struct CrateData<'a> {
doc: String,
items: Vec<&'a IndexItem>,
@@ -230,6 +255,8 @@ pub(crate) fn build_index<'tcx>(
//
// To be noted: the `usize` elements are indexes to `items`.
aliases: &'a BTreeMap<String, Vec<usize>>,
+ // Used when a type has more than one impl with an associated item with the same name.
+ associated_item_disambiguators: &'a Vec<(usize, String)>,
}
struct Paths {
@@ -382,6 +409,7 @@ pub(crate) fn build_index<'tcx>(
crate_data.serialize_field("f", &functions)?;
crate_data.serialize_field("c", &deprecated)?;
crate_data.serialize_field("p", &paths)?;
+ crate_data.serialize_field("b", &self.associated_item_disambiguators)?;
if has_aliases {
crate_data.serialize_field("a", &self.aliases)?;
}
@@ -398,6 +426,7 @@ pub(crate) fn build_index<'tcx>(
items: crate_items,
paths: crate_paths,
aliases: &aliases,
+ associated_item_disambiguators: &associated_item_disambiguators,
})
.expect("failed serde conversion")
// All these `replace` calls are because we have to go through JS string for JSON content.
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index 76f63c6f6..ba4aaaff5 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -19,7 +19,7 @@ pub(super) struct Sidebar<'a> {
pub(super) title_prefix: &'static str,
pub(super) title: &'a str,
pub(super) is_crate: bool,
- pub(super) version: &'a str,
+ pub(super) is_mod: bool,
pub(super) blocks: Vec<LinkBlock<'a>>,
pub(super) path: String,
}
@@ -38,18 +38,19 @@ pub(crate) struct LinkBlock<'a> {
/// as well as the link to it, e.g. `#implementations`.
/// Will be rendered inside an `<h3>` tag
heading: Link<'a>,
+ class: &'static str,
links: Vec<Link<'a>>,
/// Render the heading even if there are no links
force_render: bool,
}
impl<'a> LinkBlock<'a> {
- pub fn new(heading: Link<'a>, links: Vec<Link<'a>>) -> Self {
- Self { heading, links, force_render: false }
+ pub fn new(heading: Link<'a>, class: &'static str, links: Vec<Link<'a>>) -> Self {
+ Self { heading, links, class, force_render: false }
}
- pub fn forced(heading: Link<'a>) -> Self {
- Self { heading, links: vec![], force_render: true }
+ pub fn forced(heading: Link<'a>, class: &'static str) -> Self {
+ Self { heading, links: vec![], class, force_render: true }
}
pub fn should_render(&self) -> bool {
@@ -99,12 +100,12 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
|| it.is_primitive()
|| it.is_union()
|| it.is_enum()
- || it.is_mod()
+ // crate title is displayed as part of logo lockup
+ || (it.is_mod() && !it.is_crate())
|| it.is_type_alias()
{
(
match *it.kind {
- clean::ModuleItem(..) if it.is_crate() => "Crate ",
clean::ModuleItem(..) => "Module ",
_ => "",
},
@@ -113,14 +114,22 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
} else {
("", "")
};
- let version =
- if it.is_crate() { cx.cache().crate_version.as_deref().unwrap_or_default() } else { "" };
- let path: String = if !it.is_mod() {
- cx.current.iter().map(|s| s.as_str()).intersperse("::").collect()
+ // need to show parent path header if:
+ // - it's a child module, instead of the crate root
+ // - there's a sidebar section for the item itself
+ //
+ // otherwise, the parent path header is redundant with the big crate
+ // branding area at the top of the sidebar
+ let sidebar_path =
+ if it.is_mod() { &cx.current[..cx.current.len() - 1] } else { &cx.current[..] };
+ let path: String = if sidebar_path.len() > 1 || !title.is_empty() {
+ let path = sidebar_path.iter().map(|s| s.as_str()).intersperse("::").collect();
+ if sidebar_path.len() == 1 { format!("crate {path}") } else { path }
} else {
"".into()
};
- let sidebar = Sidebar { title_prefix, title, is_crate: it.is_crate(), version, blocks, path };
+ let sidebar =
+ Sidebar { title_prefix, title, is_mod: it.is_mod(), is_crate: it.is_crate(), blocks, path };
sidebar.render_into(buffer).unwrap();
}
@@ -149,7 +158,7 @@ fn sidebar_struct<'a>(
};
let mut items = vec![];
if let Some(name) = field_name {
- items.push(LinkBlock::new(Link::new("fields", name), fields));
+ items.push(LinkBlock::new(Link::new("fields", name), "structfield", fields));
}
sidebar_assoc_items(cx, it, &mut items);
items
@@ -206,12 +215,23 @@ fn sidebar_trait<'a>(
("foreign-impls", "Implementations on Foreign Types", foreign_impls),
]
.into_iter()
- .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), items))
+ .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items))
.collect();
sidebar_assoc_items(cx, it, &mut blocks);
- blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors")));
+
+ if !t.is_object_safe(cx.tcx()) {
+ blocks.push(LinkBlock::forced(
+ Link::new("object-safety", "Object Safety"),
+ "object-safety-note",
+ ));
+ }
+
+ blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"), "impl"));
if t.is_auto(cx.tcx()) {
- blocks.push(LinkBlock::forced(Link::new("synthetic-implementors", "Auto Implementors")));
+ blocks.push(LinkBlock::forced(
+ Link::new("synthetic-implementors", "Auto Implementors"),
+ "impl-auto",
+ ));
}
blocks
}
@@ -237,7 +257,7 @@ fn sidebar_type_alias<'a>(
) -> Vec<LinkBlock<'a>> {
let mut items = vec![];
if let Some(inner_type) = &t.inner_type {
- items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type")));
+ items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type"), "type"));
match inner_type {
clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => {
let mut variants = variants
@@ -248,12 +268,12 @@ fn sidebar_type_alias<'a>(
.collect::<Vec<_>>();
variants.sort_unstable();
- items.push(LinkBlock::new(Link::new("variants", "Variants"), variants));
+ items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants));
}
clean::TypeAliasInnerType::Union { fields }
| clean::TypeAliasInnerType::Struct { ctor_kind: _, fields } => {
let fields = get_struct_fields_name(fields);
- items.push(LinkBlock::new(Link::new("fields", "Fields"), fields));
+ items.push(LinkBlock::new(Link::new("fields", "Fields"), "field", fields));
}
}
}
@@ -267,7 +287,7 @@ fn sidebar_union<'a>(
u: &'a clean::Union,
) -> Vec<LinkBlock<'a>> {
let fields = get_struct_fields_name(&u.fields);
- let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), fields)];
+ let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), "structfield", fields)];
sidebar_assoc_items(cx, it, &mut items);
items
}
@@ -279,12 +299,11 @@ fn sidebar_assoc_items<'a>(
links: &mut Vec<LinkBlock<'a>>,
) {
let did = it.item_id.expect_def_id();
- let v = cx.shared.all_impls_for_item(it, it.item_id.expect_def_id());
- let v = v.as_slice();
+ let cache = cx.cache();
let mut assoc_consts = Vec::new();
let mut methods = Vec::new();
- if !v.is_empty() {
+ if let Some(v) = cache.impls.get(&did) {
let mut used_links = FxHashSet::default();
let mut id_map = IdMap::new();
@@ -320,7 +339,7 @@ fn sidebar_assoc_items<'a>(
cx,
&mut deref_methods,
impl_,
- v.iter().copied(),
+ v,
&mut derefs,
&mut used_links,
);
@@ -333,12 +352,16 @@ fn sidebar_assoc_items<'a>(
sidebar_render_assoc_items(cx, &mut id_map, concrete, synthetic, blanket_impl)
} else {
- std::array::from_fn(|_| LinkBlock::new(Link::empty(), vec![]))
+ std::array::from_fn(|_| LinkBlock::new(Link::empty(), "", vec![]))
};
let mut blocks = vec![
- LinkBlock::new(Link::new("implementations", "Associated Constants"), assoc_consts),
- LinkBlock::new(Link::new("implementations", "Methods"), methods),
+ LinkBlock::new(
+ Link::new("implementations", "Associated Constants"),
+ "associatedconstant",
+ assoc_consts,
+ ),
+ LinkBlock::new(Link::new("implementations", "Methods"), "method", methods),
];
blocks.append(&mut deref_methods);
blocks.extend([concrete, synthetic, blanket]);
@@ -350,7 +373,7 @@ fn sidebar_deref_methods<'a>(
cx: &'a Context<'_>,
out: &mut Vec<LinkBlock<'a>>,
impl_: &Impl,
- v: impl Iterator<Item = &'a Impl>,
+ v: &[Impl],
derefs: &mut DefIdSet,
used_links: &mut FxHashSet<String>,
) {
@@ -375,7 +398,7 @@ fn sidebar_deref_methods<'a>(
// Avoid infinite cycles
return;
}
- let deref_mut = { v }.any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
+ let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
let inner_impl = target
.def_id(c)
.or_else(|| {
@@ -407,7 +430,7 @@ fn sidebar_deref_methods<'a>(
);
// We want links' order to be reproducible so we don't use unstable sort.
ret.sort();
- out.push(LinkBlock::new(Link::new(id, title), ret));
+ out.push(LinkBlock::new(Link::new(id, title), "deref-methods", ret));
}
}
@@ -426,7 +449,7 @@ fn sidebar_deref_methods<'a>(
cx,
out,
target_deref_impl,
- target_impls.iter(),
+ target_impls,
derefs,
used_links,
);
@@ -446,7 +469,7 @@ fn sidebar_enum<'a>(
.collect::<Vec<_>>();
variants.sort_unstable();
- let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), variants)];
+ let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), "variant", variants)];
sidebar_assoc_items(cx, it, &mut items);
items
}
@@ -460,7 +483,7 @@ pub(crate) fn sidebar_module_like(
.filter(|sec| item_sections_in_use.contains(sec))
.map(|sec| Link::new(sec.id(), sec.name()))
.collect();
- LinkBlock::new(Link::empty(), item_sections)
+ LinkBlock::new(Link::empty(), "", item_sections)
}
fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> {
@@ -503,8 +526,7 @@ fn sidebar_render_assoc_items(
.iter()
.filter_map(|it| {
let trait_ = it.inner_impl().trait_.as_ref()?;
- let encoded =
- id_map.derive(super::get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
+ let encoded = id_map.derive(super::get_id_for_impl(cx.tcx(), it.impl_item.item_id));
let prefix = match it.inner_impl().polarity {
ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
@@ -522,12 +544,21 @@ fn sidebar_render_assoc_items(
let synthetic = format_impls(synthetic, id_map);
let blanket = format_impls(blanket_impl, id_map);
[
- LinkBlock::new(Link::new("trait-implementations", "Trait Implementations"), concrete),
+ LinkBlock::new(
+ Link::new("trait-implementations", "Trait Implementations"),
+ "trait-implementation",
+ concrete,
+ ),
LinkBlock::new(
Link::new("synthetic-implementations", "Auto Trait Implementations"),
+ "synthetic-implementation",
synthetic,
),
- LinkBlock::new(Link::new("blanket-implementations", "Blanket Implementations"), blanket),
+ LinkBlock::new(
+ Link::new("blanket-implementations", "Blanket Implementations"),
+ "blanket-implementation",
+ blanket,
+ ),
]
}
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index e824651e7..d2c7c578c 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -5,18 +5,28 @@ use std::io::{self, BufReader};
use std::path::{Component, Path};
use std::rc::{Rc, Weak};
+use indexmap::IndexMap;
use itertools::Itertools;
use rustc_data_structures::flock;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
+use rustc_span::def_id::DefId;
+use rustc_span::Symbol;
use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
use super::{collect_paths_for_type, ensure_trailing_slash, Context};
-use crate::clean::Crate;
+use crate::clean::{Crate, Item, ItemId, ItemKind};
use crate::config::{EmitType, RenderOptions};
use crate::docfs::PathError;
use crate::error::Error;
+use crate::formats::cache::Cache;
+use crate::formats::item_type::ItemType;
+use crate::formats::{Impl, RenderMode};
+use crate::html::format::Buffer;
+use crate::html::render::{AssocItemLink, ImplRenderingParameters};
use crate::html::{layout, static_files};
+use crate::visit::DocVisitor;
use crate::{try_err, try_none};
/// Rustdoc writes out two kinds of shared files:
@@ -336,33 +346,286 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
let dst = cx.dst.join("index.html");
let page = layout::Page {
title: "Index of crates",
- css_class: "mod",
+ css_class: "mod sys",
root_path: "./",
static_root_path: shared.static_root_path.as_deref(),
description: "List of crates",
resource_suffix: &shared.resource_suffix,
+ rust_logo: true,
};
let content = format!(
"<h1>List of all crates</h1><ul class=\"all-items\">{}</ul>",
- krates
- .iter()
- .map(|s| {
- format!(
- "<li><a href=\"{trailing_slash}index.html\">{s}</a></li>",
- trailing_slash = ensure_trailing_slash(s),
- )
- })
- .collect::<String>()
+ krates.iter().format_with("", |k, f| {
+ f(&format_args!(
+ "<li><a href=\"{trailing_slash}index.html\">{k}</a></li>",
+ trailing_slash = ensure_trailing_slash(k),
+ ))
+ })
);
let v = layout::render(&shared.layout, &page, "", content, &shared.style_files);
shared.fs.write(dst, v)?;
}
}
+ let cloned_shared = Rc::clone(&cx.shared);
+ let cache = &cloned_shared.cache;
+
+ // Collect the list of aliased types and their aliases.
+ // <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+type.impl&type=code>
+ //
+ // The clean AST has type aliases that point at their types, but
+ // this visitor works to reverse that: `aliased_types` is a map
+ // from target to the aliases that reference it, and each one
+ // will generate one file.
+ struct TypeImplCollector<'cx, 'cache> {
+ // Map from DefId-of-aliased-type to its data.
+ aliased_types: IndexMap<DefId, AliasedType<'cache>>,
+ visited_aliases: FxHashSet<DefId>,
+ cache: &'cache Cache,
+ cx: &'cache mut Context<'cx>,
+ }
+ // Data for an aliased type.
+ //
+ // In the final file, the format will be roughly:
+ //
+ // ```json
+ // // type.impl/CRATE/TYPENAME.js
+ // JSONP(
+ // "CRATE": [
+ // ["IMPL1 HTML", "ALIAS1", "ALIAS2", ...],
+ // ["IMPL2 HTML", "ALIAS3", "ALIAS4", ...],
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ struct AliasedType
+ // ...
+ // ]
+ // )
+ // ```
+ struct AliasedType<'cache> {
+ // This is used to generate the actual filename of this aliased type.
+ target_fqp: &'cache [Symbol],
+ target_type: ItemType,
+ // This is the data stored inside the file.
+ // ItemId is used to deduplicate impls.
+ impl_: IndexMap<ItemId, AliasedTypeImpl<'cache>>,
+ }
+ // The `impl_` contains data that's used to figure out if an alias will work,
+ // and to generate the HTML at the end.
+ //
+ // The `type_aliases` list is built up with each type alias that matches.
+ struct AliasedTypeImpl<'cache> {
+ impl_: &'cache Impl,
+ type_aliases: Vec<(&'cache [Symbol], Item)>,
+ }
+ impl<'cx, 'cache> DocVisitor for TypeImplCollector<'cx, 'cache> {
+ fn visit_item(&mut self, it: &Item) {
+ self.visit_item_recur(it);
+ let cache = self.cache;
+ let ItemKind::TypeAliasItem(ref t) = *it.kind else { return };
+ let Some(self_did) = it.item_id.as_def_id() else { return };
+ if !self.visited_aliases.insert(self_did) {
+ return;
+ }
+ let Some(target_did) = t.type_.def_id(cache) else { return };
+ let get_extern = { || cache.external_paths.get(&target_did) };
+ let Some(&(ref target_fqp, target_type)) =
+ cache.paths.get(&target_did).or_else(get_extern)
+ else {
+ return;
+ };
+ let aliased_type = self.aliased_types.entry(target_did).or_insert_with(|| {
+ let impl_ = cache
+ .impls
+ .get(&target_did)
+ .map(|v| &v[..])
+ .unwrap_or_default()
+ .iter()
+ .map(|impl_| {
+ (
+ impl_.impl_item.item_id,
+ AliasedTypeImpl { impl_, type_aliases: Vec::new() },
+ )
+ })
+ .collect();
+ AliasedType { target_fqp: &target_fqp[..], target_type, impl_ }
+ });
+ let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) };
+ let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) else {
+ return;
+ };
+ let aliased_ty = self.cx.tcx().type_of(self_did).skip_binder();
+ // Exclude impls that are directly on this type. They're already in the HTML.
+ // Some inlining scenarios can cause there to be two versions of the same
+ // impl: one on the type alias and one on the underlying target type.
+ let mut seen_impls: FxHashSet<ItemId> = cache
+ .impls
+ .get(&self_did)
+ .map(|s| &s[..])
+ .unwrap_or_default()
+ .iter()
+ .map(|i| i.impl_item.item_id)
+ .collect();
+ for (impl_item_id, aliased_type_impl) in &mut aliased_type.impl_ {
+ // Only include this impl if it actually unifies with this alias.
+ // Synthetic impls are not included; those are also included in the HTML.
+ //
+ // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this
+ // to use type unification.
+ // Be aware of `tests/rustdoc/type-alias/deeply-nested-112515.rs` which might regress.
+ let Some(impl_did) = impl_item_id.as_def_id() else { continue };
+ let for_ty = self.cx.tcx().type_of(impl_did).skip_binder();
+ let reject_cx =
+ DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey };
+ if !reject_cx.types_may_unify(aliased_ty, for_ty) {
+ continue;
+ }
+ // Avoid duplicates
+ if !seen_impls.insert(*impl_item_id) {
+ continue;
+ }
+ // This impl was not found in the set of rejected impls
+ aliased_type_impl.type_aliases.push((&self_fqp[..], it.clone()));
+ }
+ }
+ }
+ let mut type_impl_collector = TypeImplCollector {
+ aliased_types: IndexMap::default(),
+ visited_aliases: FxHashSet::default(),
+ cache,
+ cx,
+ };
+ DocVisitor::visit_crate(&mut type_impl_collector, &krate);
+ // Final serialized form of the alias impl
+ struct AliasSerializableImpl {
+ text: String,
+ trait_: Option<String>,
+ aliases: Vec<String>,
+ }
+ impl Serialize for AliasSerializableImpl {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let mut seq = serializer.serialize_seq(None)?;
+ seq.serialize_element(&self.text)?;
+ if let Some(trait_) = &self.trait_ {
+ seq.serialize_element(trait_)?;
+ } else {
+ seq.serialize_element(&0)?;
+ }
+ for type_ in &self.aliases {
+ seq.serialize_element(type_)?;
+ }
+ seq.end()
+ }
+ }
+ let cx = type_impl_collector.cx;
+ let dst = cx.dst.join("type.impl");
+ let aliased_types = type_impl_collector.aliased_types;
+ for aliased_type in aliased_types.values() {
+ let impls = aliased_type
+ .impl_
+ .values()
+ .flat_map(|AliasedTypeImpl { impl_, type_aliases }| {
+ let mut ret = Vec::new();
+ let trait_ = impl_
+ .inner_impl()
+ .trait_
+ .as_ref()
+ .map(|trait_| format!("{:#}", trait_.print(cx)));
+ // render_impl will filter out "impossible-to-call" methods
+ // to make that functionality work here, it needs to be called with
+ // each type alias, and if it gives a different result, split the impl
+ for &(type_alias_fqp, ref type_alias_item) in type_aliases {
+ let mut buf = Buffer::html();
+ cx.id_map = Default::default();
+ cx.deref_id_map = Default::default();
+ let target_did = impl_
+ .inner_impl()
+ .trait_
+ .as_ref()
+ .map(|trait_| trait_.def_id())
+ .or_else(|| impl_.inner_impl().for_.def_id(cache));
+ let provided_methods;
+ let assoc_link = if let Some(target_did) = target_did {
+ provided_methods = impl_.inner_impl().provided_trait_methods(cx.tcx());
+ AssocItemLink::GotoSource(ItemId::DefId(target_did), &provided_methods)
+ } else {
+ AssocItemLink::Anchor(None)
+ };
+ super::render_impl(
+ &mut buf,
+ cx,
+ *impl_,
+ &type_alias_item,
+ assoc_link,
+ RenderMode::Normal,
+ None,
+ &[],
+ ImplRenderingParameters {
+ show_def_docs: true,
+ show_default_items: true,
+ show_non_assoc_items: true,
+ toggle_open_by_default: true,
+ },
+ );
+ let text = buf.into_inner();
+ let type_alias_fqp = (*type_alias_fqp).iter().join("::");
+ if Some(&text) == ret.last().map(|s: &AliasSerializableImpl| &s.text) {
+ ret.last_mut()
+ .expect("already established that ret.last() is Some()")
+ .aliases
+ .push(type_alias_fqp);
+ } else {
+ ret.push(AliasSerializableImpl {
+ text,
+ trait_: trait_.clone(),
+ aliases: vec![type_alias_fqp],
+ })
+ }
+ }
+ ret
+ })
+ .collect::<Vec<_>>();
+ let impls = format!(
+ r#""{}":{}"#,
+ krate.name(cx.tcx()),
+ serde_json::to_string(&impls).expect("failed serde conversion"),
+ );
+
+ let mut mydst = dst.clone();
+ for part in &aliased_type.target_fqp[..aliased_type.target_fqp.len() - 1] {
+ mydst.push(part.to_string());
+ }
+ cx.shared.ensure_dir(&mydst)?;
+ let aliased_item_type = aliased_type.target_type;
+ mydst.push(&format!(
+ "{aliased_item_type}.{}.js",
+ aliased_type.target_fqp[aliased_type.target_fqp.len() - 1]
+ ));
+
+ let (mut all_impls, _) = try_err!(collect(&mydst, krate.name(cx.tcx()).as_str()), &mydst);
+ all_impls.push(impls);
+ // Sort the implementors by crate so the file will be generated
+ // identically even with rustdoc running in parallel.
+ all_impls.sort();
+
+ let mut v = String::from("(function() {var type_impls = {\n");
+ v.push_str(&all_impls.join(",\n"));
+ v.push_str("\n};");
+ v.push_str(
+ "if (window.register_type_impls) {\
+ window.register_type_impls(type_impls);\
+ } else {\
+ window.pending_type_impls = type_impls;\
+ }",
+ );
+ v.push_str("})()");
+ cx.shared.fs.write(mydst, v)?;
+ }
+
// Update the list of all implementors for traits
- let dst = cx.dst.join("implementors");
- let cache = cx.cache();
+ // <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+trait.impl&type=code>
+ let dst = cx.dst.join("trait.impl");
for (&did, imps) in &cache.implementors {
// Private modules can leak through to this phase of rustdoc, which
// could contain implementations for otherwise private types. In some
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 1d6eafe51..ce620c226 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -1,4 +1,5 @@
use crate::clean;
+use crate::clean::utils::has_doc_flag;
use crate::docfs::PathError;
use crate::error::Error;
use crate::html::format;
@@ -12,7 +13,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
-use rustc_span::source_map::FileName;
+use rustc_span::{sym, FileName};
use std::cell::RefCell;
use std::ffi::OsStr;
@@ -223,7 +224,8 @@ impl SourceCollector<'_, '_> {
cur.push(&fname);
let title = format!("{} - source", src_fname.to_string_lossy());
- let desc = format!("Source of the Rust file `{}`.", filename.prefer_remapped());
+ let desc =
+ format!("Source of the Rust file `{}`.", filename.prefer_remapped_unconditionaly());
let page = layout::Page {
title: &title,
css_class: "src",
@@ -231,6 +233,7 @@ impl SourceCollector<'_, '_> {
static_root_path: shared.static_root_path.as_deref(),
description: &desc,
resource_suffix: &shared.resource_suffix,
+ rust_logo: has_doc_flag(self.cx.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
};
let v = layout::render(
&shared.layout,
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 47f9e6502..9efdcd601 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -461,19 +461,9 @@ img {
display: none !important;
}
-.sidebar .logo-container {
- margin-top: 10px;
- margin-bottom: 10px;
- text-align: center;
-}
-
-.version {
- overflow-wrap: break-word;
-}
-
.logo-container > img {
- height: 100px;
- width: 100px;
+ height: 48px;
+ width: 48px;
}
ul.block, .block li {
@@ -502,6 +492,7 @@ ul.block, .block li {
}
.sidebar-elems,
+.sidebar > .version,
.sidebar > h2 {
padding-left: 24px;
}
@@ -510,6 +501,8 @@ ul.block, .block li {
color: var(--sidebar-link-color);
}
.sidebar .current,
+.sidebar .current a,
+.sidebar-crate a.logo-container:hover + h2 a,
.sidebar a:hover:not(.logo-container) {
background-color: var(--sidebar-current-link-background-color);
}
@@ -524,6 +517,75 @@ ul.block, .block li {
overflow: hidden;
}
+.sidebar-crate {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ /* there's a 10px padding at the top of <main>, and a 4px margin at the
+ top of the search form. To line them up, add them. */
+ margin: 14px 32px 1rem;
+ row-gap: 10px;
+ column-gap: 32px;
+ flex-wrap: wrap;
+}
+
+.sidebar-crate h2 {
+ flex-grow: 1;
+ /* This setup with the margins and row-gap is designed to make flex-wrap
+ work the way we want. If they're in the side-by-side lockup, there
+ should be a 16px margin to the left of the logo (visually the same as
+ the 24px one on everything else, which are not giant circles) and 8px
+ between it and the crate's name and version. When they're line wrapped,
+ the logo needs to have the same margin on both sides of itself (to
+ center properly) and the crate name and version need 24px on their
+ left margin. */
+ margin: 0 -8px;
+ /* To align this with the search bar, it should not be centered, even when
+ the logo is. */
+ align-self: start;
+}
+
+.sidebar-crate .logo-container {
+ /* The logo is expected to have 8px "slop" along its edges, so we can optically
+ center it. */
+ margin: 0 -16px 0 -16px;
+ text-align: center;
+}
+
+.sidebar-crate h2 a {
+ display: block;
+ margin: 0 calc(-24px + 0.25rem) 0 -0.5rem;
+ /* Align the sidebar crate link with the search bar, which have different
+ font sizes.
+
+ | | font-size | line-height | total line-height | padding-y | total |
+ |:-------|----------:|------------:|------------------:|----------:|-------------:|
+ | crate | 1.375rem | 1.25 | 1.72rem | x | 2x+1.72rem |
+ | search | 1rem | 1.15 | 1.15rem | 8px | 1.15rem+16px |
+
+ 2x + 1.72rem = 1.15rem + 16px
+ 2x = 1.15rem + 16px - 1.72rem
+ 2x = 16px - 0.57rem
+ x = ( 16px - 0.57rem ) / 2
+ */
+ padding: calc( ( 16px - 0.57rem ) / 2 ) 0.25rem;
+ padding-left: 0.5rem;
+}
+
+.sidebar-crate h2 .version {
+ display: block;
+ font-weight: normal;
+ font-size: 1rem;
+ overflow-wrap: break-word;
+ /* opposite of the link padding, cut in half again */
+ margin-top: calc( ( -16px + 0.57rem ) / 2 );
+}
+
+.sidebar-crate + .version {
+ margin-top: -1rem;
+ margin-bottom: 1rem;
+}
+
.mobile-topbar {
display: none;
}
@@ -1045,6 +1107,7 @@ so that we can apply CSS-filters to change the arrow color in themes */
white-space: pre-wrap;
border-radius: 3px;
display: inline;
+ vertical-align: baseline;
}
.stab.portability > code {
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index eb256455b..7c052606a 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -51,9 +51,14 @@ function setMobileTopbar() {
// but with the current code it's hard to get the right information in the right place.
const mobileTopbar = document.querySelector(".mobile-topbar");
const locationTitle = document.querySelector(".sidebar h2.location");
- if (mobileTopbar && locationTitle) {
+ if (mobileTopbar) {
const mobileTitle = document.createElement("h2");
- mobileTitle.innerHTML = locationTitle.innerHTML;
+ mobileTitle.className = "location";
+ if (hasClass(document.body, "crate")) {
+ mobileTitle.innerText = `Crate ${window.currentCrate}`;
+ } else if (locationTitle) {
+ mobileTitle.innerHTML = locationTitle.innerHTML;
+ }
mobileTopbar.appendChild(mobileTitle);
}
}
@@ -354,6 +359,34 @@ function preLoadCss(cssUrl) {
expandSection(pageId);
}
}
+ if (savedHash.startsWith("impl-")) {
+ // impl-disambiguated links, used by the search engine
+ // format: impl-X[-for-Y]/method.WHATEVER
+ // turn this into method.WHATEVER[-NUMBER]
+ const splitAt = savedHash.indexOf("/");
+ if (splitAt !== -1) {
+ const implId = savedHash.slice(0, splitAt);
+ const assocId = savedHash.slice(splitAt + 1);
+ const implElem = document.getElementById(implId);
+ if (implElem && implElem.parentElement.tagName === "SUMMARY" &&
+ implElem.parentElement.parentElement.tagName === "DETAILS") {
+ onEachLazy(implElem.parentElement.parentElement.querySelectorAll(
+ `[id^="${assocId}"]`),
+ item => {
+ const numbered = /([^-]+)-([0-9]+)/.exec(item.id);
+ if (item.id === assocId || (numbered && numbered[1] === assocId)) {
+ openParentDetails(item);
+ item.scrollIntoView();
+ // Let the section expand itself before trying to highlight
+ setTimeout(() => {
+ window.location.replace("#" + item.id);
+ }, 0);
+ }
+ }
+ );
+ }
+ }
+ }
}
function onHashChange(ev) {
@@ -452,22 +485,27 @@ function preLoadCss(cssUrl) {
return;
}
+ const modpath = hasClass(document.body, "mod") ? "../" : "";
+
const h3 = document.createElement("h3");
- h3.innerHTML = `<a href="index.html#${id}">${longty}</a>`;
+ h3.innerHTML = `<a href="${modpath}index.html#${id}">${longty}</a>`;
const ul = document.createElement("ul");
ul.className = "block " + shortty;
for (const name of filtered) {
let path;
if (shortty === "mod") {
- path = name + "/index.html";
+ path = `${modpath}${name}/index.html`;
} else {
- path = shortty + "." + name + ".html";
+ path = `${modpath}${shortty}.${name}.html`;
+ }
+ let current_page = document.location.href.toString();
+ if (current_page.endsWith("/")) {
+ current_page += "index.html";
}
- const current_page = document.location.href.split("/").pop();
const link = document.createElement("a");
link.href = path;
- if (path === current_page) {
+ if (link.href === current_page) {
link.className = "current";
}
link.textContent = name;
@@ -480,23 +518,38 @@ function preLoadCss(cssUrl) {
}
if (sidebar) {
+ // keep this synchronized with ItemSection::ALL in html/render/mod.rs
+ // Re-exports aren't shown here, because they don't have child pages
+ //block("reexport", "reexports", "Re-exports");
block("primitive", "primitives", "Primitive Types");
block("mod", "modules", "Modules");
block("macro", "macros", "Macros");
block("struct", "structs", "Structs");
block("enum", "enums", "Enums");
- block("union", "unions", "Unions");
block("constant", "constants", "Constants");
block("static", "static", "Statics");
block("trait", "traits", "Traits");
block("fn", "functions", "Functions");
block("type", "types", "Type Aliases");
+ block("union", "unions", "Unions");
+ // No point, because these items don't appear in modules
+ //block("impl", "impls", "Implementations");
+ //block("tymethod", "tymethods", "Type Methods");
+ //block("method", "methods", "Methods");
+ //block("structfield", "fields", "Fields");
+ //block("variant", "variants", "Variants");
+ //block("associatedtype", "associated-types", "Associated Types");
+ //block("associatedconstant", "associated-consts", "Associated Constants");
block("foreigntype", "foreign-types", "Foreign Types");
block("keyword", "keywords", "Keywords");
+ block("opaque", "opaque-types", "Opaque Types");
+ block("attr", "attributes", "Attribute Macros");
+ block("derive", "derives", "Derive Macros");
block("traitalias", "trait-aliases", "Trait Aliases");
}
}
+ // <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+trait.impl&type=code>
window.register_implementors = imp => {
const implementors = document.getElementById("implementors-list");
const synthetic_implementors = document.getElementById("synthetic-implementors-list");
@@ -563,7 +616,7 @@ function preLoadCss(cssUrl) {
onEachLazy(code.getElementsByTagName("a"), elem => {
const href = elem.getAttribute("href");
- if (href && !/^(?:[a-z+]+:)?\/\//.test(href)) {
+ if (href && !href.startsWith("#") && !/^(?:[a-z+]+:)?\/\//.test(href)) {
elem.setAttribute("href", window.rootPath + href);
}
});
@@ -587,6 +640,216 @@ function preLoadCss(cssUrl) {
window.register_implementors(window.pending_implementors);
}
+ /**
+ * <https://github.com/search?q=repo%3Arust-lang%2Frust+[RUSTDOCIMPL]+type.impl&type=code>
+ *
+ * [RUSTDOCIMPL] type.impl
+ *
+ * This code inlines implementations into the type alias docs at runtime. It's done at
+ * runtime because some crates have many type aliases and many methods, and we don't want
+ * to generate *O*`(types*methods)` HTML text. The data inside is mostly HTML fragments,
+ * wrapped in JSON.
+ *
+ * - It only includes docs generated for the current crate. This function accepts an
+ * object mapping crate names to the set of impls.
+ *
+ * - It filters down to the set of applicable impls. The Rust type checker is used to
+ * tag each HTML blob with the set of type aliases that can actually use it, so the
+ * JS only needs to consult the attached list of type aliases.
+ *
+ * - It renames the ID attributes, to avoid conflicting IDs in the resulting DOM.
+ *
+ * - It adds the necessary items to the sidebar. If it's an inherent impl, that means
+ * adding methods, associated types, and associated constants. If it's a trait impl,
+ * that means adding it to the trait impl sidebar list.
+ *
+ * - It adds the HTML block itself. If it's an inherent impl, it goes after the type
+ * alias's own inherent impls. If it's a trait impl, it goes in the Trait
+ * Implementations section.
+ *
+ * - After processing all of the impls, it sorts the sidebar items by name.
+ *
+ * @param {{[cratename: string]: Array<Array<string|0>>}} impl
+ */
+ window.register_type_impls = imp => {
+ if (!imp || !imp[window.currentCrate]) {
+ return;
+ }
+ window.pending_type_impls = null;
+ const idMap = new Map();
+
+ let implementations = document.getElementById("implementations-list");
+ let trait_implementations = document.getElementById("trait-implementations-list");
+ let trait_implementations_header = document.getElementById("trait-implementations");
+
+ // We want to include the current type alias's impls, and no others.
+ const script = document.querySelector("script[data-self-path]");
+ const selfPath = script ? script.getAttribute("data-self-path") : null;
+
+ // These sidebar blocks need filled in, too.
+ const mainContent = document.querySelector("#main-content");
+ const sidebarSection = document.querySelector(".sidebar section");
+ let methods = document.querySelector(".sidebar .block.method");
+ let associatedTypes = document.querySelector(".sidebar .block.associatedtype");
+ let associatedConstants = document.querySelector(".sidebar .block.associatedconstant");
+ let sidebarTraitList = document.querySelector(".sidebar .block.trait-implementation");
+
+ for (const impList of imp[window.currentCrate]) {
+ const types = impList.slice(2);
+ const text = impList[0];
+ const isTrait = impList[1] !== 0;
+ const traitName = impList[1];
+ if (types.indexOf(selfPath) === -1) {
+ continue;
+ }
+ let outputList = isTrait ? trait_implementations : implementations;
+ if (outputList === null) {
+ const outputListName = isTrait ? "Trait Implementations" : "Implementations";
+ const outputListId = isTrait ?
+ "trait-implementations-list" :
+ "implementations-list";
+ const outputListHeaderId = isTrait ? "trait-implementations" : "implementations";
+ const outputListHeader = document.createElement("h2");
+ outputListHeader.id = outputListHeaderId;
+ outputListHeader.innerText = outputListName;
+ outputList = document.createElement("div");
+ outputList.id = outputListId;
+ if (isTrait) {
+ const link = document.createElement("a");
+ link.href = `#${outputListHeaderId}`;
+ link.innerText = "Trait Implementations";
+ const h = document.createElement("h3");
+ h.appendChild(link);
+ trait_implementations = outputList;
+ trait_implementations_header = outputListHeader;
+ sidebarSection.appendChild(h);
+ sidebarTraitList = document.createElement("ul");
+ sidebarTraitList.className = "block trait-implementation";
+ sidebarSection.appendChild(sidebarTraitList);
+ mainContent.appendChild(outputListHeader);
+ mainContent.appendChild(outputList);
+ } else {
+ implementations = outputList;
+ if (trait_implementations) {
+ mainContent.insertBefore(outputListHeader, trait_implementations_header);
+ mainContent.insertBefore(outputList, trait_implementations_header);
+ } else {
+ const mainContent = document.querySelector("#main-content");
+ mainContent.appendChild(outputListHeader);
+ mainContent.appendChild(outputList);
+ }
+ }
+ }
+ const template = document.createElement("template");
+ template.innerHTML = text;
+
+ onEachLazy(template.content.querySelectorAll("a"), elem => {
+ const href = elem.getAttribute("href");
+
+ if (href && !href.startsWith("#") && !/^(?:[a-z+]+:)?\/\//.test(href)) {
+ elem.setAttribute("href", window.rootPath + href);
+ }
+ });
+ onEachLazy(template.content.querySelectorAll("[id]"), el => {
+ let i = 0;
+ if (idMap.has(el.id)) {
+ i = idMap.get(el.id);
+ } else if (document.getElementById(el.id)) {
+ i = 1;
+ while (document.getElementById(`${el.id}-${2 * i}`)) {
+ i = 2 * i;
+ }
+ while (document.getElementById(`${el.id}-${i}`)) {
+ i += 1;
+ }
+ }
+ if (i !== 0) {
+ const oldHref = `#${el.id}`;
+ const newHref = `#${el.id}-${i}`;
+ el.id = `${el.id}-${i}`;
+ onEachLazy(template.content.querySelectorAll("a[href]"), link => {
+ if (link.getAttribute("href") === oldHref) {
+ link.href = newHref;
+ }
+ });
+ }
+ idMap.set(el.id, i + 1);
+ });
+ const templateAssocItems = template.content.querySelectorAll("section.tymethod, " +
+ "section.method, section.associatedtype, section.associatedconstant");
+ if (isTrait) {
+ const li = document.createElement("li");
+ const a = document.createElement("a");
+ a.href = `#${template.content.querySelector(".impl").id}`;
+ a.textContent = traitName;
+ li.appendChild(a);
+ sidebarTraitList.append(li);
+ } else {
+ onEachLazy(templateAssocItems, item => {
+ let block = hasClass(item, "associatedtype") ? associatedTypes : (
+ hasClass(item, "associatedconstant") ? associatedConstants : (
+ methods));
+ if (!block) {
+ const blockTitle = hasClass(item, "associatedtype") ? "Associated Types" : (
+ hasClass(item, "associatedconstant") ? "Associated Constants" : (
+ "Methods"));
+ const blockClass = hasClass(item, "associatedtype") ? "associatedtype" : (
+ hasClass(item, "associatedconstant") ? "associatedconstant" : (
+ "method"));
+ const blockHeader = document.createElement("h3");
+ const blockLink = document.createElement("a");
+ blockLink.href = "#implementations";
+ blockLink.innerText = blockTitle;
+ blockHeader.appendChild(blockLink);
+ block = document.createElement("ul");
+ block.className = `block ${blockClass}`;
+ const insertionReference = methods || sidebarTraitList;
+ if (insertionReference) {
+ const insertionReferenceH = insertionReference.previousElementSibling;
+ sidebarSection.insertBefore(blockHeader, insertionReferenceH);
+ sidebarSection.insertBefore(block, insertionReferenceH);
+ } else {
+ sidebarSection.appendChild(blockHeader);
+ sidebarSection.appendChild(block);
+ }
+ if (hasClass(item, "associatedtype")) {
+ associatedTypes = block;
+ } else if (hasClass(item, "associatedconstant")) {
+ associatedConstants = block;
+ } else {
+ methods = block;
+ }
+ }
+ const li = document.createElement("li");
+ const a = document.createElement("a");
+ a.innerText = item.id.split("-")[0].split(".")[1];
+ a.href = `#${item.id}`;
+ li.appendChild(a);
+ block.appendChild(li);
+ });
+ }
+ outputList.appendChild(template.content);
+ }
+
+ for (const list of [methods, associatedTypes, associatedConstants, sidebarTraitList]) {
+ if (!list) {
+ continue;
+ }
+ const newChildren = Array.prototype.slice.call(list.children);
+ newChildren.sort((a, b) => {
+ const aI = a.innerText;
+ const bI = b.innerText;
+ return aI < bI ? -1 :
+ aI > bI ? 1 :
+ 0;
+ });
+ list.replaceChildren(...newChildren);
+ }
+ };
+ if (window.pending_type_impls) {
+ window.register_type_impls(window.pending_type_impls);
+ }
+
function addSidebarCrates() {
if (!window.ALL_CRATES) {
return;
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 2f0cae0a4..48c9a53a2 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1555,7 +1555,7 @@ function initSearch(rawSearchIndex) {
return false;
}
}
- } else if (fnType.id !== null) {
+ } else {
if (queryElem.id === typeNameIdOfArrayOrSlice &&
(fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray)
) {
@@ -1752,6 +1752,7 @@ function initSearch(rawSearchIndex) {
type: item.type,
is_alias: true,
deprecated: item.deprecated,
+ implDisambiguator: item.implDisambiguator,
};
}
@@ -2218,7 +2219,7 @@ function initSearch(rawSearchIndex) {
href = ROOT_PATH + name + "/index.html";
} else if (item.parent !== undefined) {
const myparent = item.parent;
- let anchor = "#" + type + "." + name;
+ let anchor = type + "." + name;
const parentType = itemTypes[myparent.ty];
let pageType = parentType;
let pageName = myparent.name;
@@ -2232,16 +2233,19 @@ function initSearch(rawSearchIndex) {
const enumName = item.path.substr(enumNameIdx + 2);
path = item.path.substr(0, enumNameIdx);
displayPath = path + "::" + enumName + "::" + myparent.name + "::";
- anchor = "#variant." + myparent.name + ".field." + name;
+ anchor = "variant." + myparent.name + ".field." + name;
pageType = "enum";
pageName = enumName;
} else {
displayPath = path + "::" + myparent.name + "::";
}
+ if (item.implDisambiguator !== null) {
+ anchor = item.implDisambiguator + "/" + anchor;
+ }
href = ROOT_PATH + path.replace(/::/g, "/") +
"/" + pageType +
"." + pageName +
- ".html" + anchor;
+ ".html#" + anchor;
} else {
displayPath = item.path + "::";
href = ROOT_PATH + item.path.replace(/::/g, "/") +
@@ -2727,6 +2731,10 @@ ${item.displayPath}<span class="${type}">${name}</span>\
* Types are also represented as arrays; the first item is an index into the `p`
* array, while the second is a list of types representing any generic parameters.
*
+ * b[i] contains an item's impl disambiguator. This is only present if an item
+ * is defined in an impl block and, the impl block's type has more than one associated
+ * item with the same name.
+ *
* `a` defines aliases with an Array of pairs: [name, offset], where `offset`
* points into the n/t/d/q/i/f arrays.
*
@@ -2746,6 +2754,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
* i: Array<Number>,
* f: Array<RawFunctionSearchType>,
* p: Array<Object>,
+ * b: Array<[Number, String]>,
* c: Array<Number>
* }}
*/
@@ -2766,6 +2775,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
id: id,
normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""),
deprecated: null,
+ implDisambiguator: null,
};
id += 1;
searchIndex.push(crateRow);
@@ -2789,6 +2799,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
const itemFunctionSearchTypes = crateCorpus.f;
// an array of (Number) indices for the deprecated items
const deprecatedItems = new Set(crateCorpus.c);
+ // an array of (Number) indices for the deprecated items
+ const implDisambiguator = new Map(crateCorpus.b);
// an array of [(Number) item type,
// (String) name]
const paths = crateCorpus.p;
@@ -2849,6 +2861,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
id: id,
normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""),
deprecated: deprecatedItems.has(i),
+ implDisambiguator: implDisambiguator.has(i) ? implDisambiguator.get(i) : null,
};
id += 1;
searchIndex.push(row);
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index 579c782be..3f6147bb9 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -10,7 +10,6 @@
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.fira_sans_regular}}"> {# #}
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.fira_sans_medium}}"> {# #}
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_code_pro_regular}}"> {# #}
- <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_serif_4_bold}}"> {# #}
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_code_pro_semibold}}"> {# #}
<link rel="stylesheet" {#+ #}
href="{{static_root_path|safe}}{{files.normalize_css}}"> {# #}
@@ -42,6 +41,8 @@
<script defer src="{{page.root_path|safe}}src-files{{page.resource_suffix}}.js"></script> {# #}
{% else if !page.css_class.contains("mod") %}
<script defer src="sidebar-items{{page.resource_suffix}}.js"></script> {# #}
+ {% else if !page.css_class.contains("sys") %}
+ <script defer src="../sidebar-items{{page.resource_suffix}}.js"></script> {# #}
{% endif %}
<script defer src="{{static_root_path|safe}}{{files.main_js}}"></script> {# #}
{% if layout.scrape_examples_extension %}
@@ -77,36 +78,51 @@
{% if page.css_class != "src" %}
<nav class="mobile-topbar"> {# #}
<button class="sidebar-menu-toggle">&#9776;</button> {# #}
- <a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
- {% if !layout.logo.is_empty() %}
- <img src="{{layout.logo}}" alt="logo"> {# #}
- {% else %}
- <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+ {% if !layout.logo.is_empty() || page.rust_logo %}
+ <a class="logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> {# #}
+ {% if page.rust_logo %}
+ <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt=""> {# #}
+ {% else if !layout.logo.is_empty() %}
+ <img src="{{layout.logo}}" alt=""> {# #}
{% endif %}
</a> {# #}
+ {% endif %}
</nav>
{% endif %}
<nav class="sidebar"> {# #}
{% if page.css_class != "src" %}
- <a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
- {% if !layout.logo.is_empty() %}
- <img src="{{layout.logo}}" alt="logo"> {# #}
- {% else %}
- <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+ <div class="sidebar-crate">
+ {% if !layout.logo.is_empty() || page.rust_logo %}
+ <a class="logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> {# #}
+ {% if page.rust_logo %}
+ <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+ {% else if !layout.logo.is_empty() %}
+ <img src="{{layout.logo}}" alt="logo"> {# #}
+ {% endif %}
+ </a> {# #}
{% endif %}
- </a> {# #}
+ <h2> {# #}
+ <a href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html">{{display_krate}}</a> {# #}
+ {% if !display_krate_version_number.is_empty() %}
+ <span class="version">{{+ display_krate_version_number}}</span>
+ {% endif %}
+ </h2> {# #}
+ </div> {# #}
+ {% if !display_krate_version_extra.is_empty() %}
+ <div class="version">{{+ display_krate_version_extra}}</div> {# #}
+ {% endif %}
{% endif %}
{{ sidebar|safe }}
</nav> {# #}
<main> {# #}
{% if page.css_class != "src" %}<div class="width-limiter">{% endif %}
<nav class="sub"> {# #}
- {% if page.css_class == "src" %}
- <a class="sub-logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
- {% if !layout.logo.is_empty() %}
- <img src="{{layout.logo}}" alt="logo"> {# #}
- {% else %}
- <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+ {% if page.css_class == "src" && (!layout.logo.is_empty() || page.rust_logo) %}
+ <a class="sub-logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> {# #}
+ {% if page.rust_logo %}
+ <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="{{display_krate}}"> {# #}
+ {% else if !layout.logo.is_empty() %}
+ <img src="{{layout.logo}}" alt="{{display_krate}}"> {# #}
{% endif %}
</a> {# #}
{% endif %}
diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html
index 01d476ad2..d98213418 100644
--- a/src/librustdoc/html/templates/sidebar.html
+++ b/src/librustdoc/html/templates/sidebar.html
@@ -6,9 +6,6 @@
<div class="sidebar-elems">
{% if is_crate %}
<ul class="block">
- {% if !version.is_empty() %}
- <li class="version">Version {{+ version}}</li>
- {% endif %}
<li><a id="all-types" href="all.html">All Items</a></li> {# #}
</ul>
{% endif %}
@@ -21,7 +18,7 @@
<h3><a href="#{{block.heading.href|safe}}">{{block.heading.name}}</a></h3>
{% endif %}
{% if !block.links.is_empty() %}
- <ul class="block">
+ <ul class="block{% if !block.class.is_empty() +%} {{+block.class}}{% endif %}">
{% for link in block.links %}
<li><a href="#{{link.href|safe}}">{{link.name}}</a></li>
{% endfor %}
@@ -32,6 +29,6 @@
</section>
{% endif %}
{% if !path.is_empty() %}
- <h2><a href="index.html">In {{+ path}}</a></h2>
+ <h2><a href="{% if is_mod %}../{% endif %}index.html">In {{+ path}}</a></h2>
{% endif %}
</div>
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 05f3e66b0..285923251 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -7,6 +7,7 @@
use std::fmt;
use rustc_ast::ast;
+use rustc_attr::DeprecatedSince;
use rustc_hir::{def::CtorKind, def::DefKind, def_id::DefId};
use rustc_metadata::rendered_const;
use rustc_middle::ty::{self, TyCtxt};
@@ -18,6 +19,7 @@ use rustdoc_json_types::*;
use crate::clean::{self, ItemId};
use crate::formats::item_type::ItemType;
+use crate::formats::FormatRenderer;
use crate::json::JsonRenderer;
use crate::passes::collect_intra_doc_links::UrlFragment;
@@ -41,7 +43,7 @@ impl JsonRenderer<'_> {
})
.collect();
let docs = item.opt_doc_value();
- let attrs = item.attributes(self.tcx, true);
+ let attrs = item.attributes(self.tcx, self.cache(), true);
let span = item.span(self.tcx);
let visibility = item.visibility(self.tcx);
let clean::Item { name, item_id, .. } = item;
@@ -137,9 +139,14 @@ where
}
pub(crate) fn from_deprecation(deprecation: rustc_attr::Deprecation) -> Deprecation {
- #[rustfmt::skip]
- let rustc_attr::Deprecation { since, note, is_since_rustc_version: _, suggestion: _ } = deprecation;
- Deprecation { since: since.map(|s| s.to_string()), note: note.map(|s| s.to_string()) }
+ let rustc_attr::Deprecation { since, note, suggestion: _ } = deprecation;
+ let since = match since {
+ DeprecatedSince::RustcVersion(version) => Some(version.to_string()),
+ DeprecatedSince::Future => Some("TBD".to_owned()),
+ DeprecatedSince::NonStandard(since) => Some(since.to_string()),
+ DeprecatedSince::Unspecified | DeprecatedSince::Err => None,
+ };
+ Deprecation { since, note: note.map(|s| s.to_string()) }
}
impl FromWithTcx<clean::GenericArgs> for GenericArgs {
@@ -176,7 +183,7 @@ impl FromWithTcx<clean::Constant> for Constant {
let expr = constant.expr(tcx);
let value = constant.value(tcx);
let is_literal = constant.is_literal(tcx);
- Constant { type_: constant.type_.into_tcx(tcx), expr, value, is_literal }
+ Constant { type_: (*constant.type_).into_tcx(tcx), expr, value, is_literal }
}
}
@@ -324,11 +331,11 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
}
// FIXME(generic_const_items): Add support for generic associated consts.
TyAssocConstItem(_generics, ty) => {
- ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: None }
+ ItemEnum::AssocConst { type_: (*ty).into_tcx(tcx), default: None }
}
// FIXME(generic_const_items): Add support for generic associated consts.
AssocConstItem(_generics, ty, default) => {
- ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: Some(default.expr(tcx)) }
+ ItemEnum::AssocConst { type_: (*ty).into_tcx(tcx), default: Some(default.expr(tcx)) }
}
TyAssocTypeItem(g, b) => ItemEnum::AssocType {
generics: g.into_tcx(tcx),
@@ -508,9 +515,8 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
lifetime: convert_lifetime(lifetime),
bounds: bounds.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) }
+ EqPredicate { lhs, rhs } => {
+ WherePredicate::EqPredicate { lhs: lhs.into_tcx(tcx), rhs: rhs.into_tcx(tcx) }
}
}
}
@@ -747,7 +753,7 @@ impl FromWithTcx<clean::Discriminant> for Discriminant {
// `rustc_middle` types, not `rustc_hir`, but because JSON never inlines
// the expr is always some.
expr: disr.expr(tcx).unwrap(),
- value: disr.value(tcx),
+ value: disr.value(tcx, false),
}
}
}
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 67f5ea5d9..dda06d4c9 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -74,6 +74,7 @@ extern crate jemalloc_sys;
use std::env::{self, VarError};
use std::io::{self, IsTerminal};
use std::process;
+use std::sync::{atomic::AtomicBool, Arc};
use rustc_driver::abort_on_err;
use rustc_errors::ErrorGuaranteed;
@@ -157,7 +158,7 @@ pub fn main() {
let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
- rustc_driver::install_ice_hook(
+ let using_internal_features = rustc_driver::install_ice_hook(
"https://github.com/rust-lang/rust/issues/new\
?labels=C-bug%2C+I-ICE%2C+T-rustdoc&template=ice.md",
|_| (),
@@ -177,7 +178,7 @@ pub fn main() {
rustc_driver::init_env_logger(&handler, "RUSTDOC_LOG");
let exit_code = rustc_driver::catch_with_exit_code(|| match get_args(&handler) {
- Some(args) => main_args(&mut handler, &args),
+ Some(args) => main_args(&mut handler, &args, using_internal_features),
_ =>
{
#[allow(deprecated)]
@@ -701,7 +702,11 @@ fn run_renderer<'tcx, T: formats::FormatRenderer<'tcx>>(
}
}
-fn main_args(handler: &mut EarlyErrorHandler, at_args: &[String]) -> MainResult {
+fn main_args(
+ handler: &mut EarlyErrorHandler,
+ at_args: &[String],
+ using_internal_features: Arc<AtomicBool>,
+) -> MainResult {
// Throw away the first argument, the name of the binary.
// In case of at_args being empty, as might be the case by
// passing empty argument array to execve under some platforms,
@@ -752,7 +757,7 @@ fn main_args(handler: &mut EarlyErrorHandler, at_args: &[String]) -> MainResult
(false, true) => {
let input = options.input.clone();
let edition = options.edition;
- let config = core::create_config(handler, options, &render_options);
+ let config = core::create_config(options, &render_options, using_internal_features);
// `markdown::render` can invoke `doctest::make_test`, which
// requires session globals and a thread pool, so we use
@@ -785,7 +790,7 @@ fn main_args(handler: &mut EarlyErrorHandler, at_args: &[String]) -> MainResult
let scrape_examples_options = options.scrape_examples_options.clone();
let bin_crate = options.bin_crate;
- let config = core::create_config(handler, options, &render_options);
+ let config = core::create_config(options, &render_options, using_internal_features);
interface::run_compiler(config, |compiler| {
let sess = compiler.session();
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index b74a9392f..b661ced01 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -4,7 +4,7 @@ use std::io::prelude::*;
use std::path::Path;
use rustc_span::edition::Edition;
-use rustc_span::source_map::DUMMY_SP;
+use rustc_span::DUMMY_SP;
use crate::config::{Options, RenderOptions};
use crate::doctest::{Collector, GlobalTestOptions};
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index d216305e6..fcd078858 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -521,8 +521,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
}
ty::Alias(..)
| ty::Closure(..)
- | ty::Generator(..)
- | ty::GeneratorWitness(..)
+ | ty::Coroutine(..)
+ | ty::CoroutineWitness(..)
| ty::Dynamic(..)
| ty::Param(_)
| ty::Bound(..)
@@ -1918,7 +1918,7 @@ fn resolution_failure(
Variant
| Field
| Closure
- | Generator
+ | Coroutine
| AssocTy
| AssocConst
| AssocFn
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index ff89d4e08..a57321b58 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -36,7 +36,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
let prims: FxHashSet<PrimitiveType> = local_crate.primitives(tcx).iter().map(|p| p.1).collect();
let crate_items = {
- let mut coll = ItemCollector::new();
+ let mut coll = ItemAndAliasCollector::new(&cx.cache);
cx.sess().time("collect_items_for_trait_impls", || coll.visit_crate(&krate));
coll.items
};
@@ -235,21 +235,27 @@ impl<'a, 'tcx> DocVisitor for SyntheticImplCollector<'a, 'tcx> {
}
}
-#[derive(Default)]
-struct ItemCollector {
+struct ItemAndAliasCollector<'cache> {
items: FxHashSet<ItemId>,
+ cache: &'cache Cache,
}
-impl ItemCollector {
- fn new() -> Self {
- Self::default()
+impl<'cache> ItemAndAliasCollector<'cache> {
+ fn new(cache: &'cache Cache) -> Self {
+ ItemAndAliasCollector { items: FxHashSet::default(), cache }
}
}
-impl DocVisitor for ItemCollector {
+impl<'cache> DocVisitor for ItemAndAliasCollector<'cache> {
fn visit_item(&mut self, i: &Item) {
self.items.insert(i.item_id);
+ if let TypeAliasItem(alias) = &*i.kind &&
+ let Some(did) = alias.type_.def_id(self.cache)
+ {
+ self.items.insert(ItemId::DefId(did));
+ }
+
self.visit_item_recur(i)
}
}
diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs
index 79fc599e1..00d15a3ca 100644
--- a/src/librustdoc/passes/lint/html_tags.rs
+++ b/src/librustdoc/passes/lint/html_tags.rs
@@ -25,91 +25,85 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
Some(sp) => sp,
None => item.attr_span(tcx),
};
- tcx.struct_span_lint_hir(
- crate::lint::INVALID_HTML_TAGS,
- hir_id,
- sp,
- msg.to_string(),
- |lint| {
- use rustc_lint_defs::Applicability;
- // If a tag looks like `<this>`, it might actually be a generic.
- // We don't try to detect stuff `<like, this>` because that's not valid HTML,
- // and we don't try to detect stuff `<like this>` because that's not valid Rust.
- let mut generics_end = range.end;
- if let Some(Some(mut generics_start)) = (is_open_tag
- && dox[..generics_end].ends_with('>'))
- .then(|| extract_path_backwards(&dox, range.start))
+ tcx.struct_span_lint_hir(crate::lint::INVALID_HTML_TAGS, hir_id, sp, msg, |lint| {
+ use rustc_lint_defs::Applicability;
+ // If a tag looks like `<this>`, it might actually be a generic.
+ // We don't try to detect stuff `<like, this>` because that's not valid HTML,
+ // and we don't try to detect stuff `<like this>` because that's not valid Rust.
+ let mut generics_end = range.end;
+ if let Some(Some(mut generics_start)) = (is_open_tag
+ && dox[..generics_end].ends_with('>'))
+ .then(|| extract_path_backwards(&dox, range.start))
+ {
+ while generics_start != 0
+ && generics_end < dox.len()
+ && dox.as_bytes()[generics_start - 1] == b'<'
+ && dox.as_bytes()[generics_end] == b'>'
{
- while generics_start != 0
- && generics_end < dox.len()
- && dox.as_bytes()[generics_start - 1] == b'<'
- && dox.as_bytes()[generics_end] == b'>'
- {
- generics_end += 1;
- generics_start -= 1;
- if let Some(new_start) = extract_path_backwards(&dox, generics_start) {
- generics_start = new_start;
- }
- if let Some(new_end) = extract_path_forward(&dox, generics_end) {
- generics_end = new_end;
- }
+ generics_end += 1;
+ generics_start -= 1;
+ if let Some(new_start) = extract_path_backwards(&dox, generics_start) {
+ generics_start = new_start;
}
if let Some(new_end) = extract_path_forward(&dox, generics_end) {
generics_end = new_end;
}
- let generics_sp = match source_span_for_markdown_range(
- tcx,
- &dox,
- &(generics_start..generics_end),
- &item.attrs.doc_strings,
- ) {
- Some(sp) => sp,
- None => item.attr_span(tcx),
- };
- // Sometimes, we only extract part of a path. For example, consider this:
- //
- // <[u32] as IntoIter<u32>>::Item
- // ^^^^^ unclosed HTML tag `u32`
- //
- // We don't have any code for parsing fully-qualified trait paths.
- // In theory, we could add it, but doing it correctly would require
- // parsing the entire path grammar, which is problematic because of
- // overlap between the path grammar and Markdown.
- //
- // The example above shows that ambiguity. Is `[u32]` intended to be an
- // intra-doc link to the u32 primitive, or is it intended to be a slice?
- //
- // If the below conditional were removed, we would suggest this, which is
- // not what the user probably wants.
- //
- // <[u32] as `IntoIter<u32>`>::Item
- //
- // We know that the user actually wants to wrap the whole thing in a code
- // block, but the only reason we know that is because `u32` does not, in
- // fact, implement IntoIter. If the example looks like this:
- //
- // <[Vec<i32>] as IntoIter<i32>::Item
- //
- // The ideal fix would be significantly different.
- if (generics_start > 0 && dox.as_bytes()[generics_start - 1] == b'<')
- || (generics_end < dox.len() && dox.as_bytes()[generics_end] == b'>')
- {
- return lint;
- }
- // multipart form is chosen here because ``Vec<i32>`` would be confusing.
- lint.multipart_suggestion(
- "try marking as source code",
- vec![
- (generics_sp.shrink_to_lo(), String::from("`")),
- (generics_sp.shrink_to_hi(), String::from("`")),
- ],
- Applicability::MaybeIncorrect,
- );
}
+ if let Some(new_end) = extract_path_forward(&dox, generics_end) {
+ generics_end = new_end;
+ }
+ let generics_sp = match source_span_for_markdown_range(
+ tcx,
+ &dox,
+ &(generics_start..generics_end),
+ &item.attrs.doc_strings,
+ ) {
+ Some(sp) => sp,
+ None => item.attr_span(tcx),
+ };
+ // Sometimes, we only extract part of a path. For example, consider this:
+ //
+ // <[u32] as IntoIter<u32>>::Item
+ // ^^^^^ unclosed HTML tag `u32`
+ //
+ // We don't have any code for parsing fully-qualified trait paths.
+ // In theory, we could add it, but doing it correctly would require
+ // parsing the entire path grammar, which is problematic because of
+ // overlap between the path grammar and Markdown.
+ //
+ // The example above shows that ambiguity. Is `[u32]` intended to be an
+ // intra-doc link to the u32 primitive, or is it intended to be a slice?
+ //
+ // If the below conditional were removed, we would suggest this, which is
+ // not what the user probably wants.
+ //
+ // <[u32] as `IntoIter<u32>`>::Item
+ //
+ // We know that the user actually wants to wrap the whole thing in a code
+ // block, but the only reason we know that is because `u32` does not, in
+ // fact, implement IntoIter. If the example looks like this:
+ //
+ // <[Vec<i32>] as IntoIter<i32>::Item
+ //
+ // The ideal fix would be significantly different.
+ if (generics_start > 0 && dox.as_bytes()[generics_start - 1] == b'<')
+ || (generics_end < dox.len() && dox.as_bytes()[generics_end] == b'>')
+ {
+ return lint;
+ }
+ // multipart form is chosen here because ``Vec<i32>`` would be confusing.
+ lint.multipart_suggestion(
+ "try marking as source code",
+ vec![
+ (generics_sp.shrink_to_lo(), String::from("`")),
+ (generics_sp.shrink_to_hi(), String::from("`")),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
- lint
- },
- );
+ lint
+ });
};
let mut tags = Vec::new();
diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs
index 0c15bf5f7..472781e7d 100644
--- a/src/librustdoc/passes/lint/redundant_explicit_links.rs
+++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs
@@ -87,7 +87,7 @@ fn check_redundant_explicit_link<'md>(
let link_data = collect_link_data(&mut offset_iter);
if let Some(resolvable_link) = link_data.resolvable_link.as_ref() {
- if &link_data.display_link.replace("`", "") != resolvable_link {
+ if &link_data.display_link.replace('`', "") != resolvable_link {
// Skips if display link does not match to actual
// resolvable link, usually happens if display link
// has several segments, e.g.