summaryrefslogtreecommitdiffstats
path: root/src/librustdoc
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc')
-rw-r--r--src/librustdoc/clean/inline.rs23
-rw-r--r--src/librustdoc/clean/mod.rs211
-rw-r--r--src/librustdoc/clean/types.rs121
-rw-r--r--src/librustdoc/clean/utils.rs116
-rw-r--r--src/librustdoc/config.rs35
-rw-r--r--src/librustdoc/core.rs10
-rw-r--r--src/librustdoc/doctest.rs8
-rw-r--r--src/librustdoc/externalfiles.rs4
-rw-r--r--src/librustdoc/fold.rs23
-rw-r--r--src/librustdoc/formats/cache.rs3
-rw-r--r--src/librustdoc/formats/item_type.rs8
-rw-r--r--src/librustdoc/html/format.rs5
-rw-r--r--src/librustdoc/html/highlight.rs25
-rw-r--r--src/librustdoc/html/markdown.rs665
-rw-r--r--src/librustdoc/html/markdown/tests.rs204
-rw-r--r--src/librustdoc/html/render/context.rs74
-rw-r--r--src/librustdoc/html/render/mod.rs77
-rw-r--r--src/librustdoc/html/render/print_item.rs449
-rw-r--r--src/librustdoc/html/render/search_index.rs491
-rw-r--r--src/librustdoc/html/render/sidebar.rs46
-rw-r--r--src/librustdoc/html/sources.rs2
-rw-r--r--src/librustdoc/html/static/css/noscript.css213
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css512
-rw-r--r--src/librustdoc/html/static/css/settings.css63
-rw-r--r--src/librustdoc/html/static/css/themes/ayu.css180
-rw-r--r--src/librustdoc/html/static/css/themes/dark.css101
-rw-r--r--src/librustdoc/html/static/css/themes/light.css98
-rw-r--r--src/librustdoc/html/static/js/externs.js54
-rw-r--r--src/librustdoc/html/static/js/main.js49
-rw-r--r--src/librustdoc/html/static/js/search.js752
-rw-r--r--src/librustdoc/html/static/js/settings.js8
-rw-r--r--src/librustdoc/html/static/js/storage.js36
-rw-r--r--src/librustdoc/html/static_files.rs4
-rw-r--r--src/librustdoc/html/templates/page.html16
-rw-r--r--src/librustdoc/html/templates/type_layout.html4
-rw-r--r--src/librustdoc/json/conversions.rs32
-rw-r--r--src/librustdoc/json/mod.rs2
-rw-r--r--src/librustdoc/lib.rs15
-rw-r--r--src/librustdoc/markdown.rs14
-rw-r--r--src/librustdoc/passes/calculate_doc_coverage.rs9
-rw-r--r--src/librustdoc/passes/check_custom_code_classes.rs92
-rw-r--r--src/librustdoc/passes/check_doc_test_visibility.rs11
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs48
-rw-r--r--src/librustdoc/passes/lint/bare_urls.rs7
-rw-r--r--src/librustdoc/passes/lint/check_code_block_syntax.rs20
-rw-r--r--src/librustdoc/passes/lint/html_tags.rs7
-rw-r--r--src/librustdoc/passes/lint/redundant_explicit_links.rs34
-rw-r--r--src/librustdoc/passes/lint/unescaped_backticks.rs13
-rw-r--r--src/librustdoc/passes/mod.rs96
-rw-r--r--src/librustdoc/passes/stripper.rs2
-rw-r--r--src/librustdoc/scrape_examples.rs2
-rw-r--r--src/librustdoc/theme.rs3
-rw-r--r--src/librustdoc/theme/tests.rs2
-rw-r--r--src/librustdoc/visit.rs2
54 files changed, 3351 insertions, 1750 deletions
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index cac211307..fcbcfbf5c 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -20,7 +20,8 @@ use rustc_span::symbol::{kw, sym, Symbol};
use crate::clean::{
self, clean_fn_decl_from_did_and_sig, clean_generics, clean_impl_item, clean_middle_assoc_item,
clean_middle_field, clean_middle_ty, clean_trait_ref_with_bindings, clean_ty,
- clean_ty_generics, clean_variant_def, utils, Attributes, AttributesExt, ImplKind, ItemId, Type,
+ clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes,
+ AttributesExt, ImplKind, ItemId, Type,
};
use crate::core::DocContext;
use crate::formats::item_type::ItemType;
@@ -79,10 +80,10 @@ pub(crate) fn try_inline(
build_impls(cx, did, attrs_without_docs, &mut ret);
clean::UnionItem(build_union(cx, did))
}
- Res::Def(DefKind::TyAlias { .. }, did) => {
- record_extern_fqn(cx, did, ItemType::Typedef);
+ Res::Def(DefKind::TyAlias, did) => {
+ record_extern_fqn(cx, did, ItemType::TypeAlias);
build_impls(cx, did, attrs_without_docs, &mut ret);
- clean::TypedefItem(build_type_alias(cx, did))
+ clean::TypeAliasItem(build_type_alias(cx, did))
}
Res::Def(DefKind::Enum, did) => {
record_extern_fqn(cx, did, ItemType::Enum);
@@ -287,18 +288,16 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union {
clean::Union { generics, fields }
}
-fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::Typedef> {
+fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::TypeAlias> {
let predicates = cx.tcx.explicit_predicates_of(did);
- let type_ = clean_middle_ty(
- ty::Binder::dummy(cx.tcx.type_of(did).instantiate_identity()),
- cx,
- Some(did),
- None,
- );
+ let ty = cx.tcx.type_of(did).instantiate_identity();
+ let type_ = clean_middle_ty(ty::Binder::dummy(ty), cx, Some(did), None);
+ let inner_type = clean_ty_alias_inner_type(ty, cx);
- Box::new(clean::Typedef {
+ Box::new(clean::TypeAlias {
type_,
generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
+ inner_type,
item_type: None,
})
}
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index c9a05460b..0caa92e44 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -24,6 +24,7 @@ use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
use rustc_middle::metadata::Reexport;
use rustc_middle::middle::resolve_bound_vars as rbv;
use rustc_middle::ty::fold::TypeFolder;
+use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::TypeVisitableExt;
use rustc_middle::ty::{self, AdtKind, EarlyBinder, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
@@ -541,7 +542,7 @@ fn clean_generic_param_def<'tcx>(
},
)
}
- ty::GenericParamDefKind::Const { has_default } => (
+ ty::GenericParamDefKind::Const { has_default, is_host_effect } => (
def.name,
GenericParamDefKind::Const {
ty: Box::new(clean_middle_ty(
@@ -561,6 +562,7 @@ fn clean_generic_param_def<'tcx>(
)),
false => None,
},
+ is_host_effect,
},
),
};
@@ -617,6 +619,7 @@ fn clean_generic_param<'tcx>(
ty: Box::new(clean_ty(ty, cx)),
default: default
.map(|ct| Box::new(ty::Const::from_anon_const(cx.tcx, ct.def_id).to_string())),
+ is_host_effect: cx.tcx.has_attr(param.def_id, sym::rustc_host),
},
),
};
@@ -792,6 +795,7 @@ fn clean_ty_generics<'tcx>(
}
Some(clean_generic_param_def(param, cx))
}
+ ty::GenericParamDefKind::Const { is_host_effect: true, .. } => None,
ty::GenericParamDefKind::Const { .. } => Some(clean_generic_param_def(param, cx)),
})
.collect::<ThinVec<GenericParamDef>>();
@@ -955,6 +959,43 @@ fn clean_ty_generics<'tcx>(
}
}
+fn clean_ty_alias_inner_type<'tcx>(
+ ty: Ty<'tcx>,
+ cx: &mut DocContext<'tcx>,
+) -> Option<TypeAliasInnerType> {
+ let ty::Adt(adt_def, args) = ty.kind() else {
+ return None;
+ };
+
+ Some(if adt_def.is_enum() {
+ let variants: rustc_index::IndexVec<_, _> = adt_def
+ .variants()
+ .iter()
+ .map(|variant| clean_variant_def_with_args(variant, args, cx))
+ .collect();
+
+ TypeAliasInnerType::Enum {
+ variants,
+ is_non_exhaustive: adt_def.is_variant_list_non_exhaustive(),
+ }
+ } else {
+ let variant = adt_def
+ .variants()
+ .iter()
+ .next()
+ .unwrap_or_else(|| bug!("a struct or union should always have one variant def"));
+
+ let fields: Vec<_> =
+ clean_variant_def_with_args(variant, args, cx).kind.inner_items().cloned().collect();
+
+ if adt_def.is_struct() {
+ TypeAliasInnerType::Struct { ctor_kind: variant.ctor_kind(), fields }
+ } else {
+ TypeAliasInnerType::Union { fields }
+ }
+ })
+}
+
fn clean_proc_macro<'tcx>(
item: &hir::Item<'tcx>,
name: &mut Symbol,
@@ -1069,10 +1110,7 @@ fn clean_function<'tcx>(
clean_args_from_types_and_names(cx, sig.decl.inputs, names)
}
};
- let mut decl = clean_fn_decl_with_args(cx, sig.decl, args);
- if sig.header.is_async() {
- decl.output = decl.sugared_async_return_type();
- }
+ let decl = clean_fn_decl_with_args(cx, sig.decl, Some(&sig.header), args);
(generics, decl)
});
Box::new(Function { decl, generics })
@@ -1123,12 +1161,16 @@ fn clean_args_from_types_and_body_id<'tcx>(
fn clean_fn_decl_with_args<'tcx>(
cx: &mut DocContext<'tcx>,
decl: &hir::FnDecl<'tcx>,
+ header: Option<&hir::FnHeader>,
args: Arguments,
) -> FnDecl {
- let output = match decl.output {
+ let mut output = match decl.output {
hir::FnRetTy::Return(typ) => clean_ty(typ, cx),
hir::FnRetTy::DefaultReturn(..) => Type::Tuple(Vec::new()),
};
+ if let Some(header) = header && header.is_async() {
+ output = output.sugared_async_return_type();
+ }
FnDecl { inputs: args, output, c_variadic: decl.c_variadic }
}
@@ -1141,7 +1183,17 @@ fn clean_fn_decl_from_did_and_sig<'tcx>(
// We assume all empty tuples are default return type. This theoretically can discard `-> ()`,
// but shouldn't change any code meaning.
- let output = clean_middle_ty(sig.output(), cx, None, None);
+ let mut output = clean_middle_ty(sig.output(), cx, None, None);
+
+ // If the return type isn't an `impl Trait`, we can safely assume that this
+ // function isn't async without needing to execute the query `asyncness` at
+ // all which gives us a noticeable performance boost.
+ if let Some(did) = did
+ && let Type::ImplTrait(_) = output
+ && cx.tcx.asyncness(did).is_async()
+ {
+ output = output.sugared_async_return_type();
+ }
FnDecl {
output,
@@ -1219,9 +1271,10 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
None,
);
AssocTypeItem(
- Box::new(Typedef {
+ Box::new(TypeAlias {
type_: clean_ty(default, cx),
generics,
+ inner_type: None,
item_type: Some(item_type),
}),
bounds,
@@ -1264,7 +1317,12 @@ pub(crate) fn clean_impl_item<'tcx>(
None,
);
AssocTypeItem(
- Box::new(Typedef { type_, generics, item_type: Some(item_type) }),
+ Box::new(TypeAlias {
+ type_,
+ generics,
+ inner_type: None,
+ item_type: Some(item_type),
+ }),
Vec::new(),
)
}
@@ -1461,7 +1519,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
if tcx.defaultness(assoc_item.def_id).has_value() {
AssocTypeItem(
- Box::new(Typedef {
+ Box::new(TypeAlias {
type_: clean_middle_ty(
ty::Binder::dummy(
tcx.type_of(assoc_item.def_id).instantiate_identity(),
@@ -1471,6 +1529,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
None,
),
generics,
+ inner_type: None,
item_type: None,
}),
bounds,
@@ -1480,7 +1539,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
}
} else {
AssocTypeItem(
- Box::new(Typedef {
+ Box::new(TypeAlias {
type_: clean_middle_ty(
ty::Binder::dummy(
tcx.type_of(assoc_item.def_id).instantiate_identity(),
@@ -1490,6 +1549,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
None,
),
generics,
+ inner_type: None,
item_type: None,
}),
// Associated types inside trait or inherent impls are not allowed to have
@@ -1706,7 +1766,7 @@ fn maybe_expand_private_type_alias<'tcx>(
cx: &mut DocContext<'tcx>,
path: &hir::Path<'tcx>,
) -> Option<Type> {
- let Res::Def(DefKind::TyAlias { .. }, def_id) = path.res else { return None };
+ let Res::Def(DefKind::TyAlias, def_id) = path.res else { return None };
// Substitute private type aliases
let def_id = def_id.as_local()?;
let alias = if !cx.cache.effective_visibilities.is_exported(cx.tcx, def_id.to_def_id())
@@ -1817,7 +1877,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
// does nothing for `ConstKind::Param`.
let ct = ty::Const::from_anon_const(cx.tcx, anon_const.def_id);
let param_env = cx.tcx.param_env(anon_const.def_id);
- print_const(cx, ct.eval(cx.tcx, param_env))
+ print_const(cx, ct.normalize(cx.tcx, param_env))
}
};
@@ -1975,7 +2035,7 @@ impl<'tcx> ContainerTy<'tcx> {
let (DefKind::Struct
| DefKind::Union
| DefKind::Enum
- | DefKind::TyAlias { .. }
+ | DefKind::TyAlias
| DefKind::Trait) = tcx.def_kind(container)
else {
return ObjectLifetimeDefault::Empty;
@@ -2036,7 +2096,7 @@ pub(crate) fn clean_middle_ty<'tcx>(
ty::Str => Primitive(PrimitiveType::Str),
ty::Slice(ty) => Slice(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None))),
ty::Array(ty, mut n) => {
- n = n.eval(cx.tcx, ty::ParamEnv::reveal_all());
+ n = n.normalize(cx.tcx, ty::ParamEnv::reveal_all());
let n = print_const(cx, n);
Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), n.into())
}
@@ -2241,7 +2301,6 @@ pub(crate) fn clean_middle_ty<'tcx>(
ty::Bound(..) => panic!("Bound"),
ty::Placeholder(..) => panic!("Placeholder"),
ty::GeneratorWitness(..) => panic!("GeneratorWitness"),
- ty::GeneratorWitnessMIR(..) => panic!("GeneratorWitnessMIR"),
ty::Infer(..) => panic!("Infer"),
ty::Error(_) => rustc_errors::FatalError.raise(),
}
@@ -2363,6 +2422,83 @@ pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocCont
)
}
+pub(crate) fn clean_variant_def_with_args<'tcx>(
+ variant: &ty::VariantDef,
+ args: &GenericArgsRef<'tcx>,
+ cx: &mut DocContext<'tcx>,
+) -> Item {
+ let discriminant = match variant.discr {
+ ty::VariantDiscr::Explicit(def_id) => Some(Discriminant { expr: None, value: def_id }),
+ ty::VariantDiscr::Relative(_) => None,
+ };
+
+ use rustc_middle::traits::ObligationCause;
+ use rustc_trait_selection::infer::TyCtxtInferExt;
+ use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
+
+ let infcx = cx.tcx.infer_ctxt().build();
+ let kind = match variant.ctor_kind() {
+ Some(CtorKind::Const) => VariantKind::CLike,
+ Some(CtorKind::Fn) => VariantKind::Tuple(
+ variant
+ .fields
+ .iter()
+ .map(|field| {
+ let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args);
+
+ // normalize the type to only show concrete types
+ // note: we do not use try_normalize_erasing_regions since we
+ // do care about showing the regions
+ let ty = infcx
+ .at(&ObligationCause::dummy(), cx.param_env)
+ .query_normalize(ty)
+ .map(|normalized| normalized.value)
+ .unwrap_or(ty);
+
+ clean_field_with_def_id(
+ field.did,
+ field.name,
+ clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None),
+ cx,
+ )
+ })
+ .collect(),
+ ),
+ None => VariantKind::Struct(VariantStruct {
+ fields: variant
+ .fields
+ .iter()
+ .map(|field| {
+ let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args);
+
+ // normalize the type to only show concrete types
+ // note: we do not use try_normalize_erasing_regions since we
+ // do care about showing the regions
+ let ty = infcx
+ .at(&ObligationCause::dummy(), cx.param_env)
+ .query_normalize(ty)
+ .map(|normalized| normalized.value)
+ .unwrap_or(ty);
+
+ clean_field_with_def_id(
+ field.did,
+ field.name,
+ clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None),
+ cx,
+ )
+ })
+ .collect(),
+ }),
+ };
+
+ Item::from_def_id_and_parts(
+ variant.def_id,
+ Some(variant.name),
+ VariantItem(Variant { kind, discriminant }),
+ cx,
+ )
+}
+
fn clean_variant_data<'tcx>(
variant: &hir::VariantData<'tcx>,
disr_expr: &Option<hir::AnonConst>,
@@ -2406,14 +2542,22 @@ fn clean_generic_args<'tcx>(
let args = generic_args
.args
.iter()
- .map(|arg| match arg {
- hir::GenericArg::Lifetime(lt) if !lt.is_anonymous() => {
- 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,
+ .filter_map(|arg| {
+ Some(match arg {
+ hir::GenericArg::Lifetime(lt) if !lt.is_anonymous() => {
+ GenericArg::Lifetime(clean_lifetime(*lt, cx))
+ }
+ hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()),
+ hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty, cx)),
+ // FIXME(effects): This will still emit `<true>` for non-const impls of const traits
+ hir::GenericArg::Const(ct)
+ if cx.tcx.has_attr(ct.value.def_id, sym::rustc_host) =>
+ {
+ return None;
+ }
+ hir::GenericArg::Const(ct) => GenericArg::Const(Box::new(clean_const(ct, cx))),
+ hir::GenericArg::Infer(_inf) => GenericArg::Infer,
+ })
})
.collect::<Vec<_>>()
.into();
@@ -2443,7 +2587,7 @@ fn clean_bare_fn_ty<'tcx>(
.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);
+ let decl = clean_fn_decl_with_args(cx, bare_fn.decl, None, args);
(generic_params, decl)
});
BareFunctionDecl { unsafety: bare_fn.unsafety, abi: bare_fn.abi, decl, generic_params }
@@ -2617,7 +2761,7 @@ fn clean_maybe_renamed_item<'tcx>(
ItemKind::TyAlias(hir_ty, generics) => {
*cx.current_type_aliases.entry(def_id).or_insert(0) += 1;
let rustdoc_ty = clean_ty(hir_ty, cx);
- let ty = clean_middle_ty(
+ let type_ = clean_middle_ty(
ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)),
cx,
None,
@@ -2630,7 +2774,16 @@ fn clean_maybe_renamed_item<'tcx>(
cx.current_type_aliases.remove(&def_id);
}
}
- TypedefItem(Box::new(Typedef { type_: rustdoc_ty, generics, item_type: Some(ty) }))
+
+ let ty = cx.tcx.type_of(def_id).instantiate_identity();
+ let inner_type = clean_ty_alias_inner_type(ty, cx);
+
+ TypeAliasItem(Box::new(TypeAlias {
+ generics,
+ inner_type,
+ type_: rustdoc_ty,
+ item_type: Some(type_),
+ }))
}
ItemKind::Enum(ref def, generics) => EnumItem(Enum {
variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(),
@@ -2722,7 +2875,7 @@ fn clean_impl<'tcx>(
let for_ = clean_ty(impl_.self_ty, cx);
let type_alias =
for_.def_id(&cx.cache).and_then(|alias_def_id: DefId| match tcx.def_kind(alias_def_id) {
- DefKind::TyAlias { .. } => Some(clean_middle_ty(
+ DefKind::TyAlias => Some(clean_middle_ty(
ty::Binder::dummy(tcx.type_of(def_id).instantiate_identity()),
cx,
Some(def_id.to_def_id()),
@@ -2945,7 +3098,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
// NOTE: generics must be cleaned before args
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);
+ let decl = clean_fn_decl_with_args(cx, decl, None, args);
(generics, decl)
});
ForeignFunctionItem(Box::new(Function { decl, generics }))
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 49bde1d31..4256e7b51 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -22,20 +22,23 @@ use rustc_hir::lang_items::LangItem;
use rustc_hir::{BodyId, Mutability};
use rustc_hir_analysis::check::intrinsic::intrinsic_operation_unsafety;
use rustc_index::IndexVec;
+use rustc_metadata::rendered_const;
use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::{self, TyCtxt, Visibility};
-use rustc_resolve::rustdoc::{add_doc_fragment, attrs_to_doc_fragments, inner_docs, DocFragment};
+use rustc_resolve::rustdoc::{
+ add_doc_fragment, attrs_to_doc_fragments, inner_docs, span_of_fragments, DocFragment,
+};
use rustc_session::Session;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_span::{self, FileName, Loc};
+use rustc_span::{self, FileName, Loc, DUMMY_SP};
use rustc_target::abi::VariantIdx;
use rustc_target::spec::abi::Abi;
use crate::clean::cfg::Cfg;
use crate::clean::external_path;
use crate::clean::inline::{self, print_inlined_const};
-use crate::clean::utils::{is_literal_expr, print_const_expr, print_evaluated_const};
+use crate::clean::utils::{is_literal_expr, print_evaluated_const};
use crate::core::DocContext;
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
@@ -396,7 +399,7 @@ impl Item {
}
pub(crate) fn attr_span(&self, tcx: TyCtxt<'_>) -> rustc_span::Span {
- crate::passes::span_of_attrs(&self.attrs)
+ span_of_fragments(&self.attrs.doc_strings)
.unwrap_or_else(|| self.span(tcx).map_or(rustc_span::DUMMY_SP, |span| span.inner()))
}
@@ -530,8 +533,8 @@ impl Item {
pub(crate) fn is_ty_method(&self) -> bool {
self.type_() == ItemType::TyMethod
}
- pub(crate) fn is_typedef(&self) -> bool {
- self.type_() == ItemType::Typedef
+ pub(crate) fn is_type_alias(&self) -> bool {
+ self.type_() == ItemType::TypeAlias
}
pub(crate) fn is_primitive(&self) -> bool {
self.type_() == ItemType::Primitive
@@ -619,7 +622,7 @@ impl Item {
fn build_fn_header(
def_id: DefId,
tcx: TyCtxt<'_>,
- asyncness: hir::IsAsync,
+ asyncness: ty::Asyncness,
) -> hir::FnHeader {
let sig = tcx.fn_sig(def_id).skip_binder();
let constness =
@@ -628,6 +631,10 @@ impl Item {
} else {
hir::Constness::NotConst
};
+ let asyncness = match asyncness {
+ ty::Asyncness::Yes => hir::IsAsync::Async(DUMMY_SP),
+ ty::Asyncness::No => hir::IsAsync::NotAsync,
+ };
hir::FnHeader { unsafety: sig.unsafety(), abi: sig.abi(), constness, asyncness }
}
let header = match *self.kind {
@@ -799,7 +806,7 @@ pub(crate) enum ItemKind {
EnumItem(Enum),
FunctionItem(Box<Function>),
ModuleItem(Module),
- TypedefItem(Box<Typedef>),
+ TypeAliasItem(Box<TypeAlias>),
OpaqueTyItem(OpaqueTy),
StaticItem(Static),
ConstantItem(Constant),
@@ -832,7 +839,7 @@ pub(crate) enum ItemKind {
/// The bounds may be non-empty if there is a `where` clause.
TyAssocTypeItem(Generics, Vec<GenericBound>),
/// An associated type in a trait impl or a provided one in a trait declaration.
- AssocTypeItem(Box<Typedef>, Vec<GenericBound>),
+ AssocTypeItem(Box<TypeAlias>, Vec<GenericBound>),
/// An item that has been stripped by a rustdoc pass
StrippedItem(Box<ItemKind>),
KeywordItem,
@@ -857,7 +864,7 @@ impl ItemKind {
ExternCrateItem { .. }
| ImportItem(_)
| FunctionItem(_)
- | TypedefItem(_)
+ | TypeAliasItem(_)
| OpaqueTyItem(_)
| StaticItem(_)
| ConstantItem(_)
@@ -891,7 +898,7 @@ impl ItemKind {
| ModuleItem(_)
| ExternCrateItem { .. }
| FunctionItem(_)
- | TypedefItem(_)
+ | TypeAliasItem(_)
| OpaqueTyItem(_)
| StaticItem(_)
| ConstantItem(_)
@@ -1308,7 +1315,7 @@ impl WherePredicate {
pub(crate) enum GenericParamDefKind {
Lifetime { outlives: Vec<Lifetime> },
Type { did: DefId, bounds: Vec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
- Const { ty: Box<Type>, default: Option<Box<String>> },
+ Const { ty: Box<Type>, default: Option<Box<String>>, is_host_effect: bool },
}
impl GenericParamDefKind {
@@ -1328,9 +1335,10 @@ impl GenericParamDef {
Self { name, kind: GenericParamDefKind::Lifetime { outlives: Vec::new() } }
}
- pub(crate) fn is_synthetic_type_param(&self) -> bool {
+ pub(crate) fn is_synthetic_param(&self) -> bool {
match self.kind {
- GenericParamDefKind::Lifetime { .. } | GenericParamDefKind::Const { .. } => false,
+ GenericParamDefKind::Lifetime { .. } => false,
+ GenericParamDefKind::Const { is_host_effect, .. } => is_host_effect,
GenericParamDefKind::Type { synthetic, .. } => synthetic,
}
}
@@ -1377,28 +1385,6 @@ impl FnDecl {
pub(crate) fn self_type(&self) -> Option<SelfTy> {
self.inputs.values.get(0).and_then(|v| v.to_self())
}
-
- /// Returns the sugared return type for an async function.
- ///
- /// For example, if the return type is `impl std::future::Future<Output = i32>`, this function
- /// will return `i32`.
- ///
- /// # Panics
- ///
- /// This function will panic if the return type does not match the expected sugaring for async
- /// functions.
- pub(crate) fn sugared_async_return_type(&self) -> Type {
- if let Type::ImplTrait(v) = &self.output &&
- let [GenericBound::TraitBound(PolyTrait { trait_, .. }, _ )] = &v[..]
- {
- let bindings = trait_.bindings().unwrap();
- let ret_ty = bindings[0].term();
- let ty = ret_ty.ty().expect("Unexpected constant return term");
- ty.clone()
- } else {
- panic!("unexpected desugaring of async function")
- }
- }
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
@@ -1610,6 +1596,30 @@ impl Type {
}
}
+ /// Returns the sugared return type for an async function.
+ ///
+ /// For example, if the return type is `impl std::future::Future<Output = i32>`, this function
+ /// will return `i32`.
+ ///
+ /// # Panics
+ ///
+ /// This function will panic if the return type does not match the expected sugaring for async
+ /// functions.
+ pub(crate) fn sugared_async_return_type(self) -> Type {
+ if let Type::ImplTrait(mut v) = self
+ && let Some(GenericBound::TraitBound(PolyTrait { mut trait_, .. }, _ )) = v.pop()
+ && let Some(segment) = trait_.segments.pop()
+ && let GenericArgs::AngleBracketed { mut bindings, .. } = segment.args
+ && let Some(binding) = bindings.pop()
+ && let TypeBindingKind::Equality { term } = binding.kind
+ && let Term::Type(ty) = term
+ {
+ ty
+ } else {
+ panic!("unexpected async fn return type")
+ }
+ }
+
/// Checks if this is a `T::Name` path for an associated type.
pub(crate) fn is_assoc_ty(&self) -> bool {
match self {
@@ -1636,10 +1646,6 @@ impl Type {
matches!(self, Type::Generic(_))
}
- pub(crate) fn is_impl_trait(&self) -> bool {
- matches!(self, Type::ImplTrait(_))
- }
-
pub(crate) fn is_unit(&self) -> bool {
matches!(self, Type::Tuple(v) if v.is_empty())
}
@@ -2086,7 +2092,7 @@ 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))
+ self.expr.map(|body| rendered_const(tcx, body))
}
/// Will always be a machine readable number, without underscores or suffixes.
pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> String {
@@ -2186,16 +2192,6 @@ impl Path {
}
})
}
-
- pub(crate) fn bindings(&self) -> Option<&[TypeBinding]> {
- self.segments.last().and_then(|seg| {
- if let GenericArgs::AngleBracketed { ref bindings, .. } = seg.args {
- Some(&**bindings)
- } else {
- None
- }
- })
- }
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
@@ -2230,9 +2226,19 @@ pub(crate) struct PathSegment {
}
#[derive(Clone, Debug)]
-pub(crate) struct Typedef {
+pub(crate) enum TypeAliasInnerType {
+ Enum { variants: IndexVec<VariantIdx, Item>, is_non_exhaustive: bool },
+ Union { fields: Vec<Item> },
+ Struct { ctor_kind: Option<CtorKind>, fields: Vec<Item> },
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct TypeAlias {
pub(crate) type_: Type,
pub(crate) generics: Generics,
+ /// Inner `AdtDef` type, ie `type TyKind = IrTyKind<Adt, Ty>`,
+ /// to be shown directly on the typedef page.
+ pub(crate) inner_type: Option<TypeAliasInnerType>,
/// `type_` can come from either the HIR or from metadata. If it comes from HIR, it may be a type
/// alias instead of the final type. This will always have the final type, regardless of whether
/// `type_` came from HIR or from metadata.
@@ -2326,7 +2332,7 @@ impl ConstantKind {
ConstantKind::TyConst { ref expr } => expr.to_string(),
ConstantKind::Extern { def_id } => print_inlined_const(tcx, def_id),
ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => {
- print_const_expr(tcx, body)
+ rendered_const(tcx, body)
}
}
}
@@ -2465,15 +2471,6 @@ pub(crate) enum TypeBindingKind {
Constraint { bounds: Vec<GenericBound> },
}
-impl TypeBinding {
- pub(crate) fn term(&self) -> &Term {
- match self.kind {
- TypeBindingKind::Equality { ref term } => term,
- _ => panic!("expected equality type binding for parenthesized generic args"),
- }
- }
-}
-
/// The type, lifetime, or constant that a private type alias's parameter should be
/// replaced with when expanding a use of that type alias.
///
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index a86bbcc76..61e653423 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -14,8 +14,8 @@ use rustc_ast::tokenstream::TokenTree;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
+use rustc_metadata::rendered_const;
use rustc_middle::mir;
-use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, TyCtxt};
use rustc_span::symbol::{kw, sym, Symbol};
use std::fmt::Write as _;
@@ -104,6 +104,10 @@ pub(crate) fn ty_args_to_args<'tcx>(
arg: index,
}),
))),
+ // FIXME(effects): this relies on the host effect being called `host`, which users could also name
+ // their const generics.
+ // FIXME(effects): this causes `host = true` and `host = false` generics to also be emitted.
+ GenericArgKind::Const(ct) if let ty::ConstKind::Param(p) = ct.kind() && p.name == sym::host => None,
GenericArgKind::Const(ct) => {
Some(GenericArg::Const(Box::new(clean_middle_const(kind.rebind(ct), cx))))
}
@@ -153,7 +157,7 @@ pub(super) fn external_path<'tcx>(
args: ty::Binder<'tcx, GenericArgsRef<'tcx>>,
) -> Path {
let def_kind = cx.tcx.def_kind(did);
- let name = cx.tcx.item_name(did);
+ let name = cx.tcx.opt_item_name(did).unwrap_or(kw::Empty);
Path {
res: Res::Def(def_kind, did),
segments: thin_vec![PathSegment {
@@ -255,7 +259,7 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
match n.kind() {
ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args: _ }) => {
let s = if let Some(def) = def.as_local() {
- print_const_expr(cx.tcx, cx.tcx.hir().body_owned_by(def))
+ rendered_const(cx.tcx, cx.tcx.hir().body_owned_by(def))
} else {
inline::print_inlined_const(cx.tcx, def)
};
@@ -281,9 +285,9 @@ pub(crate) fn print_evaluated_const(
let ty = tcx.type_of(def_id).instantiate_identity();
match (val, ty.kind()) {
(_, &ty::Ref(..)) => None,
- (ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
- (ConstValue::Scalar(_), _) => {
- let const_ = mir::ConstantKind::from_value(val, ty);
+ (mir::ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
+ (mir::ConstValue::Scalar(_), _) => {
+ let const_ = mir::Const::from_value(val, ty);
Some(print_const_with_custom_print_scalar(tcx, const_, underscores_and_type))
}
_ => None,
@@ -319,20 +323,20 @@ fn format_integer_with_underscore_sep(num: &str) -> String {
fn print_const_with_custom_print_scalar<'tcx>(
tcx: TyCtxt<'tcx>,
- ct: mir::ConstantKind<'tcx>,
+ ct: mir::Const<'tcx>,
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)) => {
+ (mir::Const::Val(mir::ConstValue::Scalar(int), _), ty::Uint(ui)) => {
if underscores_and_type {
format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
} else {
int.to_string()
}
}
- (mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Int(i)) => {
+ (mir::Const::Val(mir::ConstValue::Scalar(int), _), ty::Int(i)) => {
let ty = ct.ty();
let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size;
let data = int.assert_bits(size);
@@ -367,100 +371,6 @@ pub(crate) fn is_literal_expr(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
false
}
-/// Build a textual representation of an unevaluated constant expression.
-///
-/// If the const expression is too complex, an underscore `_` is returned.
-/// For const arguments, it's `{ _ }` to be precise.
-/// This means that the output is not necessarily valid Rust code.
-///
-/// Currently, only
-///
-/// * literals (optionally with a leading `-`)
-/// * unit `()`
-/// * blocks (`{ … }`) around simple expressions and
-/// * paths without arguments
-///
-/// are considered simple enough. Simple blocks are included since they are
-/// necessary to disambiguate unit from the unit type.
-/// This list might get extended in the future.
-///
-/// Without this censoring, in a lot of cases the output would get too large
-/// and verbose. Consider `match` expressions, blocks and deeply nested ADTs.
-/// Further, private and `doc(hidden)` fields of structs would get leaked
-/// since HIR datatypes like the `body` parameter do not contain enough
-/// semantic information for this function to be able to hide them –
-/// at least not without significant performance overhead.
-///
-/// Whenever possible, prefer to evaluate the constant first and try to
-/// use a different method for pretty-printing. Ideally this function
-/// should only ever be used as a fallback.
-pub(crate) fn print_const_expr(tcx: TyCtxt<'_>, body: hir::BodyId) -> String {
- let hir = tcx.hir();
- let value = &hir.body(body).value;
-
- #[derive(PartialEq, Eq)]
- enum Classification {
- Literal,
- Simple,
- Complex,
- }
-
- use Classification::*;
-
- fn classify(expr: &hir::Expr<'_>) -> Classification {
- match &expr.kind {
- hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
- if matches!(expr.kind, hir::ExprKind::Lit(_)) { Literal } else { Complex }
- }
- hir::ExprKind::Lit(_) => Literal,
- hir::ExprKind::Tup([]) => Simple,
- hir::ExprKind::Block(hir::Block { stmts: [], expr: Some(expr), .. }, _) => {
- if classify(expr) == Complex { Complex } else { Simple }
- }
- // Paths with a self-type or arguments are too “complex” following our measure since
- // they may leak private fields of structs (with feature `adt_const_params`).
- // Consider: `<Self as Trait<{ Struct { private: () } }>>::CONSTANT`.
- // Paths without arguments are definitely harmless though.
- hir::ExprKind::Path(hir::QPath::Resolved(_, hir::Path { segments, .. })) => {
- if segments.iter().all(|segment| segment.args.is_none()) { Simple } else { Complex }
- }
- // FIXME: Claiming that those kinds of QPaths are simple is probably not true if the Ty
- // contains const arguments. Is there a *concise* way to check for this?
- hir::ExprKind::Path(hir::QPath::TypeRelative(..)) => Simple,
- // FIXME: Can they contain const arguments and thus leak private struct fields?
- hir::ExprKind::Path(hir::QPath::LangItem(..)) => Simple,
- _ => Complex,
- }
- }
-
- let classification = classify(value);
-
- if classification == Literal
- && !value.span.from_expansion()
- && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(value.span) {
- // For literals, we avoid invoking the pretty-printer and use the source snippet instead to
- // preserve certain stylistic choices the user likely made for the sake legibility like
- //
- // * hexadecimal notation
- // * underscores
- // * character escapes
- //
- // FIXME: This passes through `-/*spacer*/0` verbatim.
- snippet
- } else if classification == Simple {
- // Otherwise we prefer pretty-printing to get rid of extraneous whitespace, comments and
- // other formatting artifacts.
- rustc_hir_pretty::id_to_string(&hir, body.hir_id)
- } else if tcx.def_kind(hir.body_owner_def_id(body).to_def_id()) == DefKind::AnonConst {
- // FIXME: Omit the curly braces if the enclosing expression is an array literal
- // with a repeated element (an `ExprKind::Repeat`) as in such case it
- // would not actually need any disambiguation.
- "{ _ }".to_owned()
- } else {
- "_".to_owned()
- }
-}
-
/// Given a type Path, resolve it to a Type using the TyCtxt
pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type {
debug!("resolve_type({path:?})");
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 81fb13f41..99aa97902 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -157,6 +157,12 @@ pub(crate) struct Options {
/// Note: this field is duplicated in `RenderOptions` because it's useful
/// to have it in both places.
pub(crate) unstable_features: rustc_feature::UnstableFeatures,
+
+ /// All commandline args used to invoke the compiler, with @file args fully expanded.
+ /// This will only be used within debug info, e.g. in the pdb file on windows
+ /// This is mainly useful for other tools that reads that debuginfo to figure out
+ /// how to call the compiler with the same arguments.
+ pub(crate) expanded_args: Vec<String>,
}
impl fmt::Debug for Options {
@@ -273,6 +279,8 @@ pub(crate) struct RenderOptions {
pub(crate) call_locations: AllCallLocations,
/// If `true`, Context::init will not emit shared files.
pub(crate) no_emit_shared: bool,
+ /// If `true`, HTML source code pages won't be generated.
+ pub(crate) html_no_source: bool,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -402,9 +410,15 @@ impl Options {
let to_check = matches.opt_strs("check-theme");
if !to_check.is_empty() {
- let paths = match theme::load_css_paths(
- std::str::from_utf8(static_files::STATIC_FILES.theme_light_css.bytes).unwrap(),
- ) {
+ let mut content =
+ std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.bytes).unwrap();
+ if let Some((_, inside)) = content.split_once("/* Begin theme: light */") {
+ content = inside;
+ }
+ if let Some((inside, _)) = content.split_once("/* End theme: light */") {
+ content = inside;
+ }
+ let paths = match theme::load_css_paths(content) {
Ok(p) => p,
Err(e) => {
diag.struct_err(e).emit();
@@ -542,9 +556,15 @@ impl Options {
let mut themes = Vec::new();
if matches.opt_present("theme") {
- let paths = match theme::load_css_paths(
- std::str::from_utf8(static_files::STATIC_FILES.theme_light_css.bytes).unwrap(),
- ) {
+ let mut content =
+ std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.bytes).unwrap();
+ if let Some((_, inside)) = content.split_once("/* Begin theme: light */") {
+ content = inside;
+ }
+ if let Some((inside, _)) = content.split_once("/* End theme: light */") {
+ content = inside;
+ }
+ let paths = match theme::load_css_paths(content) {
Ok(p) => p,
Err(e) => {
diag.struct_err(e).emit();
@@ -686,6 +706,7 @@ impl Options {
let generate_link_to_definition = matches.opt_present("generate-link-to-definition");
let extern_html_root_takes_precedence =
matches.opt_present("extern-html-root-takes-precedence");
+ let html_no_source = matches.opt_present("html-no-source");
if generate_link_to_definition && (show_coverage || output_format != OutputFormat::Html) {
diag.struct_err(
@@ -741,6 +762,7 @@ impl Options {
json_unused_externs,
scrape_examples_options,
unstable_features,
+ expanded_args: args,
};
let render_options = RenderOptions {
output,
@@ -769,6 +791,7 @@ impl Options {
generate_link_to_definition,
call_locations,
no_emit_shared: false,
+ html_no_source,
};
Ok((options, render_options))
}
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 4c8dab61f..3e6066c78 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -1,7 +1,7 @@
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::sync::{self, Lrc};
+use rustc_data_structures::sync::Lrc;
use rustc_data_structures::unord::UnordSet;
-use rustc_errors::emitter::{Emitter, EmitterWriter};
+use rustc_errors::emitter::{DynEmitter, EmitterWriter};
use rustc_errors::json::JsonEmitter;
use rustc_errors::TerminalUrl;
use rustc_feature::UnstableFeatures;
@@ -133,7 +133,7 @@ pub(crate) fn new_handler(
rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
false,
);
- let emitter: Box<dyn Emitter + sync::Send> = match error_format {
+ let emitter: Box<DynEmitter> = match error_format {
ErrorOutputType::HumanReadable(kind) => {
let (short, color_config) = kind.unzip();
Box::new(
@@ -194,6 +194,7 @@ pub(crate) fn create_config(
describe_lints,
lint_cap,
scrape_examples_options,
+ expanded_args,
..
}: RustdocOptions,
RenderOptions { document_private, .. }: &RenderOptions,
@@ -262,7 +263,7 @@ pub(crate) fn create_config(
lint_caps,
parse_sess_created: None,
register_lints: Some(Box::new(crate::lint::register_lints)),
- override_queries: Some(|_sess, providers, _external_providers| {
+ override_queries: Some(|_sess, providers| {
// We do not register late module lints, so this only runs `MissingDoc`.
// Most lints will require typechecking, so just don't run them.
providers.lint_mod = |tcx, module_def_id| late_lint_mod(tcx, module_def_id, MissingDoc);
@@ -291,6 +292,7 @@ pub(crate) fn create_config(
make_codegen_backend: None,
registry: rustc_driver::diagnostics_registry(),
ice_file: None,
+ expanded_args,
}
}
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 36d5adb63..741d329fb 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -10,6 +10,7 @@ use rustc_middle::hir::nested_filter;
use rustc_middle::ty::TyCtxt;
use rustc_parse::maybe_new_parser_from_source_str;
use rustc_parse::parser::attr::InnerAttrPolicy;
+use rustc_resolve::rustdoc::span_of_fragments;
use rustc_session::config::{self, CrateType, ErrorOutputType};
use rustc_session::parse::ParseSess;
use rustc_session::{lint, EarlyErrorHandler, Session};
@@ -33,7 +34,6 @@ use crate::clean::{types::AttributesExt, Attributes};
use crate::config::Options as RustdocOptions;
use crate::html::markdown::{self, ErrorCodes, Ignore, LangString};
use crate::lint::init_lints;
-use crate::passes::span_of_attrs;
/// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`).
#[derive(Clone, Default)]
@@ -109,6 +109,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
make_codegen_backend: None,
registry: rustc_driver::diagnostics_registry(),
ice_file: None,
+ expanded_args: options.expanded_args.clone(),
};
let test_args = options.test_args.clone();
@@ -680,7 +681,7 @@ pub(crate) fn make_test(
if s.contains(crate_name) {
// rustdoc implicitly inserts an `extern crate` item for the own crate
// which may be unused, so we need to allow the lint.
- prog.push_str(&format!("#[allow(unused_extern_crates)]\n"));
+ prog.push_str("#[allow(unused_extern_crates)]\n");
prog.push_str(&format!("extern crate r#{crate_name};\n"));
line_offset += 1;
@@ -1240,8 +1241,9 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
Some(&crate::html::markdown::ExtraInfo::new(
self.tcx,
def_id.to_def_id(),
- span_of_attrs(&attrs).unwrap_or(sp),
+ span_of_fragments(&attrs.doc_strings).unwrap_or(sp),
)),
+ self.tcx.features().custom_code_classes_in_docs,
);
}
diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs
index f0ebb8e5a..b34b69b1f 100644
--- a/src/librustdoc/externalfiles.rs
+++ b/src/librustdoc/externalfiles.rs
@@ -46,6 +46,8 @@ impl ExternalHtml {
edition,
playground,
heading_offset: HeadingOffset::H2,
+ // For external files, it'll be disabled until the feature is enabled by default.
+ custom_code_classes_in_docs: false,
}
.into_string()
);
@@ -61,6 +63,8 @@ impl ExternalHtml {
edition,
playground,
heading_offset: HeadingOffset::H2,
+ // For external files, it'll be disabled until the feature is enabled by default.
+ custom_code_classes_in_docs: false,
}
.into_string()
);
diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs
index 656aeefb0..cf11e2d78 100644
--- a/src/librustdoc/fold.rs
+++ b/src/librustdoc/fold.rs
@@ -52,10 +52,31 @@ pub(crate) trait DocFolder: Sized {
VariantItem(Variant { kind, discriminant })
}
+ TypeAliasItem(mut typealias) => {
+ typealias.inner_type = typealias.inner_type.map(|inner_type| match inner_type {
+ TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
+ let variants = variants
+ .into_iter_enumerated()
+ .filter_map(|(_, x)| self.fold_item(x))
+ .collect();
+
+ TypeAliasInnerType::Enum { variants, is_non_exhaustive }
+ }
+ TypeAliasInnerType::Union { fields } => {
+ let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
+ TypeAliasInnerType::Union { fields }
+ }
+ TypeAliasInnerType::Struct { ctor_kind, fields } => {
+ let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
+ TypeAliasInnerType::Struct { ctor_kind, fields }
+ }
+ });
+
+ TypeAliasItem(typealias)
+ }
ExternCrateItem { src: _ }
| ImportItem(_)
| FunctionItem(_)
- | TypedefItem(_)
| OpaqueTyItem(_)
| StaticItem(_)
| ConstantItem(_)
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index d1deda0c7..4c6e7dfb9 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -389,7 +389,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
match *item.kind {
clean::StructItem(..)
| clean::EnumItem(..)
- | clean::TypedefItem(..)
+ | clean::TypeAliasItem(..)
| clean::TraitItem(..)
| clean::TraitAliasItem(..)
| clean::FunctionItem(..)
@@ -457,6 +457,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
| clean::StructItem(..)
| clean::UnionItem(..)
| clean::VariantItem(..)
+ | clean::TypeAliasItem(..)
| clean::ImplItem(..) => {
self.cache.parent_stack.push(ParentStackItem::new(&item));
(self.fold_item_recur(item), true)
diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs
index a78893558..be2ee7915 100644
--- a/src/librustdoc/formats/item_type.rs
+++ b/src/librustdoc/formats/item_type.rs
@@ -29,7 +29,7 @@ pub(crate) enum ItemType {
Struct = 3,
Enum = 4,
Function = 5,
- Typedef = 6,
+ TypeAlias = 6,
Static = 7,
Trait = 8,
Impl = 9,
@@ -75,7 +75,7 @@ impl<'a> From<&'a clean::Item> for ItemType {
clean::UnionItem(..) => ItemType::Union,
clean::EnumItem(..) => ItemType::Enum,
clean::FunctionItem(..) => ItemType::Function,
- clean::TypedefItem(..) => ItemType::Typedef,
+ clean::TypeAliasItem(..) => ItemType::TypeAlias,
clean::OpaqueTyItem(..) => ItemType::OpaqueTy,
clean::StaticItem(..) => ItemType::Static,
clean::ConstantItem(..) => ItemType::Constant,
@@ -115,7 +115,7 @@ impl From<DefKind> for ItemType {
DefKind::Struct => Self::Struct,
DefKind::Union => Self::Union,
DefKind::Trait => Self::Trait,
- DefKind::TyAlias { .. } => Self::Typedef,
+ DefKind::TyAlias => Self::TypeAlias,
DefKind::TraitAlias => Self::TraitAlias,
DefKind::Macro(kind) => match kind {
MacroKind::Bang => ItemType::Macro,
@@ -156,7 +156,7 @@ impl ItemType {
ItemType::Union => "union",
ItemType::Enum => "enum",
ItemType::Function => "fn",
- ItemType::Typedef => "type",
+ ItemType::TypeAlias => "type",
ItemType::Static => "static",
ItemType::Trait => "trait",
ItemType::Impl => "impl",
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 2f611c31a..2751b6613 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -250,8 +250,7 @@ impl clean::Generics {
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
- let mut real_params =
- self.params.iter().filter(|p| !p.is_synthetic_type_param()).peekable();
+ let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable();
if real_params.peek().is_none() {
return Ok(());
}
@@ -1599,7 +1598,7 @@ impl PrintWithSpace for hir::Unsafety {
impl PrintWithSpace for hir::IsAsync {
fn print_with_space(&self) -> &str {
match self {
- hir::IsAsync::Async => "async ",
+ hir::IsAsync::Async(_) => "async ",
hir::IsAsync::NotAsync => "",
}
}
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 039e8cdb9..d8e36139a 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -52,8 +52,9 @@ pub(crate) fn render_example_with_highlighting(
out: &mut Buffer,
tooltip: Tooltip,
playground_button: Option<&str>,
+ extra_classes: &[String],
) {
- write_header(out, "rust-example-rendered", None, tooltip);
+ write_header(out, "rust-example-rendered", None, tooltip, extra_classes);
write_code(out, src, None, None);
write_footer(out, playground_button);
}
@@ -65,7 +66,13 @@ pub(crate) fn render_item_decl_with_highlighting(src: &str, out: &mut Buffer) {
write!(out, "</pre>");
}
-fn write_header(out: &mut Buffer, class: &str, extra_content: Option<Buffer>, tooltip: Tooltip) {
+fn write_header(
+ out: &mut Buffer,
+ class: &str,
+ extra_content: Option<Buffer>,
+ tooltip: Tooltip,
+ extra_classes: &[String],
+) {
write!(
out,
"<div class=\"example-wrap{}\">",
@@ -100,9 +107,19 @@ fn write_header(out: &mut Buffer, class: &str, extra_content: Option<Buffer>, to
out.push_buffer(extra);
}
if class.is_empty() {
- write!(out, "<pre class=\"rust\">");
+ write!(
+ out,
+ "<pre class=\"rust{}{}\">",
+ if extra_classes.is_empty() { "" } else { " " },
+ extra_classes.join(" "),
+ );
} else {
- write!(out, "<pre class=\"rust {class}\">");
+ write!(
+ out,
+ "<pre class=\"rust {class}{}{}\">",
+ if extra_classes.is_empty() { "" } else { " " },
+ extra_classes.join(" "),
+ );
}
write!(out, "<code>");
}
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 98cc38a10..d24e6e5fa 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -20,12 +20,14 @@
//! edition: Edition::Edition2015,
//! playground: &None,
//! heading_offset: HeadingOffset::H2,
+//! custom_code_classes_in_docs: true,
//! };
//! let html = md.into_string();
//! // ... something using html
//! ```
use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
use rustc_hir::def_id::DefId;
use rustc_middle::ty::TyCtxt;
pub(crate) use rustc_resolve::rustdoc::main_body_opts;
@@ -37,8 +39,9 @@ use once_cell::sync::Lazy;
use std::borrow::Cow;
use std::collections::VecDeque;
use std::fmt::Write;
+use std::iter::Peekable;
use std::ops::{ControlFlow, Range};
-use std::str;
+use std::str::{self, CharIndices};
use crate::clean::RenderedLink;
use crate::doctest;
@@ -93,6 +96,8 @@ pub struct Markdown<'a> {
/// Offset at which we render headings.
/// E.g. if `heading_offset: HeadingOffset::H2`, then `# something` renders an `<h2>`.
pub heading_offset: HeadingOffset,
+ /// `true` if the `custom_code_classes_in_docs` feature is enabled.
+ pub custom_code_classes_in_docs: bool,
}
/// A struct like `Markdown` that renders the markdown with a table of contents.
pub(crate) struct MarkdownWithToc<'a> {
@@ -101,6 +106,8 @@ pub(crate) struct MarkdownWithToc<'a> {
pub(crate) error_codes: ErrorCodes,
pub(crate) edition: Edition,
pub(crate) playground: &'a Option<Playground>,
+ /// `true` if the `custom_code_classes_in_docs` feature is enabled.
+ pub(crate) custom_code_classes_in_docs: bool,
}
/// A tuple struct like `Markdown` that renders the markdown escaping HTML tags
/// and includes no paragraph tags.
@@ -201,6 +208,7 @@ struct CodeBlocks<'p, 'a, I: Iterator<Item = Event<'a>>> {
// Information about the playground if a URL has been specified, containing an
// optional crate name and the URL.
playground: &'p Option<Playground>,
+ custom_code_classes_in_docs: bool,
}
impl<'p, 'a, I: Iterator<Item = Event<'a>>> CodeBlocks<'p, 'a, I> {
@@ -209,8 +217,15 @@ impl<'p, 'a, I: Iterator<Item = Event<'a>>> CodeBlocks<'p, 'a, I> {
error_codes: ErrorCodes,
edition: Edition,
playground: &'p Option<Playground>,
+ custom_code_classes_in_docs: bool,
) -> Self {
- CodeBlocks { inner: iter, check_error_codes: error_codes, edition, playground }
+ CodeBlocks {
+ inner: iter,
+ check_error_codes: error_codes,
+ edition,
+ playground,
+ custom_code_classes_in_docs,
+ }
}
}
@@ -240,14 +255,28 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
let parse_result = match kind {
CodeBlockKind::Fenced(ref lang) => {
- let parse_result =
- LangString::parse_without_check(lang, self.check_error_codes, false);
+ let parse_result = LangString::parse_without_check(
+ lang,
+ self.check_error_codes,
+ false,
+ self.custom_code_classes_in_docs,
+ );
if !parse_result.rust {
+ let added_classes = parse_result.added_classes;
+ let lang_string = if let Some(lang) = parse_result.unknown.first() {
+ format!("language-{}", lang)
+ } else {
+ String::new()
+ };
+ let whitespace = if added_classes.is_empty() { "" } else { " " };
return Some(Event::Html(
format!(
"<div class=\"example-wrap\">\
- <pre class=\"language-{lang}\"><code>{text}</code></pre>\
+ <pre class=\"{lang_string}{whitespace}{added_classes}\">\
+ <code>{text}</code>\
+ </pre>\
</div>",
+ added_classes = added_classes.join(" "),
text = Escape(&original_text),
)
.into(),
@@ -258,6 +287,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
CodeBlockKind::Indented => Default::default(),
};
+ let added_classes = parse_result.added_classes;
let lines = original_text.lines().filter_map(|l| map_line(l).for_html());
let text = lines.intersperse("\n".into()).collect::<String>();
@@ -315,6 +345,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
&mut s,
tooltip,
playground_button.as_deref(),
+ &added_classes,
);
Some(Event::Html(s.into_inner().into()))
}
@@ -711,6 +742,27 @@ pub(crate) fn find_testable_code<T: doctest::Tester>(
error_codes: ErrorCodes,
enable_per_target_ignores: bool,
extra_info: Option<&ExtraInfo<'_>>,
+ custom_code_classes_in_docs: bool,
+) {
+ find_codes(
+ doc,
+ tests,
+ error_codes,
+ enable_per_target_ignores,
+ extra_info,
+ false,
+ custom_code_classes_in_docs,
+ )
+}
+
+pub(crate) fn find_codes<T: doctest::Tester>(
+ doc: &str,
+ tests: &mut T,
+ error_codes: ErrorCodes,
+ enable_per_target_ignores: bool,
+ extra_info: Option<&ExtraInfo<'_>>,
+ include_non_rust: bool,
+ custom_code_classes_in_docs: bool,
) {
let mut parser = Parser::new(doc).into_offset_iter();
let mut prev_offset = 0;
@@ -729,12 +781,13 @@ pub(crate) fn find_testable_code<T: doctest::Tester>(
error_codes,
enable_per_target_ignores,
extra_info,
+ custom_code_classes_in_docs,
)
}
}
CodeBlockKind::Indented => Default::default(),
};
- if !block_info.rust {
+ if !include_non_rust && !block_info.rust {
continue;
}
@@ -784,7 +837,23 @@ impl<'tcx> ExtraInfo<'tcx> {
ExtraInfo { def_id, sp, tcx }
}
- fn error_invalid_codeblock_attr(&self, msg: String, help: &'static str) {
+ fn error_invalid_codeblock_attr(&self, msg: impl Into<DiagnosticMessage>) {
+ if let Some(def_id) = self.def_id.as_local() {
+ self.tcx.struct_span_lint_hir(
+ crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
+ self.tcx.hir().local_def_id_to_hir_id(def_id),
+ self.sp,
+ msg,
+ |l| l,
+ );
+ }
+ }
+
+ fn error_invalid_codeblock_attr_with_help(
+ &self,
+ msg: impl Into<DiagnosticMessage>,
+ help: impl Into<SubdiagnosticMessage>,
+ ) {
if let Some(def_id) = self.def_id.as_local() {
self.tcx.struct_span_lint_hir(
crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
@@ -799,7 +868,7 @@ impl<'tcx> ExtraInfo<'tcx> {
#[derive(Eq, PartialEq, Clone, Debug)]
pub(crate) struct LangString {
- original: String,
+ pub(crate) original: String,
pub(crate) should_panic: bool,
pub(crate) no_run: bool,
pub(crate) ignore: Ignore,
@@ -808,6 +877,8 @@ pub(crate) struct LangString {
pub(crate) compile_fail: bool,
pub(crate) error_codes: Vec<String>,
pub(crate) edition: Option<Edition>,
+ pub(crate) added_classes: Vec<String>,
+ pub(crate) unknown: Vec<String>,
}
#[derive(Eq, PartialEq, Clone, Debug)]
@@ -817,6 +888,317 @@ pub(crate) enum Ignore {
Some(Vec<String>),
}
+/// This is the parser for fenced codeblocks attributes. It implements the following eBNF:
+///
+/// ```eBNF
+/// lang-string = *(token-list / delimited-attribute-list / comment)
+///
+/// bareword = LEADINGCHAR *(CHAR)
+/// bareword-without-leading-char = CHAR *(CHAR)
+/// quoted-string = QUOTE *(NONQUOTE) QUOTE
+/// token = bareword / quoted-string
+/// token-without-leading-char = bareword-without-leading-char / quoted-string
+/// sep = COMMA/WS *(COMMA/WS)
+/// attribute = (DOT token)/(token EQUAL token-without-leading-char)
+/// attribute-list = [sep] attribute *(sep attribute) [sep]
+/// delimited-attribute-list = OPEN-CURLY-BRACKET attribute-list CLOSE-CURLY-BRACKET
+/// token-list = [sep] token *(sep token) [sep]
+/// comment = OPEN_PAREN *(all characters) CLOSE_PAREN
+///
+/// OPEN_PAREN = "("
+/// CLOSE_PARENT = ")"
+/// OPEN-CURLY-BRACKET = "{"
+/// CLOSE-CURLY-BRACKET = "}"
+/// LEADINGCHAR = ALPHA | DIGIT | "_" | "-" | ":"
+/// ; All ASCII punctuation except comma, quote, equals, backslash, grave (backquote) and braces.
+/// ; Comma is used to separate language tokens, so it can't be used in one.
+/// ; Quote is used to allow otherwise-disallowed characters in language tokens.
+/// ; Equals is used to make key=value pairs in attribute blocks.
+/// ; Backslash and grave are special Markdown characters.
+/// ; Braces are used to start an attribute block.
+/// CHAR = ALPHA | DIGIT | "_" | "-" | ":" | "." | "!" | "#" | "$" | "%" | "&" | "*" | "+" | "/" |
+/// ";" | "<" | ">" | "?" | "@" | "^" | "|" | "~"
+/// NONQUOTE = %x09 / %x20 / %x21 / %x23-7E ; TAB / SPACE / all printable characters except `"`
+/// COMMA = ","
+/// DOT = "."
+/// EQUAL = "="
+///
+/// ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
+/// DIGIT = %x30-39
+/// WS = %x09 / " "
+/// ```
+pub(crate) struct TagIterator<'a, 'tcx> {
+ inner: Peekable<CharIndices<'a>>,
+ data: &'a str,
+ is_in_attribute_block: bool,
+ extra: Option<&'a ExtraInfo<'tcx>>,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub(crate) enum LangStringToken<'a> {
+ LangToken(&'a str),
+ ClassAttribute(&'a str),
+ KeyValueAttribute(&'a str, &'a str),
+}
+
+fn is_leading_char(c: char) -> bool {
+ c == '_' || c == '-' || c == ':' || c.is_ascii_alphabetic() || c.is_ascii_digit()
+}
+fn is_bareword_char(c: char) -> bool {
+ is_leading_char(c) || ".!#$%&*+/;<>?@^|~".contains(c)
+}
+fn is_separator(c: char) -> bool {
+ c == ' ' || c == ',' || c == '\t'
+}
+
+struct Indices {
+ start: usize,
+ end: usize,
+}
+
+impl<'a, 'tcx> TagIterator<'a, 'tcx> {
+ pub(crate) fn new(data: &'a str, extra: Option<&'a ExtraInfo<'tcx>>) -> Self {
+ Self { inner: data.char_indices().peekable(), data, is_in_attribute_block: false, extra }
+ }
+
+ fn emit_error(&self, err: impl Into<DiagnosticMessage>) {
+ if let Some(extra) = self.extra {
+ extra.error_invalid_codeblock_attr(err);
+ }
+ }
+
+ fn skip_separators(&mut self) -> Option<usize> {
+ while let Some((pos, c)) = self.inner.peek() {
+ if !is_separator(*c) {
+ return Some(*pos);
+ }
+ self.inner.next();
+ }
+ None
+ }
+
+ fn parse_string(&mut self, start: usize) -> Option<Indices> {
+ while let Some((pos, c)) = self.inner.next() {
+ if c == '"' {
+ return Some(Indices { start: start + 1, end: pos });
+ }
+ }
+ self.emit_error("unclosed quote string `\"`");
+ None
+ }
+
+ fn parse_class(&mut self, start: usize) -> Option<LangStringToken<'a>> {
+ while let Some((pos, c)) = self.inner.peek().copied() {
+ if is_bareword_char(c) {
+ self.inner.next();
+ } else {
+ let class = &self.data[start + 1..pos];
+ if class.is_empty() {
+ self.emit_error(format!("unexpected `{c}` character after `.`"));
+ return None;
+ } else if self.check_after_token() {
+ return Some(LangStringToken::ClassAttribute(class));
+ } else {
+ return None;
+ }
+ }
+ }
+ let class = &self.data[start + 1..];
+ if class.is_empty() {
+ self.emit_error("missing character after `.`");
+ None
+ } else if self.check_after_token() {
+ Some(LangStringToken::ClassAttribute(class))
+ } else {
+ None
+ }
+ }
+
+ fn parse_token(&mut self, start: usize) -> Option<Indices> {
+ while let Some((pos, c)) = self.inner.peek() {
+ if !is_bareword_char(*c) {
+ return Some(Indices { start, end: *pos });
+ }
+ self.inner.next();
+ }
+ self.emit_error("unexpected end");
+ None
+ }
+
+ fn parse_key_value(&mut self, c: char, start: usize) -> Option<LangStringToken<'a>> {
+ let key_indices =
+ if c == '"' { self.parse_string(start)? } else { self.parse_token(start)? };
+ if key_indices.start == key_indices.end {
+ self.emit_error("unexpected empty string as key");
+ return None;
+ }
+
+ if let Some((_, c)) = self.inner.next() {
+ if c != '=' {
+ self.emit_error(format!("expected `=`, found `{}`", c));
+ return None;
+ }
+ } else {
+ self.emit_error("unexpected end");
+ return None;
+ }
+ let value_indices = match self.inner.next() {
+ Some((pos, '"')) => self.parse_string(pos)?,
+ Some((pos, c)) if is_bareword_char(c) => self.parse_token(pos)?,
+ Some((_, c)) => {
+ self.emit_error(format!("unexpected `{c}` character after `=`"));
+ return None;
+ }
+ None => {
+ self.emit_error("expected value after `=`");
+ return None;
+ }
+ };
+ if value_indices.start == value_indices.end {
+ self.emit_error("unexpected empty string as value");
+ None
+ } else if self.check_after_token() {
+ Some(LangStringToken::KeyValueAttribute(
+ &self.data[key_indices.start..key_indices.end],
+ &self.data[value_indices.start..value_indices.end],
+ ))
+ } else {
+ None
+ }
+ }
+
+ /// Returns `false` if an error was emitted.
+ fn check_after_token(&mut self) -> bool {
+ if let Some((_, c)) = self.inner.peek().copied() {
+ if c == '}' || is_separator(c) || c == '(' {
+ true
+ } else {
+ self.emit_error(format!("unexpected `{c}` character"));
+ false
+ }
+ } else {
+ // The error will be caught on the next iteration.
+ true
+ }
+ }
+
+ fn parse_in_attribute_block(&mut self) -> Option<LangStringToken<'a>> {
+ if let Some((pos, c)) = self.inner.next() {
+ if c == '}' {
+ self.is_in_attribute_block = false;
+ return self.next();
+ } else if c == '.' {
+ return self.parse_class(pos);
+ } else if c == '"' || is_leading_char(c) {
+ return self.parse_key_value(c, pos);
+ } else {
+ self.emit_error(format!("unexpected character `{c}`"));
+ return None;
+ }
+ }
+ self.emit_error("unclosed attribute block (`{}`): missing `}` at the end");
+ None
+ }
+
+ /// Returns `false` if an error was emitted.
+ fn skip_paren_block(&mut self) -> bool {
+ while let Some((_, c)) = self.inner.next() {
+ if c == ')' {
+ return true;
+ }
+ }
+ self.emit_error("unclosed comment: missing `)` at the end");
+ false
+ }
+
+ fn parse_outside_attribute_block(&mut self, start: usize) -> Option<LangStringToken<'a>> {
+ while let Some((pos, c)) = self.inner.next() {
+ if c == '"' {
+ if pos != start {
+ self.emit_error("expected ` `, `{` or `,` found `\"`");
+ return None;
+ }
+ let indices = self.parse_string(pos)?;
+ if let Some((_, c)) = self.inner.peek().copied() &&
+ c != '{' &&
+ !is_separator(c) &&
+ c != '('
+ {
+ self.emit_error(format!("expected ` `, `{{` or `,` after `\"`, found `{c}`"));
+ return None;
+ }
+ return Some(LangStringToken::LangToken(&self.data[indices.start..indices.end]));
+ } else if c == '{' {
+ self.is_in_attribute_block = true;
+ return self.next();
+ } else if is_separator(c) {
+ if pos != start {
+ return Some(LangStringToken::LangToken(&self.data[start..pos]));
+ }
+ return self.next();
+ } else if c == '(' {
+ if !self.skip_paren_block() {
+ return None;
+ }
+ if pos != start {
+ return Some(LangStringToken::LangToken(&self.data[start..pos]));
+ }
+ return self.next();
+ } else if pos == start && is_leading_char(c) {
+ continue;
+ } else if pos != start && is_bareword_char(c) {
+ continue;
+ } else {
+ self.emit_error(format!("unexpected character `{c}`"));
+ return None;
+ }
+ }
+ let token = &self.data[start..];
+ if token.is_empty() { None } else { Some(LangStringToken::LangToken(&self.data[start..])) }
+ }
+}
+
+impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> {
+ type Item = LangStringToken<'a>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let Some(start) = self.skip_separators() else {
+ if self.is_in_attribute_block {
+ self.emit_error("unclosed attribute block (`{}`): missing `}` at the end");
+ }
+ return None;
+ };
+ if self.is_in_attribute_block {
+ self.parse_in_attribute_block()
+ } else {
+ self.parse_outside_attribute_block(start)
+ }
+ }
+}
+
+fn tokens(string: &str) -> impl Iterator<Item = LangStringToken<'_>> {
+ // Pandoc, which Rust once used for generating documentation,
+ // expects lang strings to be surrounded by `{}` and for each token
+ // to be proceeded by a `.`. Since some of these lang strings are still
+ // loose in the wild, we strip a pair of surrounding `{}` from the lang
+ // string and a leading `.` from each token.
+
+ let string = string.trim();
+
+ let first = string.chars().next();
+ let last = string.chars().last();
+
+ let string =
+ if first == Some('{') && last == Some('}') { &string[1..string.len() - 1] } else { string };
+
+ string
+ .split(|c| c == ',' || c == ' ' || c == '\t')
+ .map(str::trim)
+ .map(|token| token.strip_prefix('.').unwrap_or(token))
+ .filter(|token| !token.is_empty())
+ .map(|token| LangStringToken::LangToken(token))
+}
+
impl Default for LangString {
fn default() -> Self {
Self {
@@ -829,6 +1211,8 @@ impl Default for LangString {
compile_fail: false,
error_codes: Vec::new(),
edition: None,
+ added_classes: Vec::new(),
+ unknown: Vec::new(),
}
}
}
@@ -838,33 +1222,15 @@ impl LangString {
string: &str,
allow_error_code_check: ErrorCodes,
enable_per_target_ignores: bool,
- ) -> LangString {
- Self::parse(string, allow_error_code_check, enable_per_target_ignores, None)
- }
-
- fn tokens(string: &str) -> impl Iterator<Item = &str> {
- // Pandoc, which Rust once used for generating documentation,
- // expects lang strings to be surrounded by `{}` and for each token
- // to be proceeded by a `.`. Since some of these lang strings are still
- // loose in the wild, we strip a pair of surrounding `{}` from the lang
- // string and a leading `.` from each token.
-
- let string = string.trim();
-
- let first = string.chars().next();
- let last = string.chars().last();
-
- let string = if first == Some('{') && last == Some('}') {
- &string[1..string.len() - 1]
- } else {
- string
- };
-
- string
- .split(|c| c == ',' || c == ' ' || c == '\t')
- .map(str::trim)
- .map(|token| token.strip_prefix('.').unwrap_or(token))
- .filter(|token| !token.is_empty())
+ custom_code_classes_in_docs: bool,
+ ) -> Self {
+ Self::parse(
+ string,
+ allow_error_code_check,
+ enable_per_target_ignores,
+ None,
+ custom_code_classes_in_docs,
+ )
}
fn parse(
@@ -872,102 +1238,141 @@ impl LangString {
allow_error_code_check: ErrorCodes,
enable_per_target_ignores: bool,
extra: Option<&ExtraInfo<'_>>,
- ) -> LangString {
+ custom_code_classes_in_docs: bool,
+ ) -> Self {
let allow_error_code_check = allow_error_code_check.as_bool();
let mut seen_rust_tags = false;
let mut seen_other_tags = false;
+ let mut seen_custom_tag = false;
let mut data = LangString::default();
let mut ignores = vec![];
data.original = string.to_owned();
- for token in Self::tokens(string) {
- match token {
- "should_panic" => {
- data.should_panic = true;
- seen_rust_tags = !seen_other_tags;
- }
- "no_run" => {
- data.no_run = true;
- seen_rust_tags = !seen_other_tags;
- }
- "ignore" => {
- data.ignore = Ignore::All;
- seen_rust_tags = !seen_other_tags;
- }
- x if x.starts_with("ignore-") => {
- if enable_per_target_ignores {
- ignores.push(x.trim_start_matches("ignore-").to_owned());
+ let mut call = |tokens: &mut dyn Iterator<Item = LangStringToken<'_>>| {
+ for token in tokens {
+ match token {
+ LangStringToken::LangToken("should_panic") => {
+ data.should_panic = true;
seen_rust_tags = !seen_other_tags;
}
- }
- "rust" => {
- data.rust = true;
- seen_rust_tags = true;
- }
- "test_harness" => {
- data.test_harness = true;
- seen_rust_tags = !seen_other_tags || seen_rust_tags;
- }
- "compile_fail" => {
- data.compile_fail = true;
- seen_rust_tags = !seen_other_tags || seen_rust_tags;
- data.no_run = true;
- }
- x if x.starts_with("edition") => {
- data.edition = x[7..].parse::<Edition>().ok();
- }
- x if allow_error_code_check && x.starts_with('E') && x.len() == 5 => {
- if x[1..].parse::<u32>().is_ok() {
- data.error_codes.push(x.to_owned());
+ LangStringToken::LangToken("no_run") => {
+ data.no_run = true;
+ seen_rust_tags = !seen_other_tags;
+ }
+ LangStringToken::LangToken("ignore") => {
+ data.ignore = Ignore::All;
+ seen_rust_tags = !seen_other_tags;
+ }
+ LangStringToken::LangToken(x) if x.starts_with("ignore-") => {
+ if enable_per_target_ignores {
+ ignores.push(x.trim_start_matches("ignore-").to_owned());
+ seen_rust_tags = !seen_other_tags;
+ }
+ }
+ LangStringToken::LangToken("rust") => {
+ data.rust = true;
+ seen_rust_tags = true;
+ }
+ LangStringToken::LangToken("custom") => {
+ if custom_code_classes_in_docs {
+ seen_custom_tag = true;
+ } else {
+ seen_other_tags = true;
+ }
+ }
+ LangStringToken::LangToken("test_harness") => {
+ data.test_harness = true;
seen_rust_tags = !seen_other_tags || seen_rust_tags;
- } else {
- seen_other_tags = true;
}
- }
- x if extra.is_some() => {
- let s = x.to_lowercase();
- if let Some((flag, help)) = if s == "compile-fail"
- || s == "compile_fail"
- || s == "compilefail"
+ LangStringToken::LangToken("compile_fail") => {
+ data.compile_fail = true;
+ seen_rust_tags = !seen_other_tags || seen_rust_tags;
+ data.no_run = true;
+ }
+ LangStringToken::LangToken(x) if x.starts_with("edition") => {
+ data.edition = x[7..].parse::<Edition>().ok();
+ }
+ LangStringToken::LangToken(x)
+ if allow_error_code_check && x.starts_with('E') && x.len() == 5 =>
{
- Some((
- "compile_fail",
- "the code block will either not be tested if not marked as a rust one \
- or won't fail if it compiles successfully",
- ))
- } else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" {
- Some((
- "should_panic",
- "the code block will either not be tested if not marked as a rust one \
- or won't fail if it doesn't panic when running",
- ))
- } else if s == "no-run" || s == "no_run" || s == "norun" {
- Some((
- "no_run",
- "the code block will either not be tested if not marked as a rust one \
- or will be run (which you might not want)",
- ))
- } else if s == "test-harness" || s == "test_harness" || s == "testharness" {
- Some((
- "test_harness",
- "the code block will either not be tested if not marked as a rust one \
- or the code will be wrapped inside a main function",
- ))
- } else {
- None
- } {
- if let Some(extra) = extra {
- extra.error_invalid_codeblock_attr(
- format!("unknown attribute `{x}`. Did you mean `{flag}`?"),
- help,
- );
+ if x[1..].parse::<u32>().is_ok() {
+ data.error_codes.push(x.to_owned());
+ seen_rust_tags = !seen_other_tags || seen_rust_tags;
+ } else {
+ seen_other_tags = true;
}
}
- seen_other_tags = true;
+ LangStringToken::LangToken(x) if extra.is_some() => {
+ let s = x.to_lowercase();
+ if let Some((flag, help)) = if s == "compile-fail"
+ || s == "compile_fail"
+ || s == "compilefail"
+ {
+ Some((
+ "compile_fail",
+ "the code block will either not be tested if not marked as a rust one \
+ or won't fail if it compiles successfully",
+ ))
+ } else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" {
+ Some((
+ "should_panic",
+ "the code block will either not be tested if not marked as a rust one \
+ or won't fail if it doesn't panic when running",
+ ))
+ } else if s == "no-run" || s == "no_run" || s == "norun" {
+ Some((
+ "no_run",
+ "the code block will either not be tested if not marked as a rust one \
+ or will be run (which you might not want)",
+ ))
+ } else if s == "test-harness" || s == "test_harness" || s == "testharness" {
+ Some((
+ "test_harness",
+ "the code block will either not be tested if not marked as a rust one \
+ or the code will be wrapped inside a main function",
+ ))
+ } else {
+ None
+ } {
+ if let Some(extra) = extra {
+ extra.error_invalid_codeblock_attr_with_help(
+ format!("unknown attribute `{x}`. Did you mean `{flag}`?"),
+ help,
+ );
+ }
+ }
+ seen_other_tags = true;
+ data.unknown.push(x.to_owned());
+ }
+ LangStringToken::LangToken(x) => {
+ seen_other_tags = true;
+ data.unknown.push(x.to_owned());
+ }
+ LangStringToken::KeyValueAttribute(key, value) => {
+ if custom_code_classes_in_docs {
+ if key == "class" {
+ data.added_classes.push(value.to_owned());
+ } else if let Some(extra) = extra {
+ extra.error_invalid_codeblock_attr(format!(
+ "unsupported attribute `{key}`"
+ ));
+ }
+ } else {
+ seen_other_tags = true;
+ }
+ }
+ LangStringToken::ClassAttribute(class) => {
+ data.added_classes.push(class.to_owned());
+ }
}
- _ => seen_other_tags = true,
}
+ };
+
+ if custom_code_classes_in_docs {
+ call(&mut TagIterator::new(string, extra).into_iter())
+ } else {
+ call(&mut tokens(string))
}
// ignore-foo overrides ignore
@@ -975,7 +1380,7 @@ impl LangString {
data.ignore = Ignore::Some(ignores);
}
- data.rust &= !seen_other_tags || seen_rust_tags;
+ data.rust &= !seen_custom_tag && (!seen_other_tags || seen_rust_tags);
data
}
@@ -991,6 +1396,7 @@ impl Markdown<'_> {
edition,
playground,
heading_offset,
+ custom_code_classes_in_docs,
} = self;
// This is actually common enough to special-case
@@ -1013,7 +1419,7 @@ impl Markdown<'_> {
let p = Footnotes::new(p);
let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
let p = TableWrapper::new(p);
- let p = CodeBlocks::new(p, codes, edition, playground);
+ let p = CodeBlocks::new(p, codes, edition, playground, custom_code_classes_in_docs);
html::push_html(&mut s, p);
s
@@ -1022,7 +1428,14 @@ impl Markdown<'_> {
impl MarkdownWithToc<'_> {
pub(crate) fn into_string(self) -> String {
- let MarkdownWithToc { content: md, ids, error_codes: codes, edition, playground } = self;
+ let MarkdownWithToc {
+ content: md,
+ ids,
+ error_codes: codes,
+ edition,
+ playground,
+ custom_code_classes_in_docs,
+ } = self;
let p = Parser::new_ext(md, main_body_opts()).into_offset_iter();
@@ -1034,7 +1447,7 @@ impl MarkdownWithToc<'_> {
let p = HeadingLinks::new(p, Some(&mut toc), ids, HeadingOffset::H1);
let p = Footnotes::new(p);
let p = TableWrapper::new(p.map(|(ev, _)| ev));
- let p = CodeBlocks::new(p, codes, edition, playground);
+ let p = CodeBlocks::new(p, codes, edition, playground, custom_code_classes_in_docs);
html::push_html(&mut s, p);
}
@@ -1475,7 +1888,11 @@ pub(crate) struct RustCodeBlock {
/// Returns a range of bytes for each code block in the markdown that is tagged as `rust` or
/// untagged (and assumed to be rust).
-pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<RustCodeBlock> {
+pub(crate) fn rust_code_blocks(
+ md: &str,
+ extra_info: &ExtraInfo<'_>,
+ custom_code_classes_in_docs: bool,
+) -> Vec<RustCodeBlock> {
let mut code_blocks = vec![];
if md.is_empty() {
@@ -1492,7 +1909,13 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<Rust
let lang_string = if syntax.is_empty() {
Default::default()
} else {
- LangString::parse(&*syntax, ErrorCodes::Yes, false, Some(extra_info))
+ LangString::parse(
+ &*syntax,
+ ErrorCodes::Yes,
+ false,
+ Some(extra_info),
+ custom_code_classes_in_docs,
+ )
};
if !lang_string.rust {
continue;
@@ -1574,7 +1997,6 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
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);
map.insert("themeStyle".into(), 1);
map.insert("settings-menu".into(), 1);
map.insert("help-button".into(), 1);
@@ -1610,6 +2032,7 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
map.insert("blanket-implementations-list".into(), 1);
map.insert("deref-methods".into(), 1);
map.insert("layout".into(), 1);
+ map.insert("aliased-type".into(), 1);
map
}
diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs
index db8504d15..5eba1d060 100644
--- a/src/librustdoc/html/markdown/tests.rs
+++ b/src/librustdoc/html/markdown/tests.rs
@@ -1,5 +1,8 @@
use super::{find_testable_code, plain_text_summary, short_markdown_summary};
-use super::{ErrorCodes, HeadingOffset, IdMap, Ignore, LangString, Markdown, MarkdownItemInfo};
+use super::{
+ ErrorCodes, HeadingOffset, IdMap, Ignore, LangString, LangStringToken, Markdown,
+ MarkdownItemInfo, TagIterator,
+};
use rustc_span::edition::{Edition, DEFAULT_EDITION};
#[test]
@@ -46,15 +49,37 @@ fn test_unique_id() {
fn test_lang_string_parse() {
fn t(lg: LangString) {
let s = &lg.original;
- assert_eq!(LangString::parse(s, ErrorCodes::Yes, true, None), lg)
+ assert_eq!(LangString::parse(s, ErrorCodes::Yes, true, None, true), lg)
}
t(Default::default());
t(LangString { original: "rust".into(), ..Default::default() });
- t(LangString { original: ".rust".into(), ..Default::default() });
- t(LangString { original: "{rust}".into(), ..Default::default() });
- t(LangString { original: "{.rust}".into(), ..Default::default() });
- t(LangString { original: "sh".into(), rust: false, ..Default::default() });
+ t(LangString {
+ original: "rusta".into(),
+ rust: false,
+ unknown: vec!["rusta".into()],
+ ..Default::default()
+ });
+ // error
+ t(LangString { original: "{rust}".into(), rust: true, ..Default::default() });
+ t(LangString {
+ original: "{.rust}".into(),
+ rust: true,
+ added_classes: vec!["rust".into()],
+ ..Default::default()
+ });
+ t(LangString {
+ original: "custom,{.rust}".into(),
+ rust: false,
+ added_classes: vec!["rust".into()],
+ ..Default::default()
+ });
+ t(LangString {
+ original: "sh".into(),
+ rust: false,
+ unknown: vec!["sh".into()],
+ ..Default::default()
+ });
t(LangString { original: "ignore".into(), ignore: Ignore::All, ..Default::default() });
t(LangString {
original: "ignore-foo".into(),
@@ -70,41 +95,56 @@ fn test_lang_string_parse() {
compile_fail: true,
..Default::default()
});
- t(LangString { original: "no_run,example".into(), no_run: true, ..Default::default() });
+ t(LangString {
+ original: "no_run,example".into(),
+ no_run: true,
+ unknown: vec!["example".into()],
+ ..Default::default()
+ });
t(LangString {
original: "sh,should_panic".into(),
should_panic: true,
rust: false,
+ unknown: vec!["sh".into()],
+ ..Default::default()
+ });
+ t(LangString {
+ original: "example,rust".into(),
+ unknown: vec!["example".into()],
..Default::default()
});
- t(LangString { original: "example,rust".into(), ..Default::default() });
t(LangString {
- original: "test_harness,.rust".into(),
+ original: "test_harness,rusta".into(),
test_harness: true,
+ unknown: vec!["rusta".into()],
..Default::default()
});
t(LangString {
original: "text, no_run".into(),
no_run: true,
rust: false,
+ unknown: vec!["text".into()],
..Default::default()
});
t(LangString {
original: "text,no_run".into(),
no_run: true,
rust: false,
+ unknown: vec!["text".into()],
..Default::default()
});
t(LangString {
original: "text,no_run, ".into(),
no_run: true,
rust: false,
+ unknown: vec!["text".into()],
..Default::default()
});
t(LangString {
original: "text,no_run,".into(),
no_run: true,
rust: false,
+ unknown: vec!["text".into()],
..Default::default()
});
t(LangString {
@@ -117,29 +157,140 @@ fn test_lang_string_parse() {
edition: Some(Edition::Edition2018),
..Default::default()
});
+ t(LangString {
+ original: "{class=test}".into(),
+ added_classes: vec!["test".into()],
+ rust: true,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "custom,{class=test}".into(),
+ added_classes: vec!["test".into()],
+ rust: false,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "{.test}".into(),
+ added_classes: vec!["test".into()],
+ rust: true,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "custom,{.test}".into(),
+ added_classes: vec!["test".into()],
+ rust: false,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "rust,{class=test,.test2}".into(),
+ added_classes: vec!["test".into(), "test2".into()],
+ rust: true,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "{class=test:with:colon .test1}".into(),
+ added_classes: vec!["test:with:colon".into(), "test1".into()],
+ rust: true,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "custom,{class=test:with:colon .test1}".into(),
+ added_classes: vec!["test:with:colon".into(), "test1".into()],
+ rust: false,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "{class=first,class=second}".into(),
+ added_classes: vec!["first".into(), "second".into()],
+ rust: true,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "custom,{class=first,class=second}".into(),
+ added_classes: vec!["first".into(), "second".into()],
+ rust: false,
+ ..Default::default()
+ });
+ t(LangString {
+ original: "{class=first,.second},unknown".into(),
+ added_classes: vec!["first".into(), "second".into()],
+ rust: false,
+ unknown: vec!["unknown".into()],
+ ..Default::default()
+ });
+ t(LangString {
+ original: "{class=first .second} unknown".into(),
+ added_classes: vec!["first".into(), "second".into()],
+ rust: false,
+ unknown: vec!["unknown".into()],
+ ..Default::default()
+ });
+ // error
+ t(LangString {
+ original: "{.first.second}".into(),
+ rust: true,
+ added_classes: vec!["first.second".into()],
+ ..Default::default()
+ });
+ // error
+ t(LangString { original: "{class=first=second}".into(), rust: true, ..Default::default() });
+ // error
+ t(LangString {
+ original: "{class=first.second}".into(),
+ rust: true,
+ added_classes: vec!["first.second".into()],
+ ..Default::default()
+ });
+ // error
+ t(LangString {
+ original: "{class=.first}".into(),
+ added_classes: vec![".first".into()],
+ rust: true,
+ ..Default::default()
+ });
+ t(LangString {
+ original: r#"{class="first"}"#.into(),
+ added_classes: vec!["first".into()],
+ rust: true,
+ ..Default::default()
+ });
+ t(LangString {
+ original: r#"custom,{class="first"}"#.into(),
+ added_classes: vec!["first".into()],
+ rust: false,
+ ..Default::default()
+ });
+ // error
+ t(LangString { original: r#"{class=f"irst"}"#.into(), rust: true, ..Default::default() });
}
#[test]
fn test_lang_string_tokenizer() {
- fn case(lang_string: &str, want: &[&str]) {
- let have = LangString::tokens(lang_string).collect::<Vec<&str>>();
+ fn case(lang_string: &str, want: &[LangStringToken<'_>]) {
+ let have = TagIterator::new(lang_string, None).collect::<Vec<_>>();
assert_eq!(have, want, "Unexpected lang string split for `{}`", lang_string);
}
case("", &[]);
- case("foo", &["foo"]);
- case("foo,bar", &["foo", "bar"]);
- case(".foo,.bar", &["foo", "bar"]);
- case("{.foo,.bar}", &["foo", "bar"]);
- case(" {.foo,.bar} ", &["foo", "bar"]);
- case("foo bar", &["foo", "bar"]);
- case("foo\tbar", &["foo", "bar"]);
- case("foo\t, bar", &["foo", "bar"]);
- case(" foo , bar ", &["foo", "bar"]);
- case(",,foo,,bar,,", &["foo", "bar"]);
- case("foo=bar", &["foo=bar"]);
- case("a-b-c", &["a-b-c"]);
- case("a_b_c", &["a_b_c"]);
+ case("foo", &[LangStringToken::LangToken("foo")]);
+ case("foo,bar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+ case(".foo,.bar", &[]);
+ case(
+ "{.foo,.bar}",
+ &[LangStringToken::ClassAttribute("foo"), LangStringToken::ClassAttribute("bar")],
+ );
+ case(
+ " {.foo,.bar} ",
+ &[LangStringToken::ClassAttribute("foo"), LangStringToken::ClassAttribute("bar")],
+ );
+ case("foo bar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+ case("foo\tbar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+ case("foo\t, bar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+ case(" foo , bar ", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+ case(",,foo,,bar,,", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]);
+ case("foo=bar", &[]);
+ case("a-b-c", &[LangStringToken::LangToken("a-b-c")]);
+ case("a_b_c", &[LangStringToken::LangToken("a_b_c")]);
}
#[test]
@@ -154,6 +305,7 @@ fn test_header() {
edition: DEFAULT_EDITION,
playground: &None,
heading_offset: HeadingOffset::H2,
+ custom_code_classes_in_docs: true,
}
.into_string();
assert_eq!(output, expect, "original: {}", input);
@@ -193,6 +345,7 @@ fn test_header_ids_multiple_blocks() {
edition: DEFAULT_EDITION,
playground: &None,
heading_offset: HeadingOffset::H2,
+ custom_code_classes_in_docs: true,
}
.into_string();
assert_eq!(output, expect, "original: {}", input);
@@ -297,7 +450,7 @@ fn test_find_testable_code_line() {
}
}
let mut lines = Vec::<usize>::new();
- find_testable_code(input, &mut lines, ErrorCodes::No, false, None);
+ find_testable_code(input, &mut lines, ErrorCodes::No, false, None, true);
assert_eq!(lines, expect);
}
@@ -322,6 +475,7 @@ fn test_ascii_with_prepending_hashtag() {
edition: DEFAULT_EDITION,
playground: &None,
heading_offset: HeadingOffset::H2,
+ custom_code_classes_in_docs: true,
}
.into_string();
assert_eq!(output, expect, "original: {}", input);
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index d7ff248a9..97714afaa 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -7,8 +7,10 @@ use std::sync::mpsc::{channel, Receiver};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE};
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
+use rustc_span::def_id::DefId;
use rustc_span::edition::Edition;
use rustc_span::source_map::FileName;
use rustc_span::{sym, Symbol};
@@ -22,13 +24,13 @@ use super::{
sidebar::{sidebar_module_like, Sidebar},
AllTypes, LinkFromSrc, StylePath,
};
-use crate::clean::{self, types::ExternalLocation, ExternalCrate};
+use crate::clean::{self, types::ExternalLocation, ExternalCrate, TypeAliasItem};
use crate::config::{ModuleSorting, RenderOptions};
use crate::docfs::{DocFS, PathError};
use crate::error::Error;
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
-use crate::formats::FormatRenderer;
+use crate::formats::{self, 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};
@@ -105,8 +107,8 @@ pub(crate) struct SharedContext<'tcx> {
pub(super) module_sorting: ModuleSorting,
/// Additional CSS files to be added to the generated docs.
pub(crate) style_files: Vec<StylePath>,
- /// Suffix to be added on resource files (if suffix is "-v2" then "light.css" becomes
- /// "light-v2.css").
+ /// Suffix to add on resource files (if suffix is "-v2" then "search-index.js" becomes
+ /// "search-index-v2.js").
pub(crate) resource_suffix: String,
/// Optional path string to be used to load static files on output pages. If not set, uses
/// combinations of `../` to reach the documentation root.
@@ -147,6 +149,53 @@ impl SharedContext<'_> {
pub(crate) fn edition(&self) -> Edition {
self.tcx.sess.edition()
}
+
+ /// Returns a list of impls on the given type, and, if it's a type alias,
+ /// other types that it aliases.
+ pub(crate) fn all_impls_for_item<'a>(
+ &'a self,
+ it: &clean::Item,
+ did: DefId,
+ ) -> Vec<&'a formats::Impl> {
+ let tcx = self.tcx;
+ let cache = &self.cache;
+ let mut saw_impls = FxHashSet::default();
+ let mut v: Vec<&formats::Impl> = cache
+ .impls
+ .get(&did)
+ .map(Vec::as_slice)
+ .unwrap_or(&[])
+ .iter()
+ .filter(|i| saw_impls.insert(i.def_id()))
+ .collect();
+ if let TypeAliasItem(ait) = &*it.kind &&
+ let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) &&
+ let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) &&
+ let Some(av) = cache.impls.get(&aliased_type_defid) &&
+ let Some(alias_def_id) = it.item_id.as_def_id()
+ {
+ // This branch of the compiler compares types structually, but does
+ // not check trait bounds. That's probably fine, since type aliases
+ // don't normally constrain on them anyway.
+ // https://github.com/rust-lang/rust/issues/21903
+ //
+ // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification.
+ // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress.
+ let aliased_ty = tcx.type_of(alias_def_id).skip_binder();
+ let reject_cx = DeepRejectCtxt {
+ treat_obligation_params: TreatParams::AsCandidateKey,
+ };
+ v.extend(av.iter().filter(|impl_| {
+ if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() {
+ reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder())
+ && saw_impls.insert(impl_def_id)
+ } else {
+ false
+ }
+ }));
+ }
+ v
+ }
}
impl<'tcx> Context<'tcx> {
@@ -463,6 +512,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
generate_link_to_definition,
call_locations,
no_emit_shared,
+ html_no_source,
..
} = options;
@@ -488,7 +538,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
scrape_examples_extension: !call_locations.is_empty(),
};
let mut issue_tracker_base_url = None;
- let mut include_sources = true;
+ let mut include_sources = !html_no_source;
// Crawl the crate attributes looking for attributes which control how we're
// going to emit HTML
@@ -664,21 +714,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
You need to enable JavaScript be able to update your settings.\
</section>\
</noscript>\
- <link rel=\"stylesheet\" \
- href=\"{static_root_path}{settings_css}\">\
- <script defer src=\"{static_root_path}{settings_js}\"></script>\
- <link rel=\"preload\" href=\"{static_root_path}{theme_light_css}\" \
- as=\"style\">\
- <link rel=\"preload\" href=\"{static_root_path}{theme_dark_css}\" \
- as=\"style\">\
- <link rel=\"preload\" href=\"{static_root_path}{theme_ayu_css}\" \
- as=\"style\">",
+ <script defer src=\"{static_root_path}{settings_js}\"></script>",
static_root_path = page.get_static_root_path(),
- settings_css = static_files::STATIC_FILES.settings_css,
settings_js = static_files::STATIC_FILES.settings_js,
- theme_light_css = static_files::STATIC_FILES.theme_light_css,
- theme_dark_css = static_files::STATIC_FILES.theme_dark_css,
- theme_ayu_css = static_files::STATIC_FILES.theme_ayu_css,
);
// Pre-load all theme CSS files, so that switching feels seamless.
//
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index ac9c180a6..3e671a64b 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -101,7 +101,7 @@ pub(crate) struct IndexItem {
pub(crate) path: String,
pub(crate) desc: String,
pub(crate) parent: Option<DefId>,
- pub(crate) parent_idx: Option<usize>,
+ pub(crate) parent_idx: Option<isize>,
pub(crate) search_type: Option<IndexItemFunctionType>,
pub(crate) aliases: Box<[Symbol]>,
pub(crate) deprecation: Option<Deprecation>,
@@ -122,7 +122,10 @@ impl Serialize for RenderType {
let id = match &self.id {
// 0 is a sentinel, everything else is one-indexed
None => 0,
- Some(RenderTypeId::Index(idx)) => idx + 1,
+ // concrete type
+ Some(RenderTypeId::Index(idx)) if *idx >= 0 => idx + 1,
+ // generic type parameter
+ Some(RenderTypeId::Index(idx)) => *idx,
_ => panic!("must convert render types to indexes before serializing"),
};
if let Some(generics) = &self.generics {
@@ -140,7 +143,7 @@ impl Serialize for RenderType {
pub(crate) enum RenderTypeId {
DefId(DefId),
Primitive(clean::PrimitiveType),
- Index(usize),
+ Index(isize),
}
/// Full type of functions/methods in the search index.
@@ -148,6 +151,7 @@ pub(crate) enum RenderTypeId {
pub(crate) struct IndexItemFunctionType {
inputs: Vec<RenderType>,
output: Vec<RenderType>,
+ where_clause: Vec<Vec<RenderType>>,
}
impl Serialize for IndexItemFunctionType {
@@ -170,10 +174,17 @@ impl Serialize for IndexItemFunctionType {
_ => seq.serialize_element(&self.inputs)?,
}
match &self.output[..] {
- [] => {}
+ [] if self.where_clause.is_empty() => {}
[one] if one.generics.is_none() => seq.serialize_element(one)?,
_ => seq.serialize_element(&self.output)?,
}
+ for constraint in &self.where_clause {
+ if let [one] = &constraint[..] && one.generics.is_none() {
+ seq.serialize_element(one)?;
+ } else {
+ seq.serialize_element(constraint)?;
+ }
+ }
seq.end()
}
}
@@ -235,7 +246,7 @@ struct AllTypes {
traits: FxHashSet<ItemEntry>,
macros: FxHashSet<ItemEntry>,
functions: FxHashSet<ItemEntry>,
- typedefs: FxHashSet<ItemEntry>,
+ type_aliases: FxHashSet<ItemEntry>,
opaque_tys: FxHashSet<ItemEntry>,
statics: FxHashSet<ItemEntry>,
constants: FxHashSet<ItemEntry>,
@@ -255,7 +266,7 @@ impl AllTypes {
traits: new_set(100),
macros: new_set(100),
functions: new_set(100),
- typedefs: new_set(100),
+ type_aliases: new_set(100),
opaque_tys: new_set(100),
statics: new_set(100),
constants: new_set(100),
@@ -279,7 +290,7 @@ impl AllTypes {
ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)),
ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)),
ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)),
- ItemType::Typedef => self.typedefs.insert(ItemEntry::new(new_url, name)),
+ ItemType::TypeAlias => self.type_aliases.insert(ItemEntry::new(new_url, name)),
ItemType::OpaqueTy => self.opaque_tys.insert(ItemEntry::new(new_url, name)),
ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)),
ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)),
@@ -317,8 +328,8 @@ impl AllTypes {
if !self.functions.is_empty() {
sections.insert(ItemSection::Functions);
}
- if !self.typedefs.is_empty() {
- sections.insert(ItemSection::TypeDefinitions);
+ if !self.type_aliases.is_empty() {
+ sections.insert(ItemSection::TypeAliases);
}
if !self.opaque_tys.is_empty() {
sections.insert(ItemSection::OpaqueTypes);
@@ -374,7 +385,7 @@ impl AllTypes {
print_entries(f, &self.attribute_macros, ItemSection::AttributeMacros);
print_entries(f, &self.derive_macros, ItemSection::DeriveMacros);
print_entries(f, &self.functions, ItemSection::Functions);
- print_entries(f, &self.typedefs, ItemSection::TypeDefinitions);
+ print_entries(f, &self.type_aliases, ItemSection::TypeAliases);
print_entries(f, &self.trait_aliases, ItemSection::TraitAliases);
print_entries(f, &self.opaque_tys, ItemSection::OpaqueTypes);
print_entries(f, &self.statics, ItemSection::Statics);
@@ -403,7 +414,8 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
error_codes: shared.codes,
edition: shared.edition(),
playground: &shared.playground,
- heading_offset: HeadingOffset::H1
+ heading_offset: HeadingOffset::H1,
+ custom_code_classes_in_docs: false,
}
.into_string()
)
@@ -437,6 +449,7 @@ fn render_markdown<'a, 'cx: 'a>(
heading_offset: HeadingOffset,
) -> impl fmt::Display + 'a + Captures<'cx> {
display_fn(move |f| {
+ let custom_code_classes_in_docs = cx.tcx().features().custom_code_classes_in_docs;
write!(
f,
"<div class=\"docblock\">{}</div>",
@@ -448,6 +461,7 @@ fn render_markdown<'a, 'cx: 'a>(
edition: cx.shared.edition(),
playground: &cx.shared.playground,
heading_offset,
+ custom_code_classes_in_docs,
}
.into_string()
)
@@ -1117,13 +1131,13 @@ pub(crate) fn render_all_impls(
fn render_assoc_items<'a, 'cx: 'a>(
cx: &'a mut Context<'cx>,
containing_item: &'a clean::Item,
- it: DefId,
+ did: DefId,
what: AssocItemRender<'a>,
) -> impl fmt::Display + 'a + Captures<'cx> {
let mut derefs = DefIdSet::default();
- derefs.insert(it);
+ derefs.insert(did);
display_fn(move |f| {
- render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
+ render_assoc_items_inner(f, cx, containing_item, did, what, &mut derefs);
Ok(())
})
}
@@ -1132,15 +1146,17 @@ fn render_assoc_items_inner(
mut w: &mut dyn fmt::Write,
cx: &mut Context<'_>,
containing_item: &clean::Item,
- it: DefId,
+ did: DefId,
what: AssocItemRender<'_>,
derefs: &mut DefIdSet,
) {
info!("Documenting associated items of {:?}", containing_item.name);
let shared = Rc::clone(&cx.shared);
- let cache = &shared.cache;
- let Some(v) = cache.impls.get(&it) else { return };
- let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
+ let v = shared.all_impls_for_item(containing_item, did);
+ let v = v.as_slice();
+ let (non_trait, traits): (Vec<&Impl>, _) =
+ v.iter().partition(|i| i.inner_impl().trait_.is_none());
+ let mut saw_impls = FxHashSet::default();
if !non_trait.is_empty() {
let mut tmp_buf = Buffer::html();
let (render_mode, id, class_html) = match what {
@@ -1169,6 +1185,9 @@ fn render_assoc_items_inner(
};
let mut impls_buf = Buffer::html();
for i in &non_trait {
+ if !saw_impls.insert(i.def_id()) {
+ continue;
+ }
render_impl(
&mut impls_buf,
cx,
@@ -1214,8 +1233,10 @@ fn render_assoc_items_inner(
let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
- let (blanket_impl, concrete): (Vec<&Impl>, _) =
- concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
+ let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete
+ .into_iter()
+ .filter(|t| saw_impls.insert(t.def_id()))
+ .partition(|t| t.inner_impl().kind.is_blanket());
render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
}
@@ -1237,7 +1258,7 @@ fn render_deref_methods(
.iter()
.find_map(|item| match *item.kind {
clean::AssocTypeItem(box ref t, _) => Some(match *t {
- clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
+ clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
_ => (&t.type_, &t.type_),
}),
_ => None,
@@ -1771,6 +1792,7 @@ fn render_impl(
</div>",
);
}
+ let custom_code_classes_in_docs = cx.tcx().features().custom_code_classes_in_docs;
write!(
w,
"<div class=\"docblock\">{}</div>",
@@ -1781,7 +1803,8 @@ fn render_impl(
error_codes: cx.shared.codes,
edition: cx.shared.edition(),
playground: &cx.shared.playground,
- heading_offset: HeadingOffset::H4
+ heading_offset: HeadingOffset::H4,
+ custom_code_classes_in_docs,
}
.into_string()
);
@@ -2035,7 +2058,7 @@ pub(crate) enum ItemSection {
Statics,
Traits,
Functions,
- TypeDefinitions,
+ TypeAliases,
Unions,
Implementations,
TypeMethods,
@@ -2067,7 +2090,7 @@ impl ItemSection {
Statics,
Traits,
Functions,
- TypeDefinitions,
+ TypeAliases,
Unions,
Implementations,
TypeMethods,
@@ -2093,7 +2116,7 @@ impl ItemSection {
Self::Unions => "unions",
Self::Enums => "enums",
Self::Functions => "functions",
- Self::TypeDefinitions => "types",
+ Self::TypeAliases => "types",
Self::Statics => "statics",
Self::Constants => "constants",
Self::Traits => "traits",
@@ -2123,7 +2146,7 @@ impl ItemSection {
Self::Unions => "Unions",
Self::Enums => "Enums",
Self::Functions => "Functions",
- Self::TypeDefinitions => "Type Definitions",
+ Self::TypeAliases => "Type Aliases",
Self::Statics => "Statics",
Self::Constants => "Constants",
Self::Traits => "Traits",
@@ -2154,7 +2177,7 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection {
ItemType::Union => ItemSection::Unions,
ItemType::Enum => ItemSection::Enums,
ItemType::Function => ItemSection::Functions,
- ItemType::Typedef => ItemSection::TypeDefinitions,
+ ItemType::TypeAlias => ItemSection::TypeAliases,
ItemType::Static => ItemSection::Statics,
ItemType::Constant => ItemSection::Constants,
ItemType::Trait => ItemSection::Traits,
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 6cab34986..c6751c958 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -198,7 +198,7 @@ pub(super) fn print_item(
clean::StructItem(..) => "Struct ",
clean::UnionItem(..) => "Union ",
clean::EnumItem(..) => "Enum ",
- clean::TypedefItem(..) => "Type Definition ",
+ clean::TypeAliasItem(..) => "Type Alias ",
clean::MacroItem(..) => "Macro ",
clean::ProcMacroItem(ref mac) => match mac.kind {
MacroKind::Bang => "Macro ",
@@ -273,7 +273,7 @@ pub(super) fn print_item(
clean::StructItem(ref s) => item_struct(buf, cx, item, s),
clean::UnionItem(ref s) => item_union(buf, cx, item, s),
clean::EnumItem(ref e) => item_enum(buf, cx, item, e),
- clean::TypedefItem(ref t) => item_typedef(buf, cx, item, t),
+ clean::TypeAliasItem(ref t) => item_type_alias(buf, cx, item, t),
clean::MacroItem(ref m) => item_macro(buf, cx, item, m),
clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m),
clean::PrimitiveItem(_) => item_primitive(buf, cx, item),
@@ -343,7 +343,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
ItemType::Static => 8,
ItemType::Trait => 9,
ItemType::Function => 10,
- ItemType::Typedef => 12,
+ ItemType::TypeAlias => 12,
ItemType::Union => 13,
_ => 14 + ty as u8,
}
@@ -1217,8 +1217,8 @@ fn item_opaque_ty(
.unwrap();
}
-fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) {
- fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
+fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TypeAlias) {
+ fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) {
wrap_item(w, |w| {
write!(
w,
@@ -1237,6 +1237,75 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
+ if let Some(inner_type) = &t.inner_type {
+ write!(
+ w,
+ "<h2 id=\"aliased-type\" class=\"small-section-header\">\
+ Aliased Type<a href=\"#aliased-type\" class=\"anchor\">§</a></h2>"
+ );
+
+ match inner_type {
+ clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
+ let variants_iter = || variants.iter().filter(|i| !i.is_stripped());
+ wrap_item(w, |w| {
+ let variants_len = variants.len();
+ let variants_count = variants_iter().count();
+ let has_stripped_entries = variants_len != variants_count;
+
+ write!(w, "enum {}{}", it.name.unwrap(), t.generics.print(cx));
+ render_enum_fields(
+ w,
+ cx,
+ Some(&t.generics),
+ variants_iter(),
+ variants_count,
+ has_stripped_entries,
+ *is_non_exhaustive,
+ )
+ });
+ item_variants(w, cx, it, variants_iter());
+ }
+ clean::TypeAliasInnerType::Union { fields } => {
+ wrap_item(w, |w| {
+ let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
+ let has_stripped_fields = fields.len() != fields_count;
+
+ write!(w, "union {}{}", it.name.unwrap(), t.generics.print(cx));
+ render_struct_fields(
+ w,
+ Some(&t.generics),
+ None,
+ fields,
+ "",
+ true,
+ has_stripped_fields,
+ cx,
+ );
+ });
+ item_fields(w, cx, it, fields, None);
+ }
+ clean::TypeAliasInnerType::Struct { ctor_kind, fields } => {
+ wrap_item(w, |w| {
+ let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
+ let has_stripped_fields = fields.len() != fields_count;
+
+ write!(w, "struct {}{}", it.name.unwrap(), t.generics.print(cx));
+ render_struct_fields(
+ w,
+ Some(&t.generics),
+ *ctor_kind,
+ fields,
+ "",
+ true,
+ has_stripped_fields,
+ cx,
+ );
+ });
+ item_fields(w, cx, it, fields, None);
+ }
+ }
+ }
+
let def_id = it.item_id.expect_def_id();
// Render any items associated directly to this alias, as otherwise they
// won't be visible anywhere in the docs. It would be nice to also show
@@ -1315,6 +1384,12 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>(
s: &'a [clean::Item],
) -> impl fmt::Display + 'a + Captures<'cx> {
display_fn(|f| {
+ if s.iter()
+ .all(|field| matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..))))
+ {
+ return f.write_str("/* private fields */");
+ }
+
for (i, ty) in s.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
@@ -1332,7 +1407,7 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>(
fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) {
let tcx = cx.tcx();
let count_variants = e.variants().count();
- wrap_item(w, |mut w| {
+ wrap_item(w, |w| {
render_attributes_in_code(w, it, tcx);
write!(
w,
@@ -1341,148 +1416,179 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
it.name.unwrap(),
e.generics.print(cx),
);
- if !print_where_clause_and_check(w, &e.generics, cx) {
- // If there wasn't a `where` clause, we add a whitespace.
- w.write_str(" ");
- }
+ render_enum_fields(
+ w,
+ cx,
+ Some(&e.generics),
+ e.variants(),
+ count_variants,
+ e.has_stripped_entries(),
+ it.is_non_exhaustive(),
+ );
+ });
- let variants_stripped = e.has_stripped_entries();
- if count_variants == 0 && !variants_stripped {
- w.write_str("{}");
- } else {
- w.write_str("{\n");
- let toggle = should_hide_fields(count_variants);
- if toggle {
- toggle_open(&mut w, format_args!("{count_variants} variants"));
- }
- for v in e.variants() {
- w.write_str(" ");
- let name = v.name.unwrap();
- match *v.kind {
- // FIXME(#101337): Show discriminant
- clean::VariantItem(ref var) => match var.kind {
- clean::VariantKind::CLike => w.write_str(name.as_str()),
- clean::VariantKind::Tuple(ref s) => {
- write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
- }
- clean::VariantKind::Struct(ref s) => {
- render_struct(w, v, None, None, &s.fields, " ", false, cx);
- }
- },
- _ => unreachable!(),
- }
- w.write_str(",\n");
- }
+ write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
- if variants_stripped && !it.is_non_exhaustive() {
- w.write_str(" // some variants omitted\n");
- }
- if toggle {
- toggle_close(&mut w);
+ if count_variants != 0 {
+ item_variants(w, cx, it, e.variants());
+ }
+ let def_id = it.item_id.expect_def_id();
+ write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
+ write!(w, "{}", document_type_layout(cx, def_id));
+}
+
+fn render_enum_fields<'a>(
+ mut w: &mut Buffer,
+ cx: &mut Context<'_>,
+ g: Option<&clean::Generics>,
+ variants: impl Iterator<Item = &'a clean::Item>,
+ count_variants: usize,
+ has_stripped_entries: bool,
+ is_non_exhaustive: bool,
+) {
+ if !g.is_some_and(|g| print_where_clause_and_check(w, g, cx)) {
+ // If there wasn't a `where` clause, we add a whitespace.
+ w.write_str(" ");
+ }
+
+ let variants_stripped = has_stripped_entries;
+ if count_variants == 0 && !variants_stripped {
+ w.write_str("{}");
+ } else {
+ w.write_str("{\n");
+ let toggle = should_hide_fields(count_variants);
+ if toggle {
+ toggle_open(&mut w, format_args!("{count_variants} variants"));
+ }
+ const TAB: &str = " ";
+ for v in variants {
+ w.write_str(TAB);
+ let name = v.name.unwrap();
+ match *v.kind {
+ // FIXME(#101337): Show discriminant
+ clean::VariantItem(ref var) => match var.kind {
+ clean::VariantKind::CLike => w.write_str(name.as_str()),
+ clean::VariantKind::Tuple(ref s) => {
+ write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
+ }
+ clean::VariantKind::Struct(ref s) => {
+ render_struct(w, v, None, None, &s.fields, TAB, false, cx);
+ }
+ },
+ _ => unreachable!(),
}
- w.write_str("}");
+ w.write_str(",\n");
}
- });
- write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
+ if variants_stripped && !is_non_exhaustive {
+ w.write_str(" // some variants omitted\n");
+ }
+ if toggle {
+ toggle_close(&mut w);
+ }
+ w.write_str("}");
+ }
+}
- if count_variants != 0 {
+fn item_variants<'a>(
+ w: &mut Buffer,
+ cx: &mut Context<'_>,
+ it: &clean::Item,
+ variants: impl Iterator<Item = &'a clean::Item>,
+) {
+ let tcx = cx.tcx();
+ write!(
+ w,
+ "<h2 id=\"variants\" class=\"variants small-section-header\">\
+ Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
+ </h2>\
+ {}\
+ <div class=\"variants\">",
+ document_non_exhaustive_header(it),
+ document_non_exhaustive(it)
+ );
+ for variant in variants {
+ let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
write!(
w,
- "<h2 id=\"variants\" class=\"variants small-section-header\">\
- Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
- </h2>\
- {}\
- <div class=\"variants\">",
- document_non_exhaustive_header(it),
- document_non_exhaustive(it)
+ "<section id=\"{id}\" class=\"variant\">\
+ <a href=\"#{id}\" class=\"anchor\">§</a>",
);
- for variant in e.variants() {
- let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
- write!(
- w,
- "<section id=\"{id}\" class=\"variant\">\
- <a href=\"#{id}\" class=\"anchor\">§</a>",
- );
- render_stability_since_raw_with_extra(
- w,
- variant.stable_since(tcx),
- variant.const_stability(tcx),
- it.stable_since(tcx),
- it.const_stable_since(tcx),
- " rightside",
- );
- write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
+ render_stability_since_raw_with_extra(
+ w,
+ variant.stable_since(tcx),
+ variant.const_stability(tcx),
+ it.stable_since(tcx),
+ it.const_stable_since(tcx),
+ " rightside",
+ );
+ write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
- let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
+ let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
- if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
- write!(w, "({})", print_tuple_struct_fields(cx, s));
- }
- w.write_str("</h3></section>");
-
- let heading_and_fields = match &variant_data.kind {
- clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)),
- clean::VariantKind::Tuple(fields) => {
- // Documentation on tuple variant fields is rare, so to reduce noise we only emit
- // the section if at least one field is documented.
- if fields.iter().any(|f| !f.doc_value().is_empty()) {
- Some(("Tuple Fields", fields))
- } else {
- None
- }
+ if let clean::VariantKind::Tuple(ref s) = variant_data.kind {
+ write!(w, "({})", print_tuple_struct_fields(cx, s));
+ }
+ w.write_str("</h3></section>");
+
+ let heading_and_fields = match &variant_data.kind {
+ clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)),
+ clean::VariantKind::Tuple(fields) => {
+ // Documentation on tuple variant fields is rare, so to reduce noise we only emit
+ // the section if at least one field is documented.
+ if fields.iter().any(|f| !f.doc_value().is_empty()) {
+ Some(("Tuple Fields", fields))
+ } else {
+ None
}
- clean::VariantKind::CLike => None,
- };
+ }
+ clean::VariantKind::CLike => None,
+ };
- if let Some((heading, fields)) = heading_and_fields {
- let variant_id =
- cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
- write!(
- w,
- "<div class=\"sub-variant\" id=\"{variant_id}\">\
- <h4>{heading}</h4>\
- {}",
- document_non_exhaustive(variant)
- );
- for field in fields {
- match *field.kind {
- clean::StrippedItem(box clean::StructFieldItem(_)) => {}
- clean::StructFieldItem(ref ty) => {
- let id = cx.derive_id(format!(
- "variant.{}.field.{}",
- variant.name.unwrap(),
- field.name.unwrap()
- ));
- write!(
- w,
- "<div class=\"sub-variant-field\">\
+ if let Some((heading, fields)) = heading_and_fields {
+ let variant_id =
+ cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap()));
+ write!(
+ w,
+ "<div class=\"sub-variant\" id=\"{variant_id}\">\
+ <h4>{heading}</h4>\
+ {}",
+ document_non_exhaustive(variant)
+ );
+ for field in fields {
+ match *field.kind {
+ clean::StrippedItem(box clean::StructFieldItem(_)) => {}
+ clean::StructFieldItem(ref ty) => {
+ let id = cx.derive_id(format!(
+ "variant.{}.field.{}",
+ variant.name.unwrap(),
+ field.name.unwrap()
+ ));
+ write!(
+ w,
+ "<div class=\"sub-variant-field\">\
<span id=\"{id}\" class=\"small-section-header\">\
<a href=\"#{id}\" class=\"anchor field\">§</a>\
<code>{f}: {t}</code>\
</span>",
- f = field.name.unwrap(),
- t = ty.print(cx),
- );
- write!(
- w,
- "{}</div>",
- document(cx, field, Some(variant), HeadingOffset::H5)
- );
- }
- _ => unreachable!(),
+ f = field.name.unwrap(),
+ t = ty.print(cx),
+ );
+ write!(
+ w,
+ "{}</div>",
+ document(cx, field, Some(variant), HeadingOffset::H5)
+ );
}
+ _ => unreachable!(),
}
- w.write_str("</div>");
}
-
- write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4));
+ w.write_str("</div>");
}
- write!(w, "</div>");
+
+ write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4));
}
- let def_id = it.item_id.expect_def_id();
- write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
- write!(w, "{}", document_type_layout(cx, def_id));
+ write!(w, "</div>");
}
fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) {
@@ -1593,15 +1699,28 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
- let mut fields = s
- .fields
+ item_fields(w, cx, it, &s.fields, s.ctor_kind);
+
+ let def_id = it.item_id.expect_def_id();
+ write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
+ write!(w, "{}", document_type_layout(cx, def_id));
+}
+
+fn item_fields(
+ w: &mut Buffer,
+ cx: &mut Context<'_>,
+ it: &clean::Item,
+ fields: &Vec<clean::Item>,
+ ctor_kind: Option<CtorKind>,
+) {
+ let mut fields = fields
.iter()
.filter_map(|f| match *f.kind {
clean::StructFieldItem(ref ty) => Some((f, ty)),
_ => None,
})
.peekable();
- if let None | Some(CtorKind::Fn) = s.ctor_kind {
+ if let None | Some(CtorKind::Fn) = ctor_kind {
if fields.peek().is_some() {
write!(
w,
@@ -1609,7 +1728,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
{}{}<a href=\"#fields\" class=\"anchor\">§</a>\
</h2>\
{}",
- if s.ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
+ if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
document_non_exhaustive_header(it),
document_non_exhaustive(it)
);
@@ -1630,9 +1749,6 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
}
}
}
- let def_id = it.item_id.expect_def_id();
- write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
- write!(w, "{}", document_type_layout(cx, def_id));
}
fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) {
@@ -1871,7 +1987,7 @@ fn render_union<'a, 'cx: 'a>(
}
fn render_struct(
- mut w: &mut Buffer,
+ w: &mut Buffer,
it: &clean::Item,
g: Option<&clean::Generics>,
ty: Option<CtorKind>,
@@ -1891,6 +2007,29 @@ fn render_struct(
if let Some(g) = g {
write!(w, "{}", g.print(cx))
}
+ render_struct_fields(
+ w,
+ g,
+ ty,
+ fields,
+ tab,
+ structhead,
+ it.has_stripped_entries().unwrap_or(false),
+ cx,
+ )
+}
+
+fn render_struct_fields(
+ mut w: &mut Buffer,
+ g: Option<&clean::Generics>,
+ ty: Option<CtorKind>,
+ fields: &[clean::Item],
+ tab: &str,
+ structhead: bool,
+ has_stripped_entries: bool,
+ cx: &Context<'_>,
+) {
+ let tcx = cx.tcx();
match ty {
None => {
let where_displayed =
@@ -1922,11 +2061,11 @@ fn render_struct(
}
if has_visible_fields {
- if it.has_stripped_entries().unwrap() {
+ if has_stripped_entries {
write!(w, "\n{tab} /* private fields */");
}
write!(w, "\n{tab}");
- } else if it.has_stripped_entries().unwrap() {
+ } else if has_stripped_entries {
write!(w, " /* private fields */ ");
}
if toggle {
@@ -1936,21 +2075,31 @@ fn render_struct(
}
Some(CtorKind::Fn) => {
w.write_str("(");
- for (i, field) in fields.iter().enumerate() {
- if i > 0 {
- w.write_str(", ");
- }
- match *field.kind {
- clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
- clean::StructFieldItem(ref ty) => {
- write!(
- w,
- "{}{}",
- visibility_print_with_space(field.visibility(tcx), field.item_id, cx),
- ty.print(cx),
- )
+ if fields.iter().all(|field| {
+ matches!(*field.kind, clean::StrippedItem(box clean::StructFieldItem(..)))
+ }) {
+ write!(w, "/* private fields */");
+ } else {
+ for (i, field) in fields.iter().enumerate() {
+ if i > 0 {
+ w.write_str(", ");
+ }
+ match *field.kind {
+ clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
+ clean::StructFieldItem(ref ty) => {
+ write!(
+ w,
+ "{}{}",
+ visibility_print_with_space(
+ field.visibility(tcx),
+ field.item_id,
+ cx
+ ),
+ ty.print(cx),
+ )
+ }
+ _ => unreachable!(),
}
- _ => unreachable!(),
}
}
w.write_str(")");
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index f34be120d..78c443b22 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -1,10 +1,10 @@
use std::collections::hash_map::Entry;
use std::collections::BTreeMap;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::Symbol;
-use serde::ser::{Serialize, SerializeStruct, Serializer};
+use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer};
use crate::clean;
use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate};
@@ -68,19 +68,19 @@ pub(crate) fn build_index<'tcx>(
// Reduce `DefId` in paths into smaller sequential numbers,
// and prune the paths that do not appear in the index.
let mut lastpath = "";
- let mut lastpathid = 0usize;
+ let mut lastpathid = 0isize;
// First, on function signatures
let mut search_index = std::mem::replace(&mut cache.search_index, Vec::new());
for item in search_index.iter_mut() {
fn insert_into_map<F: std::hash::Hash + Eq>(
ty: &mut RenderType,
- map: &mut FxHashMap<F, usize>,
+ map: &mut FxHashMap<F, isize>,
itemid: F,
- lastpathid: &mut usize,
- crate_paths: &mut Vec<(ItemType, Symbol)>,
+ lastpathid: &mut isize,
+ crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
item_type: ItemType,
- path: Symbol,
+ path: &[Symbol],
) {
match map.entry(itemid) {
Entry::Occupied(entry) => ty.id = Some(RenderTypeId::Index(*entry.get())),
@@ -88,7 +88,7 @@ pub(crate) fn build_index<'tcx>(
let pathid = *lastpathid;
entry.insert(pathid);
*lastpathid += 1;
- crate_paths.push((item_type, path));
+ crate_paths.push((item_type, path.to_vec()));
ty.id = Some(RenderTypeId::Index(pathid));
}
}
@@ -97,10 +97,10 @@ pub(crate) fn build_index<'tcx>(
fn convert_render_type(
ty: &mut RenderType,
cache: &mut Cache,
- itemid_to_pathid: &mut FxHashMap<ItemId, usize>,
- primitives: &mut FxHashMap<Symbol, usize>,
- lastpathid: &mut usize,
- crate_paths: &mut Vec<(ItemType, Symbol)>,
+ itemid_to_pathid: &mut FxHashMap<ItemId, isize>,
+ primitives: &mut FxHashMap<Symbol, isize>,
+ lastpathid: &mut isize,
+ crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
) {
if let Some(generics) = &mut ty.generics {
for item in generics {
@@ -131,7 +131,7 @@ pub(crate) fn build_index<'tcx>(
lastpathid,
crate_paths,
item_type,
- *fqp.last().unwrap(),
+ fqp,
);
} else {
ty.id = None;
@@ -146,7 +146,7 @@ pub(crate) fn build_index<'tcx>(
lastpathid,
crate_paths,
ItemType::Primitive,
- sym,
+ &[sym],
);
}
RenderTypeId::Index(_) => {}
@@ -173,6 +173,18 @@ pub(crate) fn build_index<'tcx>(
&mut crate_paths,
);
}
+ for constraint in &mut search_type.where_clause {
+ for trait_ in &mut constraint[..] {
+ convert_render_type(
+ trait_,
+ cache,
+ &mut itemid_to_pathid,
+ &mut primitives,
+ &mut lastpathid,
+ &mut crate_paths,
+ );
+ }
+ }
}
}
@@ -191,7 +203,7 @@ pub(crate) fn build_index<'tcx>(
lastpathid += 1;
if let Some(&(ref fqp, short)) = paths.get(&defid) {
- crate_paths.push((short, *fqp.last().unwrap()));
+ crate_paths.push((short, fqp.clone()));
Some(pathid)
} else {
None
@@ -213,118 +225,163 @@ pub(crate) fn build_index<'tcx>(
struct CrateData<'a> {
doc: String,
items: Vec<&'a IndexItem>,
- paths: Vec<(ItemType, Symbol)>,
+ paths: Vec<(ItemType, Vec<Symbol>)>,
// The String is alias name and the vec is the list of the elements with this alias.
//
// To be noted: the `usize` elements are indexes to `items`.
aliases: &'a BTreeMap<String, Vec<usize>>,
}
+ struct Paths {
+ ty: ItemType,
+ name: Symbol,
+ path: Option<usize>,
+ }
+
+ impl Serialize for Paths {
+ 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.ty)?;
+ seq.serialize_element(self.name.as_str())?;
+ if let Some(ref path) = self.path {
+ seq.serialize_element(path)?;
+ }
+ seq.end()
+ }
+ }
+
impl<'a> Serialize for CrateData<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
+ let mut extra_paths = FxHashMap::default();
+ // We need to keep the order of insertion, hence why we use an `IndexMap`. Then we will
+ // insert these "extra paths" (which are paths of items from external crates) into the
+ // `full_paths` list at the end.
+ let mut revert_extra_paths = FxIndexMap::default();
+ let mut mod_paths = FxHashMap::default();
+ for (index, item) in self.items.iter().enumerate() {
+ if item.path.is_empty() {
+ continue;
+ }
+ mod_paths.insert(&item.path, index);
+ }
+ let mut paths = Vec::with_capacity(self.paths.len());
+ for (ty, path) in &self.paths {
+ if path.len() < 2 {
+ paths.push(Paths { ty: *ty, name: path[0], path: None });
+ continue;
+ }
+ let full_path = join_with_double_colon(&path[..path.len() - 1]);
+ if let Some(index) = mod_paths.get(&full_path) {
+ paths.push(Paths { ty: *ty, name: *path.last().unwrap(), path: Some(*index) });
+ continue;
+ }
+ // It means it comes from an external crate so the item and its path will be
+ // stored into another array.
+ //
+ // `index` is put after the last `mod_paths`
+ let index = extra_paths.len() + self.items.len();
+ if !revert_extra_paths.contains_key(&index) {
+ revert_extra_paths.insert(index, full_path.clone());
+ }
+ match extra_paths.entry(full_path) {
+ Entry::Occupied(entry) => {
+ paths.push(Paths {
+ ty: *ty,
+ name: *path.last().unwrap(),
+ path: Some(*entry.get()),
+ });
+ }
+ Entry::Vacant(entry) => {
+ entry.insert(index);
+ paths.push(Paths {
+ ty: *ty,
+ name: *path.last().unwrap(),
+ path: Some(index),
+ });
+ }
+ }
+ }
+
+ let mut names = Vec::with_capacity(self.items.len());
+ let mut types = String::with_capacity(self.items.len());
+ let mut full_paths = Vec::with_capacity(self.items.len());
+ let mut descriptions = Vec::with_capacity(self.items.len());
+ let mut parents = Vec::with_capacity(self.items.len());
+ let mut functions = Vec::with_capacity(self.items.len());
+ let mut deprecated = Vec::with_capacity(self.items.len());
+
+ for (index, item) in self.items.iter().enumerate() {
+ let n = item.ty as u8;
+ let c = char::try_from(n + b'A').expect("item types must fit in ASCII");
+ assert!(c <= 'z', "item types must fit within ASCII printables");
+ types.push(c);
+
+ assert_eq!(
+ item.parent.is_some(),
+ item.parent_idx.is_some(),
+ "`{}` is missing idx",
+ item.name
+ );
+ // 0 is a sentinel, everything else is one-indexed
+ parents.push(item.parent_idx.map(|x| x + 1).unwrap_or(0));
+
+ names.push(item.name.as_str());
+ descriptions.push(&item.desc);
+
+ if !item.path.is_empty() {
+ full_paths.push((index, &item.path));
+ }
+
+ // Fake option to get `0` out as a sentinel instead of `null`.
+ // We want to use `0` because it's three less bytes.
+ enum FunctionOption<'a> {
+ Function(&'a IndexItemFunctionType),
+ None,
+ }
+ impl<'a> Serialize for FunctionOption<'a> {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ match self {
+ FunctionOption::None => 0.serialize(serializer),
+ FunctionOption::Function(ty) => ty.serialize(serializer),
+ }
+ }
+ }
+ functions.push(match &item.search_type {
+ Some(ty) => FunctionOption::Function(ty),
+ None => FunctionOption::None,
+ });
+
+ if item.deprecation.is_some() {
+ deprecated.push(index);
+ }
+ }
+
+ for (index, path) in &revert_extra_paths {
+ full_paths.push((*index, path));
+ }
+
let has_aliases = !self.aliases.is_empty();
let mut crate_data =
serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?;
crate_data.serialize_field("doc", &self.doc)?;
- crate_data.serialize_field(
- "t",
- &self
- .items
- .iter()
- .map(|item| {
- let n = item.ty as u8;
- let c = char::try_from(n + b'A').expect("item types must fit in ASCII");
- assert!(c <= 'z', "item types must fit within ASCII printables");
- c
- })
- .collect::<String>(),
- )?;
- crate_data.serialize_field(
- "n",
- &self.items.iter().map(|item| item.name.as_str()).collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "q",
- &self
- .items
- .iter()
- .enumerate()
- // Serialize as an array of item indices and full paths
- .filter_map(
- |(index, item)| {
- if item.path.is_empty() { None } else { Some((index, &item.path)) }
- },
- )
- .collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "d",
- &self.items.iter().map(|item| &item.desc).collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "i",
- &self
- .items
- .iter()
- .map(|item| {
- assert_eq!(
- item.parent.is_some(),
- item.parent_idx.is_some(),
- "`{}` is missing idx",
- item.name
- );
- // 0 is a sentinel, everything else is one-indexed
- item.parent_idx.map(|x| x + 1).unwrap_or(0)
- })
- .collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "f",
- &self
- .items
- .iter()
- .map(|item| {
- // Fake option to get `0` out as a sentinel instead of `null`.
- // We want to use `0` because it's three less bytes.
- enum FunctionOption<'a> {
- Function(&'a IndexItemFunctionType),
- None,
- }
- impl<'a> Serialize for FunctionOption<'a> {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- match self {
- FunctionOption::None => 0.serialize(serializer),
- FunctionOption::Function(ty) => ty.serialize(serializer),
- }
- }
- }
- match &item.search_type {
- Some(ty) => FunctionOption::Function(ty),
- None => FunctionOption::None,
- }
- })
- .collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "c",
- &self
- .items
- .iter()
- .enumerate()
- // Serialize as an array of deprecated item indices
- .filter_map(|(index, item)| item.deprecation.map(|_| index))
- .collect::<Vec<_>>(),
- )?;
- crate_data.serialize_field(
- "p",
- &self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::<Vec<_>>(),
- )?;
+ crate_data.serialize_field("t", &types)?;
+ crate_data.serialize_field("n", &names)?;
+ // Serialize as an array of item indices and full paths
+ crate_data.serialize_field("q", &full_paths)?;
+ crate_data.serialize_field("d", &descriptions)?;
+ crate_data.serialize_field("i", &parents)?;
+ crate_data.serialize_field("f", &functions)?;
+ crate_data.serialize_field("c", &deprecated)?;
+ crate_data.serialize_field("p", &paths)?;
if has_aliases {
crate_data.serialize_field("a", &self.aliases)?;
}
@@ -357,7 +414,7 @@ pub(crate) fn get_function_type_for_search<'tcx>(
impl_generics: Option<&(clean::Type, clean::Generics)>,
cache: &Cache,
) -> Option<IndexItemFunctionType> {
- let (mut inputs, mut output) = match *item.kind {
+ let (mut inputs, mut output, where_clause) = match *item.kind {
clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, impl_generics, cache),
clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
@@ -367,7 +424,7 @@ pub(crate) fn get_function_type_for_search<'tcx>(
inputs.retain(|a| a.id.is_some() || a.generics.is_some());
output.retain(|a| a.id.is_some() || a.generics.is_some());
- Some(IndexItemFunctionType { inputs, output })
+ Some(IndexItemFunctionType { inputs, output, where_clause })
}
fn get_index_type(clean_type: &clean::Type, generics: Vec<RenderType>) -> RenderType {
@@ -387,96 +444,48 @@ fn get_index_type_id(clean_type: &clean::Type) -> Option<RenderTypeId> {
clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => {
get_index_type_id(type_)
}
- // The type parameters are converted to generics in `add_generics_and_bounds_as_types`
+ // The type parameters are converted to generics in `simplify_fn_type`
clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)),
clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)),
+ clean::Tuple(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Tuple)),
// Not supported yet
clean::BareFunction(_)
| clean::Generic(_)
| clean::ImplTrait(_)
- | clean::Tuple(_)
| clean::QPath { .. }
| clean::Infer => None,
}
}
-/// The point of this function is to replace bounds with types.
+#[derive(Clone, Copy, Eq, Hash, PartialEq)]
+enum SimplifiedParam {
+ // other kinds of type parameters are identified by their name
+ Symbol(Symbol),
+ // every argument-position impl trait is its own type parameter
+ Anonymous(isize),
+}
+
+/// The point of this function is to lower generics and types into the simplified form that the
+/// frontend search engine can use.
///
-/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
-/// `[Display, Option]`. If a type parameter has no trait bound, it is discarded.
+/// For example, `[T, U, i32]]` where you have the bounds: `T: Display, U: Option<T>` will return
+/// `[-1, -2, i32] where -1: Display, -2: Option<-1>`. If a type parameter has no traid bound, it
+/// will still get a number. If a constraint is present but not used in the actual types, it will
+/// not be added to the map.
///
-/// Important note: It goes through generics recursively. So if you have
-/// `T: Option<Result<(), ()>>`, it'll go into `Option` and then into `Result`.
-#[instrument(level = "trace", skip(tcx, res, cache))]
-fn add_generics_and_bounds_as_types<'tcx, 'a>(
+/// This function also works recursively.
+#[instrument(level = "trace", skip(tcx, res, rgen, cache))]
+fn simplify_fn_type<'tcx, 'a>(
self_: Option<&'a Type>,
generics: &Generics,
arg: &'a Type,
tcx: TyCtxt<'tcx>,
recurse: usize,
res: &mut Vec<RenderType>,
+ rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
+ is_return: bool,
cache: &Cache,
) {
- fn insert_ty(res: &mut Vec<RenderType>, ty: Type, mut generics: Vec<RenderType>) {
- // generics and impl trait are both identified by their generics,
- // rather than a type name itself
- let anonymous = ty.is_full_generic() || ty.is_impl_trait();
- let generics_empty = generics.is_empty();
-
- if anonymous {
- if generics_empty {
- // This is a type parameter with no trait bounds (for example: `T` in
- // `fn f<T>(p: T)`, so not useful for the rustdoc search because we would end up
- // with an empty type with an empty name. Let's just discard it.
- return;
- } else if generics.len() == 1 {
- // In this case, no need to go through an intermediate state if the type parameter
- // contains only one trait bound.
- //
- // For example:
- //
- // `fn foo<T: Display>(r: Option<T>) {}`
- //
- // In this case, it would contain:
- //
- // ```
- // [{
- // name: "option",
- // generics: [{
- // name: "",
- // generics: [
- // name: "Display",
- // generics: []
- // }]
- // }]
- // }]
- // ```
- //
- // After removing the intermediate (unnecessary) type parameter, it'll become:
- //
- // ```
- // [{
- // name: "option",
- // generics: [{
- // name: "Display",
- // generics: []
- // }]
- // }]
- // ```
- //
- // To be noted that it can work if there is ONLY ONE trait bound, otherwise we still
- // need to keep it as is!
- res.push(generics.pop().unwrap());
- return;
- }
- }
- let index_ty = get_index_type(&ty, generics);
- if index_ty.id.is_none() && generics_empty {
- return;
- }
- res.push(index_ty);
- }
-
if recurse >= 10 {
// FIXME: remove this whole recurse thing when the recursion bug is fixed
// See #59502 for the original issue.
@@ -503,88 +512,126 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
// for its bounds.
if let Type::Generic(arg_s) = *arg {
// First we check if the bounds are in a `where` predicate...
+ let mut type_bounds = Vec::new();
for where_pred in generics.where_predicates.iter().filter(|g| match g {
WherePredicate::BoundPredicate { ty: Type::Generic(ty_s), .. } => *ty_s == arg_s,
_ => false,
}) {
- let mut ty_generics = Vec::new();
let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
for bound in bounds.iter() {
if let Some(path) = bound.get_trait_path() {
let ty = Type::Path { path };
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
- &mut ty_generics,
+ &mut type_bounds,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
}
// Otherwise we check if the trait bounds are "inlined" like `T: Option<u32>`...
if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
- let mut ty_generics = Vec::new();
for bound in bound.get_bounds().unwrap_or(&[]) {
if let Some(path) = bound.get_trait_path() {
let ty = Type::Path { path };
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
- &mut ty_generics,
+ &mut type_bounds,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
+ }
+ if let Some((idx, _)) = rgen.get(&SimplifiedParam::Symbol(arg_s)) {
+ res.push(RenderType { id: Some(RenderTypeId::Index(*idx)), generics: None });
+ } else {
+ let idx = -isize::try_from(rgen.len() + 1).unwrap();
+ rgen.insert(SimplifiedParam::Symbol(arg_s), (idx, type_bounds));
+ res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
}
} else if let Type::ImplTrait(ref bounds) = *arg {
- let mut ty_generics = Vec::new();
+ let mut type_bounds = Vec::new();
for bound in bounds {
if let Some(path) = bound.get_trait_path() {
let ty = Type::Path { path };
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
- &mut ty_generics,
+ &mut type_bounds,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
+ if is_return && !type_bounds.is_empty() {
+ // In parameter position, `impl Trait` is a unique thing.
+ res.push(RenderType { id: None, generics: Some(type_bounds) });
+ } else {
+ // In parameter position, `impl Trait` is the same as an unnamed generic parameter.
+ let idx = -isize::try_from(rgen.len() + 1).unwrap();
+ rgen.insert(SimplifiedParam::Anonymous(idx), (idx, type_bounds));
+ res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
+ }
} else if let Type::Slice(ref ty) = *arg {
let mut ty_generics = Vec::new();
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
&mut ty_generics,
+ rgen,
+ is_return,
cache,
);
- insert_ty(res, arg.clone(), ty_generics);
+ res.push(get_index_type(arg, ty_generics));
} else if let Type::Array(ref ty, _) = *arg {
let mut ty_generics = Vec::new();
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
&ty,
tcx,
recurse + 1,
&mut ty_generics,
+ rgen,
+ is_return,
cache,
);
- insert_ty(res, arg.clone(), ty_generics);
+ res.push(get_index_type(arg, ty_generics));
+ } else if let Type::Tuple(ref tys) = *arg {
+ let mut ty_generics = Vec::new();
+ for ty in tys {
+ simplify_fn_type(
+ self_,
+ generics,
+ &ty,
+ tcx,
+ recurse + 1,
+ &mut ty_generics,
+ rgen,
+ is_return,
+ cache,
+ );
+ }
+ res.push(get_index_type(arg, ty_generics));
} else {
// This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
// looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
@@ -594,18 +641,26 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
let mut ty_generics = Vec::new();
if let Some(arg_generics) = arg.generics() {
for gen in arg_generics.iter() {
- add_generics_and_bounds_as_types(
+ simplify_fn_type(
self_,
generics,
gen,
tcx,
recurse + 1,
&mut ty_generics,
+ rgen,
+ is_return,
cache,
);
}
}
- insert_ty(res, arg.clone(), ty_generics);
+ let id = get_index_type_id(&arg);
+ if id.is_some() || !ty_generics.is_empty() {
+ res.push(RenderType {
+ id,
+ generics: if ty_generics.is_empty() { None } else { Some(ty_generics) },
+ });
+ }
}
}
@@ -618,7 +673,7 @@ fn get_fn_inputs_and_outputs<'tcx>(
tcx: TyCtxt<'tcx>,
impl_generics: Option<&(clean::Type, clean::Generics)>,
cache: &Cache,
-) -> (Vec<RenderType>, Vec<RenderType>) {
+) -> (Vec<RenderType>, Vec<RenderType>, Vec<Vec<RenderType>>) {
let decl = &func.decl;
let combined_generics;
@@ -644,21 +699,27 @@ fn get_fn_inputs_and_outputs<'tcx>(
(None, &func.generics)
};
- let mut all_types = Vec::new();
+ let mut rgen: FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
+
+ let mut arg_types = Vec::new();
for arg in decl.inputs.values.iter() {
- let mut args = Vec::new();
- add_generics_and_bounds_as_types(self_, generics, &arg.type_, tcx, 0, &mut args, cache);
- if !args.is_empty() {
- all_types.extend(args);
- } else {
- all_types.push(get_index_type(&arg.type_, vec![]));
- }
+ simplify_fn_type(
+ self_,
+ generics,
+ &arg.type_,
+ tcx,
+ 0,
+ &mut arg_types,
+ &mut rgen,
+ false,
+ cache,
+ );
}
let mut ret_types = Vec::new();
- add_generics_and_bounds_as_types(self_, generics, &decl.output, tcx, 0, &mut ret_types, cache);
- if ret_types.is_empty() {
- ret_types.push(get_index_type(&decl.output, vec![]));
- }
- (all_types, ret_types)
+ simplify_fn_type(self_, generics, &decl.output, tcx, 0, &mut ret_types, &mut rgen, true, cache);
+
+ let mut simplified_params = rgen.into_values().collect::<Vec<_>>();
+ simplified_params.sort_by_key(|(idx, _)| -idx);
+ (arg_types, ret_types, simplified_params.into_iter().map(|(_idx, traits)| traits).collect())
}
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index f3da61056..76f63c6f6 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -82,7 +82,7 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
clean::PrimitiveItem(_) => sidebar_primitive(cx, it),
clean::UnionItem(ref u) => sidebar_union(cx, it, u),
clean::EnumItem(ref e) => sidebar_enum(cx, it, e),
- clean::TypedefItem(_) => sidebar_typedef(cx, it),
+ clean::TypeAliasItem(ref t) => sidebar_type_alias(cx, it, t),
clean::ModuleItem(ref m) => vec![sidebar_module(&m.items)],
clean::ForeignTypeItem => sidebar_foreign_type(cx, it),
_ => vec![],
@@ -100,7 +100,7 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
|| it.is_union()
|| it.is_enum()
|| it.is_mod()
- || it.is_typedef()
+ || it.is_type_alias()
{
(
match *it.kind {
@@ -230,8 +230,33 @@ fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBl
}
}
-fn sidebar_typedef<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+fn sidebar_type_alias<'a>(
+ cx: &'a Context<'_>,
+ it: &'a clean::Item,
+ t: &'a clean::TypeAlias,
+) -> Vec<LinkBlock<'a>> {
let mut items = vec![];
+ if let Some(inner_type) = &t.inner_type {
+ items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type")));
+ match inner_type {
+ clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => {
+ let mut variants = variants
+ .iter()
+ .filter(|i| !i.is_stripped())
+ .filter_map(|v| v.name)
+ .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
+ .collect::<Vec<_>>();
+ variants.sort_unstable();
+
+ items.push(LinkBlock::new(Link::new("variants", "Variants"), variants));
+ }
+ clean::TypeAliasInnerType::Union { fields }
+ | clean::TypeAliasInnerType::Struct { ctor_kind: _, fields } => {
+ let fields = get_struct_fields_name(fields);
+ items.push(LinkBlock::new(Link::new("fields", "Fields"), fields));
+ }
+ }
+ }
sidebar_assoc_items(cx, it, &mut items);
items
}
@@ -254,11 +279,12 @@ fn sidebar_assoc_items<'a>(
links: &mut Vec<LinkBlock<'a>>,
) {
let did = it.item_id.expect_def_id();
- let cache = cx.cache();
+ let v = cx.shared.all_impls_for_item(it, it.item_id.expect_def_id());
+ let v = v.as_slice();
let mut assoc_consts = Vec::new();
let mut methods = Vec::new();
- if let Some(v) = cache.impls.get(&did) {
+ if !v.is_empty() {
let mut used_links = FxHashSet::default();
let mut id_map = IdMap::new();
@@ -294,7 +320,7 @@ fn sidebar_assoc_items<'a>(
cx,
&mut deref_methods,
impl_,
- v,
+ v.iter().copied(),
&mut derefs,
&mut used_links,
);
@@ -324,7 +350,7 @@ fn sidebar_deref_methods<'a>(
cx: &'a Context<'_>,
out: &mut Vec<LinkBlock<'a>>,
impl_: &Impl,
- v: &[Impl],
+ v: impl Iterator<Item = &'a Impl>,
derefs: &mut DefIdSet,
used_links: &mut FxHashSet<String>,
) {
@@ -334,7 +360,7 @@ fn sidebar_deref_methods<'a>(
if let Some((target, real_target)) =
impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
clean::AssocTypeItem(box ref t, _) => Some(match *t {
- clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
+ clean::TypeAlias { item_type: Some(ref type_), .. } => (type_, &t.type_),
_ => (&t.type_, &t.type_),
}),
_ => None,
@@ -349,7 +375,7 @@ fn sidebar_deref_methods<'a>(
// Avoid infinite cycles
return;
}
- let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
+ let deref_mut = { v }.any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
let inner_impl = target
.def_id(c)
.or_else(|| {
@@ -400,7 +426,7 @@ fn sidebar_deref_methods<'a>(
cx,
out,
target_deref_impl,
- target_impls,
+ target_impls.iter(),
derefs,
used_links,
);
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index c4a1ebbec..1d6eafe51 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -134,7 +134,7 @@ impl DocVisitor for SourceCollector<'_, '_> {
let filename = span.filename(sess);
let span = span.inner();
let pos = sess.source_map().lookup_source_file(span.lo());
- let file_span = span.with_lo(pos.start_pos).with_hi(pos.end_pos);
+ let file_span = span.with_lo(pos.start_pos).with_hi(pos.end_position());
// If it turns out that we couldn't read this file, then we probably
// can't read any of the files (generating html output from json or
// something like that), so just don't include sources for the
diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css
index 93aa11a58..fe0cf6dc8 100644
--- a/src/librustdoc/html/static/css/noscript.css
+++ b/src/librustdoc/html/static/css/noscript.css
@@ -28,3 +28,216 @@ nav.sub {
https://github.com/rust-lang/rust/issues/102576 */
display: none;
}
+
+/* Begin: styles for themes
+ Keep the default light and dark themes synchronized with the ones
+ in rustdoc.css */
+
+/* Begin theme: light */
+:root {
+ --main-background-color: white;
+ --main-color: black;
+ --settings-input-color: #2196f3;
+ --settings-input-border-color: #717171;
+ --settings-button-color: #000;
+ --settings-button-border-focus: #717171;
+ --sidebar-background-color: #f5f5f5;
+ --sidebar-background-color-hover: #e0e0e0;
+ --code-block-background-color: #f5f5f5;
+ --scrollbar-track-background-color: #dcdcdc;
+ --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;
+ --toggle-filter: none;
+ --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);
+ --warning-border-color: #ff8e00;
+ --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;
+ --sidebar-link-color: #356da4;
+ --sidebar-current-link-background-color: #fff;
+ --search-result-link-focus-background-color: #ccc;
+ --search-result-border-color: #aaa3;
+ --search-color: #000;
+ --search-error-code-background-color: #d0cccc;
+ --search-results-alias-color: #000;
+ --search-results-grey-color: #999;
+ --search-tab-title-count-color: #888;
+ --search-tab-button-not-selected-border-top-color: #e6e6e6;
+ --search-tab-button-not-selected-background: #e6e6e6;
+ --search-tab-button-selected-border-top-color: #0089ff;
+ --search-tab-button-selected-background: #fff;
+ --stab-background-color: #fff5d6;
+ --stab-code-color: #000;
+ --code-highlight-kw-color: #8959a8;
+ --code-highlight-kw-2-color: #4271ae;
+ --code-highlight-lifetime-color: #b76514;
+ --code-highlight-prelude-color: #4271ae;
+ --code-highlight-prelude-val-color: #c82829;
+ --code-highlight-number-color: #718c00;
+ --code-highlight-string-color: #718c00;
+ --code-highlight-literal-color: #c82829;
+ --code-highlight-attribute-color: #c82829;
+ --code-highlight-self-color: #c82829;
+ --code-highlight-macro-color: #3e999f;
+ --code-highlight-question-mark-color: #ff9011;
+ --code-highlight-comment-color: #8e908c;
+ --code-highlight-doc-comment-color: #4d4d4c;
+ --src-line-numbers-span-color: #c67e2d;
+ --src-line-number-highlighted-background-color: #fdffd3;
+ --test-arrow-color: #f5f5f5;
+ --test-arrow-background-color: rgba(78, 139, 202, 0.2);
+ --test-arrow-hover-color: #f5f5f5;
+ --test-arrow-hover-background-color: rgb(78, 139, 202);
+ --target-background-color: #fdffd3;
+ --target-border-color: #ad7c37;
+ --kbd-color: #000;
+ --kbd-background: #fafbfc;
+ --kbd-box-shadow-color: #c6cbd1;
+ --rust-logo-filter: initial;
+ /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+ --crate-search-div-filter: invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg)
+ brightness(114%) contrast(76%);
+ --crate-search-div-hover-filter: invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg)
+ brightness(96%) contrast(93%);
+ --crate-search-hover-border: #717171;
+ --src-sidebar-background-selected: #fff;
+ --src-sidebar-background-hover: #e0e0e0;
+ --table-alt-row-background-color: #f5f5f5;
+ --codeblock-link-background: #eee;
+ --scrape-example-toggle-line-background: #ccc;
+ --scrape-example-toggle-line-hover-background: #999;
+ --scrape-example-code-line-highlight: #fcffd6;
+ --scrape-example-code-line-highlight-focus: #f6fdb0;
+ --scrape-example-help-border-color: #555;
+ --scrape-example-help-color: #333;
+ --scrape-example-help-hover-border-color: #000;
+ --scrape-example-help-hover-color: #000;
+ --scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1);
+ --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0);
+}
+/* End theme: light */
+
+@media (prefers-color-scheme: dark) {
+ /* Begin theme: dark */
+ :root {
+ --main-background-color: #353535;
+ --main-color: #ddd;
+ --settings-input-color: #2196f3;
+ --settings-input-border-color: #999;
+ --settings-button-color: #000;
+ --settings-button-border-focus: #ffb900;
+ --sidebar-background-color: #505050;
+ --sidebar-background-color-hover: #676767;
+ --code-block-background-color: #2A2A2A;
+ --scrollbar-track-background-color: #717171;
+ --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;
+ --toggle-filter: invert(100%);
+ --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);
+ --warning-border-color: #ff8e00;
+ --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;
+ --sidebar-link-color: #fdbf35;
+ --sidebar-current-link-background-color: #444;
+ --search-result-link-focus-background-color: #616161;
+ --search-result-border-color: #aaa3;
+ --search-color: #111;
+ --search-error-code-background-color: #484848;
+ --search-results-alias-color: #fff;
+ --search-results-grey-color: #ccc;
+ --search-tab-title-count-color: #888;
+ --search-tab-button-not-selected-border-top-color: #252525;
+ --search-tab-button-not-selected-background: #252525;
+ --search-tab-button-selected-border-top-color: #0089ff;
+ --search-tab-button-selected-background: #353535;
+ --stab-background-color: #314559;
+ --stab-code-color: #e6e1cf;
+ --code-highlight-kw-color: #ab8ac1;
+ --code-highlight-kw-2-color: #769acb;
+ --code-highlight-lifetime-color: #d97f26;
+ --code-highlight-prelude-color: #769acb;
+ --code-highlight-prelude-val-color: #ee6868;
+ --code-highlight-number-color: #83a300;
+ --code-highlight-string-color: #83a300;
+ --code-highlight-literal-color: #ee6868;
+ --code-highlight-attribute-color: #ee6868;
+ --code-highlight-self-color: #ee6868;
+ --code-highlight-macro-color: #3e999f;
+ --code-highlight-question-mark-color: #ff9011;
+ --code-highlight-comment-color: #8d8d8b;
+ --code-highlight-doc-comment-color: #8ca375;
+ --src-line-numbers-span-color: #3b91e2;
+ --src-line-number-highlighted-background-color: #0a042f;
+ --test-arrow-color: #dedede;
+ --test-arrow-background-color: rgba(78, 139, 202, 0.2);
+ --test-arrow-hover-color: #dedede;
+ --test-arrow-hover-background-color: #4e8bca;
+ --target-background-color: #494a3d;
+ --target-border-color: #bb7410;
+ --kbd-color: #000;
+ --kbd-background: #fafbfc;
+ --kbd-box-shadow-color: #c6cbd1;
+ --rust-logo-filter: drop-shadow(1px 0 0px #fff)
+ drop-shadow(0 1px 0 #fff)
+ drop-shadow(-1px 0 0 #fff)
+ drop-shadow(0 -1px 0 #fff);
+ /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+ --crate-search-div-filter: invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg)
+ brightness(90%) contrast(90%);
+ --crate-search-div-hover-filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg)
+ brightness(100%) contrast(91%);
+ --crate-search-hover-border: #2196f3;
+ --src-sidebar-background-selected: #333;
+ --src-sidebar-background-hover: #444;
+ --table-alt-row-background-color: #2a2a2a;
+ --codeblock-link-background: #333;
+ --scrape-example-toggle-line-background: #999;
+ --scrape-example-toggle-line-hover-background: #c5c5c5;
+ --scrape-example-code-line-highlight: #5b3b01;
+ --scrape-example-code-line-highlight-focus: #7c4b0f;
+ --scrape-example-help-border-color: #aaa;
+ --scrape-example-help-color: #eee;
+ --scrape-example-help-hover-border-color: #fff;
+ --scrape-example-help-hover-color: #fff;
+ --scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1);
+ --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0);
+ }
+/* End theme: dark */
+}
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index b1de8c152..47f9e6502 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -270,7 +270,7 @@ ul ul, ol ul, ul ol, ol ol {
margin-bottom: .625em;
}
-p {
+p, .docblock > .warning {
/* Paragraph spacing at least 1.5 times line spacing per Web Content Accessibility Guidelines.
Line-height is 1.5rem, so line spacing is .5rem; .75em is 1.5 times that.
https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html */
@@ -278,7 +278,7 @@ p {
}
/* For the last child of a div, the margin will be taken care of
by the margin-top of the next item. */
-p:last-child {
+p:last-child, .docblock > .warning:last-child {
margin: 0;
}
@@ -925,6 +925,70 @@ so that we can apply CSS-filters to change the arrow color in themes */
top: -5px;
}
+.setting-line {
+ margin: 1.2em 0.6em;
+}
+
+.setting-radio input, .setting-check input {
+ margin-right: 0.3em;
+ height: 1.2rem;
+ width: 1.2rem;
+ border: 2px solid var(--settings-input-border-color);
+ outline: none;
+ -webkit-appearance: none;
+ cursor: pointer;
+}
+.setting-radio input {
+ border-radius: 50%;
+}
+
+.setting-radio span, .setting-check span {
+ padding-bottom: 1px;
+}
+
+.setting-radio {
+ margin-top: 0.1em;
+ margin-bottom: 0.1em;
+ min-width: 3.8em;
+ padding: 0.3em;
+ display: inline-flex;
+ align-items: center;
+ cursor: pointer;
+}
+.setting-radio + .setting-radio {
+ margin-left: 0.5em;
+}
+
+.setting-check {
+ margin-right: 20px;
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+}
+
+.setting-radio input:checked {
+ box-shadow: inset 0 0 0 3px var(--main-background-color);
+ background-color: var(--settings-input-color);
+}
+.setting-check input:checked {
+ background-color: var(--settings-input-color);
+ border-width: 1px;
+ content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\
+ <path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\
+ <path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>');
+}
+.setting-radio input:focus, .setting-check input:focus {
+ box-shadow: 0 0 1px 1px var(--settings-input-color);
+}
+/* In here we combine both `:focus` and `:checked` properties. */
+.setting-radio input:checked:focus {
+ box-shadow: inset 0 0 0 3px var(--main-background-color),
+ 0 0 2px 2px var(--settings-input-color);
+}
+.setting-radio input:hover, .setting-check input:hover {
+ border-color: var(--settings-input-color) !important;
+}
+
/* use larger max-width for help popover, but not for help.html */
#help.popover {
max-width: 600px;
@@ -1096,7 +1160,7 @@ pre.rust .doccomment {
}
.example-wrap.ignore .tooltip {
- color: var(--codeblock-ignore-color);
+ color: var(--codeblock-ignore-color);
}
.example-wrap.compile_fail:hover .tooltip,
@@ -1124,6 +1188,26 @@ pre.rust .doccomment {
font-size: 1.25rem;
}
+/* This class only exists for users who want to draw attention to a particular element in their
+documentation. */
+.content .docblock .warning {
+ border-left: 2px solid var(--warning-border-color);
+ padding: 14px;
+ position: relative;
+ /* The "!important" part is required because the rule is otherwise overruled in this CSS
+ selector: ".docblock > :not(.more-examples-toggle):not(.example-wrap)" */
+ overflow-x: visible !important;
+}
+.content .docblock .warning::before {
+ color: var(--warning-border-color);
+ content: "ⓘ";
+ position: absolute;
+ left: -25px;
+ top: 5px;
+ font-weight: bold;
+ font-size: 1.25rem;
+}
+
a.test-arrow {
visibility: hidden;
position: absolute;
@@ -1271,6 +1355,7 @@ a.tooltip:hover::after {
#search-tabs .count {
font-size: 1rem;
+ font-variant-numeric: tabular-nums;
color: var(--search-tab-title-count-color);
}
@@ -1553,6 +1638,13 @@ However, it's not needed with smaller screen width because the doc/code block is
/* Media Queries */
+/* Make sure all the buttons line wrap at the same time */
+@media (max-width: 850px) {
+ #search-tabs .count {
+ display: block;
+ }
+}
+
/*
WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
If you update this line, then you also need to update the line with the same warning
@@ -1680,10 +1772,6 @@ in src-script.js
display: none !important;
}
- #search-tabs .count {
- display: block;
- }
-
#main-content > details.toggle > summary::before,
#main-content > div > details.toggle > summary::before {
left: -11px;
@@ -2014,3 +2102,413 @@ in src-script.js
}
/* End: styles for --scrape-examples feature */
+
+/* Begin: styles for themes
+
+ Keep the default light and dark themes synchronized with the ones
+ in noscript.css
+
+ The special "Begin theme" and "End theme" below are used by a lot of
+ tooling to ensure different themes all define all the variables. Do not
+ alter their formatting. */
+
+/* Begin theme: light */
+:root[data-theme="light"] {
+ --main-background-color: white;
+ --main-color: black;
+ --settings-input-color: #2196f3;
+ --settings-input-border-color: #717171;
+ --settings-button-color: #000;
+ --settings-button-border-focus: #717171;
+ --sidebar-background-color: #f5f5f5;
+ --sidebar-background-color-hover: #e0e0e0;
+ --code-block-background-color: #f5f5f5;
+ --scrollbar-track-background-color: #dcdcdc;
+ --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;
+ --toggle-filter: none;
+ --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);
+ --warning-border-color: #ff8e00;
+ --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;
+ --sidebar-link-color: #356da4;
+ --sidebar-current-link-background-color: #fff;
+ --search-result-link-focus-background-color: #ccc;
+ --search-result-border-color: #aaa3;
+ --search-color: #000;
+ --search-error-code-background-color: #d0cccc;
+ --search-results-alias-color: #000;
+ --search-results-grey-color: #999;
+ --search-tab-title-count-color: #888;
+ --search-tab-button-not-selected-border-top-color: #e6e6e6;
+ --search-tab-button-not-selected-background: #e6e6e6;
+ --search-tab-button-selected-border-top-color: #0089ff;
+ --search-tab-button-selected-background: #fff;
+ --stab-background-color: #fff5d6;
+ --stab-code-color: #000;
+ --code-highlight-kw-color: #8959a8;
+ --code-highlight-kw-2-color: #4271ae;
+ --code-highlight-lifetime-color: #b76514;
+ --code-highlight-prelude-color: #4271ae;
+ --code-highlight-prelude-val-color: #c82829;
+ --code-highlight-number-color: #718c00;
+ --code-highlight-string-color: #718c00;
+ --code-highlight-literal-color: #c82829;
+ --code-highlight-attribute-color: #c82829;
+ --code-highlight-self-color: #c82829;
+ --code-highlight-macro-color: #3e999f;
+ --code-highlight-question-mark-color: #ff9011;
+ --code-highlight-comment-color: #8e908c;
+ --code-highlight-doc-comment-color: #4d4d4c;
+ --src-line-numbers-span-color: #c67e2d;
+ --src-line-number-highlighted-background-color: #fdffd3;
+ --test-arrow-color: #f5f5f5;
+ --test-arrow-background-color: rgba(78, 139, 202, 0.2);
+ --test-arrow-hover-color: #f5f5f5;
+ --test-arrow-hover-background-color: rgb(78, 139, 202);
+ --target-background-color: #fdffd3;
+ --target-border-color: #ad7c37;
+ --kbd-color: #000;
+ --kbd-background: #fafbfc;
+ --kbd-box-shadow-color: #c6cbd1;
+ --rust-logo-filter: initial;
+ /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+ --crate-search-div-filter: invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg)
+ brightness(114%) contrast(76%);
+ --crate-search-div-hover-filter: invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg)
+ brightness(96%) contrast(93%);
+ --crate-search-hover-border: #717171;
+ --src-sidebar-background-selected: #fff;
+ --src-sidebar-background-hover: #e0e0e0;
+ --table-alt-row-background-color: #f5f5f5;
+ --codeblock-link-background: #eee;
+ --scrape-example-toggle-line-background: #ccc;
+ --scrape-example-toggle-line-hover-background: #999;
+ --scrape-example-code-line-highlight: #fcffd6;
+ --scrape-example-code-line-highlight-focus: #f6fdb0;
+ --scrape-example-help-border-color: #555;
+ --scrape-example-help-color: #333;
+ --scrape-example-help-hover-border-color: #000;
+ --scrape-example-help-hover-color: #000;
+ --scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1);
+ --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0);
+}
+/* End theme: light */
+
+/* Begin theme: dark */
+:root[data-theme="dark"] {
+ --main-background-color: #353535;
+ --main-color: #ddd;
+ --settings-input-color: #2196f3;
+ --settings-input-border-color: #999;
+ --settings-button-color: #000;
+ --settings-button-border-focus: #ffb900;
+ --sidebar-background-color: #505050;
+ --sidebar-background-color-hover: #676767;
+ --code-block-background-color: #2A2A2A;
+ --scrollbar-track-background-color: #717171;
+ --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;
+ --toggle-filter: invert(100%);
+ --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);
+ --warning-border-color: #ff8e00;
+ --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;
+ --sidebar-link-color: #fdbf35;
+ --sidebar-current-link-background-color: #444;
+ --search-result-link-focus-background-color: #616161;
+ --search-result-border-color: #aaa3;
+ --search-color: #111;
+ --search-error-code-background-color: #484848;
+ --search-results-alias-color: #fff;
+ --search-results-grey-color: #ccc;
+ --search-tab-title-count-color: #888;
+ --search-tab-button-not-selected-border-top-color: #252525;
+ --search-tab-button-not-selected-background: #252525;
+ --search-tab-button-selected-border-top-color: #0089ff;
+ --search-tab-button-selected-background: #353535;
+ --stab-background-color: #314559;
+ --stab-code-color: #e6e1cf;
+ --code-highlight-kw-color: #ab8ac1;
+ --code-highlight-kw-2-color: #769acb;
+ --code-highlight-lifetime-color: #d97f26;
+ --code-highlight-prelude-color: #769acb;
+ --code-highlight-prelude-val-color: #ee6868;
+ --code-highlight-number-color: #83a300;
+ --code-highlight-string-color: #83a300;
+ --code-highlight-literal-color: #ee6868;
+ --code-highlight-attribute-color: #ee6868;
+ --code-highlight-self-color: #ee6868;
+ --code-highlight-macro-color: #3e999f;
+ --code-highlight-question-mark-color: #ff9011;
+ --code-highlight-comment-color: #8d8d8b;
+ --code-highlight-doc-comment-color: #8ca375;
+ --src-line-numbers-span-color: #3b91e2;
+ --src-line-number-highlighted-background-color: #0a042f;
+ --test-arrow-color: #dedede;
+ --test-arrow-background-color: rgba(78, 139, 202, 0.2);
+ --test-arrow-hover-color: #dedede;
+ --test-arrow-hover-background-color: #4e8bca;
+ --target-background-color: #494a3d;
+ --target-border-color: #bb7410;
+ --kbd-color: #000;
+ --kbd-background: #fafbfc;
+ --kbd-box-shadow-color: #c6cbd1;
+ --rust-logo-filter: drop-shadow(1px 0 0px #fff)
+ drop-shadow(0 1px 0 #fff)
+ drop-shadow(-1px 0 0 #fff)
+ drop-shadow(0 -1px 0 #fff);
+ /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+ --crate-search-div-filter: invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg)
+ brightness(90%) contrast(90%);
+ --crate-search-div-hover-filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg)
+ brightness(100%) contrast(91%);
+ --crate-search-hover-border: #2196f3;
+ --src-sidebar-background-selected: #333;
+ --src-sidebar-background-hover: #444;
+ --table-alt-row-background-color: #2a2a2a;
+ --codeblock-link-background: #333;
+ --scrape-example-toggle-line-background: #999;
+ --scrape-example-toggle-line-hover-background: #c5c5c5;
+ --scrape-example-code-line-highlight: #5b3b01;
+ --scrape-example-code-line-highlight-focus: #7c4b0f;
+ --scrape-example-help-border-color: #aaa;
+ --scrape-example-help-color: #eee;
+ --scrape-example-help-hover-border-color: #fff;
+ --scrape-example-help-hover-color: #fff;
+ --scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1);
+ --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0);
+}
+/* End theme: dark */
+
+/* Begin theme: ayu */
+/*
+Based off of the Ayu theme
+Original by Dempfi (https://github.com/dempfi/ayu)
+*/
+:root[data-theme="ayu"] {
+ --main-background-color: #0f1419;
+ --main-color: #c5c5c5;
+ --settings-input-color: #ffb454;
+ --settings-input-border-color: #999;
+ --settings-button-color: #fff;
+ --settings-button-border-focus: #e0e0e0;
+ --sidebar-background-color: #14191f;
+ --sidebar-background-color-hover: rgba(70, 70, 70, 0.33);
+ --code-block-background-color: #191f26;
+ --scrollbar-track-background-color: transparent;
+ --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;
+ --toggle-filter: invert(100%);
+ --search-input-focused-border-color: #5c6773; /* Same as `--border-color`. */
+ --copy-path-button-color: #fff;
+ --copy-path-img-filter: invert(70%);
+ --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);
+ --warning-border-color: #ff8e00;
+ --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;
+ --sidebar-link-color: #53b1db;
+ --sidebar-current-link-background-color: transparent;
+ --search-result-link-focus-background-color: #3c3c3c;
+ --search-result-border-color: #aaa3;
+ --search-color: #fff;
+ --search-error-code-background-color: #4f4c4c;
+ --search-results-alias-color: #c5c5c5;
+ --search-results-grey-color: #999;
+ --search-tab-title-count-color: #888;
+ --search-tab-button-not-selected-border-top-color: none;
+ --search-tab-button-not-selected-background: transparent !important;
+ --search-tab-button-selected-border-top-color: none;
+ --search-tab-button-selected-background: #141920 !important;
+ --stab-background-color: #314559;
+ --stab-code-color: #e6e1cf;
+ --code-highlight-kw-color: #ff7733;
+ --code-highlight-kw-2-color: #ff7733;
+ --code-highlight-lifetime-color: #ff7733;
+ --code-highlight-prelude-color: #69f2df;
+ --code-highlight-prelude-val-color: #ff7733;
+ --code-highlight-number-color: #b8cc52;
+ --code-highlight-string-color: #b8cc52;
+ --code-highlight-literal-color: #ff7733;
+ --code-highlight-attribute-color: #e6e1cf;
+ --code-highlight-self-color: #36a3d9;
+ --code-highlight-macro-color: #a37acc;
+ --code-highlight-question-mark-color: #ff9011;
+ --code-highlight-comment-color: #788797;
+ --code-highlight-doc-comment-color: #a1ac88;
+ --src-line-numbers-span-color: #5c6773;
+ --src-line-number-highlighted-background-color: rgba(255, 236, 164, 0.06);
+ --test-arrow-color: #788797;
+ --test-arrow-background-color: rgba(57, 175, 215, 0.09);
+ --test-arrow-hover-color: #c5c5c5;
+ --test-arrow-hover-background-color: rgba(57, 175, 215, 0.368);
+ --target-background-color: rgba(255, 236, 164, 0.06);
+ --target-border-color: rgba(255, 180, 76, 0.85);
+ --kbd-color: #c5c5c5;
+ --kbd-background: #314559;
+ --kbd-box-shadow-color: #5c6773;
+ --rust-logo-filter: drop-shadow(1px 0 0px #fff)
+ drop-shadow(0 1px 0 #fff)
+ drop-shadow(-1px 0 0 #fff)
+ drop-shadow(0 -1px 0 #fff);
+ /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
+ --crate-search-div-filter: invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg)
+ brightness(94%) contrast(94%);
+ --crate-search-div-hover-filter: invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg)
+ brightness(113%) contrast(76%);
+ --crate-search-hover-border: #e0e0e0;
+ --src-sidebar-background-selected: #14191f;
+ --src-sidebar-background-hover: #14191f;
+ --table-alt-row-background-color: #191f26;
+ --codeblock-link-background: #333;
+ --scrape-example-toggle-line-background: #999;
+ --scrape-example-toggle-line-hover-background: #c5c5c5;
+ --scrape-example-code-line-highlight: #5b3b01;
+ --scrape-example-code-line-highlight-focus: #7c4b0f;
+ --scrape-example-help-border-color: #aaa;
+ --scrape-example-help-color: #eee;
+ --scrape-example-help-hover-border-color: #fff;
+ --scrape-example-help-hover-color: #fff;
+ --scrape-example-code-wrapper-background-start: rgba(15, 20, 25, 1);
+ --scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0);
+}
+
+:root[data-theme="ayu"] h1,
+:root[data-theme="ayu"] h2,
+:root[data-theme="ayu"] h3,
+:root[data-theme="ayu"] h4,
+:where(:root[data-theme="ayu"]) h1 a,
+:root[data-theme="ayu"] .sidebar h2 a,
+:root[data-theme="ayu"] .sidebar h3 a,
+:root[data-theme="ayu"] #source-sidebar > .title {
+ color: #fff;
+}
+
+:root[data-theme="ayu"] .docblock code {
+ color: #ffb454;
+}
+
+:root[data-theme="ayu"] .docblock a > code {
+ color: #39AFD7 !important;
+}
+
+:root[data-theme="ayu"] .code-header,
+:root[data-theme="ayu"] .docblock pre > code,
+:root[data-theme="ayu"] pre,
+:root[data-theme="ayu"] pre > code,
+:root[data-theme="ayu"] .item-info code,
+:root[data-theme="ayu"] .rustdoc.source .example-wrap {
+ color: #e6e1cf;
+}
+
+:root[data-theme="ayu"] .sidebar .current,
+:root[data-theme="ayu"] .sidebar a:hover,
+:root[data-theme="ayu"] #src-sidebar div.files > a:hover,
+:root[data-theme="ayu"] details.dir-entry summary:hover,
+:root[data-theme="ayu"] #src-sidebar div.files > a:focus,
+:root[data-theme="ayu"] details.dir-entry summary:focus,
+:root[data-theme="ayu"] #src-sidebar div.files > a.selected {
+ color: #ffb44c;
+}
+
+:root[data-theme="ayu"] .sidebar-elems .location {
+ color: #ff7733;
+}
+
+:root[data-theme="ayu"] .src-line-numbers .line-highlighted {
+ color: #708090;
+ padding-right: 7px;
+ border-right: 1px solid #ffb44c;
+}
+
+:root[data-theme="ayu"] .search-results a:hover,
+:root[data-theme="ayu"] .search-results a:focus {
+ color: #fff !important;
+ background-color: #3c3c3c;
+}
+
+:root[data-theme="ayu"] .search-results a {
+ color: #0096cf;
+}
+
+:root[data-theme="ayu"] .search-results a div.desc {
+ color: #c5c5c5;
+}
+
+:root[data-theme="ayu"] .result-name .primitive > i,
+:root[data-theme="ayu"] .result-name .keyword > i {
+ color: #788797;
+}
+
+:root[data-theme="ayu"] #search-tabs > button.selected {
+ border-bottom: 1px solid #ffb44c !important;
+ border-top: none;
+}
+:root[data-theme="ayu"] #search-tabs > button:not(.selected) {
+ border: none;
+ background-color: transparent !important;
+}
+:root[data-theme="ayu"] #search-tabs > button:hover {
+ border-bottom: 1px solid rgba(242, 151, 24, 0.3);
+}
+
+:root[data-theme="ayu"] #settings-menu > a img {
+ filter: invert(100);
+}
+/* End theme: ayu */
+
+/* End: styles for themes */
diff --git a/src/librustdoc/html/static/css/settings.css b/src/librustdoc/html/static/css/settings.css
deleted file mode 100644
index c1324c076..000000000
--- a/src/librustdoc/html/static/css/settings.css
+++ /dev/null
@@ -1,63 +0,0 @@
-.setting-line {
- margin: 1.2em 0.6em;
-}
-
-.setting-radio input, .setting-check input {
- margin-right: 0.3em;
- height: 1.2rem;
- width: 1.2rem;
- border: 2px solid var(--settings-input-border-color);
- outline: none;
- -webkit-appearance: none;
- cursor: pointer;
-}
-.setting-radio input {
- border-radius: 50%;
-}
-
-.setting-radio span, .setting-check span {
- padding-bottom: 1px;
-}
-
-.setting-radio {
- margin-top: 0.1em;
- margin-bottom: 0.1em;
- min-width: 3.8em;
- padding: 0.3em;
- display: inline-flex;
- align-items: center;
- cursor: pointer;
-}
-.setting-radio + .setting-radio {
- margin-left: 0.5em;
-}
-
-.setting-check {
- margin-right: 20px;
- display: flex;
- align-items: center;
- cursor: pointer;
-}
-
-.setting-radio input:checked {
- box-shadow: inset 0 0 0 3px var(--main-background-color);
- background-color: var(--settings-input-color);
-}
-.setting-check input:checked {
- background-color: var(--settings-input-color);
- border-width: 1px;
- content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\
- <path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\
- <path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>');
-}
-.setting-radio input:focus, .setting-check input:focus {
- box-shadow: 0 0 1px 1px var(--settings-input-color);
-}
-/* In here we combine both `:focus` and `:checked` properties. */
-.setting-radio input:checked:focus {
- box-shadow: inset 0 0 0 3px var(--main-background-color),
- 0 0 2px 2px var(--settings-input-color);
-}
-.setting-radio input:hover, .setting-check input:hover {
- border-color: var(--settings-input-color) !important;
-}
diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css
deleted file mode 100644
index d8dae51eb..000000000
--- a/src/librustdoc/html/static/css/themes/ayu.css
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
-Based off of the Ayu theme
-Original by Dempfi (https://github.com/dempfi/ayu)
-*/
-
-:root {
- --main-background-color: #0f1419;
- --main-color: #c5c5c5;
- --settings-input-color: #ffb454;
- --settings-input-border-color: #999;
- --settings-button-color: #fff;
- --settings-button-border-focus: #e0e0e0;
- --sidebar-background-color: #14191f;
- --sidebar-background-color-hover: rgba(70, 70, 70, 0.33);
- --code-block-background-color: #191f26;
- --scrollbar-track-background-color: transparent;
- --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;
- --toggle-filter: invert(100%);
- --search-input-focused-border-color: #5c6773; /* Same as `--border-color`. */
- --copy-path-button-color: #fff;
- --copy-path-img-filter: invert(70%);
- --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;
- --sidebar-link-color: #53b1db;
- --sidebar-current-link-background-color: transparent;
- --search-result-link-focus-background-color: #3c3c3c;
- --search-result-border-color: #aaa3;
- --search-color: #fff;
- --search-error-code-background-color: #4f4c4c;
- --search-results-alias-color: #c5c5c5;
- --search-results-grey-color: #999;
- --search-tab-title-count-color: #888;
- --search-tab-button-not-selected-border-top-color: none;
- --search-tab-button-not-selected-background: transparent !important;
- --search-tab-button-selected-border-top-color: none;
- --search-tab-button-selected-background: #141920 !important;
- --stab-background-color: #314559;
- --stab-code-color: #e6e1cf;
- --code-highlight-kw-color: #ff7733;
- --code-highlight-kw-2-color: #ff7733;
- --code-highlight-lifetime-color: #ff7733;
- --code-highlight-prelude-color: #69f2df;
- --code-highlight-prelude-val-color: #ff7733;
- --code-highlight-number-color: #b8cc52;
- --code-highlight-string-color: #b8cc52;
- --code-highlight-literal-color: #ff7733;
- --code-highlight-attribute-color: #e6e1cf;
- --code-highlight-self-color: #36a3d9;
- --code-highlight-macro-color: #a37acc;
- --code-highlight-question-mark-color: #ff9011;
- --code-highlight-comment-color: #788797;
- --code-highlight-doc-comment-color: #a1ac88;
- --src-line-numbers-span-color: #5c6773;
- --src-line-number-highlighted-background-color: rgba(255, 236, 164, 0.06);
- --test-arrow-color: #788797;
- --test-arrow-background-color: rgba(57, 175, 215, 0.09);
- --test-arrow-hover-color: #c5c5c5;
- --test-arrow-hover-background-color: rgba(57, 175, 215, 0.368);
- --target-background-color: rgba(255, 236, 164, 0.06);
- --target-border-color: rgba(255, 180, 76, 0.85);
- --kbd-color: #c5c5c5;
- --kbd-background: #314559;
- --kbd-box-shadow-color: #5c6773;
- --rust-logo-filter: drop-shadow(1px 0 0px #fff)
- drop-shadow(0 1px 0 #fff)
- drop-shadow(-1px 0 0 #fff)
- drop-shadow(0 -1px 0 #fff);
- /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
- --crate-search-div-filter: invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg)
- brightness(94%) contrast(94%);
- --crate-search-div-hover-filter: invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg)
- brightness(113%) contrast(76%);
- --crate-search-hover-border: #e0e0e0;
- --src-sidebar-background-selected: #14191f;
- --src-sidebar-background-hover: #14191f;
- --table-alt-row-background-color: #191f26;
- --codeblock-link-background: #333;
- --scrape-example-toggle-line-background: #999;
- --scrape-example-toggle-line-hover-background: #c5c5c5;
- --scrape-example-code-line-highlight: rgb(91, 59, 1);
- --scrape-example-code-line-highlight-focus: rgb(124, 75, 15);
- --scrape-example-help-border-color: #aaa;
- --scrape-example-help-color: #eee;
- --scrape-example-help-hover-border-color: #fff;
- --scrape-example-help-hover-color: #fff;
- --scrape-example-code-wrapper-background-start: rgba(15, 20, 25, 1);
- --scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0);
-}
-
-h1, h2, h3, h4,
-h1 a, .sidebar h2 a, .sidebar h3 a,
-#src-sidebar > .title {
- color: #fff;
-}
-h4 {
- border: none;
-}
-
-.docblock code {
- color: #ffb454;
-}
-.docblock a > code {
- color: #39AFD7 !important;
-}
-.code-header,
-.docblock pre > code,
-pre, pre > code,
-.item-info code,
-.rustdoc.src .example-wrap {
- color: #e6e1cf;
-}
-
-.sidebar .current,
-.sidebar a:hover,
-#src-sidebar div.files > a:hover, details.dir-entry summary:hover,
-#src-sidebar div.files > a:focus, details.dir-entry summary:focus,
-#src-sidebar div.files > a.selected {
- color: #ffb44c;
-}
-
-.sidebar-elems .location {
- color: #ff7733;
-}
-
-.src-line-numbers .line-highlighted {
- color: #708090;
- padding-right: 7px;
- border-right: 1px solid #ffb44c;
-}
-
-.search-results a:hover,
-.search-results a:focus {
- color: #fff !important;
- background-color: #3c3c3c;
-}
-
-.search-results a {
- color: #0096cf;
-}
-.search-results a div.desc {
- color: #c5c5c5;
-}
-
-.result-name .primitive > i, .result-name .keyword > i {
- color: #788797;
-}
-
-#search-tabs > button.selected {
- border-bottom: 1px solid #ffb44c !important;
- border-top: none;
-}
-#search-tabs > button:not(.selected) {
- border: none;
- background-color: transparent !important;
-}
-#search-tabs > button:hover {
- border-bottom: 1px solid rgba(242, 151, 24, 0.3);
-}
-
-#settings-menu > a img {
- filter: invert(100);
-}
diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css
deleted file mode 100644
index 2b3029887..000000000
--- a/src/librustdoc/html/static/css/themes/dark.css
+++ /dev/null
@@ -1,101 +0,0 @@
-:root {
- --main-background-color: #353535;
- --main-color: #ddd;
- --settings-input-color: #2196f3;
- --settings-input-border-color: #999;
- --settings-button-color: #000;
- --settings-button-border-focus: #ffb900;
- --sidebar-background-color: #505050;
- --sidebar-background-color-hover: #676767;
- --code-block-background-color: #2A2A2A;
- --scrollbar-track-background-color: #717171;
- --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;
- --toggle-filter: invert(100%);
- --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;
- --sidebar-link-color: #fdbf35;
- --sidebar-current-link-background-color: #444;
- --search-result-link-focus-background-color: #616161;
- --search-result-border-color: #aaa3;
- --search-color: #111;
- --search-error-code-background-color: #484848;
- --search-results-alias-color: #fff;
- --search-results-grey-color: #ccc;
- --search-tab-title-count-color: #888;
- --search-tab-button-not-selected-border-top-color: #252525;
- --search-tab-button-not-selected-background: #252525;
- --search-tab-button-selected-border-top-color: #0089ff;
- --search-tab-button-selected-background: #353535;
- --stab-background-color: #314559;
- --stab-code-color: #e6e1cf;
- --code-highlight-kw-color: #ab8ac1;
- --code-highlight-kw-2-color: #769acb;
- --code-highlight-lifetime-color: #d97f26;
- --code-highlight-prelude-color: #769acb;
- --code-highlight-prelude-val-color: #ee6868;
- --code-highlight-number-color: #83a300;
- --code-highlight-string-color: #83a300;
- --code-highlight-literal-color: #ee6868;
- --code-highlight-attribute-color: #ee6868;
- --code-highlight-self-color: #ee6868;
- --code-highlight-macro-color: #3e999f;
- --code-highlight-question-mark-color: #ff9011;
- --code-highlight-comment-color: #8d8d8b;
- --code-highlight-doc-comment-color: #8ca375;
- --src-line-numbers-span-color: #3b91e2;
- --src-line-number-highlighted-background-color: #0a042f;
- --test-arrow-color: #dedede;
- --test-arrow-background-color: rgba(78, 139, 202, 0.2);
- --test-arrow-hover-color: #dedede;
- --test-arrow-hover-background-color: rgb(78, 139, 202);
- --target-background-color: #494a3d;
- --target-border-color: #bb7410;
- --kbd-color: #000;
- --kbd-background: #fafbfc;
- --kbd-box-shadow-color: #c6cbd1;
- --rust-logo-filter: drop-shadow(1px 0 0px #fff)
- drop-shadow(0 1px 0 #fff)
- drop-shadow(-1px 0 0 #fff)
- drop-shadow(0 -1px 0 #fff);
- /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
- --crate-search-div-filter: invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg)
- brightness(90%) contrast(90%);
- --crate-search-div-hover-filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg)
- brightness(100%) contrast(91%);
- --crate-search-hover-border: #2196f3;
- --src-sidebar-background-selected: #333;
- --src-sidebar-background-hover: #444;
- --table-alt-row-background-color: #2A2A2A;
- --codeblock-link-background: #333;
- --scrape-example-toggle-line-background: #999;
- --scrape-example-toggle-line-hover-background: #c5c5c5;
- --scrape-example-code-line-highlight: rgb(91, 59, 1);
- --scrape-example-code-line-highlight-focus: rgb(124, 75, 15);
- --scrape-example-help-border-color: #aaa;
- --scrape-example-help-color: #eee;
- --scrape-example-help-hover-border-color: #fff;
- --scrape-example-help-hover-color: #fff;
- --scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1);
- --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0);
-}
diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css
deleted file mode 100644
index 56fd8cbef..000000000
--- a/src/librustdoc/html/static/css/themes/light.css
+++ /dev/null
@@ -1,98 +0,0 @@
-:root {
- --main-background-color: white;
- --main-color: black;
- --settings-input-color: #2196f3;
- --settings-input-border-color: #717171;
- --settings-button-color: #000;
- --settings-button-border-focus: #717171;
- --sidebar-background-color: #F5F5F5;
- --sidebar-background-color-hover: #E0E0E0;
- --code-block-background-color: #F5F5F5;
- --scrollbar-track-background-color: #dcdcdc;
- --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;
- --toggle-filter: none;
- --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;
- --sidebar-link-color: #356da4;
- --sidebar-current-link-background-color: #fff;
- --search-result-link-focus-background-color: #ccc;
- --search-result-border-color: #aaa3;
- --search-color: #000;
- --search-error-code-background-color: #d0cccc;
- --search-results-alias-color: #000;
- --search-results-grey-color: #999;
- --search-tab-title-count-color: #888;
- --search-tab-button-not-selected-border-top-color: #e6e6e6;
- --search-tab-button-not-selected-background: #e6e6e6;
- --search-tab-button-selected-border-top-color: #0089ff;
- --search-tab-button-selected-background: #ffffff;
- --stab-background-color: #fff5d6;
- --stab-code-color: #000;
- --code-highlight-kw-color: #8959a8;
- --code-highlight-kw-2-color: #4271ae;
- --code-highlight-lifetime-color: #b76514;
- --code-highlight-prelude-color: #4271ae;
- --code-highlight-prelude-val-color: #c82829;
- --code-highlight-number-color: #718c00;
- --code-highlight-string-color: #718c00;
- --code-highlight-literal-color: #c82829;
- --code-highlight-attribute-color: #c82829;
- --code-highlight-self-color: #c82829;
- --code-highlight-macro-color: #3e999f;
- --code-highlight-question-mark-color: #ff9011;
- --code-highlight-comment-color: #8e908c;
- --code-highlight-doc-comment-color: #4d4d4c;
- --src-line-numbers-span-color: #c67e2d;
- --src-line-number-highlighted-background-color: #fdffd3;
- --test-arrow-color: #f5f5f5;
- --test-arrow-background-color: rgba(78, 139, 202, 0.2);
- --test-arrow-hover-color: #f5f5f5;
- --test-arrow-hover-background-color: rgb(78, 139, 202);
- --target-background-color: #fdffd3;
- --target-border-color: #ad7c37;
- --kbd-color: #000;
- --kbd-background: #fafbfc;
- --kbd-box-shadow-color: #c6cbd1;
- --rust-logo-filter: initial;
- /* match border-color; uses https://codepen.io/sosuke/pen/Pjoqqp */
- --crate-search-div-filter: invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg)
- brightness(114%) contrast(76%);
- --crate-search-div-hover-filter: invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg)
- brightness(96%) contrast(93%);
- --crate-search-hover-border: #717171;
- --src-sidebar-background-selected: #fff;
- --src-sidebar-background-hover: #e0e0e0;
- --table-alt-row-background-color: #F5F5F5;
- --codeblock-link-background: #eee;
- --scrape-example-toggle-line-background: #ccc;
- --scrape-example-toggle-line-hover-background: #999;
- --scrape-example-code-line-highlight: #fcffd6;
- --scrape-example-code-line-highlight-focus: #f6fdb0;
- --scrape-example-help-border-color: #555;
- --scrape-example-help-color: #333;
- --scrape-example-help-hover-border-color: #000;
- --scrape-example-help-hover-color: #000;
- --scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1);
- --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0);
-}
diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js
index f697abd07..c7811b43d 100644
--- a/src/librustdoc/html/static/js/externs.js
+++ b/src/librustdoc/html/static/js/externs.js
@@ -9,7 +9,7 @@ function initSearch(searchIndex){}
/**
* @typedef {{
* name: string,
- * id: integer,
+ * id: integer|null,
* fullPath: Array<string>,
* pathWithoutLast: Array<string>,
* pathLast: string,
@@ -37,6 +37,7 @@ let ParserState;
* args: Array<QueryElement>,
* returned: Array<QueryElement>,
* foundElems: number,
+ * totalElems: number,
* literalSearch: boolean,
* corrections: Array<{from: string, to: integer}>,
* }}
@@ -103,7 +104,7 @@ let ResultObject;
*
* fn something() -> Result<usize, usize>
*
- * If output was allowed to be any RawFunctionType, it would look like this
+ * If output was allowed to be any RawFunctionType, it would look like thi
*
* [[], [50, [3, 3]]]
*
@@ -113,10 +114,56 @@ let ResultObject;
* in favor of the pair of types interpretation. This is why the `(number|Array<RawFunctionType>)`
* is used instead of `(RawFunctionType|Array<RawFunctionType>)`.
*
+ * The output can be skipped if it's actually unit and there's no type constraints. If thi
+ * function accepts constrained generics, then the output will be unconditionally emitted, and
+ * after it will come a list of trait constraints. The position of the item in the list will
+ * determine which type parameter it is. For example:
+ *
+ * [1, 2, 3, 4, 5]
+ * ^ ^ ^ ^ ^
+ * | | | | - generic parameter (-3) of trait 5
+ * | | | - generic parameter (-2) of trait 4
+ * | | - generic parameter (-1) of trait 3
+ * | - this function returns a single value (type 2)
+ * - this function takes a single input parameter (type 1)
+ *
+ * Or, for a less contrived version:
+ *
+ * [[[4, -1], 3], [[5, -1]], 11]
+ * -^^^^^^^---- ^^^^^^^ ^^
+ * | | | - generic parameter, roughly `where -1: 11`
+ * | | | since -1 is the type parameter and 11 the trait
+ * | | - function output 5<-1>
+ * | - the overall function signature is something like
+ * | `fn(4<-1>, 3) -> 5<-1> where -1: 11`
+ * - function input, corresponds roughly to 4<-1>
+ * 4 is an index into the `p` array for a type
+ * -1 is the generic parameter, given by 11
+ *
+ * If a generic parameter has multiple trait constraints, it gets wrapped in an array, just like
+ * function inputs and outputs:
+ *
+ * [-1, -1, [4, 3]]
+ * ^^^^^^ where -1: 4 + 3
+ *
+ * If a generic parameter's trait constraint has generic parameters, it gets wrapped in the array
+ * even if only one exists. In other words, the ambiguity of `4<3>` and `4 + 3` is resolved in
+ * favor of `4 + 3`:
+ *
+ * [-1, -1, [[4, 3]]]
+ * ^^^^^^^^ where -1: 4 + 3
+ *
+ * [-1, -1, [5, [4, 3]]]
+ * ^^^^^^^^^^^ where -1: 5, -2: 4 + 3
+ *
+ * If a generic parameter has no trait constraints (like in Rust, the `Sized` constraint i
+ * implied and a fake `?Sized` constraint used to note its absence), it will be filled in with 0.
+ *
* @typedef {(
* 0 |
* [(number|Array<RawFunctionType>)] |
- * [(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)]
+ * [(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)] |
+ * Array<(number|Array<RawFunctionType>)>
* )}
*/
let RawFunctionSearchType;
@@ -136,6 +183,7 @@ let RawFunctionType;
* @typedef {{
* inputs: Array<FunctionType>,
* output: Array<FunctionType>,
+ * where_clause: Array<Array<FunctionType>>,
* }}
*/
let FunctionSearchType;
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 254b0d8bf..eb256455b 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -49,10 +49,12 @@ window.currentCrate = getVar("current-crate");
function setMobileTopbar() {
// FIXME: It would be nicer to generate this text content directly in HTML,
// but with the current code it's hard to get the right information in the right place.
- const mobileLocationTitle = document.querySelector(".mobile-topbar h2");
+ const mobileTopbar = document.querySelector(".mobile-topbar");
const locationTitle = document.querySelector(".sidebar h2.location");
- if (mobileLocationTitle && locationTitle) {
- mobileLocationTitle.innerHTML = locationTitle.innerHTML;
+ if (mobileTopbar && locationTitle) {
+ const mobileTitle = document.createElement("h2");
+ mobileTitle.innerHTML = locationTitle.innerHTML;
+ mobileTopbar.appendChild(mobileTitle);
}
}
@@ -176,13 +178,6 @@ function browserSupportsHistoryApi() {
return window.history && typeof window.history.pushState === "function";
}
-function loadCss(cssUrl) {
- const link = document.createElement("link");
- link.href = cssUrl;
- link.rel = "stylesheet";
- document.getElementsByTagName("head")[0].appendChild(link);
-}
-
function preLoadCss(cssUrl) {
// https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload
const link = document.createElement("link");
@@ -210,11 +205,7 @@ function preLoadCss(cssUrl) {
event.preventDefault();
// Sending request for the CSS and the JS files at the same time so it will
// hopefully be loaded when the JS will generate the settings content.
- loadCss(getVar("static-root-path") + getVar("settings-css"));
loadScript(getVar("static-root-path") + getVar("settings-js"));
- preLoadCss(getVar("static-root-path") + getVar("theme-light-css"));
- preLoadCss(getVar("static-root-path") + getVar("theme-dark-css"));
- preLoadCss(getVar("static-root-path") + getVar("theme-ayu-css"));
// Pre-load all theme CSS files, so that switching feels seamless.
//
// When loading settings.html as a standalone page, the equivalent HTML is
@@ -499,7 +490,7 @@ function preLoadCss(cssUrl) {
block("static", "static", "Statics");
block("trait", "traits", "Traits");
block("fn", "functions", "Functions");
- block("type", "types", "Type Definitions");
+ block("type", "types", "Type Aliases");
block("foreigntype", "foreign-types", "Foreign Types");
block("keyword", "keywords", "Keywords");
block("traitalias", "trait-aliases", "Trait Aliases");
@@ -852,14 +843,14 @@ function preLoadCss(cssUrl) {
window.CURRENT_TOOLTIP_ELEMENT = wrapper;
window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE = e;
clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);
- wrapper.onpointerenter = function(ev) {
+ wrapper.onpointerenter = ev => {
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
if (ev.pointerType !== "mouse") {
return;
}
clearTooltipHoverTimeout(e);
};
- wrapper.onpointerleave = function(ev) {
+ wrapper.onpointerleave = ev => {
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
if (ev.pointerType !== "mouse") {
return;
@@ -963,38 +954,38 @@ function preLoadCss(cssUrl) {
}
onEachLazy(document.getElementsByClassName("tooltip"), e => {
- e.onclick = function() {
- this.TOOLTIP_FORCE_VISIBLE = this.TOOLTIP_FORCE_VISIBLE ? false : true;
- if (window.CURRENT_TOOLTIP_ELEMENT && !this.TOOLTIP_FORCE_VISIBLE) {
+ e.onclick = () => {
+ e.TOOLTIP_FORCE_VISIBLE = e.TOOLTIP_FORCE_VISIBLE ? false : true;
+ if (window.CURRENT_TOOLTIP_ELEMENT && !e.TOOLTIP_FORCE_VISIBLE) {
hideTooltip(true);
} else {
- showTooltip(this);
+ showTooltip(e);
window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex", "0");
window.CURRENT_TOOLTIP_ELEMENT.focus();
window.CURRENT_TOOLTIP_ELEMENT.onblur = tooltipBlurHandler;
}
return false;
};
- e.onpointerenter = function(ev) {
+ e.onpointerenter = ev => {
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
if (ev.pointerType !== "mouse") {
return;
}
- setTooltipHoverTimeout(this, true);
+ setTooltipHoverTimeout(e, true);
};
- e.onpointermove = function(ev) {
+ e.onpointermove = ev => {
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
if (ev.pointerType !== "mouse") {
return;
}
- setTooltipHoverTimeout(this, true);
+ setTooltipHoverTimeout(e, true);
};
- e.onpointerleave = function(ev) {
+ e.onpointerleave = ev => {
// If this is a synthetic touch event, ignore it. A click event will be along shortly.
if (ev.pointerType !== "mouse") {
return;
}
- if (!this.TOOLTIP_FORCE_VISIBLE &&
+ if (!e.TOOLTIP_FORCE_VISIBLE &&
!elemIsInParent(ev.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT)) {
// Tooltip pointer leave gesture:
//
@@ -1139,7 +1130,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
*
* Pass "true" to reset focus for tooltip popovers.
*/
- window.hideAllModals = function(switchFocus) {
+ window.hideAllModals = switchFocus => {
hideSidebar();
window.hidePopoverMenus();
hideTooltip(switchFocus);
@@ -1148,7 +1139,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
/**
* Hide all the popover menus.
*/
- window.hidePopoverMenus = function() {
+ window.hidePopoverMenus = () => {
onEachLazy(document.querySelectorAll(".search-form .popover"), elem => {
elem.style.display = "none";
});
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 42088e735..2f0cae0a4 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -3,6 +3,17 @@
"use strict";
+// polyfill
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSpliced
+if (!Array.prototype.toSpliced) {
+ // Can't use arrow functions, because we want `this`
+ Array.prototype.toSpliced = function() {
+ const me = this.slice();
+ Array.prototype.splice.apply(me, arguments);
+ return me;
+ };
+}
+
(function() {
// This mapping table should match the discriminants of
// `rustdoc::formats::item_type::ItemType` type in Rust.
@@ -33,6 +44,7 @@ const itemTypes = [
"attr",
"derive",
"traitalias",
+ "generic",
];
const longItemTypes = [
@@ -67,6 +79,7 @@ const longItemTypes = [
// used for special search precedence
const TY_PRIMITIVE = itemTypes.indexOf("primitive");
const TY_KEYWORD = itemTypes.indexOf("keyword");
+const TY_GENERIC = itemTypes.indexOf("generic");
const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
function hasOwnPropertyRustdoc(obj, property) {
@@ -252,7 +265,7 @@ function initSearch(rawSearchIndex) {
/**
* Add an item to the type Name->ID map, or, if one already exists, use it.
- * Returns the number. If name is "" or null, return -1 (pure generic).
+ * Returns the number. If name is "" or null, return null (pure generic).
*
* This is effectively string interning, so that function matching can be
* done more quickly. Two types with the same name but different item kinds
@@ -263,9 +276,8 @@ function initSearch(rawSearchIndex) {
* @returns {integer}
*/
function buildTypeMapIndex(name) {
-
if (name === "" || name === null) {
- return -1;
+ return null;
}
if (typeNameIdMap.has(name)) {
@@ -490,7 +502,7 @@ function initSearch(rawSearchIndex) {
}
return {
name: "never",
- id: -1,
+ id: null,
fullPath: ["never"],
pathWithoutLast: [],
pathLast: "never",
@@ -532,7 +544,7 @@ function initSearch(rawSearchIndex) {
}
return {
name: name.trim(),
- id: -1,
+ id: null,
fullPath: pathSegments,
pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1),
pathLast: pathSegments[pathSegments.length - 1],
@@ -661,7 +673,7 @@ function initSearch(rawSearchIndex) {
}
elems.push({
name: "[]",
- id: -1,
+ id: null,
fullPath: ["[]"],
pathWithoutLast: [],
pathLast: "[]",
@@ -972,9 +984,13 @@ function initSearch(rawSearchIndex) {
returned: [],
// Total number of "top" elements (does not include generics).
foundElems: 0,
+ // Total number of elements (includes generics).
+ totalElems: 0,
literalSearch: false,
error: null,
correction: null,
+ proposeCorrectionFrom: null,
+ proposeCorrectionTo: null,
};
}
@@ -1015,64 +1031,10 @@ function initSearch(rawSearchIndex) {
/**
* Parses the query.
*
- * The supported syntax by this parser is as follow:
- *
- * ident = *(ALPHA / DIGIT / "_")
- * path = ident *(DOUBLE-COLON/{WS} ident) [!]
- * slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET
- * arg = [type-filter *WS COLON *WS] (path [generics] / slice)
- * type-sep = *WS COMMA *(COMMA)
- * nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
- * generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep)
- * CLOSE-ANGLE-BRACKET
- * return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
- *
- * exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ]
- * type-search = [ nonempty-arg-list ] [ return-args ]
- *
- * query = *WS (exact-search / type-search) *WS
+ * The supported syntax by this parser is given in the rustdoc book chapter
+ * /src/doc/rustdoc/src/read-documentation/search.md
*
- * type-filter = (
- * "mod" /
- * "externcrate" /
- * "import" /
- * "struct" /
- * "enum" /
- * "fn" /
- * "type" /
- * "static" /
- * "trait" /
- * "impl" /
- * "tymethod" /
- * "method" /
- * "structfield" /
- * "variant" /
- * "macro" /
- * "primitive" /
- * "associatedtype" /
- * "constant" /
- * "associatedconstant" /
- * "union" /
- * "foreigntype" /
- * "keyword" /
- * "existential" /
- * "attr" /
- * "derive" /
- * "traitalias")
- *
- * OPEN-ANGLE-BRACKET = "<"
- * CLOSE-ANGLE-BRACKET = ">"
- * OPEN-SQUARE-BRACKET = "["
- * CLOSE-SQUARE-BRACKET = "]"
- * COLON = ":"
- * DOUBLE-COLON = "::"
- * QUOTE = %x22
- * COMMA = ","
- * RETURN-ARROW = "->"
- *
- * ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
- * DIGIT = %x30-39
- * WS = %x09 / " "
+ * When adding new things to the parser, add them there, too!
*
* @param {string} val - The user query
*
@@ -1125,6 +1087,7 @@ function initSearch(rawSearchIndex) {
query.literalSearch = parserState.totalElems > 1;
}
query.foundElems = query.elems.length + query.returned.length;
+ query.totalElems = parserState.totalElems;
return query;
}
@@ -1173,7 +1136,7 @@ function initSearch(rawSearchIndex) {
const out = [];
for (const result of results) {
- if (result.id > -1) {
+ if (result.id !== -1) {
const obj = searchIndex[result.id];
obj.dist = result.dist;
const res = buildHrefAndPath(obj);
@@ -1349,166 +1312,311 @@ function initSearch(rawSearchIndex) {
* This function checks generics in search query `queryElem` can all be found in the
* search index (`fnType`),
*
- * @param {FunctionType} fnType - The object to check.
- * @param {QueryElement} queryElem - The element from the parsed query.
+ * This function returns `true` if it matches, and also writes the results to mgensInout.
+ * It returns `false` if no match is found, and leaves mgensInout untouched.
+ *
+ * @param {FunctionType} fnType - The object to check.
+ * @param {QueryElement} queryElem - The element from the parsed query.
+ * @param {[FunctionType]} whereClause - Trait bounds for generic items.
+ * @param {Map<number,number>|null} mgensInout - Map functions generics to query generics.
*
* @return {boolean} - Returns true if a match, false otherwise.
*/
- function checkGenerics(fnType, queryElem) {
- return unifyFunctionTypes(fnType.generics, queryElem.generics);
+ function checkGenerics(fnType, queryElem, whereClause, mgensInout) {
+ return unifyFunctionTypes(
+ fnType.generics,
+ queryElem.generics,
+ whereClause,
+ mgensInout,
+ mgens => {
+ if (mgensInout) {
+ for (const [fid, qid] of mgens.entries()) {
+ mgensInout.set(fid, qid);
+ }
+ }
+ return true;
+ }
+ );
}
/**
* This function checks if a list of search query `queryElems` can all be found in the
* search index (`fnTypes`).
*
- * @param {Array<FunctionType>} fnTypes - The objects to check.
+ * This function returns `true` on a match, or `false` if none. If `solutionCb` is
+ * supplied, it will call that function with mgens, and that callback can accept or
+ * reject the result bu returning `true` or `false`. If the callback returns false,
+ * then this function will try with a different solution, or bail with false if it
+ * runs out of candidates.
+ *
+ * @param {Array<FunctionType>} fnTypes - The objects to check.
* @param {Array<QueryElement>} queryElems - The elements from the parsed query.
+ * @param {[FunctionType]} whereClause - Trait bounds for generic items.
+ * @param {Map<number,number>|null} mgensIn
+ * - Map functions generics to query generics (never modified).
+ * @param {null|Map<number,number> -> bool} solutionCb - Called for each `mgens` solution.
*
* @return {boolean} - Returns true if a match, false otherwise.
*/
- function unifyFunctionTypes(fnTypes, queryElems) {
- // This search engine implements order-agnostic unification. There
- // should be no missing duplicates (generics have "bag semantics"),
- // and the row is allowed to have extras.
+ function unifyFunctionTypes(fnTypesIn, queryElems, whereClause, mgensIn, solutionCb) {
+ /**
+ * @type Map<integer, integer>
+ */
+ let mgens = new Map(mgensIn);
if (queryElems.length === 0) {
- return true;
+ return !solutionCb || solutionCb(mgens);
}
- if (!fnTypes || fnTypes.length === 0) {
+ if (!fnTypesIn || fnTypesIn.length === 0) {
return false;
}
+ const ql = queryElems.length;
+ let fl = fnTypesIn.length;
/**
- * @type Map<integer, QueryElement[]>
+ * @type Array<FunctionType>
*/
- const queryElemSet = new Map();
- const addQueryElemToQueryElemSet = function addQueryElemToQueryElemSet(queryElem) {
- let currentQueryElemList;
- if (queryElemSet.has(queryElem.id)) {
- currentQueryElemList = queryElemSet.get(queryElem.id);
- } else {
- currentQueryElemList = [];
- queryElemSet.set(queryElem.id, currentQueryElemList);
- }
- currentQueryElemList.push(queryElem);
- };
- for (const queryElem of queryElems) {
- addQueryElemToQueryElemSet(queryElem);
- }
+ let fnTypes = fnTypesIn.slice();
/**
- * @type Map<integer, FunctionType[]>
+ * loop works by building up a solution set in the working arrays
+ * fnTypes gets mutated in place to make this work, while queryElems
+ * is left alone
+ *
+ * vvvvvvv `i` points here
+ * queryElems = [ good, good, good, unknown, unknown ],
+ * fnTypes = [ good, good, good, unknown, unknown ],
+ * ---------------- ^^^^^^^^^^^^^^^^ `j` iterates after `i`,
+ * | looking for candidates
+ * everything before `i` is the
+ * current working solution
+ *
+ * Everything in the current working solution is known to be a good
+ * match, but it might not be the match we wind up going with, because
+ * there might be more than one candidate match, and we need to try them all
+ * before giving up. So, to handle this, it backtracks on failure.
+ *
+ * @type Array<{
+ * "fnTypesScratch": Array<FunctionType>,
+ * "queryElemsOffset": integer,
+ * "fnTypesOffset": integer
+ * }>
*/
- const fnTypeSet = new Map();
- const addFnTypeToFnTypeSet = function addFnTypeToFnTypeSet(fnType) {
- // Pure generic, or an item that's not matched by any query elems.
- // Try [unboxing] it.
- //
- // [unboxing]:
- // http://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
- const queryContainsArrayOrSliceElem = queryElemSet.has(typeNameIdOfArrayOrSlice);
- if (fnType.id === -1 || !(
- queryElemSet.has(fnType.id) ||
- (fnType.id === typeNameIdOfSlice && queryContainsArrayOrSliceElem) ||
- (fnType.id === typeNameIdOfArray && queryContainsArrayOrSliceElem)
- )) {
- for (const innerFnType of fnType.generics) {
- addFnTypeToFnTypeSet(innerFnType);
- }
- return;
- }
- let currentQueryElemList = queryElemSet.get(fnType.id) || [];
- let matchIdx = currentQueryElemList.findIndex(queryElem => {
- return typePassesFilter(queryElem.typeFilter, fnType.ty) &&
- checkGenerics(fnType, queryElem);
- });
- if (matchIdx === -1 &&
- (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray) &&
- queryContainsArrayOrSliceElem
- ) {
- currentQueryElemList = queryElemSet.get(typeNameIdOfArrayOrSlice) || [];
- matchIdx = currentQueryElemList.findIndex(queryElem => {
- return typePassesFilter(queryElem.typeFilter, fnType.ty) &&
- checkGenerics(fnType, queryElem);
- });
- }
- // None of the query elems match the function type.
- // Try [unboxing] it.
- if (matchIdx === -1) {
- for (const innerFnType of fnType.generics) {
- addFnTypeToFnTypeSet(innerFnType);
+ const backtracking = [];
+ let i = 0;
+ let j = 0;
+ const backtrack = () => {
+ while (backtracking.length !== 0) {
+ // this session failed, but there are other possible solutions
+ // to backtrack, reset to (a copy of) the old array, do the swap or unboxing
+ const {
+ fnTypesScratch,
+ mgensScratch,
+ queryElemsOffset,
+ fnTypesOffset,
+ unbox,
+ } = backtracking.pop();
+ mgens = new Map(mgensScratch);
+ const fnType = fnTypesScratch[fnTypesOffset];
+ const queryElem = queryElems[queryElemsOffset];
+ if (unbox) {
+ if (fnType.id < 0) {
+ if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) {
+ continue;
+ }
+ mgens.set(fnType.id, 0);
+ }
+ const generics = fnType.id < 0 ?
+ whereClause[(-fnType.id) - 1] :
+ fnType.generics;
+ fnTypes = fnTypesScratch.toSpliced(fnTypesOffset, 1, ...generics);
+ fl = fnTypes.length;
+ // re-run the matching algorithm on this item
+ i = queryElemsOffset - 1;
+ } else {
+ if (fnType.id < 0) {
+ if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) {
+ continue;
+ }
+ mgens.set(fnType.id, queryElem.id);
+ }
+ fnTypes = fnTypesScratch.slice();
+ fl = fnTypes.length;
+ const tmp = fnTypes[queryElemsOffset];
+ fnTypes[queryElemsOffset] = fnTypes[fnTypesOffset];
+ fnTypes[fnTypesOffset] = tmp;
+ // this is known as a good match; go to the next one
+ i = queryElemsOffset;
}
- return;
- }
- let currentFnTypeList;
- if (fnTypeSet.has(fnType.id)) {
- currentFnTypeList = fnTypeSet.get(fnType.id);
- } else {
- currentFnTypeList = [];
- fnTypeSet.set(fnType.id, currentFnTypeList);
+ return true;
}
- currentFnTypeList.push(fnType);
+ return false;
};
- for (const fnType of fnTypes) {
- addFnTypeToFnTypeSet(fnType);
- }
- const doHandleQueryElemList = (currentFnTypeList, queryElemList) => {
- if (queryElemList.length === 0) {
- return true;
+ for (i = 0; i !== ql; ++i) {
+ const queryElem = queryElems[i];
+ /**
+ * list of potential function types that go with the current query element.
+ * @type Array<integer>
+ */
+ const matchCandidates = [];
+ let fnTypesScratch = null;
+ let mgensScratch = null;
+ // don't try anything before `i`, because they've already been
+ // paired off with the other query elements
+ for (j = i; j !== fl; ++j) {
+ const fnType = fnTypes[j];
+ if (unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) {
+ if (!fnTypesScratch) {
+ fnTypesScratch = fnTypes.slice();
+ }
+ unifyFunctionTypes(
+ fnType.generics,
+ queryElem.generics,
+ whereClause,
+ mgens,
+ mgensScratch => {
+ matchCandidates.push({
+ fnTypesScratch,
+ mgensScratch,
+ queryElemsOffset: i,
+ fnTypesOffset: j,
+ unbox: false,
+ });
+ return false; // "reject" all candidates to gather all of them
+ }
+ );
+ }
+ if (unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) {
+ if (!fnTypesScratch) {
+ fnTypesScratch = fnTypes.slice();
+ }
+ if (!mgensScratch) {
+ mgensScratch = new Map(mgens);
+ }
+ backtracking.push({
+ fnTypesScratch,
+ mgensScratch,
+ queryElemsOffset: i,
+ fnTypesOffset: j,
+ unbox: true,
+ });
+ }
}
- // Multiple items in one list might match multiple items in another.
- // Since an item with fewer generics can match an item with more, we
- // need to check all combinations for a potential match.
- const queryElem = queryElemList.pop();
- const l = currentFnTypeList.length;
- for (let i = 0; i < l; i += 1) {
- const fnType = currentFnTypeList[i];
- if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
+ if (matchCandidates.length === 0) {
+ if (backtrack()) {
continue;
+ } else {
+ return false;
}
- if (queryElem.generics.length === 0 || checkGenerics(fnType, queryElem)) {
- currentFnTypeList.splice(i, 1);
- const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
- if (result) {
- return true;
- }
- currentFnTypeList.splice(i, 0, fnType);
+ }
+ // use the current candidate
+ const {fnTypesOffset: candidate, mgensScratch: mgensNew} = matchCandidates.pop();
+ if (fnTypes[candidate].id < 0 && queryElems[i].id < 0) {
+ mgens.set(fnTypes[candidate].id, queryElems[i].id);
+ }
+ for (const [fid, qid] of mgensNew) {
+ mgens.set(fid, qid);
+ }
+ // `i` and `j` are paired off
+ // `queryElems[i]` is left in place
+ // `fnTypes[j]` is swapped with `fnTypes[i]` to pair them off
+ const tmp = fnTypes[candidate];
+ fnTypes[candidate] = fnTypes[i];
+ fnTypes[i] = tmp;
+ // write other candidates to backtracking queue
+ for (const otherCandidate of matchCandidates) {
+ backtracking.push(otherCandidate);
+ }
+ // If we're on the last item, check the solution with the callback
+ // backtrack if the callback says its unsuitable
+ while (i === (ql - 1) && solutionCb && !solutionCb(mgens)) {
+ if (!backtrack()) {
+ return false;
}
}
+ }
+ return true;
+ }
+ function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens) {
+ // type filters look like `trait:Read` or `enum:Result`
+ if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
return false;
- };
- const handleQueryElemList = (id, queryElemList) => {
- if (!fnTypeSet.has(id)) {
- if (id === typeNameIdOfArrayOrSlice) {
- return handleQueryElemList(typeNameIdOfSlice, queryElemList) ||
- handleQueryElemList(typeNameIdOfArray, queryElemList);
+ }
+ // fnType.id < 0 means generic
+ // queryElem.id < 0 does too
+ // mgens[fnType.id] = queryElem.id
+ // or, if mgens[fnType.id] = 0, then we've matched this generic with a bare trait
+ // and should make that same decision everywhere it appears
+ if (fnType.id < 0 && queryElem.id < 0) {
+ if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) {
+ return false;
+ }
+ for (const [fid, qid] of mgens.entries()) {
+ if (fnType.id !== fid && queryElem.id === qid) {
+ return false;
+ }
+ if (fnType.id === fid && queryElem.id !== qid) {
+ return false;
}
+ }
+ } else if (fnType.id !== null) {
+ if (queryElem.id === typeNameIdOfArrayOrSlice &&
+ (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray)
+ ) {
+ // [] matches primitive:array or primitive:slice
+ // if it matches, then we're fine, and this is an appropriate match candidate
+ } else if (fnType.id !== queryElem.id) {
return false;
}
- const currentFnTypeList = fnTypeSet.get(id);
- if (currentFnTypeList.length < queryElemList.length) {
- // It's not possible for all the query elems to find a match.
+ // If the query elem has generics, and the function doesn't,
+ // it can't match.
+ if (fnType.generics.length === 0 && queryElem.generics.length !== 0) {
return false;
}
- const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
- if (result) {
- // Found a solution.
- // Any items that weren't used for it can be unboxed, and might form
- // part of the solution for another item.
- for (const innerFnType of currentFnTypeList) {
- addFnTypeToFnTypeSet(innerFnType);
+ // If the query element is a path (it contains `::`), we need to check if this
+ // path is compatible with the target type.
+ const queryElemPathLength = queryElem.pathWithoutLast.length;
+ if (queryElemPathLength > 0) {
+ const fnTypePath = fnType.path !== undefined && fnType.path !== null ?
+ fnType.path.split("::") : [];
+ // If the path provided in the query element is longer than this type,
+ // no need to check it since it won't match in any case.
+ if (queryElemPathLength > fnTypePath.length) {
+ return false;
}
- fnTypeSet.delete(id);
- }
- return result;
- };
- let queryElemSetSize = -1;
- while (queryElemSetSize !== queryElemSet.size) {
- queryElemSetSize = queryElemSet.size;
- for (const [id, queryElemList] of queryElemSet) {
- if (handleQueryElemList(id, queryElemList)) {
- queryElemSet.delete(id);
+ let i = 0;
+ for (const path of fnTypePath) {
+ if (path === queryElem.pathWithoutLast[i]) {
+ i += 1;
+ if (i >= queryElemPathLength) {
+ break;
+ }
+ }
+ }
+ if (i < queryElemPathLength) {
+ // If we didn't find all parts of the path of the query element inside
+ // the fn type, then it's not the right one.
+ return false;
}
}
}
- return queryElemSetSize === 0;
+ return true;
+ }
+ function unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens) {
+ if (fnType.id < 0 && queryElem.id >= 0) {
+ if (!whereClause) {
+ return false;
+ }
+ // mgens[fnType.id] === 0 indicates that we committed to unboxing this generic
+ // mgens[fnType.id] === null indicates that we haven't decided yet
+ if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) {
+ return false;
+ }
+ // This is only a potential unbox if the search query appears in the where clause
+ // for example, searching `Read -> usize` should find
+ // `fn read_all<R: Read>(R) -> Result<usize>`
+ // generic `R` is considered "unboxed"
+ return checkIfInList(whereClause[(-fnType.id) - 1], queryElem, whereClause);
+ } else if (fnType.generics && fnType.generics.length > 0) {
+ return checkIfInList(fnType.generics, queryElem, whereClause);
+ }
+ return false;
}
/**
@@ -1516,13 +1624,14 @@ function initSearch(rawSearchIndex) {
* generics (if any).
*
* @param {Array<FunctionType>} list
- * @param {QueryElement} elem - The element from the parsed query.
+ * @param {QueryElement} elem - The element from the parsed query.
+ * @param {[FunctionType]} whereClause - Trait bounds for generic items.
*
* @return {boolean} - Returns true if found, false otherwise.
*/
- function checkIfInList(list, elem) {
+ function checkIfInList(list, elem, whereClause) {
for (const entry of list) {
- if (checkType(entry, elem)) {
+ if (checkType(entry, elem, whereClause)) {
return true;
}
}
@@ -1534,14 +1643,26 @@ function initSearch(rawSearchIndex) {
* generics (if any).
*
* @param {Row} row
- * @param {QueryElement} elem - The element from the parsed query.
+ * @param {QueryElement} elem - The element from the parsed query.
+ * @param {[FunctionType]} whereClause - Trait bounds for generic items.
*
* @return {boolean} - Returns true if the type matches, false otherwise.
*/
- function checkType(row, elem) {
- if (row.id === -1) {
+ function checkType(row, elem, whereClause) {
+ if (row.id === null) {
// This is a pure "generic" search, no need to run other checks.
- return row.generics.length > 0 ? checkIfInList(row.generics, elem) : false;
+ return row.generics.length > 0
+ ? checkIfInList(row.generics, elem, whereClause)
+ : false;
+ }
+
+ if (row.id < 0 && elem.id >= 0) {
+ const gid = (-row.id) - 1;
+ return checkIfInList(whereClause[gid], elem, whereClause);
+ }
+
+ if (row.id < 0 && elem.id < 0) {
+ return true;
}
const matchesExact = row.id === elem.id;
@@ -1551,7 +1672,7 @@ function initSearch(rawSearchIndex) {
if ((matchesExact || matchesArrayOrSlice) &&
typePassesFilter(elem.typeFilter, row.ty)) {
if (elem.generics.length > 0) {
- return checkGenerics(row, elem);
+ return checkGenerics(row, elem, whereClause, new Map());
}
return true;
}
@@ -1559,7 +1680,7 @@ function initSearch(rawSearchIndex) {
// If the current item does not match, try [unboxing] the generic.
// [unboxing]:
// https://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
- return checkIfInList(row.generics, elem);
+ return checkIfInList(row.generics, elem, whereClause);
}
function checkPath(contains, ty, maxEditDistance) {
@@ -1760,13 +1881,15 @@ function initSearch(rawSearchIndex) {
const fullId = row.id;
const searchWord = searchWords[pos];
- const in_args = row.type && row.type.inputs && checkIfInList(row.type.inputs, elem);
+ const in_args = row.type && row.type.inputs
+ && checkIfInList(row.type.inputs, elem, row.type.where_clause);
if (in_args) {
// path_dist is 0 because no parent path information is currently stored
// in the search index
addIntoResults(results_in_args, fullId, pos, -1, 0, 0, maxEditDistance);
}
- const returned = row.type && row.type.output && checkIfInList(row.type.output, elem);
+ const returned = row.type && row.type.output
+ && checkIfInList(row.type.output, elem, row.type.where_clause);
if (returned) {
addIntoResults(results_returned, fullId, pos, -1, 0, 0, maxEditDistance);
}
@@ -1828,10 +1951,20 @@ function initSearch(rawSearchIndex) {
}
// If the result is too "bad", we return false and it ends this search.
- if (!unifyFunctionTypes(row.type.inputs, parsedQuery.elems)) {
- return;
- }
- if (!unifyFunctionTypes(row.type.output, parsedQuery.returned)) {
+ if (!unifyFunctionTypes(
+ row.type.inputs,
+ parsedQuery.elems,
+ row.type.where_clause,
+ null,
+ mgens => {
+ return unifyFunctionTypes(
+ row.type.output,
+ parsedQuery.returned,
+ row.type.where_clause,
+ mgens
+ );
+ }
+ )) {
return;
}
@@ -1851,6 +1984,11 @@ function initSearch(rawSearchIndex) {
const maxEditDistance = Math.floor(queryLen / 3);
/**
+ * @type {Map<string, integer>}
+ */
+ const genericSymbols = new Map();
+
+ /**
* Convert names to ids in parsed query elements.
* This is not used for the "In Names" tab, but is used for the
* "In Params", "In Returns", and "In Function Signature" tabs.
@@ -1863,14 +2001,14 @@ function initSearch(rawSearchIndex) {
* @param {QueryElement} elem
*/
function convertNameToId(elem) {
- if (typeNameIdMap.has(elem.name)) {
- elem.id = typeNameIdMap.get(elem.name);
+ if (typeNameIdMap.has(elem.pathLast)) {
+ elem.id = typeNameIdMap.get(elem.pathLast);
} else if (!parsedQuery.literalSearch) {
- let match = -1;
+ let match = null;
let matchDist = maxEditDistance + 1;
let matchName = "";
for (const [name, id] of typeNameIdMap) {
- const dist = editDistance(name, elem.name, maxEditDistance);
+ const dist = editDistance(name, elem.pathLast, maxEditDistance);
if (dist <= matchDist && dist <= maxEditDistance) {
if (dist === matchDist && matchName > name) {
continue;
@@ -1880,11 +2018,52 @@ function initSearch(rawSearchIndex) {
matchName = name;
}
}
- if (match !== -1) {
+ if (match !== null) {
parsedQuery.correction = matchName;
}
elem.id = match;
}
+ if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1
+ && elem.generics.length === 0)
+ || elem.typeFilter === TY_GENERIC) {
+ if (genericSymbols.has(elem.name)) {
+ elem.id = genericSymbols.get(elem.name);
+ } else {
+ elem.id = -(genericSymbols.size + 1);
+ genericSymbols.set(elem.name, elem.id);
+ }
+ if (elem.typeFilter === -1 && elem.name.length >= 3) {
+ // Silly heuristic to catch if the user probably meant
+ // to not write a generic parameter. We don't use it,
+ // just bring it up.
+ const maxPartDistance = Math.floor(elem.name.length / 3);
+ let matchDist = maxPartDistance + 1;
+ let matchName = "";
+ for (const name of typeNameIdMap.keys()) {
+ const dist = editDistance(name, elem.name, maxPartDistance);
+ if (dist <= matchDist && dist <= maxPartDistance) {
+ if (dist === matchDist && matchName > name) {
+ continue;
+ }
+ matchDist = dist;
+ matchName = name;
+ }
+ }
+ if (matchName !== "") {
+ parsedQuery.proposeCorrectionFrom = elem.name;
+ parsedQuery.proposeCorrectionTo = matchName;
+ }
+ }
+ elem.typeFilter = TY_GENERIC;
+ }
+ if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) {
+ // Rust does not have HKT
+ parsedQuery.error = [
+ "Generic type parameter ",
+ elem.name,
+ " does not accept generic parameters",
+ ];
+ }
for (const elem2 of elem.generics) {
convertNameToId(elem2);
}
@@ -1918,8 +2097,11 @@ function initSearch(rawSearchIndex) {
elem = parsedQuery.returned[0];
for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
row = searchIndex[i];
- in_returned = row.type &&
- unifyFunctionTypes(row.type.output, parsedQuery.returned);
+ in_returned = row.type && unifyFunctionTypes(
+ row.type.output,
+ parsedQuery.returned,
+ row.type.where_clause
+ );
if (in_returned) {
addIntoResults(
results_others,
@@ -2152,11 +2334,20 @@ ${item.displayPath}<span class="${type}">${name}</span>\
}
function makeTabHeader(tabNb, text, nbElems) {
+ // https://blog.horizon-eda.org/misc/2020/02/19/ui.html
+ //
+ // CSS runs with `font-variant-numeric: tabular-nums` to ensure all
+ // digits are the same width. \u{2007} is a Unicode space character
+ // that is defined to be the same width as a digit.
+ const fmtNbElems =
+ nbElems < 10 ? `\u{2007}(${nbElems})\u{2007}\u{2007}` :
+ nbElems < 100 ? `\u{2007}(${nbElems})\u{2007}` :
+ `\u{2007}(${nbElems})`;
if (searchState.currentTab === tabNb) {
return "<button class=\"selected\">" + text +
- " <span class=\"count\">(" + nbElems + ")</span></button>";
+ "<span class=\"count\">" + fmtNbElems + "</span></button>";
}
- return "<button>" + text + " <span class=\"count\">(" + nbElems + ")</span></button>";
+ return "<button>" + text + "<span class=\"count\">" + fmtNbElems + "</span></button>";
}
/**
@@ -2270,6 +2461,13 @@ ${item.displayPath}<span class="${type}">${name}</span>\
"Showing results for closest type name " +
`"${results.query.correction}" instead.</h3>`;
}
+ if (results.query.proposeCorrectionFrom !== null) {
+ const orig = results.query.proposeCorrectionFrom;
+ const targ = results.query.proposeCorrectionTo;
+ output += "<h3 class=\"search-corrections\">" +
+ `Type "${orig}" not found and used as generic parameter. ` +
+ `Consider searching for "${targ}" instead.</h3>`;
+ }
const resultsElem = document.createElement("div");
resultsElem.id = "results";
@@ -2371,29 +2569,54 @@ ${item.displayPath}<span class="${type}">${name}</span>\
* @return {Array<FunctionSearchType>}
*/
function buildItemSearchTypeAll(types, lowercasePaths) {
+ return types.map(type => buildItemSearchType(type, lowercasePaths));
+ }
+
+ /**
+ * Converts a single type.
+ *
+ * @param {RawFunctionType} type
+ */
+ function buildItemSearchType(type, lowercasePaths) {
const PATH_INDEX_DATA = 0;
const GENERICS_DATA = 1;
- return types.map(type => {
- let pathIndex, generics;
- if (typeof type === "number") {
- pathIndex = type;
- generics = [];
- } else {
- pathIndex = type[PATH_INDEX_DATA];
- generics = buildItemSearchTypeAll(
- type[GENERICS_DATA],
- lowercasePaths
- );
- }
+ let pathIndex, generics;
+ if (typeof type === "number") {
+ pathIndex = type;
+ generics = [];
+ } else {
+ pathIndex = type[PATH_INDEX_DATA];
+ generics = buildItemSearchTypeAll(
+ type[GENERICS_DATA],
+ lowercasePaths
+ );
+ }
+ if (pathIndex < 0) {
+ // types less than 0 are generic parameters
+ // the actual names of generic parameters aren't stored, since they aren't API
return {
- // `0` is used as a sentinel because it's fewer bytes than `null`
- id: pathIndex === 0
- ? -1
- : buildTypeMapIndex(lowercasePaths[pathIndex - 1].name),
- ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty,
- generics: generics,
+ id: pathIndex,
+ ty: TY_GENERIC,
+ path: null,
+ generics,
};
- });
+ }
+ if (pathIndex === 0) {
+ // `0` is used as a sentinel because it's fewer bytes than `null`
+ return {
+ id: null,
+ ty: null,
+ path: null,
+ generics,
+ };
+ }
+ const item = lowercasePaths[pathIndex - 1];
+ return {
+ id: buildTypeMapIndex(item.name),
+ ty: item.ty,
+ path: item.path,
+ generics,
+ };
}
/**
@@ -2421,14 +2644,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
}
let inputs, output;
if (typeof functionSearchType[INPUTS_DATA] === "number") {
- const pathIndex = functionSearchType[INPUTS_DATA];
- inputs = [{
- id: pathIndex === 0
- ? -1
- : buildTypeMapIndex(lowercasePaths[pathIndex - 1].name),
- ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty,
- generics: [],
- }];
+ inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)];
} else {
inputs = buildItemSearchTypeAll(
functionSearchType[INPUTS_DATA],
@@ -2437,14 +2653,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
}
if (functionSearchType.length > 1) {
if (typeof functionSearchType[OUTPUT_DATA] === "number") {
- const pathIndex = functionSearchType[OUTPUT_DATA];
- output = [{
- id: pathIndex === 0
- ? -1
- : buildTypeMapIndex(lowercasePaths[pathIndex - 1].name),
- ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty,
- generics: [],
- }];
+ output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)];
} else {
output = buildItemSearchTypeAll(
functionSearchType[OUTPUT_DATA],
@@ -2454,8 +2663,15 @@ ${item.displayPath}<span class="${type}">${name}</span>\
} else {
output = [];
}
+ const where_clause = [];
+ const l = functionSearchType.length;
+ for (let i = 2; i < l; ++i) {
+ where_clause.push(typeof functionSearchType[i] === "number"
+ ? [buildItemSearchType(functionSearchType[i], lowercasePaths)]
+ : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths));
+ }
return {
- inputs, output,
+ inputs, output, where_clause,
};
}
@@ -2486,18 +2702,25 @@ ${item.displayPath}<span class="${type}">${name}</span>\
let crateSize = 0;
/**
- * The raw search data for a given crate. `n`, `t`, `d`, and `q`, `i`, and `f`
- * are arrays with the same length. n[i] contains the name of an item.
- * t[i] contains the type of that item (as a string of characters that represent an
- * offset in `itemTypes`). d[i] contains the description of that item.
+ * The raw search data for a given crate. `n`, `t`, `d`, `i`, and `f`
+ * are arrays with the same length. `q`, `a`, and `c` use a sparse
+ * representation for compactness.
+ *
+ * `n[i]` contains the name of an item.
+ *
+ * `t[i]` contains the type of that item
+ * (as a string of characters that represent an offset in `itemTypes`).
*
- * q[i] contains the full path of the item, or an empty string indicating
- * "same as q[i-1]".
+ * `d[i]` contains the description of that item.
*
- * i[i] contains an item's parent, usually a module. For compactness,
+ * `q` contains the full paths of the items. For compactness, it is a set of
+ * (index, path) pairs used to create a map. If a given index `i` is
+ * not present, this indicates "same as the last index present".
+ *
+ * `i[i]` contains an item's parent, usually a module. For compactness,
* it is a set of indexes into the `p` array.
*
- * f[i] contains function signatures, or `0` if the item isn't a function.
+ * `f[i]` contains function signatures, or `0` if the item isn't a function.
* Functions are themselves encoded as arrays. The first item is a list of
* types representing the function's inputs, and the second list item is a list
* of types representing the function's output. Tuples are flattened.
@@ -2511,6 +2734,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
*
* `p` is a list of path/type pairs. It is used for parents and function parameters.
*
+ * `c` is an array of item indices that are deprecated.
+ *
* @type {{
* doc: string,
* a: Object,
@@ -2577,9 +2802,19 @@ ${item.displayPath}<span class="${type}">${name}</span>\
// convert `rawPaths` entries into object form
// generate normalizedPaths for function search mode
let len = paths.length;
+ let lastPath = itemPaths.get(0);
for (let i = 0; i < len; ++i) {
- lowercasePaths.push({ty: paths[i][0], name: paths[i][1].toLowerCase()});
- paths[i] = {ty: paths[i][0], name: paths[i][1]};
+ const elem = paths[i];
+ const ty = elem[0];
+ const name = elem[1];
+ let path = null;
+ if (elem.length > 2) {
+ path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath;
+ lastPath = path;
+ }
+
+ lowercasePaths.push({ty: ty, name: name.toLowerCase(), path: path});
+ paths[i] = {ty: ty, name: name, path: path};
}
// convert `item*` into an object form, and construct word indices.
@@ -2589,8 +2824,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
// operation that is cached for the life of the page state so that
// all other search operations have access to this cached data for
// faster analysis operations
+ lastPath = "";
len = itemTypes.length;
- let lastPath = "";
for (let i = 0; i < len; ++i) {
let word = "";
// This object should have exactly the same set of fields as the "crateRow"
@@ -2599,11 +2834,12 @@ ${item.displayPath}<span class="${type}">${name}</span>\
word = itemNames[i].toLowerCase();
}
searchWords.push(word);
+ const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath;
const row = {
crate: crate,
ty: itemTypes.charCodeAt(i) - charA,
name: itemNames[i],
- path: itemPaths.has(i) ? itemPaths.get(i) : lastPath,
+ path: path,
desc: itemDescs[i],
parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
type: buildFunctionSearchType(
diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js
index 2cba32c1b..63947789c 100644
--- a/src/librustdoc/html/static/js/settings.js
+++ b/src/librustdoc/html/static/js/settings.js
@@ -59,8 +59,8 @@
if (settingValue !== null) {
toggle.checked = settingValue === "true";
}
- toggle.onchange = function() {
- changeSetting(this.id, this.checked);
+ toggle.onchange = () => {
+ changeSetting(toggle.id, toggle.checked);
};
});
onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => {
@@ -224,14 +224,14 @@
if (isSettingsPage) {
// We replace the existing "onclick" callback to do nothing if clicked.
- getSettingsButton().onclick = function(event) {
+ getSettingsButton().onclick = event => {
event.preventDefault();
};
} else {
// We replace the existing "onclick" callback.
const settingsButton = getSettingsButton();
const settingsMenu = document.getElementById("settings");
- settingsButton.onclick = function(event) {
+ settingsButton.onclick = event => {
if (elemIsInParent(event.target, settingsMenu)) {
return;
}
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index af3ca42a6..c69641092 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -5,6 +5,7 @@
// the page, so we don't see major layout changes during the load of the page.
"use strict";
+const builtinThemes = ["light", "dark", "ayu"];
const darkThemes = ["dark", "ayu"];
window.currentTheme = document.getElementById("themeStyle");
@@ -119,19 +120,32 @@ function switchTheme(newThemeName, saveTheme) {
updateLocalStorage("theme", newThemeName);
}
- let newHref;
+ document.documentElement.setAttribute("data-theme", newThemeName);
- if (newThemeName === "light" || newThemeName === "dark" || newThemeName === "ayu") {
- newHref = getVar("static-root-path") + getVar("theme-" + newThemeName + "-css");
+ if (builtinThemes.indexOf(newThemeName) !== -1) {
+ if (window.currentTheme) {
+ window.currentTheme.parentNode.removeChild(window.currentTheme);
+ window.currentTheme = null;
+ }
} else {
- newHref = getVar("root-path") + newThemeName + getVar("resource-suffix") + ".css";
- }
-
- if (!window.currentTheme) {
- document.write(`<link rel="stylesheet" id="themeStyle" href="${newHref}">`);
- window.currentTheme = document.getElementById("themeStyle");
- } else if (newHref !== window.currentTheme.href) {
- window.currentTheme.href = newHref;
+ const newHref = getVar("root-path") + newThemeName +
+ getVar("resource-suffix") + ".css";
+ if (!window.currentTheme) {
+ // If we're in the middle of loading, document.write blocks
+ // rendering, but if we are done, it would blank the page.
+ if (document.readyState === "loading") {
+ document.write(`<link rel="stylesheet" id="themeStyle" href="${newHref}">`);
+ window.currentTheme = document.getElementById("themeStyle");
+ } else {
+ window.currentTheme = document.createElement("link");
+ window.currentTheme.rel = "stylesheet";
+ window.currentTheme.id = "themeStyle";
+ window.currentTheme.href = newHref;
+ document.documentElement.appendChild(window.currentTheme);
+ }
+ } else if (newHref !== window.currentTheme.href) {
+ window.currentTheme.href = newHref;
+ }
}
}
diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs
index a27aa2b58..ca9a78f51 100644
--- a/src/librustdoc/html/static_files.rs
+++ b/src/librustdoc/html/static_files.rs
@@ -91,7 +91,6 @@ macro_rules! static_files {
static_files! {
rustdoc_css => "static/css/rustdoc.css",
- settings_css => "static/css/settings.css",
noscript_css => "static/css/noscript.css",
normalize_css => "static/css/normalize.css",
main_js => "static/js/main.js",
@@ -109,9 +108,6 @@ static_files! {
rust_favicon_svg => "static/images/favicon.svg",
rust_favicon_png_16 => "static/images/favicon-16x16.png",
rust_favicon_png_32 => "static/images/favicon-32x32.png",
- theme_light_css => "static/css/themes/light.css",
- theme_dark_css => "static/css/themes/dark.css",
- theme_ayu_css => "static/css/themes/ayu.css",
fira_sans_regular => "static/fonts/FiraSans-Regular.woff2",
fira_sans_medium => "static/fonts/FiraSans-Medium.woff2",
fira_sans_license => "static/fonts/FiraSans-LICENSE.txt",
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index 60ccfe4da..579c782be 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -15,8 +15,7 @@
<link rel="stylesheet" {#+ #}
href="{{static_root_path|safe}}{{files.normalize_css}}"> {# #}
<link rel="stylesheet" {#+ #}
- href="{{static_root_path|safe}}{{files.rustdoc_css}}" {#+ #}
- id="mainThemeStyle"> {# #}
+ href="{{static_root_path|safe}}{{files.rustdoc_css}}"> {# #}
{% if !layout.default_settings.is_empty() %}
<script id="default-settings" {#+ #}
{%~ for (k, v) in layout.default_settings ~%}
@@ -34,10 +33,6 @@
data-channel="{{rust_channel}}" {#+ #}
data-search-js="{{files.search_js}}" {#+ #}
data-settings-js="{{files.settings_js}}" {#+ #}
- data-settings-css="{{files.settings_css}}" {#+ #}
- data-theme-light-css="{{files.theme_light_css}}" {#+ #}
- data-theme-dark-css="{{files.theme_dark_css}}" {#+ #}
- data-theme-ayu-css="{{files.theme_ayu_css}}" {#+ #}
> {# #}
<script src="{{static_root_path|safe}}{{files.storage_js}}"></script> {# #}
{% if page.css_class.contains("crate") %}
@@ -54,12 +49,6 @@
{% endif %}
<noscript> {# #}
<link rel="stylesheet" {#+ #}
- media="(prefers-color-scheme:light)" {#+ #}
- href="{{static_root_path|safe}}{{files.theme_light_css}}"> {# #}
- <link rel="stylesheet" {#+ #}
- media="(prefers-color-scheme:dark)" {#+ #}
- href="{{static_root_path|safe}}{{files.theme_dark_css}}"> {# #}
- <link rel="stylesheet" {#+ #}
href="{{static_root_path|safe}}{{files.noscript_css}}"> {# #}
</noscript> {# #}
{% if layout.css_file_extension.is_some() %}
@@ -95,8 +84,7 @@
<img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
{% endif %}
</a> {# #}
- <h2></h2> {# #}
- </nav> {# #}
+ </nav>
{% endif %}
<nav class="sidebar"> {# #}
{% if page.css_class != "src" %}
diff --git a/src/librustdoc/html/templates/type_layout.html b/src/librustdoc/html/templates/type_layout.html
index 287cbab07..b8b7785a2 100644
--- a/src/librustdoc/html/templates/type_layout.html
+++ b/src/librustdoc/html/templates/type_layout.html
@@ -9,12 +9,12 @@
<strong>Note:</strong> Most layout information is <strong>completely {#+ #}
unstable</strong> and may even differ between compilations. {#+ #}
The only exception is types with certain <code>repr(...)</code> {#+ #}
- attributes. Please see the Rust Reference’s {#+ #}
+ attributes. Please see the Rust Reference's {#+ #}
<a href="https://doc.rust-lang.org/reference/type-layout.html">“Type Layout”</a> {#+ #}
chapter for details on type layout guarantees. {# #}
</p> {# #}
</div> {# #}
- <p><strong>Size:</strong> {{ type_layout_size|safe }}</p> {# #}
+ <p><strong>Size:</strong> {{+ type_layout_size|safe }}</p> {# #}
{% if !variants.is_empty() %}
<p> {# #}
<strong>Size for each variant:</strong> {# #}
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 8673138f6..05f3e66b0 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -8,6 +8,7 @@ use std::fmt;
use rustc_ast::ast;
use rustc_hir::{def::CtorKind, def::DefKind, def_id::DefId};
+use rustc_metadata::rendered_const;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::{Pos, Symbol};
@@ -15,7 +16,6 @@ use rustc_target::spec::abi::Abi as RustcAbi;
use rustdoc_json_types::*;
-use crate::clean::utils::print_const_expr;
use crate::clean::{self, ItemId};
use crate::formats::item_type::ItemType;
use crate::json::JsonRenderer;
@@ -311,7 +311,7 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
StaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)),
ForeignStaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)),
ForeignTypeItem => ItemEnum::ForeignType,
- TypedefItem(t) => ItemEnum::Typedef(t.into_tcx(tcx)),
+ TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_tcx(tcx)),
OpaqueTyItem(t) => ItemEnum::OpaqueTy(t.into_tcx(tcx)),
ConstantItem(c) => ItemEnum::Constant(c.into_tcx(tcx)),
MacroItem(m) => ItemEnum::Macro(m.source),
@@ -453,7 +453,7 @@ impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind {
default: default.map(|x| (*x).into_tcx(tcx)),
synthetic,
},
- Const { ty, default } => GenericParamDefKind::Const {
+ Const { ty, default, is_host_effect: _ } => GenericParamDefKind::Const {
type_: (*ty).into_tcx(tcx),
default: default.map(|x| *x),
},
@@ -491,12 +491,14 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
default: default.map(|ty| (*ty).into_tcx(tcx)),
synthetic,
},
- clean::GenericParamDefKind::Const { ty, default } => {
- GenericParamDefKind::Const {
- type_: (*ty).into_tcx(tcx),
- default: default.map(|d| *d),
- }
- }
+ clean::GenericParamDefKind::Const {
+ ty,
+ default,
+ is_host_effect: _,
+ } => GenericParamDefKind::Const {
+ type_: (*ty).into_tcx(tcx),
+ default: default.map(|d| *d),
+ },
};
GenericParamDef { name, kind }
})
@@ -787,10 +789,10 @@ pub(crate) fn from_macro_kind(kind: rustc_span::hygiene::MacroKind) -> MacroKind
}
}
-impl FromWithTcx<Box<clean::Typedef>> for Typedef {
- fn from_tcx(typedef: Box<clean::Typedef>, tcx: TyCtxt<'_>) -> Self {
- let clean::Typedef { type_, generics, item_type: _ } = *typedef;
- Typedef { type_: type_.into_tcx(tcx), generics: generics.into_tcx(tcx) }
+impl FromWithTcx<Box<clean::TypeAlias>> for TypeAlias {
+ fn from_tcx(type_alias: Box<clean::TypeAlias>, tcx: TyCtxt<'_>) -> Self {
+ let clean::TypeAlias { type_, generics, item_type: _, inner_type: _ } = *type_alias;
+ TypeAlias { type_: type_.into_tcx(tcx), generics: generics.into_tcx(tcx) }
}
}
@@ -805,7 +807,7 @@ impl FromWithTcx<clean::Static> for Static {
Static {
type_: stat.type_.into_tcx(tcx),
mutable: stat.mutability == ast::Mutability::Mut,
- expr: stat.expr.map(|e| print_const_expr(tcx, e)).unwrap_or_default(),
+ expr: stat.expr.map(|e| rendered_const(tcx, e)).unwrap_or_default(),
}
}
}
@@ -827,7 +829,7 @@ impl FromWithTcx<ItemType> for ItemKind {
Union => ItemKind::Union,
Enum => ItemKind::Enum,
Function | TyMethod | Method => ItemKind::Function,
- Typedef => ItemKind::Typedef,
+ TypeAlias => ItemKind::TypeAlias,
OpaqueTy => ItemKind::OpaqueTy,
Static => ItemKind::Static,
Constant => ItemKind::Constant,
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index cd791ce00..27e8a27ba 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -184,7 +184,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
| types::ItemEnum::Variant(_)
| types::ItemEnum::TraitAlias(_)
| types::ItemEnum::Impl(_)
- | types::ItemEnum::Typedef(_)
+ | types::ItemEnum::TypeAlias(_)
| types::ItemEnum::OpaqueTy(_)
| types::ItemEnum::Constant(_)
| types::ItemEnum::Static(_)
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 8220df5d4..67f5ea5d9 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -6,6 +6,7 @@
#![feature(array_methods)]
#![feature(assert_matches)]
#![feature(box_patterns)]
+#![feature(if_let_guard)]
#![feature(impl_trait_in_assoc_type)]
#![feature(iter_intersperse)]
#![feature(lazy_cell)]
@@ -430,8 +431,8 @@ fn opts() -> Vec<RustcOptGroup> {
o.optopt(
"",
"resource-suffix",
- "suffix to add to CSS and JavaScript files, e.g., \"light.css\" will become \
- \"light-suffix.css\"",
+ "suffix to add to CSS and JavaScript files, e.g., \"search-index.js\" will \
+ become \"search-index-suffix.js\"",
"PATH",
)
}),
@@ -503,13 +504,6 @@ fn opts() -> Vec<RustcOptGroup> {
"PATH",
)
}),
- unstable("disable-per-crate-search", |o| {
- o.optflagmulti(
- "",
- "disable-per-crate-search",
- "disables generating the crate selector on the search box",
- )
- }),
unstable("persist-doctests", |o| {
o.optopt(
"",
@@ -656,6 +650,9 @@ fn opts() -> Vec<RustcOptGroup> {
"[rust]",
)
}),
+ unstable("html-no-source", |o| {
+ o.optflag("", "html-no-source", "Disable HTML source code pages generation")
+ }),
]
}
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index 526eea304..b74a9392f 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -80,6 +80,8 @@ pub(crate) fn render<P: AsRef<Path>>(
error_codes,
edition,
playground: &playground,
+ // For markdown files, it'll be disabled until the feature is enabled by default.
+ custom_code_classes_in_docs: false,
}
.into_string()
} else {
@@ -91,6 +93,8 @@ pub(crate) fn render<P: AsRef<Path>>(
edition,
playground: &playground,
heading_offset: HeadingOffset::H1,
+ // For markdown files, it'll be disabled until the feature is enabled by default.
+ custom_code_classes_in_docs: false,
}
.into_string()
};
@@ -154,7 +158,15 @@ pub(crate) fn test(options: Options) -> Result<(), String> {
collector.set_position(DUMMY_SP);
let codes = ErrorCodes::from(options.unstable_features.is_nightly_build());
- find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None);
+ // For markdown files, custom code classes will be disabled until the feature is enabled by default.
+ find_testable_code(
+ &input_str,
+ &mut collector,
+ codes,
+ options.enable_per_target_ignores,
+ None,
+ false,
+ );
crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests);
Ok(())
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index 592dd0a14..60def4058 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -208,7 +208,14 @@ impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> {
let has_docs = !i.attrs.doc_strings.is_empty();
let mut tests = Tests { found_tests: 0 };
- find_testable_code(&i.doc_value(), &mut tests, ErrorCodes::No, false, None);
+ find_testable_code(
+ &i.doc_value(),
+ &mut tests,
+ ErrorCodes::No,
+ false,
+ None,
+ self.ctx.tcx.features().custom_code_classes_in_docs,
+ );
let has_doc_example = tests.found_tests != 0;
let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap();
diff --git a/src/librustdoc/passes/check_custom_code_classes.rs b/src/librustdoc/passes/check_custom_code_classes.rs
new file mode 100644
index 000000000..6266d3ff5
--- /dev/null
+++ b/src/librustdoc/passes/check_custom_code_classes.rs
@@ -0,0 +1,92 @@
+//! NIGHTLY & UNSTABLE CHECK: custom_code_classes_in_docs
+//!
+//! This pass will produce errors when finding custom classes outside of
+//! nightly + relevant feature active.
+
+use super::Pass;
+use crate::clean::{Crate, Item};
+use crate::core::DocContext;
+use crate::fold::DocFolder;
+use crate::html::markdown::{find_codes, ErrorCodes, LangString};
+
+use rustc_errors::StashKey;
+use rustc_feature::GateIssue;
+use rustc_session::parse::add_feature_diagnostics_for_issue;
+use rustc_span::symbol::sym;
+
+pub(crate) const CHECK_CUSTOM_CODE_CLASSES: Pass = Pass {
+ name: "check-custom-code-classes",
+ run: check_custom_code_classes,
+ description: "check for custom code classes without the feature-gate enabled",
+};
+
+pub(crate) fn check_custom_code_classes(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
+ if cx.tcx.features().custom_code_classes_in_docs {
+ // Nothing to check here if the feature is enabled.
+ return krate;
+ }
+ let mut coll = CustomCodeClassLinter { cx };
+
+ coll.fold_crate(krate)
+}
+
+struct CustomCodeClassLinter<'a, 'tcx> {
+ cx: &'a DocContext<'tcx>,
+}
+
+impl<'a, 'tcx> DocFolder for CustomCodeClassLinter<'a, 'tcx> {
+ fn fold_item(&mut self, item: Item) -> Option<Item> {
+ look_for_custom_classes(&self.cx, &item);
+ Some(self.fold_item_recur(item))
+ }
+}
+
+#[derive(Debug)]
+struct TestsWithCustomClasses {
+ custom_classes_found: Vec<String>,
+}
+
+impl crate::doctest::Tester for TestsWithCustomClasses {
+ fn add_test(&mut self, _: String, config: LangString, _: usize) {
+ self.custom_classes_found.extend(config.added_classes.into_iter());
+ }
+}
+
+pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item) {
+ if !item.item_id.is_local() {
+ // If non-local, no need to check anything.
+ return;
+ }
+
+ let mut tests = TestsWithCustomClasses { custom_classes_found: vec![] };
+
+ let dox = item.attrs.doc_value();
+ find_codes(&dox, &mut tests, ErrorCodes::No, false, None, true, true);
+
+ if !tests.custom_classes_found.is_empty() {
+ let span = item.attr_span(cx.tcx);
+ let sess = &cx.tcx.sess.parse_sess;
+ let mut err = sess
+ .span_diagnostic
+ .struct_span_warn(span, "custom classes in code blocks will change behaviour");
+ add_feature_diagnostics_for_issue(
+ &mut err,
+ sess,
+ sym::custom_code_classes_in_docs,
+ GateIssue::Language,
+ false,
+ );
+
+ err.note(
+ // This will list the wrong items to make them more easily searchable.
+ // To ensure the most correct hits, it adds back the 'class:' that was stripped.
+ format!(
+ "found these custom classes: class={}",
+ tests.custom_classes_found.join(",class=")
+ ),
+ );
+
+ // A later feature_err call can steal and cancel this warning.
+ err.stash(span, StashKey::EarlySyntaxWarning);
+ }
+}
diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs
index e732a4057..d1c4cc1f5 100644
--- a/src/librustdoc/passes/check_doc_test_visibility.rs
+++ b/src/librustdoc/passes/check_doc_test_visibility.rs
@@ -60,7 +60,7 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -
| clean::VariantItem(_)
| clean::AssocConstItem(..)
| clean::AssocTypeItem(..)
- | clean::TypedefItem(_)
+ | clean::TypeAliasItem(_)
| clean::StaticItem(_)
| clean::ConstantItem(_)
| clean::ExternCrateItem { .. }
@@ -113,7 +113,14 @@ pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item
let mut tests = Tests { found_tests: 0 };
- find_testable_code(dox, &mut tests, ErrorCodes::No, false, None);
+ find_testable_code(
+ dox,
+ &mut tests,
+ ErrorCodes::No,
+ false,
+ None,
+ cx.tcx.features().custom_code_classes_in_docs,
+ );
if tests.found_tests == 0 && cx.tcx.features().rustdoc_missing_doc_code_examples {
if should_have_doc_example(cx, item) {
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 7b0a7a90d..d216305e6 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -16,7 +16,9 @@ use rustc_hir::Mutability;
use rustc_middle::ty::{Ty, TyCtxt};
use rustc_middle::{bug, span_bug, ty};
use rustc_resolve::rustdoc::{has_primitive_or_keyword_docs, prepare_to_doc_link_resolution};
-use rustc_resolve::rustdoc::{strip_generics_from_path, MalformedGenerics};
+use rustc_resolve::rustdoc::{
+ source_span_for_markdown_range, strip_generics_from_path, MalformedGenerics,
+};
use rustc_session::lint::Lint;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{sym, Ident, Symbol};
@@ -520,8 +522,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
ty::Alias(..)
| ty::Closure(..)
| ty::Generator(..)
- | ty::GeneratorWitness(_)
- | ty::GeneratorWitnessMIR(..)
+ | ty::GeneratorWitness(..)
| ty::Dynamic(..)
| ty::Param(_)
| ty::Bound(..)
@@ -592,7 +593,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
.unwrap_or(Vec::new())
}
}
- Res::Def(DefKind::TyAlias { .. }, did) => {
+ Res::Def(DefKind::TyAlias, did) => {
// Resolve the link on the type the alias points to.
// FIXME: if the associated item is defined directly on the type alias,
// it will show up on its documentation page, we should link there instead.
@@ -1211,11 +1212,11 @@ impl LinkCollector<'_, '_> {
ori_link: &MarkdownLinkRange,
item: &Item,
) {
- let span = super::source_span_for_markdown_range(
+ let span = source_span_for_markdown_range(
self.cx.tcx,
dox,
ori_link.inner_range(),
- &item.attrs,
+ &item.attrs.doc_strings,
)
.unwrap_or_else(|| item.attr_span(self.cx.tcx));
rustc_session::parse::feature_err(
@@ -1702,26 +1703,27 @@ fn report_diagnostic(
let (span, link_range) = match link_range {
MarkdownLinkRange::Destination(md_range) => {
let mut md_range = md_range.clone();
- let sp = super::source_span_for_markdown_range(tcx, dox, &md_range, &item.attrs)
- .map(|mut sp| {
- while dox.as_bytes().get(md_range.start) == Some(&b' ')
- || dox.as_bytes().get(md_range.start) == Some(&b'`')
- {
- md_range.start += 1;
- sp = sp.with_lo(sp.lo() + BytePos(1));
- }
- while dox.as_bytes().get(md_range.end - 1) == Some(&b' ')
- || dox.as_bytes().get(md_range.end - 1) == Some(&b'`')
- {
- md_range.end -= 1;
- sp = sp.with_hi(sp.hi() - BytePos(1));
- }
- sp
- });
+ let sp =
+ source_span_for_markdown_range(tcx, dox, &md_range, &item.attrs.doc_strings)
+ .map(|mut sp| {
+ while dox.as_bytes().get(md_range.start) == Some(&b' ')
+ || dox.as_bytes().get(md_range.start) == Some(&b'`')
+ {
+ md_range.start += 1;
+ sp = sp.with_lo(sp.lo() + BytePos(1));
+ }
+ while dox.as_bytes().get(md_range.end - 1) == Some(&b' ')
+ || dox.as_bytes().get(md_range.end - 1) == Some(&b'`')
+ {
+ md_range.end -= 1;
+ sp = sp.with_hi(sp.hi() - BytePos(1));
+ }
+ sp
+ });
(sp, MarkdownLinkRange::Destination(md_range))
}
MarkdownLinkRange::WholeLink(md_range) => (
- super::source_span_for_markdown_range(tcx, dox, &md_range, &item.attrs),
+ source_span_for_markdown_range(tcx, dox, &md_range, &item.attrs.doc_strings),
link_range.clone(),
),
};
diff --git a/src/librustdoc/passes/lint/bare_urls.rs b/src/librustdoc/passes/lint/bare_urls.rs
index 97078a5cb..0c5cfffe1 100644
--- a/src/librustdoc/passes/lint/bare_urls.rs
+++ b/src/librustdoc/passes/lint/bare_urls.rs
@@ -4,11 +4,11 @@
use crate::clean::*;
use crate::core::DocContext;
use crate::html::markdown::main_body_opts;
-use crate::passes::source_span_for_markdown_range;
use core::ops::Range;
use pulldown_cmark::{Event, Parser, Tag};
use regex::Regex;
use rustc_errors::Applicability;
+use rustc_resolve::rustdoc::source_span_for_markdown_range;
use std::mem;
use std::sync::LazyLock;
@@ -21,8 +21,9 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item) {
if !dox.is_empty() {
let report_diag =
|cx: &DocContext<'_>, msg: &'static str, url: &str, range: Range<usize>| {
- let sp = source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs)
- .unwrap_or_else(|| item.attr_span(cx.tcx));
+ let sp =
+ source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs.doc_strings)
+ .unwrap_or_else(|| item.attr_span(cx.tcx));
cx.tcx.struct_span_lint_hir(crate::lint::BARE_URLS, hir_id, sp, msg, |lint| {
lint.note("bare URLs are not automatically turned into clickable links")
.span_suggestion(
diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs
index 37e28e1fb..ac8a75a4f 100644
--- a/src/librustdoc/passes/lint/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs
@@ -6,6 +6,7 @@ use rustc_errors::{
Applicability, Diagnostic, Handler, LazyFallbackBundle,
};
use rustc_parse::parse_stream_from_source_str;
+use rustc_resolve::rustdoc::source_span_for_markdown_range;
use rustc_session::parse::ParseSess;
use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId};
use rustc_span::source_map::{FilePathMapping, SourceMap};
@@ -14,13 +15,14 @@ use rustc_span::{FileName, InnerSpan, DUMMY_SP};
use crate::clean;
use crate::core::DocContext;
use crate::html::markdown::{self, RustCodeBlock};
-use crate::passes::source_span_for_markdown_range;
pub(crate) fn visit_item(cx: &DocContext<'_>, item: &clean::Item) {
if let Some(dox) = &item.opt_doc_value() {
let sp = item.attr_span(cx.tcx);
let extra = crate::html::markdown::ExtraInfo::new(cx.tcx, item.item_id.expect_def_id(), sp);
- for code_block in markdown::rust_code_blocks(dox, &extra) {
+ for code_block in
+ markdown::rust_code_blocks(dox, &extra, cx.tcx.features().custom_code_classes_in_docs)
+ {
check_rust_syntax(cx, item, dox, code_block);
}
}
@@ -77,11 +79,15 @@ fn check_rust_syntax(
let is_ignore = code_block.lang_string.ignore != markdown::Ignore::None;
// The span and whether it is precise or not.
- let (sp, precise_span) =
- match source_span_for_markdown_range(cx.tcx, dox, &code_block.range, &item.attrs) {
- Some(sp) => (sp, true),
- None => (item.attr_span(cx.tcx), false),
- };
+ let (sp, precise_span) = match source_span_for_markdown_range(
+ cx.tcx,
+ dox,
+ &code_block.range,
+ &item.attrs.doc_strings,
+ ) {
+ Some(sp) => (sp, true),
+ None => (item.attr_span(cx.tcx), false),
+ };
let msg = if buffer.has_errors {
"could not parse code block as Rust code"
diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs
index c135c584c..79fc599e1 100644
--- a/src/librustdoc/passes/lint/html_tags.rs
+++ b/src/librustdoc/passes/lint/html_tags.rs
@@ -2,9 +2,9 @@
use crate::clean::*;
use crate::core::DocContext;
use crate::html::markdown::main_body_opts;
-use crate::passes::source_span_for_markdown_range;
use pulldown_cmark::{BrokenLink, Event, LinkType, Parser, Tag};
+use rustc_resolve::rustdoc::source_span_for_markdown_range;
use std::iter::Peekable;
use std::ops::Range;
@@ -20,7 +20,8 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
let dox = item.doc_value();
if !dox.is_empty() {
let report_diag = |msg: String, range: &Range<usize>, is_open_tag: bool| {
- let sp = match source_span_for_markdown_range(tcx, &dox, range, &item.attrs) {
+ let sp = match source_span_for_markdown_range(tcx, &dox, range, &item.attrs.doc_strings)
+ {
Some(sp) => sp,
None => item.attr_span(tcx),
};
@@ -60,7 +61,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
tcx,
&dox,
&(generics_start..generics_end),
- &item.attrs,
+ &item.attrs.doc_strings,
) {
Some(sp) => sp,
None => item.attr_span(tcx),
diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs
index 67cd2cc97..0c15bf5f7 100644
--- a/src/librustdoc/passes/lint/redundant_explicit_links.rs
+++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs
@@ -6,6 +6,7 @@ use rustc_errors::SuggestionStyle;
use rustc_hir::def::{DefKind, DocLinkResMap, Namespace, Res};
use rustc_hir::HirId;
use rustc_lint_defs::Applicability;
+use rustc_resolve::rustdoc::source_span_for_markdown_range;
use rustc_span::Symbol;
use crate::clean::utils::find_nearest_parent_module;
@@ -13,7 +14,6 @@ use crate::clean::utils::inherits_doc_hidden;
use crate::clean::Item;
use crate::core::DocContext;
use crate::html::markdown::main_body_opts;
-use crate::passes::source_span_for_markdown_range;
#[derive(Debug)]
struct LinkData {
@@ -160,16 +160,21 @@ fn check_inline_or_reference_unknown_redundancy(
(find_resolution(resolutions, &dest)?, find_resolution(resolutions, resolvable_link)?);
if dest_res == display_res {
- let link_span = source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs)
- .unwrap_or(item.attr_span(cx.tcx));
+ let link_span =
+ source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs.doc_strings)
+ .unwrap_or(item.attr_span(cx.tcx));
let explicit_span = source_span_for_markdown_range(
cx.tcx,
&doc,
&offset_explicit_range(doc, link_range, open, close),
- &item.attrs,
+ &item.attrs.doc_strings,
+ )?;
+ let display_span = source_span_for_markdown_range(
+ cx.tcx,
+ &doc,
+ &resolvable_link_range,
+ &item.attrs.doc_strings,
)?;
- let display_span =
- source_span_for_markdown_range(cx.tcx, &doc, &resolvable_link_range, &item.attrs)?;
cx.tcx.struct_span_lint_hir(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, "redundant explicit link target", |lint| {
lint.span_label(explicit_span, "explicit target is redundant")
@@ -201,21 +206,26 @@ fn check_reference_redundancy(
(find_resolution(resolutions, &dest)?, find_resolution(resolutions, resolvable_link)?);
if dest_res == display_res {
- let link_span = source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs)
- .unwrap_or(item.attr_span(cx.tcx));
+ let link_span =
+ source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs.doc_strings)
+ .unwrap_or(item.attr_span(cx.tcx));
let explicit_span = source_span_for_markdown_range(
cx.tcx,
&doc,
&offset_explicit_range(doc, link_range.clone(), b'[', b']'),
- &item.attrs,
+ &item.attrs.doc_strings,
+ )?;
+ let display_span = source_span_for_markdown_range(
+ cx.tcx,
+ &doc,
+ &resolvable_link_range,
+ &item.attrs.doc_strings,
)?;
- let display_span =
- source_span_for_markdown_range(cx.tcx, &doc, &resolvable_link_range, &item.attrs)?;
let def_span = source_span_for_markdown_range(
cx.tcx,
&doc,
&offset_reference_def_range(doc, dest, link_range),
- &item.attrs,
+ &item.attrs.doc_strings,
)?;
cx.tcx.struct_span_lint_hir(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, "redundant explicit link target", |lint| {
diff --git a/src/librustdoc/passes/lint/unescaped_backticks.rs b/src/librustdoc/passes/lint/unescaped_backticks.rs
index 256958d71..8b7fdd6ab 100644
--- a/src/librustdoc/passes/lint/unescaped_backticks.rs
+++ b/src/librustdoc/passes/lint/unescaped_backticks.rs
@@ -3,10 +3,10 @@
use crate::clean::Item;
use crate::core::DocContext;
use crate::html::markdown::main_body_opts;
-use crate::passes::source_span_for_markdown_range;
use pulldown_cmark::{BrokenLink, Event, Parser};
use rustc_errors::DiagnosticBuilder;
use rustc_lint_defs::Applicability;
+use rustc_resolve::rustdoc::source_span_for_markdown_range;
use std::ops::Range;
pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
@@ -52,7 +52,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
tcx,
&dox,
&(backtick_index..backtick_index + 1),
- &item.attrs,
+ &item.attrs.doc_strings,
)
.unwrap_or_else(|| item.attr_span(tcx));
@@ -378,9 +378,12 @@ fn suggest_insertion(
/// Maximum bytes of context to show around the insertion.
const CONTEXT_MAX_LEN: usize = 80;
- if let Some(span) =
- source_span_for_markdown_range(cx.tcx, &dox, &(insert_index..insert_index), &item.attrs)
- {
+ if let Some(span) = source_span_for_markdown_range(
+ cx.tcx,
+ &dox,
+ &(insert_index..insert_index),
+ &item.attrs.doc_strings,
+ ) {
lint.span_suggestion(span, message, suggestion, Applicability::MaybeIncorrect);
} else {
let line_start = dox[..insert_index].rfind('\n').map_or(0, |idx| idx + 1);
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
index 4b1ff68df..4eeaaa2bb 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -1,11 +1,6 @@
//! Contains information about "passes", used to modify crate information during the documentation
//! process.
-use rustc_middle::ty::TyCtxt;
-use rustc_resolve::rustdoc::DocFragmentKind;
-use rustc_span::{InnerSpan, Span, DUMMY_SP};
-use std::ops::Range;
-
use self::Condition::*;
use crate::clean;
use crate::core::DocContext;
@@ -40,6 +35,9 @@ pub(crate) use self::calculate_doc_coverage::CALCULATE_DOC_COVERAGE;
mod lint;
pub(crate) use self::lint::RUN_LINTS;
+mod check_custom_code_classes;
+pub(crate) use self::check_custom_code_classes::CHECK_CUSTOM_CODE_CLASSES;
+
/// A single pass over the cleaned documentation.
///
/// Runs in the compiler context, so it has access to types and traits and the like.
@@ -71,6 +69,7 @@ pub(crate) enum Condition {
/// The full list of passes.
pub(crate) const PASSES: &[Pass] = &[
+ CHECK_CUSTOM_CODE_CLASSES,
CHECK_DOC_TEST_VISIBILITY,
STRIP_HIDDEN,
STRIP_PRIVATE,
@@ -84,6 +83,7 @@ pub(crate) const PASSES: &[Pass] = &[
/// The list of passes run by default.
pub(crate) const DEFAULT_PASSES: &[ConditionalPass] = &[
+ ConditionalPass::always(CHECK_CUSTOM_CODE_CLASSES),
ConditionalPass::always(COLLECT_TRAIT_IMPLS),
ConditionalPass::always(CHECK_DOC_TEST_VISIBILITY),
ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden),
@@ -115,89 +115,3 @@ impl ConditionalPass {
pub(crate) fn defaults(show_coverage: bool) -> &'static [ConditionalPass] {
if show_coverage { COVERAGE_PASSES } else { DEFAULT_PASSES }
}
-
-/// Returns a span encompassing all the given attributes.
-pub(crate) fn span_of_attrs(attrs: &clean::Attributes) -> Option<Span> {
- if attrs.doc_strings.is_empty() {
- return None;
- }
- let start = attrs.doc_strings[0].span;
- if start == DUMMY_SP {
- return None;
- }
- let end = attrs.doc_strings.last().expect("no doc strings provided").span;
- Some(start.to(end))
-}
-
-/// Attempts to match a range of bytes from parsed markdown to a `Span` in the source code.
-///
-/// This method will return `None` if we cannot construct a span from the source map or if the
-/// attributes are not all sugared doc comments. It's difficult to calculate the correct span in
-/// that case due to escaping and other source features.
-pub(crate) fn source_span_for_markdown_range(
- tcx: TyCtxt<'_>,
- markdown: &str,
- md_range: &Range<usize>,
- attrs: &clean::Attributes,
-) -> Option<Span> {
- let is_all_sugared_doc =
- attrs.doc_strings.iter().all(|frag| frag.kind == DocFragmentKind::SugaredDoc);
-
- if !is_all_sugared_doc {
- return None;
- }
-
- let snippet = tcx.sess.source_map().span_to_snippet(span_of_attrs(attrs)?).ok()?;
-
- let starting_line = markdown[..md_range.start].matches('\n').count();
- let ending_line = starting_line + markdown[md_range.start..md_range.end].matches('\n').count();
-
- // We use `split_terminator('\n')` instead of `lines()` when counting bytes so that we treat
- // CRLF and LF line endings the same way.
- let mut src_lines = snippet.split_terminator('\n');
- let md_lines = markdown.split_terminator('\n');
-
- // The number of bytes from the source span to the markdown span that are not part
- // of the markdown, like comment markers.
- let mut start_bytes = 0;
- let mut end_bytes = 0;
-
- 'outer: for (line_no, md_line) in md_lines.enumerate() {
- loop {
- let source_line = src_lines.next()?;
- match source_line.find(md_line) {
- Some(offset) => {
- if line_no == starting_line {
- start_bytes += offset;
-
- if starting_line == ending_line {
- break 'outer;
- }
- } else if line_no == ending_line {
- end_bytes += offset;
- break 'outer;
- } else if line_no < starting_line {
- start_bytes += source_line.len() - md_line.len();
- } else {
- end_bytes += source_line.len() - md_line.len();
- }
- break;
- }
- None => {
- // Since this is a source line that doesn't include a markdown line,
- // we have to count the newline that we split from earlier.
- if line_no <= starting_line {
- start_bytes += source_line.len() + 1;
- } else {
- end_bytes += source_line.len() + 1;
- }
- }
- }
- }
- }
-
- Some(span_of_attrs(attrs)?.from_inner(InnerSpan::new(
- md_range.start + start_bytes,
- md_range.end + start_bytes + end_bytes,
- )))
-}
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index 64a4ace5b..b35618415 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -49,7 +49,7 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
}
// These items can all get re-exported
clean::OpaqueTyItem(..)
- | clean::TypedefItem(..)
+ | clean::TypeAliasItem(..)
| clean::StaticItem(..)
| clean::StructItem(..)
| clean::EnumItem(..)
diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs
index 534c6eebb..dd52deef6 100644
--- a/src/librustdoc/scrape_examples.rs
+++ b/src/librustdoc/scrape_examples.rs
@@ -73,7 +73,7 @@ pub(crate) struct SyntaxRange {
impl SyntaxRange {
fn new(span: rustc_span::Span, file: &SourceFile) -> Option<Self> {
let get_pos = |bytepos: BytePos| file.original_relative_byte_pos(bytepos).0;
- let get_line = |bytepos: BytePos| file.lookup_line(bytepos);
+ let get_line = |bytepos: BytePos| file.lookup_line(file.relative_position(bytepos));
Some(SyntaxRange {
byte_span: (get_pos(span.lo()), get_pos(span.hi())),
diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs
index 722e01cd1..8c1acbd73 100644
--- a/src/librustdoc/theme.rs
+++ b/src/librustdoc/theme.rs
@@ -185,6 +185,9 @@ pub(crate) fn parse_selectors(
while let Some(c) = iter.next() {
match c {
'{' => {
+ if selector.trim().starts_with(":root[data-theme") {
+ selector = String::from(":root");
+ }
let s = minifier::css::minify(selector.trim()).map(|s| s.to_string())?;
parse_rules(content, s, iter, paths)?;
selector.clear();
diff --git a/src/librustdoc/theme/tests.rs b/src/librustdoc/theme/tests.rs
index 2a28c19c3..56f40b804 100644
--- a/src/librustdoc/theme/tests.rs
+++ b/src/librustdoc/theme/tests.rs
@@ -100,7 +100,7 @@ fn check_invalid_css() {
#[test]
fn test_with_minification() {
- let text = include_str!("../html/static/css/themes/dark.css");
+ let text = include_str!("../html/static/css/noscript.css");
let minified = minifier::css::minify(&text).expect("CSS minification failed").to_string();
let against = load_css_paths(text).unwrap();
diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs
index 390b94361..01e6cb4b9 100644
--- a/src/librustdoc/visit.rs
+++ b/src/librustdoc/visit.rs
@@ -25,7 +25,7 @@ pub(crate) trait DocVisitor: Sized {
ExternCrateItem { src: _ }
| ImportItem(_)
| FunctionItem(_)
- | TypedefItem(_)
+ | TypeAliasItem(_)
| OpaqueTyItem(_)
| StaticItem(_)
| ConstantItem(_)