From 3f09a2054921708ac8288ac9912885dbe66682ec Mon Sep 17 00:00:00 2001
From: Jack Rickard <jack.rickard@outlook.com>
Date: Mon, 3 Feb 2025 13:56:20 +0000
Subject: [PATCH 01/11] Do not allow attributes on struct field rest patterns

This removes support for attributes on struct field rest patterns (the `..`) from the parser.
Previously they were being parsed but dropped from the AST, so didn't work and were deleted by rustfmt.
---
 compiler/rustc_parse/src/parser/pat.rs        | 22 +++++++++----------
 .../parser/attribute/attr-pat-struct-rest.rs  |  8 +++++++
 .../attribute/attr-pat-struct-rest.stderr     | 10 +++++++++
 3 files changed, 29 insertions(+), 11 deletions(-)
 create mode 100644 tests/ui/parser/attribute/attr-pat-struct-rest.rs
 create mode 100644 tests/ui/parser/attribute/attr-pat-struct-rest.stderr

diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 64bcb1a5a36cc..8ce749ec81417 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -1472,17 +1472,6 @@ impl<'a> Parser<'a> {
         let mut last_non_comma_dotdot_span = None;
 
         while self.token != token::CloseDelim(Delimiter::Brace) {
-            let attrs = match self.parse_outer_attributes() {
-                Ok(attrs) => attrs,
-                Err(err) => {
-                    if let Some(delayed) = delayed_err {
-                        delayed.emit();
-                    }
-                    return Err(err);
-                }
-            };
-            let lo = self.token.span;
-
             // check that a comma comes after every field
             if !ate_comma {
                 let err = if self.token == token::At {
@@ -1585,6 +1574,17 @@ impl<'a> Parser<'a> {
                 }
             }
 
+            let attrs = match self.parse_outer_attributes() {
+                Ok(attrs) => attrs,
+                Err(err) => {
+                    if let Some(delayed) = delayed_err {
+                        delayed.emit();
+                    }
+                    return Err(err);
+                }
+            };
+            let lo = self.token.span;
+
             let field = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
                 let field = match this.parse_pat_field(lo, attrs) {
                     Ok(field) => Ok(field),
diff --git a/tests/ui/parser/attribute/attr-pat-struct-rest.rs b/tests/ui/parser/attribute/attr-pat-struct-rest.rs
new file mode 100644
index 0000000000000..b2bfcf82df8d1
--- /dev/null
+++ b/tests/ui/parser/attribute/attr-pat-struct-rest.rs
@@ -0,0 +1,8 @@
+// #81282: Attributes are not allowed on struct field rest patterns (the ..).
+
+struct S {}
+
+fn main() {
+    let S { #[cfg(any())] .. } = S {};
+    //~^ ERROR expected identifier, found `..`
+}
diff --git a/tests/ui/parser/attribute/attr-pat-struct-rest.stderr b/tests/ui/parser/attribute/attr-pat-struct-rest.stderr
new file mode 100644
index 0000000000000..f72c54973fce7
--- /dev/null
+++ b/tests/ui/parser/attribute/attr-pat-struct-rest.stderr
@@ -0,0 +1,10 @@
+error: expected identifier, found `..`
+  --> $DIR/attr-pat-struct-rest.rs:6:27
+   |
+LL |     let S { #[cfg(any())] .. } = S {};
+   |         -                 ^^ expected identifier
+   |         |
+   |         while parsing the fields for this pattern
+
+error: aborting due to 1 previous error
+

From 0aa2e6b606fdca2c0166b6033e3ea0cb8484eeb7 Mon Sep 17 00:00:00 2001
From: yukang <moorekang@gmail.com>
Date: Tue, 11 Feb 2025 15:26:21 +0800
Subject: [PATCH 02/11] Try to recover from path sep error in parser

---
 compiler/rustc_parse/messages.ftl             |  3 --
 compiler/rustc_parse/src/errors.rs            |  8 ----
 compiler/rustc_parse/src/parser/item.rs       |  3 --
 compiler/rustc_parse/src/parser/path.rs       | 15 +++++-
 ...ingle-colon-path-not-const-generics.stderr |  4 +-
 ...ment-list-from-path-sep-error-129273.fixed | 15 ++++++
 ...rgument-list-from-path-sep-error-129273.rs | 15 ++++++
 ...ent-list-from-path-sep-error-129273.stderr | 14 ++++++
 ...truct-field-type-including-single-colon.rs |  8 ++--
 ...t-field-type-including-single-colon.stderr | 47 ++++++++++++-------
 10 files changed, 92 insertions(+), 40 deletions(-)
 create mode 100644 tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed
 create mode 100644 tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs
 create mode 100644 tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr

diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index d50bd18a1d7d8..563081c7240c4 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -743,9 +743,6 @@ parse_single_colon_import_path = expected `::`, found `:`
     .suggestion = use double colon
     .note = import paths are delimited using `::`
 
-parse_single_colon_struct_type = found single colon in a struct field type path
-    .suggestion = write a path separator here
-
 parse_static_with_generics = static items may not have generic parameters
 
 parse_struct_literal_body_without_path =
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 8f0e29c27695b..dc03d6f9521d4 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -3071,14 +3071,6 @@ pub(crate) struct BadItemKind {
     pub help: bool,
 }
 
-#[derive(Diagnostic)]
-#[diag(parse_single_colon_struct_type)]
-pub(crate) struct SingleColonStructType {
-    #[primary_span]
-    #[suggestion(code = "::", applicability = "maybe-incorrect", style = "verbose")]
-    pub span: Span,
-}
-
 #[derive(Diagnostic)]
 #[diag(parse_macro_rules_missing_bang)]
 pub(crate) struct MacroRulesMissingBang {
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 637ed2774a293..c923717ecaf27 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -2043,9 +2043,6 @@ impl<'a> Parser<'a> {
         }
         self.expect_field_ty_separator()?;
         let ty = self.parse_ty()?;
-        if self.token == token::Colon && self.look_ahead(1, |t| *t != token::Colon) {
-            self.dcx().emit_err(errors::SingleColonStructType { span: self.token.span });
-        }
         let default = if self.token == token::Eq {
             self.bump();
             let const_expr = self.parse_expr_anon_const()?;
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 576711e66777e..b241aa892db92 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -246,8 +246,19 @@ impl<'a> Parser<'a> {
             segments.push(segment);
 
             if self.is_import_coupler() || !self.eat_path_sep() {
-                if style == PathStyle::Expr
-                    && self.may_recover()
+                let ok_for_recovery = self.may_recover()
+                    && match style {
+                        PathStyle::Expr => true,
+                        PathStyle::Type if let Some((ident, _)) = self.prev_token.ident() => {
+                            self.token == token::Colon
+                                && ident.as_str().chars().all(|c| c.is_lowercase())
+                                && self.token.span.lo() == self.prev_token.span.hi()
+                                && self
+                                    .look_ahead(1, |token| self.token.span.hi() == token.span.lo())
+                        }
+                        _ => false,
+                    };
+                if ok_for_recovery
                     && self.token == token::Colon
                     && self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident())
                 {
diff --git a/tests/ui/generics/single-colon-path-not-const-generics.stderr b/tests/ui/generics/single-colon-path-not-const-generics.stderr
index 4e695b2dcd6d9..c14a5e62a0c8f 100644
--- a/tests/ui/generics/single-colon-path-not-const-generics.stderr
+++ b/tests/ui/generics/single-colon-path-not-const-generics.stderr
@@ -1,8 +1,6 @@
 error: path separator must be a double colon
   --> $DIR/single-colon-path-not-const-generics.rs:8:18
    |
-LL | pub struct Foo {
-   |            --- while parsing this struct
 LL |   a: Vec<foo::bar:A>,
    |                  ^
    |
@@ -10,7 +8,7 @@ LL |   a: Vec<foo::bar:A>,
 help: use a double colon instead
    |
 LL |   a: Vec<foo::bar::A>,
-   |                  +
+   |                   +
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed
new file mode 100644
index 0000000000000..f5dbf0c8b6f4e
--- /dev/null
+++ b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed
@@ -0,0 +1,15 @@
+//@ run-rustfix
+
+use std::fmt;
+
+struct Hello;
+
+impl fmt::Display for Hello {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { //~ ERROR path separator must be a double colon
+        write!(f, "hello")
+    }
+}
+
+fn main() {
+    let _ = Hello;
+}
diff --git a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs
new file mode 100644
index 0000000000000..c41880a26f6ec
--- /dev/null
+++ b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs
@@ -0,0 +1,15 @@
+//@ run-rustfix
+
+use std::fmt;
+
+struct Hello;
+
+impl fmt::Display for Hello {
+    fn fmt(&self, f: &mut fmt:Formatter) -> fmt::Result { //~ ERROR path separator must be a double colon
+        write!(f, "hello")
+    }
+}
+
+fn main() {
+    let _ = Hello;
+}
diff --git a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr
new file mode 100644
index 0000000000000..92947e3b177f0
--- /dev/null
+++ b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr
@@ -0,0 +1,14 @@
+error: path separator must be a double colon
+  --> $DIR/argument-list-from-path-sep-error-129273.rs:8:30
+   |
+LL |     fn fmt(&self, f: &mut fmt:Formatter) -> fmt::Result {
+   |                              ^
+   |
+   = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>
+help: use a double colon instead
+   |
+LL |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+   |                               +
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/suggestions/struct-field-type-including-single-colon.rs b/tests/ui/suggestions/struct-field-type-including-single-colon.rs
index b7ad6d996f1ab..a3111028895dd 100644
--- a/tests/ui/suggestions/struct-field-type-including-single-colon.rs
+++ b/tests/ui/suggestions/struct-field-type-including-single-colon.rs
@@ -7,14 +7,14 @@ mod foo {
 
 struct Foo {
     a: foo:A,
-    //~^ ERROR found single colon in a struct field type path
-    //~| expected `,`, or `}`, found `:`
+    //~^ ERROR path separator must be a double colon
+    //~| ERROR struct `A` is private
 }
 
 struct Bar {
     b: foo::bar:B,
-    //~^ ERROR found single colon in a struct field type path
-    //~| expected `,`, or `}`, found `:`
+    //~^ ERROR path separator must be a double colon
+    //~| ERROR module `bar` is private
 }
 
 fn main() {}
diff --git a/tests/ui/suggestions/struct-field-type-including-single-colon.stderr b/tests/ui/suggestions/struct-field-type-including-single-colon.stderr
index 5ffc5b40849b6..ce16aca1e14a2 100644
--- a/tests/ui/suggestions/struct-field-type-including-single-colon.stderr
+++ b/tests/ui/suggestions/struct-field-type-including-single-colon.stderr
@@ -1,40 +1,53 @@
-error: found single colon in a struct field type path
+error: path separator must be a double colon
   --> $DIR/struct-field-type-including-single-colon.rs:9:11
    |
 LL |     a: foo:A,
    |           ^
    |
-help: write a path separator here
+   = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>
+help: use a double colon instead
    |
 LL |     a: foo::A,
    |            +
 
-error: expected `,`, or `}`, found `:`
-  --> $DIR/struct-field-type-including-single-colon.rs:9:11
-   |
-LL | struct Foo {
-   |        --- while parsing this struct
-LL |     a: foo:A,
-   |           ^
-
-error: found single colon in a struct field type path
+error: path separator must be a double colon
   --> $DIR/struct-field-type-including-single-colon.rs:15:16
    |
 LL |     b: foo::bar:B,
    |                ^
    |
-help: write a path separator here
+   = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>
+help: use a double colon instead
    |
 LL |     b: foo::bar::B,
    |                 +
 
-error: expected `,`, or `}`, found `:`
-  --> $DIR/struct-field-type-including-single-colon.rs:15:16
+error[E0603]: struct `A` is private
+  --> $DIR/struct-field-type-including-single-colon.rs:9:12
+   |
+LL |     a: foo:A,
+   |            ^ private struct
+   |
+note: the struct `A` is defined here
+  --> $DIR/struct-field-type-including-single-colon.rs:2:5
+   |
+LL |     struct A;
+   |     ^^^^^^^^^
+
+error[E0603]: module `bar` is private
+  --> $DIR/struct-field-type-including-single-colon.rs:15:13
    |
-LL | struct Bar {
-   |        --- while parsing this struct
 LL |     b: foo::bar:B,
-   |                ^
+   |             ^^^ - struct `B` is not publicly re-exported
+   |             |
+   |             private module
+   |
+note: the module `bar` is defined here
+  --> $DIR/struct-field-type-including-single-colon.rs:3:5
+   |
+LL |     mod bar {
+   |     ^^^^^^^
 
 error: aborting due to 4 previous errors
 
+For more information about this error, try `rustc --explain E0603`.

From 351cc1f5b7d7bee7f9accf75d768e2d179a6f89d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Sat, 15 Feb 2025 03:27:22 +0100
Subject: [PATCH 03/11] rustdoc: Properly restore search input placeholder

---
 src/librustdoc/html/static/js/search.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 121a43e3d92e2..ccbd6811b0712 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -5318,8 +5318,9 @@ function registerSearchEvents() {
 
     // @ts-expect-error
     searchState.input.addEventListener("blur", () => {
-        // @ts-expect-error
-        searchState.input.placeholder = searchState.input.origPlaceholder;
+        if (window.searchState.input) {
+            window.searchState.input.placeholder = window.searchState.origPlaceholder;
+        }
     });
 
     // Push and pop states are used to add search results to the browser

From 3b4ff16bb116c81950608b3241593428c50aadad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Wed, 10 Jul 2024 13:59:42 +0200
Subject: [PATCH 04/11] Clean up
 rustc_session::output::{find,validate}_crate_name

---
 compiler/rustc_session/messages.ftl           |  6 +-
 compiler/rustc_session/src/errors.rs          | 16 +---
 compiler/rustc_session/src/output.rs          | 93 ++++++++++---------
 .../need-crate-arg-ignore-tidy$x.stderr       |  4 +-
 4 files changed, 61 insertions(+), 58 deletions(-)

diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index e5fba8cc5a2d2..f108488cd5829 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -8,7 +8,7 @@ session_cannot_mix_and_match_sanitizers = `-Zsanitizer={$first}` is incompatible
 session_cli_feature_diagnostic_help =
     add `-Zcrate-attr="feature({$feature})"` to the command-line options to enable
 
-session_crate_name_does_not_match = `--crate-name` and `#[crate_name]` are required to match, but `{$s}` != `{$name}`
+session_crate_name_does_not_match = `--crate-name` and `#[crate_name]` are required to match, but `{$crate_name}` != `{$attr_crate_name}`
 
 session_crate_name_empty = crate name must not be empty
 
@@ -52,8 +52,8 @@ session_instrumentation_not_supported = {$us} instrumentation is not supported f
 session_int_literal_too_large = integer literal is too large
     .note = value exceeds limit of `{$limit}`
 
-session_invalid_character_in_create_name = invalid character `{$character}` in crate name: `{$crate_name}`
-session_invalid_character_in_create_name_help = you can either pass `--crate-name` on the command line or add `#![crate_name="…"]` to set the crate name
+session_invalid_character_in_crate_name = invalid character {$character} in crate name: `{$crate_name}`
+    .help = you can either pass `--crate-name` on the command line or add `#![crate_name = "…"]` to set the crate name
 
 session_invalid_float_literal_suffix = invalid suffix `{$suffix}` for float literal
     .label = invalid suffix `{$suffix}`
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index 75c3b2c7a8592..b57560ff78221 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -217,8 +217,8 @@ pub(crate) struct FileWriteFail<'a> {
 pub(crate) struct CrateNameDoesNotMatch {
     #[primary_span]
     pub(crate) span: Span,
-    pub(crate) s: Symbol,
-    pub(crate) name: Symbol,
+    pub(crate) crate_name: Symbol,
+    pub(crate) attr_crate_name: Symbol,
 }
 
 #[derive(Diagnostic)]
@@ -235,20 +235,14 @@ pub(crate) struct CrateNameEmpty {
 }
 
 #[derive(Diagnostic)]
-#[diag(session_invalid_character_in_create_name)]
+#[diag(session_invalid_character_in_crate_name)]
 pub(crate) struct InvalidCharacterInCrateName {
     #[primary_span]
     pub(crate) span: Option<Span>,
     pub(crate) character: char,
     pub(crate) crate_name: Symbol,
-    #[subdiagnostic]
-    pub(crate) crate_name_help: Option<InvalidCrateNameHelp>,
-}
-
-#[derive(Subdiagnostic)]
-pub(crate) enum InvalidCrateNameHelp {
-    #[help(session_invalid_character_in_create_name_help)]
-    AddCrateName,
+    #[help]
+    pub(crate) help: Option<()>,
 }
 
 #[derive(Subdiagnostic)]
diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs
index ff0419d06bff9..a6d4ebf23c70b 100644
--- a/compiler/rustc_session/src/output.rs
+++ b/compiler/rustc_session/src/output.rs
@@ -9,7 +9,7 @@ use crate::Session;
 use crate::config::{self, CrateType, Input, OutFileName, OutputFilenames, OutputType};
 use crate::errors::{
     self, CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable,
-    InvalidCharacterInCrateName, InvalidCrateNameHelp,
+    InvalidCharacterInCrateName,
 };
 
 pub fn out_filename(
@@ -49,10 +49,13 @@ fn is_writeable(p: &Path) -> bool {
     }
 }
 
+/// Find and [validate] the crate name.
+///
+/// [validate]: validate_crate_name
 pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute]) -> Symbol {
-    let validate = |s: Symbol, span: Option<Span>| {
-        validate_crate_name(sess, s, span);
-        s
+    let validate = |name, span| {
+        validate_crate_name(sess, name, span);
+        name
     };
 
     // Look in attributes 100% of the time to make sure the attribute is marked
@@ -62,56 +65,62 @@ pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute]) -> Symbol {
     let attr_crate_name =
         attr::find_by_name(attrs, sym::crate_name).and_then(|at| at.value_str().map(|s| (at, s)));
 
-    if let Some(ref s) = sess.opts.crate_name {
-        let s = Symbol::intern(s);
-        if let Some((attr, name)) = attr_crate_name {
-            if name != s {
-                sess.dcx().emit_err(CrateNameDoesNotMatch { span: attr.span, s, name });
-            }
+    if let Some(crate_name) = &sess.opts.crate_name {
+        let crate_name = Symbol::intern(crate_name);
+        if let Some((attr, attr_crate_name)) = attr_crate_name
+            && attr_crate_name != crate_name
+        {
+            sess.dcx().emit_err(CrateNameDoesNotMatch {
+                span: attr.span,
+                crate_name,
+                attr_crate_name,
+            });
         }
-        return validate(s, None);
+        return validate(crate_name, None);
     }
 
-    if let Some((attr, s)) = attr_crate_name {
-        return validate(s, Some(attr.span));
+    if let Some((attr, crate_name)) = attr_crate_name {
+        return validate(crate_name, Some(attr.span));
     }
-    if let Input::File(ref path) = sess.io.input {
-        if let Some(s) = path.file_stem().and_then(|s| s.to_str()) {
-            if s.starts_with('-') {
-                sess.dcx().emit_err(CrateNameInvalid { s });
-            } else {
-                return validate(Symbol::intern(&s.replace('-', "_")), None);
-            }
+
+    if let Input::File(ref path) = sess.io.input
+        && let Some(s) = path.file_stem().and_then(|s| s.to_str())
+    {
+        if s.starts_with('-') {
+            sess.dcx().emit_err(CrateNameInvalid { s });
+        } else {
+            return validate(Symbol::intern(&s.replace('-', "_")), None);
         }
     }
 
     sym::rust_out
 }
 
-pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) {
+/// Validate the given crate name.
+///
+/// Note that this validation is more permissive than identifier parsing. It considers
+/// non-empty sequences of alphanumeric and underscore characters to be valid crate names.
+/// Most notably, it accepts names starting with a numeric character like `0`!
+///
+/// Furthermore, this shouldn't be taken as the canonical crate name validator.
+/// Other places may use a more restrictive grammar (e.g., identifier or ASCII identifier).
+pub fn validate_crate_name(sess: &Session, crate_name: Symbol, span: Option<Span>) {
     let mut guar = None;
-    {
-        if s.is_empty() {
-            guar = Some(sess.dcx().emit_err(CrateNameEmpty { span: sp }));
-        }
-        for c in s.as_str().chars() {
-            if c.is_alphanumeric() {
-                continue;
-            }
-            if c == '_' {
-                continue;
-            }
-            guar = Some(sess.dcx().emit_err(InvalidCharacterInCrateName {
-                span: sp,
-                character: c,
-                crate_name: s,
-                crate_name_help: if sp.is_none() {
-                    Some(InvalidCrateNameHelp::AddCrateName)
-                } else {
-                    None
-                },
-            }));
+
+    if crate_name.is_empty() {
+        guar = Some(sess.dcx().emit_err(CrateNameEmpty { span }));
+    }
+
+    for c in crate_name.as_str().chars() {
+        if c.is_alphanumeric() || c == '_' {
+            continue;
         }
+        guar = Some(sess.dcx().emit_err(InvalidCharacterInCrateName {
+            span,
+            character: c,
+            crate_name,
+            help: span.is_none().then_some(()),
+        }));
     }
 
     if let Some(guar) = guar {
diff --git a/tests/ui/command/need-crate-arg-ignore-tidy$x.stderr b/tests/ui/command/need-crate-arg-ignore-tidy$x.stderr
index 28f6d31b1ce59..861510212f920 100644
--- a/tests/ui/command/need-crate-arg-ignore-tidy$x.stderr
+++ b/tests/ui/command/need-crate-arg-ignore-tidy$x.stderr
@@ -1,6 +1,6 @@
-error: invalid character `'$'` in crate name: `need_crate_arg_ignore_tidy$x`
+error: invalid character '$' in crate name: `need_crate_arg_ignore_tidy$x`
    |
-   = help: you can either pass `--crate-name` on the command line or add `#![crate_name="…"]` to set the crate name
+   = help: you can either pass `--crate-name` on the command line or add `#![crate_name = "…"]` to set the crate name
 
 error: aborting due to 1 previous error
 

From 46d53a68aa1b10833ec25178171f34e29e71f1f1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Wed, 10 Jul 2024 17:22:40 +0200
Subject: [PATCH 05/11] Move `#![crate_type]` UI tests into attributes
 directory

Gets rid of two top-level UI tests which is always great.

Furthermore, move `need-crate-arg-ignore-tidy$x.rs`
from `command/` to `invalid-compile-flags/`.
`command/` concerns `std::process::Command` tests, not CLI tests.
---
 .../crate-type-delimited.rs}                                    | 0
 .../crate-type-delimited.stderr}                                | 2 +-
 tests/ui/{no_crate_type.rs => attributes/crate-type-empty.rs}   | 0
 .../crate-type-empty.stderr}                                    | 2 +-
 .../crate-type-macro-call.rs}                                   | 0
 .../crate-type-macro-call.stderr}                               | 2 +-
 .../need-crate-arg-ignore-tidy$x.rs                             | 0
 .../need-crate-arg-ignore-tidy$x.stderr                         | 0
 8 files changed, 3 insertions(+), 3 deletions(-)
 rename tests/ui/{invalid_crate_type_syntax.rs => attributes/crate-type-delimited.rs} (100%)
 rename tests/ui/{invalid_crate_type_syntax.stderr => attributes/crate-type-delimited.stderr} (82%)
 rename tests/ui/{no_crate_type.rs => attributes/crate-type-empty.rs} (100%)
 rename tests/ui/{no_crate_type.stderr => attributes/crate-type-empty.stderr} (84%)
 rename tests/ui/{invalid/invalid-crate-type-macro.rs => attributes/crate-type-macro-call.rs} (100%)
 rename tests/ui/{invalid/invalid-crate-type-macro.stderr => attributes/crate-type-macro-call.stderr} (83%)
 rename tests/ui/{command => invalid-compile-flags}/need-crate-arg-ignore-tidy$x.rs (100%)
 rename tests/ui/{command => invalid-compile-flags}/need-crate-arg-ignore-tidy$x.stderr (100%)

diff --git a/tests/ui/invalid_crate_type_syntax.rs b/tests/ui/attributes/crate-type-delimited.rs
similarity index 100%
rename from tests/ui/invalid_crate_type_syntax.rs
rename to tests/ui/attributes/crate-type-delimited.rs
diff --git a/tests/ui/invalid_crate_type_syntax.stderr b/tests/ui/attributes/crate-type-delimited.stderr
similarity index 82%
rename from tests/ui/invalid_crate_type_syntax.stderr
rename to tests/ui/attributes/crate-type-delimited.stderr
index a5563720f6223..0bbbe07b19810 100644
--- a/tests/ui/invalid_crate_type_syntax.stderr
+++ b/tests/ui/attributes/crate-type-delimited.stderr
@@ -1,5 +1,5 @@
 error: malformed `crate_type` attribute input
-  --> $DIR/invalid_crate_type_syntax.rs:2:1
+  --> $DIR/crate-type-delimited.rs:2:1
    |
 LL | #![crate_type(lib)]
    | ^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_type = "bin|lib|..."]`
diff --git a/tests/ui/no_crate_type.rs b/tests/ui/attributes/crate-type-empty.rs
similarity index 100%
rename from tests/ui/no_crate_type.rs
rename to tests/ui/attributes/crate-type-empty.rs
diff --git a/tests/ui/no_crate_type.stderr b/tests/ui/attributes/crate-type-empty.stderr
similarity index 84%
rename from tests/ui/no_crate_type.stderr
rename to tests/ui/attributes/crate-type-empty.stderr
index 85d8f87afa673..b9279d961eef4 100644
--- a/tests/ui/no_crate_type.stderr
+++ b/tests/ui/attributes/crate-type-empty.stderr
@@ -1,5 +1,5 @@
 error: malformed `crate_type` attribute input
-  --> $DIR/no_crate_type.rs:2:1
+  --> $DIR/crate-type-empty.rs:2:1
    |
 LL | #![crate_type]
    | ^^^^^^^^^^^^^^ help: must be of the form: `#![crate_type = "bin|lib|..."]`
diff --git a/tests/ui/invalid/invalid-crate-type-macro.rs b/tests/ui/attributes/crate-type-macro-call.rs
similarity index 100%
rename from tests/ui/invalid/invalid-crate-type-macro.rs
rename to tests/ui/attributes/crate-type-macro-call.rs
diff --git a/tests/ui/invalid/invalid-crate-type-macro.stderr b/tests/ui/attributes/crate-type-macro-call.stderr
similarity index 83%
rename from tests/ui/invalid/invalid-crate-type-macro.stderr
rename to tests/ui/attributes/crate-type-macro-call.stderr
index 1cf77d4086bb0..6ccc3edf885e3 100644
--- a/tests/ui/invalid/invalid-crate-type-macro.stderr
+++ b/tests/ui/attributes/crate-type-macro-call.stderr
@@ -1,5 +1,5 @@
 error: malformed `crate_type` attribute input
-  --> $DIR/invalid-crate-type-macro.rs:1:1
+  --> $DIR/crate-type-macro-call.rs:1:1
    |
 LL | #![crate_type = foo!()]
    | ^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_type = "bin|lib|..."]`
diff --git a/tests/ui/command/need-crate-arg-ignore-tidy$x.rs b/tests/ui/invalid-compile-flags/need-crate-arg-ignore-tidy$x.rs
similarity index 100%
rename from tests/ui/command/need-crate-arg-ignore-tidy$x.rs
rename to tests/ui/invalid-compile-flags/need-crate-arg-ignore-tidy$x.rs
diff --git a/tests/ui/command/need-crate-arg-ignore-tidy$x.stderr b/tests/ui/invalid-compile-flags/need-crate-arg-ignore-tidy$x.stderr
similarity index 100%
rename from tests/ui/command/need-crate-arg-ignore-tidy$x.stderr
rename to tests/ui/invalid-compile-flags/need-crate-arg-ignore-tidy$x.stderr

From 02f1f346c2c83a930f2f4541b5181225491b875e Mon Sep 17 00:00:00 2001
From: Tapan Prakash <tapanprakasht@gmail.com>
Date: Sat, 15 Feb 2025 15:34:27 +0530
Subject: [PATCH 06/11] fix(rustdoc): Fixed `Copy Item Path` in rust doc

---
 src/librustdoc/html/static/js/main.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index a348c6c5678b9..e46cc1897e9e0 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -2039,7 +2039,10 @@ function preLoadCss(cssUrl) {
         // Most page titles are '<Item> in <path::to::module> - Rust', except
         // modules (which don't have the first part) and keywords/primitives
         // (which don't have a module path)
-        const [item, module] = document.title.split(" in ");
+        const titleElement = document.querySelector("title");
+        const title = titleElement && titleElement.textContent ?
+                      titleElement.textContent.replace(" - Rust", "") : "";
+        const [item, module] = title.split(" in ");
         const path = [item];
         if (module !== undefined) {
             path.unshift(module);

From e3412617dfb2440e00f46efb921b721c7ed16230 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= <berykubik@gmail.com>
Date: Sat, 15 Feb 2025 11:45:19 +0100
Subject: [PATCH 07/11] Do not generate invalid links in job summaries

---
 src/ci/scripts/upload-artifacts.sh | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/src/ci/scripts/upload-artifacts.sh b/src/ci/scripts/upload-artifacts.sh
index 0bc91f6ba7152..975b4c527267b 100755
--- a/src/ci/scripts/upload-artifacts.sh
+++ b/src/ci/scripts/upload-artifacts.sh
@@ -52,10 +52,15 @@ access_url="https://ci-artifacts.rust-lang.org/${deploy_dir}/$(ciCommit)"
 # to make them easily accessible.
 if [ -n "${GITHUB_STEP_SUMMARY}" ]
 then
-  echo "# CI artifacts" >> "${GITHUB_STEP_SUMMARY}"
+  archives=($(find "${upload_dir}" -maxdepth 1 -name "*.xz"))
 
-  for filename in "${upload_dir}"/*.xz; do
-    filename=$(basename "${filename}")
-    echo "- [${filename}](${access_url}/${filename})" >> "${GITHUB_STEP_SUMMARY}"
-  done
+  # Avoid generating an invalid "*.xz" file if there are no archives
+  if [ ${#archives[@]} -gt 0 ]; then
+    echo "# CI artifacts" >> "${GITHUB_STEP_SUMMARY}"
+
+    for filename in "${upload_dir}"/*.xz; do
+      filename=$(basename "${filename}")
+      echo "- [${filename}](${access_url}/${filename})" >> "${GITHUB_STEP_SUMMARY}"
+    done
+  fi
 fi

From f065a5588eb6d417be3befdc3629ea2d559580f7 Mon Sep 17 00:00:00 2001
From: Alona Enraght-Moony <code@alona.page>
Date: Sat, 15 Feb 2025 13:07:44 +0000
Subject: [PATCH 08/11] triagebot.toml: ping me on changes to
 `tests/rustdoc-json`

---
 triagebot.toml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/triagebot.toml b/triagebot.toml
index 49cf1213987a1..148fb723ed37d 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -917,6 +917,9 @@ message = "This PR changes how LLVM is built. Consider updating src/bootstrap/do
 [mentions."test/crashes"]
 message = "This PR changes a file inside `tests/crashes`. If a crash was fixed, please move into the corresponding `ui` subdir and add 'Fixes #<issueNr>' to the PR description to autoclose the issue upon merge."
 
+[mentions."tests/rustdoc-json"]
+cc = ["@aDotInTheVoid"]
+
 [mentions."tests/ui/deriving/deriving-all-codegen.stdout"]
 message = "Changes to the code generated for builtin derived traits."
 cc = ["@nnethercote"]

From 880d8c000086ab7b4c315d69eb35ab52cb7cd5bb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?=
 <39484203+jieyouxu@users.noreply.github.com>
Date: Sat, 15 Feb 2025 18:42:45 +0800
Subject: [PATCH 09/11] compiletest: add `{ignore,only}-rustc_abi-x86-sse2`
 directives

---
 src/tools/compiletest/src/common.rs         | 13 ++++++++++++-
 src/tools/compiletest/src/directive-list.rs |  2 ++
 src/tools/compiletest/src/header/cfg.rs     |  8 ++++++++
 src/tools/compiletest/src/header/tests.rs   | 14 ++++++++++++++
 4 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index cde4f7a665cbf..1614c35cb1ce4 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -517,6 +517,7 @@ pub struct TargetCfgs {
     pub all_abis: HashSet<String>,
     pub all_families: HashSet<String>,
     pub all_pointer_widths: HashSet<String>,
+    pub all_rustc_abis: HashSet<String>,
 }
 
 impl TargetCfgs {
@@ -536,6 +537,9 @@ impl TargetCfgs {
         let mut all_abis = HashSet::new();
         let mut all_families = HashSet::new();
         let mut all_pointer_widths = HashSet::new();
+        // NOTE: for distinction between `abi` and `rustc_abi`, see comment on
+        // `TargetCfg::rustc_abi`.
+        let mut all_rustc_abis = HashSet::new();
 
         // If current target is not included in the `--print=all-target-specs-json` output,
         // we check whether it is a custom target from the user or a synthetic target from bootstrap.
@@ -576,7 +580,9 @@ impl TargetCfgs {
                 all_families.insert(family.clone());
             }
             all_pointer_widths.insert(format!("{}bit", cfg.pointer_width));
-
+            if let Some(rustc_abi) = &cfg.rustc_abi {
+                all_rustc_abis.insert(rustc_abi.clone());
+            }
             all_targets.insert(target.clone());
         }
 
@@ -590,6 +596,7 @@ impl TargetCfgs {
             all_abis,
             all_families,
             all_pointer_widths,
+            all_rustc_abis,
         }
     }
 
@@ -676,6 +683,10 @@ pub struct TargetCfg {
     pub(crate) xray: bool,
     #[serde(default = "default_reloc_model")]
     pub(crate) relocation_model: String,
+    // NOTE: `rustc_abi` should not be confused with `abi`. `rustc_abi` was introduced in #137037 to
+    // make SSE2 *required* by the ABI (kind of a hack to make a target feature *required* via the
+    // target spec).
+    pub(crate) rustc_abi: Option<String>,
 
     // Not present in target cfg json output, additional derived information.
     #[serde(skip)]
diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs
index a7ac875d0a36c..8c909bcb19527 100644
--- a/src/tools/compiletest/src/directive-list.rs
+++ b/src/tools/compiletest/src/directive-list.rs
@@ -87,6 +87,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "ignore-remote",
     "ignore-riscv64",
     "ignore-rustc-debug-assertions",
+    "ignore-rustc_abi-x86-sse2",
     "ignore-s390x",
     "ignore-sgx",
     "ignore-sparc64",
@@ -198,6 +199,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "only-nvptx64",
     "only-powerpc",
     "only-riscv64",
+    "only-rustc_abi-x86-sse2",
     "only-s390x",
     "only-sparc",
     "only-sparc64",
diff --git a/src/tools/compiletest/src/header/cfg.rs b/src/tools/compiletest/src/header/cfg.rs
index cfe51b5655f82..72a3b9d85c8cf 100644
--- a/src/tools/compiletest/src/header/cfg.rs
+++ b/src/tools/compiletest/src/header/cfg.rs
@@ -234,6 +234,14 @@ fn parse_cfg_name_directive<'a>(
         allowed_names: ["coverage-map", "coverage-run"],
         message: "when the test mode is {name}",
     }
+    condition! {
+        name: target_cfg.rustc_abi.as_ref().map(|abi| format!("rustc_abi-{abi}")).unwrap_or_default(),
+        allowed_names: ContainsPrefixed {
+            prefix: "rustc_abi-",
+            inner: target_cfgs.all_rustc_abis.clone(),
+        },
+        message: "when the target `rustc_abi` is {name}",
+    }
 
     condition! {
         name: "dist",
diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs
index 522d340b678f9..55292c46bba9c 100644
--- a/src/tools/compiletest/src/header/tests.rs
+++ b/src/tools/compiletest/src/header/tests.rs
@@ -889,3 +889,17 @@ fn test_needs_target_has_atomic() {
     assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 8, ptr"));
     assert!(check_ignore(&config, "//@ needs-target-has-atomic: 8, ptr, 128"));
 }
+
+#[test]
+// FIXME: this test will fail against stage 0 until #137037 changes reach beta.
+#[cfg_attr(bootstrap, ignore)]
+fn test_rustc_abi() {
+    let config = cfg().target("i686-unknown-linux-gnu").build();
+    assert_eq!(config.target_cfg().rustc_abi, Some("x86-sse2".to_string()));
+    assert!(check_ignore(&config, "//@ ignore-rustc_abi-x86-sse2"));
+    assert!(!check_ignore(&config, "//@ only-rustc_abi-x86-sse2"));
+    let config = cfg().target("x86_64-unknown-linux-gnu").build();
+    assert_eq!(config.target_cfg().rustc_abi, None);
+    assert!(!check_ignore(&config, "//@ ignore-rustc_abi-x86-sse2"));
+    assert!(check_ignore(&config, "//@ only-rustc_abi-x86-sse2"));
+}

From d419da1a49a3298072c3659a0a7f9cc183f0f29e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?=
 <39484203+jieyouxu@users.noreply.github.com>
Date: Sat, 15 Feb 2025 19:16:42 +0800
Subject: [PATCH 10/11] rustc-dev-guide: document
 `{ignore,only}-rustc_abi-x86-sse2`

---
 src/doc/rustc-dev-guide/src/tests/directives.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md
index b6209bcb2d806..00bb2bc4dbb1e 100644
--- a/src/doc/rustc-dev-guide/src/tests/directives.md
+++ b/src/doc/rustc-dev-guide/src/tests/directives.md
@@ -154,6 +154,7 @@ Some examples of `X` in `ignore-X` or `only-X`:
   `ignore-coverage-map`, `ignore-coverage-run`
 - When testing a dist toolchain: `dist`
   - This needs to be enabled with `COMPILETEST_ENABLE_DIST_TESTS=1`
+- The `rustc_abi` of the target: e.g. `rustc_abi-x86_64-sse2`
 
 The following directives will check rustc build settings and target
 settings:

From 9b6fd35738965ef3f246018fddc743b5e5cd8d2c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Wed, 10 Jul 2024 17:38:52 +0200
Subject: [PATCH 11/11] Reject macro calls inside of `#![crate_name]`

---
 compiler/rustc_driver_impl/src/lib.rs         |  10 +-
 compiler/rustc_expand/src/module.rs           |  12 +-
 compiler/rustc_incremental/src/persist/fs.rs  |  10 +-
 .../rustc_incremental/src/persist/load.rs     |   5 +-
 compiler/rustc_interface/messages.ftl         |   4 +
 compiler/rustc_interface/src/errors.rs        |  15 +++
 compiler/rustc_interface/src/passes.rs        | 105 ++++++++++++++----
 compiler/rustc_interface/src/util.rs          |   6 +-
 compiler/rustc_session/messages.ftl           |   4 -
 compiler/rustc_session/src/errors.rs          |  15 ---
 compiler/rustc_session/src/output.rs          |  56 +---------
 tests/ui/attributes/crate-name-macro-call.rs  |   6 +
 .../attributes/crate-name-macro-call.stderr   |   8 ++
 ...crate-name-request-malformed-crate-name.rs |   5 +
 ...e-name-request-malformed-crate-name.stderr |   8 ++
 ...le-names-request-malformed-crate-name-1.rs |   4 +
 ...ames-request-malformed-crate-name-1.stderr |   8 ++
 ...le-names-request-malformed-crate-name-2.rs |   7 ++
 ...ames-request-malformed-crate-name-2.stderr |   8 ++
 ...file-names-request-malformed-crate-name.rs |   5 +
 ...-names-request-malformed-crate-name.stderr |   8 ++
 21 files changed, 195 insertions(+), 114 deletions(-)
 create mode 100644 tests/ui/attributes/crate-name-macro-call.rs
 create mode 100644 tests/ui/attributes/crate-name-macro-call.stderr
 create mode 100644 tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.rs
 create mode 100644 tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr
 create mode 100644 tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.rs
 create mode 100644 tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr
 create mode 100644 tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.rs
 create mode 100644 tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr
 create mode 100644 tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.rs
 create mode 100644 tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr

diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 2bcc33241dfa1..a2ddff7183e60 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -667,11 +667,12 @@ fn print_crate_info(
                     return Compilation::Continue;
                 };
                 let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess);
-                let id = rustc_session::output::find_crate_name(sess, attrs);
+                let crate_name = passes::get_crate_name(sess, attrs);
                 let crate_types = collect_crate_types(sess, attrs);
                 for &style in &crate_types {
-                    let fname =
-                        rustc_session::output::filename_for_input(sess, style, id, &t_outputs);
+                    let fname = rustc_session::output::filename_for_input(
+                        sess, style, crate_name, &t_outputs,
+                    );
                     println_info!("{}", fname.as_path().file_name().unwrap().to_string_lossy());
                 }
             }
@@ -680,8 +681,7 @@ fn print_crate_info(
                     // no crate attributes, print out an error and exit
                     return Compilation::Continue;
                 };
-                let id = rustc_session::output::find_crate_name(sess, attrs);
-                println_info!("{id}");
+                println_info!("{}", passes::get_crate_name(sess, attrs));
             }
             Cfg => {
                 let mut cfgs = sess
diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs
index 9c35b26772b61..e925052c60709 100644
--- a/compiler/rustc_expand/src/module.rs
+++ b/compiler/rustc_expand/src/module.rs
@@ -183,12 +183,12 @@ pub(crate) fn mod_file_path_from_attr(
     let first_path = attrs.iter().find(|at| at.has_name(sym::path))?;
     let Some(path_sym) = first_path.value_str() else {
         // This check is here mainly to catch attempting to use a macro,
-        // such as #[path = concat!(...)]. This isn't currently supported
-        // because otherwise the InvocationCollector would need to defer
-        // loading a module until the #[path] attribute was expanded, and
-        // it doesn't support that (and would likely add a bit of
-        // complexity). Usually bad forms are checked in AstValidator (via
-        // `check_builtin_attribute`), but by the time that runs the macro
+        // such as `#[path = concat!(...)]`. This isn't supported because
+        // otherwise the `InvocationCollector` would need to defer loading
+        // a module until the `#[path]` attribute was expanded, and it
+        // doesn't support that (and would likely add a bit of complexity).
+        // Usually bad forms are checked during semantic analysis via
+        // `TyCtxt::check_mod_attrs`), but by the time that runs the macro
         // is expanded, and it doesn't give an error.
         validate_attr::emit_fatal_malformed_builtin_attribute(&sess.psess, first_path, sym::path);
     };
diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs
index 4ea171ab4a932..19cca48af6127 100644
--- a/compiler/rustc_incremental/src/persist/fs.rs
+++ b/compiler/rustc_incremental/src/persist/fs.rs
@@ -117,8 +117,9 @@ use rustc_data_structures::{base_n, flock};
 use rustc_fs_util::{LinkOrCopy, link_or_copy, try_canonicalize};
 use rustc_middle::bug;
 use rustc_session::config::CrateType;
-use rustc_session::output::{collect_crate_types, find_crate_name};
+use rustc_session::output::collect_crate_types;
 use rustc_session::{Session, StableCrateId};
+use rustc_span::Symbol;
 use tracing::debug;
 
 use crate::errors;
@@ -211,7 +212,7 @@ pub fn in_incr_comp_dir(incr_comp_session_dir: &Path, file_name: &str) -> PathBu
 /// The garbage collection will take care of it.
 ///
 /// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph
-pub(crate) fn prepare_session_directory(sess: &Session) {
+pub(crate) fn prepare_session_directory(sess: &Session, crate_name: Symbol) {
     if sess.opts.incremental.is_none() {
         return;
     }
@@ -221,7 +222,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) {
     debug!("prepare_session_directory");
 
     // {incr-comp-dir}/{crate-name-and-disambiguator}
-    let crate_dir = crate_path(sess);
+    let crate_dir = crate_path(sess, crate_name);
     debug!("crate-dir: {}", crate_dir.display());
     create_dir(sess, &crate_dir, "crate");
 
@@ -594,10 +595,9 @@ fn string_to_timestamp(s: &str) -> Result<SystemTime, &'static str> {
     Ok(UNIX_EPOCH + duration)
 }
 
-fn crate_path(sess: &Session) -> PathBuf {
+fn crate_path(sess: &Session, crate_name: Symbol) -> PathBuf {
     let incr_dir = sess.opts.incremental.as_ref().unwrap().clone();
 
-    let crate_name = find_crate_name(sess, &[]);
     let crate_types = collect_crate_types(sess, &[]);
     let stable_crate_id = StableCrateId::new(
         crate_name,
diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs
index 48df84f3d099e..7977bcc68911d 100644
--- a/compiler/rustc_incremental/src/persist/load.rs
+++ b/compiler/rustc_incremental/src/persist/load.rs
@@ -11,6 +11,7 @@ use rustc_serialize::Decodable;
 use rustc_serialize::opaque::MemDecoder;
 use rustc_session::Session;
 use rustc_session::config::IncrementalStateAssertion;
+use rustc_span::Symbol;
 use tracing::{debug, warn};
 
 use super::data::*;
@@ -203,9 +204,9 @@ pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache> {
 
 /// Setups the dependency graph by loading an existing graph from disk and set up streaming of a
 /// new graph to an incremental session directory.
-pub fn setup_dep_graph(sess: &Session) -> DepGraph {
+pub fn setup_dep_graph(sess: &Session, crate_name: Symbol) -> DepGraph {
     // `load_dep_graph` can only be called after `prepare_session_directory`.
-    prepare_session_directory(sess);
+    prepare_session_directory(sess, crate_name);
 
     let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess));
 
diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl
index 31123625369bd..43c69c8e5718e 100644
--- a/compiler/rustc_interface/messages.ftl
+++ b/compiler/rustc_interface/messages.ftl
@@ -6,6 +6,10 @@ interface_abi_required_feature_issue = for more information, see issue #116344 <
 interface_cant_emit_mir =
     could not emit MIR: {$error}
 
+interface_crate_name_does_not_match = `--crate-name` and `#[crate_name]` are required to match, but `{$crate_name}` != `{$attr_crate_name}`
+
+interface_crate_name_invalid = crate names cannot start with a `-`, but `{$crate_name}` has a leading hyphen
+
 interface_emoji_identifier =
     identifiers cannot contain emoji: `{$ident}`
 
diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs
index b62950d670964..c3b858d4f2ec6 100644
--- a/compiler/rustc_interface/src/errors.rs
+++ b/compiler/rustc_interface/src/errors.rs
@@ -4,6 +4,21 @@ use std::path::Path;
 use rustc_macros::Diagnostic;
 use rustc_span::{Span, Symbol};
 
+#[derive(Diagnostic)]
+#[diag(interface_crate_name_does_not_match)]
+pub(crate) struct CrateNameDoesNotMatch {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) crate_name: Symbol,
+    pub(crate) attr_crate_name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(interface_crate_name_invalid)]
+pub(crate) struct CrateNameInvalid<'a> {
+    pub(crate) crate_name: &'a str,
+}
+
 #[derive(Diagnostic)]
 #[diag(interface_ferris_identifier)]
 pub struct FerrisIdentifier {
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index e5adcdb244f6d..fcebca3ecc91f 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -28,10 +28,12 @@ use rustc_passes::{abi_test, input_stats, layout_test};
 use rustc_resolve::Resolver;
 use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
 use rustc_session::cstore::Untracked;
-use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_name};
+use rustc_session::output::{collect_crate_types, filename_for_input};
 use rustc_session::search_paths::PathKind;
 use rustc_session::{Limit, Session};
-use rustc_span::{ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Symbol, sym};
+use rustc_span::{
+    ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym,
+};
 use rustc_target::spec::PanicStrategy;
 use rustc_trait_selection::traits;
 use tracing::{info, instrument};
@@ -725,8 +727,7 @@ pub fn create_and_enter_global_ctxt<T, F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> T>(
 
     let pre_configured_attrs = rustc_expand::config::pre_configure_attrs(sess, &krate.attrs);
 
-    // parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches.
-    let crate_name = find_crate_name(sess, &pre_configured_attrs);
+    let crate_name = get_crate_name(sess, &pre_configured_attrs);
     let crate_types = collect_crate_types(sess, &pre_configured_attrs);
     let stable_crate_id = StableCrateId::new(
         crate_name,
@@ -735,7 +736,7 @@ pub fn create_and_enter_global_ctxt<T, F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> T>(
         sess.cfg_version,
     );
     let outputs = util::build_output_filenames(&pre_configured_attrs, sess);
-    let dep_graph = setup_dep_graph(sess);
+    let dep_graph = setup_dep_graph(sess, crate_name);
 
     let cstore =
         FreezeLock::new(Box::new(CStore::new(compiler.codegen_backend.metadata_loader())) as _);
@@ -1080,23 +1081,85 @@ pub(crate) fn start_codegen<'tcx>(
     codegen
 }
 
-fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit {
-    if let Some(attr) = krate_attrs
-        .iter()
-        .find(|attr| attr.has_name(sym::recursion_limit) && attr.value_str().is_none())
+/// Compute and validate the crate name.
+pub fn get_crate_name(sess: &Session, krate_attrs: &[ast::Attribute]) -> Symbol {
+    // We validate *all* occurrences of `#![crate_name]`, pick the first find and
+    // if a crate name was passed on the command line via `--crate-name` we enforce
+    // that they match.
+    // We perform the validation step here instead of later to ensure it gets run
+    // in all code paths that require the crate name very early on, namely before
+    // macro expansion.
+
+    let attr_crate_name =
+        validate_and_find_value_str_builtin_attr(sym::crate_name, sess, krate_attrs);
+
+    let validate = |name, span| {
+        rustc_session::output::validate_crate_name(sess, name, span);
+        name
+    };
+
+    if let Some(crate_name) = &sess.opts.crate_name {
+        let crate_name = Symbol::intern(crate_name);
+        if let Some((attr_crate_name, span)) = attr_crate_name
+            && attr_crate_name != crate_name
+        {
+            sess.dcx().emit_err(errors::CrateNameDoesNotMatch {
+                span,
+                crate_name,
+                attr_crate_name,
+            });
+        }
+        return validate(crate_name, None);
+    }
+
+    if let Some((crate_name, span)) = attr_crate_name {
+        return validate(crate_name, Some(span));
+    }
+
+    if let Input::File(ref path) = sess.io.input
+        && let Some(file_stem) = path.file_stem().and_then(|s| s.to_str())
     {
-        // This is here mainly to check for using a macro, such as
-        // #![recursion_limit = foo!()]. That is not supported since that
-        // would require expanding this while in the middle of expansion,
-        // which needs to know the limit before expanding. Otherwise,
-        // validation would normally be caught in AstValidator (via
-        // `check_builtin_attribute`), but by the time that runs the macro
-        // is expanded, and it doesn't give an error.
-        validate_attr::emit_fatal_malformed_builtin_attribute(
-            &sess.psess,
-            attr,
-            sym::recursion_limit,
-        );
+        if file_stem.starts_with('-') {
+            sess.dcx().emit_err(errors::CrateNameInvalid { crate_name: file_stem });
+        } else {
+            return validate(Symbol::intern(&file_stem.replace('-', "_")), None);
+        }
     }
+
+    sym::rust_out
+}
+
+fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit {
+    // We don't permit macro calls inside of the attribute (e.g., #![recursion_limit = `expand!()`])
+    // because that would require expanding this while in the middle of expansion, which needs to
+    // know the limit before expanding.
+    let _ = validate_and_find_value_str_builtin_attr(sym::recursion_limit, sess, krate_attrs);
     rustc_middle::middle::limits::get_recursion_limit(krate_attrs, sess)
 }
+
+/// Validate *all* occurrences of the given "[value-str]" built-in attribute and return the first find.
+///
+/// This validator is intended for built-in attributes whose value needs to be known very early
+/// during compilation (namely, before macro expansion) and it mainly exists to reject macro calls
+/// inside of the attributes, such as in `#![name = expand!()]`. Normal attribute validation happens
+/// during semantic analysis via [`TyCtxt::check_mod_attrs`] which happens *after* macro expansion
+/// when such macro calls (here: `expand`) have already been expanded and we can no longer check for
+/// their presence.
+///
+/// [value-str]: ast::Attribute::value_str
+fn validate_and_find_value_str_builtin_attr(
+    name: Symbol,
+    sess: &Session,
+    krate_attrs: &[ast::Attribute],
+) -> Option<(Symbol, Span)> {
+    let mut result = None;
+    // Validate *all* relevant attributes, not just the first occurrence.
+    for attr in ast::attr::filter_by_name(krate_attrs, name) {
+        let Some(value) = attr.value_str() else {
+            validate_attr::emit_fatal_malformed_builtin_attribute(&sess.psess, attr, name)
+        };
+        // Choose the first occurrence as our result.
+        result.get_or_insert((value, attr.span));
+    }
+    result
+}
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index e900ec14fcab6..bc2aae7cd87e2 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -433,11 +433,11 @@ pub(crate) fn check_attr_crate_type(
                 }
             } else {
                 // This is here mainly to check for using a macro, such as
-                // #![crate_type = foo!()]. That is not supported since the
+                // `#![crate_type = foo!()]`. That is not supported since the
                 // crate type needs to be known very early in compilation long
                 // before expansion. Otherwise, validation would normally be
-                // caught in AstValidator (via `check_builtin_attribute`), but
-                // by the time that runs the macro is expanded, and it doesn't
+                // caught during semantic analysis via `TyCtxt::check_mod_attrs`,
+                // but by the time that runs the macro is expanded, and it doesn't
                 // give an error.
                 validate_attr::emit_fatal_malformed_builtin_attribute(
                     &sess.psess,
diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index f108488cd5829..74b8087e07769 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -8,12 +8,8 @@ session_cannot_mix_and_match_sanitizers = `-Zsanitizer={$first}` is incompatible
 session_cli_feature_diagnostic_help =
     add `-Zcrate-attr="feature({$feature})"` to the command-line options to enable
 
-session_crate_name_does_not_match = `--crate-name` and `#[crate_name]` are required to match, but `{$crate_name}` != `{$attr_crate_name}`
-
 session_crate_name_empty = crate name must not be empty
 
-session_crate_name_invalid = crate names cannot start with a `-`, but `{$s}` has a leading hyphen
-
 session_embed_source_insufficient_dwarf_version = `-Zembed-source=y` requires at least `-Z dwarf-version=5` but DWARF version is {$dwarf_version}
 
 session_embed_source_requires_debug_info = `-Zembed-source=y` requires debug information to be enabled
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index b57560ff78221..71d8dbe44fed0 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -212,21 +212,6 @@ pub(crate) struct FileWriteFail<'a> {
     pub(crate) err: String,
 }
 
-#[derive(Diagnostic)]
-#[diag(session_crate_name_does_not_match)]
-pub(crate) struct CrateNameDoesNotMatch {
-    #[primary_span]
-    pub(crate) span: Span,
-    pub(crate) crate_name: Symbol,
-    pub(crate) attr_crate_name: Symbol,
-}
-
-#[derive(Diagnostic)]
-#[diag(session_crate_name_invalid)]
-pub(crate) struct CrateNameInvalid<'a> {
-    pub(crate) s: &'a str,
-}
-
 #[derive(Diagnostic)]
 #[diag(session_crate_name_empty)]
 pub(crate) struct CrateNameEmpty {
diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs
index a6d4ebf23c70b..b37a80274c0cb 100644
--- a/compiler/rustc_session/src/output.rs
+++ b/compiler/rustc_session/src/output.rs
@@ -2,15 +2,12 @@
 
 use std::path::Path;
 
-use rustc_ast::{self as ast, attr};
+use rustc_ast as ast;
 use rustc_span::{Span, Symbol, sym};
 
 use crate::Session;
-use crate::config::{self, CrateType, Input, OutFileName, OutputFilenames, OutputType};
-use crate::errors::{
-    self, CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable,
-    InvalidCharacterInCrateName,
-};
+use crate::config::{self, CrateType, OutFileName, OutputFilenames, OutputType};
+use crate::errors::{self, CrateNameEmpty, FileIsNotWriteable, InvalidCharacterInCrateName};
 
 pub fn out_filename(
     sess: &Session,
@@ -49,53 +46,6 @@ fn is_writeable(p: &Path) -> bool {
     }
 }
 
-/// Find and [validate] the crate name.
-///
-/// [validate]: validate_crate_name
-pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute]) -> Symbol {
-    let validate = |name, span| {
-        validate_crate_name(sess, name, span);
-        name
-    };
-
-    // Look in attributes 100% of the time to make sure the attribute is marked
-    // as used. After doing this, however, we still prioritize a crate name from
-    // the command line over one found in the #[crate_name] attribute. If we
-    // find both we ensure that they're the same later on as well.
-    let attr_crate_name =
-        attr::find_by_name(attrs, sym::crate_name).and_then(|at| at.value_str().map(|s| (at, s)));
-
-    if let Some(crate_name) = &sess.opts.crate_name {
-        let crate_name = Symbol::intern(crate_name);
-        if let Some((attr, attr_crate_name)) = attr_crate_name
-            && attr_crate_name != crate_name
-        {
-            sess.dcx().emit_err(CrateNameDoesNotMatch {
-                span: attr.span,
-                crate_name,
-                attr_crate_name,
-            });
-        }
-        return validate(crate_name, None);
-    }
-
-    if let Some((attr, crate_name)) = attr_crate_name {
-        return validate(crate_name, Some(attr.span));
-    }
-
-    if let Input::File(ref path) = sess.io.input
-        && let Some(s) = path.file_stem().and_then(|s| s.to_str())
-    {
-        if s.starts_with('-') {
-            sess.dcx().emit_err(CrateNameInvalid { s });
-        } else {
-            return validate(Symbol::intern(&s.replace('-', "_")), None);
-        }
-    }
-
-    sym::rust_out
-}
-
 /// Validate the given crate name.
 ///
 /// Note that this validation is more permissive than identifier parsing. It considers
diff --git a/tests/ui/attributes/crate-name-macro-call.rs b/tests/ui/attributes/crate-name-macro-call.rs
new file mode 100644
index 0000000000000..1aae2e506e29b
--- /dev/null
+++ b/tests/ui/attributes/crate-name-macro-call.rs
@@ -0,0 +1,6 @@
+// issue: rust-lang/rust#122001
+// Ensure we reject macro calls inside `#![crate_name]` as their result wouldn't get honored anyway.
+
+#![crate_name = concat!("my", "crate")] //~ ERROR malformed `crate_name` attribute input
+
+fn main() {}
diff --git a/tests/ui/attributes/crate-name-macro-call.stderr b/tests/ui/attributes/crate-name-macro-call.stderr
new file mode 100644
index 0000000000000..ab562b41a31f4
--- /dev/null
+++ b/tests/ui/attributes/crate-name-macro-call.stderr
@@ -0,0 +1,8 @@
+error: malformed `crate_name` attribute input
+  --> $DIR/crate-name-macro-call.rs:4:1
+   |
+LL | #![crate_name = concat!("my", "crate")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.rs b/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.rs
new file mode 100644
index 0000000000000..c7f3c2c5403d1
--- /dev/null
+++ b/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.rs
@@ -0,0 +1,5 @@
+// Ensure we validate `#![crate_name]` on print requests and reject macro calls inside of it.
+// See also <https://github.com/rust-lang/rust/issues/122001>.
+
+//@ compile-flags: --print=crate-name
+#![crate_name = concat!("wrapped")] //~ ERROR malformed `crate_name` attribute input
diff --git a/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr b/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr
new file mode 100644
index 0000000000000..6bf09a2b13171
--- /dev/null
+++ b/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr
@@ -0,0 +1,8 @@
+error: malformed `crate_name` attribute input
+  --> $DIR/print-crate-name-request-malformed-crate-name.rs:5:1
+   |
+LL | #![crate_name = concat!("wrapped")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.rs b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.rs
new file mode 100644
index 0000000000000..5cf1ae36b42b7
--- /dev/null
+++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.rs
@@ -0,0 +1,4 @@
+// Ensure we validate `#![crate_name]` on print requests.
+
+//@ compile-flags: --print=file-names
+#![crate_name]  //~ ERROR malformed `crate_name` attribute input
diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr
new file mode 100644
index 0000000000000..de62aed79fce5
--- /dev/null
+++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr
@@ -0,0 +1,8 @@
+error: malformed `crate_name` attribute input
+  --> $DIR/print-file-names-request-malformed-crate-name-1.rs:4:1
+   |
+LL | #![crate_name]
+   | ^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.rs b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.rs
new file mode 100644
index 0000000000000..13c9d1e002739
--- /dev/null
+++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.rs
@@ -0,0 +1,7 @@
+// Ensure that we validate *all* `#![crate_name]`s on print requests, not just the first,
+// and that we reject macro calls inside of them.
+// See also <https://github.com/rust-lang/rust/issues/122001>.
+
+//@ compile-flags: --print=file-names
+#![crate_name = "this_one_is_okay"]
+#![crate_name = concat!("this_one_is_not")]  //~ ERROR malformed `crate_name` attribute input
diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr
new file mode 100644
index 0000000000000..42c33de12210d
--- /dev/null
+++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr
@@ -0,0 +1,8 @@
+error: malformed `crate_name` attribute input
+  --> $DIR/print-file-names-request-malformed-crate-name-2.rs:7:1
+   |
+LL | #![crate_name = concat!("this_one_is_not")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.rs b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.rs
new file mode 100644
index 0000000000000..5fe8bd7945f4a
--- /dev/null
+++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.rs
@@ -0,0 +1,5 @@
+// Ensure we validate `#![crate_name]` on print requests and reject macro calls inside of it.
+// See also <https://github.com/rust-lang/rust/issues/122001>.
+
+//@ compile-flags: --print=file-names
+#![crate_name = concat!("wrapped")]  //~ ERROR malformed `crate_name` attribute input
diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr
new file mode 100644
index 0000000000000..cb5ffaab9ca20
--- /dev/null
+++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr
@@ -0,0 +1,8 @@
+error: malformed `crate_name` attribute input
+  --> $DIR/print-file-names-request-malformed-crate-name.rs:5:1
+   |
+LL | #![crate_name = concat!("wrapped")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]`
+
+error: aborting due to 1 previous error
+