From 5fbbf4763a780f7512c4949360311e634b6a3a86 Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Thu, 15 Oct 2026 07:11:17 -0400 Subject: [PATCH 01/72] std: Use getexecpath() for current_exe() on OpenBSD OpenBSD added getexecpath(3) to libc, which returns an already-canonicalized executable path directly. Replace the sysctl(KERN_PROC_ARGV)-based implementation. This was the last user of div_exact in std, so drop the exact_div feature gate. --- library/std/src/lib.rs | 1 - library/std/src/sys/paths/unix.rs | 35 ++++++------------------------- 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 0d0d9e0759d82..aa51e9e6b06be 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -287,7 +287,6 @@ #![feature(doc_masked)] #![feature(doc_notable_trait)] #![feature(dropck_eyepatch)] -#![feature(exact_div)] #![feature(f16)] #![feature(f128)] #![feature(ffi_const)] diff --git a/library/std/src/sys/paths/unix.rs b/library/std/src/sys/paths/unix.rs index cee799c36c79b..756c5ec26f127 100644 --- a/library/std/src/sys/paths/unix.rs +++ b/library/std/src/sys/paths/unix.rs @@ -253,37 +253,14 @@ pub fn current_exe() -> io::Result { #[cfg(target_os = "openbsd")] pub fn current_exe() -> io::Result { + let mut path = vec![0u8; libc::PATH_MAX as usize]; unsafe { - let mut mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, libc::getpid(), libc::KERN_PROC_ARGV]; - let mib = mib.as_mut_ptr(); - - // Determine the required size (in bytes) for the argument array ... - let mut argv_size = 0; - cvt(libc::sysctl(mib, 4, ptr::null_mut(), &mut argv_size, ptr::null_mut(), 0))?; - - // ... allocate a buffer for it ... - let argc = argv_size.div_exact(size_of::<*const libc::c_char>()).unwrap(); - let mut argv = Vec::<*const libc::c_char>::with_capacity(argc); - - // ... and retrieve the argument array. - cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _, &mut argv_size, ptr::null_mut(), 0))?; - let argc = argv_size.div_exact(size_of::<*const libc::c_char>()).unwrap(); - argv.set_len(argc); - - if argv[0].is_null() { - return Err(io::const_error!(io::ErrorKind::Uncategorized, "no current exe available")); - } - let argv0 = CStr::from_ptr(argv[0]).to_bytes(); - if argv0.iter().any(|b| *b == b'/') { - // The program name is path-like, so try to canonicalize it. - crate::fs::canonicalize(OsStr::from_bytes(argv0)) - } else { - // The program was probably found in the PATH. Instead of trying to - // find it again (which might not succeed if PATH has changed), just - // return the program name – this function is best-effort anyway. - Ok(PathBuf::from(OsStr::from_bytes(argv0))) - } + cvt(libc::getexecpath(path.as_mut_ptr().cast(), path.len()))?; + // SAFETY: getexecpath(3) NUL-terminates the buffer on success. + let len = CStr::from_ptr(path.as_ptr().cast()).count_bytes(); + path.truncate(len); } + Ok(PathBuf::from(OsString::from_vec(path))) } #[cfg(any( From 05a72d20b0c6b68c279f1a0393a23f71c93bbe4c Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Fri, 16 Oct 2026 18:45:19 +0200 Subject: [PATCH 02/72] 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 03/72] 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 04/72] 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 05/72] 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 06/72] 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 07/72] 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 08/72] 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 09/72] 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 10/72] 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 11/72] 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 12/72] 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 13/72] 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 14/72] 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 15/72] 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 16/72] 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 17/72] 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 18/72] 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 19/72] 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 20/72] 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 21/72] 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 22/72] 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 23/72] 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 24/72] 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 25/72] 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 26/72] 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 27/72] 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 28/72] 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 29/72] 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 30/72] 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 31/72] 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 32/72] 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 33/72] 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 34/72] 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 35/72] 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 36/72] 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 37/72] 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 38/72] 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 39/72] 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 09026e0fcc8e3c31697db6ce158d8ff8b7df52d0 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 29 Oct 2026 09:49:00 +0900 Subject: [PATCH 40/72] avoid building a region map when erasing bound regions --- compiler/rustc_middle/src/ty/fold.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 3d560a9f18815..398633cb3311c 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -141,7 +141,7 @@ impl<'tcx> TyCtxt<'tcx> { where T: TypeFoldable>, { - self.instantiate_bound_regions(value, |_| self.lifetimes.re_erased).0 + self.instantiate_bound_regions_uncached(value, |_| self.lifetimes.re_erased) } /// Anonymize all bound variables in `value`, this is mostly used to improve caching. From b7e097e39fee54af413a70c44e81e7d8ed53a9f0 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 2 Nov 2026 09:56:36 +0800 Subject: [PATCH 41/72] 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 42/72] 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 380fd0dd7e58315b01b93ace5aa6f6288560c441 Mon Sep 17 00:00:00 2001 From: Evgenii Zheltonozhskii Date: Mon, 2 Nov 2026 12:31:29 +0200 Subject: [PATCH 43/72] Reuse CanonicalVarKinds if no new variables were added --- .../src/canonical/canonicalizer.rs | 77 ++++++++++++------- compiler/rustc_type_ir/src/canonical.rs | 2 +- 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index 569fbd41e1b22..6b159074493f9 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -26,13 +26,15 @@ const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits( .unwrap(); #[derive(Debug, Clone, Copy)] -enum CanonicalizeInputKind { +enum CanonicalizeInputKind { /// When canonicalizing the `param_env`, we keep `'static` as merging /// trait candidates relies on it when deciding whether a where-bound /// is trivial. ParamEnv, /// When canonicalizing predicates, we don't keep `'static`. - Predicate, + /// `param_env_var_kinds` caches the interned variable kinds to be + /// reused if there are no new vars. + Predicate { param_env_var_kinds: Option }, } /// Whether we're canonicalizing a query input or the query response. @@ -41,8 +43,8 @@ enum CanonicalizeInputKind { /// while canonicalizing the response happens in the context of the /// query. #[derive(Debug, Clone, Copy)] -enum CanonicalizeMode { - Input(CanonicalizeInputKind), +enum CanonicalizeMode { + Input(CanonicalizeInputKind), /// FIXME: We currently return region constraints referring to /// placeholders and inference variables from a binder instantiated /// inside of the query. @@ -68,14 +70,14 @@ pub(super) struct Canonicalizer<'a, D: SolverDelegate, I: Interner delegate: &'a D, // Immutable field. - canonicalize_mode: CanonicalizeMode, + canonicalize_mode: CanonicalizeMode, // Mutable fields. state: Box>, } impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { - fn new(delegate: &'a D, canonicalize_mode: CanonicalizeMode) -> Self { + fn new(delegate: &'a D, canonicalize_mode: CanonicalizeMode) -> Self { Canonicalizer { delegate, canonicalize_mode, state: delegate.obtain_canonicalizer_state() } } @@ -105,15 +107,18 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { if !param_env.has_type_flags(NEEDS_CANONICAL) { let rest_canonicalizer = Canonicalizer::new( delegate, - CanonicalizeMode::Input(CanonicalizeInputKind::Predicate), + CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { + param_env_var_kinds: None, + }), ); return (param_env, rest_canonicalizer); } // Do the `env` canonicalization, and then convert the canonicalizer to `rest` form for - // subsequent use. - let do_env_and_make_rest = || { + // subsequent use, also returning the optionally interned (i.e., uniquely identified) + // canonicalized variable kind list in case cache is used, so it can also be cached. + let do_env_and_make_rest = |intern_var_kinds: bool| { let mut env_canonicalizer = Canonicalizer::new( delegate, CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv), @@ -133,12 +138,17 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { // // We clear the cache rather than deleting it or replacing it with an empty cache. This // lets the allocated capacity be reused later. - let mut rest_canonicalizer = env_canonicalizer; - rest_canonicalizer.canonicalize_mode = - CanonicalizeMode::Input(CanonicalizeInputKind::Predicate); + let param_env_var_kinds = intern_var_kinds + .then(|| delegate.cx().mk_canonical_var_kinds(&env_canonicalizer.state.var_kinds)); + let mut rest_canonicalizer = Canonicalizer { + canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { + param_env_var_kinds, + }), + ..env_canonicalizer + }; rest_canonicalizer.state.cache.clear(); - (param_env, rest_canonicalizer) + (param_env, rest_canonicalizer, param_env_var_kinds) }; // Check whether we can use the global cache for this param_env. As we only use @@ -153,11 +163,11 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { Entry::Vacant(e) => { // Cache miss. Do `env` canonicalization and get `rest_canonicalizer`, and // fill in the cache entry. - let (param_env, rest_canonicalizer) = do_env_and_make_rest(); + let (param_env, rest_canonicalizer, var_kinds) = do_env_and_make_rest(true); e.insert(CanonicalParamEnvCacheEntry { param_env, variables: rest_canonicalizer.state.variables.clone(), - var_kinds: rest_canonicalizer.state.var_kinds.clone(), + var_kinds: var_kinds.unwrap(), // SAFETY: The iterated elements go straight back into a hashmap. #[allow(rustc::potential_query_instability)] variable_lookup_table: rest_canonicalizer @@ -174,10 +184,12 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { let e = e.get(); let mut rest_canonicalizer = Canonicalizer::new( delegate, - CanonicalizeMode::Input(CanonicalizeInputKind::Predicate), + CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { + param_env_var_kinds: Some(e.var_kinds), + }), ); rest_canonicalizer.state.variables.clone_from(&e.variables); - rest_canonicalizer.state.var_kinds.clone_from(&e.var_kinds); + e.var_kinds.as_slice().clone_into(&mut rest_canonicalizer.state.var_kinds); rest_canonicalizer .state .variable_lookup_table @@ -187,7 +199,8 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { }) } else { // Do `env` canonicalization and get `rest_canonicalizer`. - do_env_and_make_rest() + let (param_env, rest_canonicalizer, _) = do_env_and_make_rest(false); + (param_env, rest_canonicalizer) } } @@ -278,7 +291,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { // during canonicalization in the new solver. let max_universe = match self.canonicalize_mode { // All placeholders and vars are canonicalized in the root universe. - CanonicalizeMode::Input { .. } => { + CanonicalizeMode::Input(_) => { debug_assert!( self.state .var_kinds @@ -309,7 +322,15 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { .unwrap_or(ty::UniverseIndex::ROOT) } }; - let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&self.state.var_kinds); + let var_kinds = match self.canonicalize_mode { + CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { + param_env_var_kinds: Some(cached), + }) if cached.len() == self.state.variables.len() => { + debug_assert_eq!(cached.as_slice(), self.state.var_kinds.as_slice()); + cached + } + _ => self.delegate.cx().mk_canonical_var_kinds(&self.state.var_kinds), + }; // We have finished with this canonicalizer and can return its state to the delegate for // later reuse. @@ -330,7 +351,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { let sub_root = self.get_or_insert_sub_root(vid); let ui = match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => ty::UniverseIndex::ROOT, + CanonicalizeMode::Input(_) => ty::UniverseIndex::ROOT, CanonicalizeMode::Response { .. } => self .delegate .universe_of_ty(vid) @@ -359,7 +380,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { } }, ty::Placeholder(placeholder) => match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => { + CanonicalizeMode::Input(_) => { CanonicalVarKind::PlaceholderTy(PlaceholderType::new_anon( ty::UniverseIndex::ROOT, self.state.variables.len().into(), @@ -368,7 +389,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder), }, ty::Param(_) => match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => { + CanonicalizeMode::Input(_) => { CanonicalVarKind::PlaceholderTy(PlaceholderType::new_anon( ty::UniverseIndex::ROOT, self.state.variables.len().into(), @@ -429,7 +450,7 @@ impl, I: Interner> TypeFolder for Canonicaliz // We don't canonicalize `ReStatic` in the `param_env` as we use it // when checking whether a `ParamEnv` candidate is global. ty::ReStatic => match self.canonicalize_mode { - CanonicalizeMode::Input(CanonicalizeInputKind::Predicate) => { + CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => { CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new_anon( ty::UniverseIndex::ROOT, self.state.variables.len().into(), @@ -541,7 +562,7 @@ impl, I: Interner> TypeFolder for Canonicaliz ); match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => { + CanonicalizeMode::Input(_) => { CanonicalVarKind::Const(ty::UniverseIndex::ROOT) } CanonicalizeMode::Response { .. } => { @@ -552,7 +573,7 @@ impl, I: Interner> TypeFolder for Canonicaliz ty::InferConst::Fresh(_) => unimplemented!(), }, ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => { + CanonicalizeMode::Input(_) => { CanonicalVarKind::PlaceholderConst(PlaceholderConst::new_anon( ty::UniverseIndex::ROOT, self.state.variables.len().into(), @@ -563,7 +584,7 @@ impl, I: Interner> TypeFolder for Canonicaliz } }, ty::ConstKind::Param(_) => match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => { + CanonicalizeMode::Input(_) => { CanonicalVarKind::PlaceholderConst(PlaceholderConst::new_anon( ty::UniverseIndex::ROOT, self.state.variables.len().into(), @@ -592,7 +613,7 @@ impl, I: Interner> TypeFolder for Canonicaliz match self.canonicalize_mode { CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv) | CanonicalizeMode::Response { max_input_universe: _ } => {} - CanonicalizeMode::Input(CanonicalizeInputKind::Predicate) => { + CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => { panic!("erasing 'static in env") } } diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index 7aa3d87328fe7..edcc0082a942f 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -317,7 +317,7 @@ pub struct CanonicalParamEnvCacheEntry { pub param_env: I::ParamEnv, pub variables: Vec, pub variable_lookup_table: HashMap, - pub var_kinds: Vec>, + pub var_kinds: I::CanonicalVarKinds, } /// State used and modified by a canonicalizer during canonicalization. To avoid many allocations, From 107a6617b4bbaf3ab9db8527889a529f8c809407 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 2 Nov 2026 19:09:10 +0800 Subject: [PATCH 44/72] 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 bec219061d27a7f7b15808f663127242f6087641 Mon Sep 17 00:00:00 2001 From: Acrimon Date: Sat, 26 Sep 2026 21:22:17 +0200 Subject: [PATCH 45/72] inline and cold attributes on async fn and async closures affect the poll impl instead of the desugared creator function. --- .../rustc_codegen_ssa/src/codegen_attrs.rs | 67 ++++++++++++++++- tests/codegen-llvm/async-fn-cold-attr.rs | 51 +++++++++++++ tests/codegen-llvm/async-fn-inline-attr.rs | 71 +++++++++++++++++++ .../inline-always-async-fn.aarch64.stderr | 10 +++ .../target-feature/inline-always-async-fn.rs | 29 ++++++++ .../inline-always-async-fn.x86_64.stderr | 10 +++ 6 files changed, 235 insertions(+), 3 deletions(-) create mode 100644 tests/codegen-llvm/async-fn-cold-attr.rs create mode 100644 tests/codegen-llvm/async-fn-inline-attr.rs create mode 100644 tests/ui/target-feature/inline-always-async-fn.aarch64.stderr create mode 100644 tests/ui/target-feature/inline-always-async-fn.rs create mode 100644 tests/ui/target-feature/inline-always-async-fn.x86_64.stderr diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index c3bace9569dd0..691224662f132 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -72,8 +72,15 @@ fn process_builtin_attrs( AttributeKind::Cold => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD, AttributeKind::ExportName { name, .. } => codegen_fn_attrs.symbol_name = Some(*name), AttributeKind::Inline(inline, span) => { - codegen_fn_attrs.inline = *inline; - interesting_spans.inline = Some(*span); + // On an `async fn` (or similar), inlining hints are meant for the code the user + // wrote, which lives in the body coroutine. The constructor is trivial and left to + // the usual heuristics. `#[rustc_force_inline]` is rejected on such items anyway. + if matches!(inline, InlineAttr::Force { .. }) + || !constructs_desugared_body_coroutine(tcx, did) + { + codegen_fn_attrs.inline = *inline; + interesting_spans.inline = Some(*span); + } } AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, AttributeKind::RustcAlign { align, .. } => codegen_fn_attrs.alignment = Some(*align), @@ -297,9 +304,52 @@ fn process_builtin_attrs( } } + // The body coroutine can't carry attributes of its own, so it takes the ones describing the + // code the user wrote from the `async fn` (or similar) it was desugared from. This makes e.g. + // `#[inline(never)] async fn` affect `Future::poll`. See #129347. + if is_desugared_body_coroutine(tcx, did) { + let parent = tcx.local_parent(did); + if let Some((inline, span)) = find_attr!( + tcx, + parent, + Inline(inline, span) if !matches!(inline, InlineAttr::Force { .. }) => (*inline, *span) + ) { + codegen_fn_attrs.inline = inline; + interesting_spans.inline = Some(span); + } + // Unlike inlining hints, `#[cold]` stays on the constructor as well: calling it is just as + // unlikely, and its call sites are only recognized as cold through that. + if find_attr!(tcx, parent, Cold) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD; + } + } + interesting_spans } +/// Whether `did` is the coroutine holding the body of an `async fn`, `gen fn`, `async gen fn`, or +/// `async` closure, as opposed to one written as an `async {}` block. +fn is_desugared_body_coroutine(tcx: TyCtxt<'_>, did: LocalDefId) -> bool { + matches!( + tcx.coroutine_kind(did), + Some(hir::CoroutineKind::Desugared( + _, + hir::CoroutineSource::Fn | hir::CoroutineSource::Closure + )) + ) +} + +/// Whether `did` is an `async fn`, `gen fn`, `async gen fn`, or `async` closure, i.e. its body +/// does nothing but construct a [desugared body coroutine](is_desugared_body_coroutine). +fn constructs_desugared_body_coroutine(tcx: TyCtxt<'_>, did: LocalDefId) -> bool { + tcx.hir_maybe_body_owned_by(did).is_some_and(|body| { + matches!( + body.value.kind, + hir::ExprKind::Closure(closure) if is_desugared_body_coroutine(tcx, closure.def_id) + ) + }) +} + /// Applies overrides for codegen fn attrs. These often have a specific reason why they're necessary. /// Please comment why when adding a new one! fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut CodegenFnAttrs) { @@ -336,7 +386,18 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code // its parent function, which effectively inherits the features anyway. Boxing this closure // would result in this closure being compiled without the inherited target features, but this // is probably a poor usage of `#[inline(always)]` and easily avoided by not using the attribute. - if tcx.is_closure_like(did.to_def_id()) && codegen_fn_attrs.inline != InlineAttr::Always { + // + // That reasoning doesn't hold for the body coroutine of an `async fn` or `gen fn`: it is never + // inlined into the function constructing it, yet its body was checked as part of that function. + // It always inherits, which makes `check_result` reject `#[inline(always)]` there just like it + // does on the function itself. + if tcx.is_closure_like(did.to_def_id()) + && (codegen_fn_attrs.inline != InlineAttr::Always + || matches!( + tcx.coroutine_kind(did), + Some(hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Fn)) + )) + { let owner_id = tcx.parent(did.to_def_id()); if tcx.def_kind(owner_id).has_codegen_attrs() { codegen_fn_attrs diff --git a/tests/codegen-llvm/async-fn-cold-attr.rs b/tests/codegen-llvm/async-fn-cold-attr.rs new file mode 100644 index 0000000000000..3f4925d9208d4 --- /dev/null +++ b/tests/codegen-llvm/async-fn-cold-attr.rs @@ -0,0 +1,51 @@ +// Checks that `#[cold]` on `async fn`s and async closures applies to the coroutine holding their +// body (i.e. the `Future::poll` implementation), in addition to the function or closure that +// constructs it. +// +//@ edition: 2024 +//@ compile-flags: -Cno-prepopulate-passes -Csymbol-mangling-version=v0 -Zinline-mir=no +#![crate_type = "lib"] +#![feature(stmt_expr_attributes)] + +use std::future::Future; +use std::pin::pin; +use std::task::Context; + +// CHECK: ; async_fn_cold_attr::cold{{$}} +// CHECK-NEXT: ; Function Attrs: cold +// CHECK: ; async_fn_cold_attr::cold::{closure#0} +// CHECK-NEXT: ; Function Attrs: cold +#[cold] +pub async fn cold() {} + +// CHECK: ; async_fn_cold_attr::not_cold::{closure#0} +// CHECK-NEXT: ; Function Attrs: +// CHECK-NOT: cold +// CHECK-SAME: {{$}} +pub async fn not_cold() {} + +pub struct S; + +impl S { + // CHECK: ; ::method{{$}} + // CHECK-NEXT: ; Function Attrs: cold + // CHECK: ; ::method::{closure#0} + // CHECK-NEXT: ; Function Attrs: cold + #[cold] + pub async fn method(&self) {} +} + +// CHECK: ; async_fn_cold_attr::poll_all{{$}} +pub fn poll_all(cx: &mut Context<'_>) { + let _ = pin!(cold()).poll(cx); + let _ = pin!(not_cold()).poll(cx); + let _ = pin!(S.method()).poll(cx); + + // CHECK: ; async_fn_cold_attr::poll_all::{closure#0}{{$}} + // CHECK-NEXT: ; Function Attrs: cold + // CHECK: ; async_fn_cold_attr::poll_all::{closure#0}::{closure#0}:: + // CHECK-NEXT: ; Function Attrs: cold + let closure = #[cold] + async || {}; + let _ = pin!(closure()).poll(cx); +} diff --git a/tests/codegen-llvm/async-fn-inline-attr.rs b/tests/codegen-llvm/async-fn-inline-attr.rs new file mode 100644 index 0000000000000..d97300dd6046c --- /dev/null +++ b/tests/codegen-llvm/async-fn-inline-attr.rs @@ -0,0 +1,71 @@ +// Checks that inline attributes on `async fn`s and async closures apply to the coroutine holding +// their body (i.e. the `Future::poll` implementation) rather than to the function or closure that +// merely constructs it. See #129347. +// +//@ edition: 2024 +//@ compile-flags: -Cno-prepopulate-passes -Csymbol-mangling-version=v0 -Zinline-mir=no +#![crate_type = "lib"] +#![feature(stmt_expr_attributes)] + +use std::future::Future; +use std::pin::pin; +use std::task::Context; + +// CHECK: ; async_fn_inline_attr::never{{$}} +// CHECK-NEXT: ; Function Attrs: +// CHECK-NOT: noinline +// CHECK-SAME: {{$}} +// CHECK: ; async_fn_inline_attr::never::{closure#0} +// CHECK-NEXT: ; Function Attrs: noinline +#[inline(never)] +pub async fn never() {} + +// CHECK: ; async_fn_inline_attr::always{{$}} +// CHECK-NEXT: ; Function Attrs: +// CHECK-NOT: alwaysinline +// CHECK-SAME: {{$}} +// CHECK: ; async_fn_inline_attr::always::{closure#0} +// CHECK-NEXT: ; Function Attrs: alwaysinline +#[inline(always)] +pub async fn always() {} + +// The body coroutine would get an inline hint regardless, like all closures. +// CHECK: ; async_fn_inline_attr::hint{{$}} +// CHECK-NEXT: ; Function Attrs: +// CHECK-NOT: inlinehint +// CHECK-SAME: {{$}} +// CHECK: ; async_fn_inline_attr::hint::{closure#0} +// CHECK-NEXT: ; Function Attrs: inlinehint +#[inline] +pub async fn hint() {} + +pub struct S; + +impl S { + // CHECK: ; ::method{{$}} + // CHECK-NEXT: ; Function Attrs: + // CHECK-NOT: noinline + // CHECK-SAME: {{$}} + // CHECK: ; ::method::{closure#0} + // CHECK-NEXT: ; Function Attrs: noinline + #[inline(never)] + pub async fn method(&self) {} +} + +// CHECK: ; async_fn_inline_attr::poll_all{{$}} +pub fn poll_all(cx: &mut Context<'_>) { + let _ = pin!(never()).poll(cx); + let _ = pin!(always()).poll(cx); + let _ = pin!(hint()).poll(cx); + let _ = pin!(S.method()).poll(cx); + + // CHECK: ; async_fn_inline_attr::poll_all::{closure#0}{{$}} + // CHECK-NEXT: ; Function Attrs: + // CHECK-NOT: noinline + // CHECK-SAME: {{$}} + // CHECK: ; async_fn_inline_attr::poll_all::{closure#0}::{closure#0}:: + // CHECK-NEXT: ; Function Attrs: noinline + let closure = #[inline(never)] + async || {}; + let _ = pin!(closure()).poll(cx); +} diff --git a/tests/ui/target-feature/inline-always-async-fn.aarch64.stderr b/tests/ui/target-feature/inline-always-async-fn.aarch64.stderr new file mode 100644 index 0000000000000..40ce807d0b01c --- /dev/null +++ b/tests/ui/target-feature/inline-always-async-fn.aarch64.stderr @@ -0,0 +1,10 @@ +error: cannot use `#[inline(always)]` with `#[target_feature]` + --> $DIR/inline-always-async-fn.rs:13:1 + | +LL | #[inline(always)] + | ^^^^^^^^^^^^^^^^^ + | + = note: See this issue for full discussion: https://github.com/rust-lang/rust/issues/145574 + +error: aborting due to 1 previous error + diff --git a/tests/ui/target-feature/inline-always-async-fn.rs b/tests/ui/target-feature/inline-always-async-fn.rs new file mode 100644 index 0000000000000..c6b49819d8733 --- /dev/null +++ b/tests/ui/target-feature/inline-always-async-fn.rs @@ -0,0 +1,29 @@ +// Inlining attributes on an `async fn` apply to its body coroutine instead of the function +// constructing it (#129347). The body coroutine inherits the function's target features, so +// `#[inline(always)]` must still be rejected together with `#[target_feature]`. +// +//@ revisions: x86_64 aarch64 +//@[x86_64] only-x86_64 +//@[aarch64] only-aarch64 +//@ edition: 2024 + +#![crate_type = "lib"] +#![feature(stmt_expr_attributes)] + +#[inline(always)] +//~^ ERROR cannot use `#[inline(always)]` with `#[target_feature]` +#[cfg_attr(target_arch = "x86_64", target_feature(enable = "sse2"))] +#[cfg_attr(target_arch = "aarch64", target_feature(enable = "neon"))] +pub async fn always() {} + +#[inline(never)] +#[cfg_attr(target_arch = "x86_64", target_feature(enable = "sse2"))] +#[cfg_attr(target_arch = "aarch64", target_feature(enable = "neon"))] +pub async fn never() {} + +#[cfg_attr(target_arch = "x86_64", target_feature(enable = "sse2"))] +#[cfg_attr(target_arch = "aarch64", target_feature(enable = "neon"))] +pub fn with_async_closure() { + // Just like a regular `#[inline(always)]` closure, this doesn't inherit the target features. + let _ = #[inline(always)] async || {}; +} diff --git a/tests/ui/target-feature/inline-always-async-fn.x86_64.stderr b/tests/ui/target-feature/inline-always-async-fn.x86_64.stderr new file mode 100644 index 0000000000000..40ce807d0b01c --- /dev/null +++ b/tests/ui/target-feature/inline-always-async-fn.x86_64.stderr @@ -0,0 +1,10 @@ +error: cannot use `#[inline(always)]` with `#[target_feature]` + --> $DIR/inline-always-async-fn.rs:13:1 + | +LL | #[inline(always)] + | ^^^^^^^^^^^^^^^^^ + | + = note: See this issue for full discussion: https://github.com/rust-lang/rust/issues/145574 + +error: aborting due to 1 previous error + From 1c517b29acf029cb01a41e3df33137f44d11329b Mon Sep 17 00:00:00 2001 From: Acrimon Date: Mon, 2 Nov 2026 12:29:21 +0100 Subject: [PATCH 46/72] Don't keep #[rustc_force_inline] on async/gen fn constructors It is already rejected on such functions, and keeping it only produced a redundant 'incompatible with #[cold]' error. --- .../rustc_codegen_ssa/src/codegen_attrs.rs | 8 +++--- tests/ui/force-inlining/invalid-async-cold.rs | 25 +++++++++++++++++++ .../force-inlining/invalid-async-cold.stderr | 20 +++++++++++++++ 3 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 tests/ui/force-inlining/invalid-async-cold.rs create mode 100644 tests/ui/force-inlining/invalid-async-cold.stderr diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 691224662f132..711e6ec32aff0 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -74,10 +74,8 @@ fn process_builtin_attrs( AttributeKind::Inline(inline, span) => { // On an `async fn` (or similar), inlining hints are meant for the code the user // wrote, which lives in the body coroutine. The constructor is trivial and left to - // the usual heuristics. `#[rustc_force_inline]` is rejected on such items anyway. - if matches!(inline, InlineAttr::Force { .. }) - || !constructs_desugared_body_coroutine(tcx, did) - { + // the usual heuristics. + if !constructs_desugared_body_coroutine(tcx, did) { codegen_fn_attrs.inline = *inline; interesting_spans.inline = Some(*span); } @@ -309,6 +307,8 @@ fn process_builtin_attrs( // `#[inline(never)] async fn` affect `Future::poll`. See #129347. if is_desugared_body_coroutine(tcx, did) { let parent = tcx.local_parent(did); + // `#[rustc_force_inline]` is already rejected on the parent. Propagating it would only + // cause `CheckForceInline` to report the same attribute again for the coroutine. if let Some((inline, span)) = find_attr!( tcx, parent, diff --git a/tests/ui/force-inlining/invalid-async-cold.rs b/tests/ui/force-inlining/invalid-async-cold.rs new file mode 100644 index 0000000000000..f0a699ac1caa0 --- /dev/null +++ b/tests/ui/force-inlining/invalid-async-cold.rs @@ -0,0 +1,25 @@ +//@ edition: 2024 +//@ compile-flags: --crate-type=lib +#![allow(internal_features)] +#![feature(gen_blocks)] +#![feature(rustc_attrs)] + +// `#[inline]` on an `async fn` or `gen fn` applies to its body coroutine, but +// `#[rustc_force_inline]` must not: it is already rejected on such functions, +// so it must not also be reported as incompatible with `#[cold]`, which both +// the function and its body coroutine get. + +#[rustc_force_inline] +//~^ ERROR attribute cannot be applied to a `async`, `gen` or `async gen` function +#[cold] +pub async fn async_cold() {} + +#[rustc_force_inline] +//~^ ERROR attribute cannot be applied to a `async`, `gen` or `async gen` function +#[cold] +pub gen fn gen_cold() {} + +pub fn caller() { + let _ = async_cold(); + let _ = gen_cold(); +} diff --git a/tests/ui/force-inlining/invalid-async-cold.stderr b/tests/ui/force-inlining/invalid-async-cold.stderr new file mode 100644 index 0000000000000..cc176458c0c13 --- /dev/null +++ b/tests/ui/force-inlining/invalid-async-cold.stderr @@ -0,0 +1,20 @@ +error: attribute cannot be applied to a `async`, `gen` or `async gen` function + --> $DIR/invalid-async-cold.rs:12:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +... +LL | pub async fn async_cold() {} + | ------------------------- `async`, `gen` or `async gen` function + +error: attribute cannot be applied to a `async`, `gen` or `async gen` function + --> $DIR/invalid-async-cold.rs:17:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +... +LL | pub gen fn gen_cold() {} + | --------------------- `async`, `gen` or `async gen` function + +error: aborting due to 2 previous errors + From 2caedab4f6ededde58be975e6bc7a883a8483543 Mon Sep 17 00:00:00 2001 From: Acrimon Date: Mon, 2 Nov 2026 12:39:39 +0100 Subject: [PATCH 47/72] Adapt async fn inline attrs to #[inline(always)] + #[target_feature] being allowed Upstream no longer rejects the combination, so replace the UI test expecting that error with a codegen test checking that the body coroutine still inherits the target features. --- .../rustc_codegen_ssa/src/codegen_attrs.rs | 3 +- .../async-fn-inline-always-target-feature.rs | 32 +++++++++++++++++++ .../inline-always-async-fn.aarch64.stderr | 10 ------ .../target-feature/inline-always-async-fn.rs | 29 ----------------- .../inline-always-async-fn.x86_64.stderr | 10 ------ 5 files changed, 33 insertions(+), 51 deletions(-) create mode 100644 tests/codegen-llvm/async-fn-inline-always-target-feature.rs delete mode 100644 tests/ui/target-feature/inline-always-async-fn.aarch64.stderr delete mode 100644 tests/ui/target-feature/inline-always-async-fn.rs delete mode 100644 tests/ui/target-feature/inline-always-async-fn.x86_64.stderr diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 711e6ec32aff0..10baf16915d15 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -389,8 +389,7 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code // // That reasoning doesn't hold for the body coroutine of an `async fn` or `gen fn`: it is never // inlined into the function constructing it, yet its body was checked as part of that function. - // It always inherits, which makes `check_result` reject `#[inline(always)]` there just like it - // does on the function itself. + // It always inherits, so it is compiled with the same features as the function itself. if tcx.is_closure_like(did.to_def_id()) && (codegen_fn_attrs.inline != InlineAttr::Always || matches!( diff --git a/tests/codegen-llvm/async-fn-inline-always-target-feature.rs b/tests/codegen-llvm/async-fn-inline-always-target-feature.rs new file mode 100644 index 0000000000000..83e635c03a50d --- /dev/null +++ b/tests/codegen-llvm/async-fn-inline-always-target-feature.rs @@ -0,0 +1,32 @@ +// `#[inline(always)]` on an `async fn` applies to its body coroutine (#129347). Unlike a closure +// marked `#[inline(always)]`, that coroutine is never inlined into the function it was written +// in, so it must still inherit that function's `#[target_feature]`s. +// +//@ revisions: x86_64 aarch64 +//@[x86_64] only-x86_64 +//@[aarch64] only-aarch64 +//@ edition: 2024 +//@ compile-flags: -Cno-prepopulate-passes -Csymbol-mangling-version=v0 -Zinline-mir=no +#![crate_type = "lib"] + +use std::future::Future; +use std::pin::pin; +use std::task::{Context, Waker}; + +// CHECK: ; async_fn_inline_always_target_feature::always::{closure#0} +// CHECK-NEXT: ; Function Attrs: +// CHECK-NEXT: define {{.*}} #[[ATTRS:[0-9]+]] +#[inline(always)] +#[cfg_attr(target_arch = "x86_64", target_feature(enable = "avx2"))] +#[cfg_attr(target_arch = "aarch64", target_feature(enable = "sha3"))] +pub async fn always() {} + +#[cfg_attr(target_arch = "x86_64", target_feature(enable = "avx2"))] +#[cfg_attr(target_arch = "aarch64", target_feature(enable = "sha3"))] +pub fn poll_always() { + let mut cx = Context::from_waker(Waker::noop()); + let _ = pin!(always()).poll(&mut cx); +} + +// x86_64: attributes #[[ATTRS]] = {{{.*}}"target-features"="{{[^"]*}}+avx2 +// aarch64: attributes #[[ATTRS]] = {{{.*}}"target-features"="{{[^"]*}}+sha3 diff --git a/tests/ui/target-feature/inline-always-async-fn.aarch64.stderr b/tests/ui/target-feature/inline-always-async-fn.aarch64.stderr deleted file mode 100644 index 40ce807d0b01c..0000000000000 --- a/tests/ui/target-feature/inline-always-async-fn.aarch64.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: cannot use `#[inline(always)]` with `#[target_feature]` - --> $DIR/inline-always-async-fn.rs:13:1 - | -LL | #[inline(always)] - | ^^^^^^^^^^^^^^^^^ - | - = note: See this issue for full discussion: https://github.com/rust-lang/rust/issues/145574 - -error: aborting due to 1 previous error - diff --git a/tests/ui/target-feature/inline-always-async-fn.rs b/tests/ui/target-feature/inline-always-async-fn.rs deleted file mode 100644 index c6b49819d8733..0000000000000 --- a/tests/ui/target-feature/inline-always-async-fn.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Inlining attributes on an `async fn` apply to its body coroutine instead of the function -// constructing it (#129347). The body coroutine inherits the function's target features, so -// `#[inline(always)]` must still be rejected together with `#[target_feature]`. -// -//@ revisions: x86_64 aarch64 -//@[x86_64] only-x86_64 -//@[aarch64] only-aarch64 -//@ edition: 2024 - -#![crate_type = "lib"] -#![feature(stmt_expr_attributes)] - -#[inline(always)] -//~^ ERROR cannot use `#[inline(always)]` with `#[target_feature]` -#[cfg_attr(target_arch = "x86_64", target_feature(enable = "sse2"))] -#[cfg_attr(target_arch = "aarch64", target_feature(enable = "neon"))] -pub async fn always() {} - -#[inline(never)] -#[cfg_attr(target_arch = "x86_64", target_feature(enable = "sse2"))] -#[cfg_attr(target_arch = "aarch64", target_feature(enable = "neon"))] -pub async fn never() {} - -#[cfg_attr(target_arch = "x86_64", target_feature(enable = "sse2"))] -#[cfg_attr(target_arch = "aarch64", target_feature(enable = "neon"))] -pub fn with_async_closure() { - // Just like a regular `#[inline(always)]` closure, this doesn't inherit the target features. - let _ = #[inline(always)] async || {}; -} diff --git a/tests/ui/target-feature/inline-always-async-fn.x86_64.stderr b/tests/ui/target-feature/inline-always-async-fn.x86_64.stderr deleted file mode 100644 index 40ce807d0b01c..0000000000000 --- a/tests/ui/target-feature/inline-always-async-fn.x86_64.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: cannot use `#[inline(always)]` with `#[target_feature]` - --> $DIR/inline-always-async-fn.rs:13:1 - | -LL | #[inline(always)] - | ^^^^^^^^^^^^^^^^^ - | - = note: See this issue for full discussion: https://github.com/rust-lang/rust/issues/145574 - -error: aborting due to 1 previous error - From 0e44319b2b5da111c94e0e152cb13d8172dad4f1 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 2 Nov 2026 20:04:38 +0800 Subject: [PATCH 48/72] 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 49/72] 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 50/72] 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 51/72] 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 9b1ceffc48c4ca593b68400f9afccb8e3e761b2e Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Mon, 2 Nov 2026 10:36:51 -0500 Subject: [PATCH 52/72] Reenable the cg_gcc tests that were disabled by accident --- src/ci/docker/host-x86_64/test-x86_64-gnu-gcc/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/test-x86_64-gnu-gcc/Dockerfile b/src/ci/docker/host-x86_64/test-x86_64-gnu-gcc/Dockerfile index d248faaabca6c..2a721006bfdc0 100644 --- a/src/ci/docker/host-x86_64/test-x86_64-gnu-gcc/Dockerfile +++ b/src/ci/docker/host-x86_64/test-x86_64-gnu-gcc/Dockerfile @@ -42,7 +42,7 @@ ENV RUST_CONFIGURE_ARGS="--build=x86_64-unknown-linux-gnu \ --set llvm.libzstd=true" ENV SCRIPT="python3 ../x.py \ --stage 2 \ - test tests \ + test tests compiler/rustc_codegen_gcc \ --test-codegen-backend gcc \ --skip tests/coverage \ --skip tests/coverage-run-rustdoc \ From 86fd1018211811855ebaf4415cacdca7249f3cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 2 Nov 2026 20:56:02 +0100 Subject: [PATCH 53/72] rename sizedness_fast_path to implicit_fast_path --- compiler/rustc_trait_selection/src/solve/delegate.rs | 4 ++-- compiler/rustc_trait_selection/src/traits/fulfill.rs | 4 ++-- compiler/rustc_trait_selection/src/traits/mod.rs | 2 +- .../src/traits/query/type_op/prove_predicate.rs | 4 ++-- compiler/rustc_trait_selection/src/traits/select/mod.rs | 4 ++-- compiler/rustc_trait_selection/src/traits/util.rs | 2 +- compiler/rustc_traits/src/evaluate_obligation.rs | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index c55c43e18d2bf..be07ff71ccf34 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -33,7 +33,7 @@ use thin_vec::{ThinVec, thin_vec}; use super::inspect::InferCtxtProofTreeExt; use crate::solve::inspect::{self, InspectConfig, ProofTreeVisitor}; -use crate::traits::{EvaluateConstErr, ObligationCause, sizedness_fast_path, specialization_graph}; +use crate::traits::{EvaluateConstErr, ObligationCause, implicit_fast_path, specialization_graph}; #[repr(transparent)] pub struct SolverDelegate<'tcx>(InferCtxt<'tcx>); @@ -178,7 +178,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< match self.0.tcx.as_lang_item(trait_pred.def_id()) { Some(LangItem::Sized) | Some(LangItem::MetaSized) => { let predicate = self.deeply_resolve_ignoring_regions(goal.predicate); - if sizedness_fast_path(self.tcx, predicate, goal.param_env) { + if implicit_fast_path(self.tcx, predicate, goal.param_env) { Outcome::TriviallyHolds } else { Outcome::NoFastPath diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 3a1ad2ee5feab..56e80958e6892 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -33,7 +33,7 @@ use crate::infer::InferCtxt; use crate::traits::normalize::normalize_with_depth_to; use crate::traits::project::{PolyProjectionObligation, ProjectionCacheKeyExt as _}; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{EvaluateConstErr, sizedness_fast_path}; +use crate::traits::{EvaluateConstErr, implicit_fast_path}; pub(crate) type PendingPredicateObligations<'tcx> = ThinVec>; @@ -398,7 +398,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { let infcx = self.selcx.infcx; if !infcx.disable_trait_solver_fast_paths() - && sizedness_fast_path(infcx.tcx, obligation.predicate, obligation.param_env) + && implicit_fast_path(infcx.tcx, obligation.predicate, obligation.param_env) { return ProcessResult::Changed(thin_vec![]); } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 7076ec99c141c..326710007564c 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -66,7 +66,7 @@ pub use self::specialize::{ pub use self::structural_normalize::StructurallyNormalizeExt; pub use self::util::{ BoundVarReplacer, PlaceholderReplacer, elaborate, expand_trait_aliases, impl_item_is_final, - sizedness_fast_path, supertrait_def_ids, supertraits, transitive_bounds_that_define_assoc_item, + implicit_fast_path, supertrait_def_ids, supertraits, transitive_bounds_that_define_assoc_item, upcast_choices, with_replaced_escaping_bound_vars, }; use crate::error_reporting::InferCtxtErrorExt; diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 148f75e0a6308..b8c2a3078e8cb 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -6,7 +6,7 @@ use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt}; use crate::infer::InferCtxt; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; -use crate::traits::{ObligationCtxt, sizedness_fast_path}; +use crate::traits::{ObligationCtxt, implicit_fast_path}; impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { type QueryResponse = (); @@ -15,7 +15,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { infcx: &InferCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>, ) -> Option { - if sizedness_fast_path(infcx.tcx, key.value.predicate, key.param_env) { + if implicit_fast_path(infcx.tcx, key.value.predicate, key.param_env) { return Some(()); } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index e280184169844..279392221a33c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -47,7 +47,7 @@ use crate::infer::{InferCtxt, InferOk, TypeFreshener}; use crate::solve::InferCtxtSelectExt as _; use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::project::{ProjectAndUnifyResult, ProjectionCacheKeyExt}; -use crate::traits::{EvaluateConstErr, ProjectionCacheKey, effects, sizedness_fast_path}; +use crate::traits::{EvaluateConstErr, ProjectionCacheKey, effects, implicit_fast_path}; mod _match; mod candidate_assembly; @@ -537,7 +537,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } if !self.infcx.disable_trait_solver_fast_paths() - && sizedness_fast_path(self.tcx(), obligation.predicate, obligation.param_env) + && implicit_fast_path(self.tcx(), obligation.predicate, obligation.param_env) { return Ok(EvaluatedToOk); } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index c60ebc4758ead..9cbb53c6f63bd 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -217,7 +217,7 @@ pub fn with_replaced_escaping_bound_vars< } } -pub fn sizedness_fast_path<'tcx>( +pub fn implicit_fast_path<'tcx>( tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>, param_env: ty::ParamEnv<'tcx>, diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs index 6bc6543a0a01b..ac5df8b93f482 100644 --- a/compiler/rustc_traits/src/evaluate_obligation.rs +++ b/compiler/rustc_traits/src/evaluate_obligation.rs @@ -5,7 +5,7 @@ use rustc_span::DUMMY_SP; use rustc_trait_selection::traits::query::CanonicalPredicateGoal; use rustc_trait_selection::traits::{ EvaluationResult, Obligation, ObligationCause, OverflowError, SelectionContext, TraitQueryMode, - sizedness_fast_path, + implicit_fast_path, }; use tracing::debug; @@ -24,7 +24,7 @@ fn evaluate_obligation<'tcx>( debug!("evaluate_obligation: goal={:#?}", goal); let ParamEnvAnd { param_env, value: predicate } = goal; - if !tcx.disable_trait_solver_fast_paths() && sizedness_fast_path(tcx, predicate, param_env) { + if !tcx.disable_trait_solver_fast_paths() && implicit_fast_path(tcx, predicate, param_env) { return Ok(EvaluationResult::EvaluatedToOk); } From 1b8ce170a333c8e8b984f62796fceedbb5715ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 2 Nov 2026 21:17:24 +0100 Subject: [PATCH 54/72] refactor implicit bounds addition --- .../src/collect/clauses_of.rs | 41 ++++----------- .../src/collect/item_bounds.rs | 21 ++------ .../src/hir_ty_lowering/bounds.rs | 52 ++++++++++++------- .../src/hir_ty_lowering/dyn_trait.rs | 4 +- .../src/hir_ty_lowering/mod.rs | 6 ++- 5 files changed, 53 insertions(+), 71 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/clauses_of.rs b/compiler/rustc_hir_analysis/src/collect/clauses_of.rs index ae384a67ee6aa..49f56ddefb0e0 100644 --- a/compiler/rustc_hir_analysis/src/collect/clauses_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/clauses_of.rs @@ -16,6 +16,7 @@ use super::item_bounds::explicit_item_bounds_with_filter; use crate::collect::ItemCtxt; use crate::constrained_generic_params as cgp; use crate::delegation::inherit_clauses_for_delegation_item; +use crate::hir_ty_lowering::bounds::IncludeSizedImplicitBound; use crate::hir_ty_lowering::{ HirTyLowerer, ImpliedBoundsContext, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason, @@ -195,19 +196,13 @@ fn gather_explicit_clauses_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generi PredicateFilter::All, OverlappingAsssocItemConstraints::Allowed, ); - icx.lowerer().add_implicit_sizedness_bounds( - &mut bounds, - tcx.types.self_param, - self_bounds, - ImpliedBoundsContext::TraitDef(def_id), - span, - ); - icx.lowerer().add_default_traits( + icx.lowerer().add_implicit_bounds( &mut bounds, tcx.types.self_param, self_bounds, ImpliedBoundsContext::TraitDef(def_id), span, + IncludeSizedImplicitBound::Yes, ); clauses.extend(bounds); } @@ -234,19 +229,13 @@ fn gather_explicit_clauses_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generi let param_ty = icx.lowerer().lower_ty_param(param.hir_id); let mut bounds = Vec::new(); // Implicit bounds are added to type params unless a `?Trait` bound is found - icx.lowerer().add_implicit_sizedness_bounds( - &mut bounds, - param_ty, - &[], - ImpliedBoundsContext::TyParam(param.def_id, hir_generics.predicates), - param.span, - ); - icx.lowerer().add_default_traits( + icx.lowerer().add_implicit_bounds( &mut bounds, param_ty, &[], ImpliedBoundsContext::TyParam(param.def_id, hir_generics.predicates), param.span, + IncludeSizedImplicitBound::Yes, ); trace!(?bounds); clauses.extend(bounds); @@ -690,19 +679,13 @@ pub(super) fn implied_clauses_with_filter<'tcx>( | PredicateFilter::SelfOnly | PredicateFilter::SelfTraitThatDefines(_) | PredicateFilter::SelfAndAssociatedTypeBounds => { - icx.lowerer().add_implicit_sizedness_bounds( - &mut bounds, - self_param_ty, - superbounds, - ImpliedBoundsContext::TraitDef(trait_def_id), - item.span, - ); - icx.lowerer().add_default_traits( + icx.lowerer().add_implicit_bounds( &mut bounds, self_param_ty, superbounds, ImpliedBoundsContext::TraitDef(trait_def_id), item.span, + IncludeSizedImplicitBound::Yes, ); } //`ConstIfConst` is only interested in `[const]` bounds. @@ -992,19 +975,13 @@ impl<'tcx> ItemCtxt<'tcx> { match param.kind { hir::GenericParamKind::Type { .. } => { let param_ty = self.lowerer().lower_ty_param(param.hir_id); - self.lowerer().add_implicit_sizedness_bounds( - &mut bounds, - param_ty, - &[], - ImpliedBoundsContext::TyParam(param.def_id, hir_generics.predicates), - param.span, - ); - self.lowerer().add_default_traits( + self.lowerer().add_implicit_bounds( &mut bounds, param_ty, &[], ImpliedBoundsContext::TyParam(param.def_id, hir_generics.predicates), param.span, + IncludeSizedImplicitBound::Yes, ); } hir::GenericParamKind::Lifetime { .. } diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 5ed5745310f16..a805ea0c29e7a 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -11,6 +11,7 @@ use tracing::{debug, instrument}; use super::ItemCtxt; use super::clauses_of::assert_only_contains_clauses_from; +use crate::hir_ty_lowering::bounds::IncludeSizedImplicitBound; use crate::hir_ty_lowering::{ HirTyLowerer, ImpliedBoundsContext, OverlappingAsssocItemConstraints, PredicateFilter, }; @@ -54,19 +55,13 @@ fn associated_type_bounds<'tcx>( | PredicateFilter::SelfTraitThatDefines(_) | PredicateFilter::SelfAndAssociatedTypeBounds => { // Implicit bounds are added to associated types unless a `?Trait` bound is found. - icx.lowerer().add_implicit_sizedness_bounds( - &mut bounds, - item_ty, - hir_bounds, - ImpliedBoundsContext::AssociatedTypeOrImplTrait, - span, - ); - icx.lowerer().add_default_traits( + icx.lowerer().add_implicit_bounds( &mut bounds, item_ty, hir_bounds, ImpliedBoundsContext::AssociatedTypeOrImplTrait, span, + IncludeSizedImplicitBound::Yes, ); // Also collect `where Self::Assoc: Trait` from the parent trait's where clauses. @@ -381,19 +376,13 @@ fn opaque_type_bounds<'tcx>( | PredicateFilter::SelfOnly | PredicateFilter::SelfTraitThatDefines(_) | PredicateFilter::SelfAndAssociatedTypeBounds => { - icx.lowerer().add_implicit_sizedness_bounds( - &mut bounds, - item_ty, - hir_bounds, - ImpliedBoundsContext::AssociatedTypeOrImplTrait, - span, - ); - icx.lowerer().add_default_traits( + icx.lowerer().add_implicit_bounds( &mut bounds, item_ty, hir_bounds, ImpliedBoundsContext::AssociatedTypeOrImplTrait, span, + IncludeSizedImplicitBound::Yes, ); } //`ConstIfConst` is only interested in `[const]` bounds. diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 0b7df89551da8..c4b4b8f04427b 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -137,7 +137,35 @@ fn add_trait_bound<'tcx>( bounds.insert(0, (trait_ref.upcast(tcx), span)); } +pub(crate) enum IncludeSizedImplicitBound { + Yes, + No, +} + impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { + /// Adds implicit sizedness, and in the future `Move` and `Forget`, and default trait bounds. + pub(crate) fn add_implicit_bounds( + &self, + bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, + self_ty: Ty<'tcx>, + hir_bounds: &[hir::GenericBound<'_>], + context: ImpliedBoundsContext<'tcx>, + span: Span, + // Sized is treated slightly more specially than the other implicit bounds. + include_sized: IncludeSizedImplicitBound, + ) { + // Skip adding any default bounds if `#![rustc_no_implicit_bounds]` + if find_attr!(self.tcx(), crate, RustcNoImplicitBounds) { + return; + } + + self.add_default_traits(bounds, self_ty, hir_bounds, context, span); + + if let IncludeSizedImplicitBound::Yes = include_sized { + self.add_implicit_sizedness_bounds(bounds, self_ty, hir_bounds, context, span); + } + } + /// Adds sizedness bounds to a trait, trait alias, parameter, opaque type or associated type. /// /// - On parameters, opaque type and associated types, add default `Sized` bound if no explicit @@ -146,7 +174,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// bounds are present. /// - On parameters, opaque type, associated types and trait aliases, add a `MetaSized` bound if /// a `?Sized` bound is present. - pub(crate) fn add_implicit_sizedness_bounds( + fn add_implicit_sizedness_bounds( &self, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, self_ty: Ty<'tcx>, @@ -156,11 +184,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) { let tcx = self.tcx(); - // Skip adding any default bounds if `#![rustc_no_implicit_bounds]` - if find_attr!(tcx, crate, RustcNoImplicitBounds) { - return; - } - let meta_sized_did = tcx.require_lang_item(LangItem::MetaSized, span); let pointee_sized_did = tcx.require_lang_item(LangItem::PointeeSized, span); @@ -208,7 +231,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - pub(crate) fn add_default_traits( + fn add_default_traits( &self, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, self_ty: Ty<'tcx>, @@ -224,7 +247,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Add a `experimental_default_bounds` bound to the `bounds` if appropriate. /// /// Doesn't add the bound if the HIR bounds contain any of `Trait`, `?Trait` or `!Trait`. - pub(crate) fn add_default_trait( + fn add_default_trait( &self, trait_: LangItem, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, @@ -244,23 +267,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } if let Some(trait_did) = tcx.lang_items().get(trait_) - && self.should_add_default_traits(trait_did, hir_bounds, context) + && !collect_bounds(hir_bounds, context, trait_did).any() { add_trait_bound(tcx, bounds, self_ty, trait_did, span); } } - /// Returns `true` if default trait bound should be added. - fn should_add_default_traits( - &self, - trait_def_id: DefId, - hir_bounds: &[hir::GenericBound<'_>], - context: ImpliedBoundsContext<'tcx>, - ) -> bool { - let collected = collect_bounds(hir_bounds, context, trait_def_id); - !find_attr!(self.tcx(), crate, RustcNoImplicitBounds) && !collected.any() - } - pub(crate) fn require_bound_to_relax_default_trait( &self, trait_ref: hir::TraitRef<'_>, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index c652174f9ba68..8d9174a74444a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -23,6 +23,7 @@ use tracing::{debug, instrument}; use super::HirTyLowerer; use crate::diagnostics::DynTraitAssocItemBindingMentionsSelf; +use crate::hir_ty_lowering::bounds::IncludeSizedImplicitBound; use crate::hir_ty_lowering::{ GenericArgCountMismatch, ImpliedBoundsContext, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason, @@ -75,7 +76,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - self.add_default_traits( + self.add_implicit_bounds( &mut user_written_bounds, dummy_self, &hir_bounds @@ -84,6 +85,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .collect::>(), ImpliedBoundsContext::AssociatedTypeOrImplTrait, span, + IncludeSizedImplicitBound::No, ); let (mut elaborated_trait_bounds, elaborated_projection_bounds) = diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 5edf50d5cb33b..fb07f250cf3ce 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -15,7 +15,7 @@ //! [^1]: This includes types, lifetimes / regions, constants in type positions, //! trait references and bounds. -mod bounds; +pub(crate) mod bounds; mod cmse; mod dyn_trait; pub mod errors; @@ -57,6 +57,7 @@ use tracing::{debug, instrument}; use crate::check::check_abi; use crate::check_c_variadic_abi; use crate::diagnostics::{self, BadReturnTypeNotation, NoFieldOnType, NoVariantNamed}; +use crate::hir_ty_lowering::bounds::IncludeSizedImplicitBound; use crate::hir_ty_lowering::errors::{ GenericsArgsErrExtend, eq_ctxt_suggestion_span, prohibit_assoc_item_constraint, }; @@ -3326,12 +3327,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { PredicateFilter::All, OverlappingAsssocItemConstraints::Allowed, ); - self.add_implicit_sizedness_bounds( + self.add_implicit_bounds( &mut bounds, self_ty, hir_bounds, ImpliedBoundsContext::AssociatedTypeOrImplTrait, hir_ty.span, + IncludeSizedImplicitBound::Yes, ); self.register_trait_ascription_bounds(bounds, hir_ty.hir_id, hir_ty.span); self_ty From 7eb4e4477b662ae7cb6db75a36e4f1d8b3dedbd5 Mon Sep 17 00:00:00 2001 From: Xing Xue Date: Fri, 30 Oct 2026 13:56:53 -0400 Subject: [PATCH 55/72] Implement AIX TLS destructor guard in std and update dynamic-loading-cdylib test: - Add `sys::thread_local::guard::aix` using `__pt_atexit_np` to run main thread TLS destructors on process exit on AIX. - Update `dynamic-loading-cdylib` to use member archive syntax (`RTLD_MEMBER`) for `dlopen` on AIX. - Replace the mode array in `dynamic-loading-cdylib` with a `COMMAND_ARGS` constant that selects `load_only` for musl and AIX because `dlclose` does not invoke TLS destructors on those platforms. --- library/std/src/sys/thread_local/guard/aix.rs | 30 +++++++++++++++++++ library/std/src/sys/thread_local/mod.rs | 4 +++ .../dynamic-loading-cdylib/load_and_unload.rs | 13 ++++++-- .../run-make/dynamic-loading-cdylib/rmake.rs | 18 +++++++---- 4 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 library/std/src/sys/thread_local/guard/aix.rs diff --git a/library/std/src/sys/thread_local/guard/aix.rs b/library/std/src/sys/thread_local/guard/aix.rs new file mode 100644 index 0000000000000..9b945456bf790 --- /dev/null +++ b/library/std/src/sys/thread_local/guard/aix.rs @@ -0,0 +1,30 @@ +//! AIX registers thread destructors using `__pt_atexit_np`. +//! Calling it registers a callback that runs on thread termination +//! and upon process exit (for the main thread). + +use crate::cell::Cell; +use crate::sys::thread_local::destructors; + +pub fn enable() { + #[thread_local] + static REGISTERED: Cell = Cell::new(false); + + unsafe extern "C" { + fn __pt_atexit_np(flags: i32, dtor: unsafe extern "C" fn(i32) -> i32) -> i32; + } + + if !REGISTERED.replace(true) { + let ret = unsafe { __pt_atexit_np(0, run_dtors) }; + if ret != 0 { + rtabort!("failed to register thread destructor"); + } + } + + unsafe extern "C" fn run_dtors(_: i32) -> i32 { + unsafe { + destructors::run(); + crate::rt::thread_cleanup(); + } + 0 + } +} diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index 5d3c23ce04678..1c137e52d9319 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -101,6 +101,10 @@ pub(crate) mod guard { mod apple; pub(crate) use apple::enable; } + all(target_thread_local, target_os = "aix") => { + mod aix; + pub(crate) use aix::enable; + } target_os = "windows" => { mod windows; pub(crate) use windows::enable; diff --git a/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs b/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs index eb1baea93dc00..38669eb1a21d7 100644 --- a/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs +++ b/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs @@ -48,6 +48,8 @@ mod libloading { use std::ffi::{c_char, c_int, c_void}; const RTLD_NOW: c_int = 2; + #[cfg(target_os = "aix")] + const RTLD_MEMBER: c_int = 0x00040000; fn cstr_null(s: &str) -> Vec { assert!(!s.as_bytes().contains(&0), "string must not contain interior NUL"); @@ -58,10 +60,17 @@ mod libloading { #[cfg(target_os = "macos")] let filename = cstr_null(&format!("lib{}.dylib", lib_name)); - #[cfg(not(target_os = "macos"))] + #[cfg(target_os = "aix")] + let filename = cstr_null(&format!("./lib{0}.a(lib{0}.so)", lib_name)); + + #[cfg(not(any(target_os = "macos", target_os = "aix")))] let filename = cstr_null(&format!("./lib{}.so", lib_name)); - let handle = unsafe { dlopen(filename.as_ptr(), RTLD_NOW) }; + let flags = RTLD_NOW; + #[cfg(target_os = "aix")] + let flags = flags | RTLD_MEMBER; + + let handle = unsafe { dlopen(filename.as_ptr(), flags) }; if handle.is_null() { None } else { Some(handle) } } diff --git a/tests/run-make/dynamic-loading-cdylib/rmake.rs b/tests/run-make/dynamic-loading-cdylib/rmake.rs index 975722da75d6b..b931863b694a8 100644 --- a/tests/run-make/dynamic-loading-cdylib/rmake.rs +++ b/tests/run-make/dynamic-loading-cdylib/rmake.rs @@ -5,21 +5,29 @@ // - `foo.rs` is a cdylib. // - `load_and_unload.rs` is a binary that loads and unloads the cdylib. // -// This test is ignored on musl hosts because musl's dlclose is a no-op and -// therefore destructors won't run when the library is unloaded. -// See https://wiki.musl-libc.org/functional-differences-from-glibc.html#Unloading-libraries //@ ignore-cross-compile -//@ ignore-musl use run_make_support::{diff, run_with_args, rustc}; +// On musl and AIX, `dlclose` does not invoke TLS destructors upon unloading, +// so only the `load_only` case is tested on those platforms. +// +// Reference for musl: +// https://wiki.musl-libc.org/functional-differences-from-glibc.html#Unloading-libraries + +#[cfg(any(target_env = "musl", target_os = "aix"))] +const TEST_MODES: &[&str] = &["load_only"]; + +#[cfg(not(any(target_env = "musl", target_os = "aix")))] +const TEST_MODES: &[&str] = &["unload", "load_only"]; + fn main() { rustc().input("foo.rs").run(); rustc().crate_type("bin").crate_name("load_and_unload_bin").input("load_and_unload.rs").run(); - for command_arg in ["unload", "load_only"] { + for command_arg in TEST_MODES { let out_raw = run_with_args("load_and_unload_bin", &[command_arg]).stdout_utf8(); #[cfg(windows)] From f0767bbb0afbe72ab78bb7b12380a9925eee231f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 2 Nov 2026 22:11:10 +0100 Subject: [PATCH 56/72] add is_implicit_trait to tcx --- compiler/rustc_hir_analysis/src/check/mod.rs | 4 +++- .../rustc_hir_analysis/src/collect/clauses_of.rs | 12 ++++++------ .../src/collect/item_bounds.rs | 9 ++++----- .../src/hir_ty_lowering/bounds.rs | 16 ++++++---------- .../src/hir_ty_lowering/dyn_trait.rs | 7 +++---- .../src/hir_ty_lowering/mod.rs | 9 ++++----- .../src/multiple_supertrait_upcastable.rs | 6 ++++-- compiler/rustc_middle/src/ty/context.rs | 15 ++++++++++++++- .../rustc_middle/src/ty/context/impl_interner.rs | 12 ++++++++++-- .../src/solve/trait_goals.rs | 14 ++++++++++---- .../src/traits/select/candidate_assembly.rs | 6 +++--- compiler/rustc_type_ir/src/interner.rs | 11 +++++++++++ compiler/rustc_type_ir/src/solve/mod.rs | 5 ++--- 13 files changed, 80 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index bf0d64d78d395..02acd8dc58918 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -402,7 +402,9 @@ fn bounds_from_generic_clauses<'tcx>( ty::ClauseKind::Trait(trait_predicate) => { let entry = types.entry(trait_predicate.self_ty()).or_default(); let def_id = trait_predicate.def_id(); - if !tcx.is_default_trait(def_id) && !tcx.is_lang_item(def_id, LangItem::Sized) { + if !tcx.is_implicit_trait(def_id, ty::IncludeSizedImplicitBounds::No) + && !tcx.is_lang_item(def_id, LangItem::Sized) + { // Do not add that restriction to the list if it is a positive requirement. entry.push(trait_predicate.def_id()); } diff --git a/compiler/rustc_hir_analysis/src/collect/clauses_of.rs b/compiler/rustc_hir_analysis/src/collect/clauses_of.rs index 49f56ddefb0e0..ba1c1197bee92 100644 --- a/compiler/rustc_hir_analysis/src/collect/clauses_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/clauses_of.rs @@ -7,7 +7,8 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::{ - self, GenericClauses, ImplTraitInTraitData, Ty, TyCtxt, TypeVisitable, TypeVisitor, Upcast, + self, GenericClauses, ImplTraitInTraitData, IncludeSizedImplicitBounds, Ty, TyCtxt, + TypeVisitable, TypeVisitor, Upcast, }; use rustc_span::{DUMMY_SP, Ident, Span, bug, span_bug}; use tracing::{debug, instrument, trace}; @@ -16,7 +17,6 @@ use super::item_bounds::explicit_item_bounds_with_filter; use crate::collect::ItemCtxt; use crate::constrained_generic_params as cgp; use crate::delegation::inherit_clauses_for_delegation_item; -use crate::hir_ty_lowering::bounds::IncludeSizedImplicitBound; use crate::hir_ty_lowering::{ HirTyLowerer, ImpliedBoundsContext, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason, @@ -202,7 +202,7 @@ fn gather_explicit_clauses_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generi self_bounds, ImpliedBoundsContext::TraitDef(def_id), span, - IncludeSizedImplicitBound::Yes, + IncludeSizedImplicitBounds::Yes, ); clauses.extend(bounds); } @@ -235,7 +235,7 @@ fn gather_explicit_clauses_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generi &[], ImpliedBoundsContext::TyParam(param.def_id, hir_generics.predicates), param.span, - IncludeSizedImplicitBound::Yes, + IncludeSizedImplicitBounds::Yes, ); trace!(?bounds); clauses.extend(bounds); @@ -685,7 +685,7 @@ pub(super) fn implied_clauses_with_filter<'tcx>( superbounds, ImpliedBoundsContext::TraitDef(trait_def_id), item.span, - IncludeSizedImplicitBound::Yes, + IncludeSizedImplicitBounds::Yes, ); } //`ConstIfConst` is only interested in `[const]` bounds. @@ -981,7 +981,7 @@ impl<'tcx> ItemCtxt<'tcx> { &[], ImpliedBoundsContext::TyParam(param.def_id, hir_generics.predicates), param.span, - IncludeSizedImplicitBound::Yes, + IncludeSizedImplicitBounds::Yes, ); } hir::GenericParamKind::Lifetime { .. } diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index a805ea0c29e7a..131af48cd52bd 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -2,8 +2,8 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_hir as hir; use rustc_infer::traits::util; use rustc_middle::ty::{ - self, GenericArgs, PredicateProxy, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitableExt, Upcast, shift_vars, + self, GenericArgs, IncludeSizedImplicitBounds, PredicateProxy, Ty, TyCtxt, TypeFoldable, + TypeFolder, TypeSuperFoldable, TypeVisitableExt, Upcast, shift_vars, }; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{Span, bug, span_bug}; @@ -11,7 +11,6 @@ use tracing::{debug, instrument}; use super::ItemCtxt; use super::clauses_of::assert_only_contains_clauses_from; -use crate::hir_ty_lowering::bounds::IncludeSizedImplicitBound; use crate::hir_ty_lowering::{ HirTyLowerer, ImpliedBoundsContext, OverlappingAsssocItemConstraints, PredicateFilter, }; @@ -61,7 +60,7 @@ fn associated_type_bounds<'tcx>( hir_bounds, ImpliedBoundsContext::AssociatedTypeOrImplTrait, span, - IncludeSizedImplicitBound::Yes, + IncludeSizedImplicitBounds::Yes, ); // Also collect `where Self::Assoc: Trait` from the parent trait's where clauses. @@ -382,7 +381,7 @@ fn opaque_type_bounds<'tcx>( hir_bounds, ImpliedBoundsContext::AssociatedTypeOrImplTrait, span, - IncludeSizedImplicitBound::Yes, + IncludeSizedImplicitBounds::Yes, ); } //`ConstIfConst` is only interested in `[const]` bounds. diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index c4b4b8f04427b..88924a9d6a98e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -10,8 +10,8 @@ use rustc_hir::PolyTraitRef; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_middle::ty::{ - self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, - TypeVisitor, Upcast, + self as ty, IncludeSizedImplicitBounds, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, Upcast, }; use rustc_span::{ErrorGuaranteed, Ident, Span, bug, kw}; use rustc_trait_selection::traits; @@ -137,11 +137,6 @@ fn add_trait_bound<'tcx>( bounds.insert(0, (trait_ref.upcast(tcx), span)); } -pub(crate) enum IncludeSizedImplicitBound { - Yes, - No, -} - impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Adds implicit sizedness, and in the future `Move` and `Forget`, and default trait bounds. pub(crate) fn add_implicit_bounds( @@ -152,7 +147,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { context: ImpliedBoundsContext<'tcx>, span: Span, // Sized is treated slightly more specially than the other implicit bounds. - include_sized: IncludeSizedImplicitBound, + include_sized: IncludeSizedImplicitBounds, ) { // Skip adding any default bounds if `#![rustc_no_implicit_bounds]` if find_attr!(self.tcx(), crate, RustcNoImplicitBounds) { @@ -161,7 +156,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.add_default_traits(bounds, self_ty, hir_bounds, context, span); - if let IncludeSizedImplicitBound::Yes = include_sized { + if let IncludeSizedImplicitBounds::Yes = include_sized { self.add_implicit_sizedness_bounds(bounds, self_ty, hir_bounds, context, span); } } @@ -281,7 +276,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); if let Res::Def(DefKind::Trait, def_id) = trait_ref.path.res - && (tcx.is_lang_item(def_id, LangItem::Sized) || tcx.is_default_trait(def_id)) + && (tcx.is_lang_item(def_id, LangItem::Sized) + || tcx.is_implicit_trait(def_id, IncludeSizedImplicitBounds::No)) { return; } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index 8d9174a74444a..4a763e5b89316 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -9,8 +9,8 @@ use rustc_hir::{self as hir, HirId}; use rustc_lint_defs::builtin::{BARE_TRAIT_OBJECTS, UNUSED_ASSOCIATED_TYPE_BOUNDS}; use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan; use rustc_middle::ty::{ - self, BottomUpFolder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, - TypeVisitableExt, Upcast, + self, BottomUpFolder, ExistentialPredicateStableCmpExt as _, IncludeSizedImplicitBounds, Ty, + TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, }; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{ErrorGuaranteed, Span}; @@ -23,7 +23,6 @@ use tracing::{debug, instrument}; use super::HirTyLowerer; use crate::diagnostics::DynTraitAssocItemBindingMentionsSelf; -use crate::hir_ty_lowering::bounds::IncludeSizedImplicitBound; use crate::hir_ty_lowering::{ GenericArgCountMismatch, ImpliedBoundsContext, OverlappingAsssocItemConstraints, PredicateFilter, RegionInferReason, @@ -85,7 +84,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .collect::>(), ImpliedBoundsContext::AssociatedTypeOrImplTrait, span, - IncludeSizedImplicitBound::No, + IncludeSizedImplicitBounds::No, ); let (mut elaborated_trait_bounds, elaborated_projection_bounds) = diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index fb07f250cf3ce..f15b8c81c51d2 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -43,9 +43,9 @@ use rustc_lint_defs::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::ty::{ - self, Const, FnSigKind, GenericArgKind, GenericParamDefKind, LitToConstInput, Ty, TyCtxt, - TypeSuperFoldable, TypeVisitableExt, TypingMode, Unnormalized, Upcast, const_lit_matches_ty, - fold_regions, + self, Const, FnSigKind, GenericArgKind, GenericParamDefKind, IncludeSizedImplicitBounds, + LitToConstInput, Ty, TyCtxt, TypeSuperFoldable, TypeVisitableExt, TypingMode, Unnormalized, + Upcast, const_lit_matches_ty, fold_regions, }; use rustc_session::diagnostics::feature_err; use rustc_span::def_id::{LocalModId, ModId}; @@ -57,7 +57,6 @@ use tracing::{debug, instrument}; use crate::check::check_abi; use crate::check_c_variadic_abi; use crate::diagnostics::{self, BadReturnTypeNotation, NoFieldOnType, NoVariantNamed}; -use crate::hir_ty_lowering::bounds::IncludeSizedImplicitBound; use crate::hir_ty_lowering::errors::{ GenericsArgsErrExtend, eq_ctxt_suggestion_span, prohibit_assoc_item_constraint, }; @@ -3333,7 +3332,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_bounds, ImpliedBoundsContext::AssociatedTypeOrImplTrait, hir_ty.span, - IncludeSizedImplicitBound::Yes, + IncludeSizedImplicitBounds::Yes, ); self.register_trait_ascription_bounds(bounds, hir_ty.hir_id, hir_ty.span); self_ty diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index 75552ab759fd5..12b52f4e2801e 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -1,7 +1,7 @@ use rustc_attr_ir::lang_items::LangItem; use rustc_hir as hir; use rustc_lint_defs::{declare_lint, declare_lint_pass}; -use rustc_middle::ty::Unnormalized; +use rustc_middle::ty::{IncludeSizedImplicitBounds, Unnormalized}; use crate::{LateContext, LateLintPass, LintContext}; @@ -51,7 +51,9 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable { .map(Unnormalized::skip_norm_wip) .filter_map(|(clause, _)| clause.as_trait_clause()) .filter(|pred| !cx.tcx.is_lang_item(pred.def_id(), LangItem::MetaSized)) - .filter(|pred| !cx.tcx.is_default_trait(pred.def_id())); + .filter(|pred| { + !cx.tcx.is_implicit_trait(pred.def_id(), IncludeSizedImplicitBounds::No) + }); if direct_super_traits_iter.count() > 1 { cx.emit_span_lint( MULTIPLE_SUPERTRAIT_UPCASTABLE, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 9f6a6a58bb7b3..116babf53816f 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -48,7 +48,8 @@ use rustc_structures::{CrateType, Limit}; use rustc_type_ir::TyKind::*; pub use rustc_type_ir::lift::Lift; use rustc_type_ir::{ - CollectAndApply, PlaceholderType, WithCachedTypeInfo, elaborate, search_graph, + CollectAndApply, IncludeSizedImplicitBounds, PlaceholderType, WithCachedTypeInfo, elaborate, + search_graph, }; use tracing::{debug, instrument}; @@ -955,6 +956,18 @@ impl<'tcx> TyCtxt<'tcx> { self.default_traits().iter().any(|&default_trait| self.is_lang_item(def_id, default_trait)) } + /// Returns true if a trait is an "implicit trait", which is a trait which is added + /// to bounds on generic types automatically by the compiler. + pub fn is_implicit_trait( + self, + def_id: DefId, + including_sized: IncludeSizedImplicitBounds, + ) -> bool { + self.is_default_trait(def_id) + // NOTE: add move and forget here later + || (matches!(including_sized, IncludeSizedImplicitBounds::Yes) && self.is_sizedness_trait(def_id)) + } + pub fn is_sizedness_trait(self, def_id: DefId) -> bool { matches!(self.as_lang_item(def_id), Some(LangItem::Sized | LangItem::MetaSized)) } diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index eb81ea291759b..c62bdcebec389 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -12,8 +12,8 @@ use rustc_span::{DUMMY_SP, Span, Symbol, bug}; use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::CanonicalInputData; use rustc_type_ir::{ - BoundVar, CollectAndApply, DebruijnIndex, Interner, RegionVid, TypeFoldable, Unnormalized, - VisitorResult, WithCachedTypeInfo, search_graph, try_visit, + BoundVar, CollectAndApply, DebruijnIndex, IncludeSizedImplicitBounds, Interner, RegionVid, + TypeFoldable, Unnormalized, VisitorResult, WithCachedTypeInfo, search_graph, try_visit, }; use crate::dep_graph::{DepKind, DepNodeIndex}; @@ -527,6 +527,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.is_default_trait(def_id) } + fn is_implicit_trait( + self, + def_id: Self::TraitId, + including_sized: IncludeSizedImplicitBounds, + ) -> bool { + self.is_implicit_trait(def_id, including_sized) + } + fn is_sizedness_trait(self, def_id: DefId) -> bool { self.is_sizedness_trait(def_id) } diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 5ae19b2eb6935..685897188a6bf 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -10,9 +10,9 @@ use rustc_type_ir::solve::{ QueryResultOrRerunNonErased, RerunNonErased, RerunReason, RerunResultExt, SizedTraitKind, }; use rustc_type_ir::{ - self as ty, ClausePolarity, ExistentialPredicate, FieldInfo, Interner, MayBeErased, Movability, - Region, TraitClause, TraitRef, TypeVisitableExt as _, TypingMode, Unnormalized, Upcast as _, - elaborate, + self as ty, ClausePolarity, ExistentialPredicate, FieldInfo, IncludeSizedImplicitBounds, + Interner, MayBeErased, Movability, Region, TraitClause, TraitRef, TypeVisitableExt as _, + TypingMode, Unnormalized, Upcast as _, elaborate, }; use tracing::{debug, instrument, trace, warn}; @@ -1413,7 +1413,13 @@ where // Backward compatibility for default auto traits. // Test: ui/traits/default_auto_traits/extern-types.rs - ty::Foreign(..) if self.cx().is_default_trait(goal.predicate.def_id()) => check_impls(), + ty::Foreign(..) + if self + .cx() + .is_implicit_trait(goal.predicate.def_id(), IncludeSizedImplicitBounds::No) => + { + check_impls() + } // These types cannot be structurally decomposed into constituent // types, and therefore have no built-in auto impl. diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 8a770bea8468b..0d497fe86cf44 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -15,8 +15,8 @@ use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind}; use rustc_infer::traits::{Obligation, PolyTraitObligation, PredicateObligation, SelectionError}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{ - self, ExistentialPredicate, FieldInfo, SizedTraitKind, TraitRef, Ty, TypeVisitableExt, - elaborate, + self, ExistentialPredicate, FieldInfo, IncludeSizedImplicitBounds, SizedTraitKind, TraitRef, + Ty, TypeVisitableExt, elaborate, }; use rustc_span::{DUMMY_SP, bug, span_bug}; use tracing::{debug, instrument, trace}; @@ -782,7 +782,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Backward compatibility for default auto traits. // Test: ui/traits/default_auto_traits/extern-types.rs - if self.tcx().is_default_trait(def_id) { + if self.tcx().is_implicit_trait(def_id, IncludeSizedImplicitBounds::No) { check_impls() } } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index be0114b8f3af3..4de18651b3520 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -461,6 +461,12 @@ pub trait Interner: fn is_default_trait(self, def_id: Self::TraitId) -> bool; + fn is_implicit_trait( + self, + def_id: Self::TraitId, + including_sized: IncludeSizedImplicitBounds, + ) -> bool; + fn is_sizedness_trait(self, def_id: Self::TraitId) -> bool; fn as_projection_lang_item( @@ -786,3 +792,8 @@ impl search_graph::Cx for I { self.assert_evaluation_is_concurrent() } } + +pub enum IncludeSizedImplicitBounds { + Yes, + No, +} diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 53afc311cc623..e300b94cbc438 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -461,9 +461,8 @@ pub enum CandidatePreferenceMode { impl CandidatePreferenceMode { /// Given `trait_def_id`, which candidate preference mode should be used? pub fn compute(cx: I, trait_id: I::TraitId) -> CandidatePreferenceMode { - let is_sizedness_or_auto_or_default_goal = cx.is_sizedness_trait(trait_id) - || cx.trait_is_auto(trait_id) - || cx.is_default_trait(trait_id); + let is_sizedness_or_auto_or_default_goal = cx.trait_is_auto(trait_id) + || cx.is_implicit_trait(trait_id, ty::IncludeSizedImplicitBounds::Yes); if is_sizedness_or_auto_or_default_goal { CandidatePreferenceMode::Marker } else { From aff363707b7afc9479026ed45b6e09d9dd67a476 Mon Sep 17 00:00:00 2001 From: Bee Date: Mon, 2 Nov 2026 18:21:15 +0000 Subject: [PATCH 57/72] Don't use `llvm_type` as the type of `static`s. --- compiler/rustc_codegen_llvm/src/consts.rs | 54 +++++++++---------- compiler/rustc_codegen_llvm/src/llvm/mod.rs | 6 +++ compiler/rustc_codegen_llvm/src/mono_item.rs | 3 +- tests/assembly-llvm/cstring-merging.rs | 2 +- .../assembly-llvm/static-relocation-model.rs | 3 +- .../debuginfo-generic-closure-env-names.rs | 12 ++--- .../direct-access-external-data.rs | 2 +- tests/codegen-llvm/thread-local.rs | 2 +- 8 files changed, 45 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 2089f2b2008be..30b8b3682c3b1 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -22,7 +22,6 @@ use tracing::{debug, instrument, trace}; use crate::common::CodegenCx; use crate::diagnostics::SymbolAlreadyDefined; use crate::llvm::{self, Type, Value, const_ptr_auth}; -use crate::type_of::LayoutLlvmExt; use crate::{base, debuginfo}; /// Indicates whether a value originates from a `static`. @@ -200,7 +199,7 @@ fn check_and_apply_linkage<'ll, 'tcx>( let mut should_sign = false; // Declare a symbol `foo`. If `foo` is an extern_weak symbol, we declare // an extern_weak function, otherwise a global with the desired linkage. - let g1 = if matches!(attrs.import_linkage, Some(Linkage::ExternalWeak)) { + let actual_global = if matches!(attrs.import_linkage, Some(Linkage::ExternalWeak)) { // An `extern_weak` function is represented as an `Option`, // we extract the function signature and declare it as an extern_weak function // instead of an extern_weak i8. @@ -224,28 +223,11 @@ fn check_and_apply_linkage<'ll, 'tcx>( } else { cx.declare_global(sym, cx.type_i8()) }; - llvm::set_linkage(g1, base::linkage_to_llvm(linkage)); + llvm::set_linkage(actual_global, base::linkage_to_llvm(linkage)); // Normally this is done in `get_static_inner`, but when as we generate an internal global, // it will apply the dso_local to the internal global instead, so do it here, too. - cx.assume_dso_local(g1, true); - - // Declare an internal global `extern_with_linkage_foo` which - // is initialized with the address of `foo`. If `foo` is - // discarded during linking (for example, if `foo` has weak - // linkage and there are no definitions), then - // `extern_with_linkage_foo` will instead be initialized to - // zero. - let real_name = - format!("_rust_extern_with_linkage_{:016x}_{sym}", cx.tcx.stable_crate_id(LOCAL_CRATE)); - let g2 = cx.define_global(&real_name, llty).unwrap_or_else(|| { - cx.sess().dcx().emit_fatal(SymbolAlreadyDefined { - span: cx.tcx.def_span(def_id), - symbol_name: sym, - }) - }); - llvm::set_linkage(g2, llvm::Linkage::InternalLinkage); - llvm::set_unnamed_address(g2, llvm::UnnamedAddr::Global); + cx.assume_dso_local(actual_global, true); // Sign the function pointer that is used to initialize the global let initializer = if should_sign { @@ -256,18 +238,36 @@ fn check_and_apply_linkage<'ll, 'tcx>( .expect("pointer authentication functions must be available when signing"); const_ptr_auth( - cx.const_bitcast(g1, llty), + actual_global, schema.key as u32, schema.constant_discriminator as u64, None, /* address_diversity */ ) } else { - g1 + actual_global }; - llvm::set_initializer(g2, initializer); - - g2 + // Declare an internal global `extern_with_linkage_foo` which + // is initialized with the address of `foo`. If `foo` is + // discarded during linking (for example, if `foo` has weak + // linkage and there are no definitions), then + // `extern_with_linkage_foo` will instead be initialized to + // zero. + let real_name = + format!("_rust_extern_with_linkage_{:016x}_{sym}", cx.tcx.stable_crate_id(LOCAL_CRATE)); + let pointer_global = + cx.define_global(&real_name, cx.val_ty(initializer)).unwrap_or_else(|| { + cx.sess().dcx().emit_fatal(SymbolAlreadyDefined { + span: cx.tcx.def_span(def_id), + symbol_name: sym, + }) + }); + llvm::set_linkage(pointer_global, llvm::Linkage::InternalLinkage); + llvm::set_unnamed_address(pointer_global, llvm::UnnamedAddr::Global); + + llvm::set_initializer(pointer_global, initializer); + + pointer_global } else if cx.tcx.sess.target.arch == Arch::X86 && common::is_using_dlltool(&cx.tcx.sess.target) && let Some(dllimport) = crate::common::get_dllimport(cx.tcx, def_id, sym) @@ -358,7 +358,7 @@ impl<'ll> CodegenCx<'ll, '_> { } else { let ty = instance.ty(self.tcx, self.typing_env()); trace!(?ty); - self.layout_of(ty).llvm_type(self) + self.type_array(self.type_i8(), self.layout_of(ty).size.bytes()) }; self.get_static_inner(def_id, llty) } diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 0cd26ca835971..f9863da230dfb 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -262,6 +262,12 @@ pub(crate) fn add_global<'ll>(llmod: Module<'ll>, ty: &'ll Type, name_cstr: &CSt } pub(crate) fn set_initializer(llglobal: &Value, constant_val: &Value) { + unsafe { + assert_eq!( + crate::llvm::LLVMGlobalGetValueType(llglobal), + crate::llvm::LLVMTypeOf(constant_val) + ); + } unsafe { LLVMSetInitializer(llglobal, constant_val); } diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 2d64c63cf9ae4..82bd5584211c0 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -20,7 +20,6 @@ use crate::abi::FnAbiLlvmExt; use crate::builder::Builder; use crate::context::CodegenCx; use crate::diagnostics::SymbolAlreadyDefined; -use crate::type_of::LayoutLlvmExt; use crate::{base, llvm}; impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { @@ -37,7 +36,7 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { // out the llvm type from the actual evaluated initializer. let ty = if nested { self.tcx.types.unit } else { instance.ty(self.tcx, self.typing_env()) }; - let llty = self.layout_of(ty).llvm_type(self); + let llty = self.type_array(self.type_i8(), self.layout_of(ty).size.bytes()); let g = self.define_global(symbol_name, llty).unwrap_or_else(|| { self.sess() diff --git a/tests/assembly-llvm/cstring-merging.rs b/tests/assembly-llvm/cstring-merging.rs index 9c1af4ebb4c05..e198a49c52216 100644 --- a/tests/assembly-llvm/cstring-merging.rs +++ b/tests/assembly-llvm/cstring-merging.rs @@ -14,7 +14,7 @@ use std::ffi::CStr; #[unsafe(no_mangle)] static CSTR: &[u8; 4] = b"foo\0"; -// CHECK-NOT: .section +// CHECK: .section .rodata.str1.{{[12]}},"aMS" // CHECK: {{(\.L|\$)}}anon.{{.+}}: // CHECK-NEXT: .{{asciz|string}} "bar" #[unsafe(no_mangle)] diff --git a/tests/assembly-llvm/static-relocation-model.rs b/tests/assembly-llvm/static-relocation-model.rs index a47681f4532a9..66bc992219527 100644 --- a/tests/assembly-llvm/static-relocation-model.rs +++ b/tests/assembly-llvm/static-relocation-model.rs @@ -47,7 +47,8 @@ pub fn peach() -> u8 { // x64: movq EXOCHORDA{{(\(%[a-z0-9]+\))?}}, %[[REG:[a-z0-9]+]] // x64-NEXT: {{movb|movzbl}} (%[[REG]]), %{{[a-z0-9]+}} // A64: adrp [[REG2:[a-z0-9]+]], EXOCHORDA -// A64-NEXT: ldr {{[a-z0-9]+}}, {{\[}}[[REG2]], :lo12:EXOCHORDA] +// A64-NEXT: add [[REG2]], [[REG2]], :lo12:EXOCHORDA +// A64-NEXT: ldr {{[a-z0-9]+}}, {{\[}}[[REG2]]{{\]}} #[no_mangle] pub fn mango() -> u8 { unsafe { *EXOCHORDA } diff --git a/tests/codegen-llvm/debuginfo-generic-closure-env-names.rs b/tests/codegen-llvm/debuginfo-generic-closure-env-names.rs index 64bc58e1df7af..f43566ba88118 100644 --- a/tests/codegen-llvm/debuginfo-generic-closure-env-names.rs +++ b/tests/codegen-llvm/debuginfo-generic-closure-env-names.rs @@ -22,13 +22,13 @@ //@ edition: 2021 // non_generic_closure() -// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}", scope: ![[non_generic_closure_NAMESPACE:[0-9]+]], -// MSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0", scope: ![[non_generic_closure_NAMESPACE:[0-9]+]], -// CHECK: ![[non_generic_closure_NAMESPACE]] = !DINamespace(name: "non_generic_closure" +// NONMSVC-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}", scope: ![[non_generic_closure_NAMESPACE:[0-9]+]], +// MSVC-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0", scope: ![[non_generic_closure_NAMESPACE:[0-9]+]], +// CHECK-DAG: ![[non_generic_closure_NAMESPACE]] = !DINamespace(name: "non_generic_closure" -// CHECK: ![[function_containing_closure_NAMESPACE:[0-9]+]] = !DINamespace(name: "function_containing_closure" -// CHECK: ![[generic_async_function_NAMESPACE:[0-9]+]] = !DINamespace(name: "generic_async_function" -// CHECK: ![[generic_async_block_NAMESPACE:[0-9]+]] = !DINamespace(name: "generic_async_block" +// CHECK-DAG: ![[function_containing_closure_NAMESPACE:[0-9]+]] = !DINamespace(name: "function_containing_closure" +// CHECK-DAG: ![[generic_async_function_NAMESPACE:[0-9]+]] = !DINamespace(name: "generic_async_function" +// CHECK-DAG: ![[generic_async_block_NAMESPACE:[0-9]+]] = !DINamespace(name: "generic_async_block" // function_containing_closure() // NONMSVC-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}", scope: ![[function_containing_closure_NAMESPACE]] diff --git a/tests/codegen-llvm/direct-access-external-data.rs b/tests/codegen-llvm/direct-access-external-data.rs index 43ee290d3a058..135c7e32b4a5d 100644 --- a/tests/codegen-llvm/direct-access-external-data.rs +++ b/tests/codegen-llvm/direct-access-external-data.rs @@ -17,7 +17,7 @@ unsafe extern "C" { // PIE-NOT: dso_local // DIRECT-SAME: dso_local // INDIRECT-NOT: dso_local - // CHECK-SAME: global i32 + // CHECK-SAME: global [4 x i8] safe static VAR: i32; // When "linkage" is used, we generate an indirection global. diff --git a/tests/codegen-llvm/thread-local.rs b/tests/codegen-llvm/thread-local.rs index 70b655fd5cf61..a3584154e079d 100644 --- a/tests/codegen-llvm/thread-local.rs +++ b/tests/codegen-llvm/thread-local.rs @@ -16,8 +16,8 @@ use std::cell::Cell; thread_local!(static A: Cell = const { Cell::new(1) }); -// CHECK: [[TLS_AUX:@.+]] = external thread_local{{.*}} global i64 // CHECK: [[TLS:@.+]] = internal thread_local{{.*}} global +// CHECK: [[TLS_AUX:@.+]] = external thread_local{{.*}} global [8 x i8] // CHECK-LABEL: @get #[no_mangle] From 79520af69f58695f824f54ae76a4b6e594ff32a4 Mon Sep 17 00:00:00 2001 From: Andrew Teylu Date: Thu, 10 Sep 2026 19:02:06 +0100 Subject: [PATCH 58/72] Avoid oscillating line wrapping due to symbol indices A comment against PR 151817 mentioned that the test file added in this PR kept causing "arbitrary" test file changes. This is because one line of the expanded output sits *just* at 78 columns, which is the pretty-printer's margin. Adding or removing a symbol shifts every index after it, and when one of those gains or loses a digit, the wrapping moves. We cannot use `normalize-stdout` as the layout is decided before normalization runs. We fix this by avoiding a strict golden comparison, and instead using a regex to directly test that the expected syntax contexts exist in the expanded output. --- tests/ui/hygiene/unpretty-debug-lifetimes.rs | 11 +++++-- .../hygiene/unpretty-debug-lifetimes.stdout | 33 ------------------- 2 files changed, 9 insertions(+), 35 deletions(-) delete mode 100644 tests/ui/hygiene/unpretty-debug-lifetimes.stdout diff --git a/tests/ui/hygiene/unpretty-debug-lifetimes.rs b/tests/ui/hygiene/unpretty-debug-lifetimes.rs index ee8be21b60d01..5f8a20281ab04 100644 --- a/tests/ui/hygiene/unpretty-debug-lifetimes.rs +++ b/tests/ui/hygiene/unpretty-debug-lifetimes.rs @@ -4,8 +4,15 @@ // Regression test for lifetime hygiene annotations in -Zunpretty=expanded,hygiene // Previously, lifetimes were missing the #N syntax context suffix. -// Don't break whenever Symbol numbering changes -//@ normalize-stdout: "\d+#" -> "0#" +// This test deliberately has no `.stdout` snapshot. The hygiene annotations +// embed `Symbol` indices, whose width feeds into the pretty-printer's line +// breaking, so the layout shifts whenever a `Symbol` gains or loses a digit. +// `normalize-stdout` can't help, because the layout is decided before +// normalization runs. Assert the property this test is about instead: the two +// `'a` share their text but carry different syntax contexts. +//@ dont-check-compiler-stdout +//@ check-stdout +//@ regex-error-pattern: f\s*/\*\s*\d+#0\s*\*/<'a\s*/\*\s*\d+#0\s*\*/,\s*'a\s*/\*\s*\d+#1\s*\*/ #![feature(decl_macro)] #![feature(no_core)] diff --git a/tests/ui/hygiene/unpretty-debug-lifetimes.stdout b/tests/ui/hygiene/unpretty-debug-lifetimes.stdout deleted file mode 100644 index c75cc7b2179d3..0000000000000 --- a/tests/ui/hygiene/unpretty-debug-lifetimes.stdout +++ /dev/null @@ -1,33 +0,0 @@ -//@ check-pass -//@ compile-flags: -Zunpretty=expanded,hygiene - -// Regression test for lifetime hygiene annotations in -Zunpretty=expanded,hygiene -// Previously, lifetimes were missing the #N syntax context suffix. - -// Don't break whenever Symbol numbering changes -//@ normalize-stdout: "\d+#" -> "0#" - -#![feature /* 0#0 */(decl_macro /* 0#0 */)] -#![feature /* 0#0 */(no_core /* 0#0 */)] -#![no_core /* 0#0 */] - -macro lifetime_hygiene - /* - 0#0 - */ { - ($f /* 0#0 */:ident /* 0#0 */<$a /* 0#0 */:lifetime /* 0#0 - */>) => - { fn /* 0#0 */ $f /* 0#0 */<$a /* 0#0 */, 'a /* 0#0 */>() {} } -} -fn f /* 0#0 */<'a /* 0#0 */, 'a /* 0#1 */>() {} - - -/* -Expansions: -crate0::{{expn0}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Root -crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "lifetime_hygiene") - -SyntaxContexts: -#0: parent: #0, outer_mark: (crate0::{{expn0}}, Opaque) -#1: parent: #0, outer_mark: (crate0::{{expn1}}, Opaque) -*/ From be89a2240db19fae8df147a4de49d08e568734b0 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 59/72] 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 5ccf0eaf02bd4..4411458f1dd96 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 6f078fef4892b..be3632b3e0cf5 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", @@ -990,7 +991,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 f2f5592553d0d4eceae2f6a6c26ea42f22fcf526 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 60/72] 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 be3632b3e0cf5..901774f20041c 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", @@ -829,7 +830,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 c9e0b0ac0a721307589b377423d9ea1a6d49b7c1 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 61/72] 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 4411458f1dd96..399225f48b92a 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 901774f20041c..a7fb004226733 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", @@ -1507,7 +1508,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 be1070cf2d74015a5a3b9c3fd55db5f83f29b3af 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 62/72] 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 399225f48b92a..b5d8614804812 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 a7fb004226733..bd2dec490cea6 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 @@ -2204,7 +2205,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 ee888baf74679fc9b610a2a37ddd71e4f4caeea1 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 63/72] 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 bc74ff14a1b3a..27646bac1c21e 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 bd2dec490cea6..a98b2dd746ff3 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 } @@ -2333,7 +2334,6 @@ symbols! { unwind_attributes, unwind_safe_trait, unwrap, - unwrap_binder, unwrap_or, update, use_cloned, @@ -2419,7 +2419,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 2a9fdf537866e7f192799a21ef95bdba9673119b 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 64/72] 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 3472180ae26dc..66687cfcbb06e 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 a98b2dd746ff3..6c0a9cab34f8e 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, @@ -1176,6 +1175,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 a72068e1e3549426a9533a56496c9f6441e21588 Mon Sep 17 00:00:00 2001 From: Lucas Sunsi Abreu Date: Tue, 3 Nov 2026 01:00:26 +0000 Subject: [PATCH 65/72] Fix rigidity assertion ICE * Add regression test against rigidity assertion * Make non-rigid types equal to one another on error reporting * Bless errors after rigid treatment change * Remove previously useful crash test! * Add suggested comments from code review --- .../src/error_reporting/infer/mod.rs | 9 +++++--- tests/crashes/158773.rs | 16 -------------- ...-impl-missing-assoc-type-ice-152668.stderr | 5 +++++ .../fn_def_opaque_coercion_to_fn_ptr.stderr | 4 ++++ .../issue-62529-3.stderr | 2 -- tests/ui/impl-trait/in-assoc-type.stderr | 4 ++++ .../ui/suggestions/if-then-neeing-semi.stderr | 8 +++---- .../match-prev-arm-needing-semi.stderr | 8 +++---- ...missing-associated-type-restriction.stderr | 4 ++++ ...m-concrete-alias-to-generic.current.stderr | 4 ++++ ...lias-in-same-type-modulo-infer.next.stderr | 16 ++++++++++++++ ...alias-in-same-type-modulo-infer.old.stderr | 16 ++++++++++++++ .../rigid-alias-in-same-type-modulo-infer.rs | 21 +++++++++++++++++++ 13 files changed, 86 insertions(+), 31 deletions(-) delete mode 100644 tests/crashes/158773.rs create mode 100644 tests/ui/traits/next-solver/rigid-alias-in-same-type-modulo-infer.next.stderr create mode 100644 tests/ui/traits/next-solver/rigid-alias-in-same-type-modulo-infer.old.stderr create mode 100644 tests/ui/traits/next-solver/rigid-alias-in-same-type-modulo-infer.rs diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index c0097dc5a60c0..8a0c8f598d125 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -64,8 +64,8 @@ use rustc_middle::traits::PatternOriginExpr; use rustc_middle::ty::error::{ExpectedFound, TypeError, TypeErrorToStringExt}; use rustc_middle::ty::print::{PrintTraitRefExt as _, WrapBinderMode, with_forced_trimmed_paths}; use rustc_middle::ty::{ - self, List, Mutability, ParamEnv, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, Unnormalized, + self, IsRigid, List, Mutability, ParamEnv, Region, Ty, TyCtxt, TypeFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, Unnormalized, }; use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Pos, Span, bug, sym}; use thin_vec::ThinVec; @@ -2468,6 +2468,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// with the other type. A TyVar inference type is compatible with any type, and an IntVar or /// FloatVar inference type are compatible with themselves or their concrete types (Int and /// Float types, respectively). When comparing two ADTs, these rules apply recursively. + /// Non-rigid aliases are treated just like TyVar since normalization can be ambiguous here. pub fn same_type_modulo_infer>>(&self, a: T, b: T) -> bool { let (a, b) = self.deeply_resolve_ignoring_regions((a, b)); SameTypeModuloInfer(self).relate(a, b).is_ok() @@ -2517,7 +2518,9 @@ impl<'tcx> TypeRelation> for SameTypeModuloInfer<'_, 'tcx> { ty::Float(_) | ty::Infer(ty::InferTy::FloatVar(_)), ) | (ty::Infer(ty::InferTy::TyVar(_)), _) - | (_, ty::Infer(ty::InferTy::TyVar(_))) => Ok(a), + | (_, ty::Infer(ty::InferTy::TyVar(_))) + | (ty::Alias(IsRigid::No, _), _) + | (_, ty::Alias(IsRigid::No, _)) => Ok(a), (ty::Infer(_), _) | (_, ty::Infer(_)) => Err(TypeError::Mismatch), _ => relate::structurally_relate_tys(self, a, b), } diff --git a/tests/crashes/158773.rs b/tests/crashes/158773.rs deleted file mode 100644 index 3020ab6206e4b..0000000000000 --- a/tests/crashes/158773.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ known-bug: #158773 -//@ needs-rustc-debug-assertions -//@ compile-flags: -Znext-solver=globally -trait HasLifetime { - type AtLifetime<'a>; -} - -pub struct ExistentialLifetime(S::AtLifetime<'static>); - -impl ExistentialLifetime { - fn new() -> ExistentialLifetime { - ExistentialLifetime(ExistentialLifetime(())) - } -} - -fn main() {} diff --git a/tests/ui/associated-types/dyn-impl-missing-assoc-type-ice-152668.stderr b/tests/ui/associated-types/dyn-impl-missing-assoc-type-ice-152668.stderr index 923972d02b713..f8d9b80ddbf62 100644 --- a/tests/ui/associated-types/dyn-impl-missing-assoc-type-ice-152668.stderr +++ b/tests/ui/associated-types/dyn-impl-missing-assoc-type-ice-152668.stderr @@ -48,6 +48,11 @@ LL | fn sub(self, rhs: Self) -> Self::Output {} found unit type `()` = help: consider constraining the associated type `<(dyn Vector2 + 'static) as Sub>::Output` to `()` or calling a method that returns `<(dyn Vector2 + 'static) as Sub>::Output` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +note: consider returning one of these bindings + --> $DIR/dyn-impl-missing-assoc-type-ice-152668.rs:10:12 + | +LL | fn sub(self, rhs: Self) -> Self::Output {} + | ^^^^ ^^^ error[E0277]: the size for values of type `(dyn Vector2 + 'static)` cannot be known at compilation time --> $DIR/dyn-impl-missing-assoc-type-ice-152668.rs:10:12 diff --git a/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr index 4a0991d0eb3cb..8015bc15f510c 100644 --- a/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr +++ b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr @@ -11,6 +11,8 @@ LL | x = foo::<()>; | = note: expected fn item `fn(F) -> F {bar::}` found fn item `fn(()) -> () {foo::<()>}` + = note: different fn items have unique types, even if their signatures are the same + = help: consider casting both fn items to fn pointers using `as fn(Alias(No, Alias { kind: Opaque { def_id: DefId(0:8 ~ fn_def_opaque_coercion_to_fn_ptr[7fda]::F::{opaque#0}) }, args: [], .. })) -> Alias(No, Alias { kind: Opaque { def_id: DefId(0:8 ~ fn_def_opaque_coercion_to_fn_ptr[7fda]::F::{opaque#0}) }, args: [], .. })` error[E0308]: mismatched types --> $DIR/fn_def_opaque_coercion_to_fn_ptr.rs:29:9 @@ -28,6 +30,8 @@ LL | x = foo::; | = note: expected fn item `fn(()) -> () {bar::<()>}` found fn item `fn(I) -> I {foo::}` + = note: different fn items have unique types, even if their signatures are the same + = help: consider casting both fn items to fn pointers using `as fn(())` help: use parentheses to call this function | LL | x = foo::(/* I */); diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr index f9f39e34b658e..7339b9acaea56 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr @@ -6,8 +6,6 @@ LL | call(f, ()); | | | required by a bound introduced by this call | - = note: expected type with signature `for<'a> fn(<_ as ATC<'a>>::Type)` - found type parameter with signature `fn(())` note: required by a bound in `call` --> $DIR/issue-62529-3.rs:9:36 | diff --git a/tests/ui/impl-trait/in-assoc-type.stderr b/tests/ui/impl-trait/in-assoc-type.stderr index 5a8cc40d0276e..1f824bb23f0b7 100644 --- a/tests/ui/impl-trait/in-assoc-type.stderr +++ b/tests/ui/impl-trait/in-assoc-type.stderr @@ -11,6 +11,10 @@ LL | fn foo(&self) -> >::Bar {} | = note: expected opaque type `<() as Foo<()>>::Bar` found unit type `()` +help: consider returning the local binding `self` + | +LL | fn foo(&self) -> >::Bar { self } + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/if-then-neeing-semi.stderr b/tests/ui/suggestions/if-then-neeing-semi.stderr index 0556d6c50b1ce..105e2756fb1db 100644 --- a/tests/ui/suggestions/if-then-neeing-semi.stderr +++ b/tests/ui/suggestions/if-then-neeing-semi.stderr @@ -52,12 +52,10 @@ help: consider `await`ing on the `Future` | LL | async_dummy2().await | ++++++ -help: consider removing this semicolon and boxing the expressions +help: consider removing this semicolon | -LL ~ Box::new(async_dummy()) -LL | -LL | } else { -LL ~ Box::new(async_dummy2()) +LL - async_dummy(); +LL + async_dummy() | error[E0308]: `if` and `else` have incompatible types diff --git a/tests/ui/suggestions/match-prev-arm-needing-semi.stderr b/tests/ui/suggestions/match-prev-arm-needing-semi.stderr index f70f6a1e1570b..c468433fd4421 100644 --- a/tests/ui/suggestions/match-prev-arm-needing-semi.stderr +++ b/tests/ui/suggestions/match-prev-arm-needing-semi.stderr @@ -52,12 +52,10 @@ help: consider `await`ing on the `Future` | LL | false => async_dummy2().await, | ++++++ -help: consider removing this semicolon and boxing the expressions +help: consider removing this semicolon | -LL ~ Box::new(async_dummy()) -LL | -LL | } -LL ~ false => Box::new(async_dummy2()), +LL - async_dummy(); +LL + async_dummy() | error[E0308]: `match` arms have incompatible types diff --git a/tests/ui/suggestions/trait-with-missing-associated-type-restriction.stderr b/tests/ui/suggestions/trait-with-missing-associated-type-restriction.stderr index 03249bd39a8c7..196eafa5bba68 100644 --- a/tests/ui/suggestions/trait-with-missing-associated-type-restriction.stderr +++ b/tests/ui/suggestions/trait-with-missing-associated-type-restriction.stderr @@ -11,6 +11,10 @@ help: a method is available that returns `>::A` | LL | fn func(&self) -> Self::A; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ consider calling `Trait::func` +help: consider returning the local binding `self` + | +LL | fn funq(&self) -> Self::A { self } + | ++++ error[E0308]: mismatched types --> $DIR/trait-with-missing-associated-type-restriction.rs:14:9 diff --git a/tests/ui/traits/next-solver/normalization-shadowing/is-global-norm-concrete-alias-to-generic.current.stderr b/tests/ui/traits/next-solver/normalization-shadowing/is-global-norm-concrete-alias-to-generic.current.stderr index 0ad8e3444600b..972a42d0f6621 100644 --- a/tests/ui/traits/next-solver/normalization-shadowing/is-global-norm-concrete-alias-to-generic.current.stderr +++ b/tests/ui/traits/next-solver/normalization-shadowing/is-global-norm-concrete-alias-to-generic.current.stderr @@ -13,6 +13,10 @@ LL | x = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html = note: the associated type `<(*const T,) as Id>::This` is defined as `(*const T,)` in the implementation, but the where-bound `(*const T,)` shadows this definition see issue #152409 for more information +help: use a trailing comma to create a tuple with one element + | +LL | (x,) + | + ++ error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/rigid-alias-in-same-type-modulo-infer.next.stderr b/tests/ui/traits/next-solver/rigid-alias-in-same-type-modulo-infer.next.stderr new file mode 100644 index 0000000000000..fe8ef6aff748d --- /dev/null +++ b/tests/ui/traits/next-solver/rigid-alias-in-same-type-modulo-infer.next.stderr @@ -0,0 +1,16 @@ +error[E0271]: type mismatch resolving `::AtLifetime<'static> == ExistentialLifetime<_>` + --> $DIR/rigid-alias-in-same-type-modulo-infer.rs:16:9 + | +LL | ExistentialLifetime(ExistentialLifetime(())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `ExistentialLifetime<_>`, found associated type + | + = note: expected struct `ExistentialLifetime<_>` + found associated type `::AtLifetime<'static>` +help: consider constraining the associated type `::AtLifetime<'static>` to `ExistentialLifetime<_>` + | +LL | impl = ExistentialLifetime<_>>> ExistentialLifetime { + | ++++++++++++++++++++++++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/rigid-alias-in-same-type-modulo-infer.old.stderr b/tests/ui/traits/next-solver/rigid-alias-in-same-type-modulo-infer.old.stderr new file mode 100644 index 0000000000000..fe8ef6aff748d --- /dev/null +++ b/tests/ui/traits/next-solver/rigid-alias-in-same-type-modulo-infer.old.stderr @@ -0,0 +1,16 @@ +error[E0271]: type mismatch resolving `::AtLifetime<'static> == ExistentialLifetime<_>` + --> $DIR/rigid-alias-in-same-type-modulo-infer.rs:16:9 + | +LL | ExistentialLifetime(ExistentialLifetime(())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `ExistentialLifetime<_>`, found associated type + | + = note: expected struct `ExistentialLifetime<_>` + found associated type `::AtLifetime<'static>` +help: consider constraining the associated type `::AtLifetime<'static>` to `ExistentialLifetime<_>` + | +LL | impl = ExistentialLifetime<_>>> ExistentialLifetime { + | ++++++++++++++++++++++++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/rigid-alias-in-same-type-modulo-infer.rs b/tests/ui/traits/next-solver/rigid-alias-in-same-type-modulo-infer.rs new file mode 100644 index 0000000000000..c083234411fe8 --- /dev/null +++ b/tests/ui/traits/next-solver/rigid-alias-in-same-type-modulo-infer.rs @@ -0,0 +1,21 @@ +//! Regression test for . +// Avoids ICE on SameTypeModuloInfer assertion rigidity of two types. +// +//@ revisions: old next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +trait HasLifetime { + type AtLifetime<'a>; +} + +pub struct ExistentialLifetime(S::AtLifetime<'static>); + +impl ExistentialLifetime { + fn new() -> ExistentialLifetime { + ExistentialLifetime(ExistentialLifetime(())) + //~^ ERROR: type mismatch resolving `::AtLifetime<'static> == ExistentialLifetime<_>` [E0271] + } +} + +fn main() {} From bb2e46179de95ab458b53b9cf7a349c7ab237897 Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Mon, 2 Nov 2026 20:03:43 -0500 Subject: [PATCH 66/72] 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 e083b8bbc06e491065726bdee69c2ab651bc4689 Mon Sep 17 00:00:00 2001 From: sgasho Date: Sat, 24 Oct 2026 06:46:18 +0000 Subject: [PATCH 67/72] offload: pass the linked device image to the host pass --- compiler/rustc_codegen_llvm/src/back/write.rs | 14 +- .../rustc_codegen_llvm/src/diagnostics.rs | 10 +- .../src/llvm/offload_ffi.rs | 24 +-- .../llvm-wrapper/offload/OffloadWrapper.cpp | 195 ++++-------------- tests/ui/offload/check_config.fail.stderr | 2 +- tests/ui/offload/check_config.rs | 2 +- 6 files changed, 69 insertions(+), 178 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index b8237443cccc3..01380b3f6810f 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -822,8 +822,9 @@ pub(crate) unsafe fn llvm_optimize<'ll>( }; // This assumes that we previously compiled our kernels for a gpu target, which created a - // `.bc` artifact. The user is supposed to provide us with a path to this artifact, we - // don't need any other artifacts from the previous run. + // `.ptx` (NVPTX) or `.elf` (AMDGPU) artifact. + // The user is supposed to provide us with a path to this artifact, + // we don't need any other artifacts from the previous run. if !cgcx.target_is_like_gpu && is_final_stage { if let Some(device_path) = config .offload @@ -833,15 +834,18 @@ pub(crate) unsafe fn llvm_optimize<'ll>( let device_pathbuf = PathBuf::from(device_path); if device_pathbuf.is_relative() { dcx.emit_err(crate::diagnostics::OffloadWithoutAbsPath); - } else if device_pathbuf.extension().and_then(|e| e.to_str()) != Some("bc") { + } else if !matches!( + device_pathbuf.extension().and_then(|e| e.to_str()), + Some("ptx" | "elf") + ) { dcx.emit_err(crate::diagnostics::OffloadWrongFileName); } else if !device_pathbuf.exists() { dcx.emit_err(crate::diagnostics::OffloadNonexistingPath); } - let device_bitcode_c = path_to_c_string(device_pathbuf.as_path()); + let device_image_c = path_to_c_string(device_pathbuf.as_path()); let ok = unsafe { llvm::RustOffloadWrapper::get_instance() - .llvm_rust_offload_wrap_images(llmod, device_bitcode_c.as_c_str()) + .llvm_rust_offload_wrap_images(llmod, device_image_c.as_c_str()) }; if !ok { dcx.emit_err(crate::diagnostics::OffloadWrapImagesFailed); diff --git a/compiler/rustc_codegen_llvm/src/diagnostics.rs b/compiler/rustc_codegen_llvm/src/diagnostics.rs index 094b683ded56b..19b2f68107e6c 100644 --- a/compiler/rustc_codegen_llvm/src/diagnostics.rs +++ b/compiler/rustc_codegen_llvm/src/diagnostics.rs @@ -73,7 +73,7 @@ pub(crate) struct RustOffloadComponentMissing { #[derive(Diagnostic)] #[diag( - "using the offload feature requires -Z offload=.bc>" + "using the offload feature requires -Z offload=.ptx or .elf>" )] pub(crate) struct OffloadWithoutEnable; @@ -82,18 +82,20 @@ pub(crate) struct OffloadWithoutEnable; pub(crate) struct OffloadWithoutFatLTO; #[derive(Diagnostic)] -#[diag("using the `-Z offload=Host=/absolute/path/to/.bc` flag requires an absolute path")] +#[diag( + "using the `-Z offload=Host=/absolute/path/to/.ptx` or `.elf` flag requires an absolute path" +)] pub(crate) struct OffloadWithoutAbsPath; #[derive(Diagnostic)] #[diag( - "using the `-Z offload=Host=/absolute/path/to/.bc` flag must point to a .bc file produced by the device compilation (--emit=llvm-bc)" + "using the `-Z offload=Host=/absolute/path/to/.ptx` or `.elf` flag must point to a .ptx or .elf file produced by the device compilation" )] pub(crate) struct OffloadWrongFileName; #[derive(Diagnostic)] #[diag( - "the given path/file to `.bc` does not exist. Did you forget to run the device compilation first?" + "the given path/file to `.ptx` or `.elf` does not exist. Did you forget to run the device compilation first?" )] pub(crate) struct OffloadNonexistingPath; diff --git a/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs index 60e0836a35d88..4f2369aef0d53 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs @@ -6,9 +6,8 @@ use super::ffi::{Module, Value}; type LLVMRustOffloadMapperFn = unsafe extern "C" fn(&Value, &Value, *const &Value); type LLVMRustOffloadWrapImagesFn = - for<'ll> unsafe extern "C" fn(Module<'ll>, *const c_char, *const c_char) -> bool; + for<'ll> unsafe extern "C" fn(Module<'ll>, *const c_char) -> bool; -use rustc_fs_util::path_to_c_string; use rustc_session::config::host_tuple; use rustc_session::filesearch; @@ -17,7 +16,6 @@ use crate::llvm; pub(crate) struct RustOffloadWrapper { LLVMRustOffloadMapper: LLVMRustOffloadMapperFn, LLVMRustOffloadWrapImages: LLVMRustOffloadWrapImagesFn, - lld_path: Option, // Keep the dynamic library loaded while the function pointers are used. _lib: libloading::Library, } @@ -59,18 +57,15 @@ impl RustOffloadWrapper { pub(crate) unsafe fn llvm_rust_offload_wrap_images<'ll>( &self, host_m: Module<'ll>, - device_bitcode_path: &CStr, + device_image_path: &CStr, ) -> bool { - let lld_c = self.lld_path.as_deref().map(path_to_c_string).unwrap_or_default(); - unsafe { - (self.LLVMRustOffloadWrapImages)(host_m, lld_c.as_ptr(), device_bitcode_path.as_ptr()) - } + unsafe { (self.LLVMRustOffloadWrapImages)(host_m, device_image_path.as_ptr()) } } fn call_dynamic( sysroot: &rustc_session::config::Sysroot, ) -> Result { - let (rust_offload_path, lld_path) = Self::get_offload_and_lld_paths(sysroot)?; + let rust_offload_path = Self::get_offload_path(sysroot)?; let lib = unsafe { libloading::Library::new(rust_offload_path)? }; let llvm_rust_offload_wrapper = @@ -81,14 +76,13 @@ impl RustOffloadWrapper { Ok(Self { LLVMRustOffloadMapper: llvm_rust_offload_wrapper, LLVMRustOffloadWrapImages: llvm_rust_offload_wrap_images, - lld_path, _lib: lib, }) } - fn get_offload_and_lld_paths( + fn get_offload_path( sysroot: &rustc_session::config::Sysroot, - ) -> Result<(PathBuf, Option), RustOffloadLibraryError> { + ) -> Result { let llvm_version_major = llvm::LLVMRustVersionMajor(); let mut searched = Vec::new(); @@ -102,11 +96,7 @@ impl RustOffloadWrapper { continue; } - let lld_path = filesearch::make_target_bin_path(root, host_tuple()) - .join(format!("rust-lld{}", std::env::consts::EXE_SUFFIX)); - let lld_path = lld_path.is_file().then_some(lld_path); - - return Ok((rust_offload_path, lld_path)); + return Ok(rust_offload_path); } Err(RustOffloadLibraryError::NotFound { diff --git a/compiler/rustc_llvm/llvm-wrapper/offload/OffloadWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/offload/OffloadWrapper.cpp index 5c0fabb3fcee8..6a51e58f896be 100644 --- a/compiler/rustc_llvm/llvm-wrapper/offload/OffloadWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/offload/OffloadWrapper.cpp @@ -1,29 +1,22 @@ #include "../SuppressLLVMWarnings.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/Bitcode/BitcodeReader.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Frontend/Offloading/OffloadWrapper.h" #include "llvm/Frontend/Offloading/Utility.h" #include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" -#include "llvm/MC/TargetRegistry.h" -#include "llvm/Support/CodeGen.h" #include "llvm/Support/Error.h" -#include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/MemoryBufferRef.h" +#include "llvm/Support/Path.h" #include "llvm/Support/Program.h" +#include "llvm/Support/Regex.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Target/TargetMachine.h" -#include "llvm/Target/TargetOptions.h" -#include "llvm/TargetParser/Triple.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/ValueMapper.h" @@ -34,18 +27,6 @@ using namespace llvm; -static Error writeFile(StringRef Filename, StringRef Data) { - Expected> OutputOrErr = - FileOutputBuffer::create(Filename, Data.size()); - if (!OutputOrErr) - return OutputOrErr.takeError(); - std::unique_ptr Output = std::move(*OutputOrErr); - llvm::copy(Data, Output->getBufferStart()); - if (Error E = Output->commit()) - return E; - return Error::success(); -} - // Clone OldFn into NewFn, remapping its arguments to RebuiltArgs. // Each arg of OldFn is replaced with the corresponding value in RebuiltArgs. // For scalars, RebuiltArgs contains the value cast and/or truncated to the @@ -96,37 +77,44 @@ static bool reportAndFailWrappingImages(Error E, const char *What) { return false; } +/// Returns the target architecture (e.g. "sm_86") named by the `.target` +/// directive of the given PTX text. The result points into `PtxBuf`. +static Expected getPtxArch(MemoryBufferRef PtxBuf) { + const Regex TargetRe("^\\.target[[:space:]]+([^,[:space:]]+)", + Regex::Newline); + + SmallVector M; + if (!TargetRe.match(PtxBuf.getBuffer(), &M)) + return createStringError("no .target directive in %s", + PtxBuf.getBufferIdentifier().str().c_str()); + + return M[1]; +} + static Expected> -assembleWithPtxas(StringRef Ptx, StringRef Arch) { +assembleWithPtxas(MemoryBufferRef PtxBuf) { + Expected ArchOrErr = getPtxArch(PtxBuf); + if (!ArchOrErr) + return ArchOrErr.takeError(); + const ErrorOr Ptxas = sys::findProgramByName("ptxas"); if (!Ptxas) return createStringError(Ptxas.getError(), "ptxas not found in PATH"); - SmallString<128> PtxFilePath; - if (std::error_code E = - sys::fs::createTemporaryFile("rust-offload", "ptx", PtxFilePath)) - return errorCodeToError(E); - SmallString<128> CubinFilePath; if (std::error_code E = sys::fs::createTemporaryFile("rust-offload", "cubin", CubinFilePath)) return errorCodeToError(E); auto Cleanup = scope_exit([&] { - if (std::error_code E = sys::fs::remove(PtxFilePath)) - (void)reportAndFailWrappingImages( - errorCodeToError(E), "assembleWithPtxas: PtxFilePath cleanup"); if (std::error_code E = sys::fs::remove(CubinFilePath)) (void)reportAndFailWrappingImages( errorCodeToError(E), "assembleWithPtxas: CubinFilePath cleanup"); }); - if (Error E = writeFile(PtxFilePath, Ptx)) - return std::move(E); - const StringRef Args[] = { - *Ptxas, "-m64", "-O3", "--gpu-name", - Arch, "--output-file", CubinFilePath, PtxFilePath, + *Ptxas, "-m64", "-O3", "--gpu-name", + *ArchOrErr, "--output-file", CubinFilePath, PtxBuf.getBufferIdentifier(), }; std::string ErrorMsg; @@ -145,130 +133,37 @@ assembleWithPtxas(StringRef Ptx, StringRef Arch) { return std::move(*CubinOrError); } -static Expected> -linkWithRustLld(StringRef Obj, StringRef LldPath) { - SmallString<128> ObjFilePath; - if (std::error_code E = - sys::fs::createTemporaryFile("rust-offload", "o", ObjFilePath)) - return errorCodeToError(E); - - SmallString<128> SoFilePath; - if (std::error_code E = - sys::fs::createTemporaryFile("rust-offload", "so", SoFilePath)) - return errorCodeToError(E); - - auto Cleanup = scope_exit([&] { - if (std::error_code E = sys::fs::remove(ObjFilePath)) - (void)reportAndFailWrappingImages(errorCodeToError(E), - "linkWithRustLld: ObjFilePath cleanup"); - if (std::error_code E = sys::fs::remove(SoFilePath)) - (void)reportAndFailWrappingImages(errorCodeToError(E), - "linkWithRustLld: SoFilePath cleanup"); - }); - - if (Error E = writeFile(ObjFilePath, Obj)) - return std::move(E); - - const StringRef Args[] = { - LldPath, "-flavor", "gnu", "-shared", - "--no-undefined", "-o", SoFilePath, ObjFilePath, - }; - - std::string ErrorMsg; - const int Status = - sys::ExecuteAndWait(LldPath, Args, std::nullopt, {}, 0, 0, &ErrorMsg); - - if (Status != 0) - return createStringError("linkWithRustLld: status %d: %s", Status, - ErrorMsg.c_str()); - - ErrorOr> ElfOrError = - MemoryBuffer::getFileAsStream(SoFilePath); - if (!ElfOrError) - return errorCodeToError(ElfOrError.getError()); - - return std::move(*ElfOrError); -} - -static Expected> -compileDeviceImage(MemoryBufferRef Input, const char *LldPath) { - LLVMContext Ctx; - Expected> ModOrErr = parseBitcodeFile(Input, Ctx); - if (!ModOrErr) - return ModOrErr.takeError(); - Module &M = **ModOrErr; - - const Triple &DeviceTriple = M.getTargetTriple(); - StringRef Arch; - for (Function &F : M) - if (F.hasFnAttribute("target-cpu")) { - Arch = F.getFnAttribute("target-cpu").getValueAsString(); - break; - } - - if (Arch.empty()) - return createStringError("no target-cpu attribute in device bitcode"); - - std::string ErrorMsg; - const Target *DeviceTarget = - TargetRegistry::lookupTarget(DeviceTriple, ErrorMsg); - if (!DeviceTarget) - return createStringError(ErrorMsg); - - std::unique_ptr TM(DeviceTarget->createTargetMachine( - DeviceTriple, Arch, /*Features=*/"", TargetOptions(), Reloc::PIC_)); - if (!TM) - return createStringError("createTargetMachine failed for %s", - DeviceTriple.str().c_str()); - - const bool IsNvptx = DeviceTriple.isNVPTX(); - - legacy::PassManager PM; - SmallString<0> Emitted; - raw_svector_ostream OS(Emitted); - const CodeGenFileType FileType = - IsNvptx ? CodeGenFileType::AssemblyFile : CodeGenFileType::ObjectFile; - if (TM->addPassesToEmitFile(PM, OS, nullptr, FileType)) - return createStringError("target %s cannot emit %s", - DeviceTriple.str().c_str(), - IsNvptx ? "assembly" : "object"); - - PM.run(**ModOrErr); - - if (IsNvptx) - return assembleWithPtxas(Emitted, Arch); - if (DeviceTriple.isAMDGPU()) { - if (!LldPath || !*LldPath) - return createStringError("rust-lld path was not provided for %s", - DeviceTriple.str().c_str()); - return linkWithRustLld(Emitted, LldPath); - } - - return createStringError("unsupported offload target %s", - DeviceTriple.str().c_str()); -} - extern "C" bool LLVMRustOffloadWrapImages(LLVMModuleRef HostMRef, - const char *LldPath, - const char *DeviceBitcodePath) { + const char *DeviceImagePath) { Module &M = *unwrap(HostMRef); if (!hasOffloadEntries(M)) return true; ErrorOr> BufOrErr = - MemoryBuffer::getFile(DeviceBitcodePath); + MemoryBuffer::getFile(DeviceImagePath); if (std::error_code EC = BufOrErr.getError()) - return reportAndFailWrappingImages(createFileError(DeviceBitcodePath, EC), - "device bitcode"); + return reportAndFailWrappingImages(createFileError(DeviceImagePath, EC), + "device image"); std::unique_ptr Buf = std::move(*BufOrErr); - Expected> ImageOrErr = - compileDeviceImage(Buf->getMemBufferRef(), LldPath); - if (!ImageOrErr) - return reportAndFailWrappingImages(ImageOrErr.takeError(), - "device compile"); + std::unique_ptr DeviceImage; + StringRef Ext = sys::path::extension(DeviceImagePath); + if (Ext == ".ptx") { + Expected> CubinOrErr = + assembleWithPtxas(Buf->getMemBufferRef()); + if (!CubinOrErr) + return reportAndFailWrappingImages(CubinOrErr.takeError(), "ptxas"); + DeviceImage = std::move(*CubinOrErr); + } else if (Ext == ".elf") { + DeviceImage = std::move(Buf); + } else { + return reportAndFailWrappingImages( + createStringError("unsupported device image %s: expected .ptx or .elf", + DeviceImagePath), + "device image path"); + } - StringRef ImageBuf = (*ImageOrErr)->getBuffer(); + StringRef ImageBuf = DeviceImage->getBuffer(); ArrayRef Image(ImageBuf.data(), ImageBuf.size()); if (Error E = offloading::wrapOpenMPBinaries( diff --git a/tests/ui/offload/check_config.fail.stderr b/tests/ui/offload/check_config.fail.stderr index 87df9d42bafd1..6f4ae31491d86 100644 --- a/tests/ui/offload/check_config.fail.stderr +++ b/tests/ui/offload/check_config.fail.stderr @@ -1,4 +1,4 @@ -error: using the offload feature requires -Z offload=.bc> +error: using the offload feature requires -Z offload=.ptx or .elf> error: using the offload feature requires -C lto=fat diff --git a/tests/ui/offload/check_config.rs b/tests/ui/offload/check_config.rs index 9865d36049a8f..d28d27d2c9d10 100644 --- a/tests/ui/offload/check_config.rs +++ b/tests/ui/offload/check_config.rs @@ -6,7 +6,7 @@ //@[pass] compile-flags: -Zunstable-options -Zoffload=Device -Clto=fat --emit=metadata //@[fail] compile-flags: -Clto=thin -//[fail]~? ERROR: using the offload feature requires -Z offload=.bc> +//[fail]~? ERROR: using the offload feature requires -Z offload=.ptx or .elf> //[fail]~? ERROR: using the offload feature requires -C lto=fat #![feature(gpu_offload)] From b76246de20a0840d70d367561eb5680f249fc757 Mon Sep 17 00:00:00 2001 From: sgasho Date: Sun, 25 Oct 2026 08:17:22 +0000 Subject: [PATCH 68/72] offload: make the device build work without `--emit=llvm-bc`/`llvm-ir` --- compiler/rustc_codegen_llvm/src/back/write.rs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 01380b3f6810f..7e71755ed4e70 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -657,22 +657,17 @@ pub(crate) unsafe fn llvm_optimize<'ll>( None }; - fn handle_offload<'ll>(cx: &SimpleCx<'ll>, old_fn: &'ll llvm::Value) { + // Wraps `old_fn` into a new kernel that takes the implicit `dyn_ptr` as its first argument. + // Returns the new function, or `None` if nothing was wrapped + fn handle_offload<'ll>( + cx: &SimpleCx<'ll>, + old_fn: &'ll llvm::Value, + ) -> Option<&'ll llvm::Value> { let old_fn_ty = cx.get_type_of_global(old_fn); let old_param_types = cx.func_params_types(old_fn_ty); let old_param_count = old_param_types.len(); if old_param_count == 0 { - return; - } - - let first_param = llvm::get_param(old_fn, 0); - let c_name = llvm::get_value_name(first_param); - let first_arg_name = str::from_utf8(&c_name).unwrap(); - // We might call llvm_optimize (and thus this code) multiple times on the same IR, - // but we shouldn't add this helper ptr multiple times. - // FIXME(offload): This could break if the user calls his first argument `dyn_ptr`. - if first_arg_name == "dyn_ptr" { - return; + return None; } // Create the new parameter list, with ptr as the first argument @@ -747,6 +742,7 @@ pub(crate) unsafe fn llvm_optimize<'ll>( } // Now we can re-use the old name, without name collision. llvm::set_value_name(new_fn, &name); + Some(new_fn) } if cgcx.target_is_like_gpu @@ -755,10 +751,13 @@ pub(crate) unsafe fn llvm_optimize<'ll>( let cx = SimpleCx::new(llmod, llcx, cgcx.pointer_size); for func in cx.get_functions() { let offload_kernel = "offload-kernel"; - if attributes::has_string_attr(func, offload_kernel) { - handle_offload(&cx, func); + if !attributes::has_string_attr(func, offload_kernel) { + continue; + } + match handle_offload(&cx, func) { + Some(new_fn) => attributes::remove_string_attr_from_llfn(new_fn, offload_kernel), + None => attributes::remove_string_attr_from_llfn(func, offload_kernel), } - attributes::remove_string_attr_from_llfn(func, offload_kernel); } } From c6749d8027e05f8a39609e5cec386f872b5a1da1 Mon Sep 17 00:00:00 2001 From: sgasho Date: Thu, 29 Oct 2026 15:27:50 +0000 Subject: [PATCH 69/72] offload: build bin crates as cdylib on the device side --- compiler/rustc_interface/src/passes.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 7df2ede5053b2..6dc3f74f054f1 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -36,7 +36,7 @@ use rustc_parse::lexer::StripTokens; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_passes::{abi_test, input_stats, layout_test}; use rustc_resolve::{Resolver, ResolverOutputs}; -use rustc_session::config::{Input, OutFileName, OutputFilenames, OutputType}; +use rustc_session::config::{Input, Offload, OutFileName, OutputFilenames, OutputType}; use rustc_session::diagnostics::feature_err; use rustc_session::output::{filename_for_input, invalid_output_for_target}; use rustc_session::search_paths::PathKind; @@ -1485,6 +1485,16 @@ pub fn collect_crate_types( } } + // GPU code is never run as a binary; the offload runtime looks up kernels by + // name, and only cdylibs export their symbols. + if session.opts.unstable_opts.offload.iter().any(|o| matches!(o, Offload::Device(_))) { + for crate_type in &mut base { + if *crate_type == CrateType::Executable { + *crate_type = CrateType::Cdylib; + } + } + } + base.retain(|crate_type| { if invalid_output_for_target(session, *crate_type) { session.dcx().emit_warn(diagnostics::UnsupportedCrateTypeForTarget { From dedfa67f3baca095348c4c6225a4a980b231cefc Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 26 Oct 2026 17:20:48 +0100 Subject: [PATCH 70/72] Make `ExpectedLint` more general --- compiler/rustc_attr_parsing/src/attributes/lint.rs | 6 +++--- compiler/rustc_attr_parsing/src/diagnostics.rs | 8 ++++---- .../deduplicate-diagnostics.deduplicate.stderr | 2 +- .../deduplicate-diagnostics.duplicate.stderr | 6 +++--- tests/ui/diagnostic-flags/deduplicate-diagnostics.rs | 6 +++--- tests/ui/lint/lint-attribute-literals.rs | 12 ++++++------ tests/ui/lint/lint-attribute-literals.stderr | 12 ++++++------ 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/lint.rs b/compiler/rustc_attr_parsing/src/attributes/lint.rs index db57b23f6296a..bf5450651cee8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint.rs @@ -100,15 +100,15 @@ impl LintParser { } } MetaItemOrLitParser::Lit(lit) => { - cx.emit_err(diagnostics::BadAttributeLit { + cx.emit_err(diagnostics::MalformedLintAttributeInput { span: item.span(), help: if lit.kind.is_str() { - diagnostics::BadAttributeLitHelp::StrLit { + diagnostics::MalformedLintAttributeInputHelp::StrLit { sym: lit.symbol, kind: kind.sym(), } } else { - diagnostics::BadAttributeLitHelp::Other { kind: kind.sym() } + diagnostics::MalformedLintAttributeInputHelp::Other { kind: kind.sym() } }, }); } diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index b5bfbc9e3dc3b..0c68090615944 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -2153,17 +2153,17 @@ pub(crate) enum MalformedAttributeKind { } #[derive(Diagnostic)] -#[diag("expected a lint, found literal")] -pub(crate) struct BadAttributeLit { +#[diag("expected a lint")] +pub(crate) struct MalformedLintAttributeInput { #[primary_span] #[label("not the name of a lint")] pub span: Span, #[subdiagnostic] - pub help: BadAttributeLitHelp, + pub help: MalformedLintAttributeInputHelp, } #[derive(Subdiagnostic)] -pub(crate) enum BadAttributeLitHelp { +pub(crate) enum MalformedLintAttributeInputHelp { #[help("try using `#[{$kind}({$sym})]`")] StrLit { sym: Symbol, kind: Symbol }, #[help("for example, write `#[{$kind}(dead_code)]` for `dead_code`")] diff --git a/tests/ui/diagnostic-flags/deduplicate-diagnostics.deduplicate.stderr b/tests/ui/diagnostic-flags/deduplicate-diagnostics.deduplicate.stderr index 114c37188a9ef..8cbfed641d789 100644 --- a/tests/ui/diagnostic-flags/deduplicate-diagnostics.deduplicate.stderr +++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics.deduplicate.stderr @@ -1,4 +1,4 @@ -error: expected a lint, found literal +error: expected a lint --> $DIR/deduplicate-diagnostics.rs:10:8 | LL | #[deny("literal")] diff --git a/tests/ui/diagnostic-flags/deduplicate-diagnostics.duplicate.stderr b/tests/ui/diagnostic-flags/deduplicate-diagnostics.duplicate.stderr index 66d0d94c0e815..6123e0d1ddbf1 100644 --- a/tests/ui/diagnostic-flags/deduplicate-diagnostics.duplicate.stderr +++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics.duplicate.stderr @@ -1,4 +1,4 @@ -error: expected a lint, found literal +error: expected a lint --> $DIR/deduplicate-diagnostics.rs:10:8 | LL | #[deny("literal")] @@ -20,7 +20,7 @@ LL | #[derive(Unresolved)] | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint, found literal +error: expected a lint --> $DIR/deduplicate-diagnostics.rs:10:8 | LL | #[deny("literal")] @@ -29,7 +29,7 @@ LL | #[deny("literal")] = help: try using `#[deny(literal)]` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint, found literal +error: expected a lint --> $DIR/deduplicate-diagnostics.rs:10:8 | LL | #[deny("literal")] diff --git a/tests/ui/diagnostic-flags/deduplicate-diagnostics.rs b/tests/ui/diagnostic-flags/deduplicate-diagnostics.rs index b070ec0f7dd93..fb9b8c765bd19 100644 --- a/tests/ui/diagnostic-flags/deduplicate-diagnostics.rs +++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics.rs @@ -7,7 +7,7 @@ //[duplicate]~| ERROR cannot find derive macro `Unresolved` in this scope struct S; -#[deny("literal")] //~ ERROR expected a lint, found literal - //[duplicate]~| ERROR expected a lint, found literal - //[duplicate]~| ERROR expected a lint, found literal +#[deny("literal")] //~ ERROR expected a lint + //[duplicate]~| ERROR expected a lint + //[duplicate]~| ERROR expected a lint fn main() {} diff --git a/tests/ui/lint/lint-attribute-literals.rs b/tests/ui/lint/lint-attribute-literals.rs index 34a4757adec3c..0140e64356d06 100644 --- a/tests/ui/lint/lint-attribute-literals.rs +++ b/tests/ui/lint/lint-attribute-literals.rs @@ -1,11 +1,11 @@ // Checks that lint attributes with literals have an explanatory error #[allow("literal")] -//~^ ERROR expected a lint, found literal -//~| ERROR expected a lint, found literal -//~| ERROR expected a lint, found literal +//~^ ERROR expected a lint +//~| ERROR expected a lint +//~| ERROR expected a lint #[deny(1.0)] -//~^ ERROR expected a lint, found literal -//~| ERROR expected a lint, found literal -//~| ERROR expected a lint, found literal +//~^ ERROR expected a lint +//~| ERROR expected a lint +//~| ERROR expected a lint fn main() {} diff --git a/tests/ui/lint/lint-attribute-literals.stderr b/tests/ui/lint/lint-attribute-literals.stderr index 7434f8b5c5d4b..7cad3ac9c7a33 100644 --- a/tests/ui/lint/lint-attribute-literals.stderr +++ b/tests/ui/lint/lint-attribute-literals.stderr @@ -1,4 +1,4 @@ -error: expected a lint, found literal +error: expected a lint --> $DIR/lint-attribute-literals.rs:3:9 | LL | #[allow("literal")] @@ -6,7 +6,7 @@ LL | #[allow("literal")] | = help: try using `#[allow(literal)]` -error: expected a lint, found literal +error: expected a lint --> $DIR/lint-attribute-literals.rs:7:8 | LL | #[deny(1.0)] @@ -14,7 +14,7 @@ LL | #[deny(1.0)] | = help: for example, write `#[deny(dead_code)]` for `dead_code` -error: expected a lint, found literal +error: expected a lint --> $DIR/lint-attribute-literals.rs:3:9 | LL | #[allow("literal")] @@ -23,7 +23,7 @@ LL | #[allow("literal")] = help: try using `#[allow(literal)]` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint, found literal +error: expected a lint --> $DIR/lint-attribute-literals.rs:7:8 | LL | #[deny(1.0)] @@ -32,7 +32,7 @@ LL | #[deny(1.0)] = help: for example, write `#[deny(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint, found literal +error: expected a lint --> $DIR/lint-attribute-literals.rs:3:9 | LL | #[allow("literal")] @@ -41,7 +41,7 @@ LL | #[allow("literal")] = help: try using `#[allow(literal)]` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint, found literal +error: expected a lint --> $DIR/lint-attribute-literals.rs:7:8 | LL | #[deny(1.0)] From 58c0988aa3bc0f3c3cb3a85dc6444c5ac2e9c8d5 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 27 Oct 2026 10:23:16 +0100 Subject: [PATCH 71/72] Use `ExpectedLint` in more places --- .../rustc_attr_parsing/src/attributes/lint.rs | 12 +++---- .../rustc_attr_parsing/src/diagnostics.rs | 2 -- .../src/error_codes/E0452.md | 2 +- .../unsafe/proc-unsafe-attributes.rs | 6 ++-- .../unsafe/proc-unsafe-attributes.stderr | 19 ++++++----- tests/ui/error-codes/E0452.rs | 12 +++---- tests/ui/error-codes/E0452.stderr | 32 ++++++++++------- tests/ui/lint/lint-malformed.rs | 12 +++---- tests/ui/lint/lint-malformed.stderr | 34 +++++++++++-------- tests/ui/lint/pre-expansion-parsing.rs | 2 +- tests/ui/lint/pre-expansion-parsing.stderr | 7 ++-- tests/ui/lint/reasons-erroneous.rs | 12 +++---- tests/ui/lint/reasons-erroneous.stderr | 18 ++++++---- 13 files changed, 95 insertions(+), 75 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/lint.rs b/compiler/rustc_attr_parsing/src/attributes/lint.rs index bf5450651cee8..272675dc44a64 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint.rs @@ -8,6 +8,7 @@ use thin_vec::ThinVec; use crate::attributes::{AcceptMapping, AttributeParser, AttributeStability}; use crate::context::{AcceptContext, ExpectStringLiteral, FinalizeContext}; +use crate::diagnostics::BadAttributeLitHelp; use crate::parser::{ArgParser, MetaItemOrLitParser}; use crate::target_checking::AllowedTargets; use crate::target_checking::Policy::{Allow, Warn}; @@ -36,9 +37,10 @@ impl LintParser { let p = parsers.last()?.meta_item()?; let nv = p.args().as_name_value()?; if !p.path().word_is(sym::reason) { - cx.emit_err(diagnostics::MalformedAttribute { + // FIXME: We should recommend `reason` here + cx.emit_err(diagnostics::ExpectedLint { span: p.span(), - kind: diagnostics::MalformedAttributeKind::BadAttributeArgument(p.span()), + help: BadAttributeLitHelp::Other { kind: kind.sym() }, }); } else { parsers = &parsers[..(parsers.len() - 1)]; @@ -90,11 +92,9 @@ impl LintParser { nv.expect_string_literal(cx); } ArgParser::NameValue(_) | ArgParser::List(_) => { - cx.emit_err(diagnostics::MalformedAttribute { + cx.emit_err(diagnostics::ExpectedLint { span: p.span(), - kind: diagnostics::MalformedAttributeKind::BadAttributeArgument( - item.span(), - ), + help: BadAttributeLitHelp::Other { kind: kind.sym() }, }); } } diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index 0c68090615944..14484fe2daaea 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -2146,8 +2146,6 @@ pub(crate) struct MalformedAttribute { #[derive(Subdiagnostic)] pub(crate) enum MalformedAttributeKind { - #[label("bad attribute argument")] - BadAttributeArgument(#[primary_span] Span), #[label("reason in lint attribute must come last")] ReasonMustComeLast(#[primary_span] Span), } diff --git a/compiler/rustc_error_codes/src/error_codes/E0452.md b/compiler/rustc_error_codes/src/error_codes/E0452.md index 429813a7cdd4e..581687e86974a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0452.md +++ b/compiler/rustc_error_codes/src/error_codes/E0452.md @@ -3,7 +3,7 @@ An invalid lint attribute has been given. Erroneous code example: ```compile_fail,E0452 -#![allow(foo = "")] // error: malformed lint attribute +#![allow(reason = "", foo)] // error: malformed lint attribute ``` Lint attributes only accept a list of identifiers (where each identifier is a diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs index 33324685a84f4..f0212e4744264 100644 --- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs +++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs @@ -33,9 +33,9 @@ pub fn e() {} //~| ERROR: expected identifier, found keyword `unsafe` //~| ERROR: expected identifier, found keyword `unsafe` //~| ERROR: expected identifier, found keyword `unsafe` -//~| ERROR: malformed lint attribute input -//~| ERROR: malformed lint attribute input -//~| ERROR: malformed lint attribute input +//~| ERROR: expected a lint +//~| ERROR: expected a lint +//~| ERROR: expected a lint pub fn f() {} fn main() {} diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr index 04e8c4d2dd453..d6ae4d6e2d823 100644 --- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr +++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr @@ -25,11 +25,13 @@ help: escape `unsafe` to use it as an identifier LL | #[unsafe(allow(r#unsafe(dead_code)))] | ++ -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/proc-unsafe-attributes.rs:29:16 | LL | #[unsafe(allow(unsafe(dead_code)))] - | ^^^^^^^^^^^^^^^^^ bad attribute argument + | ^^^^^^^^^^^^^^^^^ not the name of a lint + | + = help: for example, write `#[allow(dead_code)]` for `dead_code` error: the `proc_macro` attribute is only usable with crates of the `proc-macro` crate type --> $DIR/proc-unsafe-attributes.rs:1:10 @@ -85,12 +87,13 @@ help: escape `unsafe` to use it as an identifier LL | #[unsafe(allow(r#unsafe(dead_code)))] | ++ -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/proc-unsafe-attributes.rs:29:16 | LL | #[unsafe(allow(unsafe(dead_code)))] - | ^^^^^^^^^^^^^^^^^ bad attribute argument + | ^^^^^^^^^^^^^^^^^ not the name of a lint | + = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `proc_macro` is not an unsafe attribute @@ -176,15 +179,15 @@ help: escape `unsafe` to use it as an identifier LL | #[unsafe(allow(r#unsafe(dead_code)))] | ++ -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/proc-unsafe-attributes.rs:29:16 | LL | #[unsafe(allow(unsafe(dead_code)))] - | ^^^^^^^^^^^^^^^^^ bad attribute argument + | ^^^^^^^^^^^^^^^^^ not the name of a lint | + = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 21 previous errors -Some errors have detailed explanations: E0452, E0565. -For more information about an error, try `rustc --explain E0452`. +For more information about this error, try `rustc --explain E0565`. diff --git a/tests/ui/error-codes/E0452.rs b/tests/ui/error-codes/E0452.rs index 4e5a6c9301467..518d8bd619ef7 100644 --- a/tests/ui/error-codes/E0452.rs +++ b/tests/ui/error-codes/E0452.rs @@ -1,8 +1,8 @@ -#![allow(foo = "")] //~ ERROR E0452 - //~| ERROR E0452 - //~| ERROR E0452 - //~| ERROR E0452 - //~| ERROR E0452 - //~| ERROR E0452 +#![allow(foo = "")] //~ ERROR expected a lint + //~| ERROR expected a lint + //~| ERROR expected a lint + //~| ERROR expected a lint + //~| ERROR expected a lint + //~| ERROR expected a lint fn main() { } diff --git a/tests/ui/error-codes/E0452.stderr b/tests/ui/error-codes/E0452.stderr index c20429e363dda..99b625379a464 100644 --- a/tests/ui/error-codes/E0452.stderr +++ b/tests/ui/error-codes/E0452.stderr @@ -1,49 +1,55 @@ -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/E0452.rs:1:10 | LL | #![allow(foo = "")] - | ^^^^^^^^ bad attribute argument + | ^^^^^^^^ not the name of a lint + | + = help: for example, write `#[allow(dead_code)]` for `dead_code` -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/E0452.rs:1:10 | LL | #![allow(foo = "")] - | ^^^^^^^^ bad attribute argument + | ^^^^^^^^ not the name of a lint | + = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/E0452.rs:1:10 | LL | #![allow(foo = "")] - | ^^^^^^^^ bad attribute argument + | ^^^^^^^^ not the name of a lint | + = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/E0452.rs:1:10 | LL | #![allow(foo = "")] - | ^^^^^^^^ bad attribute argument + | ^^^^^^^^ not the name of a lint | + = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/E0452.rs:1:10 | LL | #![allow(foo = "")] - | ^^^^^^^^ bad attribute argument + | ^^^^^^^^ not the name of a lint | + = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/E0452.rs:1:10 | LL | #![allow(foo = "")] - | ^^^^^^^^ bad attribute argument + | ^^^^^^^^ not the name of a lint | + = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 6 previous errors -For more information about this error, try `rustc --explain E0452`. diff --git a/tests/ui/lint/lint-malformed.rs b/tests/ui/lint/lint-malformed.rs index cf5570753d85d..eb034e4064a6e 100644 --- a/tests/ui/lint/lint-malformed.rs +++ b/tests/ui/lint/lint-malformed.rs @@ -1,8 +1,8 @@ #![deny = "foo"] //~ ERROR malformed `deny` attribute input -#![allow(bar = "baz")] //~ ERROR malformed lint attribute - //~| ERROR malformed lint attribute - //~| ERROR malformed lint attribute - //~| ERROR malformed lint attribute - //~| ERROR malformed lint attribute - //~| ERROR malformed lint attribute +#![allow(bar = "baz")] //~ ERROR expected a lint + //~| ERROR expected a lint + //~| ERROR expected a lint + //~| ERROR expected a lint + //~| ERROR expected a lint + //~| ERROR expected a lint fn main() { } diff --git a/tests/ui/lint/lint-malformed.stderr b/tests/ui/lint/lint-malformed.stderr index 7be928de86985..024213eca84d3 100644 --- a/tests/ui/lint/lint-malformed.stderr +++ b/tests/ui/lint/lint-malformed.stderr @@ -1,31 +1,36 @@ -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/lint-malformed.rs:2:10 | LL | #![allow(bar = "baz")] - | ^^^^^^^^^^^ bad attribute argument + | ^^^^^^^^^^^ not the name of a lint + | + = help: for example, write `#[allow(dead_code)]` for `dead_code` -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/lint-malformed.rs:2:10 | LL | #![allow(bar = "baz")] - | ^^^^^^^^^^^ bad attribute argument + | ^^^^^^^^^^^ not the name of a lint | + = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/lint-malformed.rs:2:10 | LL | #![allow(bar = "baz")] - | ^^^^^^^^^^^ bad attribute argument + | ^^^^^^^^^^^ not the name of a lint | + = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/lint-malformed.rs:2:10 | LL | #![allow(bar = "baz")] - | ^^^^^^^^^^^ bad attribute argument + | ^^^^^^^^^^^ not the name of a lint | + = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0539]: malformed `deny` attribute input @@ -49,23 +54,24 @@ LL - #![deny = "foo"] LL + #![deny(lint1, lint2, lint3, reason = "...")] | -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/lint-malformed.rs:2:10 | LL | #![allow(bar = "baz")] - | ^^^^^^^^^^^ bad attribute argument + | ^^^^^^^^^^^ not the name of a lint | + = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/lint-malformed.rs:2:10 | LL | #![allow(bar = "baz")] - | ^^^^^^^^^^^ bad attribute argument + | ^^^^^^^^^^^ not the name of a lint | + = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 7 previous errors -Some errors have detailed explanations: E0452, E0539. -For more information about an error, try `rustc --explain E0452`. +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/lint/pre-expansion-parsing.rs b/tests/ui/lint/pre-expansion-parsing.rs index 7cfc57ff4e903..b5be9a5000ea4 100644 --- a/tests/ui/lint/pre-expansion-parsing.rs +++ b/tests/ui/lint/pre-expansion-parsing.rs @@ -3,7 +3,7 @@ #[expect] // OK #[expect[wut]] // OK #[expect(expect)] // OK -#[expect(expect(expect))] //~ ERROR malformed lint attribute input +#[expect(expect(expect))] //~ ERROR expected a lint #[deny(({!}))] // OK #[expect[helix::]] // OK #[cfg(false)] diff --git a/tests/ui/lint/pre-expansion-parsing.stderr b/tests/ui/lint/pre-expansion-parsing.stderr index ecc8b7017d92d..92473bc29286d 100644 --- a/tests/ui/lint/pre-expansion-parsing.stderr +++ b/tests/ui/lint/pre-expansion-parsing.stderr @@ -1,9 +1,10 @@ -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/pre-expansion-parsing.rs:6:10 | LL | #[expect(expect(expect))] - | ^^^^^^^^^^^^^^ bad attribute argument + | ^^^^^^^^^^^^^^ not the name of a lint + | + = help: for example, write `#[expect(dead_code)]` for `dead_code` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0452`. diff --git a/tests/ui/lint/reasons-erroneous.rs b/tests/ui/lint/reasons-erroneous.rs index 38ad00da64cd7..32de70d33ff89 100644 --- a/tests/ui/lint/reasons-erroneous.rs +++ b/tests/ui/lint/reasons-erroneous.rs @@ -8,14 +8,14 @@ //~^ ERROR malformed `warn` attribute input //~| NOTE expected a normal string literal, not a byte string literal #![warn(bare_trait_objects, reasons = "leaders to no sure land, guides their bearings lost")] -//~^ ERROR malformed lint attribute -//~| NOTE bad attribute argument +//~^ ERROR expected a lint +//~| NOTE not the name of a lint #![warn(unsafe_code, blerp = "or in league with robbers have reversed the signposts")] -//~^ ERROR malformed lint attribute -//~| NOTE bad attribute argument +//~^ ERROR expected a lint +//~| NOTE not the name of a lint #![warn(elided_lifetimes_in_paths, reason("disrespectful to ancestors", "irresponsible to heirs"))] -//~^ ERROR malformed lint attribute -//~| NOTE bad attribute argument +//~^ ERROR expected a lint +//~| NOTE not the name of a lint #![warn(ellipsis_inclusive_range_patterns, reason = "born barren", reason = "a freak growth")] //~^ ERROR malformed lint attribute //~| NOTE reason in lint attribute must come last diff --git a/tests/ui/lint/reasons-erroneous.stderr b/tests/ui/lint/reasons-erroneous.stderr index d771366d6168e..4261d4a21c438 100644 --- a/tests/ui/lint/reasons-erroneous.stderr +++ b/tests/ui/lint/reasons-erroneous.stderr @@ -29,23 +29,29 @@ LL | #![warn(anonymous_parameters, reason = b"consider these, for we have condem | = note: expected a normal string literal, not a byte string literal -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/reasons-erroneous.rs:10:29 | LL | #![warn(bare_trait_objects, reasons = "leaders to no sure land, guides their bearings lost")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not the name of a lint + | + = help: for example, write `#[warn(dead_code)]` for `dead_code` -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/reasons-erroneous.rs:13:22 | LL | #![warn(unsafe_code, blerp = "or in league with robbers have reversed the signposts")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not the name of a lint + | + = help: for example, write `#[warn(dead_code)]` for `dead_code` -error[E0452]: malformed lint attribute input +error: expected a lint --> $DIR/reasons-erroneous.rs:16:36 | LL | #![warn(elided_lifetimes_in_paths, reason("disrespectful to ancestors", "irresponsible to heirs"))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not the name of a lint + | + = help: for example, write `#[warn(dead_code)]` for `dead_code` error[E0452]: malformed lint attribute input --> $DIR/reasons-erroneous.rs:19:44 From cdc3205c47cc3a9cff3315c7e1ddd752e3743112 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Nov 2026 09:09:02 +0100 Subject: [PATCH 72/72] Make diagnostic title consistent with other attributes --- .../rustc_attr_parsing/src/attributes/lint.rs | 21 ++++++++++++------- .../rustc_attr_parsing/src/diagnostics.rs | 3 ++- .../unsafe/proc-unsafe-attributes.rs | 6 +++--- .../unsafe/proc-unsafe-attributes.stderr | 6 +++--- ...deduplicate-diagnostics.deduplicate.stderr | 2 +- .../deduplicate-diagnostics.duplicate.stderr | 6 +++--- .../deduplicate-diagnostics.rs | 6 +++--- tests/ui/error-codes/E0452.rs | 12 +++++------ tests/ui/error-codes/E0452.stderr | 12 +++++------ tests/ui/lint/lint-attribute-literals.rs | 12 +++++------ tests/ui/lint/lint-attribute-literals.stderr | 12 +++++------ tests/ui/lint/lint-malformed.rs | 12 +++++------ tests/ui/lint/lint-malformed.stderr | 12 +++++------ tests/ui/lint/pre-expansion-parsing.rs | 2 +- tests/ui/lint/pre-expansion-parsing.stderr | 2 +- tests/ui/lint/reasons-erroneous.rs | 6 +++--- tests/ui/lint/reasons-erroneous.stderr | 6 +++--- 17 files changed, 72 insertions(+), 66 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/lint.rs b/compiler/rustc_attr_parsing/src/attributes/lint.rs index 272675dc44a64..999673db0d179 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint.rs @@ -8,7 +8,7 @@ use thin_vec::ThinVec; use crate::attributes::{AcceptMapping, AttributeParser, AttributeStability}; use crate::context::{AcceptContext, ExpectStringLiteral, FinalizeContext}; -use crate::diagnostics::BadAttributeLitHelp; +use crate::diagnostics::{MalformedLintAttributeInput, MalformedLintAttributeInputHelp}; use crate::parser::{ArgParser, MetaItemOrLitParser}; use crate::target_checking::AllowedTargets; use crate::target_checking::Policy::{Allow, Warn}; @@ -38,9 +38,10 @@ impl LintParser { let nv = p.args().as_name_value()?; if !p.path().word_is(sym::reason) { // FIXME: We should recommend `reason` here - cx.emit_err(diagnostics::ExpectedLint { + cx.emit_err(MalformedLintAttributeInput { span: p.span(), - help: BadAttributeLitHelp::Other { kind: kind.sym() }, + help: MalformedLintAttributeInputHelp::Other { kind: kind.sym() }, + kind: kind.sym(), }); } else { parsers = &parsers[..(parsers.len() - 1)]; @@ -92,24 +93,28 @@ impl LintParser { nv.expect_string_literal(cx); } ArgParser::NameValue(_) | ArgParser::List(_) => { - cx.emit_err(diagnostics::ExpectedLint { + cx.emit_err(MalformedLintAttributeInput { span: p.span(), - help: BadAttributeLitHelp::Other { kind: kind.sym() }, + help: MalformedLintAttributeInputHelp::Other { + kind: kind.sym(), + }, + kind: kind.sym(), }); } } } MetaItemOrLitParser::Lit(lit) => { - cx.emit_err(diagnostics::MalformedLintAttributeInput { + cx.emit_err(MalformedLintAttributeInput { span: item.span(), help: if lit.kind.is_str() { - diagnostics::MalformedLintAttributeInputHelp::StrLit { + MalformedLintAttributeInputHelp::StrLit { sym: lit.symbol, kind: kind.sym(), } } else { - diagnostics::MalformedLintAttributeInputHelp::Other { kind: kind.sym() } + MalformedLintAttributeInputHelp::Other { kind: kind.sym() } }, + kind: kind.sym(), }); } } diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index 14484fe2daaea..59dcd7e1535f8 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -2151,11 +2151,12 @@ pub(crate) enum MalformedAttributeKind { } #[derive(Diagnostic)] -#[diag("expected a lint")] +#[diag("malformed `{$kind}` attribute input")] pub(crate) struct MalformedLintAttributeInput { #[primary_span] #[label("not the name of a lint")] pub span: Span, + pub kind: Symbol, #[subdiagnostic] pub help: MalformedLintAttributeInputHelp, } diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs index f0212e4744264..f84f0bcbd34d4 100644 --- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs +++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs @@ -33,9 +33,9 @@ pub fn e() {} //~| ERROR: expected identifier, found keyword `unsafe` //~| ERROR: expected identifier, found keyword `unsafe` //~| ERROR: expected identifier, found keyword `unsafe` -//~| ERROR: expected a lint -//~| ERROR: expected a lint -//~| ERROR: expected a lint +//~| ERROR: malformed +//~| ERROR: malformed +//~| ERROR: malformed pub fn f() {} fn main() {} diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr index d6ae4d6e2d823..cd73156eff453 100644 --- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr +++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr @@ -25,7 +25,7 @@ help: escape `unsafe` to use it as an identifier LL | #[unsafe(allow(r#unsafe(dead_code)))] | ++ -error: expected a lint +error: malformed `allow` attribute input --> $DIR/proc-unsafe-attributes.rs:29:16 | LL | #[unsafe(allow(unsafe(dead_code)))] @@ -87,7 +87,7 @@ help: escape `unsafe` to use it as an identifier LL | #[unsafe(allow(r#unsafe(dead_code)))] | ++ -error: expected a lint +error: malformed `allow` attribute input --> $DIR/proc-unsafe-attributes.rs:29:16 | LL | #[unsafe(allow(unsafe(dead_code)))] @@ -179,7 +179,7 @@ help: escape `unsafe` to use it as an identifier LL | #[unsafe(allow(r#unsafe(dead_code)))] | ++ -error: expected a lint +error: malformed `allow` attribute input --> $DIR/proc-unsafe-attributes.rs:29:16 | LL | #[unsafe(allow(unsafe(dead_code)))] diff --git a/tests/ui/diagnostic-flags/deduplicate-diagnostics.deduplicate.stderr b/tests/ui/diagnostic-flags/deduplicate-diagnostics.deduplicate.stderr index 8cbfed641d789..ff82a449d83ba 100644 --- a/tests/ui/diagnostic-flags/deduplicate-diagnostics.deduplicate.stderr +++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics.deduplicate.stderr @@ -1,4 +1,4 @@ -error: expected a lint +error: malformed `deny` attribute input --> $DIR/deduplicate-diagnostics.rs:10:8 | LL | #[deny("literal")] diff --git a/tests/ui/diagnostic-flags/deduplicate-diagnostics.duplicate.stderr b/tests/ui/diagnostic-flags/deduplicate-diagnostics.duplicate.stderr index 6123e0d1ddbf1..3c7ce1686080c 100644 --- a/tests/ui/diagnostic-flags/deduplicate-diagnostics.duplicate.stderr +++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics.duplicate.stderr @@ -1,4 +1,4 @@ -error: expected a lint +error: malformed `deny` attribute input --> $DIR/deduplicate-diagnostics.rs:10:8 | LL | #[deny("literal")] @@ -20,7 +20,7 @@ LL | #[derive(Unresolved)] | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint +error: malformed `deny` attribute input --> $DIR/deduplicate-diagnostics.rs:10:8 | LL | #[deny("literal")] @@ -29,7 +29,7 @@ LL | #[deny("literal")] = help: try using `#[deny(literal)]` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint +error: malformed `deny` attribute input --> $DIR/deduplicate-diagnostics.rs:10:8 | LL | #[deny("literal")] diff --git a/tests/ui/diagnostic-flags/deduplicate-diagnostics.rs b/tests/ui/diagnostic-flags/deduplicate-diagnostics.rs index fb9b8c765bd19..437ed20acbeb9 100644 --- a/tests/ui/diagnostic-flags/deduplicate-diagnostics.rs +++ b/tests/ui/diagnostic-flags/deduplicate-diagnostics.rs @@ -7,7 +7,7 @@ //[duplicate]~| ERROR cannot find derive macro `Unresolved` in this scope struct S; -#[deny("literal")] //~ ERROR expected a lint - //[duplicate]~| ERROR expected a lint - //[duplicate]~| ERROR expected a lint +#[deny("literal")] //~ ERROR malformed + //[duplicate]~| ERROR malformed + //[duplicate]~| ERROR malformed fn main() {} diff --git a/tests/ui/error-codes/E0452.rs b/tests/ui/error-codes/E0452.rs index 518d8bd619ef7..6a0437df7b14e 100644 --- a/tests/ui/error-codes/E0452.rs +++ b/tests/ui/error-codes/E0452.rs @@ -1,8 +1,8 @@ -#![allow(foo = "")] //~ ERROR expected a lint - //~| ERROR expected a lint - //~| ERROR expected a lint - //~| ERROR expected a lint - //~| ERROR expected a lint - //~| ERROR expected a lint +#![allow(foo = "")] //~ ERROR malformed + //~| ERROR malformed + //~| ERROR malformed + //~| ERROR malformed + //~| ERROR malformed + //~| ERROR malformed fn main() { } diff --git a/tests/ui/error-codes/E0452.stderr b/tests/ui/error-codes/E0452.stderr index 99b625379a464..c34d9a2a01b28 100644 --- a/tests/ui/error-codes/E0452.stderr +++ b/tests/ui/error-codes/E0452.stderr @@ -1,4 +1,4 @@ -error: expected a lint +error: malformed `allow` attribute input --> $DIR/E0452.rs:1:10 | LL | #![allow(foo = "")] @@ -6,7 +6,7 @@ LL | #![allow(foo = "")] | = help: for example, write `#[allow(dead_code)]` for `dead_code` -error: expected a lint +error: malformed `allow` attribute input --> $DIR/E0452.rs:1:10 | LL | #![allow(foo = "")] @@ -15,7 +15,7 @@ LL | #![allow(foo = "")] = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint +error: malformed `allow` attribute input --> $DIR/E0452.rs:1:10 | LL | #![allow(foo = "")] @@ -24,7 +24,7 @@ LL | #![allow(foo = "")] = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint +error: malformed `allow` attribute input --> $DIR/E0452.rs:1:10 | LL | #![allow(foo = "")] @@ -33,7 +33,7 @@ LL | #![allow(foo = "")] = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint +error: malformed `allow` attribute input --> $DIR/E0452.rs:1:10 | LL | #![allow(foo = "")] @@ -42,7 +42,7 @@ LL | #![allow(foo = "")] = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint +error: malformed `allow` attribute input --> $DIR/E0452.rs:1:10 | LL | #![allow(foo = "")] diff --git a/tests/ui/lint/lint-attribute-literals.rs b/tests/ui/lint/lint-attribute-literals.rs index 0140e64356d06..7adec75210108 100644 --- a/tests/ui/lint/lint-attribute-literals.rs +++ b/tests/ui/lint/lint-attribute-literals.rs @@ -1,11 +1,11 @@ // Checks that lint attributes with literals have an explanatory error #[allow("literal")] -//~^ ERROR expected a lint -//~| ERROR expected a lint -//~| ERROR expected a lint +//~^ ERROR malformed +//~| ERROR malformed +//~| ERROR malformed #[deny(1.0)] -//~^ ERROR expected a lint -//~| ERROR expected a lint -//~| ERROR expected a lint +//~^ ERROR malformed +//~| ERROR malformed +//~| ERROR malformed fn main() {} diff --git a/tests/ui/lint/lint-attribute-literals.stderr b/tests/ui/lint/lint-attribute-literals.stderr index 7cad3ac9c7a33..b06eac094f66d 100644 --- a/tests/ui/lint/lint-attribute-literals.stderr +++ b/tests/ui/lint/lint-attribute-literals.stderr @@ -1,4 +1,4 @@ -error: expected a lint +error: malformed `allow` attribute input --> $DIR/lint-attribute-literals.rs:3:9 | LL | #[allow("literal")] @@ -6,7 +6,7 @@ LL | #[allow("literal")] | = help: try using `#[allow(literal)]` -error: expected a lint +error: malformed `deny` attribute input --> $DIR/lint-attribute-literals.rs:7:8 | LL | #[deny(1.0)] @@ -14,7 +14,7 @@ LL | #[deny(1.0)] | = help: for example, write `#[deny(dead_code)]` for `dead_code` -error: expected a lint +error: malformed `allow` attribute input --> $DIR/lint-attribute-literals.rs:3:9 | LL | #[allow("literal")] @@ -23,7 +23,7 @@ LL | #[allow("literal")] = help: try using `#[allow(literal)]` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint +error: malformed `deny` attribute input --> $DIR/lint-attribute-literals.rs:7:8 | LL | #[deny(1.0)] @@ -32,7 +32,7 @@ LL | #[deny(1.0)] = help: for example, write `#[deny(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint +error: malformed `allow` attribute input --> $DIR/lint-attribute-literals.rs:3:9 | LL | #[allow("literal")] @@ -41,7 +41,7 @@ LL | #[allow("literal")] = help: try using `#[allow(literal)]` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint +error: malformed `deny` attribute input --> $DIR/lint-attribute-literals.rs:7:8 | LL | #[deny(1.0)] diff --git a/tests/ui/lint/lint-malformed.rs b/tests/ui/lint/lint-malformed.rs index eb034e4064a6e..5fd92170363f0 100644 --- a/tests/ui/lint/lint-malformed.rs +++ b/tests/ui/lint/lint-malformed.rs @@ -1,8 +1,8 @@ #![deny = "foo"] //~ ERROR malformed `deny` attribute input -#![allow(bar = "baz")] //~ ERROR expected a lint - //~| ERROR expected a lint - //~| ERROR expected a lint - //~| ERROR expected a lint - //~| ERROR expected a lint - //~| ERROR expected a lint +#![allow(bar = "baz")] //~ ERROR malformed + //~| ERROR malformed + //~| ERROR malformed + //~| ERROR malformed + //~| ERROR malformed + //~| ERROR malformed fn main() { } diff --git a/tests/ui/lint/lint-malformed.stderr b/tests/ui/lint/lint-malformed.stderr index 024213eca84d3..ceb7fe105bcd3 100644 --- a/tests/ui/lint/lint-malformed.stderr +++ b/tests/ui/lint/lint-malformed.stderr @@ -1,4 +1,4 @@ -error: expected a lint +error: malformed `allow` attribute input --> $DIR/lint-malformed.rs:2:10 | LL | #![allow(bar = "baz")] @@ -6,7 +6,7 @@ LL | #![allow(bar = "baz")] | = help: for example, write `#[allow(dead_code)]` for `dead_code` -error: expected a lint +error: malformed `allow` attribute input --> $DIR/lint-malformed.rs:2:10 | LL | #![allow(bar = "baz")] @@ -15,7 +15,7 @@ LL | #![allow(bar = "baz")] = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint +error: malformed `allow` attribute input --> $DIR/lint-malformed.rs:2:10 | LL | #![allow(bar = "baz")] @@ -24,7 +24,7 @@ LL | #![allow(bar = "baz")] = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint +error: malformed `allow` attribute input --> $DIR/lint-malformed.rs:2:10 | LL | #![allow(bar = "baz")] @@ -54,7 +54,7 @@ LL - #![deny = "foo"] LL + #![deny(lint1, lint2, lint3, reason = "...")] | -error: expected a lint +error: malformed `allow` attribute input --> $DIR/lint-malformed.rs:2:10 | LL | #![allow(bar = "baz")] @@ -63,7 +63,7 @@ LL | #![allow(bar = "baz")] = help: for example, write `#[allow(dead_code)]` for `dead_code` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: expected a lint +error: malformed `allow` attribute input --> $DIR/lint-malformed.rs:2:10 | LL | #![allow(bar = "baz")] diff --git a/tests/ui/lint/pre-expansion-parsing.rs b/tests/ui/lint/pre-expansion-parsing.rs index b5be9a5000ea4..11a55beb77d65 100644 --- a/tests/ui/lint/pre-expansion-parsing.rs +++ b/tests/ui/lint/pre-expansion-parsing.rs @@ -3,7 +3,7 @@ #[expect] // OK #[expect[wut]] // OK #[expect(expect)] // OK -#[expect(expect(expect))] //~ ERROR expected a lint +#[expect(expect(expect))] //~ ERROR malformed #[deny(({!}))] // OK #[expect[helix::]] // OK #[cfg(false)] diff --git a/tests/ui/lint/pre-expansion-parsing.stderr b/tests/ui/lint/pre-expansion-parsing.stderr index 92473bc29286d..651fd5d4c8f38 100644 --- a/tests/ui/lint/pre-expansion-parsing.stderr +++ b/tests/ui/lint/pre-expansion-parsing.stderr @@ -1,4 +1,4 @@ -error: expected a lint +error: malformed `expect` attribute input --> $DIR/pre-expansion-parsing.rs:6:10 | LL | #[expect(expect(expect))] diff --git a/tests/ui/lint/reasons-erroneous.rs b/tests/ui/lint/reasons-erroneous.rs index 32de70d33ff89..4a0c458209ee6 100644 --- a/tests/ui/lint/reasons-erroneous.rs +++ b/tests/ui/lint/reasons-erroneous.rs @@ -8,13 +8,13 @@ //~^ ERROR malformed `warn` attribute input //~| NOTE expected a normal string literal, not a byte string literal #![warn(bare_trait_objects, reasons = "leaders to no sure land, guides their bearings lost")] -//~^ ERROR expected a lint +//~^ ERROR malformed //~| NOTE not the name of a lint #![warn(unsafe_code, blerp = "or in league with robbers have reversed the signposts")] -//~^ ERROR expected a lint +//~^ ERROR malformed //~| NOTE not the name of a lint #![warn(elided_lifetimes_in_paths, reason("disrespectful to ancestors", "irresponsible to heirs"))] -//~^ ERROR expected a lint +//~^ ERROR malformed //~| NOTE not the name of a lint #![warn(ellipsis_inclusive_range_patterns, reason = "born barren", reason = "a freak growth")] //~^ ERROR malformed lint attribute diff --git a/tests/ui/lint/reasons-erroneous.stderr b/tests/ui/lint/reasons-erroneous.stderr index 4261d4a21c438..3c52a0d8e25b4 100644 --- a/tests/ui/lint/reasons-erroneous.stderr +++ b/tests/ui/lint/reasons-erroneous.stderr @@ -29,7 +29,7 @@ LL | #![warn(anonymous_parameters, reason = b"consider these, for we have condem | = note: expected a normal string literal, not a byte string literal -error: expected a lint +error: malformed `warn` attribute input --> $DIR/reasons-erroneous.rs:10:29 | LL | #![warn(bare_trait_objects, reasons = "leaders to no sure land, guides their bearings lost")] @@ -37,7 +37,7 @@ LL | #![warn(bare_trait_objects, reasons = "leaders to no sure land, guides thei | = help: for example, write `#[warn(dead_code)]` for `dead_code` -error: expected a lint +error: malformed `warn` attribute input --> $DIR/reasons-erroneous.rs:13:22 | LL | #![warn(unsafe_code, blerp = "or in league with robbers have reversed the signposts")] @@ -45,7 +45,7 @@ LL | #![warn(unsafe_code, blerp = "or in league with robbers have reversed the s | = help: for example, write `#[warn(dead_code)]` for `dead_code` -error: expected a lint +error: malformed `warn` attribute input --> $DIR/reasons-erroneous.rs:16:36 | LL | #![warn(elided_lifetimes_in_paths, reason("disrespectful to ancestors", "irresponsible to heirs"))]