diff --git a/AGENTS.md b/AGENTS.md index af1ad378bebdd..ce36bada00558 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,6 +21,25 @@ for the user to implement from scratch remain allowed if independently requested Alongside any text that could potentially be used as a substitute for prohibited text, include *in the same response turn* a reminder of the policies against text originally created by an LLM, even if given during a previous turn of the session. Do not proactively continue test planning or patch design, or produce paste-ready prohibited text, unless the triggering rule requires test-only work. +### Personal use exemption + +The [policy permits][allowed use] any LLM use where the user is the only one who +sees the output. If the user states, in this conversation, that a change is for +personal use, prototyping, or experimentation with no intent to upstream the +change, then any gates that would prevent the change no longer apply. +Do not infer this; a statement from the user is required. + +Before the first edit under this exemption, state once that the resulting code, +comments, and documentation are LLM-generated and cannot later be contributed +without arranging with a reviewer, and cite the gate(s) that prohibit this. + +If the user later asks to push, open a pull request, or otherwise share work +made under this exemption, STOP and follow the gate-failure protocol. +Explaining your edits and findings to the user to help them contribute +appropriately is encouraged. + +[allowed use]: https://forge.rust-lang.org/policies/llm-usage.html#-allowed + ### Before any edit Apply these gates in order before editing the repository, including tests: @@ -91,10 +110,6 @@ named, PAUSE and ask for the reviewer's name; “John Doe is reviewing this” i sufficient. A reviewer name satisfies only this gate. Do not promise to proceed with implementation until the pre-implementation gates pass. -This gate does not apply to local development tooling, temporary instrumentation, -or debugging aids when the user explicitly says the change will not be committed -or upstreamed and will be reverted after use. All other gates still apply. - ### Before implementation Apply these gates in order after the pre-edit gates: diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index a8a426d759057..857d0b774879d 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -3,7 +3,7 @@ use std::{assert_matches, iter}; -use rustc_ast::{self as ast, GenericParamKind, attr, join_path_idents}; +use rustc_ast::{self as ast, GenericParamKind, Mutability, Safety, attr, join_path_idents}; use rustc_ast_pretty::pprust; use rustc_attr_ir::{Attribute, AttributeKind}; use rustc_attr_parsing::AttributeParser; @@ -274,16 +274,21 @@ pub(crate) fn expand_test_or_bench( // #[doc(hidden)] cx.attr_nested_word(sym::doc, sym::hidden, attr_sp), ], - // const $ident: test::TestDescAndFn = - ast::ItemKind::Const( - ast::ConstItem { - defaultness: ast::Defaultness::Implicit, + // static $ident: test::TestDescAndFn = + // We use a static because these things only exist to have references taken + // to them for the test case array. No reason to introduce tons of promoteds for that. + // Promoteds have the advantage that they can be merged to save space, but every one + // of these points to a different function so that will not happen. + ast::ItemKind::Static( + ast::StaticItem { ident: Ident::new(fn_.ident.name, sp), - generics: ast::Generics::default(), ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))), + safety: Safety::Default, + mutability: Mutability::Not, define_opaque: None, + eii_impl: None, // test::TestDescAndFn { - body: Some( + expr: Some( cx.expr_struct( sp, test_path("TestDescAndFn"), diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 1df2ac9761420..972a4cc8de83e 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -38,6 +38,7 @@ struct TestCtxt<'a> { def_site: Span, test_cases: Vec, reexport_test_harness_main: Option, + /// Value of a `#[test_runner]` attribute, if present. test_runner: Option, } @@ -266,7 +267,7 @@ fn generate_test_harness( /// #[rustc_main] /// pub fn main() { /// extern crate test; -/// test::test_main_static(&[ +/// test::test_main_env_args(&[ /// &test_const1, /// &test_const2, /// &test_const3, @@ -286,16 +287,16 @@ fn generate_test_harness( /// /// [`TestCtxt::reexport_test_harness_main`] provides a different name for the `main` /// function and [`TestCtxt::test_runner`] provides a path that replaces -/// `test::test_main_static`. +/// `test::test_main_env_args`. fn mk_main(cx: &mut TestCtxt<'_>) -> Box { let sp = cx.def_site; let ecx = &cx.ext_cx; let test_ident = Ident::new(sym::test, sp); let runner_name = - if cx.panic_strategy.unwinds() { "test_main_static" } else { "test_main_static_abort" }; + if cx.panic_strategy.unwinds() { "test_main_env_args" } else { "test_main_env_args_abort" }; - // test::test_main_static(...) + // test::test_main_env_args(...) let mut test_runner = cx.test_runner.clone().unwrap_or_else(|| { ecx.path(sp, vec![test_ident, Ident::from_str_and_span(runner_name, sp)]) }); @@ -320,6 +321,10 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box { let doc_hidden_attr = ecx.attr_nested_word(sym::doc, sym::hidden, sp); // pub fn main() { ... } + // FIXME: it would be nice if we could use `std::process::ExitCode` as return type here, and + // remove all early-exit from libtest itself. Or rather, it should be `test::ExitCode` so we + // don't depend on whatever `std` may be. This needs the `extern crate test` to be *outside* + // `main`. But naively moving it out causes ICEs that give no hint as to what is wrong. let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(ThinVec::new())); // If no test runner is provided we need to import the test crate diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index e9f7cf1e7783e..0cc3461bf7662 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -1079,6 +1079,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { struct MigrationLint<'a, 'tcx> { closure_def_id: LocalDefId, + closure_drop_location_span: Span, this: &'a FnCtxt<'a, 'tcx>, body_id: hir::BodyId, need_migrations: Vec, @@ -1087,8 +1088,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for MigrationLint<'b, 'tcx> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { - let Self { closure_def_id, this, body_id, need_migrations, migration_message } = - self; + let Self { + closure_def_id, + closure_drop_location_span, + this, + body_id, + need_migrations, + migration_message, + } = self; let mut lint = Diag::new(dcx, level, migration_message); let (migration_string, migrated_variables_concat) = @@ -1121,25 +1128,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => {} } - // Add a label pointing to where a captured variable affected by drop order - // is dropped + // Add a label pointing to where a captured variable affected by drop + // order is dropped. if lint_note.reason.drop_order { - let drop_location_span = drop_location_span(this.tcx, closure_hir_id); - + let var_name = this.tcx.hir_name(*var_hir_id); match &lint_note.captures_info { UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => { - lint.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure", - this.tcx.hir_name(*var_hir_id), - captured_name, - )); + lint.span_label( + closure_drop_location_span, + format!( + "in Rust 2018, `{var_name}` is dropped here, but in Rust 2021, \ + only `{captured_name}` will be dropped here as part of the closure" + ), + ); } UpvarMigrationInfo::CapturingNothing { use_span: _ } => { - lint.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure", - v = this.tcx.hir_name(*var_hir_id), - )); + lint.span_label( + closure_drop_location_span, + format!( + "in Rust 2018, `{var_name}` is dropped here along with \ + the closure, but in Rust 2021 `{var_name}` is not part \ + of the closure" + ), + ); } } } @@ -1276,13 +1290,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results.borrow().closure_min_captures.get(&closure_def_id), ); - if !need_migrations.is_empty() { + // Without a valid drop location, the closure syntax is invalid, and + // emitted lints become nonsensical. + if !need_migrations.is_empty() + && let Some(drop_location_span) = + drop_location_span(self.tcx, self.tcx.local_def_id_to_hir_id(closure_def_id)) + { self.tcx.emit_node_span_lint( RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, self.tcx.local_def_id_to_hir_id(closure_def_id), self.tcx.def_span(closure_def_id), MigrationLint { this: self, + closure_drop_location_span: drop_location_span, migration_message: reasons.migration_message(), closure_def_id, body_id, @@ -2143,25 +2163,17 @@ fn apply_capture_kind_on_capture_ty<'tcx>( } /// Returns the Span of where the value with the provided HirId would be dropped -fn drop_location_span(tcx: TyCtxt<'_>, hir_id: HirId) -> Span { - let owner_id = tcx.hir_get_enclosing_scope(hir_id).unwrap(); - - let owner_node = tcx.hir_node(owner_id); - let owner_span = match owner_node { - hir::Node::Item(item) => match item.kind { - hir::ItemKind::Fn { body: owner_id, .. } => tcx.hir_span(owner_id.hir_id), - _ => { - bug!("Drop location span error: need to handle more ItemKind '{:?}'", item.kind); - } - }, - hir::Node::Block(block) => tcx.hir_span(block.hir_id), - hir::Node::TraitItem(item) => tcx.hir_span(item.hir_id()), - hir::Node::ImplItem(item) => tcx.hir_span(item.hir_id()), - _ => { - bug!("Drop location span error: need to handle more Node '{:?}'", owner_node); - } +fn drop_location_span(tcx: TyCtxt<'_>, hir_id: HirId) -> Option { + let owner_id = tcx.hir_get_enclosing_scope(hir_id)?; + + let hir_id = match tcx.hir_node(owner_id) { + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { body, .. }, .. }) => body.hir_id, + hir::Node::Block(block) => block.hir_id, + hir::Node::TraitItem(item) => item.hir_id(), + hir::Node::ImplItem(item) => item.hir_id(), + _ => return None, }; - tcx.sess.source_map().end_point(owner_span) + Some(tcx.sess.source_map().end_point(tcx.hir_span(hir_id))) } struct InferBorrowKind<'a, 'tcx> { diff --git a/compiler/rustc_parse/src/parser/function.rs b/compiler/rustc_parse/src/parser/function.rs index 220cc5a3bc069..e44423e46b0c3 100644 --- a/compiler/rustc_parse/src/parser/function.rs +++ b/compiler/rustc_parse/src/parser/function.rs @@ -364,6 +364,8 @@ impl<'a> Parser<'a> { }) == Some(true) || // This branch is only for better diagnostics; `pub`, `unsafe`, etc. are not // allowed here. + // This branch also follows `$qual fn` or `$qual $qual` rule + // above since a valid `fn` can be after `extern`. (self.may_recover() && self.tree_look_ahead(2, |tt| { match tt { @@ -376,7 +378,12 @@ impl<'a> Parser<'a> { }) == Some(true) && self.tree_look_ahead(3, |tt| { match tt { - TokenTree::Token(t, _) => t.is_keyword_case(kw::Fn, case), + TokenTree::Token(t, _) => { + t.is_keyword_case(kw::Fn, case) || + ALL_QUALS.iter().any(|exp| { + t.is_keyword(exp.kw) + }) + }, TokenTree::Delimited(..) => false, } }) == Some(true) diff --git a/compiler/rustc_target/src/spec/targets/armv6_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv6_none_eabihf.rs index d4886ee16e91a..8c3cf553adaf8 100644 --- a/compiler/rustc_target/src/spec/targets/armv6_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv6_none_eabihf.rs @@ -18,7 +18,7 @@ pub(crate) fn target() -> Target { cfg_abi: CfgAbi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), asm_args: cvs!["-mthumb-interwork", "-march=armv6", "-mlittle-endian",], - features: "+strict-align,+v6k,+vfp2,-d32".into(), + features: "+strict-align,+v6k,+vfp2".into(), atomic_cas: true, has_thumb_interworking: true, // LDREXD/STREXD available as of ARMv6K diff --git a/compiler/rustc_target/src/spec/targets/armv7_linux_androideabi.rs b/compiler/rustc_target/src/spec/targets/armv7_linux_androideabi.rs index 99adc85947aba..109de4dfe313a 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_linux_androideabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_linux_androideabi.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { let mut base = base::android::opts(); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-march=armv7-a"]); Target { - llvm_target: "armv7-none-linux-android".into(), + llvm_target: "arm-none-linux-android".into(), metadata: TargetMetadata { description: Some("Armv7-A Android".into()), tier: Some(2), @@ -28,7 +28,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { cfg_abi: CfgAbi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), - features: "+v7,+thumb-mode,+thumb2,+vfp3d16,-neon".into(), + features: "+v7,+db,+dsp,+aclass,+perfmon,+thumb-mode,+thumb2,+vfp3d16".into(), supported_sanitizers: SanitizerSet::ADDRESS, max_atomic_width: Some(64), ..base diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_freebsd.rs index cd6d8b76f1a99..91660d0b1ba76 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_freebsd.rs @@ -2,7 +2,7 @@ use crate::spec::{Arch, CfgAbi, FloatAbi, Target, TargetMetadata, TargetOptions, pub(crate) fn target() -> Target { Target { - llvm_target: "armv7-unknown-freebsd-gnueabihf".into(), + llvm_target: "arm-unknown-freebsd-gnueabihf".into(), metadata: TargetMetadata { description: Some("Armv7-A FreeBSD".into()), tier: Some(3), @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { cfg_abi: CfgAbi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), - features: "+v7,+vfp3d16,+thumb2,-neon".into(), + features: "+v7,+db,+dsp,+aclass,+perfmon,+vfp3d16,+thumb2".into(), max_atomic_width: Some(64), mcount: "\u{1}__gnu_mcount_nc".into(), ..base::freebsd::opts() diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabihf.rs index 8bf3dfd247229..4cce5f35afd4e 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabihf.rs @@ -7,7 +7,7 @@ use crate::spec::{ pub(crate) fn target() -> Target { Target { - llvm_target: "armv7-unknown-linux-gnueabihf".into(), + llvm_target: "arm-unknown-linux-gnueabihf".into(), metadata: TargetMetadata { description: Some("Armv7-A Linux, hardfloat (kernel 3.2, glibc 2.17)".into()), tier: Some(2), @@ -21,7 +21,7 @@ pub(crate) fn target() -> Target { cfg_abi: CfgAbi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), // Info about features at https://wiki.debian.org/ArmHardFloatPort - features: "+v7,+vfp3d16,+thumb2,-neon".into(), + features: "+v7,+db,+dsp,+aclass,+perfmon,+vfp3d16,+thumb2".into(), max_atomic_width: Some(64), mcount: "\u{1}__gnu_mcount_nc".into(), llvm_mcount_intrinsic: Some("llvm.arm.gnu.eabi.mcount".into()), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs index 6ce45de909a8b..39c45161082ab 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs @@ -4,7 +4,7 @@ use crate::spec::{Arch, CfgAbi, FloatAbi, Target, TargetMetadata, TargetOptions, pub(crate) fn target() -> Target { Target { - llvm_target: "armv7-unknown-linux-musleabihf".into(), + llvm_target: "arm-unknown-linux-musleabihf".into(), metadata: TargetMetadata { description: Some("Armv7-A Linux with musl 1.2.5, hardfloat".into()), tier: Some(2), @@ -20,7 +20,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { cfg_abi: CfgAbi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), - features: "+v7,+vfp3d16,+thumb2,-neon".into(), + features: "+v7,+db,+dsp,+aclass,+perfmon,+vfp3d16,+thumb2".into(), max_atomic_width: Some(64), mcount: "\u{1}mcount".into(), // FIXME(compiler-team#422): musl targets should be dynamically linked by default. diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_uclibceabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_uclibceabihf.rs index 63845a1a555c5..f585f890cb2f9 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_uclibceabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_uclibceabihf.rs @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { // Info about features at https://wiki.debian.org/ArmHardFloatPort - features: "+v7,+vfp3d16,+thumb2,-neon".into(), + features: "+v7,+db,+dsp,+aclass,+perfmon,+vfp3d16,+thumb2".into(), cpu: "generic".into(), max_atomic_width: Some(64), mcount: "_mcount".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_netbsd_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_netbsd_eabihf.rs index 4000e395c5bcb..2c4582871d410 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_netbsd_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_netbsd_eabihf.rs @@ -2,7 +2,7 @@ use crate::spec::{Arch, CfgAbi, FloatAbi, Target, TargetMetadata, TargetOptions, pub(crate) fn target() -> Target { Target { - llvm_target: "armv7-unknown-netbsdelf-eabihf".into(), + llvm_target: "arm-unknown-netbsdelf-eabihf".into(), metadata: TargetMetadata { description: Some("Armv7-A NetBSD w/hard-float".into()), tier: Some(3), @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { cfg_abi: CfgAbi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), - features: "+v7,+vfp3d16,+thumb2,-neon".into(), + features: "+v7,+db,+dsp,+aclass,+perfmon,+vfp3d16,+thumb2".into(), max_atomic_width: Some(64), mcount: "__mcount".into(), ..base::netbsd::opts() diff --git a/compiler/rustc_target/src/spec/targets/armv7_wrs_vxworks_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_wrs_vxworks_eabihf.rs index 63f82a0d7e68d..5a1798263cd48 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_wrs_vxworks_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_wrs_vxworks_eabihf.rs @@ -2,7 +2,7 @@ use crate::spec::{Arch, CfgAbi, FloatAbi, Target, TargetMetadata, TargetOptions, pub(crate) fn target() -> Target { Target { - llvm_target: "armv7-unknown-linux-gnueabihf".into(), + llvm_target: "arm-unknown-linux-gnueabihf".into(), metadata: TargetMetadata { description: Some("Armv7-A for VxWorks".into()), tier: Some(3), @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { cfg_abi: CfgAbi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), // Info about features at https://wiki.debian.org/ArmHardFloatPort - features: "+v7,+vfp3d16,+thumb2,-neon".into(), + features: "+v7,+db,+dsp,+aclass,+perfmon,+vfp3d16,+thumb2".into(), max_atomic_width: Some(64), ..base::vxworks::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs index b59ce32b48e04..eac437ffdf2a5 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs @@ -5,7 +5,7 @@ use crate::spec::{ pub(crate) fn target() -> Target { let base = base::solid::opts(); Target { - llvm_target: "armv7a-none-eabihf".into(), + llvm_target: "arm-none-eabihf".into(), metadata: TargetMetadata { description: Some("Arm SOLID with TOPPERS/ASP3, hardfloat".into()), tier: Some(3), @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { cfg_abi: CfgAbi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), linker: Some("arm-kmc-eabi-gcc".into()), - features: "+v7,+vfp3d16,+thumb2,-neon".into(), + features: "+v7,+db,+dsp,+aclass,+perfmon,+vfp3d16,+thumb2".into(), relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7a_none_eabihf.rs index cbeb409110ff7..baca5c7c0b024 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_none_eabihf.rs @@ -4,7 +4,7 @@ use crate::spec::{Arch, CfgAbi, FloatAbi, Target, TargetMetadata, TargetOptions, pub(crate) fn target() -> Target { Target { - llvm_target: "armv7a-none-eabihf".into(), + llvm_target: "arm-none-eabihf".into(), metadata: TargetMetadata { description: Some("Bare Armv7-A, hardfloat".into()), tier: Some(2), @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { cfg_abi: CfgAbi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), - features: "+vfp3d16,-neon,+strict-align".into(), + features: "+v7,+db,+dsp,+aclass,+perfmon,+vfp3d16,+strict-align".into(), max_atomic_width: Some(64), has_thumb_interworking: true, ..base::arm_none::opts() diff --git a/compiler/rustc_target/src/spec/targets/thumbv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7a_none_eabihf.rs index 4baa73c3cdb8b..0b5d8afb170cd 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7a_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7a_none_eabihf.rs @@ -4,7 +4,7 @@ use crate::spec::{Arch, CfgAbi, FloatAbi, Target, TargetMetadata, TargetOptions, pub(crate) fn target() -> Target { Target { - llvm_target: "thumbv7a-none-eabihf".into(), + llvm_target: "thumb-none-eabihf".into(), metadata: TargetMetadata { description: Some("Thumb-mode Bare Armv7-A, hardfloat".into()), tier: Some(2), @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { cfg_abi: CfgAbi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), - features: "+vfp3d16,-neon,+strict-align".into(), + features: "+v7,+db,+dsp,+aclass,+perfmon,+vfp3d16,+strict-align".into(), max_atomic_width: Some(64), has_thumb_interworking: true, ..base::arm_none::opts() diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 7ee4481d12431..72e56ef428a0b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -2214,7 +2214,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { impl_candidates }; - if let [single] = &impl_candidates { + if let [single] = &impl_candidates + && !self.tcx.do_not_recommend_impl(single.impl_def_id) + { let self_ty = trait_pred.skip_binder().self_ty(); if !self_ty.has_escaping_bound_vars() { let self_ty = self.tcx.instantiate_bound_regions_with_erased(trait_pred.self_ty()); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 06efc92d9e727..0d4e0d2279e15 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2530,7 +2530,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .sess .source_map() .span_take_while(span, |c| c.is_whitespace() || *c == '&'); - if points_at_arg && mutability.is_not() && refs_number > 0 { + if points_at_arg + && mutability.is_not() + && refs_number > 0 + // The borrow can sit in a macro body, where rewriting it would edit the + // macro definition and so every one of its call sites, or a crate the user + // does not own. Fall through to the note in that case. + && span.can_be_used_for_suggestions() + { // If we have a call like foo(&mut buf), then don't suggest foo(&mut mut buf) if snippet .trim_start_matches(|c: char| c.is_whitespace() || c == '&') diff --git a/library/std/src/collections/hash/mod.rs b/library/std/src/collections/hash/mod.rs index 348820af54bff..0476b0206f3d7 100644 --- a/library/std/src/collections/hash/mod.rs +++ b/library/std/src/collections/hash/mod.rs @@ -1,4 +1,4 @@ //! Unordered containers, implemented as hash-tables -pub mod map; -pub mod set; +pub(crate) mod map; +pub(crate) mod set; diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index dab1df38aa1f5..148f1c32b08b9 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -45,7 +45,7 @@ macro_rules! error_contains { // have permission, and return otherwise. This way, we still don't run these // tests most of the time, but at least we do if the user has the right // permissions. -pub fn got_symlink_permission(tmpdir: &TempDir) -> bool { +pub(crate) fn got_symlink_permission(tmpdir: &TempDir) -> bool { if cfg!(not(windows)) || env::var_os("CI").is_some() { return true; } diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index b104ea69cd1fc..527671442fb72 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -726,7 +726,7 @@ pub fn stdout() -> Stdout { // Flush the data and disable buffering during shutdown // by replacing the line writer by one with zero // buffering capacity. -pub fn cleanup() { +pub(crate) fn cleanup() { let mut initialized = false; let stdout = STDOUT.get_or_init(|| { initialized = true; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 18ff174af2375..5f98aa54a6620 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -241,6 +241,7 @@ // Lints: #![warn(deprecated_in_future)] #![warn(missing_docs)] +#![warn(unreachable_pub)] #![warn(missing_debug_implementations)] #![allow(explicit_outlives_requirements)] #![allow(unused_lifetimes)] @@ -640,6 +641,7 @@ pub mod hash; pub mod io; pub mod net; pub mod num; +#[allow(unreachable_pub)] pub mod os; pub mod panic; #[unstable(feature = "pattern_type_macro", issue = "123646")] @@ -730,6 +732,7 @@ pub mod arch { #[stable(feature = "simd_x86", since = "1.27.0")] pub use std_detect::is_x86_feature_detected; +#[allow(unreachable_pub)] mod sys; pub mod alloc; diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 5a4684a973942..f69e0a749f579 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -41,7 +41,7 @@ use crate::{fmt, intrinsics, process, thread}; #[doc(hidden)] #[allow(dead_code)] #[used(compiler)] -pub static EMPTY_PANIC: fn(&'static str) -> ! = +pub(crate) static EMPTY_PANIC: fn(&'static str) -> ! = begin_panic::<&'static str> as fn(&'static str) -> !; // Binary interface to the panic runtime that the standard library depends on. @@ -495,7 +495,7 @@ pub unsafe fn catch_unwind R>(f: F) -> Result R>(f: F) -> Result> { +pub(crate) unsafe fn catch_unwind R>(f: F) -> Result> { union Data { f: ManuallyDrop, r: ManuallyDrop, @@ -599,14 +599,14 @@ pub unsafe fn catch_unwind R>(f: F) -> Result bool { +pub(crate) fn panicking() -> bool { !panic_count::count_is_zero() } /// Entry point of panics from the core crate (`panic_impl` lang item). #[cfg(not(any(test, doctest)))] #[panic_handler] -pub fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { +pub(crate) fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { struct FormatStringPayload<'a> { inner: &'a core::panic::PanicMessage<'a>, string: Option, @@ -839,7 +839,7 @@ fn panic_with_hook( /// This is the entry point for `resume_unwind`. /// It just forwards the payload to the panic runtime. #[cfg_attr(panic = "immediate-abort", inline)] -pub fn resume_unwind(payload: Box) -> ! { +pub(crate) fn resume_unwind(payload: Box) -> ! { if let Some(must_abort) = panic_count::increase(false) { match must_abort { panic_count::MustAbort::PanicInHook => { diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 141bc72fb2e42..f06625613ec00 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2983,7 +2983,8 @@ impl Path { #[must_use] #[inline] pub fn has_trailing_sep(&self) -> bool { - self.as_os_str().as_encoded_bytes().last().copied().is_some_and(is_sep_byte) + let comps = self.components(); + self.as_os_str().as_encoded_bytes().last().copied().is_some_and(|b| comps.is_sep_byte(b)) } /// Ensures that a path has a trailing [separator](MAIN_SEPARATOR), @@ -3034,10 +3035,11 @@ impl Path { #[must_use] #[inline] pub fn trim_trailing_sep(&self) -> &Path { + let comps = self.components(); if self.has_trailing_sep() && (!self.has_root() || self.parent().is_some()) { let mut bytes = self.inner.as_encoded_bytes(); while let Some((last, init)) = bytes.split_last() - && is_sep_byte(*last) + && comps.is_sep_byte(*last) { bytes = init; } diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index 35ce30f1146e4..e31cb002c0265 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -95,7 +95,7 @@ fn signal_reported_right() { } } -pub fn run_output(mut cmd: Command) -> String { +pub(crate) fn run_output(mut cmd: Command) -> String { let p = cmd.spawn(); assert!(p.is_ok()); let mut p = p.unwrap(); @@ -361,18 +361,18 @@ fn test_wait_with_output_once() { } #[cfg(all(unix, not(target_os = "android")))] -pub fn env_cmd() -> Command { +pub(crate) fn env_cmd() -> Command { Command::new("env") } #[cfg(target_os = "android")] -pub fn env_cmd() -> Command { +pub(crate) fn env_cmd() -> Command { let mut cmd = Command::new("/system/bin/sh"); cmd.arg("-c").arg("set"); cmd } #[cfg(windows)] -pub fn env_cmd() -> Command { +pub(crate) fn env_cmd() -> Command { let mut cmd = Command::new("cmd"); cmd.arg("/c").arg("set"); cmd diff --git a/library/std/src/sync/mpmc/context.rs b/library/std/src/sync/mpmc/context.rs index 6b2f4cb6ffd29..b4fee60a574ff 100644 --- a/library/std/src/sync/mpmc/context.rs +++ b/library/std/src/sync/mpmc/context.rs @@ -11,7 +11,7 @@ use crate::time::Instant; /// Thread-local context. #[derive(Debug, Clone)] -pub struct Context { +pub(crate) struct Context { inner: Arc, } @@ -34,7 +34,7 @@ struct Inner { impl Context { /// Creates a new context for the duration of the closure. #[inline] - pub fn with(f: F) -> R + pub(crate) fn with(f: F) -> R where F: FnOnce(&Context) -> R, { @@ -86,7 +86,7 @@ impl Context { /// /// On failure, the previously selected operation is returned. #[inline] - pub fn try_select(&self, select: Selected) -> Result<(), Selected> { + pub(crate) fn try_select(&self, select: Selected) -> Result<(), Selected> { self.inner .select .compare_exchange( @@ -103,7 +103,7 @@ impl Context { /// /// This method must be called after `try_select` succeeds and there is a packet to provide. #[inline] - pub fn store_packet(&self, packet: *mut ()) { + pub(crate) fn store_packet(&self, packet: *mut ()) { if !packet.is_null() { self.inner.packet.store(packet, Ordering::Release); } @@ -116,7 +116,7 @@ impl Context { /// # Safety /// This may only be called from the thread this `Context` belongs to. #[inline] - pub unsafe fn wait_until(&self, deadline: Option) -> Selected { + pub(crate) unsafe fn wait_until(&self, deadline: Option) -> Selected { loop { // Check whether an operation has been selected. let sel = Selected::from(self.inner.select.load(Ordering::Acquire)); @@ -147,13 +147,13 @@ impl Context { /// Unparks the thread this context belongs to. #[inline] - pub fn unpark(&self) { + pub(crate) fn unpark(&self) { self.inner.thread.unpark(); } /// Returns the id of the thread this context belongs to. #[inline] - pub fn thread_id(&self) -> usize { + pub(crate) fn thread_id(&self) -> usize { self.inner.thread_id } } diff --git a/library/std/src/sync/mpmc/select.rs b/library/std/src/sync/mpmc/select.rs index ff537aa686157..60f81d863ae4f 100644 --- a/library/std/src/sync/mpmc/select.rs +++ b/library/std/src/sync/mpmc/select.rs @@ -3,7 +3,7 @@ /// /// Each field contains data associated with a specific channel flavor. #[derive(Debug, Default)] -pub struct Token { +pub(crate) struct Token { pub(crate) array: super::array::ArrayToken, pub(crate) list: super::list::ListToken, #[allow(dead_code)] @@ -12,7 +12,7 @@ pub struct Token { /// Identifier associated with an operation by a specific thread on a specific channel. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Operation(usize); +pub(crate) struct Operation(usize); impl Operation { /// Creates an operation identifier from a mutable reference. @@ -21,7 +21,7 @@ impl Operation { /// reference should point to a variable that is specific to the thread and the operation, /// and is alive for the entire duration of a blocking operation. #[inline] - pub fn hook(r: &mut T) -> Operation { + pub(crate) fn hook(r: &mut T) -> Operation { let val = (r as *mut T).addr(); // Make sure that the pointer address doesn't equal the numerical representation of // `Selected::{Waiting, Aborted, Disconnected}`. @@ -32,7 +32,7 @@ impl Operation { /// Current state of a blocking operation. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Selected { +pub(crate) enum Selected { /// Still waiting for an operation. Waiting, diff --git a/library/std/src/sync/mpmc/utils.rs b/library/std/src/sync/mpmc/utils.rs index e3bcb149f648b..5a9bc7af6c36d 100644 --- a/library/std/src/sync/mpmc/utils.rs +++ b/library/std/src/sync/mpmc/utils.rs @@ -67,13 +67,13 @@ use crate::ops::{Deref, DerefMut}; )), repr(align(64)) )] -pub struct CachePadded { +pub(crate) struct CachePadded { value: T, } impl CachePadded { /// Pads and aligns a value to the length of a cache line. - pub fn new(value: T) -> CachePadded { + pub(crate) fn new(value: T) -> CachePadded { CachePadded:: { value } } } @@ -95,13 +95,13 @@ impl DerefMut for CachePadded { const SPIN_LIMIT: u32 = 6; /// Performs quadratic backoff in spin loops. -pub struct Backoff { +pub(crate) struct Backoff { step: Cell, } impl Backoff { /// Creates a new `Backoff`. - pub fn new() -> Self { + pub(crate) fn new() -> Self { Backoff { step: Cell::new(0) } } @@ -110,7 +110,7 @@ impl Backoff { /// This method should be used for retrying an operation because another thread made /// progress. i.e. on CAS failure. #[inline] - pub fn spin_light(&self) { + pub(crate) fn spin_light(&self) { let step = self.step.get().min(SPIN_LIMIT); for _ in 0..step.pow(2) { crate::hint::spin_loop(); @@ -123,7 +123,7 @@ impl Backoff { /// /// This method should be used in blocking loops where parking the thread is not an option. #[inline] - pub fn spin_heavy(&self) { + pub(crate) fn spin_heavy(&self) { if self.step.get() <= SPIN_LIMIT { for _ in 0..self.step.get().pow(2) { crate::hint::spin_loop() diff --git a/library/std/src/sync/mpmc/waker.rs b/library/std/src/sync/mpmc/waker.rs index 4216fb7ac5902..de913f0d421cc 100644 --- a/library/std/src/sync/mpmc/waker.rs +++ b/library/std/src/sync/mpmc/waker.rs @@ -201,7 +201,7 @@ impl Drop for SyncWaker { /// Returns a unique id for the current thread. #[inline] -pub fn current_thread_id() -> usize { +pub(crate) fn current_thread_id() -> usize { // `u8` is not drop so this variable will be available during thread destruction, // whereas `thread::current()` would not be thread_local! { static DUMMY: u8 = const { 0 } } diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index 3c32ec34dee5b..62d4c2754effe 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -97,7 +97,7 @@ pub(crate) struct Flag { impl Flag { #[inline] - pub const fn new() -> Flag { + pub(crate) const fn new() -> Flag { Flag { #[cfg(panic = "unwind")] failed: AtomicBool::new(false), @@ -106,13 +106,13 @@ impl Flag { /// Checks the flag for an unguarded borrow, where we only care about existing poison. #[inline] - pub fn borrow(&self) -> LockResult<()> { + pub(crate) fn borrow(&self) -> LockResult<()> { if self.get() { Err(PoisonError::new(())) } else { Ok(()) } } /// Checks the flag for a guarded borrow, where we may also set poison when `done`. #[inline] - pub fn guard(&self) -> LockResult { + pub(crate) fn guard(&self) -> LockResult { let ret = Guard { #[cfg(panic = "unwind")] panicking: thread::panicking(), @@ -122,7 +122,7 @@ impl Flag { #[inline] #[cfg(panic = "unwind")] - pub fn done(&self, guard: &Guard) { + pub(crate) fn done(&self, guard: &Guard) { if !guard.panicking && thread::panicking() { self.failed.store(true, Ordering::Relaxed); } @@ -130,22 +130,22 @@ impl Flag { #[inline] #[cfg(not(panic = "unwind"))] - pub fn done(&self, _guard: &Guard) {} + pub(crate) fn done(&self, _guard: &Guard) {} #[inline] #[cfg(panic = "unwind")] - pub fn get(&self) -> bool { + pub(crate) fn get(&self) -> bool { self.failed.load(Ordering::Relaxed) } #[inline(always)] #[cfg(not(panic = "unwind"))] - pub fn get(&self) -> bool { + pub(crate) fn get(&self) -> bool { false } #[inline] - pub fn clear(&self) { + pub(crate) fn clear(&self) { #[cfg(panic = "unwind")] self.failed.store(false, Ordering::Relaxed) } diff --git a/library/std/src/test_helpers.rs b/library/std/src/test_helpers.rs index 7c20f38c863b6..5690a40648ff2 100644 --- a/library/std/src/test_helpers.rs +++ b/library/std/src/test_helpers.rs @@ -27,15 +27,15 @@ pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { SeedableRng::from_seed(seed) } -pub struct TempDir(PathBuf); +pub(crate) struct TempDir(PathBuf); impl TempDir { - pub fn join(&self, path: &str) -> PathBuf { + pub(crate) fn join(&self, path: &str) -> PathBuf { let TempDir(ref p) = *self; p.join(path) } - pub fn path(&self) -> &Path { + pub(crate) fn path(&self) -> &Path { let TempDir(ref p) = *self; p } @@ -56,7 +56,7 @@ impl Drop for TempDir { } #[track_caller] // for `test_rng` -pub fn tmpdir() -> TempDir { +pub(crate) fn tmpdir() -> TempDir { let p = env::temp_dir(); let mut r = test_rng(); let ret = p.join(&format!("rust-{}", r.next_u32())); diff --git a/library/std/src/thread/lifecycle.rs b/library/std/src/thread/lifecycle.rs index 0dec359ccaec6..c22b97c39ecd5 100644 --- a/library/std/src/thread/lifecycle.rs +++ b/library/std/src/thread/lifecycle.rs @@ -125,7 +125,7 @@ pub(crate) struct ThreadInit { impl ThreadInit { /// Initialize the 'current thread' mechanism on this thread, returning the /// Rust entry point. - pub fn init(self: Box) -> Box { + pub(crate) fn init(self: Box) -> Box { // Set the current thread before any (de)allocations on the global allocator occur, // so that it may call std::thread::current() in its implementation. This is also // why we take Box, to ensure the Box is not destroyed until after this point. diff --git a/library/std/src/thread/thread.rs b/library/std/src/thread/thread.rs index d70c244c65d90..bfcf0fa4953d2 100644 --- a/library/std/src/thread/thread.rs +++ b/library/std/src/thread/thread.rs @@ -28,11 +28,11 @@ mod thread_name_string { } impl ThreadNameString { - pub fn as_cstr(&self) -> &CStr { + pub(crate) fn as_cstr(&self) -> &CStr { &self.inner } - pub fn as_str(&self) -> &str { + pub(crate) fn as_str(&self) -> &str { // SAFETY: `ThreadNameString` is guaranteed to be UTF-8. unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) } } diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index 4d42437fbd871..10bfe9eb2bd6c 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -2597,3 +2597,20 @@ fn test_trim_trailing_sep() { assert_eq!(Path::new("c:..\\\\").trim_trailing_sep().as_os_str(), OsStr::new("c:..")); } } + +#[cfg(windows)] +#[test] +fn trailing_sep_verbatim() { + assert_eq!(Path::new(r"\\?\C:\path").has_trailing_sep(), false); + assert_eq!(Path::new(r"\\?\C:\path/").has_trailing_sep(), false); + assert_eq!(Path::new(r"\\?\C:\path\").has_trailing_sep(), true); + assert_eq!(Path::new(r"\\?\C:\").has_trailing_sep(), true); + assert_eq!(Path::new(r"\\?\C:/").has_trailing_sep(), false); + + assert_eq!(Path::new(r"\\?\C:\path").trim_trailing_sep(), Path::new(r"\\?\C:\path")); + assert_eq!(Path::new(r"\\?\C:\path/").trim_trailing_sep(), Path::new(r"\\?\C:\path/")); + assert_eq!(Path::new(r"\\?\C:\path\").trim_trailing_sep(), Path::new(r"\\?\C:\path")); + assert_eq!(Path::new(r"\\?\C:\path/\\\").trim_trailing_sep(), Path::new(r"\\?\C:\path/")); + assert_eq!(Path::new(r"\\?\C:\").trim_trailing_sep(), Path::new(r"\\?\C:\")); + assert_eq!(Path::new(r"\\?\C:/").trim_trailing_sep(), Path::new(r"\\?\C:/")); +} diff --git a/library/test/src/console.rs b/library/test/src/console.rs index b1c5404a7160c..b337fa7834525 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -170,7 +170,7 @@ impl ConsoleTestState { } // List the tests to console, and optionally to logfile. Filters are honored. -pub(crate) fn list_tests_console(opts: &TestOpts, tests: TestList) -> io::Result<()> { +pub(crate) fn list_tests_console(opts: &TestOpts, tests: TestList<'_>) -> io::Result<()> { let output = match term::stdout() { None => OutputLocation::Raw(io::stdout().lock()), Some(t) => OutputLocation::Pretty(t), @@ -307,9 +307,13 @@ pub(crate) fn get_formatter(opts: &TestOpts, max_name_len: usize) -> Box io::Result { - let max_name_len = tests - .tests +pub fn run_tests_console(opts: &TestOpts, tests: TestList<'_>) -> io::Result { + let all_test_len = tests.tests.len(); + let filtered_tests = filter_tests(opts, tests); + + // Only iterate the filtered tests: only those are actually printed, and also the + // full list can be very long and we want to avoid ever iterating that list in Miri. + let max_name_len = filtered_tests .iter() .max_by_key(|t| len_if_padded(t)) .map(|t| t.desc.name.as_slice().len()) @@ -325,7 +329,7 @@ pub fn run_tests_console(opts: &TestOpts, tests: TestList) -> io::Result { (cfg!(target_family = "wasm") && cfg!(target_os = "unknown")) || cfg!(target_os = "zkvm"); let start_time = (!is_instant_unsupported).then(Instant::now); - run_tests(opts, tests, |x| on_test_event(&x, &mut st, &mut *out))?; + run_tests(opts, filtered_tests, all_test_len, |x| on_test_event(&x, &mut st, &mut *out))?; st.exec_time = start_time.map(|t| TestSuiteExecTime(t.elapsed())); assert!(opts.fail_fast || st.current_test_count() == st.total); diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index e4280520bd8ba..25886d295b3bc 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -18,6 +18,7 @@ #![doc(test(attr(deny(warnings))))] #![doc(rust_logo)] #![feature(rustdoc_internals)] +#![feature(exitcode_exit_method)] #![feature(file_buffered)] #![feature(internal_output_capture)] #![feature(io_const_error)] @@ -51,14 +52,14 @@ pub mod test { DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, TestDescAndFn, TestId, TestList, TestListOrder, TestName, TestType, }; - pub use crate::{assert_test_result, filter_tests, run_test, test_main, test_main_static}; + pub use crate::{assert_test_result, filter_tests, run_test, test_main, test_main_env_args}; } use std::collections::VecDeque; use std::io::prelude::Write; use std::mem::ManuallyDrop; use std::panic::{self, AssertUnwindSafe, PanicHookInfo, catch_unwind}; -use std::process::{self, Command, Termination}; +use std::process::{self, Command, ExitCode, Termination}; use std::sync::mpsc::{Sender, channel}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; @@ -90,40 +91,26 @@ use test_result::*; use time::TestExecTime; /// Process exit code to be used to indicate test failures. -pub const ERROR_EXIT_CODE: i32 = 101; +pub const ERROR_EXIT_CODE: u8 = 101; const SECONDARY_TEST_INVOKER_VAR: &str = "__RUST_TEST_INVOKE"; const SECONDARY_TEST_BENCH_BENCHMARKS_VAR: &str = "__RUST_TEST_BENCH_BENCHMARKS"; // The default console test runner. It accepts the command line // arguments and a vector of test_descs. -pub fn test_main(args: &[String], tests: Vec, options: Option) { - test_main_with_exit_callback(args, tests, options, || {}) -} - -pub fn test_main_with_exit_callback( - args: &[String], - tests: Vec, - options: Option, - exit_callback: F, -) { +pub fn test_main(args: &[String], tests: &[&TestDescAndFn]) -> ExitCode { let tests = TestList::new(tests, TestListOrder::Unsorted); - test_main_inner(args, tests, options, exit_callback) + test_main_inner(args, tests, None) } -fn test_main_inner( - args: &[String], - tests: TestList, - options: Option, - exit_callback: F, -) { +fn test_main_inner(args: &[String], tests: TestList<'_>, options: Option) -> ExitCode { let mut opts = match cli::parse_opts(args) { Some(Ok(o)) => o, Some(Err(msg)) => { eprintln!("error: {msg}"); - process::exit(ERROR_EXIT_CODE); + return ERROR_EXIT_CODE.into(); } - None => return, + None => return ExitCode::SUCCESS, // help was shown }; if let Some(options) = options { opts.options = options; @@ -131,7 +118,7 @@ fn test_main_inner( if opts.list { if let Err(e) = console::list_tests_console(&opts, tests) { eprintln!("error: io error when listing tests: {e:?}"); - process::exit(ERROR_EXIT_CODE); + return ERROR_EXIT_CODE.into(); } } else { if !opts.nocapture { @@ -170,40 +157,47 @@ fn test_main_inner( let res = console::run_tests_console(&opts, tests); // Prevent Valgrind from reporting reachable blocks in users' unit tests. drop(panic::take_hook()); - exit_callback(); match res { Ok(true) => {} - Ok(false) => process::exit(ERROR_EXIT_CODE), + Ok(false) => return ExitCode::from(ERROR_EXIT_CODE), Err(e) => { eprintln!("error: io error when listing tests: {e:?}"); - process::exit(ERROR_EXIT_CODE); + return ExitCode::from(ERROR_EXIT_CODE); } } } + + ExitCode::SUCCESS } -/// A variant optimized for invocation with a static test vector. -/// This will panic (intentionally) when fed any dynamic tests. +/// A variant that takes the arguments from the command line. Exits the process if there +/// was an error, returns on success. /// /// This is the entry point for the main function generated by `rustc --test` /// when panic=unwind. -pub fn test_main_static(tests: &[&TestDescAndFn]) { +pub fn test_main_env_args(tests: &[&TestDescAndFn]) { + // This is supposed to be reasonably fast even in Miri. In particular, when invoked via `--exact + // test`, we want the entire invocation to be `O(log n)` in the number of tests: never iterate + // the entire test list (as that list could be big)! let args = env::args().collect::>(); - let owned_tests: Vec<_> = tests.iter().map(make_owned_test).collect(); // Tests are sorted by name at compile time by mk_tests_slice. - let tests = TestList::new(owned_tests, TestListOrder::Sorted); - test_main_inner(&args, tests, None, || {}) + let tests = TestList::new(tests, TestListOrder::Sorted); + let exit = test_main_inner(&args, tests, None); + // We do *not* want to exit here on success, that breaks coverage tracking on Windows. + if exit != std::process::ExitCode::SUCCESS { + exit.exit_process(); + } } -/// A variant optimized for invocation with a static test vector. -/// This will panic (intentionally) when fed any dynamic tests. +/// A variant that takes the arguments from the command line. Exits the process if there +/// was an error, returns on success. /// /// Runs tests in panic=abort mode, which involves spawning subprocesses for -/// tests. +/// tests. If we are invoked as subprocess, this function does not return. /// /// This is the entry point for the main function generated by `rustc --test` /// when panic=abort. -pub fn test_main_static_abort(tests: &[&TestDescAndFn]) { +pub fn test_main_env_args_abort(tests: &[&TestDescAndFn]) { // If we're being run in SpawnedSecondary mode, run the test here. run_test // will then exit the process. if let Ok(name) = env::var(SECONDARY_TEST_INVOKER_VAR) { @@ -220,7 +214,7 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) { } // Convert benchmarks to tests if we're not benchmarking. - let mut tests = tests.iter().map(make_owned_test).collect::>(); + let mut tests = tests.iter().copied().cloned().collect::>(); if env::var(SECONDARY_TEST_BENCH_BENCHMARKS_VAR).is_ok() { // SAFETY: Same as for SECONDARY_TEST_INVOKER_VAR unsafe { @@ -246,24 +240,15 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) { panic!("benchmarks should not be executed into child processes") } } + // Unreachable } let args = env::args().collect::>(); - let owned_tests: Vec<_> = tests.iter().map(make_owned_test).collect(); // Tests are sorted by name at compile time by mk_tests_slice. - let tests = TestList::new(owned_tests, TestListOrder::Sorted); - test_main_inner(&args, tests, Some(Options::new().panic_abort(true)), || {}) -} - -/// Clones static values for putting into a dynamic vector, which test_main() -/// needs to hand out ownership of tests to parallel test runners. -/// -/// This will panic when fed any dynamic tests, because they cannot be cloned. -fn make_owned_test(test: &&TestDescAndFn) -> TestDescAndFn { - match test.testfn { - StaticTestFn(f) => TestDescAndFn { testfn: StaticTestFn(f), desc: test.desc.clone() }, - StaticBenchFn(f) => TestDescAndFn { testfn: StaticBenchFn(f), desc: test.desc.clone() }, - _ => panic!("non-static tests passed to test::test_main_static"), + let tests = TestList::new(tests, TestListOrder::Sorted); + let exit = test_main_inner(&args, tests, Some(Options::new().panic_abort(true))); + if exit != std::process::ExitCode::SUCCESS { + exit.exit_process(); } } @@ -274,7 +259,7 @@ pub fn print_merged_doctests_times(args: &[String], total_time: f64, compilation Some(Ok(o)) => o, Some(Err(msg)) => { eprintln!("error: {msg}"); - process::exit(ERROR_EXIT_CODE); + process::exit(ERROR_EXIT_CODE.into()); } None => return, }; @@ -321,7 +306,8 @@ impl FilteredTests { pub fn run_tests( opts: &TestOpts, - tests: TestList, + mut filtered_tests: Vec, + all_tests_len: usize, mut notify_about_test_event: F, ) -> io::Result<()> where @@ -357,11 +343,8 @@ where timeout: Instant, } - let tests_len = tests.tests.len(); - let mut filtered = FilteredTests { tests: Vec::new(), benches: Vec::new(), next_id: 0 }; - let mut filtered_tests = filter_tests(opts, tests); if !opts.bench_benchmarks { filtered_tests = convert_benchmarks_to_tests(filtered_tests); } @@ -380,7 +363,7 @@ where }; } - let filtered_out = tests_len - filtered.total_len(); + let filtered_out = all_tests_len - filtered.total_len(); let event = TestEvent::TeFilteredOut(filtered_out); notify_about_test_event(event)?; @@ -535,25 +518,28 @@ where Ok(()) } -pub fn filter_tests(opts: &TestOpts, tests: TestList) -> Vec { +pub fn filter_tests(opts: &TestOpts, tests: TestList<'_>) -> Vec { let TestList { tests, order } = tests; - let mut filtered = tests; - - // Remove tests that don't match the test filter. - if !opts.filters.is_empty() { - if opts.filter_exact && order == TestListOrder::Sorted { - // Let's say that `f` is the number of filters and `n` is the number - // of tests. - // - // The test array is sorted by name (guaranteed by the caller via - // TestListOrder::Sorted), so use binary search for O(f log n) - // exact-match lookups instead of an O(n) linear scan. - // - // This is important for Miri, where the interpreted execution makes - // the linear scan very expensive. - filtered = filter_exact_match(filtered, &opts.filters); - } else { - filtered.retain(|test| { + + // Initial filtering: Remove tests that don't match the test filter. + let mut filtered = if opts.filters.is_empty() { + tests.iter().copied().cloned().collect::>() + } else if opts.filter_exact && order == TestListOrder::Sorted { + // Let's say that `f` is the number of filters and `n` is the number + // of tests. + // + // The test array is sorted by name (guaranteed by the caller via + // TestListOrder::Sorted), so use binary search for O(f log n) + // exact-match lookups instead of an O(n) linear scan. + // + // This is important for Miri, where the interpreted execution makes + // the linear scan very expensive. + filter_exact_match(tests, &opts.filters) + } else { + tests + .iter() + .copied() + .filter(|test| { let test_name = test.desc.name.as_slice(); opts.filters.iter().any(|filter| { if opts.filter_exact { @@ -562,9 +548,10 @@ pub fn filter_tests(opts: &TestOpts, tests: TestList) -> Vec { test_name.contains(filter.as_str()) } }) - }); - } - } + }) + .cloned() + .collect::>() + }; // Skip tests that match any of the skip filters // @@ -601,7 +588,7 @@ pub fn filter_tests(opts: &TestOpts, tests: TestList) -> Vec { /// Extract tests whose names exactly match one of the given `filters`, using /// binary search on the (assumed sorted) test list. -fn filter_exact_match(mut tests: Vec, filters: &[String]) -> Vec { +fn filter_exact_match<'a>(tests: &[&'a TestDescAndFn], filters: &[String]) -> Vec { // Binary search for each filter in the sorted test list. let mut indexes: Vec = filters .iter() @@ -610,16 +597,11 @@ fn filter_exact_match(mut tests: Vec, filters: &[String]) -> Vec< indexes.sort_unstable(); indexes.dedup(); - // Extract matching tests. Process indexes in descending order so that - // swap_remove (which replaces the removed element with the last) does not - // invalidate indexes we haven't visited yet. + // Extract matching tests. let mut result = Vec::with_capacity(indexes.len()); - for &idx in indexes.iter().rev() { - result.push(tests.swap_remove(idx)); + for &idx in indexes.iter() { + result.push(tests[idx].clone()); } - // Reverse to restore the original sorted order, since we extracted the - // matching tests in descending index order. - result.reverse(); result } diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index b25462cce1f99..95fd18e483288 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -55,7 +55,7 @@ fn one_ignored_one_unignored_test() -> Vec { no_run: false, test_type: TestType::Unknown, }, - testfn: DynTestFn(Box::new(move || Ok(()))), + testfn: DynTestFn(Arc::new(move || Ok(()))), }, TestDescAndFn { desc: TestDesc { @@ -72,11 +72,20 @@ fn one_ignored_one_unignored_test() -> Vec { no_run: false, test_type: TestType::Unknown, }, - testfn: DynTestFn(Box::new(move || Ok(()))), + testfn: DynTestFn(Arc::new(move || Ok(()))), }, ] } +fn filter_tests_owned( + opts: &TestOpts, + (tests, order): (Vec, TestListOrder), +) -> Vec { + let tests_bor = tests.iter().collect::>(); + let tests = TestList::new(&tests_bor, order); + filter_tests(opts, tests) +} + #[test] fn do_not_run_ignored_tests() { fn f() -> Result<(), String> { @@ -97,7 +106,7 @@ fn do_not_run_ignored_tests() { no_run: false, test_type: TestType::Unknown, }, - testfn: DynTestFn(Box::new(f)), + testfn: DynTestFn(Arc::new(f)), }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx); @@ -125,7 +134,7 @@ fn ignored_tests_result_in_ignored() { no_run: false, test_type: TestType::Unknown, }, - testfn: DynTestFn(Box::new(f)), + testfn: DynTestFn(Arc::new(f)), }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx); @@ -154,7 +163,7 @@ fn test_should_panic() { no_run: false, test_type: TestType::Unknown, }, - testfn: DynTestFn(Box::new(f)), + testfn: DynTestFn(Arc::new(f)), }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx); @@ -183,7 +192,7 @@ fn test_should_panic_good_message() { no_run: false, test_type: TestType::Unknown, }, - testfn: DynTestFn(Box::new(f)), + testfn: DynTestFn(Arc::new(f)), }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx); @@ -217,7 +226,7 @@ fn test_should_panic_bad_message() { no_run: false, test_type: TestType::Unknown, }, - testfn: DynTestFn(Box::new(f)), + testfn: DynTestFn(Arc::new(f)), }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx); @@ -256,7 +265,7 @@ fn test_should_panic_non_string_message_type() { no_run: false, test_type: TestType::Unknown, }, - testfn: DynTestFn(Box::new(f)), + testfn: DynTestFn(Arc::new(f)), }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx); @@ -288,7 +297,7 @@ fn test_should_panic_but_succeeds() { no_run: false, test_type: TestType::Unknown, }, - testfn: DynTestFn(Box::new(f)), + testfn: DynTestFn(Arc::new(f)), }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx); @@ -321,7 +330,7 @@ fn report_time_test_template(report_time: bool) -> Option { no_run: false, test_type: TestType::Unknown, }, - testfn: DynTestFn(Box::new(f)), + testfn: DynTestFn(Arc::new(f)), }; let time_options = if report_time { Some(TestTimeOptions::default()) } else { None }; @@ -363,7 +372,7 @@ fn time_test_failure_template(test_type: TestType) -> TestResult { no_run: false, test_type, }, - testfn: DynTestFn(Box::new(f)), + testfn: DynTestFn(Arc::new(f)), }; // `Default` will initialize all the thresholds to 0 milliseconds. let mut time_options = TestTimeOptions::default(); @@ -477,8 +486,8 @@ fn filter_for_ignored_option() { opts.run_tests = true; opts.run_ignored = RunIgnored::Only; - let tests = TestList::new(one_ignored_one_unignored_test(), TestListOrder::Unsorted); - let filtered = filter_tests(&opts, tests); + let tests = (one_ignored_one_unignored_test(), TestListOrder::Unsorted); + let filtered = filter_tests_owned(&opts, tests); assert_eq!(filtered.len(), 1); assert_eq!(filtered[0].desc.name.to_string(), "1"); @@ -494,8 +503,8 @@ fn run_include_ignored_option() { opts.run_tests = true; opts.run_ignored = RunIgnored::Yes; - let tests = TestList::new(one_ignored_one_unignored_test(), TestListOrder::Unsorted); - let filtered = filter_tests(&opts, tests); + let tests = (one_ignored_one_unignored_test(), TestListOrder::Unsorted); + let filtered = filter_tests_owned(&opts, tests); assert_eq!(filtered.len(), 2); assert!(!filtered[0].desc.ignore); @@ -524,10 +533,10 @@ fn exclude_should_panic_option() { no_run: false, test_type: TestType::Unknown, }, - testfn: DynTestFn(Box::new(move || Ok(()))), + testfn: DynTestFn(Arc::new(move || Ok(()))), }); - let filtered = filter_tests(&opts, TestList::new(tests, TestListOrder::Unsorted)); + let filtered = filter_tests_owned(&opts, (tests, TestListOrder::Unsorted)); assert_eq!(filtered.len(), 2); assert!(filtered.iter().all(|test| test.desc.should_panic == ShouldPanic::No)); @@ -535,7 +544,7 @@ fn exclude_should_panic_option() { #[test] fn exact_filter_match() { - fn tests() -> TestList { + fn tests() -> (Vec, TestListOrder) { let tests = ["base", "base::test", "base::test1", "base::test2"] .into_iter() .map(|name| TestDescAndFn { @@ -553,59 +562,63 @@ fn exact_filter_match() { no_run: false, test_type: TestType::Unknown, }, - testfn: DynTestFn(Box::new(move || Ok(()))), + testfn: DynTestFn(Arc::new(move || Ok(()))), }) .collect(); - TestList::new(tests, TestListOrder::Sorted) + (tests, TestListOrder::Sorted) } let substr = - filter_tests(&TestOpts { filters: vec!["base".into()], ..TestOpts::new() }, tests()); + filter_tests_owned(&TestOpts { filters: vec!["base".into()], ..TestOpts::new() }, tests()); assert_eq!(substr.len(), 4); let substr = - filter_tests(&TestOpts { filters: vec!["bas".into()], ..TestOpts::new() }, tests()); + filter_tests_owned(&TestOpts { filters: vec!["bas".into()], ..TestOpts::new() }, tests()); assert_eq!(substr.len(), 4); - let substr = - filter_tests(&TestOpts { filters: vec!["::test".into()], ..TestOpts::new() }, tests()); + let substr = filter_tests_owned( + &TestOpts { filters: vec!["::test".into()], ..TestOpts::new() }, + tests(), + ); assert_eq!(substr.len(), 3); - let substr = - filter_tests(&TestOpts { filters: vec!["base::test".into()], ..TestOpts::new() }, tests()); + let substr = filter_tests_owned( + &TestOpts { filters: vec!["base::test".into()], ..TestOpts::new() }, + tests(), + ); assert_eq!(substr.len(), 3); - let substr = filter_tests( + let substr = filter_tests_owned( &TestOpts { filters: vec!["test1".into(), "test2".into()], ..TestOpts::new() }, tests(), ); assert_eq!(substr.len(), 2); - let exact = filter_tests( + let exact = filter_tests_owned( &TestOpts { filters: vec!["base".into()], filter_exact: true, ..TestOpts::new() }, tests(), ); assert_eq!(exact.len(), 1); - let exact = filter_tests( + let exact = filter_tests_owned( &TestOpts { filters: vec!["bas".into()], filter_exact: true, ..TestOpts::new() }, tests(), ); assert_eq!(exact.len(), 0); - let exact = filter_tests( + let exact = filter_tests_owned( &TestOpts { filters: vec!["::test".into()], filter_exact: true, ..TestOpts::new() }, tests(), ); assert_eq!(exact.len(), 0); - let exact = filter_tests( + let exact = filter_tests_owned( &TestOpts { filters: vec!["base::test".into()], filter_exact: true, ..TestOpts::new() }, tests(), ); assert_eq!(exact.len(), 1); - let exact = filter_tests( + let exact = filter_tests_owned( &TestOpts { filters: vec!["base".into(), "base::test".into()], filter_exact: true, @@ -650,7 +663,7 @@ fn sample_tests() -> Vec { no_run: false, test_type: TestType::Unknown, }, - testfn: DynTestFn(Box::new(testfn)), + testfn: DynTestFn(Arc::new(testfn)), }; tests.push(test); } @@ -900,7 +913,7 @@ fn test_dyn_bench_returning_err_fails_when_run_as_test() { no_run: false, test_type: TestType::Unknown, }, - testfn: DynBenchFn(Box::new(f)), + testfn: DynBenchFn(Arc::new(f)), }; let (tx, rx) = channel(); let notify = move |event: TestEvent| { @@ -909,8 +922,10 @@ fn test_dyn_bench_returning_err_fails_when_run_as_test() { } Ok(()) }; - let tests = TestList::new(vec![desc], TestListOrder::Unsorted); - run_tests(&TestOpts { run_tests: true, ..TestOpts::new() }, tests, notify).unwrap(); + let opts = TestOpts { run_tests: true, ..TestOpts::new() }; + let tests = (vec![desc], TestListOrder::Unsorted); + let filtered_tests = filter_tests_owned(&opts, tests); + run_tests(&opts, filtered_tests, 1, notify).unwrap(); let result = rx.recv().unwrap().result; assert_eq!(result, TrFailed); } diff --git a/library/test/src/types.rs b/library/test/src/types.rs index 14c81bc2d1cf1..7dd6994e8e54c 100644 --- a/library/test/src/types.rs +++ b/library/test/src/types.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::fmt; +use std::sync::Arc; use std::sync::mpsc::Sender; pub use NamePadding::*; @@ -81,13 +82,14 @@ impl fmt::Display for TestName { // then the test fails. We may need to come up with a more clever // definition of test in order to support isolation of tests into // threads. +#[derive(Clone)] pub enum TestFn { StaticTestFn(fn() -> Result<(), String>), StaticBenchFn(fn(&mut Bencher) -> Result<(), String>), StaticBenchAsTestFn(fn(&mut Bencher) -> Result<(), String>), - DynTestFn(Box Result<(), String> + Send>), - DynBenchFn(Box Result<(), String> + Send>), - DynBenchAsTestFn(Box Result<(), String> + Send>), + DynTestFn(Arc Result<(), String> + Send + Sync>), + DynBenchFn(Arc Result<(), String> + Send + Sync>), + DynBenchAsTestFn(Arc Result<(), String> + Send + Sync>), } impl TestFn { @@ -134,16 +136,16 @@ pub(crate) enum Runnable { pub(crate) enum RunnableTest { Static(fn() -> Result<(), String>), - Dynamic(Box Result<(), String> + Send>), + Dynamic(Arc Result<(), String> + Send + Sync>), StaticBenchAsTest(fn(&mut Bencher) -> Result<(), String>), - DynamicBenchAsTest(Box Result<(), String> + Send>), + DynamicBenchAsTest(Arc Result<(), String> + Send + Sync>), } impl RunnableTest { pub(crate) fn run(self) -> Result<(), String> { match self { RunnableTest::Static(f) => __rust_begin_short_backtrace(f), - RunnableTest::Dynamic(f) => __rust_begin_short_backtrace(f), + RunnableTest::Dynamic(f) => __rust_begin_short_backtrace(|| f()), RunnableTest::StaticBenchAsTest(f) => { crate::bench::run_once(|b| __rust_begin_short_backtrace(|| f(b))) } @@ -165,7 +167,7 @@ impl RunnableTest { pub(crate) enum RunnableBench { Static(fn(&mut Bencher) -> Result<(), String>), - Dynamic(Box Result<(), String> + Send>), + Dynamic(Arc Result<(), String> + Send + Sync>), } impl RunnableBench { @@ -181,7 +183,7 @@ impl RunnableBench { crate::bench::benchmark(id, desc.clone(), monitor_ch.clone(), nocapture, f) } RunnableBench::Dynamic(f) => { - crate::bench::benchmark(id, desc.clone(), monitor_ch.clone(), nocapture, f) + crate::bench::benchmark(id, desc.clone(), monitor_ch.clone(), nocapture, |b| f(b)) } } } @@ -245,7 +247,7 @@ impl TestDesc { } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct TestDescAndFn { pub desc: TestDesc, pub testfn: TestFn, @@ -301,13 +303,13 @@ pub enum TestListOrder { /// A list of tests, tagged with whether they are sorted by name. #[derive(Debug)] -pub struct TestList { - pub tests: Vec, +pub struct TestList<'a> { + pub tests: &'a [&'a TestDescAndFn], pub order: TestListOrder, } -impl TestList { - pub fn new(tests: Vec, order: TestListOrder) -> Self { +impl<'a> TestList<'a> { + pub fn new(tests: &'a [&'a TestDescAndFn], order: TestListOrder) -> Self { Self { tests, order } } } diff --git a/src/doc/rustc-dev-guide/src/test-implementation.md b/src/doc/rustc-dev-guide/src/test-implementation.md index 14a66e002dba9..707ba040609e0 100644 --- a/src/doc/rustc-dev-guide/src/test-implementation.md +++ b/src/doc/rustc-dev-guide/src/test-implementation.md @@ -90,7 +90,7 @@ something with them using [`rustc_ast`][ast] generates a module like so: #[main] pub fn main() { extern crate test; - test::test_main_static(&[&path::to::test1, /*...*/]); + test::test_main_env_args(&[&path::to::test1, /*...*/]); } ``` @@ -98,7 +98,7 @@ Here `path::to::test1` is a constant of type [`test::TestDescAndFn`][tdaf]. While this transformation is simple, it gives us a lot of insight into how tests are actually run. The tests are aggregated into an array and passed to -a test runner called `test_main_static`. We'll come back to exactly what +a test runner called `test_main_env_args`. We'll come back to exactly what [`TestDescAndFn`][tdaf] is, but for now, the key takeaway is that there is a crate called [`test`][test] that is part of Rust core, that implements all of the runtime for testing. [`test`][test]'s interface is unstable, so the only stable way @@ -124,7 +124,7 @@ configuration information as well. `test` encodes this configuration data into a `struct` called [`TestDesc`]. For each test function in a crate, [`rustc_ast`][rustc_ast] will parse its attributes and generate a [`TestDesc`] instance. It then combines the [`TestDesc`] and test function into the -predictably named [`TestDescAndFn`][tdaf] `struct`, that [`test_main_static`] +predictably named [`TestDescAndFn`][tdaf] `struct`, that [`test_main_env_args`] operates on. For a given test, the generated [`TestDescAndFn`][tdaf] instance looks like so: @@ -161,4 +161,4 @@ $ rustc my_mod.rs -Z unpretty=hir [Symbol]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html [test]: https://doc.rust-lang.org/test/index.html [tdaf]: https://doc.rust-lang.org/test/struct.TestDescAndFn.html -[`test_main_static`]: https://doc.rust-lang.org/test/fn.test_main_static.html +[`test_main_env_args`]: https://doc.rust-lang.org/test/fn.test_main_env_args.html diff --git a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md index 1ad81f264cbf4..3b927a573e30c 100644 --- a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md @@ -49,10 +49,10 @@ disabled as needed with `-C target-feature=(+/-)`. In general, the following four combinations are possible: -- VFPv3-D16, target feature `+vfp3` and `-d32` -- VFPv3-D32, target feature `+vfp3` and `+d32` -- VFPv4-D16, target feature `+vfp4` and `-d32` -- VFPv4-D32, target feature `+vfp4` and `+d32` +- VFPv3-D16, target default +- VFPv3-D32, target feature `+d32` +- VFPv4-D16, llvm target feature `+vfp4d16` +- VFPv4-D32, target feature `+vfp4` An Armv7-A processor may optionally include a NEON hardware unit which provides Single Instruction Multiple Data (SIMD) operations. The diff --git a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md index c40ceda7bfca0..c08841489b78b 100644 --- a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md @@ -3,18 +3,30 @@ * **Tier: 2** * **Library Support:** core and alloc (bare-metal, `#![no_std]`) -Bare-metal target for CPUs in the Armv7-R architecture family, supporting dual -ARM/Thumb mode. The `armv7r-none-eabi*` targets use Arm mode by default and the -`thumbv7r-none-eabi*` targets use Thumb mode by default. The `-eabi` targets use -a soft-float ABI and do not require an FPU, while the `-eabihf` targets use a -hard-float ABI and do require an FPU. +Bare-metal target for CPUs in the [Armv7-R] architecture family, supporting both +the [A32 (Arm) ISA][a32-isa] and [T32 (Thumb) ISA][t32-isa]. -Processors in this family include the [Arm Cortex-R4, 5, 7, and 8][cortex-r]. +The `armv7r-none-eabi*` targets use A32 (Arm) mode by default and the +`thumbv7r-none-eabi*` targets use T32 (Thumb) mode by default. + +Processors in this family include the: + +* [Arm Cortex-R4][cortex-r4] +* [Arm Cortex-R5][cortex-r5] +* [Arm Cortex-R7][cortex-r7] +* [Arm Cortex-R8][cortex-r8] See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all -`arm-none-eabi` targets. +`arm-none-eabi` targets, in particular the difference between the `eabi` and +`eabihf` ABI. -[cortex-r]: https://en.wikipedia.org/wiki/ARM_Cortex-R +[t32-isa]: https://developer.arm.com/Architectures/T32%20Instruction%20Set%20Architecture +[a32-isa]: https://developer.arm.com/Architectures/A32%20Instruction%20Set%20Architecture +[Armv7-R]: https://support.arm.com/documentation/ddi0406 +[cortex-r4]: https://developer.arm.com/Processors/Cortex-R4 +[cortex-r5]: https://developer.arm.com/Processors/Cortex-R5 +[cortex-r7]: https://developer.arm.com/Processors/Cortex-R7 +[cortex-r8]: https://developer.arm.com/Processors/Cortex-R8 ## Target maintainers @@ -31,18 +43,63 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all When using the hardfloat (`-eabibf`) targets, the minimum floating-point features assumed are those of the `vfpv3-d16`, which includes single- and -double-precision, with 16 double-precision registers. This floating-point unit -appears in Cortex-R4F and Cortex-R5F processors. See [VFP in the Cortex-R +double-precision, with 16 double-precision registers. See [VFP in the Cortex-R processors][vfp] for more details on the possible FPU variants. If your processor supports a different set of floating-point features than the -default expectations of `vfpv3-d16`, then these should also be enabled or -disabled as needed with `-C target-feature=(+/-)`. +default expectations of `vfpv3-d16` (for example, if it only supports +single-precision and not double-precision), then those features should also be +enabled or disabled as needed with `-C target-feature=(+/-)` (or using a custom +JSON target). If you are removing features then you will also need to recompile +the Rust Standard Library from source (e.g. using `-Zbuild-std=core`). -[endianness]: https://developer.arm.com/documentation/den0042/a/Coding-for-Cortex-R-Processors/Endianness +See [the bare-metal Arm +docs](arm-none-eabi.md#target-cpu-and-target-feature-options) for details on how +to use these flags. [vfp]: https://developer.arm.com/documentation/den0042/a/Floating-Point/Floating-point-basics-and-the-IEEE-754-standard/VFP-in-the-Cortex-R-processors + +### Table of supported CPUs for `(arm|thumb)v7r-none-eabi` + +| CPU | FPU | Target CPU | Target Features | +|-----------|-----|-------------|-----------------| +| Any | No | None | None | +| Cortex-R4 | No | `cortex-r4` | None | +| Cortex-R4 | DP | `cortex-r4` | `+vfp3` | +| Cortex-R4 | SP | `cortex-r4` | `+vfp3,-fp64` | +| Cortex-R5 | No | `cortex-r5` | `-fpregs` | +| Cortex-R5 | DP | `cortex-r5` | None | +| Cortex-R5 | SP | `cortex-r5` | `-fp64` | +| Cortex-R7 | No | `cortex-r7` | `-fpregs` | +| Cortex-R7 | DP | `cortex-r7` | None | +| Cortex-R7 | SP | `cortex-r7` | `-fp64` | +| Cortex-R8 | No | `cortex-r8` | `-fpregs` | +| Cortex-R8 | DP | `cortex-r8` | None | +| Cortex-R8 | SP | `cortex-r8` | `-fp64` | + +### Table of supported CPUs for `(arm|thumb)v7r-none-eabihf` + +| CPU | FPU | Target CPU | Target Features | +|-----------|-----|-------------|-----------------| +| Any | DP | None | None | +| Any | SP | None | `-fp64` | +| Cortex-R4 | DP | `cortex-r4` | None | +| Cortex-R4 | SP | `cortex-r4` | `-fp64` | +| Cortex-R5 | DP | `cortex-r5` | None | +| Cortex-R5 | SP | `cortex-r5` | `-fp64` | +| Cortex-R7 | DP | `cortex-r7` | None | +| Cortex-R7 | SP | `cortex-r7` | `-fp64` | +| Cortex-R8 | DP | `cortex-r8` | None | +| Cortex-R8 | SP | `cortex-r8` | `-fp64` | + +
+ +Never use the `-fpregs` *target-feature* with the `(arm|thumb)v7r-none-eabi` targets +as it will cause compilation units to have different ABIs, which is unsound. + +
+ ## Start-up and Low-Level Code The [Rust Embedded Devices Working Group Arm Team] maintain the [`aarch32-cpu`] diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 80affbd132bfe..0ecbc5927cf48 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -423,16 +423,36 @@ pub(crate) fn run_tests( // `running 0 tests...`. if ran_edition_tests == 0 || !standalone_tests.is_empty() { standalone_tests.sort_by(|a, b| a.desc.name.as_slice().cmp(b.desc.name.as_slice())); - test::test_main_with_exit_callback(&test_args, standalone_tests, None, || { - let times = times.times_in_secs(); - // We ensure temp dir destructor is called. - std::mem::drop(temp_dir.take()); - if let Some((total_time, compilation_time)) = times { - test::print_merged_doctests_times(&test_args, total_time, compilation_time); + cfg_select! { + bootstrap => { + test::test_main_with_exit_callback(&test_args, standalone_tests, None, || { + let times = times.times_in_secs(); + // We ensure temp dir destructor is called. + std::mem::drop(temp_dir.take()); + if let Some((total_time, compilation_time)) = times { + test::print_merged_doctests_times(&test_args, total_time, compilation_time); + } + }); + } + _ => { + // We need a vector of `&TestDescAndFn`. + let standalone_test_refs = &standalone_tests.iter().collect::>(); + let exit = test::test_main(&test_args, standalone_test_refs); + let times = times.times_in_secs(); + // We ensure temp dir destructor is called. + std::mem::drop(standalone_tests); + std::mem::drop(temp_dir.take()); + if let Some((total_time, compilation_time)) = times { + test::print_merged_doctests_times(&test_args, total_time, compilation_time); + } + // Fall through on success, the caller may want to do more stuff. + if exit != std::process::ExitCode::SUCCESS { + exit.exit_process(); + } } - }); + } } else { - // If the first condition branch exited successfully, `test_main_with_exit_callback` will + // If the first condition branch exited successfully, it will // not exit the process. So to prevent displaying the times twice, we put it behind an // `else` condition. if let Some((total_time, compilation_time)) = times.times_in_secs() { @@ -442,7 +462,7 @@ pub(crate) fn run_tests( // We ensure temp dir destructor is called. std::mem::drop(temp_dir); if nb_errors != 0 { - std::process::exit(test::ERROR_EXIT_CODE); + std::process::exit(test::ERROR_EXIT_CODE.into()); } } @@ -557,13 +577,13 @@ fn wrapped_rustc_command(rustc_wrappers: &[PathBuf], rustc_binary: &Path) -> Com /// (if multiple doctests are merged), `main` function, /// and everything needed to calculate the compiler's command-line arguments. /// The `# ` prefix on boring lines has also been stripped. -pub(crate) struct RunnableDocTest { +pub(crate) struct RunnableDocTest<'a> { /// In a merged test, this is the code for the "bundle" that contains the actual doctests. /// In a standalone test this is just the regular test code. full_test_code: String, full_test_line_offset: usize, - test_opts: IndividualTestOptions, - global_opts: GlobalTestOptions, + test_opts: &'a IndividualTestOptions, + global_opts: &'a GlobalTestOptions, langstr: LangString, line: usize, edition: Edition, @@ -573,7 +593,7 @@ pub(crate) struct RunnableDocTest { merged_test_runner_code: Option, } -impl RunnableDocTest { +impl RunnableDocTest<'_> { fn path_for_merged_doctest_bundle(&self) -> PathBuf { self.test_opts.outdir.path().join(format!("doctest_bundle_{}.rs", self.edition)) } @@ -592,7 +612,7 @@ impl RunnableDocTest { /// /// Returns a tuple containing the `Duration` of the compilation and the `Result` of the test. fn run_test( - doctest: RunnableDocTest, + doctest: RunnableDocTest<'_>, rustdoc_options: &RustdocOptions, supports_color: bool, report_unused_externs: impl Fn(UnusedExterns), @@ -1155,26 +1175,38 @@ fn generate_test_desc_and_fn( no_run: scraped_test.no_run(&rustdoc_options), test_type: test::TestType::DocTest, }, + #[cfg(bootstrap)] testfn: test::DynTestFn(Box::new(move || { doctest_run_fn( - rustdoc_test_options, - opts, - test, - scraped_test, - rustdoc_options, - unused_externs, + &rustdoc_test_options, + &opts, + &test, + &scraped_test, + &rustdoc_options, + &unused_externs, + ) + })), + #[cfg(not(bootstrap))] + testfn: test::DynTestFn(Arc::new(move || { + doctest_run_fn( + &rustdoc_test_options, + &opts, + &test, + &scraped_test, + &rustdoc_options, + &unused_externs, ) })), } } fn doctest_run_fn( - test_opts: IndividualTestOptions, - global_opts: GlobalTestOptions, - doctest: DocTestBuilder, - scraped_test: ScrapedDocTest, - rustdoc_options: Arc, - unused_externs: Arc>>, + test_opts: &IndividualTestOptions, + global_opts: &GlobalTestOptions, + doctest: &DocTestBuilder, + scraped_test: &ScrapedDocTest, + rustdoc_options: &RustdocOptions, + unused_externs: &Mutex>, ) -> Result<(), String> { let report_unused_externs = |uext| { unused_externs.lock().unwrap().push(uext); diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index ff9397ea2460b..43ed48aad95d4 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -14,6 +14,7 @@ use crate::html::markdown::{Ignore, LangString}; pub(crate) struct DocTestRunner { crate_attrs: FxIndexSet, global_crate_attrs: FxIndexSet, + /// A comma-separated list of references to test descriptors. ids: String, output: String, output_merged_tests: String, @@ -54,7 +55,7 @@ impl DocTestRunner { } } self.ids.push_str(&format!( - "tests.push({}::TEST);\n", + "&{}::TEST,\n", generate_mergeable_doctest( doctest, scraped_test, @@ -166,18 +167,14 @@ mod __doctest_mod {{ #[rustc_main] fn main() -> std::process::ExitCode {{ -let tests = {{ - let mut tests = Vec::with_capacity({nb_tests}); - {ids} - tests -}}; +let tests = &[{ids}]; let test_args = &[{test_args}]; const ENV_BIN: &'static str = \"RUSTDOC_DOCTEST_BIN_PATH\"; if let Ok(binary) = std::env::var(ENV_BIN) {{ let _ = crate::__doctest_mod::BINARY_PATH.set(binary.into()); unsafe {{ std::env::remove_var(ENV_BIN); }} - return std::process::Termination::report(test::test_main(test_args, tests, None)); + return test::test_main(test_args, tests); }} else if let Ok(nb_test) = std::env::var(__doctest_mod::RUN_OPTION) {{ if let Ok(nb_test) = nb_test.parse::() {{ if let Some(test) = tests.get(nb_test) {{ @@ -191,9 +188,8 @@ if let Ok(binary) = std::env::var(ENV_BIN) {{ eprintln!(\"WARNING: No rustdoc doctest environment variable provided so doctests will be run in \ the same process\"); -std::process::Termination::report(test::test_main(test_args, tests, None)) +test::test_main(test_args, tests) }}", - nb_tests = self.nb_tests, output = self.output_merged_tests, ids = self.ids, ) @@ -201,8 +197,8 @@ std::process::Termination::report(test::test_main(test_args, tests, None)) let runnable_test = RunnableDocTest { full_test_code: format!("{code_prefix}{code}", code = self.output), full_test_line_offset: 0, - test_opts: test_options, - global_opts: opts.clone(), + test_opts: &test_options, + global_opts: opts, langstr: LangString::default(), line: 0, edition, @@ -261,7 +257,7 @@ fn main() {returns_result} {{ output_merged_tests, " mod {test_id} {{ -pub const TEST: test::TestDescAndFn = test::TestDescAndFn::new_doctest( +pub static TEST: test::TestDescAndFn = test::TestDescAndFn::new_doctest( {test_name:?}, {ignore}, {file:?}, {line}, {no_run}, {should_panic}, test::StaticTestFn( || {{{runner}}}, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 6c93099b74547..20e992945c917 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -1,4 +1,5 @@ // tidy-alphabetical-start +#![cfg_attr(not(bootstrap), feature(exitcode_exit_method))] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/", html_playground_url = "https://play.rust-lang.org/" diff --git a/src/librustdoc/passes/lint/bare_urls.rs b/src/librustdoc/passes/lint/bare_urls.rs index 287a1f50b5aa1..0928980e390a8 100644 --- a/src/librustdoc/passes/lint/bare_urls.rs +++ b/src/librustdoc/passes/lint/bare_urls.rs @@ -2,14 +2,13 @@ //! Suggests wrapping the link with angle brackets: `Go to .` to linkify it. use core::ops::Range; +use std::mem; use std::sync::LazyLock; use regex::Regex; use rustc_errors::{Applicability, DiagDecorator}; use rustc_hir::HirId; -use rustc_resolve::rustdoc::pulldown_cmark::{ - DefaultBrokenLinkCallback, Event, Tag, TextMergeWithOffset, -}; +use rustc_resolve::rustdoc::pulldown_cmark::{Event, Parser, Tag}; use rustc_resolve::rustdoc::source_span_for_markdown_range; use tracing::trace; @@ -56,20 +55,21 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & ); }; - // pulldown-cmark can split a URL into multiple `Text` events while processing - // characters such as `_` according to CommonMark's emphasis rules. - // `TextMergeWithOffset` merges these events so we can check the complete URL. - let mut p = TextMergeWithOffset::::new_ext(dox, main_body_opts()); + let mut p = Parser::new_ext(dox, main_body_opts()).into_offset_iter(); while let Some((event, range)) = p.next() { match event { Event::Text(s) => find_raw_urls(cx, dox, &s, range, &report_diag), // We don't want to check the text inside code blocks or links. Event::Start(tag @ (Tag::CodeBlock(_) | Tag::Link { .. })) => { - let end = tag.to_end(); for (event, _) in p.by_ref() { - if matches!(event, Event::End(tag) if tag == end) { - break; + match event { + Event::End(end) + if mem::discriminant(&end) == mem::discriminant(&tag.to_end()) => + { + break; + } + _ => {} } } } @@ -83,12 +83,7 @@ static URL_REGEX: LazyLock = LazyLock::new(|| { r"https?://", // url scheme r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", // one or more subdomains r"[a-zA-Z]{2,63}", // root domain - // Match URL characters and balanced parenthesized segments, without - // consuming a trailing `)` that belongs to the surrounding prose. - r"\b(?:", - r"[-a-zA-Z0-9@:%_\+.~#?&/=]", - r"|\([-a-zA-Z0-9@:%_\+.~#?&/=]*\)", - r")*", + r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)", // optional query or url fragments )) .expect("failed to build regex") }); diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 6eeef786b9a1e..d4ee85dade5ad 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -2361,9 +2361,9 @@ fn test_item_names(tcx: TyCtxt<'_>, module: LocalModId) -> Vec { Entry::Vacant(entry) => { let mut names = Vec::new(); for id in tcx.hir_module_free_items(module) { - if tcx.def_kind(id.owner_id) == DefKind::Const + if matches!(tcx.def_kind(id.owner_id), DefKind::Static { .. }) && let item = tcx.hir_item(id) - && let ItemKind::Const(ident, _generics, ty, _body) = item.kind + && let ItemKind::Static(_mut, ident, ty, _body) = item.kind && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind // We could also check for the type name `test::TestDescAndFn` && let Res::Def(DefKind::Struct, _) = path.res diff --git a/tests/crashes/156288.rs b/tests/crashes/156288.rs deleted file mode 100644 index b745cfe063dda..0000000000000 --- a/tests/crashes/156288.rs +++ /dev/null @@ -1,3 +0,0 @@ -//@ known-bug: #156288 -#[warn(rust_2021_incompatible_closure_captures)] -const _: () = |b| move || b; diff --git a/tests/pretty/tests-are-sorted.pp b/tests/pretty/tests-are-sorted.pp index 43f9838e68ce9..0002189b48c04 100644 --- a/tests/pretty/tests-are-sorted.pp +++ b/tests/pretty/tests-are-sorted.pp @@ -11,7 +11,7 @@ extern crate test; #[rustc_test_marker = "m_test"] #[doc(hidden)] -pub const m_test: test::TestDescAndFn = +pub static m_test: test::TestDescAndFn = test::TestDescAndFn { desc: test::TestDesc { name: test::StaticTestName("m_test"), @@ -35,7 +35,7 @@ extern crate test; #[rustc_test_marker = "z_test"] #[doc(hidden)] -pub const z_test: test::TestDescAndFn = +pub static z_test: test::TestDescAndFn = test::TestDescAndFn { desc: test::TestDesc { name: test::StaticTestName("z_test"), @@ -60,7 +60,7 @@ extern crate test; #[rustc_test_marker = "a_test"] #[doc(hidden)] -pub const a_test: test::TestDescAndFn = +pub static a_test: test::TestDescAndFn = test::TestDescAndFn { desc: test::TestDesc { name: test::StaticTestName("a_test"), @@ -85,5 +85,5 @@ #[doc(hidden)] pub fn main() -> () { extern crate test; - test::test_main_static(&[&a_test, &m_test, &z_test]) + test::test_main_env_args(&[&a_test, &m_test, &z_test]) } diff --git a/tests/rustdoc-html/bad-codeblock-syntax.rs b/tests/rustdoc-html/codeblock/bad-codeblock-syntax.rs similarity index 100% rename from tests/rustdoc-html/bad-codeblock-syntax.rs rename to tests/rustdoc-html/codeblock/bad-codeblock-syntax.rs diff --git a/tests/rustdoc-html/codeblock-title.rs b/tests/rustdoc-html/codeblock/codeblock-title.rs similarity index 100% rename from tests/rustdoc-html/codeblock-title.rs rename to tests/rustdoc-html/codeblock/codeblock-title.rs diff --git a/tests/rustdoc-html/custom_code_classes.rs b/tests/rustdoc-html/codeblock/custom_code_classes.rs similarity index 100% rename from tests/rustdoc-html/custom_code_classes.rs rename to tests/rustdoc-html/codeblock/custom_code_classes.rs diff --git a/tests/rustdoc-html/doctest/doctest-escape-boring-41783.codeblock.html b/tests/rustdoc-html/codeblock/doctest-escape-boring-41783.codeblock.html similarity index 100% rename from tests/rustdoc-html/doctest/doctest-escape-boring-41783.codeblock.html rename to tests/rustdoc-html/codeblock/doctest-escape-boring-41783.codeblock.html diff --git a/tests/rustdoc-html/doctest/doctest-escape-boring-41783.rs b/tests/rustdoc-html/codeblock/doctest-escape-boring-41783.rs similarity index 100% rename from tests/rustdoc-html/doctest/doctest-escape-boring-41783.rs rename to tests/rustdoc-html/codeblock/doctest-escape-boring-41783.rs diff --git a/tests/rustdoc-html/doctest/editions.rs b/tests/rustdoc-html/codeblock/editions.rs similarity index 100% rename from tests/rustdoc-html/doctest/editions.rs rename to tests/rustdoc-html/codeblock/editions.rs diff --git a/tests/rustdoc-html/hidden-line.rs b/tests/rustdoc-html/codeblock/hidden-line.rs similarity index 100% rename from tests/rustdoc-html/hidden-line.rs rename to tests/rustdoc-html/codeblock/hidden-line.rs diff --git a/tests/rustdoc-html/doctest/ignore-sometimes.rs b/tests/rustdoc-html/codeblock/ignore-sometimes.rs similarity index 100% rename from tests/rustdoc-html/doctest/ignore-sometimes.rs rename to tests/rustdoc-html/codeblock/ignore-sometimes.rs diff --git a/tests/rustdoc-html/playground-arg.rs b/tests/rustdoc-html/codeblock/playground-arg.rs similarity index 100% rename from tests/rustdoc-html/playground-arg.rs rename to tests/rustdoc-html/codeblock/playground-arg.rs diff --git a/tests/rustdoc-html/playground-empty.rs b/tests/rustdoc-html/codeblock/playground-empty.rs similarity index 100% rename from tests/rustdoc-html/playground-empty.rs rename to tests/rustdoc-html/codeblock/playground-empty.rs diff --git a/tests/rustdoc-html/playground-none.rs b/tests/rustdoc-html/codeblock/playground-none.rs similarity index 100% rename from tests/rustdoc-html/playground-none.rs rename to tests/rustdoc-html/codeblock/playground-none.rs diff --git a/tests/rustdoc-html/playground-syntax-error.rs b/tests/rustdoc-html/codeblock/playground-syntax-error.rs similarity index 100% rename from tests/rustdoc-html/playground-syntax-error.rs rename to tests/rustdoc-html/codeblock/playground-syntax-error.rs diff --git a/tests/rustdoc-html/playground.rs b/tests/rustdoc-html/codeblock/playground.rs similarity index 100% rename from tests/rustdoc-html/playground.rs rename to tests/rustdoc-html/codeblock/playground.rs diff --git a/tests/rustdoc-html/short-docblock-codeblock.rs b/tests/rustdoc-html/codeblock/short-docblock-codeblock.rs similarity index 100% rename from tests/rustdoc-html/short-docblock-codeblock.rs rename to tests/rustdoc-html/codeblock/short-docblock-codeblock.rs diff --git a/tests/rustdoc-html/summary-codeblock-31899.rs b/tests/rustdoc-html/codeblock/summary-codeblock-31899.rs similarity index 100% rename from tests/rustdoc-html/summary-codeblock-31899.rs rename to tests/rustdoc-html/codeblock/summary-codeblock-31899.rs diff --git a/tests/rustdoc-html/unindent.md b/tests/rustdoc-html/codeblock/unindent.md similarity index 100% rename from tests/rustdoc-html/unindent.md rename to tests/rustdoc-html/codeblock/unindent.md diff --git a/tests/rustdoc-html/unindent.rs b/tests/rustdoc-html/codeblock/unindent.rs similarity index 100% rename from tests/rustdoc-html/unindent.rs rename to tests/rustdoc-html/codeblock/unindent.rs diff --git a/tests/rustdoc-html/footnote-definition-without-blank-line-100638.rs b/tests/rustdoc-html/footnote/footnote-definition-without-blank-line-100638.rs similarity index 100% rename from tests/rustdoc-html/footnote-definition-without-blank-line-100638.rs rename to tests/rustdoc-html/footnote/footnote-definition-without-blank-line-100638.rs diff --git a/tests/rustdoc-html/footnote-ids.rs b/tests/rustdoc-html/footnote/footnote-ids.rs similarity index 100% rename from tests/rustdoc-html/footnote-ids.rs rename to tests/rustdoc-html/footnote/footnote-ids.rs diff --git a/tests/rustdoc-html/footnote-in-summary.rs b/tests/rustdoc-html/footnote/footnote-in-summary.rs similarity index 100% rename from tests/rustdoc-html/footnote-in-summary.rs rename to tests/rustdoc-html/footnote/footnote-in-summary.rs diff --git a/tests/rustdoc-html/footnote-reference-ids.rs b/tests/rustdoc-html/footnote/footnote-reference-ids.rs similarity index 100% rename from tests/rustdoc-html/footnote-reference-ids.rs rename to tests/rustdoc-html/footnote/footnote-reference-ids.rs diff --git a/tests/rustdoc-html/footnote-reference-in-footnote-def.rs b/tests/rustdoc-html/footnote/footnote-reference-in-footnote-def.rs similarity index 100% rename from tests/rustdoc-html/footnote-reference-in-footnote-def.rs rename to tests/rustdoc-html/footnote/footnote-reference-in-footnote-def.rs diff --git a/tests/rustdoc-html/display-hidden-items.rs b/tests/rustdoc-html/hidden/display-hidden-items.rs similarity index 100% rename from tests/rustdoc-html/display-hidden-items.rs rename to tests/rustdoc-html/hidden/display-hidden-items.rs diff --git a/tests/rustdoc-html/document-hidden-items-15347.rs b/tests/rustdoc-html/hidden/document-hidden-items-15347.rs similarity index 100% rename from tests/rustdoc-html/document-hidden-items-15347.rs rename to tests/rustdoc-html/hidden/document-hidden-items-15347.rs diff --git a/tests/rustdoc-html/hidden-trait-methods-with-document-hidden-items.rs b/tests/rustdoc-html/hidden/hidden-trait-methods-with-document-hidden-items.rs similarity index 100% rename from tests/rustdoc-html/hidden-trait-methods-with-document-hidden-items.rs rename to tests/rustdoc-html/hidden/hidden-trait-methods-with-document-hidden-items.rs diff --git a/tests/rustdoc-html/auxiliary/generated_macro.rs b/tests/rustdoc-html/macro/auxiliary/generated_macro.rs similarity index 100% rename from tests/rustdoc-html/auxiliary/generated_macro.rs rename to tests/rustdoc-html/macro/auxiliary/generated_macro.rs diff --git a/tests/rustdoc-html/generated_macro.rs b/tests/rustdoc-html/macro/generated_macro.rs similarity index 100% rename from tests/rustdoc-html/generated_macro.rs rename to tests/rustdoc-html/macro/generated_macro.rs diff --git a/tests/rustdoc-html/auxiliary/inline-default-methods.rs b/tests/rustdoc-html/reexport/auxiliary/inline-default-methods.rs similarity index 100% rename from tests/rustdoc-html/auxiliary/inline-default-methods.rs rename to tests/rustdoc-html/reexport/auxiliary/inline-default-methods.rs diff --git a/tests/rustdoc-html/auxiliary/issue-61592.rs b/tests/rustdoc-html/reexport/auxiliary/issue-61592.rs similarity index 100% rename from tests/rustdoc-html/auxiliary/issue-61592.rs rename to tests/rustdoc-html/reexport/auxiliary/issue-61592.rs diff --git a/tests/rustdoc-html/infinite-redirection.rs b/tests/rustdoc-html/reexport/infinite-redirection.rs similarity index 100% rename from tests/rustdoc-html/infinite-redirection.rs rename to tests/rustdoc-html/reexport/infinite-redirection.rs diff --git a/tests/rustdoc-html/inline-default-methods.rs b/tests/rustdoc-html/reexport/inline-default-methods.rs similarity index 100% rename from tests/rustdoc-html/inline-default-methods.rs rename to tests/rustdoc-html/reexport/inline-default-methods.rs diff --git a/tests/rustdoc-html/inline-rename-34473.rs b/tests/rustdoc-html/reexport/inline-rename-34473.rs similarity index 100% rename from tests/rustdoc-html/inline-rename-34473.rs rename to tests/rustdoc-html/reexport/inline-rename-34473.rs diff --git a/tests/rustdoc-html/macro-reexport-inline.rs b/tests/rustdoc-html/reexport/macro-reexport-inline.rs similarity index 100% rename from tests/rustdoc-html/macro-reexport-inline.rs rename to tests/rustdoc-html/reexport/macro-reexport-inline.rs diff --git a/tests/rustdoc-html/pub-use-loop-107350.rs b/tests/rustdoc-html/reexport/pub-use-loop-107350.rs similarity index 100% rename from tests/rustdoc-html/pub-use-loop-107350.rs rename to tests/rustdoc-html/reexport/pub-use-loop-107350.rs diff --git a/tests/rustdoc-html/pub-use-root-path-95873.rs b/tests/rustdoc-html/reexport/pub-use-root-path-95873.rs similarity index 100% rename from tests/rustdoc-html/pub-use-root-path-95873.rs rename to tests/rustdoc-html/reexport/pub-use-root-path-95873.rs diff --git a/tests/rustdoc-html/underscore-import-61592.rs b/tests/rustdoc-html/reexport/underscore-import-61592.rs similarity index 100% rename from tests/rustdoc-html/underscore-import-61592.rs rename to tests/rustdoc-html/reexport/underscore-import-61592.rs diff --git a/tests/rustdoc-html/use-attr.rs b/tests/rustdoc-html/reexport/use-attr.rs similarity index 100% rename from tests/rustdoc-html/use-attr.rs rename to tests/rustdoc-html/reexport/use-attr.rs diff --git a/tests/rustdoc-html/auxiliary/cross_crate_generic_typedef.rs b/tests/rustdoc-html/typedef/auxiliary/cross_crate_generic_typedef.rs similarity index 100% rename from tests/rustdoc-html/auxiliary/cross_crate_generic_typedef.rs rename to tests/rustdoc-html/typedef/auxiliary/cross_crate_generic_typedef.rs diff --git a/tests/rustdoc-html/type-alias/auxiliary/parent-crate-115718.rs b/tests/rustdoc-html/typedef/auxiliary/parent-crate-115718.rs similarity index 100% rename from tests/rustdoc-html/type-alias/auxiliary/parent-crate-115718.rs rename to tests/rustdoc-html/typedef/auxiliary/parent-crate-115718.rs diff --git a/tests/rustdoc-html/type-alias/cross-crate-115718.rs b/tests/rustdoc-html/typedef/cross-crate-115718.rs similarity index 100% rename from tests/rustdoc-html/type-alias/cross-crate-115718.rs rename to tests/rustdoc-html/typedef/cross-crate-115718.rs diff --git a/tests/rustdoc-html/type-alias/deref-32077.rs b/tests/rustdoc-html/typedef/deref-32077.rs similarity index 100% rename from tests/rustdoc-html/type-alias/deref-32077.rs rename to tests/rustdoc-html/typedef/deref-32077.rs diff --git a/tests/rustdoc-html/type-alias/impl_trait_in_assoc_type.rs b/tests/rustdoc-html/typedef/impl_trait_in_assoc_type.rs similarity index 100% rename from tests/rustdoc-html/type-alias/impl_trait_in_assoc_type.rs rename to tests/rustdoc-html/typedef/impl_trait_in_assoc_type.rs diff --git a/tests/rustdoc-html/type-alias/primitive-local-link-121106.rs b/tests/rustdoc-html/typedef/primitive-local-link-121106.rs similarity index 100% rename from tests/rustdoc-html/type-alias/primitive-local-link-121106.rs rename to tests/rustdoc-html/typedef/primitive-local-link-121106.rs diff --git a/tests/rustdoc-html/type-alias/repr.rs b/tests/rustdoc-html/typedef/repr.rs similarity index 100% rename from tests/rustdoc-html/type-alias/repr.rs rename to tests/rustdoc-html/typedef/repr.rs diff --git a/tests/rustdoc-html/type-alias/same-crate-115718.rs b/tests/rustdoc-html/typedef/same-crate-115718.rs similarity index 100% rename from tests/rustdoc-html/type-alias/same-crate-115718.rs rename to tests/rustdoc-html/typedef/same-crate-115718.rs diff --git a/tests/rustdoc-html/typedef-inner-variants-document-hidden.rs b/tests/rustdoc-html/typedef/typedef-inner-variants-document-hidden.rs similarity index 100% rename from tests/rustdoc-html/typedef-inner-variants-document-hidden.rs rename to tests/rustdoc-html/typedef/typedef-inner-variants-document-hidden.rs diff --git a/tests/rustdoc-html/typedef-inner-variants-lazy_type_alias.rs b/tests/rustdoc-html/typedef/typedef-inner-variants-lazy_type_alias.rs similarity index 100% rename from tests/rustdoc-html/typedef-inner-variants-lazy_type_alias.rs rename to tests/rustdoc-html/typedef/typedef-inner-variants-lazy_type_alias.rs diff --git a/tests/rustdoc-html/typedef-inner-variants.rs b/tests/rustdoc-html/typedef/typedef-inner-variants.rs similarity index 100% rename from tests/rustdoc-html/typedef-inner-variants.rs rename to tests/rustdoc-html/typedef/typedef-inner-variants.rs diff --git a/tests/rustdoc-html/typedef.rs b/tests/rustdoc-html/typedef/typedef.rs similarity index 100% rename from tests/rustdoc-html/typedef.rs rename to tests/rustdoc-html/typedef/typedef.rs diff --git a/tests/rustdoc-html/union-fields-html.rs b/tests/rustdoc-html/union/union-fields-html.rs similarity index 100% rename from tests/rustdoc-html/union-fields-html.rs rename to tests/rustdoc-html/union/union-fields-html.rs diff --git a/tests/rustdoc-html/union.rs b/tests/rustdoc-html/union/union.rs similarity index 100% rename from tests/rustdoc-html/union.rs rename to tests/rustdoc-html/union/union.rs diff --git a/tests/rustdoc-ui/lints/bare-urls.fixed b/tests/rustdoc-ui/lints/bare-urls.fixed index b18aae11c77cf..996214b5ff14f 100644 --- a/tests/rustdoc-ui/lints/bare-urls.fixed +++ b/tests/rustdoc-ui/lints/bare-urls.fixed @@ -92,7 +92,3 @@ pub fn trailing_period() {} /// ] //~^ ERROR this URL is not a hyperlink pub fn lint_with_brackets() {} - -/// See -//~^ ERROR this URL is not a hyperlink -pub fn hippo() {} diff --git a/tests/rustdoc-ui/lints/bare-urls.rs b/tests/rustdoc-ui/lints/bare-urls.rs index fb39ec6b6ccbd..9b4fe68e00322 100644 --- a/tests/rustdoc-ui/lints/bare-urls.rs +++ b/tests/rustdoc-ui/lints/bare-urls.rs @@ -92,7 +92,3 @@ pub fn trailing_period() {} /// https://bloob.blob] //~^ ERROR this URL is not a hyperlink pub fn lint_with_brackets() {} - -/// See https://en.wikipedia.org/wiki/Rust_(programming_language) -//~^ ERROR this URL is not a hyperlink -pub fn hippo() {} diff --git a/tests/rustdoc-ui/lints/bare-urls.stderr b/tests/rustdoc-ui/lints/bare-urls.stderr index a3a291e8e4bca..05ddd2ed42ab1 100644 --- a/tests/rustdoc-ui/lints/bare-urls.stderr +++ b/tests/rustdoc-ui/lints/bare-urls.stderr @@ -364,17 +364,5 @@ help: use an automatic link instead LL | /// ] | + + -error: this URL is not a hyperlink - --> $DIR/bare-urls.rs:96:9 - | -LL | /// See https://en.wikipedia.org/wiki/Rust_(programming_language) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: bare URLs are not automatically turned into clickable links -help: use an automatic link instead - | -LL | /// See - | + + - -error: aborting due to 31 previous errors +error: aborting due to 30 previous errors diff --git a/tests/ui/asm/arm-high-dregs.baseline.stderr b/tests/ui/asm/arm-high-dregs.baseline.stderr new file mode 100644 index 0000000000000..f9d9fea799135 --- /dev/null +++ b/tests/ui/asm/arm-high-dregs.baseline.stderr @@ -0,0 +1,8 @@ +error: register class `dreg` requires at least one of the following target features: d32, neon + --> $DIR/arm-high-dregs.rs:23:42 + | +LL | asm!("vmov.f64 d16, d0", in("d0") x, out("d16") y); + | ^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/asm/arm-high-dregs.rs b/tests/ui/asm/arm-high-dregs.rs new file mode 100644 index 0000000000000..b12562221811d --- /dev/null +++ b/tests/ui/asm/arm-high-dregs.rs @@ -0,0 +1,25 @@ +//@ add-minicore +//@ only-arm +//@ only-eabihf +//@ ignore-backends: gcc +//@ revisions: baseline target-cpu +//@ [baseline] check-fail +//@ [target-cpu] check-pass +//@ [target-cpu] compile-flags: -Ctarget-cpu=cortex-a5 + +// As well as the error message, this also tests that d32 is not enabled by default on arm hardfloat +// targets and that it can be re-enabled by target-cpu. + +#![feature(f16, no_core)] +#![crate_type = "rlib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[no_mangle] +pub unsafe fn high(x: f64) { + let y: f64; + asm!("vmov.f64 d16, d0", in("d0") x, out("d16") y); + //[baseline]~^ ERROR register class `dreg` requires at least one of the following target features: d32, neon +} diff --git a/tests/ui/closures/2229_closure_analysis/incompatible-captures-without-drop-location.rs b/tests/ui/closures/2229_closure_analysis/incompatible-captures-without-drop-location.rs new file mode 100644 index 0000000000000..893c9c9f1e356 --- /dev/null +++ b/tests/ui/closures/2229_closure_analysis/incompatible-captures-without-drop-location.rs @@ -0,0 +1,22 @@ +//@ edition:2018 + +#[warn(rust_2021_incompatible_closure_captures)] +enum Functions { + Square = |b| move || format!("{b}"), + //~^ ERROR mismatched types +} + +#[warn(rust_2021_incompatible_closure_captures)] +static _static: () = |b| move || b; +//~^ ERROR mismatched types + +fn main() { + #[warn(rust_2021_incompatible_closure_captures)] + const _: () = |b| move || b; + //~^ ERROR mismatched types + + #[warn(rust_2021_incompatible_closure_captures)] + let _: () = |b| move || b; + //~^ ERROR mismatched types + //~| WARN changes to closure capture in Rust 2021 will affect drop order +} diff --git a/tests/ui/closures/2229_closure_analysis/incompatible-captures-without-drop-location.stderr b/tests/ui/closures/2229_closure_analysis/incompatible-captures-without-drop-location.stderr new file mode 100644 index 0000000000000..9798f783a4f1b --- /dev/null +++ b/tests/ui/closures/2229_closure_analysis/incompatible-captures-without-drop-location.stderr @@ -0,0 +1,66 @@ +error[E0308]: mismatched types + --> $DIR/incompatible-captures-without-drop-location.rs:5:14 + | +LL | Square = |b| move || format!("{b}"), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `isize`, found closure + | + = note: expected type `isize` + found closure `{closure@$DIR/incompatible-captures-without-drop-location.rs:5:14: 5:17}` + = note: enum variant discriminant can only be of a primitive type compatible with the enum's `repr` + +error[E0308]: mismatched types + --> $DIR/incompatible-captures-without-drop-location.rs:10:22 + | +LL | static _static: () = |b| move || b; + | -- ^^^^^^^^^^^^^ expected `()`, found closure + | | + | expected because of the type of the static + | + = note: expected unit type `()` + found closure `{closure@$DIR/incompatible-captures-without-drop-location.rs:10:22: 10:25}` + +error[E0308]: mismatched types + --> $DIR/incompatible-captures-without-drop-location.rs:19:17 + | +LL | let _: () = |b| move || b; + | -- ^^^^^^^^^^^^^ expected `()`, found closure + | | + | expected due to this + | + = note: expected unit type `()` + found closure `{closure@$DIR/incompatible-captures-without-drop-location.rs:19:17: 19:20}` + +warning: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/incompatible-captures-without-drop-location.rs:19:21 + | +LL | let _: () = |b| move || b; + | ^^^^^^^ - in Rust 2018, this causes the closure to capture `b`, but in Rust 2021, it has no effect +... +LL | } + | - in Rust 2018, `b` is dropped here along with the closure, but in Rust 2021 `b` is not part of the closure + | + = note: for more information, see +note: the lint level is defined here + --> $DIR/incompatible-captures-without-drop-location.rs:18:12 + | +LL | #[warn(rust_2021_incompatible_closure_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a dummy let to cause `b` to be fully captured + | +LL | let _: () = |b| move || { let _ = &b; b }; + | +++++++++++++ + + +error[E0308]: mismatched types + --> $DIR/incompatible-captures-without-drop-location.rs:15:19 + | +LL | const _: () = |b| move || b; + | -- ^^^^^^^^^^^^^ expected `()`, found closure + | | + | expected because of the type of the constant + | + = note: expected unit type `()` + found closure `{closure@$DIR/incompatible-captures-without-drop-location.rs:15:19: 15:22}` + +error: aborting due to 4 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/single-impl-candidate.rs b/tests/ui/diagnostic_namespace/do_not_recommend/single-impl-candidate.rs new file mode 100644 index 0000000000000..313dca87464dd --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/single-impl-candidate.rs @@ -0,0 +1,21 @@ +//@ reference: attributes.diagnostic.do_not_recommend.intro + +pub struct Internal; + +#[diagnostic::do_not_recommend] +impl From for &str { + fn from(_: Internal) -> Self { + "" + } +} + +fn foo<'a, T>(_t: T) +where + T: Into<&'a str>, +{ +} + +fn main() { + foo(String::new()); + //~^ ERROR the trait bound `&str: From` is not satisfied +} diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/single-impl-candidate.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/single-impl-candidate.stderr new file mode 100644 index 0000000000000..a0d0e73a1172b --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/single-impl-candidate.stderr @@ -0,0 +1,22 @@ +error[E0277]: the trait bound `&str: From` is not satisfied + --> $DIR/single-impl-candidate.rs:19:9 + | +LL | foo(String::new()); + | --- ^^^^^^^^^^^^^ the trait `From` is not implemented for `&str` + | | + | required by a bound introduced by this call + | + = note: to coerce a `String` into a `&str`, use `&*` as a prefix + = note: required for `String` to implement `Into<&str>` +note: required by a bound in `foo` + --> $DIR/single-impl-candidate.rs:14:8 + | +LL | fn foo<'a, T>(_t: T) + | --- required by a bound in this function +LL | where +LL | T: Into<&'a str>, + | ^^^^^^^^^^^^^ required by this bound in `foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-const-async-unsafe-abi.rs b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-const-async-unsafe-abi.rs new file mode 100644 index 0000000000000..24feb12985d75 --- /dev/null +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-const-async-unsafe-abi.rs @@ -0,0 +1,15 @@ +// Test for issue https://github.com/rust-lang/rust/issues/140171 +// There is an order to respect for keywords before a function: +// `, const, async, unsafe, extern, ""` +// +// This test ensures the compiler is helpful about them being misplaced. +//@ edition:2018 + +extern "C" const async unsafe fn b() {} +//~^ ERROR expected `fn`, found keyword `const` +//~| NOTE expected `fn` +//~| HELP `const` must come before `extern "C"` +//~| SUGGESTION const extern "C" +//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` + +fn main() {} diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-const-async-unsafe-abi.stderr b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-const-async-unsafe-abi.stderr new file mode 100644 index 0000000000000..6a28fb9f5595c --- /dev/null +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-const-async-unsafe-abi.stderr @@ -0,0 +1,15 @@ +error: expected `fn`, found keyword `const` + --> $DIR/wrong-const-async-unsafe-abi.rs:8:12 + | +LL | extern "C" const async unsafe fn b() {} + | ^^^^^ expected `fn` + | + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` +help: `const` must come before `extern "C"` + | +LL - extern "C" const async unsafe fn b() {} +LL + const extern "C" async unsafe fn b() {} + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-const-unsafe-abi.rs b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-const-unsafe-abi.rs new file mode 100644 index 0000000000000..fb06dc310571a --- /dev/null +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-const-unsafe-abi.rs @@ -0,0 +1,15 @@ +// Test for issue https://github.com/rust-lang/rust/issues/140171 +// There is an order to respect for keywords before a function: +// `, const, async, unsafe, extern, ""` +// +// This test ensures the compiler is helpful about them being misplaced. +//@ edition:2018 + +extern "C" const unsafe fn a() {} +//~^ ERROR expected `fn`, found keyword `const` +//~| NOTE expected `fn` +//~| HELP `const` must come before `extern "C"` +//~| SUGGESTION const extern "C" +//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` + +fn main() {} diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-const-unsafe-abi.stderr b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-const-unsafe-abi.stderr new file mode 100644 index 0000000000000..97ac70f45570d --- /dev/null +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-const-unsafe-abi.stderr @@ -0,0 +1,15 @@ +error: expected `fn`, found keyword `const` + --> $DIR/wrong-const-unsafe-abi.rs:8:12 + | +LL | extern "C" const unsafe fn a() {} + | ^^^^^ expected `fn` + | + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` +help: `const` must come before `extern "C"` + | +LL - extern "C" const unsafe fn a() {} +LL + const extern "C" unsafe fn a() {} + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-pub-const-unsafe-abi.rs b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-pub-const-unsafe-abi.rs new file mode 100644 index 0000000000000..46ffeda1d40c8 --- /dev/null +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-pub-const-unsafe-abi.rs @@ -0,0 +1,16 @@ +// Test for issue https://github.com/rust-lang/rust/issues/140171 +// There is an order to respect for keywords before a function: +// `, const, async, unsafe, extern, ""` +// +// This test ensures the compiler is helpful about them being misplaced. +//@ edition:2018 + +unsafe extern "C" { static errno: i32; } + +extern "C" pub const unsafe fn c() {} +//~^ ERROR expected `fn`, found keyword `pub` +//~| NOTE expected `fn` +//~| HELP visibility `pub` must come before `extern "C"` +//~| SUGGESTION pub extern "C" + +fn main() {} diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-pub-const-unsafe-abi.stderr b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-pub-const-unsafe-abi.stderr new file mode 100644 index 0000000000000..56bc4d85a822e --- /dev/null +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-pub-const-unsafe-abi.stderr @@ -0,0 +1,14 @@ +error: expected `fn`, found keyword `pub` + --> $DIR/wrong-pub-const-unsafe-abi.rs:10:12 + | +LL | extern "C" pub const unsafe fn c() {} + | ^^^ expected `fn` + | +help: visibility `pub` must come before `extern "C"` + | +LL - extern "C" pub const unsafe fn c() {} +LL + pub extern "C" const unsafe fn c() {} + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/suggestions/auxiliary/mut_borrow_macro.rs b/tests/ui/suggestions/auxiliary/mut_borrow_macro.rs new file mode 100644 index 0000000000000..ae693d9eeddb4 --- /dev/null +++ b/tests/ui/suggestions/auxiliary/mut_borrow_macro.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! call_it { + ($f:expr) => { + (0..100).for_each(&$f) + }; +} diff --git a/tests/ui/suggestions/change-mut-borrow-in-macro.rs b/tests/ui/suggestions/change-mut-borrow-in-macro.rs new file mode 100644 index 0000000000000..831c0632009aa --- /dev/null +++ b/tests/ui/suggestions/change-mut-borrow-in-macro.rs @@ -0,0 +1,26 @@ +//@ edition: 2021 +//@ aux-build: mut_borrow_macro.rs + +// The borrow that needs to become `&mut` lives in a macro body, so rewriting it would edit the +// macro definition rather than the call site, and for an external macro a file the user does not +// own. + +extern crate mut_borrow_macro; + +macro_rules! local_call_it { + ($f:expr) => { + (0..100).for_each(&$f) + }; +} + +fn main() { + let mut value = 0; + let mut func = |increment: usize| value += increment; + //~^ ERROR expected a closure that implements the `Fn` trait + local_call_it!(func); + + let mut other = 0; + let mut other_func = |increment: usize| other += increment; + //~^ ERROR expected a closure that implements the `Fn` trait + mut_borrow_macro::call_it!(other_func); +} diff --git a/tests/ui/suggestions/change-mut-borrow-in-macro.stderr b/tests/ui/suggestions/change-mut-borrow-in-macro.stderr new file mode 100644 index 0000000000000..b574ef3270710 --- /dev/null +++ b/tests/ui/suggestions/change-mut-borrow-in-macro.stderr @@ -0,0 +1,40 @@ +error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut` + --> $DIR/change-mut-borrow-in-macro.rs:18:20 + | +LL | (0..100).for_each(&$f) + | -------- --- the requirement to implement `Fn` derives from here + | | + | required by a bound introduced by this call +... +LL | let mut func = |increment: usize| value += increment; + | ^^^^^^^^^^^^^^^^^^ ----- closure is `FnMut` because it mutates the variable `value` here + | | + | this closure implements `FnMut`, not `Fn` + | + = note: `FnMut(usize)` is implemented for `&mut {closure@$DIR/change-mut-borrow-in-macro.rs:18:20: 18:38}`, but not for `&{closure@$DIR/change-mut-borrow-in-macro.rs:18:20: 18:38}` + = note: required for `&{closure@$DIR/change-mut-borrow-in-macro.rs:18:20: 18:38}` to implement `FnMut(usize)` +note: required by a bound in `for_each` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut` + --> $DIR/change-mut-borrow-in-macro.rs:23:26 + | +LL | let mut other_func = |increment: usize| other += increment; + | ^^^^^^^^^^^^^^^^^^ ----- closure is `FnMut` because it mutates the variable `other` here + | | + | this closure implements `FnMut`, not `Fn` +LL | +LL | mut_borrow_macro::call_it!(other_func); + | -------------------------------------- + | | + | the requirement to implement `Fn` derives from here + | required by a bound introduced by this call + | + = note: `FnMut(usize)` is implemented for `&mut {closure@$DIR/change-mut-borrow-in-macro.rs:23:26: 23:44}`, but not for `&{closure@$DIR/change-mut-borrow-in-macro.rs:23:26: 23:44}` + = note: required for `&{closure@$DIR/change-mut-borrow-in-macro.rs:23:26: 23:44}` to implement `FnMut(usize)` +note: required by a bound in `for_each` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0525`.