about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJonathan Brouwer <jonathantbrouwer@gmail.com>2025-06-24 23:05:45 +0200
committerJonathan Brouwer <jonathantbrouwer@gmail.com>2025-06-26 08:50:42 +0200
commit3d1cee53244dce3a9e58e04c87ba8d02d964f79f (patch)
tree2370a72d097c42d9173078b0a1722c46b7a46265
parent287d9afce729dd81fa9abfa860af31656d9c5e16 (diff)
downloadrust-3d1cee53244dce3a9e58e04c87ba8d02d964f79f.tar.gz
rust-3d1cee53244dce3a9e58e04c87ba8d02d964f79f.zip
Move mixed export_name/no_mangle check to check_attr.rs and improve the error
Signed-off-by: Jonathan Brouwer <jonathantbrouwer@gmail.com>
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl5
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs56
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs12
-rw-r--r--compiler/rustc_passes/messages.ftl5
-rw-r--r--compiler/rustc_passes/src/check_attr.rs33
-rw-r--r--compiler/rustc_passes/src/errors.rs12
-rw-r--r--tests/ui/attributes/mixed_export_name_and_no_mangle.fixed4
-rw-r--r--tests/ui/attributes/mixed_export_name_and_no_mangle.rs4
-rw-r--r--tests/ui/attributes/mixed_export_name_and_no_mangle.stderr12
-rw-r--r--tests/ui/attributes/mixed_export_name_and_no_mangle_2024.fixed15
-rw-r--r--tests/ui/attributes/mixed_export_name_and_no_mangle_2024.rs17
-rw-r--r--tests/ui/attributes/mixed_export_name_and_no_mangle_2024.stderr39
12 files changed, 133 insertions, 81 deletions
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 4f2c832a3a4..6362d2edf85 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -205,11 +205,6 @@ codegen_ssa_missing_features = add the missing features in a `target_feature` at
 codegen_ssa_missing_query_depgraph =
     found CGU-reuse attribute but `-Zquery-dep-graph` was not specified
 
-codegen_ssa_mixed_export_name_and_no_mangle = `{$no_mangle_attr}` attribute may not be used in combination with `#[export_name]`
-    .label = `{$no_mangle_attr}` is ignored
-    .note = `#[export_name]` takes precedence
-    .suggestion = remove the `{$no_mangle_attr}` attribute
-
 codegen_ssa_msvc_missing_linker = the msvc targets depend on the msvc linker but `link.exe` was not found
 
 codegen_ssa_multiple_external_func_decl = multiple declarations of external function `{$function}` from library `{$library_name}` have different calling conventions
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index a6b3dfec4dc..94df5e8b361 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -9,7 +9,7 @@ use rustc_attr_data_structures::{
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
 use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
-use rustc_hir::{self as hir, HirId, LangItem, lang_items};
+use rustc_hir::{self as hir, LangItem, lang_items};
 use rustc_middle::middle::codegen_fn_attrs::{
     CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
 };
@@ -87,7 +87,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
 
     let mut link_ordinal_span = None;
     let mut no_sanitize_span = None;
-    let mut mixed_export_name_no_mangle_lint_state = MixedExportNameAndNoMangleState::default();
 
     for attr in attrs.iter() {
         // In some cases, attribute are only valid on functions, but it's the `check_attr`
@@ -119,20 +118,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                         .max();
                 }
                 AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
-                AttributeKind::ExportName { name, span: attr_span } => {
+                AttributeKind::ExportName { name, .. } => {
                     codegen_fn_attrs.export_name = Some(*name);
-                    mixed_export_name_no_mangle_lint_state.track_export_name(*attr_span);
                 }
                 AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
                 AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
                 AttributeKind::NoMangle(attr_span) => {
                     if tcx.opt_item_name(did.to_def_id()).is_some() {
                         codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
-                        mixed_export_name_no_mangle_lint_state.track_no_mangle(
-                            *attr_span,
-                            tcx.local_def_id_to_hir_id(did),
-                            attr,
-                        );
                     } else {
                         tcx.dcx().emit_err(NoMangleNameless {
                             span: *attr_span,
@@ -437,8 +430,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
         }
     }
 
-    mixed_export_name_no_mangle_lint_state.lint_if_mixed(tcx);
-
     // Apply the minimum function alignment here, so that individual backends don't have to.
     codegen_fn_attrs.alignment =
         Ord::max(codegen_fn_attrs.alignment, tcx.sess.opts.unstable_opts.min_function_alignment);
@@ -665,49 +656,6 @@ fn check_link_name_xor_ordinal(
     }
 }
 
-#[derive(Default)]
-struct MixedExportNameAndNoMangleState<'a> {
-    export_name: Option<Span>,
-    hir_id: Option<HirId>,
-    no_mangle: Option<Span>,
-    no_mangle_attr: Option<&'a hir::Attribute>,
-}
-
-impl<'a> MixedExportNameAndNoMangleState<'a> {
-    fn track_export_name(&mut self, span: Span) {
-        self.export_name = Some(span);
-    }
-
-    fn track_no_mangle(&mut self, span: Span, hir_id: HirId, attr_name: &'a hir::Attribute) {
-        self.no_mangle = Some(span);
-        self.hir_id = Some(hir_id);
-        self.no_mangle_attr = Some(attr_name);
-    }
-
-    /// Emit diagnostics if the lint condition is met.
-    fn lint_if_mixed(self, tcx: TyCtxt<'_>) {
-        if let Self {
-            export_name: Some(export_name),
-            no_mangle: Some(no_mangle),
-            hir_id: Some(hir_id),
-            no_mangle_attr: Some(_),
-        } = self
-        {
-            tcx.emit_node_span_lint(
-                lint::builtin::UNUSED_ATTRIBUTES,
-                hir_id,
-                no_mangle,
-                errors::MixedExportNameAndNoMangle {
-                    no_mangle,
-                    no_mangle_attr: "#[unsafe(no_mangle)]".to_string(),
-                    export_name,
-                    removal_span: no_mangle,
-                },
-            );
-        }
-    }
-}
-
 /// We now check the #\[rustc_autodiff\] attributes which we generated from the #[autodiff(...)]
 /// macros. There are two forms. The pure one without args to mark primal functions (the functions
 /// being differentiated). The other form is #[rustc_autodiff(Mode, ActivityList)] on top of the
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index 315d9c92ab0..db536af0162 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -1200,18 +1200,6 @@ pub(crate) struct ErrorCreatingImportLibrary<'a> {
 #[diag(codegen_ssa_aix_strip_not_used)]
 pub(crate) struct AixStripNotUsed;
 
-#[derive(LintDiagnostic)]
-#[diag(codegen_ssa_mixed_export_name_and_no_mangle)]
-pub(crate) struct MixedExportNameAndNoMangle {
-    #[label]
-    pub no_mangle: Span,
-    pub no_mangle_attr: String,
-    #[note]
-    pub export_name: Span,
-    #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")]
-    pub removal_span: Span,
-}
-
 #[derive(Diagnostic, Debug)]
 pub(crate) enum XcrunError {
     #[diag(codegen_ssa_xcrun_failed_invoking)]
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index f601d058b33..e2995daadfe 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -486,6 +486,11 @@ passes_missing_panic_handler =
 passes_missing_stability_attr =
     {$descr} has missing stability attribute
 
+passes_mixed_export_name_and_no_mangle = `{$no_mangle_attr}` attribute may not be used in combination with `{$export_name_attr}`
+    .label = `{$no_mangle_attr}` is ignored
+    .note = `{$export_name_attr}` takes precedence
+    .suggestion = remove the `{$no_mangle_attr}` attribute
+
 passes_multiple_rustc_main =
     multiple functions with a `#[rustc_main]` attribute
     .first = first `#[rustc_main]` function
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 7c985fa9f03..491b3699f4c 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -30,11 +30,13 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{self, TyCtxt, TypingMode};
 use rustc_middle::{bug, span_bug};
 use rustc_session::config::CrateType;
+use rustc_session::lint;
 use rustc_session::lint::builtin::{
     CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS,
     UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,
 };
 use rustc_session::parse::feature_err;
+use rustc_span::edition::Edition;
 use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, edition, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs};
@@ -403,6 +405,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         self.check_repr(attrs, span, target, item, hir_id);
         self.check_used(attrs, target, span);
         self.check_rustc_force_inline(hir_id, attrs, span, target);
+        self.check_mix_no_mangle_export(hir_id, attrs);
     }
 
     fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr_span: Span, sym: &str) {
@@ -2628,6 +2631,36 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
     }
 
+    fn check_mix_no_mangle_export(&self, hir_id: HirId, attrs: &[Attribute]) {
+        if let Some(export_name_span) = find_attr!(attrs, AttributeKind::ExportName { span: export_name_span, .. } => *export_name_span)
+            && let Some(no_mangle_span) =
+                find_attr!(attrs, AttributeKind::NoMangle(no_mangle_span) => *no_mangle_span)
+        {
+            let no_mangle_attr = if no_mangle_span.edition() >= Edition::Edition2024 {
+                "#[unsafe(no_mangle)]"
+            } else {
+                "#[no_mangle]"
+            };
+            let export_name_attr = if export_name_span.edition() >= Edition::Edition2024 {
+                "#[unsafe(export_name)]"
+            } else {
+                "#[export_name]"
+            };
+
+            self.tcx.emit_node_span_lint(
+                lint::builtin::UNUSED_ATTRIBUTES,
+                hir_id,
+                no_mangle_span,
+                errors::MixedExportNameAndNoMangle {
+                    no_mangle_span,
+                    export_name_span,
+                    no_mangle_attr,
+                    export_name_attr,
+                },
+            );
+        }
+    }
+
     /// Checks if `#[autodiff]` is applied to an item other than a function item.
     fn check_autodiff(&self, _hir_id: HirId, _attr: &Attribute, span: Span, target: Target) {
         debug!("check_autodiff");
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index d9ec167aae3..3286ccc94f2 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -50,6 +50,18 @@ pub(crate) struct ConstContinueAttr {
 }
 
 #[derive(LintDiagnostic)]
+#[diag(passes_mixed_export_name_and_no_mangle)]
+pub(crate) struct MixedExportNameAndNoMangle {
+    #[label]
+    #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")]
+    pub no_mangle_span: Span,
+    #[note]
+    pub export_name_span: Span,
+    pub no_mangle_attr: &'static str,
+    pub export_name_attr: &'static str,
+}
+
+#[derive(LintDiagnostic)]
 #[diag(passes_outer_crate_level_attr)]
 pub(crate) struct OuterCrateLevelAttr;
 
diff --git a/tests/ui/attributes/mixed_export_name_and_no_mangle.fixed b/tests/ui/attributes/mixed_export_name_and_no_mangle.fixed
index d8b5235c52f..55c196f6dec 100644
--- a/tests/ui/attributes/mixed_export_name_and_no_mangle.fixed
+++ b/tests/ui/attributes/mixed_export_name_and_no_mangle.fixed
@@ -3,11 +3,11 @@
 //@ check-pass
 
 #![warn(unused_attributes)]
-//~^ WARN `#[unsafe(no_mangle)]` attribute may not be used in combination with `#[export_name]` [unused_attributes]
+//~^ WARN `#[no_mangle]` attribute may not be used in combination with `#[export_name]` [unused_attributes]
 #[export_name = "foo"]
 pub fn bar() {}
 
-//~^ WARN `#[unsafe(no_mangle)]` attribute may not be used in combination with `#[export_name]` [unused_attributes]
+//~^ WARN `#[no_mangle]` attribute may not be used in combination with `#[export_name]` [unused_attributes]
 #[export_name = "baz"]
 pub fn bak() {}
 
diff --git a/tests/ui/attributes/mixed_export_name_and_no_mangle.rs b/tests/ui/attributes/mixed_export_name_and_no_mangle.rs
index 83a673a7d13..79f1e5c19c5 100644
--- a/tests/ui/attributes/mixed_export_name_and_no_mangle.rs
+++ b/tests/ui/attributes/mixed_export_name_and_no_mangle.rs
@@ -4,12 +4,12 @@
 
 #![warn(unused_attributes)]
 #[no_mangle]
-//~^ WARN `#[unsafe(no_mangle)]` attribute may not be used in combination with `#[export_name]` [unused_attributes]
+//~^ WARN `#[no_mangle]` attribute may not be used in combination with `#[export_name]` [unused_attributes]
 #[export_name = "foo"]
 pub fn bar() {}
 
 #[unsafe(no_mangle)]
-//~^ WARN `#[unsafe(no_mangle)]` attribute may not be used in combination with `#[export_name]` [unused_attributes]
+//~^ WARN `#[no_mangle]` attribute may not be used in combination with `#[export_name]` [unused_attributes]
 #[export_name = "baz"]
 pub fn bak() {}
 
diff --git a/tests/ui/attributes/mixed_export_name_and_no_mangle.stderr b/tests/ui/attributes/mixed_export_name_and_no_mangle.stderr
index c760d27db25..1dcaa636800 100644
--- a/tests/ui/attributes/mixed_export_name_and_no_mangle.stderr
+++ b/tests/ui/attributes/mixed_export_name_and_no_mangle.stderr
@@ -1,8 +1,8 @@
-warning: `#[unsafe(no_mangle)]` attribute may not be used in combination with `#[export_name]`
+warning: `#[no_mangle]` attribute may not be used in combination with `#[export_name]`
   --> $DIR/mixed_export_name_and_no_mangle.rs:6:1
    |
 LL | #[no_mangle]
-   | ^^^^^^^^^^^^ `#[unsafe(no_mangle)]` is ignored
+   | ^^^^^^^^^^^^ `#[no_mangle]` is ignored
    |
 note: `#[export_name]` takes precedence
   --> $DIR/mixed_export_name_and_no_mangle.rs:8:1
@@ -14,23 +14,23 @@ note: the lint level is defined here
    |
 LL | #![warn(unused_attributes)]
    |         ^^^^^^^^^^^^^^^^^
-help: remove the `#[unsafe(no_mangle)]` attribute
+help: remove the `#[no_mangle]` attribute
    |
 LL - #[no_mangle]
    |
 
-warning: `#[unsafe(no_mangle)]` attribute may not be used in combination with `#[export_name]`
+warning: `#[no_mangle]` attribute may not be used in combination with `#[export_name]`
   --> $DIR/mixed_export_name_and_no_mangle.rs:11:1
    |
 LL | #[unsafe(no_mangle)]
-   | ^^^^^^^^^^^^^^^^^^^^ `#[unsafe(no_mangle)]` is ignored
+   | ^^^^^^^^^^^^^^^^^^^^ `#[no_mangle]` is ignored
    |
 note: `#[export_name]` takes precedence
   --> $DIR/mixed_export_name_and_no_mangle.rs:13:1
    |
 LL | #[export_name = "baz"]
    | ^^^^^^^^^^^^^^^^^^^^^^
-help: remove the `#[unsafe(no_mangle)]` attribute
+help: remove the `#[no_mangle]` attribute
    |
 LL - #[unsafe(no_mangle)]
    |
diff --git a/tests/ui/attributes/mixed_export_name_and_no_mangle_2024.fixed b/tests/ui/attributes/mixed_export_name_and_no_mangle_2024.fixed
new file mode 100644
index 00000000000..581cb200770
--- /dev/null
+++ b/tests/ui/attributes/mixed_export_name_and_no_mangle_2024.fixed
@@ -0,0 +1,15 @@
+// issue: rust-lang/rust#47446
+//@ run-rustfix
+//@ check-pass
+//@ edition:2024
+
+#![warn(unused_attributes)]
+//~^ WARN `#[unsafe(no_mangle)]` attribute may not be used in combination with `#[unsafe(export_name)]` [unused_attributes]
+#[unsafe(export_name = "foo")]
+pub fn bar() {}
+
+//~^ WARN `#[unsafe(no_mangle)]` attribute may not be used in combination with `#[unsafe(export_name)]` [unused_attributes]
+#[unsafe(export_name = "baz")]
+pub fn bak() {}
+
+fn main() {}
diff --git a/tests/ui/attributes/mixed_export_name_and_no_mangle_2024.rs b/tests/ui/attributes/mixed_export_name_and_no_mangle_2024.rs
new file mode 100644
index 00000000000..1e4a06132f2
--- /dev/null
+++ b/tests/ui/attributes/mixed_export_name_and_no_mangle_2024.rs
@@ -0,0 +1,17 @@
+// issue: rust-lang/rust#47446
+//@ run-rustfix
+//@ check-pass
+//@ edition:2024
+
+#![warn(unused_attributes)]
+#[unsafe(no_mangle)]
+//~^ WARN `#[unsafe(no_mangle)]` attribute may not be used in combination with `#[unsafe(export_name)]` [unused_attributes]
+#[unsafe(export_name = "foo")]
+pub fn bar() {}
+
+#[unsafe(no_mangle)]
+//~^ WARN `#[unsafe(no_mangle)]` attribute may not be used in combination with `#[unsafe(export_name)]` [unused_attributes]
+#[unsafe(export_name = "baz")]
+pub fn bak() {}
+
+fn main() {}
diff --git a/tests/ui/attributes/mixed_export_name_and_no_mangle_2024.stderr b/tests/ui/attributes/mixed_export_name_and_no_mangle_2024.stderr
new file mode 100644
index 00000000000..09804f9f92b
--- /dev/null
+++ b/tests/ui/attributes/mixed_export_name_and_no_mangle_2024.stderr
@@ -0,0 +1,39 @@
+warning: `#[unsafe(no_mangle)]` attribute may not be used in combination with `#[unsafe(export_name)]`
+  --> $DIR/mixed_export_name_and_no_mangle_2024.rs:7:1
+   |
+LL | #[unsafe(no_mangle)]
+   | ^^^^^^^^^^^^^^^^^^^^ `#[unsafe(no_mangle)]` is ignored
+   |
+note: `#[unsafe(export_name)]` takes precedence
+  --> $DIR/mixed_export_name_and_no_mangle_2024.rs:9:1
+   |
+LL | #[unsafe(export_name = "foo")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: the lint level is defined here
+  --> $DIR/mixed_export_name_and_no_mangle_2024.rs:6:9
+   |
+LL | #![warn(unused_attributes)]
+   |         ^^^^^^^^^^^^^^^^^
+help: remove the `#[unsafe(no_mangle)]` attribute
+   |
+LL - #[unsafe(no_mangle)]
+   |
+
+warning: `#[unsafe(no_mangle)]` attribute may not be used in combination with `#[unsafe(export_name)]`
+  --> $DIR/mixed_export_name_and_no_mangle_2024.rs:12:1
+   |
+LL | #[unsafe(no_mangle)]
+   | ^^^^^^^^^^^^^^^^^^^^ `#[unsafe(no_mangle)]` is ignored
+   |
+note: `#[unsafe(export_name)]` takes precedence
+  --> $DIR/mixed_export_name_and_no_mangle_2024.rs:14:1
+   |
+LL | #[unsafe(export_name = "baz")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: remove the `#[unsafe(no_mangle)]` attribute
+   |
+LL - #[unsafe(no_mangle)]
+   |
+
+warning: 2 warnings emitted
+