From 05a72d20b0c6b68c279f1a0393a23f71c93bbe4c Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Fri, 16 Oct 2026 18:45:19 +0200 Subject: [PATCH 01/53] Rewrite `--print-config` integration tests The main thing here is to be more explicit on assertions: e.g. to answer are we expecting something to be printed to stdout or stderr? I thought about rewriting the existing macro to handle this case too, but couldn't find a good approach for that, and decided that the plain code, while more lines, was more readable. --- src/tools/rustfmt/tests/rustfmt/main.rs | 153 ++++++++++++++---------- 1 file changed, 93 insertions(+), 60 deletions(-) diff --git a/src/tools/rustfmt/tests/rustfmt/main.rs b/src/tools/rustfmt/tests/rustfmt/main.rs index 5fbf64d8862c1..c37951c085982 100644 --- a/src/tools/rustfmt/tests/rustfmt/main.rs +++ b/src/tools/rustfmt/tests/rustfmt/main.rs @@ -1,6 +1,5 @@ //! Integration tests for rustfmt. -use indoc::panicdoc; use std::env; use std::fs::{File, remove_file}; use std::io::Write; @@ -57,79 +56,113 @@ fn rustfmt(args: &[&str]) -> (String, String) { rustfmt_with_extra(args, None, &[], None) } -macro_rules! assert_that { - ($args:expr, $($check:ident $check_args:tt)&&+) => { - let (stdout, stderr) = rustfmt($args); - if $(!stdout.$check$check_args && !stderr.$check$check_args)||* { - let args = $args; - let expected = stringify!($( $check$check_args )&&*); - panicdoc! {" - Output not expected for rustfmt {args:?} - expected: {expected} - actual stdout: - {stdout} - actual stderr: - {stderr}", - }; - } - }; +#[test] +fn print_config_errors_on_unknown_option() { + let args = &["--print-config", "unknown"]; + + let (stdout, stderr) = rustfmt(args); + + assert_eq!(&stdout, ""); + assert!( + stderr.contains("Unknown print-config option"), + "expected helpful error message, got: {stderr}" + ); } -#[rustfmt_only_ci_test] #[test] -fn print_config() { - assert_that!( - &["--print-config", "unknown"], - starts_with("Unknown print-config option") +fn print_config_default() { + let args = &["--print-config", "default"]; + let (stdout, stderr) = rustfmt(args); + + assert!( + stdout.contains("max_width = 100"), + "expected stdout to contain a default config, got: {stdout}", ); - assert_that!(&["--print-config", "default"], contains("max_width = 100")); - assert_that!(&["--print-config", "minimal"], contains("PATH required")); - assert_that!( - &["--print-config", "minimal", "minimal-config"], - contains("doesn't work with standard input.") + assert_eq!(&stderr, ""); +} + +#[test] +fn print_config_errors_minimal_with_no_path() { + let args = &["--print-config", "minimal"]; + + let (stdout, stderr) = rustfmt(args); + + // NOTE: existing the behaviour, possibly a bug + assert_eq!(&stdout, "\n"); + assert!( + stderr.contains("PATH required"), + "expected helpful error message, got: {stderr}" ); +} - let (stdout, stderr) = rustfmt(&[ - "--print-config", - "minimal", - "minimal-config", - "src/shape.rs", - ]); +#[test] +fn print_config_errors_minimal_without_input_file() { + let args = &["--print-config", "minimal", "minimal.toml"]; + + let (stdout, stderr) = rustfmt(args); + + assert_eq!(&stdout, ""); assert!( - Path::new("minimal-config").exists(), - "stdout:\n{stdout}\nstderr:\n{stderr}" + stderr.contains("doesn't work with standard input"), + "expected helpful error message, got: {stderr}" ); - remove_file("minimal-config").unwrap(); } +// test writes to file within the repo, rust-lang/rust doesn't like that #[rustfmt_only_ci_test] #[test] -fn inline_config() { - // single invocation - assert_that!( - &[ - "--print-config", - "current", - ".", - "--config=color=Never,edition=2018" - ], - contains("color = \"Never\"") && contains("edition = \"2018\"") +fn print_config_writes_to_file() { + let out_file = "minimal.toml"; + let args = &["--print-config", "minimal", out_file, "src/shape.rs"]; + + let (stdout, stderr) = rustfmt(args); + + assert_eq!(stderr, ""); + assert!( + Path::new(out_file).exists(), + "stdout:\n{stdout}\nstderr:\n{stderr}" + ); + remove_file(out_file).unwrap(); +} + +#[test] +fn print_config_merges_override_from_args() { + let args = &[ + "--print-config", + "current", + ".", + "--config=color=Never,edition=2018", + ]; + + let (stdout, stderr) = rustfmt(args); + + assert_eq!(stderr, ""); + assert!( + stdout.contains(r#"color = "Never""#) && stdout.contains(r#"edition = "2018""#), + "expected overrides in stdout, got: {stdout}", ); +} - // multiple overriding invocations - assert_that!( - &[ - "--print-config", - "current", - ".", - "--config", - "color=never,edition=2018", - "--config", - "color=always,format_strings=true" - ], - contains("color = \"Always\"") - && contains("edition = \"2018\"") - && contains("format_strings = true") +#[test] +fn print_config_merges_multiple_overrides_from_args() { + let args = &[ + "--print-config", + "current", + ".", + "--config", + "color=never,edition=2018", + "--config", + "color=always,format_strings=true", + ]; + + let (stdout, stderr) = rustfmt(args); + + assert_eq!(stderr, ""); + assert!( + stdout.contains(r#"color = "Always""#) + && stdout.contains(r#"edition = "2018""#) + && stdout.contains("format_strings = true"), + "expected overrides in stdout, got: {stdout}", ); } From d7ecf4d6fc403c51bc2ff8299def0a0713abae97 Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Sat, 17 Oct 2026 16:01:39 +0100 Subject: [PATCH 02/53] Update config discovery integration tests Firstly, Simplify the directory structure: we only need a single package here, so drag everything up for that. Secondly, make it more explicit exactly which config we expect to read by setting up configs with different options, rather than just ignoring all formatting. Also add tests for other existing behaviour: searching in `HOME` and `XDG_CONFIG_HOME` when no config is given nor found. --- .../tests/config/issue_4660/Cargo.toml | 8 +- .../issue_4660/configs/rustfmt.hard_tabs.toml | 1 + .../inner_bin/.rustfmt.unstable.toml | 1 - .../config/issue_4660/inner_bin/Cargo.toml | 6 - .../config/issue_4660/inner_bin/src/main.rs | 1 - .../config/issue_4660/inner_lib/Cargo.toml | 6 - .../config/issue_4660/inner_lib/rustfmt.toml | 1 - .../config/issue_4660/inner_lib/src/lib.rs | 1 - .../tests/config/issue_4660/rustfmt.toml | 1 + .../tests/config/issue_4660/src/lib.rs | 2 + .../issue_4660/src/longhand_field_init.rs | 10 ++ .../config/issue_4660/src/with_spaces.rs | 3 + src/tools/rustfmt/tests/rustfmt/main.rs | 114 +++++++++++++++--- 13 files changed, 122 insertions(+), 33 deletions(-) create mode 100644 src/tools/rustfmt/tests/config/issue_4660/configs/rustfmt.hard_tabs.toml delete mode 100644 src/tools/rustfmt/tests/config/issue_4660/inner_bin/.rustfmt.unstable.toml delete mode 100644 src/tools/rustfmt/tests/config/issue_4660/inner_bin/Cargo.toml delete mode 100644 src/tools/rustfmt/tests/config/issue_4660/inner_bin/src/main.rs delete mode 100644 src/tools/rustfmt/tests/config/issue_4660/inner_lib/Cargo.toml delete mode 100644 src/tools/rustfmt/tests/config/issue_4660/inner_lib/rustfmt.toml delete mode 100644 src/tools/rustfmt/tests/config/issue_4660/inner_lib/src/lib.rs create mode 100644 src/tools/rustfmt/tests/config/issue_4660/rustfmt.toml create mode 100644 src/tools/rustfmt/tests/config/issue_4660/src/lib.rs create mode 100644 src/tools/rustfmt/tests/config/issue_4660/src/longhand_field_init.rs create mode 100644 src/tools/rustfmt/tests/config/issue_4660/src/with_spaces.rs diff --git a/src/tools/rustfmt/tests/config/issue_4660/Cargo.toml b/src/tools/rustfmt/tests/config/issue_4660/Cargo.toml index c716e9d057548..c9ddf1793b88f 100644 --- a/src/tools/rustfmt/tests/config/issue_4660/Cargo.toml +++ b/src/tools/rustfmt/tests/config/issue_4660/Cargo.toml @@ -1,2 +1,6 @@ -[workspace] -members = ["inner_lib"] +[package] +name = "inner_lib" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/src/tools/rustfmt/tests/config/issue_4660/configs/rustfmt.hard_tabs.toml b/src/tools/rustfmt/tests/config/issue_4660/configs/rustfmt.hard_tabs.toml new file mode 100644 index 0000000000000..218e203215e94 --- /dev/null +++ b/src/tools/rustfmt/tests/config/issue_4660/configs/rustfmt.hard_tabs.toml @@ -0,0 +1 @@ +hard_tabs = true diff --git a/src/tools/rustfmt/tests/config/issue_4660/inner_bin/.rustfmt.unstable.toml b/src/tools/rustfmt/tests/config/issue_4660/inner_bin/.rustfmt.unstable.toml deleted file mode 100644 index c7ad93bafe36c..0000000000000 --- a/src/tools/rustfmt/tests/config/issue_4660/inner_bin/.rustfmt.unstable.toml +++ /dev/null @@ -1 +0,0 @@ -disable_all_formatting = true diff --git a/src/tools/rustfmt/tests/config/issue_4660/inner_bin/Cargo.toml b/src/tools/rustfmt/tests/config/issue_4660/inner_bin/Cargo.toml deleted file mode 100644 index bb6a80cd0ce9b..0000000000000 --- a/src/tools/rustfmt/tests/config/issue_4660/inner_bin/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "inner_bin" -version = "0.1.0" -edition = "2021" - -[dependencies] diff --git a/src/tools/rustfmt/tests/config/issue_4660/inner_bin/src/main.rs b/src/tools/rustfmt/tests/config/issue_4660/inner_bin/src/main.rs deleted file mode 100644 index 21c34773b8af5..0000000000000 --- a/src/tools/rustfmt/tests/config/issue_4660/inner_bin/src/main.rs +++ /dev/null @@ -1 +0,0 @@ -fn main(){println!("Hello, world!")} diff --git a/src/tools/rustfmt/tests/config/issue_4660/inner_lib/Cargo.toml b/src/tools/rustfmt/tests/config/issue_4660/inner_lib/Cargo.toml deleted file mode 100644 index c9ddf1793b88f..0000000000000 --- a/src/tools/rustfmt/tests/config/issue_4660/inner_lib/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "inner_lib" -version = "0.1.0" -edition = "2021" - -[dependencies] diff --git a/src/tools/rustfmt/tests/config/issue_4660/inner_lib/rustfmt.toml b/src/tools/rustfmt/tests/config/issue_4660/inner_lib/rustfmt.toml deleted file mode 100644 index c7ad93bafe36c..0000000000000 --- a/src/tools/rustfmt/tests/config/issue_4660/inner_lib/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -disable_all_formatting = true diff --git a/src/tools/rustfmt/tests/config/issue_4660/inner_lib/src/lib.rs b/src/tools/rustfmt/tests/config/issue_4660/inner_lib/src/lib.rs deleted file mode 100644 index 42375c99d2c97..0000000000000 --- a/src/tools/rustfmt/tests/config/issue_4660/inner_lib/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub fn add(left:u64,right:u64)->u64{left+right} diff --git a/src/tools/rustfmt/tests/config/issue_4660/rustfmt.toml b/src/tools/rustfmt/tests/config/issue_4660/rustfmt.toml new file mode 100644 index 0000000000000..8a1751f67698f --- /dev/null +++ b/src/tools/rustfmt/tests/config/issue_4660/rustfmt.toml @@ -0,0 +1 @@ +use_field_init_shorthand = true diff --git a/src/tools/rustfmt/tests/config/issue_4660/src/lib.rs b/src/tools/rustfmt/tests/config/issue_4660/src/lib.rs new file mode 100644 index 0000000000000..012fb0f850f8c --- /dev/null +++ b/src/tools/rustfmt/tests/config/issue_4660/src/lib.rs @@ -0,0 +1,2 @@ +mod longhand_field_init; +mod with_spaces; diff --git a/src/tools/rustfmt/tests/config/issue_4660/src/longhand_field_init.rs b/src/tools/rustfmt/tests/config/issue_4660/src/longhand_field_init.rs new file mode 100644 index 0000000000000..87ee30033c955 --- /dev/null +++ b/src/tools/rustfmt/tests/config/issue_4660/src/longhand_field_init.rs @@ -0,0 +1,10 @@ +struct Foo { + x: u32, + y: u32, +} +fn foo() -> Foo { + let x = 1; + let y = 2; + + Foo { x: x, y: y } +} diff --git a/src/tools/rustfmt/tests/config/issue_4660/src/with_spaces.rs b/src/tools/rustfmt/tests/config/issue_4660/src/with_spaces.rs new file mode 100644 index 0000000000000..211d2de3c959e --- /dev/null +++ b/src/tools/rustfmt/tests/config/issue_4660/src/with_spaces.rs @@ -0,0 +1,3 @@ +fn f() { + // comment +} diff --git a/src/tools/rustfmt/tests/rustfmt/main.rs b/src/tools/rustfmt/tests/rustfmt/main.rs index c37951c085982..832087289af0f 100644 --- a/src/tools/rustfmt/tests/rustfmt/main.rs +++ b/src/tools/rustfmt/tests/rustfmt/main.rs @@ -87,8 +87,8 @@ fn print_config_errors_minimal_with_no_path() { let (stdout, stderr) = rustfmt(args); - // NOTE: existing the behaviour, possibly a bug - assert_eq!(&stdout, "\n"); + // NOTE: existing the behaviour: prints an empty line + assert_eq!(stdout.trim(), ""); assert!( stderr.contains("PATH required"), "expected helpful error message, got: {stderr}" @@ -113,7 +113,16 @@ fn print_config_errors_minimal_without_input_file() { #[test] fn print_config_writes_to_file() { let out_file = "minimal.toml"; - let args = &["--print-config", "minimal", out_file, "src/shape.rs"]; + let args = &[ + "--config-path", + Path::new("tests/config/disable_all_formatting.toml") + .to_str() + .unwrap(), + "--print-config", + "minimal", + out_file, + "src/shape.rs", + ]; let (stdout, stderr) = rustfmt(args); @@ -396,30 +405,105 @@ fn rustfmt_error_improvement_regarding_invalid_toml() { #[test] fn config_path_walks_parent_directories_with_dir_name() { - let src_dir = "tests/config/issue_4660/inner_lib/src"; - let src_file = src_dir.to_owned() + "/lib.rs"; - let args = ["--config-path", src_dir, "--check", &src_file]; + let src_dir = Path::new("tests/config/issue_4660/src"); + let src_file = src_dir.join("longhand_field_init.rs"); + let args = [ + "--config-path", + &src_dir.to_str().unwrap(), + "--check", + "--files-with-diff", + &src_file.to_str().unwrap(), + ]; + let (stdout, stderr) = rustfmt(&args); assert_eq!(stderr, ""); - // Due to `disable_all_formatting = true` in `tests/config/issue_4660/inner_lib/rustfmt.toml`, - // the source file should not be modified. - assert_eq!(stdout, ""); + assert_eq!( + stdout, + format!("{}\n", src_file.canonicalize().unwrap().display()), + // this config sets `use_field_init_shorthand = true` + "we should walk up to tests/config/issue_4660/rustfmt.toml" + ); +} + +// on unix [`std::env::home_dir`] reads the `HOME` env var. +#[cfg(unix)] +#[test] +fn config_path_searches_home_dir_when_dir_path_no_resolve() { + // move the src file to a tempdir, since we'll search up from there looking for the config file + // so we need to avoid using a path within this repo + let new_src_dir = tempfile::tempdir().unwrap().keep(); + let src_file = Path::new("tests/config/issue_4660/src/with_spaces.rs"); + let new_src_path = new_src_dir.join("with_spaces.rs"); + let config_file = Path::new("tests/config/issue_4660/configs/rustfmt.hard_tabs.toml"); + let homedir_path = tempfile::tempdir().unwrap().keep(); + + std::fs::copy(&config_file, homedir_path.join("rustfmt.toml")).unwrap(); + std::fs::copy(&src_file, &new_src_path).unwrap(); + + let args = [ + "--check", + "--files-with-diff", + &new_src_path.to_str().unwrap(), + ]; + let env = &[("HOME", homedir_path.to_str().unwrap())]; + + let (stdout, stderr) = rustfmt_with_extra(&args, None, env, None); + + assert_eq!(stderr, ""); + assert_eq!( + stdout, + format!("{}\n", new_src_path.canonicalize().unwrap().display()), + "should search in HOME" + ); +} + +// The behaviour [`dirs::config_dir`] is os-dependent, so pick one and test to that. +#[cfg(target_os = "linux")] +#[test] +fn config_path_searches_config_dir_when_dir_path_no_resolve() { + // move the src file to a tempdir, since we'll search up from there looking for the config file + let new_src_dir = tempfile::tempdir().unwrap().keep(); + let src_file = Path::new("tests/config/issue_4660/src/with_spaces.rs"); + let new_src_path = new_src_dir.join("with_spaces.rs"); + let config_file = Path::new("tests/config/issue_4660/configs/rustfmt.hard_tabs.toml"); + let xdg_config_dir_path = tempfile::tempdir().unwrap().keep(); + let config_path = xdg_config_dir_path.join("rustfmt/rustfmt.toml"); + + std::fs::create_dir(&config_path.parent().unwrap()).unwrap(); + std::fs::copy(&config_file, config_path).unwrap(); + std::fs::copy(&src_file, &new_src_path).unwrap(); + + let args = [ + "--check", + "--files-with-diff", + &new_src_path.to_str().unwrap(), + ]; + let env = &[("XDG_CONFIG_HOME", xdg_config_dir_path.to_str().unwrap())]; + + let (stdout, stderr) = rustfmt_with_extra(&args, None, env, None); + + assert_eq!(stderr, ""); + assert_eq!( + stdout, + format!("{}\n", new_src_path.canonicalize().unwrap().display()), + "should search in XDG_CONFIG_HOME" + ); } #[test] -fn config_path_does_not_walk_parent_directories_with_toml_name() { - let src_dir = "tests/config/issue_4660/inner_bin/src"; - let toml_file = src_dir.to_owned() + "/.rustfmt.unstable.toml"; - let src_file = src_dir.to_owned() + "/main.rs"; - let args = ["--config-path", &toml_file, "--check", &src_file]; +fn config_path_errors_when_invalid_file_given() { + let src_file = "not_a_file.rs"; + let config_file = "also_not_a_file.toml"; + let args = ["--config-path", &config_file, "--check", &src_file]; + let (stdout, stderr) = rustfmt(&args); assert_eq!( stderr, format!( "Error: unable to find a config file for the given path: `{}`\n", - Path::new(&toml_file).display(), + Path::new(&config_file).display(), ) ); assert_eq!(stdout, ""); From a50d9a911ce8f2d1862543cf3244e10d799e6ac8 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Thu, 22 Oct 2026 11:15:15 +0800 Subject: [PATCH 03/53] Allow `llm-assisted` label to be set by users without write access --- src/tools/rustfmt/triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rustfmt/triagebot.toml b/src/tools/rustfmt/triagebot.toml index de28ad00a4272..7197066a41a98 100644 --- a/src/tools/rustfmt/triagebot.toml +++ b/src/tools/rustfmt/triagebot.toml @@ -13,6 +13,7 @@ allow-unauthenticated = [ "E-*", "F-*", "I-*", + "llm-assisted", "needs-rfc", "needs-triage", "regression-*", From fad0b13215bb67bb9cd784a1501ba16c18e53069 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 16 Oct 2026 17:54:00 +0800 Subject: [PATCH 04/53] Add test demonstrating current error leak formatting --- .../rustfmt/tests/source/cfg-if-error-count-leak.rs | 10 ++++++++++ .../rustfmt/tests/target/cfg-if-error-count-leak.rs | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/tools/rustfmt/tests/source/cfg-if-error-count-leak.rs create mode 100644 src/tools/rustfmt/tests/target/cfg-if-error-count-leak.rs diff --git a/src/tools/rustfmt/tests/source/cfg-if-error-count-leak.rs b/src/tools/rustfmt/tests/source/cfg-if-error-count-leak.rs new file mode 100644 index 0000000000000..e2441228108f3 --- /dev/null +++ b/src/tools/rustfmt/tests/source/cfg-if-error-count-leak.rs @@ -0,0 +1,10 @@ +cfg_if::cfg_if! { + if #[cfg(unix)] { + fn foo {} + } +} + +fn main() { + foo!(1 + 2); + foo!(1 + 2); +} diff --git a/src/tools/rustfmt/tests/target/cfg-if-error-count-leak.rs b/src/tools/rustfmt/tests/target/cfg-if-error-count-leak.rs new file mode 100644 index 0000000000000..c62d3a17c1a09 --- /dev/null +++ b/src/tools/rustfmt/tests/target/cfg-if-error-count-leak.rs @@ -0,0 +1,10 @@ +cfg_if::cfg_if! { + if #[cfg(unix)] { + fn foo {} + } +} + +fn main() { + foo!(1 + 2); + foo!(1 + 2); +} From b1c0b194703ebd321a769dbf4d8e0c6f7f533b99 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 16 Oct 2026 17:56:32 +0800 Subject: [PATCH 05/53] Properly reset error count for `cfg_if!` --- src/tools/rustfmt/src/parse/macros/cfg_if.rs | 10 +++++++--- .../rustfmt/tests/source/cfg-if-error-count-leak.rs | 2 ++ .../rustfmt/tests/target/cfg-if-error-count-leak.rs | 4 +++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/tools/rustfmt/src/parse/macros/cfg_if.rs b/src/tools/rustfmt/src/parse/macros/cfg_if.rs index 495f12c8f5d5d..08accda2fbd09 100644 --- a/src/tools/rustfmt/src/parse/macros/cfg_if.rs +++ b/src/tools/rustfmt/src/parse/macros/cfg_if.rs @@ -13,9 +13,13 @@ pub(crate) fn parse_cfg_if<'a>( psess: &'a ParseSess, mac: &'a ast::MacCall, ) -> Result, &'static str> { - match catch_unwind(AssertUnwindSafe(|| parse_cfg_if_inner(psess, mac))) { - Ok(Ok(items)) => Ok(items), - Ok(err @ Err(_)) => err, + let result = catch_unwind(AssertUnwindSafe(|| parse_cfg_if_inner(psess, mac))); + let dcx = psess.inner().dcx(); + let recovered = dcx.has_errors().is_some(); + dcx.reset_err_count(); + match result { + Ok(Ok(_)) if recovered => Err("cfg_if! parsed with recovery"), + Ok(res) => res, Err(..) => Err("failed to parse cfg_if!"), } } diff --git a/src/tools/rustfmt/tests/source/cfg-if-error-count-leak.rs b/src/tools/rustfmt/tests/source/cfg-if-error-count-leak.rs index e2441228108f3..d5e289ec95b12 100644 --- a/src/tools/rustfmt/tests/source/cfg-if-error-count-leak.rs +++ b/src/tools/rustfmt/tests/source/cfg-if-error-count-leak.rs @@ -1,3 +1,5 @@ +//@ rustfmt-show_parse_errors: false + cfg_if::cfg_if! { if #[cfg(unix)] { fn foo {} diff --git a/src/tools/rustfmt/tests/target/cfg-if-error-count-leak.rs b/src/tools/rustfmt/tests/target/cfg-if-error-count-leak.rs index c62d3a17c1a09..48ef7ddc3ba7a 100644 --- a/src/tools/rustfmt/tests/target/cfg-if-error-count-leak.rs +++ b/src/tools/rustfmt/tests/target/cfg-if-error-count-leak.rs @@ -1,3 +1,5 @@ +//@ rustfmt-show_parse_errors: false + cfg_if::cfg_if! { if #[cfg(unix)] { fn foo {} @@ -5,6 +7,6 @@ cfg_if::cfg_if! { } fn main() { - foo!(1 + 2); + foo!(1 + 2); foo!(1 + 2); } From ba09f12ddab5c1b58490fc63a7a2ce0a5ce33ce4 Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Mon, 19 Oct 2026 20:30:49 +0100 Subject: [PATCH 06/53] Integration tests: also capture exit code So we can assert that `rustfmt` exits how we expect. --- src/tools/rustfmt/tests/rustfmt/main.rs | 53 +++++++++++++------------ 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/tools/rustfmt/tests/rustfmt/main.rs b/src/tools/rustfmt/tests/rustfmt/main.rs index 832087289af0f..71f52abe430c1 100644 --- a/src/tools/rustfmt/tests/rustfmt/main.rs +++ b/src/tools/rustfmt/tests/rustfmt/main.rs @@ -15,7 +15,7 @@ fn rustfmt_with_extra( working_dir: Option<&str>, envs: &[(&str, &str)], input: Option<&str>, -) -> (String, String) { +) -> (String, String, i32) { let rustfmt_exe = env!("CARGO_BIN_EXE_rustfmt"); let bin_dir = Path::new(rustfmt_exe).parent().unwrap(); @@ -47,12 +47,13 @@ fn rustfmt_with_extra( Ok(output) => ( String::from_utf8(output.stdout).expect("utf-8"), String::from_utf8(output.stderr).expect("utf-8"), + output.status.code().expect("should exit with a status"), ), Err(e) => panic!("failed to run `{cmd:?} {args:?}`: {e}"), } } -fn rustfmt(args: &[&str]) -> (String, String) { +fn rustfmt(args: &[&str]) -> (String, String, i32) { rustfmt_with_extra(args, None, &[], None) } @@ -60,7 +61,7 @@ fn rustfmt(args: &[&str]) -> (String, String) { fn print_config_errors_on_unknown_option() { let args = &["--print-config", "unknown"]; - let (stdout, stderr) = rustfmt(args); + let (stdout, stderr, _) = rustfmt(args); assert_eq!(&stdout, ""); assert!( @@ -72,7 +73,7 @@ fn print_config_errors_on_unknown_option() { #[test] fn print_config_default() { let args = &["--print-config", "default"]; - let (stdout, stderr) = rustfmt(args); + let (stdout, stderr, _) = rustfmt(args); assert!( stdout.contains("max_width = 100"), @@ -85,7 +86,7 @@ fn print_config_default() { fn print_config_errors_minimal_with_no_path() { let args = &["--print-config", "minimal"]; - let (stdout, stderr) = rustfmt(args); + let (stdout, stderr, _) = rustfmt(args); // NOTE: existing the behaviour: prints an empty line assert_eq!(stdout.trim(), ""); @@ -99,7 +100,7 @@ fn print_config_errors_minimal_with_no_path() { fn print_config_errors_minimal_without_input_file() { let args = &["--print-config", "minimal", "minimal.toml"]; - let (stdout, stderr) = rustfmt(args); + let (stdout, stderr, _) = rustfmt(args); assert_eq!(&stdout, ""); assert!( @@ -124,7 +125,7 @@ fn print_config_writes_to_file() { "src/shape.rs", ]; - let (stdout, stderr) = rustfmt(args); + let (stdout, stderr, _) = rustfmt(args); assert_eq!(stderr, ""); assert!( @@ -143,7 +144,7 @@ fn print_config_merges_override_from_args() { "--config=color=Never,edition=2018", ]; - let (stdout, stderr) = rustfmt(args); + let (stdout, stderr, _) = rustfmt(args); assert_eq!(stderr, ""); assert!( @@ -164,7 +165,7 @@ fn print_config_merges_multiple_overrides_from_args() { "color=always,format_strings=true", ]; - let (stdout, stderr) = rustfmt(args); + let (stdout, stderr, _) = rustfmt(args); assert_eq!(stderr, ""); assert!( @@ -179,7 +180,7 @@ fn print_config_merges_multiple_overrides_from_args() { #[test] fn rustfmt_usage_text() { let args = ["--help"]; - let (stdout, _) = rustfmt(&args); + let (stdout, _, _) = rustfmt(&args); insta::assert_snapshot!(stdout, @" Format Rust code @@ -227,7 +228,7 @@ fn rustfmt_usage_text() { #[test] fn rustfmt_nightly_usage_text() { let args = ["--help"]; - let (stdout, _) = rustfmt(&args); + let (stdout, _, _) = rustfmt(&args); insta::assert_snapshot!(stdout, @" Format Rust code @@ -295,14 +296,14 @@ fn mod_resolution_error_multiple_candidate_files() { ); let args = ["tests/mod-resolver/issue-5167/src/lib.rs"]; - let (_stdout, stderr) = rustfmt(&args); + let (_stdout, stderr, _) = rustfmt(&args); assert!(stderr.contains(&error_message)) } #[test] fn mod_resolution_error_sibling_module_not_found() { let args = ["tests/mod-resolver/module-not-found/sibling_module/lib.rs"]; - let (_stdout, stderr) = rustfmt(&args); + let (_stdout, stderr, _) = rustfmt(&args); // Module resolution fails because we're unable to find `a.rs` in the same directory as lib.rs assert!(stderr.contains("a.rs does not exist")) } @@ -310,7 +311,7 @@ fn mod_resolution_error_sibling_module_not_found() { #[test] fn mod_resolution_error_relative_module_not_found() { let args = ["tests/mod-resolver/module-not-found/relative_module/lib.rs"]; - let (_stdout, stderr) = rustfmt(&args); + let (_stdout, stderr, _) = rustfmt(&args); // The file `./a.rs` and directory `./a` both exist. // Module resolution fails because we're unable to find `./a/b.rs` #[cfg(not(windows))] @@ -322,7 +323,7 @@ fn mod_resolution_error_relative_module_not_found() { #[test] fn mod_resolution_error_path_attribute_does_not_exist() { let args = ["tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs"]; - let (_stdout, stderr) = rustfmt(&args); + let (_stdout, stderr, _) = rustfmt(&args); // The path attribute points to a file that does not exist assert!(stderr.contains("does_not_exist.rs does not exist")); } @@ -336,7 +337,7 @@ fn rustfmt_emits_error_on_line_overflow_true() { "tests/cargo-fmt/source/issue_3164/src/main.rs", ]; - let (_stdout, stderr) = rustfmt(&args); + let (_stdout, stderr, _) = rustfmt(&args); assert!(stderr.contains( "line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option)" )) @@ -357,7 +358,7 @@ fn dont_emit_ICE() { let panic_re = regex::Regex::new("thread.*panicked").unwrap(); for file in files { let args = [file]; - let (_stdout, stderr) = rustfmt(&args); + let (_stdout, stderr, _) = rustfmt(&args); assert!(!panic_re.is_match(&stderr)); } } @@ -374,7 +375,7 @@ fn rustfmt_emits_error_when_control_brace_style_is_always_next_line() { "tests/target/issue_5912.rs", ]; - let (_stdout, stderr) = rustfmt(&args); + let (_stdout, stderr, _) = rustfmt(&args); assert!(!stderr.contains("error[internal]: left behind trailing whitespace")) } @@ -385,7 +386,7 @@ fn rustfmt_generates_no_error_if_failed_format_code_in_doc_comments() { let file = "tests/target/issue-6109.rs"; let args = ["--config", "format_code_in_doc_comments=true", file]; - let (stdout, stderr) = rustfmt(&args); + let (stdout, stderr, _) = rustfmt(&args); assert!(stderr.is_empty()); assert!(stdout.is_empty()); } @@ -395,7 +396,7 @@ fn rustfmt_error_improvement_regarding_invalid_toml() { // See also https://github.com/rust-lang/rustfmt/issues/6302 let invalid_toml_config = "tests/config/issue-6302.toml"; let args = ["--config-path", invalid_toml_config]; - let (_stdout, stderr) = rustfmt(&args); + let (_stdout, stderr, _) = rustfmt(&args); let toml_path = Path::new(invalid_toml_config).canonicalize().unwrap(); let expected_error_message = format!("The file `{}` failed to parse", toml_path.display()); @@ -415,7 +416,7 @@ fn config_path_walks_parent_directories_with_dir_name() { &src_file.to_str().unwrap(), ]; - let (stdout, stderr) = rustfmt(&args); + let (stdout, stderr, _) = rustfmt(&args); assert_eq!(stderr, ""); assert_eq!( @@ -448,7 +449,7 @@ fn config_path_searches_home_dir_when_dir_path_no_resolve() { ]; let env = &[("HOME", homedir_path.to_str().unwrap())]; - let (stdout, stderr) = rustfmt_with_extra(&args, None, env, None); + let (stdout, stderr, _) = rustfmt_with_extra(&args, None, env, None); assert_eq!(stderr, ""); assert_eq!( @@ -481,7 +482,7 @@ fn config_path_searches_config_dir_when_dir_path_no_resolve() { ]; let env = &[("XDG_CONFIG_HOME", xdg_config_dir_path.to_str().unwrap())]; - let (stdout, stderr) = rustfmt_with_extra(&args, None, env, None); + let (stdout, stderr, _) = rustfmt_with_extra(&args, None, env, None); assert_eq!(stderr, ""); assert_eq!( @@ -497,7 +498,7 @@ fn config_path_errors_when_invalid_file_given() { let config_file = "also_not_a_file.toml"; let args = ["--config-path", &config_file, "--check", &src_file]; - let (stdout, stderr) = rustfmt(&args); + let (stdout, stderr, _) = rustfmt(&args); assert_eq!( stderr, @@ -533,7 +534,7 @@ fn rustfmt_allow_not_a_dir_errors() { let args = [empty_rs.to_str().unwrap()]; let envs = &[("HOME", fake_home_str)]; - let (stdout, stderr) = rustfmt_with_extra(&args, Some(fake_home_str), envs, None); + let (stdout, stderr, _) = rustfmt_with_extra(&args, Some(fake_home_str), envs, None); // Should pass without any errors assert_eq!(stdout, ""); @@ -547,7 +548,7 @@ fn cfg_select_parser_recovery_ice_7087() { // See also https://github.com/rust-lang/rustfmt/issues/7087 let source = "cfg_select! { true => { fn foo {} } }\n"; let panic_re = regex::Regex::new("thread.*panicked").unwrap(); - let (stdout, stderr) = rustfmt_with_extra(&[], None, &[], Some(source)); + let (stdout, stderr, _) = rustfmt_with_extra(&[], None, &[], Some(source)); assert!(!panic_re.is_match(&stderr)); // Invalid arm should cause `cfg_select!` to be re-emitted unmodified. assert_eq!(source, stdout); From 4689345faf4fdf3dc9037111d947cf7b17c181cb Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 23 Oct 2026 22:20:57 +0800 Subject: [PATCH 07/53] test: rename multi-file `cfg_select!` tests subfolder --- src/tools/rustfmt/src/test/mod.rs | 6 +++--- .../format_me_please_1.rs | 0 .../format_me_please_2.rs | 0 .../format_me_please_3.rs | 0 .../format_me_please_4.rs | 0 .../source/{cfg_select => cfg_select_multifile}/lib.rs | 0 .../source/{cfg_select => cfg_select_multifile}/lib2.rs | 0 .../format_me_please_1.rs | 0 .../format_me_please_2.rs | 0 .../format_me_please_3.rs | 0 .../format_me_please_4.rs | 0 .../target/{cfg_select => cfg_select_multifile}/lib.rs | 0 .../target/{cfg_select => cfg_select_multifile}/lib2.rs | 0 13 files changed, 3 insertions(+), 3 deletions(-) rename src/tools/rustfmt/tests/source/{cfg_select => cfg_select_multifile}/format_me_please_1.rs (100%) rename src/tools/rustfmt/tests/source/{cfg_select => cfg_select_multifile}/format_me_please_2.rs (100%) rename src/tools/rustfmt/tests/source/{cfg_select => cfg_select_multifile}/format_me_please_3.rs (100%) rename src/tools/rustfmt/tests/source/{cfg_select => cfg_select_multifile}/format_me_please_4.rs (100%) rename src/tools/rustfmt/tests/source/{cfg_select => cfg_select_multifile}/lib.rs (100%) rename src/tools/rustfmt/tests/source/{cfg_select => cfg_select_multifile}/lib2.rs (100%) rename src/tools/rustfmt/tests/target/{cfg_select => cfg_select_multifile}/format_me_please_1.rs (100%) rename src/tools/rustfmt/tests/target/{cfg_select => cfg_select_multifile}/format_me_please_2.rs (100%) rename src/tools/rustfmt/tests/target/{cfg_select => cfg_select_multifile}/format_me_please_3.rs (100%) rename src/tools/rustfmt/tests/target/{cfg_select => cfg_select_multifile}/format_me_please_4.rs (100%) rename src/tools/rustfmt/tests/target/{cfg_select => cfg_select_multifile}/lib.rs (100%) rename src/tools/rustfmt/tests/target/{cfg_select => cfg_select_multifile}/lib2.rs (100%) diff --git a/src/tools/rustfmt/src/test/mod.rs b/src/tools/rustfmt/src/test/mod.rs index 96b96700a9288..5d5be709e5fb5 100644 --- a/src/tools/rustfmt/src/test/mod.rs +++ b/src/tools/rustfmt/src/test/mod.rs @@ -44,7 +44,7 @@ const FILE_SKIP_LIST: &[&str] = &[ "issue-3253/bar.rs", "issue-3253/paths", // This directory is directly tested by format_files_find_new_files_via_cfg_select - "cfg_select", + "cfg_select_multifile", // These files and directory are a part of modules defined inside `cfg_attr(..)`. "cfg_mod/dir", "cfg_mod/bar.rs", @@ -575,8 +575,8 @@ fn format_files_find_new_files_via_cfg_select() { // transparent mod discovery, and to ensure that it does not suffer // from a similar issue as cfg_if! support did with issue-4656. let files = vec![ - Path::new("tests/source/cfg_select/lib2.rs"), - Path::new("tests/source/cfg_select/lib.rs"), + Path::new("tests/source/cfg_select_multifile/lib2.rs"), + Path::new("tests/source/cfg_select_multifile/lib.rs"), ]; let config = Config::default(); diff --git a/src/tools/rustfmt/tests/source/cfg_select/format_me_please_1.rs b/src/tools/rustfmt/tests/source/cfg_select_multifile/format_me_please_1.rs similarity index 100% rename from src/tools/rustfmt/tests/source/cfg_select/format_me_please_1.rs rename to src/tools/rustfmt/tests/source/cfg_select_multifile/format_me_please_1.rs diff --git a/src/tools/rustfmt/tests/source/cfg_select/format_me_please_2.rs b/src/tools/rustfmt/tests/source/cfg_select_multifile/format_me_please_2.rs similarity index 100% rename from src/tools/rustfmt/tests/source/cfg_select/format_me_please_2.rs rename to src/tools/rustfmt/tests/source/cfg_select_multifile/format_me_please_2.rs diff --git a/src/tools/rustfmt/tests/source/cfg_select/format_me_please_3.rs b/src/tools/rustfmt/tests/source/cfg_select_multifile/format_me_please_3.rs similarity index 100% rename from src/tools/rustfmt/tests/source/cfg_select/format_me_please_3.rs rename to src/tools/rustfmt/tests/source/cfg_select_multifile/format_me_please_3.rs diff --git a/src/tools/rustfmt/tests/source/cfg_select/format_me_please_4.rs b/src/tools/rustfmt/tests/source/cfg_select_multifile/format_me_please_4.rs similarity index 100% rename from src/tools/rustfmt/tests/source/cfg_select/format_me_please_4.rs rename to src/tools/rustfmt/tests/source/cfg_select_multifile/format_me_please_4.rs diff --git a/src/tools/rustfmt/tests/source/cfg_select/lib.rs b/src/tools/rustfmt/tests/source/cfg_select_multifile/lib.rs similarity index 100% rename from src/tools/rustfmt/tests/source/cfg_select/lib.rs rename to src/tools/rustfmt/tests/source/cfg_select_multifile/lib.rs diff --git a/src/tools/rustfmt/tests/source/cfg_select/lib2.rs b/src/tools/rustfmt/tests/source/cfg_select_multifile/lib2.rs similarity index 100% rename from src/tools/rustfmt/tests/source/cfg_select/lib2.rs rename to src/tools/rustfmt/tests/source/cfg_select_multifile/lib2.rs diff --git a/src/tools/rustfmt/tests/target/cfg_select/format_me_please_1.rs b/src/tools/rustfmt/tests/target/cfg_select_multifile/format_me_please_1.rs similarity index 100% rename from src/tools/rustfmt/tests/target/cfg_select/format_me_please_1.rs rename to src/tools/rustfmt/tests/target/cfg_select_multifile/format_me_please_1.rs diff --git a/src/tools/rustfmt/tests/target/cfg_select/format_me_please_2.rs b/src/tools/rustfmt/tests/target/cfg_select_multifile/format_me_please_2.rs similarity index 100% rename from src/tools/rustfmt/tests/target/cfg_select/format_me_please_2.rs rename to src/tools/rustfmt/tests/target/cfg_select_multifile/format_me_please_2.rs diff --git a/src/tools/rustfmt/tests/target/cfg_select/format_me_please_3.rs b/src/tools/rustfmt/tests/target/cfg_select_multifile/format_me_please_3.rs similarity index 100% rename from src/tools/rustfmt/tests/target/cfg_select/format_me_please_3.rs rename to src/tools/rustfmt/tests/target/cfg_select_multifile/format_me_please_3.rs diff --git a/src/tools/rustfmt/tests/target/cfg_select/format_me_please_4.rs b/src/tools/rustfmt/tests/target/cfg_select_multifile/format_me_please_4.rs similarity index 100% rename from src/tools/rustfmt/tests/target/cfg_select/format_me_please_4.rs rename to src/tools/rustfmt/tests/target/cfg_select_multifile/format_me_please_4.rs diff --git a/src/tools/rustfmt/tests/target/cfg_select/lib.rs b/src/tools/rustfmt/tests/target/cfg_select_multifile/lib.rs similarity index 100% rename from src/tools/rustfmt/tests/target/cfg_select/lib.rs rename to src/tools/rustfmt/tests/target/cfg_select_multifile/lib.rs diff --git a/src/tools/rustfmt/tests/target/cfg_select/lib2.rs b/src/tools/rustfmt/tests/target/cfg_select_multifile/lib2.rs similarity index 100% rename from src/tools/rustfmt/tests/target/cfg_select/lib2.rs rename to src/tools/rustfmt/tests/target/cfg_select_multifile/lib2.rs From 49c47fc3a39550c4a24dc67e597a1f507a28575d Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 23 Oct 2026 22:30:29 +0800 Subject: [PATCH 08/53] test: group `cfg_select!` related tests under subfolder --- src/tools/rustfmt/tests/source/{ => cfg_select}/cfg_select.rs | 0 .../tests/source/{ => cfg_select}/cfg_select_edge_cases.rs | 0 .../rustfmt/tests/source/{ => cfg_select}/cfg_select_stable.rs | 0 .../cfg_select_valid_then_invalid_item_error_count_leak.rs} | 0 src/tools/rustfmt/tests/source/{ => cfg_select}/issue_7045.rs | 0 .../rustfmt/tests/source/{ => cfg_select}/issue_7088_stable.rs | 0 .../rustfmt/tests/source/{ => cfg_select}/issue_7088_unstable.rs | 0 src/tools/rustfmt/tests/target/{ => cfg_select}/cfg_select.rs | 0 .../tests/target/{ => cfg_select}/cfg_select_edge_cases.rs | 0 .../rustfmt/tests/target/{ => cfg_select}/cfg_select_stable.rs | 0 .../cfg_select_valid_then_invalid_item_error_count_leak.rs} | 0 src/tools/rustfmt/tests/target/{ => cfg_select}/issue_7045.rs | 0 .../rustfmt/tests/target/{ => cfg_select}/issue_7088_stable.rs | 0 .../rustfmt/tests/target/{ => cfg_select}/issue_7088_unstable.rs | 0 src/tools/rustfmt/tests/target/{ => cfg_select}/issue_7198.rs | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename src/tools/rustfmt/tests/source/{ => cfg_select}/cfg_select.rs (100%) rename src/tools/rustfmt/tests/source/{ => cfg_select}/cfg_select_edge_cases.rs (100%) rename src/tools/rustfmt/tests/source/{ => cfg_select}/cfg_select_stable.rs (100%) rename src/tools/rustfmt/tests/source/{cfg-select-valid-then-invalid-item-error-count-leak.rs => cfg_select/cfg_select_valid_then_invalid_item_error_count_leak.rs} (100%) rename src/tools/rustfmt/tests/source/{ => cfg_select}/issue_7045.rs (100%) rename src/tools/rustfmt/tests/source/{ => cfg_select}/issue_7088_stable.rs (100%) rename src/tools/rustfmt/tests/source/{ => cfg_select}/issue_7088_unstable.rs (100%) rename src/tools/rustfmt/tests/target/{ => cfg_select}/cfg_select.rs (100%) rename src/tools/rustfmt/tests/target/{ => cfg_select}/cfg_select_edge_cases.rs (100%) rename src/tools/rustfmt/tests/target/{ => cfg_select}/cfg_select_stable.rs (100%) rename src/tools/rustfmt/tests/target/{cfg-select-valid-then-invalid-item-error-count-leak.rs => cfg_select/cfg_select_valid_then_invalid_item_error_count_leak.rs} (100%) rename src/tools/rustfmt/tests/target/{ => cfg_select}/issue_7045.rs (100%) rename src/tools/rustfmt/tests/target/{ => cfg_select}/issue_7088_stable.rs (100%) rename src/tools/rustfmt/tests/target/{ => cfg_select}/issue_7088_unstable.rs (100%) rename src/tools/rustfmt/tests/target/{ => cfg_select}/issue_7198.rs (100%) diff --git a/src/tools/rustfmt/tests/source/cfg_select.rs b/src/tools/rustfmt/tests/source/cfg_select/cfg_select.rs similarity index 100% rename from src/tools/rustfmt/tests/source/cfg_select.rs rename to src/tools/rustfmt/tests/source/cfg_select/cfg_select.rs diff --git a/src/tools/rustfmt/tests/source/cfg_select_edge_cases.rs b/src/tools/rustfmt/tests/source/cfg_select/cfg_select_edge_cases.rs similarity index 100% rename from src/tools/rustfmt/tests/source/cfg_select_edge_cases.rs rename to src/tools/rustfmt/tests/source/cfg_select/cfg_select_edge_cases.rs diff --git a/src/tools/rustfmt/tests/source/cfg_select_stable.rs b/src/tools/rustfmt/tests/source/cfg_select/cfg_select_stable.rs similarity index 100% rename from src/tools/rustfmt/tests/source/cfg_select_stable.rs rename to src/tools/rustfmt/tests/source/cfg_select/cfg_select_stable.rs diff --git a/src/tools/rustfmt/tests/source/cfg-select-valid-then-invalid-item-error-count-leak.rs b/src/tools/rustfmt/tests/source/cfg_select/cfg_select_valid_then_invalid_item_error_count_leak.rs similarity index 100% rename from src/tools/rustfmt/tests/source/cfg-select-valid-then-invalid-item-error-count-leak.rs rename to src/tools/rustfmt/tests/source/cfg_select/cfg_select_valid_then_invalid_item_error_count_leak.rs diff --git a/src/tools/rustfmt/tests/source/issue_7045.rs b/src/tools/rustfmt/tests/source/cfg_select/issue_7045.rs similarity index 100% rename from src/tools/rustfmt/tests/source/issue_7045.rs rename to src/tools/rustfmt/tests/source/cfg_select/issue_7045.rs diff --git a/src/tools/rustfmt/tests/source/issue_7088_stable.rs b/src/tools/rustfmt/tests/source/cfg_select/issue_7088_stable.rs similarity index 100% rename from src/tools/rustfmt/tests/source/issue_7088_stable.rs rename to src/tools/rustfmt/tests/source/cfg_select/issue_7088_stable.rs diff --git a/src/tools/rustfmt/tests/source/issue_7088_unstable.rs b/src/tools/rustfmt/tests/source/cfg_select/issue_7088_unstable.rs similarity index 100% rename from src/tools/rustfmt/tests/source/issue_7088_unstable.rs rename to src/tools/rustfmt/tests/source/cfg_select/issue_7088_unstable.rs diff --git a/src/tools/rustfmt/tests/target/cfg_select.rs b/src/tools/rustfmt/tests/target/cfg_select/cfg_select.rs similarity index 100% rename from src/tools/rustfmt/tests/target/cfg_select.rs rename to src/tools/rustfmt/tests/target/cfg_select/cfg_select.rs diff --git a/src/tools/rustfmt/tests/target/cfg_select_edge_cases.rs b/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs similarity index 100% rename from src/tools/rustfmt/tests/target/cfg_select_edge_cases.rs rename to src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs diff --git a/src/tools/rustfmt/tests/target/cfg_select_stable.rs b/src/tools/rustfmt/tests/target/cfg_select/cfg_select_stable.rs similarity index 100% rename from src/tools/rustfmt/tests/target/cfg_select_stable.rs rename to src/tools/rustfmt/tests/target/cfg_select/cfg_select_stable.rs diff --git a/src/tools/rustfmt/tests/target/cfg-select-valid-then-invalid-item-error-count-leak.rs b/src/tools/rustfmt/tests/target/cfg_select/cfg_select_valid_then_invalid_item_error_count_leak.rs similarity index 100% rename from src/tools/rustfmt/tests/target/cfg-select-valid-then-invalid-item-error-count-leak.rs rename to src/tools/rustfmt/tests/target/cfg_select/cfg_select_valid_then_invalid_item_error_count_leak.rs diff --git a/src/tools/rustfmt/tests/target/issue_7045.rs b/src/tools/rustfmt/tests/target/cfg_select/issue_7045.rs similarity index 100% rename from src/tools/rustfmt/tests/target/issue_7045.rs rename to src/tools/rustfmt/tests/target/cfg_select/issue_7045.rs diff --git a/src/tools/rustfmt/tests/target/issue_7088_stable.rs b/src/tools/rustfmt/tests/target/cfg_select/issue_7088_stable.rs similarity index 100% rename from src/tools/rustfmt/tests/target/issue_7088_stable.rs rename to src/tools/rustfmt/tests/target/cfg_select/issue_7088_stable.rs diff --git a/src/tools/rustfmt/tests/target/issue_7088_unstable.rs b/src/tools/rustfmt/tests/target/cfg_select/issue_7088_unstable.rs similarity index 100% rename from src/tools/rustfmt/tests/target/issue_7088_unstable.rs rename to src/tools/rustfmt/tests/target/cfg_select/issue_7088_unstable.rs diff --git a/src/tools/rustfmt/tests/target/issue_7198.rs b/src/tools/rustfmt/tests/target/cfg_select/issue_7198.rs similarity index 100% rename from src/tools/rustfmt/tests/target/issue_7198.rs rename to src/tools/rustfmt/tests/target/cfg_select/issue_7198.rs From 04d74cee3fa5b921bd37540988acf4b159b7790c Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sat, 24 Oct 2026 01:39:11 +0800 Subject: [PATCH 09/53] test: add regression test for miri between-block comment --- .../rustfmt/tests/source/cfg_select/issue_7209.rs | 11 +++++++++++ .../rustfmt/tests/target/cfg_select/issue_7209.rs | 10 ++++++++++ 2 files changed, 21 insertions(+) create mode 100644 src/tools/rustfmt/tests/source/cfg_select/issue_7209.rs create mode 100644 src/tools/rustfmt/tests/target/cfg_select/issue_7209.rs diff --git a/src/tools/rustfmt/tests/source/cfg_select/issue_7209.rs b/src/tools/rustfmt/tests/source/cfg_select/issue_7209.rs new file mode 100644 index 0000000000000..4c06baa145027 --- /dev/null +++ b/src/tools/rustfmt/tests/source/cfg_select/issue_7209.rs @@ -0,0 +1,11 @@ +//@ rustfmt-unstable: true +//@ rustfmt-edition: 2024 + +fn gettid() -> u64 { + cfg_select! { + any(target_os = "solaris", target_os = "illumos") => { + // On Solaris and Illumos, the `pthread_t` is the OS TID. + unsafe { libc::pthread_self() as u64 } + } + } +} diff --git a/src/tools/rustfmt/tests/target/cfg_select/issue_7209.rs b/src/tools/rustfmt/tests/target/cfg_select/issue_7209.rs new file mode 100644 index 0000000000000..19d36edf1d2c7 --- /dev/null +++ b/src/tools/rustfmt/tests/target/cfg_select/issue_7209.rs @@ -0,0 +1,10 @@ +//@ rustfmt-unstable: true +//@ rustfmt-edition: 2024 + +fn gettid() -> u64 { + cfg_select! { + any(target_os = "solaris", target_os = "illumos") => {unsafe { + libc::pthread_self() as u64 + }} + } +} From 44541eb0f2e6d3379166a4ef09065b29c68683f0 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 23 Oct 2026 23:00:41 +0800 Subject: [PATCH 10/53] fix(cfg_select): don't use concise format when in-between comments exist I.e. do not try to use `{{ ... }}` concise double-block formatting if there are comments in-between the outer/inner braces. --- src/tools/rustfmt/src/macros.rs | 2 +- .../rustfmt/src/parse/macros/cfg_select.rs | 61 +++++++++++++++---- .../cfg_select/cfg_select_edge_cases.rs | 6 +- .../tests/target/cfg_select/issue_7209.rs | 7 ++- 4 files changed, 58 insertions(+), 18 deletions(-) diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index 935459ee4670e..46771ceb7a7b2 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -1630,7 +1630,7 @@ fn format_cfg_select( } }; - match arm.double_block_inner() { + match arm.maybe_double_block_concise_formatting(context) { // FIXME(ytmimi) The formatting for the `$predicate => {{}}` case isn't very robust. // There's the potential that we'll lose comments in between the predicate, the `=>` // and the body. We also don't handle comments like `{/*comment*/{}/*comment*/}`. diff --git a/src/tools/rustfmt/src/parse/macros/cfg_select.rs b/src/tools/rustfmt/src/parse/macros/cfg_select.rs index 599eb889dabf9..b2e7cafdee09b 100644 --- a/src/tools/rustfmt/src/parse/macros/cfg_select.rs +++ b/src/tools/rustfmt/src/parse/macros/cfg_select.rs @@ -13,9 +13,11 @@ use rustc_parse::parser::{AllowConstBlockItems, ForceCollect}; use rustc_span::Span; use tracing::debug; +use crate::comment::contains_comment; use crate::expr::is_unsafe_block; use crate::parse::macros::build_stream_parser; use crate::parse::session::ParseSess; +use crate::rewrite::RewriteContext; use crate::spanned::Spanned; // NB: parser recovery can return `Ok(expr)` (or other non-terminals) and still emit errors. We need @@ -161,26 +163,59 @@ impl std::fmt::Debug for CfgSelectArm { } impl CfgSelectArm { - /// Is the cfg_select! arm written with two consecutive blocks. - /// For example `$predicate => {{}}`. If so, extract the inner block. - /// rustfmt will preserve the double block syntax. - pub(crate) fn double_block_inner(&self) -> Option<&ast::Expr> { - // In order to see if we can reach in and grab the inner block we'll - // check the following properties of the outer block: + /// Is the `cfg_select!` arm written with two consecutive blocks in a way that we can apply a + /// concise formatting happy path? For example: + /// + /// ```text + /// $predicate => {{}} + /// ``` + /// + /// If so, extract the inner block. rustfmt will preserve the double block syntax. + pub(crate) fn maybe_double_block_concise_formatting( + &self, + context: &RewriteContext<'_>, + ) -> Option<&ast::Expr> { + // To be able to safely use the concise double block `{{ ... }}` concise formatting, the + // outer block must satisfy all of the following properties: + // // 1. It has a single statement that's also a block // 2. It doesn't have a label // 3. It isn't an unsafe block // 4. It doesn't have any attributes - match &self.expr.kind { - ast::ExprKind::Block(b, label) - if b.stmts.len() == 1 + // 5. There are no comments between the outer blocks and the inner blocks, i.e. + // ``` + // { /* A */ { ... } /* B */ } + // ``` + let outer_expr = &self.expr; + match &outer_expr.kind { + ast::ExprKind::Block(outer_block, label) + if let [stmt] = outer_block.stmts.as_slice() && label.is_none() - && !is_unsafe_block(b) + && !is_unsafe_block(&outer_block) && self.expr.attrs.is_empty() => { - match &b.stmts[0].kind { - // This is where we check that the inner block's statement is also a block - ast::StmtKind::Expr(e) if matches!(e.kind, ast::ExprKind::Block(..)) => Some(e), + match &stmt.kind { + // The inner expr has to be a block. + ast::StmtKind::Expr(e) + if let ast::ExprKind::Block(ref inner_block, _inner_label) = e.kind => + { + // Check for comments in between outer/inner blocks. If there are in-between + // comments, we should not apply the concise formatting. Let's call them + // ``` + // { { ... } } + // ^--- ^--- + // pre_sp post_sp + // ``` + let pre_sp = outer_block.span.until(inner_block.span); + let post_sp = inner_block.span.until(outer_block.span); + if contains_comment(context.snippet(pre_sp)) + || contains_comment(context.snippet(post_sp)) + { + None + } else { + Some(e) + } + } _ => None, } } diff --git a/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs b/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs index 96a3373e339ba..e31cc1c209ead 100644 --- a/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs +++ b/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs @@ -72,7 +72,11 @@ fn check_match_block_formatting() { fn cfg_select_comments_that_might_get_lost() { cfg_select! { - _ => {{ x }} + _ => { + // after opening brace + { x } + // before closing brace + } } cfg_select! { _ => {{ x }} diff --git a/src/tools/rustfmt/tests/target/cfg_select/issue_7209.rs b/src/tools/rustfmt/tests/target/cfg_select/issue_7209.rs index 19d36edf1d2c7..4c06baa145027 100644 --- a/src/tools/rustfmt/tests/target/cfg_select/issue_7209.rs +++ b/src/tools/rustfmt/tests/target/cfg_select/issue_7209.rs @@ -3,8 +3,9 @@ fn gettid() -> u64 { cfg_select! { - any(target_os = "solaris", target_os = "illumos") => {unsafe { - libc::pthread_self() as u64 - }} + any(target_os = "solaris", target_os = "illumos") => { + // On Solaris and Illumos, the `pthread_t` is the OS TID. + unsafe { libc::pthread_self() as u64 } + } } } From 018f54c219ec3ae69498f9e529feffd9acf86c8e Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 23 Oct 2026 23:23:40 +0800 Subject: [PATCH 11/53] fix(cfg_select): don't lose comments/attrs between `=>` and arm expr --- src/tools/rustfmt/src/macros.rs | 77 +++++++++++---- .../rustfmt/src/parse/macros/cfg_select.rs | 2 +- .../cfg_select/cfg_select_edge_cases.rs | 52 ++++++++++ .../tests/target/cfg_select/cfg_select.rs | 20 +++- .../cfg_select/cfg_select_edge_cases.rs | 94 ++++++++++++++++++- 5 files changed, 221 insertions(+), 24 deletions(-) diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index 46771ceb7a7b2..d4c9ac4b152e1 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -21,7 +21,7 @@ use rustc_span::{BytePos, DUMMY_SP, Ident, Pos, Span, Symbol}; use tracing::debug; use crate::comment::{ - CharClasses, FindUncommented, FullCodeCharKind, LineClasses, contains_comment, + CharClasses, FindUncommented, FullCodeCharKind, LineClasses, contains_comment, rewrite_comment, }; use crate::config::StyleEdition; use crate::config::lists::*; @@ -1636,31 +1636,72 @@ fn format_cfg_select( // and the body. We also don't handle comments like `{/*comment*/{}/*comment*/}`. // The `rewrite_match_body` branch also handles `control_brace_style` better. // That said, it helps fix . - Some(expr) => { + Some(inner_expr) => { + // ``` + // => /* comment */ {{ ... }} + // ^------------- between span + // ``` + let after_arrow_comment = context + .snippet(arm.arrow.span.between(arm.expr.span())) + .trim(); + let after_arrow_comment = if after_arrow_comment.is_empty() { + None + } else { + Some(rewrite_comment( + after_arrow_comment, + false, + nested_shape, + context.config, + )?) + }; + let trailing_comma = if arm.trailing_comma.is_some() { "," } else { "" }; - let offset = extra_offset(&predicate_str, nested_shape) + " => {".len(); - let nested_shape = nested_shape - .offset_left(offset, arm.span())? + + // Match `match` layout for after arrow comment, where comment and expr each go + // on own lines. + let prefix_width = if after_arrow_comment.is_some() { + 0 + } else { + extra_offset(&predicate_str, nested_shape) + " => ".len() + }; + + let body_shape = nested_shape + .offset_left(prefix_width + "{".len(), arm.span())? .sub_width("}".len() + trailing_comma.len(), arm.span())?; - let body = expr - .rewrite_result(context, nested_shape) - .map(|rw| format!("{{{rw}}}"))?; + let body = inner_expr.rewrite_result(context, body_shape)?; - Ok(format!("{predicate_str} => {body}{trailing_comma}")) + let body = format!("{{{body}}}{trailing_comma}"); + match after_arrow_comment { + Some(comment) => { + let indent = nested_shape.indent.to_string_with_newline(context.config); + Ok(format!("{predicate_str} =>{indent}{comment}{indent}{body}")) + } + None => Ok(format!("{predicate_str} => {body}")), + } + } + None => { + // Note that we can't _just_ pass the `=>`'s span, we need to pass + // + // ``` + // => /* comment */ #[attr] { ... } + // ^--------------- this whole span until outer expr + // ``` + // + // Using `Spanned::span` here for `arm.expr` to account for outer attributes. + crate::matches::rewrite_match_body( + context, + &arm.expr, + &predicate_str, + nested_shape, + false, + arm.arrow.span.until(arm.expr.span()), + last_arm.is_some_and(|la| la == arm), + ) } - None => crate::matches::rewrite_match_body( - context, - &arm.expr, - &predicate_str, - nested_shape, - false, - arm.arrow.span, - last_arm.is_some_and(|la| la == arm), - ), } }, // Start Span after the opening delimiter. For example, diff --git a/src/tools/rustfmt/src/parse/macros/cfg_select.rs b/src/tools/rustfmt/src/parse/macros/cfg_select.rs index b2e7cafdee09b..6db647eaabb88 100644 --- a/src/tools/rustfmt/src/parse/macros/cfg_select.rs +++ b/src/tools/rustfmt/src/parse/macros/cfg_select.rs @@ -207,7 +207,7 @@ impl CfgSelectArm { // pre_sp post_sp // ``` let pre_sp = outer_block.span.until(inner_block.span); - let post_sp = inner_block.span.until(outer_block.span); + let post_sp = outer_block.span.with_lo(inner_block.span.hi()); if contains_comment(context.snippet(pre_sp)) || contains_comment(context.snippet(post_sp)) { diff --git a/src/tools/rustfmt/tests/source/cfg_select/cfg_select_edge_cases.rs b/src/tools/rustfmt/tests/source/cfg_select/cfg_select_edge_cases.rs index 586a961864c64..955fa72c989e5 100644 --- a/src/tools/rustfmt/tests/source/cfg_select/cfg_select_edge_cases.rs +++ b/src/tools/rustfmt/tests/source/cfg_select/cfg_select_edge_cases.rs @@ -73,6 +73,41 @@ fn cfg_select_comments_that_might_get_lost() { // No trailing comma in source three_blocks_exclamation_point => {{/* a */{ b; c } /* d */}} } + + cfg_select! { + _ => { { x } /* after */ } + } + + cfg_select! { + _ => { { x } // after newline + } + } + + cfg_select! { + _ => /* between */ {{ x }} + } + + cfg_select! { + _ => // between trailing + {{ x }} + } + + // Trailing comma + cfg_select! { + _ => /* between */ {{ x }}, + } + cfg_select! { + _ => /* between */ {{ a; b }}, + } + + cfg_select! { + _ => /* between */ {{ a; b }} + } + + cfg_select! { + _ => /* multi + line */ {{ x }} + } } fn other_cfg_select_cases() { @@ -197,6 +232,23 @@ fn other_cfg_select_cases() { #[inline] || { }} }; + + cfg_select! { + _ => /* before attr */ #[attr] { z } + } + + cfg_select! { + _ => #[attr] /* after attr */ { z } + } + + cfg_select! { + _ => // line comment before attr + #[attr] { z } + } + + cfg_select! { + _ => /* before attr */ #[attr] /* after attr */ { z } + } } fn long_predicate() { diff --git a/src/tools/rustfmt/tests/target/cfg_select/cfg_select.rs b/src/tools/rustfmt/tests/target/cfg_select/cfg_select.rs index 1c2a1ed91a7e0..508dc0d931b1a 100644 --- a/src/tools/rustfmt/tests/target/cfg_select/cfg_select.rs +++ b/src/tools/rustfmt/tests/target/cfg_select/cfg_select.rs @@ -292,9 +292,15 @@ cfg_select! { cfg_select! { any(true, true, true, true,) => {} - not(false) => {} + not(false) => + /* comment before opening brace */ + {} - any(false) => "any", + any(false) => + // comment before brace + { + "any" + } } // A bunch of mixed predicates @@ -704,9 +710,15 @@ mod nested { cfg_select! { any(true, true, true, true,) => {} - not(false) => {} + not(false) => + /* comment before opening brace */ + {} - any(false) => "any", + any(false) => + // comment before brace + { + "any" + } } // A bunch of mixed predicates diff --git a/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs b/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs index e31cc1c209ead..aa49a5e928c5a 100644 --- a/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs +++ b/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs @@ -79,7 +79,9 @@ fn cfg_select_comments_that_might_get_lost() { } } cfg_select! { - _ => {{ x }} + _ => + /* after => */ + {{ x }} } cfg_select! { _ => {{ x }} @@ -102,6 +104,61 @@ fn cfg_select_comments_that_might_get_lost() { } /* d */ }} } + + cfg_select! { + _ => { + { x } /* after */ + } + } + + cfg_select! { + _ => { + { x } // after newline + } + } + + cfg_select! { + _ => + /* between */ + {{ x }} + } + + cfg_select! { + _ => + // between trailing + {{ x }} + } + + // Trailing comma + cfg_select! { + _ => + /* between */ + {{ x }}, + } + cfg_select! { + _ => + /* between */ + {{ + a; + b + }}, + } + + cfg_select! { + _ => + /* between */ + {{ + a; + b + }} + } + + cfg_select! { + _ => + /* multi + line */ + {{ x }} + } } fn other_cfg_select_cases() { @@ -281,6 +338,41 @@ fn other_cfg_select_cases() { || {} } }; + + cfg_select! { + _ => + /* before attr */ + #[attr] + { + z + } + } + + cfg_select! { + _ => + #[attr] /* after attr */ + { + z + } + } + + cfg_select! { + _ => + // line comment before attr + #[attr] + { + z + } + } + + cfg_select! { + _ => + /* before attr */ + #[attr] /* after attr */ + { + z + } + } } fn long_predicate() { From 0aff56c6f448ccaf19ba3fdb53a12597919dee7d Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sat, 24 Oct 2026 00:46:48 +0800 Subject: [PATCH 12/53] fix(cfg_select): don't lose comments between predicate and `=>` --- src/tools/rustfmt/src/macros.rs | 36 ++++++++++++++----- .../tests/source/cfg_select/cfg_select.rs | 21 +++++++---- .../cfg_select/cfg_select_edge_cases.rs | 15 ++++++++ .../tests/target/cfg_select/cfg_select.rs | 25 +++++++++---- .../cfg_select/cfg_select_edge_cases.rs | 20 ++++++++++- 5 files changed, 96 insertions(+), 21 deletions(-) diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index d4c9ac4b152e1..065f1c64dfab2 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -21,7 +21,8 @@ use rustc_span::{BytePos, DUMMY_SP, Ident, Pos, Span, Symbol}; use tracing::debug; use crate::comment::{ - CharClasses, FindUncommented, FullCodeCharKind, LineClasses, contains_comment, rewrite_comment, + CharClasses, FindUncommented, FullCodeCharKind, LineClasses, + combine_strs_with_missing_comments, contains_comment, rewrite_comment, }; use crate::config::StyleEdition; use crate::config::lists::*; @@ -40,8 +41,9 @@ use crate::shape::{Indent, Shape}; use crate::source_map::SpanUtils; use crate::spanned::Spanned; use crate::utils::{ - NodeIdExt, extra_offset, filtered_str_fits, indent_next_line, is_empty_line, mk_sp, - remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, + NodeIdExt, extra_offset, filtered_str_fits, indent_next_line, is_empty_line, + last_line_contains_single_line_comment, mk_sp, remove_trailing_white_spaces, rewrite_ident, + trim_left_preserve_layout, }; use crate::visitor::FmtVisitor; @@ -1630,12 +1632,30 @@ fn format_cfg_select( } }; + // We need to account for comments between predicate and `=>`. + let before_arrow_sp = arm.predicate.span().between(arm.arrow.span); + let before_arrow = context.snippet(before_arrow_sp); + let predicate_str = if contains_comment(before_arrow) { + // We cannot rewrite if the last line is a line comment, it will comment out `=>`. + if last_line_contains_single_line_comment(before_arrow.trim_end()) { + return Err(RewriteError::Unknown); + } + Cow::Owned(combine_strs_with_missing_comments( + context, + &predicate_str, + "", + before_arrow_sp, + nested_shape, + true, + )?) + } else { + predicate_str + }; + match arm.maybe_double_block_concise_formatting(context) { - // FIXME(ytmimi) The formatting for the `$predicate => {{}}` case isn't very robust. - // There's the potential that we'll lose comments in between the predicate, the `=>` - // and the body. We also don't handle comments like `{/*comment*/{}/*comment*/}`. - // The `rewrite_match_body` branch also handles `control_brace_style` better. - // That said, it helps fix . + // FIXME(ytmimi): the `rewrite_match_body` branch also handles `control_brace_style` + // better. That said, it helps fix + // . Some(inner_expr) => { // ``` // => /* comment */ {{ ... }} diff --git a/src/tools/rustfmt/tests/source/cfg_select/cfg_select.rs b/src/tools/rustfmt/tests/source/cfg_select/cfg_select.rs index ca6abc8aa4af2..d6dc391dd564f 100644 --- a/src/tools/rustfmt/tests/source/cfg_select/cfg_select.rs +++ b/src/tools/rustfmt/tests/source/cfg_select/cfg_select.rs @@ -327,7 +327,7 @@ cfg_select! { any(true, /* comment */ true, true, // true, true, ) - // comment before arrow + /* comment before arrow */ => {} not(false // comment @@ -339,12 +339,12 @@ cfg_select! { ) => "any" } -// comments before and after the `=>` get dropped right now +// comments before and after the `=>` are kept cfg_select! { any(true, true, true, true, ) - // commetn before arrow + /* comment before arrow */ => {} not(false @@ -357,6 +357,15 @@ cfg_select! { "any" } +// FIXME: not formatted, a line comment before `=>` would comment out the `=>` +cfg_select! { + any(true, + true, true, + true, ) + // line comment before arrow + => {} +} + // A bunch of mixed predicates cfg_select! { @@ -752,7 +761,7 @@ cfg_select! { any(true, /* comment */ true, true, // true, true, ) - // comment before arrow + /* comment before arrow */ => {} not(false // comment @@ -764,12 +773,12 @@ cfg_select! { ) => "any" } -// comments before and after the `=>` get dropped right now +// comments before and after the `=>` are kept cfg_select! { any(true, true, true, true, ) - // commetn before arrow + /* comment before arrow */ => {} not(false diff --git a/src/tools/rustfmt/tests/source/cfg_select/cfg_select_edge_cases.rs b/src/tools/rustfmt/tests/source/cfg_select/cfg_select_edge_cases.rs index 955fa72c989e5..d029f8eb30469 100644 --- a/src/tools/rustfmt/tests/source/cfg_select/cfg_select_edge_cases.rs +++ b/src/tools/rustfmt/tests/source/cfg_select/cfg_select_edge_cases.rs @@ -108,6 +108,21 @@ fn cfg_select_comments_that_might_get_lost() { _ => /* multi line */ {{ x }} } + + // Comments before `=>` + cfg_select! { + _ /* before arrow */ => {{ x }} + _ /* before arrow */ => x, + _ /* before arrow */ => /* after arrow */ {{ x }} + all(target_os = "linux", target_arch = "x86_64", target_env = "gnu") /* long comment before arrow */ => {{ x }} + _ /* before arrow */ => {} + } + + cfg_select! { + // Not formatted: a line comment before `=>`. + c // before arrow + => x, + } } fn other_cfg_select_cases() { diff --git a/src/tools/rustfmt/tests/target/cfg_select/cfg_select.rs b/src/tools/rustfmt/tests/target/cfg_select/cfg_select.rs index 508dc0d931b1a..9d9bbfe0b4c9a 100644 --- a/src/tools/rustfmt/tests/target/cfg_select/cfg_select.rs +++ b/src/tools/rustfmt/tests/target/cfg_select/cfg_select.rs @@ -277,7 +277,8 @@ cfg_select! { true, /* comment */ true, true, // true, true, - ) => {} + ) + /* comment before arrow */ => {} not( false // comment @@ -288,9 +289,10 @@ cfg_select! { ) => "any", } -// comments before and after the `=>` get dropped right now +// comments before and after the `=>` are kept cfg_select! { - any(true, true, true, true,) => {} + any(true, true, true, true,) + /* comment before arrow */ => {} not(false) => /* comment before opening brace */ @@ -303,6 +305,15 @@ cfg_select! { } } +// FIXME: not formatted, a line comment before `=>` would comment out the `=>` +cfg_select! { + any(true, + true, true, + true, ) + // line comment before arrow + => {} +} + // A bunch of mixed predicates cfg_select! { // When all predicates are simple uses mixed list formatting @@ -695,7 +706,8 @@ mod nested { true, /* comment */ true, true, // true, true, - ) => {} + ) + /* comment before arrow */ => {} not( false // comment @@ -706,9 +718,10 @@ mod nested { ) => "any", } - // comments before and after the `=>` get dropped right now + // comments before and after the `=>` are kept cfg_select! { - any(true, true, true, true,) => {} + any(true, true, true, true,) + /* comment before arrow */ => {} not(false) => /* comment before opening brace */ diff --git a/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs b/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs index aa49a5e928c5a..629570412180c 100644 --- a/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs +++ b/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs @@ -84,7 +84,7 @@ fn cfg_select_comments_that_might_get_lost() { {{ x }} } cfg_select! { - _ => {{ x }} + _ /* before arrow */ => {{ x }} } cfg_select! { @@ -159,6 +159,24 @@ fn cfg_select_comments_that_might_get_lost() { line */ {{ x }} } + + // Comments before `=>` + cfg_select! { + _ /* before arrow */ => {{ x }} + _ /* before arrow */ => x, + _ /* before arrow */ => + /* after arrow */ + {{ x }} + all(target_os = "linux", target_arch = "x86_64", target_env = "gnu") + /* long comment before arrow */ => {{ x }} + _ /* before arrow */ => {} + } + + cfg_select! { + // Not formatted: a line comment before `=>`. + c // before arrow + => x, + } } fn other_cfg_select_cases() { From 0dd546ab5f2008fbbc7494706c001c9f2c896ae3 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sat, 24 Oct 2026 01:29:50 +0800 Subject: [PATCH 13/53] fix(cfg_select): don't lose comment between body and trailing comma --- src/tools/rustfmt/src/macros.rs | 222 ++++++++++-------- .../cfg_select/cfg_select_edge_cases.rs | 21 ++ .../cfg_select/cfg_select_edge_cases.rs | 21 ++ 3 files changed, 162 insertions(+), 102 deletions(-) diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index 065f1c64dfab2..3eddfa6317a61 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -22,7 +22,7 @@ use tracing::debug; use crate::comment::{ CharClasses, FindUncommented, FullCodeCharKind, LineClasses, - combine_strs_with_missing_comments, contains_comment, rewrite_comment, + combine_strs_with_missing_comments, contains_comment, recover_comment_removed, rewrite_comment, }; use crate::config::StyleEdition; use crate::config::lists::*; @@ -31,7 +31,9 @@ use crate::header::{HeaderPart, format_header}; use crate::is_nightly_channel; use crate::lists::{ListFormatting, itemize_list, write_list}; use crate::overflow; -use crate::parse::macros::cfg_select::{CfgSelectFormatPredicate, parse_cfg_select_arms}; +use crate::parse::macros::cfg_select::{ + CfgSelectArm, CfgSelectFormatPredicate, parse_cfg_select_arms, +}; use crate::parse::macros::lazy_static::parse_lazy_static; use crate::parse::macros::{ParsedMacroArgs, parse_expr, parse_macro_args}; use crate::rewrite::{ @@ -1624,106 +1626,7 @@ fn format_cfg_select( "}", |arm| arm.span().lo(), |arm| arm.span().hi(), - |arm| { - let predicate_str = match &arm.predicate { - CfgSelectFormatPredicate::Wildcard(_t) => Cow::Borrowed("_"), - CfgSelectFormatPredicate::Cfg(meta_item_inner) => { - Cow::Owned(meta_item_inner.rewrite_result(context, nested_shape)?) - } - }; - - // We need to account for comments between predicate and `=>`. - let before_arrow_sp = arm.predicate.span().between(arm.arrow.span); - let before_arrow = context.snippet(before_arrow_sp); - let predicate_str = if contains_comment(before_arrow) { - // We cannot rewrite if the last line is a line comment, it will comment out `=>`. - if last_line_contains_single_line_comment(before_arrow.trim_end()) { - return Err(RewriteError::Unknown); - } - Cow::Owned(combine_strs_with_missing_comments( - context, - &predicate_str, - "", - before_arrow_sp, - nested_shape, - true, - )?) - } else { - predicate_str - }; - - match arm.maybe_double_block_concise_formatting(context) { - // FIXME(ytmimi): the `rewrite_match_body` branch also handles `control_brace_style` - // better. That said, it helps fix - // . - Some(inner_expr) => { - // ``` - // => /* comment */ {{ ... }} - // ^------------- between span - // ``` - let after_arrow_comment = context - .snippet(arm.arrow.span.between(arm.expr.span())) - .trim(); - let after_arrow_comment = if after_arrow_comment.is_empty() { - None - } else { - Some(rewrite_comment( - after_arrow_comment, - false, - nested_shape, - context.config, - )?) - }; - - let trailing_comma = if arm.trailing_comma.is_some() { - "," - } else { - "" - }; - - // Match `match` layout for after arrow comment, where comment and expr each go - // on own lines. - let prefix_width = if after_arrow_comment.is_some() { - 0 - } else { - extra_offset(&predicate_str, nested_shape) + " => ".len() - }; - - let body_shape = nested_shape - .offset_left(prefix_width + "{".len(), arm.span())? - .sub_width("}".len() + trailing_comma.len(), arm.span())?; - let body = inner_expr.rewrite_result(context, body_shape)?; - - let body = format!("{{{body}}}{trailing_comma}"); - match after_arrow_comment { - Some(comment) => { - let indent = nested_shape.indent.to_string_with_newline(context.config); - Ok(format!("{predicate_str} =>{indent}{comment}{indent}{body}")) - } - None => Ok(format!("{predicate_str} => {body}")), - } - } - None => { - // Note that we can't _just_ pass the `=>`'s span, we need to pass - // - // ``` - // => /* comment */ #[attr] { ... } - // ^--------------- this whole span until outer expr - // ``` - // - // Using `Spanned::span` here for `arm.expr` to account for outer attributes. - crate::matches::rewrite_match_body( - context, - &arm.expr, - &predicate_str, - nested_shape, - false, - arm.arrow.span.until(arm.expr.span()), - last_arm.is_some_and(|la| la == arm), - ) - } - } - }, + |arm| rewrite_cfg_select_arm(context, nested_shape, last_arm, arm), // Start Span after the opening delimiter. For example, // ``` // cfg_select! { @@ -1755,3 +1658,118 @@ fn format_cfg_select( Ok(rewrite) } + +fn rewrite_cfg_select_arm( + context: &RewriteContext<'_>, + nested_shape: Shape, + last_arm: Option<&CfgSelectArm>, + arm: &CfgSelectArm, +) -> Result { + let try_rewrite = || { + let predicate_str = match &arm.predicate { + CfgSelectFormatPredicate::Wildcard(_t) => Cow::Borrowed("_"), + CfgSelectFormatPredicate::Cfg(meta_item_inner) => { + Cow::Owned(meta_item_inner.rewrite_result(context, nested_shape)?) + } + }; + + // We need to account for comments between predicate and `=>`. + let before_arrow_sp = arm.predicate.span().between(arm.arrow.span); + let before_arrow = context.snippet(before_arrow_sp); + let predicate_str = if contains_comment(before_arrow) { + // We cannot rewrite if the last line is a line comment, it will comment out + // `=>`. + if last_line_contains_single_line_comment(before_arrow.trim_end()) { + return Ok(context.snippet(arm.span()).to_owned()); + } + + Cow::Owned(combine_strs_with_missing_comments( + context, + &predicate_str, + "", + before_arrow_sp, + nested_shape, + true, + )?) + } else { + predicate_str + }; + + match arm.maybe_double_block_concise_formatting(context) { + // FIXME(ytmimi): the `rewrite_match_body` branch also handles + // `control_brace_style` better. That said, it helps fix + // . + Some(inner_expr) => { + // ``` + // => /* comment */ {{ ... }} + // ^------------- between span + // ``` + let after_arrow_comment = context + .snippet(arm.arrow.span.between(arm.expr.span())) + .trim(); + let after_arrow_comment = if after_arrow_comment.is_empty() { + None + } else { + Some(rewrite_comment( + after_arrow_comment, + false, + nested_shape, + context.config, + )?) + }; + + let trailing_comma = if arm.trailing_comma.is_some() { + "," + } else { + "" + }; + + // Match `match` layout for after arrow comment, where comment and expr each + // go on own lines. + let prefix_width = if after_arrow_comment.is_some() { + 0 + } else { + extra_offset(&predicate_str, nested_shape) + " => ".len() + }; + + let body_shape = nested_shape + .offset_left(prefix_width + "{".len(), arm.span())? + .sub_width("}".len() + trailing_comma.len(), arm.span())?; + let body = inner_expr.rewrite_result(context, body_shape)?; + + let body = format!("{{{body}}}{trailing_comma}"); + match after_arrow_comment { + Some(comment) => { + let indent = nested_shape.indent.to_string_with_newline(context.config); + Ok(format!("{predicate_str} =>{indent}{comment}{indent}{body}")) + } + None => Ok(format!("{predicate_str} => {body}")), + } + } + None => { + // Note that we can't _just_ pass the `=>`'s span, we need to pass + // + // ``` + // => /* comment */ #[attr] { ... } + // ^--------------- this whole span until outer expr + // ``` + // + // Using `Spanned::span` here for `arm.expr` to account for outer + // attributes. + crate::matches::rewrite_match_body( + context, + &arm.expr, + &predicate_str, + nested_shape, + false, + arm.arrow.span.until(arm.expr.span()), + last_arm.is_some_and(|la| la == arm), + ) + } + } + }; + + let rw = try_rewrite()?; + // NB: there could be a comment between body and trailing comma. + Ok(recover_comment_removed(rw, arm.span(), context)) +} diff --git a/src/tools/rustfmt/tests/source/cfg_select/cfg_select_edge_cases.rs b/src/tools/rustfmt/tests/source/cfg_select/cfg_select_edge_cases.rs index d029f8eb30469..cec0bb4b9c239 100644 --- a/src/tools/rustfmt/tests/source/cfg_select/cfg_select_edge_cases.rs +++ b/src/tools/rustfmt/tests/source/cfg_select/cfg_select_edge_cases.rs @@ -123,6 +123,27 @@ fn cfg_select_comments_that_might_get_lost() { c // before arrow => x, } + + cfg_select! { + _ => x /* before comma */, + } + + cfg_select! { + _ => {{ x }} /* before comma */, + } + + cfg_select! { + a => b, + } + + cfg_select! { + _ => xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx /* too long */, + } + + cfg_select! { + _ => x // trailing newline + , + } } fn other_cfg_select_cases() { diff --git a/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs b/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs index 629570412180c..6b2f713116e0c 100644 --- a/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs +++ b/src/tools/rustfmt/tests/target/cfg_select/cfg_select_edge_cases.rs @@ -177,6 +177,27 @@ fn cfg_select_comments_that_might_get_lost() { c // before arrow => x, } + + cfg_select! { + _ => x /* before comma */, + } + + cfg_select! { + _ => {{ x }} /* before comma */, + } + + cfg_select! { + a => b, + } + + cfg_select! { + _ => xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx /* too long */, + } + + cfg_select! { + _ => x // trailing newline + , + } } fn other_cfg_select_cases() { From a5198eb788defcf1b45900af0fe2dc3a38c3c183 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sat, 24 Oct 2026 14:25:54 +0800 Subject: [PATCH 14/53] test(cfg_select): snapshot current `control_brace_style` behavior --- .../control_brace_style_always_next_line.rs | 33 +++++++++++++++++++ .../control_brace_style_always_same_line.rs | 33 +++++++++++++++++++ .../control_brace_style_always_next_line.rs | 28 ++++++++++++++++ .../control_brace_style_always_same_line.rs | 27 +++++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_next_line.rs create mode 100644 src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_same_line.rs create mode 100644 src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs create mode 100644 src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_same_line.rs diff --git a/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_next_line.rs b/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_next_line.rs new file mode 100644 index 0000000000000..0214052f3cdcd --- /dev/null +++ b/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_next_line.rs @@ -0,0 +1,33 @@ +//@ rustfmt-unstable: true +//@ rustfmt-edition: 2024 +//@ rustfmt-control_brace_style: AlwaysNextLine + +cfg_select! { + _ => { x } +} + +cfg_select! { + _ => { + x + } +} + +cfg_select! { + _ => {{ x }} +} + +cfg_select! { + _ => {{ + x + }} +} + +cfg_select! { + _ => unsafe { x } +} + +cfg_select! { + _ => { + unsafe { x } + } +} diff --git a/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_same_line.rs b/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_same_line.rs new file mode 100644 index 0000000000000..a3aacb709811c --- /dev/null +++ b/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_same_line.rs @@ -0,0 +1,33 @@ +//@ rustfmt-unstable: true +//@ rustfmt-control_brace_style: AlwaysSameLine +// ^ NB: default value + +cfg_select! { + _ => { x } +} + +cfg_select! { + _ => { + x + } +} + +cfg_select! { + _ => {{ x }} +} + +cfg_select! { + _ => {{ + x + }} +} + +cfg_select! { + _ => unsafe { x } +} + +cfg_select! { + _ => { + unsafe { x } + } +} diff --git a/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs b/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs new file mode 100644 index 0000000000000..156a9749696ec --- /dev/null +++ b/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs @@ -0,0 +1,28 @@ +//@ rustfmt-unstable: true +//@ rustfmt-edition: 2024 +//@ rustfmt-control_brace_style: AlwaysNextLine + +cfg_select! { + _ => x, +} + +cfg_select! { + _ => x, +} + +cfg_select! { + _ => {{ x }} +} + +cfg_select! { + _ => {{ x }} +} + +cfg_select! { + _ => + unsafe { x }, +} + +cfg_select! { + _ => {unsafe { x }} +} diff --git a/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_same_line.rs b/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_same_line.rs new file mode 100644 index 0000000000000..f77205cd03d32 --- /dev/null +++ b/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_same_line.rs @@ -0,0 +1,27 @@ +//@ rustfmt-unstable: true +//@ rustfmt-control_brace_style: AlwaysSameLine +// ^ NB: default value + +cfg_select! { + _ => x, +} + +cfg_select! { + _ => x, +} + +cfg_select! { + _ => {{ x }} +} + +cfg_select! { + _ => {{ x }} +} + +cfg_select! { + _ => unsafe { x }, +} + +cfg_select! { + _ => {unsafe { x }} +} From 19cdcb60c8adb276df8990393cd7800db5cd0e56 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sun, 25 Oct 2026 23:35:24 -0400 Subject: [PATCH 15/53] refactor: exhaustively match on `token::LitKind` Based on rustfmt issue 7215 it seems that we missed handling different kinds of string literals. Before trying to address that issue I thought it would be best if we refactored the match to be exhaustive. --- src/tools/rustfmt/src/expr.rs | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 0b82acba264da..a9eccf1c7785a 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -1371,13 +1371,31 @@ pub(crate) fn rewrite_literal( token::LitKind::Str => rewrite_string_lit(context, span, shape), token::LitKind::Integer => rewrite_int_lit(context, token_lit, span, shape), token::LitKind::Float => rewrite_float_lit(context, token_lit, span, shape), - _ => wrap_str( - context.snippet(span).to_owned(), - context.config.max_width(), - context.config.tab_spaces(), - shape, - ) - .max_width_error(shape.width, span), + token::LitKind::Bool + | token::LitKind::Byte + | token::LitKind::ByteStr + | token::LitKind::ByteStrRaw(_) + | token::LitKind::Char + | token::LitKind::CStr + | token::LitKind::CStrRaw(_) + | token::LitKind::StrRaw(_) + | token::LitKind::Err(_) => { + // token::LitKind::Bool is true or false + // token::LitKind::Byte is b'' + // token::LitKind::ByteStr is b"" + // token::LitKind::ByteStrRaw(_) is br"" or br#""# + // token::LitKind::Char is '' + // token::LitKind::CStr is c"" + // token::LitKind::CStrRaw(_) is cr"" or cr#""# + // token::LitKind::StrRaw(_) is r"" or r#""# + wrap_str( + context.snippet(span).to_owned(), + context.config.max_width(), + context.config.tab_spaces(), + shape, + ) + .max_width_error(shape.width, span) + } } } From e5ad9ad1b3eb83d01fea1b2742c6385781b178ea Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sat, 24 Oct 2026 15:05:43 +0800 Subject: [PATCH 16/53] fix(cfg_select): respect `control_brace_style` config --- src/tools/rustfmt/src/macros.rs | 16 ++++++++++++---- .../control_brace_style_always_next_line.rs | 9 ++++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index 3eddfa6317a61..421094b25dfc9 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -24,8 +24,8 @@ use crate::comment::{ CharClasses, FindUncommented, FullCodeCharKind, LineClasses, combine_strs_with_missing_comments, contains_comment, recover_comment_removed, rewrite_comment, }; -use crate::config::StyleEdition; use crate::config::lists::*; +use crate::config::{ControlBraceStyle, StyleEdition}; use crate::expr::{RhsAssignKind, rewrite_array, rewrite_assign_rhs}; use crate::header::{HeaderPart, format_header}; use crate::is_nightly_channel; @@ -1724,9 +1724,13 @@ fn rewrite_cfg_select_arm( "" }; - // Match `match` layout for after arrow comment, where comment and expr each - // go on own lines. - let prefix_width = if after_arrow_comment.is_some() { + // Force next-line layout if there's a comment after `=>`, like `match` arms. + let next_line_open_brace = matches!( + context.config.control_brace_style(), + ControlBraceStyle::AlwaysNextLine, + ) || after_arrow_comment.is_some(); + + let prefix_width = if next_line_open_brace { 0 } else { extra_offset(&predicate_str, nested_shape) + " => ".len() @@ -1743,6 +1747,10 @@ fn rewrite_cfg_select_arm( let indent = nested_shape.indent.to_string_with_newline(context.config); Ok(format!("{predicate_str} =>{indent}{comment}{indent}{body}")) } + None if next_line_open_brace => { + let indent = nested_shape.indent.to_string_with_newline(context.config); + Ok(format!("{predicate_str} =>{indent}{body}")) + } None => Ok(format!("{predicate_str} => {body}")), } } diff --git a/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs b/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs index 156a9749696ec..67b1c76d05c11 100644 --- a/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs +++ b/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs @@ -11,11 +11,13 @@ cfg_select! { } cfg_select! { - _ => {{ x }} + _ => + {{ x }} } cfg_select! { - _ => {{ x }} + _ => + {{ x }} } cfg_select! { @@ -24,5 +26,6 @@ cfg_select! { } cfg_select! { - _ => {unsafe { x }} + _ => + {unsafe { x }} } From 3d87f59b272499ce0f66983ae8242d5ad057622b Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sun, 25 Oct 2026 10:49:00 +0800 Subject: [PATCH 17/53] test(cfg_select): add coverage for `control_brace_style` with comments after `=>` --- .../control_brace_style_always_next_line.rs | 40 ++++++++++++ .../control_brace_style_always_same_line.rs | 40 ++++++++++++ .../control_brace_style_always_next_line.rs | 62 +++++++++++++++++++ .../control_brace_style_always_same_line.rs | 62 +++++++++++++++++++ 4 files changed, 204 insertions(+) diff --git a/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_next_line.rs b/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_next_line.rs index 0214052f3cdcd..f749ec75094e2 100644 --- a/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_next_line.rs +++ b/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_next_line.rs @@ -31,3 +31,43 @@ cfg_select! { unsafe { x } } } + +cfg_select! { + _ => // line comment + {{ x }} +} + +cfg_select! { + _ => // line comment + { a; b } +} + +cfg_select! { + _ => /* after */ {{ x }} +} + +cfg_select! { + _ => /* after */ {a; b} +} + +cfg_select! { + _ => /* after + multiline */ {{ x }} +} + +cfg_select! { + _ => /* after + multiline */ { a; b } +} + +cfg_select! { + _ => /* after */ {{ + x + }} +} + +cfg_select! { + _ => /* after */ { + a; b + } +} diff --git a/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_same_line.rs b/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_same_line.rs index a3aacb709811c..ee76980bd3cce 100644 --- a/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_same_line.rs +++ b/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_same_line.rs @@ -31,3 +31,43 @@ cfg_select! { unsafe { x } } } + +cfg_select! { + _ => // line comment + {{ x }} +} + +cfg_select! { + _ => // line comment + { a; b } +} + +cfg_select! { + _ => /* after */ {{ x }} +} + +cfg_select! { + _ => /* after */ {a; b} +} + +cfg_select! { + _ => /* after + multiline */ {{ x }} +} + +cfg_select! { + _ => /* after + multiline */ { a; b } +} + +cfg_select! { + _ => /* after */ {{ + x + }} +} + +cfg_select! { + _ => /* after */ { + a; b + } +} diff --git a/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs b/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs index 67b1c76d05c11..bacaede7266de 100644 --- a/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs +++ b/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs @@ -29,3 +29,65 @@ cfg_select! { _ => {unsafe { x }} } + +cfg_select! { + _ => + // line comment + {{ x }} +} + +cfg_select! { + _ => + // line comment + { + a; + b + } +} + +cfg_select! { + _ => + /* after */ + {{ x }} +} + +cfg_select! { + _ => + /* after */ + { + a; + b + } +} + +cfg_select! { + _ => + /* after + multiline */ + {{ x }} +} + +cfg_select! { + _ => + /* after + multiline */ + { + a; + b + } +} + +cfg_select! { + _ => + /* after */ + {{ x }} +} + +cfg_select! { + _ => + /* after */ + { + a; + b + } +} diff --git a/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_same_line.rs b/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_same_line.rs index f77205cd03d32..fed03ee4d2fab 100644 --- a/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_same_line.rs +++ b/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_same_line.rs @@ -25,3 +25,65 @@ cfg_select! { cfg_select! { _ => {unsafe { x }} } + +cfg_select! { + _ => + // line comment + {{ x }} +} + +cfg_select! { + _ => + // line comment + { + a; + b + } +} + +cfg_select! { + _ => + /* after */ + {{ x }} +} + +cfg_select! { + _ => + /* after */ + { + a; + b + } +} + +cfg_select! { + _ => + /* after + multiline */ + {{ x }} +} + +cfg_select! { + _ => + /* after + multiline */ + { + a; + b + } +} + +cfg_select! { + _ => + /* after */ + {{ x }} +} + +cfg_select! { + _ => + /* after */ + { + a; + b + } +} From 67bfa44ac8f4f194d23e1f89e8a36c813092d556 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sun, 25 Oct 2026 22:42:12 +0800 Subject: [PATCH 18/53] test(cfg_select): add coverage for long comment before arrow --- .../control_brace_style_always_next_line.rs | 27 +++++++++++++ .../control_brace_style_always_same_line.rs | 27 +++++++++++++ .../control_brace_style_always_next_line.rs | 39 +++++++++++++++++++ .../control_brace_style_always_same_line.rs | 38 ++++++++++++++++++ 4 files changed, 131 insertions(+) diff --git a/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_next_line.rs b/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_next_line.rs index f749ec75094e2..a12391c20298d 100644 --- a/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_next_line.rs +++ b/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_next_line.rs @@ -71,3 +71,30 @@ cfg_select! { a; b } } + +cfg_select! { + _ /* this comment is _very_ long but brace should fit ---------------------------------- */ => { a; b } +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 101 columns incl. arrow ------------------- */ => { a; b } +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 100 columns itself -------------------------- */ => { a; b } +} + +cfg_select! { + _ /* this comment is _very_ long but shouldn't change next next line --------------------- */ => + {{ ThisLineIsShort }} +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 101 columns incl. arrow ------------------- */ => + {{ ThisLineIsShort }} +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 100 columns itself -------------------------- */ => + {{ ThisLineIsShort }} +} diff --git a/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_same_line.rs b/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_same_line.rs index ee76980bd3cce..cda500f61afe6 100644 --- a/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_same_line.rs +++ b/src/tools/rustfmt/tests/source/cfg_select/control_brace_style_always_same_line.rs @@ -71,3 +71,30 @@ cfg_select! { a; b } } + +cfg_select! { + _ /* this comment is _very_ long but brace should fit ---------------------------------- */ => { a; b } +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 101 columns incl. arrow ------------------- */ => { a; b } +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 100 columns itself -------------------------- */ => { a; b } +} + +cfg_select! { + _ /* this comment is _very_ long but shouldn't change next next line --------------------- */ => + {{ ThisLineIsShort }} +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 101 columns incl. arrow ------------------- */ => + {{ ThisLineIsShort }} +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 100 columns itself -------------------------- */ => + {{ ThisLineIsShort }} +} diff --git a/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs b/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs index bacaede7266de..d0a8880d97bd7 100644 --- a/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs +++ b/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_next_line.rs @@ -91,3 +91,42 @@ cfg_select! { b } } + +cfg_select! { + _ /* this comment is _very_ long but brace should fit ---------------------------------- */ => + { + a; + b + } +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 101 columns incl. arrow ------------------- */ => + { + a; + b + } +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 100 columns itself -------------------------- */ => + { + a; + b + } +} + +cfg_select! { + _ /* this comment is _very_ long but shouldn't change next next line --------------------- */ => + {{ ThisLineIsShort }} +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 101 columns incl. arrow ------------------- */ => + {{ ThisLineIsShort }} +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 100 columns itself -------------------------- */ => + {{ ThisLineIsShort }} +} diff --git a/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_same_line.rs b/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_same_line.rs index fed03ee4d2fab..827b579509d84 100644 --- a/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_same_line.rs +++ b/src/tools/rustfmt/tests/target/cfg_select/control_brace_style_always_same_line.rs @@ -87,3 +87,41 @@ cfg_select! { b } } + +cfg_select! { + _ /* this comment is _very_ long but brace should fit ---------------------------------- */ => { + a; + b + } +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 101 columns incl. arrow ------------------- */ => + { + a; + b + } +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 100 columns itself -------------------------- */ => + { + a; + b + } +} + +cfg_select! { + _ /* this comment is _very_ long but shouldn't change next next line --------------------- */ => + {{ ThisLineIsShort }} +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 101 columns incl. arrow ------------------- */ => + {{ ThisLineIsShort }} +} + +cfg_select! { + _ /* this comment is _very_ long and adds up to 100 columns itself -------------------------- */ => + {{ ThisLineIsShort }} +} From ecc66a846c851d076b7c323e91e043cad4f82c2d Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:05:55 +0000 Subject: [PATCH 19/53] ci: Disable credential persistence Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/check_diff.yml | 2 ++ src/tools/rustfmt/.github/workflows/clippy.yml | 4 ++++ src/tools/rustfmt/.github/workflows/integration.yml | 2 ++ src/tools/rustfmt/.github/workflows/rustdoc_check.yml | 2 ++ src/tools/rustfmt/.github/workflows/test.yml | 2 ++ src/tools/rustfmt/.github/workflows/upload-assets.yml | 2 ++ 6 files changed, 14 insertions(+) diff --git a/src/tools/rustfmt/.github/workflows/check_diff.yml b/src/tools/rustfmt/.github/workflows/check_diff.yml index b3543a49c3e9b..dc9d74a79b538 100644 --- a/src/tools/rustfmt/.github/workflows/check_diff.yml +++ b/src/tools/rustfmt/.github/workflows/check_diff.yml @@ -51,6 +51,8 @@ jobs: steps: - name: checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Build check_diff binary working-directory: ./check_diff diff --git a/src/tools/rustfmt/.github/workflows/clippy.yml b/src/tools/rustfmt/.github/workflows/clippy.yml index a5aa91346dd84..a6ce1d0b9ceb5 100644 --- a/src/tools/rustfmt/.github/workflows/clippy.yml +++ b/src/tools/rustfmt/.github/workflows/clippy.yml @@ -13,6 +13,8 @@ jobs: steps: - name: checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: install rustup run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh @@ -35,6 +37,8 @@ jobs: steps: - name: checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: install rustup run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh diff --git a/src/tools/rustfmt/.github/workflows/integration.yml b/src/tools/rustfmt/.github/workflows/integration.yml index 224605740e55e..ea797e36f6896 100644 --- a/src/tools/rustfmt/.github/workflows/integration.yml +++ b/src/tools/rustfmt/.github/workflows/integration.yml @@ -60,6 +60,8 @@ jobs: steps: - name: checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false # Run build - name: install rustup diff --git a/src/tools/rustfmt/.github/workflows/rustdoc_check.yml b/src/tools/rustfmt/.github/workflows/rustdoc_check.yml index b3978005fdf62..e2b5e5a68ed71 100644 --- a/src/tools/rustfmt/.github/workflows/rustdoc_check.yml +++ b/src/tools/rustfmt/.github/workflows/rustdoc_check.yml @@ -13,6 +13,8 @@ jobs: steps: - name: checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: install rustup run: | diff --git a/src/tools/rustfmt/.github/workflows/test.yml b/src/tools/rustfmt/.github/workflows/test.yml index 6d0f25834e596..73b87755e7659 100644 --- a/src/tools/rustfmt/.github/workflows/test.yml +++ b/src/tools/rustfmt/.github/workflows/test.yml @@ -43,6 +43,8 @@ jobs: run: git config --global core.autocrlf false - name: checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Install Rustup using win.rustup.rs if: ${{ matrix.os == 'windows-latest' }} diff --git a/src/tools/rustfmt/.github/workflows/upload-assets.yml b/src/tools/rustfmt/.github/workflows/upload-assets.yml index 991cc965e3d51..98013641005df 100644 --- a/src/tools/rustfmt/.github/workflows/upload-assets.yml +++ b/src/tools/rustfmt/.github/workflows/upload-assets.yml @@ -32,6 +32,8 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false # Run build - name: install rustup From a754ca9c86cedade0bdfdd98bc3b0973281faee1 Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:09:32 +0000 Subject: [PATCH 20/53] ci: Expand `github.event.inputs.commit_hash` in `env` Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/check_diff.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tools/rustfmt/.github/workflows/check_diff.yml b/src/tools/rustfmt/.github/workflows/check_diff.yml index dc9d74a79b538..5d3c943f09b95 100644 --- a/src/tools/rustfmt/.github/workflows/check_diff.yml +++ b/src/tools/rustfmt/.github/workflows/check_diff.yml @@ -62,12 +62,13 @@ jobs: working-directory: ./check_diff env: CHECK_DIFF_LOG: info + GITHUB_EVENT_INPUTS_COMMIT_HASH: ${{ github.event.inputs.commit_hash }} shell: bash run: | OPTIONS="" - if [[ -n "${{ github.event.inputs.commit_hash }}" ]]; then - OPTIONS+="--commit-hash ${{ github.event.inputs.commit_hash }} " + if [[ -n "${GITHUB_EVENT_INPUTS_COMMIT_HASH}" ]]; then + OPTIONS+="--commit-hash ${GITHUB_EVENT_INPUTS_COMMIT_HASH} " fi if [[ -n "${{ github.event.inputs.rustfmt_configs }}" ]]; then From 2c9df224467ed8f8f45345c158b04bd30719bfde Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:10:35 +0000 Subject: [PATCH 21/53] ci: Expand `github.event.inputs.rustfmt_configs` in `env` Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/check_diff.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tools/rustfmt/.github/workflows/check_diff.yml b/src/tools/rustfmt/.github/workflows/check_diff.yml index 5d3c943f09b95..31baf901072cd 100644 --- a/src/tools/rustfmt/.github/workflows/check_diff.yml +++ b/src/tools/rustfmt/.github/workflows/check_diff.yml @@ -63,6 +63,7 @@ jobs: env: CHECK_DIFF_LOG: info GITHUB_EVENT_INPUTS_COMMIT_HASH: ${{ github.event.inputs.commit_hash }} + GITHUB_EVENT_INPUTS_RUSTFMT_CONFIGS: ${{ github.event.inputs.rustfmt_configs }} shell: bash run: | OPTIONS="" @@ -71,8 +72,8 @@ jobs: OPTIONS+="--commit-hash ${GITHUB_EVENT_INPUTS_COMMIT_HASH} " fi - if [[ -n "${{ github.event.inputs.rustfmt_configs }}" ]]; then - OPTIONS+="--rustfmt-config ${{ github.event.inputs.rustfmt_configs }} " + if [[ -n "${GITHUB_EVENT_INPUTS_RUSTFMT_CONFIGS}" ]]; then + OPTIONS+="--rustfmt-config ${GITHUB_EVENT_INPUTS_RUSTFMT_CONFIGS} " fi target/release/check_diff ${{ github.event.inputs.clone_url }} ${{ github.event.inputs.branch_name }} \ From ad47c3e1c7bd2f40e239a146089ea094cb576802 Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:12:41 +0000 Subject: [PATCH 22/53] ci: Expand `github.event.inputs.clone_url` in `env` Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/check_diff.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rustfmt/.github/workflows/check_diff.yml b/src/tools/rustfmt/.github/workflows/check_diff.yml index 31baf901072cd..f554ce3467167 100644 --- a/src/tools/rustfmt/.github/workflows/check_diff.yml +++ b/src/tools/rustfmt/.github/workflows/check_diff.yml @@ -64,6 +64,7 @@ jobs: CHECK_DIFF_LOG: info GITHUB_EVENT_INPUTS_COMMIT_HASH: ${{ github.event.inputs.commit_hash }} GITHUB_EVENT_INPUTS_RUSTFMT_CONFIGS: ${{ github.event.inputs.rustfmt_configs }} + GITHUB_EVENT_INPUTS_CLONE_URL: ${{ github.event.inputs.clone_url }} shell: bash run: | OPTIONS="" @@ -76,7 +77,7 @@ jobs: OPTIONS+="--rustfmt-config ${GITHUB_EVENT_INPUTS_RUSTFMT_CONFIGS} " fi - target/release/check_diff ${{ github.event.inputs.clone_url }} ${{ github.event.inputs.branch_name }} \ + target/release/check_diff ${GITHUB_EVENT_INPUTS_CLONE_URL} ${{ github.event.inputs.branch_name }} \ --edition ${{ github.event.inputs.language_edition }} \ --style-edition ${{ github.event.inputs.style_edition }} \ $OPTIONS From e2e31602324caa8e2ba0ba3a1645b64f71ba6005 Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:14:21 +0000 Subject: [PATCH 23/53] ci: Expand `github.event.inputs.branch_name` in `env` Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/check_diff.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rustfmt/.github/workflows/check_diff.yml b/src/tools/rustfmt/.github/workflows/check_diff.yml index f554ce3467167..73bfd77dfe8ee 100644 --- a/src/tools/rustfmt/.github/workflows/check_diff.yml +++ b/src/tools/rustfmt/.github/workflows/check_diff.yml @@ -65,6 +65,7 @@ jobs: GITHUB_EVENT_INPUTS_COMMIT_HASH: ${{ github.event.inputs.commit_hash }} GITHUB_EVENT_INPUTS_RUSTFMT_CONFIGS: ${{ github.event.inputs.rustfmt_configs }} GITHUB_EVENT_INPUTS_CLONE_URL: ${{ github.event.inputs.clone_url }} + GITHUB_EVENT_INPUTS_BRANCH_NAME: ${{ github.event.inputs.branch_name }} shell: bash run: | OPTIONS="" @@ -77,7 +78,7 @@ jobs: OPTIONS+="--rustfmt-config ${GITHUB_EVENT_INPUTS_RUSTFMT_CONFIGS} " fi - target/release/check_diff ${GITHUB_EVENT_INPUTS_CLONE_URL} ${{ github.event.inputs.branch_name }} \ + target/release/check_diff ${GITHUB_EVENT_INPUTS_CLONE_URL} ${GITHUB_EVENT_INPUTS_BRANCH_NAME} \ --edition ${{ github.event.inputs.language_edition }} \ --style-edition ${{ github.event.inputs.style_edition }} \ $OPTIONS From ce40bba6fe534bc41968155a21b9aedf22c41999 Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:15:08 +0000 Subject: [PATCH 24/53] ci: Expand `github.event.inputs.language_edition` in `env` Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/check_diff.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rustfmt/.github/workflows/check_diff.yml b/src/tools/rustfmt/.github/workflows/check_diff.yml index 73bfd77dfe8ee..1a5e95c755ac9 100644 --- a/src/tools/rustfmt/.github/workflows/check_diff.yml +++ b/src/tools/rustfmt/.github/workflows/check_diff.yml @@ -66,6 +66,7 @@ jobs: GITHUB_EVENT_INPUTS_RUSTFMT_CONFIGS: ${{ github.event.inputs.rustfmt_configs }} GITHUB_EVENT_INPUTS_CLONE_URL: ${{ github.event.inputs.clone_url }} GITHUB_EVENT_INPUTS_BRANCH_NAME: ${{ github.event.inputs.branch_name }} + GITHUB_EVENT_INPUTS_LANGUAGE_EDITION: ${{ github.event.inputs.language_edition }} shell: bash run: | OPTIONS="" @@ -79,6 +80,6 @@ jobs: fi target/release/check_diff ${GITHUB_EVENT_INPUTS_CLONE_URL} ${GITHUB_EVENT_INPUTS_BRANCH_NAME} \ - --edition ${{ github.event.inputs.language_edition }} \ + --edition ${GITHUB_EVENT_INPUTS_LANGUAGE_EDITION} \ --style-edition ${{ github.event.inputs.style_edition }} \ $OPTIONS From 52d6915ea886769fe6ba0f23fc27885f0c818753 Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:15:56 +0000 Subject: [PATCH 25/53] ci: Expand `github.event.inputs.style_edition` in `env` Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/check_diff.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rustfmt/.github/workflows/check_diff.yml b/src/tools/rustfmt/.github/workflows/check_diff.yml index 1a5e95c755ac9..7f7bc62846a3c 100644 --- a/src/tools/rustfmt/.github/workflows/check_diff.yml +++ b/src/tools/rustfmt/.github/workflows/check_diff.yml @@ -67,6 +67,7 @@ jobs: GITHUB_EVENT_INPUTS_CLONE_URL: ${{ github.event.inputs.clone_url }} GITHUB_EVENT_INPUTS_BRANCH_NAME: ${{ github.event.inputs.branch_name }} GITHUB_EVENT_INPUTS_LANGUAGE_EDITION: ${{ github.event.inputs.language_edition }} + GITHUB_EVENT_INPUTS_STYLE_EDITION: ${{ github.event.inputs.style_edition }} shell: bash run: | OPTIONS="" @@ -81,5 +82,5 @@ jobs: target/release/check_diff ${GITHUB_EVENT_INPUTS_CLONE_URL} ${GITHUB_EVENT_INPUTS_BRANCH_NAME} \ --edition ${GITHUB_EVENT_INPUTS_LANGUAGE_EDITION} \ - --style-edition ${{ github.event.inputs.style_edition }} \ + --style-edition ${GITHUB_EVENT_INPUTS_STYLE_EDITION} \ $OPTIONS From 9cda3d38415573fe456677d1ff6fdcaaa086db79 Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:19:37 +0000 Subject: [PATCH 26/53] ci: Expand `github.event.release.tag_name` in `env` Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/upload-assets.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/tools/rustfmt/.github/workflows/upload-assets.yml b/src/tools/rustfmt/.github/workflows/upload-assets.yml index 98013641005df..7f655437a6f4f 100644 --- a/src/tools/rustfmt/.github/workflows/upload-assets.yml +++ b/src/tools/rustfmt/.github/workflows/upload-assets.yml @@ -53,7 +53,7 @@ jobs: - name: Build archive shell: bash run: | - staging="rustfmt_${{ matrix.build }}_${{ github.event.release.tag_name }}" + staging="rustfmt_${{ matrix.build }}_${GITHUB_EVENT_RELEASE_TAG_NAME}" mkdir -p "$staging" cp {README.md,Configurations.md,CHANGELOG.md,LICENSE-MIT,LICENSE-APACHE} "$staging/" @@ -67,9 +67,13 @@ jobs: tar czf "$staging.tar.gz" "$staging" echo "ASSET=$staging.tar.gz" >> $GITHUB_ENV fi + env: + GITHUB_EVENT_RELEASE_TAG_NAME: ${{ github.event.release.tag_name }} - name: Upload Release Asset + shell: bash if: github.event_name == 'release' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: gh release upload "${{ github.event.release.tag_name }}" "${{ env.ASSET }}" + GITHUB_EVENT_RELEASE_TAG_NAME: ${{ github.event.release.tag_name }} + run: gh release upload "${GITHUB_EVENT_RELEASE_TAG_NAME}" "${{ env.ASSET }}" From a16fdd3094ecb8ca4b5497cad34f02a681fbfed6 Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:41:06 +0000 Subject: [PATCH 27/53] ci: Prefer shell expansion over template expansion Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/upload-assets.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rustfmt/.github/workflows/upload-assets.yml b/src/tools/rustfmt/.github/workflows/upload-assets.yml index 7f655437a6f4f..e4deb20c49b68 100644 --- a/src/tools/rustfmt/.github/workflows/upload-assets.yml +++ b/src/tools/rustfmt/.github/workflows/upload-assets.yml @@ -76,4 +76,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_EVENT_RELEASE_TAG_NAME: ${{ github.event.release.tag_name }} - run: gh release upload "${GITHUB_EVENT_RELEASE_TAG_NAME}" "${{ env.ASSET }}" + run: gh release upload "${GITHUB_EVENT_RELEASE_TAG_NAME}" "${ASSET}" From e6c6acbafc81f7aa3bd1c4a065c11877bcd730e3 Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:48:11 +0000 Subject: [PATCH 28/53] ci: Expand `matrix.target` in `env` Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/test.yml | 8 ++++++-- src/tools/rustfmt/.github/workflows/upload-assets.yml | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/tools/rustfmt/.github/workflows/test.yml b/src/tools/rustfmt/.github/workflows/test.yml index 73b87755e7659..541c084578308 100644 --- a/src/tools/rustfmt/.github/workflows/test.yml +++ b/src/tools/rustfmt/.github/workflows/test.yml @@ -54,7 +54,9 @@ jobs: Invoke-WebRequest https://win.rustup.rs/ -OutFile rustup-init.exe .\rustup-init.exe -y --default-host=x86_64-pc-windows-msvc --default-toolchain=none del rustup-init.exe - rustup target add ${{ matrix.target }} + rustup target add ${env:MATRIX_TARGET} + env: + MATRIX_TARGET: ${{ matrix.target }} shell: powershell - name: Add mingw32 to path for i686-gnu @@ -73,7 +75,9 @@ jobs: run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh sh rustup-init.sh -y --default-toolchain none - rustup target add ${{ matrix.target }} + rustup target add ${MATRIX_TARGET} + env: + MATRIX_TARGET: ${{ matrix.target }} # Run build - name: Build and Test diff --git a/src/tools/rustfmt/.github/workflows/upload-assets.yml b/src/tools/rustfmt/.github/workflows/upload-assets.yml index e4deb20c49b68..40ccf8b529b8f 100644 --- a/src/tools/rustfmt/.github/workflows/upload-assets.yml +++ b/src/tools/rustfmt/.github/workflows/upload-assets.yml @@ -37,10 +37,13 @@ jobs: # Run build - name: install rustup + shell: bash run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh sh rustup-init.sh -y --default-toolchain none - rustup target add ${{ matrix.target }} + rustup target add ${MATRIX_TARGET} + env: + MATRIX_TARGET: ${{ matrix.target }} - name: Add mingw64 to path for x86_64-gnu run: echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH From c136f963a92af719344e180df2ae175ff48d3069 Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:50:25 +0000 Subject: [PATCH 29/53] ci: Expand `matrix.os` in `env` Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/upload-assets.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rustfmt/.github/workflows/upload-assets.yml b/src/tools/rustfmt/.github/workflows/upload-assets.yml index 40ccf8b529b8f..a99305e7dd4b9 100644 --- a/src/tools/rustfmt/.github/workflows/upload-assets.yml +++ b/src/tools/rustfmt/.github/workflows/upload-assets.yml @@ -61,7 +61,7 @@ jobs: cp {README.md,Configurations.md,CHANGELOG.md,LICENSE-MIT,LICENSE-APACHE} "$staging/" - if [ "${{ matrix.os }}" = "windows-latest" ]; then + if [ "${MATRIX_OS}" = "windows-latest" ]; then cp target/release/{rustfmt.exe,cargo-fmt.exe,rustfmt-format-diff.exe,git-rustfmt.exe} "$staging/" 7z a "$staging.zip" "$staging" echo "ASSET=$staging.zip" >> $GITHUB_ENV @@ -71,6 +71,7 @@ jobs: echo "ASSET=$staging.tar.gz" >> $GITHUB_ENV fi env: + MATRIX_OS: ${{ matrix.os }} GITHUB_EVENT_RELEASE_TAG_NAME: ${{ github.event.release.tag_name }} - name: Upload Release Asset From c47f1f6a834ba08c9e0068e14b6f8e74c282553d Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:52:44 +0000 Subject: [PATCH 30/53] ci: Expand `matrix.build` in `env` Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/upload-assets.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rustfmt/.github/workflows/upload-assets.yml b/src/tools/rustfmt/.github/workflows/upload-assets.yml index a99305e7dd4b9..fd4bdf26d694a 100644 --- a/src/tools/rustfmt/.github/workflows/upload-assets.yml +++ b/src/tools/rustfmt/.github/workflows/upload-assets.yml @@ -56,7 +56,7 @@ jobs: - name: Build archive shell: bash run: | - staging="rustfmt_${{ matrix.build }}_${GITHUB_EVENT_RELEASE_TAG_NAME}" + staging="rustfmt_${MATRIX_BUILD}_${GITHUB_EVENT_RELEASE_TAG_NAME}" mkdir -p "$staging" cp {README.md,Configurations.md,CHANGELOG.md,LICENSE-MIT,LICENSE-APACHE} "$staging/" @@ -72,6 +72,7 @@ jobs: fi env: MATRIX_OS: ${{ matrix.os }} + MATRIX_BUILD: ${{ matrix.build }} GITHUB_EVENT_RELEASE_TAG_NAME: ${{ github.event.release.tag_name }} - name: Upload Release Asset From 905157be81bdaeec182c726d24adb69e01089039 Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:54:32 +0000 Subject: [PATCH 31/53] ci: Expand `matrix.integration` in `env` Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/integration.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rustfmt/.github/workflows/integration.yml b/src/tools/rustfmt/.github/workflows/integration.yml index ea797e36f6896..e8da0d8d3aed1 100644 --- a/src/tools/rustfmt/.github/workflows/integration.yml +++ b/src/tools/rustfmt/.github/workflows/integration.yml @@ -72,5 +72,6 @@ jobs: - name: run integration tests env: TARGET: x86_64-unknown-linux-gnu - run: cargo run --locked --manifest-path ci/Cargo.toml integration ${{ matrix.integration }} + MATRIX_INTEGRATION: ${{ matrix.integration }} + run: cargo run --locked --manifest-path ci/Cargo.toml integration ${MATRIX_INTEGRATION} continue-on-error: ${{ matrix.allow-failure == true }} From 6c0248df6bfb6e64dad9e80cf18cb6243c218e0d Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:57:53 +0000 Subject: [PATCH 32/53] ci: Expand `matrix.package` in `env` Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/clippy.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/rustfmt/.github/workflows/clippy.yml b/src/tools/rustfmt/.github/workflows/clippy.yml index a6ce1d0b9ceb5..fc3eb8916a6bd 100644 --- a/src/tools/rustfmt/.github/workflows/clippy.yml +++ b/src/tools/rustfmt/.github/workflows/clippy.yml @@ -44,5 +44,7 @@ jobs: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh sh rustup-init.sh -y --default-toolchain none - name: clippy - run: cargo clippy --locked --manifest-path ${{ matrix.package }}/Cargo.toml + run: cargo clippy --locked --manifest-path "${MATRIX_PACKAGE}/Cargo.toml" + env: + MATRIX_PACKAGE: ${{ matrix.package }} From febf7c171e81e38a0da96304685372fb20ec8bca Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Fri, 23 Oct 2026 23:59:44 +0000 Subject: [PATCH 33/53] ci: Expand `toJson(needs)` in `env` Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/test.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tools/rustfmt/.github/workflows/test.yml b/src/tools/rustfmt/.github/workflows/test.yml index 541c084578308..44d36898f216f 100644 --- a/src/tools/rustfmt/.github/workflows/test.yml +++ b/src/tools/rustfmt/.github/workflows/test.yml @@ -103,6 +103,8 @@ jobs: - name: Conclusion run: | # Print the dependent jobs to see them in the CI log - jq -C <<< '${{ toJson(needs) }}' + jq -C <<< "${JSON_NEEDS}" # Check if all jobs that we depend on (in the needs array) were successful (or have been skipped). - jq --exit-status 'all(.result == "success" or .result == "skipped")' <<< '${{ toJson(needs) }}' + jq --exit-status 'all(.result == "success" or .result == "skipped")' <<< "${JSON_NEEDS}" + env: + JSON_NEEDS: ${{ toJson(needs) }} From 17a6414e4305cbe568f5807b61ab7627f7d60b28 Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Sat, 24 Oct 2026 00:08:47 +0000 Subject: [PATCH 34/53] ci: Disable all permissions by default Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/upload-assets.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tools/rustfmt/.github/workflows/upload-assets.yml b/src/tools/rustfmt/.github/workflows/upload-assets.yml index fd4bdf26d694a..4a3e4de9a92f9 100644 --- a/src/tools/rustfmt/.github/workflows/upload-assets.yml +++ b/src/tools/rustfmt/.github/workflows/upload-assets.yml @@ -6,6 +6,8 @@ on: types: [created] workflow_dispatch: +permissions: {} + jobs: build-release: name: build-release From 1dde57ffd6b6df58639067a405857d703914745c Mon Sep 17 00:00:00 2001 From: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> Date: Sat, 24 Oct 2026 00:28:13 +0000 Subject: [PATCH 35/53] ci: Grant minimum required permissions Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com> --- src/tools/rustfmt/.github/workflows/upload-assets.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tools/rustfmt/.github/workflows/upload-assets.yml b/src/tools/rustfmt/.github/workflows/upload-assets.yml index 4a3e4de9a92f9..6d83e9b004a85 100644 --- a/src/tools/rustfmt/.github/workflows/upload-assets.yml +++ b/src/tools/rustfmt/.github/workflows/upload-assets.yml @@ -11,6 +11,8 @@ permissions: {} jobs: build-release: name: build-release + permissions: + contents: write # Required by `gh release upload` strategy: matrix: build: [linux-x86_64, macos-x86_64, windows-x86_64-gnu, windows-x86_64-msvc] From f39d517432d9758ef2e8ac18c4da42fd27db84b6 Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Mon, 19 Oct 2026 20:49:24 +0100 Subject: [PATCH 36/53] Integration test: assert on exit codes Asserting on the current state of things. Except for one case in `dont_emit_ICE`: this test reads a file that doesn't panic, but exits 1 because it has a too-long line, so split that line up to ensure we get a 0 exit. --- src/tools/rustfmt/tests/rustfmt/main.rs | 90 ++++++++++++++------ src/tools/rustfmt/tests/target/issue_6069.rs | 3 +- 2 files changed, 64 insertions(+), 29 deletions(-) diff --git a/src/tools/rustfmt/tests/rustfmt/main.rs b/src/tools/rustfmt/tests/rustfmt/main.rs index 71f52abe430c1..886de55fd7f12 100644 --- a/src/tools/rustfmt/tests/rustfmt/main.rs +++ b/src/tools/rustfmt/tests/rustfmt/main.rs @@ -61,32 +61,34 @@ fn rustfmt(args: &[&str]) -> (String, String, i32) { fn print_config_errors_on_unknown_option() { let args = &["--print-config", "unknown"]; - let (stdout, stderr, _) = rustfmt(args); + let (stdout, stderr, exit_code) = rustfmt(args); assert_eq!(&stdout, ""); assert!( stderr.contains("Unknown print-config option"), "expected helpful error message, got: {stderr}" ); + assert_eq!(exit_code, 1); } #[test] fn print_config_default() { let args = &["--print-config", "default"]; - let (stdout, stderr, _) = rustfmt(args); + let (stdout, stderr, exit_code) = rustfmt(args); assert!( stdout.contains("max_width = 100"), "expected stdout to contain a default config, got: {stdout}", ); assert_eq!(&stderr, ""); + assert_eq!(exit_code, 0) } #[test] fn print_config_errors_minimal_with_no_path() { let args = &["--print-config", "minimal"]; - let (stdout, stderr, _) = rustfmt(args); + let (stdout, stderr, exit_code) = rustfmt(args); // NOTE: existing the behaviour: prints an empty line assert_eq!(stdout.trim(), ""); @@ -94,19 +96,21 @@ fn print_config_errors_minimal_with_no_path() { stderr.contains("PATH required"), "expected helpful error message, got: {stderr}" ); + assert_eq!(exit_code, 0) } #[test] fn print_config_errors_minimal_without_input_file() { let args = &["--print-config", "minimal", "minimal.toml"]; - let (stdout, stderr, _) = rustfmt(args); + let (stdout, stderr, exit_code) = rustfmt(args); assert_eq!(&stdout, ""); assert!( stderr.contains("doesn't work with standard input"), "expected helpful error message, got: {stderr}" ); + assert_eq!(exit_code, 1); } // test writes to file within the repo, rust-lang/rust doesn't like that @@ -125,7 +129,7 @@ fn print_config_writes_to_file() { "src/shape.rs", ]; - let (stdout, stderr, _) = rustfmt(args); + let (stdout, stderr, exit_code) = rustfmt(args); assert_eq!(stderr, ""); assert!( @@ -133,6 +137,7 @@ fn print_config_writes_to_file() { "stdout:\n{stdout}\nstderr:\n{stderr}" ); remove_file(out_file).unwrap(); + assert_eq!(exit_code, 0); } #[test] @@ -144,13 +149,14 @@ fn print_config_merges_override_from_args() { "--config=color=Never,edition=2018", ]; - let (stdout, stderr, _) = rustfmt(args); + let (stdout, stderr, exit_code) = rustfmt(args); assert_eq!(stderr, ""); assert!( stdout.contains(r#"color = "Never""#) && stdout.contains(r#"edition = "2018""#), "expected overrides in stdout, got: {stdout}", ); + assert_eq!(exit_code, 0); } #[test] @@ -165,7 +171,7 @@ fn print_config_merges_multiple_overrides_from_args() { "color=always,format_strings=true", ]; - let (stdout, stderr, _) = rustfmt(args); + let (stdout, stderr, exit_code) = rustfmt(args); assert_eq!(stderr, ""); assert!( @@ -174,13 +180,14 @@ fn print_config_merges_multiple_overrides_from_args() { && stdout.contains("format_strings = true"), "expected overrides in stdout, got: {stdout}", ); + assert_eq!(exit_code, 0); } #[stable_only_test] #[test] fn rustfmt_usage_text() { let args = ["--help"]; - let (stdout, _, _) = rustfmt(&args); + let (stdout, _, exit_code) = rustfmt(&args); insta::assert_snapshot!(stdout, @" Format Rust code @@ -222,13 +229,14 @@ fn rustfmt_usage_text() { -h, --help [=TOPIC] Show this message or help about a specific topic: `config` "); + assert_eq!(exit_code, 0); } #[nightly_only_test] #[test] fn rustfmt_nightly_usage_text() { let args = ["--help"]; - let (stdout, _, _) = rustfmt(&args); + let (stdout, _, exit_code) = rustfmt(&args); insta::assert_snapshot!(stdout, @" Format Rust code @@ -282,6 +290,7 @@ fn rustfmt_nightly_usage_text() { -h, --help [=TOPIC] Show this message or help about a specific topic: `config` or `file-lines` "); + assert_eq!(exit_code, 0); } #[test] @@ -296,36 +305,44 @@ fn mod_resolution_error_multiple_candidate_files() { ); let args = ["tests/mod-resolver/issue-5167/src/lib.rs"]; - let (_stdout, stderr, _) = rustfmt(&args); - assert!(stderr.contains(&error_message)) + let (_stdout, stderr, exit_code) = rustfmt(&args); + + assert!(stderr.contains(&error_message)); + assert_eq!(exit_code, 1); } #[test] fn mod_resolution_error_sibling_module_not_found() { let args = ["tests/mod-resolver/module-not-found/sibling_module/lib.rs"]; - let (_stdout, stderr, _) = rustfmt(&args); + let (_stdout, stderr, exit_code) = rustfmt(&args); + // Module resolution fails because we're unable to find `a.rs` in the same directory as lib.rs - assert!(stderr.contains("a.rs does not exist")) + assert!(stderr.contains("a.rs does not exist")); + assert_eq!(exit_code, 1); } #[test] fn mod_resolution_error_relative_module_not_found() { let args = ["tests/mod-resolver/module-not-found/relative_module/lib.rs"]; - let (_stdout, stderr, _) = rustfmt(&args); + let (_stdout, stderr, exit_code) = rustfmt(&args); + // The file `./a.rs` and directory `./a` both exist. // Module resolution fails because we're unable to find `./a/b.rs` #[cfg(not(windows))] assert!(stderr.contains("a/b.rs does not exist")); #[cfg(windows)] assert!(stderr.contains("a\\b.rs does not exist")); + assert_eq!(exit_code, 1); } #[test] fn mod_resolution_error_path_attribute_does_not_exist() { let args = ["tests/mod-resolver/module-not-found/bad_path_attribute/lib.rs"]; - let (_stdout, stderr, _) = rustfmt(&args); + let (_stdout, stderr, exit_code) = rustfmt(&args); + // The path attribute points to a file that does not exist assert!(stderr.contains("does_not_exist.rs does not exist")); + assert_eq!(exit_code, 1); } #[test] @@ -337,10 +354,11 @@ fn rustfmt_emits_error_on_line_overflow_true() { "tests/cargo-fmt/source/issue_3164/src/main.rs", ]; - let (_stdout, stderr, _) = rustfmt(&args); + let (_stdout, stderr, exit_code) = rustfmt(&args); assert!(stderr.contains( "line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option)" - )) + )); + assert_eq!(exit_code, 1); } #[test] @@ -358,8 +376,11 @@ fn dont_emit_ICE() { let panic_re = regex::Regex::new("thread.*panicked").unwrap(); for file in files { let args = [file]; - let (_stdout, stderr, _) = rustfmt(&args); + + let (_stdout, stderr, exit_code) = rustfmt(&args); + assert!(!panic_re.is_match(&stderr)); + assert_eq!(exit_code, 0); } } @@ -375,8 +396,10 @@ fn rustfmt_emits_error_when_control_brace_style_is_always_next_line() { "tests/target/issue_5912.rs", ]; - let (_stdout, stderr, _) = rustfmt(&args); - assert!(!stderr.contains("error[internal]: left behind trailing whitespace")) + let (_stdout, stderr, exit_code) = rustfmt(&args); + + assert!(!stderr.contains("error[internal]: left behind trailing whitespace")); + assert_eq!(exit_code, 0); } #[nightly_only_test] @@ -386,9 +409,11 @@ fn rustfmt_generates_no_error_if_failed_format_code_in_doc_comments() { let file = "tests/target/issue-6109.rs"; let args = ["--config", "format_code_in_doc_comments=true", file]; - let (stdout, stderr, _) = rustfmt(&args); + let (stdout, stderr, exit_code) = rustfmt(&args); + assert!(stderr.is_empty()); assert!(stdout.is_empty()); + assert_eq!(exit_code, 0); } #[test] @@ -396,12 +421,13 @@ fn rustfmt_error_improvement_regarding_invalid_toml() { // See also https://github.com/rust-lang/rustfmt/issues/6302 let invalid_toml_config = "tests/config/issue-6302.toml"; let args = ["--config-path", invalid_toml_config]; - let (_stdout, stderr, _) = rustfmt(&args); + let (_stdout, stderr, exit_code) = rustfmt(&args); let toml_path = Path::new(invalid_toml_config).canonicalize().unwrap(); let expected_error_message = format!("The file `{}` failed to parse", toml_path.display()); assert!(stderr.contains(&expected_error_message)); + assert_eq!(exit_code, 1); } #[test] @@ -416,7 +442,7 @@ fn config_path_walks_parent_directories_with_dir_name() { &src_file.to_str().unwrap(), ]; - let (stdout, stderr, _) = rustfmt(&args); + let (stdout, stderr, exit_code) = rustfmt(&args); assert_eq!(stderr, ""); assert_eq!( @@ -425,6 +451,7 @@ fn config_path_walks_parent_directories_with_dir_name() { // this config sets `use_field_init_shorthand = true` "we should walk up to tests/config/issue_4660/rustfmt.toml" ); + assert_eq!(exit_code, 1); } // on unix [`std::env::home_dir`] reads the `HOME` env var. @@ -449,7 +476,7 @@ fn config_path_searches_home_dir_when_dir_path_no_resolve() { ]; let env = &[("HOME", homedir_path.to_str().unwrap())]; - let (stdout, stderr, _) = rustfmt_with_extra(&args, None, env, None); + let (stdout, stderr, exit_code) = rustfmt_with_extra(&args, None, env, None); assert_eq!(stderr, ""); assert_eq!( @@ -457,6 +484,7 @@ fn config_path_searches_home_dir_when_dir_path_no_resolve() { format!("{}\n", new_src_path.canonicalize().unwrap().display()), "should search in HOME" ); + assert_eq!(exit_code, 1); } // The behaviour [`dirs::config_dir`] is os-dependent, so pick one and test to that. @@ -482,7 +510,7 @@ fn config_path_searches_config_dir_when_dir_path_no_resolve() { ]; let env = &[("XDG_CONFIG_HOME", xdg_config_dir_path.to_str().unwrap())]; - let (stdout, stderr, _) = rustfmt_with_extra(&args, None, env, None); + let (stdout, stderr, exit_code) = rustfmt_with_extra(&args, None, env, None); assert_eq!(stderr, ""); assert_eq!( @@ -490,6 +518,7 @@ fn config_path_searches_config_dir_when_dir_path_no_resolve() { format!("{}\n", new_src_path.canonicalize().unwrap().display()), "should search in XDG_CONFIG_HOME" ); + assert_eq!(exit_code, 1); } #[test] @@ -498,7 +527,7 @@ fn config_path_errors_when_invalid_file_given() { let config_file = "also_not_a_file.toml"; let args = ["--config-path", &config_file, "--check", &src_file]; - let (stdout, stderr, _) = rustfmt(&args); + let (stdout, stderr, exit_code) = rustfmt(&args); assert_eq!( stderr, @@ -508,6 +537,7 @@ fn config_path_errors_when_invalid_file_given() { ) ); assert_eq!(stdout, ""); + assert_eq!(exit_code, 1); } #[test] @@ -534,11 +564,12 @@ fn rustfmt_allow_not_a_dir_errors() { let args = [empty_rs.to_str().unwrap()]; let envs = &[("HOME", fake_home_str)]; - let (stdout, stderr, _) = rustfmt_with_extra(&args, Some(fake_home_str), envs, None); + let (stdout, stderr, exit_code) = rustfmt_with_extra(&args, Some(fake_home_str), envs, None); // Should pass without any errors assert_eq!(stdout, ""); assert_eq!(stderr, ""); + assert_eq!(exit_code, 0); } // FIXME(cfg_select): ungate when `cfg_select!` formatting can be enabled on stable. @@ -548,8 +579,11 @@ fn cfg_select_parser_recovery_ice_7087() { // See also https://github.com/rust-lang/rustfmt/issues/7087 let source = "cfg_select! { true => { fn foo {} } }\n"; let panic_re = regex::Regex::new("thread.*panicked").unwrap(); - let (stdout, stderr, _) = rustfmt_with_extra(&[], None, &[], Some(source)); + + let (stdout, stderr, exit_code) = rustfmt_with_extra(&[], None, &[], Some(source)); + assert!(!panic_re.is_match(&stderr)); // Invalid arm should cause `cfg_select!` to be re-emitted unmodified. assert_eq!(source, stdout); + assert_eq!(exit_code, 0); } diff --git a/src/tools/rustfmt/tests/target/issue_6069.rs b/src/tools/rustfmt/tests/target/issue_6069.rs index d866ce7c646e3..febd840880a36 100644 --- a/src/tools/rustfmt/tests/target/issue_6069.rs +++ b/src/tools/rustfmt/tests/target/issue_6069.rs @@ -1,3 +1,4 @@ // `Foó` as written here ends with ASCII 6F `'o'` followed by `'\u{0301}'` COMBINING ACUTE ACCENT. -// The compiler normalizes that combination to NFC form, `'\u{00F3}'` LATIN SMALL LETTER O WITH ACUTE. +// The compiler normalizes that combination to NFC form, `'\u{00F3}'` +// LATIN SMALL LETTER O WITH ACUTE. trait Foó: Bar {} From 302895a9fccea10ffe68c5832b23b41afcc1d124 Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Mon, 19 Oct 2026 21:11:46 +0100 Subject: [PATCH 37/53] Error on `--print-config minimal` with no output path Without an output path this option makes no sense, this also reflects what happens when you provide an output option but no input file. Also tidy up some of the code for this option handling. How it behaved previously: $ cargo run --quiet --bin rustfmt -- --print-config minimal for OperationError { @@ -490,23 +499,24 @@ fn determine_operation(matches: &Matches) -> Result { } let mut free_matches = matches.free.iter(); - let mut minimal_config_path = None; - if let Some(kind) = matches.opt_str("print-config") { - let path = free_matches.next().cloned(); - match kind.as_str() { - "default" => return Ok(Operation::ConfigOutputDefault { path }), - "current" => return Ok(Operation::ConfigOutputCurrent { path }), - "minimal" => { - minimal_config_path = path; - if minimal_config_path.is_none() { - eprintln!("WARNING: PATH required for `--print-config minimal`."); + let minimal_config_path = match matches.opt_str("print-config") { + None => None, + Some(kind) => { + let path = free_matches.next().cloned(); + match kind.as_str() { + "default" => return Ok(Operation::ConfigOutputDefault { path }), + "current" => return Ok(Operation::ConfigOutputCurrent { path }), + "minimal" => Some(path.ok_or(OperationError::PrintConfig( + PrintConfigError::MinimalPathNoPath, + ))?), + _ => { + return Err(OperationError::PrintConfig( + PrintConfigError::UnknownOption(kind), + )); } } - _ => { - return Err(OperationError::UnknownPrintConfigTopic(kind)); - } } - } + }; if matches.opt_present("version") { return Ok(Operation::Version); @@ -524,7 +534,9 @@ fn determine_operation(matches: &Matches) -> Result { // if no file argument is supplied, read from stdin if files.is_empty() { if minimal_config_path.is_some() { - return Err(OperationError::MinimalPathWithStdin); + return Err(OperationError::PrintConfig( + PrintConfigError::MinimalPathWithStdin, + )); } let mut buffer = String::new(); io::stdin().read_to_string(&mut buffer)?; diff --git a/src/tools/rustfmt/tests/rustfmt/main.rs b/src/tools/rustfmt/tests/rustfmt/main.rs index 886de55fd7f12..1656ea84db837 100644 --- a/src/tools/rustfmt/tests/rustfmt/main.rs +++ b/src/tools/rustfmt/tests/rustfmt/main.rs @@ -90,13 +90,12 @@ fn print_config_errors_minimal_with_no_path() { let (stdout, stderr, exit_code) = rustfmt(args); - // NOTE: existing the behaviour: prints an empty line - assert_eq!(stdout.trim(), ""); - assert!( - stderr.contains("PATH required"), + assert_eq!(stdout, ""); + assert_eq!( + stderr, "The `--print-config=minimal` option requires an output path to write to\n", "expected helpful error message, got: {stderr}" ); - assert_eq!(exit_code, 0) + assert_eq!(exit_code, 1) } #[test] @@ -106,8 +105,8 @@ fn print_config_errors_minimal_without_input_file() { let (stdout, stderr, exit_code) = rustfmt(args); assert_eq!(&stdout, ""); - assert!( - stderr.contains("doesn't work with standard input"), + assert_eq!( + stderr, "The `--print-config=minimal` option doesn't work with standard input.\n", "expected helpful error message, got: {stderr}" ); assert_eq!(exit_code, 1); From a7e179a2cde9698e01c06fd7ff3c479f1ca8017c Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Tue, 20 Oct 2026 18:19:43 +0100 Subject: [PATCH 38/53] Remove test that writes within the repo Because this is incompatible with how `rust-lang/rust` runs tests (see 8fdd3c21ed3df5edd211fd137b27c184d1c30ac6). But, to be safe, the `rustfmt_only_ci_test` marker is left in place, it can be removed upstream once we're sure this test really is safe to run there. --- src/tools/rustfmt/tests/rustfmt/main.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/tools/rustfmt/tests/rustfmt/main.rs b/src/tools/rustfmt/tests/rustfmt/main.rs index 1656ea84db837..04ef79db920e3 100644 --- a/src/tools/rustfmt/tests/rustfmt/main.rs +++ b/src/tools/rustfmt/tests/rustfmt/main.rs @@ -1,12 +1,12 @@ //! Integration tests for rustfmt. use std::env; -use std::fs::{File, remove_file}; +use std::fs::File; use std::io::Write; use std::path::Path; use std::process::{Command, Stdio}; -use rustfmt_config_proc_macro::{nightly_only_test, rustfmt_only_ci_test, stable_only_test}; +use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test, rustfmt_only_ci_test}; /// Run the rustfmt executable with environment vars set and return its output. /// If `input` is given, it is piped to the executable's stdin. @@ -112,11 +112,11 @@ fn print_config_errors_minimal_without_input_file() { assert_eq!(exit_code, 1); } -// test writes to file within the repo, rust-lang/rust doesn't like that #[rustfmt_only_ci_test] #[test] fn print_config_writes_to_file() { - let out_file = "minimal.toml"; + let out_file = tempfile::tempdir().unwrap().keep().join("minimal.toml"); + let args = &[ "--config-path", Path::new("tests/config/disable_all_formatting.toml") @@ -124,18 +124,14 @@ fn print_config_writes_to_file() { .unwrap(), "--print-config", "minimal", - out_file, + out_file.to_str().unwrap(), "src/shape.rs", ]; let (stdout, stderr, exit_code) = rustfmt(args); assert_eq!(stderr, ""); - assert!( - Path::new(out_file).exists(), - "stdout:\n{stdout}\nstderr:\n{stderr}" - ); - remove_file(out_file).unwrap(); + assert!(out_file.exists(), "stdout:\n{stdout}\nstderr:\n{stderr}"); assert_eq!(exit_code, 0); } From b7e097e39fee54af413a70c44e81e7d8ed53a9f0 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 2 Nov 2026 09:56:36 +0800 Subject: [PATCH 39/53] Revert "Use saturating arithmetics for `adjust_max_width`" Reverts rust-lang/rustfmt#7152. This reverts commit 677b954590cc0d5e86cb97e7db4e4b5fb6290add, reversing changes made to b4260f44f5d8cd538718b9046eb51baaa1eb8f5e. --- src/tools/rustfmt/src/config/config_type.rs | 10 ++++------ src/tools/rustfmt/src/config/mod.rs | 17 ----------------- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/src/tools/rustfmt/src/config/config_type.rs b/src/tools/rustfmt/src/config/config_type.rs index ee1ccdb437801..f1648df4e3c70 100644 --- a/src/tools/rustfmt/src/config/config_type.rs +++ b/src/tools/rustfmt/src/config/config_type.rs @@ -447,14 +447,14 @@ macro_rules! create_config { /// arms within a declarative macro. #[allow(unreachable_pub)] pub fn reduce_max_width(&mut self, delta: usize) { - self.adjust_max_width(-(delta.min(isize::MAX as usize) as isize)); + self.adjust_max_width(-(delta as isize)); } /// Increases the maximum width of the configuration. /// /// This method is intended to be used when creating a forked /// configuration for a particular formatting context in which the - /// total available width needs to be increased. This increases the + /// total available width needs to be reduced. This increases the /// size of max_width, and all other properties affected by small /// heuristics, without treating those adjustments as overrides. /// @@ -462,7 +462,7 @@ macro_rules! create_config { /// arms within a declarative macro. #[allow(unreachable_pub)] pub fn increase_max_width(&mut self, delta: usize) { - self.adjust_max_width(delta.min(isize::MAX as usize) as isize); + self.adjust_max_width(delta as isize); } /// Adjusts the maximum width of the configuration. @@ -476,9 +476,7 @@ macro_rules! create_config { /// As an example use case, this method is used when formatting /// arms within a declarative macro. fn adjust_max_width(&mut self, delta: isize) { - // NB: we need to either saturate or report an error (or some kind of testable - // warning). I picked to saturate here. - let adjust = |value: usize| value.saturating_add_signed(delta); + let adjust = |value: usize| (value as isize + delta) as usize; self.array_width.2 = adjust(self.array_width.2); self.attr_fn_like_width.2 = adjust(self.attr_fn_like_width.2); diff --git a/src/tools/rustfmt/src/config/mod.rs b/src/tools/rustfmt/src/config/mod.rs index 7ecf2ec3b58cf..0180bb89841c0 100644 --- a/src/tools/rustfmt/src/config/mod.rs +++ b/src/tools/rustfmt/src/config/mod.rs @@ -689,23 +689,6 @@ mod test { assert_eq!(config.verbose(), Verbosity::Normal); } - #[test] - fn test_adjust_max_width() { - // Regression tests for #7147, where we didn't handle overflows / integral cast outside of - // range properly. - - // Reduce with huge amount saturates to zero instead of wraparound. - let mut config = Config::default(); - config.reduce_max_width(usize::MAX); - assert_eq!(config.max_width(), 0); - - // Increase with huge amount saturates instead of wraparound. - let mut config = Config::default(); - config.set().max_width(usize::MAX - 1); - config.increase_max_width(2); - assert_eq!(config.max_width(), usize::MAX); - } - #[test] fn test_config_used_to_toml() { let config = Config::default(); From bfd66ed8eaa210054ac0d1c81c40d0b5f15c3701 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 2 Nov 2026 10:05:20 +0800 Subject: [PATCH 40/53] Revert "Fixes adjustment of max_width within macros" Reverts rust-lang/rustfmt#6651 since it affects stable macro formatting, even though `format_macro_bodies` is unstable option (unstability applies only to the config, the "format macro bodies" value is stable default formatting). We generally cannot make this change on SE 2024. This reverts commit 93e78f2d3a0901718530aa871565257a41869983. --- src/tools/rustfmt/src/config/config_type.rs | 57 ------------------- src/tools/rustfmt/src/macros.rs | 6 +- .../tests/target/issue-6651/custom_width.rs | 7 +-- 3 files changed, 5 insertions(+), 65 deletions(-) diff --git a/src/tools/rustfmt/src/config/config_type.rs b/src/tools/rustfmt/src/config/config_type.rs index f1648df4e3c70..69749feb0f357 100644 --- a/src/tools/rustfmt/src/config/config_type.rs +++ b/src/tools/rustfmt/src/config/config_type.rs @@ -435,63 +435,6 @@ macro_rules! create_config { )+ } - /// Reduces the maximum width of the configuration. - /// - /// This method is intended to be used when creating a forked - /// configuration for a particular formatting context in which the - /// total available width needs to be reduced. This reduces the - /// size of max_width, and all other properties affected by small - /// heuristics, without treating those adjustments as overrides. - /// - /// As an example use case, this method is used when formatting - /// arms within a declarative macro. - #[allow(unreachable_pub)] - pub fn reduce_max_width(&mut self, delta: usize) { - self.adjust_max_width(-(delta as isize)); - } - - /// Increases the maximum width of the configuration. - /// - /// This method is intended to be used when creating a forked - /// configuration for a particular formatting context in which the - /// total available width needs to be reduced. This increases the - /// size of max_width, and all other properties affected by small - /// heuristics, without treating those adjustments as overrides. - /// - /// As an example use case, this method is used when formatting - /// arms within a declarative macro. - #[allow(unreachable_pub)] - pub fn increase_max_width(&mut self, delta: usize) { - self.adjust_max_width(delta as isize); - } - - /// Adjusts the maximum width of the configuration. - /// - /// This method is intended to be used when creating a forked - /// configuration for a particular formatting context in which the - /// total available width needs to be reduced. This adjusts the - /// size of max_width, and all other properties affected by small - /// heuristics, without treating those adjustments as overrides. - /// - /// As an example use case, this method is used when formatting - /// arms within a declarative macro. - fn adjust_max_width(&mut self, delta: isize) { - let adjust = |value: usize| (value as isize + delta) as usize; - - self.array_width.2 = adjust(self.array_width.2); - self.attr_fn_like_width.2 = adjust(self.attr_fn_like_width.2); - self.chain_width.2 = adjust(self.chain_width.2); - self.fn_call_width.2 = adjust(self.fn_call_width.2); - self.single_line_if_else_max_width.2 = adjust(self.single_line_if_else_max_width.2); - self.single_line_let_else_max_width.2 = - adjust(self.single_line_let_else_max_width.2); - self.struct_lit_width.2 = adjust(self.struct_lit_width.2); - self.struct_variant_width.2 = adjust(self.struct_variant_width.2); - - self.max_width.2 = adjust(self.max_width.2); - self.set_heuristics(); - } - fn set_width_heuristics(&mut self, heuristics: WidthHeuristics) { let max_width = self.max_width.2; let get_width_value = | diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index 421094b25dfc9..d5c27400106c6 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -1365,13 +1365,15 @@ impl MacroBranch { } else { shape.indent.block_indent(&config) }; - config.reduce_max_width(body_indent.width()); + let new_width = config.max_width() - body_indent.width(); + config.set().max_width(new_width); // First try to format as items, then as statements. let new_body_snippet = match crate::format_snippet(&body_str, &config, true) { Some(new_body) => new_body, None => { - config.increase_max_width(config.tab_spaces()); + let new_width = new_width + config.tab_spaces(); + config.set().max_width(new_width); match crate::format_code_block(&body_str, &config, true) { Some(new_body) => new_body, None => { diff --git a/src/tools/rustfmt/tests/target/issue-6651/custom_width.rs b/src/tools/rustfmt/tests/target/issue-6651/custom_width.rs index dfe747871018f..6bdfb562f5386 100644 --- a/src/tools/rustfmt/tests/target/issue-6651/custom_width.rs +++ b/src/tools/rustfmt/tests/target/issue-6651/custom_width.rs @@ -16,11 +16,6 @@ macro_rules! medium { macro_rules! long { () => { - m!( - aaaaaaaaaaaaaaaa, - bbbbbbbbbbbbbbbb, - cccccccccccccccc, - ddddddddddddddd - ); + m!(aaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccc, ddddddddddddddd); }; } From 107a6617b4bbaf3ab9db8527889a529f8c809407 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 2 Nov 2026 19:09:10 +0800 Subject: [PATCH 41/53] Prepare for merging from rust-lang/rust This updates the rust-toolchain.toml file to nightly-2026-11-02 (3e92e1a2d20b991ef23a6c6064c34d57ee8c98d3). --- src/tools/rustfmt/rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rustfmt/rust-toolchain.toml b/src/tools/rustfmt/rust-toolchain.toml index d621e4fb08484..0a85b69efd3e9 100644 --- a/src/tools/rustfmt/rust-toolchain.toml +++ b/src/tools/rustfmt/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2026-10-22" +channel = "nightly-2026-11-02" components = ["llvm-tools", "rustc-dev"] From 0e44319b2b5da111c94e0e152cb13d8172dad4f1 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 2 Nov 2026 20:04:38 +0800 Subject: [PATCH 42/53] chore: prepare changelog for v1.12.0 --- src/tools/rustfmt/CHANGELOG.md | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/tools/rustfmt/CHANGELOG.md b/src/tools/rustfmt/CHANGELOG.md index 454ffc1a91cca..72b916164aefa 100644 --- a/src/tools/rustfmt/CHANGELOG.md +++ b/src/tools/rustfmt/CHANGELOG.md @@ -2,9 +2,34 @@ ## [Unreleased] -### Changed -- The unstable `required_version` configuration option now defaults to a caret (`^`) semver requirement instead of an exact match when no comparator is given, matching how Cargo treats dependency version requirements. Use the explicit `=` operator (e.g. `required_version = "=1.2.3"`) to opt back into exact matching [#6729](https://github.com/rust-lang/rustfmt/issues/6729) + +## [1.12.0] 2026-12-24 +### Changed +- The unstable `required_version` configuration option now defaults to a caret (`^`) semver requirement instead of an exact match when no comparator is given, matching how Cargo treats dependency version requirements. Use the explicit `=` operator (e.g. `required_version = "=1.2.3"`) to opt back into exact matching [#6729](https://github.com/rust-lang/rustfmt/issues/6729) [#7048](https://github.com/rust-lang/rustfmt/pull/7048) +> [!IMPORTANT] +> Potential breaking changes: +> - When `--config-path` is a directory, rustfmt now also searches its parent directories for config files [#4660](https://github.com/rust-lang/rustfmt/issues/4660) [#7115](https://github.com/rust-lang/rustfmt/pull/7115) +> - Chains with comments now format under `use_try_shorthand=true` [#6121](https://github.com/rust-lang/rustfmt/issues/6121) [#7095](https://github.com/rust-lang/rustfmt/pull/7095) +> - Error on `--print-config minimal` with no output path [#7208](https://github.com/rust-lang/rustfmt/pull/7208) +> - Previously, this printed a warning and exited with `0`. + +### Fixed +- Fix ragged statement comment wrapping under unstable option `wrap_comments=true` and `comment_width` [#7167](https://github.com/rust-lang/rustfmt/issues/7167) [#7170](https://github.com/rust-lang/rustfmt/pull/7170) +- Enforce comment width in item lists [#7174](https://github.com/rust-lang/rustfmt/pull/7174) +- Fix lookup of global config files [#7178](https://github.com/rust-lang/rustfmt/issues/7178) [#7179](https://github.com/rust-lang/rustfmt/pull/7179) +- Don't remove empty generic args when formatting in a macro context [#7136](https://github.com/rust-lang/rustfmt/issues/7136) [#7181](https://github.com/rust-lang/rustfmt/pull/7181) +- Various `cfg_select!` fixes. Note that `cfg_select!` formatting is still nightly-gated. + - Fix `cfg_select!` parser recovery handling and error count leak [#7171](https://github.com/rust-lang/rustfmt/pull/7171) + - Don't remove necessary blocks when formatting `cfg_select!` arms [#7045](https://github.com/rust-lang/rustfmt/issues/7045) [#7183](https://github.com/rust-lang/rustfmt/pull/7183) + - Prevent more comment loss in `cfg_select!` [#7209](https://github.com/rust-lang/rustfmt/issues/7209) [rust-lang/rust#164815](https://github.com/rust-lang/rust/pull/164815) + - Don't lose comments and attributes between `cfg_select!` predicates, outer/inner blocks, arm body and trailing commas [#7209](https://github.com/rust-lang/rustfmt/issues/7209) [#7211](https://github.com/rust-lang/rustfmt/pull/7211) + - Have `cfg_select!` double-brace concise formatting respect `control_brace_style` unstable option [#7214](https://github.com/rust-lang/rustfmt/pull/7214) +- Prevent rustfmt from removing expression attributes when removing nested parentheses [#7190](https://github.com/rust-lang/rustfmt/issues/7190) [#7192](https://github.com/rust-lang/rustfmt/pull/7192) +- Fix `cfg_if!` error count leaks [#7195](https://github.com/rust-lang/rustfmt/pull/7195) +- Format comments after `where` clauses using `clause_shape` budget (affecting unstable options `wrap_comments=true` and `comment_width`) [#7148](https://github.com/rust-lang/rustfmt/issues/7148) [#7156](https://github.com/rust-lang/rustfmt/pull/7156) +- Fix ICE on failure to format generics in enum [#5738](https://github.com/rust-lang/rustfmt/issues/5738) [#6137](https://github.com/rust-lang/rustfmt/issues/6137) [#6318](https://github.com/rust-lang/rustfmt/issues/6318) [#6646](https://github.com/rust-lang/rustfmt/issues/6646) [#6396](https://github.com/rust-lang/rustfmt/pull/6396) +- Avoid leaking wrapper space from doc comment code block formatting (affecting unstable options `format_code_in_doc_comments=true` and `fn_single_line=true`) [#6832](https://github.com/rust-lang/rustfmt/issues/6832) [#6851](https://github.com/rust-lang/rustfmt/pull/6851) ## [1.11.0] 2026-11-12 From 3512466d8f99c4b74a4eddcdc8f5b7af67395444 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 2 Nov 2026 20:35:24 +0800 Subject: [PATCH 43/53] chore: remove changelog entry that's intended to be reverted in beta See . --- src/tools/rustfmt/CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/rustfmt/CHANGELOG.md b/src/tools/rustfmt/CHANGELOG.md index 72b916164aefa..b28723aa08e5f 100644 --- a/src/tools/rustfmt/CHANGELOG.md +++ b/src/tools/rustfmt/CHANGELOG.md @@ -53,7 +53,6 @@ - Correct visibility and defaultness order on associated impl type alias [#7064](https://github.com/rust-lang/rustfmt/pull/7064). - Correctly calculate the `shape` for inline const blocks when using `style_edition=2027` [#7065](https://github.com/rust-lang/rustfmt/pull/7065). - Prevent invalid code generation in macros that use raw identifiers [#7110](https://github.com/rust-lang/rustfmt/pull/7110). -- Prevent rustfmt from reporting on `max_width` configuration issues when formatting macros [#6651](https://github.com/rust-lang/rustfmt/pull/6651). ### Changed - No longer apply `use_field_init_shorthand` when formatting macros [#6981](https://github.com/rust-lang/rustfmt/pull/6981). From f237e206cc1f11bc08750822833dec2457d0c490 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 2 Nov 2026 20:05:54 +0800 Subject: [PATCH 44/53] chore: bump rustfmt version to v1.12.0 --- src/tools/rustfmt/Cargo.lock | 2 +- src/tools/rustfmt/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rustfmt/Cargo.lock b/src/tools/rustfmt/Cargo.lock index b83c8ec9612c5..38d5f7724e84d 100644 --- a/src/tools/rustfmt/Cargo.lock +++ b/src/tools/rustfmt/Cargo.lock @@ -582,7 +582,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.11.0" +version = "1.12.0" dependencies = [ "annotate-snippets", "anyhow", diff --git a/src/tools/rustfmt/Cargo.toml b/src/tools/rustfmt/Cargo.toml index 9e689fc0041f1..78ee1fb2742a0 100644 --- a/src/tools/rustfmt/Cargo.toml +++ b/src/tools/rustfmt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustfmt-nightly" -version = "1.11.0" +version = "1.12.0" description = "Tool to find and fix Rust formatting issues" repository = "https://github.com/rust-lang/rustfmt" license = "Apache-2.0 OR MIT" From 003a89309645c7edaca9ab830a2415e59893639b Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 2 Nov 2026 20:55:47 +0800 Subject: [PATCH 45/53] ci: remove no longer used `upload-assets` workflow We used to need them for standalone binaries, but we no longer do. Remove the no longer used GHA workflow. Discussed in . --- .../.github/workflows/upload-assets.yml | 88 ------------------- 1 file changed, 88 deletions(-) delete mode 100644 src/tools/rustfmt/.github/workflows/upload-assets.yml diff --git a/src/tools/rustfmt/.github/workflows/upload-assets.yml b/src/tools/rustfmt/.github/workflows/upload-assets.yml deleted file mode 100644 index 6d83e9b004a85..0000000000000 --- a/src/tools/rustfmt/.github/workflows/upload-assets.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: upload - -on: - push: - release: - types: [created] - workflow_dispatch: - -permissions: {} - -jobs: - build-release: - name: build-release - permissions: - contents: write # Required by `gh release upload` - strategy: - matrix: - build: [linux-x86_64, macos-x86_64, windows-x86_64-gnu, windows-x86_64-msvc] - include: - - build: linux-x86_64 - os: ubuntu-latest - rust: nightly - target: x86_64-unknown-linux-gnu - - build: macos-x86_64 - os: macos-latest - rust: nightly - target: x86_64-apple-darwin - - build: windows-x86_64-gnu - os: windows-latest - rust: nightly-x86_64-gnu - target: x86_64-pc-windows-gnu - - build: windows-x86_64-msvc - os: windows-latest - rust: nightly-x86_64-msvc - target: x86_64-pc-windows-msvc - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - # Run build - - name: install rustup - shell: bash - run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh - sh rustup-init.sh -y --default-toolchain none - rustup target add ${MATRIX_TARGET} - env: - MATRIX_TARGET: ${{ matrix.target }} - - - name: Add mingw64 to path for x86_64-gnu - run: echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH - if: matrix.rust == 'nightly-x86_64-gnu' - shell: bash - - - name: Build release binaries - run: cargo build --release --locked - - - name: Build archive - shell: bash - run: | - staging="rustfmt_${MATRIX_BUILD}_${GITHUB_EVENT_RELEASE_TAG_NAME}" - mkdir -p "$staging" - - cp {README.md,Configurations.md,CHANGELOG.md,LICENSE-MIT,LICENSE-APACHE} "$staging/" - - if [ "${MATRIX_OS}" = "windows-latest" ]; then - cp target/release/{rustfmt.exe,cargo-fmt.exe,rustfmt-format-diff.exe,git-rustfmt.exe} "$staging/" - 7z a "$staging.zip" "$staging" - echo "ASSET=$staging.zip" >> $GITHUB_ENV - else - cp target/release/{rustfmt,cargo-fmt,rustfmt-format-diff,git-rustfmt} "$staging/" - tar czf "$staging.tar.gz" "$staging" - echo "ASSET=$staging.tar.gz" >> $GITHUB_ENV - fi - env: - MATRIX_OS: ${{ matrix.os }} - MATRIX_BUILD: ${{ matrix.build }} - GITHUB_EVENT_RELEASE_TAG_NAME: ${{ github.event.release.tag_name }} - - - name: Upload Release Asset - shell: bash - if: github.event_name == 'release' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_EVENT_RELEASE_TAG_NAME: ${{ github.event.release.tag_name }} - run: gh release upload "${GITHUB_EVENT_RELEASE_TAG_NAME}" "${ASSET}" From bb2e46179de95ab458b53b9cf7a349c7ab237897 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 2 Nov 2026 20:03:43 -0500 Subject: [PATCH 46/53] chore: bump rustfmt version to v1.12.0 in the r-l/r workspace --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 0b47aae128338..27ebe6d13c2c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5178,7 +5178,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.11.0" +version = "1.12.0" dependencies = [ "annotate-snippets 0.12.16", "anyhow", From ebb4a8bddcf9c2b1b54059af0c05b206ccf6785d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Sep 2026 14:18:54 +0200 Subject: [PATCH 47/53] Replace `builtin # field_of` with `k#field_of` --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_ast/src/token.rs | 6 +++ compiler/rustc_ast_pretty/src/pprust/state.rs | 2 +- compiler/rustc_parse/src/parser/mod.rs | 9 +++++ compiler/rustc_parse/src/parser/ty.rs | 37 +++++++++---------- compiler/rustc_span/src/symbol.rs | 2 +- library/core/src/field.rs | 2 +- library/core/src/lib.rs | 1 + src/librustdoc/clean/types.rs | 2 +- src/tools/rustfmt/src/types.rs | 13 ++----- .../tests/target/field-representing-types.rs | 6 ++- .../invalid.next.stderr | 6 +-- .../invalid.old.stderr | 6 +-- tests/ui/field_representing_types/invalid.rs | 8 ++-- 14 files changed, 57 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index b3b85655bb261..9a49264b54b75 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2570,7 +2570,7 @@ pub enum TyKind { /// Pattern types like `pattern_type!(u32 is 1..=)`, which is the same as `NonZero`, /// just as part of the type system. Pat(Box, Box), - /// A `field_of` expression (e.g., `builtin # field_of(Struct, field)`). + /// A `field_of` expression (e.g., `k#field_of(Struct, field)`). /// /// Usually not written directly in user code but indirectly via the macro /// `core::field::field_of!(...)`. diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 20079dd039f6f..97717d08ef336 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -350,6 +350,8 @@ fn ident_can_begin_type(name: Symbol, span: Span, kind: IdentKind) -> bool { // WARNING: Take care when modifying this function! It will change the stable(!) set of // tokens that are allowed to match an `ty` nonterminal which is user observable. + // NOTE: We don't care about forced keywords that are gated behind `builtin_syntax`. + let ident_token = Token::new(Ident(name, kind), span); !ident_token.is_reserved_ident() @@ -936,6 +938,10 @@ impl Token { self.non_raw_ident().is_some_and(|id| id.name == kw) } + pub fn is_forced_keyword(&self, kw: Symbol) -> bool { + self.ident().is_some_and(|(id, kind)| kind == IdentKind::ForcedKeyword && id.name == kw) + } + /// Returns `true` if the token is a given keyword, `kw` or if `case` is `Insensitive` and this /// token is an identifier equal to `kw` ignoring the case. pub fn is_keyword_case(&self, kw: Symbol, case: Case) -> bool { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index a082300bb47ed..6836d44f320d1 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1442,7 +1442,7 @@ impl<'a> State<'a> { self.print_ty_pat(pat); } ast::TyKind::FieldOf(ty, variant, field) => { - self.word("builtin # field_of"); + self.word("k#field_of"); self.popen(); let ib = self.ibox(0); self.print_type(ty); diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index fb26ef5e8ace6..a89fd9b6298f3 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -630,6 +630,15 @@ impl<'a> Parser<'a> { if !self.eat_keyword(exp) { self.unexpected() } else { Ok(()) } } + #[must_use] + pub(crate) fn eat_forced_keyword_noexpect(&mut self, kw: Symbol) -> bool { + if self.token.is_forced_keyword(kw) { + self.bump(); + return true; + } + false + } + /// Consume a sequence produced by a metavar expansion, if present. pub fn eat_metavar_seq( &mut self, diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index e2187612f5d20..82f22b59a2060 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -326,8 +326,6 @@ impl<'a> Parser<'a> { self.parse_borrowed_pointee()? } else if self.eat_keyword_noexpect(kw::Typeof) { self.parse_typeof_ty(lo)? - } else if self.is_builtin() { - self.parse_builtin_ty()? } else if self.eat_keyword(exp!(Underscore)) { // A type to be inferred `_` TyKind::Infer @@ -423,6 +421,8 @@ impl<'a> Parser<'a> { && self.look_ahead(1, |tok| tok.kind == token::Lt) { self.parse_unsafe_binder_ty()? + } else if self.eat_forced_keyword_noexpect(kw::FieldOf) { + self.parse_ty_field_of(lo)? } else { let msg = format!("expected type, found {}", super::token_descr(&self.token)); let mut err = self.dcx().struct_span_err(lo, msg); @@ -826,16 +826,9 @@ impl<'a> Parser<'a> { Ok(TyKind::Err(guar)) } - fn parse_builtin_ty(&mut self) -> PResult<'a, TyKind> { - self.parse_builtin(|this, lo, ident| { - Ok(match ident.name { - sym::field_of => Some(this.parse_ty_field_of(lo)?), - _ => None, - }) - }) - } + pub(crate) fn parse_ty_field_of(&mut self, lo: Span) -> PResult<'a, TyKind> { + self.expect(exp!(OpenParen))?; - pub(crate) fn parse_ty_field_of(&mut self, _lo: Span) -> PResult<'a, TyKind> { let container = self.parse_ty()?; self.expect(exp!(Comma))?; @@ -844,30 +837,36 @@ impl<'a> Parser<'a> { if let Err(mut e) = self.expect_one_of(&[], &[exp!(CloseParen)]) { if trailing_comma { - e.note("unexpected third argument to field_of"); + e.note("unexpected third argument to `field_of`"); } else { - e.note("field_of expects dot-separated field and variant names"); + e.note("`field_of` expects dot-separated field and variant names"); } e.emit(); } - // Eat tokens until the macro call ends. + // Eat tokens until the construct ends. if self.may_recover() { while !self.token.kind.is_close_delim_or_eof() { self.bump(); } } + // FIXME: Odd not to include the closing paren (contrary to the leading one etc.) but + // it actually "improves" diagnostics slightly by not pointing into the wrapper macros. + let span = self.token.span; + self.expect(exp!(CloseParen))?; + + self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + match *fields { - [] => Err(self.dcx().struct_span_err( - self.token.span, - "`field_of!` expects dot-separated field and variant names", - )), + [] => Err(self + .dcx() + .struct_span_err(span, "`field_of` expects dot-separated field and variant names")), [field] => Ok(TyKind::FieldOf(container, None, field)), [variant, field] => Ok(TyKind::FieldOf(container, Some(variant), field)), _ => Err(self.dcx().struct_span_err( fields.iter().map(|f| f.span).collect::>(), - "`field_of!` only supports a single field or a variant with a field", + "`field_of` only supports a single field or a variant with a field", )), } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index de141dcc8b823..168e2e6d5eefb 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -130,6 +130,7 @@ symbols! { ContractEnsures: "contract_ensures", ContractRequires: "contract_requires", Default: "default", + FieldOf: "field_of", MacroRules: "macro_rules", Pin: "pin", Raw: "raw", @@ -991,7 +992,6 @@ symbols! { field, field_base, field_init_shorthand, - field_of, field_offset, field_projections, field_representing_type, diff --git a/library/core/src/field.rs b/library/core/src/field.rs index 5a8ae7759bc1e..07c8c991bac43 100644 --- a/library/core/src/field.rs +++ b/library/core/src/field.rs @@ -134,7 +134,7 @@ impl Ord // it to `FieldRepresentingType<...>`. Thus stabilizing this requires careful thought about the // completeness of the trait impls for `FieldRepresentingType`. pub macro field_of($Container:ty, $($fields:expr)+ $(,)?) { - builtin # field_of($Container, $($fields)+) + k#field_of($Container, $($fields)+) } /// Type representing a field of a `struct`, `union`, `enum` variant or tuple. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 36a19f89e58cd..67c216053f7b1 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -129,6 +129,7 @@ #![feature(f128)] #![feature(field_projections)] #![feature(final_associated_functions)] +#![feature(forced_keywords)] #![feature(freeze_impls)] #![feature(fundamental)] #![feature(impl_restriction)] diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 56d651aff2731..7cbdaaac864f8 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1853,7 +1853,7 @@ impl PrimitiveType { Char => sym::char, Array => sym::array, Pat => sym::pat, - FieldOf => sym::field_of, + FieldOf => kw::FieldOf, Slice => sym::slice, Tuple => sym::tuple, Unit => sym::unit, diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 28c2740444822..6451595449046 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -1015,14 +1015,6 @@ impl Rewrite for ast::Ty { }) } ast::TyKind::CVarArgs => Ok("...".to_owned()), - ast::TyKind::FieldOf(ref ty, ref variant, ref field) => { - let ty = ty.rewrite_result(context, shape)?; - if let Some(variant) = variant { - Ok(format!("builtin # field_of({ty}, {variant}.{field})")) - } else { - Ok(format!("builtin # field_of({ty}, {field})")) - } - } ast::TyKind::UnsafeBinder(ref binder) => { let mut result = String::new(); if binder.generic_params.is_empty() { @@ -1049,7 +1041,10 @@ impl Rewrite for ast::Ty { result.push_str(&rewrite); Ok(result) } - ast::TyKind::Pat(..) | ast::TyKind::View(..) | ast::TyKind::GcaMacro(..) => { + ast::TyKind::FieldOf(..) + | ast::TyKind::GcaMacro(..) + | ast::TyKind::Pat(..) + | ast::TyKind::View(..) => { // These don't normally occur in the AST because macros aren't expanded. However, // rustfmt tries to parse macro arguments when formatting macros, so it's not // totally impossible for rustfmt to come across these nodes when formatting a file. diff --git a/src/tools/rustfmt/tests/target/field-representing-types.rs b/src/tools/rustfmt/tests/target/field-representing-types.rs index a31ccbfb461f2..c5b400c6b3442 100644 --- a/src/tools/rustfmt/tests/target/field-representing-types.rs +++ b/src/tools/rustfmt/tests/target/field-representing-types.rs @@ -1,2 +1,4 @@ -type FRT = builtin # field_of(Type, ident); -type FRT2 = builtin # field_of(Type, ident); +//@ rustfmt-edition: 2021 + +type FRT = k#field_of(Type, ident); +type FRT2 = k#field_of(Type, ident); diff --git a/tests/ui/field_representing_types/invalid.next.stderr b/tests/ui/field_representing_types/invalid.next.stderr index 52872001b4dee..82b2aa81451ab 100644 --- a/tests/ui/field_representing_types/invalid.next.stderr +++ b/tests/ui/field_representing_types/invalid.next.stderr @@ -33,7 +33,7 @@ error: offset_of expects dot-separated field and variant names LL | let _: field_of!(Enum, Variant..field); | ^^^^^^^^^^^^^^ -error: `field_of!` expects dot-separated field and variant names +error: `field_of` expects dot-separated field and variant names --> $DIR/invalid.rs:28:12 | LL | let _: field_of!(Enum, Variant..field); @@ -48,7 +48,7 @@ error: offset_of expects dot-separated field and variant names LL | let _: field_of!(Struct, [42]); | ^^^^ -error: `field_of!` expects dot-separated field and variant names +error: `field_of` expects dot-separated field and variant names --> $DIR/invalid.rs:30:12 | LL | let _: field_of!(Struct, [42]); @@ -57,7 +57,7 @@ LL | let _: field_of!(Struct, [42]); | in this macro invocation | this macro call doesn't expand to a type -error: `field_of!` only supports a single field or a variant with a field +error: `field_of` only supports a single field or a variant with a field --> $DIR/invalid.rs:32:30 | LL | let _: field_of!(Struct, field1.field2.field3); diff --git a/tests/ui/field_representing_types/invalid.old.stderr b/tests/ui/field_representing_types/invalid.old.stderr index 52872001b4dee..82b2aa81451ab 100644 --- a/tests/ui/field_representing_types/invalid.old.stderr +++ b/tests/ui/field_representing_types/invalid.old.stderr @@ -33,7 +33,7 @@ error: offset_of expects dot-separated field and variant names LL | let _: field_of!(Enum, Variant..field); | ^^^^^^^^^^^^^^ -error: `field_of!` expects dot-separated field and variant names +error: `field_of` expects dot-separated field and variant names --> $DIR/invalid.rs:28:12 | LL | let _: field_of!(Enum, Variant..field); @@ -48,7 +48,7 @@ error: offset_of expects dot-separated field and variant names LL | let _: field_of!(Struct, [42]); | ^^^^ -error: `field_of!` expects dot-separated field and variant names +error: `field_of` expects dot-separated field and variant names --> $DIR/invalid.rs:30:12 | LL | let _: field_of!(Struct, [42]); @@ -57,7 +57,7 @@ LL | let _: field_of!(Struct, [42]); | in this macro invocation | this macro call doesn't expand to a type -error: `field_of!` only supports a single field or a variant with a field +error: `field_of` only supports a single field or a variant with a field --> $DIR/invalid.rs:32:30 | LL | let _: field_of!(Struct, field1.field2.field3); diff --git a/tests/ui/field_representing_types/invalid.rs b/tests/ui/field_representing_types/invalid.rs index 0d8c5d563401c..e0ba33dd4eb59 100644 --- a/tests/ui/field_representing_types/invalid.rs +++ b/tests/ui/field_representing_types/invalid.rs @@ -24,10 +24,10 @@ fn main() { let _: field_of!(Struct); //~ ERROR: unexpected end of macro invocation let _: field_of!(Struct,); //~ ERROR: unexpected end of macro invocation let _: field_of!(Struct, field, extra); //~ ERROR: no rules expected `extra` - // FIXME(FRTs): adjust error message to mention `field_of!` & prevent double errors + // FIXME(FRTs): adjust error message to mention `field_of` & prevent double errors let _: field_of!(Enum, Variant..field); //~ ERROR: offset_of expects dot-separated field and variant names - //~^ ERROR: `field_of!` expects dot-separated field and variant names + //~^ ERROR: `field_of` expects dot-separated field and variant names let _: field_of!(Struct, [42]); //~ ERROR: offset_of expects dot-separated field and variant names - //~^ ERROR: `field_of!` expects dot-separated field and variant names - let _: field_of!(Struct, field1.field2.field3); //~ ERROR: `field_of!` only supports a single field or a variant with a field + //~^ ERROR: `field_of` expects dot-separated field and variant names + let _: field_of!(Struct, field1.field2.field3); //~ ERROR: `field_of` only supports a single field or a variant with a field } From c5bab76234637597378b419a8d1b2ecf8710c4ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Sep 2026 12:27:18 +0200 Subject: [PATCH 48/53] Replace `builtin # deref` with `k#deref` --- compiler/rustc_ast_pretty/src/pprust/state.rs | 5 ++++ compiler/rustc_attr_ir/src/lang_items.rs | 2 +- compiler/rustc_hir_typeck/src/place_op.rs | 4 +-- .../src/builder/matches/test.rs | 4 +-- compiler/rustc_parse/src/parser/pat.rs | 30 +++++++------------ compiler/rustc_span/src/symbol.rs | 2 +- library/core/src/macros/mod.rs | 2 +- .../src/methods/should_implement_trait.rs | 2 +- 8 files changed, 24 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 6836d44f320d1..955bca61f7960 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -2012,6 +2012,11 @@ impl<'a> State<'a> { self.pclose(); } PatKind::Deref(inner) => { + // FIXME(deref_patterns, builtin_syntax, forced_keywords, clippy): + // This should render `k#deref(...)` but unfortunately Clippy uses `pat_to_string` + // for the suggestions of its lint `unnested_or_patterns`. Smh fix Clippy to not + // rely on AST pretty printing `deref!(…)` for `Deref` patterns and please don't + // think about adding a flag to AST pretty to control the output. self.word("deref!"); self.popen(); self.print_pat(inner); diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index d715a6d3af8c5..411c62f902d23 100644 --- a/compiler/rustc_attr_ir/src/lang_items.rs +++ b/compiler/rustc_attr_ir/src/lang_items.rs @@ -239,7 +239,7 @@ language_item_table! { Complex, sym::complex, complex, Target::Struct, GenericRequirement::Exact(1); PpcF128, sym::ppcf128, ppcf128, Target::Struct, GenericRequirement::None; - Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0); + Deref, kw::Deref, deref_trait, Target::Trait, GenericRequirement::Exact(0); DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0); DerefPure, sym::deref_pure, deref_pure_trait, Target::Trait, GenericRequirement::Exact(0); DerefTarget, sym::deref_target, deref_target, Target::AssocTy(AssocCtxt::Trait), GenericRequirement::None; diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index f3cba3a27d69f..7ccd7eb435d03 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -9,7 +9,7 @@ use rustc_middle::ty::adjustment::{ OverloadedDeref, PointerCoercion, }; use rustc_middle::ty::{self, Ty}; -use rustc_span::{Span, span_bug, sym}; +use rustc_span::{Span, kw, span_bug, sym}; use tracing::debug; use crate::method::{MethodCallee, TreatNotYetDefinedOpaques}; @@ -203,7 +203,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op); let (Some(imm_tr), imm_op) = (match op { - PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref), + PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), kw::Deref), PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index), }) else { // Bail if `Deref` or `Index` isn't defined. diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index d116806fc0b73..a8ae60b4daba7 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -14,7 +14,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt}; use rustc_span::def_id::DefId; -use rustc_span::{DUMMY_SP, Span, Spanned, Symbol, bug, sym}; +use rustc_span::{DUMMY_SP, Span, Spanned, Symbol, bug, kw, sym}; use tracing::{debug, instrument}; use crate::builder::Builder; @@ -343,7 +343,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, ) { let (trait_item, method) = match mutability { - Mutability::Not => (LangItem::Deref, sym::deref), + Mutability::Not => (LangItem::Deref, kw::Deref), Mutability::Mut => (LangItem::DerefMut, sym::deref_mut), }; let borrow_kind = super::util::ref_pat_borrow_kind(mutability); diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 9fd38aa0f3b8f..042da455da182 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -801,8 +801,17 @@ impl<'a> Parser<'a> { } else { PatKind::Expr(const_expr) } - } else if self.is_builtin() { - self.parse_pat_builtin()? + } else if self.eat_forced_keyword_noexpect(kw::Deref) { + self.expect(exp!(OpenParen))?; + let pat = ast::PatKind::Deref(Box::new(self.parse_pat_allow_top_guard( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::LikelyTuple, + )?)); + self.expect(exp!(CloseParen))?; + self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + pat } // Don't eagerly error on semantically invalid tokens when matching // declarative macros, as the input to those doesn't have to be @@ -1608,23 +1617,6 @@ impl<'a> Parser<'a> { .contains(&self.token.kind) } - fn parse_pat_builtin(&mut self) -> PResult<'a, PatKind> { - self.parse_builtin(|self_, _lo, ident| { - Ok(match ident.name { - // builtin#deref(PAT) - sym::deref => { - Some(ast::PatKind::Deref(Box::new(self_.parse_pat_allow_top_guard( - None, - RecoverComma::Yes, - RecoverColon::Yes, - CommaRecoveryMode::LikelyTuple, - )?))) - } - _ => None, - }) - }) - } - // FIXME: remove this entirely eventually /// Parses the removed `box pat` syntax to provide a more helpful error message. fn parse_pat_box(&mut self) -> PResult<'a, PatKind> { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 168e2e6d5eefb..9a536a8404234 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -130,6 +130,7 @@ symbols! { ContractEnsures: "contract_ensures", ContractRequires: "contract_requires", Default: "default", + Deref: "deref", FieldOf: "field_of", MacroRules: "macro_rules", Pin: "pin", @@ -830,7 +831,6 @@ symbols! { deprecated, deprecated_safe, deprecated_suggestion, - deref, deref_method, deref_mut, deref_patterns, diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 3b378078d3284..9949a5cfa897e 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1927,7 +1927,7 @@ pub(crate) mod builtin { )] #[diagnostic::opaque] pub macro deref($pat:pat) { - builtin # deref($pat) + k#deref($pat) } /// Derive macro generating an impl of the trait `From`. diff --git a/src/tools/clippy/clippy_lints/src/methods/should_implement_trait.rs b/src/tools/clippy/clippy_lints/src/methods/should_implement_trait.rs index c233fe3f28ed5..2536ad574239b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/should_implement_trait.rs +++ b/src/tools/clippy/clippy_lints/src/methods/should_implement_trait.rs @@ -112,7 +112,7 @@ const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, SelfKind::Ref, OutType::Any, true, Edition2015), ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, SelfKind::Ref, OutType::Any, true, Edition2015), ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, SelfKind::No, OutType::Any, true, Edition2015), - ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Deref", kw::Deref, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, SelfKind::RefMut, OutType::Ref, true, Edition2015), ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, SelfKind::Value, OutType::Any, true, Edition2015), ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, SelfKind::RefMut, OutType::Unit, true, Edition2015), From 44d0732bada064e9a4acd174a135dbbd985bbeab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Sep 2026 13:00:27 +0200 Subject: [PATCH 49/53] Replace `builtin # offset_of` with `k#offset_of` --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_ast/src/token.rs | 2 + .../rustc_ast_pretty/src/pprust/state/expr.rs | 2 +- compiler/rustc_attr_ir/src/lang_items.rs | 2 +- .../src/interpret/intrinsics.rs | 4 +- .../rustc_hir_analysis/src/check/intrinsic.rs | 6 +- compiler/rustc_parse/src/parser/expr.rs | 12 ++- compiler/rustc_span/src/symbol.rs | 2 +- library/core/src/mem/mod.rs | 4 +- src/tools/rustfmt/tests/target/issue-6105.rs | 4 +- .../feature-gate-builtin_syntax.rs | 5 +- .../feature-gate-builtin_syntax.stderr | 6 +- tests/ui/offset-of/offset-of-builtin.rs | 32 ------- tests/ui/offset-of/offset-of-builtin.stderr | 46 ---------- tests/ui/offset-of/offset-of-tuple-field.rs | 8 -- .../ui/offset-of/offset-of-tuple-field.stderr | 64 ++------------ tests/ui/offset-of/offset-of-tuple.rs | 24 +++--- tests/ui/offset-of/offset-of-tuple.stderr | 86 +++++++++---------- tests/ui/unpretty/exhaustive.expanded.stdout | 2 +- 19 files changed, 99 insertions(+), 214 deletions(-) delete mode 100644 tests/ui/offset-of/offset-of-builtin.rs delete mode 100644 tests/ui/offset-of/offset-of-builtin.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 9a49264b54b75..ced51551a77c1 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1871,7 +1871,7 @@ pub enum ExprKind { /// Output of the `asm!()` macro. InlineAsm(Box), - /// An `offset_of` expression (e.g., `builtin # offset_of(Struct, field)`). + /// An `offset_of` expression (e.g., `k#offset_of(Struct, field)`). /// /// Usually not written directly in user code but /// indirectly via the macro `core::mem::offset_of!(...)`. diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 97717d08ef336..820cd9bd99644 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -313,6 +313,8 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, kind: IdentKind) -> bool { // WARNING: Take care when modifying this function! It will change the stable(!) set of // tokens that are allowed to match an `expr` nonterminal which is user observable. + // NOTE: We don't care about forced keywords that are gated behind `builtin_syntax`. + let ident_token = Token::new(Ident(name, kind), span); // FIXME: Remove `box` from this list given we officially no longer support box expressions diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index d6e8f69dbfa03..aeb380ecd0ca1 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -795,7 +795,7 @@ impl<'a> State<'a> { self.pclose(); } ast::ExprKind::OffsetOf(container, fields) => { - self.word("builtin # offset_of"); + self.word("k#offset_of"); self.popen(); let ib = self.ibox(0); self.print_type(container); diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index 411c62f902d23..56fb1a4b6c865 100644 --- a/compiler/rustc_attr_ir/src/lang_items.rs +++ b/compiler/rustc_attr_ir/src/lang_items.rs @@ -163,7 +163,7 @@ language_item_table! { Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1); AlignOf, sym::mem_align_const, align_const, Target::AssocConst(AssocCtxt::Trait), GenericRequirement::Exact(0); SizeOf, sym::mem_size_const, size_const, Target::AssocConst(AssocCtxt::Trait), GenericRequirement::Exact(0); - OffsetOf, sym::offset_of, offset_of, Target::Fn, GenericRequirement::Exact(1); + OffsetOf, kw::OffsetOf, offset_of, Target::Fn, GenericRequirement::Exact(1); /// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ"). StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None; Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 941af4a4ab01d..95ca582e011df 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -14,7 +14,7 @@ use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_targ use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::{Symbol, bug, span_bug, sym}; +use rustc_span::{Symbol, bug, kw, span_bug, sym}; use tracing::trace; use super::memory::MemoryKind; @@ -231,7 +231,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let val = layout.align.bytes(); self.write_scalar(Scalar::from_target_usize(val, self), dest)?; } - sym::offset_of => { + kw::OffsetOf => { let tp_ty = instance.args.type_at(0); let variant = self.read_scalar(&args[0])?.to_u32()?; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 85b666372074a..8b55734d90c8f 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -6,7 +6,7 @@ use rustc_hir as hir; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::{self, Const, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol, kw, sym}; use crate::check::check_function_signature; use crate::diagnostics::{UnrecognizedIntrinsicFunction, WrongNumberOfGenericArgumentsToIntrinsic}; @@ -66,6 +66,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi // safe extern fns are otherwise unprecedented. // tidy-alphabetical-start + | kw::OffsetOf | sym::abort_immediate | sym::add_with_overflow | sym::aggregate_raw_ptr @@ -141,7 +142,6 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::needs_drop | sym::offload | sym::offload_get_num_devices - | sym::offset_of | sym::overflow_checks | sym::powf | sym::powi @@ -271,7 +271,7 @@ pub(crate) fn check_intrinsic_type( (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize) } sym::type_id_size_of => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, tcx.types.usize)), - sym::offset_of => (1, 0, vec![tcx.types.u32, tcx.types.u32], tcx.types.usize), + kw::OffsetOf => (1, 0, vec![tcx.types.u32, tcx.types.u32], tcx.types.usize), sym::field_offset => (1, 0, vec![], tcx.types.usize), sym::rustc_peek => (1, 0, vec![param(0)], param(0)), sym::caller_location => (0, 0, vec![], tcx.caller_location_ty()), diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index fd59519f1e5a6..e3403506423a9 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1385,6 +1385,8 @@ impl<'a> Parser<'a> { return Ok(expr); } Ok(this.mk_expr(this.prev_token.span, ExprKind::Underscore)) + } else if this.eat_forced_keyword_noexpect(kw::OffsetOf) { + this.parse_expr_offset_of(lo) } else if this.token_uninterpolated_span().at_least_rust_2018() { // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. let at_async = this.check_keyword(exp!(Async)); @@ -1830,7 +1832,6 @@ impl<'a> Parser<'a> { fn parse_expr_builtin(&mut self) -> PResult<'a, Box> { self.parse_builtin(|this, lo, ident| { Ok(match ident.name { - sym::offset_of => Some(this.parse_expr_offset_of(lo)?), sym::type_ascribe => Some(this.parse_expr_type_ascribe(lo)?), sym::wrap_binder => { Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Wrap)?) @@ -1876,8 +1877,9 @@ impl<'a> Parser<'a> { ret } - /// Built-in macro for `offset_of!` expressions. pub(crate) fn parse_expr_offset_of(&mut self, lo: Span) -> PResult<'a, Box> { + self.expect(exp!(OpenParen))?; + let container = self.parse_ty()?; self.expect(exp!(Comma))?; @@ -1900,7 +1902,13 @@ impl<'a> Parser<'a> { } } + // FIXME: Odd not to include the closing paren (contrary to the leading one etc.) but + // it actually "improves" diagnostics slightly by not pointing into the wrapper macros. let span = lo.to(self.token.span); + self.expect(exp!(CloseParen))?; + + self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields))) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9a536a8404234..c0c90ba4bbc44 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -133,6 +133,7 @@ symbols! { Deref: "deref", FieldOf: "field_of", MacroRules: "macro_rules", + OffsetOf: "offset_of", Pin: "pin", Raw: "raw", Reuse: "reuse", @@ -1508,7 +1509,6 @@ symbols! { offload_kernel, offload_region, offset, - offset_of, offset_of_enum, offset_of_nested, offset_of_slice, diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index b38557779092c..ba91c0c8f27cc 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1664,10 +1664,10 @@ impl SizedTypeProperties for T {} note = "this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)`" )] #[doc(alias = "memoffset")] -#[allow_internal_unstable(builtin_syntax, core_intrinsics)] +#[allow_internal_unstable(core_intrinsics, builtin_syntax)] #[diagnostic::opaque] pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { - const { builtin # offset_of($Container, $($fields)+) } + const { k#offset_of($Container, $($fields)+) } } /// Create a fresh instance of the inhabited ZST type `T`. diff --git a/src/tools/rustfmt/tests/target/issue-6105.rs b/src/tools/rustfmt/tests/target/issue-6105.rs index 1eb0ce89f7812..cb73fc6042e24 100644 --- a/src/tools/rustfmt/tests/target/issue-6105.rs +++ b/src/tools/rustfmt/tests/target/issue-6105.rs @@ -1 +1,3 @@ -const _: () = builtin # offset_of(x, x); +//@ rustfmt-edition: 2021 + +const _: () = k#offset_of(x, x); diff --git a/tests/ui/feature-gates/feature-gate-builtin_syntax.rs b/tests/ui/feature-gates/feature-gate-builtin_syntax.rs index 832bb5a96bc3d..e36ffe1503956 100644 --- a/tests/ui/feature-gates/feature-gate-builtin_syntax.rs +++ b/tests/ui/feature-gates/feature-gate-builtin_syntax.rs @@ -1,7 +1,10 @@ +//@ edition: 2021.. +#![feature(forced_keywords)] + struct Foo { v: u8, w: u8, } fn main() { - builtin # offset_of(Foo, v); //~ ERROR `builtin #` syntax is unstable + k#offset_of(Foo, v); //~ ERROR `builtin #` syntax is unstable } diff --git a/tests/ui/feature-gates/feature-gate-builtin_syntax.stderr b/tests/ui/feature-gates/feature-gate-builtin_syntax.stderr index 297363b3de711..59a39df794c44 100644 --- a/tests/ui/feature-gates/feature-gate-builtin_syntax.stderr +++ b/tests/ui/feature-gates/feature-gate-builtin_syntax.stderr @@ -1,8 +1,8 @@ error[E0658]: `builtin #` syntax is unstable - --> $DIR/feature-gate-builtin_syntax.rs:6:15 + --> $DIR/feature-gate-builtin_syntax.rs:9:5 | -LL | builtin # offset_of(Foo, v); - | ^^^^^^^^^ +LL | k#offset_of(Foo, v); + | ^^^^^^^^^^^^^^^^^^^^ | = note: see issue #110680 for more information = help: add `#![feature(builtin_syntax)]` to the crate attributes to enable diff --git a/tests/ui/offset-of/offset-of-builtin.rs b/tests/ui/offset-of/offset-of-builtin.rs deleted file mode 100644 index 6664c10f905ef..0000000000000 --- a/tests/ui/offset-of/offset-of-builtin.rs +++ /dev/null @@ -1,32 +0,0 @@ -#![feature(builtin_syntax)] - -// For the exposed macro we already test these errors in the other files, -// but this test helps to make sure the builtin construct also errors. -// This has the same examples as offset-of-arg-count.rs - -fn main() { - builtin # offset_of(NotEnoughArguments); //~ ERROR expected one of -} -fn t1() { - builtin # offset_of(NotEnoughArgumentsWithAComma, ); //~ ERROR expected expression -} -fn t2() { - builtin # offset_of(S, f, too many arguments); //~ ERROR expected `)`, found `too` -} -fn t3() { - builtin # offset_of(S, f); // compiles fine -} -fn t4() { - builtin # offset_of(S, f.); //~ ERROR unexpected token -} -fn t5() { - builtin # offset_of(S, f.,); //~ ERROR unexpected token -} -fn t6() { - builtin # offset_of(S, f..); //~ ERROR offset_of expects dot-separated field and variant names -} -fn t7() { - builtin # offset_of(S, f..,); //~ ERROR offset_of expects dot-separated field and variant names -} - -struct S { f: u8, } diff --git a/tests/ui/offset-of/offset-of-builtin.stderr b/tests/ui/offset-of/offset-of-builtin.stderr deleted file mode 100644 index 5917ee2936361..0000000000000 --- a/tests/ui/offset-of/offset-of-builtin.stderr +++ /dev/null @@ -1,46 +0,0 @@ -error: expected one of `!`, `(`, `+`, `,`, `::`, or `<`, found `)` - --> $DIR/offset-of-builtin.rs:8:43 - | -LL | builtin # offset_of(NotEnoughArguments); - | ^ expected one of `!`, `(`, `+`, `,`, `::`, or `<` - -error: expected expression, found `)` - --> $DIR/offset-of-builtin.rs:11:55 - | -LL | builtin # offset_of(NotEnoughArgumentsWithAComma, ); - | ^ expected expression - -error: expected `)`, found `too` - --> $DIR/offset-of-builtin.rs:14:31 - | -LL | builtin # offset_of(S, f, too many arguments); - | ^^^ expected `)` - | - = note: unexpected third argument to offset_of - -error: unexpected token: `)` - --> $DIR/offset-of-builtin.rs:20:30 - | -LL | builtin # offset_of(S, f.); - | ^ - -error: unexpected token: `,` - --> $DIR/offset-of-builtin.rs:23:30 - | -LL | builtin # offset_of(S, f.,); - | ^ - -error: offset_of expects dot-separated field and variant names - --> $DIR/offset-of-builtin.rs:26:28 - | -LL | builtin # offset_of(S, f..); - | ^^^ - -error: offset_of expects dot-separated field and variant names - --> $DIR/offset-of-builtin.rs:29:28 - | -LL | builtin # offset_of(S, f..,); - | ^^^ - -error: aborting due to 7 previous errors - diff --git a/tests/ui/offset-of/offset-of-tuple-field.rs b/tests/ui/offset-of/offset-of-tuple-field.rs index 02d41f91a2563..755d61d18b739 100644 --- a/tests/ui/offset-of/offset-of-tuple-field.rs +++ b/tests/ui/offset-of/offset-of-tuple-field.rs @@ -1,5 +1,3 @@ -#![feature(builtin_syntax)] - use std::mem::offset_of; fn main() { @@ -9,12 +7,6 @@ fn main() { offset_of!((u8, u8), 1_u8); //~ ERROR no field `1_` //~| ERROR suffixes on a tuple index - builtin # offset_of((u8, u8), 1e2); //~ ERROR no field `1e2` - builtin # offset_of((u8, u8), _0); //~ ERROR no field `_0` - builtin # offset_of((u8, u8), 01); //~ ERROR no field `01` - builtin # offset_of((u8, u8), 1_u8); //~ ERROR no field `1_` - //~| ERROR suffixes on a tuple index - offset_of!(((u8, u16), (u32, u16, u8)), 0.2); //~ ERROR no field `2` offset_of!(((u8, u16), (u32, u16, u8)), 0.1e2); //~ ERROR no field `1e2` offset_of!(((u8, u16), (u32, u16, u8)), 1.2); diff --git a/tests/ui/offset-of/offset-of-tuple-field.stderr b/tests/ui/offset-of/offset-of-tuple-field.stderr index 01622c5fa2da6..052e4b0596fd6 100644 --- a/tests/ui/offset-of/offset-of-tuple-field.stderr +++ b/tests/ui/offset-of/offset-of-tuple-field.stderr @@ -1,17 +1,11 @@ error: suffixes on a tuple index are invalid - --> $DIR/offset-of-tuple-field.rs:15:35 - | -LL | builtin # offset_of((u8, u8), 1_u8); - | ^^^^ invalid suffix `u8` - -error: suffixes on a tuple index are invalid - --> $DIR/offset-of-tuple-field.rs:9:26 + --> $DIR/offset-of-tuple-field.rs:7:26 | LL | offset_of!((u8, u8), 1_u8); | ^^^^ invalid suffix `u8` error[E0609]: no field `_0` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:6:26 + --> $DIR/offset-of-tuple-field.rs:4:26 | LL | offset_of!((u8, u8), _0); | ^^ @@ -23,7 +17,7 @@ LL + offset_of!((u8, u8), 0); | error[E0609]: no field `01` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:7:26 + --> $DIR/offset-of-tuple-field.rs:5:26 | LL | offset_of!((u8, u8), 01); | ^^ @@ -31,7 +25,7 @@ LL | offset_of!((u8, u8), 01); = note: available fields are: `0`, `1` error[E0609]: no field `1e2` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:8:26 + --> $DIR/offset-of-tuple-field.rs:6:26 | LL | offset_of!((u8, u8), 1e2); | ^^^ @@ -39,7 +33,7 @@ LL | offset_of!((u8, u8), 1e2); = note: available fields are: `0`, `1` error[E0609]: no field `1_` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:9:26 + --> $DIR/offset-of-tuple-field.rs:7:26 | LL | offset_of!((u8, u8), 1_u8); | ^^^^ @@ -50,48 +44,8 @@ LL - offset_of!((u8, u8), 1_u8); LL + offset_of!((u8, u8), 1); | -error[E0609]: no field `1e2` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:12:35 - | -LL | builtin # offset_of((u8, u8), 1e2); - | ^^^ - | - = note: available fields are: `0`, `1` - -error[E0609]: no field `_0` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:13:35 - | -LL | builtin # offset_of((u8, u8), _0); - | ^^ - | -help: a field with a similar name exists - | -LL - builtin # offset_of((u8, u8), _0); -LL + builtin # offset_of((u8, u8), 0); - | - -error[E0609]: no field `01` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:14:35 - | -LL | builtin # offset_of((u8, u8), 01); - | ^^ - | - = note: available fields are: `0`, `1` - -error[E0609]: no field `1_` on type `(u8, u8)` - --> $DIR/offset-of-tuple-field.rs:15:35 - | -LL | builtin # offset_of((u8, u8), 1_u8); - | ^^^^ - | -help: a field with a similar name exists - | -LL - builtin # offset_of((u8, u8), 1_u8); -LL + builtin # offset_of((u8, u8), 1); - | - error[E0609]: no field `2` on type `(u8, u16)` - --> $DIR/offset-of-tuple-field.rs:18:47 + --> $DIR/offset-of-tuple-field.rs:10:47 | LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.2); | ^ @@ -99,7 +53,7 @@ LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.2); = note: available fields are: `0`, `1` error[E0609]: no field `1e2` on type `(u8, u16)` - --> $DIR/offset-of-tuple-field.rs:19:47 + --> $DIR/offset-of-tuple-field.rs:11:47 | LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.1e2); | ^^^ @@ -107,11 +61,11 @@ LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.1e2); = note: available fields are: `0`, `1` error[E0609]: no field `0` on type `u8` - --> $DIR/offset-of-tuple-field.rs:21:49 + --> $DIR/offset-of-tuple-field.rs:13:49 | LL | offset_of!(((u8, u16), (u32, u16, u8)), 1.2.0); | ^ -error: aborting due to 13 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0609`. diff --git a/tests/ui/offset-of/offset-of-tuple.rs b/tests/ui/offset-of/offset-of-tuple.rs index ddbaee97b1bb0..fdb571bc6086f 100644 --- a/tests/ui/offset-of/offset-of-tuple.rs +++ b/tests/ui/offset-of/offset-of-tuple.rs @@ -1,4 +1,5 @@ -#![feature(builtin_syntax)] +//@ edition: 2021.. +#![feature(forced_keywords, builtin_syntax)] use std::mem::offset_of; @@ -9,9 +10,9 @@ fn main() { offset_of!((u8, u8), 1 .); //~ ERROR unexpected token: `)` // We need to put these into curly braces, otherwise only one of the // errors will be emitted and the others suppressed. - { builtin # offset_of((u8, u8), +1) }; //~ ERROR leading `+` is not supported - { builtin # offset_of((u8, u8), 1.) }; //~ ERROR offset_of expects dot-separated field and variant names - { builtin # offset_of((u8, u8), 1 .) }; //~ ERROR unexpected token: `)` + { k#offset_of((u8, u8), +1) }; //~ ERROR leading `+` is not supported + { k#offset_of((u8, u8), 1.) }; //~ ERROR offset_of expects dot-separated field and variant names + { k#offset_of((u8, u8), 1 .) }; //~ ERROR unexpected token: `)` } type ComplexTup = (((u8, u8), u8), u8); @@ -26,14 +27,15 @@ fn nested() { offset_of!(ComplexTup, 0.0 . 1.); //~ ERROR unexpected token: `)` offset_of!(ComplexTup, 0.0. 1.); //~ ERROR unexpected token: `)` + // FIXME(fmease): Update comment. // Test for builtin too to ensure that the builtin syntax can also handle these cases // We need to put these into curly braces, otherwise only one of the // errors will be emitted and the others suppressed. - { builtin # offset_of(ComplexTup, 0.0.1.) }; //~ ERROR unexpected token: `)` - { builtin # offset_of(ComplexTup, 0 .0.1.) }; //~ ERROR unexpected token: `)` - { builtin # offset_of(ComplexTup, 0 . 0.1.) }; //~ ERROR unexpected token: `)` - { builtin # offset_of(ComplexTup, 0. 0.1.) }; //~ ERROR unexpected token: `)` - { builtin # offset_of(ComplexTup, 0.0 .1.) }; //~ ERROR unexpected token: `)` - { builtin # offset_of(ComplexTup, 0.0 . 1.) }; //~ ERROR unexpected token: `)` - { builtin # offset_of(ComplexTup, 0.0. 1.) }; //~ ERROR unexpected token: `)` + { k#offset_of(ComplexTup, 0.0.1.) }; //~ ERROR unexpected token: `)` + { k#offset_of(ComplexTup, 0 .0.1.) }; //~ ERROR unexpected token: `)` + { k#offset_of(ComplexTup, 0 . 0.1.) }; //~ ERROR unexpected token: `)` + { k#offset_of(ComplexTup, 0. 0.1.) }; //~ ERROR unexpected token: `)` + { k#offset_of(ComplexTup, 0.0 .1.) }; //~ ERROR unexpected token: `)` + { k#offset_of(ComplexTup, 0.0 . 1.) }; //~ ERROR unexpected token: `)` + { k#offset_of(ComplexTup, 0.0. 1.) }; //~ ERROR unexpected token: `)` } diff --git a/tests/ui/offset-of/offset-of-tuple.stderr b/tests/ui/offset-of/offset-of-tuple.stderr index f90f2db1c6c35..db46373449218 100644 --- a/tests/ui/offset-of/offset-of-tuple.stderr +++ b/tests/ui/offset-of/offset-of-tuple.stderr @@ -1,71 +1,71 @@ error: leading `+` is not supported - --> $DIR/offset-of-tuple.rs:12:37 + --> $DIR/offset-of-tuple.rs:13:29 | -LL | { builtin # offset_of((u8, u8), +1) }; - | ^ unexpected `+` +LL | { k#offset_of((u8, u8), +1) }; + | ^ unexpected `+` | help: try removing the `+` | -LL - { builtin # offset_of((u8, u8), +1) }; -LL + { builtin # offset_of((u8, u8), 1) }; +LL - { k#offset_of((u8, u8), +1) }; +LL + { k#offset_of((u8, u8), 1) }; | error: offset_of expects dot-separated field and variant names - --> $DIR/offset-of-tuple.rs:13:38 + --> $DIR/offset-of-tuple.rs:14:30 | -LL | { builtin # offset_of((u8, u8), 1.) }; - | ^ +LL | { k#offset_of((u8, u8), 1.) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:14:40 + --> $DIR/offset-of-tuple.rs:15:32 | -LL | { builtin # offset_of((u8, u8), 1 .) }; - | ^ +LL | { k#offset_of((u8, u8), 1 .) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:32:45 + --> $DIR/offset-of-tuple.rs:34:37 | -LL | { builtin # offset_of(ComplexTup, 0.0.1.) }; - | ^ +LL | { k#offset_of(ComplexTup, 0.0.1.) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:33:46 + --> $DIR/offset-of-tuple.rs:35:38 | -LL | { builtin # offset_of(ComplexTup, 0 .0.1.) }; - | ^ +LL | { k#offset_of(ComplexTup, 0 .0.1.) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:34:47 + --> $DIR/offset-of-tuple.rs:36:39 | -LL | { builtin # offset_of(ComplexTup, 0 . 0.1.) }; - | ^ +LL | { k#offset_of(ComplexTup, 0 . 0.1.) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:35:46 + --> $DIR/offset-of-tuple.rs:37:38 | -LL | { builtin # offset_of(ComplexTup, 0. 0.1.) }; - | ^ +LL | { k#offset_of(ComplexTup, 0. 0.1.) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:36:46 + --> $DIR/offset-of-tuple.rs:38:38 | -LL | { builtin # offset_of(ComplexTup, 0.0 .1.) }; - | ^ +LL | { k#offset_of(ComplexTup, 0.0 .1.) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:37:47 + --> $DIR/offset-of-tuple.rs:39:39 | -LL | { builtin # offset_of(ComplexTup, 0.0 . 1.) }; - | ^ +LL | { k#offset_of(ComplexTup, 0.0 . 1.) }; + | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:38:46 + --> $DIR/offset-of-tuple.rs:40:38 | -LL | { builtin # offset_of(ComplexTup, 0.0. 1.) }; - | ^ +LL | { k#offset_of(ComplexTup, 0.0. 1.) }; + | ^ error: no rules expected `+` - --> $DIR/offset-of-tuple.rs:6:26 + --> $DIR/offset-of-tuple.rs:7:26 | LL | offset_of!((u8, u8), +1); | ^ no rules expected this token in macro call @@ -75,61 +75,61 @@ note: while trying to match meta-variable `$fields:expr` = note: this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)` error: offset_of expects dot-separated field and variant names - --> $DIR/offset-of-tuple.rs:7:26 + --> $DIR/offset-of-tuple.rs:8:26 | LL | offset_of!((u8, u8), -1); | ^^ error: offset_of expects dot-separated field and variant names - --> $DIR/offset-of-tuple.rs:8:27 + --> $DIR/offset-of-tuple.rs:9:27 | LL | offset_of!((u8, u8), 1.); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:9:29 + --> $DIR/offset-of-tuple.rs:10:29 | LL | offset_of!((u8, u8), 1 .); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:21:34 + --> $DIR/offset-of-tuple.rs:22:34 | LL | offset_of!(ComplexTup, 0.0.1.); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:22:35 + --> $DIR/offset-of-tuple.rs:23:35 | LL | offset_of!(ComplexTup, 0 .0.1.); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:23:36 + --> $DIR/offset-of-tuple.rs:24:36 | LL | offset_of!(ComplexTup, 0 . 0.1.); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:24:35 + --> $DIR/offset-of-tuple.rs:25:35 | LL | offset_of!(ComplexTup, 0. 0.1.); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:25:35 + --> $DIR/offset-of-tuple.rs:26:35 | LL | offset_of!(ComplexTup, 0.0 .1.); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:26:36 + --> $DIR/offset-of-tuple.rs:27:36 | LL | offset_of!(ComplexTup, 0.0 . 1.); | ^ error: unexpected token: `)` - --> $DIR/offset-of-tuple.rs:27:35 + --> $DIR/offset-of-tuple.rs:28:35 | LL | offset_of!(ComplexTup, 0.0. 1.); | ^ diff --git a/tests/ui/unpretty/exhaustive.expanded.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout index 7eca271aeda13..38ac3eaee68c9 100644 --- a/tests/ui/unpretty/exhaustive.expanded.stdout +++ b/tests/ui/unpretty/exhaustive.expanded.stdout @@ -315,7 +315,7 @@ mod expressions { - const { builtin # offset_of(T, field) }; + const { k#offset_of(T, field) }; } /// ExprKind::MacCall fn expr_mac_call() { "..."; "..."; "..."; } From 33bb9b071bda6b057c1664bb886dc0508d9d1f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Sep 2026 13:09:06 +0200 Subject: [PATCH 50/53] Replace `builtin # type_ascribe` with `k#type_ascribe` --- compiler/rustc_ast/src/ast.rs | 2 +- .../rustc_ast_pretty/src/pprust/state/expr.rs | 2 +- compiler/rustc_parse/src/parser/expr.rs | 9 ++++- compiler/rustc_span/src/symbol.rs | 2 +- library/core/src/macros/mod.rs | 2 +- src/tools/rustfmt/tests/target/issue_6159.rs | 4 +- tests/ui/unpretty/exhaustive.expanded.stdout | 3 +- tests/ui/unpretty/exhaustive.hir.stderr | 40 +++++++++---------- tests/ui/unpretty/exhaustive.hir.stdout | 7 ++-- tests/ui/unpretty/exhaustive.rs | 3 +- 10 files changed, 42 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index ced51551a77c1..ffe9b79b4942d 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1779,7 +1779,7 @@ pub enum ExprKind { Lit(token::Lit), /// A cast (e.g., `foo as f64`). Cast(Box, Box), - /// A type ascription (e.g., `builtin # type_ascribe(42, usize)`). + /// A type ascription (e.g., `k#type_ascribe(42, usize)`). /// /// Usually not written directly in user code but /// indirectly via the macro `type_ascribe!(...)`. diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index aeb380ecd0ca1..2c6315c003396 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -497,7 +497,7 @@ impl<'a> State<'a> { self.print_type(ty); } ast::ExprKind::Type(expr, ty) => { - self.word("builtin # type_ascribe"); + self.word("k#type_ascribe"); self.popen(); let ib = self.ibox(0); self.print_expr(expr, FixupContext::default()); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e3403506423a9..111a0d4487cc5 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1387,6 +1387,8 @@ impl<'a> Parser<'a> { Ok(this.mk_expr(this.prev_token.span, ExprKind::Underscore)) } else if this.eat_forced_keyword_noexpect(kw::OffsetOf) { this.parse_expr_offset_of(lo) + } else if this.eat_forced_keyword_noexpect(kw::TypeAscribe) { + this.parse_expr_type_ascribe(lo) } else if this.token_uninterpolated_span().at_least_rust_2018() { // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. let at_async = this.check_keyword(exp!(Async)); @@ -1832,7 +1834,6 @@ impl<'a> Parser<'a> { fn parse_expr_builtin(&mut self) -> PResult<'a, Box> { self.parse_builtin(|this, lo, ident| { Ok(match ident.name { - sym::type_ascribe => Some(this.parse_expr_type_ascribe(lo)?), sym::wrap_binder => { Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Wrap)?) } @@ -1912,12 +1913,16 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields))) } - /// Built-in macro for type ascription expressions. pub(crate) fn parse_expr_type_ascribe(&mut self, lo: Span) -> PResult<'a, Box> { + self.expect(exp!(OpenParen))?; let expr = self.parse_expr()?; self.expect(exp!(Comma))?; let ty = self.parse_ty()?; + // FIXME: Odd not to include the closing paren (contrary to the leading one etc.) but + // it actually "improves" diagnostics slightly by not pointing into the wrapper macros. let span = lo.to(self.token.span); + self.expect(exp!(CloseParen))?; + self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); Ok(self.mk_expr(span, ExprKind::Type(expr, ty))) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c0c90ba4bbc44..35beb14d22b87 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -138,6 +138,7 @@ symbols! { Raw: "raw", Reuse: "reuse", Safe: "safe", + TypeAscribe: "type_ascribe", Union: "union", Yeet: "yeet", // tidy-alphabetical-end @@ -2205,7 +2206,6 @@ symbols! { ty, type_alias_enum_variants, type_alias_impl_trait, - type_ascribe, type_ascription, type_changing_struct_update, type_id, diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 9949a5cfa897e..8f4ce5400e687 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1915,7 +1915,7 @@ pub(crate) mod builtin { )] #[diagnostic::opaque] pub macro type_ascribe($expr:expr, $ty:ty) { - builtin # type_ascribe($expr, $ty) + k#type_ascribe($expr, $ty) } /// Unstable placeholder for deref patterns. diff --git a/src/tools/rustfmt/tests/target/issue_6159.rs b/src/tools/rustfmt/tests/target/issue_6159.rs index 49fd539d3ef5a..35047aadcec6b 100644 --- a/src/tools/rustfmt/tests/target/issue_6159.rs +++ b/src/tools/rustfmt/tests/target/issue_6159.rs @@ -1,3 +1,5 @@ +//@ rustfmt-edition: 2021 + fn main() { - builtin # type_ascribe(10, usize) + k#type_ascribe(10, usize) } diff --git a/tests/ui/unpretty/exhaustive.expanded.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout index 38ac3eaee68c9..a53874f8e3849 100644 --- a/tests/ui/unpretty/exhaustive.expanded.stdout +++ b/tests/ui/unpretty/exhaustive.expanded.stdout @@ -16,6 +16,7 @@ #![feature(decl_macro)] #![feature(deref_patterns)] #![feature(explicit_tail_calls)] +#![feature(forced_keywords)] #![feature(gen_blocks)] #![feature(more_qualified_paths)] #![feature(never_patterns)] @@ -131,7 +132,7 @@ mod expressions { fn expr_cast() { let expr; expr as T; expr as T; } /// ExprKind::Type - fn expr_type() { let expr; builtin # type_ascribe(expr, T); } + fn expr_type() { let expr; k#type_ascribe(expr, T); } /// ExprKind::Let fn expr_let() { diff --git a/tests/ui/unpretty/exhaustive.hir.stderr b/tests/ui/unpretty/exhaustive.hir.stderr index 5ca4b080a95e5..276bc5b5d2573 100644 --- a/tests/ui/unpretty/exhaustive.hir.stderr +++ b/tests/ui/unpretty/exhaustive.hir.stderr @@ -1,17 +1,17 @@ error[E0697]: closures cannot be static - --> $DIR/exhaustive.rs:209:9 + --> $DIR/exhaustive.rs:210:9 | LL | static || value; | ^^^^^^^^^ error[E0697]: closures cannot be static - --> $DIR/exhaustive.rs:210:9 + --> $DIR/exhaustive.rs:211:9 | LL | static move || value; | ^^^^^^^^^^^^^^ error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/exhaustive.rs:239:13 + --> $DIR/exhaustive.rs:240:13 | LL | fn expr_await() { | --------------- this is not `async` @@ -20,19 +20,19 @@ LL | fut.await; | ^^^^^ only allowed inside `async` functions and blocks error: in expressions, `_` can only be used on the left-hand side of an assignment - --> $DIR/exhaustive.rs:290:9 + --> $DIR/exhaustive.rs:291:9 | LL | _; | ^ `_` not allowed here error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:300:9 + --> $DIR/exhaustive.rs:301:9 | LL | x::(); | ^^^^^ only `Fn` traits may use parentheses error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:301:9 + --> $DIR/exhaustive.rs:302:9 | LL | x::(T, T) -> T; | ^^^^^^^^^^^^^^ only `Fn` traits may use parentheses @@ -44,31 +44,31 @@ LL + x:: -> T; | error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:302:9 + --> $DIR/exhaustive.rs:303:9 | LL | crate::() -> ()::expressions::() -> ()::expr_path; | ^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:302:26 + --> $DIR/exhaustive.rs:303:26 | LL | crate::() -> ()::expressions::() -> ()::expr_path; | ^^^^^^^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:305:9 + --> $DIR/exhaustive.rs:306:9 | LL | core::()::marker::()::PhantomData; | ^^^^^^^^ only `Fn` traits may use parentheses error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:305:19 + --> $DIR/exhaustive.rs:306:19 | LL | core::()::marker::()::PhantomData; | ^^^^^^^^^^ only `Fn` traits may use parentheses error: `yield` can only be used in `#[coroutine]` closures, or `gen` blocks - --> $DIR/exhaustive.rs:392:9 + --> $DIR/exhaustive.rs:393:9 | LL | yield; | ^^^^^ @@ -79,7 +79,7 @@ LL | #[coroutine] fn expr_yield() { | ++++++++++++ error[E0703]: invalid ABI: found `C++` - --> $DIR/exhaustive.rs:472:23 + --> $DIR/exhaustive.rs:473:23 | LL | unsafe extern "C++" {} | ^^^^^ invalid ABI @@ -87,7 +87,7 @@ LL | unsafe extern "C++" {} = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions error: `..` patterns are not allowed here - --> $DIR/exhaustive.rs:674:13 + --> $DIR/exhaustive.rs:675:13 | LL | let ..; | ^^ @@ -95,13 +95,13 @@ LL | let ..; = note: only allowed in tuple, tuple struct, and slice patterns error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/exhaustive.rs:789:16 + --> $DIR/exhaustive.rs:790:16 | LL | let _: T() -> !; | ^^^^^^^^ only `Fn` traits may use parentheses error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:803:16 + --> $DIR/exhaustive.rs:804:16 | LL | let _: impl Send; | ^^^^^^^^^ @@ -112,7 +112,7 @@ LL | let _: impl Send; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:804:16 + --> $DIR/exhaustive.rs:805:16 | LL | let _: impl Send + 'static; | ^^^^^^^^^^^^^^^^^^^ @@ -123,7 +123,7 @@ LL | let _: impl Send + 'static; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:805:16 + --> $DIR/exhaustive.rs:806:16 | LL | let _: impl 'static + Send; | ^^^^^^^^^^^^^^^^^^^ @@ -134,7 +134,7 @@ LL | let _: impl 'static + Send; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:806:16 + --> $DIR/exhaustive.rs:807:16 | LL | let _: impl ?Sized; | ^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | let _: impl ?Sized; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:807:16 + --> $DIR/exhaustive.rs:808:16 | LL | let _: impl [const] Clone; | ^^^^^^^^^^^^^^^^^^ @@ -156,7 +156,7 @@ LL | let _: impl [const] Clone; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0562]: `impl Trait` is not allowed in the type of variable bindings - --> $DIR/exhaustive.rs:808:16 + --> $DIR/exhaustive.rs:809:16 | LL | let _: impl for<'a> Send; | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index b1c956c2fe9f1..b27ed61e88e32 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -1,8 +1,9 @@ #![attr = Feature([auto_traits#0, builtin_syntax#0, const_trait_impl#0, coroutines#0, decl_macro#0, deref_patterns#0, explicit_tail_calls#0, -gen_blocks#0, more_qualified_paths#0, never_patterns#0, pattern_types#0, -pattern_type_macro#0, prelude_import#0, specialization#0, trace_macros#0, -trait_alias#0, try_blocks#0, try_blocks_heterogeneous#0, yeet_expr#0])] +forced_keywords#0, gen_blocks#0, more_qualified_paths#0, never_patterns#0, +pattern_types#0, pattern_type_macro#0, prelude_import#0, specialization#0, +trace_macros#0, trait_alias#0, try_blocks#0, try_blocks_heterogeneous#0, +yeet_expr#0])] #![attr = LintCheck([LintCheck {lint_name: LintName { tool: None, name: "incomplete_features" }, kind: Allow}, LintCheck {lint_name: LintName { tool: None, name: "unused_attributes" }, diff --git a/tests/ui/unpretty/exhaustive.rs b/tests/ui/unpretty/exhaustive.rs index 03ef63874ac0a..f4b275d551922 100644 --- a/tests/ui/unpretty/exhaustive.rs +++ b/tests/ui/unpretty/exhaustive.rs @@ -15,6 +15,7 @@ #![feature(decl_macro)] #![feature(deref_patterns)] #![feature(explicit_tail_calls)] +#![feature(forced_keywords)] #![feature(gen_blocks)] #![feature(more_qualified_paths)] #![feature(never_patterns)] @@ -144,7 +145,7 @@ mod expressions { /// ExprKind::Type fn expr_type() { let expr; - builtin # type_ascribe(expr, T); + k#type_ascribe(expr, T); } /// ExprKind::Let From 247b8c19193e50ecf769c30d86cd2f74b220b344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Sep 2026 13:23:59 +0200 Subject: [PATCH 51/53] Replace `builtin # {,un}wrap_binder` with `k#{,un}wrap_binder` --- .../rustc_ast_pretty/src/pprust/state/expr.rs | 5 +- compiler/rustc_parse/src/diagnostics.rs | 15 ----- compiler/rustc_parse/src/parser/expr.rs | 64 +++---------------- compiler/rustc_parse/src/parser/item.rs | 8 --- compiler/rustc_parse/src/parser/stmt.rs | 6 +- compiler/rustc_span/src/symbol.rs | 5 +- library/core/src/unsafe_binder.rs | 18 ++---- tests/ui/parser/builtin-syntax.rs | 9 --- tests/ui/parser/builtin-syntax.stderr | 14 ---- 9 files changed, 19 insertions(+), 125 deletions(-) delete mode 100644 tests/ui/parser/builtin-syntax.rs delete mode 100644 tests/ui/parser/builtin-syntax.stderr diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 2c6315c003396..4a44847cede17 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -855,10 +855,9 @@ impl<'a> State<'a> { self.print_block_with_attrs(blk, attrs, cb, ib) } ast::ExprKind::UnsafeBinderCast(kind, expr, ty) => { - self.word("builtin # "); match kind { - ast::UnsafeBinderCastKind::Wrap => self.word("wrap_binder"), - ast::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder"), + ast::UnsafeBinderCastKind::Wrap => self.word("k#wrap_binder"), + ast::UnsafeBinderCastKind::Unwrap => self.word("k#unwrap_binder"), } self.popen(); let ib = self.ibox(0); diff --git a/compiler/rustc_parse/src/diagnostics.rs b/compiler/rustc_parse/src/diagnostics.rs index ab220cc56a9e0..72947b1c576c8 100644 --- a/compiler/rustc_parse/src/diagnostics.rs +++ b/compiler/rustc_parse/src/diagnostics.rs @@ -4079,21 +4079,6 @@ impl IntoDiagArg for Case { } } -#[derive(Diagnostic)] -#[diag("unknown `builtin #` construct `{$name}`")] -pub(crate) struct UnknownBuiltinConstruct { - #[primary_span] - pub span: Span, - pub name: Ident, -} - -#[derive(Diagnostic)] -#[diag("expected identifier after `builtin #`")] -pub(crate) struct ExpectedBuiltinIdent { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag("static items may not have generic parameters")] pub(crate) struct StaticWithGenerics { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 111a0d4487cc5..454ef26dc74c2 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1315,8 +1315,6 @@ impl<'a> Parser<'a> { }) } else if this.check(exp!(OpenBracket)) { this.parse_expr_array_or_repeat(exp!(CloseBracket)) - } else if this.is_builtin() { - this.parse_expr_builtin() } else if this.check_path() { this.parse_expr_path_start() } else if this.token.is_keyword(kw::Move) @@ -1389,6 +1387,10 @@ impl<'a> Parser<'a> { this.parse_expr_offset_of(lo) } else if this.eat_forced_keyword_noexpect(kw::TypeAscribe) { this.parse_expr_type_ascribe(lo) + } else if this.eat_forced_keyword_noexpect(kw::WrapBinder) { + this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Wrap) + } else if this.eat_forced_keyword_noexpect(kw::UnwrapBinder) { + this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Unwrap) } else if this.token_uninterpolated_span().at_least_rust_2018() { // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. let at_async = this.check_keyword(exp!(Async)); @@ -1830,54 +1832,6 @@ impl<'a> Parser<'a> { self.maybe_recover_from_bad_qpath(expr) } - /// Parse `builtin # ident(args,*)`. - fn parse_expr_builtin(&mut self) -> PResult<'a, Box> { - self.parse_builtin(|this, lo, ident| { - Ok(match ident.name { - sym::wrap_binder => { - Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Wrap)?) - } - sym::unwrap_binder => { - Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Unwrap)?) - } - _ => None, - }) - }) - } - - pub(crate) fn parse_builtin( - &mut self, - parse: impl FnOnce(&mut Parser<'a>, Span, Ident) -> PResult<'a, Option>, - ) -> PResult<'a, T> { - let lo = self.token.span; - - self.bump(); // `builtin` - self.bump(); // `#` - - let Some((ident, IdentKind::Normal)) = self.token.ident() else { - let err = self - .dcx() - .create_err(crate::diagnostics::ExpectedBuiltinIdent { span: self.token.span }); - return Err(err); - }; - self.psess.gated_spans.gate(sym::builtin_syntax, ident.span); - self.bump(); - - self.expect(exp!(OpenParen))?; - let ret = if let Some(res) = parse(self, lo, ident)? { - Ok(res) - } else { - let err = self.dcx().create_err(crate::diagnostics::UnknownBuiltinConstruct { - span: lo.to(ident.span), - name: ident, - }); - return Err(err); - }; - self.expect(exp!(CloseParen))?; - - ret - } - pub(crate) fn parse_expr_offset_of(&mut self, lo: Span) -> PResult<'a, Box> { self.expect(exp!(OpenParen))?; @@ -1931,9 +1885,15 @@ impl<'a> Parser<'a> { lo: Span, kind: UnsafeBinderCastKind, ) -> PResult<'a, Box> { + self.expect(exp!(OpenParen))?; + let expr = self.parse_expr()?; let ty = if self.eat(exp!(Comma)) { Some(self.parse_ty()?) } else { None }; + // FIXME: Odd not to include the closing paren (contrary to the leading one etc.) but + // it actually "improves" diagnostics slightly by not pointing into the wrapper macros. let span = lo.to(self.token.span); + self.expect(exp!(CloseParen))?; + self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); Ok(self.mk_expr(span, ExprKind::UnsafeBinderCast(kind, expr, ty))) } @@ -3478,10 +3438,6 @@ impl<'a> Parser<'a> { Ok(expr) } - pub(crate) fn is_builtin(&self) -> bool { - self.token.is_keyword(kw::Builtin) && self.look_ahead(1, |t| *t == token::Pound) - } - /// Parses a `try {...}` or `try bikeshed Ty {...}` expression (`try` token already eaten). fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, Box> { let annotation = diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index fb8d9ce303865..ba142036946a2 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -354,9 +354,6 @@ impl<'a> Parser<'a> { // UNION ITEM self.bump(); // `union` self.parse_item_union()? - } else if self.is_builtin() { - // BUILTIN# ITEM - return self.parse_item_builtin(); } else if self.eat_keyword_case(exp!(Macro), case) { // MACROS 2.0 ITEM self.parse_item_decl_macro(lo)? @@ -599,11 +596,6 @@ impl<'a> Parser<'a> { if let Some(err) = err { Err(self.dcx().create_err(err)) } else { Ok(()) } } - fn parse_item_builtin(&mut self) -> PResult<'a, Option> { - // To be expanded - Ok(None) - } - /// Parses an item macro, e.g., `item!();`. fn parse_item_macro(&mut self, vis: &Visibility) -> PResult<'a, MacCall> { let path = self.parse_path(PathStyle::Mod)?; // `foo::bar` diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 49bcdbbe8fa57..df2df24854ee1 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -126,11 +126,7 @@ impl<'a> Parser<'a> { diagnostics::InvalidVariableDeclarationSub::UseLetNotVar, force_collect, )? - } else if self.check_path() - && !self.token.is_qpath_start() - && !self.is_path_start_item() - && !self.is_builtin() - { + } else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() { // We have avoided contextual keywords like `union`, items with `crate` visibility, // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something // that starts like a path (1 token), but it fact not a path. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 35beb14d22b87..64261ed080743 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -125,7 +125,6 @@ symbols! { // Matching predicates: `is_weak` // tidy-alphabetical-start Auto: "auto", - Builtin: "builtin", Catch: "catch", ContractEnsures: "contract_ensures", ContractRequires: "contract_requires", @@ -140,6 +139,8 @@ symbols! { Safe: "safe", TypeAscribe: "type_ascribe", Union: "union", + UnwrapBinder: "unwrap_binder", + WrapBinder: "wrap_binder", Yeet: "yeet", // tidy-alphabetical-end } @@ -2334,7 +2335,6 @@ symbols! { unwind_attributes, unwind_safe_trait, unwrap, - unwrap_binder, unwrap_or, update, use_cloned, @@ -2420,7 +2420,6 @@ symbols! { windows, windows_subsystem, with_negative_coherence, - wrap_binder, wrapping_add, wrapping_div, wrapping_mul, diff --git a/library/core/src/unsafe_binder.rs b/library/core/src/unsafe_binder.rs index 3d2a04e95f078..923ee76c11b44 100644 --- a/library/core/src/unsafe_binder.rs +++ b/library/core/src/unsafe_binder.rs @@ -4,24 +4,14 @@ #[allow_internal_unstable(builtin_syntax)] #[unstable(feature = "unsafe_binders", issue = "130516")] #[diagnostic::opaque] -pub macro unwrap_binder { - ($expr:expr) => { - builtin # unwrap_binder ( $expr ) - }, - ($expr:expr ; $ty:ty) => { - builtin # unwrap_binder ( $expr, $ty ) - }, +pub macro unwrap_binder($expr:expr $( ; $ty:ty )?) { + k#unwrap_binder($expr $( , $ty )?) } /// Wrap a type into an unsafe binder. #[allow_internal_unstable(builtin_syntax)] #[unstable(feature = "unsafe_binders", issue = "130516")] #[diagnostic::opaque] -pub macro wrap_binder { - ($expr:expr) => { - builtin # wrap_binder ( $expr ) - }, - ($expr:expr ; $ty:ty) => { - builtin # wrap_binder ( $expr, $ty ) - }, +pub macro wrap_binder($expr:expr $( ; $ty:ty )?) { + k#wrap_binder($expr $( , $ty )?) } diff --git a/tests/ui/parser/builtin-syntax.rs b/tests/ui/parser/builtin-syntax.rs deleted file mode 100644 index 897dab8ec50ae..0000000000000 --- a/tests/ui/parser/builtin-syntax.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![feature(builtin_syntax)] - -fn main() { - builtin # foobar(); //~ ERROR unknown `builtin #` construct -} - -fn not_identifier() { - builtin # {}(); //~ ERROR expected identifier after -} diff --git a/tests/ui/parser/builtin-syntax.stderr b/tests/ui/parser/builtin-syntax.stderr deleted file mode 100644 index ee3764a62216a..0000000000000 --- a/tests/ui/parser/builtin-syntax.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: unknown `builtin #` construct `foobar` - --> $DIR/builtin-syntax.rs:4:5 - | -LL | builtin # foobar(); - | ^^^^^^^^^^^^^^^^ - -error: expected identifier after `builtin #` - --> $DIR/builtin-syntax.rs:8:15 - | -LL | builtin # {}(); - | ^ - -error: aborting due to 2 previous errors - From c9a27971570b73d1cc4127518d913ad7a81bfd65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Sep 2026 14:12:44 +0200 Subject: [PATCH 52/53] Rename feature `builtin_syntax` to `internal_syntax` --- compiler/rustc_ast/src/token.rs | 4 ++-- compiler/rustc_ast_passes/src/feature_gate.rs | 2 +- compiler/rustc_ast_pretty/src/pprust/state.rs | 2 +- compiler/rustc_ast_pretty/src/pprust/state/expr.rs | 4 ++-- compiler/rustc_ast_pretty/src/pprust/state/item.rs | 2 +- compiler/rustc_feature/src/unstable.rs | 4 ++-- compiler/rustc_parse/src/parser/expr.rs | 6 +++--- compiler/rustc_parse/src/parser/pat.rs | 2 +- compiler/rustc_parse/src/parser/ty.rs | 2 +- compiler/rustc_span/src/symbol.rs | 2 +- library/core/src/field.rs | 2 +- library/core/src/macros/mod.rs | 4 ++-- library/core/src/mem/mod.rs | 2 +- library/core/src/unsafe_binder.rs | 4 ++-- ...te-builtin_syntax.rs => feature-gate-internal_syntax.rs} | 2 +- ...in_syntax.stderr => feature-gate-internal_syntax.stderr} | 6 +++--- tests/ui/offset-of/offset-of-tuple.rs | 2 +- tests/ui/unpretty/exhaustive.expanded.stdout | 2 +- tests/ui/unpretty/exhaustive.hir.stdout | 2 +- tests/ui/unpretty/exhaustive.rs | 2 +- 20 files changed, 29 insertions(+), 29 deletions(-) rename tests/ui/feature-gates/{feature-gate-builtin_syntax.rs => feature-gate-internal_syntax.rs} (60%) rename tests/ui/feature-gates/{feature-gate-builtin_syntax.stderr => feature-gate-internal_syntax.stderr} (68%) diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 820cd9bd99644..4ec5fe2a8bd84 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -313,7 +313,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, kind: IdentKind) -> bool { // WARNING: Take care when modifying this function! It will change the stable(!) set of // tokens that are allowed to match an `expr` nonterminal which is user observable. - // NOTE: We don't care about forced keywords that are gated behind `builtin_syntax`. + // NOTE: We don't care about forced keywords that are gated behind `internal_syntax`. let ident_token = Token::new(Ident(name, kind), span); @@ -352,7 +352,7 @@ fn ident_can_begin_type(name: Symbol, span: Span, kind: IdentKind) -> bool { // WARNING: Take care when modifying this function! It will change the stable(!) set of // tokens that are allowed to match an `ty` nonterminal which is user observable. - // NOTE: We don't care about forced keywords that are gated behind `builtin_syntax`. + // NOTE: We don't care about forced keywords that are gated behind `internal_syntax`. let ident_token = Token::new(Ident(name, kind), span); diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index bc0604cf96e95..862d4736ca33c 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -431,7 +431,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { // tidy-alphabetical-start gate_all!(async_for_loop, "`for await` loops are experimental"); - gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); gate_all!(const_block_items, "const block items are experimental"); gate_all!(const_closures, "const closures are experimental"); gate_all!(const_trait_impl, "const trait impls are experimental"); @@ -452,6 +451,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(global_registration, "global registration is experimental"); gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards"); gate_all!(impl_restriction, "`impl` restrictions are experimental"); + gate_all!(internal_syntax, "this syntax is internal"); gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental"); gate_all!(move_expr, "`move(expr)` syntax is experimental"); gate_all!(mut_ref, "mutable by-reference bindings are experimental"); diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 955bca61f7960..a5dbaf81376c1 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -2012,7 +2012,7 @@ impl<'a> State<'a> { self.pclose(); } PatKind::Deref(inner) => { - // FIXME(deref_patterns, builtin_syntax, forced_keywords, clippy): + // FIXME(deref_patterns, internal_syntax, forced_keywords, clippy): // This should render `k#deref(...)` but unfortunately Clippy uses `pat_to_string` // for the suggestions of its lint `unnested_or_patterns`. Smh fix Clippy to not // rely on AST pretty printing `deref!(…)` for `Deref` patterns and please don't diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 4a44847cede17..51e3988577827 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -777,12 +777,12 @@ impl<'a> State<'a> { self.print_expr(result, fixup.rightmost_subexpression()); } ast::ExprKind::InlineAsm(a) => { - // FIXME: Print `builtin # asm` once macro `asm` uses `builtin_syntax`. + // FIXME: Print `k#asm` once macro `asm` uses `internal_syntax`. self.word(format!("{}!", a.asm_macro.macro_name())); self.print_inline_asm(a); } ast::ExprKind::FormatArgs(fmt) => { - // FIXME: Print `builtin # format_args` once macro `format_args` uses `builtin_syntax`. + // FIXME: Print `k#format_args` once macro `format_args` uses `internal_syntax`. self.word("format_args!"); self.popen(); let ib = self.ibox(0); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 6ce34cecc7e97..84d620b9b5250 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -288,7 +288,7 @@ impl<'a> State<'a> { self.bclose(item.span, empty, cb); } ast::ItemKind::GlobalAsm(asm) => { - // FIXME: Print `builtin # global_asm` once macro `global_asm` uses `builtin_syntax`. + // FIXME: Print `k#global_asm` once macro `global_asm` uses `internal_syntax`. let (cb, ib) = self.head(visibility_qualified(&item.vis, "global_asm!")); self.print_inline_asm(asm); self.word(";"); diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 468a13ad4fd61..cbaf7cba40ba7 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -301,8 +301,6 @@ declare_features! ( /// Allows features specific to auto traits. /// Renamed from `optin_builtin_traits`. (unstable, auto_traits, "1.50.0", Some(13231)), - /// Allows builtin # foo() syntax - (internal, builtin_syntax, "1.71.0", Some(110680)), /// Allows `#[doc(notable_trait)]`. /// Renamed from `doc_spotlight`. (unstable, doc_notable_trait, "1.52.0", Some(45040)), @@ -310,6 +308,8 @@ declare_features! ( (unstable, dropck_eyepatch, "1.10.0", Some(34761)), /// Allows using the `#[fundamental]` attribute. (unstable, fundamental, "1.0.0", Some(29635)), + /// Allows internal syntax. + (internal, internal_syntax, "CURRENT_RUSTC_VERSION", Some(110680)), /// Allows using `#[link_name="llvm.*"]`. (internal, link_llvm_intrinsics, "1.0.0", Some(29602)), /// Allows using the `#[linkage = ".."]` attribute. diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 454ef26dc74c2..fd5886e0cec90 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1862,7 +1862,7 @@ impl<'a> Parser<'a> { let span = lo.to(self.token.span); self.expect(exp!(CloseParen))?; - self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + self.psess.gated_spans.gate(sym::internal_syntax, lo.to(self.token.span)); Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields))) } @@ -1876,7 +1876,7 @@ impl<'a> Parser<'a> { // it actually "improves" diagnostics slightly by not pointing into the wrapper macros. let span = lo.to(self.token.span); self.expect(exp!(CloseParen))?; - self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + self.psess.gated_spans.gate(sym::internal_syntax, lo.to(self.token.span)); Ok(self.mk_expr(span, ExprKind::Type(expr, ty))) } @@ -1893,7 +1893,7 @@ impl<'a> Parser<'a> { // it actually "improves" diagnostics slightly by not pointing into the wrapper macros. let span = lo.to(self.token.span); self.expect(exp!(CloseParen))?; - self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + self.psess.gated_spans.gate(sym::internal_syntax, lo.to(self.token.span)); Ok(self.mk_expr(span, ExprKind::UnsafeBinderCast(kind, expr, ty))) } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 042da455da182..422e3cbfd082e 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -810,7 +810,7 @@ impl<'a> Parser<'a> { CommaRecoveryMode::LikelyTuple, )?)); self.expect(exp!(CloseParen))?; - self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + self.psess.gated_spans.gate(sym::internal_syntax, lo.to(self.token.span)); pat } // Don't eagerly error on semantically invalid tokens when matching diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 82f22b59a2060..b66d7310dbea5 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -856,7 +856,7 @@ impl<'a> Parser<'a> { let span = self.token.span; self.expect(exp!(CloseParen))?; - self.psess.gated_spans.gate(sym::builtin_syntax, lo.to(self.token.span)); + self.psess.gated_spans.gate(sym::internal_syntax, lo.to(self.token.span)); match *fields { [] => Err(self diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 64261ed080743..d7bdc396269c4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -585,7 +585,6 @@ symbols! { bridge, bswap, built, - builtin_syntax, bundle, c_dash_variadic, c_str_literals, @@ -1177,6 +1176,7 @@ symbols! { integral, internal_eq_trait_method_impls, internal_features, + internal_syntax, interrupt, into_async_iter_into_iter, into_future, diff --git a/library/core/src/field.rs b/library/core/src/field.rs index 07c8c991bac43..b17a78846ce2a 100644 --- a/library/core/src/field.rs +++ b/library/core/src/field.rs @@ -124,7 +124,7 @@ impl Ord /// The container type may be a tuple, `struct`, `union` or `enum`. In the case of an enum, the /// variant must also be specified. Only a single field is supported. #[unstable(feature = "field_projections", issue = "145383")] -#[allow_internal_unstable(field_representing_type_raw, builtin_syntax)] +#[allow_internal_unstable(field_representing_type_raw, internal_syntax)] #[diagnostic::on_unmatched_args( note = "this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)`" )] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 8f4ce5400e687..789119fb20052 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1907,7 +1907,7 @@ pub(crate) mod builtin { } /// Unstable placeholder for type ascription. - #[allow_internal_unstable(builtin_syntax)] + #[allow_internal_unstable(internal_syntax)] #[unstable( feature = "type_ascription", issue = "101728", @@ -1919,7 +1919,7 @@ pub(crate) mod builtin { } /// Unstable placeholder for deref patterns. - #[allow_internal_unstable(builtin_syntax)] + #[allow_internal_unstable(internal_syntax)] #[unstable( feature = "deref_patterns", issue = "87121", diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index ba91c0c8f27cc..39b07963fb9a9 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1664,7 +1664,7 @@ impl SizedTypeProperties for T {} note = "this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)`" )] #[doc(alias = "memoffset")] -#[allow_internal_unstable(core_intrinsics, builtin_syntax)] +#[allow_internal_unstable(core_intrinsics, internal_syntax)] #[diagnostic::opaque] pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { const { k#offset_of($Container, $($fields)+) } diff --git a/library/core/src/unsafe_binder.rs b/library/core/src/unsafe_binder.rs index 923ee76c11b44..bb7144fc5dddd 100644 --- a/library/core/src/unsafe_binder.rs +++ b/library/core/src/unsafe_binder.rs @@ -1,7 +1,7 @@ //! Operators used to turn types into unsafe binders and back. /// Unwrap an unsafe binder into its underlying type. -#[allow_internal_unstable(builtin_syntax)] +#[allow_internal_unstable(internal_syntax)] #[unstable(feature = "unsafe_binders", issue = "130516")] #[diagnostic::opaque] pub macro unwrap_binder($expr:expr $( ; $ty:ty )?) { @@ -9,7 +9,7 @@ pub macro unwrap_binder($expr:expr $( ; $ty:ty )?) { } /// Wrap a type into an unsafe binder. -#[allow_internal_unstable(builtin_syntax)] +#[allow_internal_unstable(internal_syntax)] #[unstable(feature = "unsafe_binders", issue = "130516")] #[diagnostic::opaque] pub macro wrap_binder($expr:expr $( ; $ty:ty )?) { diff --git a/tests/ui/feature-gates/feature-gate-builtin_syntax.rs b/tests/ui/feature-gates/feature-gate-internal_syntax.rs similarity index 60% rename from tests/ui/feature-gates/feature-gate-builtin_syntax.rs rename to tests/ui/feature-gates/feature-gate-internal_syntax.rs index e36ffe1503956..5b4293d900923 100644 --- a/tests/ui/feature-gates/feature-gate-builtin_syntax.rs +++ b/tests/ui/feature-gates/feature-gate-internal_syntax.rs @@ -6,5 +6,5 @@ struct Foo { w: u8, } fn main() { - k#offset_of(Foo, v); //~ ERROR `builtin #` syntax is unstable + k#offset_of(Foo, v); //~ ERROR this syntax is internal } diff --git a/tests/ui/feature-gates/feature-gate-builtin_syntax.stderr b/tests/ui/feature-gates/feature-gate-internal_syntax.stderr similarity index 68% rename from tests/ui/feature-gates/feature-gate-builtin_syntax.stderr rename to tests/ui/feature-gates/feature-gate-internal_syntax.stderr index 59a39df794c44..9db1a51e0d999 100644 --- a/tests/ui/feature-gates/feature-gate-builtin_syntax.stderr +++ b/tests/ui/feature-gates/feature-gate-internal_syntax.stderr @@ -1,11 +1,11 @@ -error[E0658]: `builtin #` syntax is unstable - --> $DIR/feature-gate-builtin_syntax.rs:9:5 +error[E0658]: this syntax is internal + --> $DIR/feature-gate-internal_syntax.rs:9:5 | LL | k#offset_of(Foo, v); | ^^^^^^^^^^^^^^^^^^^^ | = note: see issue #110680 for more information - = help: add `#![feature(builtin_syntax)]` to the crate attributes to enable + = help: add `#![feature(internal_syntax)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 1 previous error diff --git a/tests/ui/offset-of/offset-of-tuple.rs b/tests/ui/offset-of/offset-of-tuple.rs index fdb571bc6086f..be668d9e08946 100644 --- a/tests/ui/offset-of/offset-of-tuple.rs +++ b/tests/ui/offset-of/offset-of-tuple.rs @@ -1,5 +1,5 @@ //@ edition: 2021.. -#![feature(forced_keywords, builtin_syntax)] +#![feature(forced_keywords, internal_syntax)] use std::mem::offset_of; diff --git a/tests/ui/unpretty/exhaustive.expanded.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout index a53874f8e3849..3551d40dffbfc 100644 --- a/tests/ui/unpretty/exhaustive.expanded.stdout +++ b/tests/ui/unpretty/exhaustive.expanded.stdout @@ -10,7 +10,7 @@ // errors that only occur once we get past the AST. #![feature(auto_traits)] -#![feature(builtin_syntax)] +#![feature(internal_syntax)] #![feature(const_trait_impl)] #![feature(coroutines)] #![feature(decl_macro)] diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index b27ed61e88e32..6af4d3b5b81de 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -1,4 +1,4 @@ -#![attr = Feature([auto_traits#0, builtin_syntax#0, const_trait_impl#0, +#![attr = Feature([auto_traits#0, internal_syntax#0, const_trait_impl#0, coroutines#0, decl_macro#0, deref_patterns#0, explicit_tail_calls#0, forced_keywords#0, gen_blocks#0, more_qualified_paths#0, never_patterns#0, pattern_types#0, pattern_type_macro#0, prelude_import#0, specialization#0, diff --git a/tests/ui/unpretty/exhaustive.rs b/tests/ui/unpretty/exhaustive.rs index f4b275d551922..15f684fc3dd72 100644 --- a/tests/ui/unpretty/exhaustive.rs +++ b/tests/ui/unpretty/exhaustive.rs @@ -9,7 +9,7 @@ // errors that only occur once we get past the AST. #![feature(auto_traits)] -#![feature(builtin_syntax)] +#![feature(internal_syntax)] #![feature(const_trait_impl)] #![feature(coroutines)] #![feature(decl_macro)] From 6da7b55abe7e6f00603135205fce16cdcaaaa56e Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Tue, 3 Nov 2026 21:54:56 +0800 Subject: [PATCH 53/53] test(rustfmt): specify edition 2021 for test involving `forced_keywords` This is failing when we combine: 1. Actually checking exit code for `dont_emit_ICE`, and 2. rust-lang/rust#162232 removing `builtin_syntax` `builtin #` (all editions) in favor of keyword prefixes `k#` as part of `forced_keywords`. Cause is simple: 1. `dont_emit_ICE` checks exit code. 2. One of the listed tests, `tests/target/issue_6159.rs` was previously using `builtin #` syntax which is parsed on all editions, including default 2015 edition. 3. rust-lang/rust#162232 switched that test over to use `k#` reserved keyword syntax which is only Edition 2021+. Note the UI test actually get `//@ rustfmt-edition: 2021`. 4. `dont_emit_ICE` as an integration test does not set `--edition` explicitly, so Edition 2015 is used. 5. Now `issue_6159.rs` will not ICE but will fail to parse since it's on Edition 2015 not Edition 2021+. To fix it, just run rustfmt with `--edition=2021` for `issue_6159.rs` in `dont_emit_ICE` as well. Relevant links: - `builtin_syntax`: - MCP for switching to Edition 2021+ `k#` reserved keyword prefixes: - Tracking issue for `forced_keywords`: - Impl of `forced_keywords`: https://github.com/rust-lang/rust/pull/161775 - Impl of MCP that also modified rustfmt tests to use `forced_keywords`: . --- src/tools/rustfmt/tests/rustfmt/main.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/tools/rustfmt/tests/rustfmt/main.rs b/src/tools/rustfmt/tests/rustfmt/main.rs index 04ef79db920e3..c2e0c544c2b9c 100644 --- a/src/tools/rustfmt/tests/rustfmt/main.rs +++ b/src/tools/rustfmt/tests/rustfmt/main.rs @@ -359,18 +359,18 @@ fn rustfmt_emits_error_on_line_overflow_true() { #[test] #[allow(non_snake_case)] fn dont_emit_ICE() { - let files = [ - "tests/target/issue_5728.rs", - "tests/target/issue_5729.rs", - "tests/target/issue-5885.rs", - "tests/target/issue_6082.rs", - "tests/target/issue_6069.rs", - "tests/target/issue-6105.rs", + let file_args_pairs = [ + ("tests/target/issue_5728.rs", vec![]), + ("tests/target/issue_5729.rs", vec![]), + ("tests/target/issue-5885.rs", vec![]), + ("tests/target/issue_6082.rs", vec![]), + ("tests/target/issue_6069.rs", vec![]), + ("tests/target/issue-6105.rs", vec!["--edition=2021"]), ]; let panic_re = regex::Regex::new("thread.*panicked").unwrap(); - for file in files { - let args = [file]; + for (file, mut args) in file_args_pairs { + args.push(file); let (_stdout, stderr, exit_code) = rustfmt(&args);