about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJana Dönszelmann <jana@donsz.nl>2025-08-11 11:46:30 +0200
committerJana Dönszelmann <jana@donsz.nl>2025-08-24 09:20:57 +0200
commit59ceb02d65f13a20d29422f4d923ecde429d4c0c (patch)
tree5de1da32932e192b400b383f38d1a70e09ed0d97
parent4b35cde904c1015df42b2c63244c1db1ed51fff9 (diff)
downloadrust-59ceb02d65f13a20d29422f4d923ecde429d4c0c.tar.gz
rust-59ceb02d65f13a20d29422f4d923ecde429d4c0c.zip
Port crate name to the new attribute system
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/crate_level.rs33
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/mod.rs1
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/util.rs6
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs8
-rw-r--r--compiler/rustc_attr_parsing/src/lib.rs4
-rw-r--r--compiler/rustc_hir/src/attrs/data_structures.rs3
-rw-r--r--compiler/rustc_hir/src/attrs/encode_cross_crate.rs6
-rw-r--r--compiler/rustc_interface/src/passes.rs28
-rw-r--r--compiler/rustc_interface/src/util.rs12
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs52
-rw-r--r--compiler/rustc_passes/src/check_attr.rs46
-rw-r--r--tests/ui/attributes/crate-name-empty.stderr4
-rw-r--r--tests/ui/attributes/crate-name-macro-call.rs2
-rw-r--r--tests/ui/attributes/crate-name-macro-call.stderr8
-rw-r--r--tests/ui/attributes/crate-name-mismatch.stderr4
-rw-r--r--tests/ui/attributes/malformed-attrs.stderr14
-rw-r--r--tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.rs2
-rw-r--r--tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr8
-rw-r--r--tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr5
-rw-r--r--tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.rs2
-rw-r--r--tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr8
-rw-r--r--tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.rs2
-rw-r--r--tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr8
-rw-r--r--tests/ui/lint/unused/unused-attr-duplicate.rs8
-rw-r--r--tests/ui/lint/unused/unused-attr-duplicate.stderr130
25 files changed, 248 insertions, 156 deletions
diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs
new file mode 100644
index 00000000000..9175d7479e1
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs
@@ -0,0 +1,33 @@
+use super::prelude::*;
+
+pub(crate) struct CrateNameParser;
+
+impl<S: Stage> SingleAttributeParser<S> for CrateNameParser {
+    const PATH: &[Symbol] = &[sym::crate_name];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
+    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
+
+    // FIXME: crate name is allowed on all targets and ignored,
+    //        even though it should only be valid on crates of course
+    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        let ArgParser::NameValue(n) = args else {
+            cx.expected_name_value(cx.attr_span, None);
+            return None;
+        };
+
+        let Some(name) = n.value_as_str() else {
+            cx.expected_string_literal(n.value_span, Some(n.value_as_lit()));
+            return None;
+        };
+
+        Some(AttributeKind::CrateName {
+            name,
+            name_span: n.value_span,
+            attr_span: cx.attr_span,
+            style: cx.attr_style,
+        })
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
index b98678041d7..9dad9c893f0 100644
--- a/compiler/rustc_attr_parsing/src/attributes/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -35,6 +35,7 @@ pub(crate) mod cfg;
 pub(crate) mod cfg_old;
 pub(crate) mod codegen_attrs;
 pub(crate) mod confusables;
+pub(crate) mod crate_level;
 pub(crate) mod deprecation;
 pub(crate) mod dummy;
 pub(crate) mod inline;
diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs
index ef9701cb7cb..77e8c32e59d 100644
--- a/compiler/rustc_attr_parsing/src/attributes/util.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/util.rs
@@ -1,5 +1,5 @@
 use rustc_ast::LitKind;
-use rustc_ast::attr::{AttributeExt, first_attr_value_str_by_name};
+use rustc_ast::attr::AttributeExt;
 use rustc_feature::is_builtin_attr_name;
 use rustc_hir::RustcVersion;
 use rustc_span::{Symbol, sym};
@@ -27,10 +27,6 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
     attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
 }
 
-pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
-    first_attr_value_str_by_name(attrs, sym::crate_name)
-}
-
 pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(
     attrs: impl Iterator<Item = &'tcx T>,
     symbol: Symbol,
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index f30edbfaaa1..128ecd67d2a 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -24,6 +24,7 @@ use crate::attributes::codegen_attrs::{
     UsedParser,
 };
 use crate::attributes::confusables::ConfusablesParser;
+use crate::attributes::crate_level::CrateNameParser;
 use crate::attributes::deprecation::DeprecationParser;
 use crate::attributes::dummy::DummyParser;
 use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
@@ -166,6 +167,7 @@ attribute_parsers!(
 
         // tidy-alphabetical-start
         Single<CoverageParser>,
+        Single<CrateNameParser>,
         Single<CustomMirParser>,
         Single<DeprecationParser>,
         Single<DummyParser>,
@@ -310,7 +312,9 @@ pub struct AcceptContext<'f, 'sess, S: Stage> {
     /// The span of the attribute currently being parsed
     pub(crate) attr_span: Span,
 
+    /// Whether it is an inner or outer attribute
     pub(crate) attr_style: AttrStyle,
+
     /// The expected structure of the attribute.
     ///
     /// Used in reporting errors to give a hint to users what the attribute *should* look like.
@@ -683,7 +687,9 @@ impl ShouldEmit {
             ShouldEmit::ErrorsAndLints => {
                 diag.emit();
             }
-            ShouldEmit::Nothing => {}
+            ShouldEmit::Nothing => {
+                diag.cancel();
+            }
         }
     }
 }
diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs
index 969c24a4f89..4dd908cdc40 100644
--- a/compiler/rustc_attr_parsing/src/lib.rs
+++ b/compiler/rustc_attr_parsing/src/lib.rs
@@ -106,9 +106,7 @@ pub mod validate_attr;
 
 pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr};
 pub use attributes::cfg_old::*;
-pub use attributes::util::{
-    find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version,
-};
+pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version};
 pub use context::{Early, Late, OmitDoc, ShouldEmit};
 pub use interface::AttributeParser;
 pub use lints::emit_attribute_lint;
diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs
index 1e73bb6f5fd..09da5772d23 100644
--- a/compiler/rustc_hir/src/attrs/data_structures.rs
+++ b/compiler/rustc_hir/src/attrs/data_structures.rs
@@ -367,6 +367,9 @@ pub enum AttributeKind {
     /// Represents `#[coverage(..)]`.
     Coverage(Span, CoverageAttrKind),
 
+    /// Represents `#[crate_name = ...]`
+    CrateName { name: Symbol, name_span: Span, attr_span: Span, style: AttrStyle },
+
     /// Represents `#[custom_mir]`.
     CustomMir(Option<(MirDialect, Span)>, Option<(MirPhase, Span)>, Span),
 
diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
index 77c6faa7acd..e5329c104bb 100644
--- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
+++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
@@ -7,6 +7,11 @@ pub enum EncodeCrossCrate {
 }
 
 impl AttributeKind {
+    /// Whether this attribute should be encoded in metadata files.
+    ///
+    /// If this is "Yes", then another crate can do `tcx.get_all_attrs(did)` for a did in this crate, and get the attribute.
+    /// When this is No, the attribute is filtered out while encoding and other crate won't be able to observe it.
+    /// This can be unexpectedly good for performance, so unless necessary for cross-crate compilation, prefer No.
     pub fn encode_cross_crate(&self) -> EncodeCrossCrate {
         use AttributeKind::*;
         use EncodeCrossCrate::*;
@@ -31,6 +36,7 @@ impl AttributeKind {
             ConstTrait(..) => No,
             Coroutine(..) => No,
             Coverage(..) => No,
+            CrateName { .. } => No,
             CustomMir(_, _, _) => Yes,
             DenyExplicitImpl(..) => No,
             Deprecation { .. } => Yes,
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 90f7ae76387..bc5ef04079e 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -6,7 +6,7 @@ use std::sync::{Arc, LazyLock, OnceLock};
 use std::{env, fs, iter};
 
 use rustc_ast as ast;
-use rustc_attr_parsing::validate_attr;
+use rustc_attr_parsing::{AttributeParser, ShouldEmit, validate_attr};
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_data_structures::jobserver::Proxy;
 use rustc_data_structures::steal::Steal;
@@ -16,6 +16,7 @@ use rustc_errors::timings::TimingSection;
 use rustc_expand::base::{ExtCtxt, LintStoreExpand};
 use rustc_feature::Features;
 use rustc_fs_util::try_canonicalize;
+use rustc_hir::attrs::AttributeKind;
 use rustc_hir::def_id::{LOCAL_CRATE, StableCrateId, StableCrateIdMap};
 use rustc_hir::definitions::Definitions;
 use rustc_incremental::setup_dep_graph;
@@ -1244,8 +1245,7 @@ pub fn get_crate_name(sess: &Session, krate_attrs: &[ast::Attribute]) -> Symbol
     // 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 attr_crate_name = parse_crate_name(sess, krate_attrs, ShouldEmit::EarlyFatal);
 
     let validate = |name, span| {
         rustc_session::output::validate_crate_name(sess, name, span);
@@ -1283,6 +1283,28 @@ pub fn get_crate_name(sess: &Session, krate_attrs: &[ast::Attribute]) -> Symbol
     sym::rust_out
 }
 
+pub(crate) fn parse_crate_name(
+    sess: &Session,
+    attrs: &[ast::Attribute],
+    emit_errors: ShouldEmit,
+) -> Option<(Symbol, Span)> {
+    let rustc_hir::Attribute::Parsed(AttributeKind::CrateName { name, name_span, .. }) =
+        AttributeParser::parse_limited_should_emit(
+            sess,
+            &attrs,
+            sym::crate_name,
+            DUMMY_SP,
+            rustc_ast::node_id::CRATE_NODE_ID,
+            None,
+            emit_errors,
+        )?
+    else {
+        unreachable!("crate_name is the only attr we could've parsed here");
+    };
+
+    Some((name, name_span))
+}
+
 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
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 9a432a60819..04006f3e446 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -5,7 +5,7 @@ use std::sync::{Arc, OnceLock};
 use std::{env, thread};
 
 use rustc_ast as ast;
-use rustc_attr_parsing::validate_attr;
+use rustc_attr_parsing::{ShouldEmit, validate_attr};
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_data_structures::jobserver::Proxy;
 use rustc_data_structures::sync;
@@ -24,6 +24,7 @@ use rustc_target::spec::Target;
 use tracing::info;
 
 use crate::errors;
+use crate::passes::parse_crate_name;
 
 /// Function pointer type that constructs a new CodegenBackend.
 type MakeBackendFn = fn() -> Box<dyn CodegenBackend>;
@@ -520,11 +521,10 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu
         sess.dcx().emit_fatal(errors::MultipleOutputTypesToStdout);
     }
 
-    let crate_name = sess
-        .opts
-        .crate_name
-        .clone()
-        .or_else(|| rustc_attr_parsing::find_crate_name(attrs).map(|n| n.to_string()));
+    let crate_name =
+        sess.opts.crate_name.clone().or_else(|| {
+            parse_crate_name(sess, attrs, ShouldEmit::Nothing).map(|i| i.0.to_string())
+        });
 
     match sess.io.output_file {
         None => {
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index 8fafaa33d0c..65075cfecfa 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -5,7 +5,7 @@ use rustc_hir::attrs::{AttributeKind, ReprAttr};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{FnKind, Visitor};
-use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatExprKind, PatKind, find_attr};
+use rustc_hir::{Attribute, GenericParamKind, PatExprKind, PatKind, find_attr};
 use rustc_middle::hir::nested_filter::All;
 use rustc_middle::ty;
 use rustc_session::config::CrateType;
@@ -343,35 +343,27 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
         let crate_ident = if let Some(name) = &cx.tcx.sess.opts.crate_name {
             Some(Ident::from_str(name))
         } else {
-            ast::attr::find_by_name(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), sym::crate_name).and_then(
-                |attr| {
-                    if let Attribute::Unparsed(n) = attr
-                        && let AttrItem { args: AttrArgs::Eq { eq_span: _, expr: lit }, .. } =
-                            n.as_ref()
-                        && let ast::LitKind::Str(name, ..) = lit.kind
-                    {
-                        // Discard the double quotes surrounding the literal.
-                        let sp = cx
-                            .sess()
-                            .source_map()
-                            .span_to_snippet(lit.span)
-                            .ok()
-                            .and_then(|snippet| {
-                                let left = snippet.find('"')?;
-                                let right = snippet.rfind('"').map(|pos| snippet.len() - pos)?;
-
-                                Some(
-                                    lit.span
-                                        .with_lo(lit.span.lo() + BytePos(left as u32 + 1))
-                                        .with_hi(lit.span.hi() - BytePos(right as u32)),
-                                )
-                            })
-                            .unwrap_or(lit.span);
-
-                        Some(Ident::new(name, sp))
-                    } else {
-                        None
-                    }
+            find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::CrateName{name, name_span,..} => (name, name_span)).map(
+                |(&name, &span)| {
+                    // Discard the double quotes surrounding the literal.
+                    let sp = cx
+                        .sess()
+                        .source_map()
+                        .span_to_snippet(span)
+                        .ok()
+                        .and_then(|snippet| {
+                            let left = snippet.find('"')?;
+                            let right = snippet.rfind('"').map(|pos| snippet.len() - pos)?;
+
+                            Some(
+                                span
+                                    .with_lo(span.lo() + BytePos(left as u32 + 1))
+                                    .with_hi(span.hi() - BytePos(right as u32)),
+                            )
+                        })
+                        .unwrap_or(span);
+
+                    Ident::new(name, sp)
                 },
             )
         };
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index c610ce4fc85..7aef60b7b91 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -250,7 +250,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     | AttributeKind::ShouldPanic { .. }
                     | AttributeKind::Coroutine(..)
                     | AttributeKind::Linkage(..)
-                    | AttributeKind::MustUse { .. },
+                    | AttributeKind::MustUse { .. }
+                    | AttributeKind::CrateName { .. }
                 ) => { /* do nothing  */ }
                 Attribute::Unparsed(attr_item) => {
                     style = Some(attr_item.style);
@@ -367,22 +368,49 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
 
             if hir_id != CRATE_HIR_ID {
-                if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
-                    attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
-                {
-                    match style {
-                        Some(ast::AttrStyle::Outer) => self.tcx.emit_node_span_lint(
+                match attr {
+                    // FIXME(jdonszelmann) move to attribute parsing when validation gets better there
+                    &Attribute::Parsed(AttributeKind::CrateName {
+                        attr_span: span, style, ..
+                    }) => match style {
+                        ast::AttrStyle::Outer => self.tcx.emit_node_span_lint(
                             UNUSED_ATTRIBUTES,
                             hir_id,
-                            attr.span(),
+                            span,
                             errors::OuterCrateLevelAttr,
                         ),
-                        Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
+                        ast::AttrStyle::Inner => self.tcx.emit_node_span_lint(
                             UNUSED_ATTRIBUTES,
                             hir_id,
-                            attr.span(),
+                            span,
                             errors::InnerCrateLevelAttr,
                         ),
+                    },
+                    Attribute::Parsed(_) => { /* not crate-level */ }
+                    Attribute::Unparsed(attr) => {
+                        // FIXME(jdonszelmann): remove once all crate-level attrs are parsed and caught by
+                        // the above
+                        if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
+                            attr.path
+                                .segments
+                                .first()
+                                .and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
+                        {
+                            match attr.style {
+                                ast::AttrStyle::Outer => self.tcx.emit_node_span_lint(
+                                    UNUSED_ATTRIBUTES,
+                                    hir_id,
+                                    attr.span,
+                                    errors::OuterCrateLevelAttr,
+                                ),
+                                ast::AttrStyle::Inner => self.tcx.emit_node_span_lint(
+                                    UNUSED_ATTRIBUTES,
+                                    hir_id,
+                                    attr.span,
+                                    errors::InnerCrateLevelAttr,
+                                ),
+                            }
+                        }
                     }
                 }
             }
diff --git a/tests/ui/attributes/crate-name-empty.stderr b/tests/ui/attributes/crate-name-empty.stderr
index 509a42d05f7..31d9dd0e948 100644
--- a/tests/ui/attributes/crate-name-empty.stderr
+++ b/tests/ui/attributes/crate-name-empty.stderr
@@ -1,8 +1,8 @@
 error: crate name must not be empty
-  --> $DIR/crate-name-empty.rs:3:1
+  --> $DIR/crate-name-empty.rs:3:17
    |
 LL | #![crate_name = ""]
-   | ^^^^^^^^^^^^^^^^^^^
+   |                 ^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/attributes/crate-name-macro-call.rs b/tests/ui/attributes/crate-name-macro-call.rs
index 1aae2e506e2..61076349cc3 100644
--- a/tests/ui/attributes/crate-name-macro-call.rs
+++ b/tests/ui/attributes/crate-name-macro-call.rs
@@ -1,6 +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
+#![crate_name = concat!("my", "crate")] //~ ERROR attribute value must be a literal
 
 fn main() {}
diff --git a/tests/ui/attributes/crate-name-macro-call.stderr b/tests/ui/attributes/crate-name-macro-call.stderr
index 56827aa11a4..e988b8461d7 100644
--- a/tests/ui/attributes/crate-name-macro-call.stderr
+++ b/tests/ui/attributes/crate-name-macro-call.stderr
@@ -1,10 +1,8 @@
-error: malformed `crate_name` attribute input
-  --> $DIR/crate-name-macro-call.rs:4:1
+error: attribute value must be a literal
+  --> $DIR/crate-name-macro-call.rs:4:17
    |
 LL | #![crate_name = concat!("my", "crate")]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]`
-   |
-   = note: for more information, visit <https://doc.rust-lang.org/reference/crates-and-source-files.html#the-crate_name-attribute>
+   |                 ^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/attributes/crate-name-mismatch.stderr b/tests/ui/attributes/crate-name-mismatch.stderr
index 4021fbe7c18..6416ad2d41a 100644
--- a/tests/ui/attributes/crate-name-mismatch.stderr
+++ b/tests/ui/attributes/crate-name-mismatch.stderr
@@ -1,8 +1,8 @@
 error: `--crate-name` and `#[crate_name]` are required to match, but `foo` != `bar`
-  --> $DIR/crate-name-mismatch.rs:5:1
+  --> $DIR/crate-name-mismatch.rs:5:17
    |
 LL | #![crate_name = "bar"]
-   | ^^^^^^^^^^^^^^^^^^^^^^
+   |                 ^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr
index 11e01ac29be..69f9a18c179 100644
--- a/tests/ui/attributes/malformed-attrs.stderr
+++ b/tests/ui/attributes/malformed-attrs.stderr
@@ -41,14 +41,6 @@ LL | #![windows_subsystem = "console"]
 LL | #![windows_subsystem = "windows"]
    |                      +++++++++++
 
-error: malformed `crate_name` attribute input
-  --> $DIR/malformed-attrs.rs:74:1
-   |
-LL | #[crate_name]
-   | ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]`
-   |
-   = note: for more information, visit <https://doc.rust-lang.org/reference/crates-and-source-files.html#the-crate_name-attribute>
-
 error: malformed `instruction_set` attribute input
   --> $DIR/malformed-attrs.rs:106:1
    |
@@ -478,6 +470,12 @@ LL | #[used()]
    |
    = help: `#[used]` can only be applied to statics
 
+error[E0539]: malformed `crate_name` attribute input
+  --> $DIR/malformed-attrs.rs:74:1
+   |
+LL | #[crate_name]
+   | ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]`
+
 error[E0539]: malformed `target_feature` attribute input
   --> $DIR/malformed-attrs.rs:79:1
    |
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
index c7f3c2c5403..62f1f6cd09c 100644
--- 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
@@ -2,4 +2,4 @@
 // 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
+#![crate_name = concat!("wrapped")] //~ ERROR attribute value must be a literal
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
index b773f7c97aa..fab38dae78b 100644
--- 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
@@ -1,10 +1,8 @@
-error: malformed `crate_name` attribute input
-  --> $DIR/print-crate-name-request-malformed-crate-name.rs:5:1
+error: attribute value must be a literal
+  --> $DIR/print-crate-name-request-malformed-crate-name.rs:5:17
    |
 LL | #![crate_name = concat!("wrapped")]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]`
-   |
-   = note: for more information, visit <https://doc.rust-lang.org/reference/crates-and-source-files.html#the-crate_name-attribute>
+   |                 ^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
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
index 64e38ed8533..d3e60948e4c 100644
--- 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
@@ -1,10 +1,9 @@
-error: malformed `crate_name` attribute input
+error[E0539]: 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"]`
-   |
-   = note: for more information, visit <https://doc.rust-lang.org/reference/crates-and-source-files.html#the-crate_name-attribute>
 
 error: aborting due to 1 previous error
 
+For more information about this error, try `rustc --explain E0539`.
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
index 13c9d1e0027..1ac1208ee44 100644
--- 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
@@ -4,4 +4,4 @@
 
 //@ compile-flags: --print=file-names
 #![crate_name = "this_one_is_okay"]
-#![crate_name = concat!("this_one_is_not")]  //~ ERROR malformed `crate_name` attribute input
+#![crate_name = concat!("this_one_is_not")] //~ ERROR attribute value must be a literal
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
index e9a5b58e4f9..1571521ff17 100644
--- 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
@@ -1,10 +1,8 @@
-error: malformed `crate_name` attribute input
-  --> $DIR/print-file-names-request-malformed-crate-name-2.rs:7:1
+error: attribute value must be a literal
+  --> $DIR/print-file-names-request-malformed-crate-name-2.rs:7:17
    |
 LL | #![crate_name = concat!("this_one_is_not")]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]`
-   |
-   = note: for more information, visit <https://doc.rust-lang.org/reference/crates-and-source-files.html#the-crate_name-attribute>
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 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
index 5fe8bd7945f..6441017c665 100644
--- 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
@@ -2,4 +2,4 @@
 // 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
+#![crate_name = concat!("wrapped")] //~ ERROR attribute value must be a literal
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
index e63525c6a5d..155e2e5a3fa 100644
--- 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
@@ -1,10 +1,8 @@
-error: malformed `crate_name` attribute input
-  --> $DIR/print-file-names-request-malformed-crate-name.rs:5:1
+error: attribute value must be a literal
+  --> $DIR/print-file-names-request-malformed-crate-name.rs:5:17
    |
 LL | #![crate_name = concat!("wrapped")]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]`
-   |
-   = note: for more information, visit <https://doc.rust-lang.org/reference/crates-and-source-files.html#the-crate_name-attribute>
+   |                 ^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/lint/unused/unused-attr-duplicate.rs b/tests/ui/lint/unused/unused-attr-duplicate.rs
index cfa6c2b4228..a334c49788c 100644
--- a/tests/ui/lint/unused/unused-attr-duplicate.rs
+++ b/tests/ui/lint/unused/unused-attr-duplicate.rs
@@ -11,8 +11,12 @@
 // - no_main: extra setup
 #![deny(unused_attributes)]
 #![crate_name = "unused_attr_duplicate"]
-#![crate_name = "unused_attr_duplicate2"] //~ ERROR unused attribute
-//~^ WARN this was previously accepted
+#![crate_name = "unused_attr_duplicate2"]
+//~^ ERROR unused attribute
+//~| WARN this was previously accepted
+//~| ERROR unused attribute
+//~| WARN this was previously accepted
+// FIXME(jdonszelmann) this error is given twice now. I'll look at this in the future
 #![recursion_limit = "128"]
 #![recursion_limit = "256"] //~ ERROR unused attribute
 //~^ WARN this was previously accepted
diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr
index 6c44e884ba5..203211d0d56 100644
--- a/tests/ui/lint/unused/unused-attr-duplicate.stderr
+++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr
@@ -1,14 +1,15 @@
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:33:1
+  --> $DIR/unused-attr-duplicate.rs:14:1
    |
-LL | #[no_link]
-   | ^^^^^^^^^^ help: remove this attribute
+LL | #![crate_name = "unused_attr_duplicate2"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:32:1
+  --> $DIR/unused-attr-duplicate.rs:13:1
    |
-LL | #[no_link]
-   | ^^^^^^^^^^
+LL | #![crate_name = "unused_attr_duplicate"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 note: the lint level is defined here
   --> $DIR/unused-attr-duplicate.rs:12:9
    |
@@ -16,291 +17,304 @@ LL | #![deny(unused_attributes)]
    |         ^^^^^^^^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:14:1
+  --> $DIR/unused-attr-duplicate.rs:37:1
    |
-LL | #![crate_name = "unused_attr_duplicate2"]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
+LL | #[no_link]
+   | ^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:13:1
+  --> $DIR/unused-attr-duplicate.rs:36:1
    |
-LL | #![crate_name = "unused_attr_duplicate"]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+LL | #[no_link]
+   | ^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:17:1
+  --> $DIR/unused-attr-duplicate.rs:21:1
    |
 LL | #![recursion_limit = "256"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:16:1
+  --> $DIR/unused-attr-duplicate.rs:20:1
    |
 LL | #![recursion_limit = "128"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:20:1
+  --> $DIR/unused-attr-duplicate.rs:24:1
    |
 LL | #![type_length_limit = "1"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:19:1
+  --> $DIR/unused-attr-duplicate.rs:23:1
    |
 LL | #![type_length_limit = "1048576"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:23:1
+  --> $DIR/unused-attr-duplicate.rs:27:1
    |
 LL | #![no_std]
    | ^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:22:1
+  --> $DIR/unused-attr-duplicate.rs:26:1
    |
 LL | #![no_std]
    | ^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:27:1
+  --> $DIR/unused-attr-duplicate.rs:31:1
    |
 LL | #![windows_subsystem = "windows"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:26:1
+  --> $DIR/unused-attr-duplicate.rs:30:1
    |
 LL | #![windows_subsystem = "console"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:30:1
+  --> $DIR/unused-attr-duplicate.rs:34:1
    |
 LL | #![no_builtins]
    | ^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:29:1
+  --> $DIR/unused-attr-duplicate.rs:33:1
    |
 LL | #![no_builtins]
    | ^^^^^^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:40:5
+  --> $DIR/unused-attr-duplicate.rs:44:5
    |
 LL |     #[macro_export]
    |     ^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:39:5
+  --> $DIR/unused-attr-duplicate.rs:43:5
    |
 LL |     #[macro_export]
    |     ^^^^^^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:37:1
+  --> $DIR/unused-attr-duplicate.rs:41:1
    |
 LL | #[macro_use]
    | ^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:36:1
+  --> $DIR/unused-attr-duplicate.rs:40:1
    |
 LL | #[macro_use]
    | ^^^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:47:1
+  --> $DIR/unused-attr-duplicate.rs:51:1
    |
 LL | #[path = "bar.rs"]
    | ^^^^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:46:1
+  --> $DIR/unused-attr-duplicate.rs:50:1
    |
 LL | #[path = "auxiliary/lint_unused_extern_crate.rs"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:53:1
+  --> $DIR/unused-attr-duplicate.rs:57:1
    |
 LL | #[ignore = "some text"]
    | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:52:1
+  --> $DIR/unused-attr-duplicate.rs:56:1
    |
 LL | #[ignore]
    | ^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:55:1
+  --> $DIR/unused-attr-duplicate.rs:59:1
    |
 LL | #[should_panic(expected = "values don't match")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:54:1
+  --> $DIR/unused-attr-duplicate.rs:58:1
    |
 LL | #[should_panic]
    | ^^^^^^^^^^^^^^^
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:60:1
+  --> $DIR/unused-attr-duplicate.rs:64:1
    |
 LL | #[must_use = "some message"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:59:1
+  --> $DIR/unused-attr-duplicate.rs:63:1
    |
 LL | #[must_use]
    | ^^^^^^^^^^^
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:66:1
+  --> $DIR/unused-attr-duplicate.rs:70:1
    |
 LL | #[non_exhaustive]
    | ^^^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:65:1
+  --> $DIR/unused-attr-duplicate.rs:69:1
    |
 LL | #[non_exhaustive]
    | ^^^^^^^^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:72:1
+  --> $DIR/unused-attr-duplicate.rs:76:1
    |
 LL | #[automatically_derived]
    | ^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:71:1
+  --> $DIR/unused-attr-duplicate.rs:75:1
    |
 LL | #[automatically_derived]
    | ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:76:1
+  --> $DIR/unused-attr-duplicate.rs:80:1
    |
 LL | #[inline(never)]
    | ^^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:75:1
+  --> $DIR/unused-attr-duplicate.rs:79:1
    |
 LL | #[inline(always)]
    | ^^^^^^^^^^^^^^^^^
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:79:1
+  --> $DIR/unused-attr-duplicate.rs:83:1
    |
 LL | #[cold]
    | ^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:78:1
+  --> $DIR/unused-attr-duplicate.rs:82:1
    |
 LL | #[cold]
    | ^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:81:1
+  --> $DIR/unused-attr-duplicate.rs:85:1
    |
 LL | #[track_caller]
    | ^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:80:1
+  --> $DIR/unused-attr-duplicate.rs:84:1
    |
 LL | #[track_caller]
    | ^^^^^^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:88:5
+  --> $DIR/unused-attr-duplicate.rs:92:5
    |
 LL |     #[link_name = "this_does_not_exist"]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:90:5
+  --> $DIR/unused-attr-duplicate.rs:94:5
    |
 LL |     #[link_name = "rust_dbg_extern_identity_u32"]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:94:1
+  --> $DIR/unused-attr-duplicate.rs:98:1
    |
 LL | #[export_name = "exported_symbol_name"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:96:1
+  --> $DIR/unused-attr-duplicate.rs:100:1
    |
 LL | #[export_name = "exported_symbol_name2"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:100:1
+  --> $DIR/unused-attr-duplicate.rs:104:1
    |
 LL | #[no_mangle]
    | ^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:99:1
+  --> $DIR/unused-attr-duplicate.rs:103:1
    |
 LL | #[no_mangle]
    | ^^^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:104:1
+  --> $DIR/unused-attr-duplicate.rs:108:1
    |
 LL | #[used]
    | ^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:103:1
+  --> $DIR/unused-attr-duplicate.rs:107:1
    |
 LL | #[used]
    | ^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:107:1
+  --> $DIR/unused-attr-duplicate.rs:111:1
    |
 LL | #[link_section = ".text"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:110:1
+  --> $DIR/unused-attr-duplicate.rs:114:1
    |
 LL | #[link_section = ".bss"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:25:1
+  --> $DIR/unused-attr-duplicate.rs:14:1
+   |
+LL | #![crate_name = "unused_attr_duplicate2"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
+   |
+note: attribute also specified here
+  --> $DIR/unused-attr-duplicate.rs:13:1
+   |
+LL | #![crate_name = "unused_attr_duplicate"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: unused attribute
+  --> $DIR/unused-attr-duplicate.rs:29:1
    |
 LL | #![no_implicit_prelude]
    | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:24:1
+  --> $DIR/unused-attr-duplicate.rs:28:1
    |
 LL | #![no_implicit_prelude]
    | ^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 24 previous errors
+error: aborting due to 25 previous errors