Auto merge of #135715 - matthiaskrgr:rollup-9a18sxj, r=matthiaskrgr

Rollup of 4 pull requests

Successful merges:

 - #135641 ([rustdoc] Replace module list items `ul`/`li` with `dl`/`dd`/`dt` elements)
 - #135703 (Disallow `A { .. }` if `A` has no fields)
 - #135705 (Consolidate ad-hoc MIR lints into real pass-manager-based MIR lints)
 - #135708 (Some random compiler nits)

Failed merges:

 - #135685 (Remove unused `item-row` CSS class)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-01-19 03:23:56 +00:00
commit 98572840b6
64 changed files with 394 additions and 353 deletions

View File

@ -1991,18 +1991,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
adt_ty: Ty<'tcx>,
expected: Expectation<'tcx>,
expr: &hir::Expr<'_>,
span: Span,
path_span: Span,
variant: &'tcx ty::VariantDef,
hir_fields: &'tcx [hir::ExprField<'tcx>],
base_expr: &'tcx hir::StructTailExpr<'tcx>,
) {
let tcx = self.tcx;
let adt_ty = self.try_structurally_resolve_type(span, adt_ty);
let adt_ty = self.try_structurally_resolve_type(path_span, adt_ty);
let adt_ty_hint = expected.only_has_type(self).and_then(|expected| {
self.fudge_inference_if_ok(|| {
let ocx = ObligationCtxt::new(self);
ocx.sup(&self.misc(span), self.param_env, expected, adt_ty)?;
ocx.sup(&self.misc(path_span), self.param_env, expected, adt_ty)?;
if !ocx.select_where_possible().is_empty() {
return Err(TypeError::Mismatch);
}
@ -2012,11 +2012,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
if let Some(adt_ty_hint) = adt_ty_hint {
// re-link the variables that the fudging above can create.
self.demand_eqtype(span, adt_ty_hint, adt_ty);
self.demand_eqtype(path_span, adt_ty_hint, adt_ty);
}
let ty::Adt(adt, args) = adt_ty.kind() else {
span_bug!(span, "non-ADT passed to check_expr_struct_fields");
span_bug!(path_span, "non-ADT passed to check_expr_struct_fields");
};
let adt_kind = adt.adt_kind();
@ -2107,7 +2107,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if adt_kind == AdtKind::Union && hir_fields.len() != 1 {
struct_span_code_err!(
self.dcx(),
span,
path_span,
E0784,
"union expressions should have exactly one field",
)
@ -2167,6 +2167,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
return;
}
if variant.fields.is_empty() {
let mut err = self.dcx().struct_span_err(
span,
format!(
"`{adt_ty}` has no fields, `..` needs at least one default field in the \
struct definition",
),
);
err.span_label(path_span, "this type has no fields");
err.emit();
}
if !missing_mandatory_fields.is_empty() {
let s = pluralize!(missing_mandatory_fields.len());
let fields: Vec<_> =
@ -2316,11 +2327,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.collect();
if !private_fields.is_empty() {
self.report_private_fields(adt_ty, span, expr.span, private_fields, hir_fields);
self.report_private_fields(
adt_ty,
path_span,
expr.span,
private_fields,
hir_fields,
);
} else {
self.report_missing_fields(
adt_ty,
span,
path_span,
remaining_fields,
variant,
hir_fields,

View File

@ -87,7 +87,7 @@ fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &UnordSet<LocalDef
}
fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> {
typeck_with_fallback(tcx, def_id, None)
typeck_with_inspect(tcx, def_id, None)
}
/// Same as `typeck` but `inspect` is invoked on evaluation of each root obligation.
@ -99,11 +99,11 @@ pub fn inspect_typeck<'tcx>(
def_id: LocalDefId,
inspect: ObligationInspector<'tcx>,
) -> &'tcx ty::TypeckResults<'tcx> {
typeck_with_fallback(tcx, def_id, Some(inspect))
typeck_with_inspect(tcx, def_id, Some(inspect))
}
#[instrument(level = "debug", skip(tcx, inspector), ret)]
fn typeck_with_fallback<'tcx>(
fn typeck_with_inspect<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
inspector: Option<ObligationInspector<'tcx>>,

View File

@ -875,6 +875,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
});
// Freeze definitions as we don't add new ones at this point.
// We need to wait until now since we synthesize a by-move body
// for all coroutine-closures.
//
// This improves performance by allowing lock-free access to them.
tcx.untracked().definitions.freeze();
@ -887,7 +889,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
});
});
sess.time("MIR_effect_checking", || {
for def_id in tcx.hir().body_owners() {
tcx.hir().par_body_owners(|def_id| {
tcx.ensure().has_ffi_unwind_calls(def_id);
// If we need to codegen, ensure that we emit all errors from
@ -898,15 +900,17 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
{
tcx.ensure().mir_drops_elaborated_and_const_checked(def_id);
}
}
});
});
tcx.hir().par_body_owners(|def_id| {
if tcx.is_coroutine(def_id.to_def_id()) {
tcx.ensure().mir_coroutine_witnesses(def_id);
tcx.ensure().check_coroutine_obligations(
tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(),
);
}
sess.time("coroutine_obligations", || {
tcx.hir().par_body_owners(|def_id| {
if tcx.is_coroutine(def_id.to_def_id()) {
tcx.ensure().mir_coroutine_witnesses(def_id);
tcx.ensure().check_coroutine_obligations(
tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(),
);
}
});
});
sess.time("layout_testing", || layout_test::test_layout(tcx));

View File

@ -1164,8 +1164,7 @@ rustc_queries! {
}
/// Check whether the function has any recursion that could cause the inliner to trigger
/// a cycle. Returns the call stack causing the cycle. The call stack does not contain the
/// current function, just all intermediate functions.
/// a cycle.
query mir_callgraph_reachable(key: (ty::Instance<'tcx>, LocalDefId)) -> bool {
fatal_cycle
desc { |tcx|

View File

@ -118,12 +118,6 @@ mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
.note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
.label = use of extern static
mir_build_force_inline =
`{$callee}` is incompatible with `#[rustc_force_inline]`
.attr = annotation here
.callee = `{$callee}` defined here
.note = incompatible due to: {$reason}
mir_build_inform_irrefutable = `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
mir_build_initializing_type_with_requires_unsafe =
@ -330,12 +324,6 @@ mir_build_type_not_structural_more_info = see https://doc.rust-lang.org/stable/s
mir_build_type_not_structural_tip =
the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
mir_build_unconditional_recursion = function cannot return without recursing
.label = cannot return without recursing
.help = a `loop` may express intention better if this is on purpose
mir_build_unconditional_recursion_call_site_label = recursive call site
mir_build_union_field_requires_unsafe =
access to union field is unsafe and requires unsafe block
.note = the field may not be properly initialized: using uninitialized data will cause undefined behavior

View File

@ -26,10 +26,8 @@ use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt, TypeVisitableExt, TypingMode
use rustc_middle::{bug, span_bug};
use rustc_span::{Span, Symbol, sym};
use super::lints;
use crate::builder::expr::as_place::PlaceBuilder;
use crate::builder::scope::DropKind;
use crate::check_inline;
pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
tcx: TyCtxt<'tcx>,
@ -48,7 +46,7 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
}
/// Construct the MIR for a given `DefId`.
pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx> {
pub(crate) fn build_mir<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx> {
let tcx = tcx.tcx;
tcx.ensure_with_value().thir_abstract_const(def);
if let Err(e) = tcx.check_match(def) {
@ -80,9 +78,6 @@ pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx
}
};
lints::check(tcx, &body);
check_inline::check_force_inline(tcx, &body);
// The borrow checker will replace all the regions here with its own
// inference variables. There's no point having non-erased regions here.
// The exception is `body.user_type_annotations`, which is used unmodified

View File

@ -11,16 +11,6 @@ use rustc_span::{Span, Symbol};
use crate::fluent_generated as fluent;
#[derive(LintDiagnostic)]
#[diag(mir_build_unconditional_recursion)]
#[help]
pub(crate) struct UnconditionalRecursion {
#[label]
pub(crate) span: Span,
#[label(mir_build_unconditional_recursion_call_site_label)]
pub(crate) call_sites: Vec<Span>,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_call_to_deprecated_safe_fn_requires_unsafe)]
pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafe {
@ -1107,15 +1097,3 @@ impl<'a> Subdiagnostic for Rust2024IncompatiblePatSugg<'a> {
);
}
}
#[derive(Diagnostic)]
#[diag(mir_build_force_inline)]
#[note]
pub(crate) struct InvalidForceInline {
#[primary_span]
pub attr_span: Span,
#[label(mir_build_callee)]
pub callee_span: Span,
pub callee: String,
pub reason: &'static str,
}

View File

@ -15,11 +15,9 @@
// "Go to file" feature to silently ignore all files in the module, probably
// because it assumes that "build" is a build-output directory. See #134365.
mod builder;
pub mod check_inline;
mod check_tail_calls;
mod check_unsafety;
mod errors;
pub mod lints;
mod thir;
use rustc_middle::util::Providers;
@ -29,7 +27,7 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
pub fn provide(providers: &mut Providers) {
providers.check_match = thir::pattern::check_match;
providers.lit_to_const = thir::constant::lit_to_const;
providers.hooks.build_mir = builder::mir_build;
providers.hooks.build_mir = builder::build_mir;
providers.closure_saved_names_of_captured_variables =
builder::closure_saved_names_of_captured_variables;
providers.check_unsafety = check_unsafety::check_unsafety;

View File

@ -27,6 +27,12 @@ mir_transform_force_inline =
.callee = `{$callee}` defined here
.note = could not be inlined due to: {$reason}
mir_transform_force_inline_attr =
`{$callee}` is incompatible with `#[rustc_force_inline]`
.attr = annotation here
.callee = `{$callee}` defined here
.note = incompatible due to: {$reason}
mir_transform_force_inline_justification =
`{$callee}` is required to be inlined to: {$sym}
@ -66,6 +72,12 @@ mir_transform_unaligned_packed_ref = reference to packed field is unaligned
.note_ub = creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
.help = copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
mir_transform_unconditional_recursion = function cannot return without recursing
.label = cannot return without recursing
.help = a `loop` may express intention better if this is on purpose
mir_transform_unconditional_recursion_call_site_label = recursive call site
mir_transform_undefined_transmute = pointers cannot be transmuted to integers during const eval
.note = at compile-time, pointers do not have an integer value
.note2 = avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior

View File

@ -10,25 +10,54 @@ use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION;
use rustc_span::Span;
use crate::errors::UnconditionalRecursion;
use crate::pass_manager::MirLint;
pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
check_call_recursion(tcx, body);
pub(super) struct CheckCallRecursion;
impl<'tcx> MirLint<'tcx> for CheckCallRecursion {
fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
let def_id = body.source.def_id().expect_local();
if let DefKind::Fn | DefKind::AssocFn = tcx.def_kind(def_id) {
// If this is trait/impl method, extract the trait's args.
let trait_args = match tcx.trait_of_item(def_id.to_def_id()) {
Some(trait_def_id) => {
let trait_args_count = tcx.generics_of(trait_def_id).count();
&GenericArgs::identity_for_item(tcx, def_id)[..trait_args_count]
}
_ => &[],
};
check_recursion(tcx, body, CallRecursion { trait_args })
}
}
}
fn check_call_recursion<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
let def_id = body.source.def_id().expect_local();
/// Requires drop elaboration to have been performed.
pub(super) struct CheckDropRecursion;
if let DefKind::Fn | DefKind::AssocFn = tcx.def_kind(def_id) {
// If this is trait/impl method, extract the trait's args.
let trait_args = match tcx.trait_of_item(def_id.to_def_id()) {
Some(trait_def_id) => {
let trait_args_count = tcx.generics_of(trait_def_id).count();
&GenericArgs::identity_for_item(tcx, def_id)[..trait_args_count]
impl<'tcx> MirLint<'tcx> for CheckDropRecursion {
fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
let def_id = body.source.def_id().expect_local();
// First check if `body` is an `fn drop()` of `Drop`
if let DefKind::AssocFn = tcx.def_kind(def_id)
&& let Some(trait_ref) =
tcx.impl_of_method(def_id.to_def_id()).and_then(|def_id| tcx.impl_trait_ref(def_id))
&& let Some(drop_trait) = tcx.lang_items().drop_trait()
&& drop_trait == trait_ref.instantiate_identity().def_id
// avoid erroneous `Drop` impls from causing ICEs below
&& let sig = tcx.fn_sig(def_id).instantiate_identity()
&& sig.inputs().skip_binder().len() == 1
{
// It was. Now figure out for what type `Drop` is implemented and then
// check for recursion.
if let ty::Ref(_, dropped_ty, _) =
tcx.liberate_late_bound_regions(def_id.to_def_id(), sig.input(0)).kind()
{
check_recursion(tcx, body, RecursiveDrop { drop_for: *dropped_ty });
}
_ => &[],
};
check_recursion(tcx, body, CallRecursion { trait_args })
}
}
}
@ -61,30 +90,6 @@ fn check_recursion<'tcx>(
}
}
/// Requires drop elaboration to have been performed first.
pub fn check_drop_recursion<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
let def_id = body.source.def_id().expect_local();
// First check if `body` is an `fn drop()` of `Drop`
if let DefKind::AssocFn = tcx.def_kind(def_id)
&& let Some(trait_ref) =
tcx.impl_of_method(def_id.to_def_id()).and_then(|def_id| tcx.impl_trait_ref(def_id))
&& let Some(drop_trait) = tcx.lang_items().drop_trait()
&& drop_trait == trait_ref.instantiate_identity().def_id
// avoid erroneous `Drop` impls from causing ICEs below
&& let sig = tcx.fn_sig(def_id).instantiate_identity()
&& sig.inputs().skip_binder().len() == 1
{
// It was. Now figure out for what type `Drop` is implemented and then
// check for recursion.
if let ty::Ref(_, dropped_ty, _) =
tcx.liberate_late_bound_regions(def_id.to_def_id(), sig.input(0)).kind()
{
check_recursion(tcx, body, RecursiveDrop { drop_for: *dropped_ty });
}
}
}
trait TerminatorClassifier<'tcx> {
fn is_recursive_terminator(
&self,

View File

@ -1,3 +1,6 @@
//! Check that a body annotated with `#[rustc_force_inline]` will not fail to inline based on its
//! definition alone (irrespective of any specific caller).
use rustc_attr_parsing::InlineAttr;
use rustc_hir::def_id::DefId;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
@ -6,30 +9,37 @@ use rustc_middle::ty;
use rustc_middle::ty::TyCtxt;
use rustc_span::sym;
/// Check that a body annotated with `#[rustc_force_inline]` will not fail to inline based on its
/// definition alone (irrespective of any specific caller).
pub(crate) fn check_force_inline<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
let def_id = body.source.def_id();
if !tcx.hir().body_owner_kind(def_id).is_fn_or_closure() || !def_id.is_local() {
return;
}
let InlineAttr::Force { attr_span, .. } = tcx.codegen_fn_attrs(def_id).inline else {
return;
};
use crate::pass_manager::MirLint;
if let Err(reason) =
is_inline_valid_on_fn(tcx, def_id).and_then(|_| is_inline_valid_on_body(tcx, body))
{
tcx.dcx().emit_err(crate::errors::InvalidForceInline {
attr_span,
callee_span: tcx.def_span(def_id),
callee: tcx.def_path_str(def_id),
reason,
});
pub(super) struct CheckForceInline;
impl<'tcx> MirLint<'tcx> for CheckForceInline {
fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
let def_id = body.source.def_id();
if !tcx.hir().body_owner_kind(def_id).is_fn_or_closure() || !def_id.is_local() {
return;
}
let InlineAttr::Force { attr_span, .. } = tcx.codegen_fn_attrs(def_id).inline else {
return;
};
if let Err(reason) =
is_inline_valid_on_fn(tcx, def_id).and_then(|_| is_inline_valid_on_body(tcx, body))
{
tcx.dcx().emit_err(crate::errors::InvalidForceInline {
attr_span,
callee_span: tcx.def_span(def_id),
callee: tcx.def_path_str(def_id),
reason,
});
}
}
}
pub fn is_inline_valid_on_fn<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<(), &'static str> {
pub(super) fn is_inline_valid_on_fn<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
) -> Result<(), &'static str> {
let codegen_attrs = tcx.codegen_fn_attrs(def_id);
if tcx.has_attr(def_id, sym::rustc_no_mir_inline) {
return Err("#[rustc_no_mir_inline]");
@ -65,7 +75,7 @@ pub fn is_inline_valid_on_fn<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<(
Ok(())
}
pub fn is_inline_valid_on_body<'tcx>(
pub(super) fn is_inline_valid_on_body<'tcx>(
_: TyCtxt<'tcx>,
body: &Body<'tcx>,
) -> Result<(), &'static str> {

View File

@ -9,6 +9,28 @@ use rustc_span::{Span, Symbol};
use crate::fluent_generated as fluent;
#[derive(LintDiagnostic)]
#[diag(mir_transform_unconditional_recursion)]
#[help]
pub(crate) struct UnconditionalRecursion {
#[label]
pub(crate) span: Span,
#[label(mir_transform_unconditional_recursion_call_site_label)]
pub(crate) call_sites: Vec<Span>,
}
#[derive(Diagnostic)]
#[diag(mir_transform_force_inline_attr)]
#[note]
pub(crate) struct InvalidForceInline {
#[primary_span]
pub attr_span: Span,
#[label(mir_transform_callee)]
pub callee_span: Span,
pub callee: String,
pub reason: &'static str,
}
#[derive(LintDiagnostic)]
pub(crate) enum ConstMutate {
#[diag(mir_transform_const_modify)]

View File

@ -21,8 +21,8 @@ use tracing::{debug, instrument, trace, trace_span};
use crate::cost_checker::CostChecker;
use crate::deref_separator::deref_finder;
use crate::simplify::simplify_cfg;
use crate::util;
use crate::validate::validate_types;
use crate::{check_inline, util};
pub(crate) mod cycle;
@ -575,7 +575,7 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
check_mir_is_available(inliner, caller_body, callsite.callee)?;
let callee_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id());
rustc_mir_build::check_inline::is_inline_valid_on_fn(tcx, callsite.callee.def_id())?;
check_inline::is_inline_valid_on_fn(tcx, callsite.callee.def_id())?;
check_codegen_attributes(inliner, callsite, callee_attrs)?;
let terminator = caller_body[callsite.block].terminator.as_ref().unwrap();
@ -590,7 +590,7 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
}
let callee_body = try_instance_mir(tcx, callsite.callee.def)?;
rustc_mir_build::check_inline::is_inline_valid_on_body(tcx, callee_body)?;
check_inline::is_inline_valid_on_body(tcx, callee_body)?;
inliner.check_callee_mir_body(callsite, callee_body, callee_attrs)?;
let Ok(callee_body) = callsite.callee.try_instantiate_mir_and_normalize_erasing_regions(

View File

@ -114,6 +114,8 @@ declare_passes! {
mod add_moves_for_packed_drops : AddMovesForPackedDrops;
mod add_retag : AddRetag;
mod add_subtyping_projections : Subtyper;
mod check_inline : CheckForceInline;
mod check_call_recursion : CheckCallRecursion, CheckDropRecursion;
mod check_alignment : CheckAlignment;
mod check_const_item_mutation : CheckConstItemMutation;
mod check_packed_ref : CheckPackedRef;
@ -375,6 +377,8 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> {
&mut body,
&[
// MIR-level lints.
&Lint(check_inline::CheckForceInline),
&Lint(check_call_recursion::CheckCallRecursion),
&Lint(check_packed_ref::CheckPackedRef),
&Lint(check_const_item_mutation::CheckConstItemMutation),
&Lint(function_item_references::FunctionItemReferences),
@ -505,10 +509,6 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &
run_analysis_to_runtime_passes(tcx, &mut body);
// Now that drop elaboration has been performed, we can check for
// unconditional drop recursion.
rustc_mir_build::lints::check_drop_recursion(tcx, &body);
tcx.alloc_steal_mir(body)
}
@ -570,6 +570,8 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
// Calling this after `PostAnalysisNormalize` ensures that we don't deal with opaque types.
&add_subtyping_projections::Subtyper,
&elaborate_drops::ElaborateDrops,
// Needs to happen after drop elaboration.
&Lint(check_call_recursion::CheckDropRecursion),
// This will remove extraneous landing pads which are no longer
// necessary as well as forcing any call in a non-unwinding
// function calling a possibly-unwinding function to abort the process.

View File

@ -140,10 +140,9 @@ macro_rules! item_template_methods {
};
}
const ITEM_TABLE_OPEN: &str = "<ul class=\"item-table\">";
const ITEM_TABLE_CLOSE: &str = "</ul>";
const ITEM_TABLE_ROW_OPEN: &str = "<li>";
const ITEM_TABLE_ROW_CLOSE: &str = "</li>";
const ITEM_TABLE_OPEN: &str = "<dl class=\"item-table\">";
const REEXPORTS_TABLE_OPEN: &str = "<dl class=\"item-table reexports\">";
const ITEM_TABLE_CLOSE: &str = "</dl>";
// A component in a `use` path, like `string` in std::string::ToString
struct PathComponent {
@ -400,37 +399,32 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
w.write_str(ITEM_TABLE_CLOSE);
}
last_section = Some(my_section);
write_section_heading(
w,
my_section.name(),
&cx.derive_id(my_section.id()),
None,
ITEM_TABLE_OPEN,
);
let section_id = my_section.id();
let tag =
if section_id == "reexports" { REEXPORTS_TABLE_OPEN } else { ITEM_TABLE_OPEN };
write_section_heading(w, my_section.name(), &cx.derive_id(section_id), None, tag);
}
match myitem.kind {
clean::ExternCrateItem { ref src } => {
use crate::html::format::anchor;
w.write_str(ITEM_TABLE_ROW_OPEN);
match *src {
Some(src) => write!(
w,
"<div class=\"item-name\"><code>{}extern crate {} as {};",
"<dt><code>{}extern crate {} as {};",
visibility_print_with_space(myitem, cx),
anchor(myitem.item_id.expect_def_id(), src, cx),
EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()),
),
None => write!(
w,
"<div class=\"item-name\"><code>{}extern crate {};",
"<dt><code>{}extern crate {};",
visibility_print_with_space(myitem, cx),
anchor(myitem.item_id.expect_def_id(), myitem.name.unwrap(), cx),
),
}
w.write_str("</code></div>");
w.write_str(ITEM_TABLE_ROW_CLOSE);
w.write_str("</code></dt>");
}
clean::ImportItem(ref import) => {
@ -438,28 +432,20 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
extra_info_tags(tcx, myitem, item, Some(import_def_id)).to_string()
});
w.write_str(ITEM_TABLE_ROW_OPEN);
let id = match import.kind {
clean::ImportKind::Simple(s) => {
format!(" id=\"{}\"", cx.derive_id(format!("reexport.{s}")))
}
clean::ImportKind::Glob => String::new(),
};
let (stab_tags_before, stab_tags_after) = if stab_tags.is_empty() {
("", "")
} else {
("<div class=\"desc docblock-short\">", "</div>")
};
write!(
w,
"<div class=\"item-name\"{id}>\
<code>{vis}{imp}</code>\
</div>\
{stab_tags_before}{stab_tags}{stab_tags_after}",
"<dt{id}>\
<code>{vis}{imp}</code>{stab_tags}\
</dt>",
vis = visibility_print_with_space(myitem, cx),
imp = import.print(cx),
);
w.write_str(ITEM_TABLE_ROW_CLOSE);
}
_ => {
@ -492,22 +478,18 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
_ => "",
};
w.write_str(ITEM_TABLE_ROW_OPEN);
let docs =
MarkdownSummaryLine(&myitem.doc_value(), &myitem.links(cx)).into_string();
let (docs_before, docs_after) = if docs.is_empty() {
("", "")
} else {
("<div class=\"desc docblock-short\">", "</div>")
};
let (docs_before, docs_after) =
if docs.is_empty() { ("", "") } else { ("<dd>", "</dd>") };
write!(
w,
"<div class=\"item-name\">\
"<dt>\
<a class=\"{class}\" href=\"{href}\" title=\"{title}\">{name}</a>\
{visibility_and_hidden}\
{unsafety_flag}\
{stab_tags}\
</div>\
</dt>\
{docs_before}{docs}{docs_after}",
name = EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()),
visibility_and_hidden = visibility_and_hidden,
@ -521,7 +503,6 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
.collect::<Vec<_>>()
.join(" "),
);
w.write_str(ITEM_TABLE_ROW_CLOSE);
}
}
}

View File

@ -242,7 +242,7 @@ h1, h2, h3, h4, h5, h6,
.mobile-topbar,
.search-input,
.search-results .result-name,
.item-name > a,
.item-table dt > a,
.out-of-band,
.sub-heading,
span.since,
@ -385,11 +385,11 @@ details:not(.toggle) summary {
code, pre, .code-header, .type-signature {
font-family: "Source Code Pro", monospace;
}
.docblock code, .docblock-short code {
.docblock code, .item-table dd code {
border-radius: 3px;
padding: 0 0.125em;
}
.docblock pre code, .docblock-short pre code {
.docblock pre code, .item-table dd pre code {
padding: 0;
}
pre {
@ -887,13 +887,13 @@ both the code example and the line numbers, so we need to remove the radius in t
text-align: center;
}
.docblock-short {
.item-table dd {
overflow-wrap: break-word;
overflow-wrap: anywhere;
}
/* Wrap non-pre code blocks (`text`) but not (```text```). */
.docblock :not(pre) > code,
.docblock-short code {
.item-table dd code {
white-space: pre-wrap;
}
@ -938,7 +938,7 @@ rustdoc-toolbar {
min-height: 60px;
}
.docblock code, .docblock-short code,
.docblock code, .item-table dd code,
pre, .rustdoc.src .example-wrap, .example-wrap .src-line-numbers {
background-color: var(--code-block-background-color);
border-radius: var(--code-block-border-radius);
@ -964,7 +964,7 @@ pre, .rustdoc.src .example-wrap, .example-wrap .src-line-numbers {
background: var(--table-alt-row-background-color);
}
.docblock .stab, .docblock-short .stab, .docblock p code {
.docblock .stab, .item-table dd .stab, .docblock p code {
display: inline-block;
}
@ -1069,7 +1069,7 @@ because of the `[-]` element which would overlap with it. */
.example-wrap .rust a:hover,
.all-items a:hover,
.docblock a:not(.scrape-help):not(.tooltip):hover:not(.doc-anchor),
.docblock-short a:not(.scrape-help):not(.tooltip):hover,
.item-table dd a:not(.scrape-help):not(.tooltip):hover,
.item-info a {
text-decoration: underline;
}
@ -1102,20 +1102,17 @@ table,
}
.item-table {
display: table;
padding: 0;
margin: 0;
width: 100%;
}
.item-table > li {
display: table-row;
}
.item-table > li > div {
display: table-cell;
}
.item-table > li > .item-name {
.item-table > dt {
padding-right: 1.25rem;
}
.item-table > dd {
margin-inline-start: 0;
margin-left: 0;
}
.search-results-title {
margin-top: 0;
@ -1415,7 +1412,7 @@ so that we can apply CSS-filters to change the arrow color in themes */
padding: 3px;
margin-bottom: 5px;
}
.item-name .stab {
.item-table dt .stab {
margin-left: 0.3125em;
}
.stab {
@ -2476,8 +2473,7 @@ in src-script.js and main.js
}
/* Display an alternating layout on tablets and phones */
.item-table, .item-row, .item-table > li, .item-table > li > div,
.search-results > a, .search-results > a > div {
.item-row, .search-results > a, .search-results > a > div {
display: block;
}
@ -2485,7 +2481,7 @@ in src-script.js and main.js
.search-results > a {
padding: 5px 0px;
}
.search-results > a > div.desc, .item-table > li > div.desc {
.search-results > a > div.desc, .item-table dd {
padding-left: 2em;
}
.search-results .result-name {
@ -2546,12 +2542,20 @@ in src-script.js and main.js
box-shadow: 0 0 4px var(--main-background-color);
}
.item-table > li > .item-name {
width: 33%;
/* Since the screen is wide enough, we show items on their description on the same line. */
.item-table:not(.reexports) {
display: grid;
grid-template-columns: 33% 67%;
}
.item-table > li > div {
.item-table > dt, .item-table > dd {
overflow-wrap: anywhere;
}
.item-table > dt {
grid-column-start: 1;
}
.item-table > dd {
grid-column-start: 2;
}
}
@media print {

View File

@ -3,7 +3,7 @@
go-to: "file://" + |DOC_PATH| + "/test_docs/huge_amount_of_consts/index.html"
compare-elements-position-near-false: (
"//ul[@class='item-table']/li[last()-1]",
"//ul[@class='item-table']/li[last()-3]",
"//dl[@class='item-table']/dt[last()-1]",
"//dl[@class='item-table']/dt[last()-3]",
{"y": 12},
)

View File

@ -3,21 +3,21 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/short_docs/index.html"
set-window-size: (1000, 600)
// First we ensure that there is only one `item-table`...
assert-count: ("ul.item-table", 1)
assert-count: ("dl.item-table", 1)
// And only two items in it.
assert-count: ("ul.item-table li", 2)
assert-count: ("dl.item-table dt", 2)
// If they don't have the same height, then it means one of the two is on two lines whereas it
// shouldn't!
compare-elements-size: (
".item-table .item-name a[href='fn.mult_vec_num.html']",
".item-table .item-name a[href='fn.subt_vec_num.html']",
".item-table dt a[href='fn.mult_vec_num.html']",
".item-table dt a[href='fn.subt_vec_num.html']",
["height"],
)
// We also check that the `item-table` is taking the full width.
compare-elements-size: (
"#functions",
"ul.item-table",
"dl.item-table",
["width"],
)

View File

@ -1,6 +1,6 @@
// This test ensures that <table> elements aren't display in items summary.
go-to: "file://" + |DOC_PATH| + "/lib2/summary_table/index.html"
// We check that we picked the right item first.
assert-text: (".item-table .item-name", "Foo")
assert-text: (".item-table dt", "Foo")
// Then we check that its summary is empty.
assert-false: ".item-table .desc"
assert-false: ".item-table dd"

View File

@ -12,59 +12,59 @@ assert: (".stab.portability")
// make sure that deprecated and portability have the right colors
assert-css: (
".item-table .item-name .stab.deprecated",
".item-table dt .stab.deprecated",
{ "background-color": "#fff5d6" },
)
assert-css: (
".item-table .item-name .stab.portability",
".item-table dt .stab.portability",
{ "background-color": "#fff5d6" },
)
// table like view
assert-css: (".desc.docblock-short", { "padding-left": "0px" })
assert-css: ("dd", { "padding-left": "0px" })
compare-elements-position-near: (
"//*[@class='item-name']//a[normalize-space()='replaced_function']",
".item-name .stab.deprecated",
"//dt//a[normalize-space()='replaced_function']",
"dt .stab.deprecated",
{"y": 2},
)
// "Unix" part is on second line
compare-elements-position-false: (
".item-name .stab.deprecated",
".item-name .stab.portability",
"dt .stab.deprecated",
"dt .stab.portability",
["y"],
)
// Ensure no wrap
compare-elements-position: (
"//*[@class='item-name']//a[normalize-space()='replaced_function']/..",
"//*[@class='desc docblock-short'][normalize-space()='a thing with a label']",
"//dt//a[normalize-space()='replaced_function']/..",
"//dd[normalize-space()='a thing with a label']",
["y"],
)
// Mobile view
set-window-size: (600, 600)
// staggered layout with 2em spacing
assert-css: (".desc.docblock-short", { "padding-left": "32px" })
assert-css: ("dd", { "padding-left": "32px" })
compare-elements-position-near: (
"//*[@class='item-name']//a[normalize-space()='replaced_function']",
".item-name .stab.deprecated",
"//dt//a[normalize-space()='replaced_function']",
"dt .stab.deprecated",
{"y": 2},
)
compare-elements-position: (
".item-name .stab.deprecated",
".item-name .stab.portability",
"dt .stab.deprecated",
"dt .stab.portability",
["y"],
)
// Ensure wrap
compare-elements-position-false: (
"//*[@class='item-name']//a[normalize-space()='replaced_function']/..",
"//*[@class='desc docblock-short'][normalize-space()='a thing with a label']",
"//dt//a[normalize-space()='replaced_function']/..",
"//dd[normalize-space()='a thing with a label']",
["y"],
)
compare-elements-position-false: (
".item-name .stab.deprecated",
"//*[@class='desc docblock-short'][normalize-space()='a thing with a label']",
"dt .stab.deprecated",
"//dd[normalize-space()='a thing with a label']",
["y"],
)

View File

@ -37,9 +37,9 @@ define-function: (
},
ALL,
)
move-cursor-to: ".desc a[href='long_code_block_link/index.html']"
move-cursor-to: "dd a[href='long_code_block_link/index.html']"
assert-css: (
".desc a[href='long_code_block_link/index.html']",
"dd a[href='long_code_block_link/index.html']",
{"text-decoration": "underline solid " + |mod|},
)
},

View File

@ -1,67 +1,67 @@
// This test checks that the correct font is used on module items (in index.html pages).
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
assert-css: (
".item-table .item-name > a",
".item-table dt > a",
{"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
ALL,
)
assert-css: (
".item-table .docblock-short",
".item-table dd",
{"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
ALL,
)
// modules
assert-css: (
"#modules + .item-table .item-name a",
"#modules + .item-table dt a",
{"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
)
assert-css: (
"#modules + .item-table .desc.docblock-short",
"#modules + .item-table ",
{"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
)
// structs
assert-css: (
"#structs + .item-table .item-name a",
"#structs + .item-table dt a",
{"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
)
assert-css: (
"#structs + .item-table .desc.docblock-short",
"#structs + .item-table dd",
{"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
)
// enums
assert-css: (
"#enums + .item-table .item-name a",
"#enums + .item-table dt a",
{"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
)
assert-css: (
"#enums + .item-table .desc.docblock-short",
"#enums + .item-table dd",
{"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
)
// traits
assert-css: (
"#traits + .item-table .item-name a",
"#traits + .item-table dt a",
{"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
)
assert-css: (
"#traits + .item-table .desc.docblock-short",
"#traits + .item-table dd",
{"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
)
// functions
assert-css: (
"#functions + .item-table .item-name a",
"#functions + .item-table dt a",
{"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
)
assert-css: (
"#functions + .item-table .desc.docblock-short",
"#functions + .item-table dd",
{"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
)
// keywords
assert-css: (
"#keywords + .item-table .item-name a",
"#keywords + .item-table dt a",
{"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
)
assert-css: (
"#keywords + .item-table .desc.docblock-short",
"#keywords + .item-table dd",
{"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
)

View File

@ -65,8 +65,8 @@ assert-text: (".sidebar-elems section ul > li:nth-child(8)", "Functions")
assert-text: (".sidebar-elems section ul > li:nth-child(9)", "Type Aliases")
assert-text: (".sidebar-elems section ul > li:nth-child(10)", "Unions")
assert-text: (".sidebar-elems section ul > li:nth-child(11)", "Keywords")
assert-text: ("#structs + .item-table .item-name > a", "Foo")
click: "#structs + .item-table .item-name > a"
assert-text: ("#structs + .item-table dt > a", "Foo")
click: "#structs + .item-table dt > a"
// PAGE: struct.Foo.html
assert-count: (".sidebar .sidebar-crate", 1)
@ -101,8 +101,8 @@ assert-text: (".sidebar-elems > section ul.block > li:nth-child(2)", "Structs")
assert-text: (".sidebar-elems > section ul.block > li:nth-child(3)", "Traits")
assert-text: (".sidebar-elems > section ul.block > li:nth-child(4)", "Functions")
assert-text: (".sidebar-elems > section ul.block > li:nth-child(5)", "Type Aliases")
assert-text: ("#functions + .item-table .item-name > a", "foobar")
click: "#functions + .item-table .item-name > a"
assert-text: ("#functions + .item-table dt > a", "foobar")
click: "#functions + .item-table dt > a"
// PAGE: fn.foobar.html
// In items containing no items (like functions or constants) and in modules, we have no
@ -145,7 +145,7 @@ assert-text: (".sidebar-elems ul.block > li.current > a", "sub_sub_module")
// We check that we don't have the crate list.
assert-false: ".sidebar-elems .crate"
assert-text: (".sidebar-elems > section ul > li:nth-child(1)", "Functions")
assert-text: ("#functions + .item-table .item-name > a", "foo")
assert-text: ("#functions + .item-table dt > a", "foo")
// Links to trait implementations in the sidebar should not wrap even if they are long.
go-to: "file://" + |DOC_PATH| + "/lib2/struct.HasALongTraitWithParams.html"

View File

@ -17,7 +17,7 @@ define-function: (
[theme, color],
block {
call-function: ("switch-theme", {"theme": |theme|})
assert-css: (".item-name sup", {"color": |color|})
assert-css: ("dt sup", {"color": |color|})
},
)

View File

@ -16,7 +16,7 @@ mod bar {
//@ count - '//*[@id="main-content"]/h2' 2
//@ has - '//*[@id="main-content"]/h2' 'Re-exports'
//@ has - '//*[@id="main-content"]/h2' 'Modules'
//@ has - '//*[@id="main-content"]//*[@class="item-table"]//li//code' 'pub use foo::Foo as _;'
//@ has - '//*[@id="main-content"]//*[@class="item-table"]//li//code' 'pub use bar::Bar as _;'
//@ has - '//*[@id="main-content"]//*[@class="item-table reexports"]/dt//code' 'pub use foo::Foo as _;'
//@ has - '//*[@id="main-content"]//*[@class="item-table reexports"]/dt//code' 'pub use bar::Bar as _;'
pub use foo::Foo as _;
pub use bar::Bar as _;

View File

@ -9,7 +9,7 @@
//@ has - '//*[@id="main-content"]/h2' 'Structs'
//@ has - '//*[@id="main-content"]/h2' 'Re-exports'
// The 3 re-exports.
//@ count - '//*[@id="main-content"]//*[@class="item-table"]//li//code' 3
//@ count - '//*[@id="main-content"]//*[@class="item-table reexports"]/dt//code' 3
// The public struct.
//@ count - '//*[@id="main-content"]//a[@class="struct"]' 1

View File

@ -11,15 +11,15 @@ mod sub {
pub fn public() {}
}
//@ matches - '//*[@class="desc docblock-short"]' '^Displayed$'
//@ matches - '//dd' '^Displayed$'
/// Displayed
#[doc(inline)]
pub use crate::bar as Bar;
//@ matches - '//*[@class="desc docblock-short"]' '^Hello\sDisplayed$'
//@ matches - '//dd' '^Hello\sDisplayed$'
#[doc(inline)]
/// Hello
pub use crate::Bar as Bar2;
//@ matches - '//*[@class="desc docblock-short"]' '^Public$'
//@ matches - '//dd' '^Public$'
/// Public
pub use crate::sub::public as Public;

View File

@ -5,8 +5,8 @@
#![no_core]
//@ has 'foo/index.html'
//@ has - '//*[@class="item-name"]/*[@class="stab portability"]' 'foobar'
//@ has - '//*[@class="item-name"]/*[@class="stab portability"]' 'bar'
//@ has - '//dt/*[@class="stab portability"]' 'foobar'
//@ has - '//dt/*[@class="stab portability"]' 'bar'
#[doc(cfg(feature = "foobar"))]
mod imp_priv {

View File

@ -1,6 +1,5 @@
//@ has deprecated/index.html '//*[@class="item-name"]/span[@class="stab deprecated"]' \
// 'Deprecated'
//@ has - '//*[@class="desc docblock-short"]' 'Deprecated docs'
//@ has deprecated/index.html '//dt/span[@class="stab deprecated"]' 'Deprecated'
//@ has - '//dd' 'Deprecated docs'
//@ has deprecated/struct.S.html '//*[@class="stab deprecated"]' \
// 'Deprecated since 1.0.0: text'
@ -8,7 +7,7 @@
#[deprecated(since = "1.0.0", note = "text")]
pub struct S;
//@ matches deprecated/index.html '//*[@class="desc docblock-short"]' '^Docs'
//@ matches deprecated/index.html '//dd' '^Docs'
/// Docs
pub struct T;

View File

@ -5,19 +5,19 @@
#![crate_name = "foo"]
//@ has 'foo/index.html'
//@ has - '//*[@class="item-name"]/span[@title="Hidden item"]' '👻'
//@ has - '//dt/span[@title="Hidden item"]' '👻'
//@ has - '//*[@id="reexport.hidden_reexport"]/code' '#[doc(hidden)] pub use hidden::inside_hidden as hidden_reexport;'
#[doc(hidden)]
pub use hidden::inside_hidden as hidden_reexport;
//@ has - '//*[@class="item-name"]/a[@class="trait"]' 'TraitHidden'
//@ has - '//dt/a[@class="trait"]' 'TraitHidden'
//@ has 'foo/trait.TraitHidden.html'
//@ has - '//code' '#[doc(hidden)] pub trait TraitHidden'
#[doc(hidden)]
pub trait TraitHidden {}
//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="trait"]' 'Trait'
//@ has 'foo/index.html' '//dt/a[@class="trait"]' 'Trait'
pub trait Trait {
//@ has 'foo/trait.Trait.html'
//@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]' '#[doc(hidden)] const BAR: u32 = 0u32'
@ -29,7 +29,7 @@ pub trait Trait {
fn foo() {}
}
//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="struct"]' 'Struct'
//@ has 'foo/index.html' '//dt/a[@class="struct"]' 'Struct'
//@ has 'foo/struct.Struct.html'
pub struct Struct {
//@ has - '//*[@id="structfield.a"]/code' 'a: u32'
@ -50,7 +50,7 @@ impl Trait for Struct {
//@ has - '//*[@id="impl-TraitHidden-for-Struct"]/*[@class="code-header"]' 'impl TraitHidden for Struct'
impl TraitHidden for Struct {}
//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="enum"]' 'HiddenEnum'
//@ has 'foo/index.html' '//dt/a[@class="enum"]' 'HiddenEnum'
//@ has 'foo/enum.HiddenEnum.html'
//@ has - '//code' '#[doc(hidden)] pub enum HiddenEnum'
#[doc(hidden)]
@ -58,18 +58,18 @@ pub enum HiddenEnum {
A,
}
//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="enum"]' 'Enum'
//@ has 'foo/index.html' '//dt/a[@class="enum"]' 'Enum'
pub enum Enum {
//@ has 'foo/enum.Enum.html' '//*[@id="variant.A"]/*[@class="code-header"]' 'A'
#[doc(hidden)]
A,
}
//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="mod"]' 'hidden'
//@ has 'foo/index.html' '//dt/a[@class="mod"]' 'hidden'
#[doc(hidden)]
pub mod hidden {
//@ has 'foo/hidden/index.html'
//@ has - '//*[@class="item-name"]/a[@class="fn"]' 'inside_hidden'
//@ has - '//dt/a[@class="fn"]' 'inside_hidden'
//@ has 'foo/hidden/fn.inside_hidden.html'
pub fn inside_hidden() {}
}

View File

@ -12,7 +12,7 @@ pub struct Portable;
//@ has doc_cfg/unix_only/index.html \
// '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'Available on Unix only.'
//@ matches - '//*[@class="item-name"]//*[@class="stab portability"]' '\AARM\Z'
//@ matches - '//dt//*[@class="stab portability"]' '\AARM\Z'
//@ count - '//*[@class="stab portability"]' 2
#[doc(cfg(unix))]
pub mod unix_only {
@ -42,7 +42,7 @@ pub mod unix_only {
//@ has doc_cfg/wasi_only/index.html \
// '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'Available on WASI only.'
//@ matches - '//*[@class="item-name"]//*[@class="stab portability"]' '\AWebAssembly\Z'
//@ matches - '//dt//*[@class="stab portability"]' '\AWebAssembly\Z'
//@ count - '//*[@class="stab portability"]' 2
#[doc(cfg(target_os = "wasi"))]
pub mod wasi_only {
@ -74,7 +74,7 @@ pub mod wasi_only {
// the portability header is different on the module view versus the full view
//@ has doc_cfg/index.html
//@ matches - '//*[@class="item-name"]//*[@class="stab portability"]' '\Aavx\Z'
//@ matches - '//dt//*[@class="stab portability"]' '\Aavx\Z'
//@ has doc_cfg/fn.uses_target_feature.html
//@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \

View File

@ -26,7 +26,7 @@ pub mod single_reexport {
//@ has 'foo/single_reexport/index.html'
// First we check that we have 4 type aliases.
//@ count - '//*[@id="main-content"]/*[@class="item-table"]//code' 4
//@ count - '//*[@id="main-content"]/*[@class="item-table reexports"]//code' 4
// Then we check that we have the correct link for each re-export.
@ -131,10 +131,10 @@ mod private {
pub mod doc_hidden_reexport {
//@ has 'foo/doc_hidden_reexport/index.html'
// Ensure there is only one item in this page and that it's a struct.
//@ count - '//*[@class="item-name"]' 1
//@ count - '//dt' 1
//@ has - '//a[@class="struct"]' 'Reexport'
// Check that the `#[doc(hidden)]` re-export's attributes are not taken into account.
//@ has - '//*[@class="desc docblock-short"]' 'Visible. Original.'
//@ has - '//dd' 'Visible. Original.'
/// Visible.
pub use self::Bar3 as Reexport;
/// Hidden.

View File

@ -2,7 +2,7 @@
#![crate_name = "foo"]
//@ has 'foo/index.html' '//*[@class="desc docblock-short"]' ''
//@ has 'foo/index.html' '//dd' ''
//@ has 'foo/struct.Bar.html' '//*[@class="docblock"]' ''
/// --

View File

@ -2,8 +2,8 @@
#![feature(doc_cfg)]
//@ has 'foo/index.html'
//@ matches '-' '//*[@class="item-name"]//*[@class="stab portability"]' '^sync$'
//@ has '-' '//*[@class="item-name"]//*[@class="stab portability"]/@title' 'Available on crate feature `sync` only'
//@ matches '-' '//dt//*[@class="stab portability"]' '^sync$'
//@ has '-' '//dt//*[@class="stab portability"]/@title' 'Available on crate feature `sync` only'
//@ has 'foo/struct.Foo.html'
//@ has '-' '//*[@class="stab portability"]' 'sync'

View File

@ -4,8 +4,8 @@
#![crate_name = "foo"]
//@ has 'foo/index.html'
//@ has - '//*[@class="desc docblock-short"]' 'hello bla'
//@ !has - '//*[@class="desc docblock-short"]/sup' '1'
//@ has - '//dd' 'hello bla'
//@ !has - '//dd/sup' '1'
//@ has 'foo/struct.S.html'
//@ has - '//*[@class="docblock"]//sup' '1'

View File

@ -7,9 +7,9 @@
//@ has 'foo/index.html'
// There are two items.
//@ count - '//*[@class="item-table"]//div[@class="item-name"]' 2
//@ count - '//*[@class="item-table"]/dt' 2
// Only one of them should have an attribute.
//@ count - '//*[@class="item-table"]//div[@class="item-name"]/*[@class="stab portability"]' 1
//@ count - '//*[@class="item-table"]/dt/*[@class="stab portability"]' 1
mod a {
#[doc(cfg(not(feature = "a")))]

View File

@ -6,9 +6,9 @@
//@ has 'foo/index.html'
// There are two items.
//@ count - '//*[@class="item-table"]//div[@class="item-name"]' 2
//@ count - '//*[@class="item-table"]/dt' 2
// Only one of them should have an attribute.
//@ count - '//*[@class="item-table"]//div[@class="item-name"]/*[@class="stab portability"]' 1
//@ count - '//*[@class="item-table"]/dt/*[@class="stab portability"]' 1
mod a {
#[cfg(not(feature = "a"))]

View File

@ -15,6 +15,6 @@ mod sub4 {
pub use sub4::inner::*;
//@ has 'foo/index.html'
//@ has - '//div[@class="desc docblock-short"]' '1'
//@ !has - '//div[@class="desc docblock-short"]' '0'
//@ has - '//dd' '1'
//@ !has - '//dd' '0'
fn main() { assert_eq!(X, 1); }

View File

@ -1,17 +1,17 @@
//@ has 'glob_shadowing/index.html'
//@ count - '//div[@class="item-name"]' 6
//@ !has - '//div[@class="desc docblock-short"]' 'sub1::describe'
//@ has - '//div[@class="desc docblock-short"]' 'sub2::describe'
//@ count - '//dt' 6
//@ !has - '//dd' 'sub1::describe'
//@ has - '//dd' 'sub2::describe'
//@ !has - '//div[@class="desc docblock-short"]' 'sub1::describe2'
//@ !has - '//dd' 'sub1::describe2'
//@ !has - '//div[@class="desc docblock-short"]' 'sub1::prelude'
//@ has - '//div[@class="desc docblock-short"]' 'mod::prelude'
//@ !has - '//dd' 'sub1::prelude'
//@ has - '//dd' 'mod::prelude'
//@ has - '//div[@class="desc docblock-short"]' 'sub1::Foo (struct)'
//@ has - '//div[@class="desc docblock-short"]' 'mod::Foo (function)'
//@ has - '//dd' 'sub1::Foo (struct)'
//@ has - '//dd' 'mod::Foo (function)'
//@ has - '//div[@class="desc docblock-short"]' 'sub4::inner::X'
//@ has - '//dd' 'sub4::inner::X'
//@ has 'glob_shadowing/fn.describe.html'
//@ has - '//div[@class="docblock"]' 'sub2::describe'

View File

@ -2,8 +2,8 @@
//@ has 'foo/index.html'
// There should be only `type A`.
//@ count - '//*[@class="item-table"]//*[@class="item-name"]' 1
//@ has - '//*[@class="item-name"]/a[@href="type.A.html"]' 'A'
//@ count - '//*[@class="item-table"]//dt' 1
//@ has - '//dt/a[@href="type.A.html"]' 'A'
mod foo {
pub struct S;

View File

@ -9,7 +9,7 @@
//@ count - '//*[@id="main-content"]/*[@class="section-header"]' 1
//@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Structs'
//@ has - '//*[@id="main-content"]//a[@href="struct.Reexport.html"]' 'Reexport'
//@ has - '//*[@id="main-content"]//*[@class="desc docblock-short"]' 'Visible. Original.'
//@ has - '//*[@id="main-content"]//dd' 'Visible. Original.'
mod private {
/// Original.

View File

@ -11,14 +11,14 @@ extern crate rustdoc_hidden;
pub use rustdoc_hidden::Foo;
// Even if the foreign item has `doc(hidden)`, we should be able to inline it.
//@ has - '//*[@class="item-name"]/a[@class="struct"]' 'Inlined'
//@ has - '//dt/a[@class="struct"]' 'Inlined'
#[doc(inline)]
pub use rustdoc_hidden::Foo as Inlined;
// Even with this import, we should not see `Foo`.
//@ count - '//*[@class="item-name"]' 4
//@ has - '//*[@class="item-name"]/a[@class="struct"]' 'Bar'
//@ has - '//*[@class="item-name"]/a[@class="fn"]' 'foo'
//@ count - '//dt' 4
//@ has - '//dt/a[@class="struct"]' 'Bar'
//@ has - '//dt/a[@class="fn"]' 'foo'
pub use rustdoc_hidden::*;
//@ has inline_hidden/fn.foo.html

View File

@ -6,10 +6,8 @@
extern crate macros;
//@ has foo/index.html '//*[@class="item-name"]/span[@class="stab deprecated"]' \
// Deprecated
//@ has - '//*[@class="item-name"]/span[@class="stab unstable"]' \
// Experimental
//@ has foo/index.html '//dt/span[@class="stab deprecated"]' Deprecated
//@ has - '//dt/span[@class="stab unstable"]' Experimental
//@ has foo/macro.my_macro.html
//@ has - '//*[@class="docblock"]' 'docs for my_macro'

View File

@ -8,7 +8,7 @@
//@ !matches internal/index.html \
// '//*[@class="desc docblock-short"]/span[@class="stab internal"]' \
// ''
//@ matches - '//*[@class="desc docblock-short"]' 'Docs'
//@ matches - '//dd' 'Docs'
//@ !has internal/struct.S.html '//*[@class="stab unstable"]' ''
//@ !has internal/struct.S.html '//*[@class="stab internal"]' ''

View File

@ -32,8 +32,8 @@ pub mod subone {
//@ has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
//@ has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
// Though there should be such links later
//@ has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="item-name"]/a[@class="fn"][@href="fn.foo.html"]' 'foo'
//@ has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="item-name"]/a[@class="fn"][@href="fn.bar.html"]' 'bar'
//@ has - '//section[@id="main-content"]/dl[@class="item-table"]/dt/a[@class="fn"][@href="fn.foo.html"]' 'foo'
//@ has - '//section[@id="main-content"]/dl[@class="item-table"]/dt/a[@class="fn"][@href="fn.bar.html"]' 'bar'
/// See either [foo] or [bar].
pub mod subtwo {
@ -71,8 +71,8 @@ pub mod subthree {
// Next we go *deeper* - In order to ensure it's not just "this or parent"
// we test `crate::` and a `super::super::...` chain
//@ has foo/subfour/subfive/subsix/subseven/subeight/index.html
//@ has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="desc docblock-short"]//a[@href="../../../../../subone/fn.foo.html"]' 'other foo'
//@ has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="desc docblock-short"]//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar'
//@ has - '//section[@id="main-content"]/dl[@class="item-table"]/dd//a[@href="../../../../../subone/fn.foo.html"]' 'other foo'
//@ has - '//section[@id="main-content"]/dl[@class="item-table"]/dd//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar'
pub mod subfour {
pub mod subfive {
pub mod subsix {

View File

@ -1 +1 @@
<ul class="item-table"><li><div class="item-name"><a class="constant" href="constant.MY_CONSTANT.html" title="constant item_desc_list_at_start::MY_CONSTANT">MY_<wbr />CONSTANT</a></div><div class="desc docblock-short">Groups: <code>SamplePatternSGIS</code>, <code>SamplePatternEXT</code></div></li></ul>
<dl class="item-table"><dt><a class="constant" href="constant.MY_CONSTANT.html" title="constant item_desc_list_at_start::MY_CONSTANT">MY_<wbr />CONSTANT</a></dt><dd>Groups: <code>SamplePatternSGIS</code>, <code>SamplePatternEXT</code></dd></dl>

View File

@ -1,7 +1,8 @@
//@ has item_desc_list_at_start/index.html
//@ count - '//ul[@class="item-table"]/li/div/li' 0
//@ count - '//ul[@class="item-table"]/li' 1
//@ snapshot item-table - '//ul[@class="item-table"]'
//@ count - '//dl[@class="item-table"]/dd//ul' 0
//@ count - '//dl[@class="item-table"]/dd//li' 0
//@ count - '//dl[@class="item-table"]/dd' 1
//@ snapshot item-table - '//dl[@class="item-table"]'
// based on https://docs.rs/gl_constants/0.1.1/src/gl_constants/lib.rs.html#16

View File

@ -1,17 +0,0 @@
// https://github.com/rust-lang/rust/issues/106142
#![crate_name="foo"]
//@ has 'foo/a/index.html'
//@ count 'foo/a/index.html' '//ul[@class="item-table"]//li//a' 1
#![allow(rustdoc::broken_intra_doc_links)]
pub mod a {
/// [`m`]
pub fn f() {}
#[macro_export]
macro_rules! m {
() => {};
}
}

View File

@ -10,7 +10,7 @@
//@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Functions'
//@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Traits'
// Checking that there are only three items.
//@ count - '//*[@id="main-content"]//*[@class="item-name"]' 3
//@ count - '//*[@id="main-content"]//dt' 3
//@ has - '//*[@id="main-content"]//a[@href="struct.Bar.html"]' 'Bar'
//@ has - '//*[@id="main-content"]//a[@href="fn.foo.html"]' 'foo'
//@ has - '//*[@id="main-content"]//a[@href="trait.Foo.html"]' 'Foo'

View File

@ -5,8 +5,8 @@
#![no_std]
//@ has 'foo/index.html'
//@ has - '//*[@class="item-name"]/a[@class="type"]' 'AtomicU8'
//@ has - '//*[@class="item-name"]/a[@class="constant"]' 'AtomicU8'
//@ has - '//dt/a[@class="type"]' 'AtomicU8'
//@ has - '//dt/a[@class="constant"]' 'AtomicU8'
// We also ensure we don't have another item displayed.
//@ count - '//*[@id="main-content"]/*[@class="section-header"]' 2
//@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Type Aliases'

View File

@ -5,8 +5,8 @@
#![no_std]
//@ has 'foo/index.html'
//@ has - '//*[@class="item-name"]/a[@class="struct"]' 'AtomicU8'
//@ has - '//*[@class="item-name"]/a[@class="constant"]' 'AtomicU8'
//@ has - '//dt/a[@class="struct"]' 'AtomicU8'
//@ has - '//dt/a[@class="constant"]' 'AtomicU8'
// We also ensure we don't have another item displayed.
//@ count - '//*[@id="main-content"]/*[@class="section-header"]' 2
//@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Structs'

View File

@ -1,5 +1,5 @@
// https://github.com/rust-lang/rust/issues/95873
#![crate_name = "foo"]
//@ has foo/index.html "//*[@class='item-name']" "pub use ::std as x;"
//@ has foo/index.html "//dt" "pub use ::std as x;"
pub use ::std as x;

View File

@ -13,18 +13,18 @@ mod foo {
}
//@ has 'foo/index.html'
//@ has - '//*[@class="item-name"]' 'BabarNon-lie'
//@ has - '//dt' 'BabarNon-lie'
#[cfg(not(feature = "lie"))]
pub use crate::foo::Bar as Babar;
//@ has - '//*[@class="item-name"]' 'Babar2Non-cake'
//@ has - '//dt' 'Babar2Non-cake'
#[doc(cfg(not(feature = "cake")))]
pub use crate::foo::Bar2 as Babar2;
//@ has - '//*[@class="item-table"]/li' 'pub use crate::Babar as Elephant;Non-robot'
//@ has - '//*[@class="item-table reexports"]/dt' 'pub use crate::Babar as Elephant;Non-robot'
#[cfg(not(feature = "robot"))]
pub use crate::Babar as Elephant;
//@ has - '//*[@class="item-table"]/li' 'pub use crate::Babar2 as Elephant2;Non-cat'
//@ has - '//*[@class="item-table reexports"]/dt' 'pub use crate::Babar2 as Elephant2;Non-cat'
#[doc(cfg(not(feature = "cat")))]
pub use crate::Babar2 as Elephant2;

View File

@ -8,13 +8,13 @@ extern crate reexport_check;
#[allow(deprecated, deprecated_in_future)]
pub use std::i32;
//@ !has 'foo/index.html' '//code' 'pub use self::string::String;'
//@ has 'foo/index.html' '//div[@class="item-name"]' 'String'
//@ has 'foo/index.html' '//dt' 'String'
pub use std::string::String;
// i32 is deprecated, String is not
//@ count 'foo/index.html' '//span[@class="stab deprecated"]' 1
//@ has 'foo/index.html' '//div[@class="desc docblock-short"]' 'Docs in original'
//@ has 'foo/index.html' '//dd' 'Docs in original'
// this is a no-op, but shows what happens if there's an attribute that isn't a doc-comment
#[doc(inline)]
pub use reexport_check::S;

View File

@ -12,5 +12,5 @@ mod private_module {
//@ has - '//*[@id="reexport.Foo"]/code' 'pub use crate::private_module::Public as Foo;'
pub use crate::private_module::Public as Foo;
// Glob re-exports with no visible items should not be displayed.
//@ count - '//*[@class="item-table"]/li' 1
//@ count - '//*[@class="item-table reexports"]/dt' 1
pub use crate::private_module::*;

View File

@ -25,5 +25,6 @@ pub mod a {
//@ has - '//*[@id="main-content"]//*[@id="reexport.A"]' 'pub use self::a::A;'
//@ has - '//*[@id="main-content"]//*[@id="reexport.B"]' 'pub use self::a::B;'
// Should only contain "Modules" and "Re-exports".
//@ count - '//*[@id="main-content"]//*[@class="item-table"]' 2
//@ count - '//*[@id="main-content"]//*[@class="item-table"]' 1
//@ count - '//*[@id="main-content"]//*[@class="item-table reexports"]' 1
pub use self::a::{A, B};

View File

@ -5,7 +5,7 @@
#![crate_name = "foo"]
//@ has 'foo/index.html'
//@ has - '//*[@id="main-content"]//*[@class="item-name"]/a[@href="trait.Foo.html"]' 'Foo'
//@ has - '//*[@id="main-content"]//dt/a[@href="trait.Foo.html"]' 'Foo'
//@ has 'foo/trait.Foo.html'
//@ has - '//*[@id="main-content"]//*[@class="code-header"]' 'fn test()'

View File

@ -1,7 +1,7 @@
#![crate_name = "foo"]
//@ has foo/index.html '//*[@class="desc docblock-short"]' 'fooo'
//@ !has foo/index.html '//*[@class="desc docblock-short"]/h1' 'fooo'
//@ has foo/index.html '//dd' 'fooo'
//@ !has foo/index.html '//dd//h1' 'fooo'
//@ has foo/fn.foo.html '//h2[@id="fooo"]' 'fooo'
//@ has foo/fn.foo.html '//h2[@id="fooo"]/a[@href="#fooo"]' '§'
@ -10,8 +10,8 @@
/// foo
pub fn foo() {}
//@ has foo/index.html '//*[@class="desc docblock-short"]' 'mooood'
//@ !has foo/index.html '//*[@class="desc docblock-short"]/h2' 'mooood'
//@ has foo/index.html '//dd' 'mooood'
//@ !has foo/index.html '//dd//h2' 'mooood'
//@ has foo/foo/index.html '//h3[@id="mooood"]' 'mooood'
//@ has foo/foo/index.html '//h3[@id="mooood"]/a[@href="#mooood"]' '§'
@ -20,8 +20,7 @@ pub fn foo() {}
/// foo mod
pub mod foo {}
//@ has foo/index.html '//*[@class="desc docblock-short"]/a[@href=\
// "https://nougat.world"]/code' 'nougat'
//@ has foo/index.html '//dd/a[@href="https://nougat.world"]/code' 'nougat'
/// [`nougat`](https://nougat.world)
pub struct Bar;

View File

@ -5,9 +5,9 @@
#![stable(feature = "core", since = "1.6.0")]
//@ has stability/index.html
//@ has - '//ul[@class="item-table"]/li[1]//a' AaStable
//@ has - '//ul[@class="item-table"]/li[2]//a' ZzStable
//@ has - '//ul[@class="item-table"]/li[3]//a' Unstable
//@ has - '//dl[@class="item-table"]/dt[1]//a' AaStable
//@ has - '//dl[@class="item-table"]/dt[2]//a' ZzStable
//@ has - '//dl[@class="item-table"]/dt[3]//a' Unstable
#[stable(feature = "rust2", since = "2.2.2")]
pub struct AaStable;

View File

@ -4,11 +4,9 @@
#![unstable(feature = "test", issue = "32374")]
#![crate_name="issue_32374"]
//@ matches issue_32374/index.html '//*[@class="item-name"]/span[@class="stab deprecated"]' \
// 'Deprecated'
//@ matches issue_32374/index.html '//*[@class="item-name"]/span[@class="stab unstable"]' \
// 'Experimental'
//@ matches issue_32374/index.html '//*[@class="desc docblock-short"]/text()' 'Docs'
//@ matches issue_32374/index.html '//dt/span[@class="stab deprecated"]' 'Deprecated'
//@ matches issue_32374/index.html '//dt/span[@class="stab unstable"]' 'Experimental'
//@ matches issue_32374/index.html '//dd/text()' 'Docs'
//@ has issue_32374/struct.T.html '//*[@class="stab deprecated"]/span' '👎'
//@ has issue_32374/struct.T.html '//*[@class="stab deprecated"]/span' \

View File

@ -1,6 +1,6 @@
// https://github.com/rust-lang/rust/issues/46377
#![crate_name="foo"]
//@ has 'foo/index.html' '//*[@class="desc docblock-short"]' 'Check out this struct!'
//@ has 'foo/index.html' '//dd' 'Check out this struct!'
/// # Check out this struct!
pub struct SomeStruct;

View File

@ -1,4 +1,4 @@
error: internal compiler error: broken MIR in Item(DefId(0:8 ~ storage_live[HASH]::multiple_storage)) (after pass CheckPackedRef) at bb0[1]:
error: internal compiler error: broken MIR in Item(DefId(0:8 ~ storage_live[HASH]::multiple_storage)) (after pass CheckForceInline) at bb0[1]:
StorageLive(_1) which already has storage here
--> $DIR/storage-live.rs:23:13
|

View File

@ -0,0 +1,21 @@
#![feature(default_field_values)]
// If an API wants users to always use `..` even if they specify all the fields, they should use a
// sentinel field. As of now, that field can't be made private so it is only constructable with this
// syntax, but this might change in the future.
struct A {}
struct B();
struct C;
struct D {
x: i32,
}
struct E(i32);
fn main() {
let _ = A { .. }; //~ ERROR has no fields
let _ = B { .. }; //~ ERROR has no fields
let _ = C { .. }; //~ ERROR has no fields
let _ = D { x: 0, .. };
let _ = E { 0: 0, .. };
}

View File

@ -0,0 +1,26 @@
error: `A` has no fields, `..` needs at least one default field in the struct definition
--> $DIR/empty-struct.rs:16:17
|
LL | let _ = A { .. };
| - ^^
| |
| this type has no fields
error: `B` has no fields, `..` needs at least one default field in the struct definition
--> $DIR/empty-struct.rs:17:17
|
LL | let _ = B { .. };
| - ^^
| |
| this type has no fields
error: `C` has no fields, `..` needs at least one default field in the struct definition
--> $DIR/empty-struct.rs:18:17
|
LL | let _ = C { .. };
| - ^^
| |
| this type has no fields
error: aborting due to 3 previous errors