https://github.com/Xazax-hun created 
https://github.com/llvm/llvm-project/pull/222902

API notes are matched against whichever declaration the compiler reaches, which 
can be a redeclaration that follows the definition. When the definition lives 
in one module and the annotated redeclaration in another, that is exactly what 
happens: checkNewAttributesAfterDef() warns "attribute declaration must precede 
definition" and erases the attribute, so the annotation is silently lost.

The warning exists to tell users that an attribute they wrote has no effect. 
Attributes from API notes are not written in the source, so the warning has 
nowhere to point and there is nothing for the user to correct. Skip attributes 
with an invalid location, alongside the existing exceptions.

rdar://186930250

From fa1e1ba081f5bfc75d4cd39f7a82789c0cc0e1eb Mon Sep 17 00:00:00 2001
From: Gabor Horvath <[email protected]>
Date: Fri, 11 Sep 2026 10:34:30 +0100
Subject: [PATCH] [clang][APINotes] Do not drop attributes applied after a
 definition

API notes are matched against whichever declaration the compiler reaches,
which can be a redeclaration that follows the definition. When the definition
lives in one module and the annotated redeclaration in another, that is
exactly what happens: checkNewAttributesAfterDef() warns "attribute
declaration must precede definition" and erases the attribute, so the
annotation is silently lost.

The warning exists to tell users that an attribute they wrote has no effect.
Attributes from API notes are not written in the source, so the warning
has nowhere to point and there is nothing for the user to correct. Skip
attributes with an invalid location, alongside the existing exceptions.

rdar://186930250
---
 clang/lib/Sema/SemaDecl.cpp                           | 11 +++++++++++
 .../APINotes/Inputs/Headers/RedeclAnnotation.apinotes |  5 +++++
 clang/test/APINotes/Inputs/Headers/RedeclAnnotation.h |  3 +++
 clang/test/APINotes/Inputs/Headers/RedeclDefinition.h |  1 +
 clang/test/APINotes/Inputs/Headers/module.modulemap   |  9 +++++++++
 clang/test/APINotes/redecl-after-definition.c         | 10 ++++++++++
 6 files changed, 39 insertions(+)
 create mode 100644 clang/test/APINotes/Inputs/Headers/RedeclAnnotation.apinotes
 create mode 100644 clang/test/APINotes/Inputs/Headers/RedeclAnnotation.h
 create mode 100644 clang/test/APINotes/Inputs/Headers/RedeclDefinition.h
 create mode 100644 clang/test/APINotes/redecl-after-definition.c

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index a9047f61a8bf5..5de5821fe263e 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -3144,6 +3144,17 @@ static void checkNewAttributesAfterDef(Sema &S, Decl 
*New, const Decl *Old) {
       continue; // regular attr merging will take care of validating this.
     }
 
+    if (NewAttribute->getLocation().isInvalid()) {
+      // An attribute with no source location was not written by the user. API
+      // notes, in particular, are matched against whichever declaration the
+      // compiler reaches, which can be a redeclaration that follows the
+      // definition, possibly in a different module. There is nothing for the
+      // user to correct, and erasing the attribute would silently change what
+      // the annotated API means.
+      ++I;
+      continue;
+    }
+
     if (isa<C11NoReturnAttr>(NewAttribute)) {
       // C's _Noreturn is allowed to be added to a function after it is 
defined.
       ++I;
diff --git a/clang/test/APINotes/Inputs/Headers/RedeclAnnotation.apinotes 
b/clang/test/APINotes/Inputs/Headers/RedeclAnnotation.apinotes
new file mode 100644
index 0000000000000..512732766fb46
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/RedeclAnnotation.apinotes
@@ -0,0 +1,5 @@
+Name: RedeclAnnotation
+Functions:
+- Name: redeclaredAfterDefinition
+  Availability: none
+  AvailabilityMsg: not available
diff --git a/clang/test/APINotes/Inputs/Headers/RedeclAnnotation.h 
b/clang/test/APINotes/Inputs/Headers/RedeclAnnotation.h
new file mode 100644
index 0000000000000..6383cee22a833
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/RedeclAnnotation.h
@@ -0,0 +1,3 @@
+#include "RedeclDefinition.h"
+
+int redeclaredAfterDefinition(int x);
diff --git a/clang/test/APINotes/Inputs/Headers/RedeclDefinition.h 
b/clang/test/APINotes/Inputs/Headers/RedeclDefinition.h
new file mode 100644
index 0000000000000..5b034df6077df
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/RedeclDefinition.h
@@ -0,0 +1 @@
+inline int redeclaredAfterDefinition(int x) { return x; }
diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap 
b/clang/test/APINotes/Inputs/Headers/module.modulemap
index 592d482ea7a57..a9b273ccc90e6 100644
--- a/clang/test/APINotes/Inputs/Headers/module.modulemap
+++ b/clang/test/APINotes/Inputs/Headers/module.modulemap
@@ -75,3 +75,12 @@ module WhereParametersSema {
   header "WhereParametersSema.h"
   export *
 }
+
+module RedeclDefinition {
+  header "RedeclDefinition.h"
+}
+
+module RedeclAnnotation {
+  header "RedeclAnnotation.h"
+  export *
+}
diff --git a/clang/test/APINotes/redecl-after-definition.c 
b/clang/test/APINotes/redecl-after-definition.c
new file mode 100644
index 0000000000000..466105d613fee
--- /dev/null
+++ b/clang/test/APINotes/redecl-after-definition.c
@@ -0,0 +1,10 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I 
%S/Inputs/Headers %s -verify
+
+#include "RedeclDefinition.h"
+#include "RedeclAnnotation.h"
+
+void test(void) {
+  redeclaredAfterDefinition(1); // expected-error{{'redeclaredAfterDefinition' 
is unavailable: not available}}
+  // 
expected-note@Inputs/Headers/RedeclAnnotation.h:3{{'redeclaredAfterDefinition' 
has been explicitly marked unavailable here}}
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to