diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 361f143d99913..acfe8188e360e 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -200,6 +200,11 @@ impl CodegenBackend for CraneliftCodegenBackend { println!("Cranelift version: {}", cranelift_codegen::VERSION); } + fn has_mnemonic(&self, sess: &Session, mnemonic: &str) -> bool { + // All Cranelift supported targets support ret except for s390x + mnemonic == "ret" && sess.target.arch != Arch::S390x + } + fn target_cpu(&self, sess: &Session) -> String { // FIXME handle `-Ctarget-cpu=native` match sess.opts.cg.target_cpu { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index b273047b98fc6..e4312ff935c04 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -314,6 +314,10 @@ impl CodegenBackend for LlvmCodegenBackend { llvm::LLVMRustLLVMHasZstdCompression() } + fn has_mnemonic(&self, sess: &Session, mnemonic: &str) -> bool { + llvm_util::target_has_mnemonic(sess, mnemonic) + } + fn target_config(&self, sess: &Session) -> TargetConfig { target_config(sess) } diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index f8b5c99c304b4..10802f8b12a35 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -480,10 +480,6 @@ pub(crate) fn print(req: &PrintRequest, out: &mut String, sess: &Session) { match req.kind { PrintKind::TargetCPUs => print_target_cpus(sess, tm.raw(), out), PrintKind::TargetFeatures => print_target_features(sess, tm.raw(), out), - PrintKind::BackendHasMnemonic => { - let mnemonic = req.arg.as_deref().expect("BackendHasMnemonic requires arg"); - print_target_has_mnemonic(tm.raw(), mnemonic, out) - } _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req), } } @@ -746,9 +742,9 @@ pub(crate) fn tune_cpu(sess: &Session) -> Option<&str> { Some(handle_native(name)) } -fn print_target_has_mnemonic(tm: &llvm::TargetMachine, mnemonic: &str, out: &mut String) { - use std::fmt::Write; +pub(crate) fn target_has_mnemonic(sess: &Session, mnemonic: &str) -> bool { + require_inited(); + let tm = create_informational_target_machine(sess, false); let cstr = SmallCStr::new(mnemonic); - let has_mnemonic = unsafe { llvm::LLVMRustTargetHasMnemonic(tm, cstr.as_ptr()) }; - writeln!(out, "{}", has_mnemonic).unwrap(); + unsafe { llvm::LLVMRustTargetHasMnemonic(tm.raw(), cstr.as_ptr()) } } diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 9898b67b91f78..6ad3447ad69ed 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -92,6 +92,14 @@ pub trait CodegenBackend { false } + /// Value printed by `--print=backend-has-mnemonic:...`. + /// + /// Used by compiletest to determine whether tests involving `asm!()` should + /// be executed or skipped. + fn has_mnemonic(&self, _sess: &Session, _mnemonic: &str) -> bool { + false + } + /// The metadata loader used to load rlib and dylib metadata. /// /// Alternative codegen backends may want to use different rlib or dylib formats than the diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 8d80e742a1b27..aaac65721dfab 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -802,7 +802,9 @@ fn print_crate_info( println_info!("{}", calling_conventions.join("\n")); } BackendHasMnemonic => { - codegen_backend.print(req, &mut crate_info, sess); + let has_mnemonic: bool = + codegen_backend.has_mnemonic(sess, req.arg.as_ref().unwrap()); + println_info!("{has_mnemonic}"); } BackendHasZstd => { let has_zstd: bool = codegen_backend.has_zstd(); diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 75be58dfbe1a6..e914489acadff 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -980,8 +980,8 @@ impl<'tcx> TyCtxt<'tcx> { span, .. }) => { - // Ensure that the returned span has the item's SyntaxContext. - fn_decl_span.find_ancestor_inside(*span).unwrap_or(*span) + // Ensure that the returned span has the closure expression's SyntaxContext. + fn_decl_span.find_ancestor_inside_same_ctxt(*span).unwrap_or(*span) } _ => self.hir_span_with_body(hir_id), }; diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 5b786f5a710f8..395eaf55265c0 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -363,13 +363,8 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { | UpvarRef { .. } | VarRef { .. } | ZstLiteral { .. } - | Yield { .. } => true, - ExprKind::Reborrow { .. } => { - // FIXME(reborrow): matching on a Reborrow expression should be impossible - // currently. Whether this remains to be true, and if the reborrow result then is a - // known valid scrutinee requires further thought. - unreachable!("Reborrow expression in match") - } + | Yield { .. } + | Reborrow { .. } => true, } } diff --git a/library/std/tests/sync/mpmc.rs b/library/std/tests/sync/mpmc.rs index db221ff15890e..e8ed5ab13d009 100644 --- a/library/std/tests/sync/mpmc.rs +++ b/library/std/tests/sync/mpmc.rs @@ -419,34 +419,48 @@ fn oneshot_multi_thread_send_recv_stress() { #[test] fn stream_send_recv_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel(); - - send(tx, 0); - recv(rx, 0); + thread::scope(|s| { + for _ in 0..stress_factor() { + let (tx, rx) = channel(); + + send(tx, 0, s); + recv(rx, 0, s); + + fn send<'scope, 'env>( + tx: Sender>, + i: i32, + s: &'scope thread::Scope<'scope, 'env>, + ) where + 'env: 'scope, + { + if i == 10 { + return; + } - fn send(tx: Sender>, i: i32) { - if i == 10 { - return; + s.spawn(move || { + tx.send(Box::new(i)).unwrap(); + send(tx, i + 1, s); + }); } - thread::spawn(move || { - tx.send(Box::new(i)).unwrap(); - send(tx, i + 1); - }); - } + fn recv<'scope, 'env>( + rx: Receiver>, + i: i32, + s: &'scope thread::Scope<'scope, 'env>, + ) where + 'env: 'scope, + { + if i == 10 { + return; + } - fn recv(rx: Receiver>, i: i32) { - if i == 10 { - return; + s.spawn(move || { + assert!(*rx.recv().unwrap() == i); + recv(rx, i + 1, s); + }); } - - thread::spawn(move || { - assert!(*rx.recv().unwrap() == i); - recv(rx, i + 1); - }); } - } + }) } #[test] diff --git a/library/std/tests/sync/mpsc.rs b/library/std/tests/sync/mpsc.rs index 4dc4b955da7c2..f1364dda713a1 100644 --- a/library/std/tests/sync/mpsc.rs +++ b/library/std/tests/sync/mpsc.rs @@ -382,34 +382,48 @@ fn oneshot_multi_thread_send_recv_stress() { #[test] fn stream_send_recv_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel(); - - send(tx, 0); - recv(rx, 0); + thread::scope(|s| { + for _ in 0..stress_factor() { + let (tx, rx) = channel(); + + send(tx, 0, s); + recv(rx, 0, s); + + fn send<'scope, 'env>( + tx: Sender>, + i: i32, + s: &'scope thread::Scope<'scope, 'env>, + ) where + 'env: 'scope, + { + if i == 10 { + return; + } - fn send(tx: Sender>, i: i32) { - if i == 10 { - return; + s.spawn(move || { + tx.send(Box::new(i)).unwrap(); + send(tx, i + 1, s); + }); } - thread::spawn(move || { - tx.send(Box::new(i)).unwrap(); - send(tx, i + 1); - }); - } + fn recv<'scope, 'env>( + rx: Receiver>, + i: i32, + s: &'scope thread::Scope<'scope, 'env>, + ) where + 'env: 'scope, + { + if i == 10 { + return; + } - fn recv(rx: Receiver>, i: i32) { - if i == 10 { - return; + s.spawn(move || { + assert!(*rx.recv().unwrap() == i); + recv(rx, i + 1, s); + }); } - - thread::spawn(move || { - assert!(*rx.recv().unwrap() == i); - recv(rx, i + 1); - }); } - } + }) } #[test] diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index d726c612acf68..3cd34f24c6e3d 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -18,6 +18,7 @@ use rustc_session::{EarlyDiagCtxt, getopts}; use rustc_span::edition::Edition; use rustc_span::{FileName, RemapPathScopeComponents}; use rustc_target::spec::TargetTuple; +use smallvec::SmallVec; use crate::core::new_dcx; use crate::externalfiles::ExternalHtml; @@ -293,7 +294,7 @@ pub(crate) struct RenderOptions { /// Note: this field is duplicated in `Options` because it's useful to have /// it in both places. pub(crate) unstable_features: rustc_feature::UnstableFeatures, - pub(crate) emit: Vec, + pub(crate) emit: SmallVec<[EmitType; 2]>, /// If `true`, HTML source pages will generate links for items to their definition. pub(crate) generate_link_to_definition: bool, /// Set of function-call locations to include as examples @@ -327,9 +328,22 @@ pub(crate) enum ModuleSorting { pub(crate) enum EmitType { HtmlStaticFiles, HtmlNonStaticFiles, + // not explicitly nameable by the user for now + JsonFiles, DepInfo(Option), } +impl fmt::Display for EmitType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Self::HtmlStaticFiles => "html-static-files", + Self::HtmlNonStaticFiles => "html-non-static-files", + Self::JsonFiles => "json-files", + Self::DepInfo(_) => "dep-info", + }) + } +} + impl FromStr for EmitType { type Err = (); @@ -352,17 +366,11 @@ impl FromStr for EmitType { } impl RenderOptions { - pub(crate) fn should_emit_crate(&self) -> bool { - self.emit.is_empty() || self.emit.contains(&EmitType::HtmlNonStaticFiles) - } - pub(crate) fn dep_info(&self) -> Option> { - for emit in &self.emit { - if let EmitType::DepInfo(file) = emit { - return Some(file.as_ref()); - } - } - None + self.emit.iter().find_map(|emit| match emit { + EmitType::DepInfo(file) => Some(file.as_ref()), + _ => None, + }) } } @@ -469,26 +477,6 @@ impl Options { let should_test = matches.opt_present("test"); - let mut emit = FxIndexMap::<_, EmitType>::default(); - for list in matches.opt_strs("emit") { - if should_test { - dcx.fatal("the `--test` flag and the `--emit` flag are not supported together"); - } - for kind in list.split(',') { - match kind.parse() { - Ok(kind) => { - // De-duplicate emit types and the last wins. - // Only one instance for each type is allowed - // regardless the actual data it carries. - // This matches rustc's `--emit` behavior. - emit.insert(std::mem::discriminant(&kind), kind); - } - Err(()) => dcx.fatal(format!("unrecognized emission type: {kind}")), - } - } - } - let emit = emit.into_values().collect::>(); - let show_coverage = matches.opt_present("show-coverage"); let output_format_s = matches.opt_str("output-format"); let output_format = match output_format_s { @@ -527,15 +515,55 @@ impl Options { } } - if output_format == OutputFormat::Json { - if let Some(emit_flag) = emit.iter().find_map(|emit| match emit { - EmitType::HtmlStaticFiles => Some("html-static-files"), - EmitType::HtmlNonStaticFiles => Some("html-non-static-files"), - EmitType::DepInfo(_) => None, - }) { - dcx.fatal(format!( - "the `--emit={emit_flag}` flag is not supported with `--output-format=json`", - )); + let mut emit = FxIndexMap::default(); + for list in matches.opt_strs("emit") { + if should_test { + dcx.fatal("the `--test` flag and the `--emit` flag are not supported together"); + } + if let OutputFormat::Doctest = output_format { + dcx.fatal("the `--emit` flag is not supported with `--output-format=doctest`"); + } + + for typ in list.split(',') { + let Ok(typ) = typ.parse::() else { + dcx.fatal(format!("unrecognized emission type: {typ}")) + }; + + match typ { + EmitType::DepInfo(_) => match output_format { + OutputFormat::Json | OutputFormat::Html => {} + OutputFormat::Doctest => unreachable!(), + }, + EmitType::HtmlStaticFiles | EmitType::HtmlNonStaticFiles => match output_format + { + OutputFormat::Html => {} + OutputFormat::Json => dcx.fatal(format!( + "the `--emit={typ}` flag is not supported with `--output-format=json`", + )), + OutputFormat::Doctest => unreachable!(), + }, + EmitType::JsonFiles => unreachable!(), + } + + // De-duplicate emit types and the last wins. + // Only one instance for each type is allowed + // regardless the actual data it carries. + // This matches rustc's `--emit` behavior. + emit.insert(std::mem::discriminant(&typ), typ); + } + } + let mut emit: SmallVec<[_; 2]> = emit.into_values().collect(); + // If `--emit` is absent we'll register default emission types depending on the requested + // output format. We can safely use `is_empty` for this since `--emit=` ("truly empty") + // will have already been rejected above. + if emit.is_empty() { + match output_format { + OutputFormat::Json => emit.push(EmitType::JsonFiles), + OutputFormat::Html => { + emit.push(EmitType::HtmlStaticFiles); + emit.push(EmitType::HtmlNonStaticFiles); + } + OutputFormat::Doctest => {} } } diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs index 305c8c39ba7f9..5c458232f8f9c 100644 --- a/src/librustdoc/formats/renderer.rs +++ b/src/librustdoc/formats/renderer.rs @@ -2,7 +2,7 @@ use rustc_data_structures::profiling::SelfProfilerRef; use rustc_middle::ty::TyCtxt; use crate::clean; -use crate::config::RenderOptions; +use crate::config::{EmitType, RenderOptions}; use crate::error::Error; use crate::formats::cache::Cache; @@ -10,14 +10,16 @@ use crate::formats::cache::Cache; /// backend renderer has hooks for initialization, documenting an item, entering and exiting a /// module, and cleanup/finalizing output. pub(crate) trait FormatRenderer<'tcx>: Sized { - /// Gives a description of the renderer. Used for performance profiling. - fn descr() -> &'static str; + /// A description of the renderer. Used for performance profiling. + const DESCR: &'static str; - /// Whether to call `item` recursively for modules + /// Whether to call `item` recursively for modules. /// - /// This is true for html, and false for json. See #80664 + /// See [#80664](https://github.com/rust-lang/rust/issues/80664). const RUN_ON_MODULE: bool; + const NON_STATIC_FILE_EMIT_TYPE: EmitType; + /// This associated type is the type where the current module information is stored. /// /// For each module, we go through their items by calling for each item: @@ -109,18 +111,18 @@ pub(crate) fn run_format< ) -> Result<(), Error> { let prof = &tcx.sess.prof; - let emit_crate = options.should_emit_crate(); + let emit_non_static_files = options.emit.contains(&T::NON_STATIC_FILE_EMIT_TYPE); let (mut format_renderer, krate) = prof - .verbose_generic_activity_with_arg("create_renderer", T::descr()) + .verbose_generic_activity_with_arg("create_renderer", T::DESCR) .run(|| init(krate, options, cache, tcx))?; - if !emit_crate { + if !emit_non_static_files { return Ok(()); } // Render the crate documentation run_format_inner(&mut format_renderer, &krate.module, prof)?; - prof.verbose_generic_activity_with_arg("renderer_after_krate", T::descr()) + prof.verbose_generic_activity_with_arg("renderer_after_krate", T::DESCR) .run(|| format_renderer.after_krate()) } diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 4a7b1d1d6c563..6c02eecbd06ee 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -23,7 +23,7 @@ use super::{AllTypes, LinkFromSrc, StylePath, collect_spans_and_sources, scrape_ use crate::clean::types::ExternalLocation; use crate::clean::utils::has_doc_flag; use crate::clean::{self, ExternalCrate}; -use crate::config::{ModuleSorting, RenderOptions, ShouldMerge}; +use crate::config::{EmitType, ModuleSorting, RenderOptions, ShouldMerge}; use crate::docfs::{DocFS, PathError}; use crate::error::Error; use crate::formats::FormatRenderer; @@ -481,7 +481,6 @@ impl<'tcx> Context<'tcx> { ) -> Result<(Self, clean::Crate), Error> { // need to save a copy of the options for rendering the index page let md_opts = options.clone(); - let emit_crate = options.should_emit_crate(); let RenderOptions { output, external_html, @@ -495,6 +494,7 @@ impl<'tcx> Context<'tcx> { static_root_path, generate_redirect_map, show_type_layout, + emit, generate_link_to_definition, call_locations, no_emit_shared, @@ -605,7 +605,7 @@ impl<'tcx> Context<'tcx> { info: ContextInfo::new(include_sources), }; - if emit_crate { + if emit.contains(&EmitType::HtmlNonStaticFiles) { sources::render(&mut cx, &krate)?; } @@ -619,11 +619,10 @@ impl<'tcx> Context<'tcx> { /// Generates the documentation for `crate` into the directory `dst` impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { - fn descr() -> &'static str { - "html" - } - + const DESCR: &'static str = "html"; const RUN_ON_MODULE: bool = true; + const NON_STATIC_FILE_EMIT_TYPE: EmitType = EmitType::HtmlNonStaticFiles; + type ModuleData = ContextInfo; fn save_module_data(&mut self) -> Self::ModuleData { diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 8de899ea0eef9..26894a64c6c14 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -165,7 +165,7 @@ fn write_rendered_cross_crate_info( resource_suffix: &str, ) -> Result<(), Error> { let m = &opt.should_merge; - if opt.should_emit_crate() { + if opt.emit.contains(&EmitType::HtmlNonStaticFiles) { if include_sources { write_rendered_cci::(SourcesPart::blank, dst, crates, m)?; } @@ -190,7 +190,7 @@ fn write_resources( css_file_extension: Option<&Path>, resource_suffix: &str, ) -> Result<(), Error> { - if opt.emit.is_empty() || opt.emit.contains(&EmitType::HtmlNonStaticFiles) { + if opt.emit.contains(&EmitType::HtmlNonStaticFiles) { // Handle added third-party themes for entry in style_files { let theme = entry.basename()?; @@ -218,7 +218,7 @@ fn write_resources( } } - if opt.emit.is_empty() || opt.emit.contains(&EmitType::HtmlStaticFiles) { + if opt.emit.contains(&EmitType::HtmlStaticFiles) { let static_dir = dst.join("static.files"); try_err!(fs::create_dir_all(&static_dir), &static_dir); diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 7c8e7b7669cd9..e33d2d44cffd3 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -27,7 +27,7 @@ use tracing::{debug, trace}; use crate::clean::ItemKind; use crate::clean::types::{ExternalCrate, ExternalLocation}; -use crate::config::RenderOptions; +use crate::config::{EmitType, RenderOptions}; use crate::docfs::PathError; use crate::error::Error; use crate::formats::FormatRenderer; @@ -132,11 +132,10 @@ impl<'tcx> JsonRenderer<'tcx> { } impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { - fn descr() -> &'static str { - "json" - } - + const DESCR: &'static str = "json"; const RUN_ON_MODULE: bool = false; + const NON_STATIC_FILE_EMIT_TYPE: EmitType = EmitType::JsonFiles; + type ModuleData = (); fn save_module_data(&mut self) -> Self::ModuleData { diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 87de4244b5c85..d51405d352549 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -869,9 +869,7 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { }; rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { let has_dep_info = render_options.dep_info().is_some(); - if render_options.emit.contains(&EmitType::HtmlNonStaticFiles) - || render_options.emit.is_empty() - { + if render_options.emit.contains(&EmitType::HtmlNonStaticFiles) { markdown::render_and_write(file, render_options, edition)?; } if has_dep_info { diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index f73d7c96f7e85..1cf9c94a11721 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -81,9 +81,74 @@ string_enum! { } } +string_enum! { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + pub(crate) enum PassFailMode { + CheckFail => "check-fail", + CheckPass => "check-pass", + BuildFail => "build-fail", + BuildPass => "build-pass", + /// Running the program must make it exit with a regular failure exit code + /// in the range `1..=127`. If the program is terminated by e.g. a signal + /// the test will fail. + RunFail => "run-fail", + /// Running the program must result in a crash, e.g. by `SIGABRT` or + /// `SIGSEGV` on Unix or on Windows by having an appropriate NTSTATUS high + /// bit in the exit code. + RunCrash => "run-crash", + /// Running the program must either fail or crash. Useful for e.g. sanitizer + /// tests since some sanitizer implementations exit the process with code 1 + /// to in the face of memory errors while others abort (crash) the process + /// in the face of memory errors. + RunFailOrCrash => "run-fail-or-crash", + RunPass => "run-pass", + } +} + +impl PassFailMode { + pub(crate) fn is_pass(&self) -> bool { + match self { + PassFailMode::CheckPass | PassFailMode::BuildPass | PassFailMode::RunPass => true, + + PassFailMode::CheckFail + | PassFailMode::BuildFail + | PassFailMode::RunFail + | PassFailMode::RunCrash + | PassFailMode::RunFailOrCrash => false, + } + } + + pub(crate) fn is_check(&self) -> bool { + match self { + PassFailMode::CheckFail | PassFailMode::CheckPass => true, + + PassFailMode::BuildFail + | PassFailMode::BuildPass + | PassFailMode::RunFail + | PassFailMode::RunCrash + | PassFailMode::RunFailOrCrash + | PassFailMode::RunPass => false, + } + } + + pub(crate) fn is_run(&self) -> bool { + match self { + PassFailMode::CheckFail + | PassFailMode::CheckPass + | PassFailMode::BuildFail + | PassFailMode::BuildPass => false, + + PassFailMode::RunFail + | PassFailMode::RunCrash + | PassFailMode::RunFailOrCrash + | PassFailMode::RunPass => true, + } + } +} + string_enum! { #[derive(Clone, Copy, PartialEq, Debug, Hash)] - pub(crate) enum PassMode { + pub(crate) enum ForcePassMode { Check => "check", Build => "build", Run => "run", @@ -99,30 +164,6 @@ string_enum! { } } -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] -pub(crate) enum RunFailMode { - /// Running the program must make it exit with a regular failure exit code - /// in the range `1..=127`. If the program is terminated by e.g. a signal - /// the test will fail. - Fail, - /// Running the program must result in a crash, e.g. by `SIGABRT` or - /// `SIGSEGV` on Unix or on Windows by having an appropriate NTSTATUS high - /// bit in the exit code. - Crash, - /// Running the program must either fail or crash. Useful for e.g. sanitizer - /// tests since some sanitizer implementations exit the process with code 1 - /// to in the face of memory errors while others abort (crash) the process - /// in the face of memory errors. - FailOrCrash, -} - -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] -pub(crate) enum FailMode { - Check, - Build, - Run(RunFailMode), -} - string_enum! { #[derive(Clone, Debug, PartialEq)] pub(crate) enum CompareMode { @@ -470,7 +511,7 @@ pub(crate) struct Config { /// FIXME: make it even more obvious (especially in PR CI where `--pass=check` is used) when a /// pass mode is forced when the test fails, because it can be very non-obvious when e.g. an /// error is emitted only when `//@ build-pass` but not `//@ check-pass`. - pub(crate) force_pass_mode: Option, + pub(crate) force_pass_mode: Option, /// Explicitly enable or disable running of the target test binary. /// @@ -507,7 +548,7 @@ pub(crate) struct Config { /// may override this setting. /// /// FIXME: this flag / config option is somewhat misleading. For instance, in ui tests, it's - /// *only* applied to the [`PassMode::Run`] test crate and not its auxiliaries. + /// *only* applied to the [`PassFailMode::RunPass`] test crate and not its auxiliaries. pub(crate) optimize_tests: bool, /// Target platform tuple. diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 17f89f1dd9a00..51a9e638fd7a4 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -6,7 +6,7 @@ use camino::{Utf8Path, Utf8PathBuf}; use semver::Version; use tracing::*; -use crate::common::{CodegenBackend, Config, Debugger, FailMode, PassMode, RunFailMode, TestMode}; +use crate::common::{CodegenBackend, Config, Debugger, PassFailMode, TestMode}; use crate::debuggers::{extract_cdb_version, extract_gdb_version}; use crate::directives::auxiliary::parse_and_update_aux; pub(crate) use crate::directives::auxiliary::{AuxCrate, AuxProps}; @@ -165,12 +165,13 @@ pub(crate) struct TestProps { // error annotations are needed, but this may be updated in the future to // include other relaxations. pub(crate) known_bug: bool, - // How far should the test proceed while still passing. - pass_mode: Option, + /// Whether this is a check, build, or build-and-run test, and whether the + /// final step should succeed or fail. + /// + /// None for non-UI tests, and for auxiliary crates used by UI tests. + pub(crate) pass_fail_mode: Option, // Ignore `--pass` overrides from the command line for this test. - ignore_pass: bool, - // How far this test should proceed to start failing. - pub(crate) fail_mode: Option, + pub(crate) ignore_pass: bool, // rustdoc will test the output of the `--test` option pub(crate) check_test_line_numbers_match: bool, // customized normalization rules @@ -296,8 +297,7 @@ impl TestProps { incremental_dir: None, incremental: false, known_bug: false, - pass_mode: None, - fail_mode: None, + pass_fail_mode: None, ignore_pass: false, check_test_line_numbers_match: false, normalize_stdout: vec![], @@ -343,10 +343,9 @@ impl TestProps { props.load_from(testfile, revision, config); props.exec_env.push(("RUSTC".to_string(), config.rustc_path.to_string())); - match (props.pass_mode, props.fail_mode) { - (None, None) if config.mode == TestMode::Ui => props.fail_mode = Some(FailMode::Check), - (Some(_), Some(_)) => panic!("cannot use a *-fail and *-pass mode together"), - _ => {} + // UI tests default to `//@ check-fail` if unspecified. + if config.mode == TestMode::Ui && props.pass_fail_mode.is_none() { + props.pass_fail_mode = Some(PassFailMode::CheckFail); } props @@ -406,73 +405,17 @@ impl TestProps { } } - fn update_fail_mode(&mut self, ln: &DirectiveLine<'_>, config: &Config) { - let check_ui = |mode: &str| { - if config.mode != TestMode::Ui { - panic!("`{}-fail` directive is only supported in UI tests", mode); - } - }; - let fail_mode = if config.parse_name_directive(ln, "check-fail") { - check_ui("check"); - Some(FailMode::Check) - } else if config.parse_name_directive(ln, "build-fail") { - check_ui("build"); - Some(FailMode::Build) - } else if config.parse_name_directive(ln, "run-fail") { - check_ui("run"); - Some(FailMode::Run(RunFailMode::Fail)) - } else if config.parse_name_directive(ln, "run-crash") { - check_ui("run"); - Some(FailMode::Run(RunFailMode::Crash)) - } else if config.parse_name_directive(ln, "run-fail-or-crash") { - check_ui("run"); - Some(FailMode::Run(RunFailMode::FailOrCrash)) - } else { - None - }; - match (self.fail_mode, fail_mode) { - (None, Some(_)) => self.fail_mode = fail_mode, - (Some(_), Some(_)) => panic!("multiple `*-fail` directives in a single test"), - (_, None) => {} + fn update_pass_fail_mode(&mut self, ln: &DirectiveLine<'_>, config: &Config) { + let name = ln.name; + if config.mode != TestMode::Ui { + panic!("`{name}` directive is only supported in UI tests"); } - } - - fn update_pass_mode(&mut self, ln: &DirectiveLine<'_>, config: &Config) { - let check_no_run = |s| match (config.mode, s) { - (TestMode::Ui, _) => (), - (mode, _) => panic!("`{s}` directive is not supported in `{mode}` tests"), - }; - let pass_mode = if config.parse_name_directive(ln, "check-pass") { - check_no_run("check-pass"); - Some(PassMode::Check) - } else if config.parse_name_directive(ln, "build-pass") { - check_no_run("build-pass"); - Some(PassMode::Build) - } else if config.parse_name_directive(ln, "run-pass") { - check_no_run("run-pass"); - Some(PassMode::Run) - } else { - None - }; - match (self.pass_mode, pass_mode) { - (None, Some(_)) => self.pass_mode = pass_mode, - (Some(_), Some(_)) => panic!("multiple `*-pass` directives in a single test"), - (_, None) => {} - } - } - - pub(crate) fn pass_mode(&self, config: &Config) -> Option { - if !self.ignore_pass && self.fail_mode.is_none() { - if let mode @ Some(_) = config.force_pass_mode { - return mode; - } + if self.pass_fail_mode.is_some() { + panic!("multiple `*-fail` or `*-pass` directives in a single test"); } - self.pass_mode - } - // does not consider CLI override for pass mode - pub(crate) fn local_pass_mode(&self) -> Option { - self.pass_mode + let mode = ln.name.parse::().unwrap(); + self.pass_fail_mode = Some(mode); } fn update_add_minicore(&mut self, ln: &DirectiveLine<'_>, config: &Config) { @@ -489,7 +432,7 @@ impl TestProps { // FIXME(jieyouxu): this check is currently order-dependent, but we should probably // collect all directives in one go then perform a validation pass after that. - if self.local_pass_mode().is_some_and(|pm| pm == PassMode::Run) { + if self.pass_fail_mode == Some(PassFailMode::RunPass) { // `minicore` can only be used with non-run modes, because it's `core` prelude stubs // and can't run. panic!("`add-minicore` cannot be used to run the test binary"); diff --git a/src/tools/compiletest/src/directives/handlers.rs b/src/tools/compiletest/src/directives/handlers.rs index a1d6bd3088239..fa36c86bde259 100644 --- a/src/tools/compiletest/src/directives/handlers.rs +++ b/src/tools/compiletest/src/directives/handlers.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use std::sync::{Arc, LazyLock}; -use crate::common::{Config, TestMode}; +use crate::common::{Config, PassFailMode, TestMode}; use crate::directives::{ DirectiveLine, NormalizeKind, NormalizeRule, TestProps, parse_and_update_aux, parse_edition_range, split_flags, @@ -196,15 +196,9 @@ fn make_directive_handlers_map() -> HashMap<&'static str, Handler> { &mut props.check_test_line_numbers_match, ); }), - multi_handler(&["check-pass", "build-pass", "run-pass"], |config, ln, props| { - props.update_pass_mode(ln, config); + multi_handler(PassFailMode::STR_VARIANTS, |config, ln, props| { + props.update_pass_fail_mode(ln, config); }), - multi_handler( - &["check-fail", "build-fail", "run-fail", "run-crash", "run-fail-or-crash"], - |config, ln, props| { - props.update_fail_mode(ln, config); - }, - ), handler(IGNORE_PASS, |config, ln, props| { config.set_name_directive(ln, IGNORE_PASS, &mut props.ignore_pass); }), diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index dcea79b18e6a8..5494d3f26eb76 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -40,8 +40,8 @@ use walkdir::WalkDir; use self::directives::{EarlyProps, make_test_description}; use crate::common::{ - CodegenBackend, CompareMode, Config, Debugger, PassMode, TestMode, TestPaths, UI_EXTENSIONS, - expected_output_path, output_base_dir, output_relative_path, + CodegenBackend, CompareMode, Config, Debugger, ForcePassMode, TestMode, TestPaths, + UI_EXTENSIONS, expected_output_path, output_base_dir, output_relative_path, }; use crate::directives::{AuxProps, DirectivesCache, FileDirectives}; use crate::edition::parse_edition; @@ -446,7 +446,7 @@ fn parse_config(args: Vec) -> Config { skip: matches.opt_strs("skip"), filter_exact: matches.opt_present("exact"), force_pass_mode: matches.opt_str("pass").map(|mode| { - mode.parse::() + mode.parse::() .unwrap_or_else(|_| panic!("unknown `--pass` option `{}` given", mode)) }), // FIXME: this run scheme is... confusing. diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index ab268f944816f..514c060e414e4 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -14,7 +14,7 @@ use regex::{Captures, Regex}; use tracing::*; use crate::common::{ - CompareMode, Config, Debugger, FailMode, PassMode, RunFailMode, RunResult, TestMode, TestPaths, + CompareMode, Config, Debugger, ForcePassMode, PassFailMode, RunResult, TestMode, TestPaths, TestSuite, UI_EXTENSIONS, UI_FIXED, UI_RUN_STDERR, UI_RUN_STDOUT, UI_STDERR, UI_STDOUT, UI_SVG, UI_WINDOWS_SVG, expected_output_path, incremental_dir, output_base_dir, output_base_name, }; @@ -289,69 +289,68 @@ impl<'test> TestCx<'test> { } } - fn pass_mode(&self) -> Option { - self.props.pass_mode(self.config) - } + /// Returns the pass/fail expectation of this UI test + /// (e.g. `//@ check-pass` or `//@ build-fail`), + /// possibly modified by an explicit `--pass=check` on the command-line. + fn effective_pass_fail_mode(&self) -> Option { + assert_eq!(self.config.mode, TestMode::Ui); + // UI tests always have a pass/fail mode, but their auxiliary crates never have one. + let declared = self.props.pass_fail_mode?; - fn should_run(&self, pm: Option) -> WillExecute { - let test_should_run = match self.config.mode { - TestMode::Ui => { - pm == Some(PassMode::Run) || matches!(self.props.fail_mode, Some(FailMode::Run(_))) + // Specifying `--pass` only overrides `//@ pass-*` modes, and only if + // the test doesn't opt out with `//@ ignore-pass`. + if let Some(force_pass_mode) = self.config.force_pass_mode + && !self.props.ignore_pass + && declared.is_pass() + { + match force_pass_mode { + ForcePassMode::Check => Some(PassFailMode::CheckPass), + ForcePassMode::Build => Some(PassFailMode::BuildPass), + ForcePassMode::Run => Some(PassFailMode::RunPass), } - mode => panic!("unimplemented for mode {:?}", mode), - }; - if test_should_run { self.run_if_enabled() } else { WillExecute::No } + } else { + Some(declared) + } } fn run_if_enabled(&self) -> WillExecute { if self.config.run_enabled() { WillExecute::Yes } else { WillExecute::Disabled } } - fn should_run_successfully(&self, pm: Option) -> bool { - match self.config.mode { - TestMode::Ui => pm == Some(PassMode::Run), - mode => panic!("unimplemented for mode {:?}", mode), - } - } + fn check_if_test_should_compile(&self, pass_fail: PassFailMode, proc_res: &ProcRes) { + assert_eq!(self.config.mode, TestMode::Ui); - fn should_compile_successfully(&self, pm: Option) -> bool { - match self.config.mode { - TestMode::RustdocJs => true, - TestMode::Ui => pm.is_some() || self.props.fail_mode > Some(FailMode::Build), - TestMode::Crashes => false, - mode => panic!("unimplemented for mode {:?}", mode), - } - } + let should_compile_successfully = match pass_fail { + PassFailMode::CheckFail | PassFailMode::BuildFail => false, - fn check_if_test_should_compile( - &self, - fail_mode: Option, - pass_mode: Option, - proc_res: &ProcRes, - ) { - if self.should_compile_successfully(pass_mode) { + PassFailMode::CheckPass + | PassFailMode::BuildPass + | PassFailMode::RunFail + | PassFailMode::RunCrash + | PassFailMode::RunFailOrCrash + | PassFailMode::RunPass => true, + }; + + if should_compile_successfully { if !proc_res.status.success() { - match (fail_mode, pass_mode) { - (Some(FailMode::Build), Some(PassMode::Check)) => { - // A `build-fail` test needs to `check-pass`. - self.fatal_proc_rec( - "`build-fail` test is required to pass check build, but check build failed", - proc_res, - ); - } - _ => { - self.fatal_proc_rec( - "test compilation failed although it shouldn't!", - proc_res, - ); - } + if pass_fail == PassFailMode::CheckPass + && self.effective_pass_fail_mode() == Some(PassFailMode::BuildFail) + { + // A `build-fail` test needs to `check-pass`. + self.fatal_proc_rec( + "`build-fail` test is required to pass check build, but check build failed", + proc_res, + ); + } else { + self.fatal_proc_rec("test compilation failed although it shouldn't!", proc_res); } } } else { if proc_res.status.success() { let err = &format!("{} test did not emit an error", self.config.mode); - let extra_note = (self.config.mode == crate::common::TestMode::Ui) - .then_some("note: by default, ui tests are expected not to compile.\nhint: use check-pass, build-pass, or run-pass directive to change this behavior."); + let extra_note = Some( + "note: by default, ui tests are expected not to compile.\nhint: use check-pass, build-pass, or run-pass directive to change this behavior.", + ); self.fatal_proc_rec_general(err, extra_note, proc_res, || ()); } @@ -906,15 +905,6 @@ impl<'test> TestCx<'test> { } } - fn should_emit_metadata(&self, pm: Option) -> Emit { - match (pm, self.props.fail_mode, self.config.mode) { - (Some(PassMode::Check), ..) | (_, Some(FailMode::Check), TestMode::Ui) => { - Emit::Metadata - } - _ => Emit::None, - } - } - fn compile_test(&self, will_execute: WillExecute, emit: Emit) -> ProcRes { self.compile_test_general(will_execute, emit, Vec::new()) } @@ -943,10 +933,12 @@ impl<'test> TestCx<'test> { // let's just ignore unused code warnings by defaults and tests // can turn it back on if needed. if compiler_kind == CompilerKind::Rustc - // Note that we use the local pass mode here as we don't want + // Note that we use the declared pass mode here as we don't want // to set unused to allow if we've overridden the pass mode // via command line flags. - && self.props.local_pass_mode() != Some(PassMode::Run) + // FIXME(Zalathar): We should probably also warn in run-fail/crash + // tests, but that requires changes to some existing tests. + && self.props.pass_fail_mode != Some(PassFailMode::RunPass) { AllowUnused::Yes } else { @@ -1655,9 +1647,11 @@ impl<'test> TestCx<'test> { TestMode::Ui => { // If optimize-tests is true we still only want to optimize tests that actually get // executed and that don't specify their own optimization levels. - // Note: aux libs don't have a pass-mode, so they won't get optimized + // Note: aux libs don't have a pass/fail mode, so they won't get optimized // unless compile-flags are set in the aux file. - if self.props.pass_mode(&self.config) == Some(PassMode::Run) + // FIXME(Zalathar): We could also optimize run-fail/run-crash tests, + // but it's unclear whether that would be helpful or a waste of time. + if self.effective_pass_fail_mode() == Some(PassFailMode::RunPass) && !self .props .compile_flags diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs index 2d8b8c057d802..f8a0c00083aec 100644 --- a/src/tools/compiletest/src/runtest/ui.rs +++ b/src/tools/compiletest/src/runtest/ui.rs @@ -5,29 +5,29 @@ use std::io::Write; use rustfix::{Filter, apply_suggestions, get_suggestions_from_json}; use tracing::debug; +use crate::common::PassFailMode; use crate::json; use crate::runtest::{ - AllowUnused, Emit, FailMode, LinkToAux, PassMode, ProcRes, RunFailMode, RunResult, - TargetLocation, TestCx, TestOutput, Truncated, UI_FIXED, WillExecute, + AllowUnused, Emit, LinkToAux, ProcRes, RunResult, TargetLocation, TestCx, TestOutput, + Truncated, UI_FIXED, WillExecute, }; impl TestCx<'_> { pub(super) fn run_ui_test(&self) { - if let Some(FailMode::Build) = self.props.fail_mode { + let pass_fail = + self.effective_pass_fail_mode().expect("UI tests always have a pass/fail mode"); + + if pass_fail == PassFailMode::BuildFail { // Make sure a build-fail test cannot fail due to failing analysis (e.g. typeck). let proc_res = self.compile_test(WillExecute::No, Emit::Metadata); - self.check_if_test_should_compile( - self.props.fail_mode, - Some(PassMode::Check), - &proc_res, - ); + self.check_if_test_should_compile(PassFailMode::CheckPass, &proc_res); } - let pm = self.pass_mode(); - let should_run = self.should_run(pm); - let emit_metadata = self.should_emit_metadata(pm); - let proc_res = self.compile_test(should_run, emit_metadata); - self.check_if_test_should_compile(self.props.fail_mode, pm, &proc_res); + let will_execute = if pass_fail.is_run() { self.run_if_enabled() } else { WillExecute::No }; + let emit_metadata = if pass_fail.is_check() { Emit::Metadata } else { Emit::None }; + let proc_res = self.compile_test(will_execute, emit_metadata); + self.check_if_test_should_compile(pass_fail, &proc_res); + if matches!(proc_res.truncated, Truncated::Yes) && !self.props.dont_check_compiler_stdout && !self.props.dont_check_compiler_stderr @@ -136,7 +136,7 @@ impl TestCx<'_> { // If the test is executed, capture its ProcRes separately so that // pattern/forbid checks can report the *runtime* stdout/stderr when they fail. let mut run_proc_res: Option = None; - let output_to_check = if let WillExecute::Yes = should_run { + let output_to_check = if will_execute == WillExecute::Yes { let proc_res = self.exec_compiled_test(); let run_output_errors = if self.props.check_run_results { self.load_compare_outputs(&proc_res, TestOutput::Run, explicit) @@ -157,45 +157,61 @@ impl TestCx<'_> { } else { RunResult::Crash }; + // Help users understand why the test failed by including the actual // exit code and actual run result in the failure message. let pass_hint = format!("code={code:?} so test would pass with `{run_result}`"); - if self.should_run_successfully(pm) { - if run_result != RunResult::Pass { - self.fatal_proc_rec( - &format!("test did not exit with success! {pass_hint}"), - &proc_res, - ); + match pass_fail { + PassFailMode::CheckFail + | PassFailMode::CheckPass + | PassFailMode::BuildFail + | PassFailMode::BuildPass => { + unreachable!("test program should not have run in mode {pass_fail:?}") } - } else if self.props.fail_mode == Some(FailMode::Run(RunFailMode::Fail)) { - // If the test is marked as `run-fail` but do not support - // unwinding we allow it to crash, since a panic will trigger an - // abort (crash) instead of unwind (exit with code 101). - let crash_ok = !self.config.can_unwind(); - if run_result != RunResult::Fail && !(crash_ok && run_result == RunResult::Crash) { - let err = if crash_ok { - format!( - "test did not exit with failure or crash (`{}` can't unwind)! {pass_hint}", - self.config.target - ) - } else { - format!("test did not exit with failure! {pass_hint}") - }; - self.fatal_proc_rec(&err, &proc_res); + + PassFailMode::RunPass => { + if run_result != RunResult::Pass { + self.fatal_proc_rec( + &format!("test did not exit with success! {pass_hint}"), + &proc_res, + ); + } } - } else if self.props.fail_mode == Some(FailMode::Run(RunFailMode::Crash)) { - if run_result != RunResult::Crash { - self.fatal_proc_rec(&format!("test did not crash! {pass_hint}"), &proc_res); + + PassFailMode::RunFail => { + // If the test is marked as `run-fail` but do not support + // unwinding we allow it to crash, since a panic will trigger an + // abort (crash) instead of unwind (exit with code 101). + let crash_ok = !self.config.can_unwind(); + if run_result != RunResult::Fail + && !(crash_ok && run_result == RunResult::Crash) + { + let err = if crash_ok { + format!( + "test did not exit with failure or crash (`{}` can't unwind)! {pass_hint}", + self.config.target + ) + } else { + format!("test did not exit with failure! {pass_hint}") + }; + self.fatal_proc_rec(&err, &proc_res); + } } - } else if self.props.fail_mode == Some(FailMode::Run(RunFailMode::FailOrCrash)) { - if run_result != RunResult::Fail && run_result != RunResult::Crash { - self.fatal_proc_rec( - &format!("test did not exit with failure or crash! {pass_hint}"), - &proc_res, - ); + + PassFailMode::RunCrash => { + if run_result != RunResult::Crash { + self.fatal_proc_rec(&format!("test did not crash! {pass_hint}"), &proc_res); + } + } + + PassFailMode::RunFailOrCrash => { + if run_result != RunResult::Fail && run_result != RunResult::Crash { + self.fatal_proc_rec( + &format!("test did not exit with failure or crash! {pass_hint}"), + &proc_res, + ); + } } - } else { - unreachable!("run_ui_test() must not be called if the test should not run"); } let output = self.get_output(&proc_res); diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 26d2edaedbbe2..2bfdd8889399d 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -95,21 +95,36 @@ macro_rules! static_regex { pub(crate) use static_regex; macro_rules! string_enum { - ($(#[$meta:meta])* $vis:vis enum $name:ident { $($variant:ident => $repr:expr,)* }) => { + ( + $(#[$meta:meta])* + $vis:vis enum $name:ident { + $( + $(#[$variant_meta:meta])* + $variant:ident => $repr:expr, + )* + } + ) => { $(#[$meta])* $vis enum $name { - $($variant,)* + $( + $(#[$variant_meta])* + $variant, + )* } impl $name { #[allow(dead_code)] - $vis const VARIANTS: &'static [Self] = &[$(Self::$variant,)*]; + $vis const VARIANTS: &'static [Self] = &[ + $( Self::$variant, )* + ]; #[allow(dead_code)] - $vis const STR_VARIANTS: &'static [&'static str] = &[$(Self::$variant.to_str(),)*]; + $vis const STR_VARIANTS: &'static [&'static str] = &[ + $( Self::$variant.to_str(), )* + ]; $vis const fn to_str(&self) -> &'static str { match self { - $(Self::$variant => $repr,)* + $( Self::$variant => $repr, )* } } } @@ -125,7 +140,7 @@ macro_rules! string_enum { fn from_str(s: &str) -> Result { match s { - $($repr => Ok(Self::$variant),)* + $( $repr => Ok(Self::$variant), )* _ => Err(format!(concat!("unknown `", stringify!($name), "` variant: `{}`"), s)), } } diff --git a/tests/rustdoc-ui/output-format-doctest-emit.dep-info.stderr b/tests/rustdoc-ui/output-format-doctest-emit.dep-info.stderr new file mode 100644 index 0000000000000..e09774d9a03bc --- /dev/null +++ b/tests/rustdoc-ui/output-format-doctest-emit.dep-info.stderr @@ -0,0 +1,2 @@ +error: the `--emit` flag is not supported with `--output-format=doctest` + diff --git a/tests/rustdoc-ui/output-format-doctest-emit.html-static-files.stderr b/tests/rustdoc-ui/output-format-doctest-emit.html-static-files.stderr new file mode 100644 index 0000000000000..e09774d9a03bc --- /dev/null +++ b/tests/rustdoc-ui/output-format-doctest-emit.html-static-files.stderr @@ -0,0 +1,2 @@ +error: the `--emit` flag is not supported with `--output-format=doctest` + diff --git a/tests/rustdoc-ui/output-format-doctest-emit.rs b/tests/rustdoc-ui/output-format-doctest-emit.rs new file mode 100644 index 0000000000000..b590d9057bb6e --- /dev/null +++ b/tests/rustdoc-ui/output-format-doctest-emit.rs @@ -0,0 +1,8 @@ +// Ensure that `--output-format=doctest` is incompatible with the `--emit` flag (for now at least). + +//@ revisions: html-static-files dep-info +//@ compile-flags: -Zunstable-options --output-format doctest +//@[html-static-files] compile-flags: --emit html-static-files +//@[dep-info] compile-flags: --emit dep-info + +//~? ERROR the `--emit` flag is not supported with `--output-format=doctest` diff --git a/tests/rustdoc-ui/output-format-json-emit-html.rs b/tests/rustdoc-ui/output-format-json-emit-html.rs index 7a99cbd91ba6e..7ce3bf756ec92 100644 --- a/tests/rustdoc-ui/output-format-json-emit-html.rs +++ b/tests/rustdoc-ui/output-format-json-emit-html.rs @@ -1,8 +1,7 @@ //@ revisions: html_static html_non_static -//@ check-fail -//@[html_static] compile-flags: -Z unstable-options --output-format=json --emit=html-static-files -//@[html_non_static] compile-flags: -Z unstable-options --output-format=json --emit=html-non-static-files +//@ compile-flags: -Zunstable-options --output-format json +//@[html_static] compile-flags: --emit html-static-files +//@[html_non_static] compile-flags: --emit html-non-static-files + //[html_static]~? ERROR the `--emit=html-static-files` flag is not supported with `--output-format=json` //[html_non_static]~? ERROR the `--emit=html-non-static-files` flag is not supported with `--output-format=json` - -pub struct Foo; diff --git a/tests/ui/proc-macro/auxiliary/closure-hir-span.rs b/tests/ui/proc-macro/auxiliary/closure-hir-span.rs new file mode 100644 index 0000000000000..6c29f0c35aff5 --- /dev/null +++ b/tests/ui/proc-macro/auxiliary/closure-hir-span.rs @@ -0,0 +1,23 @@ +//@ edition:2024 + +extern crate proc_macro; + +#[proc_macro] +pub fn m(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let mut iter = input.into_iter(); + let move_token = iter.next().unwrap(); + let pipe1 = iter.next().unwrap(); + let pipe2 = iter.next().unwrap(); + let body = iter.next().unwrap(); + + let mut inner = proc_macro::TokenStream::new(); + inner.extend([body]); + let new_body = proc_macro::TokenTree::Group(proc_macro::Group::new( + proc_macro::Delimiter::Brace, + inner, + )); + + let mut out = proc_macro::TokenStream::new(); + out.extend([move_token, pipe1, pipe2, new_body]); + out +} diff --git a/tests/ui/proc-macro/closure-hir-span.rs b/tests/ui/proc-macro/closure-hir-span.rs new file mode 100644 index 0000000000000..ff865587e26a2 --- /dev/null +++ b/tests/ui/proc-macro/closure-hir-span.rs @@ -0,0 +1,10 @@ +//! Regression test for +//@ check-pass +//@ edition:2024 +//@ proc-macro: closure-hir-span.rs + +extern crate closure_hir_span; + +fn main() { + closure_hir_span::m!(move || {}); +} diff --git a/tests/ui/reborrow/reborrow_let_match.rs b/tests/ui/reborrow/reborrow_let_match.rs new file mode 100644 index 0000000000000..fac9fba7b0332 --- /dev/null +++ b/tests/ui/reborrow/reborrow_let_match.rs @@ -0,0 +1,14 @@ +//@ check-pass +#![feature(reborrow)] + +use std::marker::Reborrow; + +#[allow(unused)] +struct Thing<'a>(&'a ()); + +impl<'a> Reborrow for Thing<'a> {} + +fn main() { + let x = Thing(&()); + let _y: Thing<'_> = x; +}