summaryrefslogtreecommitdiffstats
path: root/src/librustdoc
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc')
-rw-r--r--src/librustdoc/Cargo.toml13
-rw-r--r--src/librustdoc/clean/auto_trait.rs20
-rw-r--r--src/librustdoc/clean/blanket_impl.rs4
-rw-r--r--src/librustdoc/clean/inline.rs55
-rw-r--r--src/librustdoc/clean/mod.rs1248
-rw-r--r--src/librustdoc/clean/types.rs142
-rw-r--r--src/librustdoc/clean/utils.rs83
-rw-r--r--src/librustdoc/config.rs16
-rw-r--r--src/librustdoc/doctest.rs9
-rw-r--r--src/librustdoc/fold.rs2
-rw-r--r--src/librustdoc/formats/cache.rs2
-rw-r--r--src/librustdoc/formats/item_type.rs1
-rw-r--r--src/librustdoc/html/format.rs45
-rw-r--r--src/librustdoc/html/highlight.rs355
-rw-r--r--src/librustdoc/html/highlight/fixtures/decorations.html6
-rw-r--r--src/librustdoc/html/highlight/fixtures/dos_line.html2
-rw-r--r--src/librustdoc/html/highlight/fixtures/highlight.html8
-rw-r--r--src/librustdoc/html/highlight/fixtures/sample.html39
-rw-r--r--src/librustdoc/html/highlight/fixtures/sample.rs1
-rw-r--r--src/librustdoc/html/highlight/fixtures/union.html10
-rw-r--r--src/librustdoc/html/highlight/tests.rs18
-rw-r--r--src/librustdoc/html/markdown.rs32
-rw-r--r--src/librustdoc/html/render/context.rs37
-rw-r--r--src/librustdoc/html/render/mod.rs399
-rw-r--r--src/librustdoc/html/render/print_item.rs95
-rw-r--r--src/librustdoc/html/render/search_index.rs13
-rw-r--r--src/librustdoc/html/render/span_map.rs34
-rw-r--r--src/librustdoc/html/render/write_shared.rs79
-rw-r--r--src/librustdoc/html/sources.rs22
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css597
-rw-r--r--src/librustdoc/html/static/css/themes/ayu.css279
-rw-r--r--src/librustdoc/html/static/css/themes/dark.css216
-rw-r--r--src/librustdoc/html/static/css/themes/light.css214
-rw-r--r--src/librustdoc/html/static/images/down-arrow.svg2
-rw-r--r--src/librustdoc/html/static/js/main.js89
-rw-r--r--src/librustdoc/html/static/js/search.js31
-rw-r--r--src/librustdoc/html/static/js/source-script.js20
-rw-r--r--src/librustdoc/html/templates/page.html8
-rw-r--r--src/librustdoc/json/conversions.rs295
-rw-r--r--src/librustdoc/json/import_finder.rs38
-rw-r--r--src/librustdoc/json/mod.rs38
-rw-r--r--src/librustdoc/lib.rs4
-rw-r--r--src/librustdoc/lint.rs9
-rw-r--r--src/librustdoc/passes/calculate_doc_coverage.rs18
-rw-r--r--src/librustdoc/passes/check_code_block_syntax.rs21
-rw-r--r--src/librustdoc/passes/check_doc_test_visibility.rs2
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs26
-rw-r--r--src/librustdoc/passes/html_tags.rs85
-rw-r--r--src/librustdoc/passes/propagate_doc_cfg.rs62
-rw-r--r--src/librustdoc/passes/strip_hidden.rs8
-rw-r--r--src/librustdoc/passes/strip_private.rs10
-rw-r--r--src/librustdoc/passes/stripper.rs65
-rw-r--r--src/librustdoc/scrape_examples.rs2
-rw-r--r--src/librustdoc/theme.rs393
-rw-r--r--src/librustdoc/theme/tests.rs120
-rw-r--r--src/librustdoc/visit.rs2
-rw-r--r--src/librustdoc/visit_ast.rs12
57 files changed, 2836 insertions, 2620 deletions
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index ddaa7438e..7bc35c7d5 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -10,18 +10,19 @@ path = "lib.rs"
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"
pulldown-cmark = { version = "0.9.2", default-features = false }
-minifier = "0.2.1"
-serde = { version = "1.0", features = ["derive"] }
+regex = "1"
+rustdoc-json-types = { path = "../rustdoc-json-types" }
serde_json = "1.0"
+serde = { version = "1.0", features = ["derive"] }
smallvec = "1.8.1"
tempfile = "3"
-itertools = "0.10.1"
-regex = "1"
-rustdoc-json-types = { path = "../rustdoc-json-types" }
+thin-vec = "0.2.8"
tracing = "0.1"
tracing-tree = "0.2.0"
-once_cell = "1.10.0"
[dependencies.tracing-subscriber]
version = "0.3.3"
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index af33c1a6a..175472797 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -123,7 +123,7 @@ where
kind: Box::new(ImplItem(Box::new(Impl {
unsafety: hir::Unsafety::Normal,
generics: new_generics,
- trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, &[])),
+ trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, ThinVec::new())),
for_: clean_middle_ty(ty, self.cx, None),
items: Vec::new(),
polarity,
@@ -476,7 +476,7 @@ where
let mut ty_to_fn: FxHashMap<Type, (PolyTrait, Option<Type>)> = Default::default();
for p in clean_where_predicates {
- let (orig_p, p) = (p, p.clean(self.cx));
+ let (orig_p, p) = (p, clean_predicate(p, self.cx));
if p.is_none() {
continue;
}
@@ -525,8 +525,8 @@ where
GenericBound::TraitBound(ref mut p, _) => {
// Insert regions into the for_generics hash map first, to ensure
// that we don't end up with duplicate bounds (e.g., for<'b, 'b>)
- for_generics.extend(p.generic_params.clone());
- p.generic_params = for_generics.into_iter().collect();
+ for_generics.extend(p.generic_params.drain(..));
+ p.generic_params.extend(for_generics);
self.is_fn_trait(&p.trait_)
}
_ => false,
@@ -551,13 +551,15 @@ where
}
WherePredicate::EqPredicate { lhs, rhs } => {
match lhs {
- Type::QPath { ref assoc, ref self_type, ref trait_, .. } => {
+ Type::QPath(box QPathData {
+ ref assoc, ref self_type, ref trait_, ..
+ }) => {
let ty = &*self_type;
let mut new_trait = trait_.clone();
if self.is_fn_trait(trait_) && assoc.name == sym::Output {
ty_to_fn
- .entry(*ty.clone())
+ .entry(ty.clone())
.and_modify(|e| {
*e = (e.0.clone(), Some(rhs.ty().unwrap().clone()))
})
@@ -582,7 +584,7 @@ where
// to 'T: Iterator<Item=u8>'
GenericArgs::AngleBracketed { ref mut bindings, .. } => {
bindings.push(TypeBinding {
- assoc: *assoc.clone(),
+ assoc: assoc.clone(),
kind: TypeBindingKind::Equality { term: rhs },
});
}
@@ -596,7 +598,7 @@ where
}
}
- let bounds = ty_to_bounds.entry(*ty.clone()).or_default();
+ let bounds = ty_to_bounds.entry(ty.clone()).or_default();
bounds.insert(GenericBound::TraitBound(
PolyTrait { trait_: new_trait, generic_params: Vec::new() },
@@ -613,7 +615,7 @@ where
));
// Avoid creating any new duplicate bounds later in the outer
// loop
- ty_to_traits.entry(*ty.clone()).or_default().insert(trait_.clone());
+ ty_to_traits.entry(ty.clone()).or_default().insert(trait_.clone());
}
_ => panic!("Unexpected LHS {:?} for {:?}", lhs, item_def_id),
}
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index 01dd95e6e..cc734389e 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -115,12 +115,12 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
),
// FIXME(eddyb) compute both `trait_` and `for_` from
// the post-inference `trait_ref`, as it's more accurate.
- trait_: Some(clean_trait_ref_with_bindings(cx, trait_ref.0, &[])),
+ trait_: Some(clean_trait_ref_with_bindings(cx, trait_ref.0, ThinVec::new())),
for_: clean_middle_ty(ty.0, cx, None),
items: cx.tcx
.associated_items(impl_def_id)
.in_definition_order()
- .map(|x| x.clean(cx))
+ .map(|x| clean_middle_assoc_item(x, cx))
.collect::<Vec<_>>(),
polarity: ty::ImplPolarity::Positive,
kind: ImplKind::Blanket(Box::new(clean_middle_ty(trait_ref.0.self_ty(), cx, None))),
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 58d0aedb0..df0e9f7cc 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -3,9 +3,10 @@
use std::iter::once;
use std::sync::Arc;
+use thin_vec::ThinVec;
+
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::thin_vec::ThinVec;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
@@ -16,15 +17,14 @@ use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol};
use crate::clean::{
- self, clean_fn_decl_from_did_and_sig, clean_middle_field, clean_middle_ty,
- clean_trait_ref_with_bindings, clean_ty, clean_ty_generics, clean_variant_def,
- clean_visibility, utils, Attributes, AttributesExt, Clean, ImplKind, ItemId, Type, Visibility,
+ 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,
};
use crate::core::DocContext;
use crate::formats::item_type::ItemType;
-type Attrs<'hir> = &'hir [ast::Attribute];
-
/// Attempt to inline a definition into this AST.
///
/// This function will fetch the definition specified, and if it is
@@ -45,7 +45,7 @@ pub(crate) fn try_inline(
import_def_id: Option<DefId>,
res: Res,
name: Symbol,
- attrs: Option<Attrs<'_>>,
+ attrs: Option<&[ast::Attribute]>,
visited: &mut FxHashSet<DefId>,
) -> Option<Vec<clean::Item>> {
let did = res.opt_def_id()?;
@@ -61,7 +61,7 @@ pub(crate) fn try_inline(
Res::Def(DefKind::Trait, did) => {
record_extern_fqn(cx, did, ItemType::Trait);
build_impls(cx, Some(parent_module), did, attrs, &mut ret);
- clean::TraitItem(build_external_trait(cx, did))
+ clean::TraitItem(Box::new(build_external_trait(cx, did)))
}
Res::Def(DefKind::Fn, did) => {
record_extern_fqn(cx, did, ItemType::Function);
@@ -171,7 +171,7 @@ pub(crate) fn try_inline_glob(
}
}
-pub(crate) fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> {
+pub(crate) fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> &'hir [ast::Attribute] {
cx.tcx.get_attrs_unchecked(did)
}
@@ -217,7 +217,7 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean
// 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, ..item.clean(cx) }
+ clean::Item { visibility: Visibility::Inherited, ..clean_middle_assoc_item(item, cx) }
})
.collect();
@@ -286,7 +286,7 @@ pub(crate) fn build_impls(
cx: &mut DocContext<'_>,
parent_module: Option<DefId>,
did: DefId,
- attrs: Option<Attrs<'_>>,
+ attrs: Option<&[ast::Attribute]>,
ret: &mut Vec<clean::Item>,
) {
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_inherent_impls");
@@ -296,14 +296,29 @@ pub(crate) fn build_impls(
for &did in tcx.inherent_impls(did).iter() {
build_impl(cx, parent_module, did, attrs, ret);
}
+
+ // This pretty much exists expressly for `dyn Error` traits that exist in the `alloc` crate.
+ // See also:
+ //
+ // * https://github.com/rust-lang/rust/issues/103170 — where it didn't used to get documented
+ // * https://github.com/rust-lang/rust/pull/99917 — where the feature got used
+ // * https://github.com/rust-lang/rust/issues/53487 — overall tracking issue for Error
+ if tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) {
+ use rustc_middle::ty::fast_reject::SimplifiedTypeGen::*;
+ let type_ =
+ if tcx.is_trait(did) { TraitSimplifiedType(did) } else { AdtSimplifiedType(did) };
+ for &did in tcx.incoherent_impls(type_) {
+ build_impl(cx, parent_module, did, attrs, ret);
+ }
+ }
}
/// `parent_module` refers to the parent of the re-export, not the original item
-fn merge_attrs(
+pub(crate) fn merge_attrs(
cx: &mut DocContext<'_>,
parent_module: Option<DefId>,
- old_attrs: Attrs<'_>,
- new_attrs: Option<Attrs<'_>>,
+ old_attrs: &[ast::Attribute],
+ new_attrs: Option<&[ast::Attribute]>,
) -> (clean::Attributes, Option<Arc<clean::cfg::Cfg>>) {
// NOTE: If we have additional attributes (from a re-export),
// always insert them first. This ensure that re-export
@@ -330,7 +345,7 @@ pub(crate) fn build_impl(
cx: &mut DocContext<'_>,
parent_module: Option<DefId>,
did: DefId,
- attrs: Option<Attrs<'_>>,
+ attrs: Option<&[ast::Attribute]>,
ret: &mut Vec<clean::Item>,
) {
if !cx.inlined.insert(did.into()) {
@@ -426,9 +441,9 @@ pub(crate) fn build_impl(
true
}
})
- .map(|item| item.clean(cx))
+ .map(|item| clean_impl_item(item, cx))
.collect::<Vec<_>>(),
- impl_.generics.clean(cx),
+ clean_generics(impl_.generics, cx),
),
None => (
tcx.associated_items(did)
@@ -452,7 +467,7 @@ pub(crate) fn build_impl(
item.visibility(tcx).is_public()
}
})
- .map(|item| item.clean(cx))
+ .map(|item| clean_middle_assoc_item(item, cx))
.collect::<Vec<_>>(),
clean::enter_impl_trait(cx, |cx| {
clean_ty_generics(cx, tcx.generics_of(did), predicates)
@@ -460,7 +475,7 @@ pub(crate) fn build_impl(
),
};
let polarity = tcx.impl_polarity(did);
- let trait_ = associated_trait.map(|t| clean_trait_ref_with_bindings(cx, t, &[]));
+ let trait_ = associated_trait.map(|t| clean_trait_ref_with_bindings(cx, t, ThinVec::new()));
if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() {
super::build_deref_target_impls(cx, &trait_items, ret);
}
@@ -671,7 +686,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean:
g.where_predicates.retain(|pred| match pred {
clean::WherePredicate::BoundPredicate {
- ty: clean::QPath { self_type: box clean::Generic(ref s), trait_, .. },
+ ty: clean::QPath(box clean::QPathData { self_type: clean::Generic(ref s), trait_, .. }),
bounds,
..
} => !(bounds.is_empty() || *s == kw::SelfUpper && trait_.def_id() == trait_did),
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 929f5f89b..c8875c272 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -33,7 +33,8 @@ use std::collections::hash_map::Entry;
use std::collections::BTreeMap;
use std::default::Default;
use std::hash::Hash;
-use std::{mem, vec};
+use std::mem;
+use thin_vec::ThinVec;
use crate::core::{self, DocContext, ImplTraitParam};
use crate::formats::item_type::ItemType;
@@ -44,128 +45,126 @@ use utils::*;
pub(crate) use self::types::*;
pub(crate) use self::utils::{get_auto_trait_and_blanket_impls, krate, register_res};
-pub(crate) trait Clean<'tcx, T> {
- fn clean(&self, cx: &mut DocContext<'tcx>) -> T;
-}
+pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<'tcx>) -> Item {
+ let mut items: Vec<Item> = vec![];
+ let mut inserted = FxHashSet::default();
+ items.extend(doc.foreigns.iter().map(|(item, renamed)| {
+ let item = clean_maybe_renamed_foreign_item(cx, item, *renamed);
+ if let Some(name) = item.name && !item.attrs.lists(sym::doc).has_word(sym::hidden) {
+ inserted.insert((item.type_(), name));
+ }
+ item
+ }));
+ items.extend(doc.mods.iter().filter_map(|x| {
+ if !inserted.insert((ItemType::Module, x.name)) {
+ return None;
+ }
+ let item = clean_doc_module(x, cx);
+ if item.attrs.lists(sym::doc).has_word(sym::hidden) {
+ // Hidden modules are stripped at a later stage.
+ // If a hidden module has the same name as a visible one, we want
+ // to keep both of them around.
+ inserted.remove(&(ItemType::Module, x.name));
+ }
+ Some(item)
+ }));
-impl<'tcx> Clean<'tcx, Item> for DocModule<'tcx> {
- fn clean(&self, cx: &mut DocContext<'tcx>) -> Item {
- let mut items: Vec<Item> = vec![];
- let mut inserted = FxHashSet::default();
- items.extend(self.foreigns.iter().map(|(item, renamed)| {
- let item = clean_maybe_renamed_foreign_item(cx, item, *renamed);
- if let Some(name) = item.name {
+ // Split up imports from all other items.
+ //
+ // 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)| {
+ // 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);
+ for item in &v {
+ if let Some(name) = item.name && !item.attrs.lists(sym::doc).has_word(sym::hidden) {
inserted.insert((item.type_(), name));
}
- item
- }));
- items.extend(self.mods.iter().map(|x| {
- inserted.insert((ItemType::Module, x.name));
- x.clean(cx)
- }));
-
- // Split up imports from all other items.
- //
- // 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(self.items.iter().flat_map(|(item, renamed)| {
- // 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);
- for item in &v {
- if let Some(name) = item.name {
- inserted.insert((item.type_(), name));
- }
- }
- v
- }));
- items.extend(self.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()));
- clean_use_statement(item, name, path, hir::UseKind::Glob, cx, &mut inserted)
- } else {
- // skip everything else
- Vec::new()
- }
- }));
-
- // determine if we should display the inner contents or
- // the outer `mod` item for the source code.
-
- let span = Span::new({
- let where_outer = self.where_outer(cx.tcx);
- let sm = cx.sess().source_map();
- let outer = sm.lookup_char_pos(where_outer.lo());
- let inner = sm.lookup_char_pos(self.where_inner.lo());
- if outer.file.start_pos == inner.file.start_pos {
- // mod foo { ... }
- where_outer
- } else {
- // mod foo; (and a separate SourceFile for the contents)
- self.where_inner
- }
- });
+ }
+ v
+ }));
+ 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()));
+ clean_use_statement(item, name, path, hir::UseKind::Glob, cx, &mut inserted)
+ } else {
+ // skip everything else
+ Vec::new()
+ }
+ }));
+
+ // determine if we should display the inner contents or
+ // the outer `mod` item for the source code.
+
+ let span = Span::new({
+ let where_outer = doc.where_outer(cx.tcx);
+ let sm = cx.sess().source_map();
+ let outer = sm.lookup_char_pos(where_outer.lo());
+ let inner = sm.lookup_char_pos(doc.where_inner.lo());
+ if outer.file.start_pos == inner.file.start_pos {
+ // mod foo { ... }
+ where_outer
+ } else {
+ // mod foo; (and a separate SourceFile for the contents)
+ doc.where_inner
+ }
+ });
- Item::from_hir_id_and_parts(
- self.id,
- Some(self.name),
- ModuleItem(Module { items, span }),
- cx,
- )
- }
+ Item::from_hir_id_and_parts(doc.id, Some(doc.name), ModuleItem(Module { items, span }), cx)
}
-impl<'tcx> Clean<'tcx, Option<GenericBound>> for hir::GenericBound<'tcx> {
- fn clean(&self, cx: &mut DocContext<'tcx>) -> Option<GenericBound> {
- Some(match *self {
- hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)),
- hir::GenericBound::LangItemTrait(lang_item, span, _, generic_args) => {
- let def_id = cx.tcx.require_lang_item(lang_item, Some(span));
-
- let trait_ref = ty::TraitRef::identity(cx.tcx, def_id).skip_binder();
-
- let generic_args = generic_args.clean(cx);
- let GenericArgs::AngleBracketed { bindings, .. } = generic_args
- else {
- bug!("clean: parenthesized `GenericBound::LangItemTrait`");
- };
+fn clean_generic_bound<'tcx>(
+ bound: &hir::GenericBound<'tcx>,
+ cx: &mut DocContext<'tcx>,
+) -> Option<GenericBound> {
+ Some(match *bound {
+ hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)),
+ hir::GenericBound::LangItemTrait(lang_item, span, _, generic_args) => {
+ let def_id = cx.tcx.require_lang_item(lang_item, Some(span));
+
+ let trait_ref = ty::TraitRef::identity(cx.tcx, def_id).skip_binder();
+
+ let generic_args = clean_generic_args(generic_args, cx);
+ let GenericArgs::AngleBracketed { bindings, .. } = generic_args
+ else {
+ bug!("clean: parenthesized `GenericBound::LangItemTrait`");
+ };
- let trait_ = clean_trait_ref_with_bindings(cx, trait_ref, &bindings);
- GenericBound::TraitBound(
- PolyTrait { trait_, generic_params: vec![] },
- hir::TraitBoundModifier::None,
- )
+ let trait_ = clean_trait_ref_with_bindings(cx, trait_ref, bindings);
+ GenericBound::TraitBound(
+ PolyTrait { trait_, generic_params: vec![] },
+ hir::TraitBoundModifier::None,
+ )
+ }
+ hir::GenericBound::Trait(ref t, modifier) => {
+ // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op.
+ if modifier == hir::TraitBoundModifier::MaybeConst
+ && cx.tcx.lang_items().destruct_trait() == Some(t.trait_ref.trait_def_id().unwrap())
+ {
+ return None;
}
- hir::GenericBound::Trait(ref t, modifier) => {
- // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op.
- if modifier == hir::TraitBoundModifier::MaybeConst
- && cx.tcx.lang_items().destruct_trait()
- == Some(t.trait_ref.trait_def_id().unwrap())
- {
- return None;
- }
- GenericBound::TraitBound(t.clean(cx), modifier)
- }
- })
- }
+ GenericBound::TraitBound(clean_poly_trait_ref(t, cx), modifier)
+ }
+ })
}
pub(crate) fn clean_trait_ref_with_bindings<'tcx>(
cx: &mut DocContext<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
- bindings: &[TypeBinding],
+ bindings: ThinVec<TypeBinding>,
) -> Path {
let kind = cx.tcx.def_kind(trait_ref.def_id).into();
if !matches!(kind, ItemType::Trait | ItemType::TraitAlias) {
span_bug!(cx.tcx.def_span(trait_ref.def_id), "`TraitRef` had unexpected kind {:?}", kind);
}
inline::record_extern_fqn(cx, trait_ref.def_id, kind);
- let path = external_path(cx, trait_ref.def_id, true, bindings.to_vec(), trait_ref.substs);
+ let path = external_path(cx, trait_ref.def_id, true, bindings, trait_ref.substs);
debug!("ty::TraitRef\n subst: {:?}\n", trait_ref.substs);
@@ -175,7 +174,7 @@ pub(crate) fn clean_trait_ref_with_bindings<'tcx>(
fn clean_poly_trait_ref_with_bindings<'tcx>(
cx: &mut DocContext<'tcx>,
poly_trait_ref: ty::PolyTraitRef<'tcx>,
- bindings: &[TypeBinding],
+ bindings: ThinVec<TypeBinding>,
) -> GenericBound {
let poly_trait_ref = poly_trait_ref.lift_to_tcx(cx.tcx).unwrap();
@@ -200,16 +199,10 @@ fn clean_poly_trait_ref_with_bindings<'tcx>(
)
}
-impl<'tcx> Clean<'tcx, GenericBound> for ty::PolyTraitRef<'tcx> {
- fn clean(&self, cx: &mut DocContext<'tcx>) -> GenericBound {
- clean_poly_trait_ref_with_bindings(cx, *self, &[])
- }
-}
-
-fn clean_lifetime<'tcx>(lifetime: hir::Lifetime, cx: &mut DocContext<'tcx>) -> Lifetime {
+fn clean_lifetime<'tcx>(lifetime: &hir::Lifetime, cx: &mut DocContext<'tcx>) -> Lifetime {
let def = cx.tcx.named_region(lifetime.hir_id);
if let Some(
- rl::Region::EarlyBound(_, node_id)
+ rl::Region::EarlyBound(node_id)
| rl::Region::LateBound(_, _, node_id)
| rl::Region::Free(_, node_id),
) = def
@@ -257,7 +250,6 @@ pub(crate) fn clean_middle_region<'tcx>(region: ty::Region<'tcx>) -> Option<Life
| ty::ReFree(..)
| ty::ReVar(..)
| ty::RePlaceholder(..)
- | ty::ReEmpty(_)
| ty::ReErased => {
debug!("cannot clean region {:?}", region);
None
@@ -265,66 +257,68 @@ pub(crate) fn clean_middle_region<'tcx>(region: ty::Region<'tcx>) -> Option<Life
}
}
-impl<'tcx> Clean<'tcx, Option<WherePredicate>> for hir::WherePredicate<'tcx> {
- fn clean(&self, cx: &mut DocContext<'tcx>) -> Option<WherePredicate> {
- if !self.in_where_clause() {
- return None;
- }
- Some(match *self {
- hir::WherePredicate::BoundPredicate(ref wbp) => {
- let bound_params = wbp
- .bound_generic_params
- .iter()
- .map(|param| {
- // Higher-ranked params must be lifetimes.
- // Higher-ranked lifetimes can't have bounds.
- assert_matches!(
- param,
- hir::GenericParam { kind: hir::GenericParamKind::Lifetime { .. }, .. }
- );
- Lifetime(param.name.ident().name)
- })
- .collect();
- WherePredicate::BoundPredicate {
- ty: clean_ty(wbp.bounded_ty, cx),
- bounds: wbp.bounds.iter().filter_map(|x| x.clean(cx)).collect(),
- bound_params,
- }
+fn clean_where_predicate<'tcx>(
+ predicate: &hir::WherePredicate<'tcx>,
+ cx: &mut DocContext<'tcx>,
+) -> Option<WherePredicate> {
+ if !predicate.in_where_clause() {
+ return None;
+ }
+ Some(match *predicate {
+ hir::WherePredicate::BoundPredicate(ref wbp) => {
+ let bound_params = wbp
+ .bound_generic_params
+ .iter()
+ .map(|param| {
+ // Higher-ranked params must be lifetimes.
+ // Higher-ranked lifetimes can't have bounds.
+ assert_matches!(
+ param,
+ hir::GenericParam { kind: hir::GenericParamKind::Lifetime { .. }, .. }
+ );
+ Lifetime(param.name.ident().name)
+ })
+ .collect();
+ WherePredicate::BoundPredicate {
+ ty: clean_ty(wbp.bounded_ty, cx),
+ bounds: wbp.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
+ bound_params,
}
+ }
- hir::WherePredicate::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate {
- lifetime: clean_lifetime(wrp.lifetime, cx),
- bounds: wrp.bounds.iter().filter_map(|x| x.clean(cx)).collect(),
- },
+ hir::WherePredicate::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate {
+ lifetime: clean_lifetime(wrp.lifetime, cx),
+ bounds: wrp.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
+ },
- hir::WherePredicate::EqPredicate(ref wrp) => WherePredicate::EqPredicate {
- lhs: clean_ty(wrp.lhs_ty, cx),
- rhs: clean_ty(wrp.rhs_ty, cx).into(),
- },
- })
- }
+ hir::WherePredicate::EqPredicate(ref wrp) => WherePredicate::EqPredicate {
+ lhs: clean_ty(wrp.lhs_ty, cx),
+ rhs: clean_ty(wrp.rhs_ty, cx).into(),
+ },
+ })
}
-impl<'tcx> Clean<'tcx, Option<WherePredicate>> for ty::Predicate<'tcx> {
- fn clean(&self, cx: &mut DocContext<'tcx>) -> Option<WherePredicate> {
- let bound_predicate = self.kind();
- match bound_predicate.skip_binder() {
- ty::PredicateKind::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) => Some(clean_projection_predicate(pred, cx)),
- ty::PredicateKind::ConstEvaluatable(..) => None,
- ty::PredicateKind::WellFormed(..) => None,
-
- ty::PredicateKind::Subtype(..)
- | ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::ObjectSafe(..)
- | ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => panic!("not user writable"),
+pub(crate) fn clean_predicate<'tcx>(
+ predicate: ty::Predicate<'tcx>,
+ cx: &mut DocContext<'tcx>,
+) -> Option<WherePredicate> {
+ let bound_predicate = predicate.kind();
+ match bound_predicate.skip_binder() {
+ ty::PredicateKind::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) => Some(clean_projection_predicate(pred, cx)),
+ ty::PredicateKind::ConstEvaluatable(..) => None,
+ ty::PredicateKind::WellFormed(..) => None,
+
+ ty::PredicateKind::Subtype(..)
+ | ty::PredicateKind::Coerce(..)
+ | ty::PredicateKind::ObjectSafe(..)
+ | ty::PredicateKind::ClosureKind(..)
+ | ty::PredicateKind::ConstEquate(..)
+ | ty::PredicateKind::TypeWellFormedFromEnv(..) => panic!("not user writable"),
}
}
@@ -342,7 +336,7 @@ fn clean_poly_trait_predicate<'tcx>(
let poly_trait_ref = pred.map_bound(|pred| pred.trait_ref);
Some(WherePredicate::BoundPredicate {
ty: clean_middle_ty(poly_trait_ref.skip_binder().self_ty(), cx, None),
- bounds: vec![poly_trait_ref.clean(cx)],
+ bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, ThinVec::new())],
bound_params: Vec::new(),
})
}
@@ -352,10 +346,6 @@ fn clean_region_outlives_predicate<'tcx>(
) -> Option<WherePredicate> {
let ty::OutlivesPredicate(a, b) = pred;
- if a.is_empty() && b.is_empty() {
- return None;
- }
-
Some(WherePredicate::RegionPredicate {
lifetime: clean_middle_region(a).expect("failed to clean lifetime"),
bounds: vec![GenericBound::Outlives(
@@ -370,10 +360,6 @@ fn clean_type_outlives_predicate<'tcx>(
) -> Option<WherePredicate> {
let ty::OutlivesPredicate(ty, lt) = pred;
- if lt.is_empty() {
- return None;
- }
-
Some(WherePredicate::BoundPredicate {
ty: clean_middle_ty(ty, cx, None),
bounds: vec![GenericBound::Outlives(
@@ -384,9 +370,9 @@ fn clean_type_outlives_predicate<'tcx>(
}
fn clean_middle_term<'tcx>(term: ty::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Term {
- match term {
- ty::Term::Ty(ty) => Term::Type(clean_middle_ty(ty, cx, None)),
- ty::Term::Const(c) => Term::Constant(clean_middle_const(c, cx)),
+ match term.unpack() {
+ ty::TermKind::Ty(ty) => Term::Type(clean_middle_ty(ty, cx, None)),
+ ty::TermKind::Const(c) => Term::Constant(clean_middle_const(c, cx)),
}
}
@@ -417,7 +403,7 @@ fn clean_projection<'tcx>(
def_id: Option<DefId>,
) -> Type {
let lifted = ty.lift_to_tcx(cx.tcx).unwrap();
- let trait_ = clean_trait_ref_with_bindings(cx, lifted.trait_ref(cx.tcx), &[]);
+ let trait_ = clean_trait_ref_with_bindings(cx, lifted.trait_ref(cx.tcx), ThinVec::new());
let self_type = clean_middle_ty(ty.self_ty(), cx, None);
let self_def_id = if let Some(def_id) = def_id {
cx.tcx.opt_parent(def_id).or(Some(def_id))
@@ -425,12 +411,12 @@ fn clean_projection<'tcx>(
self_type.def_id(&cx.cache)
};
let should_show_cast = compute_should_show_cast(self_def_id, &trait_, &self_type);
- Type::QPath {
- assoc: Box::new(projection_to_path_segment(ty, cx)),
+ Type::QPath(Box::new(QPathData {
+ assoc: projection_to_path_segment(ty, cx),
should_show_cast,
- self_type: Box::new(self_type),
+ self_type,
trait_,
- }
+ }))
}
fn compute_should_show_cast(self_def_id: Option<DefId>, trait_: &Path, self_type: &Type) -> bool {
@@ -509,7 +495,7 @@ fn clean_generic_param<'tcx>(
.filter(|bp| !bp.in_where_clause)
.flat_map(|bp| bp.bounds)
.map(|bound| match bound {
- hir::GenericBound::Outlives(lt) => clean_lifetime(*lt, cx),
+ hir::GenericBound::Outlives(lt) => clean_lifetime(lt, cx),
_ => panic!(),
})
.collect()
@@ -524,7 +510,7 @@ fn clean_generic_param<'tcx>(
.bounds_for_param(did)
.filter(|bp| bp.origin != PredicateOrigin::WhereClause)
.flat_map(|bp| bp.bounds)
- .filter_map(|x| x.clean(cx))
+ .filter_map(|x| clean_generic_bound(x, cx))
.collect()
} else {
Vec::new()
@@ -572,65 +558,68 @@ fn is_elided_lifetime(param: &hir::GenericParam<'_>) -> bool {
matches!(param.kind, hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided })
}
-impl<'tcx> Clean<'tcx, Generics> for hir::Generics<'tcx> {
- fn clean(&self, cx: &mut DocContext<'tcx>) -> Generics {
- let impl_trait_params = self
- .params
- .iter()
- .filter(|param| is_impl_trait(param))
- .map(|param| {
- let param = clean_generic_param(cx, Some(self), param);
- match param.kind {
- GenericParamDefKind::Lifetime { .. } => unreachable!(),
- GenericParamDefKind::Type { did, ref bounds, .. } => {
- cx.impl_trait_bounds.insert(did.into(), bounds.clone());
- }
- GenericParamDefKind::Const { .. } => unreachable!(),
+pub(crate) fn clean_generics<'tcx>(
+ gens: &hir::Generics<'tcx>,
+ cx: &mut DocContext<'tcx>,
+) -> Generics {
+ let impl_trait_params = gens
+ .params
+ .iter()
+ .filter(|param| is_impl_trait(param))
+ .map(|param| {
+ let param = clean_generic_param(cx, Some(gens), param);
+ match param.kind {
+ GenericParamDefKind::Lifetime { .. } => unreachable!(),
+ GenericParamDefKind::Type { did, ref bounds, .. } => {
+ cx.impl_trait_bounds.insert(did.into(), bounds.clone());
}
- param
- })
- .collect::<Vec<_>>();
+ GenericParamDefKind::Const { .. } => unreachable!(),
+ }
+ param
+ })
+ .collect::<Vec<_>>();
- let mut params = Vec::with_capacity(self.params.len());
- for p in self.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) {
- let p = clean_generic_param(cx, Some(self), p);
- params.push(p);
- }
- params.extend(impl_trait_params);
+ 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);
+ }
+ params.extend(impl_trait_params);
- let mut generics = Generics {
- params,
- where_predicates: self.predicates.iter().filter_map(|x| x.clean(cx)).collect(),
- };
+ 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;
- }
+ // 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 { .. } => {}
}
+ GenericParamDefKind::Const { .. } => {}
}
}
}
- _ => continue,
}
+ _ => continue,
}
- generics
}
+ generics
}
fn clean_ty_generics<'tcx>(
@@ -701,7 +690,7 @@ fn clean_ty_generics<'tcx>(
if let Some(param_idx) = param_idx {
if let Some(b) = impl_trait.get_mut(&param_idx.into()) {
- let p: WherePredicate = p.clean(cx)?;
+ let p: WherePredicate = clean_predicate(*p, cx)?;
b.extend(
p.get_bounds()
@@ -758,7 +747,7 @@ fn clean_ty_generics<'tcx>(
// Now that `cx.impl_trait_bounds` is populated, we can process
// remaining predicates which could contain `impl Trait`.
let mut where_predicates =
- where_predicates.into_iter().flat_map(|p| p.clean(cx)).collect::<Vec<_>>();
+ where_predicates.into_iter().flat_map(|p| clean_predicate(*p, cx)).collect::<Vec<_>>();
// Type parameters have a Sized bound by default unless removed with
// ?Sized. Scan through the predicates and mark any type parameter with
@@ -896,9 +885,12 @@ fn clean_function<'tcx>(
) -> Box<Function> {
let (generics, decl) = enter_impl_trait(cx, |cx| {
// NOTE: generics must be cleaned before args
- let generics = generics.clean(cx);
+ let generics = clean_generics(generics, cx);
let args = clean_args_from_types_and_body_id(cx, sig.decl.inputs, body_id);
- let decl = clean_fn_decl_with_args(cx, sig.decl, args);
+ let mut decl = clean_fn_decl_with_args(cx, sig.decl, args);
+ if sig.header.is_async() {
+ decl.output = decl.sugared_async_return_type();
+ }
(generics, decl)
});
Box::new(Function { decl, generics })
@@ -994,305 +986,307 @@ fn clean_trait_ref<'tcx>(trait_ref: &hir::TraitRef<'tcx>, cx: &mut DocContext<'t
path
}
-impl<'tcx> Clean<'tcx, PolyTrait> for hir::PolyTraitRef<'tcx> {
- fn clean(&self, cx: &mut DocContext<'tcx>) -> PolyTrait {
- PolyTrait {
- trait_: clean_trait_ref(&self.trait_ref, cx),
- generic_params: self
- .bound_generic_params
- .iter()
- .filter(|p| !is_elided_lifetime(p))
- .map(|x| clean_generic_param(cx, None, x))
- .collect(),
- }
+fn clean_poly_trait_ref<'tcx>(
+ poly_trait_ref: &hir::PolyTraitRef<'tcx>,
+ cx: &mut DocContext<'tcx>,
+) -> PolyTrait {
+ PolyTrait {
+ trait_: clean_trait_ref(&poly_trait_ref.trait_ref, cx),
+ generic_params: poly_trait_ref
+ .bound_generic_params
+ .iter()
+ .filter(|p| !is_elided_lifetime(p))
+ .map(|x| clean_generic_param(cx, None, x))
+ .collect(),
}
}
-impl<'tcx> Clean<'tcx, Item> for hir::TraitItem<'tcx> {
- fn clean(&self, cx: &mut DocContext<'tcx>) -> Item {
- let local_did = self.def_id.to_def_id();
- cx.with_param_env(local_did, |cx| {
- let inner = match self.kind {
- hir::TraitItemKind::Const(ty, Some(default)) => AssocConstItem(
- clean_ty(ty, cx),
- ConstantKind::Local { def_id: local_did, body: default },
- ),
- 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, self.generics, 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 = self.generics.clean(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 }))
- }
- hir::TraitItemKind::Type(bounds, Some(default)) => {
- let generics = enter_impl_trait(cx, |cx| self.generics.clean(cx));
- let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect();
- let item_type = clean_middle_ty(hir_ty_to_ty(cx.tcx, default), cx, None);
- AssocTypeItem(
- Box::new(Typedef {
- type_: clean_ty(default, cx),
- generics,
- item_type: Some(item_type),
- }),
- bounds,
- )
- }
- hir::TraitItemKind::Type(bounds, None) => {
- let generics = enter_impl_trait(cx, |cx| self.generics.clean(cx));
- let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect();
- TyAssocTypeItem(Box::new(generics), bounds)
- }
- };
- let what_rustc_thinks =
- Item::from_def_id_and_parts(local_did, Some(self.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 }
- })
- }
+fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext<'tcx>) -> Item {
+ let local_did = trait_item.def_id.to_def_id();
+ cx.with_param_env(local_did, |cx| {
+ let inner = match trait_item.kind {
+ hir::TraitItemKind::Const(ty, Some(default)) => AssocConstItem(
+ clean_ty(ty, cx),
+ ConstantKind::Local { def_id: local_did, body: default },
+ ),
+ 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);
+ 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 }))
+ }
+ hir::TraitItemKind::Type(bounds, Some(default)) => {
+ let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
+ let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect();
+ let item_type = clean_middle_ty(hir_ty_to_ty(cx.tcx, default), cx, None);
+ AssocTypeItem(
+ Box::new(Typedef {
+ type_: clean_ty(default, cx),
+ generics,
+ item_type: Some(item_type),
+ }),
+ bounds,
+ )
+ }
+ 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)
+ }
+ };
+ 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 }
+ })
}
-impl<'tcx> Clean<'tcx, Item> for hir::ImplItem<'tcx> {
- fn clean(&self, cx: &mut DocContext<'tcx>) -> Item {
- let local_did = self.def_id.to_def_id();
- cx.with_param_env(local_did, |cx| {
- let inner = match self.kind {
- hir::ImplItemKind::Const(ty, expr) => {
- let default = ConstantKind::Local { def_id: local_did, body: expr };
- AssocConstItem(clean_ty(ty, cx), default)
- }
- hir::ImplItemKind::Fn(ref sig, body) => {
- let m = clean_function(cx, sig, self.generics, body);
- let defaultness = cx.tcx.impl_defaultness(self.def_id);
- MethodItem(m, Some(defaultness))
- }
- hir::ImplItemKind::TyAlias(hir_ty) => {
- let type_ = clean_ty(hir_ty, cx);
- let generics = self.generics.clean(cx);
- let item_type = clean_middle_ty(hir_ty_to_ty(cx.tcx, hir_ty), cx, None);
- AssocTypeItem(
- Box::new(Typedef { type_, generics, item_type: Some(item_type) }),
- Vec::new(),
- )
- }
- };
+pub(crate) fn clean_impl_item<'tcx>(
+ impl_: &hir::ImplItem<'tcx>,
+ cx: &mut DocContext<'tcx>,
+) -> Item {
+ let local_did = impl_.def_id.to_def_id();
+ cx.with_param_env(local_did, |cx| {
+ let inner = match impl_.kind {
+ hir::ImplItemKind::Const(ty, expr) => {
+ let default = ConstantKind::Local { def_id: local_did, body: expr };
+ AssocConstItem(clean_ty(ty, cx), default)
+ }
+ hir::ImplItemKind::Fn(ref sig, body) => {
+ let m = clean_function(cx, sig, impl_.generics, body);
+ let defaultness = cx.tcx.impl_defaultness(impl_.def_id);
+ MethodItem(m, Some(defaultness))
+ }
+ hir::ImplItemKind::TyAlias(hir_ty) => {
+ let type_ = clean_ty(hir_ty, cx);
+ let generics = clean_generics(impl_.generics, cx);
+ let item_type = clean_middle_ty(hir_ty_to_ty(cx.tcx, hir_ty), cx, None);
+ AssocTypeItem(
+ Box::new(Typedef { type_, generics, item_type: Some(item_type) }),
+ Vec::new(),
+ )
+ }
+ };
- let mut what_rustc_thinks =
- Item::from_def_id_and_parts(local_did, Some(self.ident.name), inner, cx);
+ 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(self.def_id));
+ let impl_ref = cx.tcx.impl_trait_ref(cx.tcx.local_parent(impl_.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;
- }
+ // 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
- })
- }
+ what_rustc_thinks
+ })
}
-impl<'tcx> Clean<'tcx, Item> for ty::AssocItem {
- fn clean(&self, cx: &mut DocContext<'tcx>) -> Item {
- let tcx = cx.tcx;
- let kind = match self.kind {
- ty::AssocKind::Const => {
- let ty = clean_middle_ty(tcx.type_of(self.def_id), cx, Some(self.def_id));
+pub(crate) fn clean_middle_assoc_item<'tcx>(
+ assoc_item: &ty::AssocItem,
+ cx: &mut DocContext<'tcx>,
+) -> Item {
+ let tcx = cx.tcx;
+ let kind = match assoc_item.kind {
+ ty::AssocKind::Const => {
+ let ty = clean_middle_ty(tcx.type_of(assoc_item.def_id), cx, Some(assoc_item.def_id));
- let provided = match self.container {
- ty::ImplContainer => true,
- ty::TraitContainer => tcx.impl_defaultness(self.def_id).has_value(),
- };
- if provided {
- AssocConstItem(ty, ConstantKind::Extern { def_id: self.def_id })
- } else {
- TyAssocConstItem(ty)
- }
+ let provided = match assoc_item.container {
+ ty::ImplContainer => true,
+ ty::TraitContainer => tcx.impl_defaultness(assoc_item.def_id).has_value(),
+ };
+ if provided {
+ AssocConstItem(ty, ConstantKind::Extern { def_id: assoc_item.def_id })
+ } else {
+ TyAssocConstItem(ty)
}
- ty::AssocKind::Fn => {
- let generics = clean_ty_generics(
- cx,
- tcx.generics_of(self.def_id),
- tcx.explicit_predicates_of(self.def_id),
- );
- let sig = tcx.fn_sig(self.def_id);
- let mut decl = clean_fn_decl_from_did_and_sig(cx, Some(self.def_id), sig);
-
- if self.fn_has_self_parameter {
- let self_ty = match self.container {
- ty::ImplContainer => tcx.type_of(self.container_id(tcx)),
- ty::TraitContainer => tcx.types.self_param,
- };
- let self_arg_ty = sig.input(0).skip_binder();
- if self_arg_ty == self_ty {
- decl.inputs.values[0].type_ = Generic(kw::SelfUpper);
- } else if let ty::Ref(_, ty, _) = *self_arg_ty.kind() {
- if ty == self_ty {
- match decl.inputs.values[0].type_ {
- BorrowedRef { ref mut type_, .. } => {
- **type_ = Generic(kw::SelfUpper)
- }
- _ => unreachable!(),
- }
+ }
+ ty::AssocKind::Fn => {
+ let 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);
+ let mut decl = clean_fn_decl_from_did_and_sig(cx, Some(assoc_item.def_id), sig);
+
+ if assoc_item.fn_has_self_parameter {
+ let self_ty = match assoc_item.container {
+ ty::ImplContainer => tcx.type_of(assoc_item.container_id(tcx)),
+ ty::TraitContainer => tcx.types.self_param,
+ };
+ let self_arg_ty = sig.input(0).skip_binder();
+ if self_arg_ty == self_ty {
+ decl.inputs.values[0].type_ = Generic(kw::SelfUpper);
+ } else if let ty::Ref(_, ty, _) = *self_arg_ty.kind() {
+ if ty == self_ty {
+ match decl.inputs.values[0].type_ {
+ BorrowedRef { ref mut type_, .. } => **type_ = Generic(kw::SelfUpper),
+ _ => unreachable!(),
}
}
}
+ }
- let provided = match self.container {
- ty::ImplContainer => true,
- ty::TraitContainer => self.defaultness(tcx).has_value(),
+ let provided = match assoc_item.container {
+ ty::ImplContainer => true,
+ ty::TraitContainer => assoc_item.defaultness(tcx).has_value(),
+ };
+ if provided {
+ let defaultness = match assoc_item.container {
+ ty::ImplContainer => Some(assoc_item.defaultness(tcx)),
+ ty::TraitContainer => None,
};
- if provided {
- let defaultness = match self.container {
- ty::ImplContainer => Some(self.defaultness(tcx)),
- ty::TraitContainer => None,
- };
- MethodItem(Box::new(Function { generics, decl }), defaultness)
- } else {
- TyMethodItem(Box::new(Function { generics, decl }))
- }
+ MethodItem(Box::new(Function { generics, decl }), defaultness)
+ } else {
+ TyMethodItem(Box::new(Function { generics, decl }))
}
- ty::AssocKind::Type => {
- let my_name = self.name;
-
- fn param_eq_arg(param: &GenericParamDef, arg: &GenericArg) -> bool {
- match (&param.kind, arg) {
- (GenericParamDefKind::Type { .. }, GenericArg::Type(Type::Generic(ty)))
- if *ty == param.name =>
- {
- true
- }
- (
- GenericParamDefKind::Lifetime { .. },
- GenericArg::Lifetime(Lifetime(lt)),
- ) if *lt == param.name => true,
- (GenericParamDefKind::Const { .. }, GenericArg::Const(c)) => {
- match &c.kind {
- ConstantKind::TyConst { expr } => expr == param.name.as_str(),
- _ => false,
- }
- }
- _ => false,
+ }
+ ty::AssocKind::Type => {
+ let my_name = assoc_item.name;
+
+ fn param_eq_arg(param: &GenericParamDef, arg: &GenericArg) -> bool {
+ match (&param.kind, arg) {
+ (GenericParamDefKind::Type { .. }, GenericArg::Type(Type::Generic(ty)))
+ if *ty == param.name =>
+ {
+ true
+ }
+ (GenericParamDefKind::Lifetime { .. }, GenericArg::Lifetime(Lifetime(lt)))
+ if *lt == param.name =>
+ {
+ true
}
+ (GenericParamDefKind::Const { .. }, GenericArg::Const(c)) => match &c.kind {
+ ConstantKind::TyConst { expr } => expr == param.name.as_str(),
+ _ => false,
+ },
+ _ => false,
}
+ }
- if let ty::TraitContainer = self.container {
- let bounds = tcx.explicit_item_bounds(self.def_id);
- let predicates = ty::GenericPredicates { parent: None, predicates: bounds };
- let mut generics =
- clean_ty_generics(cx, tcx.generics_of(self.def_id), predicates);
- // Filter out the bounds that are (likely?) directly attached to the associated type,
- // as opposed to being located in the where clause.
- let mut bounds = generics
- .where_predicates
- .drain_filter(|pred| match *pred {
- WherePredicate::BoundPredicate {
- ty: QPath { ref assoc, ref self_type, ref trait_, .. },
- ..
- } => {
- if assoc.name != my_name {
- return false;
- }
- if trait_.def_id() != self.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.
+ if let ty::TraitContainer = assoc_item.container {
+ let bounds = tcx.explicit_item_bounds(assoc_item.def_id);
+ let predicates = ty::GenericPredicates { parent: None, predicates: bounds };
+ let mut generics =
+ clean_ty_generics(cx, tcx.generics_of(assoc_item.def_id), predicates);
+ // Filter out the bounds that are (likely?) directly attached to the associated type,
+ // as opposed to being located in the where clause.
+ let 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;
}
}
- 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<_>>();
- // 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
- // bound, we remove it, and if we don't then we add the `?Sized` bound
- // at the end.
- match bounds.iter().position(|b| b.is_sized_bound(cx)) {
- Some(i) => {
- bounds.remove(i);
+ true
}
- None => bounds.push(GenericBound::maybe_sized(cx)),
+ _ => false,
+ })
+ .flat_map(|pred| {
+ if let WherePredicate::BoundPredicate { bounds, .. } = pred {
+ bounds
+ } else {
+ unreachable!()
+ }
+ })
+ .collect::<Vec<_>>();
+ // 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
+ // bound, we remove it, and if we don't then we add the `?Sized` bound
+ // at the end.
+ match bounds.iter().position(|b| b.is_sized_bound(cx)) {
+ Some(i) => {
+ bounds.remove(i);
}
+ None => bounds.push(GenericBound::maybe_sized(cx)),
+ }
- if tcx.impl_defaultness(self.def_id).has_value() {
- AssocTypeItem(
- Box::new(Typedef {
- type_: clean_middle_ty(
- tcx.type_of(self.def_id),
- cx,
- Some(self.def_id),
- ),
- generics,
- // FIXME: should we obtain the Type from HIR and pass it on here?
- item_type: None,
- }),
- bounds,
- )
- } else {
- TyAssocTypeItem(Box::new(generics), bounds)
- }
- } else {
- // FIXME: when could this happen? Associated items in inherent impls?
+ if tcx.impl_defaultness(assoc_item.def_id).has_value() {
AssocTypeItem(
Box::new(Typedef {
- type_: clean_middle_ty(tcx.type_of(self.def_id), cx, Some(self.def_id)),
- generics: Generics { params: Vec::new(), where_predicates: Vec::new() },
+ type_: clean_middle_ty(
+ tcx.type_of(assoc_item.def_id),
+ cx,
+ Some(assoc_item.def_id),
+ ),
+ generics,
+ // FIXME: should we obtain the Type from HIR and pass it on here?
item_type: None,
}),
- Vec::new(),
+ bounds,
)
+ } else {
+ TyAssocTypeItem(Box::new(generics), bounds)
}
+ } else {
+ // FIXME: when could this happen? Associated items in inherent impls?
+ AssocTypeItem(
+ Box::new(Typedef {
+ type_: clean_middle_ty(
+ tcx.type_of(assoc_item.def_id),
+ cx,
+ Some(assoc_item.def_id),
+ ),
+ generics: Generics { params: Vec::new(), where_predicates: Vec::new() },
+ item_type: None,
+ }),
+ Vec::new(),
+ )
}
- };
+ }
+ };
- let mut what_rustc_thinks =
- Item::from_def_id_and_parts(self.def_id, Some(self.name), kind, cx);
+ 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(self.def_id));
+ 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
+ // 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
}
fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type {
@@ -1328,18 +1322,18 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type
let trait_def = cx.tcx.associated_item(p.res.def_id()).container_id(cx.tcx);
let trait_ = self::Path {
res: Res::Def(DefKind::Trait, trait_def),
- segments: trait_segments.iter().map(|x| x.clean(cx)).collect(),
+ segments: trait_segments.iter().map(|x| clean_path_segment(x, cx)).collect(),
};
register_res(cx, trait_.res);
let self_def_id = DefId::local(qself.hir_id.owner.local_def_index);
let self_type = clean_ty(qself, cx);
let should_show_cast = compute_should_show_cast(Some(self_def_id), &trait_, &self_type);
- Type::QPath {
- assoc: Box::new(p.segments.last().expect("segments were empty").clean(cx)),
+ Type::QPath(Box::new(QPathData {
+ assoc: clean_path_segment(p.segments.last().expect("segments were empty"), cx),
should_show_cast,
- self_type: Box::new(self_type),
+ self_type,
trait_,
- }
+ }))
}
hir::QPath::TypeRelative(qself, segment) => {
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
@@ -1354,12 +1348,12 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type
let self_def_id = res.opt_def_id();
let self_type = clean_ty(qself, cx);
let should_show_cast = compute_should_show_cast(self_def_id, &trait_, &self_type);
- Type::QPath {
- assoc: Box::new(segment.clean(cx)),
+ Type::QPath(Box::new(QPathData {
+ assoc: clean_path_segment(segment, cx),
should_show_cast,
- self_type: Box::new(self_type),
+ self_type,
trait_,
- }
+ }))
}
hir::QPath::LangItem(..) => bug!("clean: requiring documentation of lang item"),
}
@@ -1398,7 +1392,7 @@ fn maybe_expand_private_type_alias<'tcx>(
}
_ => None,
});
- if let Some(lt) = lifetime.cloned() {
+ 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() };
@@ -1498,22 +1492,22 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
Array(Box::new(clean_ty(ty, cx)), length)
}
TyKind::Tup(tys) => Tuple(tys.iter().map(|ty| clean_ty(ty, cx)).collect()),
- TyKind::OpaqueDef(item_id, _) => {
+ TyKind::OpaqueDef(item_id, _, _) => {
let item = cx.tcx.hir().item(item_id);
if let hir::ItemKind::OpaqueTy(ref ty) = item.kind {
- ImplTrait(ty.bounds.iter().filter_map(|x| x.clean(cx)).collect())
+ ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect())
} else {
unreachable!()
}
}
TyKind::Path(_) => clean_qpath(ty, cx),
TyKind::TraitObject(bounds, ref lifetime, _) => {
- let bounds = bounds.iter().map(|bound| bound.clean(cx)).collect();
+ let bounds = bounds.iter().map(|bound| clean_poly_trait_ref(bound, cx)).collect();
let lifetime =
if !lifetime.is_elided() { Some(clean_lifetime(*lifetime, cx)) } else { None };
DynTrait(bounds, lifetime)
}
- TyKind::BareFn(barefn) => BareFunction(Box::new(barefn.clean(cx))),
+ TyKind::BareFn(barefn) => BareFunction(Box::new(clean_bare_fn_ty(barefn, cx))),
// Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s.
TyKind::Infer | TyKind::Err => Infer,
TyKind::Typeof(..) => panic!("unimplemented type {:?}", ty.kind),
@@ -1598,21 +1592,22 @@ pub(crate) fn clean_middle_ty<'tcx>(
AdtKind::Enum => ItemType::Enum,
};
inline::record_extern_fqn(cx, did, kind);
- let path = external_path(cx, did, false, vec![], substs);
+ let path = external_path(cx, did, false, ThinVec::new(), substs);
Type::Path { path }
}
ty::Foreign(did) => {
inline::record_extern_fqn(cx, did, ItemType::ForeignType);
- let path = external_path(cx, did, false, vec![], InternalSubsts::empty());
+ let path = external_path(cx, did, false, ThinVec::new(), InternalSubsts::empty());
Type::Path { path }
}
- ty::Dynamic(obj, ref reg) => {
+ ty::Dynamic(obj, ref reg, _) => {
// HACK: pick the first `did` as the `did` of the trait object. Someone
// might want to implement "native" support for marker-trait-only
// trait objects.
- let mut dids = obj.principal_def_id().into_iter().chain(obj.auto_traits());
- let did = dids
- .next()
+ let mut dids = obj.auto_traits();
+ let did = obj
+ .principal_def_id()
+ .or_else(|| dids.next())
.unwrap_or_else(|| panic!("found trait object `{:?}` with no traits?", this));
let substs = match obj.principal() {
Some(principal) => principal.skip_binder().substs,
@@ -1623,19 +1618,18 @@ pub(crate) fn clean_middle_ty<'tcx>(
inline::record_extern_fqn(cx, did, ItemType::Trait);
let lifetime = clean_middle_region(*reg);
- let mut bounds = vec![];
-
- for did in dids {
- let empty = cx.tcx.intern_substs(&[]);
- let path = external_path(cx, did, false, vec![], empty);
- inline::record_extern_fqn(cx, did, ItemType::Trait);
- let bound = PolyTrait { trait_: path, generic_params: Vec::new() };
- bounds.push(bound);
- }
+ let mut bounds = dids
+ .map(|did| {
+ let empty = cx.tcx.intern_substs(&[]);
+ let path = external_path(cx, did, false, ThinVec::new(), empty);
+ inline::record_extern_fqn(cx, did, ItemType::Trait);
+ PolyTrait { trait_: path, generic_params: Vec::new() }
+ })
+ .collect::<Vec<_>>();
- let mut bindings = vec![];
- for pb in obj.projection_bounds() {
- bindings.push(TypeBinding {
+ let bindings = obj
+ .projection_bounds()
+ .map(|pb| TypeBinding {
assoc: projection_to_path_segment(
pb.skip_binder()
.lift_to_tcx(cx.tcx)
@@ -1649,8 +1643,8 @@ pub(crate) fn clean_middle_ty<'tcx>(
kind: TypeBindingKind::Equality {
term: clean_middle_term(pb.skip_binder().term, cx),
},
- });
- }
+ })
+ .collect();
let path = external_path(cx, did, false, bindings, substs);
bounds.insert(0, PolyTrait { trait_: path, generic_params: Vec::new() });
@@ -1703,7 +1697,7 @@ pub(crate) fn clean_middle_ty<'tcx>(
}
}
- let bindings: Vec<_> = bounds
+ let bindings: ThinVec<_> = bounds
.iter()
.filter_map(|bound| {
if let ty::PredicateKind::Projection(proj) = bound.kind().skip_binder()
@@ -1724,7 +1718,7 @@ pub(crate) fn clean_middle_ty<'tcx>(
})
.collect();
- Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, &bindings))
+ Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, bindings))
})
.collect::<Vec<_>>();
bounds.extend(regions);
@@ -1783,21 +1777,19 @@ fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
}
}
-pub(crate) fn clean_visibility(vis: ty::Visibility) -> Visibility {
+pub(crate) fn clean_visibility(vis: ty::Visibility<DefId>) -> Visibility {
match vis {
ty::Visibility::Public => Visibility::Public,
- // NOTE: this is not quite right: `ty` uses `Invisible` to mean 'private',
- // while rustdoc really does mean inherited. That means that for enum variants, such as
- // `pub enum E { V }`, `V` will be marked as `Public` by `ty`, but as `Inherited` by rustdoc.
- // Various parts of clean override `tcx.visibility` explicitly to make sure this distinction is captured.
- ty::Visibility::Invisible => Visibility::Inherited,
ty::Visibility::Restricted(module) => Visibility::Restricted(module),
}
}
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,
+ 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(
variant.fields.iter().map(|field| clean_middle_field(field, cx)).collect(),
),
@@ -1814,6 +1806,7 @@ pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocCont
fn clean_variant_data<'tcx>(
variant: &hir::VariantData<'tcx>,
+ disr_expr: &Option<hir::AnonConst>,
cx: &mut DocContext<'tcx>,
) -> Variant {
match variant {
@@ -1824,66 +1817,75 @@ fn clean_variant_data<'tcx>(
hir::VariantData::Tuple(..) => {
Variant::Tuple(variant.fields().iter().map(|x| clean_field(x, cx)).collect())
}
- hir::VariantData::Unit(..) => Variant::CLike,
+ hir::VariantData::Unit(..) => Variant::CLike(disr_expr.map(|disr| Discriminant {
+ expr: Some(disr.body),
+ value: cx.tcx.hir().local_def_id(disr.hir_id).to_def_id(),
+ })),
}
}
fn clean_path<'tcx>(path: &hir::Path<'tcx>, cx: &mut DocContext<'tcx>) -> Path {
- Path { res: path.res, segments: path.segments.iter().map(|x| x.clean(cx)).collect() }
+ Path {
+ res: path.res,
+ segments: path.segments.iter().map(|x| clean_path_segment(x, cx)).collect(),
+ }
}
-impl<'tcx> Clean<'tcx, GenericArgs> for hir::GenericArgs<'tcx> {
- fn clean(&self, cx: &mut DocContext<'tcx>) -> GenericArgs {
- if self.parenthesized {
- let output = clean_ty(self.bindings[0].ty(), cx);
- let output =
- if output != Type::Tuple(Vec::new()) { Some(Box::new(output)) } else { None };
- let inputs = self.inputs().iter().map(|x| clean_ty(x, cx)).collect::<Vec<_>>().into();
- GenericArgs::Parenthesized { inputs, output }
- } else {
- let args = self
- .args
- .iter()
- .map(|arg| match arg {
- hir::GenericArg::Lifetime(lt) if !lt.is_elided() => {
- GenericArg::Lifetime(clean_lifetime(*lt, cx))
- }
- hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()),
- hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty, cx)),
- hir::GenericArg::Const(ct) => GenericArg::Const(Box::new(clean_const(ct, cx))),
- hir::GenericArg::Infer(_inf) => GenericArg::Infer,
- })
- .collect::<Vec<_>>()
- .into();
- let bindings =
- self.bindings.iter().map(|x| clean_type_binding(x, cx)).collect::<Vec<_>>().into();
- GenericArgs::AngleBracketed { args, bindings }
- }
+fn clean_generic_args<'tcx>(
+ generic_args: &hir::GenericArgs<'tcx>,
+ cx: &mut DocContext<'tcx>,
+) -> GenericArgs {
+ if generic_args.parenthesized {
+ let output = clean_ty(generic_args.bindings[0].ty(), cx);
+ let output = if output != Type::Tuple(Vec::new()) { Some(Box::new(output)) } else { None };
+ let inputs =
+ generic_args.inputs().iter().map(|x| clean_ty(x, cx)).collect::<Vec<_>>().into();
+ GenericArgs::Parenthesized { inputs, output }
+ } else {
+ let args = generic_args
+ .args
+ .iter()
+ .map(|arg| match arg {
+ hir::GenericArg::Lifetime(lt) if !lt.is_elided() => {
+ GenericArg::Lifetime(clean_lifetime(*lt, cx))
+ }
+ hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()),
+ hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty, cx)),
+ hir::GenericArg::Const(ct) => GenericArg::Const(Box::new(clean_const(ct, cx))),
+ hir::GenericArg::Infer(_inf) => GenericArg::Infer,
+ })
+ .collect::<Vec<_>>()
+ .into();
+ let bindings =
+ generic_args.bindings.iter().map(|x| clean_type_binding(x, cx)).collect::<ThinVec<_>>();
+ GenericArgs::AngleBracketed { args, bindings }
}
}
-impl<'tcx> Clean<'tcx, PathSegment> for hir::PathSegment<'tcx> {
- fn clean(&self, cx: &mut DocContext<'tcx>) -> PathSegment {
- PathSegment { name: self.ident.name, args: self.args().clean(cx) }
- }
+fn clean_path_segment<'tcx>(
+ path: &hir::PathSegment<'tcx>,
+ cx: &mut DocContext<'tcx>,
+) -> PathSegment {
+ PathSegment { name: path.ident.name, args: clean_generic_args(path.args(), cx) }
}
-impl<'tcx> Clean<'tcx, BareFunctionDecl> for hir::BareFnTy<'tcx> {
- fn clean(&self, cx: &mut DocContext<'tcx>) -> BareFunctionDecl {
- let (generic_params, decl) = enter_impl_trait(cx, |cx| {
- // NOTE: generics must be cleaned before args
- let generic_params = self
- .generic_params
- .iter()
- .filter(|p| !is_elided_lifetime(p))
- .map(|x| clean_generic_param(cx, None, x))
- .collect();
- let args = clean_args_from_types_and_names(cx, self.decl.inputs, self.param_names);
- let decl = clean_fn_decl_with_args(cx, self.decl, args);
- (generic_params, decl)
- });
- BareFunctionDecl { unsafety: self.unsafety, abi: self.abi, decl, generic_params }
- }
+fn clean_bare_fn_ty<'tcx>(
+ bare_fn: &hir::BareFnTy<'tcx>,
+ cx: &mut DocContext<'tcx>,
+) -> BareFunctionDecl {
+ let (generic_params, decl) = enter_impl_trait(cx, |cx| {
+ // NOTE: generics must be cleaned before args
+ let generic_params = bare_fn
+ .generic_params
+ .iter()
+ .filter(|p| !is_elided_lifetime(p))
+ .map(|x| clean_generic_param(cx, None, x))
+ .collect();
+ let args = clean_args_from_types_and_names(cx, bare_fn.decl.inputs, bare_fn.param_names);
+ let decl = clean_fn_decl_with_args(cx, bare_fn.decl, args);
+ (generic_params, decl)
+ });
+ BareFunctionDecl { unsafety: bare_fn.unsafety, abi: bare_fn.abi, decl, generic_params }
}
fn clean_maybe_renamed_item<'tcx>(
@@ -1905,33 +1907,33 @@ fn clean_maybe_renamed_item<'tcx>(
kind: ConstantKind::Local { body: body_id, def_id },
}),
ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy {
- bounds: ty.bounds.iter().filter_map(|x| x.clean(cx)).collect(),
- generics: ty.generics.clean(cx),
+ bounds: ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
+ generics: clean_generics(ty.generics, cx),
}),
ItemKind::TyAlias(hir_ty, generics) => {
let rustdoc_ty = clean_ty(hir_ty, cx);
let ty = clean_middle_ty(hir_ty_to_ty(cx.tcx, hir_ty), cx, None);
TypedefItem(Box::new(Typedef {
type_: rustdoc_ty,
- generics: generics.clean(cx),
+ generics: clean_generics(generics, cx),
item_type: Some(ty),
}))
}
ItemKind::Enum(ref def, generics) => EnumItem(Enum {
- variants: def.variants.iter().map(|v| v.clean(cx)).collect(),
- generics: generics.clean(cx),
+ variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(),
+ generics: clean_generics(generics, cx),
}),
ItemKind::TraitAlias(generics, bounds) => TraitAliasItem(TraitAlias {
- generics: generics.clean(cx),
- bounds: bounds.iter().filter_map(|x| x.clean(cx)).collect(),
+ generics: clean_generics(generics, cx),
+ bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
}),
ItemKind::Union(ref variant_data, generics) => UnionItem(Union {
- generics: generics.clean(cx),
+ generics: clean_generics(generics, cx),
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),
- generics: generics.clean(cx),
+ generics: clean_generics(generics, cx),
fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(),
}),
ItemKind::Impl(impl_) => return clean_impl(impl_, item.hir_id(), cx),
@@ -1946,15 +1948,17 @@ fn clean_maybe_renamed_item<'tcx>(
})
}
ItemKind::Trait(_, _, generics, bounds, item_ids) => {
- let items =
- item_ids.iter().map(|ti| cx.tcx.hir().trait_item(ti.id).clean(cx)).collect();
+ let items = item_ids
+ .iter()
+ .map(|ti| clean_trait_item(cx.tcx.hir().trait_item(ti.id), cx))
+ .collect();
- TraitItem(Trait {
+ TraitItem(Box::new(Trait {
def_id,
items,
- generics: generics.clean(cx),
- bounds: bounds.iter().filter_map(|x| x.clean(cx)).collect(),
- })
+ generics: clean_generics(generics, cx),
+ bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
+ }))
}
ItemKind::ExternCrate(orig_name) => {
return clean_extern_crate(item, name, orig_name, cx);
@@ -1969,14 +1973,12 @@ fn clean_maybe_renamed_item<'tcx>(
})
}
-impl<'tcx> Clean<'tcx, Item> for hir::Variant<'tcx> {
- fn clean(&self, cx: &mut DocContext<'tcx>) -> Item {
- let kind = VariantItem(clean_variant_data(&self.data, cx));
- let what_rustc_thinks =
- Item::from_hir_id_and_parts(self.id, Some(self.ident.name), kind, cx);
- // don't show `pub` for variants, which are always public
- Item { visibility: Inherited, ..what_rustc_thinks }
- }
+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 }
}
fn clean_impl<'tcx>(
@@ -1987,8 +1989,11 @@ fn clean_impl<'tcx>(
let tcx = cx.tcx;
let mut ret = Vec::new();
let trait_ = impl_.of_trait.as_ref().map(|t| clean_trait_ref(t, cx));
- let items =
- impl_.items.iter().map(|ii| tcx.hir().impl_item(ii.id).clean(cx)).collect::<Vec<_>>();
+ let items = impl_
+ .items
+ .iter()
+ .map(|ii| clean_impl_item(tcx.hir().impl_item(ii.id), cx))
+ .collect::<Vec<_>>();
let def_id = tcx.hir().local_def_id(hir_id);
// If this impl block is an implementation of the Deref trait, then we
@@ -2005,7 +2010,7 @@ fn clean_impl<'tcx>(
let mut make_item = |trait_: Option<Path>, for_: Type, items: Vec<Item>| {
let kind = ImplItem(Box::new(Impl {
unsafety: impl_.unsafety,
- generics: impl_.generics.clean(cx),
+ generics: clean_generics(impl_.generics, cx),
trait_,
for_,
items,
@@ -2106,8 +2111,8 @@ fn clean_use_statement<'tcx>(
// `pub(super)` or higher. If the current module is the top level
// module, there isn't really a parent module, which makes the results
// meaningless. In this case, we make sure the answer is `false`.
- let is_visible_from_parent_mod = visibility.is_accessible_from(parent_mod.to_def_id(), cx.tcx)
- && !current_mod.is_top_level_module();
+ let is_visible_from_parent_mod =
+ visibility.is_accessible_from(parent_mod, cx.tcx) && !current_mod.is_top_level_module();
if pub_underscore {
if let Some(ref inline) = inline_attr {
@@ -2202,7 +2207,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
hir::ForeignItemKind::Fn(decl, names, generics) => {
let (generics, decl) = enter_impl_trait(cx, |cx| {
// NOTE: generics must be cleaned before args
- let generics = generics.clean(cx);
+ let generics = clean_generics(generics, cx);
let args = clean_args_from_types_and_names(cx, decl.inputs, names);
let decl = clean_fn_decl_with_args(cx, decl, args);
(generics, decl)
@@ -2229,13 +2234,16 @@ fn clean_type_binding<'tcx>(
cx: &mut DocContext<'tcx>,
) -> TypeBinding {
TypeBinding {
- assoc: PathSegment { name: type_binding.ident.name, args: type_binding.gen_args.clean(cx) },
+ assoc: PathSegment {
+ name: type_binding.ident.name,
+ args: clean_generic_args(type_binding.gen_args, cx),
+ },
kind: match type_binding.kind {
hir::TypeBindingKind::Equality { ref term } => {
TypeBindingKind::Equality { term: clean_hir_term(term, cx) }
}
hir::TypeBindingKind::Constraint { bounds } => TypeBindingKind::Constraint {
- bounds: bounds.iter().filter_map(|b| b.clean(cx)).collect(),
+ bounds: bounds.iter().filter_map(|b| clean_generic_bound(b, cx)).collect(),
},
},
}
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 0e6de842c..f973fd088 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -8,6 +8,7 @@ use std::sync::OnceLock as OnceCell;
use std::{cmp, fmt, iter};
use arrayvec::ArrayVec;
+use thin_vec::ThinVec;
use rustc_ast::attr;
use rustc_ast::util::comments::beautify_doc_string;
@@ -15,7 +16,6 @@ use rustc_ast::{self as ast, AttrStyle};
use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel};
use rustc_const_eval::const_eval::is_unstable_const_fn;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::thin_vec::ThinVec;
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
@@ -415,29 +415,28 @@ impl Item {
.unwrap_or(false)
}
- pub(crate) fn span(&self, tcx: TyCtxt<'_>) -> Span {
+ pub(crate) fn span(&self, tcx: TyCtxt<'_>) -> Option<Span> {
let kind = match &*self.kind {
ItemKind::StrippedItem(k) => k,
_ => &*self.kind,
};
match kind {
- ItemKind::ModuleItem(Module { span, .. }) => *span,
- ItemKind::ImplItem(box Impl { kind: ImplKind::Auto, .. }) => Span::dummy(),
+ ItemKind::ModuleItem(Module { span, .. }) => Some(*span),
+ ItemKind::ImplItem(box Impl { kind: ImplKind::Auto, .. }) => None,
ItemKind::ImplItem(box Impl { kind: ImplKind::Blanket(_), .. }) => {
if let ItemId::Blanket { impl_id, .. } = self.item_id {
- rustc_span(impl_id, tcx)
+ Some(rustc_span(impl_id, tcx))
} else {
panic!("blanket impl item has non-blanket ID")
}
}
- _ => {
- self.item_id.as_def_id().map(|did| rustc_span(did, tcx)).unwrap_or_else(Span::dummy)
- }
+ _ => self.item_id.as_def_id().map(|did| rustc_span(did, tcx)),
}
}
pub(crate) fn attr_span(&self, tcx: TyCtxt<'_>) -> rustc_span::Span {
- crate::passes::span_of_attrs(&self.attrs).unwrap_or_else(|| self.span(tcx).inner())
+ crate::passes::span_of_attrs(&self.attrs)
+ .unwrap_or_else(|| self.span(tcx).map_or(rustc_span::DUMMY_SP, |span| span.inner()))
}
/// Finds the `doc` attribute as a NameValue and returns the corresponding
@@ -483,7 +482,7 @@ impl Item {
cx: &mut DocContext<'_>,
cfg: Option<Arc<Cfg>>,
) -> Item {
- trace!("name={:?}, def_id={:?}", name, def_id);
+ 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
@@ -511,7 +510,7 @@ impl Item {
.get(&self.item_id)
.map_or(&[][..], |v| v.as_slice())
.iter()
- .filter_map(|ItemLink { link: s, link_text, did, ref fragment }| {
+ .filter_map(|ItemLink { link: s, link_text, page_id: did, ref fragment }| {
debug!(?did);
if let Ok((mut href, ..)) = href(*did, cx) {
debug!(?href);
@@ -728,7 +727,7 @@ pub(crate) enum ItemKind {
OpaqueTyItem(OpaqueTy),
StaticItem(Static),
ConstantItem(Constant),
- TraitItem(Trait),
+ TraitItem(Box<Trait>),
TraitAliasItem(TraitAlias),
ImplItem(Box<Impl>),
/// A required method in a trait declaration meaning it's only a function signature.
@@ -802,6 +801,31 @@ impl ItemKind {
| KeywordItem => [].iter(),
}
}
+
+ /// Returns `true` if this item does not appear inside an impl block.
+ pub(crate) fn is_non_assoc(&self) -> bool {
+ matches!(
+ self,
+ StructItem(_)
+ | UnionItem(_)
+ | EnumItem(_)
+ | TraitItem(_)
+ | ModuleItem(_)
+ | ExternCrateItem { .. }
+ | FunctionItem(_)
+ | TypedefItem(_)
+ | OpaqueTyItem(_)
+ | StaticItem(_)
+ | ConstantItem(_)
+ | TraitAliasItem(_)
+ | ForeignFunctionItem(_)
+ | ForeignStaticItem(_)
+ | ForeignTypeItem
+ | MacroItem(_)
+ | ProcMacroItem(_)
+ | PrimitiveItem(_)
+ )
+ }
}
#[derive(Clone, Debug)]
@@ -821,8 +845,6 @@ pub(crate) trait AttributesExt {
fn inner_docs(&self) -> bool;
- fn other_attrs(&self) -> Vec<ast::Attribute>;
-
fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>>;
}
@@ -849,10 +871,6 @@ impl AttributesExt for [ast::Attribute] {
self.iter().find(|a| a.doc_str().is_some()).map_or(true, |a| a.style == AttrStyle::Inner)
}
- fn other_attrs(&self) -> Vec<ast::Attribute> {
- self.iter().filter(|attr| attr.doc_str().is_none()).cloned().collect()
- }
-
fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>> {
let sess = tcx.sess;
let doc_cfg_active = tcx.features().doc_cfg;
@@ -1116,7 +1134,10 @@ pub(crate) struct ItemLink {
/// This may not be the same as `link` if there was a disambiguator
/// in an intra-doc link (e.g. \[`fn@f`\])
pub(crate) link_text: String,
- pub(crate) did: DefId,
+ /// The `DefId` of the Item whose **HTML Page** contains the item being
+ /// linked to. This will be different to `item_id` on item's that don't
+ /// have their own page, such as struct fields and enum variants.
+ pub(crate) page_id: DefId,
/// The url fragment to append to the link
pub(crate) fragment: Option<UrlFragment>,
}
@@ -1137,7 +1158,7 @@ pub struct RenderedLink {
#[derive(Clone, Debug, Default)]
pub(crate) struct Attributes {
pub(crate) doc_strings: Vec<DocFragment>,
- pub(crate) other_attrs: Vec<ast::Attribute>,
+ pub(crate) other_attrs: ast::AttrVec,
}
impl Attributes {
@@ -1180,7 +1201,7 @@ impl Attributes {
doc_only: bool,
) -> Attributes {
let mut doc_strings = Vec::new();
- let mut other_attrs = Vec::new();
+ let mut other_attrs = ast::AttrVec::new();
for (attr, parent_module) in attrs {
if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() {
trace!("got doc_str={doc_str:?}");
@@ -1285,7 +1306,7 @@ impl GenericBound {
pub(crate) fn maybe_sized(cx: &mut DocContext<'_>) -> GenericBound {
let did = cx.tcx.require_lang_item(LangItem::Sized, None);
let empty = cx.tcx.intern_substs(&[]);
- let path = external_path(cx, did, false, vec![], empty);
+ let path = external_path(cx, did, false, ThinVec::new(), empty);
inline::record_extern_fqn(cx, did, ItemType::Trait);
GenericBound::TraitBound(
PolyTrait { trait_: path, generic_params: Vec::new() },
@@ -1557,13 +1578,7 @@ pub(crate) enum Type {
BorrowedRef { lifetime: Option<Lifetime>, mutability: Mutability, type_: Box<Type> },
/// A qualified path to an associated item: `<Type as Trait>::Name`
- QPath {
- assoc: Box<PathSegment>,
- self_type: Box<Type>,
- /// FIXME: compute this field on demand.
- should_show_cast: bool,
- trait_: Path,
- },
+ QPath(Box<QPathData>),
/// A type that is inferred: `_`
Infer,
@@ -1661,8 +1676,8 @@ impl Type {
}
pub(crate) fn projection(&self) -> Option<(&Type, DefId, PathSegment)> {
- if let QPath { self_type, trait_, assoc, .. } = self {
- Some((self_type, trait_.def_id(), *assoc.clone()))
+ if let QPath(box QPathData { self_type, trait_, assoc, .. }) = self {
+ Some((self_type, trait_.def_id(), assoc.clone()))
} else {
None
}
@@ -1686,7 +1701,7 @@ impl Type {
Slice(..) => PrimitiveType::Slice,
Array(..) => PrimitiveType::Array,
RawPointer(..) => PrimitiveType::RawPointer,
- QPath { ref self_type, .. } => return self_type.inner_def_id(cache),
+ QPath(box QPathData { ref self_type, .. }) => return self_type.inner_def_id(cache),
Generic(_) | Infer | ImplTrait(_) => return None,
};
cache.and_then(|c| Primitive(t).def_id(c))
@@ -1700,6 +1715,15 @@ impl Type {
}
}
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub(crate) struct QPathData {
+ pub assoc: PathSegment,
+ pub self_type: Type,
+ /// FIXME: compute this field on demand.
+ pub should_show_cast: bool,
+ pub trait_: Path,
+}
+
/// A primitive (aka, builtin) type.
///
/// This represents things like `i32`, `str`, etc.
@@ -2077,7 +2101,7 @@ impl Enum {
#[derive(Clone, Debug)]
pub(crate) enum Variant {
- CLike,
+ CLike(Option<Discriminant>),
Tuple(Vec<Item>),
Struct(VariantStruct),
}
@@ -2086,11 +2110,31 @@ impl Variant {
pub(crate) fn has_stripped_entries(&self) -> Option<bool> {
match *self {
Self::Struct(ref struct_) => Some(struct_.has_stripped_entries()),
- Self::CLike | Self::Tuple(_) => None,
+ Self::CLike(..) | Self::Tuple(_) => None,
}
}
}
+#[derive(Clone, Debug)]
+pub(crate) struct Discriminant {
+ // In the case of cross crate re-exports, we don't have the nessesary information
+ // to reconstruct the expression of the discriminant, only the value.
+ pub(super) expr: Option<BodyId>,
+ pub(super) value: DefId,
+}
+
+impl Discriminant {
+ /// Will be `None` in the case of cross-crate reexports, and may be
+ /// simplified
+ pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> Option<String> {
+ self.expr.map(|body| print_const_expr(tcx, body))
+ }
+ /// Will always be a machine readable number, without underscores or suffixes.
+ pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> String {
+ print_evaluated_const(tcx, self.value, false).unwrap()
+ }
+}
+
/// Small wrapper around [`rustc_span::Span`] that adds helper methods
/// and enforces calling [`rustc_span::Span::source_callsite()`].
#[derive(Copy, Clone, Debug)]
@@ -2109,14 +2153,6 @@ impl Span {
self.0
}
- pub(crate) fn dummy() -> Self {
- Self(rustc_span::DUMMY_SP)
- }
-
- pub(crate) fn is_dummy(&self) -> bool {
- self.0.is_dummy()
- }
-
pub(crate) fn filename(&self, sess: &Session) -> FileName {
sess.source_map().span_to_filename(self.0)
}
@@ -2325,7 +2361,7 @@ impl ConstantKind {
match *self {
ConstantKind::TyConst { .. } | ConstantKind::Anonymous { .. } => None,
ConstantKind::Extern { def_id } | ConstantKind::Local { def_id, .. } => {
- print_evaluated_const(tcx, def_id)
+ print_evaluated_const(tcx, def_id, true)
}
}
}
@@ -2495,14 +2531,16 @@ impl SubstParam {
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
mod size_asserts {
use super::*;
+ use rustc_data_structures::static_assert_size;
// These are in alphabetical order, which is easy to maintain.
- rustc_data_structures::static_assert_size!(Crate, 72); // frequently moved by-value
- rustc_data_structures::static_assert_size!(DocFragment, 32);
- rustc_data_structures::static_assert_size!(GenericArg, 80);
- rustc_data_structures::static_assert_size!(GenericArgs, 32);
- rustc_data_structures::static_assert_size!(GenericParamDef, 56);
- rustc_data_structures::static_assert_size!(Item, 56);
- rustc_data_structures::static_assert_size!(ItemKind, 112);
- rustc_data_structures::static_assert_size!(PathSegment, 40);
- rustc_data_structures::static_assert_size!(Type, 72);
+ static_assert_size!(Crate, 72); // frequently moved by-value
+ static_assert_size!(DocFragment, 32);
+ #[cfg(not(bootstrap))]
+ static_assert_size!(GenericArg, 56);
+ static_assert_size!(GenericArgs, 32);
+ static_assert_size!(GenericParamDef, 56);
+ static_assert_size!(Item, 56);
+ static_assert_size!(ItemKind, 96);
+ static_assert_size!(PathSegment, 40);
+ static_assert_size!(Type, 56);
}
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 43e71e90a..3eaedaf10 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -2,9 +2,9 @@ use crate::clean::auto_trait::AutoTraitFinder;
use crate::clean::blanket_impl::BlanketImplFinder;
use crate::clean::render_macro_matchers::render_macro_matcher;
use crate::clean::{
- clean_middle_const, clean_middle_region, clean_middle_ty, inline, Clean, Crate, ExternalCrate,
- Generic, GenericArg, GenericArgs, ImportSource, Item, ItemKind, Lifetime, Path, PathSegment,
- Primitive, PrimitiveType, Type, TypeBinding, Visibility,
+ 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,
};
use crate::core::DocContext;
use crate::formats::item_type::ItemType;
@@ -12,7 +12,6 @@ use crate::visit_lib::LibEmbargoVisitor;
use rustc_ast as ast;
use rustc_ast::tokenstream::TokenTree;
-use rustc_data_structures::thin_vec::ThinVec;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
@@ -23,6 +22,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;
#[cfg(test)]
mod tests;
@@ -37,7 +37,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
// Clean the crate, translating the entire librustc_ast AST to one that is
// understood by rustdoc.
- let mut module = module.clean(cx);
+ let mut module = clean_doc_module(&module, cx);
match *module.kind {
ItemKind::ModuleItem(ref module) => {
@@ -102,7 +102,7 @@ fn external_generic_args<'tcx>(
cx: &mut DocContext<'tcx>,
did: DefId,
has_self: bool,
- bindings: Vec<TypeBinding>,
+ bindings: ThinVec<TypeBinding>,
substs: SubstsRef<'tcx>,
) -> GenericArgs {
let args = substs_to_args(cx, substs, has_self);
@@ -112,7 +112,7 @@ fn external_generic_args<'tcx>(
// 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: bindings.into() },
+ _ => return GenericArgs::AngleBracketed { args: args.into(), bindings },
};
let output = None;
// FIXME(#20299) return type comes from a projection now
@@ -130,7 +130,7 @@ pub(super) fn external_path<'tcx>(
cx: &mut DocContext<'tcx>,
did: DefId,
has_self: bool,
- bindings: Vec<TypeBinding>,
+ bindings: ThinVec<TypeBinding>,
substs: SubstsRef<'tcx>,
) -> Path {
let def_kind = cx.tcx.def_kind(did);
@@ -235,14 +235,13 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
match n.kind() {
ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted }) => {
- let mut s = if let Some(def) = def.as_local() {
+ assert_eq!(promoted, ());
+ let s = if let Some(def) = def.as_local() {
print_const_expr(cx.tcx, cx.tcx.hir().body_owned_by(def.did))
} else {
inline::print_inlined_const(cx.tcx, def.did)
};
- if let Some(promoted) = promoted {
- s.push_str(&format!("::{:?}", promoted))
- }
+
s
}
_ => {
@@ -261,7 +260,11 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
}
}
-pub(crate) fn print_evaluated_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<String> {
+pub(crate) fn print_evaluated_const(
+ tcx: TyCtxt<'_>,
+ def_id: DefId,
+ underscores_and_type: bool,
+) -> Option<String> {
tcx.const_eval_poly(def_id).ok().and_then(|val| {
let ty = tcx.type_of(def_id);
match (val, ty.kind()) {
@@ -269,7 +272,7 @@ pub(crate) fn print_evaluated_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<St
(ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
(ConstValue::Scalar(_), _) => {
let const_ = mir::ConstantKind::from_value(val, ty);
- Some(print_const_with_custom_print_scalar(tcx, const_))
+ Some(print_const_with_custom_print_scalar(tcx, const_, underscores_and_type))
}
_ => None,
}
@@ -302,23 +305,35 @@ fn format_integer_with_underscore_sep(num: &str) -> String {
.collect()
}
-fn print_const_with_custom_print_scalar(tcx: TyCtxt<'_>, ct: mir::ConstantKind<'_>) -> String {
+fn print_const_with_custom_print_scalar(
+ tcx: TyCtxt<'_>,
+ ct: mir::ConstantKind<'_>,
+ underscores_and_type: bool,
+) -> String {
// Use a slightly different format for integer types which always shows the actual value.
// For all other types, fallback to the original `pretty_print_const`.
match (ct, ct.ty().kind()) {
(mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Uint(ui)) => {
- format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
+ if underscores_and_type {
+ format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
+ } else {
+ int.to_string()
+ }
}
(mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Int(i)) => {
let ty = tcx.lift(ct.ty()).unwrap();
let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size;
let data = int.assert_bits(size);
let sign_extended_data = size.sign_extend(data) as i128;
- format!(
- "{}{}",
- format_integer_with_underscore_sep(&sign_extended_data.to_string()),
- i.name_str()
- )
+ if underscores_and_type {
+ format!(
+ "{}{}",
+ format_integer_with_underscore_sep(&sign_extended_data.to_string()),
+ i.name_str()
+ )
+ } else {
+ sign_extended_data.to_string()
+ }
}
_ => ct.to_string(),
}
@@ -475,30 +490,14 @@ pub(crate) fn register_res(cx: &mut DocContext<'_>, res: Res) -> DefId {
use DefKind::*;
debug!("register_res({:?})", res);
- let (did, kind) = match res {
- // These should be added to the cache using `record_extern_fqn`.
+ let (kind, did) = match res {
Res::Def(
kind @ (AssocTy | AssocFn | AssocConst | Variant | Fn | TyAlias | Enum | Trait | Struct
| Union | Mod | ForeignTy | Const | Static(_) | Macro(..) | TraitAlias),
- i,
- ) => (i, kind.into()),
- // This is part of a trait definition or trait impl; document the trait.
- Res::SelfTy { trait_: Some(trait_def_id), alias_to: _ } => (trait_def_id, ItemType::Trait),
- // This is an inherent impl or a type definition; it doesn't have its own page.
- Res::SelfTy { trait_: None, alias_to: Some((item_def_id, _)) } => return item_def_id,
- Res::SelfTy { trait_: None, alias_to: None }
- | Res::PrimTy(_)
- | Res::ToolMod
- | Res::SelfCtor(_)
- | Res::Local(_)
- | Res::NonMacroAttr(_)
- | Res::Err => return res.def_id(),
- Res::Def(
- TyParam | ConstParam | Ctor(..) | ExternCrate | Use | ForeignMod | AnonConst
- | InlineConst | OpaqueTy | Field | LifetimeParam | GlobalAsm | Impl | Closure
- | Generator,
- id,
- ) => return id,
+ did,
+ ) => (kind.into(), did),
+
+ _ => panic!("register_res: unexpected {:?}", res),
};
if did.is_local() {
return did;
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 8a8cc272e..932533db0 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -412,7 +412,13 @@ impl Options {
let to_check = matches.opt_strs("check-theme");
if !to_check.is_empty() {
- let paths = theme::load_css_paths(static_files::themes::LIGHT.as_bytes());
+ let paths = match theme::load_css_paths(static_files::themes::LIGHT) {
+ Ok(p) => p,
+ Err(e) => {
+ diag.struct_err(&e.to_string()).emit();
+ return Err(1);
+ }
+ };
let mut errors = 0;
println!("rustdoc: [check-theme] Starting tests! (Ignoring all other arguments)");
@@ -547,7 +553,13 @@ impl Options {
let mut themes = Vec::new();
if matches.opt_present("theme") {
- let paths = theme::load_css_paths(static_files::themes::LIGHT.as_bytes());
+ let paths = match theme::load_css_paths(static_files::themes::LIGHT) {
+ Ok(p) => p,
+ Err(e) => {
+ diag.struct_err(&e.to_string()).emit();
+ return Err(1);
+ }
+ };
for (theme_file, theme_s) in
matches.opt_strs("theme").iter().map(|s| (PathBuf::from(&s), s.to_owned()))
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 35964e3ba..20ae102bc 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1289,14 +1289,9 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx>
});
}
- fn visit_variant(
- &mut self,
- v: &'hir hir::Variant<'_>,
- g: &'hir hir::Generics<'_>,
- item_id: hir::HirId,
- ) {
+ fn visit_variant(&mut self, v: &'hir hir::Variant<'_>) {
self.visit_testable(v.ident.to_string(), v.id, v.span, |this| {
- intravisit::walk_variant(this, v, g, item_id);
+ intravisit::walk_variant(this, v);
});
}
diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs
index 6b7e67e2c..ed702f5c4 100644
--- a/src/librustdoc/fold.rs
+++ b/src/librustdoc/fold.rs
@@ -46,7 +46,7 @@ pub(crate) trait DocFolder: Sized {
let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
VariantItem(Variant::Tuple(fields))
}
- Variant::CLike => VariantItem(Variant::CLike),
+ Variant::CLike(disr) => VariantItem(Variant::CLike(disr)),
},
ExternCrateItem { src: _ }
| ImportItem(_)
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 2b2691e53..86392610d 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -227,7 +227,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
if let clean::TraitItem(ref t) = *item.kind {
self.cache.traits.entry(item.item_id.expect_def_id()).or_insert_with(|| {
clean::TraitWithExtraInfo {
- trait_: t.clone(),
+ trait_: *t.clone(),
is_notable: item.attrs.has_doc_flag(sym::notable_trait),
}
});
diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs
index 0a7ee2005..f21e60a64 100644
--- a/src/librustdoc/formats/item_type.rs
+++ b/src/librustdoc/formats/item_type.rs
@@ -135,6 +135,7 @@ impl From<DefKind> for ItemType {
| DefKind::AnonConst
| DefKind::InlineConst
| DefKind::OpaqueTy
+ | DefKind::ImplTraitPlaceholder
| DefKind::Field
| DefKind::LifetimeParam
| DefKind::GlobalAsm
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 36a47b05c..b499e186c 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -152,7 +152,7 @@ impl Buffer {
}
}
-fn comma_sep<T: fmt::Display>(
+pub(crate) fn comma_sep<T: fmt::Display>(
items: impl Iterator<Item = T>,
space_after_comma: bool,
) -> impl fmt::Display {
@@ -349,8 +349,7 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
let where_preds = comma_sep(where_predicates, false);
let clause = if f.alternate() {
if ending == Ending::Newline {
- // add a space so stripping <br> tags and breaking spaces still renders properly
- format!(" where{where_preds}, ")
+ format!(" where{where_preds},")
} else {
format!(" where{where_preds}")
}
@@ -364,20 +363,16 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
if ending == Ending::Newline {
let mut clause = "&nbsp;".repeat(indent.saturating_sub(1));
- // add a space so stripping <br> tags and breaking spaces still renders properly
- write!(
- clause,
- " <span class=\"where fmt-newline\">where{where_preds},&nbsp;</span>"
- )?;
+ write!(clause, "<span class=\"where fmt-newline\">where{where_preds},</span>")?;
clause
} else {
// insert a <br> tag after a single space but before multiple spaces at the start
if indent == 0 {
- format!(" <br><span class=\"where\">where{where_preds}</span>")
+ format!("<br><span class=\"where\">where{where_preds}</span>")
} else {
let mut clause = br_with_padding;
- clause.truncate(clause.len() - 5 * "&nbsp;".len());
- write!(clause, " <span class=\"where\">where{where_preds}</span>")?;
+ clause.truncate(clause.len() - 4 * "&nbsp;".len());
+ write!(clause, "<span class=\"where\">where{where_preds}</span>")?;
clause
}
}
@@ -592,7 +587,7 @@ fn generate_macro_def_id_path(
}
})
.collect();
- let relative = fqp.iter().map(|elem| elem.to_string());
+ let mut relative = fqp.iter().map(|elem| elem.to_string());
let cstore = CStore::from_tcx(tcx);
// We need this to prevent a `panic` when this function is used from intra doc links...
if !cstore.has_crate_data(def_id.krate) {
@@ -612,7 +607,7 @@ fn generate_macro_def_id_path(
let mut path = if is_macro_2 {
once(crate_name.clone()).chain(relative).collect()
} else {
- vec![crate_name.clone(), relative.last().unwrap()]
+ vec![crate_name.clone(), relative.next_back().unwrap()]
};
if path.len() < 2 {
// The minimum we can have is the crate name followed by the macro name. If shorter, then
@@ -1079,7 +1074,12 @@ fn fmt_type<'cx>(
write!(f, "impl {}", print_generic_bounds(bounds, cx))
}
}
- clean::QPath { ref assoc, ref self_type, ref trait_, should_show_cast } => {
+ clean::QPath(box clean::QPathData {
+ ref assoc,
+ ref self_type,
+ ref trait_,
+ should_show_cast,
+ }) => {
if f.alternate() {
if should_show_cast {
write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))?
@@ -1305,22 +1305,19 @@ impl clean::FnDecl {
/// <br>Used to determine line-wrapping.
/// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is
/// necessary.
- /// * `asyncness`: Whether the function is async or not.
pub(crate) fn full_print<'a, 'tcx: 'a>(
&'a self,
header_len: usize,
indent: usize,
- asyncness: hir::IsAsync,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
- display_fn(move |f| self.inner_full_print(header_len, indent, asyncness, f, cx))
+ display_fn(move |f| self.inner_full_print(header_len, indent, f, cx))
}
fn inner_full_print(
&self,
header_len: usize,
indent: usize,
- asyncness: hir::IsAsync,
f: &mut fmt::Formatter<'_>,
cx: &Context<'_>,
) -> fmt::Result {
@@ -1385,15 +1382,9 @@ impl clean::FnDecl {
args_plain.push_str(", ...");
}
- let arrow_plain;
- let arrow = if let hir::IsAsync::Async = asyncness {
- let output = self.sugared_async_return_type();
- arrow_plain = format!("{:#}", output.print(cx));
- if f.alternate() { arrow_plain.clone() } else { format!("{}", output.print(cx)) }
- } else {
- arrow_plain = format!("{:#}", self.output.print(cx));
- if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) }
- };
+ let arrow_plain = format!("{:#}", self.output.print(cx));
+ let arrow =
+ if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) };
let declaration_len = header_len + args_plain.len() + arrow_plain.len();
let output = if declaration_len > 80 {
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 05547ea15..8922bf377 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -29,31 +29,74 @@ pub(crate) struct HrefContext<'a, 'b, 'c> {
/// This field is used to know "how far" from the top of the directory we are to link to either
/// documentation pages or other source pages.
pub(crate) root_path: &'c str,
+ /// This field is used to calculate precise local URLs.
+ pub(crate) current_href: &'c str,
}
/// Decorations are represented as a map from CSS class to vector of character ranges.
/// Each range will be wrapped in a span with that class.
+#[derive(Default)]
pub(crate) struct DecorationInfo(pub(crate) FxHashMap<&'static str, Vec<(u32, u32)>>);
-/// Highlights `src`, returning the HTML output.
-pub(crate) fn render_with_highlighting(
+#[derive(Eq, PartialEq, Clone, Copy)]
+pub(crate) enum Tooltip {
+ Ignore,
+ CompileFail,
+ ShouldPanic,
+ Edition(Edition),
+ None,
+}
+
+/// Highlights `src` as an inline example, returning the HTML output.
+pub(crate) fn render_example_with_highlighting(
src: &str,
out: &mut Buffer,
- class: Option<&str>,
+ tooltip: Tooltip,
playground_button: Option<&str>,
- tooltip: Option<(Option<Edition>, &str)>,
- edition: Edition,
- extra_content: Option<Buffer>,
- href_context: Option<HrefContext<'_, '_, '_>>,
- decoration_info: Option<DecorationInfo>,
) {
- debug!("highlighting: ================\n{}\n==============", src);
- if let Some((edition_info, class)) = tooltip {
+ write_header(out, "rust-example-rendered", None, tooltip);
+ write_code(out, src, None, None);
+ write_footer(out, playground_button);
+}
+
+/// Highlights `src` as a macro, returning the HTML output.
+pub(crate) fn render_macro_with_highlighting(src: &str, out: &mut Buffer) {
+ write_header(out, "macro", None, Tooltip::None);
+ write_code(out, src, None, None);
+ write_footer(out, None);
+}
+
+/// Highlights `src` as a source code page, returning the HTML output.
+pub(crate) fn render_source_with_highlighting(
+ src: &str,
+ out: &mut Buffer,
+ line_numbers: Buffer,
+ href_context: HrefContext<'_, '_, '_>,
+ decoration_info: DecorationInfo,
+) {
+ write_header(out, "", Some(line_numbers), Tooltip::None);
+ write_code(out, src, Some(href_context), Some(decoration_info));
+ write_footer(out, None);
+}
+
+fn write_header(out: &mut Buffer, class: &str, extra_content: Option<Buffer>, tooltip: Tooltip) {
+ write!(
+ out,
+ "<div class=\"example-wrap{}\">",
+ match tooltip {
+ Tooltip::Ignore => " ignore",
+ Tooltip::CompileFail => " compile_fail",
+ Tooltip::ShouldPanic => " should_panic",
+ Tooltip::Edition(_) => " edition",
+ Tooltip::None => "",
+ },
+ );
+
+ if tooltip != Tooltip::None {
write!(
out,
- "<div class='information'><div class='tooltip {}'{}>ⓘ</div></div>",
- class,
- if let Some(edition_info) = edition_info {
+ "<div class='tooltip'{}>ⓘ</div>",
+ if let Tooltip::Edition(edition_info) = tooltip {
format!(" data-edition=\"{}\"", edition_info)
} else {
String::new()
@@ -61,24 +104,115 @@ pub(crate) fn render_with_highlighting(
);
}
- write_header(out, class, extra_content);
- write_code(out, src, edition, href_context, decoration_info);
- write_footer(out, playground_button);
-}
-
-fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option<Buffer>) {
- write!(out, "<div class=\"example-wrap\">");
if let Some(extra) = extra_content {
out.push_buffer(extra);
}
- if let Some(class) = class {
- write!(out, "<pre class=\"rust {}\">", class);
- } else {
+ if class.is_empty() {
write!(out, "<pre class=\"rust\">");
+ } else {
+ write!(out, "<pre class=\"rust {class}\">");
}
write!(out, "<code>");
}
+/// Check if two `Class` can be merged together. In the following rules, "unclassified" means `None`
+/// basically (since it's `Option<Class>`). The following rules apply:
+///
+/// * If two `Class` have the same variant, then they can be merged.
+/// * If the other `Class` is unclassified and only contains white characters (backline,
+/// whitespace, etc), it can be merged.
+/// * `Class::Ident` is considered the same as unclassified (because it doesn't have an associated
+/// CSS class).
+fn can_merge(class1: Option<Class>, class2: Option<Class>, text: &str) -> bool {
+ match (class1, class2) {
+ (Some(c1), Some(c2)) => c1.is_equal_to(c2),
+ (Some(Class::Ident(_)), None) | (None, Some(Class::Ident(_))) => true,
+ (Some(_), None) | (None, Some(_)) => text.trim().is_empty(),
+ (None, None) => true,
+ }
+}
+
+/// This type is used as a conveniency to prevent having to pass all its fields as arguments into
+/// the various functions (which became its methods).
+struct TokenHandler<'a, 'b, 'c, 'd, 'e> {
+ out: &'a mut Buffer,
+ /// It contains the closing tag and the associated `Class`.
+ closing_tags: Vec<(&'static str, Class)>,
+ /// This is used because we don't automatically generate the closing tag on `ExitSpan` in
+ /// case an `EnterSpan` event with the same class follows.
+ pending_exit_span: Option<Class>,
+ /// `current_class` and `pending_elems` are used to group HTML elements with same `class`
+ /// attributes to reduce the DOM size.
+ current_class: Option<Class>,
+ /// We need to keep the `Class` for each element because it could contain a `Span` which is
+ /// used to generate links.
+ pending_elems: Vec<(&'b str, Option<Class>)>,
+ href_context: Option<HrefContext<'c, 'd, 'e>>,
+}
+
+impl<'a, 'b, 'c, 'd, 'e> TokenHandler<'a, 'b, 'c, 'd, 'e> {
+ fn handle_exit_span(&mut self) {
+ // We can't get the last `closing_tags` element using `pop()` because `closing_tags` is
+ // being used in `write_pending_elems`.
+ let class = self.closing_tags.last().expect("ExitSpan without EnterSpan").1;
+ // We flush everything just in case...
+ self.write_pending_elems(Some(class));
+
+ exit_span(self.out, self.closing_tags.pop().expect("ExitSpan without EnterSpan").0);
+ self.pending_exit_span = None;
+ }
+
+ /// Write all the pending elements sharing a same (or at mergeable) `Class`.
+ ///
+ /// If there is a "parent" (if a `EnterSpan` event was encountered) and the parent can be merged
+ /// with the elements' class, then we simply write the elements since the `ExitSpan` event will
+ /// close the tag.
+ ///
+ /// Otherwise, if there is only one pending element, we let the `string` function handle both
+ /// opening and closing the tag, otherwise we do it into this function.
+ ///
+ /// It returns `true` if `current_class` must be set to `None` afterwards.
+ fn write_pending_elems(&mut self, current_class: Option<Class>) -> bool {
+ if self.pending_elems.is_empty() {
+ return false;
+ }
+ if let Some((_, parent_class)) = self.closing_tags.last() &&
+ can_merge(current_class, Some(*parent_class), "")
+ {
+ for (text, class) in self.pending_elems.iter() {
+ string(self.out, Escape(text), *class, &self.href_context, false);
+ }
+ } else {
+ // We only want to "open" the tag ourselves if we have more than one pending and if the
+ // current parent tag is not the same as our pending content.
+ let close_tag = if self.pending_elems.len() > 1 && current_class.is_some() {
+ Some(enter_span(self.out, current_class.unwrap(), &self.href_context))
+ } else {
+ None
+ };
+ for (text, class) in self.pending_elems.iter() {
+ string(self.out, Escape(text), *class, &self.href_context, close_tag.is_none());
+ }
+ if let Some(close_tag) = close_tag {
+ exit_span(self.out, close_tag);
+ }
+ }
+ self.pending_elems.clear();
+ true
+ }
+}
+
+impl<'a, 'b, 'c, 'd, 'e> Drop for TokenHandler<'a, 'b, 'c, 'd, 'e> {
+ /// When leaving, we need to flush all pending data to not have missing content.
+ fn drop(&mut self) {
+ if self.pending_exit_span.is_some() {
+ self.handle_exit_span();
+ } else {
+ self.write_pending_elems(self.current_class);
+ }
+ }
+}
+
/// Convert the given `src` source code into HTML by adding classes for highlighting.
///
/// This code is used to render code blocks (in the documentation) as well as the source code pages.
@@ -93,27 +227,74 @@ fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option<Buf
fn write_code(
out: &mut Buffer,
src: &str,
- edition: Edition,
href_context: Option<HrefContext<'_, '_, '_>>,
decoration_info: Option<DecorationInfo>,
) {
// This replace allows to fix how the code source with DOS backline characters is displayed.
let src = src.replace("\r\n", "\n");
- let mut closing_tags: Vec<&'static str> = Vec::new();
+ let mut token_handler = TokenHandler {
+ out,
+ closing_tags: Vec::new(),
+ pending_exit_span: None,
+ current_class: None,
+ pending_elems: Vec::new(),
+ href_context,
+ };
+
Classifier::new(
&src,
- edition,
- href_context.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP),
+ token_handler.href_context.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP),
decoration_info,
)
.highlight(&mut |highlight| {
match highlight {
- Highlight::Token { text, class } => string(out, Escape(text), class, &href_context),
+ Highlight::Token { text, class } => {
+ // If we received a `ExitSpan` event and then have a non-compatible `Class`, we
+ // need to close the `<span>`.
+ let need_current_class_update = if let Some(pending) = token_handler.pending_exit_span &&
+ !can_merge(Some(pending), class, text) {
+ token_handler.handle_exit_span();
+ true
+ // If the two `Class` are different, time to flush the current content and start
+ // a new one.
+ } else if !can_merge(token_handler.current_class, class, text) {
+ token_handler.write_pending_elems(token_handler.current_class);
+ true
+ } else {
+ token_handler.current_class.is_none()
+ };
+
+ if need_current_class_update {
+ token_handler.current_class = class.map(Class::dummy);
+ }
+ token_handler.pending_elems.push((text, class));
+ }
Highlight::EnterSpan { class } => {
- closing_tags.push(enter_span(out, class, &href_context))
+ let mut should_add = true;
+ if let Some(pending_exit_span) = token_handler.pending_exit_span {
+ if class.is_equal_to(pending_exit_span) {
+ should_add = false;
+ } else {
+ token_handler.handle_exit_span();
+ }
+ } else {
+ // We flush everything just in case...
+ if token_handler.write_pending_elems(token_handler.current_class) {
+ token_handler.current_class = None;
+ }
+ }
+ if should_add {
+ let closing_tag = enter_span(token_handler.out, class, &token_handler.href_context);
+ token_handler.closing_tags.push((closing_tag, class));
+ }
+
+ token_handler.current_class = None;
+ token_handler.pending_exit_span = None;
}
Highlight::ExitSpan => {
- exit_span(out, closing_tags.pop().expect("ExitSpan without EnterSpan"))
+ token_handler.current_class = None;
+ token_handler.pending_exit_span =
+ Some(token_handler.closing_tags.last().as_ref().expect("ExitSpan without EnterSpan").1);
}
};
});
@@ -130,15 +311,15 @@ enum Class {
DocComment,
Attribute,
KeyWord,
- // Keywords that do pointer/reference stuff.
+ /// Keywords that do pointer/reference stuff.
RefKeyWord,
Self_(Span),
- Op,
Macro(Span),
MacroNonTerminal,
String,
Number,
Bool,
+ /// `Ident` isn't rendered in the HTML but we still need it for the `Span` it contains.
Ident(Span),
Lifetime,
PreludeTy,
@@ -148,6 +329,31 @@ enum Class {
}
impl Class {
+ /// It is only looking at the variant, not the variant content.
+ ///
+ /// It is used mostly to group multiple similar HTML elements into one `<span>` instead of
+ /// multiple ones.
+ fn is_equal_to(self, other: Self) -> bool {
+ match (self, other) {
+ (Self::Self_(_), Self::Self_(_))
+ | (Self::Macro(_), Self::Macro(_))
+ | (Self::Ident(_), Self::Ident(_)) => true,
+ (Self::Decoration(c1), Self::Decoration(c2)) => c1 == c2,
+ (x, y) => x == y,
+ }
+ }
+
+ /// If `self` contains a `Span`, it'll be replaced with `DUMMY_SP` to prevent creating links
+ /// on "empty content" (because of the attributes merge).
+ fn dummy(self) -> Self {
+ match self {
+ Self::Self_(_) => Self::Self_(DUMMY_SP),
+ Self::Macro(_) => Self::Macro(DUMMY_SP),
+ Self::Ident(_) => Self::Ident(DUMMY_SP),
+ s => s,
+ }
+ }
+
/// Returns the css class expected by rustdoc for each `Class`.
fn as_html(self) -> &'static str {
match self {
@@ -157,13 +363,12 @@ impl Class {
Class::KeyWord => "kw",
Class::RefKeyWord => "kw-2",
Class::Self_(_) => "self",
- Class::Op => "op",
Class::Macro(_) => "macro",
Class::MacroNonTerminal => "macro-nonterminal",
Class::String => "string",
Class::Number => "number",
Class::Bool => "bool-val",
- Class::Ident(_) => "ident",
+ Class::Ident(_) => "",
Class::Lifetime => "lifetime",
Class::PreludeTy => "prelude-ty",
Class::PreludeVal => "prelude-val",
@@ -182,7 +387,6 @@ impl Class {
| Self::Attribute
| Self::KeyWord
| Self::RefKeyWord
- | Self::Op
| Self::MacroNonTerminal
| Self::String
| Self::Number
@@ -220,7 +424,7 @@ impl<'a> Iterator for TokenIter<'a> {
}
/// Classifies into identifier class; returns `None` if this is a non-keyword identifier.
-fn get_real_ident_class(text: &str, edition: Edition, allow_path_keywords: bool) -> Option<Class> {
+fn get_real_ident_class(text: &str, allow_path_keywords: bool) -> Option<Class> {
let ignore: &[&str] =
if allow_path_keywords { &["self", "Self", "super", "crate"] } else { &["self", "Self"] };
if ignore.iter().any(|k| *k == text) {
@@ -229,7 +433,7 @@ fn get_real_ident_class(text: &str, edition: Edition, allow_path_keywords: bool)
Some(match text {
"ref" | "mut" => Class::RefKeyWord,
"false" | "true" => Class::Bool,
- _ if Symbol::intern(text).is_reserved(|| edition) => Class::KeyWord,
+ _ if Symbol::intern(text).is_reserved(|| Edition::Edition2021) => Class::KeyWord,
_ => return None,
})
}
@@ -250,7 +454,7 @@ impl<'a> PeekIter<'a> {
fn new(iter: TokenIter<'a>) -> Self {
Self { stored: VecDeque::new(), peek_pos: 0, iter }
}
- /// Returns the next item after the current one. It doesn't interfer with `peek_next` output.
+ /// Returns the next item after the current one. It doesn't interfere with `peek_next` output.
fn peek(&mut self) -> Option<&(TokenKind, &'a str)> {
if self.stored.is_empty() {
if let Some(next) = self.iter.next() {
@@ -259,7 +463,7 @@ impl<'a> PeekIter<'a> {
}
self.stored.front()
}
- /// Returns the next item after the last one peeked. It doesn't interfer with `peek` output.
+ /// Returns the next item after the last one peeked. It doesn't interfere with `peek` output.
fn peek_next(&mut self) -> Option<&(TokenKind, &'a str)> {
self.peek_pos += 1;
if self.peek_pos - 1 < self.stored.len() {
@@ -311,7 +515,6 @@ struct Classifier<'a> {
in_attribute: bool,
in_macro: bool,
in_macro_nonterminal: bool,
- edition: Edition,
byte_pos: u32,
file_span: Span,
src: &'a str,
@@ -321,12 +524,7 @@ struct Classifier<'a> {
impl<'a> Classifier<'a> {
/// Takes as argument the source code to HTML-ify, the rust edition to use and the source code
/// file span which will be used later on by the `span_correspondance_map`.
- fn new(
- src: &str,
- edition: Edition,
- file_span: Span,
- decoration_info: Option<DecorationInfo>,
- ) -> Classifier<'_> {
+ fn new(src: &str, file_span: Span, decoration_info: Option<DecorationInfo>) -> Classifier<'_> {
let tokens = PeekIter::new(TokenIter { src });
let decorations = decoration_info.map(Decorations::new);
Classifier {
@@ -334,7 +532,6 @@ impl<'a> Classifier<'a> {
in_attribute: false,
in_macro: false,
in_macro_nonterminal: false,
- edition,
byte_pos: 0,
file_span,
src,
@@ -354,7 +551,6 @@ impl<'a> Classifier<'a> {
let start = self.byte_pos as usize;
let mut pos = start;
let mut has_ident = false;
- let edition = self.edition;
loop {
let mut nb = 0;
@@ -376,7 +572,7 @@ impl<'a> Classifier<'a> {
if let Some((None, text)) = self.tokens.peek().map(|(token, text)| {
if *token == TokenKind::Ident {
- let class = get_real_ident_class(text, edition, true);
+ let class = get_real_ident_class(text, true);
(class, text)
} else {
// Doesn't matter which Class we put in here...
@@ -494,7 +690,7 @@ impl<'a> Classifier<'a> {
// or a reference or pointer type. Unless, of course, it looks like
// a logical and or a multiplication operator: `&&` or `* `.
TokenKind::Star => match self.tokens.peek() {
- Some((TokenKind::Whitespace, _)) => Class::Op,
+ Some((TokenKind::Whitespace, _)) => return no_highlight(sink),
Some((TokenKind::Ident, "mut")) => {
self.next();
sink(Highlight::Token { text: "*mut", class: Some(Class::RefKeyWord) });
@@ -510,15 +706,15 @@ impl<'a> Classifier<'a> {
TokenKind::And => match self.tokens.peek() {
Some((TokenKind::And, _)) => {
self.next();
- sink(Highlight::Token { text: "&&", class: Some(Class::Op) });
+ sink(Highlight::Token { text: "&&", class: None });
return;
}
Some((TokenKind::Eq, _)) => {
self.next();
- sink(Highlight::Token { text: "&=", class: Some(Class::Op) });
+ sink(Highlight::Token { text: "&=", class: None });
return;
}
- Some((TokenKind::Whitespace, _)) => Class::Op,
+ Some((TokenKind::Whitespace, _)) => return no_highlight(sink),
Some((TokenKind::Ident, "mut")) => {
self.next();
sink(Highlight::Token { text: "&mut", class: Some(Class::RefKeyWord) });
@@ -531,7 +727,7 @@ impl<'a> Classifier<'a> {
TokenKind::Eq => match lookahead {
Some(TokenKind::Eq) => {
self.next();
- sink(Highlight::Token { text: "==", class: Some(Class::Op) });
+ sink(Highlight::Token { text: "==", class: None });
return;
}
Some(TokenKind::Gt) => {
@@ -539,7 +735,7 @@ impl<'a> Classifier<'a> {
sink(Highlight::Token { text: "=>", class: None });
return;
}
- _ => Class::Op,
+ _ => return no_highlight(sink),
},
TokenKind::Minus if lookahead == Some(TokenKind::Gt) => {
self.next();
@@ -556,7 +752,7 @@ impl<'a> Classifier<'a> {
| TokenKind::Percent
| TokenKind::Bang
| TokenKind::Lt
- | TokenKind::Gt => Class::Op,
+ | TokenKind::Gt => return no_highlight(sink),
// Miscellaneous, no highlighting.
TokenKind::Dot
@@ -634,7 +830,7 @@ impl<'a> Classifier<'a> {
sink(Highlight::Token { text, class: None });
return;
}
- TokenKind::Ident => match get_real_ident_class(text, self.edition, false) {
+ TokenKind::Ident => match get_real_ident_class(text, false) {
None => match text {
"Option" | "Result" => Class::PreludeTy,
"Some" | "None" | "Ok" | "Err" => Class::PreludeVal,
@@ -682,7 +878,7 @@ fn enter_span(
klass: Class,
href_context: &Option<HrefContext<'_, '_, '_>>,
) -> &'static str {
- string_without_closing_tag(out, "", Some(klass), href_context).expect(
+ string_without_closing_tag(out, "", Some(klass), href_context, true).expect(
"internal error: enter_span was called with Some(klass) but did not return a \
closing HTML tag",
)
@@ -714,8 +910,10 @@ fn string<T: Display>(
text: T,
klass: Option<Class>,
href_context: &Option<HrefContext<'_, '_, '_>>,
+ open_tag: bool,
) {
- if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context) {
+ if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context, open_tag)
+ {
out.write_str(closing_tag);
}
}
@@ -734,6 +932,7 @@ fn string_without_closing_tag<T: Display>(
text: T,
klass: Option<Class>,
href_context: &Option<HrefContext<'_, '_, '_>>,
+ open_tag: bool,
) -> Option<&'static str> {
let Some(klass) = klass
else {
@@ -742,6 +941,10 @@ fn string_without_closing_tag<T: Display>(
};
let Some(def_span) = klass.get_span()
else {
+ if !open_tag {
+ write!(out, "{}", text);
+ return None;
+ }
write!(out, "<span class=\"{}\">{}", klass.as_html(), text);
return Some("</span>");
};
@@ -765,6 +968,7 @@ fn string_without_closing_tag<T: Display>(
path
});
}
+
if let Some(href_context) = href_context {
if let Some(href) =
href_context.context.shared.span_correspondance_map.get(&def_span).and_then(|href| {
@@ -775,9 +979,9 @@ fn string_without_closing_tag<T: Display>(
// a link to their definition can be generated using this:
// https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338
match href {
- LinkFromSrc::Local(span) => context
- .href_from_span(*span, true)
- .map(|s| format!("{}{}", href_context.root_path, s)),
+ LinkFromSrc::Local(span) => {
+ context.href_from_span_relative(*span, href_context.current_href)
+ }
LinkFromSrc::External(def_id) => {
format::href_with_root_path(*def_id, context, Some(href_context.root_path))
.ok()
@@ -793,12 +997,33 @@ fn string_without_closing_tag<T: Display>(
}
})
{
- write!(out, "<a class=\"{}\" href=\"{}\">{}", klass.as_html(), href, text_s);
+ if !open_tag {
+ // We're already inside an element which has the same klass, no need to give it
+ // again.
+ write!(out, "<a href=\"{}\">{}", href, text_s);
+ } else {
+ let klass_s = klass.as_html();
+ if klass_s.is_empty() {
+ write!(out, "<a href=\"{}\">{}", href, text_s);
+ } else {
+ write!(out, "<a class=\"{}\" href=\"{}\">{}", klass_s, href, text_s);
+ }
+ }
return Some("</a>");
}
}
- write!(out, "<span class=\"{}\">{}", klass.as_html(), text_s);
- Some("</span>")
+ if !open_tag {
+ write!(out, "{}", text_s);
+ return None;
+ }
+ let klass_s = klass.as_html();
+ if klass_s.is_empty() {
+ write!(out, "{}", text_s);
+ Some("")
+ } else {
+ write!(out, "<span class=\"{}\">{}", klass_s, text_s);
+ Some("</span>")
+ }
}
#[cfg(test)]
diff --git a/src/librustdoc/html/highlight/fixtures/decorations.html b/src/librustdoc/html/highlight/fixtures/decorations.html
index 45f567880..ebf29f9cb 100644
--- a/src/librustdoc/html/highlight/fixtures/decorations.html
+++ b/src/librustdoc/html/highlight/fixtures/decorations.html
@@ -1,2 +1,4 @@
-<span class="example"><span class="kw">let</span> <span class="ident">x</span> <span class="op">=</span> <span class="number">1</span>;</span>
-<span class="kw">let</span> <span class="ident">y</span> <span class="op">=</span> <span class="number">2</span>; \ No newline at end of file
+<span class="example"><span class="kw">let </span>x = <span class="number">1</span>;
+<span class="kw">let </span>y = <span class="number">2</span>;
+</span><span class="example2"><span class="kw">let </span>z = <span class="number">3</span>;
+</span><span class="kw">let </span>a = <span class="number">4</span>; \ No newline at end of file
diff --git a/src/librustdoc/html/highlight/fixtures/dos_line.html b/src/librustdoc/html/highlight/fixtures/dos_line.html
index 1c8dbffe7..30b50ca7c 100644
--- a/src/librustdoc/html/highlight/fixtures/dos_line.html
+++ b/src/librustdoc/html/highlight/fixtures/dos_line.html
@@ -1,3 +1,3 @@
-<span class="kw">pub</span> <span class="kw">fn</span> <span class="ident">foo</span>() {
+<span class="kw">pub fn </span>foo() {
<span class="macro">println!</span>(<span class="string">&quot;foo&quot;</span>);
}
diff --git a/src/librustdoc/html/highlight/fixtures/highlight.html b/src/librustdoc/html/highlight/fixtures/highlight.html
index abc2db179..9f73e03f9 100644
--- a/src/librustdoc/html/highlight/fixtures/highlight.html
+++ b/src/librustdoc/html/highlight/fixtures/highlight.html
@@ -1,4 +1,4 @@
-<span class="kw">use</span> <span class="ident"><span class="kw">crate</span>::a::foo</span>;
-<span class="kw">use</span> <span class="ident"><span class="self">self</span>::whatever</span>;
-<span class="kw">let</span> <span class="ident">x</span> <span class="op">=</span> <span class="ident"><span class="kw">super</span>::b::foo</span>;
-<span class="kw">let</span> <span class="ident">y</span> <span class="op">=</span> <span class="ident"><span class="self">Self</span>::whatever</span>; \ No newline at end of file
+<span class="kw">use </span><span class="kw">crate</span>::a::foo;
+<span class="kw">use </span><span class="self">self</span>::whatever;
+<span class="kw">let </span>x = <span class="kw">super</span>::b::foo;
+<span class="kw">let </span>y = <span class="self">Self</span>::whatever; \ No newline at end of file
diff --git a/src/librustdoc/html/highlight/fixtures/sample.html b/src/librustdoc/html/highlight/fixtures/sample.html
index b117a12e3..4a5a3cf60 100644
--- a/src/librustdoc/html/highlight/fixtures/sample.html
+++ b/src/librustdoc/html/highlight/fixtures/sample.html
@@ -8,30 +8,31 @@
.lifetime { color: #B76514; }
.question-mark { color: #ff9011; }
</style>
-<pre><code><span class="attribute">#![<span class="ident">crate_type</span> <span class="op">=</span> <span class="string">&quot;lib&quot;</span>]</span>
+<pre><code><span class="attribute">#![crate_type = <span class="string">&quot;lib&quot;</span>]
-<span class="kw">use</span> <span class="ident">std::path</span>::{<span class="ident">Path</span>, <span class="ident">PathBuf</span>};
+</span><span class="kw">use </span>std::path::{Path, PathBuf};
-<span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">target_os</span> <span class="op">=</span> <span class="string">&quot;linux&quot;</span>)]</span>
-<span class="kw">fn</span> <span class="ident">main</span>() -&gt; () {
- <span class="kw">let</span> <span class="ident">foo</span> <span class="op">=</span> <span class="bool-val">true</span> <span class="op">&amp;&amp;</span> <span class="bool-val">false</span> <span class="op">|</span><span class="op">|</span> <span class="bool-val">true</span>;
- <span class="kw">let</span> <span class="kw">_</span>: <span class="kw-2">*const</span> () <span class="op">=</span> <span class="number">0</span>;
- <span class="kw">let</span> <span class="kw">_</span> <span class="op">=</span> <span class="kw-2">&amp;</span><span class="ident">foo</span>;
- <span class="kw">let</span> <span class="kw">_</span> <span class="op">=</span> <span class="op">&amp;&amp;</span><span class="ident">foo</span>;
- <span class="kw">let</span> <span class="kw">_</span> <span class="op">=</span> <span class="kw-2">*</span><span class="ident">foo</span>;
- <span class="macro">mac!</span>(<span class="ident">foo</span>, <span class="kw-2">&amp;mut</span> <span class="ident">bar</span>);
- <span class="macro">assert!</span>(<span class="self">self</span>.<span class="ident">length</span> <span class="op">&lt;</span> <span class="ident">N</span> <span class="op">&amp;&amp;</span> <span class="ident">index</span> <span class="op">&lt;</span><span class="op">=</span> <span class="self">self</span>.<span class="ident">length</span>);
- <span class="ident">::std::env::var</span>(<span class="string">&quot;gateau&quot;</span>).<span class="ident">is_ok</span>();
- <span class="attribute">#[<span class="ident">rustfmt::skip</span>]</span>
- <span class="kw">let</span> <span class="ident">s</span>:<span class="ident">std::path::PathBuf</span> <span class="op">=</span> <span class="ident">std::path::PathBuf::new</span>();
- <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">s</span> <span class="op">=</span> <span class="ident">String::new</span>();
+<span class="attribute">#[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>;
+ <span class="kw">let _</span>: <span class="kw-2">*const </span>() = <span class="number">0</span>;
+ <span class="kw">let _ </span>= <span class="kw-2">&amp;</span>foo;
+ <span class="kw">let _ </span>= &amp;&amp;foo;
+ <span class="kw">let _ </span>= <span class="kw-2">*</span>foo;
+ <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><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();
- <span class="kw">match</span> <span class="kw-2">&amp;</span><span class="ident">s</span> {
- <span class="kw-2">ref</span> <span class="kw-2">mut</span> <span class="ident">x</span> =&gt; {}
+ <span class="kw">match </span><span class="kw-2">&amp;</span>s {
+ <span class="kw-2">ref mut </span>x =&gt; {}
}
}
-<span class="macro">macro_rules!</span> <span class="ident">bar</span> {
- (<span class="macro-nonterminal">$</span><span class="macro-nonterminal">foo</span>:<span class="ident">tt</span>) =&gt; {};
+<span class="macro">macro_rules! </span>bar {
+ (<span class="macro-nonterminal">$foo</span>:tt) =&gt; {};
}
</code></pre>
diff --git a/src/librustdoc/html/highlight/fixtures/sample.rs b/src/librustdoc/html/highlight/fixtures/sample.rs
index fbfdc6767..ef85b566c 100644
--- a/src/librustdoc/html/highlight/fixtures/sample.rs
+++ b/src/librustdoc/html/highlight/fixtures/sample.rs
@@ -3,6 +3,7 @@
use std::path::{Path, PathBuf};
#[cfg(target_os = "linux")]
+#[cfg(target_os = "windows")]
fn main() -> () {
let foo = true && false || true;
let _: *const () = 0;
diff --git a/src/librustdoc/html/highlight/fixtures/union.html b/src/librustdoc/html/highlight/fixtures/union.html
index c0acf31a0..9f8915282 100644
--- a/src/librustdoc/html/highlight/fixtures/union.html
+++ b/src/librustdoc/html/highlight/fixtures/union.html
@@ -1,8 +1,8 @@
-<span class="kw">union</span> <span class="ident">Foo</span> {
- <span class="ident">i</span>: <span class="ident">i8</span>,
- <span class="ident">u</span>: <span class="ident">i8</span>,
+<span class="kw">union </span>Foo {
+ i: i8,
+ u: i8,
}
-<span class="kw">fn</span> <span class="ident">main</span>() {
- <span class="kw">let</span> <span class="ident">union</span> <span class="op">=</span> <span class="number">0</span>;
+<span class="kw">fn </span>main() {
+ <span class="kw">let </span>union = <span class="number">0</span>;
}
diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs
index 1fea7e983..a5e633df4 100644
--- a/src/librustdoc/html/highlight/tests.rs
+++ b/src/librustdoc/html/highlight/tests.rs
@@ -3,7 +3,6 @@ use crate::html::format::Buffer;
use expect_test::expect_file;
use rustc_data_structures::fx::FxHashMap;
use rustc_span::create_default_session_globals_then;
-use rustc_span::edition::Edition;
const STYLE: &str = r#"
<style>
@@ -23,7 +22,7 @@ fn test_html_highlighting() {
let src = include_str!("fixtures/sample.rs");
let html = {
let mut out = Buffer::new();
- write_code(&mut out, src, Edition::Edition2018, None, None);
+ write_code(&mut out, src, None, None);
format!("{}<pre><code>{}</code></pre>\n", STYLE, out.into_inner())
};
expect_file!["fixtures/sample.html"].assert_eq(&html);
@@ -37,7 +36,7 @@ fn test_dos_backline() {
println!(\"foo\");\r\n\
}\r\n";
let mut html = Buffer::new();
- write_code(&mut html, src, Edition::Edition2018, None, None);
+ write_code(&mut html, src, None, None);
expect_file!["fixtures/dos_line.html"].assert_eq(&html.into_inner());
});
}
@@ -51,7 +50,7 @@ let x = super::b::foo;
let y = Self::whatever;";
let mut html = Buffer::new();
- write_code(&mut html, src, Edition::Edition2018, None, None);
+ write_code(&mut html, src, None, None);
expect_file!["fixtures/highlight.html"].assert_eq(&html.into_inner());
});
}
@@ -61,7 +60,7 @@ fn test_union_highlighting() {
create_default_session_globals_then(|| {
let src = include_str!("fixtures/union.rs");
let mut html = Buffer::new();
- write_code(&mut html, src, Edition::Edition2018, None, None);
+ write_code(&mut html, src, None, None);
expect_file!["fixtures/union.html"].assert_eq(&html.into_inner());
});
}
@@ -70,12 +69,15 @@ fn test_union_highlighting() {
fn test_decorations() {
create_default_session_globals_then(|| {
let src = "let x = 1;
-let y = 2;";
+let y = 2;
+let z = 3;
+let a = 4;";
let mut decorations = FxHashMap::default();
- decorations.insert("example", vec![(0, 10)]);
+ decorations.insert("example", vec![(0, 10), (11, 21)]);
+ decorations.insert("example2", vec![(22, 32)]);
let mut html = Buffer::new();
- write_code(&mut html, src, Edition::Edition2018, None, Some(DecorationInfo(decorations)));
+ write_code(&mut html, src, None, Some(DecorationInfo(decorations)));
expect_file!["fixtures/decorations.html"].assert_eq(&html.into_inner());
});
}
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 52a2effca..43d07d4a5 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -330,34 +330,27 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
});
let tooltip = if ignore != Ignore::None {
- Some((None, "ignore"))
+ highlight::Tooltip::Ignore
} else if compile_fail {
- Some((None, "compile_fail"))
+ highlight::Tooltip::CompileFail
} else if should_panic {
- Some((None, "should_panic"))
+ highlight::Tooltip::ShouldPanic
} else if explicit_edition {
- Some((Some(edition), "edition"))
+ highlight::Tooltip::Edition(edition)
} else {
- None
+ highlight::Tooltip::None
};
// insert newline to clearly separate it from the
// previous block so we can shorten the html output
let mut s = Buffer::new();
s.push_str("\n");
- highlight::render_with_highlighting(
+
+ highlight::render_example_with_highlighting(
&text,
&mut s,
- Some(&format!(
- "rust-example-rendered{}",
- if let Some((_, class)) = tooltip { format!(" {}", class) } else { String::new() }
- )),
- playground_button.as_deref(),
tooltip,
- edition,
- None,
- None,
- None,
+ playground_button.as_deref(),
);
Some(Event::Html(s.into_inner().into()))
}
@@ -1126,7 +1119,11 @@ impl MarkdownSummaryLine<'_> {
let mut s = String::new();
- html::push_html(&mut s, LinkReplacer::new(SummaryLine::new(p), links));
+ let without_paragraphs = LinkReplacer::new(SummaryLine::new(p), links).filter(|event| {
+ !matches!(event, Event::Start(Tag::Paragraph) | Event::End(Tag::Paragraph))
+ });
+
+ html::push_html(&mut s, without_paragraphs);
s
}
@@ -1446,6 +1443,8 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
map.insert("not-displayed".into(), 1);
map.insert("alternative-display".into(), 1);
map.insert("search".into(), 1);
+ map.insert("crate-search".into(), 1);
+ map.insert("crate-search-div".into(), 1);
// This is the list of IDs used in HTML generated in Rust (including the ones
// used in tera template files).
map.insert("mainThemeStyle".into(), 1);
@@ -1453,7 +1452,6 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
map.insert("settings-menu".into(), 1);
map.insert("help-button".into(), 1);
map.insert("main-content".into(), 1);
- map.insert("crate-search".into(), 1);
map.insert("toggle-all-docs".into(), 1);
map.insert("all-types".into(), 1);
map.insert("default-settings".into(), 1);
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 2ed7a6f1b..62def4a94 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -31,6 +31,7 @@ use crate::formats::FormatRenderer;
use crate::html::escape::Escape;
use crate::html::format::{join_with_double_colon, Buffer};
use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
+use crate::html::url_parts_builder::UrlPartsBuilder;
use crate::html::{layout, sources};
use crate::scrape_examples::AllCallLocations;
use crate::try_err;
@@ -71,7 +72,7 @@ pub(crate) struct Context<'tcx> {
}
// `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+#[cfg(all(not(windows), target_arch = "x86_64", target_pointer_width = "64"))]
rustc_data_structures::static_assert_size!(Context<'_>, 128);
/// Shared mutable state used in [`Context`] and elsewhere.
@@ -301,13 +302,10 @@ impl<'tcx> Context<'tcx> {
/// may happen, for example, with externally inlined items where the source
/// of their crate documentation isn't known.
pub(super) fn src_href(&self, item: &clean::Item) -> Option<String> {
- self.href_from_span(item.span(self.tcx()), true)
+ self.href_from_span(item.span(self.tcx())?, true)
}
pub(crate) fn href_from_span(&self, span: clean::Span, with_lines: bool) -> Option<String> {
- if span.is_dummy() {
- return None;
- }
let mut root = self.root_path();
let mut path = String::new();
let cnum = span.cnum(self.sess());
@@ -373,6 +371,35 @@ impl<'tcx> Context<'tcx> {
anchor = anchor
))
}
+
+ pub(crate) fn href_from_span_relative(
+ &self,
+ span: clean::Span,
+ relative_to: &str,
+ ) -> Option<String> {
+ self.href_from_span(span, false).map(|s| {
+ let mut url = UrlPartsBuilder::new();
+ let mut dest_href_parts = s.split('/');
+ let mut cur_href_parts = relative_to.split('/');
+ for (cur_href_part, dest_href_part) in (&mut cur_href_parts).zip(&mut dest_href_parts) {
+ if cur_href_part != dest_href_part {
+ url.push(dest_href_part);
+ break;
+ }
+ }
+ for dest_href_part in dest_href_parts {
+ url.push(dest_href_part);
+ }
+ let loline = span.lo(self.sess()).line;
+ let hiline = span.hi(self.sess()).line;
+ format!(
+ "{}{}#{}",
+ "../".repeat(cur_href_parts.count()),
+ url.finish(),
+ if loline == hiline { loline.to_string() } else { format!("{loline}-{hiline}") }
+ )
+ })
+ }
}
/// Generates the documentation for `crate` into the directory `dst`
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index a262c8f7d..1e6f20d2b 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -191,12 +191,6 @@ impl StylePath {
}
}
-fn write_srclink(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
- if let Some(l) = cx.src_href(item) {
- write!(buf, "<a class=\"srclink\" href=\"{}\">source</a>", l)
- }
-}
-
#[derive(Debug, Eq, PartialEq, Hash)]
struct ItemEntry {
url: String,
@@ -522,7 +516,14 @@ fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<Strin
(cfg, _) => cfg.as_deref().cloned(),
};
- debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.and_then(|p| p.cfg.as_ref()), cfg);
+ debug!(
+ "Portability {:?} {:?} (parent: {:?}) - {:?} = {:?}",
+ item.name,
+ item.cfg,
+ parent,
+ parent.and_then(|p| p.cfg.as_ref()),
+ cfg
+ );
Some(format!("<div class=\"stab portability\">{}</div>", cfg?.render_long_html()))
}
@@ -569,7 +570,10 @@ fn short_item_info(
message.push_str(&format!(": {}", html.into_string()));
}
extra_info.push(format!(
- "<div class=\"stab deprecated\"><span class=\"emoji\">👎</span> {}</div>",
+ "<div class=\"stab deprecated\">\
+ <span class=\"emoji\">👎</span>\
+ <span>{}</span>\
+ </div>",
message,
));
}
@@ -582,8 +586,9 @@ fn short_item_info(
.filter(|stab| stab.feature != sym::rustc_private)
.map(|stab| (stab.level, stab.feature))
{
- let mut message =
- "<span class=\"emoji\">🔬</span> This is a nightly-only experimental API.".to_owned();
+ let mut message = "<span class=\"emoji\">🔬</span>\
+ <span>This is a nightly-only experimental API."
+ .to_owned();
let mut feature = format!("<code>{}</code>", Escape(feature.as_str()));
if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) {
@@ -594,7 +599,7 @@ fn short_item_info(
));
}
- message.push_str(&format!(" ({})", feature));
+ message.push_str(&format!(" ({})</span>", feature));
extra_info.push(format!("<div class=\"stab unstable\">{}</div>", message));
}
@@ -608,10 +613,10 @@ fn short_item_info(
// Render the list of items inside one of the sections "Trait Implementations",
// "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages).
-fn render_impls(
+pub(crate) fn render_impls(
cx: &mut Context<'_>,
w: &mut Buffer,
- impls: &[&&Impl],
+ impls: &[&Impl],
containing_item: &clean::Item,
toggle_open_by_default: bool,
) {
@@ -816,7 +821,7 @@ fn assoc_method(
href = href,
name = name,
generics = g.print(cx),
- decl = d.full_print(header_len, indent, header.asyncness, cx),
+ decl = d.full_print(header_len, indent, cx),
notable_traits = notable_traits_decl(d, cx),
where_clause = print_where_clause(g, cx, indent, end_newline),
)
@@ -836,12 +841,13 @@ fn assoc_method(
/// Note that it is possible for an unstable function to be const-stable. In that case, the span
/// will include the const-stable version, but no stable version will be emitted, as a natural
/// consequence of the above rules.
-fn render_stability_since_raw(
+fn render_stability_since_raw_with_extra(
w: &mut Buffer,
ver: Option<Symbol>,
const_stability: Option<ConstStability>,
containing_ver: Option<Symbol>,
containing_const_ver: Option<Symbol>,
+ extra_class: &str,
) -> bool {
let stable_version = ver.filter(|inner| !inner.is_empty() && Some(*inner) != containing_ver);
@@ -889,12 +895,30 @@ fn render_stability_since_raw(
}
if !stability.is_empty() {
- write!(w, r#"<span class="since" title="{}">{}</span>"#, title, stability);
+ write!(w, r#"<span class="since{extra_class}" title="{title}">{stability}</span>"#);
}
!stability.is_empty()
}
+#[inline]
+fn render_stability_since_raw(
+ w: &mut Buffer,
+ ver: Option<Symbol>,
+ const_stability: Option<ConstStability>,
+ containing_ver: Option<Symbol>,
+ containing_const_ver: Option<Symbol>,
+) -> bool {
+ render_stability_since_raw_with_extra(
+ w,
+ ver,
+ const_stability,
+ containing_ver,
+ containing_const_ver,
+ "",
+ )
+}
+
fn render_assoc_item(
w: &mut Buffer,
item: &clean::Item,
@@ -1001,6 +1025,47 @@ impl<'a> AssocItemLink<'a> {
}
}
+fn write_impl_section_heading(w: &mut Buffer, title: &str, id: &str) {
+ write!(
+ w,
+ "<h2 id=\"{id}\" class=\"small-section-header\">\
+ {title}\
+ <a href=\"#{id}\" class=\"anchor\"></a>\
+ </h2>"
+ );
+}
+
+pub(crate) fn render_all_impls(
+ w: &mut Buffer,
+ cx: &mut Context<'_>,
+ containing_item: &clean::Item,
+ concrete: &[&Impl],
+ synthetic: &[&Impl],
+ blanket_impl: &[&Impl],
+) {
+ let mut impls = Buffer::empty_from(w);
+ render_impls(cx, &mut impls, concrete, containing_item, true);
+ let impls = impls.into_inner();
+ if !impls.is_empty() {
+ write_impl_section_heading(w, "Trait Implementations", "trait-implementations");
+ write!(w, "<div id=\"trait-implementations-list\">{}</div>", impls);
+ }
+
+ if !synthetic.is_empty() {
+ write_impl_section_heading(w, "Auto Trait Implementations", "synthetic-implementations");
+ w.write_str("<div id=\"synthetic-implementations-list\">");
+ render_impls(cx, w, synthetic, containing_item, false);
+ w.write_str("</div>");
+ }
+
+ if !blanket_impl.is_empty() {
+ write_impl_section_heading(w, "Blanket Implementations", "blanket-implementations");
+ w.write_str("<div id=\"blanket-implementations-list\">");
+ render_impls(cx, w, blanket_impl, containing_item, false);
+ w.write_str("</div>");
+ }
+}
+
fn render_assoc_items(
w: &mut Buffer,
cx: &mut Context<'_>,
@@ -1030,12 +1095,7 @@ fn render_assoc_items_inner(
let mut tmp_buf = Buffer::empty_from(w);
let (render_mode, id) = match what {
AssocItemRender::All => {
- tmp_buf.write_str(
- "<h2 id=\"implementations\" class=\"small-section-header\">\
- Implementations\
- <a href=\"#implementations\" class=\"anchor\"></a>\
- </h2>",
- );
+ write_impl_section_heading(&mut tmp_buf, "Implementations", "implementations");
(RenderMode::Normal, "implementations-list".to_owned())
}
AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
@@ -1044,15 +1104,14 @@ fn render_assoc_items_inner(
if let Some(def_id) = type_.def_id(cx.cache()) {
cx.deref_id_map.insert(def_id, id.clone());
}
- write!(
- tmp_buf,
- "<h2 id=\"{id}\" class=\"small-section-header\">\
- <span>Methods from {trait_}&lt;Target = {type_}&gt;</span>\
- <a href=\"#{id}\" class=\"anchor\"></a>\
- </h2>",
- id = id,
- trait_ = trait_.print(cx),
- type_ = type_.print(cx),
+ write_impl_section_heading(
+ &mut tmp_buf,
+ &format!(
+ "<span>Methods from {trait_}&lt;Target = {type_}&gt;</span>",
+ trait_ = trait_.print(cx),
+ type_ = type_.print(cx),
+ ),
+ &id,
);
(RenderMode::ForDeref { mut_: deref_mut_ }, cx.derive_id(id))
}
@@ -1099,49 +1158,12 @@ fn render_assoc_items_inner(
return;
}
- let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) =
- traits.iter().partition(|t| t.inner_impl().kind.is_auto());
- let (blanket_impl, concrete): (Vec<&&Impl>, _) =
+ let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
+ traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
+ let (blanket_impl, concrete): (Vec<&Impl>, _) =
concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
- let mut impls = Buffer::empty_from(w);
- render_impls(cx, &mut impls, &concrete, containing_item, true);
- let impls = impls.into_inner();
- if !impls.is_empty() {
- write!(
- w,
- "<h2 id=\"trait-implementations\" class=\"small-section-header\">\
- Trait Implementations\
- <a href=\"#trait-implementations\" class=\"anchor\"></a>\
- </h2>\
- <div id=\"trait-implementations-list\">{}</div>",
- impls
- );
- }
-
- if !synthetic.is_empty() {
- w.write_str(
- "<h2 id=\"synthetic-implementations\" class=\"small-section-header\">\
- Auto Trait Implementations\
- <a href=\"#synthetic-implementations\" class=\"anchor\"></a>\
- </h2>\
- <div id=\"synthetic-implementations-list\">",
- );
- render_impls(cx, w, &synthetic, containing_item, false);
- w.write_str("</div>");
- }
-
- if !blanket_impl.is_empty() {
- w.write_str(
- "<h2 id=\"blanket-implementations\" class=\"small-section-header\">\
- Blanket Implementations\
- <a href=\"#blanket-implementations\" class=\"anchor\"></a>\
- </h2>\
- <div id=\"blanket-implementations-list\">",
- );
- render_impls(cx, w, &blanket_impl, containing_item, false);
- w.write_str("</div>");
- }
+ render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
}
}
@@ -1550,6 +1572,15 @@ fn render_impl(
rendering_params: ImplRenderingParameters,
) {
for trait_item in &t.items {
+ // Skip over any default trait items that are impossible to call
+ // (e.g. if it has a `Self: Sized` bound on an unsized type).
+ if let Some(impl_def_id) = parent.item_id.as_def_id()
+ && let Some(trait_item_def_id) = trait_item.item_id.as_def_id()
+ && cx.tcx().is_impossible_method((impl_def_id, trait_item_def_id))
+ {
+ continue;
+ }
+
let n = trait_item.name;
if i.items.iter().any(|m| m.name == n) {
continue;
@@ -1668,23 +1699,29 @@ fn render_rightside(
RenderMode::Normal => (item.const_stability(tcx), containing_item.const_stable_since(tcx)),
RenderMode::ForDeref { .. } => (None, None),
};
+ let src_href = cx.src_href(item);
+ let has_src_ref = src_href.is_some();
let mut rightside = Buffer::new();
- let has_stability = render_stability_since_raw(
+ let has_stability = render_stability_since_raw_with_extra(
&mut rightside,
item.stable_since(tcx),
const_stability,
containing_item.stable_since(tcx),
const_stable_since,
+ if has_src_ref { "" } else { " rightside" },
);
- let mut srclink = Buffer::empty_from(w);
- write_srclink(cx, item, &mut srclink);
- if has_stability && !srclink.is_empty() {
- rightside.write_str(" · ");
+ if let Some(l) = src_href {
+ if has_stability {
+ write!(rightside, " · <a class=\"srclink\" href=\"{}\">source</a>", l)
+ } else {
+ write!(rightside, "<a class=\"srclink rightside\" href=\"{}\">source</a>", l)
+ }
}
- rightside.push_buffer(srclink);
- if !rightside.is_empty() {
+ if has_stability && has_src_ref {
write!(w, "<span class=\"rightside\">{}</span>", rightside.into_inner());
+ } else {
+ w.push_buffer(rightside);
}
}
@@ -1700,8 +1737,8 @@ pub(crate) fn render_impl_summary(
// in documentation pages for trait with automatic implementations like "Send" and "Sync".
aliases: &[String],
) {
- let id =
- cx.derive_id(get_id_for_impl(&i.inner_impl().for_, i.inner_impl().trait_.as_ref(), cx));
+ let inner_impl = i.inner_impl();
+ let id = cx.derive_id(get_id_for_impl(&inner_impl.for_, inner_impl.trait_.as_ref(), cx));
let aliases = if aliases.is_empty() {
String::new()
} else {
@@ -1713,9 +1750,9 @@ pub(crate) fn render_impl_summary(
write!(w, "<h3 class=\"code-header in-band\">");
if let Some(use_absolute) = use_absolute {
- write!(w, "{}", i.inner_impl().print(use_absolute, cx));
+ write!(w, "{}", inner_impl.print(use_absolute, cx));
if show_def_docs {
- for it in &i.inner_impl().items {
+ for it in &inner_impl.items {
if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
w.write_str("<span class=\"where fmt-newline\"> ");
assoc_type(
@@ -1733,11 +1770,11 @@ pub(crate) fn render_impl_summary(
}
}
} else {
- write!(w, "{}", i.inner_impl().print(false, cx));
+ write!(w, "{}", inner_impl.print(false, cx));
}
write!(w, "</h3>");
- let is_trait = i.inner_impl().trait_.is_some();
+ let is_trait = inner_impl.trait_.is_some();
if is_trait {
if let Some(portability) = portability(&i.impl_item, Some(parent)) {
write!(w, "<span class=\"item-info\">{}</span>", portability);
@@ -1931,6 +1968,70 @@ fn small_url_encode(s: String) -> String {
}
}
+pub(crate) fn sidebar_render_assoc_items(
+ cx: &Context<'_>,
+ out: &mut Buffer,
+ id_map: &mut IdMap,
+ concrete: Vec<&Impl>,
+ synthetic: Vec<&Impl>,
+ blanket_impl: Vec<&Impl>,
+) {
+ let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
+ let mut links = FxHashSet::default();
+
+ let mut ret = impls
+ .iter()
+ .filter_map(|it| {
+ let trait_ = it.inner_impl().trait_.as_ref()?;
+ let encoded =
+ id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
+
+ let i_display = format!("{:#}", trait_.print(cx));
+ let out = Escape(&i_display);
+ let prefix = match it.inner_impl().polarity {
+ ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
+ ty::ImplPolarity::Negative => "!",
+ };
+ let generated = format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out);
+ if links.insert(generated.clone()) { Some(generated) } else { None }
+ })
+ .collect::<Vec<String>>();
+ ret.sort();
+ ret
+ };
+
+ let concrete_format = format_impls(concrete, id_map);
+ let synthetic_format = format_impls(synthetic, id_map);
+ let blanket_format = format_impls(blanket_impl, id_map);
+
+ if !concrete_format.is_empty() {
+ print_sidebar_block(
+ out,
+ "trait-implementations",
+ "Trait Implementations",
+ concrete_format.iter(),
+ );
+ }
+
+ if !synthetic_format.is_empty() {
+ print_sidebar_block(
+ out,
+ "synthetic-implementations",
+ "Auto Trait Implementations",
+ synthetic_format.iter(),
+ );
+ }
+
+ if !blanket_format.is_empty() {
+ print_sidebar_block(
+ out,
+ "blanket-implementations",
+ "Blanket Implementations",
+ blanket_format.iter(),
+ );
+ }
+}
+
fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
let did = it.item_id.expect_def_id();
let cache = cx.cache();
@@ -1976,68 +2077,15 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
{
let mut derefs = FxHashSet::default();
derefs.insert(did);
- sidebar_deref_methods(cx, out, impl_, v, &mut derefs);
+ sidebar_deref_methods(cx, out, impl_, v, &mut derefs, &mut used_links);
}
- let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
- let mut links = FxHashSet::default();
-
- let mut ret = impls
- .iter()
- .filter_map(|it| {
- let trait_ = it.inner_impl().trait_.as_ref()?;
- let encoded =
- id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
-
- let i_display = format!("{:#}", trait_.print(cx));
- let out = Escape(&i_display);
- let prefix = match it.inner_impl().polarity {
- ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
- ty::ImplPolarity::Negative => "!",
- };
- let generated = format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out);
- if links.insert(generated.clone()) { Some(generated) } else { None }
- })
- .collect::<Vec<String>>();
- ret.sort();
- ret
- };
-
let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
- let concrete_format = format_impls(concrete, &mut id_map);
- let synthetic_format = format_impls(synthetic, &mut id_map);
- let blanket_format = format_impls(blanket_impl, &mut id_map);
-
- if !concrete_format.is_empty() {
- print_sidebar_block(
- out,
- "trait-implementations",
- "Trait Implementations",
- concrete_format.iter(),
- );
- }
-
- if !synthetic_format.is_empty() {
- print_sidebar_block(
- out,
- "synthetic-implementations",
- "Auto Trait Implementations",
- synthetic_format.iter(),
- );
- }
-
- if !blanket_format.is_empty() {
- print_sidebar_block(
- out,
- "blanket-implementations",
- "Blanket Implementations",
- blanket_format.iter(),
- );
- }
+ sidebar_render_assoc_items(cx, out, &mut id_map, concrete, synthetic, blanket_impl);
}
}
}
@@ -2048,6 +2096,7 @@ fn sidebar_deref_methods(
impl_: &Impl,
v: &[Impl],
derefs: &mut FxHashSet<DefId>,
+ used_links: &mut FxHashSet<String>,
) {
let c = cx.cache();
@@ -2080,13 +2129,10 @@ fn sidebar_deref_methods(
.and_then(|did| c.impls.get(&did));
if let Some(impls) = inner_impl {
debug!("found inner_impl: {:?}", impls);
- let mut used_links = FxHashSet::default();
let mut ret = impls
.iter()
.filter(|i| i.inner_impl().trait_.is_none())
- .flat_map(|i| {
- get_methods(i.inner_impl(), true, &mut used_links, deref_mut, cx.tcx())
- })
+ .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
.collect::<Vec<_>>();
if !ret.is_empty() {
let id = if let Some(target_def_id) = real_target.def_id(c) {
@@ -2115,7 +2161,14 @@ fn sidebar_deref_methods(
.map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
.unwrap_or(false)
}) {
- sidebar_deref_methods(cx, out, target_deref_impl, target_impls, derefs);
+ sidebar_deref_methods(
+ cx,
+ out,
+ target_deref_impl,
+ target_impls,
+ derefs,
+ used_links,
+ );
}
}
}
@@ -2302,9 +2355,54 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean
buf.push_str("</section>")
}
+/// Returns the list of implementations for the primitive reference type, filtering out any
+/// implementations that are on concrete or partially generic types, only keeping implementations
+/// of the form `impl<T> Trait for &T`.
+pub(crate) fn get_filtered_impls_for_reference<'a>(
+ shared: &'a Rc<SharedContext<'_>>,
+ it: &clean::Item,
+) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
+ let def_id = it.item_id.expect_def_id();
+ // If the reference primitive is somehow not defined, exit early.
+ let Some(v) = shared.cache.impls.get(&def_id) else { return (Vec::new(), Vec::new(), Vec::new()) };
+ // Since there is no "direct implementation" on the reference primitive type, we filter out
+ // every implementation which isn't a trait implementation.
+ let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
+ let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
+ traits.partition(|t| t.inner_impl().kind.is_auto());
+
+ let (blanket_impl, concrete): (Vec<&Impl>, _) =
+ concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
+ // Now we keep only references over full generic types.
+ let concrete: Vec<_> = concrete
+ .into_iter()
+ .filter(|t| match t.inner_impl().for_ {
+ clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
+ _ => false,
+ })
+ .collect();
+
+ (concrete, synthetic, blanket_impl)
+}
+
fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
let mut sidebar = Buffer::new();
- sidebar_assoc_items(cx, &mut sidebar, it);
+
+ if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
+ sidebar_assoc_items(cx, &mut sidebar, it);
+ } else {
+ let shared = Rc::clone(&cx.shared);
+ let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it);
+
+ sidebar_render_assoc_items(
+ cx,
+ &mut sidebar,
+ &mut IdMap::new(),
+ concrete,
+ synthetic,
+ blanket_impl,
+ );
+ }
if !sidebar.is_empty() {
write!(buf, "<section>{}</section>", sidebar.into_inner());
@@ -2614,8 +2712,8 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
clean::Type::BorrowedRef { type_, .. } => {
work.push_back(*type_);
}
- clean::Type::QPath { self_type, trait_, .. } => {
- work.push_back(*self_type);
+ clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => {
+ work.push_back(self_type);
process_path(trait_.def_id());
}
_ => {}
@@ -2668,7 +2766,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
let contents = match fs::read_to_string(&path) {
Ok(contents) => contents,
Err(err) => {
- let span = item.span(tcx).inner();
+ let span = item.span(tcx).map_or(rustc_span::DUMMY_SP, |span| span.inner());
tcx.sess
.span_err(span, &format!("failed to read file {}: {}", path.display(), err));
return false;
@@ -2764,11 +2862,10 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
sources::print_src(
w,
contents_subset,
- call_data.edition,
file_span,
cx,
&root_path,
- Some(highlight::DecorationInfo(decoration_info)),
+ highlight::DecorationInfo(decoration_info),
sources::SourceContext::Embedded { offset: line_min },
);
write!(w, "</div></div>");
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 99cf42919..cfa450942 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -16,10 +16,10 @@ use std::fmt;
use std::rc::Rc;
use super::{
- collect_paths_for_type, document, ensure_trailing_slash, item_ty_to_section,
- notable_traits_decl, render_assoc_item, render_assoc_items, render_attributes_in_code,
- render_attributes_in_pre, render_impl, render_stability_since_raw, write_srclink,
- AssocItemLink, Context, ImplRenderingParameters,
+ 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,
};
use crate::clean;
use crate::config::ModuleSorting;
@@ -371,16 +371,21 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
}
clean::ImportKind::Glob => String::new(),
};
+ let stab_tags = stab_tags.unwrap_or_default();
+ let (stab_tags_before, stab_tags_after) = if stab_tags.is_empty() {
+ ("", "")
+ } else {
+ ("<div class=\"item-right docblock-short\">", "</div>")
+ };
write!(
w,
"<div class=\"item-left {stab}{add}import-item\"{id}>\
<code>{vis}{imp}</code>\
</div>\
- <div class=\"item-right docblock-short\">{stab_tags}</div>",
+ {stab_tags_before}{stab_tags}{stab_tags_after}",
stab = stab.unwrap_or_default(),
vis = myitem.visibility.print_with_space(myitem.item_id, cx),
imp = import.print(cx),
- stab_tags = stab_tags.unwrap_or_default(),
);
w.write_str(ITEM_TABLE_ROW_CLOSE);
}
@@ -412,6 +417,12 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
let doc_value = myitem.doc_value().unwrap_or_default();
w.write_str(ITEM_TABLE_ROW_OPEN);
+ let docs = MarkdownSummaryLine(&doc_value, &myitem.links(cx)).into_string();
+ let (docs_before, docs_after) = if docs.is_empty() {
+ ("", "")
+ } else {
+ ("<div class=\"item-right docblock-short\">", "</div>")
+ };
write!(
w,
"<div class=\"item-left {stab}{add}module-item\">\
@@ -420,11 +431,10 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
{unsafety_flag}\
{stab_tags}\
</div>\
- <div class=\"item-right docblock-short\">{docs}</div>",
+ {docs_before}{docs}{docs_after}",
name = myitem.name.unwrap(),
visibility_emoji = visibility_emoji,
stab_tags = extra_info_tags(myitem, item, cx.tcx()),
- docs = MarkdownSummaryLine(&doc_value, &myitem.links(cx)).into_string(),
class = myitem.type_(),
add = add,
stab = stab.unwrap_or_default(),
@@ -477,7 +487,7 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) ->
(cfg, _) => cfg.as_deref().cloned(),
};
- debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.cfg, cfg);
+ debug!("Portability name={:?} {:?} - {:?} = {:?}", item.name, item.cfg, parent.cfg, cfg);
if let Some(ref cfg) = cfg {
tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html());
}
@@ -520,7 +530,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
name = name,
generics = f.generics.print(cx),
where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline),
- decl = f.decl.full_print(header_len, 0, header.asyncness, cx),
+ decl = f.decl.full_print(header_len, 0, cx),
notable_traits = notable_traits_decl(&f.decl, cx),
);
});
@@ -709,14 +719,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
write!(w, "<details class=\"rustdoc-toggle\" open><summary>");
}
write!(w, "<div id=\"{}\" class=\"method has-srclink\">", id);
- write!(w, "<div class=\"rightside\">");
-
- let has_stability = render_stability_since(w, m, t, cx.tcx());
- if has_stability {
- w.write_str(" · ");
- }
- write_srclink(cx, m, w);
- write!(w, "</div>");
+ render_rightside(w, cx, m, t, RenderMode::Normal);
write!(w, "<h4 class=\"code-header\">");
render_assoc_item(
w,
@@ -994,7 +997,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// So C's HTML will have something like this:
//
// ```html
- // <script type="text/javascript" src="/implementors/A/trait.Foo.js"
+ // <script src="/implementors/A/trait.Foo.js"
// data-ignore-extern-crates="A,B" async></script>
// ```
//
@@ -1020,9 +1023,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
.map(|cnum| cx.shared.tcx.crate_name(cnum).to_string())
.collect::<Vec<_>>()
.join(",");
+ let (extern_before, extern_after) =
+ if extern_crates.is_empty() { ("", "") } else { (" data-ignore-extern-crates=\"", "\"") };
write!(
w,
- "<script type=\"text/javascript\" src=\"{src}\" data-ignore-extern-crates=\"{extern_crates}\" async></script>",
+ "<script src=\"{src}\"{extern_before}{extern_crates}{extern_after} async></script>",
src = js_src_path.finish(),
);
}
@@ -1198,7 +1203,8 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
let name = v.name.unwrap();
match *v.kind {
clean::VariantItem(ref var) => match var {
- clean::Variant::CLike => write!(w, "{}", name),
+ // FIXME(#101337): Show discriminant
+ clean::Variant::CLike(..) => write!(w, "{}", name),
clean::Variant::Tuple(ref s) => {
write!(w, "{}(", name);
print_tuple_struct_fields(w, cx, s);
@@ -1260,7 +1266,13 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
w.write_str(")");
}
w.write_str("</code>");
- render_stability_since(w, variant, it, cx.tcx());
+ 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>");
use crate::clean::Variant;
@@ -1322,17 +1334,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) {
wrap_into_docblock(w, |w| {
- highlight::render_with_highlighting(
- &t.source,
- w,
- Some("macro"),
- None,
- None,
- it.span(cx.tcx()).inner().edition(),
- None,
- None,
- None,
- );
+ highlight::render_macro_with_highlighting(&t.source, w);
});
document(w, cx, it, None, HeadingOffset::H2)
}
@@ -1370,8 +1372,18 @@ fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &c
}
fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
+ let def_id = it.item_id.expect_def_id();
document(w, cx, it, None, HeadingOffset::H2);
- render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
+ if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
+ render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
+ } else {
+ // We handle the "reference" primitive type on its own because we only want to list
+ // implementations on generic types.
+ let shared = Rc::clone(&cx.shared);
+ let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it);
+
+ render_all_impls(w, cx, it, &concrete, &synthetic, &blanket_impl);
+ }
}
fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) {
@@ -1601,21 +1613,6 @@ where
w.write_str("</code></pre>");
}
-fn render_stability_since(
- w: &mut Buffer,
- item: &clean::Item,
- containing_item: &clean::Item,
- tcx: TyCtxt<'_>,
-) -> bool {
- render_stability_since_raw(
- w,
- item.stable_since(tcx),
- item.const_stability(tcx),
- containing_item.stable_since(tcx),
- containing_item.const_stable_since(tcx),
- )
-}
-
fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cx: &Context<'_>) -> Ordering {
let lhss = format!("{}", lhs.inner_impl().print(false, cx));
let rhss = format!("{}", rhs.inner_impl().print(false, cx));
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index d672f0bb5..8eb9c07f8 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -544,10 +544,15 @@ fn get_fn_inputs_and_outputs<'tcx>(
(true, _) => (Some(impl_self), &func.generics),
(_, true) => (Some(impl_self), impl_generics),
(false, false) => {
- let mut params = func.generics.params.clone();
- params.extend(impl_generics.params.clone());
- let mut where_predicates = func.generics.where_predicates.clone();
- where_predicates.extend(impl_generics.where_predicates.clone());
+ let params =
+ func.generics.params.iter().chain(&impl_generics.params).cloned().collect();
+ let where_predicates = func
+ .generics
+ .where_predicates
+ .iter()
+ .chain(&impl_generics.where_predicates)
+ .cloned()
+ .collect();
combined_generics = clean::Generics { params, where_predicates };
(Some(impl_self), &combined_generics)
}
diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs
index 34d590fb2..151ec2b28 100644
--- a/src/librustdoc/html/render/span_map.rs
+++ b/src/librustdoc/html/render/span_map.rs
@@ -166,25 +166,23 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
if let ExprKind::MethodCall(segment, ..) = expr.kind {
- if let Some(hir_id) = segment.hir_id {
- let hir = self.tcx.hir();
- let body_id = hir.enclosing_body_owner(hir_id);
- // FIXME: this is showing error messages for parts of the code that are not
- // compiled (because of cfg)!
- //
- // See discussion in https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352
- let typeck_results = self.tcx.typeck_body(
- hir.maybe_body_owned_by(body_id).expect("a body which isn't a body"),
+ let hir = self.tcx.hir();
+ let body_id = hir.enclosing_body_owner(segment.hir_id);
+ // FIXME: this is showing error messages for parts of the code that are not
+ // compiled (because of cfg)!
+ //
+ // See discussion in https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352
+ let typeck_results = self
+ .tcx
+ .typeck_body(hir.maybe_body_owned_by(body_id).expect("a body which isn't a body"));
+ if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) {
+ self.matches.insert(
+ segment.ident.span,
+ match hir.span_if_local(def_id) {
+ Some(span) => LinkFromSrc::Local(clean::Span::new(span)),
+ None => LinkFromSrc::External(def_id),
+ },
);
- if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) {
- self.matches.insert(
- segment.ident.span,
- match hir.span_if_local(def_id) {
- Some(span) => LinkFromSrc::Local(clean::Span::new(span)),
- None => LinkFromSrc::External(def_id),
- },
- );
- }
}
} else if self.handle_macro(expr.span) {
// We don't want to go deeper into the macro.
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 6fb41ff32..fc4d46fe6 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -1,5 +1,4 @@
use std::ffi::OsStr;
-use std::fmt::Write;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{self, BufReader};
@@ -10,7 +9,8 @@ use std::sync::LazyLock as Lazy;
use itertools::Itertools;
use rustc_data_structures::flock;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use serde::Serialize;
+use serde::ser::SerializeSeq;
+use serde::{Serialize, Serializer};
use super::{collect_paths_for_type, ensure_trailing_slash, Context, BASIC_KEYWORDS};
use crate::clean::Crate;
@@ -284,25 +284,43 @@ pub(super) fn write_shared(
cx.write_shared(SharedResource::Unversioned { name }, contents, &options.emit)?;
}
- fn collect(path: &Path, krate: &str, key: &str) -> io::Result<(Vec<String>, Vec<String>)> {
+ /// Read a file and return all lines that match the `"{crate}":{data},` format,
+ /// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`.
+ ///
+ /// This forms the payload of files that look like this:
+ ///
+ /// ```javascript
+ /// var data = {
+ /// "{crate1}":{data},
+ /// "{crate2}":{data}
+ /// };
+ /// use_data(data);
+ /// ```
+ ///
+ /// The file needs to be formatted so that *only crate data lines start with `"`*.
+ fn collect(path: &Path, krate: &str) -> io::Result<(Vec<String>, Vec<String>)> {
let mut ret = Vec::new();
let mut krates = Vec::new();
if path.exists() {
- let prefix = format!(r#"{}["{}"]"#, key, krate);
+ let prefix = format!("\"{}\"", krate);
for line in BufReader::new(File::open(path)?).lines() {
let line = line?;
- if !line.starts_with(key) {
+ if !line.starts_with('"') {
continue;
}
if line.starts_with(&prefix) {
continue;
}
- ret.push(line.to_string());
+ if line.ends_with(',') {
+ ret.push(line[..line.len() - 1].to_string());
+ } else {
+ // No comma (it's the case for the last added crate line)
+ ret.push(line.to_string());
+ }
krates.push(
- line[key.len() + 2..]
- .split('"')
- .next()
+ line.split('"')
+ .find(|s| !s.is_empty())
.map(|s| s.to_owned())
.unwrap_or_else(String::new),
);
@@ -311,6 +329,20 @@ pub(super) fn write_shared(
Ok((ret, krates))
}
+ /// Read a file and return all lines that match the <code>"{crate}":{data},\</code> format,
+ /// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`.
+ ///
+ /// This forms the payload of files that look like this:
+ ///
+ /// ```javascript
+ /// var data = JSON.parse('{\
+ /// "{crate1}":{data},\
+ /// "{crate2}":{data}\
+ /// }');
+ /// use_data(data);
+ /// ```
+ ///
+ /// The file needs to be formatted so that *only crate data lines start with `"`*.
fn collect_json(path: &Path, krate: &str) -> io::Result<(Vec<String>, Vec<String>)> {
let mut ret = Vec::new();
let mut krates = Vec::new();
@@ -526,13 +558,27 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
},
};
- #[derive(Serialize)]
struct Implementor {
text: String,
synthetic: bool,
types: Vec<String>,
}
+ impl Serialize for Implementor {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let mut seq = serializer.serialize_seq(None)?;
+ seq.serialize_element(&self.text)?;
+ if self.synthetic {
+ seq.serialize_element(&1)?;
+ seq.serialize_element(&self.types)?;
+ }
+ seq.end()
+ }
+ }
+
let implementors = imps
.iter()
.filter_map(|imp| {
@@ -563,9 +609,9 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
}
let implementors = format!(
- r#"implementors["{}"] = {};"#,
+ r#""{}":{}"#,
krate.name(cx.tcx()),
- serde_json::to_string(&implementors).unwrap()
+ serde_json::to_string(&implementors).expect("failed serde conversion"),
);
let mut mydst = dst.clone();
@@ -576,16 +622,15 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
mydst.push(&format!("{}.{}.js", remote_item_type, remote_path[remote_path.len() - 1]));
let (mut all_implementors, _) =
- try_err!(collect(&mydst, krate.name(cx.tcx()).as_str(), "implementors"), &mydst);
+ try_err!(collect(&mydst, krate.name(cx.tcx()).as_str()), &mydst);
all_implementors.push(implementors);
// Sort the implementors by crate so the file will be generated
// identically even with rustdoc running in parallel.
all_implementors.sort();
- let mut v = String::from("(function() {var implementors = {};\n");
- for implementor in &all_implementors {
- writeln!(v, "{}", *implementor).unwrap();
- }
+ let mut v = String::from("(function() {var implementors = {\n");
+ v.push_str(&all_implementors.join(",\n"));
+ v.push_str("\n};");
v.push_str(
"if (window.register_implementors) {\
window.register_implementors(implementors);\
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index d0fd637ba..2e2bee78b 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -11,7 +11,6 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
-use rustc_span::edition::Edition;
use rustc_span::source_map::FileName;
use std::ffi::OsStr;
@@ -54,6 +53,7 @@ impl LocalSourcesCollector<'_, '_> {
fn add_local_source(&mut self, item: &clean::Item) {
let sess = self.tcx.sess;
let span = item.span(self.tcx);
+ let Some(span) = span else { return };
// skip all synthetic "files"
if !is_real_and_local(span, sess) {
return;
@@ -110,6 +110,7 @@ impl DocVisitor for SourceCollector<'_, '_> {
let tcx = self.cx.tcx();
let span = item.span(tcx);
+ let Some(span) = span else { return };
let sess = tcx.sess;
// If we're not rendering sources, there's nothing to do.
@@ -213,11 +214,10 @@ impl SourceCollector<'_, '_> {
print_src(
buf,
contents,
- cx.shared.edition(),
file_span,
cx,
&root_path,
- None,
+ highlight::DecorationInfo::default(),
SourceContext::Standalone,
)
},
@@ -266,11 +266,10 @@ pub(crate) enum SourceContext {
pub(crate) fn print_src(
buf: &mut Buffer,
s: &str,
- edition: Edition,
file_span: rustc_span::Span,
context: &Context<'_>,
root_path: &str,
- decoration_info: Option<highlight::DecorationInfo>,
+ decoration_info: highlight::DecorationInfo,
source_context: SourceContext,
) {
let lines = s.lines().count();
@@ -289,15 +288,14 @@ pub(crate) fn print_src(
}
}
line_numbers.write_str("</pre>");
- highlight::render_with_highlighting(
+ 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,
- None,
- None,
- None,
- edition,
- Some(line_numbers),
- Some(highlight::HrefContext { context, file_span, root_path }),
+ line_numbers,
+ highlight::HrefContext { context, file_span, root_path, current_href },
decoration_info,
);
}
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 83fe14550..bb35970eb 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -197,26 +197,18 @@ h4.code-header {
position: relative;
}
-div.impl-items > div {
- padding-left: 0;
-}
-
h1, h2, h3, h4, h5, h6,
.sidebar,
.mobile-topbar,
a.source,
.search-input,
.search-results .result-name,
-.content table td:first-child > a,
.item-left > a,
.out-of-band,
span.since,
-#source-sidebar, #sidebar-toggle,
details.rustdoc-toggle > summary::before,
-div.impl-items > div:not(.docblock):not(.item-info),
.content ul.crate a.crate,
a.srclink,
-#main-content > .since,
#help-button > button,
details.rustdoc-toggle.top-doc > summary,
details.rustdoc-toggle.top-doc > summary::before,
@@ -230,7 +222,6 @@ details.rustdoc-toggle.non-exhaustive > summary::before,
font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif;
}
-h1, h2, h3, h4,
a#toggle-all-docs,
a.anchor,
.small-section-header a,
@@ -250,6 +241,51 @@ pre.rust a,
color: var(--main-color);
}
+.content span.enum, .content a.enum,
+.content span.struct, .content a.struct,
+.content span.union, .content a.union,
+.content span.primitive, .content a.primitive,
+.content span.type, .content a.type,
+.content span.foreigntype, .content a.foreigntype {
+ color: var(--type-link-color);
+}
+
+.content span.trait, .content a.trait,
+.content span.traitalias, .content a.traitalias {
+ color: var(--trait-link-color);
+}
+
+.content span.associatedtype, .content a.associatedtype,
+.content span.constant, .content a.constant,
+.content span.static, .content a.static {
+ color: var(--assoc-item-link-color);
+}
+
+.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);
+}
+
+.content span.attr, .content a.attr,
+.content span.derive, .content a.derive,
+.content span.macro, .content a.macro {
+ color: var(--macro-link-color);
+}
+
+.content span.mod, .content a.mod, .block a.current.mod {
+ color: var(--mod-link-color);
+}
+
+.content span.keyword, .content a.keyword {
+ color: var(--keyword-link-color);
+}
+
+a {
+ color: var(--link-color);
+}
+
ol, ul {
padding-left: 24px;
}
@@ -354,19 +390,8 @@ img {
max-width: 100%;
}
-li {
- position: relative;
-}
-
.source .content {
- max-width: none;
overflow: visible;
- margin-left: 0px;
-}
-
-nav.sub {
- position: relative;
- font-size: 1rem;
}
.sub-container {
@@ -515,9 +540,6 @@ nav.sub {
font-weight: 500;
}
-.block {
- padding: 0;
-}
.block ul, .block li {
padding: 0;
margin: 0;
@@ -608,19 +630,10 @@ h2.location a {
margin: 0;
}
-#search {
- position: relative;
-}
-
.search-loading {
text-align: center;
}
-#results > table {
- width: 100%;
- table-layout: fixed;
-}
-
.content > .example-wrap pre.line-numbers {
position: relative;
-webkit-user-select: none;
@@ -635,19 +648,12 @@ h2.location a {
.docblock-short {
overflow-wrap: break-word;
overflow-wrap: anywhere;
-}
-.docblock-short p {
- display: inline;
-}
-
-.docblock-short p {
overflow: hidden;
text-overflow: ellipsis;
- margin: 0;
}
/* Wrap non-pre code blocks (`text`) but not (```text```). */
.docblock > :not(pre) > code,
-.docblock-short > :not(pre) > code {
+.docblock-short > code {
white-space: pre-wrap;
}
@@ -672,7 +678,7 @@ h2.location a {
position: relative;
}
-.docblock > :not(.information):not(.more-examples-toggle) {
+.docblock > :not(.more-examples-toggle):not(.example-wrap) {
max-width: 100%;
overflow-x: auto;
}
@@ -693,8 +699,13 @@ h2.location a {
flex-grow: 1;
margin: 0px;
padding: 0px;
+ /* We use overflow-wrap: break-word for Safari, which doesn't recognize
+ `anywhere`: https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-wrap */
overflow-wrap: break-word;
+ /* Then override it with `anywhere`, which is required to make non-Safari browsers break
+ more aggressively when we want them to. */
overflow-wrap: anywhere;
+ background-color: var(--main-background-color);
}
.in-band > code, .in-band > .code-header {
@@ -709,18 +720,6 @@ pre, .rustdoc.source .example-wrap {
#main-content {
position: relative;
}
-#main-content > .since {
- top: inherit;
-}
-
-.content table:not(.table-display) {
- border-spacing: 0 5px;
-}
-.content td { vertical-align: top; }
-.content td:first-child { padding-right: 20px; }
-.content td p:first-child { margin-top: 0; }
-.content td h1, .content td h2 { margin-left: 0; font-size: 1.125rem; }
-.content tr:first-child td { border-top: 0; }
.docblock table {
margin: .5em 0;
@@ -731,17 +730,14 @@ pre, .rustdoc.source .example-wrap {
.docblock table td {
padding: .5em;
- border: 1px dashed;
+ border: 1px dashed var(--border-color);
+ vertical-align: top;
}
.docblock table th {
padding: .5em;
text-align: left;
- border: 1px solid;
-}
-
-.fields + table {
- margin-bottom: 1em;
+ border: 1px solid var(--border-color);
}
.content .item-list {
@@ -749,16 +745,6 @@ pre, .rustdoc.source .example-wrap {
padding: 0;
}
-.content .multi-column {
- -moz-column-count: 5;
- -moz-column-gap: 2.5em;
- -webkit-column-count: 5;
- -webkit-column-gap: 2.5em;
- column-count: 5;
- column-gap: 2.5em;
-}
-.content .multi-column li { width: 100%; display: inline-block; }
-
.content > .methods > .method {
font-size: 1rem;
position: relative;
@@ -771,25 +757,6 @@ pre, .rustdoc.source .example-wrap {
font-size: 0.875rem;
}
-.content .methods > div:not(.notable-traits):not(.method) {
- margin-left: 40px;
- margin-bottom: 15px;
-}
-
-.content .docblock > .impl-items {
- margin-left: 20px;
- margin-top: -34px;
-}
-.content .docblock >.impl-items .table-display {
- margin: 0;
-}
-.content .docblock >.impl-items table td {
- padding: 0;
-}
-.content .docblock > .impl-items .table-display, .impl-items table td {
- border: none;
-}
-
.item-info {
display: block;
}
@@ -803,58 +770,29 @@ pre, .rustdoc.source .example-wrap {
margin-left: 24px;
}
-.sub-variant > div > .item-info {
- margin-top: initial;
-}
-
.content .impl-items .docblock, .content .impl-items .item-info {
margin-bottom: .6em;
}
-.content .impl-items > .item-info {
- margin-left: 40px;
-}
-
-.methods > .item-info, .content .impl-items > .item-info {
- margin-top: -8px;
-}
-
-.impl-items {
- flex-basis: 100%;
-}
-
#main-content > .item-info {
margin-top: 0;
margin-left: 0;
}
nav.sub {
+ position: relative;
+ font-size: 1rem;
flex-grow: 1;
margin-bottom: 25px;
}
.source nav.sub {
margin-left: 32px;
}
-nav.main {
- padding: 20px 0;
- text-align: center;
-}
-nav.main .current {
- border-top: 1px solid;
- border-bottom: 1px solid;
-}
-nav.main .separator {
- border: 1px solid;
- display: inline-block;
- height: 23px;
- margin: 0 20px;
-}
nav.sum { text-align: right; }
nav.sub form { display: inline; }
a {
text-decoration: none;
- background: transparent;
}
.small-section-header {
@@ -942,40 +880,77 @@ table,
position: relative;
display: flex;
height: 34px;
+ margin-top: 4px;
}
.search-container > * {
height: 100%;
}
.search-results-title {
- display: inline;
+ margin-top: 0;
+ white-space: nowrap;
+ /* flex layout allows shrinking the <select> appropriately if it becomes too large */
+ display: inline-flex;
+ max-width: 100%;
+ /* 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;
}
-#search-settings {
- font-size: 1.5rem;
- font-weight: 500;
- margin-bottom: 20px;
+#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;
+ /* allows this div (and with it the <select>-element "#crate-search") to be shrunk */
+ min-width: 5em;
}
#crate-search {
min-width: 115px;
- margin-top: 5px;
- padding-left: 0.15em;
+ padding: 0;
+ /* keep these two in sync with "@-moz-document url-prefix()" below */
+ padding-left: 4px;
padding-right: 23px;
- border: 1px solid;
+ /* 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 */
+ text-overflow: ellipsis;
+ border: 1px solid var(--border-color);
border-radius: 4px;
outline: none;
cursor: pointer;
-moz-appearance: none;
-webkit-appearance: none;
/* Removes default arrow from firefox */
+ text-indent: 0.01px;
+ background-color: var(--main-background-color);
+}
+/* cancel stylistic differences in padding in firefox
+for "appearance: none"-style (or equivalent) <select>s */
+@-moz-document url-prefix() {
+ #crate-search {
+ padding-left: 0px; /* == 4px - 4px */
+ padding-right: 19px; /* == 23px - 4px */
+ }
+}
+/* pseudo-element for holding the dropdown-arrow image; needs to be a separate thing
+so that we can apply CSS-filters to change the arrow color in themes */
+#crate-search-div::after {
+ /* lets clicks through! */
+ pointer-events: none;
+ /* completely covers the underlying div */
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ content: "";
background-repeat: no-repeat;
- background-color: transparent;
background-size: 20px;
- background-position: calc(100% - 1px) 56%;
+ 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");
- max-width: 100%;
- text-overflow: ellipsis;
}
-.search-container {
- margin-top: 4px;
+#crate-search > option {
+ font-size: 1rem;
}
.search-input {
/* Override Normalize.css: it has a rule that sets
@@ -988,11 +963,15 @@ table,
-moz-box-sizing: border-box !important;
box-sizing: border-box !important;
outline: none;
- border: 1px solid;
+ border: 1px solid var(--border-color);
border-radius: 2px;
padding: 8px;
font-size: 1rem;
width: 100%;
+ background-color: var(--button-background-color);
+}
+.search-input:focus {
+ border-color: var(--search-input-focused-border-color);
}
.search-results {
@@ -1015,7 +994,6 @@ table,
.search-results > a {
display: block;
- width: 100%;
/* A little margin ensures the browser's outlining of focused links has room to display. */
margin-left: 2px;
margin-right: 2px;
@@ -1034,12 +1012,6 @@ table,
padding-right: 1em;
}
-.search-results .result-name > span {
- display: inline-block;
- margin: 0;
- font-weight: normal;
-}
-
.popover {
font-size: 1rem;
position: absolute;
@@ -1048,7 +1020,7 @@ table,
display: block;
margin-top: 7px;
border-radius: 3px;
- border: 1px solid;
+ border: 1px solid var(--border-color);
font-size: 1rem;
}
@@ -1057,7 +1029,7 @@ table,
content: '';
position: absolute;
right: 11px;
- border: solid;
+ border: solid var(--border-color);
border-width: 1px 1px 0 0;
display: inline-block;
padding: 4px;
@@ -1093,13 +1065,13 @@ table,
text-align: center;
display: block;
margin: 10px 0;
- border-bottom: 1px solid;
+ border-bottom: 1px solid var(--border-color);
padding-bottom: 4px;
margin-bottom: 6px;
}
#help-button span.bottom {
clear: both;
- border-top: 1px solid;
+ border-top: 1px solid var(--border-color);
}
.side-by-side {
text-align: initial;
@@ -1133,6 +1105,7 @@ table,
.stab .emoji {
font-size: 1.25rem;
+ margin-right: 0.3rem;
}
/* Black one-pixel outline around emoji shapes */
@@ -1170,36 +1143,125 @@ table,
padding-left: 12px;
padding-right: 2px;
position: initial;
+ float: right;
+}
+
+.rightside:not(a),
+.out-of-band {
+ color: var(--right-side-color);
}
+
.impl-items .srclink, .impl .srclink, .methods .srclink {
/* Override header settings otherwise it's too bold */
font-weight: normal;
font-size: 1rem;
}
-.rightside {
- float: right;
+td.summary-column {
+ width: 100%;
}
-.variants_table {
- width: 100%;
+.summary {
+ padding-right: 0px;
}
-.variants_table tbody tr td:first-child {
- width: 1%; /* make the variant name as small as possible */
+pre.rust .question-mark {
+ font-weight: bold;
}
-td.summary-column {
- width: 100%;
+.example-wrap.compile_fail,
+.example-wrap.should_panic {
+ border-left: 2px solid var(--codeblock-error-color);
}
-.summary {
- padding-right: 0px;
+.ignore.example-wrap {
+ border-left: 2px solid var(--codeblock-ignore-color);
}
-pre.rust .question-mark {
+.example-wrap.compile_fail:hover,
+.example-wrap.should_panic:hover {
+ border-left: 2px solid var(--codeblock-error-hover-color);
+}
+
+.example-wrap.ignore:hover {
+ border-left: 2px solid var(--codeblock-ignore-hover-color);
+}
+
+.example-wrap.compile_fail .tooltip,
+.example-wrap.should_panic .tooltip {
+ color: var(--codeblock-error-color);
+}
+
+.example-wrap.ignore .tooltip {
+ color: var(--codeblock-ignore-color);
+}
+
+.example-wrap.compile_fail:hover .tooltip,
+.example-wrap.should_panic:hover .tooltip {
+ color: var(--codeblock-error-hover-color);
+}
+
+.example-wrap.ignore:hover .tooltip {
+ color: var(--codeblock-ignore-hover-color);
+}
+
+.example-wrap .tooltip {
+ position: absolute;
+ display: block;
+ cursor: pointer;
+ left: -25px;
+ top: 5px;
+}
+
+.example-wrap .tooltip::after {
+ display: none;
+ text-align: center;
+ padding: 5px 3px 3px 3px;
+ border-radius: 6px;
+ margin-left: 5px;
+ font-size: 1rem;
+ border: 1px solid var(--border-color);
+ position: absolute;
+ width: max-content;
+ top: -2px;
+ z-index: 1;
+}
+
+.example-wrap .tooltip::before {
+ content: " ";
+ position: absolute;
+ top: 50%;
+ left: 16px;
+ margin-top: -5px;
+ border-width: 5px;
+ border-style: solid;
+ display: none;
+ z-index: 1;
+}
+
+.example-wrap.ignore .tooltip::after {
+ content: "This example is not tested";
+}
+.example-wrap.compile_fail .tooltip::after {
+ content: "This example deliberately fails to compile";
+}
+.example-wrap.should_panic .tooltip::after {
+ content: "This example panics";
+}
+.example-wrap.edition .tooltip::after {
+ content: "This code runs with edition " attr(data-edition);
+}
+
+.example-wrap .tooltip:hover::before, .example-wrap .tooltip:hover::after {
+ display: inline;
+}
+
+.example-wrap.compile_fail .tooltip,
+.example-wrap.should_panic .tooltip,
+.example-wrap.ignore .tooltip {
font-weight: bold;
+ font-size: 1.25rem;
}
a.test-arrow {
@@ -1216,12 +1278,13 @@ a.test-arrow {
.example-wrap:hover .test-arrow {
visibility: visible;
}
-a.test-arrow:hover{
+a.test-arrow:hover {
text-decoration: none;
}
.code-attribute {
font-weight: 300;
+ color: var(--code-attribute-color);
}
.item-spacer {
@@ -1230,7 +1293,6 @@ a.test-arrow:hover{
}
.out-of-band > span.since {
- position: initial;
font-size: 1.25rem;
}
@@ -1258,12 +1320,6 @@ h3.variant {
margin-left: 24px;
}
-.toggle-label {
- display: inline-block;
- margin-left: 4px;
- margin-top: 3px;
-}
-
:target > code, :target > .code-header {
opacity: 1;
}
@@ -1272,61 +1328,6 @@ h3.variant {
padding-right: 3px;
}
-.information {
- position: absolute;
- left: -25px;
- margin-top: 7px;
- z-index: 1;
-}
-
-.tooltip {
- position: relative;
- display: inline-block;
- cursor: pointer;
-}
-
-.tooltip::after {
- display: none;
- text-align: center;
- padding: 5px 3px 3px 3px;
- border-radius: 6px;
- margin-left: 5px;
- font-size: 1rem;
-}
-
-.tooltip.ignore::after {
- content: "This example is not tested";
-}
-.tooltip.compile_fail::after {
- content: "This example deliberately fails to compile";
-}
-.tooltip.should_panic::after {
- content: "This example panics";
-}
-.tooltip.edition::after {
- content: "This code runs with edition " attr(data-edition);
-}
-
-.tooltip::before {
- content: " ";
- position: absolute;
- top: 50%;
- left: 16px;
- margin-top: -5px;
- border-width: 5px;
- border-style: solid;
- display: none;
-}
-
-.tooltip:hover::before, .tooltip:hover::after {
- display: inline;
-}
-
-.tooltip.compile_fail, .tooltip.should_panic, .tooltip.ignore {
- font-weight: bold;
- font-size: 1.25rem;
-}
-
.notable-traits-tooltip {
display: inline-block;
cursor: pointer;
@@ -1368,7 +1369,7 @@ h3.variant {
display: block;
}
-.notable-traits .docblock code.content{
+.notable-traits .docblock code.content {
margin: 0;
padding: 0;
font-size: 1.25rem;
@@ -1402,27 +1403,19 @@ pre.rust {
}
#titles {
- height: 35px;
+ display: flex;
+ flex-direction: row;
+ gap: 1px;
+ margin-bottom: 4px;
}
#titles > button {
- float: left;
- width: 33.3%;
text-align: center;
font-size: 1.125rem;
cursor: pointer;
border: 0;
border-top: 2px solid;
-}
-
-#titles > button:first-child:last-child {
- margin-right: 1px;
- width: calc(100% - 1px);
-}
-
-#titles > button:not(:last-child) {
- margin-right: 1px;
- width: calc(33.3% - 1px);
+ flex: 1;
}
#titles > button > div.count {
@@ -1457,7 +1450,7 @@ pre.rust {
#source-sidebar > .title {
font-size: 1.5rem;
text-align: center;
- border-bottom: 1px solid;
+ border-bottom: 1px solid var(--border-color);
margin-bottom: 6px;
}
#sidebar-toggle > button {
@@ -1483,23 +1476,29 @@ pre.rust {
outline: none;
}
-#copy-path {
- height: 34px;
-}
#settings-menu > a, #help-button > button, #copy-path {
padding: 5px;
width: 33px;
- border: 1px solid;
+ border: 1px solid var(--border-color);
border-radius: 2px;
cursor: pointer;
}
-#settings-menu {
- padding: 0;
-}
+
#settings-menu > a, #help-button > button {
padding: 5px;
height: 100%;
display: block;
+ background-color: var(--button-background-color);
+}
+
+#copy-path {
+ color: var(--copy-path-button-color);
+}
+#copy-path > img {
+ filter: var(--copy-path-img-filter);
+}
+#copy-path:hover > img {
+ filter: var(--copy-path-img-hover-filter);
}
@keyframes rotating {
@@ -1542,85 +1541,25 @@ input:checked + .slider {
}
#copy-path {
- background: initial;
+ height: 34px;
+ background-color: var(--main-background-color);
margin-left: 10px;
padding: 0;
padding-left: 2px;
border: 0;
}
-#theme-choices {
- display: none;
- position: absolute;
- left: 0;
- top: 28px;
- border: 1px solid;
- border-radius: 3px;
- z-index: 1;
- cursor: pointer;
-}
-
-#theme-choices > button {
- border: none;
- width: 100%;
- padding: 4px 8px;
- text-align: center;
- background: rgba(0,0,0,0);
- overflow-wrap: normal;
-}
-
-#theme-choices > button:not(:first-child) {
- border-top: 1px solid;
-}
-
kbd {
display: inline-block;
padding: 3px 5px;
font: 15px monospace;
line-height: 10px;
vertical-align: middle;
- border: solid 1px;
+ border: solid 1px var(--border-color);
border-radius: 3px;
cursor: default;
}
-.hidden-by-impl-hider,
-.hidden-by-usual-hider {
- /* important because of conflicting rule for small screens */
- display: none !important;
-}
-
-#implementations-list > h3 > span.in-band {
- width: 100%;
-}
-
-.table-display {
- width: 100%;
- border: 0;
- border-collapse: collapse;
- border-spacing: 0;
- font-size: 1rem;
-}
-
-.table-display tr td:first-child {
- padding-right: 0;
-}
-
-.table-display tr td:last-child {
- float: right;
-}
-.table-display .out-of-band {
- position: relative;
- font-size: 1.125rem;
- display: block;
-}
-
-.table-display td:hover .anchor {
- display: block;
- top: 2px;
- left: -5px;
-}
-
#main-content > ul {
padding-left: 10px;
}
@@ -1681,6 +1620,12 @@ details.rustdoc-toggle > summary::before {
opacity: .5;
}
+details.rustdoc-toggle > summary.hideme > span,
+details.rustdoc-toggle > summary::before,
+.more-examples-toggle summary, .more-examples-toggle .hide-more {
+ color: var(--toggles-color);
+}
+
/* Screen readers see the text version at the end the line.
Visual readers see the icon at the start of the line, but small and transparent. */
details.rustdoc-toggle > summary::after {
@@ -1785,7 +1730,7 @@ in storage.js plus the media query with (max-width: 700px)
to prevent an overlay between the "collapse toggle" and the information tooltip.
However, it's not needed with smaller screen width because the doc/code block is always put
"one line" below. */
- .docblock > .information:first-child > .tooltip {
+ .docblock > .example-wrap:first-child .tooltip {
margin-top: 16px;
}
@@ -1826,7 +1771,6 @@ in storage.js plus the media query with (min-width: 701px)
padding-top: 0px;
}
- .rustdoc,
.main-heading {
flex-direction: column;
}
@@ -1852,10 +1796,6 @@ in storage.js plus the media query with (min-width: 701px)
display: none;
}
- .sidebar-elems {
- margin-top: 1em;
- }
-
.sidebar {
position: fixed;
top: 45px;
@@ -1949,13 +1889,10 @@ in storage.js plus the media query with (min-width: 701px)
}
.sidebar-elems {
+ margin-top: 1em;
background-color: var(--sidebar-background-color);
}
- .source nav:not(.sidebar).sub {
- margin-left: 32px;
- }
-
.content {
margin-left: 0px;
}
@@ -1964,28 +1901,12 @@ in storage.js plus the media query with (min-width: 701px)
margin-top: 10px;
}
- #search {
- margin-left: 0;
- padding: 0;
- }
-
.anchor {
display: none !important;
}
- .notable-traits {
- position: absolute;
- left: -22px;
- top: 24px;
- }
-
#titles > button > div.count {
- float: left;
- width: 100%;
- }
-
- #titles {
- height: 50px;
+ display: block;
}
/* Because of ios, we need to actually have a full height sidebar title so the
@@ -2092,6 +2013,11 @@ in storage.js plus the media query with (min-width: 701px)
#main-content > div > details.rustdoc-toggle > summary::before {
left: -11px;
}
+
+ /* Align summary-nested and unnested item-info gizmos. */
+ .content .impl-items > .item-info {
+ margin-left: 34px;
+ }
}
@media print {
@@ -2111,15 +2037,6 @@ in storage.js plus the media query with (min-width: 701px)
}
@media (max-width: 464px) {
- #titles, #titles > button {
- height: 73px;
- }
-
- #main-content > table:not(.table-display) td {
- word-break: break-word;
- width: 50%;
- }
-
#crate-search {
border-radius: 4px;
}
diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css
index c42cac59b..e7a898e9f 100644
--- a/src/librustdoc/html/static/css/themes/ayu.css
+++ b/src/librustdoc/html/static/css/themes/ayu.css
@@ -14,6 +14,27 @@ Original by Dempfi (https://github.com/dempfi/ayu)
--scrollbar-thumb-background-color: #5c6773;
--scrollbar-color: #5c6773 #24292f;
--headings-border-bottom-color: #5c6773;
+ --border-color: #5c6773;
+ --button-background-color: #141920;
+ --right-side-color: grey;
+ --code-attribute-color: #999;
+ --toggles-color: #999;
+ --search-input-focused-border-color: #5c6773; /* Same as `--border-color`. */
+ --copy-path-button-color: #fff;
+ --copy-path-img-filter: invert(70%);
+ --copy-path-img-hover-filter: invert(100%);
+ --codeblock-error-hover-color: rgb(255, 0, 0);
+ --codeblock-error-color: rgba(255, 0, 0, .5);
+ --codeblock-ignore-hover-color: rgb(255, 142, 0);
+ --codeblock-ignore-color: rgba(255, 142, 0, .6);
+ --type-link-color: #ffa0a5;
+ --trait-link-color: #39afd7;
+ --assoc-item-link-color: #39afd7;
+ --function-link-color: #fdd687;
+ --macro-link-color: #a37acc;
+ --keyword-link-color: #39afd7;
+ --mod-link-color: #39afd7;
+ --link-color: #39afd7;
}
.slider {
@@ -36,10 +57,6 @@ h4 {
border: none;
}
-.in-band {
- background-color: #0f1419;
-}
-
.docblock code {
color: #ffb454;
}
@@ -49,7 +66,7 @@ h4 {
.docblock pre > code, pre > code {
color: #e6e1cf;
}
-span code {
+.item-info code {
color: #e6e1cf;
}
.docblock a > code {
@@ -84,17 +101,14 @@ pre, .rustdoc.source .example-wrap {
border-right: 1px solid #ffb44c;
}
-.docblock table td, .docblock table th {
- border-color: #5c6773;
-}
-
.search-results a:hover {
- background-color: #777;
+ color: #fff !important;
+ background-color: #3c3c3c;
}
.search-results a:focus {
- color: #000 !important;
- background-color: #c6afb3;
+ color: #fff !important;
+ background-color: #3c3c3c;
}
.search-results a {
color: #0096cf;
@@ -105,97 +119,37 @@ pre, .rustdoc.source .example-wrap {
.content .item-info::before { color: #ccc; }
-.content span.foreigntype, .content a.foreigntype { color: #ffa0a5; }
-.content span.union, .content a.union { color: #ffa0a5; }
-.content span.constant, .content a.constant,
-.content span.static, .content a.static { color: #39AFD7; }
-.content span.primitive, .content a.primitive { color: #ffa0a5; }
-.content span.traitalias, .content a.traitalias { color: #39AFD7; }
-.content span.keyword, .content a.keyword { color: #39AFD7; }
-
-.content span.externcrate, .content span.mod, .content a.mod {
- color: #39AFD7;
-}
-.content span.struct, .content a.struct {
- color: #ffa0a5;
-}
-.content span.enum, .content a.enum {
- color: #ffa0a5;
-}
-.content span.trait, .content a.trait {
- color: #39AFD7;
-}
-.content span.type, .content a.type {
- color: #39AFD7;
-}
-.content span.type,
-.content a.type,
-.block a.current.type { color: #39AFD7; }
-.content span.associatedtype,
-.content a.associatedtype,
-.block a.current.associatedtype { color: #39AFD7; }
-.content span.fn, .content a.fn, .content span.method,
-.content a.method, .content span.tymethod,
-.content a.tymethod, .content .fnname {
- color: #fdd687;
-}
-.content span.attr, .content a.attr, .content span.derive,
-.content a.derive, .content span.macro, .content a.macro {
- color: #a37acc;
-}
-
.sidebar a { color: #53b1db; }
.sidebar a.current.type { color: #53b1db; }
-.sidebar a.current.associatedtype { color: #53b1db; }
pre.rust .comment { color: #788797; }
pre.rust .doccomment { color: #a1ac88; }
-nav.main .current {
- border-top-color: #5c6773;
- border-bottom-color: #5c6773;
-}
-nav.main .separator {
- border: 1px solid #5c6773;
-}
-a {
- color: #39AFD7;
-}
-
.sidebar h2 a,
.sidebar h3 a {
color: white;
}
-.search-results a {
- color: #0096cf;
-}
body.source .example-wrap pre.rust a {
background: #333;
}
-details.rustdoc-toggle > summary.hideme > span,
-details.rustdoc-toggle > summary::before {
- color: #999;
-}
-
details.rustdoc-toggle > summary::before {
filter: invert(100%);
}
-#crate-search, .search-input {
- background-color: #141920;
- border-color: #424c57;
+#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 {
- /* Without the `!important`, the border-color is ignored for `<select>`...
- It cannot be in the group above because `.search-input` has a different border color on
- hover. */
- border-color: #424c57 !important;
+#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%);
}
.search-input {
- color: #ffffff;
+ color: #fff;
}
.module-item .stab,
@@ -203,20 +157,9 @@ details.rustdoc-toggle > summary::before {
color: #000;
}
-/* Created this empty rule to satisfy the theme checks. */
-.stab.empty-impl {}
-.stab.must_implement {}
-
-.stab.unstable,
-.stab.deprecated,
-.stab.portability,
-.stab.empty-impl,
-.stab.must_implement {
+.stab {
color: #c5c5c5;
background: #314559 !important;
- border-style: none !important;
- border-radius: 4px;
- padding: 3px 6px 3px 6px;
}
.stab.portability > code {
@@ -224,11 +167,6 @@ details.rustdoc-toggle > summary::before {
background: none;
}
-.rightside,
-.out-of-band {
- color: grey;
-}
-
.result-name .primitive > i, .result-name .keyword > i {
color: #788797;
}
@@ -239,7 +177,7 @@ details.rustdoc-toggle > summary::before {
pre.rust .number, pre.rust .string { color: #b8cc52; }
pre.rust .kw, pre.rust .kw-2, pre.rust .prelude-ty,
pre.rust .bool-val, pre.rust .prelude-val,
-pre.rust .op, pre.rust .lifetime { color: #ff7733; }
+pre.rust .lifetime { color: #ff7733; }
pre.rust .macro, pre.rust .macro-nonterminal { color: #a37acc; }
pre.rust .question-mark {
color: #ff9011;
@@ -251,9 +189,6 @@ pre.rust .self {
pre.rust .attribute {
color: #e6e1cf;
}
-pre.rust .attribute .ident, pre.rust .attribute .op {
- color: #e6e1cf;
-}
.example-wrap > pre.line-number {
color: #5c67736e;
@@ -272,64 +207,11 @@ a.test-arrow:hover {
color: #c5c5c5;
}
-.toggle-label,
-.code-attribute {
- color: #999;
-}
-
:target {
background: rgba(255, 236, 164, 0.06);
border-right: 3px solid rgba(255, 180, 76, 0.85);
}
-pre.compile_fail {
- border-left: 2px solid rgba(255,0,0,.4);
-}
-
-pre.compile_fail:hover, .information:hover + pre.compile_fail {
- border-left: 2px solid #f00;
-}
-
-pre.should_panic {
- border-left: 2px solid rgba(255,0,0,.4);
-}
-
-pre.should_panic:hover, .information:hover + pre.should_panic {
- border-left: 2px solid #f00;
-}
-
-pre.ignore {
- border-left: 2px solid rgba(255,142,0,.6);
-}
-
-pre.ignore:hover, .information:hover + pre.ignore {
- border-left: 2px solid #ff9200;
-}
-
-.tooltip.compile_fail {
- color: rgba(255,0,0,.5);
-}
-
-.information > .compile_fail:hover {
- color: #f00;
-}
-
-.tooltip.should_panic {
- color: rgba(255,0,0,.5);
-}
-
-.information > .should_panic:hover {
- color: #f00;
-}
-
-.tooltip.ignore {
- color: rgba(255,142,0,.6);
-}
-
-.information > .ignore:hover {
- color: #ff9200;
-}
-
.search-failed a {
color: #39AFD7;
}
@@ -337,7 +219,6 @@ pre.ignore:hover, .information:hover + pre.ignore {
.tooltip::after {
background-color: #314559;
color: #c5c5c5;
- border: 1px solid #5c6773;
}
.tooltip::before {
@@ -346,11 +227,6 @@ pre.ignore:hover, .information:hover + pre.ignore {
.notable-traits-tooltiptext {
background-color: #314559;
- border-color: #5c6773;
-}
-
-.notable-traits-tooltiptext .notable {
- border-bottom-color: #5c6773;
}
#titles > button.selected {
@@ -378,38 +254,11 @@ individually rather than as a group) */
/* FIXME: these rules should be at the bottom of the file but currently must be
above the `@media (max-width: 700px)` rules due to a bug in the css checker */
/* see https://github.com/rust-lang/rust/pull/71237#issuecomment-618170143 */
-.search-input:focus {}
-.content span.attr,.content a.attr,.block a.current.attr,.content span.derive,.content a.derive,
-.block a.current.derive,.content span.macro,.content a.macro,.block a.current.macro {}
-.content span.struct,.content a.struct,.block a.current.struct {}
-#titles>button:hover,#titles>button.selected {}
-.content span.typedef,.content a.typedef,.block a.current.typedef {}
-.content span.union,.content a.union,.block a.current.union {}
pre.rust .lifetime {}
-.stab.unstable {}
-h2,
-h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod) {}
-.content span.enum,.content a.enum,.block a.current.enum {}
-.content span.constant,.content a.constant,.block a.current.constant,.content span.static,
-.content a.static, .block a.current.static {}
-.content span.keyword,.content a.keyword,.block a.current.keyword {}
-pre.rust .comment {}
-.content span.traitalias,.content a.traitalias,.block a.current.traitalias {}
-.content span.fn,.content a.fn,.block a.current.fn,.content span.method,.content a.method,
-.block a.current.method,.content span.tymethod,.content a.tymethod,.block a.current.tymethod,
-.content .fnname {}
pre.rust .kw {}
-pre.rust .self,pre.rust .bool-val,pre.rust .prelude-val,pre.rust .attribute,
-pre.rust .attribute .ident {}
-.content span.foreigntype,.content a.foreigntype,.block a.current.foreigntype {}
-pre.rust .doccomment {}
-.stab.deprecated {}
-.content a.attr,.content a.derive,.content a.macro {}
-.stab.portability {}
-.content span.primitive,.content a.primitive,.block a.current.primitive {}
-.content span.externcrate,.content span.mod,.content a.mod,.block a.current.mod {}
-pre.rust .kw-2,pre.rust .prelude-ty {}
-.content span.trait,.content a.trait,.block a.current.trait {}
+#titles > button:hover, #titles > button.selected {}
+pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val, pre.rust .attribute {}
+pre.rust .kw-2, pre.rust .prelude-ty {}
.search-results a:focus span {}
a.result-trait:focus {}
@@ -445,32 +294,18 @@ a.result-keyword:focus {}
.sidebar a.current.constant
.sidebar a.current.static {}
.sidebar a.current.primitive {}
-.sidebar a.current.externcrate
-.sidebar a.current.mod {}
.sidebar a.current.trait {}
.sidebar a.current.traitalias {}
-.sidebar a.current.fn,
-.sidebar a.current.method,
-.sidebar a.current.tymethod {}
+.sidebar a.current.fn {}
.sidebar a.current.keyword {}
-@media (max-width: 700px) {
- .sidebar-elems {
- border-right-color: #5c6773;
- }
-}
-
kbd {
color: #c5c5c5;
background-color: #314559;
- border-color: #5c6773;
- border-bottom-color: #5c6773;
box-shadow: inset 0 -1px 0 #5c6773;
}
#settings-menu > a, #help-button > button {
- border-color: #5c6773;
- background-color: #0f1419;
color: #fff;
}
@@ -478,39 +313,11 @@ kbd {
filter: invert(100);
}
-.popover, .popover::before,
-#help-button span.top, #help-button span.bottom {
- border-color: #5c6773;
-}
-
-#copy-path {
- color: #fff;
-}
-#copy-path > img {
- filter: invert(70%);
-}
-#copy-path:hover > img {
- filter: invert(100%);
-}
-
#settings-menu > a:hover, #settings-menu > a:focus,
#help-button > button:hover, #help-button > button:focus {
border-color: #e0e0e0;
}
-#theme-choices {
- border-color: #5c6773;
- background-color: #0f1419;
-}
-
-#theme-choices > button:not(:first-child) {
- border-top-color: #5c6773;
-}
-
-#theme-choices > button:hover, #theme-choices > button:focus {
- background-color: rgba(110, 110, 110, 0.33);
-}
-
.search-results .result-name span.alias {
color: #c5c5c5;
}
@@ -520,7 +327,6 @@ kbd {
#source-sidebar > .title {
color: #fff;
- border-bottom-color: #5c6773;
}
#source-sidebar div.files > a:hover, details.dir-entry summary:hover,
#source-sidebar div.files > a:focus, details.dir-entry summary:focus {
@@ -540,9 +346,6 @@ kbd {
border-color: white;
color: white;
}
-.more-examples-toggle summary, .more-examples-toggle .hide-more {
- color: #999;
-}
.scraped-example .example-wrap .rust span.highlight {
background: rgb(91, 59, 1);
}
diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css
index a550eb1c1..07a1ed8b7 100644
--- a/src/librustdoc/html/static/css/themes/dark.css
+++ b/src/librustdoc/html/static/css/themes/dark.css
@@ -9,6 +9,27 @@
--scrollbar-thumb-background-color: rgba(32, 34, 37, .6);
--scrollbar-color: rgba(32,34,37,.6) #5a5a5a;
--headings-border-bottom-color: #d2d2d2;
+ --border-color: #e0e0e0;
+ --button-background-color: #f0f0f0;
+ --right-side-color: grey;
+ --code-attribute-color: #999;
+ --toggles-color: #999;
+ --search-input-focused-border-color: #008dfd;
+ --copy-path-button-color: #999;
+ --copy-path-img-filter: invert(50%);
+ --copy-path-img-hover-filter: invert(65%);
+ --codeblock-error-hover-color: rgb(255, 0, 0);
+ --codeblock-error-color: rgba(255, 0, 0, .5);
+ --codeblock-ignore-hover-color: rgb(255, 142, 0);
+ --codeblock-ignore-color: rgba(255, 142, 0, .6);
+ --type-link-color: #2dbfb8;
+ --trait-link-color: #b78cf2;
+ --assoc-item-link-color: #d2991d;
+ --function-link-color: #2bab63;
+ --macro-link-color: #09bd00;
+ --keyword-link-color: #d2991d;
+ --mod-link-color: #d2991d;
+ --link-color: #d2991d;
}
.slider {
@@ -21,10 +42,6 @@ input:focus + .slider {
box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3);
}
-.in-band {
- background-color: #353535;
-}
-
.rust-logo {
filter: drop-shadow(1px 0 0px #fff)
drop-shadow(0 1px 0 #fff)
@@ -42,10 +59,6 @@ input:focus + .slider {
background-color: #0a042f !important;
}
-.docblock table td, .docblock table th {
- border-color: #ddd;
-}
-
.search-results a:hover {
background-color: #777;
}
@@ -78,35 +91,10 @@ a.result-keyword:focus { background-color: #884719; }
.content .item-info::before { color: #ccc; }
-.content span.enum, .content a.enum, .block a.current.enum { color: #2dbfb8; }
-.content span.struct, .content a.struct, .block a.current.struct { color: #2dbfb8; }
-.content span.type, .content a.type, .block a.current.type { color: #2dbfb8; }
-.content span.associatedtype,
-.content a.associatedtype,
-.block a.current.associatedtype { color: #D2991D; }
-.content span.foreigntype, .content a.foreigntype, .block a.current.foreigntype { color: #2dbfb8; }
-.content span.attr, .content a.attr, .block a.current.attr,
-.content span.derive, .content a.derive, .block a.current.derive,
-.content span.macro, .content a.macro, .block a.current.macro { color: #09bd00; }
-.content span.union, .content a.union, .block a.current.union { color: #2dbfb8; }
-.content span.constant, .content a.constant, .block a.current.constant,
-.content span.static, .content a.static, .block a.current.static { color: #D2991D; }
-.content span.primitive, .content a.primitive, .block a.current.primitive { color: #2dbfb8; }
-.content span.externcrate,
-.content span.mod, .content a.mod, .block a.current.mod { color: #D2991D; }
-.content span.trait, .content a.trait, .block a.current.trait { color: #b78cf2; }
-.content span.traitalias, .content a.traitalias, .block a.current.traitalias { color: #b78cf2; }
-.content span.fn, .content a.fn, .block a.current.fn,
-.content span.method, .content a.method, .block a.current.method,
-.content span.tymethod, .content a.tymethod, .block a.current.tymethod,
-.content .fnname{ color: #2BAB63; }
-.content span.keyword, .content a.keyword, .block a.current.keyword { color: #D2991D; }
-
.sidebar a { color: #fdbf35; }
.sidebar a.current.enum { color: #12ece2; }
.sidebar a.current.struct { color: #12ece2; }
.sidebar a.current.type { color: #12ece2; }
-.sidebar a.current.associatedtype { color: #fdbf35; }
.sidebar a.current.foreigntype { color: #12ece2; }
.sidebar a.current.attr,
.sidebar a.current.derive,
@@ -115,74 +103,42 @@ a.result-keyword:focus { background-color: #884719; }
.sidebar a.current.constant
.sidebar a.current.static { color: #fdbf35; }
.sidebar a.current.primitive { color: #12ece2; }
-.sidebar a.current.externcrate
-.sidebar a.current.mod { color: #fdbf35; }
.sidebar a.current.trait { color: #cca7ff; }
.sidebar a.current.traitalias { color: #cca7ff; }
-.sidebar a.current.fn,
-.sidebar a.current.method,
-.sidebar a.current.tymethod { color: #32d479; }
+.sidebar a.current.fn { color: #32d479; }
.sidebar a.current.keyword { color: #fdbf35; }
pre.rust .comment { color: #8d8d8b; }
pre.rust .doccomment { color: #8ca375; }
-nav.main .current {
- border-top-color: #eee;
- border-bottom-color: #eee;
-}
-nav.main .separator {
- border-color: #eee;
-}
-
-a {
- color: #D2991D;
-}
-
body.source .example-wrap pre.rust a {
background: #333;
}
-details.rustdoc-toggle > summary.hideme > span,
-details.rustdoc-toggle > summary::before {
- color: #999;
-}
-
details.rustdoc-toggle > summary::before {
filter: invert(100%);
}
-#crate-search, .search-input {
+.search-input {
color: #111;
- background-color: #f0f0f0;
- border-color: #f0f0f0;
}
-#crate-search {
- /* Without the `!important`, the border-color is ignored for `<select>`...
- It cannot be in the group above because `.search-input` has a different border color on
- hover. */
- border-color: #f0f0f0 !important;
+#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%);
}
-
-.search-input {
- border-color: #e0e0e0;
+#crate-search:hover, #crate-search:focus {
+ border-color: #2196f3 !important;
}
-
-.search-input:focus {
- border-color: #008dfd;
+#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%);
}
-.stab.empty-impl { background: #FFF5D6; border-color: #FFC600; color: #2f2f2f; }
-.stab.unstable { background: #FFF5D6; border-color: #FFC600; color: #2f2f2f; }
-.stab.deprecated { background: #ffc4c4; border-color: #db7b7b; color: #2f2f2f; }
-.stab.must_implement { background: #F3DFFF; border-color: #b07bdb; color: #2f2f2f; }
-.stab.portability { background: #F3DFFF; border-color: #b07bdb; color: #2f2f2f; }
-.stab.portability > code { background: none; }
+.stab { background: #314559; }
-.rightside,
-.out-of-band {
- color: grey;
+.stab.portability > code {
+ color: #e6e1cf;
+ background: none;
}
.line-numbers :target { background-color: transparent; }
@@ -192,7 +148,7 @@ pre.rust .kw { color: #ab8ac1; }
pre.rust .kw-2, pre.rust .prelude-ty { color: #769acb; }
pre.rust .number, pre.rust .string { color: #83a300; }
pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val,
-pre.rust .attribute, pre.rust .attribute .ident { color: #ee6868; }
+pre.rust .attribute { color: #ee6868; }
pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; }
pre.rust .lifetime { color: #d97f26; }
pre.rust .question-mark {
@@ -212,64 +168,11 @@ a.test-arrow:hover{
background-color: #4e8bca;
}
-.toggle-label,
-.code-attribute {
- color: #999;
-}
-
:target {
background-color: #494a3d;
border-right: 3px solid #bb7410;
}
-pre.compile_fail {
- border-left: 2px solid rgba(255,0,0,.8);
-}
-
-pre.compile_fail:hover, .information:hover + pre.compile_fail {
- border-left: 2px solid #f00;
-}
-
-pre.should_panic {
- border-left: 2px solid rgba(255,0,0,.8);
-}
-
-pre.should_panic:hover, .information:hover + pre.should_panic {
- border-left: 2px solid #f00;
-}
-
-pre.ignore {
- border-left: 2px solid rgba(255,142,0,.6);
-}
-
-pre.ignore:hover, .information:hover + pre.ignore {
- border-left: 2px solid #ff9200;
-}
-
-.tooltip.compile_fail {
- color: rgba(255,0,0,.8);
-}
-
-.information > .compile_fail:hover {
- color: #f00;
-}
-
-.tooltip.should_panic {
- color: rgba(255,0,0,.8);
-}
-
-.information > .should_panic:hover {
- color: #f00;
-}
-
-.tooltip.ignore {
- color: rgba(255,142,0,.6);
-}
-
-.information > .ignore:hover {
- color: #ff9200;
-}
-
.search-failed a {
color: #0089ff;
}
@@ -286,11 +189,6 @@ pre.ignore:hover, .information:hover + pre.ignore {
.notable-traits-tooltiptext {
background-color: #111;
- border-color: #777;
-}
-
-.notable-traits-tooltiptext .notable {
- border-bottom-color: #d2d2d2;
}
#titles > button:not(.selected) {
@@ -307,23 +205,13 @@ pre.ignore:hover, .information:hover + pre.ignore {
color: #888;
}
-@media (max-width: 700px) {
- .sidebar-elems {
- border-right-color: #000;
- }
-}
-
kbd {
color: #000;
background-color: #fafbfc;
- border-color: #d1d5da;
- border-bottom-color: #c6cbd1;
box-shadow: inset 0 -1px 0 #c6cbd1;
}
#settings-menu > a, #help-button > button {
- border-color: #e0e0e0;
- background: #f0f0f0;
color: #000;
}
@@ -332,34 +220,6 @@ kbd {
border-color: #ffb900;
}
-.popover, .popover::before,
-#help-button span.top, #help-button span.bottom {
- border-color: #d2d2d2;
-}
-
-#copy-path {
- color: #999;
-}
-#copy-path > img {
- filter: invert(50%);
-}
-#copy-path:hover > img {
- filter: invert(65%);
-}
-
-#theme-choices {
- border-color: #e0e0e0;
- background-color: #353535;
-}
-
-#theme-choices > button:not(:first-child) {
- border-top-color: #e0e0e0;
-}
-
-#theme-choices > button:hover, #theme-choices > button:focus {
- background-color: #4e4e4e;
-}
-
.search-results .result-name span.alias {
color: #fff;
}
@@ -367,9 +227,6 @@ kbd {
color: #ccc;
}
-#source-sidebar > .title {
- border-bottom-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;
@@ -386,9 +243,6 @@ kbd {
border-color: white;
color: white;
}
-.more-examples-toggle summary, .more-examples-toggle .hide-more {
- color: #999;
-}
.scraped-example .example-wrap .rust span.highlight {
background: rgb(91, 59, 1);
}
diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css
index b751acff1..64335f629 100644
--- a/src/librustdoc/html/static/css/themes/light.css
+++ b/src/librustdoc/html/static/css/themes/light.css
@@ -9,6 +9,27 @@
--scrollbar-thumb-background-color: rgba(36, 37, 39, 0.6);
--scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9;
--headings-border-bottom-color: #ddd;
+ --border-color: #e0e0e0;
+ --button-background-color: #fff;
+ --right-side-color: grey;
+ --code-attribute-color: #999;
+ --toggles-color: #999;
+ --search-input-focused-border-color: #66afe9;
+ --copy-path-button-color: #999;
+ --copy-path-img-filter: invert(50%);
+ --copy-path-img-hover-filter: invert(35%);
+ --codeblock-error-hover-color: rgb(255, 0, 0);
+ --codeblock-error-color: rgba(255, 0, 0, .5);
+ --codeblock-ignore-hover-color: rgb(255, 142, 0);
+ --codeblock-ignore-color: rgba(255, 142, 0, .6);
+ --type-link-color: #ad378a;
+ --trait-link-color: #6e4fc9;
+ --assoc-item-link-color: #3873ad;
+ --function-link-color: #ad7c37;
+ --macro-link-color: #068000;
+ --keyword-link-color: #3873ad;
+ --mod-link-color: #3873ad;
+ --link-color: #3873ad;
}
.slider {
@@ -21,10 +42,6 @@ input:focus + .slider {
box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3);
}
-.in-band {
- background-color: white;
-}
-
.rust-logo {
/* This rule exists to force other themes to explicitly style the logo.
* Rustdoc has a custom linter for this purpose.
@@ -41,10 +58,6 @@ input:focus + .slider {
background-color: #FDFFD3 !important;
}
-.docblock table td, .docblock table th {
- border-color: #ddd;
-}
-
.search-results a:hover {
background-color: #ddd;
}
@@ -77,35 +90,10 @@ a.result-keyword:focus { background-color: #afc6e4; }
.content .item-info::before { color: #ccc; }
-.content span.enum, .content a.enum, .block a.current.enum { color: #AD378A; }
-.content span.struct, .content a.struct, .block a.current.struct { color: #AD378A; }
-.content span.type, .content a.type, .block a.current.type { color: #AD378A; }
-.content span.foreigntype, .content a.foreigntype, .block a.current.foreigntype { color: #3873AD; }
-.content span.associatedtype,
-.content a.associatedtype,
-.block a.current.associatedtype { color: #3873AD; }
-.content span.attr, .content a.attr, .block a.current.attr,
-.content span.derive, .content a.derive, .block a.current.derive,
-.content span.macro, .content a.macro, .block a.current.macro { color: #068000; }
-.content span.union, .content a.union, .block a.current.union { color: #AD378A; }
-.content span.constant, .content a.constant, .block a.current.constant,
-.content span.static, .content a.static, .block a.current.static { color: #3873AD; }
-.content span.primitive, .content a.primitive, .block a.current.primitive { color: #AD378A; }
-.content span.externcrate,
-.content span.mod, .content a.mod, .block a.current.mod { color: #3873AD; }
-.content span.trait, .content a.trait, .block a.current.trait { color: #6E4FC9; }
-.content span.traitalias, .content a.traitalias, .block a.current.traitalias { color: #5137AD; }
-.content span.fn, .content a.fn, .block a.current.fn,
-.content span.method, .content a.method, .block a.current.method,
-.content span.tymethod, .content a.tymethod, .block a.current.tymethod,
-.content .fnname { color: #AD7C37; }
-.content span.keyword, .content a.keyword, .block a.current.keyword { color: #3873AD; }
-
.sidebar a { color: #356da4; }
.sidebar a.current.enum { color: #a63283; }
.sidebar a.current.struct { color: #a63283; }
.sidebar a.current.type { color: #a63283; }
-.sidebar a.current.associatedtype { color: #356da4; }
.sidebar a.current.foreigntype { color: #356da4; }
.sidebar a.current.attr,
.sidebar a.current.derive,
@@ -114,64 +102,29 @@ a.result-keyword:focus { background-color: #afc6e4; }
.sidebar a.current.constant
.sidebar a.current.static { color: #356da4; }
.sidebar a.current.primitive { color: #a63283; }
-.sidebar a.current.externcrate
-.sidebar a.current.mod { color: #356da4; }
.sidebar a.current.trait { color: #6849c3; }
.sidebar a.current.traitalias { color: #4b349e; }
-.sidebar a.current.fn,
-.sidebar a.current.method,
-.sidebar a.current.tymethod { color: #a67736; }
+.sidebar a.current.fn { color: #a67736; }
.sidebar a.current.keyword { color: #356da4; }
-nav.main .current {
- border-top-color: #000;
- border-bottom-color: #000;
-}
-nav.main .separator {
- border: 1px solid #000;
-}
-
-a {
- color: #3873AD;
-}
-
body.source .example-wrap pre.rust a {
background: #eee;
}
-details.rustdoc-toggle > summary.hideme > span,
-details.rustdoc-toggle > summary::before {
- color: #999;
+#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, .search-input {
- background-color: white;
- border-color: #e0e0e0;
-}
-
-#crate-search {
- /* Without the `!important`, the border-color is ignored for `<select>`...
- It cannot be in the group above because `.search-input` has a different border color on
- hover. */
- border-color: #e0e0e0 !important;
+#crate-search:hover, #crate-search:focus {
+ border-color: #717171 !important;
}
-
-.search-input:focus {
- border-color: #66afe9;
+#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%);
}
-.stab.empty-impl { background: #FFF5D6; border-color: #FFC600; }
-.stab.unstable { background: #FFF5D6; border-color: #FFC600; }
-.stab.deprecated { background: #ffc4c4; border-color: #db7b7b; }
-.stab.must_implement { background: #F3DFFF; border-color: #b07bdb; }
-.stab.portability { background: #F3DFFF; border-color: #b07bdb; }
+.stab { background: #FFF5D6; border-color: #FFC600; }
.stab.portability > code { background: none; }
-.rightside,
-.out-of-band {
- color: grey;
-}
-
.line-numbers :target { background-color: transparent; }
/* Code highlighting */
@@ -179,7 +132,7 @@ pre.rust .kw { color: #8959A8; }
pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; }
pre.rust .number, pre.rust .string { color: #718C00; }
pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val,
-pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; }
+pre.rust .attribute { color: #C82829; }
pre.rust .comment { color: #8E908C; }
pre.rust .doccomment { color: #4D4D4C; }
pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; }
@@ -201,64 +154,11 @@ a.test-arrow:hover{
background-color: #4e8bca;
}
-.toggle-label,
-.code-attribute {
- color: #999;
-}
-
:target {
background: #FDFFD3;
border-right: 3px solid #AD7C37;
}
-pre.compile_fail {
- border-left: 2px solid rgba(255,0,0,.5);
-}
-
-pre.compile_fail:hover, .information:hover + pre.compile_fail {
- border-left: 2px solid #f00;
-}
-
-pre.should_panic {
- border-left: 2px solid rgba(255,0,0,.5);
-}
-
-pre.should_panic:hover, .information:hover + pre.should_panic {
- border-left: 2px solid #f00;
-}
-
-pre.ignore {
- border-left: 2px solid rgba(255,142,0,.6);
-}
-
-pre.ignore:hover, .information:hover + pre.ignore {
- border-left: 2px solid #ff9200;
-}
-
-.tooltip.compile_fail {
- color: rgba(255,0,0,.5);
-}
-
-.information > .compile_fail:hover {
- color: #f00;
-}
-
-.tooltip.should_panic {
- color: rgba(255,0,0,.5);
-}
-
-.information > .should_panic:hover {
- color: #f00;
-}
-
-.tooltip.ignore {
- color: rgba(255,142,0,.6);
-}
-
-.information > .ignore:hover {
- color: #ff9200;
-}
-
.search-failed a {
color: #3873AD;
}
@@ -274,11 +174,6 @@ pre.ignore:hover, .information:hover + pre.ignore {
.notable-traits-tooltiptext {
background-color: #eee;
- border-color: #999;
-}
-
-.notable-traits-tooltiptext .notable {
- border-bottom-color: #DDDDDD;
}
#titles > button:not(.selected) {
@@ -295,58 +190,17 @@ pre.ignore:hover, .information:hover + pre.ignore {
color: #888;
}
-@media (max-width: 700px) {
- .sidebar-elems {
- border-right-color: #000;
- }
-}
-
kbd {
color: #000;
background-color: #fafbfc;
- border-color: #d1d5da;
- border-bottom-color: #c6cbd1;
box-shadow: inset 0 -1px 0 #c6cbd1;
}
-#settings-menu > a, #help-button > button {
- border-color: #e0e0e0;
- background-color: #fff;
-}
-
#settings-menu > a:hover, #settings-menu > a:focus,
#help-button > button:hover, #help-button > button:focus {
border-color: #717171;
}
-.popover, .popover::before,
-#help-button span.top, #help-button span.bottom {
- border-color: #DDDDDD;
-}
-
-#copy-path {
- color: #999;
-}
-#copy-path > img {
- filter: invert(50%);
-}
-#copy-path:hover > img {
- filter: invert(35%);
-}
-
-#theme-choices {
- border-color: #ccc;
- background-color: #fff;
-}
-
-#theme-choices > button:not(:first-child) {
- border-top-color: #e0e0e0;
-}
-
-#theme-choices > button:hover, #theme-choices > button:focus {
- background-color: #eee;
-}
-
.search-results .result-name span.alias {
color: #000;
}
@@ -354,9 +208,6 @@ kbd {
color: #999;
}
-#source-sidebar > .title {
- border-bottom-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: #E0E0E0;
@@ -372,9 +223,6 @@ kbd {
border-color: black;
color: black;
}
-.more-examples-toggle summary, .more-examples-toggle .hide-more {
- color: #999;
-}
.scraped-example .example-wrap .rust span.highlight {
background: #fcffd6;
}
diff --git a/src/librustdoc/html/static/images/down-arrow.svg b/src/librustdoc/html/static/images/down-arrow.svg
index 35437e77a..5d76a64e9 100644
--- a/src/librustdoc/html/static/images/down-arrow.svg
+++ b/src/librustdoc/html/static/images/down-arrow.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" width="128" height="128" enable-background="new 0 0 128 128" version="1.1" viewBox="-30 -20 176 176" xml:space="preserve"><g><line x1="111" x2="64" y1="40.5" y2="87.499" fill="none" stroke="#2F3435" stroke-linecap="square" stroke-miterlimit="10" stroke-width="12"/><line x1="64" x2="17" y1="87.499" y2="40.5" fill="none" stroke="#2F3435" stroke-linecap="square" stroke-miterlimit="10" stroke-width="12"/></g></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" width="128" height="128" enable-background="new 0 0 128 128" version="1.1" viewBox="-30 -20 176 176" xml:space="preserve"><g><line x1="111" x2="64" y1="40.5" y2="87.499" fill="none" stroke="#000000" stroke-linecap="square" stroke-miterlimit="10" stroke-width="12"/><line x1="64" x2="17" y1="87.499" y2="40.5" fill="none" stroke="#000000" stroke-linecap="square" stroke-miterlimit="10" stroke-width="12"/></g></svg> \ No newline at end of file
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 0702b2b0b..6e9660ddc 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -348,8 +348,7 @@ function loadCss(cssFileName) {
function onHashChange(ev) {
// If we're in mobile mode, we should hide the sidebar in any case.
- const sidebar = document.getElementsByClassName("sidebar")[0];
- removeClass(sidebar, "shown");
+ hideSidebar();
handleHashes(ev);
}
@@ -501,6 +500,10 @@ function loadCss(cssFileName) {
const synthetic_implementors = document.getElementById("synthetic-implementors-list");
const inlined_types = new Set();
+ const TEXT_IDX = 0;
+ const SYNTHETIC_IDX = 1;
+ const TYPES_IDX = 2;
+
if (synthetic_implementors) {
// This `inlined_types` variable is used to avoid having the same implementation
// showing up twice. For example "String" in the "Sync" doc page.
@@ -525,9 +528,9 @@ function loadCss(cssFileName) {
// We don't want to include impls from this JS file, when the HTML already has them.
// The current crate should always be ignored. Other crates that should also be
// ignored are included in the attribute `data-ignore-extern-crates`.
- const ignoreExternCrates = document
- .querySelector("script[data-ignore-extern-crates]")
- .getAttribute("data-ignore-extern-crates");
+ const script = document
+ .querySelector("script[data-ignore-extern-crates]");
+ const ignoreExternCrates = script ? script.getAttribute("data-ignore-extern-crates") : "";
for (const lib of libs) {
if (lib === window.currentCrate || ignoreExternCrates.indexOf(lib) !== -1) {
continue;
@@ -536,10 +539,12 @@ function loadCss(cssFileName) {
struct_loop:
for (const struct of structs) {
- const list = struct.synthetic ? synthetic_implementors : implementors;
+ const list = struct[SYNTHETIC_IDX] ? synthetic_implementors : implementors;
- if (struct.synthetic) {
- for (const struct_type of struct.types) {
+ // The types list is only used for synthetic impls.
+ // If this changes, `main.js` and `write_shared.rs` both need changed.
+ if (struct[SYNTHETIC_IDX]) {
+ for (const struct_type of struct[TYPES_IDX]) {
if (inlined_types.has(struct_type)) {
continue struct_loop;
}
@@ -548,7 +553,7 @@ function loadCss(cssFileName) {
}
const code = document.createElement("h3");
- code.innerHTML = struct.text;
+ code.innerHTML = struct[TEXT_IDX];
addClass(code, "code-header");
addClass(code, "in-band");
@@ -694,9 +699,8 @@ function loadCss(cssFileName) {
(function() {
// To avoid checking on "rustdoc-line-numbers" value on every loop...
- let lineNumbersFunc = () => {};
if (getSettingValue("line-numbers") === "true") {
- lineNumbersFunc = x => {
+ onEachLazy(document.getElementsByClassName("rust-example-rendered"), x => {
const count = x.textContent.split("\n").length;
const elems = [];
for (let i = 0; i < count; ++i) {
@@ -706,33 +710,54 @@ function loadCss(cssFileName) {
addClass(node, "line-number");
node.innerHTML = elems.join("\n");
x.parentNode.insertBefore(node, x);
- };
+ });
}
- onEachLazy(document.getElementsByClassName("rust-example-rendered"), e => {
- if (hasClass(e, "compile_fail")) {
- e.addEventListener("mouseover", function() {
- this.parentElement.previousElementSibling.childNodes[0].style.color = "#f00";
- });
- e.addEventListener("mouseout", function() {
- this.parentElement.previousElementSibling.childNodes[0].style.color = "";
- });
- } else if (hasClass(e, "ignore")) {
- e.addEventListener("mouseover", function() {
- this.parentElement.previousElementSibling.childNodes[0].style.color = "#ff9200";
- });
- e.addEventListener("mouseout", function() {
- this.parentElement.previousElementSibling.childNodes[0].style.color = "";
- });
- }
- lineNumbersFunc(e);
- });
}());
+ let oldSidebarScrollPosition = null;
+
+ function showSidebar() {
+ if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) {
+ // This is to keep the scroll position on mobile.
+ oldSidebarScrollPosition = window.scrollY;
+ document.body.style.width = `${document.body.offsetWidth}px`;
+ document.body.style.position = "fixed";
+ document.body.style.top = `-${oldSidebarScrollPosition}px`;
+ document.querySelector(".mobile-topbar").style.top = `${oldSidebarScrollPosition}px`;
+ document.querySelector(".mobile-topbar").style.position = "relative";
+ } else {
+ oldSidebarScrollPosition = null;
+ }
+ const sidebar = document.getElementsByClassName("sidebar")[0];
+ addClass(sidebar, "shown");
+ }
+
function hideSidebar() {
+ if (oldSidebarScrollPosition !== null) {
+ // This is to keep the scroll position on mobile.
+ document.body.style.width = "";
+ document.body.style.position = "";
+ document.body.style.top = "";
+ document.querySelector(".mobile-topbar").style.top = "";
+ document.querySelector(".mobile-topbar").style.position = "";
+ // The scroll position is lost when resetting the style, hence why we store it in
+ // `oldSidebarScrollPosition`.
+ window.scrollTo(0, oldSidebarScrollPosition);
+ oldSidebarScrollPosition = null;
+ }
const sidebar = document.getElementsByClassName("sidebar")[0];
removeClass(sidebar, "shown");
}
+ window.addEventListener("resize", () => {
+ if (window.innerWidth >= window.RUSTDOC_MOBILE_BREAKPOINT &&
+ oldSidebarScrollPosition !== null) {
+ // If the user opens the sidebar in "mobile" mode, and then grows the browser window,
+ // we need to switch away from mobile mode and make the main content area scrollable.
+ hideSidebar();
+ }
+ });
+
function handleClick(id, f) {
const elem = document.getElementById(id);
if (elem) {
@@ -775,9 +800,9 @@ function loadCss(cssFileName) {
sidebar_menu_toggle.addEventListener("click", () => {
const sidebar = document.getElementsByClassName("sidebar")[0];
if (!hasClass(sidebar, "shown")) {
- addClass(sidebar, "shown");
+ showSidebar();
} else {
- removeClass(sidebar, "shown");
+ hideSidebar();
}
});
}
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 75c7bd45a..d04ec357c 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -429,9 +429,9 @@ function initSearch(rawSearchIndex) {
}
const posBefore = parserState.pos;
getNextElem(query, parserState, elems, endChar === ">");
- // This case can be encountered if `getNextElem` encounted a "stop character" right from
- // the start. For example if you have `,,` or `<>`. In this case, we simply move up the
- // current position to continue the parsing.
+ // This case can be encountered if `getNextElem` encountered a "stop character" right
+ // from the start. For example if you have `,,` or `<>`. In this case, we simply move up
+ // the current position to continue the parsing.
if (posBefore === parserState.pos) {
parserState.pos += 1;
}
@@ -581,7 +581,7 @@ function initSearch(rawSearchIndex) {
const elem = document.getElementById("crate-search");
if (elem &&
- elem.value !== "All crates" &&
+ elem.value !== "all crates" &&
hasOwnPropertyRustdoc(rawSearchIndex, elem.value)
) {
return elem.value;
@@ -1551,12 +1551,6 @@ function initSearch(rawSearchIndex) {
return [displayPath, href];
}
- function escape(content) {
- const h1 = document.createElement("h1");
- h1.textContent = content;
- return h1.innerHTML;
- }
-
function pathSplitter(path) {
const tmp = "<span>" + path.replace(/::/g, "::</span><span>");
if (tmp.endsWith("<span>")) {
@@ -1710,22 +1704,15 @@ function initSearch(rawSearchIndex) {
let crates = "";
const crates_list = Object.keys(rawSearchIndex);
if (crates_list.length > 1) {
- crates = " in <select id=\"crate-search\"><option value=\"All crates\">" +
- "All crates</option>";
+ crates = " in&nbsp;<div id=\"crate-search-div\"><select id=\"crate-search\">" +
+ "<option value=\"all crates\">all crates</option>";
for (const c of crates_list) {
crates += `<option value="${c}" ${c === filterCrates && "selected"}>${c}</option>`;
}
- crates += "</select>";
- }
-
- let typeFilter = "";
- if (results.query.typeFilter !== NO_TYPE_FILTER) {
- typeFilter = " (type: " + escape(itemTypes[results.query.typeFilter]) + ")";
+ crates += "</select></div>";
}
- let output = "<div id=\"search-settings\">" +
- `<h1 class="search-results-title">Results for ${escape(results.query.userQuery)}` +
- `${typeFilter}</h1>${crates}</div>`;
+ let output = `<h1 class="search-results-title">Results${crates}</h1>`;
if (results.query.error !== null) {
output += `<h3>Query parser error: "${results.query.error}".</h3>`;
output += "<div id=\"titles\">" +
@@ -2245,7 +2232,7 @@ function initSearch(rawSearchIndex) {
}
function updateCrate(ev) {
- if (ev.target.value === "All crates") {
+ if (ev.target.value === "all crates") {
// If we don't remove it from the URL, it'll be picked up again by the search.
const params = searchState.getQueryStringParams();
const query = searchState.input.value.trim();
diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js
index c45d61429..06d15d9e5 100644
--- a/src/librustdoc/html/static/js/source-script.js
+++ b/src/librustdoc/html/static/js/source-script.js
@@ -10,7 +10,7 @@
(function() {
const rootPath = document.getElementById("rustdoc-vars").attributes["data-root-path"].value;
-let oldScrollPosition = 0;
+let oldScrollPosition = null;
const NAME_OFFSET = 0;
const DIRS_OFFSET = 1;
@@ -75,18 +75,21 @@ function toggleSidebar() {
oldScrollPosition = window.scrollY;
document.body.style.position = "fixed";
document.body.style.top = `-${oldScrollPosition}px`;
+ } else {
+ oldScrollPosition = null;
}
addClass(document.documentElement, "source-sidebar-expanded");
child.innerText = "<";
updateLocalStorage("source-sidebar-show", "true");
} else {
- if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) {
+ if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT && oldScrollPosition !== null) {
// This is to keep the scroll position on mobile.
document.body.style.position = "";
document.body.style.top = "";
// The scroll position is lost when resetting the style, hence why we store it in
- // `oldScroll`.
+ // `oldScrollPosition`.
window.scrollTo(0, oldScrollPosition);
+ oldScrollPosition = null;
}
removeClass(document.documentElement, "source-sidebar-expanded");
child.innerText = ">";
@@ -94,6 +97,17 @@ function toggleSidebar() {
}
}
+window.addEventListener("resize", () => {
+ if (window.innerWidth >= window.RUSTDOC_MOBILE_BREAKPOINT && oldScrollPosition !== null) {
+ // If the user opens the sidebar in "mobile" mode, and then grows the browser window,
+ // we need to switch away from mobile mode and make the main content area scrollable.
+ document.body.style.position = "";
+ document.body.style.top = "";
+ window.scrollTo(0, oldScrollPosition);
+ oldScrollPosition = null;
+ }
+});
+
function createSidebarToggle() {
const sidebarToggle = document.createElement("div");
sidebarToggle.id = "sidebar-toggle";
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index 8e25f6764..7caffeae3 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -13,13 +13,13 @@
<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="stylesheet" type="text/css" {# -#}
+ <link rel="stylesheet" {# -#}
href="{{static_root_path|safe}}normalize{{page.resource_suffix}}.css"> {#- -#}
- <link rel="stylesheet" type="text/css" {# -#}
+ <link rel="stylesheet" {# -#}
href="{{static_root_path|safe}}rustdoc{{page.resource_suffix}}.css" {# -#}
id="mainThemeStyle"> {#- -#}
{%- for theme in themes -%}
- <link rel="stylesheet" type="text/css" {# -#}
+ <link rel="stylesheet" {# -#}
href="{{static_root_path|safe}}{{theme}}{{page.resource_suffix}}.css" {# -#}
{%- if theme == "light" -%}
id="themeStyle"
@@ -51,7 +51,7 @@
href="{{static_root_path|safe}}noscript{{page.resource_suffix}}.css"> {#- -#}
</noscript> {#- -#}
{%- if layout.css_file_extension.is_some() -%}
- <link rel="stylesheet" type="text/css" {# -#}
+ <link rel="stylesheet" {# -#}
href="{{static_root_path|safe}}theme{{page.resource_suffix}}.css"> {#- -#}
{%- endif -%}
{%- if !layout.favicon.is_empty() -%}
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 716a4c9ea..49a31f5f1 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -19,6 +19,7 @@ use crate::clean::utils::print_const_expr;
use crate::clean::{self, ItemId};
use crate::formats::item_type::ItemType;
use crate::json::JsonRenderer;
+use crate::passes::collect_intra_doc_links::UrlFragment;
impl JsonRenderer<'_> {
pub(super) fn convert_item(&self, item: clean::Item) -> Option<Item> {
@@ -29,8 +30,14 @@ impl JsonRenderer<'_> {
.get(&item.item_id)
.into_iter()
.flatten()
- .map(|clean::ItemLink { link, did, .. }| {
- (link.clone(), from_item_id((*did).into(), self.tcx))
+ .map(|clean::ItemLink { link, page_id, fragment, .. }| {
+ let id = match fragment {
+ Some(UrlFragment::Item(frag_id)) => *frag_id,
+ // FIXME: Pass the `UserWritten` segment to JSON consumer.
+ Some(UrlFragment::UserWritten(_)) | None => *page_id,
+ };
+
+ (link.clone(), from_item_id(id.into(), self.tcx))
})
.collect();
let docs = item.attrs.collapsed_doc_value();
@@ -46,10 +53,14 @@ impl JsonRenderer<'_> {
clean::KeywordItem => return None,
clean::StrippedItem(ref inner) => {
match &**inner {
- // We document non-empty stripped modules as with `Module::is_stripped` set to
+ // We document stripped modules as with `Module::is_stripped` set to
// `true`, to prevent contained items from being orphaned for downstream users,
// as JSON does no inlining.
- clean::ModuleItem(m) if !m.items.is_empty() => from_clean_item(item, self.tcx),
+ clean::ModuleItem(_)
+ if self.imported_items.contains(&item_id.expect_def_id()) =>
+ {
+ from_clean_item(item, self.tcx)
+ }
_ => return None,
}
}
@@ -59,7 +70,7 @@ impl JsonRenderer<'_> {
id: from_item_id_with_name(item_id, self.tcx, name),
crate_id: item_id.krate().as_u32(),
name: name.map(|sym| sym.to_string()),
- span: self.convert_span(span),
+ span: span.and_then(|span| self.convert_span(span)),
visibility: self.convert_visibility(visibility),
docs,
attrs,
@@ -119,6 +130,16 @@ where
}
}
+impl<I, T, U> FromWithTcx<I> for Vec<U>
+where
+ I: IntoIterator<Item = T>,
+ U: FromWithTcx<T>,
+{
+ fn from_tcx(f: I, tcx: TyCtxt<'_>) -> Vec<U> {
+ f.into_iter().map(|x| x.into_tcx(tcx)).collect()
+ }
+}
+
pub(crate) fn from_deprecation(deprecation: rustc_attr::Deprecation) -> Deprecation {
#[rustfmt::skip]
let rustc_attr::Deprecation { since, note, is_since_rustc_version: _, suggestion: _ } = deprecation;
@@ -130,11 +151,11 @@ impl FromWithTcx<clean::GenericArgs> for GenericArgs {
use clean::GenericArgs::*;
match args {
AngleBracketed { args, bindings } => GenericArgs::AngleBracketed {
- args: args.into_vec().into_iter().map(|a| a.into_tcx(tcx)).collect(),
- bindings: bindings.into_iter().map(|a| a.into_tcx(tcx)).collect(),
+ args: args.into_vec().into_tcx(tcx),
+ bindings: bindings.into_tcx(tcx),
},
Parenthesized { inputs, output } => GenericArgs::Parenthesized {
- inputs: inputs.into_vec().into_iter().map(|a| a.into_tcx(tcx)).collect(),
+ inputs: inputs.into_vec().into_tcx(tcx),
output: output.map(|a| (*a).into_tcx(tcx)),
},
}
@@ -145,7 +166,7 @@ impl FromWithTcx<clean::GenericArg> for GenericArg {
fn from_tcx(arg: clean::GenericArg, tcx: TyCtxt<'_>) -> Self {
use clean::GenericArg::*;
match arg {
- Lifetime(l) => GenericArg::Lifetime(l.0.to_string()),
+ Lifetime(l) => GenericArg::Lifetime(convert_lifetime(l)),
Type(t) => GenericArg::Type(t.into_tcx(tcx)),
Const(box c) => GenericArg::Const(c.into_tcx(tcx)),
Infer => GenericArg::Infer,
@@ -177,9 +198,7 @@ impl FromWithTcx<clean::TypeBindingKind> for TypeBindingKind {
use clean::TypeBindingKind::*;
match kind {
Equality { term } => TypeBindingKind::Equality(term.into_tcx(tcx)),
- Constraint { bounds } => {
- TypeBindingKind::Constraint(bounds.into_iter().map(|a| a.into_tcx(tcx)).collect())
- }
+ Constraint { bounds } => TypeBindingKind::Constraint(bounds.into_tcx(tcx)),
}
}
}
@@ -240,11 +259,11 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
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)),
- TraitItem(t) => ItemEnum::Trait(t.into_tcx(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)),
- ImplItem(i) => ItemEnum::Impl(i.into_tcx(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)),
ForeignTypeItem => ItemEnum::ForeignType,
@@ -260,12 +279,12 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
}
TyAssocTypeItem(g, b) => ItemEnum::AssocType {
generics: (*g).into_tcx(tcx),
- bounds: b.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+ bounds: b.into_tcx(tcx),
default: None,
},
AssocTypeItem(t, b) => ItemEnum::AssocType {
generics: t.generics.into_tcx(tcx),
- bounds: b.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+ bounds: b.into_tcx(tcx),
default: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)),
},
// `convert_item` early returns `None` for stripped items and keywords.
@@ -292,11 +311,19 @@ 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 kind = match struct_type {
+ CtorKind::Fn => StructKind::Tuple(ids_keeping_stripped(fields, tcx)),
+ CtorKind::Const => {
+ assert!(fields.is_empty());
+ StructKind::Unit
+ }
+ CtorKind::Fictive => StructKind::Plain { fields: ids(fields, tcx), fields_stripped },
+ };
+
Struct {
- struct_type: from_ctor_kind(struct_type),
+ kind,
generics: generics.into_tcx(tcx),
- fields_stripped,
- fields: ids(fields, tcx),
impls: Vec::new(), // Added in JsonRenderer::item
}
}
@@ -315,14 +342,6 @@ impl FromWithTcx<clean::Union> for Union {
}
}
-pub(crate) fn from_ctor_kind(struct_type: CtorKind) -> StructType {
- match struct_type {
- CtorKind::Fictive => StructType::Plain,
- CtorKind::Fn => StructType::Tuple,
- CtorKind::Const => StructType::Unit,
- }
-}
-
pub(crate) fn from_fn_header(header: &rustc_hir::FnHeader) -> Header {
Header {
async_: header.is_async(),
@@ -347,15 +366,15 @@ fn convert_abi(a: RustcAbi) -> Abi {
}
}
+fn convert_lifetime(l: clean::Lifetime) -> String {
+ l.0.to_string()
+}
+
impl FromWithTcx<clean::Generics> for Generics {
fn from_tcx(generics: clean::Generics, tcx: TyCtxt<'_>) -> Self {
Generics {
- params: generics.params.into_iter().map(|x| x.into_tcx(tcx)).collect(),
- where_predicates: generics
- .where_predicates
- .into_iter()
- .map(|x| x.into_tcx(tcx))
- .collect(),
+ params: generics.params.into_tcx(tcx),
+ where_predicates: generics.where_predicates.into_tcx(tcx),
}
}
}
@@ -374,10 +393,10 @@ impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind {
use clean::GenericParamDefKind::*;
match kind {
Lifetime { outlives } => GenericParamDefKind::Lifetime {
- outlives: outlives.into_iter().map(|lt| lt.0.to_string()).collect(),
+ outlives: outlives.into_iter().map(convert_lifetime).collect(),
},
Type { did: _, bounds, default, synthetic } => GenericParamDefKind::Type {
- bounds: bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+ bounds: bounds.into_tcx(tcx),
default: default.map(|x| (*x).into_tcx(tcx)),
synthetic,
},
@@ -395,7 +414,7 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
match predicate {
BoundPredicate { ty, bounds, bound_params } => WherePredicate::BoundPredicate {
type_: ty.into_tcx(tcx),
- bounds: bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+ bounds: bounds.into_tcx(tcx),
generic_params: bound_params
.into_iter()
.map(|x| GenericParamDef {
@@ -405,8 +424,8 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
.collect(),
},
RegionPredicate { lifetime, bounds } => WherePredicate::RegionPredicate {
- lifetime: lifetime.0.to_string(),
- bounds: bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+ lifetime: convert_lifetime(lifetime),
+ bounds: bounds.into_tcx(tcx),
},
EqPredicate { lhs, rhs } => {
WherePredicate::EqPredicate { lhs: lhs.into_tcx(tcx), rhs: rhs.into_tcx(tcx) }
@@ -420,15 +439,13 @@ impl FromWithTcx<clean::GenericBound> for GenericBound {
use clean::GenericBound::*;
match bound {
TraitBound(clean::PolyTrait { trait_, generic_params }, modifier) => {
- // FIXME: should `trait_` be a clean::Path equivalent in JSON?
- let trait_ = clean::Type::Path { path: trait_ }.into_tcx(tcx);
GenericBound::TraitBound {
- trait_,
- generic_params: generic_params.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+ trait_: trait_.into_tcx(tcx),
+ generic_params: generic_params.into_tcx(tcx),
modifier: from_trait_bound_modifier(modifier),
}
}
- Outlives(lifetime) => GenericBound::Outlives(lifetime.0.to_string()),
+ Outlives(lifetime) => GenericBound::Outlives(convert_lifetime(lifetime)),
}
}
}
@@ -447,64 +464,49 @@ pub(crate) fn from_trait_bound_modifier(
impl FromWithTcx<clean::Type> for Type {
fn from_tcx(ty: clean::Type, tcx: TyCtxt<'_>) -> Self {
use clean::Type::{
- Array, BareFunction, BorrowedRef, DynTrait, Generic, ImplTrait, Infer, Primitive,
- QPath, RawPointer, Slice, Tuple,
+ Array, BareFunction, BorrowedRef, Generic, ImplTrait, Infer, Primitive, QPath,
+ RawPointer, Slice, Tuple,
};
match ty {
- clean::Type::Path { path } => Type::ResolvedPath {
- name: path.whole_name(),
- id: from_item_id(path.def_id().into(), tcx),
- args: path.segments.last().map(|args| Box::new(args.clone().args.into_tcx(tcx))),
- param_names: Vec::new(),
- },
- DynTrait(mut bounds, lt) => {
- let first_trait = bounds.remove(0).trait_;
-
- Type::ResolvedPath {
- name: first_trait.whole_name(),
- id: from_item_id(first_trait.def_id().into(), tcx),
- args: first_trait
- .segments
- .last()
- .map(|args| Box::new(args.clone().args.into_tcx(tcx))),
- param_names: bounds
- .into_iter()
- .map(|t| {
- clean::GenericBound::TraitBound(t, rustc_hir::TraitBoundModifier::None)
- })
- .chain(lt.map(clean::GenericBound::Outlives))
- .map(|bound| bound.into_tcx(tcx))
- .collect(),
- }
- }
+ clean::Type::Path { path } => Type::ResolvedPath(path.into_tcx(tcx)),
+ clean::Type::DynTrait(bounds, lt) => Type::DynTrait(DynTrait {
+ lifetime: lt.map(convert_lifetime),
+ traits: bounds.into_tcx(tcx),
+ }),
Generic(s) => Type::Generic(s.to_string()),
Primitive(p) => Type::Primitive(p.as_sym().to_string()),
BareFunction(f) => Type::FunctionPointer(Box::new((*f).into_tcx(tcx))),
- Tuple(t) => Type::Tuple(t.into_iter().map(|x| x.into_tcx(tcx)).collect()),
+ 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 },
- ImplTrait(g) => Type::ImplTrait(g.into_iter().map(|x| x.into_tcx(tcx)).collect()),
+ ImplTrait(g) => Type::ImplTrait(g.into_tcx(tcx)),
Infer => Type::Infer,
RawPointer(mutability, type_) => Type::RawPointer {
mutable: mutability == ast::Mutability::Mut,
type_: Box::new((*type_).into_tcx(tcx)),
},
BorrowedRef { lifetime, mutability, type_ } => Type::BorrowedRef {
- lifetime: lifetime.map(|l| l.0.to_string()),
+ lifetime: lifetime.map(convert_lifetime),
mutable: mutability == ast::Mutability::Mut,
type_: Box::new((*type_).into_tcx(tcx)),
},
- QPath { assoc, self_type, trait_, .. } => {
- // FIXME: should `trait_` be a clean::Path equivalent in JSON?
- let trait_ = clean::Type::Path { path: trait_ }.into_tcx(tcx);
- Type::QualifiedPath {
- name: assoc.name.to_string(),
- args: Box::new(assoc.args.clone().into_tcx(tcx)),
- self_type: Box::new((*self_type).into_tcx(tcx)),
- trait_: Box::new(trait_),
- }
- }
+ QPath(box clean::QPathData { assoc, self_type, trait_, .. }) => Type::QualifiedPath {
+ name: assoc.name.to_string(),
+ args: Box::new(assoc.args.into_tcx(tcx)),
+ self_type: Box::new(self_type.into_tcx(tcx)),
+ trait_: trait_.into_tcx(tcx),
+ },
+ }
+ }
+}
+
+impl FromWithTcx<clean::Path> for Path {
+ fn from_tcx(path: clean::Path, tcx: TyCtxt<'_>) -> Path {
+ Path {
+ name: path.whole_name(),
+ id: from_item_id(path.def_id().into(), tcx),
+ args: path.segments.last().map(|args| Box::new(args.clone().args.into_tcx(tcx))),
}
}
}
@@ -528,7 +530,7 @@ impl FromWithTcx<clean::BareFunctionDecl> for FunctionPointer {
async_: false,
abi: convert_abi(abi),
},
- generic_params: generic_params.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+ generic_params: generic_params.into_tcx(tcx),
decl: decl.into_tcx(tcx),
}
}
@@ -562,18 +564,25 @@ impl FromWithTcx<clean::Trait> for Trait {
is_unsafe,
items: ids(items, tcx),
generics: generics.into_tcx(tcx),
- bounds: bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
+ bounds: bounds.into_tcx(tcx),
implementations: Vec::new(), // Added in JsonRenderer::item
}
}
}
-impl FromWithTcx<Box<clean::Impl>> for Impl {
- fn from_tcx(impl_: Box<clean::Impl>, tcx: TyCtxt<'_>) -> Self {
+impl FromWithTcx<clean::PolyTrait> for PolyTrait {
+ fn from_tcx(
+ clean::PolyTrait { trait_, generic_params }: clean::PolyTrait,
+ tcx: TyCtxt<'_>,
+ ) -> Self {
+ PolyTrait { trait_: trait_.into_tcx(tcx), generic_params: generic_params.into_tcx(tcx) }
+ }
+}
+
+impl FromWithTcx<clean::Impl> for Impl {
+ fn from_tcx(impl_: clean::Impl, tcx: TyCtxt<'_>) -> Self {
let provided_trait_methods = impl_.provided_trait_methods(tcx);
- let clean::Impl { unsafety, generics, trait_, for_, items, polarity, kind } = *impl_;
- // FIXME: should `trait_` be a clean::Path equivalent in JSON?
- let trait_ = trait_.map(|path| clean::Type::Path { path }.into_tcx(tcx));
+ let clean::Impl { unsafety, generics, trait_, for_, items, polarity, kind } = impl_;
// FIXME: use something like ImplKind in JSON?
let (synthetic, blanket_impl) = match kind {
clean::ImplKind::Normal | clean::ImplKind::FakeVaradic => (false, None),
@@ -591,7 +600,7 @@ impl FromWithTcx<Box<clean::Impl>> for Impl {
.into_iter()
.map(|x| x.to_string())
.collect(),
- trait_,
+ trait_: trait_.map(|path| path.into_tcx(tcx)),
for_: for_.into_tcx(tcx),
items: ids(items, tcx),
negative: negative_polarity,
@@ -642,38 +651,28 @@ impl FromWithTcx<clean::Enum> for Enum {
}
}
-impl FromWithTcx<clean::VariantStruct> for Struct {
- fn from_tcx(struct_: clean::VariantStruct, tcx: TyCtxt<'_>) -> Self {
- let fields_stripped = struct_.has_stripped_entries();
- let clean::VariantStruct { struct_type, fields } = struct_;
- Struct {
- struct_type: from_ctor_kind(struct_type),
- generics: Generics { params: vec![], where_predicates: vec![] },
- fields_stripped,
- fields: ids(fields, tcx),
- impls: Vec::new(),
- }
- }
-}
-
impl FromWithTcx<clean::Variant> for Variant {
fn from_tcx(variant: clean::Variant, tcx: TyCtxt<'_>) -> Self {
use clean::Variant::*;
match variant {
- CLike => Variant::Plain,
- Tuple(fields) => Variant::Tuple(
- fields
- .into_iter()
- .map(|f| {
- if let clean::StructFieldItem(ty) = *f.kind {
- ty.into_tcx(tcx)
- } else {
- unreachable!()
- }
- })
- .collect(),
- ),
- Struct(s) => Variant::Struct(ids(s.fields, tcx)),
+ CLike(disr) => Variant::Plain(disr.map(|disr| disr.into_tcx(tcx))),
+ Tuple(fields) => Variant::Tuple(ids_keeping_stripped(fields, tcx)),
+ Struct(s) => Variant::Struct {
+ fields_stripped: s.has_stripped_entries(),
+ fields: ids(s.fields, tcx),
+ },
+ }
+ }
+}
+
+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
+ // `rustc_middle` types, not `rustc_hir`, but because JSON never inlines
+ // the expr is always some.
+ expr: disr.expr(tcx).unwrap(),
+ value: disr.value(tcx),
}
}
}
@@ -681,24 +680,18 @@ impl FromWithTcx<clean::Variant> for Variant {
impl FromWithTcx<clean::Import> for Import {
fn from_tcx(import: clean::Import, tcx: TyCtxt<'_>) -> Self {
use clean::ImportKind::*;
- match import.kind {
- Simple(s) => Import {
- source: import.source.path.whole_name(),
- name: s.to_string(),
- id: import.source.did.map(ItemId::from).map(|i| from_item_id(i, tcx)),
- glob: false,
- },
- Glob => Import {
- source: import.source.path.whole_name(),
- name: import
- .source
- .path
- .last_opt()
- .unwrap_or_else(|| Symbol::intern("*"))
- .to_string(),
- id: import.source.did.map(ItemId::from).map(|i| from_item_id(i, tcx)),
- glob: true,
- },
+ let (name, glob) = match import.kind {
+ Simple(s) => (s.to_string(), false),
+ Glob => (
+ import.source.path.last_opt().unwrap_or_else(|| Symbol::intern("*")).to_string(),
+ true,
+ ),
+ };
+ Import {
+ source: import.source.path.whole_name(),
+ name,
+ id: import.source.did.map(ItemId::from).map(|i| from_item_id(i, tcx)),
+ glob,
}
}
}
@@ -730,10 +723,7 @@ impl FromWithTcx<Box<clean::Typedef>> for Typedef {
impl FromWithTcx<clean::OpaqueTy> for OpaqueTy {
fn from_tcx(opaque: clean::OpaqueTy, tcx: TyCtxt<'_>) -> Self {
- OpaqueTy {
- bounds: opaque.bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
- generics: opaque.generics.into_tcx(tcx),
- }
+ OpaqueTy { bounds: opaque.bounds.into_tcx(tcx), generics: opaque.generics.into_tcx(tcx) }
}
}
@@ -749,10 +739,7 @@ impl FromWithTcx<clean::Static> for Static {
impl FromWithTcx<clean::TraitAlias> for TraitAlias {
fn from_tcx(alias: clean::TraitAlias, tcx: TyCtxt<'_>) -> Self {
- TraitAlias {
- generics: alias.generics.into_tcx(tcx),
- params: alias.bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
- }
+ TraitAlias { generics: alias.generics.into_tcx(tcx), params: alias.bounds.into_tcx(tcx) }
}
}
@@ -796,3 +783,19 @@ fn ids(items: impl IntoIterator<Item = clean::Item>, tcx: TyCtxt<'_>) -> Vec<Id>
.map(|i| from_item_id_with_name(i.item_id, tcx, i.name))
.collect()
}
+
+fn ids_keeping_stripped(
+ items: impl IntoIterator<Item = clean::Item>,
+ tcx: TyCtxt<'_>,
+) -> Vec<Option<Id>> {
+ items
+ .into_iter()
+ .map(|i| {
+ if !i.is_stripped() && !i.is_keyword() {
+ Some(from_item_id_with_name(i.item_id, tcx, i.name))
+ } else {
+ None
+ }
+ })
+ .collect()
+}
diff --git a/src/librustdoc/json/import_finder.rs b/src/librustdoc/json/import_finder.rs
new file mode 100644
index 000000000..c5c687df7
--- /dev/null
+++ b/src/librustdoc/json/import_finder.rs
@@ -0,0 +1,38 @@
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::def_id::DefId;
+
+use crate::{
+ clean::{self, Import, ImportSource, Item},
+ fold::DocFolder,
+};
+
+/// Get the id's of all items that are `pub use`d in the crate.
+///
+/// We need this to know if a stripped module is `pub use mod::*`, to decide
+/// if it needs to be kept in the index, despite being stripped.
+///
+/// See [#100973](https://github.com/rust-lang/rust/issues/100973) and
+/// [#101103](https://github.com/rust-lang/rust/issues/101103) for times when
+/// this information is needed.
+pub(crate) fn get_imports(krate: clean::Crate) -> (clean::Crate, FxHashSet<DefId>) {
+ let mut finder = ImportFinder { imported: FxHashSet::default() };
+ let krate = finder.fold_crate(krate);
+ (krate, finder.imported)
+}
+
+struct ImportFinder {
+ imported: FxHashSet<DefId>,
+}
+
+impl DocFolder for ImportFinder {
+ fn fold_item(&mut self, i: Item) -> Option<Item> {
+ match *i.kind {
+ clean::ImportItem(Import { source: ImportSource { did: Some(did), .. }, .. }) => {
+ self.imported.insert(did);
+ Some(i)
+ }
+
+ _ => Some(self.fold_item_recur(i)),
+ }
+ }
+}
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 6364d00d0..5e8f5f6fe 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -5,6 +5,7 @@
//! docs for usage and details.
mod conversions;
+mod import_finder;
use std::cell::RefCell;
use std::fs::{create_dir_all, File};
@@ -12,7 +13,7 @@ use std::io::{BufWriter, Write};
use std::path::PathBuf;
use std::rc::Rc;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::DefId;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
@@ -39,6 +40,7 @@ pub(crate) struct JsonRenderer<'tcx> {
/// The directory where the blob will be written to.
out_path: PathBuf,
cache: Rc<Cache>,
+ imported_items: FxHashSet<DefId>,
}
impl<'tcx> JsonRenderer<'tcx> {
@@ -99,6 +101,7 @@ impl<'tcx> JsonRenderer<'tcx> {
}
fn get_trait_items(&mut self) -> Vec<(types::Id, types::Item)> {
+ debug!("Adding foreign trait items");
Rc::clone(&self.cache)
.traits
.iter()
@@ -106,7 +109,10 @@ impl<'tcx> JsonRenderer<'tcx> {
// only need to synthesize items for external traits
if !id.is_local() {
let trait_item = &trait_item.trait_;
- trait_item.items.clone().into_iter().for_each(|i| self.item(i).unwrap());
+ 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(),
@@ -157,12 +163,16 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
tcx: TyCtxt<'tcx>,
) -> Result<(Self, clean::Crate), Error> {
debug!("Initializing json renderer");
+
+ let (krate, imported_items) = import_finder::get_imports(krate);
+
Ok((
JsonRenderer {
tcx,
index: Rc::new(RefCell::new(FxHashMap::default())),
out_path: options.output,
cache: Rc::new(cache),
+ imported_items,
},
krate,
))
@@ -176,7 +186,9 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
/// the hashmap because certain items (traits and types) need to have their mappings for trait
/// implementations filled out before they're inserted.
fn item(&mut self, item: clean::Item) -> Result<(), Error> {
- trace!("rendering {} {:?}", item.type_(), item.name);
+ let item_type = item.type_();
+ let item_name = item.name;
+ trace!("rendering {} {:?}", item_type, item_name);
// Flatten items that recursively store other items. We include orphaned items from
// stripped modules and etc that are otherwise reachable.
@@ -209,11 +221,11 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
}
types::ItemEnum::Method(_)
+ | types::ItemEnum::Module(_)
| types::ItemEnum::AssocConst { .. }
| types::ItemEnum::AssocType { .. }
| types::ItemEnum::PrimitiveType(_) => true,
- types::ItemEnum::Module(_)
- | types::ItemEnum::ExternCrate { .. }
+ types::ItemEnum::ExternCrate { .. }
| types::ItemEnum::Import(_)
| types::ItemEnum::StructField(_)
| types::ItemEnum::Variant(_)
@@ -245,6 +257,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
}
}
+ trace!("done rendering {} {:?}", item_type, item_name);
Ok(())
}
@@ -255,14 +268,20 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
fn after_krate(&mut self) -> Result<(), Error> {
debug!("Done with crate");
+ debug!("Adding Primitve impls");
for primitive in Rc::clone(&self.cache).primitive_locations.values() {
self.get_impls(*primitive);
}
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(self.get_trait_items());
+ index.extend(foreign_trait_items);
+
+ debug!("Constructing Output");
// This needs to be the default HashMap for compatibility with the public interface for
// rustdoc-json-types
#[allow(rustc::default_hash_types)]
@@ -274,10 +293,9 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
paths: self
.cache
.paths
- .clone()
- .into_iter()
- .chain(self.cache.external_paths.clone().into_iter())
- .map(|(k, (path, kind))| {
+ .iter()
+ .chain(&self.cache.external_paths)
+ .map(|(&k, &(ref path, kind))| {
(
from_item_id(k.into(), self.tcx),
types::ItemSummary {
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 92380d124..14d695582 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -9,14 +9,14 @@
#![feature(control_flow_enum)]
#![feature(drain_filter)]
#![feature(let_chains)]
-#![feature(let_else)]
+#![cfg_attr(bootstrap, feature(let_else))]
#![feature(test)]
#![feature(never_type)]
#![feature(once_cell)]
#![feature(type_ascription)]
#![feature(iter_intersperse)]
#![feature(type_alias_impl_trait)]
-#![feature(generic_associated_types)]
+#![cfg_attr(bootstrap, feature(generic_associated_types))]
#![recursion_limit = "256"]
#![warn(rustc::internal)]
#![allow(clippy::collapsible_if, clippy::collapsible_else_if)]
diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs
index 240aec52c..e76c19a61 100644
--- a/src/librustdoc/lint.rs
+++ b/src/librustdoc/lint.rs
@@ -64,9 +64,13 @@ where
}
macro_rules! declare_rustdoc_lint {
- ($(#[$attr:meta])* $name: ident, $level: ident, $descr: literal $(,)?) => {
+ (
+ $(#[$attr:meta])* $name: ident, $level: ident, $descr: literal $(,)?
+ $(@feature_gate = $gate:expr;)?
+ ) => {
declare_tool_lint! {
$(#[$attr])* pub rustdoc::$name, $level, $descr
+ $(, @feature_gate = $gate;)?
}
}
}
@@ -123,7 +127,8 @@ declare_rustdoc_lint! {
/// [rustdoc book]: ../../../rustdoc/lints.html#missing_doc_code_examples
MISSING_DOC_CODE_EXAMPLES,
Allow,
- "detects publicly-exported items without code samples in their documentation"
+ "detects publicly-exported items without code samples in their documentation",
+ @feature_gate = rustc_span::symbol::sym::rustdoc_missing_doc_code_examples;
}
declare_rustdoc_lint! {
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index 4c6e3eb04..48835abf9 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -215,7 +215,6 @@ impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> {
None,
);
- let filename = i.span(self.ctx.tcx).filename(self.ctx.sess());
let has_doc_example = tests.found_tests != 0;
// The `expect_def_id()` should be okay because `local_def_id_to_hir_id`
// would presumably panic if a fake `DefIndex` were passed.
@@ -261,13 +260,16 @@ impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> {
let should_have_docs = !should_be_ignored
&& (level != lint::Level::Allow || matches!(source, LintLevelSource::Default));
- debug!("counting {:?} {:?} in {:?}", i.type_(), i.name, filename);
- self.items.entry(filename).or_default().count_item(
- has_docs,
- has_doc_example,
- should_have_doc_example(self.ctx, i),
- should_have_docs,
- );
+ if let Some(span) = i.span(self.ctx.tcx) {
+ let filename = span.filename(self.ctx.sess());
+ debug!("counting {:?} {:?} in {:?}", i.type_(), i.name, filename);
+ self.items.entry(filename).or_default().count_item(
+ has_docs,
+ has_doc_example,
+ should_have_doc_example(self.ctx, i),
+ should_have_docs,
+ );
+ }
}
}
diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs
index 0172ef570..381ac7a5d 100644
--- a/src/librustdoc/passes/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/check_code_block_syntax.rs
@@ -1,7 +1,8 @@
//! Validates syntax inside Rust code blocks (\`\`\`rust).
use rustc_data_structures::sync::{Lock, Lrc};
use rustc_errors::{
- emitter::Emitter, Applicability, Diagnostic, Handler, LazyFallbackBundle, LintDiagnosticBuilder,
+ emitter::Emitter, translation::Translate, Applicability, Diagnostic, Handler,
+ LazyFallbackBundle, LintDiagnosticBuilder,
};
use rustc_parse::parse_stream_from_source_str;
use rustc_session::parse::ParseSess;
@@ -181,6 +182,16 @@ struct BufferEmitter {
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();
@@ -194,12 +205,4 @@ impl Emitter for BufferEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}
-
- fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
- None
- }
-
- fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
- &**self.fallback_bundle
- }
}
diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs
index e86f90833..55d5f303d 100644
--- a/src/librustdoc/passes/check_doc_test_visibility.rs
+++ b/src/librustdoc/passes/check_doc_test_visibility.rs
@@ -117,7 +117,7 @@ pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item
find_testable_code(dox, &mut tests, ErrorCodes::No, false, None);
- if tests.found_tests == 0 && cx.tcx.sess.is_nightly_build() {
+ if tests.found_tests == 0 && cx.tcx.features().rustdoc_missing_doc_code_examples {
if should_have_doc_example(cx, item) {
debug!("reporting error for {:?} (hir_id={:?})", item, hir_id);
let sp = item.attr_span(cx.tcx);
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 7d7a63c53..b2a41bfa4 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -223,6 +223,9 @@ enum MalformedGenerics {
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub(crate) enum UrlFragment {
Item(DefId),
+ /// A part of a page that isn't a rust item.
+ ///
+ /// Eg: `[Vector Examples](std::vec::Vec#examples)`
UserWritten(String),
}
@@ -477,7 +480,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
// If there's no `::`, it's not an associated item.
// So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
.ok_or_else(|| {
- debug!("found no `::`, assumming {} was correctly not in scope", item_name);
+ debug!("found no `::`, assuming {} was correctly not in scope", item_name);
UnresolvedPath {
item_id,
module_id,
@@ -750,7 +753,7 @@ fn resolve_associated_trait_item<'a>(
///
/// This is just a wrapper around [`TyCtxt::impl_item_implementor_ids()`] and
/// [`TyCtxt::associated_item()`] (with some helpful logging added).
-#[instrument(level = "debug", skip(tcx))]
+#[instrument(level = "debug", skip(tcx), ret)]
fn trait_assoc_to_impl_assoc_item<'tcx>(
tcx: TyCtxt<'tcx>,
impl_id: DefId,
@@ -760,9 +763,7 @@ fn trait_assoc_to_impl_assoc_item<'tcx>(
debug!(?trait_to_impl_assoc_map);
let impl_assoc_id = *trait_to_impl_assoc_map.get(&trait_assoc_id)?;
debug!(?impl_assoc_id);
- let impl_assoc = tcx.associated_item(impl_assoc_id);
- debug!(?impl_assoc);
- Some(impl_assoc)
+ Some(tcx.associated_item(impl_assoc_id))
}
/// Given a type, return all trait impls in scope in `module` for that type.
@@ -1129,7 +1130,7 @@ impl LinkCollector<'_, '_> {
Some(ItemLink {
link: ori_link.link.clone(),
link_text: link_text.clone(),
- did: res.def_id(self.cx.tcx),
+ page_id: res.def_id(self.cx.tcx),
fragment,
})
}
@@ -1148,11 +1149,12 @@ impl LinkCollector<'_, '_> {
item,
&diag_info,
)?;
- let id = clean::register_res(self.cx, rustc_hir::def::Res::Def(kind, id));
+
+ let page_id = clean::register_res(self.cx, rustc_hir::def::Res::Def(kind, id));
Some(ItemLink {
link: ori_link.link.clone(),
link_text: link_text.clone(),
- did: id,
+ page_id,
fragment,
})
}
@@ -1256,7 +1258,7 @@ impl LinkCollector<'_, '_> {
&mut self,
key: ResolutionInfo,
diag: DiagnosticInfo<'_>,
- // If errors are cached then they are only reported on first ocurrence
+ // If errors are cached then they are only reported on first occurrence
// which we want in some cases but not in others.
cache_errors: bool,
) -> Option<(Res, Option<UrlFragment>)> {
@@ -1807,8 +1809,8 @@ fn resolution_failure(
}
return;
}
- Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam
- | Static(_) => "associated item",
+ Trait | TyAlias | ForeignTy | OpaqueTy | ImplTraitPlaceholder
+ | TraitAlias | TyParam | Static(_) => "associated item",
Impl | GlobalAsm => unreachable!("not a path"),
}
} else {
@@ -1893,7 +1895,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/html_tags.rs b/src/librustdoc/passes/html_tags.rs
index f3a3c853c..885dadb32 100644
--- a/src/librustdoc/passes/html_tags.rs
+++ b/src/librustdoc/passes/html_tags.rs
@@ -94,6 +94,34 @@ fn extract_path_backwards(text: &str, end_pos: usize) -> Option<usize> {
if current_pos == end_pos { None } else { Some(current_pos) }
}
+fn extract_path_forward(text: &str, start_pos: usize) -> Option<usize> {
+ use rustc_lexer::{is_id_continue, is_id_start};
+ let mut current_pos = start_pos;
+ loop {
+ if current_pos < text.len() && text[current_pos..].starts_with("::") {
+ current_pos += 2;
+ } else {
+ break;
+ }
+ let mut chars = text[current_pos..].chars();
+ if let Some(c) = chars.next() {
+ if is_id_start(c) {
+ current_pos += c.len_utf8();
+ } else {
+ break;
+ }
+ }
+ while let Some(c) = chars.next() {
+ if is_id_continue(c) {
+ current_pos += c.len_utf8();
+ } else {
+ break;
+ }
+ }
+ }
+ if current_pos == start_pos { None } else { Some(current_pos) }
+}
+
fn is_valid_for_html_tag_name(c: char, is_empty: bool) -> bool {
// https://spec.commonmark.org/0.30/#raw-html
//
@@ -218,19 +246,68 @@ impl<'a, 'tcx> DocVisitor for InvalidHtmlTagsLinter<'a, 'tcx> {
// 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.
- if let Some(Some(generics_start)) = (is_open_tag
- && dox[..range.end].ends_with('>'))
+ 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..range.end),
+ &(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'>')
+ {
+ diag.emit();
+ return;
+ }
// multipart form is chosen here because ``Vec<i32>`` would be confusing.
diag.multipart_suggestion(
"try marking as source code",
@@ -278,7 +355,7 @@ impl<'a, 'tcx> DocVisitor for InvalidHtmlTagsLinter<'a, 'tcx> {
for (event, range) in p {
match event {
Event::Start(Tag::CodeBlock(_)) => in_code_block = true,
- Event::Html(text) | Event::Text(text) if !in_code_block => {
+ 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,
diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs
index 0c5d83655..765f7c61b 100644
--- a/src/librustdoc/passes/propagate_doc_cfg.rs
+++ b/src/librustdoc/passes/propagate_doc_cfg.rs
@@ -2,29 +2,74 @@
use std::sync::Arc;
use crate::clean::cfg::Cfg;
-use crate::clean::{Crate, Item};
+use crate::clean::inline::{load_attrs, merge_attrs};
+use crate::clean::{Crate, Item, ItemKind};
use crate::core::DocContext;
use crate::fold::DocFolder;
use crate::passes::Pass;
+use rustc_hir::def_id::LocalDefId;
+
pub(crate) const PROPAGATE_DOC_CFG: Pass = Pass {
name: "propagate-doc-cfg",
run: propagate_doc_cfg,
description: "propagates `#[doc(cfg(...))]` to child items",
};
-pub(crate) fn propagate_doc_cfg(cr: Crate, _: &mut DocContext<'_>) -> Crate {
- CfgPropagator { parent_cfg: None }.fold_crate(cr)
+pub(crate) fn propagate_doc_cfg(cr: Crate, cx: &mut DocContext<'_>) -> Crate {
+ CfgPropagator { parent_cfg: None, parent: None, cx }.fold_crate(cr)
}
-struct CfgPropagator {
+struct CfgPropagator<'a, 'tcx> {
parent_cfg: Option<Arc<Cfg>>,
+ parent: Option<LocalDefId>,
+ cx: &'a mut DocContext<'tcx>,
+}
+
+impl<'a, 'tcx> CfgPropagator<'a, 'tcx> {
+ // Some items need to merge their attributes with their parents' otherwise a few of them
+ // (mostly `cfg` ones) will be missing.
+ fn merge_with_parent_attributes(&mut self, item: &mut Item) {
+ let check_parent = match &*item.kind {
+ // impl blocks can be in different modules with different cfg and we need to get them
+ // as well.
+ ItemKind::ImplItem(_) => false,
+ kind if kind.is_non_assoc() => true,
+ _ => return,
+ };
+
+ let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local())
+ else { return };
+
+ let hir = self.cx.tcx.hir();
+ let hir_id = hir.local_def_id_to_hir_id(def_id);
+
+ if check_parent {
+ let expected_parent = hir.get_parent_item(hir_id);
+ // If parents are different, it means that `item` is a reexport and we need
+ // to compute the actual `cfg` by iterating through its "real" parents.
+ if self.parent == Some(expected_parent) {
+ return;
+ }
+ }
+
+ let mut attrs = Vec::new();
+ for (parent_hir_id, _) in hir.parent_iter(hir_id) {
+ if let Some(def_id) = hir.opt_local_def_id(parent_hir_id) {
+ attrs.extend_from_slice(load_attrs(self.cx, def_id.to_def_id()));
+ }
+ }
+ let (_, cfg) = merge_attrs(self.cx, None, item.attrs.other_attrs.as_slice(), Some(&attrs));
+ item.cfg = cfg;
+ }
}
-impl DocFolder for CfgPropagator {
+impl<'a, 'tcx> DocFolder for CfgPropagator<'a, 'tcx> {
fn fold_item(&mut self, mut item: Item) -> Option<Item> {
let old_parent_cfg = self.parent_cfg.clone();
+ self.merge_with_parent_attributes(&mut item);
+
let new_cfg = match (self.parent_cfg.take(), item.cfg.take()) {
(None, None) => None,
(Some(rc), None) | (None, Some(rc)) => Some(rc),
@@ -37,8 +82,15 @@ impl DocFolder for CfgPropagator {
self.parent_cfg = new_cfg.clone();
item.cfg = new_cfg;
+ let old_parent =
+ if let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) {
+ self.parent.replace(def_id)
+ } else {
+ self.parent.take()
+ };
let result = self.fold_item_recur(item);
self.parent_cfg = old_parent_cfg;
+ self.parent = old_parent;
Some(result)
}
diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
index 533e2ce46..9914edf30 100644
--- a/src/librustdoc/passes/strip_hidden.rs
+++ b/src/librustdoc/passes/strip_hidden.rs
@@ -17,6 +17,7 @@ pub(crate) const STRIP_HIDDEN: Pass = Pass {
/// Strip items marked `#[doc(hidden)]`
pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
let mut retained = ItemIdSet::default();
+ let is_json_output = cx.output_format.is_json() && !cx.show_coverage;
// strip all #[doc(hidden)] items
let krate = {
@@ -25,7 +26,12 @@ pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clea
};
// strip all impls referencing stripped items
- let mut stripper = ImplStripper { retained: &retained, cache: &cx.cache };
+ let mut stripper = ImplStripper {
+ retained: &retained,
+ cache: &cx.cache,
+ is_json_output,
+ document_private: cx.render_options.document_private,
+ };
stripper.fold_crate(krate)
}
diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs
index 9ba841a31..f3aa3c7ce 100644
--- a/src/librustdoc/passes/strip_private.rs
+++ b/src/librustdoc/passes/strip_private.rs
@@ -17,6 +17,7 @@ pub(crate) const STRIP_PRIVATE: Pass = Pass {
pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
// This stripper collects all *retained* nodes.
let mut retained = ItemIdSet::default();
+ let is_json_output = cx.output_format.is_json() && !cx.show_coverage;
// strip all private items
{
@@ -24,12 +25,17 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) ->
retained: &mut retained,
access_levels: &cx.cache.access_levels,
update_retained: true,
- is_json_output: cx.output_format.is_json() && !cx.show_coverage,
+ is_json_output,
};
krate = ImportStripper.fold_crate(stripper.fold_crate(krate));
}
// strip all impls referencing private items
- let mut stripper = ImplStripper { retained: &retained, cache: &cx.cache };
+ let mut stripper = ImplStripper {
+ retained: &retained,
+ cache: &cx.cache,
+ is_json_output,
+ document_private: cx.render_options.document_private,
+ };
stripper.fold_crate(krate)
}
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index 0d419042a..a9d768f01 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -14,17 +14,19 @@ pub(crate) struct Stripper<'a> {
pub(crate) is_json_output: bool,
}
-impl<'a> Stripper<'a> {
- // We need to handle this differently for the JSON output because some non exported items could
- // be used in public API. And so, we need these items as well. `is_exported` only checks if they
- // are in the public API, which is not enough.
- #[inline]
- fn is_item_reachable(&self, item_id: ItemId) -> bool {
- if self.is_json_output {
- self.access_levels.is_reachable(item_id.expect_def_id())
- } else {
- self.access_levels.is_exported(item_id.expect_def_id())
- }
+// We need to handle this differently for the JSON output because some non exported items could
+// be used in public API. And so, we need these items as well. `is_exported` only checks if they
+// are in the public API, which is not enough.
+#[inline]
+fn is_item_reachable(
+ is_json_output: bool,
+ access_levels: &AccessLevels<DefId>,
+ item_id: ItemId,
+) -> bool {
+ if is_json_output {
+ access_levels.is_reachable(item_id.expect_def_id())
+ } else {
+ access_levels.is_exported(item_id.expect_def_id())
}
}
@@ -61,7 +63,9 @@ impl<'a> DocFolder for Stripper<'a> {
| clean::MacroItem(..)
| clean::ForeignTypeItem => {
let item_id = i.item_id;
- if item_id.is_local() && !self.is_item_reachable(item_id) {
+ if item_id.is_local()
+ && !is_item_reachable(self.is_json_output, self.access_levels, item_id)
+ {
debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
return None;
}
@@ -84,7 +88,17 @@ impl<'a> DocFolder for Stripper<'a> {
}
// handled in the `strip-priv-imports` pass
- clean::ExternCrateItem { .. } | clean::ImportItem(..) => {}
+ clean::ExternCrateItem { .. } => {}
+ clean::ImportItem(ref imp) => {
+ // Because json doesn't inline imports from private modules, we need to mark
+ // the imported item as retained so it's impls won't be stripped.
+ //
+ // FIXME: Is it necessary to check for json output here: See
+ // https://github.com/rust-lang/rust/pull/100325#discussion_r941495215
+ if let Some(did) = imp.source.did && self.is_json_output {
+ self.retained.insert(did.into());
+ }
+ }
clean::ImplItem(..) => {}
@@ -133,6 +147,8 @@ impl<'a> DocFolder for Stripper<'a> {
pub(crate) struct ImplStripper<'a> {
pub(crate) retained: &'a ItemIdSet,
pub(crate) cache: &'a Cache,
+ pub(crate) is_json_output: bool,
+ pub(crate) document_private: bool,
}
impl<'a> DocFolder for ImplStripper<'a> {
@@ -140,8 +156,27 @@ impl<'a> DocFolder for ImplStripper<'a> {
if let clean::ImplItem(ref imp) = *i.kind {
// Impl blocks can be skipped if they are: empty; not a trait impl; and have no
// documentation.
- if imp.trait_.is_none() && imp.items.is_empty() && i.doc_value().is_none() {
- return None;
+ //
+ // There is one special case: if the impl block contains only private items.
+ if imp.trait_.is_none() {
+ // If the only items present are private ones and we're not rendering private items,
+ // we don't document it.
+ if !imp.items.is_empty()
+ && !self.document_private
+ && imp.items.iter().all(|i| {
+ let item_id = i.item_id;
+ item_id.is_local()
+ && !is_item_reachable(
+ self.is_json_output,
+ &self.cache.access_levels,
+ item_id,
+ )
+ })
+ {
+ return None;
+ } else if imp.items.is_empty() && i.doc_value().is_none() {
+ return None;
+ }
}
if let Some(did) = imp.for_.def_id(self.cache) {
if did.is_local() && !imp.for_.is_assoc_ty() && !self.retained.contains(&did.into())
diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs
index 0d9684025..ca86ac89e 100644
--- a/src/librustdoc/scrape_examples.rs
+++ b/src/librustdoc/scrape_examples.rs
@@ -159,7 +159,7 @@ where
return;
}
}
- hir::ExprKind::MethodCall(path, _, call_span) => {
+ hir::ExprKind::MethodCall(path, _, _, call_span) => {
let types = tcx.typeck(ex.hir_id.owner);
let Some(def_id) = types.type_dependent_def_id(ex.hir_id) else {
trace!("type_dependent_def_id({}) = None", ex.hir_id);
diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs
index 0118d7dd2..e7a26cb34 100644
--- a/src/librustdoc/theme.rs
+++ b/src/librustdoc/theme.rs
@@ -1,271 +1,252 @@
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxHashMap;
+use std::collections::hash_map::Entry;
use std::fs;
-use std::hash::{Hash, Hasher};
+use std::iter::Peekable;
use std::path::Path;
+use std::str::Chars;
use rustc_errors::Handler;
#[cfg(test)]
mod tests;
-#[derive(Debug, Clone, Eq)]
+#[derive(Debug)]
pub(crate) struct CssPath {
- pub(crate) name: String,
- pub(crate) children: FxHashSet<CssPath>,
-}
-
-// This PartialEq implementation IS NOT COMMUTATIVE!!!
-//
-// The order is very important: the second object must have all first's rules.
-// However, the first is not required to have all of the second's rules.
-impl PartialEq for CssPath {
- fn eq(&self, other: &CssPath) -> bool {
- if self.name != other.name {
- false
- } else {
- for child in &self.children {
- if !other.children.iter().any(|c| child == c) {
- return false;
- }
- }
- true
+ pub(crate) rules: FxHashMap<String, String>,
+ pub(crate) children: FxHashMap<String, CssPath>,
+}
+
+/// When encountering a `"` or a `'`, returns the whole string, including the quote characters.
+fn get_string(iter: &mut Peekable<Chars<'_>>, string_start: char, buffer: &mut String) {
+ buffer.push(string_start);
+ while let Some(c) = iter.next() {
+ buffer.push(c);
+ if c == '\\' {
+ iter.next();
+ } else if c == string_start {
+ break;
}
}
}
-impl Hash for CssPath {
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.name.hash(state);
- for x in &self.children {
- x.hash(state);
+fn get_inside_paren(
+ iter: &mut Peekable<Chars<'_>>,
+ paren_start: char,
+ paren_end: char,
+ buffer: &mut String,
+) {
+ buffer.push(paren_start);
+ while let Some(c) = iter.next() {
+ handle_common_chars(c, buffer, iter);
+ if c == paren_end {
+ break;
}
}
}
-impl CssPath {
- fn new(name: String) -> CssPath {
- CssPath { name, children: FxHashSet::default() }
+/// Skips a `/*` comment.
+fn skip_comment(iter: &mut Peekable<Chars<'_>>) {
+ while let Some(c) = iter.next() {
+ if c == '*' && iter.next() == Some('/') {
+ break;
+ }
}
}
-/// All variants contain the position they occur.
-#[derive(Debug, Clone, Copy)]
-enum Events {
- StartLineComment(usize),
- StartComment(usize),
- EndComment(usize),
- InBlock(usize),
- OutBlock(usize),
-}
-
-impl Events {
- fn get_pos(&self) -> usize {
- match *self {
- Events::StartLineComment(p)
- | Events::StartComment(p)
- | Events::EndComment(p)
- | Events::InBlock(p)
- | Events::OutBlock(p) => p,
+/// Skips a line comment (`//`).
+fn skip_line_comment(iter: &mut Peekable<Chars<'_>>) {
+ while let Some(c) = iter.next() {
+ if c == '\n' {
+ break;
}
}
-
- fn is_comment(&self) -> bool {
- matches!(
- self,
- Events::StartLineComment(_) | Events::StartComment(_) | Events::EndComment(_)
- )
- }
}
-fn previous_is_line_comment(events: &[Events]) -> bool {
- matches!(events.last(), Some(&Events::StartLineComment(_)))
-}
-
-fn is_line_comment(pos: usize, v: &[u8], events: &[Events]) -> bool {
- if let Some(&Events::StartComment(_)) = events.last() {
- return false;
+fn handle_common_chars(c: char, buffer: &mut String, iter: &mut Peekable<Chars<'_>>) {
+ match c {
+ '"' | '\'' => get_string(iter, c, buffer),
+ '/' if iter.peek() == Some(&'*') => skip_comment(iter),
+ '/' if iter.peek() == Some(&'/') => skip_line_comment(iter),
+ '(' => get_inside_paren(iter, c, ')', buffer),
+ '[' => get_inside_paren(iter, c, ']', buffer),
+ _ => buffer.push(c),
}
- v[pos + 1] == b'/'
}
-fn load_css_events(v: &[u8]) -> Vec<Events> {
- let mut pos = 0;
- let mut events = Vec::with_capacity(100);
-
- while pos + 1 < v.len() {
- match v[pos] {
- b'/' if v[pos + 1] == b'*' => {
- events.push(Events::StartComment(pos));
- pos += 1;
- }
- b'/' if is_line_comment(pos, v, &events) => {
- events.push(Events::StartLineComment(pos));
- pos += 1;
- }
- b'\n' if previous_is_line_comment(&events) => {
- events.push(Events::EndComment(pos));
- }
- b'*' if v[pos + 1] == b'/' => {
- events.push(Events::EndComment(pos + 2));
- pos += 1;
- }
- b'{' if !previous_is_line_comment(&events) => {
- if let Some(&Events::StartComment(_)) = events.last() {
- pos += 1;
- continue;
- }
- events.push(Events::InBlock(pos + 1));
- }
- b'}' if !previous_is_line_comment(&events) => {
- if let Some(&Events::StartComment(_)) = events.last() {
- pos += 1;
- continue;
- }
- events.push(Events::OutBlock(pos + 1));
- }
- _ => {}
+/// Returns a CSS property name. Ends when encountering a `:` character.
+///
+/// If the `:` character isn't found, returns `None`.
+///
+/// If a `{` character is encountered, returns an error.
+fn parse_property_name(iter: &mut Peekable<Chars<'_>>) -> Result<Option<String>, String> {
+ let mut content = String::new();
+
+ while let Some(c) = iter.next() {
+ match c {
+ ':' => return Ok(Some(content.trim().to_owned())),
+ '{' => return Err("Unexpected `{` in a `{}` block".to_owned()),
+ '}' => break,
+ _ => handle_common_chars(c, &mut content, iter),
}
- pos += 1;
}
- events
-}
-
-fn get_useful_next(events: &[Events], pos: &mut usize) -> Option<Events> {
- while *pos < events.len() {
- if !events[*pos].is_comment() {
- return Some(events[*pos]);
+ Ok(None)
+}
+
+/// Try to get the value of a CSS property (the `#fff` in `color: #fff`). It'll stop when it
+/// encounters a `{` or a `;` character.
+///
+/// It returns the value string and a boolean set to `true` if the value is ended with a `}` because
+/// it means that the parent block is done and that we should notify the parent caller.
+fn parse_property_value(iter: &mut Peekable<Chars<'_>>) -> (String, bool) {
+ let mut value = String::new();
+ let mut out_block = false;
+
+ while let Some(c) = iter.next() {
+ match c {
+ ';' => break,
+ '}' => {
+ out_block = true;
+ break;
+ }
+ _ => handle_common_chars(c, &mut value, iter),
}
- *pos += 1;
}
- None
+ (value.trim().to_owned(), out_block)
}
-fn get_previous_positions(events: &[Events], mut pos: usize) -> Vec<usize> {
- let mut ret = Vec::with_capacity(3);
+/// This is used to parse inside a CSS `{}` block. If we encounter a new `{` inside it, we consider
+/// it as a new block and therefore recurse into `parse_rules`.
+fn parse_rules(
+ content: &str,
+ selector: String,
+ iter: &mut Peekable<Chars<'_>>,
+ paths: &mut FxHashMap<String, CssPath>,
+) -> Result<(), String> {
+ let mut rules = FxHashMap::default();
+ let mut children = FxHashMap::default();
- ret.push(events[pos].get_pos());
- if pos > 0 {
- pos -= 1;
- }
loop {
- if pos < 1 || !events[pos].is_comment() {
- let x = events[pos].get_pos();
- if *ret.last().unwrap() != x {
- ret.push(x);
- } else {
- ret.push(0);
+ // If the parent isn't a "normal" CSS selector, we only expect sub-selectors and not CSS
+ // properties.
+ if selector.starts_with('@') {
+ parse_selectors(content, iter, &mut children)?;
+ break;
+ }
+ let rule = match parse_property_name(iter)? {
+ Some(r) => {
+ if r.is_empty() {
+ return Err(format!("Found empty rule in selector `{selector}`"));
+ }
+ r
+ }
+ None => break,
+ };
+ let (value, out_block) = parse_property_value(iter);
+ if value.is_empty() {
+ return Err(format!("Found empty value for rule `{rule}` in selector `{selector}`"));
+ }
+ match rules.entry(rule) {
+ Entry::Occupied(mut o) => {
+ *o.get_mut() = value;
+ }
+ Entry::Vacant(v) => {
+ v.insert(value);
}
+ }
+ if out_block {
break;
}
- ret.push(events[pos].get_pos());
- pos -= 1;
- }
- if ret.len() & 1 != 0 && events[pos].is_comment() {
- ret.push(0);
}
- ret.iter().rev().cloned().collect()
-}
-
-fn build_rule(v: &[u8], positions: &[usize]) -> String {
- minifier::css::minify(
- &positions
- .chunks(2)
- .map(|x| ::std::str::from_utf8(&v[x[0]..x[1]]).unwrap_or(""))
- .collect::<String>()
- .trim()
- .chars()
- .filter_map(|c| match c {
- '\n' | '\t' => Some(' '),
- '/' | '{' | '}' => None,
- c => Some(c),
- })
- .collect::<String>()
- .split(' ')
- .filter(|s| !s.is_empty())
- .intersperse(" ")
- .collect::<String>(),
- )
- .map(|css| css.to_string())
- .unwrap_or_else(|_| String::new())
-}
-
-fn inner(v: &[u8], events: &[Events], pos: &mut usize) -> FxHashSet<CssPath> {
- let mut paths = Vec::with_capacity(50);
- while *pos < events.len() {
- if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) {
- *pos += 1;
- break;
+ match paths.entry(selector) {
+ Entry::Occupied(mut o) => {
+ let v = o.get_mut();
+ for (key, value) in rules.into_iter() {
+ v.rules.insert(key, value);
+ }
+ for (sel, child) in children.into_iter() {
+ v.children.insert(sel, child);
+ }
}
- if let Some(Events::InBlock(_)) = get_useful_next(events, pos) {
- paths.push(CssPath::new(build_rule(v, &get_previous_positions(events, *pos))));
- *pos += 1;
+ Entry::Vacant(v) => {
+ v.insert(CssPath { rules, children });
}
- while let Some(Events::InBlock(_)) = get_useful_next(events, pos) {
- if let Some(ref mut path) = paths.last_mut() {
- for entry in inner(v, events, pos).iter() {
- path.children.insert(entry.clone());
- }
+ }
+ Ok(())
+}
+
+pub(crate) fn parse_selectors(
+ content: &str,
+ iter: &mut Peekable<Chars<'_>>,
+ paths: &mut FxHashMap<String, CssPath>,
+) -> Result<(), String> {
+ let mut selector = String::new();
+
+ while let Some(c) = iter.next() {
+ match c {
+ '{' => {
+ let s = minifier::css::minify(selector.trim()).map(|s| s.to_string())?;
+ parse_rules(content, s, iter, paths)?;
+ selector.clear();
}
- }
- if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) {
- *pos += 1;
+ '}' => break,
+ ';' => selector.clear(), // We don't handle inline selectors like `@import`.
+ _ => handle_common_chars(c, &mut selector, iter),
}
}
- paths.iter().cloned().collect()
-}
-
-pub(crate) fn load_css_paths(v: &[u8]) -> CssPath {
- let events = load_css_events(v);
- let mut pos = 0;
-
- let mut parent = CssPath::new("parent".to_owned());
- parent.children = inner(v, &events, &mut pos);
- parent
-}
-
-pub(crate) fn get_differences(against: &CssPath, other: &CssPath, v: &mut Vec<String>) {
- if against.name == other.name {
- for child in &against.children {
- let mut found = false;
- let mut found_working = false;
- let mut tmp = Vec::new();
-
- for other_child in &other.children {
- if child.name == other_child.name {
- if child != other_child {
- get_differences(child, other_child, &mut tmp);
- } else {
- found_working = true;
+ Ok(())
+}
+
+/// The entry point to parse the CSS rules. Every time we encounter a `{`, we then parse the rules
+/// inside it.
+pub(crate) fn load_css_paths(content: &str) -> Result<FxHashMap<String, CssPath>, String> {
+ let mut iter = content.chars().peekable();
+ let mut paths = FxHashMap::default();
+
+ parse_selectors(content, &mut iter, &mut paths)?;
+ Ok(paths)
+}
+
+pub(crate) fn get_differences(
+ origin: &FxHashMap<String, CssPath>,
+ against: &FxHashMap<String, CssPath>,
+ v: &mut Vec<String>,
+) {
+ for (selector, entry) in origin.iter() {
+ match against.get(selector) {
+ Some(a) => {
+ get_differences(&entry.children, &a.children, v);
+ if selector == ":root" {
+ // We need to check that all variables have been set.
+ for rule in entry.rules.keys() {
+ if !a.rules.contains_key(rule) {
+ v.push(format!(" Missing CSS variable `{rule}` in `:root`"));
+ }
}
- found = true;
- break;
}
}
- if !found {
- v.push(format!(" Missing \"{}\" rule", child.name));
- } else if !found_working {
- v.extend(tmp.iter().cloned());
- }
+ None => v.push(format!(" Missing rule `{selector}`")),
}
}
}
pub(crate) fn test_theme_against<P: AsRef<Path>>(
f: &P,
- against: &CssPath,
+ origin: &FxHashMap<String, CssPath>,
diag: &Handler,
) -> (bool, Vec<String>) {
- let data = match fs::read(f) {
+ let against = match fs::read_to_string(f)
+ .map_err(|e| e.to_string())
+ .and_then(|data| load_css_paths(&data))
+ {
Ok(c) => c,
Err(e) => {
- diag.struct_err(&e.to_string()).emit();
+ diag.struct_err(&e).emit();
return (false, vec![]);
}
};
- let paths = load_css_paths(&data);
let mut ret = vec![];
- get_differences(against, &paths, &mut ret);
+ get_differences(origin, &against, &mut ret);
(true, ret)
}
diff --git a/src/librustdoc/theme/tests.rs b/src/librustdoc/theme/tests.rs
index ae8f43c6d..08a174d27 100644
--- a/src/librustdoc/theme/tests.rs
+++ b/src/librustdoc/theme/tests.rs
@@ -44,11 +44,7 @@ rule j end {}
"#;
let mut ret = Vec::new();
- get_differences(
- &load_css_paths(against.as_bytes()),
- &load_css_paths(text.as_bytes()),
- &mut ret,
- );
+ get_differences(&load_css_paths(against).unwrap(), &load_css_paths(text).unwrap(), &mut ret);
assert!(ret.is_empty());
}
@@ -61,46 +57,45 @@ a
c // sdf
d {}
"#;
- let paths = load_css_paths(text.as_bytes());
- assert!(paths.children.contains(&CssPath::new("a b c d".to_owned())));
+ let paths = load_css_paths(text).unwrap();
+ assert!(paths.contains_key(&"a b c d".to_owned()));
}
#[test]
fn test_comparison() {
- let x = r#"
-a {
- b {
- c {}
- }
+ let origin = r#"
+@a {
+ b {}
+ c {}
}
"#;
- let y = r#"
-a {
+ let against = r#"
+@a {
b {}
}
"#;
- let against = load_css_paths(y.as_bytes());
- let other = load_css_paths(x.as_bytes());
+ let origin = load_css_paths(origin).unwrap();
+ let against = load_css_paths(against).unwrap();
let mut ret = Vec::new();
- get_differences(&against, &other, &mut ret);
+ get_differences(&against, &origin, &mut ret);
assert!(ret.is_empty());
- get_differences(&other, &against, &mut ret);
- assert_eq!(ret, vec![" Missing \"c\" rule".to_owned()]);
+ get_differences(&origin, &against, &mut ret);
+ assert_eq!(ret, vec![" Missing rule `c`".to_owned()]);
}
#[test]
fn check_empty_css() {
- let events = load_css_events(&[]);
- assert_eq!(events.len(), 0);
+ let paths = load_css_paths("").unwrap();
+ assert_eq!(paths.len(), 0);
}
#[test]
fn check_invalid_css() {
- let events = load_css_events(b"*");
- assert_eq!(events.len(), 0);
+ let paths = load_css_paths("*").unwrap();
+ assert_eq!(paths.len(), 0);
}
#[test]
@@ -108,10 +103,85 @@ fn test_with_minification() {
let text = include_str!("../html/static/css/themes/dark.css");
let minified = minifier::css::minify(&text).expect("CSS minification failed").to_string();
- let against = load_css_paths(text.as_bytes());
- let other = load_css_paths(minified.as_bytes());
+ let against = load_css_paths(text).unwrap();
+ let other = load_css_paths(&minified).unwrap();
+
+ let mut ret = Vec::new();
+ get_differences(&against, &other, &mut ret);
+ assert!(ret.is_empty());
+}
+
+#[test]
+fn test_media() {
+ let text = r#"
+@media (min-width: 701px) {
+ a:hover {
+ color: #fff;
+ }
+
+ b {
+ x: y;
+ }
+}
+
+@media (max-width: 1001px) {
+ b {
+ x: y;
+ }
+}
+"#;
+
+ let paths = load_css_paths(text).unwrap();
+ let p = paths.get("@media (min-width:701px)");
+ assert!(p.is_some());
+ let p = p.unwrap();
+ assert!(p.children.get("a:hover").is_some());
+ assert!(p.children.get("b").is_some());
+
+ let p = paths.get("@media (max-width:1001px)");
+ assert!(p.is_some());
+ let p = p.unwrap();
+ assert!(p.children.get("b").is_some());
+}
+
+#[test]
+fn test_css_variables() {
+ let x = r#"
+:root {
+ --a: #fff;
+}
+"#;
+
+ let y = r#"
+:root {
+ --a: #fff;
+ --b: #fff;
+}
+"#;
+
+ let against = load_css_paths(x).unwrap();
+ let other = load_css_paths(y).unwrap();
let mut ret = Vec::new();
get_differences(&against, &other, &mut ret);
assert!(ret.is_empty());
+ get_differences(&other, &against, &mut ret);
+ assert_eq!(ret, vec![" Missing CSS variable `--b` in `:root`".to_owned()]);
+}
+
+#[test]
+fn test_weird_rule_value() {
+ let x = r#"
+a[text=("a")] {
+ b: url({;}.png);
+ c: #fff
+}
+"#;
+
+ let paths = load_css_paths(&x).unwrap();
+ let p = paths.get("a[text=(\"a\")]");
+ assert!(p.is_some());
+ let p = p.unwrap();
+ assert_eq!(p.rules.get("b"), Some(&"url({;}.png)".to_owned()));
+ assert_eq!(p.rules.get("c"), Some(&"#fff".to_owned()));
}
diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs
index 0bb41977c..c40274394 100644
--- a/src/librustdoc/visit.rs
+++ b/src/librustdoc/visit.rs
@@ -20,7 +20,7 @@ pub(crate) trait DocVisitor: Sized {
VariantItem(i) => match i {
Variant::Struct(j) => j.fields.iter().for_each(|x| self.visit_item(x)),
Variant::Tuple(fields) => fields.iter().for_each(|x| self.visit_item(x)),
- Variant::CLike => {}
+ Variant::CLike(_) => {}
},
ExternCrateItem { src: _ }
| ImportItem(_)
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index ca7a20bf3..c27ac0ac4 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -164,8 +164,20 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
self.inside_public_path &= self.cx.tcx.visibility(def_id).is_public();
for &i in m.item_ids {
let item = self.cx.tcx.hir().item(i);
+ if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
+ continue;
+ }
self.visit_item(item, None, &mut om);
}
+ for &i in m.item_ids {
+ let item = self.cx.tcx.hir().item(i);
+ // To match the way import precedence works, visit glob imports last.
+ // 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.inside_public_path = orig_inside_public_path;
om
}