Auto merge of #144692 - samueltardieu:rollup-0j1y08x, r=samueltardieu

Rollup of 4 pull requests

Successful merges:

 - rust-lang/rust#143465 (Support multiple crate versions in --extern-html-root-url)
 - rust-lang/rust#144308 ([rustdoc] Display total time and compilation time of merged doctests)
 - rust-lang/rust#144655 (clean up codegen fn attrs)
 - rust-lang/rust#144675 (Reject running `compiletest` self-tests against stage 0 rustc unless explicitly allowed)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-07-30 18:16:17 +00:00
commit 3048886e59
49 changed files with 684 additions and 397 deletions

View file

@ -465,6 +465,11 @@
# What custom diff tool to use for displaying compiletest tests.
#build.compiletest-diff-tool = <none>
# Whether to allow `compiletest` self-tests and `compiletest`-managed test
# suites to be run against the stage 0 rustc. This is only intended to be used
# when the stage 0 compiler is actually built from in-tree sources.
#build.compiletest-allow-stage0 = false
# Whether to use the precompiled stage0 libtest with compiletest.
#build.compiletest-use-stage0-libtest = true

View file

@ -33,6 +33,7 @@ rustc = "$(pwd)/../dist/bin/rustc-clif"
cargo = "$(rustup which cargo)"
full-bootstrap = true
local-rebuild = true
compiletest-allow-stage0 = true
[rust]
download-rustc = false

View file

@ -166,5 +166,5 @@ index 073116933bd..c3e4578204d 100644
EOF
echo "[TEST] rustc test suite"
COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,ui,incremental}
./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,ui,incremental}
popd

View file

@ -561,8 +561,6 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> {
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("[TEST] rustc asm test suite");
env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string());
let codegen_backend_path = format!(
"{pwd}/target/{channel}/librustc_codegen_gcc.{dylib_ext}",
pwd = std::env::current_dir()
@ -588,6 +586,8 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> {
&"always",
&"--stage",
&"0",
&"--set",
&"build.compiletest-allow-stage0=true",
&"tests/assembly-llvm/asm",
&"--compiletest-rustc-args",
&rustc_args,
@ -1047,7 +1047,6 @@ where
// FIXME: create a function "display_if_not_quiet" or something along the line.
println!("[TEST] rustc {test_type} test suite");
env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string());
let extra =
if args.is_using_gcc_master_branch() { "" } else { " -Csymbol-mangling-version=v0" };
@ -1070,6 +1069,8 @@ where
&"always",
&"--stage",
&"0",
&"--set",
&"build.compiletest-allow-stage0=true",
&format!("tests/{test_type}"),
&"--compiletest-rustc-args",
&rustc_args,

View file

@ -4,12 +4,12 @@ use rustc_abi::{Align, ExternAbi};
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
use rustc_attr_data_structures::{
AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, UsedBy, find_attr,
AttributeKind, InlineAttr, InstructionSetAttr, UsedBy, find_attr,
};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
use rustc_hir::{self as hir, LangItem, lang_items};
use rustc_hir::{self as hir, Attribute, LangItem, lang_items};
use rustc_middle::middle::codegen_fn_attrs::{
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
};
@ -53,77 +53,196 @@ fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
}
}
fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
if cfg!(debug_assertions) {
let def_kind = tcx.def_kind(did);
assert!(
def_kind.has_codegen_attrs(),
"unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}",
);
/// In some cases, attributes are only valid on functions, but it's the `check_attr`
/// pass that checks that they aren't used anywhere else, rather than this module.
/// In these cases, we bail from performing further checks that are only meaningful for
/// functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
/// report a delayed bug, just in case `check_attr` isn't doing its job.
fn try_fn_sig<'tcx>(
tcx: TyCtxt<'tcx>,
did: LocalDefId,
attr_span: Span,
) -> Option<ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>>> {
use DefKind::*;
let def_kind = tcx.def_kind(did);
if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
Some(tcx.fn_sig(did))
} else {
tcx.dcx().span_delayed_bug(attr_span, "this attribute can only be applied to functions");
None
}
}
// FIXME(jdonszelmann): remove when instruction_set becomes a parsed attr
fn parse_instruction_set_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option<InstructionSetAttr> {
let list = attr.meta_item_list()?;
match &list[..] {
[MetaItemInner::MetaItem(set)] => {
let segments = set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
match segments.as_slice() {
[sym::arm, sym::a32 | sym::t32] if !tcx.sess.target.has_thumb_interworking => {
tcx.dcx().emit_err(errors::UnsupportedInstructionSet { span: attr.span() });
None
}
[sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32),
[sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32),
_ => {
tcx.dcx().emit_err(errors::InvalidInstructionSet { span: attr.span() });
None
}
}
}
[] => {
tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() });
None
}
_ => {
tcx.dcx().emit_err(errors::MultipleInstructionSet { span: attr.span() });
None
}
}
}
// FIXME(jdonszelmann): remove when linkage becomes a parsed attr
fn parse_linkage_attr(tcx: TyCtxt<'_>, did: LocalDefId, attr: &Attribute) -> Option<Linkage> {
let val = attr.value_str()?;
let linkage = linkage_by_name(tcx, did, val.as_str());
Some(linkage)
}
// FIXME(jdonszelmann): remove when no_sanitize becomes a parsed attr
fn parse_no_sanitize_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option<SanitizerSet> {
let list = attr.meta_item_list()?;
let mut sanitizer_set = SanitizerSet::empty();
for item in list.iter() {
match item.name() {
Some(sym::address) => {
sanitizer_set |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
}
Some(sym::cfi) => sanitizer_set |= SanitizerSet::CFI,
Some(sym::kcfi) => sanitizer_set |= SanitizerSet::KCFI,
Some(sym::memory) => sanitizer_set |= SanitizerSet::MEMORY,
Some(sym::memtag) => sanitizer_set |= SanitizerSet::MEMTAG,
Some(sym::shadow_call_stack) => sanitizer_set |= SanitizerSet::SHADOWCALLSTACK,
Some(sym::thread) => sanitizer_set |= SanitizerSet::THREAD,
Some(sym::hwaddress) => sanitizer_set |= SanitizerSet::HWADDRESS,
_ => {
tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() });
}
}
}
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(did));
let mut codegen_fn_attrs = CodegenFnAttrs::new();
if tcx.should_inherit_track_caller(did) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
}
Some(sanitizer_set)
}
// FIXME(jdonszelmann): remove when patchable_function_entry becomes a parsed attr
fn parse_patchable_function_entry(
tcx: TyCtxt<'_>,
attr: &Attribute,
) -> Option<PatchableFunctionEntry> {
attr.meta_item_list().and_then(|l| {
let mut prefix = None;
let mut entry = None;
for item in l {
let Some(meta_item) = item.meta_item() else {
tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
continue;
};
let Some(name_value_lit) = meta_item.name_value_literal() else {
tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
continue;
};
let attrib_to_write = match meta_item.name() {
Some(sym::prefix_nops) => &mut prefix,
Some(sym::entry_nops) => &mut entry,
_ => {
tcx.dcx().emit_err(errors::UnexpectedParameterName {
span: item.span(),
prefix_nops: sym::prefix_nops,
entry_nops: sym::entry_nops,
});
continue;
}
};
let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
tcx.dcx().emit_err(errors::InvalidLiteralValue { span: name_value_lit.span });
continue;
};
let Ok(val) = val.get().try_into() else {
tcx.dcx().emit_err(errors::OutOfRangeInteger { span: name_value_lit.span });
continue;
};
*attrib_to_write = Some(val);
}
if let (None, None) = (prefix, entry) {
tcx.dcx().span_err(attr.span(), "must specify at least one parameter");
}
Some(PatchableFunctionEntry::from_prefix_and_entry(prefix.unwrap_or(0), entry.unwrap_or(0)))
})
}
/// Spans that are collected when processing built-in attributes,
/// that are useful for emitting diagnostics later.
#[derive(Default)]
struct InterestingAttributeDiagnosticSpans {
link_ordinal: Option<Span>,
no_sanitize: Option<Span>,
inline: Option<Span>,
no_mangle: Option<Span>,
}
/// Process the builtin attrs ([`hir::Attribute`]) on the item.
/// Many of them directly translate to codegen attrs.
fn process_builtin_attrs(
tcx: TyCtxt<'_>,
did: LocalDefId,
attrs: &[Attribute],
codegen_fn_attrs: &mut CodegenFnAttrs,
) -> InterestingAttributeDiagnosticSpans {
let mut interesting_spans = InterestingAttributeDiagnosticSpans::default();
let rust_target_features = tcx.rust_target_features(LOCAL_CRATE);
// If our rustc version supports autodiff/enzyme, then we call our handler
// to check for any `#[rustc_autodiff(...)]` attributes.
// FIXME(jdonszelmann): merge with loop below
if cfg!(llvm_enzyme) {
let ad = autodiff_attrs(tcx, did.into());
codegen_fn_attrs.autodiff_item = ad;
}
// When `no_builtins` is applied at the crate level, we should add the
// `no-builtins` attribute to each function to ensure it takes effect in LTO.
let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID);
let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins);
if no_builtins {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS;
}
let rust_target_features = tcx.rust_target_features(LOCAL_CRATE);
let mut link_ordinal_span = None;
let mut no_sanitize_span = None;
for attr in attrs.iter() {
// In some cases, attribute are only valid on functions, but it's the `check_attr`
// pass that check that they aren't used anywhere else, rather this module.
// In these cases, we bail from performing further checks that are only meaningful for
// functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
// report a delayed bug, just in case `check_attr` isn't doing its job.
let fn_sig = |attr_span| {
use DefKind::*;
let def_kind = tcx.def_kind(did);
if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
Some(tcx.fn_sig(did))
} else {
tcx.dcx()
.span_delayed_bug(attr_span, "this attribute can only be applied to functions");
None
}
};
if let hir::Attribute::Parsed(p) = attr {
match p {
AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
AttributeKind::ExportName { name, .. } => {
codegen_fn_attrs.export_name = Some(*name);
codegen_fn_attrs.export_name = Some(*name)
}
AttributeKind::Inline(inline, span) => {
codegen_fn_attrs.inline = *inline;
interesting_spans.inline = Some(*span);
}
AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name),
AttributeKind::LinkOrdinal { ordinal, span } => {
codegen_fn_attrs.link_ordinal = Some(*ordinal);
link_ordinal_span = Some(*span);
interesting_spans.link_ordinal = Some(*span);
}
AttributeKind::LinkSection { name, .. } => {
codegen_fn_attrs.link_section = Some(*name)
}
AttributeKind::NoMangle(attr_span) => {
interesting_spans.no_mangle = Some(*attr_span);
if tcx.opt_item_name(did.to_def_id()).is_some() {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
} else {
@ -137,6 +256,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
});
}
}
AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize,
AttributeKind::TargetFeature(features, attr_span) => {
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn");
@ -184,7 +304,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
let is_closure = tcx.is_closure_like(did.to_def_id());
if !is_closure
&& let Some(fn_sig) = fn_sig(*attr_span)
&& let Some(fn_sig) = try_fn_sig(tcx, did, *attr_span)
&& fn_sig.skip_binder().abi() != ExternAbi::Rust
{
tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span });
@ -232,155 +352,49 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
sym::linkage => {
if let Some(val) = attr.value_str() {
let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
if tcx.is_foreign_item(did) {
codegen_fn_attrs.import_linkage = linkage;
let linkage = parse_linkage_attr(tcx, did, attr);
if tcx.is_mutable_static(did.into()) {
let mut diag = tcx.dcx().struct_span_err(
attr.span(),
"extern mutable statics are not allowed with `#[linkage]`",
);
diag.note(
"marking the extern static mutable would allow changing which \
symbol the static references rather than make the target of the \
symbol mutable",
);
diag.emit();
}
} else {
codegen_fn_attrs.linkage = linkage;
if tcx.is_foreign_item(did) {
codegen_fn_attrs.import_linkage = linkage;
if tcx.is_mutable_static(did.into()) {
let mut diag = tcx.dcx().struct_span_err(
attr.span(),
"extern mutable statics are not allowed with `#[linkage]`",
);
diag.note(
"marking the extern static mutable would allow changing which \
symbol the static references rather than make the target of the \
symbol mutable",
);
diag.emit();
}
} else {
codegen_fn_attrs.linkage = linkage;
}
}
sym::no_sanitize => {
no_sanitize_span = Some(attr.span());
if let Some(list) = attr.meta_item_list() {
for item in list.iter() {
match item.name() {
Some(sym::address) => {
codegen_fn_attrs.no_sanitize |=
SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
}
Some(sym::cfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI,
Some(sym::kcfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI,
Some(sym::memory) => {
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY
}
Some(sym::memtag) => {
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG
}
Some(sym::shadow_call_stack) => {
codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK
}
Some(sym::thread) => {
codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD
}
Some(sym::hwaddress) => {
codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS
}
_ => {
tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() });
}
}
}
}
interesting_spans.no_sanitize = Some(attr.span());
codegen_fn_attrs.no_sanitize |=
parse_no_sanitize_attr(tcx, attr).unwrap_or_default();
}
sym::instruction_set => {
codegen_fn_attrs.instruction_set =
attr.meta_item_list().and_then(|l| match &l[..] {
[MetaItemInner::MetaItem(set)] => {
let segments =
set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
match segments.as_slice() {
[sym::arm, sym::a32 | sym::t32]
if !tcx.sess.target.has_thumb_interworking =>
{
tcx.dcx().emit_err(errors::UnsupportedInstructionSet {
span: attr.span(),
});
None
}
[sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32),
[sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32),
_ => {
tcx.dcx().emit_err(errors::InvalidInstructionSet {
span: attr.span(),
});
None
}
}
}
[] => {
tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() });
None
}
_ => {
tcx.dcx()
.emit_err(errors::MultipleInstructionSet { span: attr.span() });
None
}
})
codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr)
}
sym::patchable_function_entry => {
codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
let mut prefix = None;
let mut entry = None;
for item in l {
let Some(meta_item) = item.meta_item() else {
tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
continue;
};
let Some(name_value_lit) = meta_item.name_value_literal() else {
tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
continue;
};
let attrib_to_write = match meta_item.name() {
Some(sym::prefix_nops) => &mut prefix,
Some(sym::entry_nops) => &mut entry,
_ => {
tcx.dcx().emit_err(errors::UnexpectedParameterName {
span: item.span(),
prefix_nops: sym::prefix_nops,
entry_nops: sym::entry_nops,
});
continue;
}
};
let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
tcx.dcx().emit_err(errors::InvalidLiteralValue {
span: name_value_lit.span,
});
continue;
};
let Ok(val) = val.get().try_into() else {
tcx.dcx()
.emit_err(errors::OutOfRangeInteger { span: name_value_lit.span });
continue;
};
*attrib_to_write = Some(val);
}
if let (None, None) = (prefix, entry) {
tcx.dcx().span_err(attr.span(), "must specify at least one parameter");
}
Some(PatchableFunctionEntry::from_prefix_and_entry(
prefix.unwrap_or(0),
entry.unwrap_or(0),
))
})
codegen_fn_attrs.patchable_function_entry =
parse_patchable_function_entry(tcx, attr);
}
_ => {}
}
}
interesting_spans
}
/// Applies overrides for codegen fn attrs. These often have a specific reason why they're necessary.
/// Please comment why when adding a new one!
fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut CodegenFnAttrs) {
// Apply the minimum function alignment here. This ensures that a function's alignment is
// determined by the `-C` flags of the crate it is defined in, not the `-C` flags of the crate
// it happens to be codegen'd (or const-eval'd) in.
@ -390,15 +404,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
// On trait methods, inherit the `#[align]` of the trait's method prototype.
codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.inherited_align(did));
let inline_span;
(codegen_fn_attrs.inline, inline_span) = if let Some((inline_attr, span)) =
find_attr!(attrs, AttributeKind::Inline(i, span) => (*i, *span))
{
(inline_attr, Some(span))
} else {
(InlineAttr::None, None)
};
// naked function MUST NOT be inlined! This attribute is required for the rust compiler itself,
// but not for the code generation backend because at that point the naked function will just be
// a declaration, with a definition provided in global assembly.
@ -406,9 +411,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
codegen_fn_attrs.inline = InlineAttr::Never;
}
codegen_fn_attrs.optimize =
find_attr!(attrs, AttributeKind::Optimize(i, _) => *i).unwrap_or(OptimizeAttr::Default);
// #73631: closures inherit `#[target_feature]` annotations
//
// If this closure is marked `#[inline(always)]`, simply skip adding `#[target_feature]`.
@ -431,6 +433,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
}
// When `no_builtins` is applied at the crate level, we should add the
// `no-builtins` attribute to each function to ensure it takes effect in LTO.
let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID);
let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins);
if no_builtins {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS;
}
// inherit track-caller properly
if tcx.should_inherit_track_caller(did) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
}
}
fn check_result(
tcx: TyCtxt<'_>,
did: LocalDefId,
interesting_spans: InterestingAttributeDiagnosticSpans,
codegen_fn_attrs: &CodegenFnAttrs,
) {
// If a function uses `#[target_feature]` it can't be inlined into general
// purpose functions as they wouldn't have the right target features
// enabled. For that reason we also forbid `#[inline(always)]` as it can't be
@ -446,14 +468,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
// llvm/llvm-project#70563).
if !codegen_fn_attrs.target_features.is_empty()
&& matches!(codegen_fn_attrs.inline, InlineAttr::Always)
&& let Some(span) = inline_span
&& let Some(span) = interesting_spans.inline
{
tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`");
}
// warn that inline has no effect when no_sanitize is present
if !codegen_fn_attrs.no_sanitize.is_empty()
&& codegen_fn_attrs.inline.always()
&& let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span)
&& let (Some(no_sanitize_span), Some(inline_span)) =
(interesting_spans.no_sanitize, interesting_spans.inline)
{
let hir_id = tcx.local_def_id_to_hir_id(did);
tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| {
@ -462,51 +486,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
})
}
// Weak lang items have the same semantics as "std internal" symbols in the
// sense that they're preserved through all our LTO passes and only
// strippable by the linker.
//
// Additionally weak lang items have predetermined symbol names.
if let Some((name, _)) = lang_items::extract(attrs)
&& let Some(lang_item) = LangItem::from_name(name)
// error when specifying link_name together with link_ordinal
if let Some(_) = codegen_fn_attrs.link_name
&& let Some(_) = codegen_fn_attrs.link_ordinal
{
if WEAK_LANG_ITEMS.contains(&lang_item) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
if let Some(span) = interesting_spans.link_ordinal {
tcx.dcx().span_err(span, msg);
} else {
tcx.dcx().err(msg);
}
if let Some(link_name) = lang_item.link_name() {
codegen_fn_attrs.export_name = Some(link_name);
codegen_fn_attrs.link_name = Some(link_name);
}
}
check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
&& codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
{
let no_mangle_span =
find_attr!(attrs, AttributeKind::NoMangle(no_mangle_span) => *no_mangle_span)
.unwrap_or_default();
let lang_item =
lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name));
let mut err = tcx
.dcx()
.struct_span_err(
no_mangle_span,
"`#[no_mangle]` cannot be used on internal language items",
)
.with_note("Rustc requires this item to have a specific mangled name.")
.with_span_label(tcx.def_span(did), "should be the internal language item");
if let Some(lang_item) = lang_item
&& let Some(link_name) = lang_item.link_name()
{
err = err
.with_note("If you are trying to prevent mangling to ease debugging, many")
.with_note(format!("debuggers support a command such as `rbreak {link_name}` to"))
.with_note(format!(
"match `.*{link_name}.*` instead of `break {link_name}` on a specific name"
))
}
err.emit();
}
if let Some(features) = check_tied_features(
@ -529,6 +518,84 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
})
.emit();
}
}
fn handle_lang_items(
tcx: TyCtxt<'_>,
did: LocalDefId,
interesting_spans: &InterestingAttributeDiagnosticSpans,
attrs: &[Attribute],
codegen_fn_attrs: &mut CodegenFnAttrs,
) {
// Weak lang items have the same semantics as "std internal" symbols in the
// sense that they're preserved through all our LTO passes and only
// strippable by the linker.
//
// Additionally weak lang items have predetermined symbol names.
if let Some((name, _)) = lang_items::extract(attrs)
&& let Some(lang_item) = LangItem::from_name(name)
{
if WEAK_LANG_ITEMS.contains(&lang_item) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
}
if let Some(link_name) = lang_item.link_name() {
codegen_fn_attrs.export_name = Some(link_name);
codegen_fn_attrs.link_name = Some(link_name);
}
}
// error when using no_mangle on a lang item item
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
&& codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
{
let lang_item =
lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name));
let mut err = tcx
.dcx()
.struct_span_err(
interesting_spans.no_mangle.unwrap_or_default(),
"`#[no_mangle]` cannot be used on internal language items",
)
.with_note("Rustc requires this item to have a specific mangled name.")
.with_span_label(tcx.def_span(did), "should be the internal language item");
if let Some(lang_item) = lang_item
&& let Some(link_name) = lang_item.link_name()
{
err = err
.with_note("If you are trying to prevent mangling to ease debugging, many")
.with_note(format!("debuggers support a command such as `rbreak {link_name}` to"))
.with_note(format!(
"match `.*{link_name}.*` instead of `break {link_name}` on a specific name"
))
}
err.emit();
}
}
/// Generate the [`CodegenFnAttrs`] for an item (identified by the [`LocalDefId`]).
///
/// This happens in 4 stages:
/// - apply built-in attributes that directly translate to codegen attributes.
/// - handle lang items. These have special codegen attrs applied to them.
/// - apply overrides, like minimum requirements for alignment and other settings that don't rely directly the built-in attrs on the item.
/// overrides come after applying built-in attributes since they may only apply when certain attributes were already set in the stage before.
/// - check that the result is valid. There's various ways in which this may not be the case, such as certain combinations of attrs.
fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
if cfg!(debug_assertions) {
let def_kind = tcx.def_kind(did);
assert!(
def_kind.has_codegen_attrs(),
"unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}",
);
}
let mut codegen_fn_attrs = CodegenFnAttrs::new();
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(did));
let interesting_spans = process_builtin_attrs(tcx, did, attrs, &mut codegen_fn_attrs);
handle_lang_items(tcx, did, &interesting_spans, attrs, &mut codegen_fn_attrs);
apply_overrides(tcx, did, &mut codegen_fn_attrs);
check_result(tcx, did, interesting_spans, &codegen_fn_attrs);
codegen_fn_attrs
}
@ -555,27 +622,12 @@ fn inherited_align<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Align> {
tcx.codegen_fn_attrs(opt_trait_item(tcx, def_id)?).alignment
}
fn check_link_name_xor_ordinal(
tcx: TyCtxt<'_>,
codegen_fn_attrs: &CodegenFnAttrs,
inline_span: Option<Span>,
) {
if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() {
return;
}
let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
if let Some(span) = inline_span {
tcx.dcx().span_err(span, msg);
} else {
tcx.dcx().err(msg);
}
}
/// We now check the #\[rustc_autodiff\] attributes which we generated from the #[autodiff(...)]
/// macros. There are two forms. The pure one without args to mark primal functions (the functions
/// being differentiated). The other form is #[rustc_autodiff(Mode, ActivityList)] on top of the
/// placeholder functions. We wrote the rustc_autodiff attributes ourself, so this should never
/// panic, unless we introduced a bug when parsing the autodiff macro.
//FIXME(jdonszelmann): put in the main loop. No need to have two..... :/ Let's do that when we make autodiff parsed.
fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
let attrs = tcx.get_attrs(id, sym::rustc_autodiff);

View file

@ -12,6 +12,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::owned_slice::OwnedSlice;
use rustc_data_structures::svh::Svh;
use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard};
use rustc_data_structures::unord::UnordMap;
use rustc_expand::base::SyntaxExtension;
use rustc_fs_util::try_canonicalize;
use rustc_hir as hir;
@ -69,6 +70,9 @@ pub struct CStore {
/// This crate has a `#[alloc_error_handler]` item.
has_alloc_error_handler: bool,
/// Names that were used to load the crates via `extern crate` or paths.
resolved_externs: UnordMap<Symbol, CrateNum>,
/// Unused externs of the crate
unused_externs: Vec<Symbol>,
@ -249,6 +253,22 @@ impl CStore {
self.metas[cnum] = Some(Box::new(data));
}
/// Save the name used to resolve the extern crate in the local crate
///
/// The name isn't always the crate's own name, because `sess.opts.externs` can assign it another name.
/// It's also not always the same as the `DefId`'s symbol due to renames `extern crate resolved_name as defid_name`.
pub(crate) fn set_resolved_extern_crate_name(&mut self, name: Symbol, extern_crate: CrateNum) {
self.resolved_externs.insert(name, extern_crate);
}
/// Crate resolved and loaded via the given extern name
/// (corresponds to names in `sess.opts.externs`)
///
/// May be `None` if the crate wasn't used
pub fn resolved_extern_crate(&self, externs_name: Symbol) -> Option<CrateNum> {
self.resolved_externs.get(&externs_name).copied()
}
pub(crate) fn iter_crate_data(&self) -> impl Iterator<Item = (CrateNum, &CrateMetadata)> {
self.metas
.iter_enumerated()
@ -475,6 +495,7 @@ impl CStore {
alloc_error_handler_kind: None,
has_global_allocator: false,
has_alloc_error_handler: false,
resolved_externs: UnordMap::default(),
unused_externs: Vec::new(),
used_extern_options: Default::default(),
}
@ -511,7 +532,7 @@ impl CStore {
// We're also sure to compare *paths*, not actual byte slices. The
// `source` stores paths which are normalized which may be different
// from the strings on the command line.
let source = self.get_crate_data(cnum).cdata.source();
let source = data.source();
if let Some(entry) = externs.get(name.as_str()) {
// Only use `--extern crate_name=path` here, not `--extern crate_name`.
if let Some(mut files) = entry.files() {
@ -1308,6 +1329,7 @@ impl CStore {
let path_len = definitions.def_path(def_id).data.len();
self.update_extern_crate(
cnum,
name,
ExternCrate {
src: ExternCrateSource::Extern(def_id.to_def_id()),
span: item.span,
@ -1332,6 +1354,7 @@ impl CStore {
self.update_extern_crate(
cnum,
name,
ExternCrate {
src: ExternCrateSource::Path,
span,

View file

@ -1937,9 +1937,13 @@ impl CrateMetadata {
self.root.decode_target_modifiers(&self.blob).collect()
}
pub(crate) fn update_extern_crate(&mut self, new_extern_crate: ExternCrate) -> bool {
/// Keep `new_extern_crate` if it looks better in diagnostics
pub(crate) fn update_extern_crate_diagnostics(
&mut self,
new_extern_crate: ExternCrate,
) -> bool {
let update =
Some(new_extern_crate.rank()) > self.extern_crate.as_ref().map(ExternCrate::rank);
self.extern_crate.as_ref().is_none_or(|old| old.rank() < new_extern_crate.rank());
if update {
self.extern_crate = Some(new_extern_crate);
}

View file

@ -627,14 +627,37 @@ impl CStore {
}
}
pub(crate) fn update_extern_crate(&mut self, cnum: CrateNum, extern_crate: ExternCrate) {
/// Track how an extern crate has been loaded. Called after resolving an import in the local crate.
///
/// * the `name` is for [`Self::set_resolved_extern_crate_name`] saving `--extern name=`
/// * `extern_crate` is for diagnostics
pub(crate) fn update_extern_crate(
&mut self,
cnum: CrateNum,
name: Symbol,
extern_crate: ExternCrate,
) {
debug_assert_eq!(
extern_crate.dependency_of, LOCAL_CRATE,
"this function should not be called on transitive dependencies"
);
self.set_resolved_extern_crate_name(name, cnum);
self.update_transitive_extern_crate_diagnostics(cnum, extern_crate);
}
/// `CrateMetadata` uses `ExternCrate` only for diagnostics
fn update_transitive_extern_crate_diagnostics(
&mut self,
cnum: CrateNum,
extern_crate: ExternCrate,
) {
let cmeta = self.get_crate_data_mut(cnum);
if cmeta.update_extern_crate(extern_crate) {
if cmeta.update_extern_crate_diagnostics(extern_crate) {
// Propagate the extern crate info to dependencies if it was updated.
let extern_crate = ExternCrate { dependency_of: cnum, ..extern_crate };
let dependencies = mem::take(&mut cmeta.dependencies);
for &dep_cnum in &dependencies {
self.update_extern_crate(dep_cnum, extern_crate);
self.update_transitive_extern_crate_diagnostics(dep_cnum, extern_crate);
}
self.get_crate_data_mut(cnum).dependencies = dependencies;
}

View file

@ -8,6 +8,9 @@ use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use std::{env, fs, iter};
#[cfg(feature = "tracing")]
use tracing::instrument;
use crate::core::build_steps::compile::{Std, run_cargo};
use crate::core::build_steps::doc::DocumentationFormat;
use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags};
@ -30,7 +33,7 @@ use crate::utils::helpers::{
linker_flags, t, target_supports_cranelift_backend, up_to_date,
};
use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests};
use crate::{CLang, DocTests, GitRepo, Mode, PathSet, envify};
use crate::{CLang, DocTests, GitRepo, Mode, PathSet, debug, envify};
const ADB_TEST_DIR: &str = "/data/local/tmp/work";
@ -713,9 +716,23 @@ impl Step for CompiletestTest {
}
/// Runs `cargo test` for compiletest.
#[cfg_attr(
feature = "tracing",
instrument(level = "debug", name = "CompiletestTest::run", skip_all)
)]
fn run(self, builder: &Builder<'_>) {
let host = self.host;
if builder.top_stage == 0 && !builder.config.compiletest_allow_stage0 {
eprintln!("\
ERROR: `--stage 0` runs compiletest self-tests against the stage0 (precompiled) compiler, not the in-tree compiler, and will almost always cause tests to fail
NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `--set build.compiletest-allow-stage0=true`."
);
crate::exit!(1);
}
let compiler = builder.compiler(builder.top_stage, host);
debug!(?compiler);
// We need `ToolStd` for the locally-built sysroot because
// compiletest uses unstable features of the `test` crate.
@ -723,8 +740,8 @@ impl Step for CompiletestTest {
let mut cargo = tool::prepare_tool_cargo(
builder,
compiler,
// compiletest uses libtest internals; make it use the in-tree std to make sure it never breaks
// when std sources change.
// compiletest uses libtest internals; make it use the in-tree std to make sure it never
// breaks when std sources change.
Mode::ToolStd,
host,
Kind::Test,
@ -1612,12 +1629,11 @@ impl Step for Compiletest {
return;
}
if builder.top_stage == 0 && env::var("COMPILETEST_FORCE_STAGE0").is_err() {
if builder.top_stage == 0 && !builder.config.compiletest_allow_stage0 {
eprintln!("\
ERROR: `--stage 0` runs compiletest on the stage0 (precompiled) compiler, not your local changes, and will almost always cause tests to fail
HELP: to test the compiler, use `--stage 1` instead
HELP: to test the standard library, use `--stage 0 library/std` instead
NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `COMPILETEST_FORCE_STAGE0=1`."
HELP: to test the compiler or standard library, omit the stage or explicitly use `--stage 1` instead
NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `--set build.compiletest-allow-stage0=true`."
);
crate::exit!(1);
}

View file

@ -298,8 +298,16 @@ pub struct Config {
/// Command for visual diff display, e.g. `diff-tool --color=always`.
pub compiletest_diff_tool: Option<String>,
/// Whether to allow running both `compiletest` self-tests and `compiletest`-managed test suites
/// against the stage 0 (rustc, std).
///
/// This is only intended to be used when the stage 0 compiler is actually built from in-tree
/// sources.
pub compiletest_allow_stage0: bool,
/// Whether to use the precompiled stage0 libtest with compiletest.
pub compiletest_use_stage0_libtest: bool,
/// Default value for `--extra-checks`
pub tidy_extra_checks: Option<String>,
pub is_running_on_ci: bool,
@ -749,6 +757,7 @@ impl Config {
optimized_compiler_builtins,
jobs,
compiletest_diff_tool,
compiletest_allow_stage0,
compiletest_use_stage0_libtest,
tidy_extra_checks,
ccache,
@ -1020,8 +1029,12 @@ impl Config {
config.optimized_compiler_builtins =
optimized_compiler_builtins.unwrap_or(config.channel != "dev");
config.compiletest_diff_tool = compiletest_diff_tool;
config.compiletest_allow_stage0 = compiletest_allow_stage0.unwrap_or(false);
config.compiletest_use_stage0_libtest = compiletest_use_stage0_libtest.unwrap_or(true);
config.tidy_extra_checks = tidy_extra_checks;
let download_rustc = config.download_rustc_commit.is_some();

View file

@ -68,6 +68,7 @@ define_config! {
optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins",
jobs: Option<u32> = "jobs",
compiletest_diff_tool: Option<String> = "compiletest-diff-tool",
compiletest_allow_stage0: Option<bool> = "compiletest-allow-stage0",
compiletest_use_stage0_libtest: Option<bool> = "compiletest-use-stage0-libtest",
tidy_extra_checks: Option<String> = "tidy-extra-checks",
ccache: Option<StringOrBool> = "ccache",

View file

@ -486,4 +486,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
severity: ChangeSeverity::Warning,
summary: "Removed `rust.description` and `llvm.ccache` as it was deprecated in #137723 and #136941 long time ago.",
},
ChangeInfo {
change_id: 144675,
severity: ChangeSeverity::Warning,
summary: "Added `build.compiletest-allow-stage0` flag instead of `COMPILETEST_FORCE_STAGE0` env var, and reject running `compiletest` self tests against stage 0 rustc unless explicitly allowed.",
},
];

View file

@ -43,7 +43,6 @@ ENV SCRIPT \
python3 ../x.py check bootstrap && \
/scripts/check-default-config-profiles.sh && \
python3 ../x.py build src/tools/build-manifest && \
python3 ../x.py test --stage 0 src/tools/compiletest && \
python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \
python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \
python3 ../x.py check --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \

View file

@ -30,6 +30,7 @@ ENV SCRIPT \
python3 ../x.py check && \
python3 ../x.py clippy ci && \
python3 ../x.py test --stage 1 core alloc std test proc_macro && \
python3 ../x.py test --stage 1 src/tools/compiletest && \
python3 ../x.py doc --stage 0 bootstrap && \
# Build both public and internal documentation.
RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 0 compiler && \
@ -37,6 +38,6 @@ ENV SCRIPT \
mkdir -p /checkout/obj/staging/doc && \
cp -r build/x86_64-unknown-linux-gnu/doc /checkout/obj/staging && \
RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 1 library/test && \
# The BOOTSTRAP_TRACING flag is added to verify whether the
# The BOOTSTRAP_TRACING flag is added to verify whether the
# bootstrap process compiles successfully with this flag enabled.
BOOTSTRAP_TRACING=1 python3 ../x.py --help

View file

@ -395,6 +395,12 @@ flags to control that behavior. When the `--extern-html-root-url` flag is given
one of your dependencies, rustdoc use that URL for those docs. Keep in mind that if those docs exist
in the output directory, those local docs will still override this flag.
The names in this flag are first matched against the names given in the `--extern name=` flags,
which allows selecting between multiple crates with the same name (e.g. multiple versions of
the same crate). For transitive dependencies that haven't been loaded via an `--extern` flag, matching
falls backs to using crate names only, without ability to distinguish between multiple crates with
the same name.
## `-Z force-unstable-if-unmarked`
Using this flag looks like this:

View file

@ -11,7 +11,8 @@ use std::path::{Path, PathBuf};
use std::process::{self, Command, Stdio};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::{panic, str};
use std::time::{Duration, Instant};
use std::{fmt, panic, str};
pub(crate) use make::{BuildDocTestBuilder, DocTestBuilder};
pub(crate) use markdown::test as test_markdown;
@ -36,6 +37,50 @@ use crate::config::{Options as RustdocOptions, OutputFormat};
use crate::html::markdown::{ErrorCodes, Ignore, LangString, MdRelLine};
use crate::lint::init_lints;
/// Type used to display times (compilation and total) information for merged doctests.
struct MergedDoctestTimes {
total_time: Instant,
/// Total time spent compiling all merged doctests.
compilation_time: Duration,
/// This field is used to keep track of how many merged doctests we (tried to) compile.
added_compilation_times: usize,
}
impl MergedDoctestTimes {
fn new() -> Self {
Self {
total_time: Instant::now(),
compilation_time: Duration::default(),
added_compilation_times: 0,
}
}
fn add_compilation_time(&mut self, duration: Duration) {
self.compilation_time += duration;
self.added_compilation_times += 1;
}
fn display_times(&self) {
// If no merged doctest was compiled, then there is nothing to display since the numbers
// displayed by `libtest` for standalone tests are already accurate (they include both
// compilation and runtime).
if self.added_compilation_times > 0 {
println!("{self}");
}
}
}
impl fmt::Display for MergedDoctestTimes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"all doctests ran in {:.2}s; merged doctests compilation took {:.2}s",
self.total_time.elapsed().as_secs_f64(),
self.compilation_time.as_secs_f64(),
)
}
}
/// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`).
#[derive(Clone)]
pub(crate) struct GlobalTestOptions {
@ -295,6 +340,7 @@ pub(crate) fn run_tests(
let mut nb_errors = 0;
let mut ran_edition_tests = 0;
let mut times = MergedDoctestTimes::new();
let target_str = rustdoc_options.target.to_string();
for (MergeableTestKey { edition, global_crate_attrs_hash }, mut doctests) in mergeable_tests {
@ -314,13 +360,15 @@ pub(crate) fn run_tests(
for (doctest, scraped_test) in &doctests {
tests_runner.add_test(doctest, scraped_test, &target_str);
}
if let Ok(success) = tests_runner.run_merged_tests(
let (duration, ret) = tests_runner.run_merged_tests(
rustdoc_test_options,
edition,
&opts,
&test_args,
rustdoc_options,
) {
);
times.add_compilation_time(duration);
if let Ok(success) = ret {
ran_edition_tests += 1;
if !success {
nb_errors += 1;
@ -354,11 +402,13 @@ pub(crate) fn run_tests(
test::test_main_with_exit_callback(&test_args, standalone_tests, None, || {
// We ensure temp dir destructor is called.
std::mem::drop(temp_dir.take());
times.display_times();
});
}
if nb_errors != 0 {
// We ensure temp dir destructor is called.
std::mem::drop(temp_dir);
times.display_times();
// libtest::ERROR_EXIT_CODE is not public but it's the same value.
std::process::exit(101);
}
@ -496,16 +546,19 @@ impl RunnableDocTest {
///
/// This is the function that calculates the compiler command line, invokes the compiler, then
/// invokes the test or tests in a separate executable (if applicable).
///
/// Returns a tuple containing the `Duration` of the compilation and the `Result` of the test.
fn run_test(
doctest: RunnableDocTest,
rustdoc_options: &RustdocOptions,
supports_color: bool,
report_unused_externs: impl Fn(UnusedExterns),
) -> Result<(), TestFailure> {
) -> (Duration, Result<(), TestFailure>) {
let langstr = &doctest.langstr;
// Make sure we emit well-formed executable names for our target.
let rust_out = add_exe_suffix("rust_out".to_owned(), &rustdoc_options.target);
let output_file = doctest.test_opts.outdir.path().join(rust_out);
let instant = Instant::now();
// Common arguments used for compiling the doctest runner.
// On merged doctests, the compiler is invoked twice: once for the test code itself,
@ -589,7 +642,7 @@ fn run_test(
if std::fs::write(&input_file, &doctest.full_test_code).is_err() {
// If we cannot write this file for any reason, we leave. All combined tests will be
// tested as standalone tests.
return Err(TestFailure::CompileError);
return (Duration::default(), Err(TestFailure::CompileError));
}
if !rustdoc_options.nocapture {
// If `nocapture` is disabled, then we don't display rustc's output when compiling
@ -660,7 +713,7 @@ fn run_test(
if std::fs::write(&runner_input_file, merged_test_code).is_err() {
// If we cannot write this file for any reason, we leave. All combined tests will be
// tested as standalone tests.
return Err(TestFailure::CompileError);
return (instant.elapsed(), Err(TestFailure::CompileError));
}
if !rustdoc_options.nocapture {
// If `nocapture` is disabled, then we don't display rustc's output when compiling
@ -713,7 +766,7 @@ fn run_test(
let _bomb = Bomb(&out);
match (output.status.success(), langstr.compile_fail) {
(true, true) => {
return Err(TestFailure::UnexpectedCompilePass);
return (instant.elapsed(), Err(TestFailure::UnexpectedCompilePass));
}
(true, false) => {}
(false, true) => {
@ -729,17 +782,18 @@ fn run_test(
.collect();
if !missing_codes.is_empty() {
return Err(TestFailure::MissingErrorCodes(missing_codes));
return (instant.elapsed(), Err(TestFailure::MissingErrorCodes(missing_codes)));
}
}
}
(false, false) => {
return Err(TestFailure::CompileError);
return (instant.elapsed(), Err(TestFailure::CompileError));
}
}
let duration = instant.elapsed();
if doctest.no_run {
return Ok(());
return (duration, Ok(()));
}
// Run the code!
@ -771,17 +825,17 @@ fn run_test(
cmd.output()
};
match result {
Err(e) => return Err(TestFailure::ExecutionError(e)),
Err(e) => return (duration, Err(TestFailure::ExecutionError(e))),
Ok(out) => {
if langstr.should_panic && out.status.success() {
return Err(TestFailure::UnexpectedRunPass);
return (duration, Err(TestFailure::UnexpectedRunPass));
} else if !langstr.should_panic && !out.status.success() {
return Err(TestFailure::ExecutionFailure(out));
return (duration, Err(TestFailure::ExecutionFailure(out)));
}
}
}
Ok(())
(duration, Ok(()))
}
/// Converts a path intended to use as a command to absolute if it is
@ -1071,7 +1125,7 @@ fn doctest_run_fn(
no_run: scraped_test.no_run(&rustdoc_options),
merged_test_code: None,
};
let res =
let (_, res) =
run_test(runnable_test, &rustdoc_options, doctest.supports_color, report_unused_externs);
if let Err(err) = res {

View file

@ -1,4 +1,5 @@
use std::fmt::Write;
use std::time::Duration;
use rustc_data_structures::fx::FxIndexSet;
use rustc_span::edition::Edition;
@ -67,6 +68,10 @@ impl DocTestRunner {
self.nb_tests += 1;
}
/// Returns a tuple containing the `Duration` of the compilation and the `Result` of the test.
///
/// If compilation failed, it will return `Err`, otherwise it will return `Ok` containing if
/// the test ran successfully.
pub(crate) fn run_merged_tests(
&mut self,
test_options: IndividualTestOptions,
@ -74,7 +79,7 @@ impl DocTestRunner {
opts: &GlobalTestOptions,
test_args: &[String],
rustdoc_options: &RustdocOptions,
) -> Result<bool, ()> {
) -> (Duration, Result<bool, ()>) {
let mut code = "\
#![allow(unused_extern_crates)]
#![allow(internal_features)]
@ -204,9 +209,9 @@ std::process::Termination::report(test::test_main(test_args, tests, None))
no_run: false,
merged_test_code: Some(code),
};
let ret =
let (duration, ret) =
run_test(runnable_test, rustdoc_options, self.supports_color, |_: UnusedExterns| {});
if let Err(TestFailure::CompileError) = ret { Err(()) } else { Ok(ret.is_ok()) }
(duration, if let Err(TestFailure::CompileError) = ret { Err(()) } else { Ok(ret.is_ok()) })
}
}

View file

@ -4,6 +4,7 @@ use rustc_ast::join_path_syms;
use rustc_attr_data_structures::StabilityLevel;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet};
use rustc_metadata::creader::CStore;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::Symbol;
use tracing::debug;
@ -158,18 +159,33 @@ impl Cache {
assert!(cx.external_traits.is_empty());
cx.cache.traits = mem::take(&mut krate.external_traits);
let render_options = &cx.render_options;
let extern_url_takes_precedence = render_options.extern_html_root_takes_precedence;
let dst = &render_options.output;
// Make `--extern-html-root-url` support the same names as `--extern` whenever possible
let cstore = CStore::from_tcx(tcx);
for (name, extern_url) in &render_options.extern_html_root_urls {
if let Some(crate_num) = cstore.resolved_extern_crate(Symbol::intern(name)) {
let e = ExternalCrate { crate_num };
let location = e.location(Some(extern_url), extern_url_takes_precedence, dst, tcx);
cx.cache.extern_locations.insert(e.crate_num, location);
}
}
// Cache where all our extern crates are located
// FIXME: this part is specific to HTML so it'd be nice to remove it from the common code
// This is also used in the JSON output.
for &crate_num in tcx.crates(()) {
let e = ExternalCrate { crate_num };
let name = e.name(tcx);
let render_options = &cx.render_options;
let extern_url = render_options.extern_html_root_urls.get(name.as_str()).map(|u| &**u);
let extern_url_takes_precedence = render_options.extern_html_root_takes_precedence;
let dst = &render_options.output;
let location = e.location(extern_url, extern_url_takes_precedence, dst, tcx);
cx.cache.extern_locations.insert(e.crate_num, location);
cx.cache.extern_locations.entry(e.crate_num).or_insert_with(|| {
// falls back to matching by crates' own names, because
// transitive dependencies and injected crates may be loaded without `--extern`
let extern_url =
render_options.extern_html_root_urls.get(name.as_str()).map(|u| &**u);
e.location(extern_url, extern_url_takes_precedence, dst, tcx)
});
cx.cache.external_paths.insert(e.def_id(), (vec![name], ItemType::Module));
}

View file

@ -79,6 +79,7 @@ lld = false
rustc = "{rustc}"
cargo = "{cargo}"
local-rebuild = true
compiletest-allow-stage0=true
[target.{host_triple}]
llvm-config = "{llvm_config}"
@ -117,7 +118,6 @@ llvm-config = "{llvm_config}"
args.extend(["--skip", test_path]);
}
cmd(&args)
.env("COMPILETEST_FORCE_STAGE0", "1")
// Also run dist-only tests
.env("COMPILETEST_ENABLE_DIST_TESTS", "1")
.run()

View file

@ -3,6 +3,8 @@
//@ compile-flags: --test --test-args=--test-threads=1
//@ normalize-stdout: "tests/rustdoc-ui" -> "$$DIR"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
//@ normalize-stdout: ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL"
/// ```

View file

@ -1,12 +1,13 @@
running 1 test
test $DIR/2024-doctests-checks.rs - Foo (line 8) ... ok
test $DIR/2024-doctests-checks.rs - Foo (line 10) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
running 1 test
test $DIR/2024-doctests-checks.rs - Foo (line 15) ... ok
test $DIR/2024-doctests-checks.rs - Foo (line 17) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
all doctests ran in $TIME; merged doctests compilation took $TIME

View file

@ -4,6 +4,8 @@
//@ normalize-stdout: "tests/rustdoc-ui" -> "$$DIR"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ normalize-stdout: ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL"
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
/// This doctest is used to ensure that if a crate attribute is present,
/// it will not be part of the merged doctests.

View file

@ -1,12 +1,13 @@
running 1 test
test $DIR/2024-doctests-crate-attribute.rs - Foo (line 20) ... ok
test $DIR/2024-doctests-crate-attribute.rs - Foo (line 22) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
running 1 test
test $DIR/2024-doctests-crate-attribute.rs - Foo (line 11) ... ok
test $DIR/2024-doctests-crate-attribute.rs - Foo (line 13) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
all doctests ran in $TIME; merged doctests compilation took $TIME

View file

@ -4,6 +4,8 @@
//@ compile-flags:--test
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
//@ failure-status: 101
#![doc(test(attr(allow(unused_variables), deny(warnings))))]

View file

@ -1,18 +1,18 @@
running 1 test
test $DIR/dead-code-2024.rs - f (line 13) - compile ... FAILED
test $DIR/dead-code-2024.rs - f (line 15) - compile ... FAILED
failures:
---- $DIR/dead-code-2024.rs - f (line 13) stdout ----
---- $DIR/dead-code-2024.rs - f (line 15) stdout ----
error: trait `T` is never used
--> $DIR/dead-code-2024.rs:14:7
--> $DIR/dead-code-2024.rs:16:7
|
LL | trait T { fn f(); }
| ^
|
note: the lint level is defined here
--> $DIR/dead-code-2024.rs:12:9
--> $DIR/dead-code-2024.rs:14:9
|
LL | #![deny(warnings)]
| ^^^^^^^^
@ -23,7 +23,8 @@ error: aborting due to 1 previous error
Couldn't compile the test.
failures:
$DIR/dead-code-2024.rs - f (line 13)
$DIR/dead-code-2024.rs - f (line 15)
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
all doctests ran in $TIME; merged doctests compilation took $TIME

View file

@ -4,6 +4,8 @@
//@ compile-flags:--test --test-args=--test-threads=1
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
//@ failure-status: 101
#![doc(test(attr(deny(warnings))))]

View file

@ -1,30 +1,30 @@
running 13 tests
test $DIR/dead-code-items.rs - A (line 32) - compile ... ok
test $DIR/dead-code-items.rs - A (line 88) - compile ... ok
test $DIR/dead-code-items.rs - A::field (line 39) - compile ... FAILED
test $DIR/dead-code-items.rs - A::method (line 94) - compile ... ok
test $DIR/dead-code-items.rs - C (line 22) - compile ... FAILED
test $DIR/dead-code-items.rs - Enum (line 70) - compile ... FAILED
test $DIR/dead-code-items.rs - Enum::Variant1 (line 77) - compile ... FAILED
test $DIR/dead-code-items.rs - MyTrait (line 103) - compile ... FAILED
test $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 110) - compile ... FAILED
test $DIR/dead-code-items.rs - S (line 14) - compile ... ok
test $DIR/dead-code-items.rs - U (line 48) - compile ... ok
test $DIR/dead-code-items.rs - U::field (line 55) - compile ... FAILED
test $DIR/dead-code-items.rs - U::field2 (line 61) - compile ... ok
test $DIR/dead-code-items.rs - A (line 34) - compile ... ok
test $DIR/dead-code-items.rs - A (line 90) - compile ... ok
test $DIR/dead-code-items.rs - A::field (line 41) - compile ... FAILED
test $DIR/dead-code-items.rs - A::method (line 96) - compile ... ok
test $DIR/dead-code-items.rs - C (line 24) - compile ... FAILED
test $DIR/dead-code-items.rs - Enum (line 72) - compile ... FAILED
test $DIR/dead-code-items.rs - Enum::Variant1 (line 79) - compile ... FAILED
test $DIR/dead-code-items.rs - MyTrait (line 105) - compile ... FAILED
test $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 112) - compile ... FAILED
test $DIR/dead-code-items.rs - S (line 16) - compile ... ok
test $DIR/dead-code-items.rs - U (line 50) - compile ... ok
test $DIR/dead-code-items.rs - U::field (line 57) - compile ... FAILED
test $DIR/dead-code-items.rs - U::field2 (line 63) - compile ... ok
failures:
---- $DIR/dead-code-items.rs - A::field (line 39) stdout ----
---- $DIR/dead-code-items.rs - A::field (line 41) stdout ----
error: trait `DeadCodeInField` is never used
--> $DIR/dead-code-items.rs:40:7
--> $DIR/dead-code-items.rs:42:7
|
LL | trait DeadCodeInField {}
| ^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/dead-code-items.rs:38:9
--> $DIR/dead-code-items.rs:40:9
|
LL | #![deny(dead_code)]
| ^^^^^^^^^
@ -32,15 +32,15 @@ LL | #![deny(dead_code)]
error: aborting due to 1 previous error
Couldn't compile the test.
---- $DIR/dead-code-items.rs - C (line 22) stdout ----
---- $DIR/dead-code-items.rs - C (line 24) stdout ----
error: unused variable: `unused_error`
--> $DIR/dead-code-items.rs:23:5
--> $DIR/dead-code-items.rs:25:5
|
LL | let unused_error = 5;
| ^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_error`
|
note: the lint level is defined here
--> $DIR/dead-code-items.rs:20:9
--> $DIR/dead-code-items.rs:22:9
|
LL | #![deny(warnings)]
| ^^^^^^^^
@ -49,15 +49,15 @@ LL | #![deny(warnings)]
error: aborting due to 1 previous error
Couldn't compile the test.
---- $DIR/dead-code-items.rs - Enum (line 70) stdout ----
---- $DIR/dead-code-items.rs - Enum (line 72) stdout ----
error: unused variable: `not_dead_code_but_unused`
--> $DIR/dead-code-items.rs:71:5
--> $DIR/dead-code-items.rs:73:5
|
LL | let not_dead_code_but_unused = 5;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_not_dead_code_but_unused`
|
note: the lint level is defined here
--> $DIR/dead-code-items.rs:68:9
--> $DIR/dead-code-items.rs:70:9
|
LL | #![deny(warnings)]
| ^^^^^^^^
@ -66,15 +66,15 @@ LL | #![deny(warnings)]
error: aborting due to 1 previous error
Couldn't compile the test.
---- $DIR/dead-code-items.rs - Enum::Variant1 (line 77) stdout ----
---- $DIR/dead-code-items.rs - Enum::Variant1 (line 79) stdout ----
error: unused variable: `unused_in_variant`
--> $DIR/dead-code-items.rs:80:17
--> $DIR/dead-code-items.rs:82:17
|
LL | fn main() { let unused_in_variant = 5; }
| ^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_in_variant`
|
note: the lint level is defined here
--> $DIR/dead-code-items.rs:75:9
--> $DIR/dead-code-items.rs:77:9
|
LL | #![deny(warnings)]
| ^^^^^^^^
@ -83,15 +83,15 @@ LL | #![deny(warnings)]
error: aborting due to 1 previous error
Couldn't compile the test.
---- $DIR/dead-code-items.rs - MyTrait (line 103) stdout ----
---- $DIR/dead-code-items.rs - MyTrait (line 105) stdout ----
error: trait `StillDeadCodeAtMyTrait` is never used
--> $DIR/dead-code-items.rs:104:7
--> $DIR/dead-code-items.rs:106:7
|
LL | trait StillDeadCodeAtMyTrait { }
| ^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/dead-code-items.rs:102:9
--> $DIR/dead-code-items.rs:104:9
|
LL | #![deny(dead_code)]
| ^^^^^^^^^
@ -99,15 +99,15 @@ LL | #![deny(dead_code)]
error: aborting due to 1 previous error
Couldn't compile the test.
---- $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 110) stdout ----
---- $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 112) stdout ----
error: unused variable: `unused_in_impl`
--> $DIR/dead-code-items.rs:113:17
--> $DIR/dead-code-items.rs:115:17
|
LL | fn main() { let unused_in_impl = 5; }
| ^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_in_impl`
|
note: the lint level is defined here
--> $DIR/dead-code-items.rs:108:9
--> $DIR/dead-code-items.rs:110:9
|
LL | #![deny(warnings)]
| ^^^^^^^^
@ -116,15 +116,15 @@ LL | #![deny(warnings)]
error: aborting due to 1 previous error
Couldn't compile the test.
---- $DIR/dead-code-items.rs - U::field (line 55) stdout ----
---- $DIR/dead-code-items.rs - U::field (line 57) stdout ----
error: trait `DeadCodeInUnionField` is never used
--> $DIR/dead-code-items.rs:56:7
--> $DIR/dead-code-items.rs:58:7
|
LL | trait DeadCodeInUnionField {}
| ^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/dead-code-items.rs:54:9
--> $DIR/dead-code-items.rs:56:9
|
LL | #![deny(dead_code)]
| ^^^^^^^^^
@ -134,13 +134,14 @@ error: aborting due to 1 previous error
Couldn't compile the test.
failures:
$DIR/dead-code-items.rs - A::field (line 39)
$DIR/dead-code-items.rs - C (line 22)
$DIR/dead-code-items.rs - Enum (line 70)
$DIR/dead-code-items.rs - Enum::Variant1 (line 77)
$DIR/dead-code-items.rs - MyTrait (line 103)
$DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 110)
$DIR/dead-code-items.rs - U::field (line 55)
$DIR/dead-code-items.rs - A::field (line 41)
$DIR/dead-code-items.rs - C (line 24)
$DIR/dead-code-items.rs - Enum (line 72)
$DIR/dead-code-items.rs - Enum::Variant1 (line 79)
$DIR/dead-code-items.rs - MyTrait (line 105)
$DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 112)
$DIR/dead-code-items.rs - U::field (line 57)
test result: FAILED. 6 passed; 7 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
all doctests ran in $TIME; merged doctests compilation took $TIME

View file

@ -4,6 +4,8 @@
//@ compile-flags:--test
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
//@ failure-status: 101
#![doc(test(attr(allow(unused_variables))))]

View file

@ -1,24 +1,24 @@
running 1 test
test $DIR/dead-code-module-2.rs - g (line 24) - compile ... ok
test $DIR/dead-code-module-2.rs - g (line 26) - compile ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
running 1 test
test $DIR/dead-code-module-2.rs - my_mod::f (line 16) - compile ... FAILED
test $DIR/dead-code-module-2.rs - my_mod::f (line 18) - compile ... FAILED
failures:
---- $DIR/dead-code-module-2.rs - my_mod::f (line 16) stdout ----
---- $DIR/dead-code-module-2.rs - my_mod::f (line 18) stdout ----
error: trait `T` is never used
--> $DIR/dead-code-module-2.rs:17:7
--> $DIR/dead-code-module-2.rs:19:7
|
LL | trait T { fn f(); }
| ^
|
note: the lint level is defined here
--> $DIR/dead-code-module-2.rs:15:9
--> $DIR/dead-code-module-2.rs:17:9
|
LL | #![deny(warnings)]
| ^^^^^^^^
@ -29,7 +29,8 @@ error: aborting due to 1 previous error
Couldn't compile the test.
failures:
$DIR/dead-code-module-2.rs - my_mod::f (line 16)
$DIR/dead-code-module-2.rs - my_mod::f (line 18)
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
all doctests ran in $TIME; merged doctests compilation took $TIME

View file

@ -4,6 +4,8 @@
//@ compile-flags:--test
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
//@ failure-status: 101
mod my_mod {

View file

@ -1,18 +1,18 @@
running 1 test
test $DIR/dead-code-module.rs - my_mod::f (line 14) - compile ... FAILED
test $DIR/dead-code-module.rs - my_mod::f (line 16) - compile ... FAILED
failures:
---- $DIR/dead-code-module.rs - my_mod::f (line 14) stdout ----
---- $DIR/dead-code-module.rs - my_mod::f (line 16) stdout ----
error: trait `T` is never used
--> $DIR/dead-code-module.rs:15:7
--> $DIR/dead-code-module.rs:17:7
|
LL | trait T { fn f(); }
| ^
|
note: the lint level is defined here
--> $DIR/dead-code-module.rs:13:9
--> $DIR/dead-code-module.rs:15:9
|
LL | #![deny(warnings)]
| ^^^^^^^^
@ -23,7 +23,8 @@ error: aborting due to 1 previous error
Couldn't compile the test.
failures:
$DIR/dead-code-module.rs - my_mod::f (line 14)
$DIR/dead-code-module.rs - my_mod::f (line 16)
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
all doctests ran in $TIME; merged doctests compilation took $TIME

View file

@ -2,6 +2,8 @@
//@ compile-flags:--test --test-args=--test-threads=1
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
//@ failure-status: 101
// https://github.com/rust-lang/rust/issues/130470

View file

@ -22,3 +22,4 @@ failures:
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
all doctests ran in $TIME; merged doctests compilation took $TIME

View file

@ -6,6 +6,8 @@
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
//@ normalize-stdout: "panicked at .+rs:" -> "panicked at $$TMP:"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
//@ rustc-env:RUST_BACKTRACE=0
//@ failure-status: 101

View file

@ -1,10 +1,10 @@
running 1 test
test $DIR/edition-2024-error-output.rs - (line 12) ... FAILED
test $DIR/edition-2024-error-output.rs - (line 14) ... FAILED
failures:
---- $DIR/edition-2024-error-output.rs - (line 12) stdout ----
---- $DIR/edition-2024-error-output.rs - (line 14) stdout ----
Test executable failed (exit status: 101).
stderr:
@ -18,7 +18,8 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
$DIR/edition-2024-error-output.rs - (line 12)
$DIR/edition-2024-error-output.rs - (line 14)
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
all doctests ran in $TIME; merged doctests compilation took $TIME

View file

@ -5,6 +5,8 @@
//@ compile-flags:--test
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
//@ failure-status: 101
/// ```should_panic

View file

@ -1,14 +1,15 @@
running 1 test
test $DIR/failed-doctest-should-panic.rs - Foo (line 10) - should panic ... FAILED
test $DIR/failed-doctest-should-panic.rs - Foo (line 12) - should panic ... FAILED
failures:
---- $DIR/failed-doctest-should-panic.rs - Foo (line 10) stdout ----
note: test did not panic as expected at $DIR/failed-doctest-should-panic.rs:10:0
---- $DIR/failed-doctest-should-panic.rs - Foo (line 12) stdout ----
note: test did not panic as expected at $DIR/failed-doctest-should-panic.rs:12:0
failures:
$DIR/failed-doctest-should-panic.rs - Foo (line 10)
$DIR/failed-doctest-should-panic.rs - Foo (line 12)
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
all doctests ran in $TIME; merged doctests compilation took $TIME

View file

@ -1,12 +1,12 @@
running 1 test
test $DIR/failed-doctest-test-crate.rs - m (line 14) ... FAILED
test $DIR/failed-doctest-test-crate.rs - m (line 16) ... FAILED
failures:
---- $DIR/failed-doctest-test-crate.rs - m (line 14) stdout ----
---- $DIR/failed-doctest-test-crate.rs - m (line 16) stdout ----
error[E0432]: unresolved import `test`
--> $DIR/failed-doctest-test-crate.rs:15:5
--> $DIR/failed-doctest-test-crate.rs:17:5
|
LL | use test::*;
| ^^^^ use of unresolved module or unlinked crate `test`
@ -22,7 +22,7 @@ For more information about this error, try `rustc --explain E0432`.
Couldn't compile the test.
failures:
$DIR/failed-doctest-test-crate.rs - m (line 14)
$DIR/failed-doctest-test-crate.rs - m (line 16)
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME

View file

@ -1,12 +1,12 @@
running 1 test
test $DIR/failed-doctest-test-crate.rs - m (line 14) ... FAILED
test $DIR/failed-doctest-test-crate.rs - m (line 16) ... FAILED
failures:
---- $DIR/failed-doctest-test-crate.rs - m (line 14) stdout ----
---- $DIR/failed-doctest-test-crate.rs - m (line 16) stdout ----
error[E0432]: unresolved import `test`
--> $DIR/failed-doctest-test-crate.rs:15:5
--> $DIR/failed-doctest-test-crate.rs:17:5
|
LL | use test::*;
| ^^^^ use of unresolved module or unlinked crate `test`
@ -19,7 +19,8 @@ For more information about this error, try `rustc --explain E0432`.
Couldn't compile the test.
failures:
$DIR/failed-doctest-test-crate.rs - m (line 14)
$DIR/failed-doctest-test-crate.rs - m (line 16)
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
all doctests ran in $TIME; merged doctests compilation took $TIME

View file

@ -7,6 +7,8 @@
//@ compile-flags:--test
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
//@ failure-status: 101
/// <https://github.com/rust-lang/rust/pull/137899#discussion_r1976743383>

View file

@ -1,12 +1,12 @@
running 1 test
test $DIR/relative-path-include-bytes-132203.rs - (line 18) ... FAILED
test $DIR/relative-path-include-bytes-132203.rs - (line 20) ... FAILED
failures:
---- $DIR/relative-path-include-bytes-132203.rs - (line 18) stdout ----
---- $DIR/relative-path-include-bytes-132203.rs - (line 20) stdout ----
error: couldn't read `$DIR/relative-dir-empty-file`: $FILE_NOT_FOUND_MSG (os error 2)
--> $DIR/relative-path-include-bytes-132203.rs:19:9
--> $DIR/relative-path-include-bytes-132203.rs:21:9
|
LL | let x = include_bytes!("relative-dir-empty-file");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -16,7 +16,7 @@ error: aborting due to 1 previous error
Couldn't compile the test.
failures:
$DIR/relative-path-include-bytes-132203.rs - (line 18)
$DIR/relative-path-include-bytes-132203.rs - (line 20)
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME

View file

@ -4,3 +4,4 @@ test $DIR/auxiliary/relative-dir.md - (line 1) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
all doctests ran in $TIME; merged doctests compilation took $TIME

View file

@ -9,6 +9,8 @@
//@ normalize-stdout: "tests.rustdoc-ui.doctest." -> "$$DIR/"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ normalize-stdout: "`: .* \(os error 2\)" -> "`: $$FILE_NOT_FOUND_MSG (os error 2)"
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
// https://github.com/rust-lang/rust/issues/132203
// This version, because it's edition2024, passes thanks to the new

View file

@ -9,6 +9,8 @@
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ normalize-stdout: "panicked at .+rs:" -> "panicked at $$TMP:"
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
//@ failure-status: 101
//@ rustc-env:RUST_BACKTRACE=0

View file

@ -1,12 +1,12 @@
running 3 tests
test $DIR/stdout-and-stderr.rs - (line 15) ... FAILED
test $DIR/stdout-and-stderr.rs - (line 20) ... FAILED
test $DIR/stdout-and-stderr.rs - (line 24) ... FAILED
test $DIR/stdout-and-stderr.rs - (line 17) ... FAILED
test $DIR/stdout-and-stderr.rs - (line 22) ... FAILED
test $DIR/stdout-and-stderr.rs - (line 26) ... FAILED
failures:
---- $DIR/stdout-and-stderr.rs - (line 15) stdout ----
---- $DIR/stdout-and-stderr.rs - (line 17) stdout ----
Test executable failed (exit status: 101).
stdout:
@ -21,7 +21,7 @@ assertion `left == right` failed
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
---- $DIR/stdout-and-stderr.rs - (line 20) stdout ----
---- $DIR/stdout-and-stderr.rs - (line 22) stdout ----
Test executable failed (exit status: 101).
stderr:
@ -33,14 +33,15 @@ assertion `left == right` failed
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
---- $DIR/stdout-and-stderr.rs - (line 24) stdout ----
---- $DIR/stdout-and-stderr.rs - (line 26) stdout ----
Test executable failed (exit status: 1).
failures:
$DIR/stdout-and-stderr.rs - (line 15)
$DIR/stdout-and-stderr.rs - (line 20)
$DIR/stdout-and-stderr.rs - (line 24)
$DIR/stdout-and-stderr.rs - (line 17)
$DIR/stdout-and-stderr.rs - (line 22)
$DIR/stdout-and-stderr.rs - (line 26)
test result: FAILED. 0 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
all doctests ran in $TIME; merged doctests compilation took $TIME

View file

@ -3,6 +3,8 @@
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ normalize-stdout: ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL"
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
//@ failure-status: 101
/// ```

View file

@ -1,17 +1,17 @@
running 1 test
test $DIR/wrong-ast-2024.rs - three (line 18) - should panic ... ok
test $DIR/wrong-ast-2024.rs - three (line 20) - should panic ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
running 2 tests
test $DIR/wrong-ast-2024.rs - one (line 8) ... FAILED
test $DIR/wrong-ast-2024.rs - two (line 13) ... FAILED
test $DIR/wrong-ast-2024.rs - one (line 10) ... FAILED
test $DIR/wrong-ast-2024.rs - two (line 15) ... FAILED
failures:
---- $DIR/wrong-ast-2024.rs - one (line 8) stdout ----
---- $DIR/wrong-ast-2024.rs - one (line 10) stdout ----
error[E0758]: unterminated block comment
--> $DIR/wrong-ast-2024.rs:$LINE:$COL
|
@ -22,7 +22,7 @@ error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0758`.
Couldn't compile the test.
---- $DIR/wrong-ast-2024.rs - two (line 13) stdout ----
---- $DIR/wrong-ast-2024.rs - two (line 15) stdout ----
error: unexpected closing delimiter: `}`
--> $DIR/wrong-ast-2024.rs:$LINE:$COL
|
@ -34,8 +34,9 @@ error: aborting due to 1 previous error
Couldn't compile the test.
failures:
$DIR/wrong-ast-2024.rs - one (line 8)
$DIR/wrong-ast-2024.rs - two (line 13)
$DIR/wrong-ast-2024.rs - one (line 10)
$DIR/wrong-ast-2024.rs - two (line 15)
test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
all doctests ran in $TIME; merged doctests compilation took $TIME

View file

@ -0,0 +1,9 @@
//@ compile-flags:-Z unstable-options --extern-html-root-url externs_name=https://renamed.example.com --extern-html-root-url empty=https://bad.invalid
//@ aux-crate:externs_name=empty.rs
//@ edition: 2018
extern crate externs_name as renamed;
//@ has extern_html_alias/index.html
//@ has - '//a/@href' 'https://renamed.example.com/empty/index.html'
pub use renamed as yet_different_name;

View file

@ -0,0 +1,14 @@
//@ compile-flags:-Z unstable-options --extern-html-root-url yet_another_name=https://bad.invalid --extern-html-root-url renamed_privately=https://bad.invalid --extern-html-root-url renamed_locally=https://bad.invalid --extern-html-root-url empty=https://localhost
//@ aux-crate:externs_name=empty.rs
//@ edition: 2018
mod m {
pub extern crate externs_name as renamed_privately;
}
// renaming within the crate's source code is not supposed to affect CLI flags
extern crate externs_name as renamed_locally;
//@ has extern_html_fallback/index.html
//@ has - '//a/@href' 'https://localhost/empty/index.html'
pub use crate::renamed_locally as yet_another_name;