From fc8df06f4feb6cc051e15e6596821d276b0797bb Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Mon, 14 Apr 2025 18:24:30 +0200 Subject: [PATCH 01/18] update submodules if the directory doesn't exist --- src/bootstrap/src/core/config/config.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 25ec64f90b53d..2266e61bf6088 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2888,6 +2888,13 @@ impl Config { let absolute_path = self.src.join(relative_path); + // NOTE: This check is required because `jj git clone` doesn't create directories for + // submodules, they are completely ignored. The code below assumes this directory exists, + // so create it here. + if !absolute_path.exists() { + t!(fs::create_dir_all(&absolute_path)); + } + // NOTE: The check for the empty directory is here because when running x.py the first time, // the submodule won't be checked out. Check it out now so we can build it. if !GitInfo::new(false, &absolute_path).is_managed_git_subrepository() From 1397dabd1ec2e58f0cea27fd281dac7104083cca Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Mon, 14 Apr 2025 19:04:34 +0200 Subject: [PATCH 02/18] fix typo --- src/tools/compiletest/src/runtest.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 24fc2ddb74104..093dc7fa56caa 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2383,7 +2383,7 @@ impl<'test> TestCx<'test> { if json { // escaped newlines in json strings should be readable - // in the stderr files. There's no point int being correct, + // in the stderr files. There's no point in being correct, // since only humans process the stderr files. // Thus we just turn escaped newlines back into newlines. normalized = normalized.replace("\\n", "\n"); From 6c441cc7a53e3c9ca0dd1957b8055ccdd851b488 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Mon, 14 Apr 2025 19:04:34 +0200 Subject: [PATCH 03/18] canonicalize test build dir before normalizing it Fix fixes failures of the following tests when build directory is a symlink: - `tests/ui/error-codes/E{0464,0523}.rs` - `tests/ui/crate-loading/crateresolve{1,2}.rs` (those are the same tests) --- src/tools/compiletest/src/runtest.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 093dc7fa56caa..fc83ea420430b 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2375,9 +2375,13 @@ impl<'test> TestCx<'test> { let rust_src_dir = rust_src_dir.read_link_utf8().unwrap_or(rust_src_dir.to_path_buf()); normalize_path(&rust_src_dir.join("library"), "$SRC_DIR_REAL"); + // Canonicalize test build directory path. + // Without this some tests fail if build directory is a symlink. + let output_base_dir = self.output_base_dir().canonicalize_utf8().unwrap(); + // eg. // /home/user/rust/build/x86_64-unknown-linux-gnu/test/ui//$name.$revision.$mode/ - normalize_path(&self.output_base_dir(), "$TEST_BUILD_DIR"); + normalize_path(&output_base_dir, "$TEST_BUILD_DIR"); // eg. /home/user/rust/build normalize_path(&self.config.build_root, "$BUILD_DIR"); From 502b630cd1042d8b2b613a12278a01c641cf096a Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Tue, 15 Apr 2025 13:11:14 +0200 Subject: [PATCH 04/18] tidy: don't crush on non-existent submodules --- src/tools/tidy/src/deps.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 88c2a02798ace..46e55859a5785 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -682,8 +682,10 @@ pub static CRATES: &[&str] = &[ pub fn has_missing_submodule(root: &Path, submodules: &[&str]) -> bool { !CiEnv::is_ci() && submodules.iter().any(|submodule| { + let path = root.join(submodule); + !path.exists() // If the directory is empty, we can consider it as an uninitialized submodule. - read_dir(root.join(submodule)).unwrap().next().is_none() + || read_dir(path).unwrap().next().is_none() }) } From 52f4b16075bf01d2c8f8539c26fb9b135967713f Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Tue, 15 Apr 2025 14:06:51 +0200 Subject: [PATCH 05/18] use helper function instead of writing rustfmt stamp by hand --- src/bootstrap/src/core/build_steps/format.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index b1a97bde97b5b..43b831adf1f70 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -81,7 +81,8 @@ fn update_rustfmt_version(build: &Builder<'_>) { let Some((version, stamp_file)) = get_rustfmt_version(build) else { return; }; - t!(std::fs::write(stamp_file.path(), version)) + + t!(stamp_file.add_stamp(version).write()); } /// Returns the Rust files modified between the `merge-base` of HEAD and From dda4d7bc44aa4909aae72cd38965a11ccf46c02e Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Tue, 15 Apr 2025 14:06:51 +0200 Subject: [PATCH 06/18] slightly correct comments and diagnostics about checking modifications I feel like they are still wrong, but maybe less so .-. The `info:` was unhelpful -- we only use upstream in CI nowdays. --- src/bootstrap/src/core/build_steps/format.rs | 17 +++++++++-------- src/build_helper/src/git.rs | 6 ++++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index 43b831adf1f70..25d5f97d7eec5 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -85,11 +85,15 @@ fn update_rustfmt_version(build: &Builder<'_>) { t!(stamp_file.add_stamp(version).write()); } -/// Returns the Rust files modified between the `merge-base` of HEAD and -/// rust-lang/master and what is now on the disk. Does not include removed files. +/// Returns the Rust files modified between the last merge commit and what is now on the disk. +/// Does not include removed files. /// /// Returns `None` if all files should be formatted. fn get_modified_rs_files(build: &Builder<'_>) -> Result>, String> { + // In CI `get_git_modified_files` returns something different to normal environment. + // This shouldn't be called in CI anyway. + assert!(!build.config.is_running_on_ci); + if !verify_rustfmt_version(build) { return Ok(None); } @@ -104,7 +108,7 @@ struct RustfmtConfig { // Prints output describing a collection of paths, with lines such as "formatted modified file // foo/bar/baz" or "skipped 20 untracked files". -fn print_paths(build: &Builder<'_>, verb: &str, adjective: Option<&str>, paths: &[String]) { +fn print_paths(verb: &str, adjective: Option<&str>, paths: &[String]) { let len = paths.len(); let adjective = if let Some(adjective) = adjective { format!("{adjective} ") } else { String::new() }; @@ -115,9 +119,6 @@ fn print_paths(build: &Builder<'_>, verb: &str, adjective: Option<&str>, paths: } else { println!("fmt: {verb} {len} {adjective}files"); } - if len > 1000 && !build.config.is_running_on_ci { - println!("hint: if this number seems too high, try running `git fetch origin master`"); - } } pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { @@ -190,7 +191,7 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { ) .map(|x| x.to_string()) .collect(); - print_paths(build, "skipped", Some("untracked"), &untracked_paths); + print_paths("skipped", Some("untracked"), &untracked_paths); for untracked_path in untracked_paths { // The leading `/` makes it an exact match against the @@ -319,7 +320,7 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { }); let mut paths = formatted_paths.into_inner().unwrap(); paths.sort(); - print_paths(build, if check { "checked" } else { "formatted" }, adjective, &paths); + print_paths(if check { "checked" } else { "formatted" }, adjective, &paths); drop(tx); diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs index 693e0fc8f46d8..f5347c3082410 100644 --- a/src/build_helper/src/git.rs +++ b/src/build_helper/src/git.rs @@ -114,7 +114,9 @@ fn git_upstream_merge_base( Ok(output_result(git.arg("merge-base").arg(&updated_master).arg("HEAD"))?.trim().to_owned()) } -/// Searches for the nearest merge commit in the repository that also exists upstream. +/// Searches for the nearest merge commit in the repository. +/// +/// **In CI** finds the nearest merge commit that *also exists upstream*. /// /// It looks for the most recent commit made by the merge bot by matching the author's email /// address with the merge bot's email. @@ -165,7 +167,7 @@ pub fn get_closest_merge_commit( Ok(output_result(&mut git)?.trim().to_owned()) } -/// Returns the files that have been modified in the current branch compared to the master branch. +/// Returns the files that have been modified in the current branch compared to the last merge. /// The `extensions` parameter can be used to filter the files by their extension. /// Does not include removed files. /// If `extensions` is empty, all files will be returned. From 8934ac575988f2c15b7910d2f683b2792e5916cb Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Tue, 15 Apr 2025 14:06:51 +0200 Subject: [PATCH 07/18] add a comment for code that isn't --- src/bootstrap/src/core/build_steps/format.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index 25d5f97d7eec5..6641c4b4e139b 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -214,7 +214,13 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { override_builder.add(&format!("/{file}")).expect(&file); } } - Ok(None) => {} + Ok(None) => { + // NOTE: `Ok(None)` signifies that we need to format all files. + // The tricky part here is that if `override_builder` isn't given any white + // list files (i.e. files to be formatted, added without leading `!`), it + // will instead look for *all* files. So, by doing nothing here, we are + // actually making it so we format all files. + } Err(err) => { eprintln!("fmt warning: Something went wrong running git commands:"); eprintln!("fmt warning: {err}"); From 812095031b82b5974e03dafd6bf33d69de712474 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Tue, 15 Apr 2025 23:51:10 +0900 Subject: [PATCH 08/18] Add test for issue 125668 --- tests/ui/consts/const-blocks/const-block-in-array-size.rs | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/ui/consts/const-blocks/const-block-in-array-size.rs diff --git a/tests/ui/consts/const-blocks/const-block-in-array-size.rs b/tests/ui/consts/const-blocks/const-block-in-array-size.rs new file mode 100644 index 0000000000000..ecab24322869c --- /dev/null +++ b/tests/ui/consts/const-blocks/const-block-in-array-size.rs @@ -0,0 +1,5 @@ +//@ check-pass + +type A = [u32; const { 2 }]; + +fn main() {} From 11e5987d017b4ab8dfc522d674aea704aad8605a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 15 Apr 2025 18:46:07 +0000 Subject: [PATCH 09/18] Don't compute name of associated item if it's an RPITIT --- .../rustc_hir_analysis/src/hir_ty_lowering/errors.rs | 3 +-- .../in-trait/dont-probe-missing-item-name.rs | 12 ++++++++++++ .../in-trait/dont-probe-missing-item-name.stderr | 9 +++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tests/ui/impl-trait/in-trait/dont-probe-missing-item-name.rs create mode 100644 tests/ui/impl-trait/in-trait/dont-probe-missing-item-name.stderr diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 72f219bfeb802..3759a224ff75b 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -204,8 +204,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .iter() .flat_map(|trait_def_id| tcx.associated_items(*trait_def_id).in_definition_order()) .filter_map(|item| { - (!item.is_impl_trait_in_trait() && item.as_tag() == assoc_tag) - .then_some(item.name()) + (!item.is_impl_trait_in_trait() && item.as_tag() == assoc_tag).then(|| item.name()) }) .collect(); diff --git a/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name.rs b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name.rs new file mode 100644 index 0000000000000..450f41e209dfb --- /dev/null +++ b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name.rs @@ -0,0 +1,12 @@ +// Regression test for . + +// Test that we don't try to get the (nonexistent) name of the RPITIT in `Trait::foo` +// when emitting an error for a missing associated item `Trait::Output`. + +trait Trait { + fn foo() -> impl Sized; + fn bar() -> Self::Output; + //~^ ERROR associated type `Output` not found for `Self` +} + +fn main() {} diff --git a/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name.stderr b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name.stderr new file mode 100644 index 0000000000000..74e15785af19c --- /dev/null +++ b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name.stderr @@ -0,0 +1,9 @@ +error[E0220]: associated type `Output` not found for `Self` + --> $DIR/dont-probe-missing-item-name.rs:8:23 + | +LL | fn bar() -> Self::Output; + | ^^^^^^ associated type `Output` not found + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0220`. From 2020adba86cb2852b11ab11a440975fb331e8424 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 15 Apr 2025 16:59:16 +0200 Subject: [PATCH 10/18] Fix wrong suggestion for async gen block and add regression ui test for #139839 --- .../src/diagnostics/conflict_errors.rs | 13 +++-- .../async-gen-move-suggestion.fixed | 35 ++++++++++++++ .../async-await/async-gen-move-suggestion.rs | 35 ++++++++++++++ .../async-gen-move-suggestion.stderr | 47 +++++++++++++++++++ 4 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 tests/ui/async-await/async-gen-move-suggestion.fixed create mode 100644 tests/ui/async-await/async-gen-move-suggestion.rs create mode 100644 tests/ui/async-await/async-gen-move-suggestion.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index fe6dff7ff1b63..959cf9fa51399 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3376,10 +3376,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) { Ok(string) => { - let coro_prefix = if string.starts_with("async") { - // `async` is 5 chars long. Not using `.len()` to avoid the cast from `usize` - // to `u32`. - Some(5) + let coro_prefix = if let Some(sub) = string.strip_prefix("async") { + let trimmed_sub = sub.trim_end(); + if trimmed_sub.ends_with("gen") { + // `async` is 5 chars long. + Some((trimmed_sub.len() + 5) as _) + } else { + // `async` is 5 chars long. + Some(5) + } } else if string.starts_with("gen") { // `gen` is 3 chars long Some(3) diff --git a/tests/ui/async-await/async-gen-move-suggestion.fixed b/tests/ui/async-await/async-gen-move-suggestion.fixed new file mode 100644 index 0000000000000..d802076552895 --- /dev/null +++ b/tests/ui/async-await/async-gen-move-suggestion.fixed @@ -0,0 +1,35 @@ +// This is a regression test for . +// It ensures that the "add `move` keyword" suggestion is valid. + +//@ run-rustfix +//@ edition:2024 + +#![feature(coroutines)] +#![feature(gen_blocks)] +#![feature(async_iterator)] + +use std::async_iter::AsyncIterator; + +#[allow(dead_code)] +fn moved() -> impl AsyncIterator { + let mut x = "foo".to_string(); + + async gen move { //~ ERROR + x.clear(); + for x in 3..6 { yield x } + } +} + +#[allow(dead_code)] +fn check_with_whitespace_chars() -> impl AsyncIterator { + let mut x = "foo".to_string(); + + async // Just to check that whitespace characters are correctly handled + gen move { //~^ ERROR + x.clear(); + for x in 3..6 { yield x } + } +} + +fn main() { +} diff --git a/tests/ui/async-await/async-gen-move-suggestion.rs b/tests/ui/async-await/async-gen-move-suggestion.rs new file mode 100644 index 0000000000000..825fb0fd1898c --- /dev/null +++ b/tests/ui/async-await/async-gen-move-suggestion.rs @@ -0,0 +1,35 @@ +// This is a regression test for . +// It ensures that the "add `move` keyword" suggestion is valid. + +//@ run-rustfix +//@ edition:2024 + +#![feature(coroutines)] +#![feature(gen_blocks)] +#![feature(async_iterator)] + +use std::async_iter::AsyncIterator; + +#[allow(dead_code)] +fn moved() -> impl AsyncIterator { + let mut x = "foo".to_string(); + + async gen { //~ ERROR + x.clear(); + for x in 3..6 { yield x } + } +} + +#[allow(dead_code)] +fn check_with_whitespace_chars() -> impl AsyncIterator { + let mut x = "foo".to_string(); + + async // Just to check that whitespace characters are correctly handled + gen { //~^ ERROR + x.clear(); + for x in 3..6 { yield x } + } +} + +fn main() { +} diff --git a/tests/ui/async-await/async-gen-move-suggestion.stderr b/tests/ui/async-await/async-gen-move-suggestion.stderr new file mode 100644 index 0000000000000..b8cdb8be7a4ae --- /dev/null +++ b/tests/ui/async-await/async-gen-move-suggestion.stderr @@ -0,0 +1,47 @@ +error[E0373]: async gen block may outlive the current function, but it borrows `x`, which is owned by the current function + --> $DIR/async-gen-move-suggestion.rs:17:5 + | +LL | async gen { + | ^^^^^^^^^ may outlive borrowed value `x` +LL | x.clear(); + | - `x` is borrowed here + | +note: async gen block is returned here + --> $DIR/async-gen-move-suggestion.rs:17:5 + | +LL | / async gen { +LL | | x.clear(); +LL | | for x in 3..6 { yield x } +LL | | } + | |_____^ +help: to force the async gen block to take ownership of `x` (and any other referenced variables), use the `move` keyword + | +LL | async gen move { + | ++++ + +error[E0373]: async gen block may outlive the current function, but it borrows `x`, which is owned by the current function + --> $DIR/async-gen-move-suggestion.rs:27:5 + | +LL | / async // Just to check that whitespace characters are correctly handled +LL | | gen { + | |_______^ may outlive borrowed value `x` +LL | x.clear(); + | - `x` is borrowed here + | +note: async gen block is returned here + --> $DIR/async-gen-move-suggestion.rs:27:5 + | +LL | / async // Just to check that whitespace characters are correctly handled +LL | | gen { +LL | | x.clear(); +LL | | for x in 3..6 { yield x } +LL | | } + | |_____^ +help: to force the async gen block to take ownership of `x` (and any other referenced variables), use the `move` keyword + | +LL | gen move { + | ++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0373`. From 90aec1367481ab05c78fefad314e03b26e84564f Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Tue, 15 Apr 2025 14:06:51 +0200 Subject: [PATCH 11/18] commit rustfmt stump in `x t tidy` even on `check` If checking succeeded, it's equivalent to successfully formatting. --- src/bootstrap/src/core/build_steps/format.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index 6641c4b4e139b..9da8b27a91778 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -336,7 +336,10 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { crate::exit!(1); } - if !check { - update_rustfmt_version(build); - } + // Update `build/.rustfmt-stamp`, allowing this code to ignore files which have not been changed + // since last merge. + // + // NOTE: Because of the exit above, this is only reachable if formatting / format checking + // succeeded. So we are not commiting the version if formatting was not good. + update_rustfmt_version(build); } From 89b4eba49cc15e0a4e53f7816473201d12be5f4f Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Tue, 15 Apr 2025 10:09:31 +0200 Subject: [PATCH 12/18] normalize canonical and non-canonical paths in compiletest Apparently there are tests that print canonical paths *and* tests which print non-canonical paths. An example of the latter is `tests/ui/type_length_limit.rs`. --- src/tools/compiletest/src/runtest.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index fc83ea420430b..eb298060b2ece 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2375,13 +2375,16 @@ impl<'test> TestCx<'test> { let rust_src_dir = rust_src_dir.read_link_utf8().unwrap_or(rust_src_dir.to_path_buf()); normalize_path(&rust_src_dir.join("library"), "$SRC_DIR_REAL"); - // Canonicalize test build directory path. - // Without this some tests fail if build directory is a symlink. - let output_base_dir = self.output_base_dir().canonicalize_utf8().unwrap(); - // eg. // /home/user/rust/build/x86_64-unknown-linux-gnu/test/ui//$name.$revision.$mode/ - normalize_path(&output_base_dir, "$TEST_BUILD_DIR"); + normalize_path(&self.output_base_dir(), "$TEST_BUILD_DIR"); + // Same as above, but with a canonicalized path. + // This is required because some tests print canonical paths inside test build directory, + // so if the build directory is a symlink, normalization doesn't help. + // + // NOTE: There are also tests which print the non-canonical name, so we need both this and + // the above normalizations. + normalize_path(&self.output_base_dir().canonicalize_utf8().unwrap(), "$TEST_BUILD_DIR"); // eg. /home/user/rust/build normalize_path(&self.config.build_root, "$BUILD_DIR"); From f35c85f72fc1249f0553d97482ca29fe3fa0a165 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Tue, 8 Apr 2025 16:11:28 -0700 Subject: [PATCH 13/18] Add unstable foo::bar extern command line arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also refactors some of the crate name parsing code and adds unit tests Issue #122349 Co-authored-by: León Orell Valerian Liehr --- compiler/rustc_session/src/config.rs | 39 +------- compiler/rustc_session/src/config/externs.rs | 79 ++++++++++++++++ .../rustc_session/src/config/externs/tests.rs | 92 +++++++++++++++++++ compiler/rustc_session/src/options.rs | 2 + 4 files changed, 178 insertions(+), 34 deletions(-) create mode 100644 compiler/rustc_session/src/config/externs.rs create mode 100644 compiler/rustc_session/src/config/externs/tests.rs diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index fc05470d941c4..202378560eed7 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -14,6 +14,7 @@ use std::str::{self, FromStr}; use std::sync::LazyLock; use std::{cmp, fmt, fs, iter}; +use externs::{ExternOpt, split_extern_opt}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey}; use rustc_errors::emitter::HumanReadableErrorType; @@ -39,6 +40,7 @@ use crate::utils::CanonicalizedPath; use crate::{EarlyDiagCtxt, HashStableContext, Session, filesearch, lint}; mod cfg; +mod externs; mod native_libs; pub mod sigpipe; @@ -2205,44 +2207,13 @@ pub fn parse_externs( matches: &getopts::Matches, unstable_opts: &UnstableOptions, ) -> Externs { - fn is_ascii_ident(string: &str) -> bool { - let mut chars = string.chars(); - if let Some(start) = chars.next() - && (start.is_ascii_alphabetic() || start == '_') - { - chars.all(|char| char.is_ascii_alphanumeric() || char == '_') - } else { - false - } - } - let is_unstable_enabled = unstable_opts.unstable_options; let mut externs: BTreeMap = BTreeMap::new(); for arg in matches.opt_strs("extern") { - let (name, path) = match arg.split_once('=') { - None => (arg, None), - Some((name, path)) => (name.to_string(), Some(Path::new(path))), - }; - let (options, name) = match name.split_once(':') { - None => (None, name), - Some((opts, name)) => (Some(opts), name.to_string()), - }; - - if !is_ascii_ident(&name) { - let mut error = early_dcx.early_struct_fatal(format!( - "crate name `{name}` passed to `--extern` is not a valid ASCII identifier" - )); - let adjusted_name = name.replace('-', "_"); - if is_ascii_ident(&adjusted_name) { - #[allow(rustc::diagnostic_outside_of_impl)] // FIXME - error.help(format!( - "consider replacing the dashes with underscores: `{adjusted_name}`" - )); - } - error.emit(); - } + let ExternOpt { crate_name: name, path, options } = + split_extern_opt(early_dcx, unstable_opts, &arg).unwrap_or_else(|e| e.emit()); - let path = path.map(|p| CanonicalizedPath::new(p)); + let path = path.map(|p| CanonicalizedPath::new(p.as_path())); let entry = externs.entry(name.to_owned()); diff --git a/compiler/rustc_session/src/config/externs.rs b/compiler/rustc_session/src/config/externs.rs new file mode 100644 index 0000000000000..1420ee38bf214 --- /dev/null +++ b/compiler/rustc_session/src/config/externs.rs @@ -0,0 +1,79 @@ +//! This module contains code to help parse and manipulate `--extern` arguments. + +use std::path::PathBuf; + +use rustc_errors::{Diag, FatalAbort}; + +use super::UnstableOptions; +use crate::EarlyDiagCtxt; + +#[cfg(test)] +mod tests; + +/// Represents the pieces of an `--extern` argument. +pub(crate) struct ExternOpt { + pub(crate) crate_name: String, + pub(crate) path: Option, + pub(crate) options: Option, +} + +/// Breaks out the major components of an `--extern` argument. +/// +/// The options field will be a string containing comma-separated options that will need further +/// parsing and processing. +pub(crate) fn split_extern_opt<'a>( + early_dcx: &'a EarlyDiagCtxt, + unstable_opts: &UnstableOptions, + extern_opt: &str, +) -> Result> { + let (name, path) = match extern_opt.split_once('=') { + None => (extern_opt.to_string(), None), + Some((name, path)) => (name.to_string(), Some(PathBuf::from(path))), + }; + let (options, crate_name) = match name.split_once(':') { + None => (None, name), + Some((opts, crate_name)) => { + if unstable_opts.namespaced_crates && crate_name.starts_with(':') { + // If the name starts with `:`, we know this was actually something like `foo::bar` and + // not a set of options. We can just use the original name as the crate name. + (None, name) + } else { + (Some(opts.to_string()), crate_name.to_string()) + } + } + }; + + if !valid_crate_name(&crate_name, unstable_opts) { + let mut error = early_dcx.early_struct_fatal(format!( + "crate name `{crate_name}` passed to `--extern` is not a valid ASCII identifier" + )); + let adjusted_name = crate_name.replace('-', "_"); + if is_ascii_ident(&adjusted_name) { + #[allow(rustc::diagnostic_outside_of_impl)] // FIXME + error + .help(format!("consider replacing the dashes with underscores: `{adjusted_name}`")); + } + return Err(error); + } + + Ok(ExternOpt { crate_name, path, options }) +} + +fn valid_crate_name(name: &str, unstable_opts: &UnstableOptions) -> bool { + match name.split_once("::") { + Some((a, b)) if unstable_opts.namespaced_crates => is_ascii_ident(a) && is_ascii_ident(b), + Some(_) => false, + None => is_ascii_ident(name), + } +} + +fn is_ascii_ident(string: &str) -> bool { + let mut chars = string.chars(); + if let Some(start) = chars.next() + && (start.is_ascii_alphabetic() || start == '_') + { + chars.all(|char| char.is_ascii_alphanumeric() || char == '_') + } else { + false + } +} diff --git a/compiler/rustc_session/src/config/externs/tests.rs b/compiler/rustc_session/src/config/externs/tests.rs new file mode 100644 index 0000000000000..6544886951572 --- /dev/null +++ b/compiler/rustc_session/src/config/externs/tests.rs @@ -0,0 +1,92 @@ +use std::path::PathBuf; + +use super::split_extern_opt; +use crate::EarlyDiagCtxt; +use crate::config::UnstableOptions; + +/// Verifies split_extern_opt handles the supported cases. +#[test] +fn test_split_extern_opt() { + let early_dcx = EarlyDiagCtxt::new(<_>::default()); + let unstable_opts = &UnstableOptions::default(); + + let extern_opt = + split_extern_opt(&early_dcx, unstable_opts, "priv,noprelude:foo=libbar.rlib").unwrap(); + assert_eq!(extern_opt.crate_name, "foo"); + assert_eq!(extern_opt.path, Some(PathBuf::from("libbar.rlib"))); + assert_eq!(extern_opt.options, Some("priv,noprelude".to_string())); + + let extern_opt = split_extern_opt(&early_dcx, unstable_opts, "priv,noprelude:foo").unwrap(); + assert_eq!(extern_opt.crate_name, "foo"); + assert_eq!(extern_opt.path, None); + assert_eq!(extern_opt.options, Some("priv,noprelude".to_string())); + + let extern_opt = split_extern_opt(&early_dcx, unstable_opts, "foo=libbar.rlib").unwrap(); + assert_eq!(extern_opt.crate_name, "foo"); + assert_eq!(extern_opt.path, Some(PathBuf::from("libbar.rlib"))); + assert_eq!(extern_opt.options, None); + + let extern_opt = split_extern_opt(&early_dcx, unstable_opts, "foo").unwrap(); + assert_eq!(extern_opt.crate_name, "foo"); + assert_eq!(extern_opt.path, None); + assert_eq!(extern_opt.options, None); +} + +/// Tests some invalid cases for split_extern_opt. +#[test] +fn test_split_extern_opt_invalid() { + let early_dcx = EarlyDiagCtxt::new(<_>::default()); + let unstable_opts = &UnstableOptions::default(); + + // too many `:`s + let result = split_extern_opt(&early_dcx, unstable_opts, "priv:noprelude:foo=libbar.rlib"); + assert!(result.is_err()); + let _ = result.map_err(|e| e.cancel()); + + // can't nest externs without the unstable flag + let result = split_extern_opt(&early_dcx, unstable_opts, "noprelude:foo::bar=libbar.rlib"); + assert!(result.is_err()); + let _ = result.map_err(|e| e.cancel()); +} + +/// Tests some cases for split_extern_opt with nested crates like `foo::bar`. +#[test] +fn test_split_extern_opt_nested() { + let early_dcx = EarlyDiagCtxt::new(<_>::default()); + let unstable_opts = &UnstableOptions { namespaced_crates: true, ..Default::default() }; + + let extern_opt = + split_extern_opt(&early_dcx, unstable_opts, "priv,noprelude:foo::bar=libbar.rlib").unwrap(); + assert_eq!(extern_opt.crate_name, "foo::bar"); + assert_eq!(extern_opt.path, Some(PathBuf::from("libbar.rlib"))); + assert_eq!(extern_opt.options, Some("priv,noprelude".to_string())); + + let extern_opt = + split_extern_opt(&early_dcx, unstable_opts, "priv,noprelude:foo::bar").unwrap(); + assert_eq!(extern_opt.crate_name, "foo::bar"); + assert_eq!(extern_opt.path, None); + assert_eq!(extern_opt.options, Some("priv,noprelude".to_string())); + + let extern_opt = split_extern_opt(&early_dcx, unstable_opts, "foo::bar=libbar.rlib").unwrap(); + assert_eq!(extern_opt.crate_name, "foo::bar"); + assert_eq!(extern_opt.path, Some(PathBuf::from("libbar.rlib"))); + assert_eq!(extern_opt.options, None); + + let extern_opt = split_extern_opt(&early_dcx, unstable_opts, "foo::bar").unwrap(); + assert_eq!(extern_opt.crate_name, "foo::bar"); + assert_eq!(extern_opt.path, None); + assert_eq!(extern_opt.options, None); +} + +/// Tests some invalid cases for split_extern_opt with nested crates like `foo::bar`. +#[test] +fn test_split_extern_opt_nested_invalid() { + let early_dcx = EarlyDiagCtxt::new(<_>::default()); + let unstable_opts = &UnstableOptions { namespaced_crates: true, ..Default::default() }; + + // crates can only be nested one deep. + let result = + split_extern_opt(&early_dcx, unstable_opts, "priv,noprelude:foo::bar::baz=libbar.rlib"); + assert!(result.is_err()); + let _ = result.map_err(|e| e.cancel()); +} diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index c70f1500d3930..bd66f835f1201 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2331,6 +2331,8 @@ options! { "the size at which the `large_assignments` lint starts to be emitted"), mutable_noalias: bool = (true, parse_bool, [TRACKED], "emit noalias metadata for mutable references (default: yes)"), + namespaced_crates: bool = (false, parse_bool, [TRACKED], + "allow crates to be namespaced by other crates (default: no)"), next_solver: NextSolverConfig = (NextSolverConfig::default(), parse_next_solver_config, [TRACKED], "enable and configure the next generation trait solver used by rustc"), nll_facts: bool = (false, parse_bool, [UNTRACKED], From f3f53d2183a4980f36ee6836f172a01a8b92c44a Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Tue, 15 Apr 2025 22:48:28 +0200 Subject: [PATCH 14/18] Update books --- src/doc/book | 2 +- src/doc/edition-guide | 2 +- src/doc/nomicon | 2 +- src/doc/reference | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/book b/src/doc/book index 45f05367360f0..d33916341d480 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 45f05367360f033f89235eacbbb54e8d73ce6b70 +Subproject commit d33916341d480caede1d0ae57cbeae23aab23e88 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index 1e27e5e6d5133..467f45637b73e 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit 1e27e5e6d5133ae4612f5cc195c15fc8d51b1c9c +Subproject commit 467f45637b73ec6aa70fb36bc3054bb50b8967ea diff --git a/src/doc/nomicon b/src/doc/nomicon index b4448fa406a6d..0c10c30cc5473 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit b4448fa406a6dccde62d1e2f34f70fc51814cdcc +Subproject commit 0c10c30cc54736c5c194ce98c50e2de84eeb6e79 diff --git a/src/doc/reference b/src/doc/reference index 46435cd4eba11..3340922df189b 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 46435cd4eba11b66acaa42c01da5c80ad88aee4b +Subproject commit 3340922df189bddcbaad17dc3927d51a76bcd5ed From ea1b230170c36ddf95cb24a8542316df9f786e5a Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 15 Apr 2025 15:20:27 -0700 Subject: [PATCH 15/18] Update Cargo.lock for rustbook --- src/tools/rustbook/Cargo.lock | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index e0939afc09bb7..c14372dd6aec4 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -951,6 +951,7 @@ dependencies = [ "once_cell", "pathdiff", "pulldown-cmark 0.10.3", + "railroad", "regex", "semver", "serde_json", @@ -1300,6 +1301,15 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +[[package]] +name = "railroad" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ecedffc46c1b2cb04f4b80e094eae6b3f3f470a9635f1f396dd5206428f6b58" +dependencies = [ + "unicode-width", +] + [[package]] name = "rand" version = "0.8.5" From 766cd3a58320f2ec9e30573f39e377513f1a4443 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 16 Apr 2025 08:35:34 +1000 Subject: [PATCH 16/18] Remove support for `#[rustc_mir(borrowck_graphviz_format="gen_kill")]`. Because it's equivalent to `#[rustc_mir(borrowck_graphviz_format)]`. It used to be distinct, but the distinction was removed in https://github.com/rust-lang/rust/pull/76044/commits/3233fb18a891363a2da36ce69ca16fbb219c96be. --- compiler/rustc_mir_dataflow/src/framework/graphviz.rs | 2 +- compiler/rustc_span/src/symbol.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 448fad2dc3ece..c436b8c0fb019 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -122,7 +122,7 @@ impl RustcMirAttrs { }) } else if attr.has_name(sym::borrowck_graphviz_format) { Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s { - sym::gen_kill | sym::two_phase => Ok(s), + sym::two_phase => Ok(s), _ => { tcx.dcx().emit_err(UnknownFormatter { span: attr.span() }); Err(()) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d1f3eb16e4e4a..2af567f2ec502 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1068,7 +1068,6 @@ symbols! { ge, gen_blocks, gen_future, - gen_kill, generator_clone, generators, generic_arg_infer, From 62882f355e50f9af3ddb84661dfad0559848a3f5 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 16 Apr 2025 08:57:15 +1000 Subject: [PATCH 17/18] Improve `borrowck_graphviz_*` documentation. In particular, `borrowck_graphviz_preflow` no longer exists. --- src/doc/rustc-dev-guide/src/compiler-debugging.md | 3 ++- tests/ui/mir-dataflow/README.md | 9 --------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index 47f3976202228..102e20207792e 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -301,7 +301,8 @@ Right below you can find elaborate explainers on a selected few. Some compiler options for debugging specific features yield graphviz graphs - e.g. the `#[rustc_mir(borrowck_graphviz_postflow="suffix.dot")]` attribute -dumps various borrow-checker dataflow graphs. +on a function dumps various borrow-checker dataflow graphs in conjunction with +`-Zdump-mir-dataflow`. These all produce `.dot` files. To view these files, install graphviz (e.g. `apt-get install graphviz`) and then run the following commands: diff --git a/tests/ui/mir-dataflow/README.md b/tests/ui/mir-dataflow/README.md index a3ab14b23c7db..886020226d031 100644 --- a/tests/ui/mir-dataflow/README.md +++ b/tests/ui/mir-dataflow/README.md @@ -42,12 +42,3 @@ each generated output path. on *entry* to each block, as well as the gen- and kill-sets that were so-called "transfer functions" summarizing the effect of each basic block. - - * (In addition to the `borrowck_graphviz_postflow` attribute-key - noted above, there is also `borrowck_graphviz_preflow`; it has the - same interface and generates the same set of files, but it renders - the dataflow state after building the gen- and kill-sets but - *before* running the dataflow analysis itself, so each entry-set is - just the initial default state for that dataflow analysis. This is - less useful for understanding the error message output in these - tests.) From 6999305926b4199d6a33ae4f82002e72115223a8 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Tue, 15 Apr 2025 19:01:35 +0200 Subject: [PATCH 18/18] Make CodeStat's type sizes a public field --- compiler/rustc_session/src/code_stats.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs index b4597ae2515d4..6b18d450e9e5e 100644 --- a/compiler/rustc_session/src/code_stats.rs +++ b/compiler/rustc_session/src/code_stats.rs @@ -72,7 +72,7 @@ pub struct TypeSizeInfo { #[derive(Default)] pub struct CodeStats { - type_sizes: Lock>, + pub type_sizes: Lock>, } impl CodeStats {