summaryrefslogtreecommitdiffstats
path: root/src/librustdoc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:32 +0000
commit4547b622d8d29df964fa2914213088b148c498fc (patch)
tree9fc6b25f3c3add6b745be9a2400a6e96140046e9 /src/librustdoc
parentReleasing progress-linux version 1.66.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-4547b622d8d29df964fa2914213088b148c498fc.tar.xz
rustc-4547b622d8d29df964fa2914213088b148c498fc.zip
Merging upstream version 1.67.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/librustdoc')
-rw-r--r--src/librustdoc/Cargo.toml5
-rw-r--r--src/librustdoc/clean/auto_trait.rs28
-rw-r--r--src/librustdoc/clean/blanket_impl.rs12
-rw-r--r--src/librustdoc/clean/cfg.rs2
-rw-r--r--src/librustdoc/clean/inline.rs55
-rw-r--r--src/librustdoc/clean/mod.rs635
-rw-r--r--src/librustdoc/clean/simplify.rs17
-rw-r--r--src/librustdoc/clean/types.rs156
-rw-r--r--src/librustdoc/clean/utils.rs52
-rw-r--r--src/librustdoc/config.rs24
-rw-r--r--src/librustdoc/core.rs7
-rw-r--r--src/librustdoc/doctest.rs21
-rw-r--r--src/librustdoc/formats/cache.rs50
-rw-r--r--src/librustdoc/html/format.rs168
-rw-r--r--src/librustdoc/html/highlight.rs6
-rw-r--r--src/librustdoc/html/highlight/fixtures/sample.html8
-rw-r--r--src/librustdoc/html/highlight/tests.rs2
-rw-r--r--src/librustdoc/html/layout.rs25
-rw-r--r--src/librustdoc/html/render/context.rs33
-rw-r--r--src/librustdoc/html/render/mod.rs265
-rw-r--r--src/librustdoc/html/render/print_item.rs174
-rw-r--r--src/librustdoc/html/render/span_map.rs10
-rw-r--r--src/librustdoc/html/render/write_shared.rs290
-rw-r--r--src/librustdoc/html/sources.rs25
-rw-r--r--src/librustdoc/html/static/css/noscript.css6
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css526
-rw-r--r--src/librustdoc/html/static/css/settings.css57
-rw-r--r--src/librustdoc/html/static/css/themes/ayu.css126
-rw-r--r--src/librustdoc/html/static/css/themes/dark.css122
-rw-r--r--src/librustdoc/html/static/css/themes/light.css116
-rw-r--r--src/librustdoc/html/static/fonts/README.txt12
-rw-r--r--src/librustdoc/html/static/js/main.js184
-rw-r--r--src/librustdoc/html/static/js/scrape-examples.js34
-rw-r--r--src/librustdoc/html/static/js/search.js21
-rw-r--r--src/librustdoc/html/static/js/settings.js74
-rw-r--r--src/librustdoc/html/static/js/source-script.js11
-rw-r--r--src/librustdoc/html/static/js/storage.js24
-rw-r--r--src/librustdoc/html/static/scrape-examples-help.md5
-rw-r--r--src/librustdoc/html/static_files.rs263
-rw-r--r--src/librustdoc/html/templates/page.html57
-rw-r--r--src/librustdoc/html/templates/print_item.html8
-rw-r--r--src/librustdoc/json/conversions.rs56
-rw-r--r--src/librustdoc/json/mod.rs58
-rw-r--r--src/librustdoc/lib.rs28
-rw-r--r--src/librustdoc/passes/bare_urls.rs110
-rw-r--r--src/librustdoc/passes/calculate_doc_coverage.rs4
-rw-r--r--src/librustdoc/passes/check_code_block_syntax.rs209
-rw-r--r--src/librustdoc/passes/check_doc_test_visibility.rs4
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs5
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs6
-rw-r--r--src/librustdoc/passes/lint.rs33
-rw-r--r--src/librustdoc/passes/lint/bare_urls.rs89
-rw-r--r--src/librustdoc/passes/lint/check_code_block_syntax.rs170
-rw-r--r--src/librustdoc/passes/lint/html_tags.rs (renamed from src/librustdoc/passes/html_tags.rs)309
-rw-r--r--src/librustdoc/passes/mod.rs18
-rw-r--r--src/librustdoc/passes/strip_hidden.rs1
-rw-r--r--src/librustdoc/passes/strip_priv_imports.rs4
-rw-r--r--src/librustdoc/passes/strip_private.rs4
-rw-r--r--src/librustdoc/passes/stripper.rs49
-rw-r--r--src/librustdoc/scrape_examples.rs10
-rw-r--r--src/librustdoc/visit_ast.rs112
-rw-r--r--src/librustdoc/visit_lib.rs108
62 files changed, 2459 insertions, 2644 deletions
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index 7bc35c7d5..0271c27b4 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -9,7 +9,6 @@ path = "lib.rs"
[dependencies]
arrayvec = { version = "0.7", default-features = false }
askama = { version = "0.11", default-features = false, features = ["config"] }
-atty = "0.2"
itertools = "0.10.1"
minifier = "0.2.2"
once_cell = "1.10.0"
@@ -20,7 +19,7 @@ serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
smallvec = "1.8.1"
tempfile = "3"
-thin-vec = "0.2.8"
+thin-vec = "0.2.9"
tracing = "0.1"
tracing-tree = "0.2.0"
@@ -33,7 +32,7 @@ features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"]
rayon = "1.5.1"
[dev-dependencies]
-expect-test = "1.0"
+expect-test = "1.4.0"
[features]
jemalloc = []
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index 764a6d3aa..953f4aa8a 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -3,6 +3,7 @@ use rustc_hir as hir;
use rustc_hir::lang_items::LangItem;
use rustc_middle::ty::{self, Region, RegionVid, TypeFoldable, TypeSuperFoldable};
use rustc_trait_selection::traits::auto_trait::{self, AutoTraitResult};
+use thin_vec::ThinVec;
use std::fmt::Debug;
@@ -43,7 +44,7 @@ where
discard_positive_impl: bool,
) -> Option<Item> {
let tcx = self.cx.tcx;
- let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, &[]) };
+ let trait_ref = tcx.mk_trait_ref(trait_def_id, [ty]);
if !self.cx.generated_synthetics.insert((ty, trait_def_id)) {
debug!("get_auto_trait_impl_for({:?}): already generated, aborting", trait_ref);
return None;
@@ -110,7 +111,7 @@ where
);
let params = raw_generics.params;
- Generics { params, where_predicates: Vec::new() }
+ Generics { params, where_predicates: ThinVec::new() }
}
AutoTraitResult::ExplicitImpl => return None,
};
@@ -118,7 +119,6 @@ where
Some(Item {
name: None,
attrs: Default::default(),
- visibility: Inherited,
item_id: ItemId::Auto { trait_: trait_def_id, for_: item_def_id },
kind: Box::new(ImplItem(Box::new(Impl {
unsafety: hir::Unsafety::Normal,
@@ -130,6 +130,7 @@ where
kind: ImplKind::Auto,
}))),
cfg: None,
+ inline_stmt_id: None,
})
}
@@ -183,7 +184,7 @@ where
fn handle_lifetimes<'cx>(
regions: &RegionConstraintData<'cx>,
names_map: &FxHashMap<Symbol, Lifetime>,
- ) -> Vec<WherePredicate> {
+ ) -> ThinVec<WherePredicate> {
// Our goal is to 'flatten' the list of constraints by eliminating
// all intermediate RegionVids. At the end, all constraints should
// be between Regions (aka region variables). This gives us the information
@@ -320,10 +321,10 @@ where
let bound_predicate = pred.kind();
let tcx = self.cx.tcx;
let regions = match bound_predicate.skip_binder() {
- ty::PredicateKind::Trait(poly_trait_pred) => {
+ ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_pred)) => {
tcx.collect_referenced_late_bound_regions(&bound_predicate.rebind(poly_trait_pred))
}
- ty::PredicateKind::Projection(poly_proj_pred) => {
+ ty::PredicateKind::Clause(ty::Clause::Projection(poly_proj_pred)) => {
tcx.collect_referenced_late_bound_regions(&bound_predicate.rebind(poly_proj_pred))
}
_ => return FxHashSet::default(),
@@ -335,10 +336,7 @@ where
match br {
// We only care about named late bound regions, as we need to add them
// to the 'for<>' section
- ty::BrNamed(_, name) => Some(GenericParamDef {
- name,
- kind: GenericParamDefKind::Lifetime { outlives: vec![] },
- }),
+ ty::BrNamed(_, name) => Some(GenericParamDef::lifetime(name)),
_ => None,
}
})
@@ -429,7 +427,7 @@ where
&mut self,
item_def_id: DefId,
param_env: ty::ParamEnv<'tcx>,
- mut existing_predicates: Vec<WherePredicate>,
+ mut existing_predicates: ThinVec<WherePredicate>,
vid_to_region: FxHashMap<ty::RegionVid, ty::Region<'tcx>>,
) -> Generics {
debug!(
@@ -453,7 +451,9 @@ where
.filter(|p| {
!orig_bounds.contains(p)
|| match p.kind().skip_binder() {
- ty::PredicateKind::Trait(pred) => pred.def_id() == sized_trait,
+ ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => {
+ pred.def_id() == sized_trait
+ }
_ => false,
}
})
@@ -663,7 +663,7 @@ where
/// both for visual consistency between 'rustdoc' runs, and to
/// make writing tests much easier
#[inline]
- fn sort_where_predicates(&self, predicates: &mut Vec<WherePredicate>) {
+ fn sort_where_predicates(&self, predicates: &mut [WherePredicate]) {
// We should never have identical bounds - and if we do,
// they're visually identical as well. Therefore, using
// an unstable sort is fine.
@@ -710,7 +710,7 @@ where
/// approach is probably somewhat slower, but the small number of items
/// involved (impls rarely have more than a few bounds) means that it
/// shouldn't matter in practice.
- fn unstable_debug_sort<T: Debug>(&self, vec: &mut Vec<T>) {
+ fn unstable_debug_sort<T: Debug>(&self, vec: &mut [T]) {
vec.sort_by_cached_key(|x| format!("{:?}", x))
}
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index 8b63c3db3..a1145b90d 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -20,7 +20,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
trace!("get_blanket_impls({:?})", ty);
let mut impls = Vec::new();
for trait_def_id in cx.tcx.all_traits() {
- if !cx.cache.effective_visibilities.is_directly_public(trait_def_id)
+ if !cx.cache.effective_visibilities.is_directly_public(cx.tcx, trait_def_id)
|| cx.generated_synthetics.get(&(ty.0, trait_def_id)).is_some()
{
continue;
@@ -67,15 +67,11 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
.instantiate(cx.tcx, impl_substs)
.predicates
.into_iter()
- .chain(Some(
- ty::Binder::dummy(impl_trait_ref)
- .to_poly_trait_predicate()
- .map_bound(ty::PredicateKind::Trait)
- .to_predicate(infcx.tcx),
- ));
+ .chain(Some(ty::Binder::dummy(impl_trait_ref).to_predicate(infcx.tcx)));
for predicate in predicates {
debug!("testing predicate {:?}", predicate);
let obligation = traits::Obligation::new(
+ infcx.tcx,
traits::ObligationCause::dummy(),
param_env,
predicate,
@@ -97,7 +93,6 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
impls.push(Item {
name: None,
attrs: Default::default(),
- visibility: Inherited,
item_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id },
kind: Box::new(ImplItem(Box::new(Impl {
unsafety: hir::Unsafety::Normal,
@@ -128,6 +123,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
))),
}))),
cfg: None,
+ inline_stmt_id: None,
});
}
}
diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs
index f33f5d27d..1843a2120 100644
--- a/src/librustdoc/clean/cfg.rs
+++ b/src/librustdoc/clean/cfg.rs
@@ -50,7 +50,7 @@ impl Cfg {
) -> Result<Option<Cfg>, InvalidCfgError> {
match nested_cfg {
NestedMetaItem::MetaItem(ref cfg) => Cfg::parse_without(cfg, exclude),
- NestedMetaItem::Literal(ref lit) => {
+ NestedMetaItem::Lit(ref lit) => {
Err(InvalidCfgError { msg: "unexpected literal", span: lit.span })
}
}
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 4e2031a91..e7c3e5a45 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -3,7 +3,7 @@
use std::iter::once;
use std::sync::Arc;
-use thin_vec::ThinVec;
+use thin_vec::{thin_vec, ThinVec};
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashSet;
@@ -19,8 +19,7 @@ use rustc_span::symbol::{kw, sym, Symbol};
use crate::clean::{
self, clean_fn_decl_from_did_and_sig, clean_generics, clean_impl_item, clean_middle_assoc_item,
clean_middle_field, clean_middle_ty, clean_trait_ref_with_bindings, clean_ty,
- clean_ty_generics, clean_variant_def, clean_visibility, utils, Attributes, AttributesExt,
- ImplKind, ItemId, Type, Visibility,
+ clean_ty_generics, clean_variant_def, utils, Attributes, AttributesExt, ImplKind, ItemId, Type,
};
use crate::core::DocContext;
use crate::formats::item_type::ItemType;
@@ -152,18 +151,10 @@ pub(crate) fn try_inline(
let (attrs, cfg) = merge_attrs(cx, Some(parent_module), load_attrs(cx, did), attrs);
cx.inlined.insert(did.into());
- let mut item = clean::Item::from_def_id_and_attrs_and_parts(
- did,
- Some(name),
- kind,
- Box::new(attrs),
- cx,
- cfg,
- );
- if let Some(import_def_id) = import_def_id {
- // The visibility needs to reflect the one from the reexport and not from the "source" DefId.
- item.visibility = clean_visibility(cx.tcx.visibility(import_def_id));
- }
+ let mut item =
+ clean::Item::from_def_id_and_attrs_and_parts(did, Some(name), kind, Box::new(attrs), cfg);
+ // The visibility needs to reflect the one from the reexport and not from the "source" DefId.
+ item.inline_stmt_id = import_def_id;
ret.push(item);
Some(ret)
}
@@ -239,13 +230,7 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean
.tcx
.associated_items(did)
.in_definition_order()
- .map(|item| {
- // When building an external trait, the cleaned trait will have all items public,
- // which causes methods to have a `pub` prefix, which is invalid since items in traits
- // can not have a visibility prefix. Thus we override the visibility here manually.
- // See https://github.com/rust-lang/rust/issues/81274
- clean::Item { visibility: Visibility::Inherited, ..clean_middle_assoc_item(item, cx) }
- })
+ .map(|item| clean_middle_assoc_item(item, cx))
.collect();
let predicates = cx.tcx.predicates_of(did);
@@ -258,10 +243,19 @@ 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);
- let predicates = cx.tcx.predicates_of(did);
+ 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 generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates);
+ 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);
let decl = clean_fn_decl_from_did_and_sig(cx, Some(did), sig);
(generics, decl)
});
@@ -282,7 +276,7 @@ fn build_struct(cx: &mut DocContext<'_>, did: DefId) -> clean::Struct {
let variant = cx.tcx.adt_def(did).non_enum_variant();
clean::Struct {
- struct_type: variant.ctor_kind,
+ ctor_kind: variant.ctor_kind(),
generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
fields: variant.fields.iter().map(|x| clean_middle_field(x, cx)).collect(),
}
@@ -389,7 +383,7 @@ pub(crate) fn build_impl(
if !did.is_local() {
if let Some(traitref) = associated_trait {
let did = traitref.def_id;
- if !cx.cache.effective_visibilities.is_directly_public(did) {
+ if !cx.cache.effective_visibilities.is_directly_public(tcx, did) {
return;
}
@@ -418,7 +412,7 @@ pub(crate) fn build_impl(
// reachable in rustdoc generated documentation
if !did.is_local() {
if let Some(did) = for_.def_id(&cx.cache) {
- if !cx.cache.effective_visibilities.is_directly_public(did) {
+ if !cx.cache.effective_visibilities.is_directly_public(tcx, did) {
return;
}
@@ -559,7 +553,6 @@ pub(crate) fn build_impl(
},
})),
Box::new(merged_attrs),
- cx,
cfg,
));
}
@@ -607,13 +600,12 @@ fn build_module_items(
name: None,
attrs: Box::new(clean::Attributes::default()),
item_id: ItemId::Primitive(prim_ty, did.krate),
- visibility: clean::Public,
kind: Box::new(clean::ImportItem(clean::Import::new_simple(
item.ident.name,
clean::ImportSource {
path: clean::Path {
res,
- segments: vec![clean::PathSegment {
+ segments: thin_vec![clean::PathSegment {
name: prim_ty.as_sym(),
args: clean::GenericArgs::AngleBracketed {
args: Default::default(),
@@ -626,6 +618,7 @@ fn build_module_items(
true,
))),
cfg: None,
+ inline_stmt_id: None,
});
} else if let Some(i) = try_inline(cx, did, None, res, item.ident.name, None, visited) {
items.extend(i)
@@ -669,7 +662,7 @@ fn build_macro(
match CStore::from_tcx(cx.tcx).load_macro_untracked(def_id, cx.sess()) {
LoadedMacro::MacroDef(item_def, _) => {
if let ast::ItemKind::MacroDef(ref def) = item_def.kind {
- let vis = clean_visibility(cx.tcx.visibility(import_def_id.unwrap_or(def_id)));
+ let vis = cx.tcx.visibility(import_def_id.unwrap_or(def_id));
clean::MacroItem(clean::Macro {
source: utils::display_macro_source(cx, name, def, def_id, vis),
})
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 64a18757b..2a2a9470d 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -12,7 +12,7 @@ pub(crate) mod utils;
use rustc_ast as ast;
use rustc_attr as attr;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, IndexEntry};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
@@ -74,12 +74,12 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
// This covers the case where somebody does an import which should pull in an item,
// but there's already an item with the same namespace and same name. Rust gives
// priority to the not-imported one, so we should, too.
- items.extend(doc.items.iter().flat_map(|(item, renamed)| {
+ items.extend(doc.items.iter().flat_map(|(item, renamed, import_id)| {
// First, lower everything other than imports.
if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
return Vec::new();
}
- let v = clean_maybe_renamed_item(cx, item, *renamed);
+ let v = clean_maybe_renamed_item(cx, item, *renamed, *import_id);
for item in &v {
if let Some(name) = item.name && !item.attrs.lists(sym::doc).has_word(sym::hidden) {
inserted.insert((item.type_(), name));
@@ -87,7 +87,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
}
v
}));
- items.extend(doc.items.iter().flat_map(|(item, renamed)| {
+ items.extend(doc.items.iter().flat_map(|(item, renamed, _)| {
// Now we actually lower the imports, skipping everything else.
if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind {
let name = renamed.unwrap_or_else(|| cx.tcx.hir().name(item.hir_id()));
@@ -182,10 +182,7 @@ fn clean_poly_trait_ref_with_bindings<'tcx>(
.collect_referenced_late_bound_regions(&poly_trait_ref)
.into_iter()
.filter_map(|br| match br {
- ty::BrNamed(_, name) if name != kw::UnderscoreLifetime => Some(GenericParamDef {
- name,
- kind: GenericParamDefKind::Lifetime { outlives: vec![] },
- }),
+ ty::BrNamed(_, name) if br.is_named() => Some(GenericParamDef::lifetime(name)),
_ => None,
})
.collect();
@@ -209,7 +206,7 @@ fn clean_lifetime<'tcx>(lifetime: &hir::Lifetime, cx: &mut DocContext<'tcx>) ->
return lt;
}
}
- Lifetime(lifetime.name.ident().name)
+ Lifetime(lifetime.ident.name)
}
pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg, cx: &mut DocContext<'tcx>) -> Constant {
@@ -227,23 +224,18 @@ pub(crate) fn clean_middle_const<'tcx>(
// FIXME: instead of storing the stringified expression, store `self` directly instead.
Constant {
type_: clean_middle_ty(constant.ty(), cx, None),
- kind: ConstantKind::TyConst { expr: constant.to_string() },
+ kind: ConstantKind::TyConst { expr: constant.to_string().into() },
}
}
pub(crate) fn clean_middle_region<'tcx>(region: ty::Region<'tcx>) -> Option<Lifetime> {
match *region {
ty::ReStatic => Some(Lifetime::statik()),
+ _ if !region.has_name() => None,
ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) => {
- if name != kw::UnderscoreLifetime { Some(Lifetime(name)) } else { None }
- }
- ty::ReEarlyBound(ref data) => {
- if data.name != kw::UnderscoreLifetime {
- Some(Lifetime(data.name))
- } else {
- None
- }
+ Some(Lifetime(name))
}
+ ty::ReEarlyBound(ref data) => Some(Lifetime(data.name)),
ty::ReLateBound(..)
| ty::ReFree(..)
| ty::ReVar(..)
@@ -303,12 +295,16 @@ pub(crate) fn clean_predicate<'tcx>(
) -> Option<WherePredicate> {
let bound_predicate = predicate.kind();
match bound_predicate.skip_binder() {
- ty::PredicateKind::Trait(pred) => {
+ ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => {
clean_poly_trait_predicate(bound_predicate.rebind(pred), cx)
}
- ty::PredicateKind::RegionOutlives(pred) => clean_region_outlives_predicate(pred),
- ty::PredicateKind::TypeOutlives(pred) => clean_type_outlives_predicate(pred, cx),
- ty::PredicateKind::Projection(pred) => {
+ ty::PredicateKind::Clause(ty::Clause::RegionOutlives(pred)) => {
+ clean_region_outlives_predicate(pred)
+ }
+ ty::PredicateKind::Clause(ty::Clause::TypeOutlives(pred)) => {
+ clean_type_outlives_predicate(pred, cx)
+ }
+ ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => {
Some(clean_projection_predicate(bound_predicate.rebind(pred), cx))
}
ty::PredicateKind::ConstEvaluatable(..) => None,
@@ -319,6 +315,7 @@ pub(crate) fn clean_predicate<'tcx>(
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::ConstEquate(..)
+ | ty::PredicateKind::Ambiguous
| ty::PredicateKind::TypeWellFormedFromEnv(..) => panic!("not user writable"),
}
}
@@ -396,7 +393,7 @@ fn clean_projection_predicate<'tcx>(
.collect_referenced_late_bound_regions(&pred)
.into_iter()
.filter_map(|br| match br {
- ty::BrNamed(_, name) if name != kw::UnderscoreLifetime => Some(Lifetime(name)),
+ ty::BrNamed(_, name) if br.is_named() => Some(Lifetime(name)),
_ => None,
})
.collect();
@@ -601,47 +598,105 @@ pub(crate) fn clean_generics<'tcx>(
})
.collect::<Vec<_>>();
- let mut params = Vec::with_capacity(gens.params.len());
- for p in gens.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) {
- let p = clean_generic_param(cx, Some(gens), p);
- params.push(p);
+ let mut bound_predicates = FxIndexMap::default();
+ let mut region_predicates = FxIndexMap::default();
+ let mut eq_predicates = ThinVec::default();
+ for pred in gens.predicates.iter().filter_map(|x| clean_where_predicate(x, cx)) {
+ match pred {
+ WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
+ match bound_predicates.entry(ty) {
+ IndexEntry::Vacant(v) => {
+ v.insert((bounds, bound_params));
+ }
+ IndexEntry::Occupied(mut o) => {
+ // we merge both bounds.
+ for bound in bounds {
+ if !o.get().0.contains(&bound) {
+ o.get_mut().0.push(bound);
+ }
+ }
+ for bound_param in bound_params {
+ if !o.get().1.contains(&bound_param) {
+ o.get_mut().1.push(bound_param);
+ }
+ }
+ }
+ }
+ }
+ WherePredicate::RegionPredicate { lifetime, bounds } => {
+ match region_predicates.entry(lifetime) {
+ IndexEntry::Vacant(v) => {
+ v.insert(bounds);
+ }
+ IndexEntry::Occupied(mut o) => {
+ // we merge both bounds.
+ for bound in bounds {
+ if !o.get().contains(&bound) {
+ o.get_mut().push(bound);
+ }
+ }
+ }
+ }
+ }
+ WherePredicate::EqPredicate { lhs, rhs, bound_params } => {
+ eq_predicates.push(WherePredicate::EqPredicate { lhs, rhs, bound_params });
+ }
+ }
}
- params.extend(impl_trait_params);
- let mut generics = Generics {
- params,
- where_predicates: gens
- .predicates
- .iter()
- .filter_map(|x| clean_where_predicate(x, cx))
- .collect(),
- };
-
- // Some duplicates are generated for ?Sized bounds between type params and where
- // predicates. The point in here is to move the bounds definitions from type params
- // to where predicates when such cases occur.
- for where_pred in &mut generics.where_predicates {
- match *where_pred {
- WherePredicate::BoundPredicate { ty: Generic(ref name), ref mut bounds, .. } => {
- if bounds.is_empty() {
- for param in &mut generics.params {
- match param.kind {
- GenericParamDefKind::Lifetime { .. } => {}
- GenericParamDefKind::Type { bounds: ref mut ty_bounds, .. } => {
- if &param.name == name {
- mem::swap(bounds, ty_bounds);
- break;
- }
- }
- GenericParamDefKind::Const { .. } => {}
+ let mut params = ThinVec::with_capacity(gens.params.len());
+ // In this loop, we gather the generic parameters (`<'a, B: 'a>`) and check if they have
+ // bounds in the where predicates. If so, we move their bounds into the where predicates
+ // while also preventing duplicates.
+ for p in gens.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) {
+ let mut p = clean_generic_param(cx, Some(gens), p);
+ match &mut p.kind {
+ GenericParamDefKind::Lifetime { ref mut outlives } => {
+ if let Some(region_pred) = region_predicates.get_mut(&Lifetime(p.name)) {
+ // We merge bounds in the `where` clause.
+ for outlive in outlives.drain(..) {
+ let outlive = GenericBound::Outlives(outlive);
+ if !region_pred.contains(&outlive) {
+ region_pred.push(outlive);
+ }
+ }
+ }
+ }
+ GenericParamDefKind::Type { bounds, synthetic: false, .. } => {
+ if let Some(bound_pred) = bound_predicates.get_mut(&Type::Generic(p.name)) {
+ // We merge bounds in the `where` clause.
+ for bound in bounds.drain(..) {
+ if !bound_pred.0.contains(&bound) {
+ bound_pred.0.push(bound);
}
}
}
}
- _ => continue,
+ GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
+ // nothing to do here.
+ }
}
+ params.push(p);
+ }
+ params.extend(impl_trait_params);
+
+ Generics {
+ params,
+ where_predicates: bound_predicates
+ .into_iter()
+ .map(|(ty, (bounds, bound_params))| WherePredicate::BoundPredicate {
+ ty,
+ bounds,
+ bound_params,
+ })
+ .chain(
+ region_predicates
+ .into_iter()
+ .map(|(lifetime, bounds)| WherePredicate::RegionPredicate { lifetime, bounds }),
+ )
+ .chain(eq_predicates.into_iter())
+ .collect(),
}
- generics
}
fn clean_ty_generics<'tcx>(
@@ -660,7 +715,7 @@ fn clean_ty_generics<'tcx>(
.params
.iter()
.filter_map(|param| match param.kind {
- ty::GenericParamDefKind::Lifetime if param.name == kw::UnderscoreLifetime => None,
+ ty::GenericParamDefKind::Lifetime if param.is_anonymous_lifetime() => None,
ty::GenericParamDefKind::Lifetime => Some(clean_generic_param_def(param, cx)),
ty::GenericParamDefKind::Type { synthetic, .. } => {
if param.name == kw::SelfUpper {
@@ -675,7 +730,7 @@ fn clean_ty_generics<'tcx>(
}
ty::GenericParamDefKind::Const { .. } => Some(clean_generic_param_def(param, cx)),
})
- .collect::<Vec<GenericParamDef>>();
+ .collect::<ThinVec<GenericParamDef>>();
// param index -> [(trait DefId, associated type name & generics, type, higher-ranked params)]
let mut impl_trait_proj =
@@ -689,17 +744,20 @@ fn clean_ty_generics<'tcx>(
let param_idx = (|| {
let bound_p = p.kind();
match bound_p.skip_binder() {
- ty::PredicateKind::Trait(pred) => {
+ ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => {
if let ty::Param(param) = pred.self_ty().kind() {
return Some(param.index);
}
}
- ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => {
+ ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
+ ty,
+ _reg,
+ ))) => {
if let ty::Param(param) = ty.kind() {
return Some(param.index);
}
}
- ty::PredicateKind::Projection(p) => {
+ ty::PredicateKind::Clause(ty::Clause::Projection(p)) => {
if let ty::Param(param) = p.projection_ty.self_ty().kind() {
projection = Some(bound_p.rebind(p));
return Some(param.index);
@@ -741,10 +799,7 @@ fn clean_ty_generics<'tcx>(
p.get_bound_params()
.into_iter()
.flatten()
- .map(|param| GenericParamDef {
- name: param.0,
- kind: GenericParamDefKind::Lifetime { outlives: Vec::new() },
- })
+ .map(|param| GenericParamDef::lifetime(param.0))
.collect(),
));
}
@@ -880,7 +935,7 @@ fn clean_fn_or_proc_macro<'tcx>(
ProcMacroItem(ProcMacro { kind, helpers })
}
None => {
- let mut func = clean_function(cx, sig, generics, body_id);
+ let mut func = clean_function(cx, sig, generics, FunctionArgs::Body(body_id));
clean_fn_decl_legacy_const_generics(&mut func, attrs);
FunctionItem(func)
}
@@ -896,7 +951,7 @@ fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[ast::Attrib
.filter(|a| a.has_name(sym::rustc_legacy_const_generics))
.filter_map(|a| a.meta_item_list())
{
- for (pos, literal) in meta_item_list.iter().filter_map(|meta| meta.literal()).enumerate() {
+ for (pos, literal) in meta_item_list.iter().filter_map(|meta| meta.lit()).enumerate() {
match literal.kind {
ast::LitKind::Int(a, _) => {
let gen = func.generics.params.remove(0);
@@ -917,16 +972,28 @@ fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[ast::Attrib
}
}
+enum FunctionArgs<'tcx> {
+ Body(hir::BodyId),
+ Names(&'tcx [Ident]),
+}
+
fn clean_function<'tcx>(
cx: &mut DocContext<'tcx>,
sig: &hir::FnSig<'tcx>,
generics: &hir::Generics<'tcx>,
- body_id: hir::BodyId,
+ args: FunctionArgs<'tcx>,
) -> Box<Function> {
let (generics, decl) = enter_impl_trait(cx, |cx| {
// NOTE: generics must be cleaned before args
let generics = clean_generics(generics, cx);
- let args = clean_args_from_types_and_body_id(cx, sig.decl.inputs, body_id);
+ let args = match args {
+ FunctionArgs::Body(body_id) => {
+ clean_args_from_types_and_body_id(cx, sig.decl.inputs, body_id)
+ }
+ FunctionArgs::Names(names) => {
+ clean_args_from_types_and_names(cx, sig.decl.inputs, names)
+ }
+ };
let mut decl = clean_fn_decl_with_args(cx, sig.decl, args);
if sig.header.is_async() {
decl.output = decl.sugared_async_return_type();
@@ -945,12 +1012,14 @@ fn clean_args_from_types_and_names<'tcx>(
values: types
.iter()
.enumerate()
- .map(|(i, ty)| {
- let mut name = names.get(i).map_or(kw::Empty, |ident| ident.name);
- if name.is_empty() {
- name = kw::Underscore;
- }
- Argument { name, type_: clean_ty(ty, cx), is_const: false }
+ .map(|(i, ty)| Argument {
+ type_: clean_ty(ty, cx),
+ name: names
+ .get(i)
+ .map(|ident| ident.name)
+ .filter(|ident| !ident.is_empty())
+ .unwrap_or(kw::Underscore),
+ is_const: false,
})
.collect(),
}
@@ -1012,7 +1081,11 @@ fn clean_fn_decl_from_did_and_sig<'tcx>(
.iter()
.map(|t| Argument {
type_: clean_middle_ty(*t, cx, None),
- name: names.next().map_or(kw::Empty, |i| i.name),
+ name: names
+ .next()
+ .map(|i| i.name)
+ .filter(|i| !i.is_empty())
+ .unwrap_or(kw::Underscore),
is_const: false,
})
.collect(),
@@ -1051,18 +1124,12 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
),
hir::TraitItemKind::Const(ty, None) => TyAssocConstItem(clean_ty(ty, cx)),
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
- let m = clean_function(cx, sig, trait_item.generics, body);
+ let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Body(body));
MethodItem(m, None)
}
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(names)) => {
- let (generics, decl) = enter_impl_trait(cx, |cx| {
- // NOTE: generics must be cleaned before args
- let generics = clean_generics(trait_item.generics, cx);
- let args = clean_args_from_types_and_names(cx, sig.decl.inputs, names);
- let decl = clean_fn_decl_with_args(cx, sig.decl, args);
- (generics, decl)
- });
- TyMethodItem(Box::new(Function { decl, generics }))
+ let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Names(names));
+ TyMethodItem(m)
}
hir::TraitItemKind::Type(bounds, Some(default)) => {
let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
@@ -1080,13 +1147,10 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
hir::TraitItemKind::Type(bounds, None) => {
let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect();
- TyAssocTypeItem(Box::new(generics), bounds)
+ TyAssocTypeItem(generics, bounds)
}
};
- let what_rustc_thinks =
- Item::from_def_id_and_parts(local_did, Some(trait_item.ident.name), inner, cx);
- // Trait items always inherit the trait's visibility -- we don't want to show `pub`.
- Item { visibility: Inherited, ..what_rustc_thinks }
+ Item::from_def_id_and_parts(local_did, Some(trait_item.ident.name), inner, cx)
})
}
@@ -1102,7 +1166,7 @@ pub(crate) fn clean_impl_item<'tcx>(
AssocConstItem(clean_ty(ty, cx), default)
}
hir::ImplItemKind::Fn(ref sig, body) => {
- let m = clean_function(cx, sig, impl_.generics, body);
+ let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body));
let defaultness = cx.tcx.impl_defaultness(impl_.owner_id);
MethodItem(m, Some(defaultness))
}
@@ -1117,18 +1181,7 @@ pub(crate) fn clean_impl_item<'tcx>(
}
};
- let mut what_rustc_thinks =
- Item::from_def_id_and_parts(local_did, Some(impl_.ident.name), inner, cx);
-
- let impl_ref = cx.tcx.impl_trait_ref(cx.tcx.local_parent(impl_.owner_id.def_id));
-
- // Trait impl items always inherit the impl's visibility --
- // we don't want to show `pub`.
- if impl_ref.is_some() {
- what_rustc_thinks.visibility = Inherited;
- }
-
- what_rustc_thinks
+ Item::from_def_id_and_parts(local_did, Some(impl_.ident.name), inner, cx)
})
}
@@ -1152,12 +1205,25 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
}
}
ty::AssocKind::Fn => {
- let generics = clean_ty_generics(
+ let sig = tcx.fn_sig(assoc_item.def_id);
+
+ 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),
);
- let sig = tcx.fn_sig(assoc_item.def_id);
+ // FIXME: This does not place parameters in source order (late-bound ones come last)
+ generics.params.extend(late_bound_regions);
+
let mut decl = clean_fn_decl_from_did_and_sig(cx, Some(assoc_item.def_id), sig);
if assoc_item.fn_has_self_parameter {
@@ -1208,7 +1274,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
true
}
(GenericParamDefKind::Const { .. }, GenericArg::Const(c)) => match &c.kind {
- ConstantKind::TyConst { expr } => expr == param.name.as_str(),
+ ConstantKind::TyConst { expr } => **expr == *param.name.as_str(),
_ => false,
},
_ => false,
@@ -1225,56 +1291,47 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
tcx.generics_of(assoc_item.def_id),
ty::GenericPredicates { parent: None, predicates },
);
- // Move bounds that are (likely) directly attached to the associated type
- // from the where clause to the associated type.
- // There is no guarantee that this is what the user actually wrote but we have
- // no way of knowing.
- let mut bounds = generics
- .where_predicates
- .drain_filter(|pred| match *pred {
- WherePredicate::BoundPredicate {
- ty: QPath(box QPathData { ref assoc, ref self_type, ref trait_, .. }),
- ..
- } => {
- if assoc.name != my_name {
- return false;
- }
- if trait_.def_id() != assoc_item.container_id(tcx) {
- return false;
- }
- match *self_type {
- Generic(ref s) if *s == kw::SelfUpper => {}
- _ => return false,
- }
- match &assoc.args {
- GenericArgs::AngleBracketed { args, bindings } => {
- if !bindings.is_empty()
- || generics
- .params
- .iter()
- .zip(args.iter())
- .any(|(param, arg)| !param_eq_arg(param, arg))
- {
- return false;
- }
- }
- GenericArgs::Parenthesized { .. } => {
- // The only time this happens is if we're inside the rustdoc for Fn(),
- // which only has one associated type, which is not a GAT, so whatever.
+ // Filter out the bounds that are (likely?) directly attached to the associated type,
+ // as opposed to being located in the where clause.
+ let mut bounds: Vec<GenericBound> = Vec::new();
+ generics.where_predicates.retain_mut(|pred| match *pred {
+ WherePredicate::BoundPredicate {
+ ty: QPath(box QPathData { ref assoc, ref self_type, ref trait_, .. }),
+ bounds: ref mut pred_bounds,
+ ..
+ } => {
+ if assoc.name != my_name {
+ return true;
+ }
+ if trait_.def_id() != assoc_item.container_id(tcx) {
+ return true;
+ }
+ match *self_type {
+ Generic(ref s) if *s == kw::SelfUpper => {}
+ _ => return true,
+ }
+ match &assoc.args {
+ GenericArgs::AngleBracketed { args, bindings } => {
+ if !bindings.is_empty()
+ || generics
+ .params
+ .iter()
+ .zip(args.iter())
+ .any(|(param, arg)| !param_eq_arg(param, arg))
+ {
+ return true;
}
}
- true
- }
- _ => false,
- })
- .flat_map(|pred| {
- if let WherePredicate::BoundPredicate { bounds, .. } = pred {
- bounds
- } else {
- unreachable!()
+ GenericArgs::Parenthesized { .. } => {
+ // The only time this happens is if we're inside the rustdoc for Fn(),
+ // which only has one associated type, which is not a GAT, so whatever.
+ }
}
- })
- .collect::<Vec<_>>();
+ bounds.extend(mem::replace(pred_bounds, Vec::new()));
+ false
+ }
+ _ => true,
+ });
// Our Sized/?Sized bound didn't get handled when creating the generics
// because we didn't actually get our whole set of bounds until just now
// (some of them may have come from the trait). If we do have a sized
@@ -1290,7 +1347,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
// (generic) associated type from the where clause to the respective parameter.
// There is no guarantee that this is what the user actually wrote but we have
// no way of knowing.
- let mut where_predicates = Vec::new();
+ let mut where_predicates = ThinVec::new();
for mut pred in generics.where_predicates {
if let WherePredicate::BoundPredicate { ty: Generic(arg), bounds, .. } = &mut pred
&& let Some(GenericParamDef {
@@ -1298,7 +1355,16 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
..
}) = generics.params.iter_mut().find(|param| &param.name == arg)
{
- param_bounds.extend(mem::take(bounds));
+ param_bounds.append(bounds);
+ } else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred
+ && let Some(GenericParamDef {
+ kind: GenericParamDefKind::Lifetime { outlives: param_bounds },
+ ..
+ }) = generics.params.iter_mut().find(|param| &param.name == arg) {
+ param_bounds.extend(bounds.drain(..).map(|bound| match bound {
+ GenericBound::Outlives(lifetime) => lifetime,
+ _ => unreachable!(),
+ }));
} else {
where_predicates.push(pred);
}
@@ -1320,7 +1386,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
bounds,
)
} else {
- TyAssocTypeItem(Box::new(generics), bounds)
+ TyAssocTypeItem(generics, bounds)
}
} else {
// FIXME: when could this happen? Associated items in inherent impls?
@@ -1331,7 +1397,10 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
cx,
Some(assoc_item.def_id),
),
- generics: Generics { params: Vec::new(), where_predicates: Vec::new() },
+ generics: Generics {
+ params: ThinVec::new(),
+ where_predicates: ThinVec::new(),
+ },
item_type: None,
}),
Vec::new(),
@@ -1340,18 +1409,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
}
};
- let mut what_rustc_thinks =
- Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name), kind, cx);
-
- let impl_ref = tcx.impl_trait_ref(tcx.parent(assoc_item.def_id));
-
- // Trait impl items always inherit the impl's visibility --
- // we don't want to show `pub`.
- if impl_ref.is_some() {
- what_rustc_thinks.visibility = Visibility::Inherited;
- }
-
- what_rustc_thinks
+ Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name), kind, cx)
}
fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type {
@@ -1406,7 +1464,8 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type
ty::Projection(proj) => Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id),
// Rustdoc handles `ty::Error`s by turning them into `Type::Infer`s.
ty::Error(_) => return Type::Infer,
- _ => bug!("clean: expected associated type, found `{:?}`", ty),
+ // Otherwise, this is an inherent associated type.
+ _ => return clean_middle_ty(ty, cx, None),
};
let trait_ = clean_path(&hir::Path { span, res, segments: &[] }, cx);
register_res(cx, trait_.res);
@@ -1431,7 +1490,7 @@ fn maybe_expand_private_type_alias<'tcx>(
let Res::Def(DefKind::TyAlias, def_id) = path.res else { return None };
// Substitute private type aliases
let def_id = def_id.as_local()?;
- let alias = if !cx.cache.effective_visibilities.is_exported(def_id.to_def_id()) {
+ let alias = if !cx.cache.effective_visibilities.is_exported(cx.tcx, def_id.to_def_id()) {
&cx.tcx.hir().expect_item(def_id).kind
} else {
return None;
@@ -1459,8 +1518,11 @@ fn maybe_expand_private_type_alias<'tcx>(
});
if let Some(lt) = lifetime {
let lt_def_id = cx.tcx.hir().local_def_id(param.hir_id);
- let cleaned =
- if !lt.is_elided() { clean_lifetime(lt, cx) } else { Lifetime::elided() };
+ let cleaned = if !lt.is_anonymous() {
+ clean_lifetime(lt, cx)
+ } else {
+ Lifetime::elided()
+ };
substs.insert(lt_def_id.to_def_id(), SubstParam::Lifetime(cleaned));
}
indices.lifetimes += 1;
@@ -1523,16 +1585,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
TyKind::Never => Primitive(PrimitiveType::Never),
TyKind::Ptr(ref m) => RawPointer(m.mutbl, Box::new(clean_ty(m.ty, cx))),
TyKind::Rptr(ref l, ref m) => {
- // There are two times a `Fresh` lifetime can be created:
- // 1. For `&'_ x`, written by the user. This corresponds to `lower_lifetime` in `rustc_ast_lowering`.
- // 2. For `&x` as a parameter to an `async fn`. This corresponds to `elided_ref_lifetime in `rustc_ast_lowering`.
- // See #59286 for more information.
- // Ideally we would only hide the `'_` for case 2., but I don't know a way to distinguish it.
- // Turning `fn f(&'_ self)` into `fn f(&self)` isn't the worst thing in the world, though;
- // there's no case where it could cause the function to fail to compile.
- let elided =
- l.is_elided() || matches!(l.name, LifetimeName::Param(_, ParamName::Fresh));
- let lifetime = if elided { None } else { Some(clean_lifetime(*l, cx)) };
+ let lifetime = if l.is_anonymous() { None } else { Some(clean_lifetime(*l, cx)) };
BorrowedRef { lifetime, mutability: m.mutbl, type_: Box::new(clean_ty(m.ty, cx)) }
}
TyKind::Slice(ty) => Slice(Box::new(clean_ty(ty, cx))),
@@ -1554,7 +1607,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
}
};
- Array(Box::new(clean_ty(ty, cx)), length)
+ Array(Box::new(clean_ty(ty, cx)), length.into())
}
TyKind::Tup(tys) => Tuple(tys.iter().map(|ty| clean_ty(ty, cx)).collect()),
TyKind::OpaqueDef(item_id, _, _) => {
@@ -1586,14 +1639,14 @@ fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>>
}
use crate::rustc_trait_selection::infer::TyCtxtInferExt;
- use crate::rustc_trait_selection::traits::query::normalize::AtExt;
+ use crate::rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
use rustc_middle::traits::ObligationCause;
// Try to normalize `<X as Y>::T` to a type
let infcx = cx.tcx.infer_ctxt().build();
let normalized = infcx
.at(&ObligationCause::dummy(), cx.param_env)
- .normalize(ty)
+ .query_normalize(ty)
.map(|resolved| infcx.resolve_vars_if_possible(resolved.value));
match normalized {
Ok(normalized_value) => {
@@ -1626,7 +1679,7 @@ pub(crate) fn clean_middle_ty<'tcx>(
ty::Array(ty, mut n) => {
n = n.eval(cx.tcx, ty::ParamEnv::reveal_all());
let n = print_const(cx, n);
- Array(Box::new(clean_middle_ty(ty, cx, None)), n)
+ Array(Box::new(clean_middle_ty(ty, cx, None)), n.into())
}
ty::RawPtr(mt) => RawPointer(mt.mutbl, Box::new(clean_middle_ty(mt.ty, cx, None))),
ty::Ref(r, ty, mutbl) => BorrowedRef {
@@ -1677,6 +1730,9 @@ pub(crate) fn clean_middle_ty<'tcx>(
inline::record_extern_fqn(cx, did, ItemType::Trait);
+ // FIXME(fmease): Hide the trait-object lifetime bound if it coincides with its default
+ // to partially address #44306. Follow the rules outlined at
+ // https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes
let lifetime = clean_middle_region(*reg);
let mut bounds = dids
.map(|did| {
@@ -1704,8 +1760,22 @@ pub(crate) fn clean_middle_ty<'tcx>(
})
.collect();
+ let late_bound_regions: FxIndexSet<_> = obj
+ .iter()
+ .flat_map(|pb| pb.bound_vars())
+ .filter_map(|br| match br {
+ ty::BoundVariableKind::Region(ty::BrNamed(_, name))
+ if name != kw::UnderscoreLifetime =>
+ {
+ Some(GenericParamDef::lifetime(name))
+ }
+ _ => None,
+ })
+ .collect();
+ let late_bound_regions = late_bound_regions.into_iter().collect();
+
let path = external_path(cx, did, false, bindings, substs);
- bounds.insert(0, PolyTrait { trait_: path, generic_params: Vec::new() });
+ bounds.insert(0, PolyTrait { trait_: path, generic_params: late_bound_regions });
DynTrait(bounds, lifetime)
}
@@ -1754,8 +1824,13 @@ fn clean_middle_opaque_bounds<'tcx>(
.filter_map(|bound| {
let bound_predicate = bound.kind();
let trait_ref = match bound_predicate.skip_binder() {
- ty::PredicateKind::Trait(tr) => bound_predicate.rebind(tr.trait_ref),
- ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => {
+ ty::PredicateKind::Clause(ty::Clause::Trait(tr)) => {
+ bound_predicate.rebind(tr.trait_ref)
+ }
+ ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
+ _ty,
+ reg,
+ ))) => {
if let Some(r) = clean_middle_region(reg) {
regions.push(GenericBound::Outlives(r));
}
@@ -1774,7 +1849,9 @@ fn clean_middle_opaque_bounds<'tcx>(
let bindings: ThinVec<_> = bounds
.iter()
.filter_map(|bound| {
- if let ty::PredicateKind::Projection(proj) = bound.kind().skip_binder() {
+ if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) =
+ bound.kind().skip_binder()
+ {
if proj.projection_ty.trait_ref(cx.tcx) == trait_ref.skip_binder() {
Some(TypeBinding {
assoc: projection_to_path_segment(proj.projection_ty, cx),
@@ -1821,50 +1898,24 @@ pub(crate) fn clean_field_with_def_id(
ty: Type,
cx: &mut DocContext<'_>,
) -> Item {
- let what_rustc_thinks =
- Item::from_def_id_and_parts(def_id, Some(name), StructFieldItem(ty), cx);
- if is_field_vis_inherited(cx.tcx, def_id) {
- // Variant fields inherit their enum's visibility.
- Item { visibility: Visibility::Inherited, ..what_rustc_thinks }
- } else {
- what_rustc_thinks
- }
-}
-
-fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
- let parent = tcx.parent(def_id);
- match tcx.def_kind(parent) {
- DefKind::Struct | DefKind::Union => false,
- DefKind::Variant => true,
- parent_kind => panic!("unexpected parent kind: {:?}", parent_kind),
- }
-}
-
-pub(crate) fn clean_visibility(vis: ty::Visibility<DefId>) -> Visibility {
- match vis {
- ty::Visibility::Public => Visibility::Public,
- ty::Visibility::Restricted(module) => Visibility::Restricted(module),
- }
+ Item::from_def_id_and_parts(def_id, Some(name), StructFieldItem(ty), cx)
}
pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocContext<'tcx>) -> Item {
- let kind = match variant.ctor_kind {
- CtorKind::Const => Variant::CLike(match variant.discr {
+ let kind = match variant.ctor_kind() {
+ Some(CtorKind::Const) => Variant::CLike(match variant.discr {
ty::VariantDiscr::Explicit(def_id) => Some(Discriminant { expr: None, value: def_id }),
ty::VariantDiscr::Relative(_) => None,
}),
- CtorKind::Fn => Variant::Tuple(
+ Some(CtorKind::Fn) => Variant::Tuple(
variant.fields.iter().map(|field| clean_middle_field(field, cx)).collect(),
),
- CtorKind::Fictive => Variant::Struct(VariantStruct {
- struct_type: CtorKind::Fictive,
+ None => Variant::Struct(VariantStruct {
+ ctor_kind: None,
fields: variant.fields.iter().map(|field| clean_middle_field(field, cx)).collect(),
}),
};
- let what_rustc_thinks =
- Item::from_def_id_and_parts(variant.def_id, Some(variant.name), VariantItem(kind), cx);
- // don't show `pub` for variants, which always inherit visibility
- Item { visibility: Inherited, ..what_rustc_thinks }
+ Item::from_def_id_and_parts(variant.def_id, Some(variant.name), VariantItem(kind), cx)
}
fn clean_variant_data<'tcx>(
@@ -1874,7 +1925,7 @@ fn clean_variant_data<'tcx>(
) -> Variant {
match variant {
hir::VariantData::Struct(..) => Variant::Struct(VariantStruct {
- struct_type: CtorKind::from_hir(variant),
+ ctor_kind: None,
fields: variant.fields().iter().map(|x| clean_field(x, cx)).collect(),
}),
hir::VariantData::Tuple(..) => {
@@ -1909,7 +1960,7 @@ fn clean_generic_args<'tcx>(
.args
.iter()
.map(|arg| match arg {
- hir::GenericArg::Lifetime(lt) if !lt.is_elided() => {
+ hir::GenericArg::Lifetime(lt) if !lt.is_anonymous() => {
GenericArg::Lifetime(clean_lifetime(*lt, cx))
}
hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()),
@@ -1951,10 +2002,84 @@ fn clean_bare_fn_ty<'tcx>(
BareFunctionDecl { unsafety: bare_fn.unsafety, abi: bare_fn.abi, decl, generic_params }
}
+/// This visitor is used to go through only the "top level" of a item and not enter any sub
+/// item while looking for a given `Ident` which is stored into `item` if found.
+struct OneLevelVisitor<'hir> {
+ map: rustc_middle::hir::map::Map<'hir>,
+ item: Option<&'hir hir::Item<'hir>>,
+ looking_for: Ident,
+ target_hir_id: hir::HirId,
+}
+
+impl<'hir> OneLevelVisitor<'hir> {
+ fn new(map: rustc_middle::hir::map::Map<'hir>, target_hir_id: hir::HirId) -> Self {
+ Self { map, item: None, looking_for: Ident::empty(), target_hir_id }
+ }
+
+ fn reset(&mut self, looking_for: Ident) {
+ self.looking_for = looking_for;
+ self.item = None;
+ }
+}
+
+impl<'hir> hir::intravisit::Visitor<'hir> for OneLevelVisitor<'hir> {
+ type NestedFilter = rustc_middle::hir::nested_filter::All;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.map
+ }
+
+ fn visit_item(&mut self, item: &'hir hir::Item<'hir>) {
+ if self.item.is_none()
+ && item.ident == self.looking_for
+ && matches!(item.kind, hir::ItemKind::Use(_, _))
+ || item.hir_id() == self.target_hir_id
+ {
+ self.item = Some(item);
+ }
+ }
+}
+
+/// Because a `Use` item directly links to the imported item, we need to manually go through each
+/// import one by one. To do so, we go to the parent item and look for the `Ident` into it. Then,
+/// if we found the "end item" (the imported one), we stop there because we don't need its
+/// documentation. Otherwise, we repeat the same operation until we find the "end item".
+fn get_all_import_attributes<'hir>(
+ mut item: &hir::Item<'hir>,
+ tcx: TyCtxt<'hir>,
+ target_hir_id: hir::HirId,
+ attributes: &mut Vec<ast::Attribute>,
+) {
+ let hir_map = tcx.hir();
+ let mut visitor = OneLevelVisitor::new(hir_map, target_hir_id);
+ // If the item is an import and has at least a path with two parts, we go into it.
+ while let hir::ItemKind::Use(path, _) = item.kind &&
+ path.segments.len() > 1 &&
+ let hir::def::Res::Def(_, def_id) = path.segments[path.segments.len() - 2].res
+ {
+ if let Some(hir::Node::Item(parent_item)) = hir_map.get_if_local(def_id) {
+ // We add the attributes from this import into the list.
+ attributes.extend_from_slice(hir_map.attrs(item.hir_id()));
+ // We get the `Ident` we will be looking for into `item`.
+ let looking_for = path.segments[path.segments.len() - 1].ident;
+ visitor.reset(looking_for);
+ hir::intravisit::walk_item(&mut visitor, parent_item);
+ if let Some(i) = visitor.item {
+ item = i;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+}
+
fn clean_maybe_renamed_item<'tcx>(
cx: &mut DocContext<'tcx>,
item: &hir::Item<'tcx>,
renamed: Option<Symbol>,
+ import_id: Option<hir::HirId>,
) -> Vec<Item> {
use hir::ItemKind;
@@ -1995,7 +2120,7 @@ fn clean_maybe_renamed_item<'tcx>(
fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(),
}),
ItemKind::Struct(ref variant_data, generics) => StructItem(Struct {
- struct_type: CtorKind::from_hir(variant_data),
+ ctor_kind: variant_data.ctor_kind(),
generics: clean_generics(generics, cx),
fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(),
}),
@@ -2005,7 +2130,7 @@ fn clean_maybe_renamed_item<'tcx>(
clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx)
}
ItemKind::Macro(ref macro_def, _) => {
- let ty_vis = clean_visibility(cx.tcx.visibility(def_id));
+ let ty_vis = cx.tcx.visibility(def_id);
MacroItem(Macro {
source: display_macro_source(cx, name, macro_def, def_id, ty_vis),
})
@@ -2032,16 +2157,35 @@ fn clean_maybe_renamed_item<'tcx>(
_ => unreachable!("not yet converted"),
};
- vec![Item::from_def_id_and_parts(def_id, Some(name), kind, cx)]
+ let mut extra_attrs = Vec::new();
+ if let Some(hir::Node::Item(use_node)) =
+ import_id.and_then(|hir_id| cx.tcx.hir().find(hir_id))
+ {
+ // We get all the various imports' attributes.
+ get_all_import_attributes(use_node, cx.tcx, item.hir_id(), &mut extra_attrs);
+ }
+
+ if !extra_attrs.is_empty() {
+ extra_attrs.extend_from_slice(inline::load_attrs(cx, def_id));
+ let attrs = Attributes::from_ast(&extra_attrs);
+ let cfg = extra_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg);
+
+ vec![Item::from_def_id_and_attrs_and_parts(
+ def_id,
+ Some(name),
+ kind,
+ Box::new(attrs),
+ cfg,
+ )]
+ } else {
+ vec![Item::from_def_id_and_parts(def_id, Some(name), kind, cx)]
+ }
})
}
fn clean_variant<'tcx>(variant: &hir::Variant<'tcx>, cx: &mut DocContext<'tcx>) -> Item {
let kind = VariantItem(clean_variant_data(&variant.data, &variant.disr_expr, cx));
- let what_rustc_thinks =
- Item::from_hir_id_and_parts(variant.id, Some(variant.ident.name), kind, cx);
- // don't show `pub` for variants, which are always public
- Item { visibility: Inherited, ..what_rustc_thinks }
+ Item::from_hir_id_and_parts(variant.hir_id, Some(variant.ident.name), kind, cx)
}
fn clean_impl<'tcx>(
@@ -2114,6 +2258,7 @@ fn clean_extern_crate<'tcx>(
}
});
+ let krate_owner_def_id = krate.owner_id.to_def_id();
if please_inline {
let mut visited = FxHashSet::default();
@@ -2122,7 +2267,7 @@ fn clean_extern_crate<'tcx>(
if let Some(items) = inline::try_inline(
cx,
cx.tcx.parent_module(krate.hir_id()).to_def_id(),
- Some(krate.owner_id.to_def_id()),
+ Some(krate_owner_def_id),
res,
name,
Some(attrs),
@@ -2137,15 +2282,35 @@ fn clean_extern_crate<'tcx>(
name: Some(name),
attrs: Box::new(Attributes::from_ast(attrs)),
item_id: crate_def_id.into(),
- visibility: clean_visibility(ty_vis),
kind: Box::new(ExternCrateItem { src: orig_name }),
cfg: attrs.cfg(cx.tcx, &cx.cache.hidden_cfg),
+ inline_stmt_id: Some(krate_owner_def_id),
}]
}
fn clean_use_statement<'tcx>(
import: &hir::Item<'tcx>,
name: Symbol,
+ path: &hir::UsePath<'tcx>,
+ kind: hir::UseKind,
+ cx: &mut DocContext<'tcx>,
+ inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
+) -> Vec<Item> {
+ let mut items = Vec::new();
+ let hir::UsePath { segments, ref res, span } = *path;
+ for &res in res {
+ if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res {
+ continue;
+ }
+ let path = hir::Path { segments, res, span };
+ items.append(&mut clean_use_statement_inner(import, name, &path, kind, cx, inlined_names));
+ }
+ items
+}
+
+fn clean_use_statement_inner<'tcx>(
+ import: &hir::Item<'tcx>,
+ name: Symbol,
path: &hir::Path<'tcx>,
kind: hir::UseKind,
cx: &mut DocContext<'tcx>,
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index 1bcb9fcd5..e96a9bab7 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -14,13 +14,14 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::def_id::DefId;
use rustc_middle::ty;
+use thin_vec::ThinVec;
use crate::clean;
use crate::clean::GenericArgs as PP;
use crate::clean::WherePredicate as WP;
use crate::core::DocContext;
-pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> {
+pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> ThinVec<WP> {
// First, partition the where clause into its separate components.
//
// We use `FxIndexMap` so that the insertion order is preserved to prevent messing up to
@@ -50,16 +51,13 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> {
let Some((bounds, _)) = tybounds.get_mut(ty) else { return true };
let bound_params = bound_params
.into_iter()
- .map(|param| clean::GenericParamDef {
- name: param.0,
- kind: clean::GenericParamDefKind::Lifetime { outlives: Vec::new() },
- })
+ .map(|param| clean::GenericParamDef::lifetime(param.0))
.collect();
merge_bounds(cx, bounds, bound_params, trait_did, name, rhs)
});
// And finally, let's reassemble everything
- let mut clauses = Vec::new();
+ let mut clauses = ThinVec::with_capacity(lifetimes.len() + tybounds.len() + equalities.len());
clauses.extend(
lifetimes.into_iter().map(|(lt, bounds)| WP::RegionPredicate { lifetime: lt, bounds }),
);
@@ -98,9 +96,8 @@ 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);
- // Since the parameters (probably) originate from `tcx.collect_*_late_bound_regions` which
- // returns a hash set, sort them alphabetically to guarantee a stable and deterministic
- // output (and to fully deduplicate them).
+ // 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);
@@ -135,7 +132,7 @@ fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId)
.predicates
.iter()
.filter_map(|(pred, _)| {
- if let ty::PredicateKind::Trait(pred) = pred.kind().skip_binder() {
+ if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred.kind().skip_binder() {
if pred.trait_ref.self_ty() == self_ty { Some(pred.def_id()) } else { None }
} else {
None
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index cd1f972dc..2590bb0df 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -24,7 +24,7 @@ use rustc_hir::{BodyId, Mutability};
use rustc_hir_analysis::check::intrinsic::intrinsic_operation_unsafety;
use rustc_index::vec::IndexVec;
use rustc_middle::ty::fast_reject::SimplifiedType;
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, DefIdTree, TyCtxt, Visibility};
use rustc_session::Session;
use rustc_span::hygiene::MacroKind;
use rustc_span::source_map::DUMMY_SP;
@@ -34,7 +34,6 @@ use rustc_target::abi::VariantIdx;
use rustc_target::spec::abi::Abi;
use crate::clean::cfg::Cfg;
-use crate::clean::clean_visibility;
use crate::clean::external_path;
use crate::clean::inline::{self, print_inlined_const};
use crate::clean::utils::{is_literal_expr, print_const_expr, print_evaluated_const};
@@ -51,7 +50,6 @@ pub(crate) use self::Type::{
Array, BareFunction, BorrowedRef, DynTrait, Generic, ImplTrait, Infer, Primitive, QPath,
RawPointer, Slice, Tuple,
};
-pub(crate) use self::Visibility::{Inherited, Public};
#[cfg(test)]
mod tests;
@@ -117,7 +115,6 @@ impl From<DefId> for ItemId {
#[derive(Clone, Debug)]
pub(crate) struct Crate {
pub(crate) module: Item,
- pub(crate) primitives: ThinVec<(DefId, PrimitiveType)>,
/// Only here so that they can be filtered through the rustdoc passes.
pub(crate) external_traits: Rc<RefCell<FxHashMap<DefId, Trait>>>,
}
@@ -245,7 +242,9 @@ impl ExternalCrate {
hir::ItemKind::Use(path, hir::UseKind::Single)
if tcx.visibility(id.owner_id).is_public() =>
{
- as_keyword(path.res.expect_non_local())
+ path.res
+ .iter()
+ .find_map(|res| as_keyword(res.expect_non_local()))
.map(|(_, prim)| (id.owner_id.to_def_id(), prim))
}
_ => None,
@@ -313,10 +312,11 @@ impl ExternalCrate {
hir::ItemKind::Use(path, hir::UseKind::Single)
if tcx.visibility(id.owner_id).is_public() =>
{
- as_primitive(path.res.expect_non_local()).map(|(_, prim)| {
+ path.res
+ .iter()
+ .find_map(|res| as_primitive(res.expect_non_local()))
// Pretend the primitive is local.
- (id.owner_id.to_def_id(), prim)
- })
+ .map(|(_, prim)| (id.owner_id.to_def_id(), prim))
}
_ => None,
}
@@ -348,12 +348,12 @@ pub(crate) struct Item {
/// Optional because not every item has a name, e.g. impls.
pub(crate) name: Option<Symbol>,
pub(crate) attrs: Box<Attributes>,
- pub(crate) visibility: Visibility,
/// Information about this item that is specific to what kind of item it is.
/// E.g., struct vs enum vs function.
pub(crate) kind: Box<ItemKind>,
pub(crate) item_id: ItemId,
-
+ /// This is the `DefId` of the `use` statement if the item was inlined.
+ pub(crate) inline_stmt_id: Option<DefId>,
pub(crate) cfg: Option<Arc<Cfg>>,
}
@@ -364,9 +364,7 @@ impl fmt::Debug for Item {
let alternate = f.alternate();
// hand-picked fields that don't bloat the logs too much
let mut fmt = f.debug_struct("Item");
- fmt.field("name", &self.name)
- .field("visibility", &self.visibility)
- .field("item_id", &self.item_id);
+ fmt.field("name", &self.name).field("item_id", &self.item_id);
// allow printing the full item if someone really wants to
if alternate {
fmt.field("attrs", &self.attrs).field("kind", &self.kind).field("cfg", &self.cfg);
@@ -388,6 +386,15 @@ pub(crate) fn rustc_span(def_id: DefId, tcx: TyCtxt<'_>) -> Span {
))
}
+fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+ let parent = tcx.parent(def_id);
+ match tcx.def_kind(parent) {
+ DefKind::Struct | DefKind::Union => false,
+ DefKind::Variant => true,
+ parent_kind => panic!("unexpected parent kind: {:?}", parent_kind),
+ }
+}
+
impl Item {
pub(crate) fn stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<Stability> {
self.item_id.as_def_id().and_then(|did| tcx.lookup_stability(did))
@@ -462,7 +469,6 @@ impl Item {
name,
kind,
Box::new(Attributes::from_ast(ast_attrs)),
- cx,
ast_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg),
)
}
@@ -472,21 +478,18 @@ impl Item {
name: Option<Symbol>,
kind: ItemKind,
attrs: Box<Attributes>,
- cx: &mut DocContext<'_>,
cfg: Option<Arc<Cfg>>,
) -> Item {
trace!("name={:?}, def_id={:?} cfg={:?}", name, def_id, cfg);
- // Primitives and Keywords are written in the source code as private modules.
- // The modules need to be private so that nobody actually uses them, but the
- // keywords and primitives that they are documenting are public.
- let visibility = if matches!(&kind, ItemKind::KeywordItem | ItemKind::PrimitiveItem(..)) {
- Visibility::Public
- } else {
- clean_visibility(cx.tcx.visibility(def_id))
- };
-
- Item { item_id: def_id.into(), kind: Box::new(kind), name, attrs, visibility, cfg }
+ Item {
+ item_id: def_id.into(),
+ kind: Box::new(kind),
+ name,
+ attrs,
+ cfg,
+ inline_stmt_id: None,
+ }
}
/// Finds all `doc` attributes as NameValues and returns their corresponding values, joined
@@ -691,17 +694,61 @@ impl Item {
asyncness: hir::IsAsync::NotAsync,
}
}
- ItemKind::FunctionItem(_) | ItemKind::MethodItem(_, _) => {
+ ItemKind::FunctionItem(_) | ItemKind::MethodItem(_, _) | ItemKind::TyMethodItem(_) => {
let def_id = self.item_id.as_def_id().unwrap();
build_fn_header(def_id, tcx, tcx.asyncness(def_id))
}
- ItemKind::TyMethodItem(_) => {
- build_fn_header(self.item_id.as_def_id().unwrap(), tcx, hir::IsAsync::NotAsync)
- }
_ => return None,
};
Some(header)
}
+
+ /// Returns the visibility of the current item. If the visibility is "inherited", then `None`
+ /// is returned.
+ pub(crate) fn visibility(&self, tcx: TyCtxt<'_>) -> Option<Visibility<DefId>> {
+ let def_id = match self.item_id {
+ // Anything but DefId *shouldn't* matter, but return a reasonable value anyway.
+ ItemId::Auto { .. } | ItemId::Blanket { .. } => return None,
+ // Primitives and Keywords are written in the source code as private modules.
+ // The modules need to be private so that nobody actually uses them, but the
+ // keywords and primitives that they are documenting are public.
+ ItemId::Primitive(..) => return Some(Visibility::Public),
+ ItemId::DefId(def_id) => def_id,
+ };
+
+ match *self.kind {
+ // Explication on `ItemId::Primitive` just above.
+ ItemKind::KeywordItem | ItemKind::PrimitiveItem(_) => return Some(Visibility::Public),
+ // Variant fields inherit their enum's visibility.
+ StructFieldItem(..) if is_field_vis_inherited(tcx, def_id) => {
+ return None;
+ }
+ // Variants always inherit visibility
+ VariantItem(..) => return None,
+ // Trait items inherit the trait's visibility
+ AssocConstItem(..) | TyAssocConstItem(..) | AssocTypeItem(..) | TyAssocTypeItem(..)
+ | TyMethodItem(..) | MethodItem(..) => {
+ let assoc_item = tcx.associated_item(def_id);
+ let is_trait_item = match assoc_item.container {
+ ty::TraitContainer => true,
+ ty::ImplContainer => {
+ // Trait impl items always inherit the impl's visibility --
+ // we don't want to show `pub`.
+ tcx.impl_trait_ref(tcx.parent(assoc_item.def_id)).is_some()
+ }
+ };
+ if is_trait_item {
+ return None;
+ }
+ }
+ _ => {}
+ }
+ let def_id = match self.inline_stmt_id {
+ Some(inlined) => inlined,
+ None => def_id,
+ };
+ Some(tcx.visibility(def_id))
+ }
}
#[derive(Clone, Debug)]
@@ -747,7 +794,7 @@ pub(crate) enum ItemKind {
/// A required associated type in a trait declaration.
///
/// The bounds may be non-empty if there is a `where` clause.
- TyAssocTypeItem(Box<Generics>, Vec<GenericBound>),
+ TyAssocTypeItem(Generics, Vec<GenericBound>),
/// An associated type in a trait impl or a provided one in a trait declaration.
AssocTypeItem(Box<Typedef>, Vec<GenericBound>),
/// An item that has been stripped by a rustdoc pass
@@ -1261,7 +1308,7 @@ impl Attributes {
for attr in self.other_attrs.lists(sym::doc).filter(|a| a.has_name(sym::alias)) {
if let Some(values) = attr.meta_item_list() {
for l in values {
- match l.literal().unwrap().kind {
+ match l.lit().unwrap().kind {
ast::LitKind::Str(s, _) => {
aliases.insert(s);
}
@@ -1392,6 +1439,10 @@ pub(crate) struct GenericParamDef {
}
impl GenericParamDef {
+ pub(crate) fn lifetime(name: Symbol) -> Self {
+ Self { name, kind: GenericParamDefKind::Lifetime { outlives: Vec::new() } }
+ }
+
pub(crate) fn is_synthetic_type_param(&self) -> bool {
match self.kind {
GenericParamDefKind::Lifetime { .. } | GenericParamDefKind::Const { .. } => false,
@@ -1414,8 +1465,8 @@ impl GenericParamDef {
// maybe use a Generic enum and use Vec<Generic>?
#[derive(Clone, Debug, Default)]
pub(crate) struct Generics {
- pub(crate) params: Vec<GenericParamDef>,
- pub(crate) where_predicates: Vec<WherePredicate>,
+ pub(crate) params: ThinVec<GenericParamDef>,
+ pub(crate) where_predicates: ThinVec<WherePredicate>,
}
impl Generics {
@@ -1576,7 +1627,7 @@ pub(crate) enum Type {
/// An array type.
///
/// The `String` field is a stringified version of the array's length parameter.
- Array(Box<Type>, String),
+ Array(Box<Type>, Box<str>),
/// A raw pointer type: `*const i32`, `*mut i32`
RawPointer(Mutability, Box<Type>),
/// A reference type: `&i32`, `&'a mut Foo`
@@ -2030,27 +2081,9 @@ impl From<hir::PrimTy> for PrimitiveType {
}
}
-#[derive(Copy, Clone, Debug)]
-pub(crate) enum Visibility {
- /// `pub`
- Public,
- /// Visibility inherited from parent.
- ///
- /// For example, this is the visibility of private items and of enum variants.
- Inherited,
- /// `pub(crate)`, `pub(super)`, or `pub(in path::to::somewhere)`
- Restricted(DefId),
-}
-
-impl Visibility {
- pub(crate) fn is_public(&self) -> bool {
- matches!(self, Visibility::Public)
- }
-}
-
#[derive(Clone, Debug)]
pub(crate) struct Struct {
- pub(crate) struct_type: CtorKind,
+ pub(crate) ctor_kind: Option<CtorKind>,
pub(crate) generics: Generics,
pub(crate) fields: Vec<Item>,
}
@@ -2078,7 +2111,7 @@ impl Union {
/// only as a variant in an enum.
#[derive(Clone, Debug)]
pub(crate) struct VariantStruct {
- pub(crate) struct_type: CtorKind,
+ pub(crate) ctor_kind: Option<CtorKind>,
pub(crate) fields: Vec<Item>,
}
@@ -2179,7 +2212,7 @@ impl Span {
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub(crate) struct Path {
pub(crate) res: Res,
- pub(crate) segments: Vec<PathSegment>,
+ pub(crate) segments: ThinVec<PathSegment>,
}
impl Path {
@@ -2329,7 +2362,7 @@ pub(crate) enum ConstantKind {
///
/// Note that `ty::Const` includes generic parameters, and may not always be uniquely identified
/// by a DefId. So this field must be different from `Extern`.
- TyConst { expr: String },
+ TyConst { expr: Box<str> },
/// A constant (expression) that's not an item or associated item. These are usually found
/// nested inside types (e.g., array lengths) or expressions (e.g., repeat counts), and also
/// used to define explicit discriminant values for enum variants.
@@ -2357,7 +2390,7 @@ impl Constant {
impl ConstantKind {
pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> String {
match *self {
- ConstantKind::TyConst { ref expr } => expr.clone(),
+ ConstantKind::TyConst { ref expr } => expr.to_string(),
ConstantKind::Extern { def_id } => print_inlined_const(tcx, def_id),
ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => {
print_const_expr(tcx, body)
@@ -2541,14 +2574,15 @@ mod size_asserts {
use super::*;
use rustc_data_structures::static_assert_size;
// tidy-alphabetical-start
- static_assert_size!(Crate, 72); // frequently moved by-value
+ static_assert_size!(Crate, 64); // frequently moved by-value
static_assert_size!(DocFragment, 32);
- static_assert_size!(GenericArg, 48);
+ static_assert_size!(GenericArg, 32);
static_assert_size!(GenericArgs, 32);
static_assert_size!(GenericParamDef, 56);
+ static_assert_size!(Generics, 16);
static_assert_size!(Item, 56);
- static_assert_size!(ItemKind, 88);
+ static_assert_size!(ItemKind, 64);
static_assert_size!(PathSegment, 40);
- static_assert_size!(Type, 48);
+ static_assert_size!(Type, 32);
// tidy-alphabetical-end
}
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 58767d3a4..246560bad 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -4,10 +4,10 @@ use crate::clean::render_macro_matchers::render_macro_matcher;
use crate::clean::{
clean_doc_module, clean_middle_const, clean_middle_region, clean_middle_ty, inline, Crate,
ExternalCrate, Generic, GenericArg, GenericArgs, ImportSource, Item, ItemKind, Lifetime, Path,
- PathSegment, Primitive, PrimitiveType, Type, TypeBinding, Visibility,
+ PathSegment, Primitive, PrimitiveType, Term, Type, TypeBinding, TypeBindingKind,
};
use crate::core::DocContext;
-use crate::visit_lib::LibEmbargoVisitor;
+use crate::html::format::visibility_to_src_with_space;
use rustc_ast as ast;
use rustc_ast::tokenstream::TokenTree;
@@ -21,7 +21,7 @@ use rustc_middle::ty::{self, DefIdTree, TyCtxt};
use rustc_span::symbol::{kw, sym, Symbol};
use std::fmt::Write as _;
use std::mem;
-use thin_vec::ThinVec;
+use thin_vec::{thin_vec, ThinVec};
#[cfg(test)]
mod tests;
@@ -31,7 +31,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
for &cnum in cx.tcx.crates(()) {
// Analyze doc-reachability for extern items
- LibEmbargoVisitor::new(cx).visit_lib(cnum);
+ crate::visit_lib::lib_embargo_visit_item(cx, cnum.as_def_id());
}
// Clean the crate, translating the entire librustc_ast AST to one that is
@@ -73,7 +73,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
}));
}
- Crate { module, primitives, external_traits: cx.external_traits.clone() }
+ Crate { module, external_traits: cx.external_traits.clone() }
}
pub(crate) fn substs_to_args<'tcx>(
@@ -106,19 +106,19 @@ fn external_generic_args<'tcx>(
) -> GenericArgs {
let args = substs_to_args(cx, substs, has_self);
- if cx.tcx.fn_trait_kind_from_lang_item(did).is_some() {
+ if cx.tcx.fn_trait_kind_from_def_id(did).is_some() {
let inputs =
// The trait's first substitution is the one after self, if there is one.
match substs.iter().nth(if has_self { 1 } else { 0 }).unwrap().expect_ty().kind() {
ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(t, cx, None)).collect::<Vec<_>>().into(),
_ => return GenericArgs::AngleBracketed { args: args.into(), bindings },
};
- let output = None;
- // FIXME(#20299) return type comes from a projection now
- // match types[1].kind {
- // ty::Tuple(ref v) if v.is_empty() => None, // -> ()
- // _ => Some(types[1].clean(cx))
- // };
+ let output = bindings.into_iter().next().and_then(|binding| match binding.kind {
+ TypeBindingKind::Equality { term: Term::Type(ty) } if ty != Type::Tuple(Vec::new()) => {
+ Some(Box::new(ty))
+ }
+ _ => None,
+ });
GenericArgs::Parenthesized { inputs, output }
} else {
GenericArgs::AngleBracketed { args: args.into(), bindings: bindings.into() }
@@ -136,7 +136,7 @@ pub(super) fn external_path<'tcx>(
let name = cx.tcx.item_name(did);
Path {
res: Res::Def(def_kind, did),
- segments: vec![PathSegment {
+ segments: thin_vec![PathSegment {
name,
args: external_generic_args(cx, did, has_self, bindings, substs),
}],
@@ -242,19 +242,13 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
s
}
- _ => {
- let mut s = n.to_string();
- // array lengths are obviously usize
- if s.ends_with("_usize") {
- let n = s.len() - "_usize".len();
- s.truncate(n);
- if s.ends_with(": ") {
- let n = s.len() - ": ".len();
- s.truncate(n);
- }
- }
- s
+ // array lengths are obviously usize
+ ty::ConstKind::Value(ty::ValTree::Leaf(scalar))
+ if *n.ty().kind() == ty::Uint(ty::UintTy::Usize) =>
+ {
+ scalar.to_string()
}
+ _ => n.to_string(),
}
}
@@ -584,9 +578,9 @@ pub(super) fn display_macro_source(
name: Symbol,
def: &ast::MacroDef,
def_id: DefId,
- vis: Visibility,
+ vis: ty::Visibility<DefId>,
) -> String {
- let tts: Vec<_> = def.body.inner_tokens().into_trees().collect();
+ let tts: Vec<_> = def.body.tokens.clone().into_trees().collect();
// Extract the spans of all matchers. They represent the "interface" of the macro.
let matchers = tts.chunks(4).map(|arm| &arm[0]);
@@ -596,14 +590,14 @@ pub(super) fn display_macro_source(
if matchers.len() <= 1 {
format!(
"{}macro {}{} {{\n ...\n}}",
- vis.to_src_with_space(cx.tcx, def_id),
+ visibility_to_src_with_space(Some(vis), cx.tcx, def_id),
name,
matchers.map(|matcher| render_macro_matcher(cx.tcx, matcher)).collect::<String>(),
)
} else {
format!(
"{}macro {} {{\n{}}}",
- vis.to_src_with_space(cx.tcx, def_id),
+ visibility_to_src_with_space(Some(vis), cx.tcx, def_id),
name,
render_macro_arms(cx.tcx, matchers, ","),
)
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 67ea39fb9..56b40d8c6 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -69,6 +69,8 @@ pub(crate) struct Options {
pub(crate) input: PathBuf,
/// The name of the crate being documented.
pub(crate) crate_name: Option<String>,
+ /// Whether or not this is a bin crate
+ pub(crate) bin_crate: bool,
/// Whether or not this is a proc-macro crate
pub(crate) proc_macro_crate: bool,
/// How to format errors and warnings.
@@ -176,6 +178,7 @@ impl fmt::Debug for Options {
f.debug_struct("Options")
.field("input", &self.input)
.field("crate_name", &self.crate_name)
+ .field("bin_crate", &self.bin_crate)
.field("proc_macro_crate", &self.proc_macro_crate)
.field("error_format", &self.error_format)
.field("libs", &self.libs)
@@ -239,9 +242,6 @@ pub(crate) struct RenderOptions {
pub(crate) default_settings: FxHashMap<String, String>,
/// If present, suffix added to CSS/JavaScript files when referencing them in generated pages.
pub(crate) resource_suffix: String,
- /// Whether to run the static CSS/JavaScript through a minifier when outputting them. `true` by
- /// default.
- pub(crate) enable_minification: bool,
/// Whether to create an index page in the root of the output directory. If this is true but
/// `enable_index_page` is None, generate a static listing of crates instead.
pub(crate) enable_index_page: bool,
@@ -329,7 +329,7 @@ impl Options {
crate::usage("rustdoc");
return Err(0);
} else if matches.opt_present("version") {
- rustc_driver::version("rustdoc", matches);
+ rustc_driver::version!("rustdoc", matches);
return Err(0);
}
@@ -416,10 +416,12 @@ impl Options {
let to_check = matches.opt_strs("check-theme");
if !to_check.is_empty() {
- let paths = match theme::load_css_paths(static_files::themes::LIGHT) {
+ let paths = match theme::load_css_paths(
+ std::str::from_utf8(static_files::STATIC_FILES.theme_light_css.bytes).unwrap(),
+ ) {
Ok(p) => p,
Err(e) => {
- diag.struct_err(&e.to_string()).emit();
+ diag.struct_err(e).emit();
return Err(1);
}
};
@@ -557,10 +559,12 @@ impl Options {
let mut themes = Vec::new();
if matches.opt_present("theme") {
- let paths = match theme::load_css_paths(static_files::themes::LIGHT) {
+ let paths = match theme::load_css_paths(
+ std::str::from_utf8(static_files::STATIC_FILES.theme_light_css.bytes).unwrap(),
+ ) {
Ok(p) => p,
Err(e) => {
- diag.struct_err(&e.to_string()).emit();
+ diag.struct_err(e).emit();
return Err(1);
}
};
@@ -666,6 +670,7 @@ impl Options {
None => OutputFormat::default(),
};
let crate_name = matches.opt_str("crate-name");
+ let bin_crate = crate_types.contains(&CrateType::Executable);
let proc_macro_crate = crate_types.contains(&CrateType::ProcMacro);
let playground_url = matches.opt_str("playground-url");
let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
@@ -675,7 +680,6 @@ impl Options {
ModuleSorting::Alphabetical
};
let resource_suffix = matches.opt_str("resource-suffix").unwrap_or_default();
- let enable_minification = !matches.opt_present("disable-minification");
let markdown_no_toc = matches.opt_present("markdown-no-toc");
let markdown_css = matches.opt_strs("markdown-css");
let markdown_playground_url = matches.opt_str("markdown-playground-url");
@@ -718,6 +722,7 @@ impl Options {
rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref());
let options = Options {
input,
+ bin_crate,
proc_macro_crate,
error_format,
diagnostic_width,
@@ -768,7 +773,6 @@ impl Options {
extern_html_root_takes_precedence,
default_settings,
resource_suffix,
- enable_minification,
enable_index_page,
index_page,
static_root_path,
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 3e5f42b7a..da0df596c 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -166,6 +166,7 @@ pub(crate) fn new_handler(
unstable_opts.teach,
diagnostic_width,
false,
+ unstable_opts.track_diagnostics,
)
.ui_testing(unstable_opts.ui_testing),
)
@@ -184,6 +185,7 @@ pub(crate) fn new_handler(
json_rendered,
diagnostic_width,
false,
+ unstable_opts.track_diagnostics,
)
.ui_testing(unstable_opts.ui_testing),
)
@@ -348,7 +350,6 @@ pub(crate) fn run_global_ctxt(
let auto_traits =
tcx.all_traits().filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id)).collect();
- let effective_visibilities = tcx.effective_visibilities(()).map_id(Into::into);
let mut ctxt = DocContext {
tcx,
@@ -361,7 +362,7 @@ pub(crate) fn run_global_ctxt(
impl_trait_bounds: Default::default(),
generated_synthetics: Default::default(),
auto_traits,
- cache: Cache::new(effective_visibilities, render_options.document_private),
+ cache: Cache::new(render_options.document_private),
inlined: FxHashSet::default(),
output_format,
render_options,
@@ -488,7 +489,7 @@ impl<'tcx> Visitor<'tcx> for EmitIgnoredResolutionErrors<'tcx> {
self.tcx.hir()
}
- fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
+ fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
debug!("visiting path {:?}", path);
if path.res == Res::Err {
// We have less context here than in rustc_resolve,
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index db70029f6..81d9c4644 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -19,7 +19,7 @@ use rustc_span::edition::Edition;
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::sym;
use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP};
-use rustc_target::spec::TargetTriple;
+use rustc_target::spec::{Target, TargetTriple};
use tempfile::Builder as TempFileBuilder;
use std::env;
@@ -293,6 +293,16 @@ struct UnusedExterns {
unused_extern_names: Vec<String>,
}
+fn add_exe_suffix(input: String, target: &TargetTriple) -> String {
+ let exe_suffix = match target {
+ TargetTriple::TargetTriple(_) => Target::expect_builtin(target).options.exe_suffix,
+ TargetTriple::TargetJson { contents, .. } => {
+ Target::from_json(contents.parse().unwrap()).unwrap().0.options.exe_suffix
+ }
+ };
+ input + &exe_suffix
+}
+
fn run_test(
test: &str,
crate_name: &str,
@@ -313,7 +323,9 @@ fn run_test(
let (test, line_offset, supports_color) =
make_test(test, Some(crate_name), lang_string.test_harness, opts, edition, Some(test_id));
- let output_file = outdir.path().join("rust_out");
+ // Make sure we emit well-formed executable names for our target.
+ let rust_out = add_exe_suffix("rust_out".to_owned(), &target);
+ let output_file = outdir.path().join(rust_out);
let rustc_binary = rustdoc_options
.test_builder
@@ -551,6 +563,7 @@ pub(crate) fn make_test(
false,
Some(80),
false,
+ false,
)
.supports_color();
@@ -564,6 +577,7 @@ pub(crate) fn make_test(
false,
None,
false,
+ false,
);
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
@@ -748,6 +762,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
false,
None,
false,
+ false,
);
let handler = Handler::with_emitter(false, None, Box::new(emitter));
@@ -1290,7 +1305,7 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx>
}
fn visit_variant(&mut self, v: &'hir hir::Variant<'_>) {
- self.visit_testable(v.ident.to_string(), v.id, v.span, |this| {
+ self.visit_testable(v.ident.to_string(), v.hir_id, v.span, |this| {
intravisit::walk_variant(this, v);
});
}
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index afe2264e8..d027fb6e8 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -2,7 +2,6 @@ use std::mem;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::{CrateNum, DefId};
-use rustc_middle::middle::privacy::EffectiveVisibilities;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::Symbol;
@@ -15,6 +14,7 @@ use crate::html::format::join_with_double_colon;
use crate::html::markdown::short_markdown_summary;
use crate::html::render::search_index::get_function_type_for_search;
use crate::html::render::IndexItem;
+use crate::visit_lib::RustdocEffectiveVisibilities;
/// This cache is used to store information about the [`clean::Crate`] being
/// rendered in order to provide more useful documentation. This contains
@@ -78,7 +78,7 @@ pub(crate) struct Cache {
// Note that external items for which `doc(hidden)` applies to are shown as
// non-reachable while local items aren't. This is because we're reusing
// the effective visibilities from the privacy check pass.
- pub(crate) effective_visibilities: EffectiveVisibilities<DefId>,
+ pub(crate) effective_visibilities: RustdocEffectiveVisibilities,
/// The version of the crate being documented, if given from the `--crate-version` flag.
pub(crate) crate_version: Option<String>,
@@ -132,11 +132,8 @@ struct CacheBuilder<'a, 'tcx> {
}
impl Cache {
- pub(crate) fn new(
- effective_visibilities: EffectiveVisibilities<DefId>,
- document_private: bool,
- ) -> Self {
- Cache { effective_visibilities, document_private, ..Cache::default() }
+ pub(crate) fn new(document_private: bool) -> Self {
+ Cache { document_private, ..Cache::default() }
}
/// Populates the `Cache` with more data. The returned `Crate` will be missing some data that was
@@ -319,21 +316,28 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
let desc = item.doc_value().map_or_else(String::new, |x| {
short_markdown_summary(x.as_str(), &item.link_names(self.cache))
});
- self.cache.search_index.push(IndexItem {
- ty: item.type_(),
- name: s.to_string(),
- 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(),
- });
+ let ty = item.type_();
+ let name = s.to_string();
+ if ty != ItemType::StructField || u16::from_str_radix(&name, 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,
+ 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(),
+ });
+ }
}
}
(Some(parent), None) if is_inherent_impl_item => {
@@ -387,7 +391,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
|| self
.cache
.effective_visibilities
- .is_directly_public(item.item_id.expect_def_id())
+ .is_directly_public(self.tcx, item.item_id.expect_def_id())
{
self.cache.paths.insert(
item.item_id.expect_def_id(),
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 92e7f2739..39e2a9022 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -107,10 +107,6 @@ impl Buffer {
self.buffer
}
- pub(crate) fn insert_str(&mut self, idx: usize, s: &str) {
- self.buffer.insert_str(idx, s);
- }
-
pub(crate) fn push_str(&mut self, s: &str) {
self.buffer.push_str(s);
}
@@ -659,7 +655,7 @@ pub(crate) fn href_with_root_path(
}
if !did.is_local()
- && !cache.effective_visibilities.is_directly_public(did)
+ && !cache.effective_visibilities.is_directly_public(tcx, did)
&& !cache.document_private
&& !cache.primitive_locations.values().any(|&id| id == did)
{
@@ -1232,9 +1228,8 @@ impl clean::Arguments {
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
for (i, input) in self.values.iter().enumerate() {
- if !input.name.is_empty() {
- write!(f, "{}: ", input.name)?;
- }
+ write!(f, "{}: ", input.name)?;
+
if f.alternate() {
write!(f, "{:#}", input.type_.print(cx))?;
} else {
@@ -1367,10 +1362,8 @@ impl clean::FnDecl {
args.push_str("const ");
args_plain.push_str("const ");
}
- if !input.name.is_empty() {
- write!(args, "{}: ", input.name);
- write!(args_plain, "{}: ", input.name);
- }
+ write!(args, "{}: ", input.name);
+ write!(args_plain, "{}: ", input.name);
if f.alternate() {
write!(args, "{:#}", input.type_.print(cx));
@@ -1420,87 +1413,84 @@ impl clean::FnDecl {
}
}
-impl clean::Visibility {
- pub(crate) fn print_with_space<'a, 'tcx: 'a>(
- self,
- item_did: ItemId,
- cx: &'a Context<'tcx>,
- ) -> impl fmt::Display + 'a + Captures<'tcx> {
- use std::fmt::Write as _;
-
- let to_print: Cow<'static, str> = match self {
- clean::Public => "pub ".into(),
- clean::Inherited => "".into(),
- clean::Visibility::Restricted(vis_did) => {
- // FIXME(camelid): This may not work correctly if `item_did` is a module.
- // However, rustdoc currently never displays a module's
- // visibility, so it shouldn't matter.
- let parent_module = find_nearest_parent_module(cx.tcx(), item_did.expect_def_id());
-
- if vis_did.is_crate_root() {
- "pub(crate) ".into()
- } else if parent_module == Some(vis_did) {
- // `pub(in foo)` where `foo` is the parent module
- // is the same as no visibility modifier
- "".into()
- } else if parent_module
- .and_then(|parent| find_nearest_parent_module(cx.tcx(), parent))
- == Some(vis_did)
- {
- "pub(super) ".into()
- } else {
- let path = cx.tcx().def_path(vis_did);
- debug!("path={:?}", path);
- // modified from `resolved_path()` to work with `DefPathData`
- let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
- let anchor = anchor(vis_did, last_name, cx).to_string();
-
- let mut s = "pub(in ".to_owned();
- for seg in &path.data[..path.data.len() - 1] {
- let _ = write!(s, "{}::", seg.data.get_opt_name().unwrap());
- }
- let _ = write!(s, "{}) ", anchor);
- s.into()
+pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>(
+ visibility: Option<ty::Visibility<DefId>>,
+ item_did: ItemId,
+ cx: &'a Context<'tcx>,
+) -> impl fmt::Display + 'a + Captures<'tcx> {
+ use std::fmt::Write as _;
+
+ let to_print: Cow<'static, str> = match visibility {
+ None => "".into(),
+ Some(ty::Visibility::Public) => "pub ".into(),
+ Some(ty::Visibility::Restricted(vis_did)) => {
+ // FIXME(camelid): This may not work correctly if `item_did` is a module.
+ // However, rustdoc currently never displays a module's
+ // visibility, so it shouldn't matter.
+ let parent_module = find_nearest_parent_module(cx.tcx(), item_did.expect_def_id());
+
+ if vis_did.is_crate_root() {
+ "pub(crate) ".into()
+ } else if parent_module == Some(vis_did) {
+ // `pub(in foo)` where `foo` is the parent module
+ // is the same as no visibility modifier
+ "".into()
+ } else if parent_module.and_then(|parent| find_nearest_parent_module(cx.tcx(), parent))
+ == Some(vis_did)
+ {
+ "pub(super) ".into()
+ } else {
+ let path = cx.tcx().def_path(vis_did);
+ debug!("path={:?}", path);
+ // modified from `resolved_path()` to work with `DefPathData`
+ let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
+ let anchor = anchor(vis_did, last_name, cx).to_string();
+
+ let mut s = "pub(in ".to_owned();
+ for seg in &path.data[..path.data.len() - 1] {
+ let _ = write!(s, "{}::", seg.data.get_opt_name().unwrap());
}
+ let _ = write!(s, "{}) ", anchor);
+ s.into()
}
- };
- display_fn(move |f| write!(f, "{}", to_print))
- }
+ }
+ };
+ display_fn(move |f| write!(f, "{}", to_print))
+}
- /// This function is the same as print_with_space, except that it renders no links.
- /// It's used for macros' rendered source view, which is syntax highlighted and cannot have
- /// any HTML in it.
- pub(crate) fn to_src_with_space<'a, 'tcx: 'a>(
- self,
- tcx: TyCtxt<'tcx>,
- item_did: DefId,
- ) -> impl fmt::Display + 'a + Captures<'tcx> {
- let to_print = match self {
- clean::Public => "pub ".to_owned(),
- clean::Inherited => String::new(),
- clean::Visibility::Restricted(vis_did) => {
- // FIXME(camelid): This may not work correctly if `item_did` is a module.
- // However, rustdoc currently never displays a module's
- // visibility, so it shouldn't matter.
- let parent_module = find_nearest_parent_module(tcx, item_did);
-
- if vis_did.is_crate_root() {
- "pub(crate) ".to_owned()
- } else if parent_module == Some(vis_did) {
- // `pub(in foo)` where `foo` is the parent module
- // is the same as no visibility modifier
- String::new()
- } else if parent_module.and_then(|parent| find_nearest_parent_module(tcx, parent))
- == Some(vis_did)
- {
- "pub(super) ".to_owned()
- } else {
- format!("pub(in {}) ", tcx.def_path_str(vis_did))
- }
+/// This function is the same as print_with_space, except that it renders no links.
+/// It's used for macros' rendered source view, which is syntax highlighted and cannot have
+/// any HTML in it.
+pub(crate) fn visibility_to_src_with_space<'a, 'tcx: 'a>(
+ visibility: Option<ty::Visibility<DefId>>,
+ tcx: TyCtxt<'tcx>,
+ item_did: DefId,
+) -> impl fmt::Display + 'a + Captures<'tcx> {
+ let to_print = match visibility {
+ None => String::new(),
+ Some(ty::Visibility::Public) => "pub ".to_owned(),
+ Some(ty::Visibility::Restricted(vis_did)) => {
+ // FIXME(camelid): This may not work correctly if `item_did` is a module.
+ // However, rustdoc currently never displays a module's
+ // visibility, so it shouldn't matter.
+ let parent_module = find_nearest_parent_module(tcx, item_did);
+
+ if vis_did.is_crate_root() {
+ "pub(crate) ".to_owned()
+ } else if parent_module == Some(vis_did) {
+ // `pub(in foo)` where `foo` is the parent module
+ // is the same as no visibility modifier
+ String::new()
+ } else if parent_module.and_then(|parent| find_nearest_parent_module(tcx, parent))
+ == Some(vis_did)
+ {
+ "pub(super) ".to_owned()
+ } else {
+ format!("pub(in {}) ", tcx.def_path_str(vis_did))
}
- };
- display_fn(move |f| f.write_str(&to_print))
- }
+ }
+ };
+ display_fn(move |f| f.write_str(&to_print))
}
pub(crate) trait PrintWithSpace {
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 5e28204b2..cd8c8c463 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -72,8 +72,12 @@ pub(crate) fn render_source_with_highlighting(
line_numbers: Buffer,
href_context: HrefContext<'_, '_, '_>,
decoration_info: DecorationInfo,
+ extra: Option<&str>,
) {
write_header(out, "", Some(line_numbers), Tooltip::None);
+ if let Some(extra) = extra {
+ out.push_str(extra);
+ }
write_code(out, src, Some(href_context), Some(decoration_info));
write_footer(out, None);
}
@@ -358,7 +362,7 @@ impl Class {
match self {
Class::Comment => "comment",
Class::DocComment => "doccomment",
- Class::Attribute => "attribute",
+ Class::Attribute => "attr",
Class::KeyWord => "kw",
Class::RefKeyWord => "kw-2",
Class::Self_(_) => "self",
diff --git a/src/librustdoc/html/highlight/fixtures/sample.html b/src/librustdoc/html/highlight/fixtures/sample.html
index 4a5a3cf60..fced2eacd 100644
--- a/src/librustdoc/html/highlight/fixtures/sample.html
+++ b/src/librustdoc/html/highlight/fixtures/sample.html
@@ -3,16 +3,16 @@
.kw { color: #8959A8; }
.kw-2, .prelude-ty { color: #4271AE; }
.number, .string { color: #718C00; }
-.self, .bool-val, .prelude-val, .attribute, .attribute .ident { color: #C82829; }
+.self, .bool-val, .prelude-val, .attr, .attr .ident { color: #C82829; }
.macro, .macro-nonterminal { color: #3E999F; }
.lifetime { color: #B76514; }
.question-mark { color: #ff9011; }
</style>
-<pre><code><span class="attribute">#![crate_type = <span class="string">&quot;lib&quot;</span>]
+<pre><code><span class="attr">#![crate_type = <span class="string">&quot;lib&quot;</span>]
</span><span class="kw">use </span>std::path::{Path, PathBuf};
-<span class="attribute">#[cfg(target_os = <span class="string">&quot;linux&quot;</span>)]
+<span class="attr">#[cfg(target_os = <span class="string">&quot;linux&quot;</span>)]
#[cfg(target_os = <span class="string">&quot;windows&quot;</span>)]
</span><span class="kw">fn </span>main() -&gt; () {
<span class="kw">let </span>foo = <span class="bool-val">true </span>&amp;&amp; <span class="bool-val">false </span>|| <span class="bool-val">true</span>;
@@ -23,7 +23,7 @@
<span class="macro">mac!</span>(foo, <span class="kw-2">&amp;mut </span>bar);
<span class="macro">assert!</span>(<span class="self">self</span>.length &lt; N &amp;&amp; index &lt;= <span class="self">self</span>.length);
::std::env::var(<span class="string">&quot;gateau&quot;</span>).is_ok();
- <span class="attribute">#[rustfmt::skip]
+ <span class="attr">#[rustfmt::skip]
</span><span class="kw">let </span>s:std::path::PathBuf = std::path::PathBuf::new();
<span class="kw">let </span><span class="kw-2">mut </span>s = String::new();
diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs
index a5e633df4..2c93b9a09 100644
--- a/src/librustdoc/html/highlight/tests.rs
+++ b/src/librustdoc/html/highlight/tests.rs
@@ -9,7 +9,7 @@ const STYLE: &str = r#"
.kw { color: #8959A8; }
.kw-2, .prelude-ty { color: #4271AE; }
.number, .string { color: #718C00; }
-.self, .bool-val, .prelude-val, .attribute, .attribute .ident { color: #C82829; }
+.self, .bool-val, .prelude-val, .attr, .attr .ident { color: #C82829; }
.macro, .macro-nonterminal { color: #3E999F; }
.lifetime { color: #B76514; }
.question-mark { color: #ff9011; }
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 7d6d4b71e..a60e7cb10 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -2,13 +2,14 @@ use std::path::PathBuf;
use rustc_data_structures::fx::FxHashMap;
-use crate::error::Error;
use crate::externalfiles::ExternalHtml;
use crate::html::format::{Buffer, Print};
use crate::html::render::{ensure_trailing_slash, StylePath};
use askama::Template;
+use super::static_files::{StaticFiles, STATIC_FILES};
+
#[derive(Clone)]
pub(crate) struct Layout {
pub(crate) logo: String,
@@ -34,17 +35,23 @@ pub(crate) struct Page<'a> {
}
impl<'a> Page<'a> {
- pub(crate) fn get_static_root_path(&self) -> &str {
- self.static_root_path.unwrap_or(self.root_path)
+ pub(crate) fn get_static_root_path(&self) -> String {
+ match self.static_root_path {
+ Some(s) => s.to_string(),
+ None => format!("{}static.files/", self.root_path),
+ }
}
}
#[derive(Template)]
#[template(path = "page.html")]
struct PageLayout<'a> {
- static_root_path: &'a str,
+ static_root_path: String,
page: &'a Page<'a>,
layout: &'a Layout,
+
+ files: &'static StaticFiles,
+
themes: Vec<String>,
sidebar: String,
content: String,
@@ -61,19 +68,17 @@ pub(crate) fn render<T: Print, S: Print>(
) -> String {
let static_root_path = page.get_static_root_path();
let krate_with_trailing_slash = ensure_trailing_slash(&layout.krate).to_string();
- let mut themes: Vec<String> = style_files
- .iter()
- .map(StylePath::basename)
- .collect::<Result<_, Error>>()
- .unwrap_or_default();
+ 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 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 {
static_root_path,
page,
layout,
+ files: &STATIC_FILES,
themes,
sidebar,
content,
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 5733d1f9c..73690c86f 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -32,7 +32,7 @@ use crate::html::escape::Escape;
use crate::html::format::{join_with_double_colon, Buffer};
use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
use crate::html::url_parts_builder::UrlPartsBuilder;
-use crate::html::{layout, sources};
+use crate::html::{layout, sources, static_files};
use crate::scrape_examples::AllCallLocations;
use crate::try_err;
@@ -69,11 +69,13 @@ pub(crate) struct Context<'tcx> {
/// the source files are present in the html rendering, then this will be
/// `true`.
pub(crate) include_sources: bool,
+ /// Collection of all types with notable traits referenced in the current module.
+ pub(crate) types_with_notable_traits: FxHashSet<clean::Type>,
}
// `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
#[cfg(all(not(windows), target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(Context<'_>, 128);
+rustc_data_structures::static_assert_size!(Context<'_>, 160);
/// Shared mutable state used in [`Context`] and elsewhere.
pub(crate) struct SharedContext<'tcx> {
@@ -498,7 +500,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
);
let (sender, receiver) = channel();
- let mut scx = SharedContext {
+ let scx = SharedContext {
tcx,
src_root,
local_sources,
@@ -521,19 +523,6 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
call_locations,
};
- // Add the default themes to the `Vec` of stylepaths
- //
- // Note that these must be added before `sources::render` is called
- // so that the resulting source pages are styled
- //
- // `light.css` is not disabled because it is the stylesheet that stays loaded
- // by the browser as the theme stylesheet. The theme system (hackily) works by
- // changing the href to this stylesheet. All other themes are disabled to
- // prevent rule conflicts
- scx.style_files.push(StylePath { path: PathBuf::from("light.css") });
- scx.style_files.push(StylePath { path: PathBuf::from("dark.css") });
- scx.style_files.push(StylePath { path: PathBuf::from("ayu.css") });
-
let dst = output;
scx.ensure_dir(&dst)?;
@@ -545,6 +534,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
deref_id_map: FxHashMap::default(),
shared: Rc::new(scx),
include_sources,
+ types_with_notable_traits: FxHashSet::default(),
};
if emit_crate {
@@ -573,6 +563,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
id_map: IdMap::new(),
shared: Rc::clone(&self.shared),
include_sources: self.include_sources,
+ types_with_notable_traits: FxHashSet::default(),
}
}
@@ -647,10 +638,11 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
</section>\
</noscript>\
<link rel=\"stylesheet\" type=\"text/css\" \
- href=\"{root_path}settings{suffix}.css\">\
- <script defer src=\"{root_path}settings{suffix}.js\"></script>",
- root_path = page.static_root_path.unwrap_or(""),
- suffix = page.resource_suffix,
+ href=\"{static_root_path}{settings_css}\">\
+ <script defer src=\"{static_root_path}{settings_js}\"></script>",
+ static_root_path = page.get_static_root_path(),
+ settings_css = static_files::STATIC_FILES.settings_css,
+ settings_js = static_files::STATIC_FILES.settings_js,
)
},
&shared.style_files,
@@ -815,6 +807,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
}
}
}
+
Ok(())
}
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 96c57c8c8..36d15ec3b 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -59,7 +59,7 @@ use rustc_span::{
symbol::{sym, Symbol},
BytePos, FileName, RealFileName,
};
-use serde::ser::SerializeSeq;
+use serde::ser::{SerializeMap, SerializeSeq};
use serde::{Serialize, Serializer};
use crate::clean::{self, ItemId, RenderedLink, SelfTy};
@@ -70,8 +70,8 @@ use crate::formats::{AssocItemRender, Impl, RenderMode};
use crate::html::escape::Escape;
use crate::html::format::{
href, join_with_double_colon, print_abi_with_space, print_constness_with_space,
- print_default_space, print_generic_bounds, print_where_clause, Buffer, Ending, HrefError,
- PrintWithSpace,
+ print_default_space, print_generic_bounds, print_where_clause, visibility_print_with_space,
+ Buffer, Ending, HrefError, PrintWithSpace,
};
use crate::html::highlight;
use crate::html::markdown::{
@@ -747,11 +747,12 @@ fn assoc_const(
extra: &str,
cx: &Context<'_>,
) {
+ let tcx = cx.tcx();
write!(
w,
"{extra}{vis}const <a{href} class=\"constant\">{name}</a>: {ty}",
extra = extra,
- vis = it.visibility.print_with_space(it.item_id, cx),
+ vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
href = assoc_href_attr(it, link, cx),
name = it.name.as_ref().unwrap(),
ty = ty.print(cx),
@@ -764,7 +765,7 @@ fn assoc_const(
// This hurts readability in this context especially when more complex expressions
// are involved and it doesn't add much of value.
// Find a way to print constants here without all that jazz.
- write!(w, "{}", Escape(&default.value(cx.tcx()).unwrap_or_else(|| default.expr(cx.tcx()))));
+ write!(w, "{}", Escape(&default.value(tcx).unwrap_or_else(|| default.expr(tcx))));
}
}
@@ -802,17 +803,18 @@ fn assoc_method(
d: &clean::FnDecl,
link: AssocItemLink<'_>,
parent: ItemType,
- cx: &Context<'_>,
+ cx: &mut Context<'_>,
render_mode: RenderMode,
) {
- let header = meth.fn_header(cx.tcx()).expect("Trying to get header from a non-function item");
+ let tcx = cx.tcx();
+ let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item");
let name = meth.name.as_ref().unwrap();
- let vis = meth.visibility.print_with_space(meth.item_id, cx).to_string();
+ let vis = visibility_print_with_space(meth.visibility(tcx), meth.item_id, cx).to_string();
// FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove
// this condition.
let constness = match render_mode {
RenderMode::Normal => {
- print_constness_with_space(&header.constness, meth.const_stability(cx.tcx()))
+ print_constness_with_space(&header.constness, meth.const_stability(tcx))
}
RenderMode::ForDeref { .. } => "",
};
@@ -834,6 +836,8 @@ fn assoc_method(
+ name.as_str().len()
+ generics_len;
+ let notable_traits = d.output.as_return().and_then(|output| notable_traits_button(output, cx));
+
let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
header_len += 4;
let indent_str = " ";
@@ -843,10 +847,10 @@ fn assoc_method(
render_attributes_in_code(w, meth);
(0, "", Ending::Newline)
};
- w.reserve(header_len + "<a href=\"\" class=\"fnname\">{".len() + "</a>".len());
+ w.reserve(header_len + "<a href=\"\" class=\"fn\">{".len() + "</a>".len());
write!(
w,
- "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn <a{href} class=\"fnname\">{name}</a>\
+ "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn <a{href} class=\"fn\">{name}</a>\
{generics}{decl}{notable_traits}{where_clause}",
indent = indent_str,
vis = vis,
@@ -859,9 +863,9 @@ fn assoc_method(
name = name,
generics = g.print(cx),
decl = d.full_print(header_len, indent, cx),
- notable_traits = notable_traits_decl(d, cx),
+ notable_traits = notable_traits.unwrap_or_default(),
where_clause = print_where_clause(g, cx, indent, end_newline),
- )
+ );
}
/// Writes a span containing the versions at which an item became stable and/or const-stable. For
@@ -961,7 +965,7 @@ fn render_assoc_item(
item: &clean::Item,
link: AssocItemLink<'_>,
parent: ItemType,
- cx: &Context<'_>,
+ cx: &mut Context<'_>,
render_mode: RenderMode,
) {
match &*item.kind {
@@ -1067,7 +1071,7 @@ fn write_impl_section_heading(w: &mut Buffer, title: &str, id: &str) {
w,
"<h2 id=\"{id}\" class=\"small-section-header\">\
{title}\
- <a href=\"#{id}\" class=\"anchor\"></a>\
+ <a href=\"#{id}\" class=\"anchor\">§</a>\
</h2>"
);
}
@@ -1271,88 +1275,133 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) ->
}
}
-fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String {
- let mut out = Buffer::html();
+pub(crate) fn notable_traits_button(ty: &clean::Type, cx: &mut Context<'_>) -> Option<String> {
+ let mut has_notable_trait = false;
+
+ let did = ty.def_id(cx.cache())?;
- if let Some((did, ty)) = decl.output.as_return().and_then(|t| Some((t.def_id(cx.cache())?, t)))
+ // Box has pass-through impls for Read, Write, Iterator, and Future when the
+ // boxed type implements one of those. We don't want to treat every Box return
+ // as being notably an Iterator (etc), though, so we exempt it. Pin has the same
+ // issue, with a pass-through impl for Future.
+ if Some(did) == cx.tcx().lang_items().owned_box()
+ || Some(did) == cx.tcx().lang_items().pin_type()
{
- // Box has pass-through impls for Read, Write, Iterator, and Future when the
- // boxed type implements one of those. We don't want to treat every Box return
- // as being notably an Iterator (etc), though, so we exempt it. Pin has the same
- // issue, with a pass-through impl for Future.
- if Some(did) == cx.tcx().lang_items().owned_box()
- || Some(did) == cx.tcx().lang_items().pin_type()
- {
- return "".to_string();
- }
- if let Some(impls) = cx.cache().impls.get(&did) {
- for i in impls {
- let impl_ = i.inner_impl();
- if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache())
+ return None;
+ }
+
+ if let Some(impls) = cx.cache().impls.get(&did) {
+ for i in impls {
+ let impl_ = i.inner_impl();
+ if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache()) {
+ // Two different types might have the same did,
+ // without actually being the same.
+ continue;
+ }
+ if let Some(trait_) = &impl_.trait_ {
+ let trait_did = trait_.def_id();
+
+ if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable_trait(cx.tcx()))
{
- // Two different types might have the same did,
- // without actually being the same.
- continue;
+ has_notable_trait = true;
}
- if let Some(trait_) = &impl_.trait_ {
- let trait_did = trait_.def_id();
-
- if cx
- .cache()
- .traits
- .get(&trait_did)
- .map_or(false, |t| t.is_notable_trait(cx.tcx()))
- {
- if out.is_empty() {
- write!(
- &mut out,
- "<span class=\"notable\">Notable traits for {}</span>\
- <code class=\"content\">",
- impl_.for_.print(cx)
- );
- }
+ }
+ }
+ }
- //use the "where" class here to make it small
- write!(
+ if has_notable_trait {
+ cx.types_with_notable_traits.insert(ty.clone());
+ Some(format!(
+ " <a href=\"#\" class=\"notable-traits\" data-ty=\"{ty}\">ⓘ</a>",
+ ty = Escape(&format!("{:#}", ty.print(cx))),
+ ))
+ } else {
+ None
+ }
+}
+
+fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
+ let mut out = Buffer::html();
+
+ let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this");
+
+ let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this");
+
+ for i in impls {
+ let impl_ = i.inner_impl();
+ if !impl_.for_.without_borrowed_ref().is_same(ty.without_borrowed_ref(), cx.cache()) {
+ // Two different types might have the same did,
+ // without actually being the same.
+ continue;
+ }
+ if let Some(trait_) = &impl_.trait_ {
+ let trait_did = trait_.def_id();
+
+ if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable_trait(cx.tcx())) {
+ if out.is_empty() {
+ write!(
+ &mut out,
+ "<h3>Notable traits for <code>{}</code></h3>\
+ <pre class=\"content\"><code>",
+ impl_.for_.print(cx)
+ );
+ }
+
+ //use the "where" class here to make it small
+ write!(
+ &mut out,
+ "<span class=\"where fmt-newline\">{}</span>",
+ impl_.print(false, cx)
+ );
+ for it in &impl_.items {
+ if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
+ out.push_str("<span class=\"where fmt-newline\"> ");
+ let empty_set = FxHashSet::default();
+ let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
+ assoc_type(
&mut out,
- "<span class=\"where fmt-newline\">{}</span>",
- impl_.print(false, cx)
+ it,
+ &tydef.generics,
+ &[], // intentionally leaving out bounds
+ Some(&tydef.type_),
+ src_link,
+ 0,
+ cx,
);
- for it in &impl_.items {
- if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
- out.push_str("<span class=\"where fmt-newline\"> ");
- let empty_set = FxHashSet::default();
- let src_link =
- AssocItemLink::GotoSource(trait_did.into(), &empty_set);
- assoc_type(
- &mut out,
- it,
- &tydef.generics,
- &[], // intentionally leaving out bounds
- Some(&tydef.type_),
- src_link,
- 0,
- cx,
- );
- out.push_str(";</span>");
- }
- }
+ out.push_str(";</span>");
}
}
}
}
}
-
- if !out.is_empty() {
- out.insert_str(
- 0,
- "<span class=\"notable-traits\"><span class=\"notable-traits-tooltip\">ⓘ\
- <span class=\"notable-traits-tooltiptext\"><span class=\"docblock\">",
- );
- out.push_str("</code></span></span></span></span>");
+ if out.is_empty() {
+ write!(&mut out, "</code></pre>",);
}
- out.into_inner()
+ (format!("{:#}", ty.print(cx)), out.into_inner())
+}
+
+pub(crate) fn notable_traits_json<'a>(
+ tys: impl Iterator<Item = &'a clean::Type>,
+ cx: &Context<'_>,
+) -> String {
+ let mut mp: Vec<(String, String)> = tys.map(|ty| notable_traits_decl(ty, cx)).collect();
+ mp.sort_by(|(name1, _html1), (name2, _html2)| name1.cmp(name2));
+ struct NotableTraitsMap(Vec<(String, String)>);
+ impl Serialize for NotableTraitsMap {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let mut map = serializer.serialize_map(Some(self.0.len()))?;
+ for item in &self.0 {
+ map.serialize_entry(&item.0, &item.1)?;
+ }
+ map.end()
+ }
+ }
+ serde_json::to_string(&NotableTraitsMap(mp))
+ .expect("serialize (string, string) -> json object cannot fail")
}
#[derive(Clone, Copy, Debug)]
@@ -1487,7 +1536,7 @@ fn render_impl(
render_rightside(w, cx, item, containing_item, render_mode);
if trait_.is_some() {
// Anchors are only used on trait impls.
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+ write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id);
}
w.write_str("<h4 class=\"code-header\">");
render_assoc_item(
@@ -1513,7 +1562,7 @@ fn render_impl(
render_rightside(w, cx, item, containing_item, render_mode);
if trait_.is_some() {
// Anchors are only used on trait impls.
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+ write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id);
}
w.write_str("<h4 class=\"code-header\">");
assoc_const(
@@ -1538,7 +1587,7 @@ fn render_impl(
write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class);
if trait_.is_some() {
// Anchors are only used on trait impls.
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+ write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id);
}
w.write_str("<h4 class=\"code-header\">");
assoc_type(
@@ -1564,7 +1613,7 @@ fn render_impl(
);
if trait_.is_some() {
// Anchors are only used on trait impls.
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+ write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id);
}
w.write_str("<h4 class=\"code-header\">");
assoc_type(
@@ -1797,7 +1846,7 @@ pub(crate) fn render_impl_summary(
};
write!(w, "<section id=\"{}\" class=\"impl has-srclink\"{}>", id, aliases);
render_rightside(w, cx, &i.impl_item, containing_item, RenderMode::Normal);
- write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
+ write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id);
write!(w, "<h3 class=\"code-header\">");
if let Some(use_absolute) = use_absolute {
@@ -2231,12 +2280,12 @@ fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clea
let fields = get_struct_fields_name(&s.fields);
if !fields.is_empty() {
- match s.struct_type {
- CtorKind::Fictive => {
+ match s.ctor_kind {
+ None => {
print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
}
- CtorKind::Fn => print_sidebar_title(&mut sidebar, "fields", "Tuple Fields"),
- CtorKind::Const => {}
+ Some(CtorKind::Fn) => print_sidebar_title(&mut sidebar, "fields", "Tuple Fields"),
+ Some(CtorKind::Const) => {}
}
}
@@ -2866,11 +2915,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
);
if line_ranges.len() > 1 {
- write!(w, r#"<span class="prev">&pr;</span> <span class="next">&sc;</span>"#);
- }
-
- if needs_expansion {
- write!(w, r#"<span class="expand">&varr;</span>"#);
+ write!(w, r#"<button class="prev">&pr;</button> <button class="next">&sc;</button>"#);
}
// Look for the example file in the source map if it exists, otherwise return a dummy span
@@ -2892,9 +2937,6 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
})()
.unwrap_or(rustc_span::DUMMY_SP);
- // The root path is the inverse of Context::current
- let root_path = vec!["../"; cx.current.len() - 1].join("");
-
let mut decoration_info = FxHashMap::default();
decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
decoration_info.insert("highlight", byte_ranges);
@@ -2904,9 +2946,9 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
contents_subset,
file_span,
cx,
- &root_path,
+ &cx.root_path(),
highlight::DecorationInfo(decoration_info),
- sources::SourceContext::Embedded { offset: line_min },
+ sources::SourceContext::Embedded { offset: line_min, needs_expansion },
);
write!(w, "</div></div>");
@@ -2915,14 +2957,23 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
// The call locations are output in sequence, so that sequence needs to be determined.
// Ideally the most "relevant" examples would be shown first, but there's no general algorithm
- // for determining relevance. Instead, we prefer the smallest examples being likely the easiest to
- // understand at a glance.
+ // for determining relevance. We instead proxy relevance with the following heuristics:
+ // 1. Code written to be an example is better than code not written to be an example, e.g.
+ // a snippet from examples/foo.rs is better than src/lib.rs. We don't know the Cargo
+ // directory structure in Rustdoc, so we proxy this by prioritizing code that comes from
+ // a --crate-type bin.
+ // 2. Smaller examples are better than large examples. So we prioritize snippets that have
+ // the smallest number of lines in their enclosing item.
+ // 3. Finally we sort by the displayed file name, which is arbitrary but prevents the
+ // ordering of examples from randomly changing between Rustdoc invocations.
let ordered_locations = {
- let sort_criterion = |(_, call_data): &(_, &CallData)| {
+ fn sort_criterion<'a>(
+ (_, call_data): &(&PathBuf, &'a CallData),
+ ) -> (bool, u32, &'a String) {
// Use the first location because that's what the user will see initially
let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
- hi - lo
- };
+ (!call_data.is_bin, hi - lo, &call_data.display_name)
+ }
let mut locs = call_locations.iter().collect::<Vec<_>>();
locs.sort_by_key(sort_criterion);
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 632781736..acbe3f228 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -7,19 +7,20 @@ use rustc_hir::def_id::DefId;
use rustc_middle::middle::stability;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::LayoutError;
-use rustc_middle::ty::{Adt, TyCtxt};
+use rustc_middle::ty::{self, Adt, TyCtxt};
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_target::abi::{Layout, Primitive, TagEncoding, Variants};
+use rustc_target::abi::{LayoutS, Primitive, TagEncoding, VariantIdx, Variants};
use std::cmp::Ordering;
use std::fmt;
use std::rc::Rc;
use super::{
collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference,
- item_ty_to_section, notable_traits_decl, render_all_impls, render_assoc_item,
- render_assoc_items, render_attributes_in_code, render_attributes_in_pre, render_impl,
- render_rightside, render_stability_since_raw, AssocItemLink, Context, ImplRenderingParameters,
+ item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls,
+ render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre,
+ render_impl, render_rightside, render_stability_since_raw,
+ render_stability_since_raw_with_extra, AssocItemLink, Context, ImplRenderingParameters,
};
use crate::clean;
use crate::config::ModuleSorting;
@@ -28,12 +29,12 @@ use crate::formats::{AssocItemRender, Impl, RenderMode};
use crate::html::escape::Escape;
use crate::html::format::{
join_with_double_colon, print_abi_with_space, print_constness_with_space, print_where_clause,
- Buffer, Ending, PrintWithSpace,
+ visibility_print_with_space, Buffer, Ending, PrintWithSpace,
};
-use crate::html::highlight;
use crate::html::layout::Page;
use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine};
use crate::html::url_parts_builder::UrlPartsBuilder;
+use crate::html::{highlight, static_files};
use askama::Template;
use itertools::Itertools;
@@ -52,8 +53,8 @@ struct PathComponent {
#[derive(Template)]
#[template(path = "print_item.html")]
struct ItemVars<'a> {
- page: &'a Page<'a>,
static_root_path: &'a str,
+ clipboard_svg: &'static static_files::StaticFile,
typ: &'a str,
name: &'a str,
item_type: &'a str,
@@ -147,8 +148,8 @@ pub(super) fn print_item(
};
let item_vars = ItemVars {
- page,
- static_root_path: page.get_static_root_path(),
+ static_root_path: &page.get_static_root_path(),
+ clipboard_svg: &static_files::STATIC_FILES.clipboard_svg,
typ,
name: item.name.as_ref().unwrap().as_str(),
item_type: &item.type_().to_string(),
@@ -183,6 +184,16 @@ pub(super) fn print_item(
unreachable!();
}
}
+
+ // Render notable-traits.js used for all methods in this module.
+ if !cx.types_with_notable_traits.is_empty() {
+ write!(
+ buf,
+ r#"<script type="text/json" id="notable-traits-data">{}</script>"#,
+ notable_traits_json(cx.types_with_notable_traits.iter(), cx)
+ );
+ cx.types_with_notable_traits.clear();
+ }
}
/// For large structs, enums, unions, etc, determine whether to hide their fields
@@ -318,6 +329,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
);
}
+ let tcx = cx.tcx();
match *myitem.kind {
clean::ExternCrateItem { ref src } => {
use crate::html::format::anchor;
@@ -327,14 +339,14 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
Some(src) => write!(
w,
"<div class=\"item-left\"><code>{}extern crate {} as {};",
- myitem.visibility.print_with_space(myitem.item_id, cx),
+ visibility_print_with_space(myitem.visibility(tcx), myitem.item_id, cx),
anchor(myitem.item_id.expect_def_id(), src, cx),
myitem.name.unwrap(),
),
None => write!(
w,
"<div class=\"item-left\"><code>{}extern crate {};",
- myitem.visibility.print_with_space(myitem.item_id, cx),
+ visibility_print_with_space(myitem.visibility(tcx), myitem.item_id, cx),
anchor(myitem.item_id.expect_def_id(), myitem.name.unwrap(), cx),
),
}
@@ -384,7 +396,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
</div>\
{stab_tags_before}{stab_tags}{stab_tags_after}",
stab = stab.unwrap_or_default(),
- vis = myitem.visibility.print_with_space(myitem.item_id, cx),
+ vis = visibility_print_with_space(myitem.visibility(tcx), myitem.item_id, cx),
imp = import.print(cx),
);
w.write_str(ITEM_TABLE_ROW_CLOSE);
@@ -408,8 +420,8 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
let stab = myitem.stability_class(cx.tcx());
let add = if stab.is_some() { " " } else { "" };
- let visibility_emoji = match myitem.visibility {
- clean::Visibility::Restricted(_) => {
+ let visibility_emoji = match myitem.visibility(tcx) {
+ Some(ty::Visibility::Restricted(_)) => {
"<span title=\"Restricted Visibility\">&nbsp;🔒</span> "
}
_ => "",
@@ -496,12 +508,13 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) ->
}
fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &clean::Function) {
- let header = it.fn_header(cx.tcx()).expect("printing a function which isn't a function");
- let constness = print_constness_with_space(&header.constness, it.const_stability(cx.tcx()));
+ let tcx = cx.tcx();
+ let header = it.fn_header(tcx).expect("printing a function which isn't a function");
+ let constness = print_constness_with_space(&header.constness, it.const_stability(tcx));
let unsafety = header.unsafety.print_with_space();
let abi = print_abi_with_space(header.abi).to_string();
let asyncness = header.asyncness.print_with_space();
- let visibility = it.visibility.print_with_space(it.item_id, cx).to_string();
+ let visibility = visibility_print_with_space(it.visibility(tcx), it.item_id, cx).to_string();
let name = it.name.unwrap();
let generics_len = format!("{:#}", f.generics.print(cx)).len();
@@ -514,6 +527,9 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
+ name.as_str().len()
+ generics_len;
+ let notable_traits =
+ f.decl.output.as_return().and_then(|output| notable_traits_button(output, cx));
+
wrap_into_item_decl(w, |w| {
wrap_item(w, "fn", |w| {
render_attributes_in_pre(w, it, "");
@@ -531,14 +547,15 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
generics = f.generics.print(cx),
where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline),
decl = f.decl.full_print(header_len, 0, cx),
- notable_traits = notable_traits_decl(&f.decl, cx),
+ notable_traits = notable_traits.unwrap_or_default(),
);
});
});
- document(w, cx, it, None, HeadingOffset::H2)
+ document(w, cx, it, None, HeadingOffset::H2);
}
fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Trait) {
+ let tcx = cx.tcx();
let bounds = bounds(&t.bounds, false, cx);
let required_types = t.items.iter().filter(|m| m.is_ty_associated_type()).collect::<Vec<_>>();
let provided_types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>();
@@ -549,8 +566,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
let count_types = required_types.len() + provided_types.len();
let count_consts = required_consts.len() + provided_consts.len();
let count_methods = required_methods.len() + provided_methods.len();
- let must_implement_one_of_functions =
- cx.tcx().trait_def(t.def_id).must_implement_one_of.clone();
+ let must_implement_one_of_functions = tcx.trait_def(t.def_id).must_implement_one_of.clone();
// Output the trait definition
wrap_into_item_decl(w, |w| {
@@ -559,9 +575,9 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
write!(
w,
"{}{}{}trait {}{}{}",
- it.visibility.print_with_space(it.item_id, cx),
- t.unsafety(cx.tcx()).print_with_space(),
- if t.is_auto(cx.tcx()) { "auto " } else { "" },
+ visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
+ t.unsafety(tcx).print_with_space(),
+ if t.is_auto(tcx) { "auto " } else { "" },
it.name.unwrap(),
t.generics.print(cx),
bounds
@@ -701,7 +717,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
write!(
w,
"<h2 id=\"{0}\" class=\"small-section-header\">\
- {1}<a href=\"#{0}\" class=\"anchor\"></a>\
+ {1}<a href=\"#{0}\" class=\"anchor\">§</a>\
</h2>{2}",
id, title, extra_content
)
@@ -1020,7 +1036,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}
let extern_crates = extern_crates
.into_iter()
- .map(|cnum| cx.shared.tcx.crate_name(cnum).to_string())
+ .map(|cnum| tcx.crate_name(cnum).to_string())
.collect::<Vec<_>>()
.join(",");
let (extern_before, extern_after) =
@@ -1084,7 +1100,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea
fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
wrap_item(w, "typedef", |w| {
render_attributes_in_pre(w, it, "");
- write!(w, "{}", it.visibility.print_with_space(it.item_id, cx));
+ write!(w, "{}", visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx));
write!(
w,
"type {}{}{where_clause} = {type_};",
@@ -1131,7 +1147,7 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean:
write!(
w,
"<h2 id=\"fields\" class=\"fields small-section-header\">\
- Fields<a href=\"#fields\" class=\"anchor\"></a>\
+ Fields<a href=\"#fields\" class=\"anchor\">§</a>\
</h2>"
);
for (field, ty) in fields {
@@ -1140,7 +1156,7 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean:
write!(
w,
"<span id=\"{id}\" class=\"{shortty} small-section-header\">\
- <a href=\"#{id}\" class=\"anchor field\"></a>\
+ <a href=\"#{id}\" class=\"anchor field\">§</a>\
<code>{name}: {ty}</code>\
</span>",
id = id,
@@ -1173,6 +1189,7 @@ fn print_tuple_struct_fields(w: &mut Buffer, cx: &Context<'_>, s: &[clean::Item]
}
fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) {
+ let tcx = cx.tcx();
let count_variants = e.variants().count();
wrap_into_item_decl(w, |w| {
wrap_item(w, "enum", |w| {
@@ -1180,7 +1197,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
write!(
w,
"{}enum {}{}",
- it.visibility.print_with_space(it.item_id, cx),
+ visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
it.name.unwrap(),
e.generics.print(cx),
);
@@ -1215,7 +1232,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
w,
v,
None,
- s.struct_type,
+ s.ctor_kind,
&s.fields,
" ",
false,
@@ -1245,35 +1262,35 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
write!(
w,
"<h2 id=\"variants\" class=\"variants small-section-header\">\
- Variants{}<a href=\"#variants\" class=\"anchor\"></a>\
+ Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
</h2>",
document_non_exhaustive_header(it)
);
document_non_exhaustive(w, it);
+ write!(w, "<div class=\"variants\">");
for variant in e.variants() {
let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
write!(
w,
- "<h3 id=\"{id}\" class=\"variant small-section-header\">\
- <a href=\"#{id}\" class=\"anchor field\"></a>\
- <code>{name}",
+ "<section id=\"{id}\" class=\"variant\">\
+ <a href=\"#{id}\" class=\"anchor\">§</a>",
id = id,
- name = variant.name.unwrap()
);
+ render_stability_since_raw_with_extra(
+ w,
+ variant.stable_since(tcx),
+ variant.const_stability(tcx),
+ it.stable_since(tcx),
+ it.const_stable_since(tcx),
+ " rightside",
+ );
+ write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
if let clean::VariantItem(clean::Variant::Tuple(ref s)) = *variant.kind {
w.write_str("(");
print_tuple_struct_fields(w, cx, s);
w.write_str(")");
}
- w.write_str("</code>");
- render_stability_since_raw(
- w,
- variant.stable_since(cx.tcx()),
- variant.const_stability(cx.tcx()),
- it.stable_since(cx.tcx()),
- it.const_stable_since(cx.tcx()),
- );
- w.write_str("</h3>");
+ w.write_str("</h3></section>");
use crate::clean::Variant;
@@ -1307,8 +1324,8 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
write!(
w,
"<div class=\"sub-variant-field\">\
- <span id=\"{id}\" class=\"variant small-section-header\">\
- <a href=\"#{id}\" class=\"anchor field\"></a>\
+ <span id=\"{id}\" class=\"small-section-header\">\
+ <a href=\"#{id}\" class=\"anchor field\">§</a>\
<code>{f}:&nbsp;{t}</code>\
</span>",
id = id,
@@ -1326,6 +1343,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
document(w, cx, variant, Some(it), HeadingOffset::H4);
}
+ write!(w, "</div>");
}
let def_id = it.item_id.expect_def_id();
render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
@@ -1389,12 +1407,13 @@ fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) {
wrap_into_item_decl(w, |w| {
wrap_item(w, "const", |w| {
+ let tcx = cx.tcx();
render_attributes_in_code(w, it);
write!(
w,
"{vis}const {name}: {typ}",
- vis = it.visibility.print_with_space(it.item_id, cx),
+ vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
name = it.name.unwrap(),
typ = c.type_.print(cx),
);
@@ -1408,9 +1427,9 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle
// ` = 100i32;`
// instead?
- let value = c.value(cx.tcx());
- let is_literal = c.is_literal(cx.tcx());
- let expr = c.expr(cx.tcx());
+ let value = c.value(tcx);
+ let is_literal = c.is_literal(tcx);
+ let expr = c.expr(tcx);
if value.is_some() || is_literal {
write!(w, " = {expr};", expr = Escape(&expr));
} else {
@@ -1439,7 +1458,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
wrap_into_item_decl(w, |w| {
wrap_item(w, "struct", |w| {
render_attributes_in_code(w, it);
- render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx);
+ render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx);
});
});
@@ -1453,14 +1472,14 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
_ => None,
})
.peekable();
- if let CtorKind::Fictive | CtorKind::Fn = s.struct_type {
+ if let None | Some(CtorKind::Fn) = s.ctor_kind {
if fields.peek().is_some() {
write!(
w,
"<h2 id=\"fields\" class=\"fields small-section-header\">\
- {}{}<a href=\"#fields\" class=\"anchor\"></a>\
+ {}{}<a href=\"#fields\" class=\"anchor\">§</a>\
</h2>",
- if let CtorKind::Fictive = s.struct_type { "Fields" } else { "Tuple Fields" },
+ if s.ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
document_non_exhaustive_header(it)
);
document_non_exhaustive(w, it);
@@ -1471,7 +1490,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
write!(
w,
"<span id=\"{id}\" class=\"{item_type} small-section-header\">\
- <a href=\"#{id}\" class=\"anchor field\"></a>\
+ <a href=\"#{id}\" class=\"anchor field\">§</a>\
<code>{name}: {ty}</code>\
</span>",
item_type = ItemType::StructField,
@@ -1495,7 +1514,7 @@ fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
write!(
w,
"{vis}static {mutability}{name}: {typ}",
- vis = it.visibility.print_with_space(it.item_id, cx),
+ vis = visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx),
mutability = s.mutability.print_with_space(),
name = it.name.unwrap(),
typ = s.type_.print(cx)
@@ -1513,7 +1532,7 @@ fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
write!(
w,
" {}type {};\n}}",
- it.visibility.print_with_space(it.item_id, cx),
+ visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx),
it.name.unwrap(),
);
});
@@ -1666,7 +1685,13 @@ fn render_union(
tab: &str,
cx: &Context<'_>,
) {
- write!(w, "{}union {}", it.visibility.print_with_space(it.item_id, cx), it.name.unwrap(),);
+ let tcx = cx.tcx();
+ write!(
+ w,
+ "{}union {}",
+ visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
+ it.name.unwrap(),
+ );
let where_displayed = g
.map(|g| {
@@ -1693,7 +1718,7 @@ fn render_union(
write!(
w,
" {}{}: {},\n{}",
- field.visibility.print_with_space(field.item_id, cx),
+ visibility_print_with_space(field.visibility(tcx), field.item_id, cx),
field.name.unwrap(),
ty.print(cx),
tab
@@ -1714,16 +1739,17 @@ fn render_struct(
w: &mut Buffer,
it: &clean::Item,
g: Option<&clean::Generics>,
- ty: CtorKind,
+ ty: Option<CtorKind>,
fields: &[clean::Item],
tab: &str,
structhead: bool,
cx: &Context<'_>,
) {
+ let tcx = cx.tcx();
write!(
w,
"{}{}{}",
- it.visibility.print_with_space(it.item_id, cx),
+ visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
if structhead { "struct " } else { "" },
it.name.unwrap()
);
@@ -1731,7 +1757,7 @@ fn render_struct(
write!(w, "{}", g.print(cx))
}
match ty {
- CtorKind::Fictive => {
+ None => {
let where_diplayed = g.map(|g| print_where_clause_and_check(w, g, cx)).unwrap_or(false);
// If there wasn't a `where` clause, we add a whitespace.
@@ -1753,7 +1779,7 @@ fn render_struct(
w,
"\n{} {}{}: {},",
tab,
- field.visibility.print_with_space(field.item_id, cx),
+ visibility_print_with_space(field.visibility(tcx), field.item_id, cx),
field.name.unwrap(),
ty.print(cx),
);
@@ -1773,7 +1799,7 @@ fn render_struct(
}
w.write_str("}");
}
- CtorKind::Fn => {
+ Some(CtorKind::Fn) => {
w.write_str("(");
for (i, field) in fields.iter().enumerate() {
if i > 0 {
@@ -1785,7 +1811,7 @@ fn render_struct(
write!(
w,
"{}{}",
- field.visibility.print_with_space(field.item_id, cx),
+ visibility_print_with_space(field.visibility(tcx), field.item_id, cx),
ty.print(cx),
)
}
@@ -1801,7 +1827,7 @@ fn render_struct(
w.write_str(";");
}
}
- CtorKind::Const => {
+ Some(CtorKind::Const) => {
// Needed for PhantomData.
if let Some(g) = g {
write!(w, "{}", print_where_clause(g, cx, 0, Ending::NoNewline));
@@ -1866,11 +1892,11 @@ fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) {
}
fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
- fn write_size_of_layout(w: &mut Buffer, layout: Layout<'_>, tag_size: u64) {
- if layout.abi().is_unsized() {
+ fn write_size_of_layout(w: &mut Buffer, layout: &LayoutS<VariantIdx>, tag_size: u64) {
+ if layout.abi.is_unsized() {
write!(w, "(unsized)");
} else {
- let bytes = layout.size().bytes() - tag_size;
+ let bytes = layout.size.bytes() - tag_size;
write!(w, "{size} byte{pl}", size = bytes, pl = if bytes == 1 { "" } else { "s" },);
}
}
@@ -1882,7 +1908,7 @@ fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
writeln!(
w,
"<h2 id=\"layout\" class=\"small-section-header\"> \
- Layout<a href=\"#layout\" class=\"anchor\"></a></h2>"
+ Layout<a href=\"#layout\" class=\"anchor\">§</a></h2>"
);
writeln!(w, "<div class=\"docblock\">");
@@ -1901,7 +1927,7 @@ fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
chapter for details on type layout guarantees.</p></div>"
);
w.write_str("<p><strong>Size:</strong> ");
- write_size_of_layout(w, ty_layout.layout, 0);
+ write_size_of_layout(w, &ty_layout.layout.0, 0);
writeln!(w, "</p>");
if let Variants::Multiple { variants, tag, tag_encoding, .. } =
&ty_layout.layout.variants()
@@ -1927,7 +1953,7 @@ fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
for (index, layout) in variants.iter_enumerated() {
let name = adt.variant(index).name;
write!(w, "<li><code>{name}</code>: ", name = name);
- write_size_of_layout(w, *layout, tag_size);
+ write_size_of_layout(w, layout, tag_size);
writeln!(w, "</li>");
}
w.write_str("</ul>");
diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs
index 151ec2b28..4514894ca 100644
--- a/src/librustdoc/html/render/span_map.rs
+++ b/src/librustdoc/html/render/span_map.rs
@@ -140,7 +140,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
self.tcx.hir()
}
- fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) {
+ fn visit_path(&mut self, path: &rustc_hir::Path<'tcx>, _id: HirId) {
if self.handle_macro(path.span) {
return;
}
@@ -190,12 +190,4 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
}
intravisit::walk_expr(self, expr);
}
-
- fn visit_use(&mut self, path: &'tcx rustc_hir::Path<'tcx>, id: HirId) {
- if self.handle_macro(path.span) {
- return;
- }
- self.handle_path(path);
- intravisit::walk_use(self, path, id);
- }
}
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 85f63c985..94d8a9fec 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -1,10 +1,8 @@
-use std::ffi::OsStr;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{self, BufReader};
-use std::path::{Component, Path, PathBuf};
+use std::path::{Component, Path};
use std::rc::Rc;
-use std::sync::LazyLock as Lazy;
use itertools::Itertools;
use rustc_data_structures::flock;
@@ -20,123 +18,20 @@ use crate::error::Error;
use crate::html::{layout, static_files};
use crate::{try_err, try_none};
-static FILES_UNVERSIONED: Lazy<FxHashMap<&str, &[u8]>> = Lazy::new(|| {
- map! {
- "FiraSans-Regular.woff2" => static_files::fira_sans::REGULAR,
- "FiraSans-Medium.woff2" => static_files::fira_sans::MEDIUM,
- "FiraSans-LICENSE.txt" => static_files::fira_sans::LICENSE,
- "SourceSerif4-Regular.ttf.woff2" => static_files::source_serif_4::REGULAR,
- "SourceSerif4-Bold.ttf.woff2" => static_files::source_serif_4::BOLD,
- "SourceSerif4-It.ttf.woff2" => static_files::source_serif_4::ITALIC,
- "SourceSerif4-LICENSE.md" => static_files::source_serif_4::LICENSE,
- "SourceCodePro-Regular.ttf.woff2" => static_files::source_code_pro::REGULAR,
- "SourceCodePro-Semibold.ttf.woff2" => static_files::source_code_pro::SEMIBOLD,
- "SourceCodePro-It.ttf.woff2" => static_files::source_code_pro::ITALIC,
- "SourceCodePro-LICENSE.txt" => static_files::source_code_pro::LICENSE,
- "NanumBarunGothic.ttf.woff2" => static_files::nanum_barun_gothic::REGULAR,
- "NanumBarunGothic-LICENSE.txt" => static_files::nanum_barun_gothic::LICENSE,
- "LICENSE-MIT.txt" => static_files::LICENSE_MIT,
- "LICENSE-APACHE.txt" => static_files::LICENSE_APACHE,
- "COPYRIGHT.txt" => static_files::COPYRIGHT,
- }
-});
-
-enum SharedResource<'a> {
- /// This file will never change, no matter what toolchain is used to build it.
- ///
- /// It does not have a resource suffix.
- Unversioned { name: &'static str },
- /// This file may change depending on the toolchain.
- ///
- /// It has a resource suffix.
- ToolchainSpecific { basename: &'static str },
- /// This file may change for any crate within a build, or based on the CLI arguments.
- ///
- /// This differs from normal invocation-specific files because it has a resource suffix.
- InvocationSpecific { basename: &'a str },
-}
-
-impl SharedResource<'_> {
- fn extension(&self) -> Option<&OsStr> {
- use SharedResource::*;
- match self {
- Unversioned { name }
- | ToolchainSpecific { basename: name }
- | InvocationSpecific { basename: name } => Path::new(name).extension(),
- }
- }
-
- fn path(&self, cx: &Context<'_>) -> PathBuf {
- match self {
- SharedResource::Unversioned { name } => cx.dst.join(name),
- SharedResource::ToolchainSpecific { basename } => cx.suffix_path(basename),
- SharedResource::InvocationSpecific { basename } => cx.suffix_path(basename),
- }
- }
-
- fn should_emit(&self, emit: &[EmitType]) -> bool {
- if emit.is_empty() {
- return true;
- }
- let kind = match self {
- SharedResource::Unversioned { .. } => EmitType::Unversioned,
- SharedResource::ToolchainSpecific { .. } => EmitType::Toolchain,
- SharedResource::InvocationSpecific { .. } => EmitType::InvocationSpecific,
- };
- emit.contains(&kind)
- }
-}
-
-impl Context<'_> {
- fn suffix_path(&self, filename: &str) -> PathBuf {
- // We use splitn vs Path::extension here because we might get a filename
- // like `style.min.css` and we want to process that into
- // `style-suffix.min.css`. Path::extension would just return `css`
- // which would result in `style.min-suffix.css` which isn't what we
- // want.
- let (base, ext) = filename.split_once('.').unwrap();
- let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext);
- self.dst.join(&filename)
- }
-
- fn write_shared(
- &self,
- resource: SharedResource<'_>,
- contents: impl 'static + Send + AsRef<[u8]>,
- emit: &[EmitType],
- ) -> Result<(), Error> {
- if resource.should_emit(emit) {
- self.shared.fs.write(resource.path(self), contents)
- } else {
- Ok(())
- }
- }
-
- fn write_minify(
- &self,
- resource: SharedResource<'_>,
- contents: impl 'static + Send + AsRef<str> + AsRef<[u8]>,
- minify: bool,
- emit: &[EmitType],
- ) -> Result<(), Error> {
- if minify {
- let contents = contents.as_ref();
- let contents = if resource.extension() == Some(OsStr::new("css")) {
- minifier::css::minify(contents)
- .map_err(|e| {
- Error::new(format!("failed to minify CSS file: {}", e), resource.path(self))
- })?
- .to_string()
- } else {
- minifier::js::minify(contents).to_string()
- };
- self.write_shared(resource, contents, emit)
- } else {
- self.write_shared(resource, contents, emit)
- }
- }
-}
-
+/// Rustdoc writes out two kinds of shared files:
+/// - Static files, which are embedded in the rustdoc binary and are written with a
+/// filename that includes a hash of their contents. These will always have a new
+/// URL if the contents change, so they are safe to cache with the
+/// `Cache-Control: immutable` directive. They are written under the static.files/
+/// directory and are written when --emit-type is empty (default) or contains
+/// "toolchain-specific". If using the --static-root-path flag, it should point
+/// to a URL path prefix where each of these filenames can be fetched.
+/// - Invocation specific files. These are generated based on the crate(s) being
+/// documented. Their filenames need to be predictable without knowing their
+/// contents, so they do not include a hash in their filename and are not safe to
+/// cache with `Cache-Control: immutable`. They include the contents of the
+/// --resource-suffix flag and are emitted when --emit-type is empty (default)
+/// or contains "invocation-specific".
pub(super) fn write_shared(
cx: &mut Context<'_>,
krate: &Crate,
@@ -149,139 +44,52 @@ pub(super) fn write_shared(
let lock_file = cx.dst.join(".lock");
let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file);
- // Minified resources are usually toolchain resources. If they're not, they should use `cx.write_minify` directly.
- fn write_minify(
- basename: &'static str,
- contents: impl 'static + Send + AsRef<str> + AsRef<[u8]>,
- cx: &Context<'_>,
- options: &RenderOptions,
- ) -> Result<(), Error> {
- cx.write_minify(
- SharedResource::ToolchainSpecific { basename },
- contents,
- options.enable_minification,
- &options.emit,
- )
- }
-
- // Toolchain resources should never be dynamic.
- let write_toolchain = |p: &'static _, c: &'static _| {
- cx.write_shared(SharedResource::ToolchainSpecific { basename: p }, c, &options.emit)
- };
-
- // Crate resources should always be dynamic.
- let write_crate = |p: &_, make_content: &dyn Fn() -> Result<Vec<u8>, Error>| {
+ // InvocationSpecific resources should always be dynamic.
+ let write_invocation_specific = |p: &str, make_content: &dyn Fn() -> Result<Vec<u8>, Error>| {
let content = make_content()?;
- cx.write_shared(SharedResource::InvocationSpecific { basename: p }, content, &options.emit)
+ if options.emit.is_empty() || options.emit.contains(&EmitType::InvocationSpecific) {
+ let output_filename = static_files::suffix_path(p, &cx.shared.resource_suffix);
+ cx.shared.fs.write(cx.dst.join(output_filename), content)
+ } else {
+ Ok(())
+ }
};
- // Given "foo.svg", return e.g. "url(\"foo1.58.0.svg\")"
- fn ver_url(cx: &Context<'_>, basename: &'static str) -> String {
- format!(
- "url(\"{}\")",
- SharedResource::ToolchainSpecific { basename }
- .path(cx)
- .file_name()
- .unwrap()
- .to_str()
- .unwrap()
- )
- }
-
- // We use the AUTOREPLACE mechanism to inject into our static JS and CSS certain
- // values that are only known at doc build time. Since this mechanism is somewhat
- // surprising when reading the code, please limit it to rustdoc.css.
- write_minify(
- "rustdoc.css",
- static_files::RUSTDOC_CSS
- .replace(
- "/* AUTOREPLACE: */url(\"toggle-minus.svg\")",
- &ver_url(cx, "toggle-minus.svg"),
- )
- .replace("/* AUTOREPLACE: */url(\"toggle-plus.svg\")", &ver_url(cx, "toggle-plus.svg"))
- .replace("/* AUTOREPLACE: */url(\"down-arrow.svg\")", &ver_url(cx, "down-arrow.svg")),
- cx,
- options,
- )?;
-
- // Add all the static files. These may already exist, but we just
- // overwrite them anyway to make sure that they're fresh and up-to-date.
- write_minify("settings.css", static_files::SETTINGS_CSS, cx, options)?;
- write_minify("noscript.css", static_files::NOSCRIPT_CSS, cx, options)?;
-
- // To avoid "light.css" to be overwritten, we'll first run over the received themes and only
- // then we'll run over the "official" styles.
- let mut themes: FxHashSet<String> = FxHashSet::default();
+ cx.shared
+ .fs
+ .create_dir_all(cx.dst.join("static.files"))
+ .map_err(|e| PathError::new(e, "static.files"))?;
+ // Handle added third-party themes
for entry in &cx.shared.style_files {
let theme = entry.basename()?;
let extension =
try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path);
- // Handle the official themes
- match theme.as_str() {
- "light" => write_minify("light.css", static_files::themes::LIGHT, cx, options)?,
- "dark" => write_minify("dark.css", static_files::themes::DARK, cx, options)?,
- "ayu" => write_minify("ayu.css", static_files::themes::AYU, cx, options)?,
- _ => {
- // Handle added third-party themes
- let filename = format!("{}.{}", theme, extension);
- write_crate(&filename, &|| Ok(try_err!(fs::read(&entry.path), &entry.path)))?;
- }
- };
-
- themes.insert(theme.to_owned());
- }
-
- if (*cx.shared).layout.logo.is_empty() {
- write_toolchain("rust-logo.svg", static_files::RUST_LOGO_SVG)?;
- }
- if (*cx.shared).layout.favicon.is_empty() {
- write_toolchain("favicon.svg", static_files::RUST_FAVICON_SVG)?;
- write_toolchain("favicon-16x16.png", static_files::RUST_FAVICON_PNG_16)?;
- write_toolchain("favicon-32x32.png", static_files::RUST_FAVICON_PNG_32)?;
- }
- write_toolchain("wheel.svg", static_files::WHEEL_SVG)?;
- write_toolchain("clipboard.svg", static_files::CLIPBOARD_SVG)?;
- write_toolchain("down-arrow.svg", static_files::DOWN_ARROW_SVG)?;
- write_toolchain("toggle-minus.svg", static_files::TOGGLE_MINUS_PNG)?;
- write_toolchain("toggle-plus.svg", static_files::TOGGLE_PLUS_PNG)?;
-
- let mut themes: Vec<&String> = themes.iter().collect();
- themes.sort();
-
- write_minify("main.js", static_files::MAIN_JS, cx, options)?;
- write_minify("search.js", static_files::SEARCH_JS, cx, options)?;
- write_minify("settings.js", static_files::SETTINGS_JS, cx, options)?;
-
- if cx.include_sources {
- write_minify("source-script.js", static_files::sidebar::SOURCE_SCRIPT, cx, options)?;
- }
-
- write_minify("storage.js", static_files::STORAGE_JS, cx, options)?;
+ // Skip the official themes. They are written below as part of STATIC_FILES_LIST.
+ if matches!(theme.as_str(), "light" | "dark" | "ayu") {
+ continue;
+ }
- if cx.shared.layout.scrape_examples_extension {
- cx.write_minify(
- SharedResource::InvocationSpecific { basename: "scrape-examples.js" },
- static_files::SCRAPE_EXAMPLES_JS,
- options.enable_minification,
- &options.emit,
- )?;
+ let bytes = try_err!(fs::read(&entry.path), &entry.path);
+ let filename = format!("{}{}.{}", theme, cx.shared.resource_suffix, extension);
+ cx.shared.fs.write(cx.dst.join(filename), bytes)?;
}
+ // When the user adds their own CSS files with --extend-css, we write that as an
+ // invocation-specific file (that is, with a resource suffix).
if let Some(ref css) = cx.shared.layout.css_file_extension {
let buffer = try_err!(fs::read_to_string(css), css);
- // This varies based on the invocation, so it can't go through the write_minify wrapper.
- cx.write_minify(
- SharedResource::InvocationSpecific { basename: "theme.css" },
- buffer,
- options.enable_minification,
- &options.emit,
- )?;
+ let path = static_files::suffix_path("theme.css", &cx.shared.resource_suffix);
+ cx.shared.fs.write(cx.dst.join(path), buffer)?;
}
- write_minify("normalize.css", static_files::NORMALIZE_CSS, cx, options)?;
- for (name, contents) in &*FILES_UNVERSIONED {
- cx.write_shared(SharedResource::Unversioned { name }, contents, &options.emit)?;
+
+ if options.emit.is_empty() || options.emit.contains(&EmitType::Toolchain) {
+ let static_dir = cx.dst.join(Path::new("static.files"));
+ static_files::for_each(|f: &static_files::StaticFile| {
+ let filename = static_dir.join(f.output_filename());
+ cx.shared.fs.write(filename, f.minified())
+ })?;
}
/// Read a file and return all lines that match the `"{crate}":{data},` format,
@@ -463,7 +271,7 @@ pub(super) fn write_shared(
v.push_str("\\\n}');\ncreateSourceSidebar();\n");
Ok(v.into_bytes())
};
- write_crate("source-files.js", &make_sources)?;
+ write_invocation_specific("source-files.js", &make_sources)?;
}
// Update the search index and crate list.
@@ -477,7 +285,7 @@ pub(super) fn write_shared(
// Sort the indexes by crate so the file will be generated identically even
// with rustdoc running in parallel.
all_indexes.sort();
- write_crate("search-index.js", &|| {
+ write_invocation_specific("search-index.js", &|| {
let mut v = String::from("var searchIndex = JSON.parse('{\\\n");
v.push_str(&all_indexes.join(",\\\n"));
v.push_str(
@@ -490,7 +298,7 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
Ok(v.into_bytes())
})?;
- write_crate("crates.js", &|| {
+ write_invocation_specific("crates.js", &|| {
let krates = krates.iter().map(|k| format!("\"{}\"", k)).join(",");
Ok(format!("window.ALL_CRATES = [{}];", krates).into_bytes())
})?;
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 7ab65bff3..54e296959 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -258,7 +258,7 @@ where
pub(crate) enum SourceContext {
Standalone,
- Embedded { offset: usize },
+ Embedded { offset: usize, needs_expansion: bool },
}
/// Wrapper struct to render the source code of a file. This will do things like
@@ -274,28 +274,37 @@ pub(crate) fn print_src(
) {
let lines = s.lines().count();
let mut line_numbers = Buffer::empty_from(buf);
+ let extra;
line_numbers.write_str("<pre class=\"src-line-numbers\">");
+ let current_href = &context
+ .href_from_span(clean::Span::new(file_span), false)
+ .expect("only local crates should have sources emitted");
match source_context {
SourceContext::Standalone => {
+ extra = None;
for line in 1..=lines {
- writeln!(line_numbers, "<span id=\"{0}\">{0}</span>", line)
+ writeln!(line_numbers, "<a href=\"#{line}\" id=\"{line}\">{line}</a>")
}
}
- SourceContext::Embedded { offset } => {
- for line in 1..=lines {
- writeln!(line_numbers, "<span>{0}</span>", line + offset)
+ SourceContext::Embedded { offset, needs_expansion } => {
+ extra = if needs_expansion {
+ Some(r#"<button class="expand">&varr;</button>"#)
+ } else {
+ None
+ };
+ for line_number in 1..=lines {
+ let line = line_number + offset;
+ writeln!(line_numbers, "<span>{line}</span>")
}
}
}
line_numbers.write_str("</pre>");
- let current_href = &context
- .href_from_span(clean::Span::new(file_span), false)
- .expect("only local crates should have sources emitted");
highlight::render_source_with_highlighting(
s,
buf,
line_numbers,
highlight::HrefContext { context, file_span, root_path, current_href },
decoration_info,
+ extra,
);
}
diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css
index 301f03a16..54e8b6561 100644
--- a/src/librustdoc/html/static/css/noscript.css
+++ b/src/librustdoc/html/static/css/noscript.css
@@ -22,3 +22,9 @@ nav.sub {
.source .sidebar {
display: none;
}
+
+.notable-traits {
+ /* layout requires javascript
+ https://github.com/rust-lang/rust/issues/102576 */
+ display: none;
+}
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 1cc954a98..afcb40224 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -4,7 +4,7 @@
font-style: normal;
font-weight: 400;
src: local('Fira Sans'),
- url("FiraSans-Regular.woff2") format("woff2");
+ url("FiraSans-Regular-018c141bf0843ffd.woff2") format("woff2");
font-display: swap;
}
@font-face {
@@ -12,7 +12,7 @@
font-style: normal;
font-weight: 500;
src: local('Fira Sans Medium'),
- url("FiraSans-Medium.woff2") format("woff2");
+ url("FiraSans-Medium-8f9a781e4970d388.woff2") format("woff2");
font-display: swap;
}
@@ -22,7 +22,7 @@
font-style: normal;
font-weight: 400;
src: local('Source Serif 4'),
- url("SourceSerif4-Regular.ttf.woff2") format("woff2");
+ url("SourceSerif4-Regular-1f7d512b176f0f72.ttf.woff2") format("woff2");
font-display: swap;
}
@font-face {
@@ -30,7 +30,7 @@
font-style: italic;
font-weight: 400;
src: local('Source Serif 4 Italic'),
- url("SourceSerif4-It.ttf.woff2") format("woff2");
+ url("SourceSerif4-It-d034fe4ef9d0fa00.ttf.woff2") format("woff2");
font-display: swap;
}
@font-face {
@@ -38,7 +38,7 @@
font-style: normal;
font-weight: 700;
src: local('Source Serif 4 Bold'),
- url("SourceSerif4-Bold.ttf.woff2") format("woff2");
+ url("SourceSerif4-Bold-124a1ca42af929b6.ttf.woff2") format("woff2");
font-display: swap;
}
@@ -49,28 +49,28 @@
font-weight: 400;
/* Avoid using locally installed font because bad versions are in circulation:
* see https://github.com/rust-lang/rust/issues/24355 */
- src: url("SourceCodePro-Regular.ttf.woff2") format("woff2");
+ src: url("SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-family: 'Source Code Pro';
font-style: italic;
font-weight: 400;
- src: url("SourceCodePro-It.ttf.woff2") format("woff2");
+ src: url("SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-family: 'Source Code Pro';
font-style: normal;
font-weight: 600;
- src: url("SourceCodePro-Semibold.ttf.woff2") format("woff2");
+ src: url("SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2") format("woff2");
font-display: swap;
}
/* Avoid using legacy CJK serif fonts in Windows like Batang. */
@font-face {
font-family: 'NanumBarunGothic';
- src: url("NanumBarunGothic.ttf.woff2") format("woff2");
+ src: url("NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2") format("woff2");
font-display: swap;
unicode-range: U+AC00-D7AF, U+1100-11FF, U+3130-318F, U+A960-A97F, U+D7B0-D7FF;
}
@@ -159,13 +159,9 @@ h1.fqn {
.main-heading {
display: flex;
flex-wrap: wrap;
- justify-content: space-between;
padding-bottom: 6px;
margin-bottom: 15px;
}
-#toggle-all-docs {
- text-decoration: none;
-}
/* The only headings that get underlines are:
Markdown-generated headings within the top-doc
Rustdoc-generated h2 section headings (e.g. "Implementations", "Required Methods", etc)
@@ -199,17 +195,14 @@ h1, h2, h3, h4, h5, h6,
span.since,
a.srclink,
#help-button > a,
-details.rustdoc-toggle.top-doc > summary,
-details.rustdoc-toggle.non-exhaustive > summary,
-.scraped-example-title,
-.more-examples-toggle summary, .more-examples-toggle .hide-more,
-.example-links a,
+summary.hideme,
+.scraped-example-list,
/* This selector is for the items listed in the "all items" page. */
ul.all-items {
font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif;
}
-a#toggle-all-docs,
+#toggle-all-docs,
a.anchor,
.small-section-header a,
#source-sidebar a,
@@ -219,12 +212,8 @@ pre.rust a,
.mobile-topbar h2 a,
h1 a,
.search-results a,
-.module-item .stab,
-.import-item .stab,
-.result-name .primitive > i, .result-name .keyword > i,
-.method .where,
-.fn .where,
-.where.fmt-newline {
+.item-left .stab,
+.result-name .primitive > i, .result-name .keyword > i {
color: var(--main-color);
}
@@ -249,7 +238,6 @@ h1 a,
}
.content span.fn, .content a.fn,
-.content .fnname,
.content span.method, .content a.method,
.content span.tymethod, .content a.tymethod {
color: var(--function-link-color);
@@ -297,10 +285,21 @@ p:last-child {
button {
/* Buttons on Safari have different default padding than other platforms. Make them the same. */
padding: 1px 6px;
+ /* Opinionated tweak: use pointer cursor as clickability signifier. */
+ cursor: pointer;
}
/* end tweaks for normalize.css 8 */
+button#toggle-all-docs {
+ padding: 0;
+ background: none;
+ border: none;
+ /* iOS button gradient: https://stackoverflow.com/q/5438567 */
+ -webkit-appearance: none;
+ opacity: 1;
+}
+
.rustdoc {
display: flex;
flex-direction: row;
@@ -311,7 +310,7 @@ main {
position: relative;
flex-grow: 1;
padding: 10px 15px 40px 45px;
- min-width: 0;
+ min-width: 0; /* avoid growing beyond the size limit */
}
.source main {
@@ -360,7 +359,8 @@ img {
overflow: visible;
}
-.sub-logo-container {
+.sub-logo-container, .logo-container {
+ /* zero text boxes so that computed line height = image height exactly */
line-height: 0;
}
@@ -370,14 +370,17 @@ img {
object-fit: contain;
}
+.rust-logo {
+ filter: var(--rust-logo-filter);
+}
+
.sidebar, .mobile-topbar, .sidebar-menu-toggle {
background-color: var(--sidebar-background-color);
}
.sidebar {
font-size: 0.875rem;
- width: 200px;
- min-width: 200px;
+ flex: 0 0 200px;
overflow-y: scroll;
position: sticky;
height: 100vh;
@@ -386,12 +389,7 @@ img {
}
.rustdoc.source .sidebar {
- width: 50px;
- min-width: 0px;
- max-width: 300px;
- flex-grow: 0;
- flex-shrink: 0;
- flex-basis: auto;
+ flex-basis: 50px;
border-right: 1px solid;
overflow-x: hidden;
/* The sidebar is by default hidden */
@@ -412,7 +410,7 @@ img {
.source-sidebar-expanded .source .sidebar {
overflow-y: auto;
- width: 300px;
+ flex-basis: 300px;
}
.source-sidebar-expanded .source .sidebar > *:not(#sidebar-toggle) {
@@ -458,10 +456,9 @@ img {
}
.sidebar .logo-container {
- display: flex;
margin-top: 10px;
margin-bottom: 10px;
- justify-content: center;
+ text-align: center;
}
.version {
@@ -479,23 +476,17 @@ ul.block, .block li {
list-style: none;
}
-.block a,
-.sidebar h2 a,
-.sidebar h3 a {
+.sidebar-elems a,
+.sidebar > h2 a {
display: block;
- padding: 0.25rem;
+ padding: 0.25rem; /* 4px */
margin-left: -0.25rem;
-
- text-overflow: ellipsis;
- overflow: hidden;
}
.sidebar h2 {
overflow-wrap: anywhere;
padding: 0;
- margin: 0;
- margin-top: 0.7rem;
- margin-bottom: 0.7rem;
+ margin: 0.7rem 0;
}
.sidebar h3 {
@@ -523,6 +514,8 @@ ul.block, .block li {
.sidebar-elems .block li a {
white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
}
.mobile-topbar {
@@ -570,15 +563,16 @@ ul.block, .block li {
border-color: var(--example-line-numbers-border-color);
}
-.src-line-numbers span {
- cursor: pointer;
+.src-line-numbers a, .src-line-numbers span {
color: var(--src-line-numbers-span-color);
}
-.src-line-numbers .line-highlighted {
- background-color: var(--src-line-number-highlighted-background-color);
-}
.src-line-numbers :target {
background-color: transparent;
+ border-right: none;
+ padding-right: 0;
+}
+.src-line-numbers .line-highlighted {
+ background-color: var(--src-line-number-highlighted-background-color);
}
.search-loading {
@@ -636,22 +630,16 @@ pre, .rustdoc.source .example-wrap {
.docblock table {
margin: .5em 0;
- width: calc(100% - 2px);
- overflow-x: auto;
- display: block;
border-collapse: collapse;
}
-.docblock table td {
+.docblock table td, .docblock table th {
padding: .5em;
- border: 1px dashed var(--border-color);
- vertical-align: top;
+ border: 1px solid var(--border-color);
}
-.docblock table th {
- padding: .5em;
- text-align: left;
- border: 1px solid var(--border-color);
+.docblock table tbody tr:nth-child(2n) {
+ background: var(--table-alt-row-background-color);
}
/* Shift "where ..." part of method or fn definition down a line */
@@ -672,7 +660,6 @@ pre, .rustdoc.source .example-wrap {
}
#main-content > .item-info {
- margin-top: 0;
margin-left: 0;
}
@@ -701,8 +688,8 @@ a {
}
.small-section-header {
- display: flex;
- justify-content: space-between;
+ /* fields use <span> tags, but should get their own lines */
+ display: block;
position: relative;
}
@@ -710,7 +697,7 @@ a {
display: initial;
}
-.impl:hover > .anchor, .trait-impl:hover > .anchor {
+.impl:hover > .anchor, .trait-impl:hover > .anchor, .variant:hover > .anchor {
display: inline-block;
position: absolute;
}
@@ -730,9 +717,6 @@ a {
h2.small-section-header > .anchor {
padding-right: 6px;
}
-.anchor::before {
- content: '§';
-}
.main-heading a:hover,
.example-wrap > pre.rust a:hover,
@@ -787,14 +771,12 @@ table,
margin-top: 0;
white-space: nowrap;
/* flex layout allows shrinking the <select> appropriately if it becomes too large */
- display: inline-flex;
- max-width: 100%;
+ display: flex;
/* make things look like in a line, despite the fact that we're using a layout
with boxes (i.e. from the flex layout) */
align-items: baseline;
}
#crate-search-div {
- display: inline-block;
/* ensures that 100% in properties of #crate-search-div:after
are relative to the size of this div */
position: relative;
@@ -803,10 +785,8 @@ table,
}
#crate-search {
min-width: 115px;
- padding: 0;
/* keep these two in sync with "@-moz-document url-prefix()" below */
- padding-left: 4px;
- padding-right: 23px;
+ padding: 0 23px 0 4px;
/* prevents the <select> from overflowing the containing div in case it's shrunk */
max-width: 100%;
/* contents can overflow because of max-width limit, then show ellipsis */
@@ -824,6 +804,9 @@ table,
line-height: 1.5;
font-weight: 500;
}
+#crate-search:hover, #crate-search:focus {
+ border-color: var(--crate-search-hover-border);
+}
/* cancel stylistic differences in padding in firefox
for "appearance: none"-style (or equivalent) <select>s */
@-moz-document url-prefix() {
@@ -847,8 +830,13 @@ so that we can apply CSS-filters to change the arrow color in themes */
background-repeat: no-repeat;
background-size: 20px;
background-position: calc(100% - 2px) 56%;
- /* image is black color, themes should apply a "filter" property to change the color */
- background-image: /* AUTOREPLACE: */url("down-arrow.svg");
+ /* image is black color */
+ background-image: url("down-arrow-927217e04c7463ac.svg");
+ /* changes the arrow image color */
+ filter: var(--crate-search-div-filter);
+}
+#crate-search-div:hover::after, #crate-search-div:focus-within::after {
+ filter: var(--crate-search-div-hover-filter);
}
#crate-search > option {
font-size: 1rem;
@@ -873,40 +861,29 @@ so that we can apply CSS-filters to change the arrow color in themes */
.search-results {
display: none;
- padding-bottom: 2em;
}
.search-results.active {
display: block;
- /* prevent overhanging tabs from moving the first result */
- clear: both;
-}
-
-.search-results .desc > span {
- white-space: nowrap;
- text-overflow: ellipsis;
- overflow: hidden;
- display: block;
}
.search-results > a {
- display: block;
+ display: flex;
/* A little margin ensures the browser's outlining of focused links has room to display. */
margin-left: 2px;
margin-right: 2px;
- border-bottom: 1px solid #aaa3;
+ border-bottom: 1px solid var(--search-result-border-color);
+ gap: 1em;
}
.search-results > a > div {
- display: flex;
- flex-flow: row wrap;
+ flex: 1;
}
-.search-results .result-name, .search-results div.desc {
- width: 50%;
-}
-.search-results .result-name {
- padding-right: 1em;
+.search-results > a > div.desc {
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
}
.search-results a:hover,
@@ -914,26 +891,32 @@ so that we can apply CSS-filters to change the arrow color in themes */
background-color: var(--search-result-link-focus-background-color);
}
+.search-results .result-name span.alias {
+ color: var(--search-results-alias-color);
+}
+.search-results .result-name span.grey {
+ color: var(--search-results-grey-color);
+}
+
.popover {
- font-size: 1rem;
position: absolute;
+ top: 100%;
right: 0;
z-index: 2;
display: block;
margin-top: 7px;
border-radius: 3px;
border: 1px solid var(--border-color);
- font-size: 1rem;
+ --popover-arrow-offset: 11px;
}
/* This rule is to draw the little arrow connecting the settings menu to the gear icon. */
.popover::before {
content: '';
position: absolute;
- right: 11px;
+ right: var(--popover-arrow-offset);
border: solid var(--border-color);
border-width: 1px 1px 0 0;
- display: inline-block;
padding: 4px;
transform: rotate(-45deg);
top: -5px;
@@ -947,16 +930,12 @@ so that we can apply CSS-filters to change the arrow color in themes */
/* use larger max-width for help popover, but not for help.html */
#help.popover {
max-width: 600px;
-}
-
-#help.popover::before {
- right: 48px;
+ --popover-arrow-offset: 48px;
}
#help dt {
float: left;
clear: left;
- display: block;
margin-right: 0.5rem;
}
#help span.top, #help span.bottom {
@@ -1024,11 +1003,9 @@ so that we can apply CSS-filters to change the arrow color in themes */
0 -1px 0 black;
}
-.module-item .stab,
-.import-item .stab {
+.item-left .stab {
border-radius: 3px;
display: inline-block;
- font-size: 0.875rem;
line-height: 1.2;
margin-bottom: 0;
margin-left: 0.3125em;
@@ -1048,7 +1025,6 @@ so that we can apply CSS-filters to change the arrow color in themes */
.rightside {
padding-left: 12px;
- padding-right: 2px;
float: right;
}
@@ -1090,7 +1066,7 @@ pre.rust .bool-val {
pre.rust .self {
color: var(--code-highlight-self-color);
}
-pre.rust .attribute {
+pre.rust .attr {
color: var(--code-highlight-attribute-color);
}
pre.rust .macro,
@@ -1147,7 +1123,6 @@ pre.rust .doccomment {
.example-wrap .tooltip {
position: absolute;
display: block;
- cursor: pointer;
left: -25px;
top: 5px;
}
@@ -1164,6 +1139,8 @@ pre.rust .doccomment {
width: max-content;
top: -2px;
z-index: 1;
+ background-color: var(--tooltip-background-color);
+ color: var(--tooltip-color);
}
.example-wrap .tooltip::before {
@@ -1172,10 +1149,10 @@ pre.rust .doccomment {
top: 50%;
left: 16px;
margin-top: -5px;
- border-width: 5px;
- border-style: solid;
display: none;
z-index: 1;
+ border: 5px solid transparent;
+ border-right-color: var(--tooltip-background-color);
}
.example-wrap.ignore .tooltip::after {
@@ -1203,7 +1180,6 @@ pre.rust .doccomment {
}
a.test-arrow {
- display: inline-block;
visibility: hidden;
position: absolute;
padding: 5px 10px 5px 10px;
@@ -1212,6 +1188,12 @@ a.test-arrow {
top: 5px;
right: 5px;
z-index: 1;
+ color: var(--test-arrow-color);
+ background-color: var(--test-arrow-background-color);
+}
+a.test-arrow:hover {
+ color: var(--test-arrow-hover-color);
+ background-color: var(--test-arrow-hover-background-color);
}
.example-wrap:hover .test-arrow {
visibility: visible;
@@ -1231,12 +1213,6 @@ a.test-arrow {
font-size: 1.25rem;
}
-h3.variant {
- font-weight: 600;
- font-size: 1.125rem;
- margin-bottom: 10px;
-}
-
.sub-variant h4 {
font-size: 1rem;
font-weight: 400;
@@ -1253,59 +1229,40 @@ h3.variant {
margin-left: 24px;
}
-:target > code, :target > .code-header {
- opacity: 1;
-}
-
:target {
padding-right: 3px;
+ background-color: var(--target-background-color);
+ border-right: 3px solid var(--target-border-color);
}
-.notable-traits-tooltip {
- display: inline-block;
- cursor: pointer;
-}
-
-.notable-traits:hover .notable-traits-tooltiptext,
-.notable-traits .notable-traits-tooltiptext.force-tooltip {
- display: inline-block;
+.notable-traits {
+ color: inherit;
+ margin-right: 15px;
+ position: relative;
}
-.notable-traits .notable-traits-tooltiptext {
- display: none;
- padding: 5px 3px 3px 3px;
- border-radius: 6px;
- margin-left: 5px;
- z-index: 10;
- font-size: 1rem;
- cursor: default;
+/* placeholder thunk so that the mouse can easily travel from "(i)" to popover
+ the resulting "hover tunnel" is a stepped triangle, approximating
+ https://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown */
+.notable-traits:hover::after {
position: absolute;
- border: 1px solid;
-}
-
-.notable-traits-tooltip::after {
- /* The margin on the tooltip does not capture hover events,
- this extends the area of hover enough so that mouse hover is not
- lost when moving the mouse to the tooltip */
- content: "\00a0\00a0\00a0";
-}
-
-.notable-traits .docblock {
- margin: 0;
+ top: calc(100% - 10px);
+ left: -15px;
+ right: -15px;
+ height: 20px;
+ content: "\00a0";
}
-.notable-traits .notable {
- margin: 0;
- margin-bottom: 13px;
- font-size: 1.1875rem;
- font-weight: 600;
- display: block;
+.notable .docblock {
+ margin: 0.25em 0.5em;
}
-.notable-traits .docblock code.content {
+.notable .docblock pre, .notable .docblock code {
+ background: transparent;
margin: 0;
padding: 0;
font-size: 1.25rem;
+ white-space: pre-wrap;
}
.search-failed {
@@ -1335,7 +1292,6 @@ h3.variant {
#titles > button {
text-align: center;
font-size: 1.125rem;
- cursor: pointer;
border: 0;
border-top: 2px solid;
flex: 1;
@@ -1348,12 +1304,6 @@ h3.variant {
font-size: 1rem;
}
-.notable-traits {
- cursor: pointer;
- z-index: 2;
- margin-left: 5px;
-}
-
#sidebar-toggle {
position: sticky;
top: 0;
@@ -1362,8 +1312,8 @@ h3.variant {
border-bottom: 1px solid;
display: flex;
height: 40px;
- justify-content: center;
- align-items: center;
+ justify-content: stretch;
+ align-items: stretch;
z-index: 10;
}
#source-sidebar {
@@ -1376,44 +1326,51 @@ h3.variant {
border-bottom: 1px solid var(--border-color);
margin-bottom: 6px;
}
+#source-sidebar div.files > a:hover, details.dir-entry summary:hover,
+#source-sidebar div.files > a:focus, details.dir-entry summary:focus {
+ background-color: var(--source-sidebar-background-hover);
+}
+#source-sidebar div.files > a.selected {
+ background-color: var(--source-sidebar-background-selected);
+}
#sidebar-toggle > button {
font-size: inherit;
font-weight: bold;
background: none;
color: inherit;
- cursor: pointer;
text-align: center;
border: none;
outline: none;
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
- /* work around button layout strangeness: https://stackoverflow.com/q/7271561 */
- width: 100%;
+ flex: 1 1;
/* iOS button gradient: https://stackoverflow.com/q/5438567 */
-webkit-appearance: none;
opacity: 1;
}
#settings-menu, #help-button {
margin-left: 4px;
- outline: none;
+ display: flex;
}
#settings-menu > a, #help-button > a, #copy-path {
width: 33px;
- cursor: pointer;
- line-height: 1.5;
}
#settings-menu > a, #help-button > a {
- padding: 5px;
- height: 100%;
- display: block;
+ display: flex;
+ align-items: center;
+ justify-content: center;
background-color: var(--button-background-color);
border: 1px solid var(--border-color);
border-radius: 2px;
+ color: var(--settings-button-color);
+ /* Rare exception to specifying font sizes in rem. Since this is acting
+ as an icon, it's okay to specify their sizes in pixels. */
+ font-size: 20px;
+}
+
+#settings-menu > a:hover, #settings-menu > a:focus,
+#help-button > a:hover, #help-button > a:focus {
+ border-color: var(--settings-button-border-focus);
}
#copy-path {
@@ -1444,14 +1401,6 @@ h3.variant {
animation: rotating 2s linear infinite;
}
-#help-button > a {
- text-align: center;
- /* Rare exception to specifying font sizes in rem. Since this is acting
- as an icon, it's okay to specify their sizes in pixels. */
- font-size: 20px;
- padding-top: 2px;
-}
-
kbd {
display: inline-block;
padding: 3px 5px;
@@ -1461,6 +1410,9 @@ kbd {
border: solid 1px var(--border-color);
border-radius: 3px;
cursor: default;
+ color: var(--kbd--color);
+ background-color: var(--kbd-background);
+ box-shadow: inset 0 -1px 0 var(--kbd-box-shadow-color);
}
ul.all-items > li {
@@ -1522,6 +1474,7 @@ details.rustdoc-toggle {
"Expand description" or "Show methods". */
details.rustdoc-toggle > summary.hideme {
cursor: pointer;
+ font-size: 1rem;
}
details.rustdoc-toggle > summary {
@@ -1539,15 +1492,15 @@ details.rustdoc-toggle > summary.hideme > span {
}
details.rustdoc-toggle > summary::before {
+ background: url("toggle-plus-1092eb4930d581b0.svg") no-repeat top left;
content: "";
cursor: pointer;
width: 16px;
height: 16px;
- background-repeat: no-repeat;
- background-position: top left;
display: inline-block;
vertical-align: middle;
opacity: .5;
+ filter: var(--toggle-filter);
}
details.rustdoc-toggle > summary.hideme > span,
@@ -1584,13 +1537,6 @@ details.rustdoc-toggle > summary:focus-visible::before {
outline-offset: 1px;
}
-details.rustdoc-toggle.top-doc > summary,
-details.rustdoc-toggle.top-doc > summary::before,
-details.rustdoc-toggle.non-exhaustive > summary,
-details.rustdoc-toggle.non-exhaustive > summary::before {
- font-size: 1rem;
-}
-
details.non-exhaustive {
margin-bottom: 8px;
}
@@ -1623,27 +1569,11 @@ details.rustdoc-toggle[open] > summary.hideme > span {
display: none;
}
-details.rustdoc-toggle[open] > summary::before,
-details.rustdoc-toggle[open] > summary.hideme::before {
- background-image: /* AUTOREPLACE: */url("toggle-minus.svg");
+details.rustdoc-toggle[open] > summary::before {
+ background: url("toggle-minus-31bbd6e4c77f5c96.svg") no-repeat top left;
}
-details.rustdoc-toggle > summary::before {
- background-image: /* AUTOREPLACE: */url("toggle-plus.svg");
-}
-
-details.rustdoc-toggle[open] > summary::before,
-details.rustdoc-toggle[open] > summary.hideme::before {
- width: 16px;
- height: 16px;
- background-repeat: no-repeat;
- background-position: top left;
- display: inline-block;
- content: "";
-}
-
-details.rustdoc-toggle[open] > summary::after,
-details.rustdoc-toggle[open] > summary.hideme::after {
+details.rustdoc-toggle[open] > summary::after {
content: "Collapse";
}
@@ -1677,7 +1607,6 @@ in storage.js
}
.rustdoc {
- padding-top: 0px;
/* Sidebar should overlay main content, rather than pushing main content to the right.
Turn off `display: flex` on the body element. */
display: block;
@@ -1702,10 +1631,6 @@ in storage.js
content: "Since ";
}
- #copy-path {
- display: none;
- }
-
/* Hide the logo and item name from the sidebar. Those are displayed
in the mobile-topbar instead. */
.sidebar .sidebar-logo,
@@ -1719,12 +1644,10 @@ in storage.js
/* Hide the sidebar offscreen while not in use. Doing this instead of display: none means
the sidebar stays visible for screen readers, which is useful for navigation. */
left: -1000px;
- margin-left: 0;
- margin: 0;
- padding: 0;
z-index: 11;
/* Reduce height slightly to account for mobile topbar. */
height: calc(100vh - 45px);
+ width: 200px;
}
/* The source view uses a different design for the sidebar toggle, and doesn't have a topbar,
@@ -1765,16 +1688,10 @@ in storage.js
white-space: nowrap;
}
- .mobile-topbar .logo-container {
- max-height: 45px;
- }
-
.mobile-topbar .logo-container > img {
max-width: 35px;
max-height: 35px;
- margin-left: 20px;
- margin-top: 5px;
- margin-bottom: 5px;
+ margin: 5px 0 5px 20px;
}
.mobile-topbar {
@@ -1800,7 +1717,6 @@ in storage.js
.sidebar-elems {
margin-top: 1em;
- background-color: var(--sidebar-background-color);
}
.content {
@@ -1815,21 +1731,6 @@ in storage.js
display: block;
}
- /* Because of ios, we need to actually have a full height sidebar title so the
- * actual sidebar can show up. But then we need to make it transparent so we don't
- * hide content. The filler just allows to create the background for the sidebar
- * title. But because of the absolute position, I had to lower the z-index.
- */
- #sidebar-filler {
- position: fixed;
- left: 45px;
- width: calc(100% - 45px);
- top: 0;
- height: 45px;
- z-index: -1;
- border-bottom: 1px solid;
- }
-
#main-content > details.rustdoc-toggle > summary::before,
#main-content > div > details.rustdoc-toggle > summary::before {
left: -11px;
@@ -1846,7 +1747,6 @@ in storage.js
z-index: 10;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
- cursor: pointer;
border: 1px solid;
border-left: 0;
}
@@ -1862,37 +1762,22 @@ in storage.js
border-bottom: 1px solid;
}
- .notable-traits .notable-traits-tooltiptext {
- left: 0;
- top: 100%;
- }
-
- /* We don't display the help button on mobile devices. */
- #help-button {
+ /* We don't display these buttons on mobile devices. */
+ #copy-path, #help-button {
display: none;
}
/* Display an alternating layout on tablets and phones */
- .item-table {
+ .item-table, .item-row, .item-left, .item-right,
+ .search-results > a, .search-results > a > div {
display: block;
}
- .item-row {
- display: flex;
- flex-flow: column wrap;
- }
- .item-left, .item-right {
- width: 100%;
- }
/* Display an alternating layout on tablets and phones */
.search-results > a {
- border-bottom: 1px solid #aaa9;
padding: 5px 0px;
}
- .search-results .result-name, .search-results div.desc {
- width: 100%;
- }
- .search-results div.desc, .item-right {
+ .search-results > a > div.desc, .item-right {
padding-left: 2em;
}
@@ -1922,6 +1807,22 @@ in storage.js
}
}
+/* Should have min-width: (N + 1)px where N is the mobile breakpoint above. */
+@media (min-width: 701px) {
+ /* Places file-link for a scraped example on top of the example to save space.
+ We only do this on large screens so the file-link doesn't overlap too much
+ with the example's content. */
+ .scraped-example-title {
+ position: absolute;
+ z-index: 10;
+ background: var(--main-background-color);
+ bottom: 8px;
+ right: 5px;
+ padding: 2px 4px;
+ box-shadow: 0 0 4px var(--main-background-color);
+ }
+}
+
@media print {
nav.sidebar, nav.sub, .out-of-band, a.srclink, #copy-path,
details.rustdoc-toggle[open] > summary::before, details.rustdoc-toggle > summary::before,
@@ -1969,24 +1870,28 @@ in storage.js
}
}
-.method-toggle > summary,
+.variant,
.implementors-toggle > summary,
.impl,
#implementors-list > .docblock,
.impl-items > section,
-.methods > section
+.impl-items > .rustdoc-toggle > summary,
+.methods > section,
+.methods > .rustdoc-toggle > summary
{
margin-bottom: 0.75em;
}
-.method-toggle[open]:not(:last-child),
+.variants > .docblock,
+.impl-items > .rustdoc-toggle[open]:not(:last-child),
+.methods > .rustdoc-toggle[open]:not(:last-child),
.implementors-toggle[open]:not(:last-child) {
margin-bottom: 2em;
}
-#trait-implementations-list .method-toggle:not(:last-child),
-#synthetic-implementations-list .method-toggle:not(:last-child),
-#blanket-implementations-list .method-toggle:not(:last-child) {
+#trait-implementations-list .impl-items > .rustdoc-toggle:not(:last-child),
+#synthetic-implementations-list .impl-items > .rustdoc-toggle:not(:last-child),
+#blanket-implementations-list .impl-items > .rustdoc-toggle:not(:last-child) {
margin-bottom: 1em;
}
@@ -1999,12 +1904,16 @@ in storage.js
font-size: 12px;
position: relative;
bottom: 1px;
- background: transparent;
border-width: 1px;
border-style: solid;
border-radius: 50px;
}
+.scraped-example {
+ /* So .scraped-example-title can be positioned absolutely */
+ position: relative;
+}
+
.scraped-example .code-wrapper {
position: relative;
display: flex;
@@ -2014,54 +1923,62 @@ in storage.js
}
.scraped-example:not(.expanded) .code-wrapper {
- max-height: 240px;
+ /* scrape-examples.js has a constant DEFAULT_MAX_LINES (call it N) for the number
+ * of lines shown in the un-expanded example code viewer. This pre needs to have
+ * a max-height equal to line-height * N. The line-height is currently 1.5em,
+ * and we include additional 10px for padding. */
+ max-height: calc(1.5em * 5 + 10px);
}
.scraped-example:not(.expanded) .code-wrapper pre {
overflow-y: hidden;
- max-height: 240px;
padding-bottom: 0;
+ /* See above comment, should be the same max-height. */
+ max-height: calc(1.5em * 5 + 10px);
}
-.scraped-example .code-wrapper .prev {
+.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper,
+.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper pre {
+ /* See above comment, except this height is based on HIDDEN_MAX_LINES. */
+ max-height: calc(1.5em * 10 + 10px);
+}
+
+.scraped-example .code-wrapper .next,
+.scraped-example .code-wrapper .prev,
+.scraped-example .code-wrapper .expand {
+ color: var(--main-color);
position: absolute;
top: 0.25em;
+ z-index: 1;
+ padding: 0;
+ background: none;
+ border: none;
+ /* iOS button gradient: https://stackoverflow.com/q/5438567 */
+ -webkit-appearance: none;
+ opacity: 1;
+}
+.scraped-example .code-wrapper .prev {
right: 2.25em;
- z-index: 100;
- cursor: pointer;
}
-
.scraped-example .code-wrapper .next {
- position: absolute;
- top: 0.25em;
right: 1.25em;
- z-index: 100;
- cursor: pointer;
}
-
.scraped-example .code-wrapper .expand {
- position: absolute;
- top: 0.25em;
right: 0.25em;
- z-index: 100;
- cursor: pointer;
}
-.scraped-example:not(.expanded) .code-wrapper:before {
+.scraped-example:not(.expanded) .code-wrapper:before,
+.scraped-example:not(.expanded) .code-wrapper:after {
content: " ";
width: 100%;
height: 5px;
position: absolute;
- z-index: 100;
+ z-index: 1;
+}
+.scraped-example:not(.expanded) .code-wrapper:before {
top: 0;
}
-
.scraped-example:not(.expanded) .code-wrapper:after {
- content: " ";
- width: 100%;
- height: 5px;
- position: absolute;
- z-index: 100;
bottom: 0;
}
@@ -2070,12 +1987,15 @@ in storage.js
padding: 14px 0;
}
+.scraped-example .code-wrapper .src-line-numbers a,
.scraped-example .code-wrapper .src-line-numbers span {
padding: 0 14px;
}
.scraped-example .code-wrapper .example-wrap {
- flex: 1;
+ display: grid;
+ grid-template-columns: max-content auto;
+ width: 100%;
overflow-x: auto;
overflow-y: hidden;
margin-bottom: 0;
diff --git a/src/librustdoc/html/static/css/settings.css b/src/librustdoc/html/static/css/settings.css
index 83939f63b..1f6fb961e 100644
--- a/src/librustdoc/html/static/css/settings.css
+++ b/src/librustdoc/html/static/css/settings.css
@@ -8,7 +8,8 @@
flex-wrap: wrap;
}
-.setting-line .radio-line input {
+.setting-line .radio-line input,
+.setting-line .toggle input {
margin-right: 0.3em;
height: 1.2rem;
width: 1.2rem;
@@ -17,9 +18,18 @@
outline: none;
-webkit-appearance: none;
cursor: pointer;
+}
+.setting-line .radio-line input {
border-radius: 50%;
}
-.setting-line .radio-line input + span {
+.setting-line .toggle input:checked {
+ content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\
+ <path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\
+ <path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>');
+}
+
+.setting-line .radio-line input + span,
+.setting-line .toggle span {
padding-bottom: 1px;
}
@@ -49,37 +59,6 @@
cursor: pointer;
}
-.toggle input {
- opacity: 0;
- position: absolute;
-}
-
-.slider {
- position: relative;
- width: 45px;
- min-width: 45px;
- display: block;
- height: 28px;
- margin-right: 20px;
- cursor: pointer;
- background-color: #ccc;
- transition: .3s;
-}
-
-.slider:before {
- position: absolute;
- content: "";
- height: 19px;
- width: 19px;
- left: 4px;
- bottom: 4px;
- transition: .3s;
-}
-
-input:checked + .slider:before {
- transform: translateX(19px);
-}
-
.setting-line > .sub-settings {
padding-left: 42px;
width: 100%;
@@ -94,7 +73,11 @@ input:checked + .slider:before {
box-shadow: inset 0 0 0 3px var(--main-background-color);
background-color: var(--settings-input-color);
}
-.setting-line .radio-line input:focus {
+.setting-line .toggle input:checked {
+ background-color: var(--settings-input-color);
+}
+.setting-line .radio-line input:focus,
+.setting-line .toggle input:focus {
box-shadow: 0 0 1px 1px var(--settings-input-color);
}
/* In here we combine both `:focus` and `:checked` properties. */
@@ -102,9 +85,7 @@ input:checked + .slider:before {
box-shadow: inset 0 0 0 3px var(--main-background-color),
0 0 2px 2px var(--settings-input-color);
}
-.setting-line .radio-line input:hover {
+.setting-line .radio-line input:hover,
+.setting-line .toggle input:hover {
border-color: var(--settings-input-color) !important;
}
-input:checked + .slider {
- background-color: var(--settings-input-color);
-}
diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css
index fdfdb3e19..0436fe013 100644
--- a/src/librustdoc/html/static/css/themes/ayu.css
+++ b/src/librustdoc/html/static/css/themes/ayu.css
@@ -7,6 +7,8 @@ Original by Dempfi (https://github.com/dempfi/ayu)
--main-background-color: #0f1419;
--main-color: #c5c5c5;
--settings-input-color: #ffb454;
+ --settings-button-color: #fff;
+ --settings-button-border-focus: #e0e0e0;
--sidebar-background-color: #14191f;
--sidebar-background-color-hover: rgba(70, 70, 70, 0.33);
--code-block-background-color: #191f26;
@@ -19,6 +21,7 @@ Original by Dempfi (https://github.com/dempfi/ayu)
--right-side-color: grey;
--code-attribute-color: #999;
--toggles-color: #999;
+ --toggle-filter: invert(100%);
--search-input-focused-border-color: #5c6773; /* Same as `--border-color`. */
--copy-path-button-color: #fff;
--copy-path-img-filter: invert(70%);
@@ -38,9 +41,12 @@ Original by Dempfi (https://github.com/dempfi/ayu)
--sidebar-link-color: #53b1db;
--sidebar-current-link-background-color: transparent;
--search-result-link-focus-background-color: #3c3c3c;
+ --search-result-border-color: #aaa3;
+ --search-color: #fff;
+ --search-results-alias-color: #c5c5c5;
+ --search-results-grey-color: #999;
--stab-background-color: #314559;
--stab-code-color: #e6e1cf;
- --search-color: #fff;
--code-highlight-kw-color: #ff7733;
--code-highlight-kw-2-color: #ff7733;
--code-highlight-lifetime-color: #ff7733;
@@ -58,16 +64,30 @@ Original by Dempfi (https://github.com/dempfi/ayu)
--example-line-numbers-border-color: none;
--src-line-numbers-span-color: #5c6773;
--src-line-number-highlighted-background-color: rgba(255, 236, 164, 0.06);
-}
-
-.slider {
- background-color: #ccc;
-}
-.slider:before {
- background-color: white;
-}
-input:focus + .slider {
- box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3);
+ --test-arrow-color: #788797;
+ --test-arrow-background-color: rgba(57, 175, 215, 0.09);
+ --test-arrow-hover-color: #c5c5c5;
+ --test-arrow-hover-background-color: rgba(57, 175, 215, 0.368);
+ --target-background-color: rgba(255, 236, 164, 0.06);
+ --target-border-color: rgba(255, 180, 76, 0.85);
+ --tooltip-background-color: #314559;
+ --tooltip-color: #c5c5c5;
+ --kbd-color: #c5c5c5;
+ --kbd-background: #314559;
+ --kbd-box-shadow-color: #5c6773;
+ --rust-logo-filter: drop-shadow(1px 0 0px #fff)
+ drop-shadow(0 1px 0 #fff)
+ drop-shadow(-1px 0 0 #fff)
+ drop-shadow(0 -1px 0 #fff);
+ /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+ --crate-search-div-filter: invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg)
+ brightness(94%) contrast(94%);
+ --crate-search-div-hover-filter: invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg)
+ brightness(113%) contrast(76%);
+ --crate-search-hover-border: #e0e0e0;
+ --source-sidebar-background-selected: #14191f;
+ --source-sidebar-background-hover: #14191f;
+ --table-alt-row-background-color: #191f26;
}
h1, h2, h3, h4 {
@@ -99,13 +119,6 @@ pre, .rustdoc.source .example-wrap {
color: #e6e1cf;
}
-.rust-logo {
- filter: drop-shadow(1px 0 0px #fff)
- drop-shadow(0 1px 0 #fff)
- drop-shadow(-1px 0 0 #fff)
- drop-shadow(0 -1px 0 #fff);
-}
-
.sidebar .current,
.sidebar a:hover {
color: #ffb44c;
@@ -147,21 +160,6 @@ body.source .example-wrap pre.rust a {
background: #333;
}
-details.rustdoc-toggle > summary::before {
- filter: invert(100%);
-}
-
-#crate-search-div::after {
- /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
- filter: invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg) brightness(94%) contrast(94%);
-}
-#crate-search:hover, #crate-search:focus {
- border-color: #e0e0e0 !important;
-}
-#crate-search-div:hover::after, #crate-search-div:focus-within::after {
- filter: invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg) brightness(113%) contrast(76%);
-}
-
.module-item .stab,
.import-item .stab {
color: #000;
@@ -171,40 +169,6 @@ details.rustdoc-toggle > summary::before {
color: #788797;
}
-a.test-arrow {
- font-size: 100%;
- color: #788797;
- border-radius: 4px;
- background-color: rgba(57, 175, 215, 0.09);
-}
-
-a.test-arrow:hover {
- background-color: rgba(57, 175, 215, 0.368);
- color: #c5c5c5;
-}
-
-:target {
- background: rgba(255, 236, 164, 0.06);
- border-right: 3px solid rgba(255, 180, 76, 0.85);
-}
-
-.search-failed a {
- color: #39AFD7;
-}
-
-.tooltip::after {
- background-color: #314559;
- color: #c5c5c5;
-}
-
-.tooltip::before {
- border-color: transparent #314559 transparent transparent;
-}
-
-.notable-traits-tooltiptext {
- background-color: #314559;
-}
-
#titles > button.selected {
background-color: #141920 !important;
border-bottom: 1px solid #ffb44c !important;
@@ -236,42 +200,16 @@ pre.rust .kw {}
pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val, pre.rust .attribute {}
pre.rust .kw-2, pre.rust .prelude-ty {}
-kbd {
- color: #c5c5c5;
- background-color: #314559;
- box-shadow: inset 0 -1px 0 #5c6773;
-}
-
-#settings-menu > a, #help-button > a {
- color: #fff;
-}
-
#settings-menu > a img {
filter: invert(100);
}
-#settings-menu > a:hover, #settings-menu > a:focus,
-#help-button > a:hover, #help-button > a:focus {
- border-color: #e0e0e0;
-}
-
-.search-results .result-name span.alias {
- color: #c5c5c5;
-}
-.search-results .result-name span.grey {
- color: #999;
-}
-
#source-sidebar > .title {
color: #fff;
}
#source-sidebar div.files > a:hover, details.dir-entry summary:hover,
-#source-sidebar div.files > a:focus, details.dir-entry summary:focus {
- background-color: #14191f;
- color: #ffb44c;
-}
+#source-sidebar div.files > a:focus, details.dir-entry summary:focus,
#source-sidebar div.files > a.selected {
- background-color: #14191f;
color: #ffb44c;
}
diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css
index 361d3d4a2..d945e956c 100644
--- a/src/librustdoc/html/static/css/themes/dark.css
+++ b/src/librustdoc/html/static/css/themes/dark.css
@@ -2,6 +2,8 @@
--main-background-color: #353535;
--main-color: #ddd;
--settings-input-color: #2196f3;
+ --settings-button-color: #000;
+ --settings-button-border-focus: #ffb900;
--sidebar-background-color: #505050;
--sidebar-background-color-hover: #676767;
--code-block-background-color: #2A2A2A;
@@ -14,6 +16,7 @@
--right-side-color: grey;
--code-attribute-color: #999;
--toggles-color: #999;
+ --toggle-filter: invert(100%);
--search-input-focused-border-color: #008dfd;
--copy-path-button-color: #999;
--copy-path-img-filter: invert(50%);
@@ -33,9 +36,12 @@
--sidebar-link-color: #fdbf35;
--sidebar-current-link-background-color: #444;
--search-result-link-focus-background-color: #616161;
+ --search-result-border-color: #aaa3;
+ --search-color: #111;
+ --search-results-alias-color: #fff;
+ --search-results-grey-color: #ccc;
--stab-background-color: #314559;
--stab-code-color: #e6e1cf;
- --search-color: #111;
--code-highlight-kw-color: #ab8ac1;
--code-highlight-kw-2-color: #769acb;
--code-highlight-lifetime-color: #d97f26;
@@ -53,23 +59,30 @@
--example-line-numbers-border-color: #4a4949;
--src-line-numbers-span-color: #3b91e2;
--src-line-number-highlighted-background-color: #0a042f;
-}
-
-.slider {
- background-color: #ccc;
-}
-.slider:before {
- background-color: white;
-}
-input:focus + .slider {
- box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3);
-}
-
-.rust-logo {
- filter: drop-shadow(1px 0 0px #fff)
+ --test-arrow-color: #dedede;
+ --test-arrow-background-color: rgba(78, 139, 202, 0.2);
+ --test-arrow-hover-color: #dedede;
+ --test-arrow-hover-background-color: #4e8bca;
+ --target-background-color: #494a3d;
+ --target-border-color: #bb7410;
+ --tooltip-background-color: #000;
+ --tooltip-color: #fff;
+ --kbd-color: #000;
+ --kbd-background: #fafbfc;
+ --kbd-box-shadow-color: #c6cbd1;
+ --rust-logo-filter: drop-shadow(1px 0 0px #fff)
drop-shadow(0 1px 0 #fff)
drop-shadow(-1px 0 0 #fff)
- drop-shadow(0 -1px 0 #fff)
+ drop-shadow(0 -1px 0 #fff);
+ /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+ --crate-search-div-filter: invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg)
+ brightness(90%) contrast(90%);
+ --crate-search-div-hover-filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg)
+ brightness(100%) contrast(91%);
+ --crate-search-hover-border: #2196f3;
+ --source-sidebar-background-selected: #333;
+ --source-sidebar-background-hover: #444;
+ --table-alt-row-background-color: #2A2A2A;
}
.content .item-info::before { color: #ccc; }
@@ -78,53 +91,6 @@ body.source .example-wrap pre.rust a {
background: #333;
}
-details.rustdoc-toggle > summary::before {
- filter: invert(100%);
-}
-
-#crate-search-div::after {
- /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
- filter: invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);
-}
-#crate-search:hover, #crate-search:focus {
- border-color: #2196f3 !important;
-}
-#crate-search-div:hover::after, #crate-search-div:focus-within::after {
- filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);
-}
-
-a.test-arrow {
- color: #dedede;
- background-color: rgba(78, 139, 202, 0.2);
-}
-
-a.test-arrow:hover{
- background-color: #4e8bca;
-}
-
-:target {
- background-color: #494a3d;
- border-right: 3px solid #bb7410;
-}
-
-.search-failed a {
- color: #0089ff;
-}
-
-.tooltip::after {
- background-color: #000;
- color: #fff;
- border-color: #000;
-}
-
-.tooltip::before {
- border-color: transparent black transparent transparent;
-}
-
-.notable-traits-tooltiptext {
- background-color: #111;
-}
-
#titles > button:not(.selected) {
background-color: #252525;
border-top-color: #252525;
@@ -139,36 +105,6 @@ a.test-arrow:hover{
color: #888;
}
-kbd {
- color: #000;
- background-color: #fafbfc;
- box-shadow: inset 0 -1px 0 #c6cbd1;
-}
-
-#settings-menu > a, #help-button > a {
- color: #000;
-}
-
-#settings-menu > a:hover, #settings-menu > a:focus,
-#help-button > a:hover, #help-button > a:focus {
- border-color: #ffb900;
-}
-
-.search-results .result-name span.alias {
- color: #fff;
-}
-.search-results .result-name span.grey {
- color: #ccc;
-}
-
-#source-sidebar div.files > a:hover, details.dir-entry summary:hover,
-#source-sidebar div.files > a:focus, details.dir-entry summary:focus {
- background-color: #444;
-}
-#source-sidebar div.files > a.selected {
- background-color: #333;
-}
-
.scraped-example-list .scrape-help {
border-color: #aaa;
color: #eee;
diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css
index 5eb4bbcf8..58955a793 100644
--- a/src/librustdoc/html/static/css/themes/light.css
+++ b/src/librustdoc/html/static/css/themes/light.css
@@ -2,6 +2,8 @@
--main-background-color: white;
--main-color: black;
--settings-input-color: #2196f3;
+ --settings-button-color: #000;
+ --settings-button-border-focus: #717171;
--sidebar-background-color: #F5F5F5;
--sidebar-background-color-hover: #E0E0E0;
--code-block-background-color: #F5F5F5;
@@ -14,6 +16,7 @@
--right-side-color: grey;
--code-attribute-color: #999;
--toggles-color: #999;
+ --toggle-filter: none;
--search-input-focused-border-color: #66afe9;
--copy-path-button-color: #999;
--copy-path-img-filter: invert(50%);
@@ -33,9 +36,12 @@
--sidebar-link-color: #356da4;
--sidebar-current-link-background-color: #fff;
--search-result-link-focus-background-color: #ccc;
+ --search-result-border-color: #aaa3;
+ --search-color: #000;
+ --search-results-alias-color: #000;
+ --search-results-grey-color: #999;
--stab-background-color: #fff5d6;
--stab-code-color: #000;
- --search-color: #000;
--code-highlight-kw-color: #8959a8;
--code-highlight-kw-2-color: #4271ae;
--code-highlight-lifetime-color: #b76514;
@@ -53,22 +59,27 @@
--example-line-numbers-border-color: #c7c7c7;
--src-line-numbers-span-color: #c67e2d;
--src-line-number-highlighted-background-color: #fdffd3;
-}
-
-.slider {
- background-color: #ccc;
-}
-.slider:before {
- background-color: white;
-}
-input:focus + .slider {
- box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3);
-}
-
-.rust-logo {
- /* This rule exists to force other themes to explicitly style the logo.
- * Rustdoc has a custom linter for this purpose.
- */
+ --test-arrow-color: #f5f5f5;
+ --test-arrow-background-color: rgba(78, 139, 202, 0.2);
+ --test-arrow-hover-color: #f5f5f5;
+ --test-arrow-hover-background-color: #4e8bca;
+ --target-background-color: #fdffd3;
+ --target-border-color: #ad7c37;
+ --tooltip-background-color: #000;
+ --tooltip-color: #fff;
+ --kbd-color: #000;
+ --kbd-background: #fafbfc;
+ --kbd-box-shadow-color: #c6cbd1;
+ --rust-logo-filter: initial;
+ /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+ --crate-search-div-filter: invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg)
+ brightness(114%) contrast(76%);
+ --crate-search-div-hover-filter: invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg)
+ brightness(96%) contrast(93%);
+ --crate-search-hover-border: #717171;
+ --source-sidebar-background-selected: #fff;
+ --source-sidebar-background-hover: #e0e0e0;
+ --table-alt-row-background-color: #F5F5F5;
}
.content .item-info::before { color: #ccc; }
@@ -77,48 +88,6 @@ body.source .example-wrap pre.rust a {
background: #eee;
}
-#crate-search-div::after {
- /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
- filter: invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);
-}
-#crate-search:hover, #crate-search:focus {
- border-color: #717171 !important;
-}
-#crate-search-div:hover::after, #crate-search-div:focus-within::after {
- filter: invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);
-}
-
-a.test-arrow {
- color: #f5f5f5;
- background-color: rgba(78, 139, 202, 0.2);
-}
-
-a.test-arrow:hover{
- background-color: #4e8bca;
-}
-
-:target {
- background: #FDFFD3;
- border-right: 3px solid #AD7C37;
-}
-
-.search-failed a {
- color: #3873AD;
-}
-
-.tooltip::after {
- background-color: #000;
- color: #fff;
-}
-
-.tooltip::before {
- border-color: transparent black transparent transparent;
-}
-
-.notable-traits-tooltiptext {
- background-color: #eee;
-}
-
#titles > button:not(.selected) {
background-color: #e6e6e6;
border-top-color: #e6e6e6;
@@ -133,35 +102,6 @@ a.test-arrow:hover{
color: #888;
}
-kbd {
- color: #000;
- background-color: #fafbfc;
- box-shadow: inset 0 -1px 0 #c6cbd1;
-}
-
-#settings-menu > a, #help-button > a {
- color: #000;
-}
-
-#settings-menu > a:hover, #settings-menu > a:focus,
-#help-button > a:hover, #help-button > a:focus {
- border-color: #717171;
-}
-
-.search-results .result-name span.alias {
- color: #000;
-}
-.search-results .result-name span.grey {
- color: #999;
-}
-
-#source-sidebar div.files > a:hover, details.dir-entry summary:hover,
-#source-sidebar div.files > a:focus, details.dir-entry summary:focus {
- background-color: #E0E0E0;
-}
-#source-sidebar div.files > a.selected {
- background-color: #fff;
-}
.scraped-example-list .scrape-help {
border-color: #555;
color: #333;
diff --git a/src/librustdoc/html/static/fonts/README.txt b/src/librustdoc/html/static/fonts/README.txt
new file mode 100644
index 000000000..0db15996d
--- /dev/null
+++ b/src/librustdoc/html/static/fonts/README.txt
@@ -0,0 +1,12 @@
+The Nanum Barun Gothic fonts are shipped with rustdoc because the default fonts
+on many Windows installs render Korean very badly. See:
+ - https://github.com/rust-lang/rust/pull/84048,
+ - https://github.com/rust-lang/rust/issues/84035
+ - https://github.com/rust-lang/rust/pull/90232
+
+The font files were generated with these commands:
+
+```sh
+pyftsubset NanumBarunGothic.ttf \
+--unicodes=U+AC00-D7AF:U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF \
+--output-file=NanumBarunGothic.ttf.woff2 --flavor=woff2
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 33480fa41..152116089 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -47,10 +47,8 @@ function blurHandler(event, parentElem, hideCallback) {
}
}
-(function() {
- window.rootPath = getVar("root-path");
- window.currentCrate = getVar("current-crate");
-}());
+window.rootPath = getVar("root-path");
+window.currentCrate = getVar("current-crate");
function setMobileTopbar() {
// FIXME: It would be nicer to generate this text content directly in HTML,
@@ -183,9 +181,9 @@ function browserSupportsHistoryApi() {
}
// eslint-disable-next-line no-unused-vars
-function loadCss(cssFileName) {
+function loadCss(cssUrl) {
const link = document.createElement("link");
- link.href = resourcePath(cssFileName, ".css");
+ link.href = cssUrl;
link.type = "text/css";
link.rel = "stylesheet";
document.getElementsByTagName("head")[0].appendChild(link);
@@ -204,12 +202,13 @@ function loadCss(cssFileName) {
if (event.ctrlKey || event.altKey || event.metaKey) {
return;
}
+ window.hideAllModals(false);
addClass(getSettingsButton(), "rotate");
event.preventDefault();
// Sending request for the CSS and the JS files at the same time so it will
// hopefully be loaded when the JS will generate the settings content.
- loadCss("settings");
- loadScript(resourcePath("settings", ".js"));
+ loadCss(getVar("static-root-path") + getVar("settings-css"));
+ loadScript(getVar("static-root-path") + getVar("settings-js"));
};
window.searchState = {
@@ -286,7 +285,7 @@ function loadCss(cssFileName) {
function loadSearch() {
if (!searchLoaded) {
searchLoaded = true;
- loadScript(resourcePath("search", ".js"));
+ loadScript(getVar("static-root-path") + getVar("search-js"));
loadScript(resourcePath("search-index", ".js"));
}
}
@@ -303,13 +302,15 @@ function loadCss(cssFileName) {
const params = searchState.getQueryStringParams();
if (params.search !== undefined) {
- const search = searchState.outputElement();
- search.innerHTML = "<h3 class=\"search-loading\">" +
- searchState.loadingText + "</h3>";
- searchState.showResults(search);
+ searchState.setLoadingSearch();
loadSearch();
}
},
+ setLoadingSearch: () => {
+ const search = searchState.outputElement();
+ search.innerHTML = "<h3 class=\"search-loading\">" + searchState.loadingText + "</h3>";
+ searchState.showResults(search);
+ },
};
function getPageId() {
@@ -379,7 +380,7 @@ function loadCss(cssFileName) {
}
ev.preventDefault();
searchState.defocus();
- window.hidePopoverMenus();
+ window.hideAllModals(true); // true = reset focus for notable traits
}
function handleShortcut(ev) {
@@ -621,7 +622,7 @@ function loadCss(cssFileName) {
const innerToggle = document.getElementById(toggleAllDocsId);
removeClass(innerToggle, "will-expand");
onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => {
- if (!hasClass(e, "type-contents-toggle")) {
+ if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) {
e.open = true;
}
});
@@ -725,12 +726,9 @@ function loadCss(cssFileName) {
});
};
- (function() {
- // To avoid checking on "rustdoc-line-numbers" value on every loop...
- if (getSettingValue("line-numbers") === "true") {
- window.rustdoc_add_line_numbers_to_examples();
- }
- }());
+ if (getSettingValue("line-numbers") === "true") {
+ window.rustdoc_add_line_numbers_to_examples();
+ }
let oldSidebarScrollPosition = null;
@@ -772,6 +770,7 @@ function loadCss(cssFileName) {
};
function showSidebar() {
+ window.hideAllModals(false);
window.rustdocMobileScrollLock();
const sidebar = document.getElementsByClassName("sidebar")[0];
addClass(sidebar, "shown");
@@ -790,6 +789,19 @@ function loadCss(cssFileName) {
// we need to switch away from mobile mode and make the main content area scrollable.
hideSidebar();
}
+ if (window.CURRENT_NOTABLE_ELEMENT) {
+ // As a workaround to the behavior of `contains: layout` used in doc togglers, the
+ // notable traits popup is positioned using javascript.
+ //
+ // This means when the window is resized, we need to redo the layout.
+ const base = window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE;
+ const force_visible = base.NOTABLE_FORCE_VISIBLE;
+ hideNotable(false);
+ if (force_visible) {
+ showNotable(base);
+ base.NOTABLE_FORCE_VISIBLE = true;
+ }
+ }
});
function handleClick(id, f) {
@@ -822,10 +834,123 @@ function loadCss(cssFileName) {
});
});
+ function showNotable(e) {
+ if (!window.NOTABLE_TRAITS) {
+ const data = document.getElementById("notable-traits-data");
+ if (data) {
+ window.NOTABLE_TRAITS = JSON.parse(data.innerText);
+ } else {
+ throw new Error("showNotable() called on page without any notable traits!");
+ }
+ }
+ if (window.CURRENT_NOTABLE_ELEMENT && window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE === e) {
+ // Make this function idempotent.
+ return;
+ }
+ window.hideAllModals(false);
+ const ty = e.getAttribute("data-ty");
+ const wrapper = document.createElement("div");
+ wrapper.innerHTML = "<div class=\"docblock\">" + window.NOTABLE_TRAITS[ty] + "</div>";
+ wrapper.className = "notable popover";
+ const focusCatcher = document.createElement("div");
+ focusCatcher.setAttribute("tabindex", "0");
+ focusCatcher.onfocus = hideNotable;
+ wrapper.appendChild(focusCatcher);
+ const pos = e.getBoundingClientRect();
+ // 5px overlap so that the mouse can easily travel from place to place
+ wrapper.style.top = (pos.top + window.scrollY + pos.height) + "px";
+ wrapper.style.left = 0;
+ wrapper.style.right = "auto";
+ wrapper.style.visibility = "hidden";
+ const body = document.getElementsByTagName("body")[0];
+ body.appendChild(wrapper);
+ const wrapperPos = wrapper.getBoundingClientRect();
+ // offset so that the arrow points at the center of the "(i)"
+ const finalPos = pos.left + window.scrollX - wrapperPos.width + 24;
+ if (finalPos > 0) {
+ wrapper.style.left = finalPos + "px";
+ } else {
+ wrapper.style.setProperty(
+ "--popover-arrow-offset",
+ (wrapperPos.right - pos.right + 4) + "px"
+ );
+ }
+ wrapper.style.visibility = "";
+ window.CURRENT_NOTABLE_ELEMENT = wrapper;
+ window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE = e;
+ wrapper.onpointerleave = function(ev) {
+ // If this is a synthetic touch event, ignore it. A click event will be along shortly.
+ if (ev.pointerType !== "mouse") {
+ return;
+ }
+ if (!e.NOTABLE_FORCE_VISIBLE && !elemIsInParent(event.relatedTarget, e)) {
+ hideNotable(true);
+ }
+ };
+ }
+
+ function notableBlurHandler(event) {
+ if (window.CURRENT_NOTABLE_ELEMENT &&
+ !elemIsInParent(document.activeElement, window.CURRENT_NOTABLE_ELEMENT) &&
+ !elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT) &&
+ !elemIsInParent(document.activeElement, window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE) &&
+ !elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE)
+ ) {
+ // Work around a difference in the focus behaviour between Firefox, Chrome, and Safari.
+ // When I click the button on an already-opened notable trait popover, Safari
+ // hides the popover and then immediately shows it again, while everyone else hides it
+ // and it stays hidden.
+ //
+ // To work around this, make sure the click finishes being dispatched before
+ // hiding the popover. Since `hideNotable()` is idempotent, this makes Safari behave
+ // consistently with the other two.
+ setTimeout(() => hideNotable(false), 0);
+ }
+ }
+
+ function hideNotable(focus) {
+ if (window.CURRENT_NOTABLE_ELEMENT) {
+ if (window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.NOTABLE_FORCE_VISIBLE) {
+ if (focus) {
+ window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.focus();
+ }
+ window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.NOTABLE_FORCE_VISIBLE = false;
+ }
+ const body = document.getElementsByTagName("body")[0];
+ body.removeChild(window.CURRENT_NOTABLE_ELEMENT);
+ window.CURRENT_NOTABLE_ELEMENT = null;
+ }
+ }
+
onEachLazy(document.getElementsByClassName("notable-traits"), e => {
e.onclick = function() {
- this.getElementsByClassName("notable-traits-tooltiptext")[0]
- .classList.toggle("force-tooltip");
+ this.NOTABLE_FORCE_VISIBLE = this.NOTABLE_FORCE_VISIBLE ? false : true;
+ if (window.CURRENT_NOTABLE_ELEMENT && !this.NOTABLE_FORCE_VISIBLE) {
+ hideNotable(true);
+ } else {
+ showNotable(this);
+ window.CURRENT_NOTABLE_ELEMENT.setAttribute("tabindex", "0");
+ window.CURRENT_NOTABLE_ELEMENT.focus();
+ window.CURRENT_NOTABLE_ELEMENT.onblur = notableBlurHandler;
+ }
+ return false;
+ };
+ e.onpointerenter = function(ev) {
+ // If this is a synthetic touch event, ignore it. A click event will be along shortly.
+ if (ev.pointerType !== "mouse") {
+ return;
+ }
+ showNotable(this);
+ };
+ e.onpointerleave = function(ev) {
+ // If this is a synthetic touch event, ignore it. A click event will be along shortly.
+ if (ev.pointerType !== "mouse") {
+ return;
+ }
+ if (!this.NOTABLE_FORCE_VISIBLE &&
+ !elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT)) {
+ hideNotable(true);
+ }
};
});
@@ -929,6 +1054,17 @@ function loadCss(cssFileName) {
}
/**
+ * Hide popover menus, notable trait tooltips, and the sidebar (if applicable).
+ *
+ * Pass "true" to reset focus for notable traits.
+ */
+ window.hideAllModals = function(switchFocus) {
+ hideSidebar();
+ window.hidePopoverMenus();
+ hideNotable(switchFocus);
+ };
+
+ /**
* Hide all the popover menus.
*/
window.hidePopoverMenus = function() {
@@ -959,7 +1095,7 @@ function loadCss(cssFileName) {
function showHelp() {
const menu = getHelpMenu(true);
if (menu.style.display === "none") {
- window.hidePopoverMenus();
+ window.hideAllModals();
menu.style.display = "";
}
}
diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js
index d0fd115fd..7a3a9c5f3 100644
--- a/src/librustdoc/html/static/js/scrape-examples.js
+++ b/src/librustdoc/html/static/js/scrape-examples.js
@@ -3,25 +3,33 @@
"use strict";
(function() {
- // Number of lines shown when code viewer is not expanded
- const MAX_LINES = 10;
+ // Number of lines shown when code viewer is not expanded.
+ // DEFAULT is the first example shown by default, while HIDDEN is
+ // the examples hidden beneath the "More examples" toggle.
+ //
+ // NOTE: these values MUST be synchronized with certain rules in rustdoc.css!
+ const DEFAULT_MAX_LINES = 5;
+ const HIDDEN_MAX_LINES = 10;
// Scroll code block to the given code location
- function scrollToLoc(elt, loc) {
+ function scrollToLoc(elt, loc, isHidden) {
const lines = elt.querySelector(".src-line-numbers");
let scrollOffset;
// If the block is greater than the size of the viewer,
// then scroll to the top of the block. Otherwise scroll
// to the middle of the block.
- if (loc[1] - loc[0] > MAX_LINES) {
+ const maxLines = isHidden ? HIDDEN_MAX_LINES : DEFAULT_MAX_LINES;
+ if (loc[1] - loc[0] > maxLines) {
const line = Math.max(0, loc[0] - 1);
scrollOffset = lines.children[line].offsetTop;
} else {
const wrapper = elt.querySelector(".code-wrapper");
const halfHeight = wrapper.offsetHeight / 2;
- const offsetMid = (lines.children[loc[0]].offsetTop
- + lines.children[loc[1]].offsetTop) / 2;
+ const offsetTop = lines.children[loc[0]].offsetTop;
+ const lastLine = lines.children[loc[1]];
+ const offsetBot = lastLine.offsetTop + lastLine.offsetHeight;
+ const offsetMid = (offsetTop + offsetBot) / 2;
scrollOffset = offsetMid - halfHeight;
}
@@ -29,7 +37,7 @@
elt.querySelector(".rust").scrollTo(0, scrollOffset);
}
- function updateScrapedExample(example) {
+ function updateScrapedExample(example, isHidden) {
const locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent);
let locIndex = 0;
const highlights = Array.prototype.slice.call(example.querySelectorAll(".highlight"));
@@ -40,7 +48,7 @@
const onChangeLoc = changeIndex => {
removeClass(highlights[locIndex], "focus");
changeIndex();
- scrollToLoc(example, locs[locIndex][0]);
+ scrollToLoc(example, locs[locIndex][0], isHidden);
addClass(highlights[locIndex], "focus");
const url = locs[locIndex][1];
@@ -57,7 +65,7 @@
});
});
- example.querySelector("next")
+ example.querySelector(".next")
.addEventListener("click", () => {
onChangeLoc(() => {
locIndex = (locIndex + 1) % locs.length;
@@ -70,7 +78,7 @@
expandButton.addEventListener("click", () => {
if (hasClass(example, "expanded")) {
removeClass(example, "expanded");
- scrollToLoc(example, locs[0][0]);
+ scrollToLoc(example, locs[0][0], isHidden);
} else {
addClass(example, "expanded");
}
@@ -78,11 +86,11 @@
}
// Start with the first example in view
- scrollToLoc(example, locs[0][0]);
+ scrollToLoc(example, locs[0][0], isHidden);
}
const firstExamples = document.querySelectorAll(".scraped-example-list > .scraped-example");
- onEachLazy(firstExamples, updateScrapedExample);
+ onEachLazy(firstExamples, el => updateScrapedExample(el, false));
onEachLazy(document.querySelectorAll(".more-examples-toggle"), toggle => {
// Allow users to click the left border of the <details> section to close it,
// since the section can be large and finding the [+] button is annoying.
@@ -99,7 +107,7 @@
// depends on offsetHeight, a property that requires an element to be visible to
// compute correctly.
setTimeout(() => {
- onEachLazy(moreExamples, updateScrapedExample);
+ onEachLazy(moreExamples, el => updateScrapedExample(el, true));
});
}, {once: true});
});
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index d04ec357c..23ae4e970 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1491,6 +1491,7 @@ function initSearch(rawSearchIndex) {
const target = searchState.focusedByTab[searchState.currentTab] ||
document.querySelectorAll(".search-results.active a").item(0) ||
document.querySelectorAll("#titles > button").item(searchState.currentTab);
+ searchState.focusedByTab[searchState.currentTab] = null;
if (target) {
target.focus();
}
@@ -1593,7 +1594,6 @@ function initSearch(rawSearchIndex) {
link.className = "result-" + type;
link.href = item.href;
- const wrapper = document.createElement("div");
const resultName = document.createElement("div");
resultName.className = "result-name";
@@ -1614,16 +1614,13 @@ function initSearch(rawSearchIndex) {
resultName.insertAdjacentHTML(
"beforeend",
item.displayPath + "<span class=\"" + type + "\">" + name + extra + "</span>");
- wrapper.appendChild(resultName);
+ link.appendChild(resultName);
const description = document.createElement("div");
description.className = "desc";
- const spanDesc = document.createElement("span");
- spanDesc.insertAdjacentHTML("beforeend", item.desc);
+ description.insertAdjacentHTML("beforeend", item.desc);
- description.appendChild(spanDesc);
- wrapper.appendChild(description);
- link.appendChild(wrapper);
+ link.appendChild(description);
output.appendChild(link);
});
} else if (query.error === null) {
@@ -1769,13 +1766,13 @@ function initSearch(rawSearchIndex) {
* @param {boolean} [forced]
*/
function search(e, forced) {
- const params = searchState.getQueryStringParams();
- const query = parseQuery(searchState.input.value.trim());
-
if (e) {
e.preventDefault();
}
+ const query = parseQuery(searchState.input.value.trim());
+ let filterCrates = getFilterCrates();
+
if (!forced && query.userQuery === currentResults) {
if (query.userQuery.length > 0) {
putBackSearch();
@@ -1783,7 +1780,9 @@ function initSearch(rawSearchIndex) {
return;
}
- let filterCrates = getFilterCrates();
+ searchState.setLoadingSearch();
+
+ const params = searchState.getQueryStringParams();
// In case we have no information about the saved crate and there is a URL query parameter,
// we override it with the URL query parameter.
diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js
index 5e1c7e6f0..589bfc793 100644
--- a/src/librustdoc/html/static/js/settings.js
+++ b/src/librustdoc/html/static/js/settings.js
@@ -9,13 +9,16 @@
const isSettingsPage = window.location.pathname.endsWith("/settings.html");
function changeSetting(settingName, value) {
+ if (settingName === "theme") {
+ const useSystem = value === "system preference" ? "true" : "false";
+ updateLocalStorage("use-system-theme", useSystem);
+ }
updateLocalStorage(settingName, value);
switch (settingName) {
case "theme":
case "preferred-dark-theme":
case "preferred-light-theme":
- case "use-system-theme":
updateSystemTheme();
updateLightAndDark();
break;
@@ -45,7 +48,6 @@
}
function showLightAndDark() {
- addClass(document.getElementById("theme").parentElement, "hidden");
removeClass(document.getElementById("preferred-light-theme").parentElement, "hidden");
removeClass(document.getElementById("preferred-dark-theme").parentElement, "hidden");
}
@@ -53,11 +55,11 @@
function hideLightAndDark() {
addClass(document.getElementById("preferred-light-theme").parentElement, "hidden");
addClass(document.getElementById("preferred-dark-theme").parentElement, "hidden");
- removeClass(document.getElementById("theme").parentElement, "hidden");
}
function updateLightAndDark() {
- if (getSettingValue("use-system-theme") !== "false") {
+ const useSystem = getSettingValue("use-system-theme");
+ if (useSystem === "true" || (useSystem === null && getSettingValue("theme") === null)) {
showLightAndDark();
} else {
hideLightAndDark();
@@ -66,8 +68,7 @@
function setEvents(settingsElement) {
updateLightAndDark();
- onEachLazy(settingsElement.getElementsByClassName("slider"), elem => {
- const toggle = elem.previousElementSibling;
+ onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"), toggle => {
const settingId = toggle.id;
const settingValue = getSettingValue(settingId);
if (settingValue !== null) {
@@ -92,7 +93,18 @@
});
onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => {
const settingId = elem.name;
- const settingValue = getSettingValue(settingId);
+ let settingValue = getSettingValue(settingId);
+ if (settingId === "theme") {
+ const useSystem = getSettingValue("use-system-theme");
+ if (useSystem === "true" || settingValue === null) {
+ if (useSystem !== "false") {
+ settingValue = "system preference";
+ } else {
+ // This is the default theme.
+ settingValue = "light";
+ }
+ }
+ }
if (settingValue !== null && settingValue !== "null") {
elem.checked = settingValue === elem.value;
}
@@ -121,27 +133,30 @@
if (setting["options"] !== undefined) {
// This is a select setting.
- output += `<div class="radio-line" id="${js_data_name}">\
- <span class="setting-name">${setting_name}</span>\
- <div class="choices">`;
+ output += `\
+<div class="radio-line" id="${js_data_name}">
+ <span class="setting-name">${setting_name}</span>
+<div class="choices">`;
onEach(setting["options"], option => {
const checked = option === setting["default"] ? " checked" : "";
+ const full = `${js_data_name}-${option.replace(/ /g,"-")}`;
- output += `<label for="${js_data_name}-${option}" class="choice">\
- <input type="radio" name="${js_data_name}" \
- id="${js_data_name}-${option}" value="${option}"${checked}>\
- <span>${option}</span>\
- </label>`;
+ output += `\
+<label for="${full}" class="choice">
+ <input type="radio" name="${js_data_name}"
+ id="${full}" value="${option}"${checked}>
+ <span>${option}</span>
+</label>`;
});
output += "</div></div>";
} else {
// This is a toggle.
const checked = setting["default"] === true ? " checked" : "";
- output += `<label class="toggle">\
- <input type="checkbox" id="${js_data_name}"${checked}>\
- <span class="slider"></span>\
- <span class="label">${setting_name}</span>\
- </label>`;
+ output += `\
+<label class="toggle">\
+ <input type="checkbox" id="${js_data_name}"${checked}>\
+ <span class="label">${setting_name}</span>\
+</label>`;
}
output += "</div>";
}
@@ -154,30 +169,27 @@
* @return {HTMLElement}
*/
function buildSettingsPage() {
- const themes = getVar("themes").split(",");
+ const theme_names = getVar("themes").split(",").filter(t => t);
+ theme_names.push("light", "dark", "ayu");
+
const settings = [
{
- "name": "Use system theme",
- "js_name": "use-system-theme",
- "default": true,
- },
- {
"name": "Theme",
"js_name": "theme",
- "default": "light",
- "options": themes,
+ "default": "system preference",
+ "options": theme_names.concat("system preference"),
},
{
"name": "Preferred light theme",
"js_name": "preferred-light-theme",
"default": "light",
- "options": themes,
+ "options": theme_names,
},
{
"name": "Preferred dark theme",
"js_name": "preferred-dark-theme",
"default": "dark",
- "options": themes,
+ "options": theme_names,
},
{
"name": "Auto-hide item contents for large items",
@@ -256,7 +268,7 @@
event.preventDefault();
const shouldDisplaySettings = settingsMenu.style.display === "none";
- window.hidePopoverMenus();
+ window.hideAllModals();
if (shouldDisplaySettings) {
displaySettings();
}
diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js
index 0b9368dd8..5db768c1c 100644
--- a/src/librustdoc/html/static/js/source-script.js
+++ b/src/librustdoc/html/static/js/source-script.js
@@ -157,7 +157,7 @@ function highlightSourceLines(match) {
x.scrollIntoView();
}
onEachLazy(document.getElementsByClassName("src-line-numbers"), e => {
- onEachLazy(e.getElementsByTagName("span"), i_e => {
+ onEachLazy(e.getElementsByTagName("a"), i_e => {
removeClass(i_e, "line-highlighted");
});
});
@@ -188,8 +188,13 @@ const handleSourceHighlight = (function() {
return ev => {
let cur_line_id = parseInt(ev.target.id, 10);
- // It can happen when clicking not on a line number span.
- if (isNaN(cur_line_id)) {
+ // This event handler is attached to the entire line number column, but it should only
+ // be run if one of the anchors is clicked. It also shouldn't do anything if the anchor
+ // is clicked with a modifier key (to open a new browser tab).
+ if (isNaN(cur_line_id) ||
+ ev.ctrlKey ||
+ ev.altKey ||
+ ev.metaKey) {
return;
}
ev.preventDefault();
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index b462a2c50..db2db83ca 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -126,33 +126,29 @@ function getCurrentValue(name) {
}
}
-function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) {
- const newHref = mainStyleElem.href.replace(
- /\/rustdoc([^/]*)\.css/, "/" + newTheme + "$1" + ".css");
-
+function switchTheme(styleElem, mainStyleElem, newThemeName, saveTheme) {
// If this new value comes from a system setting or from the previously
// saved theme, no need to save it.
if (saveTheme) {
- updateLocalStorage("theme", newTheme);
- }
-
- if (styleElem.href === newHref) {
- return;
+ updateLocalStorage("theme", newThemeName);
}
- let found = false;
if (savedHref.length === 0) {
onEachLazy(document.getElementsByTagName("link"), el => {
savedHref.push(el.href);
});
}
- onEach(savedHref, el => {
- if (el === newHref) {
- found = true;
+ const newHref = savedHref.find(url => {
+ const m = url.match(/static\.files\/(.*)-[a-f0-9]{16}\.css$/);
+ if (m && m[1] === newThemeName) {
+ return true;
+ }
+ const m2 = url.match(/\/([^/]*)\.css$/);
+ if (m2 && m2[1].startsWith(newThemeName)) {
return true;
}
});
- if (found) {
+ if (newHref && newHref !== styleElem.href) {
styleElem.href = newHref;
}
}
diff --git a/src/librustdoc/html/static/scrape-examples-help.md b/src/librustdoc/html/static/scrape-examples-help.md
index 035b2e18b..002d19ec9 100644
--- a/src/librustdoc/html/static/scrape-examples-help.md
+++ b/src/librustdoc/html/static/scrape-examples-help.md
@@ -1,4 +1,4 @@
-Rustdoc will automatically scrape examples of documented items from the `examples/` directory of a project. These examples will be included within the generated documentation for that item. For example, if your library contains a public function:
+Rustdoc will automatically scrape examples of documented items from a project's source code. These examples will be included within the generated documentation for that item. For example, if your library contains a public function:
```rust
// src/lib.rs
@@ -16,6 +16,7 @@ fn main() {
Then this code snippet will be included in the documentation for `a_func`.
+
## How to read scraped examples
Scraped examples are shown as blocks of code from a given file. The relevant item will be highlighted. If the file is larger than a couple lines, only a small window will be shown which you can expand by clicking &varr; in the top-right. If a file contains multiple instances of an item, you can use the &pr; and &sc; buttons to toggle through each instance.
@@ -25,7 +26,7 @@ If there is more than one file that contains examples, then you should click "Mo
## How Rustdoc scrapes examples
-When you run `cargo doc`, Rustdoc will analyze all the crates that match Cargo's `--examples` filter for instances of items that occur in the crates being documented. Then Rustdoc will include the source code of these instances in the generated documentation.
+When you run `cargo doc -Zunstable-options -Zrustdoc-scrape-examples`, Rustdoc will analyze all the documented crates for uses of documented items. Then Rustdoc will include the source code of these instances in the generated documentation.
Rustdoc has a few techniques to ensure this doesn't overwhelm documentation readers, and that it doesn't blow up the page size:
diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs
index 75f2b7e35..b48b82307 100644
--- a/src/librustdoc/html/static_files.rs
+++ b/src/librustdoc/html/static_files.rs
@@ -2,167 +2,132 @@
//!
//! All the static files are included here for centralized access in case anything other than the
//! HTML rendering code (say, the theme checker) needs to access one of these files.
-//!
-//! Note about types: CSS and JavaScript files are included as `&'static str` to allow for the
-//! minifier to run on them. All other files are included as `&'static [u8]` so they can be
-//! directly written to a `Write` handle.
-
-/// The file contents of the main `rustdoc.css` file, responsible for the core layout of the page.
-pub(crate) static RUSTDOC_CSS: &str = include_str!("static/css/rustdoc.css");
-
-/// The file contents of `settings.css`, responsible for the items on the settings page.
-pub(crate) static SETTINGS_CSS: &str = include_str!("static/css/settings.css");
-
-/// The file contents of the `noscript.css` file, used in case JS isn't supported or is disabled.
-pub(crate) static NOSCRIPT_CSS: &str = include_str!("static/css/noscript.css");
-
-/// The file contents of `normalize.css`, included to even out standard elements between browser
-/// implementations.
-pub(crate) static NORMALIZE_CSS: &str = include_str!("static/css/normalize.css");
-
-/// The file contents of `main.js`, which contains the core JavaScript used on documentation pages,
-/// including search behavior and docblock folding, among others.
-pub(crate) static MAIN_JS: &str = include_str!("static/js/main.js");
-
-/// The file contents of `search.js`, which contains the search behavior.
-pub(crate) static SEARCH_JS: &str = include_str!("static/js/search.js");
-
-/// The file contents of `settings.js`, which contains the JavaScript used to handle the settings
-/// page.
-pub(crate) static SETTINGS_JS: &str = include_str!("static/js/settings.js");
-
-/// The file contents of `storage.js`, which contains functionality related to browser Local
-/// Storage, used to store documentation settings.
-pub(crate) static STORAGE_JS: &str = include_str!("static/js/storage.js");
-
-/// The file contents of `scraped-examples.js`, which contains functionality related to the
-/// --scrape-examples flag that inserts automatically-found examples of usages of items.
-pub(crate) static SCRAPE_EXAMPLES_JS: &str = include_str!("static/js/scrape-examples.js");
-
-pub(crate) static SCRAPE_EXAMPLES_HELP_MD: &str = include_str!("static/scrape-examples-help.md");
-
-/// The file contents of `wheel.svg`, the icon used for the settings button.
-pub(crate) static WHEEL_SVG: &[u8] = include_bytes!("static/images/wheel.svg");
-
-/// The file contents of `clipboard.svg`, the icon used for the "copy path" button.
-pub(crate) static CLIPBOARD_SVG: &[u8] = include_bytes!("static/images/clipboard.svg");
-
-/// The file contents of `down-arrow.svg`, the icon used for the crate choice combobox.
-pub(crate) static DOWN_ARROW_SVG: &[u8] = include_bytes!("static/images/down-arrow.svg");
-
-/// The file contents of `toggle-minus.svg`, the icon used for opened toggles.
-pub(crate) static TOGGLE_MINUS_PNG: &[u8] = include_bytes!("static/images/toggle-minus.svg");
-
-/// The file contents of `toggle-plus.svg`, the icon used for closed toggles.
-pub(crate) static TOGGLE_PLUS_PNG: &[u8] = include_bytes!("static/images/toggle-plus.svg");
-/// The contents of `COPYRIGHT.txt`, the license listing for files distributed with documentation
-/// output.
-pub(crate) static COPYRIGHT: &[u8] = include_bytes!("static/COPYRIGHT.txt");
+use rustc_data_structures::fx::FxHasher;
+use std::hash::Hasher;
+use std::path::{Path, PathBuf};
+use std::{fmt, str};
-/// The contents of `LICENSE-APACHE.txt`, the text of the Apache License, version 2.0.
-pub(crate) static LICENSE_APACHE: &[u8] = include_bytes!("static/LICENSE-APACHE.txt");
-
-/// The contents of `LICENSE-MIT.txt`, the text of the MIT License.
-pub(crate) static LICENSE_MIT: &[u8] = include_bytes!("static/LICENSE-MIT.txt");
-
-/// The contents of `rust-logo.svg`, the default icon of the documentation.
-pub(crate) static RUST_LOGO_SVG: &[u8] = include_bytes!("static/images/rust-logo.svg");
-
-/// The default documentation favicons (SVG and PNG fallbacks)
-pub(crate) static RUST_FAVICON_SVG: &[u8] = include_bytes!("static/images/favicon.svg");
-pub(crate) static RUST_FAVICON_PNG_16: &[u8] = include_bytes!("static/images/favicon-16x16.png");
-pub(crate) static RUST_FAVICON_PNG_32: &[u8] = include_bytes!("static/images/favicon-32x32.png");
-
-/// The built-in themes given to every documentation site.
-pub(crate) mod themes {
- /// The "light" theme, selected by default when no setting is available. Used as the basis for
- /// the `--check-theme` functionality.
- pub(crate) static LIGHT: &str = include_str!("static/css/themes/light.css");
-
- /// The "dark" theme.
- pub(crate) static DARK: &str = include_str!("static/css/themes/dark.css");
-
- /// The "ayu" theme.
- pub(crate) static AYU: &str = include_str!("static/css/themes/ayu.css");
+pub(crate) struct StaticFile {
+ pub(crate) filename: PathBuf,
+ pub(crate) bytes: &'static [u8],
}
-/// Files related to the Fira Sans font.
-pub(crate) mod fira_sans {
- /// The file `FiraSans-Regular.woff2`, the Regular variant of the Fira Sans font in woff2.
- pub(crate) static REGULAR: &[u8] = include_bytes!("static/fonts/FiraSans-Regular.woff2");
-
- /// The file `FiraSans-Medium.woff2`, the Medium variant of the Fira Sans font in woff2.
- pub(crate) static MEDIUM: &[u8] = include_bytes!("static/fonts/FiraSans-Medium.woff2");
-
- /// The file `FiraSans-LICENSE.txt`, the license text for the Fira Sans font.
- pub(crate) static LICENSE: &[u8] = include_bytes!("static/fonts/FiraSans-LICENSE.txt");
+impl StaticFile {
+ fn new(filename: &str, bytes: &'static [u8]) -> StaticFile {
+ Self { filename: static_filename(filename, bytes), bytes }
+ }
+
+ pub(crate) fn minified(&self) -> Vec<u8> {
+ let extension = match self.filename.extension() {
+ Some(e) => e,
+ None => return self.bytes.to_owned(),
+ };
+ if extension == "css" {
+ minifier::css::minify(str::from_utf8(self.bytes).unwrap()).unwrap().to_string().into()
+ } else if extension == "js" {
+ minifier::js::minify(str::from_utf8(self.bytes).unwrap()).to_string().into()
+ } else {
+ self.bytes.to_owned()
+ }
+ }
+
+ pub(crate) fn output_filename(&self) -> &Path {
+ &self.filename
+ }
}
-/// Files related to the Source Serif 4 font.
-pub(crate) mod source_serif_4 {
- /// The file `SourceSerif4-Regular.ttf.woff2`, the Regular variant of the Source Serif 4 font in
- /// woff2.
- pub(crate) static REGULAR: &[u8] =
- include_bytes!("static/fonts/SourceSerif4-Regular.ttf.woff2");
-
- /// The file `SourceSerif4-Bold.ttf.woff2`, the Bold variant of the Source Serif 4 font in
- /// woff2.
- pub(crate) static BOLD: &[u8] = include_bytes!("static/fonts/SourceSerif4-Bold.ttf.woff2");
-
- /// The file `SourceSerif4-It.ttf.woff2`, the Italic variant of the Source Serif 4 font in
- /// woff2.
- pub(crate) static ITALIC: &[u8] = include_bytes!("static/fonts/SourceSerif4-It.ttf.woff2");
-
- /// The file `SourceSerif4-LICENSE.txt`, the license text for the Source Serif 4 font.
- pub(crate) static LICENSE: &[u8] = include_bytes!("static/fonts/SourceSerif4-LICENSE.md");
+/// The Display implementation for a StaticFile outputs its filename. This makes it
+/// convenient to interpolate static files into HTML templates.
+impl fmt::Display for StaticFile {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.output_filename().display())
+ }
}
-/// Files related to the Source Code Pro font.
-pub(crate) mod source_code_pro {
- /// The file `SourceCodePro-Regular.ttf.woff2`, the Regular variant of the Source Code Pro font
- /// in woff2.
- pub(crate) static REGULAR: &[u8] =
- include_bytes!("static/fonts/SourceCodePro-Regular.ttf.woff2");
-
- /// The file `SourceCodePro-Semibold.ttf.woff2`, the Semibold variant of the Source Code Pro
- /// font in woff2.
- pub(crate) static SEMIBOLD: &[u8] =
- include_bytes!("static/fonts/SourceCodePro-Semibold.ttf.woff2");
-
- /// The file `SourceCodePro-It.ttf.woff2`, the Italic variant of the Source Code Pro font in
- /// woff2.
- pub(crate) static ITALIC: &[u8] = include_bytes!("static/fonts/SourceCodePro-It.ttf.woff2");
+/// Insert the provided suffix into a filename just before the extension.
+pub(crate) fn suffix_path(filename: &str, suffix: &str) -> PathBuf {
+ // We use splitn vs Path::extension here because we might get a filename
+ // like `style.min.css` and we want to process that into
+ // `style-suffix.min.css`. Path::extension would just return `css`
+ // which would result in `style.min-suffix.css` which isn't what we
+ // want.
+ let (base, ext) = filename.split_once('.').unwrap();
+ let filename = format!("{}{}.{}", base, suffix, ext);
+ filename.into()
+}
- /// The file `SourceCodePro-LICENSE.txt`, the license text of the Source Code Pro font.
- pub(crate) static LICENSE: &[u8] = include_bytes!("static/fonts/SourceCodePro-LICENSE.txt");
+pub(crate) fn static_filename(filename: &str, contents: &[u8]) -> PathBuf {
+ let filename = filename.rsplit('/').next().unwrap();
+ suffix_path(filename, &static_suffix(contents))
}
-/// Files related to the Nanum Barun Gothic font.
-///
-/// These files are used to avoid some legacy CJK serif fonts in Windows.
-///
-/// Note that the Noto Sans KR font, which was used previously but was not very readable on Windows,
-/// has been replaced by the Nanum Barun Gothic font. This is due to Windows' implementation of font
-/// rendering that distorts OpenType fonts too much.
-///
-/// The font files were generated with these commands:
-///
-/// ```sh
-/// pyftsubset NanumBarunGothic.ttf \
-/// --unicodes=U+AC00-D7AF,U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF \
-/// --output-file=NanumBarunGothic.ttf.woff2 --flavor=woff2
-/// ```
-pub(crate) mod nanum_barun_gothic {
- /// The file `NanumBarunGothic.ttf.woff2`, the Regular variant of the Nanum Barun Gothic font.
- pub(crate) static REGULAR: &[u8] = include_bytes!("static/fonts/NanumBarunGothic.ttf.woff2");
+fn static_suffix(bytes: &[u8]) -> String {
+ let mut hasher = FxHasher::default();
+ hasher.write(bytes);
+ format!("-{:016x}", hasher.finish())
+}
- /// The file `NanumBarunGothic-LICENSE.txt`, the license text of the Nanum Barun Gothic font.
- pub(crate) static LICENSE: &[u8] = include_bytes!("static/fonts/NanumBarunGothic-LICENSE.txt");
+macro_rules! static_files {
+ ($($field:ident => $file_path:literal,)+) => {
+ pub(crate) struct StaticFiles {
+ $(pub $field: StaticFile,)+
+ }
+
+ pub(crate) static STATIC_FILES: std::sync::LazyLock<StaticFiles> = std::sync::LazyLock::new(|| StaticFiles {
+ $($field: StaticFile::new($file_path, include_bytes!($file_path)),)+
+ });
+
+ pub(crate) fn for_each<E>(f: impl Fn(&StaticFile) -> Result<(), E>) -> Result<(), E> {
+ for sf in [
+ $(&STATIC_FILES.$field,)+
+ ] {
+ f(sf)?
+ }
+ Ok(())
+ }
+ }
}
-/// Files related to the sidebar in rustdoc sources.
-pub(crate) mod sidebar {
- /// File script to handle sidebar.
- pub(crate) static SOURCE_SCRIPT: &str = include_str!("static/js/source-script.js");
+static_files! {
+ rustdoc_css => "static/css/rustdoc.css",
+ settings_css => "static/css/settings.css",
+ noscript_css => "static/css/noscript.css",
+ normalize_css => "static/css/normalize.css",
+ main_js => "static/js/main.js",
+ search_js => "static/js/search.js",
+ settings_js => "static/js/settings.js",
+ source_script_js => "static/js/source-script.js",
+ storage_js => "static/js/storage.js",
+ scrape_examples_js => "static/js/scrape-examples.js",
+ wheel_svg => "static/images/wheel.svg",
+ clipboard_svg => "static/images/clipboard.svg",
+ down_arrow_svg => "static/images/down-arrow.svg",
+ toggle_minus_png => "static/images/toggle-minus.svg",
+ toggle_plus_png => "static/images/toggle-plus.svg",
+ copyright => "static/COPYRIGHT.txt",
+ license_apache => "static/LICENSE-APACHE.txt",
+ license_mit => "static/LICENSE-MIT.txt",
+ rust_logo_svg => "static/images/rust-logo.svg",
+ rust_favicon_svg => "static/images/favicon.svg",
+ rust_favicon_png_16 => "static/images/favicon-16x16.png",
+ rust_favicon_png_32 => "static/images/favicon-32x32.png",
+ theme_light_css => "static/css/themes/light.css",
+ theme_dark_css => "static/css/themes/dark.css",
+ theme_ayu_css => "static/css/themes/ayu.css",
+ fira_sans_regular => "static/fonts/FiraSans-Regular.woff2",
+ fira_sans_medium => "static/fonts/FiraSans-Medium.woff2",
+ fira_sans_license => "static/fonts/FiraSans-LICENSE.txt",
+ source_serif_4_regular => "static/fonts/SourceSerif4-Regular.ttf.woff2",
+ source_serif_4_bold => "static/fonts/SourceSerif4-Bold.ttf.woff2",
+ source_serif_4_italic => "static/fonts/SourceSerif4-It.ttf.woff2",
+ source_serif_4_license => "static/fonts/SourceSerif4-LICENSE.md",
+ source_code_pro_regular => "static/fonts/SourceCodePro-Regular.ttf.woff2",
+ source_code_pro_semibold => "static/fonts/SourceCodePro-Semibold.ttf.woff2",
+ source_code_pro_italic => "static/fonts/SourceCodePro-It.ttf.woff2",
+ source_code_pro_license => "static/fonts/SourceCodePro-LICENSE.txt",
+ nanum_barun_gothic_regular => "static/fonts/NanumBarunGothic.ttf.woff2",
+ nanum_barun_gothic_license => "static/fonts/NanumBarunGothic-LICENSE.txt",
}
+
+pub(crate) static SCRAPE_EXAMPLES_HELP_MD: &str = include_str!("static/scrape-examples-help.md");
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index c32386916..aa3bf827d 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -7,48 +7,44 @@
<meta name="description" content="{{page.description}}"> {#- -#}
<meta name="keywords" content="{{page.keywords}}"> {#- -#}
<title>{{page.title}}</title> {#- -#}
- <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}SourceSerif4-Regular.ttf.woff2"> {#- -#}
- <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}FiraSans-Regular.woff2"> {#- -#}
- <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}FiraSans-Medium.woff2"> {#- -#}
- <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}SourceCodePro-Regular.ttf.woff2"> {#- -#}
- <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}SourceSerif4-Bold.ttf.woff2"> {#- -#}
- <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}SourceCodePro-Semibold.ttf.woff2"> {#- -#}
+ <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_serif_4_regular}}"> {#- -#}
+ <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}}normalize{{page.resource_suffix}}.css"> {#- -#}
+ href="{{static_root_path|safe}}{{files.normalize_css}}"> {#- -#}
<link rel="stylesheet" {# -#}
- href="{{static_root_path|safe}}rustdoc{{page.resource_suffix}}.css" {# -#}
+ href="{{static_root_path|safe}}{{files.rustdoc_css}}" {# -#}
id="mainThemeStyle"> {#- -#}
+ <link rel="stylesheet" id="themeStyle" href="{{static_root_path|safe}}{{files.theme_light_css}}"> {#- -#}
+ <link rel="stylesheet" disabled href="{{static_root_path|safe}}{{files.theme_dark_css}}"> {#- -#}
+ <link rel="stylesheet" disabled href="{{static_root_path|safe}}{{files.theme_ayu_css}}"> {#- -#}
{%- for theme in themes -%}
- <link rel="stylesheet" {# -#}
- href="{{static_root_path|safe}}{{theme}}{{page.resource_suffix}}.css" {# -#}
- {%- if theme == "light" -%}
- id="themeStyle"
- {%- else -%}
- disabled
- {%- endif -%}
- >
+ <link rel="stylesheet" disabled href="{{page.root_path|safe}}{{theme}}{{page.resource_suffix}}.css"> {#- -#}
{%- endfor -%}
<script id="default-settings" {# -#}
{% for (k, v) in layout.default_settings %}
data-{{k}}="{{v}}"
{%- endfor -%}
></script> {#- -#}
- <script src="{{static_root_path|safe}}storage{{page.resource_suffix}}.js"></script> {#- -#}
+ <script src="{{static_root_path|safe}}{{files.storage_js}}"></script> {#- -#}
{%- if page.css_class.contains("crate") -%}
<script defer src="{{page.root_path|safe}}crates{{page.resource_suffix}}.js"></script> {#- -#}
{%- else if page.css_class == "source" -%}
- <script defer src="{{static_root_path|safe}}source-script{{page.resource_suffix}}.js"></script> {#- -#}
+ <script defer src="{{static_root_path|safe}}{{files.source_script_js}}"></script> {#- -#}
<script defer src="{{page.root_path|safe}}source-files{{page.resource_suffix}}.js"></script> {#- -#}
{%- else if !page.css_class.contains("mod") -%}
<script defer src="sidebar-items{{page.resource_suffix}}.js"></script> {#- -#}
{%- endif -%}
- <script defer src="{{static_root_path|safe}}main{{page.resource_suffix}}.js"></script> {#- -#}
+ <script defer src="{{static_root_path|safe}}{{files.main_js}}"></script> {#- -#}
{%- if layout.scrape_examples_extension -%}
- <script defer src="{{page.root_path|safe}}scrape-examples{{page.resource_suffix}}.js"></script> {#- -#}
+ <script defer src="{{static_root_path|safe}}{{files.scrape_examples_js}}"></script> {#- -#}
{%- endif -%}
<noscript> {#- -#}
<link rel="stylesheet" {# -#}
- href="{{static_root_path|safe}}noscript{{page.resource_suffix}}.css"> {#- -#}
+ href="{{static_root_path|safe}}{{files.noscript_css}}"> {#- -#}
</noscript> {#- -#}
{%- if layout.css_file_extension.is_some() -%}
<link rel="stylesheet" {# -#}
@@ -58,11 +54,11 @@
<link rel="icon" href="{{layout.favicon}}"> {#- -#}
{%- else -%}
<link rel="alternate icon" type="image/png" {# -#}
- href="{{static_root_path|safe}}favicon-16x16{{page.resource_suffix}}.png"> {#- -#}
+ href="{{static_root_path|safe}}{{files.rust_favicon_png_16}}"> {#- -#}
<link rel="alternate icon" type="image/png" {# -#}
- href="{{static_root_path|safe}}favicon-32x32{{page.resource_suffix}}.png"> {#- -#}
+ href="{{static_root_path|safe}}{{files.rust_favicon_png_32}}"> {#- -#}
<link rel="icon" type="image/svg+xml" {# -#}
- href="{{static_root_path|safe}}favicon{{page.resource_suffix}}.svg"> {#- -#}
+ href="{{static_root_path|safe}}{{files.rust_favicon_svg}}"> {#- -#}
{%- endif -%}
{{- layout.external_html.in_header|safe -}}
</head> {#- -#}
@@ -81,7 +77,7 @@
{%- if !layout.logo.is_empty() -%}
<img src="{{layout.logo}}" alt="logo"> {#- -#}
{%- else -%}
- <img class="rust-logo" src="{{static_root_path|safe}}rust-logo{{page.resource_suffix}}.svg" alt="logo"> {#- -#}
+ <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {#- -#}
{%- endif -%}
</div> {#- -#}
</a> {#- -#}
@@ -95,7 +91,7 @@
{%- if !layout.logo.is_empty() %}
<img src="{{layout.logo}}" alt="logo"> {#- -#}
{%- else -%}
- <img class="rust-logo" src="{{static_root_path|safe}}rust-logo{{page.resource_suffix}}.svg" alt="logo"> {#- -#}
+ <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {#- -#}
{%- endif -%}
</div> {#- -#}
</a> {#- -#}
@@ -110,7 +106,7 @@
{%- if !layout.logo.is_empty() %}
<img src="{{layout.logo}}" alt="logo"> {#- -#}
{%- else -%}
- <img class="rust-logo" src="{{static_root_path|safe}}rust-logo{{page.resource_suffix}}.svg" alt="logo"> {#- -#}
+ <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {#- -#}
{%- endif -%}
</a> {#- -#}
{%- endif -%}
@@ -119,6 +115,7 @@
<input {# -#}
class="search-input" {# -#}
name="search" {# -#}
+ aria-label="Run search in the documentation" {# -#}
autocomplete="off" {# -#}
spellcheck="false" {# -#}
placeholder="Click or press ‘S’ to search, ‘?’ for more options…" {# -#}
@@ -129,7 +126,7 @@
<div id="settings-menu" tabindex="-1"> {#- -#}
<a href="{{page.root_path|safe}}settings.html" title="settings"> {#- -#}
<img width="22" height="22" alt="Change settings" {# -#}
- src="{{static_root_path|safe}}wheel{{page.resource_suffix}}.svg"> {#- -#}
+ src="{{static_root_path|safe}}{{files.wheel_svg}}"> {#- -#}
</a> {#- -#}
</div> {#- -#}
</form> {#- -#}
@@ -140,10 +137,14 @@
{{- layout.external_html.after_content|safe -}}
<div id="rustdoc-vars" {# -#}
data-root-path="{{page.root_path|safe}}" {# -#}
+ data-static-root-path="{{static_root_path|safe}}" {# -#}
data-current-crate="{{layout.krate}}" {# -#}
data-themes="{{themes|join(",") }}" {# -#}
data-resource-suffix="{{page.resource_suffix}}" {# -#}
data-rustdoc-version="{{rustdoc_version}}" {# -#}
+ data-search-js="{{files.search_js}}" {# -#}
+ data-settings-js="{{files.settings_js}}" {# -#}
+ data-settings-css="{{files.settings_css}}" {# -#}
> {#- -#}
</div> {#- -#}
</body> {#- -#}
diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html
index b6ce3ea3d..611d124d4 100644
--- a/src/librustdoc/html/templates/print_item.html
+++ b/src/librustdoc/html/templates/print_item.html
@@ -7,7 +7,7 @@
{%- endfor -%}
<a class="{{item_type}}" href="#">{{name}}</a> {#- -#}
<button id="copy-path" onclick="copy_path(this)" title="Copy item path to clipboard"> {#- -#}
- <img src="{{static_root_path|safe}}clipboard{{page.resource_suffix}}.svg" {# -#}
+ <img src="{{static_root_path|safe}}{{clipboard_svg}}" {# -#}
width="19" height="18" {# -#}
alt="Copy item path"> {#- -#}
</button> {#- -#}
@@ -21,8 +21,8 @@
<a class="srclink" href="{{href|safe}}">source</a> · {# -#}
{%- else -%}
{%- endmatch -%}
- <a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs"> {#- -#}
- [<span class="inner">&#x2212;</span>] {#- -#}
- </a> {#- -#}
+ <button id="toggle-all-docs" title="collapse all docs"> {#- -#}
+ [<span>&#x2212;</span>] {#- -#}
+ </button> {#- -#}
</span> {#- -#}
</div> {#- -#}
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index cdf59cdd3..d7184053c 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -48,7 +48,8 @@ impl JsonRenderer<'_> {
.map(rustc_ast_pretty::pprust::attribute_to_string)
.collect();
let span = item.span(self.tcx);
- let clean::Item { name, attrs: _, kind: _, visibility, item_id, cfg: _ } = item;
+ let visibility = item.visibility(self.tcx);
+ let clean::Item { name, attrs: _, kind: _, item_id, cfg: _, .. } = item;
let inner = match *item.kind {
clean::KeywordItem => return None,
clean::StrippedItem(ref inner) => {
@@ -99,13 +100,12 @@ impl JsonRenderer<'_> {
}
}
- fn convert_visibility(&self, v: clean::Visibility) -> Visibility {
- use clean::Visibility::*;
+ fn convert_visibility(&self, v: Option<ty::Visibility<DefId>>) -> Visibility {
match v {
- Public => Visibility::Public,
- Inherited => Visibility::Default,
- Restricted(did) if did.is_crate_root() => Visibility::Crate,
- Restricted(did) => Visibility::Restricted {
+ None => Visibility::Default,
+ Some(ty::Visibility::Public) => Visibility::Public,
+ Some(ty::Visibility::Restricted(did)) if did.is_crate_root() => Visibility::Crate,
+ Some(ty::Visibility::Restricted(did)) => Visibility::Restricted {
parent: from_item_id(did.into(), self.tcx),
path: self.tcx.def_path(did).to_string_no_crate_verbose(),
},
@@ -257,12 +257,12 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
StructFieldItem(f) => ItemEnum::StructField(f.into_tcx(tcx)),
EnumItem(e) => ItemEnum::Enum(e.into_tcx(tcx)),
VariantItem(v) => ItemEnum::Variant(v.into_tcx(tcx)),
- FunctionItem(f) => ItemEnum::Function(from_function(f, header.unwrap(), tcx)),
- ForeignFunctionItem(f) => ItemEnum::Function(from_function(f, header.unwrap(), tcx)),
+ FunctionItem(f) => ItemEnum::Function(from_function(f, true, header.unwrap(), tcx)),
+ ForeignFunctionItem(f) => ItemEnum::Function(from_function(f, false, header.unwrap(), tcx)),
TraitItem(t) => ItemEnum::Trait((*t).into_tcx(tcx)),
TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_tcx(tcx)),
- MethodItem(m, _) => ItemEnum::Method(from_function_method(m, true, header.unwrap(), tcx)),
- TyMethodItem(m) => ItemEnum::Method(from_function_method(m, false, header.unwrap(), tcx)),
+ MethodItem(m, _) => ItemEnum::Function(from_function(m, true, header.unwrap(), tcx)),
+ TyMethodItem(m) => ItemEnum::Function(from_function(m, false, header.unwrap(), tcx)),
ImplItem(i) => ItemEnum::Impl((*i).into_tcx(tcx)),
StaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)),
ForeignStaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)),
@@ -283,7 +283,7 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: Some(default.expr(tcx)) }
}
TyAssocTypeItem(g, b) => ItemEnum::AssocType {
- generics: (*g).into_tcx(tcx),
+ generics: g.into_tcx(tcx),
bounds: b.into_tcx(tcx),
default: None,
},
@@ -315,15 +315,15 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
impl FromWithTcx<clean::Struct> for Struct {
fn from_tcx(struct_: clean::Struct, tcx: TyCtxt<'_>) -> Self {
let fields_stripped = struct_.has_stripped_entries();
- let clean::Struct { struct_type, generics, fields } = struct_;
+ let clean::Struct { ctor_kind, generics, fields } = struct_;
- let kind = match struct_type {
- CtorKind::Fn => StructKind::Tuple(ids_keeping_stripped(fields, tcx)),
- CtorKind::Const => {
+ let kind = match ctor_kind {
+ Some(CtorKind::Fn) => StructKind::Tuple(ids_keeping_stripped(fields, tcx)),
+ Some(CtorKind::Const) => {
assert!(fields.is_empty());
StructKind::Unit
}
- CtorKind::Fictive => StructKind::Plain { fields: ids(fields, tcx), fields_stripped },
+ None => StructKind::Plain { fields: ids(fields, tcx), fields_stripped },
};
Struct {
@@ -485,7 +485,7 @@ impl FromWithTcx<clean::Type> for Type {
BareFunction(f) => Type::FunctionPointer(Box::new((*f).into_tcx(tcx))),
Tuple(t) => Type::Tuple(t.into_tcx(tcx)),
Slice(t) => Type::Slice(Box::new((*t).into_tcx(tcx))),
- Array(t, s) => Type::Array { type_: Box::new((*t).into_tcx(tcx)), len: s },
+ Array(t, s) => Type::Array { type_: Box::new((*t).into_tcx(tcx)), len: s.to_string() },
ImplTrait(g) => Type::ImplTrait(g.into_tcx(tcx)),
Infer => Type::Infer,
RawPointer(mutability, type_) => Type::RawPointer {
@@ -618,6 +618,7 @@ impl FromWithTcx<clean::Impl> for Impl {
pub(crate) fn from_function(
function: Box<clean::Function>,
+ has_body: bool,
header: rustc_hir::FnHeader,
tcx: TyCtxt<'_>,
) -> Function {
@@ -626,20 +627,6 @@ pub(crate) fn from_function(
decl: decl.into_tcx(tcx),
generics: generics.into_tcx(tcx),
header: from_fn_header(&header),
- }
-}
-
-pub(crate) fn from_function_method(
- function: Box<clean::Function>,
- has_body: bool,
- header: rustc_hir::FnHeader,
- tcx: TyCtxt<'_>,
-) -> Method {
- let clean::Function { decl, generics } = *function;
- Method {
- decl: decl.into_tcx(tcx),
- generics: generics.into_tcx(tcx),
- header: from_fn_header(&header),
has_body,
}
}
@@ -674,7 +661,7 @@ impl FromWithTcx<clean::Variant> for Variant {
impl FromWithTcx<clean::Discriminant> for Discriminant {
fn from_tcx(disr: clean::Discriminant, tcx: TyCtxt<'_>) -> Self {
Discriminant {
- // expr is only none if going throught the inlineing path, which gets
+ // expr is only none if going through the inlineing path, which gets
// `rustc_middle` types, not `rustc_hir`, but because JSON never inlines
// the expr is always some.
expr: disr.expr(tcx).unwrap(),
@@ -759,14 +746,13 @@ impl FromWithTcx<ItemType> for ItemKind {
Struct => ItemKind::Struct,
Union => ItemKind::Union,
Enum => ItemKind::Enum,
- Function => ItemKind::Function,
+ Function | TyMethod | Method => ItemKind::Function,
Typedef => ItemKind::Typedef,
OpaqueTy => ItemKind::OpaqueTy,
Static => ItemKind::Static,
Constant => ItemKind::Constant,
Trait => ItemKind::Trait,
Impl => ItemKind::Impl,
- TyMethod | Method => ItemKind::Method,
StructField => ItemKind::StructField,
Variant => ItemKind::Variant,
Macro => ItemKind::Macro,
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index d13efe6c1..1196f944f 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -99,53 +99,6 @@ impl<'tcx> JsonRenderer<'tcx> {
})
.unwrap_or_default()
}
-
- fn get_trait_items(&mut self) -> Vec<(types::Id, types::Item)> {
- debug!("Adding foreign trait items");
- Rc::clone(&self.cache)
- .traits
- .iter()
- .filter_map(|(&id, trait_item)| {
- // only need to synthesize items for external traits
- if !id.is_local() {
- for item in &trait_item.items {
- trace!("Adding subitem to {id:?}: {:?}", item.item_id);
- self.item(item.clone()).unwrap();
- }
- let item_id = from_item_id(id.into(), self.tcx);
- Some((
- item_id.clone(),
- types::Item {
- id: item_id,
- crate_id: id.krate.as_u32(),
- name: self
- .cache
- .paths
- .get(&id)
- .unwrap_or_else(|| {
- self.cache
- .external_paths
- .get(&id)
- .expect("Trait should either be in local or external paths")
- })
- .0
- .last()
- .map(|s| s.to_string()),
- visibility: types::Visibility::Public,
- inner: types::ItemEnum::Trait(trait_item.clone().into_tcx(self.tcx)),
- span: None,
- docs: Default::default(),
- links: Default::default(),
- attrs: Default::default(),
- deprecation: Default::default(),
- },
- ))
- } else {
- None
- }
- })
- .collect()
- }
}
impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
@@ -223,15 +176,14 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
false
}
- types::ItemEnum::Method(_)
+ types::ItemEnum::Function(_)
| types::ItemEnum::Module(_)
+ | types::ItemEnum::Import(_)
| types::ItemEnum::AssocConst { .. }
| types::ItemEnum::AssocType { .. } => true,
types::ItemEnum::ExternCrate { .. }
- | types::ItemEnum::Import(_)
| types::ItemEnum::StructField(_)
| types::ItemEnum::Variant(_)
- | types::ItemEnum::Function(_)
| types::ItemEnum::TraitAlias(_)
| types::ItemEnum::Impl(_)
| types::ItemEnum::Typedef(_)
@@ -277,11 +229,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
let e = ExternalCrate { crate_num: LOCAL_CRATE };
- // FIXME(adotinthevoid): Remove this, as it's not consistant with not
- // inlining foreign items.
- let foreign_trait_items = self.get_trait_items();
- let mut index = (*self.index).clone().into_inner();
- index.extend(foreign_trait_items);
+ let index = (*self.index).clone().into_inner();
debug!("Constructing Output");
// This needs to be the default HashMap for compatibility with the public interface for
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 4cf9435d9..ef1d7da5a 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -8,6 +8,7 @@
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(drain_filter)]
+#![feature(is_terminal)]
#![feature(let_chains)]
#![feature(test)]
#![feature(never_type)]
@@ -69,7 +70,7 @@ extern crate jemalloc_sys;
use std::default::Default;
use std::env::{self, VarError};
-use std::io;
+use std::io::{self, IsTerminal};
use std::process;
use rustc_driver::abort_on_err;
@@ -179,7 +180,7 @@ fn init_logging() {
let color_logs = match std::env::var("RUSTDOC_LOG_COLOR").as_deref() {
Ok("always") => true,
Ok("never") => false,
- Ok("auto") | Err(VarError::NotPresent) => atty::is(atty::Stream::Stdout),
+ Ok("auto") | Err(VarError::NotPresent) => io::stdout().is_terminal(),
Ok(value) => early_error(
ErrorOutputType::default(),
&format!("invalid log color value '{}': expected one of always, never, or auto", value),
@@ -469,9 +470,6 @@ fn opts() -> Vec<RustcOptGroup> {
stable("json", |o| {
o.optopt("", "json", "Configure the structure of JSON diagnostics", "CONFIG")
}),
- unstable("disable-minification", |o| {
- o.optflagmulti("", "disable-minification", "Disable minification applied on JS files")
- }),
stable("allow", |o| o.optmulti("A", "allow", "Set lint allowed", "LINT")),
stable("warn", |o| o.optmulti("W", "warn", "Set lint warnings", "LINT")),
stable("force-warn", |o| o.optmulti("", "force-warn", "Set lint force-warn", "LINT")),
@@ -610,6 +608,7 @@ fn opts() -> Vec<RustcOptGroup> {
)
}),
// deprecated / removed options
+ unstable("disable-minification", |o| o.optflagmulti("", "disable-minification", "removed")),
stable("plugin-path", |o| {
o.optmulti(
"",
@@ -675,7 +674,7 @@ type MainResult = Result<(), ErrorGuaranteed>;
fn wrap_return(diag: &rustc_errors::Handler, res: Result<(), String>) -> MainResult {
match res {
- Ok(()) => Ok(()),
+ Ok(()) => diag.has_errors().map_or(Ok(()), Err),
Err(err) => {
let reported = diag.struct_err(&err).emit();
Err(reported)
@@ -690,7 +689,7 @@ fn run_renderer<'tcx, T: formats::FormatRenderer<'tcx>>(
tcx: TyCtxt<'tcx>,
) -> MainResult {
match formats::run_format::<T>(krate, renderopts, cache, tcx) {
- Ok(_) => Ok(()),
+ Ok(_) => tcx.sess.has_errors().map_or(Ok(()), Err),
Err(e) => {
let mut msg =
tcx.sess.struct_err(&format!("couldn't generate documentation: {}", e.error));
@@ -775,6 +774,7 @@ fn main_args(at_args: &[String]) -> MainResult {
let output_format = options.output_format;
let externs = options.externs.clone();
let scrape_examples_options = options.scrape_examples_options.clone();
+ let bin_crate = options.bin_crate;
let config = core::create_config(options);
@@ -782,10 +782,7 @@ fn main_args(at_args: &[String]) -> MainResult {
let sess = compiler.session();
if sess.opts.describe_lints {
- let mut lint_store = rustc_lint::new_lint_store(
- sess.opts.unstable_opts.no_interleave_lints,
- sess.enable_internal_lints(),
- );
+ let mut lint_store = rustc_lint::new_lint_store(sess.enable_internal_lints());
let registered_lints = if let Some(register_lints) = compiler.register_lints() {
register_lints(sess, &mut lint_store);
true
@@ -836,7 +833,14 @@ fn main_args(at_args: &[String]) -> MainResult {
info!("finished with rustc");
if let Some(options) = scrape_examples_options {
- return scrape_examples::run(krate, render_opts, cache, tcx, options);
+ return scrape_examples::run(
+ krate,
+ render_opts,
+ cache,
+ tcx,
+ options,
+ bin_crate,
+ );
}
cache.crate_version = crate_version;
diff --git a/src/librustdoc/passes/bare_urls.rs b/src/librustdoc/passes/bare_urls.rs
deleted file mode 100644
index 7ff3ccef9..000000000
--- a/src/librustdoc/passes/bare_urls.rs
+++ /dev/null
@@ -1,110 +0,0 @@
-//! Detects links that are not linkified, e.g., in Markdown such as `Go to https://example.com/.`
-//! Suggests wrapping the link with angle brackets: `Go to <https://example.com/>.` to linkify it.
-use super::Pass;
-use crate::clean::*;
-use crate::core::DocContext;
-use crate::html::markdown::main_body_opts;
-use crate::visit::DocVisitor;
-use core::ops::Range;
-use pulldown_cmark::{Event, Parser, Tag};
-use regex::Regex;
-use rustc_errors::Applicability;
-use std::mem;
-use std::sync::LazyLock;
-
-pub(crate) const CHECK_BARE_URLS: Pass = Pass {
- name: "check-bare-urls",
- run: check_bare_urls,
- description: "detects URLs that are not hyperlinks",
-};
-
-static URL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
- Regex::new(concat!(
- r"https?://", // url scheme
- r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", // one or more subdomains
- r"[a-zA-Z]{2,63}", // root domain
- r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)" // optional query or url fragments
- ))
- .expect("failed to build regex")
-});
-
-struct BareUrlsLinter<'a, 'tcx> {
- cx: &'a mut DocContext<'tcx>,
-}
-
-impl<'a, 'tcx> BareUrlsLinter<'a, 'tcx> {
- fn find_raw_urls(
- &self,
- text: &str,
- range: Range<usize>,
- f: &impl Fn(&DocContext<'_>, &str, &str, Range<usize>),
- ) {
- trace!("looking for raw urls in {}", text);
- // For now, we only check "full" URLs (meaning, starting with "http://" or "https://").
- for match_ in URL_REGEX.find_iter(text) {
- let url = match_.as_str();
- let url_range = match_.range();
- f(
- self.cx,
- "this URL is not a hyperlink",
- url,
- Range { start: range.start + url_range.start, end: range.start + url_range.end },
- );
- }
- }
-}
-
-pub(crate) fn check_bare_urls(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
- BareUrlsLinter { cx }.visit_crate(&krate);
- krate
-}
-
-impl<'a, 'tcx> DocVisitor for BareUrlsLinter<'a, 'tcx> {
- fn visit_item(&mut self, item: &Item) {
- let Some(hir_id) = DocContext::as_local_hir_id(self.cx.tcx, item.item_id)
- else {
- // If non-local, no need to check anything.
- return;
- };
- let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
- if !dox.is_empty() {
- let report_diag = |cx: &DocContext<'_>, msg: &str, url: &str, range: Range<usize>| {
- let sp = super::source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs)
- .unwrap_or_else(|| item.attr_span(cx.tcx));
- cx.tcx.struct_span_lint_hir(crate::lint::BARE_URLS, hir_id, sp, msg, |lint| {
- lint.note("bare URLs are not automatically turned into clickable links")
- .span_suggestion(
- sp,
- "use an automatic link instead",
- format!("<{}>", url),
- Applicability::MachineApplicable,
- )
- });
- };
-
- let mut p = Parser::new_ext(&dox, main_body_opts()).into_offset_iter();
-
- while let Some((event, range)) = p.next() {
- match event {
- Event::Text(s) => self.find_raw_urls(&s, range, &report_diag),
- // We don't want to check the text inside code blocks or links.
- Event::Start(tag @ (Tag::CodeBlock(_) | Tag::Link(..))) => {
- while let Some((event, _)) = p.next() {
- match event {
- Event::End(end)
- if mem::discriminant(&end) == mem::discriminant(&tag) =>
- {
- break;
- }
- _ => {}
- }
- }
- }
- _ => {}
- }
- }
- }
-
- self.visit_item_recur(item)
- }
-}
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index 48835abf9..02b227896 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -244,10 +244,10 @@ impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> {
matches!(
node,
hir::Node::Variant(hir::Variant {
- data: hir::VariantData::Tuple(_, _),
+ data: hir::VariantData::Tuple(_, _, _),
..
}) | hir::Node::Item(hir::Item {
- kind: hir::ItemKind::Struct(hir::VariantData::Tuple(_, _), _),
+ kind: hir::ItemKind::Struct(hir::VariantData::Tuple(_, _, _), _),
..
})
)
diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs
deleted file mode 100644
index 2e651b538..000000000
--- a/src/librustdoc/passes/check_code_block_syntax.rs
+++ /dev/null
@@ -1,209 +0,0 @@
-//! Validates syntax inside Rust code blocks (\`\`\`rust).
-use rustc_data_structures::sync::{Lock, Lrc};
-use rustc_errors::{
- emitter::Emitter,
- translation::{to_fluent_args, Translate},
- Applicability, Diagnostic, Handler, LazyFallbackBundle,
-};
-use rustc_parse::parse_stream_from_source_str;
-use rustc_session::parse::ParseSess;
-use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId};
-use rustc_span::source_map::{FilePathMapping, SourceMap};
-use rustc_span::{FileName, InnerSpan, DUMMY_SP};
-
-use crate::clean;
-use crate::core::DocContext;
-use crate::html::markdown::{self, RustCodeBlock};
-use crate::passes::Pass;
-use crate::visit::DocVisitor;
-
-pub(crate) const CHECK_CODE_BLOCK_SYNTAX: Pass = Pass {
- name: "check-code-block-syntax",
- run: check_code_block_syntax,
- description: "validates syntax inside Rust code blocks",
-};
-
-pub(crate) fn check_code_block_syntax(
- krate: clean::Crate,
- cx: &mut DocContext<'_>,
-) -> clean::Crate {
- SyntaxChecker { cx }.visit_crate(&krate);
- krate
-}
-
-struct SyntaxChecker<'a, 'tcx> {
- cx: &'a DocContext<'tcx>,
-}
-
-impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> {
- fn check_rust_syntax(&self, item: &clean::Item, dox: &str, code_block: RustCodeBlock) {
- let buffer = Lrc::new(Lock::new(Buffer::default()));
- let fallback_bundle =
- rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
- let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle };
-
- let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
- let handler = Handler::with_emitter(false, None, Box::new(emitter));
- let source = dox[code_block.code].to_owned();
- let sess = ParseSess::with_span_handler(handler, sm);
-
- let edition = code_block.lang_string.edition.unwrap_or_else(|| self.cx.tcx.sess.edition());
- let expn_data = ExpnData::default(
- ExpnKind::AstPass(AstPass::TestHarness),
- DUMMY_SP,
- edition,
- None,
- None,
- );
- let expn_id =
- self.cx.tcx.with_stable_hashing_context(|hcx| LocalExpnId::fresh(expn_data, hcx));
- let span = DUMMY_SP.fresh_expansion(expn_id);
-
- let is_empty = rustc_driver::catch_fatal_errors(|| {
- parse_stream_from_source_str(
- FileName::Custom(String::from("doctest")),
- source,
- &sess,
- Some(span),
- )
- .is_empty()
- })
- .unwrap_or(false);
- let buffer = buffer.borrow();
-
- if !buffer.has_errors && !is_empty {
- // No errors in a non-empty program.
- return;
- }
-
- let Some(local_id) = item.item_id.as_def_id().and_then(|x| x.as_local())
- else {
- // We don't need to check the syntax for other crates so returning
- // without doing anything should not be a problem.
- return;
- };
-
- let hir_id = self.cx.tcx.hir().local_def_id_to_hir_id(local_id);
- let empty_block = code_block.lang_string == Default::default() && code_block.is_fenced;
- let is_ignore = code_block.lang_string.ignore != markdown::Ignore::None;
-
- // The span and whether it is precise or not.
- let (sp, precise_span) = match super::source_span_for_markdown_range(
- self.cx.tcx,
- dox,
- &code_block.range,
- &item.attrs,
- ) {
- Some(sp) => (sp, true),
- None => (item.attr_span(self.cx.tcx), false),
- };
-
- let msg = if buffer.has_errors {
- "could not parse code block as Rust code"
- } else {
- "Rust code block is empty"
- };
-
- // Finally build and emit the completed diagnostic.
- // All points of divergence have been handled earlier so this can be
- // done the same way whether the span is precise or not.
- self.cx.tcx.struct_span_lint_hir(
- crate::lint::INVALID_RUST_CODEBLOCKS,
- hir_id,
- sp,
- msg,
- |lint| {
- let explanation = if is_ignore {
- "`ignore` code blocks require valid Rust code for syntax highlighting; \
- mark blocks that do not contain Rust code as text"
- } else {
- "mark blocks that do not contain Rust code as text"
- };
-
- if precise_span {
- if is_ignore {
- // giving an accurate suggestion is hard because `ignore` might not have come first in the list.
- // just give a `help` instead.
- lint.span_help(
- sp.from_inner(InnerSpan::new(0, 3)),
- &format!("{}: ```text", explanation),
- );
- } else if empty_block {
- lint.span_suggestion(
- sp.from_inner(InnerSpan::new(0, 3)).shrink_to_hi(),
- explanation,
- "text",
- Applicability::MachineApplicable,
- );
- }
- } else if empty_block || is_ignore {
- lint.help(&format!("{}: ```text", explanation));
- }
-
- // FIXME(#67563): Provide more context for these errors by displaying the spans inline.
- for message in buffer.messages.iter() {
- lint.note(message);
- }
-
- lint
- },
- );
- }
-}
-
-impl<'a, 'tcx> DocVisitor for SyntaxChecker<'a, 'tcx> {
- fn visit_item(&mut self, item: &clean::Item) {
- if let Some(dox) = &item.attrs.collapsed_doc_value() {
- let sp = item.attr_span(self.cx.tcx);
- let extra = crate::html::markdown::ExtraInfo::new_did(
- self.cx.tcx,
- item.item_id.expect_def_id(),
- sp,
- );
- for code_block in markdown::rust_code_blocks(dox, &extra) {
- self.check_rust_syntax(item, dox, code_block);
- }
- }
-
- self.visit_item_recur(item)
- }
-}
-
-#[derive(Default)]
-struct Buffer {
- messages: Vec<String>,
- has_errors: bool,
-}
-
-struct BufferEmitter {
- buffer: Lrc<Lock<Buffer>>,
- fallback_bundle: LazyFallbackBundle,
-}
-
-impl Translate for BufferEmitter {
- fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
- None
- }
-
- fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
- &**self.fallback_bundle
- }
-}
-
-impl Emitter for BufferEmitter {
- fn emit_diagnostic(&mut self, diag: &Diagnostic) {
- let mut buffer = self.buffer.borrow_mut();
-
- let fluent_args = to_fluent_args(diag.args());
- let translated_main_message = self.translate_message(&diag.message[0].0, &fluent_args);
-
- buffer.messages.push(format!("error from rustc: {}", translated_main_message));
- if diag.is_error() {
- buffer.has_errors = true;
- }
- }
-
- fn source_map(&self) -> Option<&Lrc<SourceMap>> {
- None
- }
-}
diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs
index 7740c6d5b..057d2fdd9 100644
--- a/src/librustdoc/passes/check_doc_test_visibility.rs
+++ b/src/librustdoc/passes/check_doc_test_visibility.rs
@@ -56,7 +56,7 @@ impl crate::doctest::Tester for Tests {
}
pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool {
- if !cx.cache.effective_visibilities.is_directly_public(item.item_id.expect_def_id())
+ if !cx.cache.effective_visibilities.is_directly_public(cx.tcx, item.item_id.expect_def_id())
|| matches!(
*item.kind,
clean::StructFieldItem(_)
@@ -130,7 +130,7 @@ pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item
);
}
} else if tests.found_tests > 0
- && !cx.cache.effective_visibilities.is_exported(item.item_id.expect_def_id())
+ && !cx.cache.effective_visibilities.is_exported(cx.tcx, item.item_id.expect_def_id())
{
cx.tcx.struct_span_lint_hir(
crate::lint::PRIVATE_DOC_TESTS,
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 8aa0abd36..37a28b6b7 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -402,6 +402,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
})
.and_then(|self_id| match tcx.def_kind(self_id) {
DefKind::Impl => self.def_id_to_res(self_id),
+ DefKind::Use => None,
def_kind => Some(Res::Def(def_kind, self_id)),
})
}
@@ -1772,7 +1773,6 @@ fn resolution_failure(
// Otherwise, it must be an associated item or variant
let res = partial_res.expect("None case was handled by `last_found_module`");
- let name = res.name(tcx);
let kind = match res {
Res::Def(kind, _) => Some(kind),
Res::Primitive(_) => None,
@@ -1814,6 +1814,7 @@ fn resolution_failure(
} else {
"associated item"
};
+ let name = res.name(tcx);
let note = format!(
"the {} `{}` has no {} named `{}`",
res.descr(),
@@ -1893,7 +1894,7 @@ fn disambiguator_error(
diag_info.link_range = disambiguator_range;
report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, msg, &diag_info, |diag, _sp| {
let msg = format!(
- "see {}/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators",
+ "see {}/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators",
crate::DOC_RUST_LANG_ORG_CHANNEL
);
diag.note(&msg);
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index 6b699c790..d57f981d5 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -8,7 +8,7 @@ use crate::formats::cache::Cache;
use crate::visit::DocVisitor;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_middle::ty::{self, DefIdTree};
use rustc_span::symbol::sym;
@@ -25,7 +25,9 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
synth.impls
});
- let prims: FxHashSet<PrimitiveType> = krate.primitives.iter().map(|p| p.1).collect();
+ let local_crate = ExternalCrate { crate_num: LOCAL_CRATE };
+ let prims: FxHashSet<PrimitiveType> =
+ local_crate.primitives(cx.tcx).iter().map(|p| p.1).collect();
let crate_items = {
let mut coll = ItemCollector::new();
diff --git a/src/librustdoc/passes/lint.rs b/src/librustdoc/passes/lint.rs
new file mode 100644
index 000000000..97031c4f0
--- /dev/null
+++ b/src/librustdoc/passes/lint.rs
@@ -0,0 +1,33 @@
+//! Runs several rustdoc lints, consolidating them into a single pass for
+//! efficiency and simplicity.
+
+mod bare_urls;
+mod check_code_block_syntax;
+mod html_tags;
+
+use super::Pass;
+use crate::clean::*;
+use crate::core::DocContext;
+use crate::visit::DocVisitor;
+
+pub(crate) const RUN_LINTS: Pass =
+ Pass { name: "run-lints", run: run_lints, description: "runs some of rustdoc's lints" };
+
+struct Linter<'a, 'tcx> {
+ cx: &'a mut DocContext<'tcx>,
+}
+
+pub(crate) fn run_lints(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
+ Linter { cx }.visit_crate(&krate);
+ krate
+}
+
+impl<'a, 'tcx> DocVisitor for Linter<'a, 'tcx> {
+ fn visit_item(&mut self, item: &Item) {
+ bare_urls::visit_item(self.cx, item);
+ check_code_block_syntax::visit_item(self.cx, item);
+ html_tags::visit_item(self.cx, item);
+
+ self.visit_item_recur(item)
+ }
+}
diff --git a/src/librustdoc/passes/lint/bare_urls.rs b/src/librustdoc/passes/lint/bare_urls.rs
new file mode 100644
index 000000000..423230cfe
--- /dev/null
+++ b/src/librustdoc/passes/lint/bare_urls.rs
@@ -0,0 +1,89 @@
+//! Detects links that are not linkified, e.g., in Markdown such as `Go to https://example.com/.`
+//! Suggests wrapping the link with angle brackets: `Go to <https://example.com/>.` to linkify it.
+
+use crate::clean::*;
+use crate::core::DocContext;
+use crate::html::markdown::main_body_opts;
+use crate::passes::source_span_for_markdown_range;
+use core::ops::Range;
+use pulldown_cmark::{Event, Parser, Tag};
+use regex::Regex;
+use rustc_errors::Applicability;
+use std::mem;
+use std::sync::LazyLock;
+
+pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item) {
+ let Some(hir_id) = DocContext::as_local_hir_id(cx.tcx, item.item_id)
+ else {
+ // If non-local, no need to check anything.
+ return;
+ };
+ let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
+ if !dox.is_empty() {
+ let report_diag = |cx: &DocContext<'_>, msg: &str, url: &str, range: Range<usize>| {
+ let sp = source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs)
+ .unwrap_or_else(|| item.attr_span(cx.tcx));
+ cx.tcx.struct_span_lint_hir(crate::lint::BARE_URLS, hir_id, sp, msg, |lint| {
+ lint.note("bare URLs are not automatically turned into clickable links")
+ .span_suggestion(
+ sp,
+ "use an automatic link instead",
+ format!("<{}>", url),
+ Applicability::MachineApplicable,
+ )
+ });
+ };
+
+ let mut p = Parser::new_ext(&dox, main_body_opts()).into_offset_iter();
+
+ while let Some((event, range)) = p.next() {
+ match event {
+ Event::Text(s) => find_raw_urls(cx, &s, range, &report_diag),
+ // We don't want to check the text inside code blocks or links.
+ Event::Start(tag @ (Tag::CodeBlock(_) | Tag::Link(..))) => {
+ while let Some((event, _)) = p.next() {
+ match event {
+ Event::End(end)
+ if mem::discriminant(&end) == mem::discriminant(&tag) =>
+ {
+ break;
+ }
+ _ => {}
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+}
+
+static URL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
+ Regex::new(concat!(
+ r"https?://", // url scheme
+ r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", // one or more subdomains
+ r"[a-zA-Z]{2,63}", // root domain
+ r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)" // optional query or url fragments
+ ))
+ .expect("failed to build regex")
+});
+
+fn find_raw_urls(
+ cx: &DocContext<'_>,
+ text: &str,
+ range: Range<usize>,
+ f: &impl Fn(&DocContext<'_>, &str, &str, Range<usize>),
+) {
+ trace!("looking for raw urls in {}", text);
+ // For now, we only check "full" URLs (meaning, starting with "http://" or "https://").
+ for match_ in URL_REGEX.find_iter(text) {
+ let url = match_.as_str();
+ let url_range = match_.range();
+ f(
+ cx,
+ "this URL is not a hyperlink",
+ url,
+ Range { start: range.start + url_range.start, end: range.start + url_range.end },
+ );
+ }
+}
diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs
new file mode 100644
index 000000000..5aa4f238b
--- /dev/null
+++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs
@@ -0,0 +1,170 @@
+//! Validates syntax inside Rust code blocks (\`\`\`rust).
+use rustc_data_structures::sync::{Lock, Lrc};
+use rustc_errors::{
+ emitter::Emitter,
+ translation::{to_fluent_args, Translate},
+ Applicability, Diagnostic, Handler, LazyFallbackBundle,
+};
+use rustc_parse::parse_stream_from_source_str;
+use rustc_session::parse::ParseSess;
+use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId};
+use rustc_span::source_map::{FilePathMapping, SourceMap};
+use rustc_span::{FileName, InnerSpan, DUMMY_SP};
+
+use crate::clean;
+use crate::core::DocContext;
+use crate::html::markdown::{self, RustCodeBlock};
+use crate::passes::source_span_for_markdown_range;
+
+pub(crate) fn visit_item(cx: &DocContext<'_>, item: &clean::Item) {
+ if let Some(dox) = &item.attrs.collapsed_doc_value() {
+ let sp = item.attr_span(cx.tcx);
+ let extra =
+ crate::html::markdown::ExtraInfo::new_did(cx.tcx, item.item_id.expect_def_id(), sp);
+ for code_block in markdown::rust_code_blocks(dox, &extra) {
+ check_rust_syntax(cx, item, dox, code_block);
+ }
+ }
+}
+
+fn check_rust_syntax(
+ cx: &DocContext<'_>,
+ item: &clean::Item,
+ dox: &str,
+ code_block: RustCodeBlock,
+) {
+ let buffer = Lrc::new(Lock::new(Buffer::default()));
+ let fallback_bundle =
+ rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+ let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle };
+
+ let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+ let handler = Handler::with_emitter(false, None, Box::new(emitter));
+ let source = dox[code_block.code].to_owned();
+ let sess = ParseSess::with_span_handler(handler, sm);
+
+ let edition = code_block.lang_string.edition.unwrap_or_else(|| cx.tcx.sess.edition());
+ let expn_data =
+ ExpnData::default(ExpnKind::AstPass(AstPass::TestHarness), DUMMY_SP, edition, None, None);
+ let expn_id = cx.tcx.with_stable_hashing_context(|hcx| LocalExpnId::fresh(expn_data, hcx));
+ let span = DUMMY_SP.fresh_expansion(expn_id);
+
+ let is_empty = rustc_driver::catch_fatal_errors(|| {
+ parse_stream_from_source_str(
+ FileName::Custom(String::from("doctest")),
+ source,
+ &sess,
+ Some(span),
+ )
+ .is_empty()
+ })
+ .unwrap_or(false);
+ let buffer = buffer.borrow();
+
+ if !buffer.has_errors && !is_empty {
+ // No errors in a non-empty program.
+ return;
+ }
+
+ let Some(local_id) = item.item_id.as_def_id().and_then(|x| x.as_local())
+ else {
+ // We don't need to check the syntax for other crates so returning
+ // without doing anything should not be a problem.
+ return;
+ };
+
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
+ let empty_block = code_block.lang_string == Default::default() && code_block.is_fenced;
+ let is_ignore = code_block.lang_string.ignore != markdown::Ignore::None;
+
+ // The span and whether it is precise or not.
+ let (sp, precise_span) =
+ match source_span_for_markdown_range(cx.tcx, dox, &code_block.range, &item.attrs) {
+ Some(sp) => (sp, true),
+ None => (item.attr_span(cx.tcx), false),
+ };
+
+ let msg = if buffer.has_errors {
+ "could not parse code block as Rust code"
+ } else {
+ "Rust code block is empty"
+ };
+
+ // Finally build and emit the completed diagnostic.
+ // All points of divergence have been handled earlier so this can be
+ // done the same way whether the span is precise or not.
+ cx.tcx.struct_span_lint_hir(crate::lint::INVALID_RUST_CODEBLOCKS, hir_id, sp, msg, |lint| {
+ let explanation = if is_ignore {
+ "`ignore` code blocks require valid Rust code for syntax highlighting; \
+ mark blocks that do not contain Rust code as text"
+ } else {
+ "mark blocks that do not contain Rust code as text"
+ };
+
+ if precise_span {
+ if is_ignore {
+ // giving an accurate suggestion is hard because `ignore` might not have come first in the list.
+ // just give a `help` instead.
+ lint.span_help(
+ sp.from_inner(InnerSpan::new(0, 3)),
+ &format!("{}: ```text", explanation),
+ );
+ } else if empty_block {
+ lint.span_suggestion(
+ sp.from_inner(InnerSpan::new(0, 3)).shrink_to_hi(),
+ explanation,
+ "text",
+ Applicability::MachineApplicable,
+ );
+ }
+ } else if empty_block || is_ignore {
+ lint.help(&format!("{}: ```text", explanation));
+ }
+
+ // FIXME(#67563): Provide more context for these errors by displaying the spans inline.
+ for message in buffer.messages.iter() {
+ lint.note(message);
+ }
+
+ lint
+ });
+}
+
+#[derive(Default)]
+struct Buffer {
+ messages: Vec<String>,
+ has_errors: bool,
+}
+
+struct BufferEmitter {
+ buffer: Lrc<Lock<Buffer>>,
+ fallback_bundle: LazyFallbackBundle,
+}
+
+impl Translate for BufferEmitter {
+ fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
+ None
+ }
+
+ fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
+ &**self.fallback_bundle
+ }
+}
+
+impl Emitter for BufferEmitter {
+ fn emit_diagnostic(&mut self, diag: &Diagnostic) {
+ let mut buffer = self.buffer.borrow_mut();
+
+ let fluent_args = to_fluent_args(diag.args());
+ let translated_main_message = self.translate_message(&diag.message[0].0, &fluent_args);
+
+ buffer.messages.push(format!("error from rustc: {}", translated_main_message));
+ if diag.is_error() {
+ buffer.has_errors = true;
+ }
+ }
+
+ fn source_map(&self) -> Option<&Lrc<SourceMap>> {
+ None
+ }
+}
diff --git a/src/librustdoc/passes/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs
index a89ed7c7e..070c0aab5 100644
--- a/src/librustdoc/passes/html_tags.rs
+++ b/src/librustdoc/passes/lint/html_tags.rs
@@ -1,9 +1,8 @@
//! Detects invalid HTML (like an unclosed `<span>`) in doc comments.
-use super::Pass;
use crate::clean::*;
use crate::core::DocContext;
use crate::html::markdown::main_body_opts;
-use crate::visit::DocVisitor;
+use crate::passes::source_span_for_markdown_range;
use pulldown_cmark::{BrokenLink, Event, LinkType, Parser, Tag};
@@ -11,20 +10,150 @@ use std::iter::Peekable;
use std::ops::Range;
use std::str::CharIndices;
-pub(crate) const CHECK_INVALID_HTML_TAGS: Pass = Pass {
- name: "check-invalid-html-tags",
- run: check_invalid_html_tags,
- description: "detects invalid HTML tags in doc comments",
-};
+pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
+ let tcx = cx.tcx;
+ let Some(hir_id) = DocContext::as_local_hir_id(tcx, item.item_id)
+ // If non-local, no need to check anything.
+ else { return };
+ let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
+ if !dox.is_empty() {
+ let report_diag = |msg: &str, range: &Range<usize>, is_open_tag: bool| {
+ let sp = match source_span_for_markdown_range(tcx, &dox, range, &item.attrs) {
+ Some(sp) => sp,
+ None => item.attr_span(tcx),
+ };
+ 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'>'
+ {
+ 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;
+ }
+ }
+ 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,
+ ) {
+ 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,
+ );
+ }
-struct InvalidHtmlTagsLinter<'a, 'tcx> {
- cx: &'a mut DocContext<'tcx>,
-}
+ lint
+ });
+ };
+
+ let mut tags = Vec::new();
+ let mut is_in_comment = None;
+ let mut in_code_block = false;
+
+ let link_names = item.link_names(&cx.cache);
-pub(crate) fn check_invalid_html_tags(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
- let mut coll = InvalidHtmlTagsLinter { cx };
- coll.visit_crate(&krate);
- krate
+ let mut replacer = |broken_link: BrokenLink<'_>| {
+ if let Some(link) =
+ link_names.iter().find(|link| *link.original_text == *broken_link.reference)
+ {
+ Some((link.href.as_str().into(), link.new_text.as_str().into()))
+ } else if matches!(
+ &broken_link.link_type,
+ LinkType::Reference | LinkType::ReferenceUnknown
+ ) {
+ // If the link is shaped [like][this], suppress any broken HTML in the [this] part.
+ // The `broken_intra_doc_links` will report typos in there anyway.
+ Some((
+ broken_link.reference.to_string().into(),
+ broken_link.reference.to_string().into(),
+ ))
+ } else {
+ None
+ }
+ };
+
+ let p = Parser::new_with_broken_link_callback(&dox, main_body_opts(), Some(&mut replacer))
+ .into_offset_iter();
+
+ for (event, range) in p {
+ match event {
+ Event::Start(Tag::CodeBlock(_)) => in_code_block = true,
+ Event::Html(text) if !in_code_block => {
+ extract_tags(&mut tags, &text, range, &mut is_in_comment, &report_diag)
+ }
+ Event::End(Tag::CodeBlock(_)) => in_code_block = false,
+ _ => {}
+ }
+ }
+
+ for (tag, range) in tags.iter().filter(|(t, _)| {
+ let t = t.to_lowercase();
+ !ALLOWED_UNCLOSED.contains(&t.as_str())
+ }) {
+ report_diag(&format!("unclosed HTML tag `{}`", tag), range, true);
+ }
+
+ if let Some(range) = is_in_comment {
+ report_diag("Unclosed HTML comment", &range, false);
+ }
+ }
}
const ALLOWED_UNCLOSED: &[&str] = &[
@@ -276,155 +405,3 @@ fn extract_tags(
}
}
}
-
-impl<'a, 'tcx> DocVisitor for InvalidHtmlTagsLinter<'a, 'tcx> {
- fn visit_item(&mut self, item: &Item) {
- let tcx = self.cx.tcx;
- let Some(hir_id) = DocContext::as_local_hir_id(tcx, item.item_id)
- // If non-local, no need to check anything.
- else { return };
- let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
- if !dox.is_empty() {
- let report_diag = |msg: &str, range: &Range<usize>, is_open_tag: bool| {
- let sp = match super::source_span_for_markdown_range(tcx, &dox, range, &item.attrs)
- {
- Some(sp) => sp,
- None => item.attr_span(tcx),
- };
- 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'>'
- {
- 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;
- }
- }
- if let Some(new_end) = extract_path_forward(&dox, generics_end) {
- generics_end = new_end;
- }
- let generics_sp = match super::source_span_for_markdown_range(
- tcx,
- &dox,
- &(generics_start..generics_end),
- &item.attrs,
- ) {
- 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
- });
- };
-
- let mut tags = Vec::new();
- let mut is_in_comment = None;
- let mut in_code_block = false;
-
- let link_names = item.link_names(&self.cx.cache);
-
- let mut replacer = |broken_link: BrokenLink<'_>| {
- if let Some(link) =
- link_names.iter().find(|link| *link.original_text == *broken_link.reference)
- {
- Some((link.href.as_str().into(), link.new_text.as_str().into()))
- } else if matches!(
- &broken_link.link_type,
- LinkType::Reference | LinkType::ReferenceUnknown
- ) {
- // If the link is shaped [like][this], suppress any broken HTML in the [this] part.
- // The `broken_intra_doc_links` will report typos in there anyway.
- Some((
- broken_link.reference.to_string().into(),
- broken_link.reference.to_string().into(),
- ))
- } else {
- None
- }
- };
-
- let p =
- Parser::new_with_broken_link_callback(&dox, main_body_opts(), Some(&mut replacer))
- .into_offset_iter();
-
- for (event, range) in p {
- match event {
- Event::Start(Tag::CodeBlock(_)) => in_code_block = true,
- Event::Html(text) if !in_code_block => {
- extract_tags(&mut tags, &text, range, &mut is_in_comment, &report_diag)
- }
- Event::End(Tag::CodeBlock(_)) => in_code_block = false,
- _ => {}
- }
- }
-
- for (tag, range) in tags.iter().filter(|(t, _)| {
- let t = t.to_lowercase();
- !ALLOWED_UNCLOSED.contains(&t.as_str())
- }) {
- report_diag(&format!("unclosed HTML tag `{}`", tag), range, true);
- }
-
- if let Some(range) = is_in_comment {
- report_diag("Unclosed HTML comment", &range, false);
- }
- }
-
- self.visit_item_recur(item)
- }
-}
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
index f81b38ea3..634e70ec9 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -12,9 +12,6 @@ use crate::core::DocContext;
mod stripper;
pub(crate) use stripper::*;
-mod bare_urls;
-pub(crate) use self::bare_urls::CHECK_BARE_URLS;
-
mod strip_hidden;
pub(crate) use self::strip_hidden::STRIP_HIDDEN;
@@ -36,14 +33,11 @@ pub(crate) use self::check_doc_test_visibility::CHECK_DOC_TEST_VISIBILITY;
mod collect_trait_impls;
pub(crate) use self::collect_trait_impls::COLLECT_TRAIT_IMPLS;
-mod check_code_block_syntax;
-pub(crate) use self::check_code_block_syntax::CHECK_CODE_BLOCK_SYNTAX;
-
mod calculate_doc_coverage;
pub(crate) use self::calculate_doc_coverage::CALCULATE_DOC_COVERAGE;
-mod html_tags;
-pub(crate) use self::html_tags::CHECK_INVALID_HTML_TAGS;
+mod lint;
+pub(crate) use self::lint::RUN_LINTS;
/// A single pass over the cleaned documentation.
///
@@ -82,11 +76,9 @@ pub(crate) const PASSES: &[Pass] = &[
STRIP_PRIV_IMPORTS,
PROPAGATE_DOC_CFG,
COLLECT_INTRA_DOC_LINKS,
- CHECK_CODE_BLOCK_SYNTAX,
COLLECT_TRAIT_IMPLS,
CALCULATE_DOC_COVERAGE,
- CHECK_INVALID_HTML_TAGS,
- CHECK_BARE_URLS,
+ RUN_LINTS,
];
/// The list of passes run by default.
@@ -97,10 +89,8 @@ pub(crate) const DEFAULT_PASSES: &[ConditionalPass] = &[
ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate),
ConditionalPass::new(STRIP_PRIV_IMPORTS, WhenDocumentPrivate),
ConditionalPass::always(COLLECT_INTRA_DOC_LINKS),
- ConditionalPass::always(CHECK_CODE_BLOCK_SYNTAX),
- ConditionalPass::always(CHECK_INVALID_HTML_TAGS),
ConditionalPass::always(PROPAGATE_DOC_CFG),
- ConditionalPass::always(CHECK_BARE_URLS),
+ ConditionalPass::always(RUN_LINTS),
];
/// The list of default passes run when `--doc-coverage` is passed to rustdoc.
diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
index 9914edf30..e07a788a7 100644
--- a/src/librustdoc/passes/strip_hidden.rs
+++ b/src/librustdoc/passes/strip_hidden.rs
@@ -27,6 +27,7 @@ pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clea
// strip all impls referencing stripped items
let mut stripper = ImplStripper {
+ tcx: cx.tcx,
retained: &retained,
cache: &cx.cache,
is_json_output,
diff --git a/src/librustdoc/passes/strip_priv_imports.rs b/src/librustdoc/passes/strip_priv_imports.rs
index 85be8fa10..3bac5a8e5 100644
--- a/src/librustdoc/passes/strip_priv_imports.rs
+++ b/src/librustdoc/passes/strip_priv_imports.rs
@@ -11,6 +11,6 @@ pub(crate) const STRIP_PRIV_IMPORTS: Pass = Pass {
description: "strips all private import statements (`use`, `extern crate`) from a crate",
};
-pub(crate) fn strip_priv_imports(krate: clean::Crate, _: &mut DocContext<'_>) -> clean::Crate {
- ImportStripper.fold_crate(krate)
+pub(crate) fn strip_priv_imports(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
+ ImportStripper { tcx: cx.tcx }.fold_crate(krate)
}
diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs
index 450f69e15..8fc42462d 100644
--- a/src/librustdoc/passes/strip_private.rs
+++ b/src/librustdoc/passes/strip_private.rs
@@ -26,12 +26,14 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) ->
effective_visibilities: &cx.cache.effective_visibilities,
update_retained: true,
is_json_output,
+ tcx: cx.tcx,
};
- krate = ImportStripper.fold_crate(stripper.fold_crate(krate));
+ krate = ImportStripper { tcx: cx.tcx }.fold_crate(stripper.fold_crate(krate));
}
// strip all impls referencing private items
let mut stripper = ImplStripper {
+ tcx: cx.tcx,
retained: &retained,
cache: &cx.cache,
is_json_output,
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index 0089ce63d..995fb5dcc 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -1,19 +1,20 @@
//! A collection of utility functions for the `strip_*` passes.
use rustc_hir::def_id::DefId;
-use rustc_middle::middle::privacy::EffectiveVisibilities;
+use rustc_middle::ty::{TyCtxt, Visibility};
use rustc_span::symbol::sym;
-
use std::mem;
use crate::clean::{self, Item, ItemId, ItemIdSet, NestedAttributesExt};
use crate::fold::{strip_item, DocFolder};
use crate::formats::cache::Cache;
+use crate::visit_lib::RustdocEffectiveVisibilities;
-pub(crate) struct Stripper<'a> {
+pub(crate) struct Stripper<'a, 'tcx> {
pub(crate) retained: &'a mut ItemIdSet,
- pub(crate) effective_visibilities: &'a EffectiveVisibilities<DefId>,
+ pub(crate) effective_visibilities: &'a RustdocEffectiveVisibilities,
pub(crate) update_retained: bool,
pub(crate) is_json_output: bool,
+ pub(crate) tcx: TyCtxt<'tcx>,
}
// We need to handle this differently for the JSON output because some non exported items could
@@ -21,18 +22,19 @@ pub(crate) struct Stripper<'a> {
// are in the public API, which is not enough.
#[inline]
fn is_item_reachable(
+ tcx: TyCtxt<'_>,
is_json_output: bool,
- effective_visibilities: &EffectiveVisibilities<DefId>,
+ effective_visibilities: &RustdocEffectiveVisibilities,
item_id: ItemId,
) -> bool {
if is_json_output {
- effective_visibilities.is_reachable(item_id.expect_def_id())
+ effective_visibilities.is_reachable(tcx, item_id.expect_def_id())
} else {
- effective_visibilities.is_exported(item_id.expect_def_id())
+ effective_visibilities.is_exported(tcx, item_id.expect_def_id())
}
}
-impl<'a> DocFolder for Stripper<'a> {
+impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
match *i.kind {
clean::StrippedItem(..) => {
@@ -66,7 +68,12 @@ impl<'a> DocFolder for Stripper<'a> {
| clean::ForeignTypeItem => {
let item_id = i.item_id;
if item_id.is_local()
- && !is_item_reachable(self.is_json_output, self.effective_visibilities, item_id)
+ && !is_item_reachable(
+ self.tcx,
+ self.is_json_output,
+ self.effective_visibilities,
+ item_id,
+ )
{
debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
return None;
@@ -74,13 +81,13 @@ impl<'a> DocFolder for Stripper<'a> {
}
clean::StructFieldItem(..) => {
- if !i.visibility.is_public() {
+ if i.visibility(self.tcx) != Some(Visibility::Public) {
return Some(strip_item(i));
}
}
clean::ModuleItem(..) => {
- if i.item_id.is_local() && !i.visibility.is_public() {
+ if i.item_id.is_local() && i.visibility(self.tcx) != Some(Visibility::Public) {
debug!("Stripper: stripping module {:?}", i.name);
let old = mem::replace(&mut self.update_retained, false);
let ret = strip_item(self.fold_item_recur(i));
@@ -146,14 +153,15 @@ impl<'a> DocFolder for Stripper<'a> {
}
/// This stripper discards all impls which reference stripped items
-pub(crate) struct ImplStripper<'a> {
+pub(crate) struct ImplStripper<'a, 'tcx> {
+ pub(crate) tcx: TyCtxt<'tcx>,
pub(crate) retained: &'a ItemIdSet,
pub(crate) cache: &'a Cache,
pub(crate) is_json_output: bool,
pub(crate) document_private: bool,
}
-impl<'a> ImplStripper<'a> {
+impl<'a> ImplStripper<'a, '_> {
#[inline]
fn should_keep_impl(&self, item: &Item, for_def_id: DefId) -> bool {
if !for_def_id.is_local() || self.retained.contains(&for_def_id.into()) {
@@ -161,7 +169,7 @@ impl<'a> ImplStripper<'a> {
} else if self.is_json_output {
// If the "for" item is exported and the impl block isn't `#[doc(hidden)]`, then we
// need to keep it.
- self.cache.effective_visibilities.is_exported(for_def_id)
+ self.cache.effective_visibilities.is_exported(self.tcx, for_def_id)
&& !item.attrs.lists(sym::doc).has_word(sym::hidden)
} else {
false
@@ -169,7 +177,7 @@ impl<'a> ImplStripper<'a> {
}
}
-impl<'a> DocFolder for ImplStripper<'a> {
+impl<'a> DocFolder for ImplStripper<'a, '_> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
if let clean::ImplItem(ref imp) = *i.kind {
// Impl blocks can be skipped if they are: empty; not a trait impl; and have no
@@ -185,6 +193,7 @@ impl<'a> DocFolder for ImplStripper<'a> {
let item_id = i.item_id;
item_id.is_local()
&& !is_item_reachable(
+ self.tcx,
self.is_json_output,
&self.cache.effective_visibilities,
item_id,
@@ -229,12 +238,16 @@ impl<'a> DocFolder for ImplStripper<'a> {
}
/// This stripper discards all private import statements (`use`, `extern crate`)
-pub(crate) struct ImportStripper;
+pub(crate) struct ImportStripper<'tcx> {
+ pub(crate) tcx: TyCtxt<'tcx>,
+}
-impl DocFolder for ImportStripper {
+impl<'tcx> DocFolder for ImportStripper<'tcx> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
match *i.kind {
- clean::ExternCrateItem { .. } | clean::ImportItem(..) if !i.visibility.is_public() => {
+ clean::ExternCrateItem { .. } | clean::ImportItem(..)
+ if i.visibility(self.tcx) != Some(Visibility::Public) =>
+ {
None
}
_ => Some(self.fold_item_recur(i)),
diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs
index dfa6ba38b..f2ee99cd9 100644
--- a/src/librustdoc/scrape_examples.rs
+++ b/src/librustdoc/scrape_examples.rs
@@ -110,6 +110,7 @@ pub(crate) struct CallData {
pub(crate) url: String,
pub(crate) display_name: String,
pub(crate) edition: Edition,
+ pub(crate) is_bin: bool,
}
pub(crate) type FnCallLocations = FxHashMap<PathBuf, CallData>;
@@ -122,6 +123,7 @@ struct FindCalls<'a, 'tcx> {
cx: Context<'tcx>,
target_crates: Vec<CrateNum>,
calls: &'a mut AllCallLocations,
+ bin_crate: bool,
}
impl<'a, 'tcx> Visitor<'tcx> for FindCalls<'a, 'tcx>
@@ -245,7 +247,9 @@ where
let mk_call_data = || {
let display_name = file_path.display().to_string();
let edition = call_span.edition();
- CallData { locations: Vec::new(), url, display_name, edition }
+ let is_bin = self.bin_crate;
+
+ CallData { locations: Vec::new(), url, display_name, edition, is_bin }
};
let fn_key = tcx.def_path_hash(*def_id);
@@ -274,6 +278,7 @@ pub(crate) fn run(
cache: formats::cache::Cache,
tcx: TyCtxt<'_>,
options: ScrapeExamplesOptions,
+ bin_crate: bool,
) -> interface::Result<()> {
let inner = move || -> Result<(), String> {
// Generates source files for examples
@@ -300,7 +305,8 @@ pub(crate) fn run(
// Run call-finder on all items
let mut calls = FxHashMap::default();
- let mut finder = FindCalls { calls: &mut calls, tcx, map: tcx.hir(), cx, target_crates };
+ let mut finder =
+ FindCalls { calls: &mut calls, tcx, map: tcx.hir(), cx, target_crates, bin_crate };
tcx.hir().visit_all_item_likes_in_crate(&mut finder);
// The visitor might have found a type error, which we need to
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 06dffce55..22068ebe0 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -7,15 +7,14 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::Node;
use rustc_hir::CRATE_HIR_ID;
-use rustc_middle::middle::privacy::Level;
-use rustc_middle::ty::{TyCtxt, Visibility};
+use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::Span;
use std::mem;
-use crate::clean::{self, cfg::Cfg, AttributesExt, NestedAttributesExt};
+use crate::clean::{cfg::Cfg, AttributesExt, NestedAttributesExt};
use crate::core;
/// This module is used to store stuff from Rust's AST in a more convenient
@@ -26,8 +25,8 @@ pub(crate) struct Module<'hir> {
pub(crate) where_inner: Span,
pub(crate) mods: Vec<Module<'hir>>,
pub(crate) id: hir::HirId,
- // (item, renamed)
- pub(crate) items: Vec<(&'hir hir::Item<'hir>, Option<Symbol>)>,
+ // (item, renamed, import_id)
+ pub(crate) items: Vec<(&'hir hir::Item<'hir>, Option<Symbol>, Option<hir::HirId>)>,
pub(crate) foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option<Symbol>)>,
}
@@ -94,6 +93,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
hir::CRATE_HIR_ID,
self.cx.tcx.hir().root_module(),
self.cx.tcx.crate_name(LOCAL_CRATE),
+ None,
);
// `#[macro_export] macro_rules!` items are reexported at the top level of the
@@ -114,7 +114,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
if self.cx.tcx.has_attr(def_id, sym::macro_export) {
if inserted.insert(def_id) {
let item = self.cx.tcx.hir().expect_item(local_def_id);
- top_level_module.items.push((item, None));
+ top_level_module.items.push((item, None, None));
}
}
}
@@ -156,6 +156,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
id: hir::HirId,
m: &'tcx hir::Mod<'tcx>,
name: Symbol,
+ parent_id: Option<hir::HirId>,
) -> Module<'tcx> {
let mut om = Module::new(name, id, m.spans.inner_span);
let def_id = self.cx.tcx.hir().local_def_id(id).to_def_id();
@@ -167,7 +168,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
continue;
}
- self.visit_item(item, None, &mut om);
+ self.visit_item(item, None, &mut om, parent_id);
}
for &i in m.item_ids {
let item = self.cx.tcx.hir().item(i);
@@ -175,7 +176,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
// Later passes in rustdoc will de-duplicate by name and kind, so if glob-
// imported items appear last, then they'll be the ones that get discarded.
if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
- self.visit_item(item, None, &mut om);
+ self.visit_item(item, None, &mut om, parent_id);
}
}
self.inside_public_path = orig_inside_public_path;
@@ -221,23 +222,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
// made reachable by cross-crate inlining which we're checking here.
// (this is done here because we need to know this upfront).
if !res_did.is_local() && !is_no_inline {
- let attrs = clean::inline::load_attrs(self.cx, res_did);
- let self_is_hidden = attrs.lists(sym::doc).has_word(sym::hidden);
- if !self_is_hidden {
- if let Res::Def(kind, did) = res {
- if kind == DefKind::Mod {
- crate::visit_lib::LibEmbargoVisitor::new(self.cx).visit_mod(did)
- } else {
- // All items need to be handled here in case someone wishes to link
- // to them with intra-doc links
- self.cx.cache.effective_visibilities.set_public_at_level(
- did,
- || Visibility::Restricted(CRATE_DEF_ID),
- Level::Direct,
- );
- }
- }
- }
+ crate::visit_lib::lib_embargo_visit_item(self.cx, res_did);
return false;
}
@@ -246,7 +231,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
None => return false,
};
- let is_private = !self.cx.cache.effective_visibilities.is_directly_public(res_did);
+ let is_private =
+ !self.cx.cache.effective_visibilities.is_directly_public(self.cx.tcx, res_did);
let is_hidden = inherits_doc_hidden(self.cx.tcx, res_hir_id);
// Only inline if requested or if the item would otherwise be stripped.
@@ -263,14 +249,14 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
let prev = mem::replace(&mut self.inlining, true);
for &i in m.item_ids {
let i = self.cx.tcx.hir().item(i);
- self.visit_item(i, None, om);
+ self.visit_item(i, None, om, Some(id));
}
self.inlining = prev;
true
}
Node::Item(it) if !glob => {
let prev = mem::replace(&mut self.inlining, true);
- self.visit_item(it, renamed, om);
+ self.visit_item(it, renamed, om, Some(id));
self.inlining = prev;
true
}
@@ -291,6 +277,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
item: &'tcx hir::Item<'_>,
renamed: Option<Symbol>,
om: &mut Module<'tcx>,
+ parent_id: Option<hir::HirId>,
) {
debug!("visiting item {:?}", item);
let name = renamed.unwrap_or(item.ident.name);
@@ -314,39 +301,40 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
hir::ItemKind::GlobalAsm(..) => {}
hir::ItemKind::Use(_, hir::UseKind::ListStem) => {}
hir::ItemKind::Use(path, kind) => {
- let is_glob = kind == hir::UseKind::Glob;
-
- // Struct and variant constructors and proc macro stubs always show up alongside
- // their definitions, we've already processed them so just discard these.
- if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = path.res {
- return;
- }
-
- let attrs = self.cx.tcx.hir().attrs(item.hir_id());
+ for &res in &path.res {
+ // Struct and variant constructors and proc macro stubs always show up alongside
+ // their definitions, we've already processed them so just discard these.
+ if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res {
+ continue;
+ }
- // If there was a private module in the current path then don't bother inlining
- // anything as it will probably be stripped anyway.
- if is_pub && self.inside_public_path {
- let please_inline = attrs.iter().any(|item| match item.meta_item_list() {
- Some(ref list) if item.has_name(sym::doc) => {
- list.iter().any(|i| i.has_name(sym::inline))
+ let attrs = self.cx.tcx.hir().attrs(item.hir_id());
+
+ // If there was a private module in the current path then don't bother inlining
+ // anything as it will probably be stripped anyway.
+ if is_pub && self.inside_public_path {
+ let please_inline = attrs.iter().any(|item| match item.meta_item_list() {
+ Some(ref list) if item.has_name(sym::doc) => {
+ list.iter().any(|i| i.has_name(sym::inline))
+ }
+ _ => false,
+ });
+ let is_glob = kind == hir::UseKind::Glob;
+ let ident = if is_glob { None } else { Some(name) };
+ if self.maybe_inline_local(
+ item.hir_id(),
+ res,
+ ident,
+ is_glob,
+ om,
+ please_inline,
+ ) {
+ continue;
}
- _ => false,
- });
- let ident = if is_glob { None } else { Some(name) };
- if self.maybe_inline_local(
- item.hir_id(),
- path.res,
- ident,
- is_glob,
- om,
- please_inline,
- ) {
- return;
}
- }
- om.items.push((item, renamed))
+ om.items.push((item, renamed, parent_id))
+ }
}
hir::ItemKind::Macro(ref macro_def, _) => {
// `#[macro_export] macro_rules!` items are handled separately in `visit()`,
@@ -365,11 +353,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
let nonexported = !self.cx.tcx.has_attr(def_id, sym::macro_export);
if is_macro_2_0 || nonexported || self.inlining {
- om.items.push((item, renamed));
+ om.items.push((item, renamed, None));
}
}
hir::ItemKind::Mod(ref m) => {
- om.mods.push(self.visit_mod_contents(item.hir_id(), m, name));
+ om.mods.push(self.visit_mod_contents(item.hir_id(), m, name, parent_id));
}
hir::ItemKind::Fn(..)
| hir::ItemKind::ExternCrate(..)
@@ -380,19 +368,19 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
| hir::ItemKind::OpaqueTy(..)
| hir::ItemKind::Static(..)
| hir::ItemKind::Trait(..)
- | hir::ItemKind::TraitAlias(..) => om.items.push((item, renamed)),
+ | hir::ItemKind::TraitAlias(..) => om.items.push((item, renamed, parent_id)),
hir::ItemKind::Const(..) => {
// Underscore constants do not correspond to a nameable item and
// so are never useful in documentation.
if name != kw::Underscore {
- om.items.push((item, renamed));
+ om.items.push((item, renamed, parent_id));
}
}
hir::ItemKind::Impl(impl_) => {
// Don't duplicate impls when inlining or if it's implementing a trait, we'll pick
// them up regardless of where they're located.
if !self.inlining && impl_.of_trait.is_none() {
- om.items.push((item, None));
+ om.items.push((item, None, None));
}
}
}
diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs
index 70214e2ad..e490559b0 100644
--- a/src/librustdoc/visit_lib.rs
+++ b/src/librustdoc/visit_lib.rs
@@ -1,86 +1,74 @@
+use crate::core::DocContext;
use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_ID};
-use rustc_middle::middle::privacy::{EffectiveVisibilities, Level};
-use rustc_middle::ty::{TyCtxt, Visibility};
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty::TyCtxt;
// FIXME: this may not be exhaustive, but is sufficient for rustdocs current uses
+#[derive(Default)]
+pub(crate) struct RustdocEffectiveVisibilities {
+ extern_public: FxHashSet<DefId>,
+}
+
+macro_rules! define_method {
+ ($method:ident) => {
+ pub(crate) fn $method(&self, tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+ match def_id.as_local() {
+ Some(def_id) => tcx.effective_visibilities(()).$method(def_id),
+ None => self.extern_public.contains(&def_id),
+ }
+ }
+ };
+}
+
+impl RustdocEffectiveVisibilities {
+ define_method!(is_directly_public);
+ define_method!(is_exported);
+ define_method!(is_reachable);
+}
+
+pub(crate) fn lib_embargo_visit_item(cx: &mut DocContext<'_>, def_id: DefId) {
+ assert!(!def_id.is_local());
+ LibEmbargoVisitor {
+ tcx: cx.tcx,
+ extern_public: &mut cx.cache.effective_visibilities.extern_public,
+ visited_mods: Default::default(),
+ }
+ .visit_item(def_id)
+}
+
/// Similar to `librustc_privacy::EmbargoVisitor`, but also takes
/// specific rustdoc annotations into account (i.e., `doc(hidden)`)
-pub(crate) struct LibEmbargoVisitor<'a, 'tcx> {
+struct LibEmbargoVisitor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
// Effective visibilities for reachable nodes
- effective_visibilities: &'a mut EffectiveVisibilities<DefId>,
- // Previous level, None means unreachable
- prev_level: Option<Level>,
+ extern_public: &'a mut FxHashSet<DefId>,
// Keeps track of already visited modules, in case a module re-exports its parent
visited_mods: FxHashSet<DefId>,
}
-impl<'a, 'tcx> LibEmbargoVisitor<'a, 'tcx> {
- pub(crate) fn new(cx: &'a mut crate::core::DocContext<'tcx>) -> LibEmbargoVisitor<'a, 'tcx> {
- LibEmbargoVisitor {
- tcx: cx.tcx,
- effective_visibilities: &mut cx.cache.effective_visibilities,
- prev_level: Some(Level::Direct),
- visited_mods: FxHashSet::default(),
- }
- }
-
- pub(crate) fn visit_lib(&mut self, cnum: CrateNum) {
- let did = cnum.as_def_id();
- self.update(did, Some(Level::Direct));
- self.visit_mod(did);
- }
-
- // Updates node level and returns the updated level
- fn update(&mut self, did: DefId, level: Option<Level>) -> Option<Level> {
- let is_hidden = self.tcx.is_doc_hidden(did);
-
- let old_level = self.effective_visibilities.public_at_level(did);
- // Visibility levels can only grow
- if level > old_level && !is_hidden {
- self.effective_visibilities.set_public_at_level(
- did,
- || Visibility::Restricted(CRATE_DEF_ID),
- level.unwrap(),
- );
- level
- } else {
- old_level
- }
- }
-
- pub(crate) fn visit_mod(&mut self, def_id: DefId) {
+impl LibEmbargoVisitor<'_, '_> {
+ fn visit_mod(&mut self, def_id: DefId) {
if !self.visited_mods.insert(def_id) {
return;
}
for item in self.tcx.module_children(def_id).iter() {
if let Some(def_id) = item.res.opt_def_id() {
- if self.tcx.def_key(def_id).parent.map_or(false, |d| d == def_id.index)
- || item.vis.is_public()
- {
- self.visit_item(item.res);
+ if item.vis.is_public() {
+ self.visit_item(def_id);
}
}
}
}
- fn visit_item(&mut self, res: Res<!>) {
- let def_id = res.def_id();
- let vis = self.tcx.visibility(def_id);
- let inherited_item_level = if vis.is_public() { self.prev_level } else { None };
-
- let item_level = self.update(def_id, inherited_item_level);
-
- if let Res::Def(DefKind::Mod, _) = res {
- let orig_level = self.prev_level;
-
- self.prev_level = item_level;
- self.visit_mod(def_id);
- self.prev_level = orig_level;
+ fn visit_item(&mut self, def_id: DefId) {
+ if !self.tcx.is_doc_hidden(def_id) {
+ self.extern_public.insert(def_id);
+ if self.tcx.def_kind(def_id) == DefKind::Mod {
+ self.visit_mod(def_id);
+ }
}
}
}