Auto merge of #110324 - JohnTitor:rollup-jq31pd1, r=JohnTitor

Rollup of 7 pull requests

Successful merges:

 - #103682 (Stabilize rustdoc `--test-run-directory`)
 - #106249 (Create "suggested tests" tool in `rustbuild`)
 - #110047 (Add link to `collections` docs to `extend` trait)
 - #110269 (Add `tidy-alphabetical` to features in `core`)
 - #110292 (Add `tidy-alphabetical` to features in `alloc` & `std`)
 - #110305 (rustdoc-search: use ES6 `Map` and `Set` where they make sense)
 - #110315 (Add a stable MIR way to get the main function)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors
2023-04-14 15:06:51 +00:00
28 changed files with 579 additions and 141 deletions
+11 -2
View File
@@ -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"
+1
View File
@@ -44,6 +44,7 @@ members = [
"src/tools/lld-wrapper",
"src/tools/collect-license-metadata",
"src/tools/generate-copyright",
"src/tools/suggest-tests",
]
exclude = [
@@ -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<stable_mir::CrateItem> {
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();
@@ -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<CrateItem> {
crate::rustc_smir::entry_fn()
}
/// Access to the local crate.
pub fn local_crate() -> Crate {
crate::rustc_smir::local_crate()
+26 -22
View File
@@ -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_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_box)]
#![feature(const_convert)]
#![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)]
+29 -23
View File
@@ -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_notable_trait)]
#![feature(generic_arg_infer)]
#![feature(rustdoc_internals)]
#![feature(exhaustive_patterns)]
#![feature(doc_cfg_hide)]
#![feature(doc_notable_trait)]
#![feature(exhaustive_patterns)]
#![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)]
+5 -2
View File
@@ -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.
+19 -7
View File
@@ -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]
+21 -1
View File
@@ -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<PathBuf>,
stage: Option<u32>,
) -> 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);
}
+10 -14
View File
@@ -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<usize>,
pub ccache: Option<String>,
@@ -240,23 +239,20 @@ pub struct Config {
pub initial_rustfmt: RefCell<RustfmtState>,
}
#[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<String, String>,
pub rustfmt: Option<RustfmtMetadata>,
}
#[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<PathBuf>,
@@ -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 { .. } => {}
}
}
+11 -3
View File
@@ -84,8 +84,7 @@ pub struct Flags {
pub free_args: Option<Vec<String>>,
}
#[derive(Debug)]
#[cfg_attr(test, derive(Clone))]
#[derive(Debug, Clone)]
pub enum Subcommand {
Build {
paths: Vec<PathBuf>,
@@ -149,6 +148,9 @@ pub enum Subcommand {
Setup {
profile: Option<PathBuf>,
},
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 <subcommand> -h`",
);
@@ -349,6 +352,9 @@ To learn more about a subcommand, run `./x.py <subcommand> -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::<Vec<PathBuf>>();
@@ -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,
}
}
+14 -5
View File
@@ -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<String>,
deps: HashSet<Interned<String>>,
@@ -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) {
+80
View File
@@ -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:
// <x_subcommand> {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<PathBuf> = sections.map(|p| PathBuf::from_str(p).unwrap()).collect();
(cmd, stage, paths)
})
.collect::<Vec<_>>();
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");
}
}
+36
View File
@@ -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,
+1
View File
@@ -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)]
+14 -1
View File
@@ -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:
@@ -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.
+6 -1
View File
@@ -65,6 +65,11 @@ let Row;
*/
let ResultsTable;
/**
* @typedef {Map<String, ResultObject>}
*/
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.
+67 -57
View File
@@ -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);
}
}
}
+1 -1
View File
@@ -284,7 +284,7 @@ fn opts() -> Vec<RustcOptGroup> {
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",
+9
View File
@@ -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"
@@ -0,0 +1,23 @@
use std::path::Path;
use crate::Suggestion;
type DynamicSuggestion = fn(&Path) -> Vec<Suggestion>;
pub(crate) const DYNAMIC_SUGGESTIONS: &[DynamicSuggestion] = &[|path: &Path| -> Vec<Suggestion> {
if path.starts_with("compiler/") || path.starts_with("library/") {
let path = path.components().take(2).collect::<Vec<_>>();
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()
}
}];
+96
View File
@@ -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<T: AsRef<str>>(modified_files: &[T]) -> Vec<Suggestion> {
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<u32>,
pub paths: Vec<String>,
}
impl Suggestion {
pub fn new(cmd: &str, stage: Option<u32>, 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<u32>, 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(())
}
}
+27
View File
@@ -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
}
@@ -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<Vec<(&'static str, Vec<Suggestion>)>>
= ::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"])
]
}
+21
View File
@@ -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::<Vec<_>>();
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"]
}
+2 -2
View File
@@ -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"
@@ -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());