From 3c8dad152bc1e3baca25e5b7b6b7ea6eccef060a Mon Sep 17 00:00:00 2001
From: Shina <53410646+s7tya@users.noreply.github.com>
Date: Sat, 17 Aug 2024 15:03:20 +0900
Subject: [PATCH] Emit an error for invalid use of the linkage attribute

---
 compiler/rustc_passes/messages.ftl      |  4 ++
 compiler/rustc_passes/src/check_attr.rs | 15 ++++++-
 compiler/rustc_passes/src/errors.rs     |  9 ++++
 tests/ui/attributes/linkage.rs          | 42 +++++++++++++++++++
 tests/ui/attributes/linkage.stderr      | 55 +++++++++++++++++++++++++
 5 files changed, 124 insertions(+), 1 deletion(-)
 create mode 100644 tests/ui/attributes/linkage.rs
 create mode 100644 tests/ui/attributes/linkage.stderr

diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 59c9d1e49f5b3..86ce552ad16d9 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -427,6 +427,10 @@ passes_link_section =
     .warn = {-passes_previously_accepted}
     .label = not a function or static
 
+passes_linkage =
+    attribute should be applied to a function or static
+    .label = not a function definition or static
+
 passes_macro_export =
     `#[macro_export]` only has an effect on macro definitions
 
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index c8d4c190113b2..e16509261404f 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -243,6 +243,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 [sym::coroutine, ..] => {
                     self.check_coroutine(attr, target);
                 }
+                [sym::linkage, ..] => self.check_linkage(attr, span, target),
                 [
                     // ok
                     sym::allow
@@ -256,7 +257,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     | sym::cfi_encoding // FIXME(cfi_encoding)
                     | sym::may_dangle // FIXME(dropck_eyepatch)
                     | sym::pointee // FIXME(derive_smart_pointer)
-                    | sym::linkage // FIXME(linkage)
                     | sym::omit_gdb_pretty_printer_section // FIXME(omit_gdb_pretty_printer_section)
                     | sym::used // handled elsewhere to restrict to static items
                     | sym::repr // handled elsewhere to restrict to type decls items
@@ -2349,6 +2349,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             }
         }
     }
+
+    fn check_linkage(&self, attr: &Attribute, span: Span, target: Target) {
+        match target {
+            Target::Fn
+            | Target::Method(..)
+            | Target::Static
+            | Target::ForeignStatic
+            | Target::ForeignFn => {}
+            _ => {
+                self.dcx().emit_err(errors::Linkage { attr_span: attr.span, span });
+            }
+        }
+    }
 }
 
 impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 36dfc40e7628b..3a043e0e3c196 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -643,6 +643,15 @@ pub struct CoroutineOnNonClosure {
     pub span: Span,
 }
 
+#[derive(Diagnostic)]
+#[diag(passes_linkage)]
+pub struct Linkage {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
 #[derive(Diagnostic)]
 #[diag(passes_empty_confusables)]
 pub(crate) struct EmptyConfusables {
diff --git a/tests/ui/attributes/linkage.rs b/tests/ui/attributes/linkage.rs
new file mode 100644
index 0000000000000..0d5ce699fa808
--- /dev/null
+++ b/tests/ui/attributes/linkage.rs
@@ -0,0 +1,42 @@
+#![feature(linkage)]
+#![feature(stmt_expr_attributes)]
+#![deny(unused_attributes)]
+#![allow(dead_code)]
+
+#[linkage = "weak"] //~ ERROR attribute should be applied to a function or static
+type InvalidTy = ();
+
+#[linkage = "weak"] //~ ERROR attribute should be applied to a function or static
+mod invalid_module {}
+
+#[linkage = "weak"] //~ ERROR attribute should be applied to a function or static
+struct F;
+
+#[linkage = "weak"] //~ ERROR attribute should be applied to a function or static
+impl F {
+    #[linkage = "weak"]
+    fn valid(&self) {}
+}
+
+#[linkage = "weak"]
+fn f() {
+    #[linkage = "weak"]
+    {
+        1
+    };
+    //~^^^^ ERROR attribute should be applied to a function or static
+}
+
+extern "C" {
+    #[linkage = "weak"]
+    static A: *const ();
+
+    #[linkage = "weak"]
+    fn bar();
+}
+
+fn main() {
+    let _ = #[linkage = "weak"]
+    (|| 1);
+    //~^^ ERROR attribute should be applied to a function or static
+}
diff --git a/tests/ui/attributes/linkage.stderr b/tests/ui/attributes/linkage.stderr
new file mode 100644
index 0000000000000..d5595529f400a
--- /dev/null
+++ b/tests/ui/attributes/linkage.stderr
@@ -0,0 +1,55 @@
+error: attribute should be applied to a function or static
+  --> $DIR/linkage.rs:6:1
+   |
+LL | #[linkage = "weak"]
+   | ^^^^^^^^^^^^^^^^^^^
+LL | type InvalidTy = ();
+   | -------------------- not a function definition or static
+
+error: attribute should be applied to a function or static
+  --> $DIR/linkage.rs:9:1
+   |
+LL | #[linkage = "weak"]
+   | ^^^^^^^^^^^^^^^^^^^
+LL | mod invalid_module {}
+   | --------------------- not a function definition or static
+
+error: attribute should be applied to a function or static
+  --> $DIR/linkage.rs:12:1
+   |
+LL | #[linkage = "weak"]
+   | ^^^^^^^^^^^^^^^^^^^
+LL | struct F;
+   | --------- not a function definition or static
+
+error: attribute should be applied to a function or static
+  --> $DIR/linkage.rs:15:1
+   |
+LL |   #[linkage = "weak"]
+   |   ^^^^^^^^^^^^^^^^^^^
+LL | / impl F {
+LL | |     #[linkage = "weak"]
+LL | |     fn valid(&self) {}
+LL | | }
+   | |_- not a function definition or static
+
+error: attribute should be applied to a function or static
+  --> $DIR/linkage.rs:23:5
+   |
+LL |       #[linkage = "weak"]
+   |       ^^^^^^^^^^^^^^^^^^^
+LL | /     {
+LL | |         1
+LL | |     };
+   | |_____- not a function definition or static
+
+error: attribute should be applied to a function or static
+  --> $DIR/linkage.rs:39:13
+   |
+LL |     let _ = #[linkage = "weak"]
+   |             ^^^^^^^^^^^^^^^^^^^
+LL |     (|| 1);
+   |     ------ not a function definition or static
+
+error: aborting due to 6 previous errors
+