diff --git a/Cargo.lock b/Cargo.lock index 3f617af19b39a..12be36ef86126 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3451,9 +3451,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "opener" @@ -6101,6 +6101,15 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +[[package]] +name = "suggest-tests" +version = "0.1.0" +dependencies = [ + "build_helper", + "glob", + "once_cell", +] + [[package]] name = "syn" version = "1.0.102" diff --git a/Cargo.toml b/Cargo.toml index 15cbb2659c9b3..1fcaaf6ddc4d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,7 @@ members = [ "src/tools/lld-wrapper", "src/tools/collect-license-metadata", "src/tools/generate-copyright", + "src/tools/suggest-tests", ] exclude = [ diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 0befff894ef31..4dad3c6bce7f1 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -40,6 +40,10 @@ pub fn all_local_items() -> stable_mir::CrateItems { with(|tcx| tcx.mir_keys(()).iter().map(|item| crate_item(item.to_def_id())).collect()) } +pub fn entry_fn() -> Option { + with(|tcx| Some(crate_item(tcx.entry_fn(())?.0))) +} + /// Build a stable mir crate from a given crate number. fn smir_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> stable_mir::Crate { let crate_name = tcx.crate_name(crate_num).to_string(); diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs index ba23186224a75..1d2efb5eab947 100644 --- a/compiler/rustc_smir/src/stable_mir/mod.rs +++ b/compiler/rustc_smir/src/stable_mir/mod.rs @@ -45,6 +45,13 @@ impl CrateItem { } } +/// Return the function where execution starts if the current +/// crate defines that. This is usually `main`, but could be +/// `start` if the crate is a no-std crate. +pub fn entry_fn() -> Option { + crate::rustc_smir::entry_fn() +} + /// Access to the local crate. pub fn local_crate() -> Crate { crate::rustc_smir::local_crate() diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 55e18b0495601..aa240c37e8442 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -90,6 +90,11 @@ #![warn(multiple_supertrait_upcastable)] // // Library features: +// tidy-alphabetical-start +#![cfg_attr(not(no_global_oom_handling), feature(const_alloc_error))] +#![cfg_attr(not(no_global_oom_handling), feature(const_btree_len))] +#![cfg_attr(test, feature(is_sorted))] +#![cfg_attr(test, feature(new_uninit))] #![feature(alloc_layout_extra)] #![feature(allocator_api)] #![feature(array_chunks)] @@ -99,23 +104,21 @@ #![feature(assert_matches)] #![feature(async_iterator)] #![feature(coerce_unsized)] -#![cfg_attr(not(no_global_oom_handling), feature(const_alloc_error))] +#![feature(const_align_of_val)] #![feature(const_box)] -#![cfg_attr(not(no_global_oom_handling), feature(const_btree_len))] -#![feature(const_cow_is_borrowed)] #![feature(const_convert)] -#![feature(const_size_of_val)] -#![feature(const_align_of_val)] -#![feature(const_ptr_read)] -#![feature(const_maybe_uninit_zeroed)] -#![feature(const_maybe_uninit_write)] +#![feature(const_cow_is_borrowed)] +#![feature(const_eval_select)] #![feature(const_maybe_uninit_as_mut_ptr)] +#![feature(const_maybe_uninit_write)] +#![feature(const_maybe_uninit_zeroed)] +#![feature(const_pin)] +#![feature(const_ptr_read)] #![feature(const_refs_to_cell)] +#![feature(const_size_of_val)] +#![feature(const_waker)] #![feature(core_intrinsics)] #![feature(core_panic)] -#![feature(const_eval_select)] -#![feature(const_pin)] -#![feature(const_waker)] #![feature(dispatch_from_dyn)] #![feature(error_generic_member_access)] #![feature(error_in_core)] @@ -126,7 +129,6 @@ #![feature(hasher_prefixfree_extras)] #![feature(inline_const)] #![feature(inplace_iteration)] -#![cfg_attr(test, feature(is_sorted))] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] #![feature(iter_repeat_n)] @@ -134,7 +136,6 @@ #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_uninit_array_transpose)] -#![cfg_attr(test, feature(new_uninit))] #![feature(pattern)] #![feature(pointer_byte_offsets)] #![feature(provide_any)] @@ -150,6 +151,7 @@ #![feature(slice_ptr_get)] #![feature(slice_ptr_len)] #![feature(slice_range)] +#![feature(std_internals)] #![feature(str_internals)] #![feature(strict_provenance)] #![feature(trusted_len)] @@ -160,41 +162,43 @@ #![feature(unicode_internals)] #![feature(unsize)] #![feature(utf8_chunks)] -#![feature(std_internals)] +// tidy-alphabetical-end // // Language features: +// tidy-alphabetical-start +#![cfg_attr(not(test), feature(generator_trait))] +#![cfg_attr(test, feature(panic_update_hook))] +#![cfg_attr(test, feature(test))] #![feature(allocator_internals)] #![feature(allow_internal_unstable)] #![feature(associated_type_bounds)] +#![feature(c_unwind)] #![feature(cfg_sanitize)] #![feature(const_deref)] #![feature(const_mut_refs)] -#![feature(const_ptr_write)] #![feature(const_precise_live_drops)] +#![feature(const_ptr_write)] #![feature(const_trait_impl)] #![feature(const_try)] #![feature(dropck_eyepatch)] #![feature(exclusive_range_pattern)] #![feature(fundamental)] -#![cfg_attr(not(test), feature(generator_trait))] #![feature(hashmap_internals)] #![feature(lang_items)] #![feature(min_specialization)] +#![feature(multiple_supertrait_upcastable)] #![feature(negative_impls)] #![feature(never_type)] +#![feature(pointer_is_aligned)] #![feature(rustc_allow_const_fn_unstable)] #![feature(rustc_attrs)] -#![feature(pointer_is_aligned)] #![feature(slice_internals)] #![feature(staged_api)] #![feature(stmt_expr_attributes)] -#![cfg_attr(test, feature(test))] #![feature(unboxed_closures)] #![feature(unsized_fn_params)] -#![feature(c_unwind)] #![feature(with_negative_coherence)] -#![cfg_attr(test, feature(panic_update_hook))] -#![feature(multiple_supertrait_upcastable)] +// tidy-alphabetical-end // // Rustdoc features: #![feature(doc_cfg)] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 4fd5a4bfc65f6..04243544b8359 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -98,11 +98,14 @@ #![warn(multiple_supertrait_upcastable)] // // Library features: -#![feature(const_align_offset)] +// tidy-alphabetical-start +#![feature(char_indices_offset)] #![feature(const_align_of_val)] #![feature(const_align_of_val_raw)] +#![feature(const_align_offset)] #![feature(const_alloc_layout)] #![feature(const_arguments_as_str)] +#![feature(const_array_from_ref)] #![feature(const_array_into_iter_constructors)] #![feature(const_bigint_helper_methods)] #![feature(const_black_box)] @@ -111,6 +114,9 @@ #![feature(const_char_from_u32_unchecked)] #![feature(const_clone)] #![feature(const_cmp)] +#![feature(const_convert)] +#![feature(const_cstr_methods)] +#![feature(const_default_impls)] #![feature(const_discriminant)] #![feature(const_eval_select)] #![feature(const_exact_div)] @@ -119,17 +125,17 @@ #![feature(const_fmt_arguments_new)] #![feature(const_hash)] #![feature(const_heap)] -#![feature(const_convert)] #![feature(const_index_range_slice_index)] #![feature(const_inherent_unchecked_arith)] #![feature(const_int_unchecked_arith)] #![feature(const_intrinsic_forget)] #![feature(const_ipv4)] #![feature(const_ipv6)] +#![feature(const_is_char_boundary)] #![feature(const_likely)] -#![feature(const_maybe_uninit_uninit_array)] #![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_maybe_uninit_assume_init)] +#![feature(const_maybe_uninit_uninit_array)] #![feature(const_nonnull_new)] #![feature(const_num_from_num)] #![feature(const_ops)] @@ -138,32 +144,35 @@ #![feature(const_pin)] #![feature(const_pointer_byte_offsets)] #![feature(const_pointer_is_aligned)] -#![feature(const_ptr_sub_ptr)] -#![feature(const_replace)] -#![feature(const_result_drop)] #![feature(const_ptr_as_ref)] #![feature(const_ptr_is_null)] #![feature(const_ptr_read)] +#![feature(const_ptr_sub_ptr)] #![feature(const_ptr_write)] #![feature(const_raw_ptr_comparison)] +#![feature(const_replace)] +#![feature(const_result_drop)] #![feature(const_size_of_val)] #![feature(const_size_of_val_raw)] #![feature(const_slice_from_raw_parts_mut)] +#![feature(const_slice_from_ref)] +#![feature(const_slice_index)] #![feature(const_slice_ptr_len)] #![feature(const_slice_split_at_mut)] #![feature(const_str_from_utf8_unchecked_mut)] #![feature(const_swap)] #![feature(const_trait_impl)] +#![feature(const_transmute_copy)] #![feature(const_try)] #![feature(const_type_id)] #![feature(const_type_name)] -#![feature(const_default_impls)] #![feature(const_unicode_case_lookup)] #![feature(const_unsafecell_get_mut)] #![feature(const_waker)] #![feature(core_panic)] -#![feature(char_indices_offset)] #![feature(duration_consts_float)] +#![feature(ip)] +#![feature(is_ascii_octdigit)] #![feature(maybe_uninit_uninit_array)] #![feature(ptr_alignment_type)] #![feature(ptr_metadata)] @@ -171,25 +180,21 @@ #![feature(slice_ptr_get)] #![feature(slice_split_at_unchecked)] #![feature(str_internals)] -#![feature(str_split_remainder)] #![feature(str_split_inclusive_remainder)] +#![feature(str_split_remainder)] #![feature(strict_provenance)] #![feature(utf16_extra)] #![feature(utf16_extra_const)] #![feature(variant_count)] -#![feature(const_array_from_ref)] -#![feature(const_slice_from_ref)] -#![feature(const_slice_index)] -#![feature(const_is_char_boundary)] -#![feature(const_cstr_methods)] -#![feature(ip)] -#![feature(is_ascii_octdigit)] +// tidy-alphabetical-end // // Language features: +// tidy-alphabetical-start #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] +#![feature(asm_const)] #![feature(associated_type_bounds)] #![feature(auto_traits)] #![feature(c_unwind)] @@ -206,13 +211,12 @@ #![feature(deprecated_suggestion)] #![feature(derive_const)] #![feature(doc_cfg)] +#![feature(doc_cfg_hide)] #![feature(doc_notable_trait)] -#![feature(generic_arg_infer)] -#![feature(rustdoc_internals)] #![feature(exhaustive_patterns)] -#![feature(doc_cfg_hide)] #![feature(extern_types)] #![feature(fundamental)] +#![feature(generic_arg_infer)] #![feature(if_let_guard)] #![feature(inline_const)] #![feature(intra_doc_pointers)] @@ -221,6 +225,7 @@ #![feature(link_llvm_intrinsics)] #![feature(macro_metavar_expr)] #![feature(min_specialization)] +#![feature(multiple_supertrait_upcastable)] #![feature(must_not_suspend)] #![feature(negative_impls)] #![feature(never_type)] @@ -231,6 +236,7 @@ #![feature(repr_simd)] #![feature(rustc_allow_const_fn_unstable)] #![feature(rustc_attrs)] +#![feature(rustdoc_internals)] #![feature(simd_ffi)] #![feature(staged_api)] #![feature(stmt_expr_attributes)] @@ -240,11 +246,10 @@ #![feature(try_blocks)] #![feature(unboxed_closures)] #![feature(unsized_fn_params)] -#![feature(asm_const)] -#![feature(const_transmute_copy)] -#![feature(multiple_supertrait_upcastable)] +// tidy-alphabetical-end // // Target features: +// tidy-alphabetical-start #![feature(arm_target_feature)] #![feature(avx512_target_feature)] #![feature(hexagon_target_feature)] @@ -255,6 +260,7 @@ #![feature(sse4a_target_feature)] #![feature(tbm_target_feature)] #![feature(wasm_target_feature)] +// tidy-alphabetical-end // allow using `core::` in intra-doc links #[allow(unused_extern_crates)] diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs index 575f56ff4df29..23ed577ea609a 100644 --- a/library/std/src/collections/mod.rs +++ b/library/std/src/collections/mod.rs @@ -172,7 +172,8 @@ //! //! ## Iterators //! -//! Iterators are a powerful and robust mechanism used throughout Rust's +//! [Iterators][crate::iter] +//! are a powerful and robust mechanism used throughout Rust's //! standard libraries. Iterators provide a sequence of values in a generic, //! safe, efficient and convenient way. The contents of an iterator are usually //! *lazily* evaluated, so that only the values that are actually needed are @@ -252,7 +253,9 @@ //! //! Several other collection methods also return iterators to yield a sequence //! of results but avoid allocating an entire collection to store the result in. -//! This provides maximum flexibility as `collect` or `extend` can be called to +//! This provides maximum flexibility as +//! [`collect`][crate::iter::Iterator::collect] or +//! [`extend`][crate::iter::Extend::extend] can be called to //! "pipe" the sequence into any collection if desired. Otherwise, the sequence //! can be looped over with a `for` loop. The iterator can also be discarded //! after partial use, preventing the computation of the unused items. diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 71f3576c93d4b..98fcc76aa98f6 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -235,6 +235,7 @@ #![cfg_attr(windows, feature(round_char_boundary))] // // Language features: +// tidy-alphabetical-start #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] @@ -256,8 +257,8 @@ #![feature(intra_doc_pointers)] #![feature(lang_items)] #![feature(let_chains)] -#![feature(linkage)] #![feature(link_cfg)] +#![feature(linkage)] #![feature(min_specialization)] #![feature(must_not_suspend)] #![feature(needs_panic_runtime)] @@ -271,8 +272,10 @@ #![feature(thread_local)] #![feature(try_blocks)] #![feature(utf8_chunks)] +// tidy-alphabetical-end // // Library features (core): +// tidy-alphabetical-start #![feature(char_internals)] #![feature(core_intrinsics)] #![feature(duration_constants)] @@ -289,6 +292,7 @@ #![feature(ip)] #![feature(ip_in_core)] #![feature(maybe_uninit_slice)] +#![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_write_slice)] #![feature(panic_can_unwind)] #![feature(panic_info_message)] @@ -306,25 +310,28 @@ #![feature(std_internals)] #![feature(str_internals)] #![feature(strict_provenance)] -#![feature(maybe_uninit_uninit_array)] -#![feature(const_maybe_uninit_uninit_array)] -#![feature(const_waker)] +// tidy-alphabetical-end // // Library features (alloc): +// tidy-alphabetical-start #![feature(alloc_layout_extra)] #![feature(allocator_api)] #![feature(get_mut_unchecked)] #![feature(map_try_insert)] #![feature(new_uninit)] +#![feature(slice_concat_trait)] #![feature(thin_box)] #![feature(try_reserve_kind)] #![feature(vec_into_raw_parts)] -#![feature(slice_concat_trait)] +// tidy-alphabetical-end // // Library features (unwind): +// tidy-alphabetical-start #![feature(panic_unwind)] +// tidy-alphabetical-end // // Only for re-exporting: +// tidy-alphabetical-start #![feature(assert_matches)] #![feature(async_iterator)] #![feature(c_variadic)] @@ -336,24 +343,29 @@ #![feature(custom_test_frameworks)] #![feature(edition_panic)] #![feature(format_args_nl)] -#![feature(log_syntax)] +#![feature(get_many_mut)] #![feature(lazy_cell)] +#![feature(log_syntax)] #![feature(saturating_int_impl)] #![feature(stdsimd)] #![feature(test)] #![feature(trace_macros)] -#![feature(get_many_mut)] +// tidy-alphabetical-end // // Only used in tests/benchmarks: // // Only for const-ness: +// tidy-alphabetical-start #![feature(const_collections_with_hasher)] #![feature(const_hash)] #![feature(const_io_structs)] #![feature(const_ip)] #![feature(const_ipv4)] #![feature(const_ipv6)] +#![feature(const_maybe_uninit_uninit_array)] +#![feature(const_waker)] #![feature(thread_local_internals)] +// tidy-alphabetical-end // #![default_lib_allocator] diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index ade8fa4c74dcb..e959ea06f8b69 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -591,6 +591,7 @@ pub enum Kind { Install, Run, Setup, + Suggest, } impl Kind { @@ -610,6 +611,7 @@ impl Kind { "install" => Kind::Install, "run" | "r" => Kind::Run, "setup" => Kind::Setup, + "suggest" => Kind::Suggest, _ => return None, }) } @@ -629,6 +631,7 @@ impl Kind { Kind::Install => "install", Kind::Run => "run", Kind::Setup => "setup", + Kind::Suggest => "suggest", } } } @@ -709,6 +712,7 @@ impl<'a> Builder<'a> { test::CrateRustdoc, test::CrateRustdocJsonTypes, test::CrateJsonDocLint, + test::SuggestTestsCrate, test::Linkcheck, test::TierCheck, test::ReplacePlaceholderTest, @@ -827,7 +831,7 @@ impl<'a> Builder<'a> { Kind::Setup => describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode), Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std), // special-cased in Build::build() - Kind::Format => vec![], + Kind::Format | Kind::Suggest => vec![], } } @@ -891,6 +895,7 @@ impl<'a> Builder<'a> { Subcommand::Run { ref paths, .. } => (Kind::Run, &paths[..]), Subcommand::Clean { ref paths, .. } => (Kind::Clean, &paths[..]), Subcommand::Format { .. } => (Kind::Format, &[][..]), + Subcommand::Suggest { .. } => (Kind::Suggest, &[][..]), Subcommand::Setup { profile: ref path } => ( Kind::Setup, path.as_ref().map_or([].as_slice(), |path| std::slice::from_ref(path)), @@ -900,6 +905,21 @@ impl<'a> Builder<'a> { Self::new_internal(build, kind, paths.to_owned()) } + /// Creates a new standalone builder for use outside of the normal process + pub fn new_standalone( + build: &mut Build, + kind: Kind, + paths: Vec, + stage: Option, + ) -> Builder<'_> { + // FIXME: don't mutate `build` + if let Some(stage) = stage { + build.config.stage = stage; + } + + Self::new_internal(build, kind, paths.to_owned()) + } + pub fn execute_cli(&self) { self.run_step_descriptions(&Builder::get_step_descriptions(self.kind), &self.paths); } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index dd65dc91c0cd3..cc3b3bc25f3d5 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -56,8 +56,7 @@ pub enum DryRun { /// filled out from the decoded forms of the structs below. For documentation /// each field, see the corresponding fields in /// `config.example.toml`. -#[derive(Default)] -#[cfg_attr(test, derive(Clone))] +#[derive(Default, Clone)] pub struct Config { pub changelog_seen: Option, pub ccache: Option, @@ -240,23 +239,20 @@ pub struct Config { pub initial_rustfmt: RefCell, } -#[derive(Default, Deserialize)] -#[cfg_attr(test, derive(Clone))] +#[derive(Default, Deserialize, Clone)] pub struct Stage0Metadata { pub compiler: CompilerMetadata, pub config: Stage0Config, pub checksums_sha256: HashMap, pub rustfmt: Option, } -#[derive(Default, Deserialize)] -#[cfg_attr(test, derive(Clone))] +#[derive(Default, Deserialize, Clone)] pub struct CompilerMetadata { pub date: String, pub version: String, } -#[derive(Default, Deserialize)] -#[cfg_attr(test, derive(Clone))] +#[derive(Default, Deserialize, Clone)] pub struct Stage0Config { pub dist_server: String, pub artifacts_server: String, @@ -264,8 +260,7 @@ pub struct Stage0Config { pub git_merge_commit_email: String, pub nightly_branch: String, } -#[derive(Default, Deserialize)] -#[cfg_attr(test, derive(Clone))] +#[derive(Default, Deserialize, Clone)] pub struct RustfmtMetadata { pub date: String, pub version: String, @@ -443,8 +438,7 @@ impl PartialEq<&str> for TargetSelection { } /// Per-target configuration stored in the global configuration structure. -#[derive(Default)] -#[cfg_attr(test, derive(Clone))] +#[derive(Default, Clone)] pub struct Target { /// Some(path to llvm-config) if using an external LLVM. pub llvm_config: Option, @@ -1396,7 +1390,8 @@ impl Config { | Subcommand::Fix { .. } | Subcommand::Run { .. } | Subcommand::Setup { .. } - | Subcommand::Format { .. } => flags.stage.unwrap_or(0), + | Subcommand::Format { .. } + | Subcommand::Suggest { .. } => flags.stage.unwrap_or(0), }; // CI should always run stage 2 builds, unless it specifically states otherwise @@ -1421,7 +1416,8 @@ impl Config { | Subcommand::Fix { .. } | Subcommand::Run { .. } | Subcommand::Setup { .. } - | Subcommand::Format { .. } => {} + | Subcommand::Format { .. } + | Subcommand::Suggest { .. } => {} } } diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 2b0b772a61817..b6f5f31039838 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -84,8 +84,7 @@ pub struct Flags { pub free_args: Option>, } -#[derive(Debug)] -#[cfg_attr(test, derive(Clone))] +#[derive(Debug, Clone)] pub enum Subcommand { Build { paths: Vec, @@ -149,6 +148,9 @@ pub enum Subcommand { Setup { profile: Option, }, + Suggest { + run: bool, + }, } impl Default for Subcommand { @@ -183,6 +185,7 @@ Subcommands: install Install distribution artifacts run, r Run tools contained in this repository setup Create a config.toml (making it easier to use `x.py` itself) + suggest Suggest a subset of tests to run, based on modified files To learn more about a subcommand, run `./x.py -h`", ); @@ -349,6 +352,9 @@ To learn more about a subcommand, run `./x.py -h`", Kind::Run => { opts.optmulti("", "args", "arguments for the tool", "ARGS"); } + Kind::Suggest => { + opts.optflag("", "run", "run suggested tests"); + } _ => {} }; @@ -565,7 +571,7 @@ Arguments: Profile::all_for_help(" ").trim_end() )); } - Kind::Bench | Kind::Clean | Kind::Dist | Kind::Install => {} + Kind::Bench | Kind::Clean | Kind::Dist | Kind::Install | Kind::Suggest => {} }; // Get any optional paths which occur after the subcommand let mut paths = matches.free[1..].iter().map(|p| p.into()).collect::>(); @@ -626,6 +632,7 @@ Arguments: Kind::Format => Subcommand::Format { check: matches.opt_present("check"), paths }, Kind::Dist => Subcommand::Dist { paths }, Kind::Install => Subcommand::Install { paths }, + Kind::Suggest => Subcommand::Suggest { run: matches.opt_present("run") }, Kind::Run => { if paths.is_empty() { println!("\nrun requires at least a path!\n"); @@ -734,6 +741,7 @@ impl Subcommand { Subcommand::Install { .. } => Kind::Install, Subcommand::Run { .. } => Kind::Run, Subcommand::Setup { .. } => Kind::Setup, + Subcommand::Suggest { .. } => Kind::Suggest, } } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index eaa3afa4b7b20..1ecb52e75f190 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -58,6 +58,7 @@ mod render_tests; mod run; mod sanity; mod setup; +mod suggest; mod tarball; mod test; mod tool; @@ -190,6 +191,7 @@ pub enum GitRepo { /// although most functions are implemented as free functions rather than /// methods specifically on this structure itself (to make it easier to /// organize). +#[cfg_attr(not(feature = "build-metrics"), derive(Clone))] pub struct Build { /// User-specified configuration from `config.toml`. config: Config, @@ -243,7 +245,7 @@ pub struct Build { metrics: metrics::BuildMetrics, } -#[derive(Debug)] +#[derive(Debug, Clone)] struct Crate { name: Interned, deps: HashSet>, @@ -657,13 +659,20 @@ impl Build { job::setup(self); } - if let Subcommand::Format { check, paths } = &self.config.cmd { - return format::format(&builder::Builder::new(&self), *check, &paths); - } - // Download rustfmt early so that it can be used in rust-analyzer configs. let _ = &builder::Builder::new(&self).initial_rustfmt(); + // hardcoded subcommands + match &self.config.cmd { + Subcommand::Format { check, paths } => { + return format::format(&builder::Builder::new(&self), *check, &paths); + } + Subcommand::Suggest { run } => { + return suggest::suggest(&builder::Builder::new(&self), *run); + } + _ => (), + } + { let builder = builder::Builder::new(&self); if let Some(path) = builder.paths.get(0) { diff --git a/src/bootstrap/suggest.rs b/src/bootstrap/suggest.rs new file mode 100644 index 0000000000000..ff20ebec26772 --- /dev/null +++ b/src/bootstrap/suggest.rs @@ -0,0 +1,80 @@ +#![cfg_attr(feature = "build-metrics", allow(unused))] + +use std::str::FromStr; + +use std::path::PathBuf; + +use crate::{ + builder::{Builder, Kind}, + tool::Tool, +}; + +#[cfg(feature = "build-metrics")] +pub fn suggest(builder: &Builder<'_>, run: bool) { + panic!("`x suggest` is not supported with `build-metrics`") +} + +/// Suggests a list of possible `x.py` commands to run based on modified files in branch. +#[cfg(not(feature = "build-metrics"))] +pub fn suggest(builder: &Builder<'_>, run: bool) { + let suggestions = + builder.tool_cmd(Tool::SuggestTests).output().expect("failed to run `suggest-tests` tool"); + + if !suggestions.status.success() { + println!("failed to run `suggest-tests` tool ({})", suggestions.status); + println!( + "`suggest_tests` stdout:\n{}`suggest_tests` stderr:\n{}", + String::from_utf8(suggestions.stdout).unwrap(), + String::from_utf8(suggestions.stderr).unwrap() + ); + panic!("failed to run `suggest-tests`"); + } + + let suggestions = String::from_utf8(suggestions.stdout).unwrap(); + let suggestions = suggestions + .lines() + .map(|line| { + let mut sections = line.split_ascii_whitespace(); + + // this code expects one suggestion per line in the following format: + // {some number of flags} [optional stage number] + let cmd = sections.next().unwrap(); + let stage = sections.next_back().map(|s| str::parse(s).ok()).flatten(); + let paths: Vec = sections.map(|p| PathBuf::from_str(p).unwrap()).collect(); + + (cmd, stage, paths) + }) + .collect::>(); + + if !suggestions.is_empty() { + println!("==== SUGGESTIONS ===="); + for sug in &suggestions { + print!("x {} ", sug.0); + if let Some(stage) = sug.1 { + print!("--stage {stage} "); + } + + for path in &sug.2 { + print!("{} ", path.display()); + } + println!(); + } + println!("====================="); + } else { + println!("No suggestions found!"); + return; + } + + if run { + for sug in suggestions { + let mut build = builder.build.clone(); + + let builder = + Builder::new_standalone(&mut build, Kind::parse(&sug.0).unwrap(), sug.2, sug.1); + + builder.execute_cli() + } + } else { + println!("help: consider using the `--run` flag to automatically run suggested tests"); + } +} diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 2bf28674d3655..aedf1ecab13c4 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -128,6 +128,42 @@ impl Step for CrateJsonDocLint { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct SuggestTestsCrate { + host: TargetSelection, +} + +impl Step for SuggestTestsCrate { + type Output = (); + const ONLY_HOSTS: bool = true; + const DEFAULT: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/suggest-tests") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(SuggestTestsCrate { host: run.target }); + } + + fn run(self, builder: &Builder<'_>) { + let bootstrap_host = builder.config.build; + let compiler = builder.compiler(0, bootstrap_host); + + let suggest_tests = tool::prepare_tool_cargo( + builder, + compiler, + Mode::ToolBootstrap, + bootstrap_host, + "test", + "src/tools/suggest-tests", + SourceType::InTree, + &[], + ); + add_flags_and_try_run_tests(builder, &mut suggest_tests.into()); + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct Linkcheck { host: TargetSelection, diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 6a687a7903e0f..d1fd2e8c42cb0 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -433,6 +433,7 @@ bootstrap_tool!( ReplaceVersionPlaceholder, "src/tools/replace-version-placeholder", "replace-version-placeholder"; CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata"; GenerateCopyright, "src/tools/generate-copyright", "generate-copyright"; + SuggestTests, "src/tools/suggest-tests", "suggest-tests"; ); #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md index dfc8042637232..b46d80eb362e2 100644 --- a/src/doc/rustdoc/src/command-line-arguments.md +++ b/src/doc/rustdoc/src/command-line-arguments.md @@ -179,7 +179,7 @@ $ rustdoc src/lib.rs --test This flag will run your code examples as tests. For more, see [the chapter on documentation tests](write-documentation/documentation-tests.md). -See also `--test-args`. +See also `--test-args` and `--test-run-directory`. ## `--test-args`: pass options to test runner @@ -194,6 +194,19 @@ For more, see [the chapter on documentation tests](write-documentation/documenta See also `--test`. +## `--test-run-directory`: run code examples in a specific directory + +Using this flag looks like this: + +```bash +$ rustdoc src/lib.rs --test --test-run-directory=/path/to/working/directory +``` + +This flag will run your code examples in the specified working directory. +For more, see [the chapter on documentation tests](write-documentation/documentation-tests.md). + +See also `--test`. + ## `--target`: generate documentation for the specified target triple Using this flag looks like this: diff --git a/src/doc/rustdoc/src/write-documentation/documentation-tests.md b/src/doc/rustdoc/src/write-documentation/documentation-tests.md index 1cb5b049df404..a7d3186fb78b7 100644 --- a/src/doc/rustdoc/src/write-documentation/documentation-tests.md +++ b/src/doc/rustdoc/src/write-documentation/documentation-tests.md @@ -443,3 +443,15 @@ pub struct ReadmeDoctests; This will include your README as documentation on the hidden struct `ReadmeDoctests`, which will then be tested alongside the rest of your doctests. + +## Controlling the compilation and run directories + +By default, `rustdoc --test` will compile and run documentation test examples +from the same working directory. +The compilation directory is being used for compiler diagnostics, the `file!()` macro and +the output of `rustdoc` test runner itself, whereas the run directory has an influence on file-system +operations within documentation test examples, such as `std::fs::read_to_string`. + +The `--test-run-directory` flag allows controlling the run directory separately from the compilation directory. +This is particularly useful in workspaces, where compiler invocations and thus diagnostics should be +relative to the workspace directory, but documentation test examples should run relative to the crate directory. diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js index ecbe15a59da3c..4c81a0979c1a7 100644 --- a/src/librustdoc/html/static/js/externs.js +++ b/src/librustdoc/html/static/js/externs.js @@ -65,6 +65,11 @@ let Row; */ let ResultsTable; +/** + * @typedef {Map} + */ +let Results; + /** * @typedef {{ * desc: string, @@ -80,7 +85,7 @@ let ResultsTable; * ty: number, * }} */ -let Results; +let ResultObject; /** * A pair of [inputs, outputs], or 0 for null. This is stored in the search index. diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index c081578b8d4bb..40cdc55bbc3be 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -191,7 +191,7 @@ function initSearch(rawSearchIndex) { */ let searchIndex; let currentResults; - const ALIASES = Object.create(null); + const ALIASES = new Map(); function isWhitespace(c) { return " \t\n\r".indexOf(c) !== -1; @@ -903,10 +903,18 @@ function initSearch(rawSearchIndex) { * @return {ResultsTable} */ function execQuery(parsedQuery, searchWords, filterCrates, currentCrate) { - const results_others = {}, results_in_args = {}, results_returned = {}; + const results_others = new Map(), results_in_args = new Map(), + results_returned = new Map(); + /** + * Add extra data to result objects, and filter items that have been + * marked for removal. + * + * @param {[ResultObject]} results + * @returns {[ResultObject]} + */ function transformResults(results) { - const duplicates = {}; + const duplicates = new Set(); const out = []; for (const result of results) { @@ -919,10 +927,10 @@ function initSearch(rawSearchIndex) { // To be sure than it some items aren't considered as duplicate. obj.fullPath += "|" + obj.ty; - if (duplicates[obj.fullPath]) { + if (duplicates.has(obj.fullPath)) { continue; } - duplicates[obj.fullPath] = true; + duplicates.add(obj.fullPath); obj.href = res[1]; out.push(obj); @@ -934,24 +942,30 @@ function initSearch(rawSearchIndex) { return out; } + /** + * This function takes a result map, and sorts it by various criteria, including edit + * distance, substring match, and the crate it comes from. + * + * @param {Results} results + * @param {boolean} isType + * @param {string} preferredCrate + * @returns {[ResultObject]} + */ function sortResults(results, isType, preferredCrate) { - const userQuery = parsedQuery.userQuery; - const ar = []; - for (const entry in results) { - if (hasOwnPropertyRustdoc(results, entry)) { - const result = results[entry]; - result.word = searchWords[result.id]; - result.item = searchIndex[result.id] || {}; - ar.push(result); - } - } - results = ar; // if there are no results then return to default and fail - if (results.length === 0) { + if (results.size === 0) { return []; } - results.sort((aaa, bbb) => { + const userQuery = parsedQuery.userQuery; + const result_list = []; + for (const result of results.values()) { + result.word = searchWords[result.id]; + result.item = searchIndex[result.id] || {}; + result_list.push(result); + } + + result_list.sort((aaa, bbb) => { let a, b; // sort by exact match with regard to the last word (mismatch goes later) @@ -1060,7 +1074,7 @@ function initSearch(rawSearchIndex) { nameSplit = hasPath ? null : parsedQuery.elems[0].path; } - for (const result of results) { + for (const result of result_list) { // this validation does not make sense when searching by types if (result.dontValidate) { continue; @@ -1073,7 +1087,7 @@ function initSearch(rawSearchIndex) { result.id = -1; } } - return transformResults(results); + return transformResults(result_list); } /** @@ -1096,7 +1110,7 @@ function initSearch(rawSearchIndex) { // The names match, but we need to be sure that all generics kinda // match as well. if (elem.generics.length > 0 && row.generics.length >= elem.generics.length) { - const elems = Object.create(null); + const elems = new Map(); for (const entry of row.generics) { if (entry.name === "") { // Pure generic, needs to check into it. @@ -1106,39 +1120,30 @@ function initSearch(rawSearchIndex) { } continue; } - if (elems[entry.name] === undefined) { - elems[entry.name] = []; + let currentEntryElems; + if (elems.has(entry.name)) { + currentEntryElems = elems.get(entry.name); + } else { + currentEntryElems = []; + elems.set(entry.name, currentEntryElems); } - elems[entry.name].push(entry.ty); + currentEntryElems.push(entry.ty); } // We need to find the type that matches the most to remove it in order // to move forward. const handleGeneric = generic => { - let match = null; - if (elems[generic.name]) { - match = generic.name; - } else { - for (const elem_name in elems) { - if (!hasOwnPropertyRustdoc(elems, elem_name)) { - continue; - } - if (elem_name === generic) { - match = elem_name; - break; - } - } - } - if (match === null) { + if (!elems.has(generic.name)) { return false; } - const matchIdx = elems[match].findIndex(tmp_elem => + const matchElems = elems.get(generic.name); + const matchIdx = matchElems.findIndex(tmp_elem => typePassesFilter(generic.typeFilter, tmp_elem)); if (matchIdx === -1) { return false; } - elems[match].splice(matchIdx, 1); - if (elems[match].length === 0) { - delete elems[match]; + matchElems.splice(matchIdx, 1); + if (matchElems.length === 0) { + elems.delete(generic.name); } return true; }; @@ -1424,22 +1429,22 @@ function initSearch(rawSearchIndex) { const aliases = []; const crateAliases = []; if (filterCrates !== null) { - if (ALIASES[filterCrates] && ALIASES[filterCrates][lowerQuery]) { - const query_aliases = ALIASES[filterCrates][lowerQuery]; + if (ALIASES.has(filterCrates) && ALIASES.get(filterCrates).has(lowerQuery)) { + const query_aliases = ALIASES.get(filterCrates).get(lowerQuery); for (const alias of query_aliases) { aliases.push(createAliasFromItem(searchIndex[alias])); } } } else { - Object.keys(ALIASES).forEach(crate => { - if (ALIASES[crate][lowerQuery]) { + for (const [crate, crateAliasesIndex] of ALIASES) { + if (crateAliasesIndex.has(lowerQuery)) { const pushTo = crate === currentCrate ? crateAliases : aliases; - const query_aliases = ALIASES[crate][lowerQuery]; + const query_aliases = crateAliasesIndex.get(lowerQuery); for (const alias of query_aliases) { pushTo.push(createAliasFromItem(searchIndex[alias])); } } - }); + } } const sortFunc = (aaa, bbb) => { @@ -1496,19 +1501,19 @@ function initSearch(rawSearchIndex) { function addIntoResults(results, fullId, id, index, dist, path_dist, maxEditDistance) { const inBounds = dist <= maxEditDistance || index !== -1; if (dist === 0 || (!parsedQuery.literalSearch && inBounds)) { - if (results[fullId] !== undefined) { - const result = results[fullId]; + if (results.has(fullId)) { + const result = results.get(fullId); if (result.dontValidate || result.dist <= dist) { return; } } - results[fullId] = { + results.set(fullId, { id: id, index: index, dontValidate: parsedQuery.literalSearch, dist: dist, path_dist: path_dist, - }; + }); } } @@ -2345,17 +2350,22 @@ function initSearch(rawSearchIndex) { } if (aliases) { - ALIASES[crate] = Object.create(null); + const currentCrateAliases = new Map(); + ALIASES.set(crate, currentCrateAliases); for (const alias_name in aliases) { if (!hasOwnPropertyRustdoc(aliases, alias_name)) { continue; } - if (!hasOwnPropertyRustdoc(ALIASES[crate], alias_name)) { - ALIASES[crate][alias_name] = []; + let currentNameAliases; + if (currentCrateAliases.has(alias_name)) { + currentNameAliases = currentCrateAliases.get(alias_name); + } else { + currentNameAliases = []; + currentCrateAliases.set(alias_name, currentNameAliases); } for (const local_alias of aliases[alias_name]) { - ALIASES[crate][alias_name].push(local_alias + currentIndex); + currentNameAliases.push(local_alias + currentIndex); } } } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index b3640eab95339..60c98cc3831bd 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -284,7 +284,7 @@ fn opts() -> Vec { stable("test-args", |o| { o.optmulti("", "test-args", "arguments to pass to the test runner", "ARGS") }), - unstable("test-run-directory", |o| { + stable("test-run-directory", |o| { o.optopt( "", "test-run-directory", diff --git a/src/tools/suggest-tests/Cargo.toml b/src/tools/suggest-tests/Cargo.toml new file mode 100644 index 0000000000000..f4f4d548bb79e --- /dev/null +++ b/src/tools/suggest-tests/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "suggest-tests" +version = "0.1.0" +edition = "2021" + +[dependencies] +glob = "0.3.0" +build_helper = { version = "0.1.0", path = "../build_helper" } +once_cell = "1.17.1" diff --git a/src/tools/suggest-tests/src/dynamic_suggestions.rs b/src/tools/suggest-tests/src/dynamic_suggestions.rs new file mode 100644 index 0000000000000..2b0213cdc223c --- /dev/null +++ b/src/tools/suggest-tests/src/dynamic_suggestions.rs @@ -0,0 +1,23 @@ +use std::path::Path; + +use crate::Suggestion; + +type DynamicSuggestion = fn(&Path) -> Vec; + +pub(crate) const DYNAMIC_SUGGESTIONS: &[DynamicSuggestion] = &[|path: &Path| -> Vec { + if path.starts_with("compiler/") || path.starts_with("library/") { + let path = path.components().take(2).collect::>(); + + vec![Suggestion::with_single_path( + "test", + None, + &format!( + "{}/{}", + path[0].as_os_str().to_str().unwrap(), + path[1].as_os_str().to_str().unwrap() + ), + )] + } else { + Vec::new() + } +}]; diff --git a/src/tools/suggest-tests/src/lib.rs b/src/tools/suggest-tests/src/lib.rs new file mode 100644 index 0000000000000..44cd3c7f6a84d --- /dev/null +++ b/src/tools/suggest-tests/src/lib.rs @@ -0,0 +1,96 @@ +use std::{ + fmt::{self, Display}, + path::Path, +}; + +use dynamic_suggestions::DYNAMIC_SUGGESTIONS; +use glob::Pattern; +use static_suggestions::STATIC_SUGGESTIONS; + +mod dynamic_suggestions; +mod static_suggestions; + +#[cfg(test)] +mod tests; + +macro_rules! sug { + ($cmd:expr) => { + Suggestion::new($cmd, None, &[]) + }; + + ($cmd:expr, $paths:expr) => { + Suggestion::new($cmd, None, $paths.as_slice()) + }; + + ($cmd:expr, $stage:expr, $paths:expr) => { + Suggestion::new($cmd, Some($stage), $paths.as_slice()) + }; +} + +pub(crate) use sug; + +pub fn get_suggestions>(modified_files: &[T]) -> Vec { + let mut suggestions = Vec::new(); + + // static suggestions + for sug in STATIC_SUGGESTIONS.iter() { + let glob = Pattern::new(&sug.0).expect("Found invalid glob pattern!"); + + for file in modified_files { + if glob.matches(file.as_ref()) { + suggestions.extend_from_slice(&sug.1); + } + } + } + + // dynamic suggestions + for sug in DYNAMIC_SUGGESTIONS { + for file in modified_files { + let sugs = sug(Path::new(file.as_ref())); + + suggestions.extend_from_slice(&sugs); + } + } + + suggestions.sort(); + suggestions.dedup(); + + suggestions +} + +#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Debug)] +pub struct Suggestion { + pub cmd: String, + pub stage: Option, + pub paths: Vec, +} + +impl Suggestion { + pub fn new(cmd: &str, stage: Option, paths: &[&str]) -> Self { + Self { cmd: cmd.to_owned(), stage, paths: paths.iter().map(|p| p.to_string()).collect() } + } + + pub fn with_single_path(cmd: &str, stage: Option, path: &str) -> Self { + Self::new(cmd, stage, &[path]) + } +} + +impl Display for Suggestion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{} ", self.cmd)?; + + for path in &self.paths { + write!(f, "{} ", path)?; + } + + if let Some(stage) = self.stage { + write!(f, "{}", stage)?; + } else { + // write a sentinel value here (in place of a stage) to be consumed + // by the shim in bootstrap, it will be read and ignored. + write!(f, "N/A")?; + } + + Ok(()) + } +} diff --git a/src/tools/suggest-tests/src/main.rs b/src/tools/suggest-tests/src/main.rs new file mode 100644 index 0000000000000..0b541b60cba98 --- /dev/null +++ b/src/tools/suggest-tests/src/main.rs @@ -0,0 +1,27 @@ +use std::process::ExitCode; + +use build_helper::git::get_git_modified_files; +use suggest_tests::get_suggestions; + +fn main() -> ExitCode { + let modified_files = get_git_modified_files(None, &Vec::new()); + let modified_files = match modified_files { + Ok(Some(files)) => files, + Ok(None) => { + eprintln!("git error"); + return ExitCode::FAILURE; + } + Err(err) => { + eprintln!("Could not get modified files from git: \"{err}\""); + return ExitCode::FAILURE; + } + }; + + let suggestions = get_suggestions(&modified_files); + + for sug in &suggestions { + println!("{sug}"); + } + + ExitCode::SUCCESS +} diff --git a/src/tools/suggest-tests/src/static_suggestions.rs b/src/tools/suggest-tests/src/static_suggestions.rs new file mode 100644 index 0000000000000..d8166ead8c49d --- /dev/null +++ b/src/tools/suggest-tests/src/static_suggestions.rs @@ -0,0 +1,24 @@ +use crate::{sug, Suggestion}; + +// FIXME: perhaps this could use `std::lazy` when it is stablizied +macro_rules! static_suggestions { + ($( $glob:expr => [ $( $suggestion:expr ),* ] ),*) => { + pub(crate) const STATIC_SUGGESTIONS: ::once_cell::unsync::Lazy)>> + = ::once_cell::unsync::Lazy::new(|| vec![ $( ($glob, vec![ $($suggestion),* ]) ),*]); + } +} + +static_suggestions! { + "*.md" => [ + sug!("test", 0, ["linkchecker"]) + ], + + "compiler/*" => [ + sug!("check"), + sug!("test", 1, ["src/test/ui", "src/test/run-make"]) + ], + + "src/librustdoc/*" => [ + sug!("test", 1, ["rustdoc"]) + ] +} diff --git a/src/tools/suggest-tests/src/tests.rs b/src/tools/suggest-tests/src/tests.rs new file mode 100644 index 0000000000000..5bc1a7df7ca15 --- /dev/null +++ b/src/tools/suggest-tests/src/tests.rs @@ -0,0 +1,21 @@ +macro_rules! sugg_test { + ( $( $name:ident: $paths:expr => $suggestions:expr ),* ) => { + $( + #[test] + fn $name() { + let suggestions = crate::get_suggestions(&$paths).into_iter().map(|s| s.to_string()).collect::>(); + assert_eq!(suggestions, $suggestions); + } + )* + }; +} + +sugg_test! { + test_error_code_docs: ["compiler/rustc_error_codes/src/error_codes/E0000.md"] => + ["check N/A", "test compiler/rustc_error_codes N/A", "test linkchecker 0", "test src/test/ui src/test/run-make 1"], + + test_rustdoc: ["src/librustdoc/src/lib.rs"] => ["test rustdoc 1"], + + test_rustdoc_and_libstd: ["src/librustdoc/src/lib.rs", "library/std/src/lib.rs"] => + ["test library/std N/A", "test rustdoc 1"] +} diff --git a/tests/rustdoc-ui/run-directory.rs b/tests/rustdoc-ui/run-directory.rs index bbceaaf824f92..b8d0647f08d8e 100644 --- a/tests/rustdoc-ui/run-directory.rs +++ b/tests/rustdoc-ui/run-directory.rs @@ -2,8 +2,8 @@ // revisions: correct incorrect // check-pass -// [correct]compile-flags:--test --test-run-directory={{src-base}} -Zunstable-options -// [incorrect]compile-flags:--test --test-run-directory={{src-base}}/coverage -Zunstable-options +// [correct]compile-flags:--test --test-run-directory={{src-base}} +// [incorrect]compile-flags:--test --test-run-directory={{src-base}}/coverage // normalize-stdout-test: "tests/rustdoc-ui" -> "$$DIR" // normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" diff --git a/tests/ui-fulldeps/stable-mir/crate-info.rs b/tests/ui-fulldeps/stable-mir/crate-info.rs index 03dab2350402d..dfde8c97ec264 100644 --- a/tests/ui-fulldeps/stable-mir/crate-info.rs +++ b/tests/ui-fulldeps/stable-mir/crate-info.rs @@ -29,6 +29,8 @@ fn test_stable_mir(tcx: TyCtxt<'_>) { let local = stable_mir::local_crate(); assert_eq!(&local.name, CRATE_NAME); + assert_eq!(stable_mir::entry_fn(), None); + // Find items in the local crate. let items = stable_mir::all_local_items(); assert!(get_item(tcx, &items, (DefKind::Fn, "foo_bar")).is_some());