https://github.com/NeKon69 updated 
https://github.com/llvm/llvm-project/pull/197753

>From 297fbacd610b0ec800173109bd1713e59e72697d Mon Sep 17 00:00:00 2001
From: NeKon69 <[email protected]>
Date: Thu, 14 May 2026 19:50:21 +0300
Subject: [PATCH 1/7] add implementation

---
 .../LifetimeSafety/LifetimeAnnotations.h      |  5 +++
 .../Analyses/LifetimeSafety/LifetimeSafety.h  | 10 +++++
 clang/include/clang/Basic/DiagnosticGroups.td |  9 ++++-
 .../clang/Basic/DiagnosticSemaKinds.td        |  6 +++
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 23 +++++++++++
 .../LifetimeSafety/LifetimeAnnotations.cpp    | 13 ++++--
 clang/lib/Sema/SemaLifetimeSafety.h           | 27 +++++++++++++
 ...ifetime-safety-misplaced-lifetimebound.cpp | 40 +++++++++++++++++++
 8 files changed, 129 insertions(+), 4 deletions(-)
 create mode 100644 
clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp

diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index f418f8a5132ec..ec05e67b05853 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -41,6 +41,11 @@ bool isNormalAssignmentOperator(const FunctionDecl *FD);
 /// has the lifetimebound attribute.
 bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD);
 
+/// Returns the lifetimebound attribute for the implicit this parameter, if it
+/// exists on the current type.
+const LifetimeBoundAttr *
+getDirectImplicitObjectLifetimeBoundAttr(const FunctionDecl *FD);
+
 /// Returns the lifetimebound attribute for the implicit this parameter, if it
 /// exists on any redeclaration.
 const LifetimeBoundAttr *
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 3631b990eb542..132d20e9ed5f7 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -130,6 +130,16 @@ class LifetimeSafetySemaHelper {
   virtual void
   reportLifetimeboundViolation(const CXXMethodDecl *MDWithLifetimebound) {}
 
+  // Reports a member function definition that has [[clang::lifetimebound]] on
+  // the implicit this parameter when the previous declaration does not.
+  virtual void reportMisplacedLifetimebound(const FunctionDecl *FDef,
+                                            const FunctionDecl *FDecl) {}
+
+  // Reports a function definition parameter that has [[clang::lifetimebound]]
+  // when the corresponding parameter in the previous declaration does not.
+  virtual void reportMisplacedLifetimebound(const ParmVarDecl *PVDDef,
+                                            const ParmVarDecl *PVDDecl) {}
+
   // Suggests lifetime bound annotations for implicit this.
   virtual void suggestLifetimeboundToImplicitThis(SuggestionScope Scope,
                                                   const CXXMethodDecl *MD,
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index 03d423db9d21a..bf8973242cb3f 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -631,6 +631,12 @@ This warning may produce false-positives diagnostics when 
it cannot fully model
   }];
 }
 
+def LifetimeSafetyMisplacedLifetimebound : 
DiagGroup<"lifetime-safety-misplaced-lifetimebound"> {
+    code Documentation = [{
+Detects function definitions whose parameters or implicit this argument are 
marked as [[clang::lifetimebound]] when the corresponding declaration is not.
+}];
+}
+
 def LifetimeSafetyPermissive : DiagGroup<"lifetime-safety-permissive",
                                          [LifetimeSafetyUseAfterScope,
                                          LifetimeSafetyReturnStackAddr,
@@ -674,7 +680,8 @@ Detects misuse of [[clang::noescape]] annotation where the 
parameter escapes (fo
 
 def LifetimeSafetyValidations : DiagGroup<"lifetime-safety-validations",
                                           [LifetimeSafetyNoescape,
-                                           
LifetimeSafetyLifetimeboundViolation]> {
+                                           
LifetimeSafetyLifetimeboundViolation,
+                                           
LifetimeSafetyMisplacedLifetimebound]> {
   code Documentation = [{
 Verify function implementations adhere to the annotated lifetime contracts 
through lifetime safety
 like verifying [[clang::noescape]] and [[clang::lifetimebound]].
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d7dd20d6a45e4..5d41f751cb08a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11018,6 +11018,11 @@ def warn_lifetime_safety_lifetimebound_violation
       InGroup<LifetimeSafetyLifetimeboundViolation>,
       DefaultIgnore;
 
+def warn_lifetime_safety_misplaced_lifetimebound
+    : Warning<"'lifetimebound' attribute on the definition is not visible from 
this declaration; add it to the declaration instead">,
+      InGroup<LifetimeSafetyMisplacedLifetimebound>,
+      DefaultIgnore;
+
 def note_lifetime_safety_used_here : Note<"later used here">;
 def note_lifetime_safety_invalidated_here : Note<"invalidated here">;
 def note_lifetime_safety_destroyed_here : Note<"destroyed here">;
@@ -11030,6 +11035,7 @@ def note_lifetime_safety_dangling_static_here: 
Note<"this static storage dangles
 def note_lifetime_safety_escapes_to_field_here: Note<"escapes to this field">;
 def note_lifetime_safety_escapes_to_global_here: Note<"escapes to this global 
storage">;
 def note_lifetime_safety_escapes_to_static_storage_here: Note<"escapes to this 
static storage">;
+def note_lifetime_safety_definition_lifetimebound_attribute_here: 
Note<"'lifetimebound' attribute written on the definition is here">;
 
 def warn_lifetime_safety_intra_tu_param_suggestion
     : Warning<"parameter in intra-TU function should be marked "
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index ad928c3754fea..2de413b4baa14 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -103,6 +103,7 @@ class LifetimeChecker {
     suggestAnnotations();
     reportNoescapeViolations();
     reportLifetimeboundViolations();
+    reportMisplacedLifetimebound();
     //  Annotation inference is currently guarded by a frontend flag. In the
     //  future, this might be replaced by a design that differentiates between
     //  explicit and inferred findings with separate warning groups.
@@ -415,6 +416,28 @@ class LifetimeChecker {
     }
   }
 
+  void reportMisplacedLifetimebound() {
+    const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD);
+    if (!FDef)
+      return;
+
+    const FunctionDecl *FDecl = FDef->getPreviousDecl();
+    if (!FDecl)
+      return;
+
+    if (isa<CXXMethodDecl>(FDef) &&
+        getDirectImplicitObjectLifetimeBoundAttr(FDef) &&
+        !getDirectImplicitObjectLifetimeBoundAttr(FDecl))
+      SemaHelper->reportMisplacedLifetimebound(FDef, FDecl);
+
+    for (auto [PVDDef, PVDDecl] :
+         llvm::zip_equal(FDef->parameters(), FDecl->parameters())) {
+      if (PVDDef->hasAttr<LifetimeBoundAttr>() &&
+          !PVDDecl->hasAttr<LifetimeBoundAttr>())
+        SemaHelper->reportMisplacedLifetimebound(PVDDef, PVDDecl);
+    }
+  }
+
   void inferAnnotations() {
     for (auto [Target, EscapeTarget] : AnnotationWarningsMap) {
       if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) {
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp 
b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 393e558fd39c3..8f2b392fff514 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -72,6 +72,14 @@ getLifetimeBoundAttrFromFunctionType(const TypeSourceInfo 
&TSI) {
   return nullptr;
 }
 
+const LifetimeBoundAttr *
+getDirectImplicitObjectLifetimeBoundAttr(const FunctionDecl *FD) {
+  if (const TypeSourceInfo *TSI = FD->getTypeSourceInfo())
+    if (const auto *Attr = getLifetimeBoundAttrFromFunctionType(*TSI))
+      return Attr;
+  return nullptr;
+}
+
 const LifetimeBoundAttr *
 getImplicitObjectParamLifetimeBoundAttr(const FunctionDecl *FD) {
   FD = getDeclWithMergedLifetimeBoundAttrs(FD);
@@ -79,9 +87,8 @@ getImplicitObjectParamLifetimeBoundAttr(const FunctionDecl 
*FD) {
   // 'this' param). We need to check all redeclarations.
   auto CheckRedecls = [](const FunctionDecl *F) -> const LifetimeBoundAttr * {
     for (const FunctionDecl *Redecl : F->redecls())
-      if (const TypeSourceInfo *TSI = Redecl->getTypeSourceInfo())
-        if (const auto *Attr = getLifetimeBoundAttrFromFunctionType(*TSI))
-          return Attr;
+      if (const auto *Attr = getDirectImplicitObjectLifetimeBoundAttr(Redecl))
+        return Attr;
     return nullptr;
   };
 
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 1b5907a09e291..72d709e18d365 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -275,6 +275,33 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
         << 2 << "" << Attr->getRange();
   }
 
+  void reportMisplacedLifetimebound(const FunctionDecl *FDef,
+                                    const FunctionDecl *FDecl) override {
+    const auto *Attr = getImplicitObjectParamLifetimeBoundAttr(FDef);
+    assert(Attr && "Expected lifetimebound attribute");
+    S.Diag(Lexer::getLocForEndOfToken(FDecl->getEndLoc(), 0,
+                                      S.getSourceManager(), S.getLangOpts()),
+           diag::warn_lifetime_safety_misplaced_lifetimebound);
+
+    S.Diag(Attr->getLocation(),
+           diag::note_lifetime_safety_definition_lifetimebound_attribute_here)
+        << Attr->getRange();
+  }
+
+  void reportMisplacedLifetimebound(const ParmVarDecl *PVDDef,
+                                    const ParmVarDecl *PVDDecl) override {
+
+    const auto *Attr = PVDDef->getAttr<LifetimeBoundAttr>();
+    assert(Attr && "Expected lifetimebound attribute");
+    S.Diag(PVDDecl->getBeginLoc(),
+           diag::warn_lifetime_safety_misplaced_lifetimebound)
+        << PVDDecl->getSourceRange();
+
+    S.Diag(Attr->getLocation(),
+           diag::note_lifetime_safety_definition_lifetimebound_attribute_here)
+        << Attr->getRange();
+  }
+
   void suggestLifetimeboundToImplicitThis(SuggestionScope Scope,
                                           const CXXMethodDecl *MD,
                                           const Expr *EscapeExpr) override {
diff --git a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp 
b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp
new file mode 100644
index 0000000000000..30f5637604866
--- /dev/null
+++ b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety-validations -Wno-dangling 
-verify %s
+
+struct MyObj {
+  ~MyObj() {}
+};
+
+struct S {
+  MyObj data;
+  const MyObj &implicit_this_only(); // expected-warning {{'lifetimebound' 
attribute on the definition is not visible from this declaration; add it to the 
declaration instead}}
+  const MyObj &param_only(const MyObj &obj); // expected-warning 
{{'lifetimebound' attribute on the definition is not visible from this 
declaration; add it to the declaration instead}}
+  const MyObj &both(const MyObj &obj, bool); // expected-warning 2 
{{'lifetimebound' attribute on the definition is not visible from this 
declaration; add it to the declaration instead}}
+};
+
+const MyObj &S::implicit_this_only() [[clang::lifetimebound]] { // 
expected-note {{'lifetimebound' attribute written on the definition is here}}
+  return data;
+}
+
+const MyObj &S::param_only(
+    const MyObj &obj [[clang::lifetimebound]]) { // expected-note 
{{'lifetimebound' attribute written on the definition is here}}
+  return obj;
+}
+
+const MyObj &S::both(
+    const MyObj &obj [[clang::lifetimebound]], bool use_obj) // expected-note 
{{'lifetimebound' attribute written on the definition is here}}
+    [[clang::lifetimebound]] { // expected-note {{'lifetimebound' attribute 
written on the definition is here}}
+  return use_obj ? obj : data;
+}
+
+template <class T>
+struct MixedSpecializations {
+  T data;
+  T &both(T &arg, bool); // expected-warning 2 {{'lifetimebound' attribute on 
the definition is not visible from this declaration; add it to the declaration 
instead}}
+};
+
+template <>
+MyObj &MixedSpecializations<MyObj>::both(
+    MyObj &arg [[clang::lifetimebound]], bool use_arg) // expected-note 
{{'lifetimebound' attribute written on the definition is here}}
+    [[clang::lifetimebound]] { // expected-note {{'lifetimebound' attribute 
written on the definition is here}}
+  return use_arg ? arg : data;
+}

>From 6b3939822bce8f8b713c6690e4f30e95a3e8e35b Mon Sep 17 00:00:00 2001
From: NeKon69 <[email protected]>
Date: Thu, 14 May 2026 19:57:49 +0300
Subject: [PATCH 2/7] change diagnostics a bit

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td          | 2 +-
 .../Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5d41f751cb08a..6615c44f411bc 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11019,7 +11019,7 @@ def warn_lifetime_safety_lifetimebound_violation
       DefaultIgnore;
 
 def warn_lifetime_safety_misplaced_lifetimebound
-    : Warning<"'lifetimebound' attribute on the definition is not visible from 
this declaration; add it to the declaration instead">,
+    : Warning<"'lifetimebound' attribute on the definition is not visible to 
callers; add it to this declaration instead">,
       InGroup<LifetimeSafetyMisplacedLifetimebound>,
       DefaultIgnore;
 
diff --git a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp 
b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp
index 30f5637604866..6c0c1949931c9 100644
--- a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp
@@ -6,9 +6,9 @@ struct MyObj {
 
 struct S {
   MyObj data;
-  const MyObj &implicit_this_only(); // expected-warning {{'lifetimebound' 
attribute on the definition is not visible from this declaration; add it to the 
declaration instead}}
-  const MyObj &param_only(const MyObj &obj); // expected-warning 
{{'lifetimebound' attribute on the definition is not visible from this 
declaration; add it to the declaration instead}}
-  const MyObj &both(const MyObj &obj, bool); // expected-warning 2 
{{'lifetimebound' attribute on the definition is not visible from this 
declaration; add it to the declaration instead}}
+  const MyObj &implicit_this_only(); // expected-warning {{'lifetimebound' 
attribute on the definition is not visible to callers; add it to this 
declaration instead}}
+  const MyObj &param_only(const MyObj &obj); // expected-warning 
{{'lifetimebound' attribute on the definition is not visible to callers; add it 
to this declaration instead}}
+  const MyObj &both(const MyObj &obj, bool); // expected-warning 2 
{{'lifetimebound' attribute on the definition is not visible to callers; add it 
to this declaration instead}}
 };
 
 const MyObj &S::implicit_this_only() [[clang::lifetimebound]] { // 
expected-note {{'lifetimebound' attribute written on the definition is here}}
@@ -29,7 +29,7 @@ const MyObj &S::both(
 template <class T>
 struct MixedSpecializations {
   T data;
-  T &both(T &arg, bool); // expected-warning 2 {{'lifetimebound' attribute on 
the definition is not visible from this declaration; add it to the declaration 
instead}}
+  T &both(T &arg, bool); // expected-warning 2 {{'lifetimebound' attribute on 
the definition is not visible to callers; add it to this declaration instead}}
 };
 
 template <>

>From 3808e5511660d05cc2956c8c5e9197a76dd18571 Mon Sep 17 00:00:00 2001
From: NeKon69 <[email protected]>
Date: Fri, 15 May 2026 20:51:56 +0300
Subject: [PATCH 3/7] add cross-tu/intra-tu warnings

---
 .../LifetimeSafety/LifetimeAnnotations.h      |  4 +
 .../Analyses/LifetimeSafety/LifetimeSafety.h  | 18 ++--
 clang/include/clang/Basic/DiagnosticGroups.td |  9 +-
 .../clang/Basic/DiagnosticSemaKinds.td        | 13 ++-
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 42 ++++----
 .../LifetimeSafety/LifetimeAnnotations.cpp    | 37 +++++++
 clang/lib/Sema/SemaLifetimeSafety.h           | 39 ++++----
 ...ifetime-safety-misplaced-lifetimebound.cpp | 96 ++++++++++++++++---
 8 files changed, 197 insertions(+), 61 deletions(-)

diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index ec05e67b05853..72ab6d1ffbf4c 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -12,6 +12,7 @@
 
 #include "clang/AST/Attr.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
 
 namespace clang ::lifetimes {
 
@@ -46,6 +47,9 @@ bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl 
*CMD);
 const LifetimeBoundAttr *
 getDirectImplicitObjectLifetimeBoundAttr(const FunctionDecl *FD);
 
+const std::pair<const Decl *, WarningScope>
+getUnannotatedDeclBestMatch(const FunctionDecl *FD,
+                            const ParmVarDecl *PVD = nullptr);
 /// Returns the lifetimebound attribute for the implicit this parameter, if it
 /// exists on any redeclaration.
 const LifetimeBoundAttr *
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 132d20e9ed5f7..42c7eb304968c 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -41,9 +41,9 @@ struct LifetimeSafetyOpts {
 };
 
 /// Enum to track functions visible across or within TU.
-enum class SuggestionScope {
-  CrossTU, // For suggestions on declarations visible across Translation Units.
-  IntraTU  // For suggestions on definitions local to a Translation Unit.
+enum class WarningScope {
+  CrossTU, // For warnings on declarations visible across Translation Units.
+  IntraTU  // For warnings on functions local to a Translation Unit.
 };
 
 /// Abstract interface for operations requiring Sema access.
@@ -105,7 +105,7 @@ class LifetimeSafetySemaHelper {
       llvm::PointerUnion<const Expr *, const FieldDecl *, const VarDecl *>;
 
   // Suggests lifetime bound annotations for function parameters.
-  virtual void suggestLifetimeboundToParmVar(SuggestionScope Scope,
+  virtual void suggestLifetimeboundToParmVar(WarningScope Scope,
                                              const ParmVarDecl *ParmToAnnotate,
                                              EscapingTarget Target) {}
 
@@ -132,16 +132,18 @@ class LifetimeSafetySemaHelper {
 
   // Reports a member function definition that has [[clang::lifetimebound]] on
   // the implicit this parameter when the previous declaration does not.
-  virtual void reportMisplacedLifetimebound(const FunctionDecl *FDef,
-                                            const FunctionDecl *FDecl) {}
+  virtual void reportMisplacedLifetimebound(WarningScope Scope,
+                                            const CXXMethodDecl *FDef,
+                                            const CXXMethodDecl *FDecl) {}
 
   // Reports a function definition parameter that has [[clang::lifetimebound]]
   // when the corresponding parameter in the previous declaration does not.
-  virtual void reportMisplacedLifetimebound(const ParmVarDecl *PVDDef,
+  virtual void reportMisplacedLifetimebound(WarningScope Scope,
+                                            const ParmVarDecl *PVDDef,
                                             const ParmVarDecl *PVDDecl) {}
 
   // Suggests lifetime bound annotations for implicit this.
-  virtual void suggestLifetimeboundToImplicitThis(SuggestionScope Scope,
+  virtual void suggestLifetimeboundToImplicitThis(WarningScope Scope,
                                                   const CXXMethodDecl *MD,
                                                   const Expr *EscapeExpr) {}
 
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index bf8973242cb3f..1851d7b0b44a3 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -631,7 +631,14 @@ This warning may produce false-positives diagnostics when 
it cannot fully model
   }];
 }
 
-def LifetimeSafetyMisplacedLifetimebound : 
DiagGroup<"lifetime-safety-misplaced-lifetimebound"> {
+def LifetimeSafetyCrossTUMisplacedLifetimebound
+    : DiagGroup<"lifetime-safety-cross-tu-misplaced-lifetimebound">;
+def LifetimeSafetyIntraTUMisplacedLifetimebound
+    : DiagGroup<"lifetime-safety-intra-tu-misplaced-lifetimebound">;
+def LifetimeSafetyMisplacedLifetimebound
+    : DiagGroup<"lifetime-safety-misplaced-lifetimebound",
+                [LifetimeSafetyCrossTUMisplacedLifetimebound,
+                 LifetimeSafetyIntraTUMisplacedLifetimebound]> {
     code Documentation = [{
 Detects function definitions whose parameters or implicit this argument are 
marked as [[clang::lifetimebound]] when the corresponding declaration is not.
 }];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 6615c44f411bc..a701a73f46553 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11018,9 +11018,14 @@ def warn_lifetime_safety_lifetimebound_violation
       InGroup<LifetimeSafetyLifetimeboundViolation>,
       DefaultIgnore;
 
-def warn_lifetime_safety_misplaced_lifetimebound
-    : Warning<"'lifetimebound' attribute on the definition is not visible to 
callers; add it to this declaration instead">,
-      InGroup<LifetimeSafetyMisplacedLifetimebound>,
+def warn_lifetime_safety_intra_tu_misplaced_lifetimebound
+    : Warning<"'lifetimebound' attribute on an intra-TU definition is not 
visible to callers; add it to the declaration instead">,
+      InGroup<LifetimeSafetyIntraTUMisplacedLifetimebound>,
+      DefaultIgnore;
+
+def warn_lifetime_safety_cross_tu_misplaced_lifetimebound
+    : Warning<"'lifetimebound' attribute on a cross-TU definition is not 
visible to callers; add it to the declaration instead">,
+      InGroup<LifetimeSafetyCrossTUMisplacedLifetimebound>,
       DefaultIgnore;
 
 def note_lifetime_safety_used_here : Note<"later used here">;
@@ -11035,7 +11040,7 @@ def note_lifetime_safety_dangling_static_here: 
Note<"this static storage dangles
 def note_lifetime_safety_escapes_to_field_here: Note<"escapes to this field">;
 def note_lifetime_safety_escapes_to_global_here: Note<"escapes to this global 
storage">;
 def note_lifetime_safety_escapes_to_static_storage_here: Note<"escapes to this 
static storage">;
-def note_lifetime_safety_definition_lifetimebound_attribute_here: 
Note<"'lifetimebound' attribute written on the definition is here">;
+def note_lifetime_safety_lifetimebound_here: Note<"'lifetimebound' attribute 
appears here on the definition">;
 
 def warn_lifetime_safety_intra_tu_param_suggestion
     : Warning<"parameter in intra-TU function should be marked "
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 2de413b4baa14..5078def06e9c1 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -61,6 +61,7 @@ class LifetimeChecker {
   llvm::DenseMap<AnnotationTarget, EscapingTarget> AnnotationWarningsMap;
   llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap;
   llvm::DenseSet<const Decl *> VerifiedLiftimeboundEscapes;
+  llvm::DenseMap<const Decl *, WarningScope> DeclarationsToAnnotate;
   const LoanPropagationAnalysis &LoanPropagation;
   const MovedLoansAnalysis &MovedLoans;
   const LiveOriginsAnalysis &LiveOrigins;
@@ -135,6 +136,10 @@ class LifetimeChecker {
       if (PVD->hasAttr<LifetimeBoundAttr>()) {
         // Track that this lifetimebound parameter correctly escapes.
         VerifiedLiftimeboundEscapes.insert(PVD);
+        if (auto [UnannotatedFDecl, Scope] =
+                getUnannotatedDeclBestMatch(cast<FunctionDecl>(FD), PVD);
+            UnannotatedFDecl)
+          DeclarationsToAnnotate.try_emplace(UnannotatedFDecl, Scope);
       } else {
         // Otherwise, suggest lifetimebound for parameter escaping through
         // return or a field in constructor.
@@ -152,6 +157,10 @@ class LifetimeChecker {
         VerifiedLiftimeboundEscapes.insert(MD);
       else if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
         AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
+      if (getDirectImplicitObjectLifetimeBoundAttr(MD))
+        if (auto [UnannotatedFDecl, Scope] = getUnannotatedDeclBestMatch(MD);
+            UnannotatedFDecl)
+          DeclarationsToAnnotate.try_emplace(UnannotatedFDecl, Scope);
     };
     auto MovedAtEscape = MovedLoans.getMovedLoans(OEF);
     for (LoanID LID : EscapedLoans) {
@@ -346,11 +355,11 @@ class LifetimeChecker {
 
     if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD, SM))
       SemaHelper->suggestLifetimeboundToParmVar(
-          SuggestionScope::CrossTU,
+          WarningScope::CrossTU,
           CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()),
           EscapeTarget);
     else
-      SemaHelper->suggestLifetimeboundToParmVar(SuggestionScope::IntraTU, PVD,
+      SemaHelper->suggestLifetimeboundToParmVar(WarningScope::IntraTU, PVD,
                                                 EscapeTarget);
   }
 
@@ -360,11 +369,10 @@ class LifetimeChecker {
                                   const Expr *EscapeExpr) {
     if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD, SM))
       SemaHelper->suggestLifetimeboundToImplicitThis(
-          SuggestionScope::CrossTU, cast<CXXMethodDecl>(CrossTUDecl),
-          EscapeExpr);
+          WarningScope::CrossTU, cast<CXXMethodDecl>(CrossTUDecl), EscapeExpr);
     else
-      SemaHelper->suggestLifetimeboundToImplicitThis(SuggestionScope::IntraTU,
-                                                     MD, EscapeExpr);
+      SemaHelper->suggestLifetimeboundToImplicitThis(WarningScope::IntraTU, MD,
+                                                     EscapeExpr);
   }
 
   void suggestAnnotations() {
@@ -420,21 +428,13 @@ class LifetimeChecker {
     const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD);
     if (!FDef)
       return;
-
-    const FunctionDecl *FDecl = FDef->getPreviousDecl();
-    if (!FDecl)
-      return;
-
-    if (isa<CXXMethodDecl>(FDef) &&
-        getDirectImplicitObjectLifetimeBoundAttr(FDef) &&
-        !getDirectImplicitObjectLifetimeBoundAttr(FDecl))
-      SemaHelper->reportMisplacedLifetimebound(FDef, FDecl);
-
-    for (auto [PVDDef, PVDDecl] :
-         llvm::zip_equal(FDef->parameters(), FDecl->parameters())) {
-      if (PVDDef->hasAttr<LifetimeBoundAttr>() &&
-          !PVDDecl->hasAttr<LifetimeBoundAttr>())
-        SemaHelper->reportMisplacedLifetimebound(PVDDef, PVDDecl);
+    for (auto [Decl, Scope] : DeclarationsToAnnotate) {
+      if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl))
+        SemaHelper->reportMisplacedLifetimebound(Scope,
+                                                 cast<CXXMethodDecl>(FDef), 
MD);
+      else if (const auto *PVD = dyn_cast<ParmVarDecl>(Decl))
+        SemaHelper->reportMisplacedLifetimebound(
+            Scope, FDef->getParamDecl(PVD->getFunctionScopeIndex()), PVD);
     }
   }
 
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp 
b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 8f2b392fff514..88639e2c60eb5 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -14,6 +14,7 @@
 #include "clang/AST/Type.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/OperatorKinds.h"
+#include "clang/Basic/SourceManager.h"
 #include "llvm/ADT/StringSet.h"
 
 namespace clang::lifetimes {
@@ -105,6 +106,42 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl 
*FD) {
   return isNormalAssignmentOperator(FD);
 }
 
+const std::pair<const Decl *, WarningScope>
+getUnannotatedDeclBestMatch(const FunctionDecl *FD, const ParmVarDecl *PVD) {
+  if (!FD || !FD->isExternallyVisible())
+    return {nullptr, WarningScope::IntraTU};
+
+  auto HasAttr = [PVD](const FunctionDecl *D) -> bool {
+    if (PVD)
+      return D->getParamDecl(PVD->getFunctionScopeIndex())
+          ->hasAttr<LifetimeBoundAttr>();
+    return getDirectImplicitObjectLifetimeBoundAttr(D);
+  };
+
+  auto GetAnnotatedDecl = [PVD](const FunctionDecl *D) -> const Decl * {
+    if (!D)
+      return nullptr;
+    if (PVD)
+      return D->getParamDecl(PVD->getFunctionScopeIndex());
+    return D;
+  };
+
+  const auto &SM = FD->getASTContext().getSourceManager();
+  const FileID DefFile = SM.getFileID(SM.getExpansionLoc(FD->getLocation()));
+  const FunctionDecl *Fallback = nullptr;
+
+  for (const FunctionDecl *D = FD->getPreviousDecl(); D;
+       D = D->getPreviousDecl()) {
+    if (D->isThisDeclarationADefinition() || HasAttr(D))
+      continue;
+    if (!Fallback)
+      Fallback = D;
+    if (SM.getFileID(SM.getExpansionLoc(D->getLocation())) != DefFile)
+      return {GetAnnotatedDecl(D), WarningScope::CrossTU};
+  }
+  return {GetAnnotatedDecl(Fallback), WarningScope::IntraTU};
+}
+
 bool isInStlNamespace(const Decl *D) {
   const DeclContext *DC = D->getDeclContext();
   if (!DC)
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h 
b/clang/lib/Sema/SemaLifetimeSafety.h
index 72d709e18d365..36b0aa2f9dee9 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -40,7 +40,8 @@ inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) {
       diag::warn_lifetime_safety_dangling_global_moved,
       diag::warn_lifetime_safety_noescape_escapes,
       diag::warn_lifetime_safety_lifetimebound_violation,
-  };
+      diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound,
+      diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound};
   for (unsigned DiagID : DiagIDs)
     if (!Diags.isIgnored(DiagID, D->getBeginLoc()))
       return true;
@@ -225,11 +226,11 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
           << DanglingGlobal->getEndLoc();
   }
 
-  void suggestLifetimeboundToParmVar(SuggestionScope Scope,
+  void suggestLifetimeboundToParmVar(WarningScope Scope,
                                      const ParmVarDecl *ParmToAnnotate,
                                      EscapingTarget Target) override {
     unsigned DiagID =
-        (Scope == SuggestionScope::CrossTU)
+        (Scope == WarningScope::CrossTU)
             ? diag::warn_lifetime_safety_cross_tu_param_suggestion
             : diag::warn_lifetime_safety_intra_tu_param_suggestion;
     SourceLocation InsertionPoint = Lexer::getLocForEndOfToken(
@@ -275,37 +276,43 @@ class LifetimeSafetySemaHelperImpl : public 
LifetimeSafetySemaHelper {
         << 2 << "" << Attr->getRange();
   }
 
-  void reportMisplacedLifetimebound(const FunctionDecl *FDef,
-                                    const FunctionDecl *FDecl) override {
+  void reportMisplacedLifetimebound(WarningScope Scope,
+                                    const CXXMethodDecl *FDef,
+                                    const CXXMethodDecl *FDecl) override {
     const auto *Attr = getImplicitObjectParamLifetimeBoundAttr(FDef);
     assert(Attr && "Expected lifetimebound attribute");
+    unsigned DiagID =
+        Scope == WarningScope::CrossTU
+            ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
+            : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
     S.Diag(Lexer::getLocForEndOfToken(FDecl->getEndLoc(), 0,
                                       S.getSourceManager(), S.getLangOpts()),
-           diag::warn_lifetime_safety_misplaced_lifetimebound);
+           DiagID);
 
-    S.Diag(Attr->getLocation(),
-           diag::note_lifetime_safety_definition_lifetimebound_attribute_here)
+    S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here)
         << Attr->getRange();
   }
 
-  void reportMisplacedLifetimebound(const ParmVarDecl *PVDDef,
+  void reportMisplacedLifetimebound(WarningScope Scope,
+                                    const ParmVarDecl *PVDDef,
                                     const ParmVarDecl *PVDDecl) override {
 
     const auto *Attr = PVDDef->getAttr<LifetimeBoundAttr>();
     assert(Attr && "Expected lifetimebound attribute");
-    S.Diag(PVDDecl->getBeginLoc(),
-           diag::warn_lifetime_safety_misplaced_lifetimebound)
-        << PVDDecl->getSourceRange();
+    unsigned DiagID =
+        Scope == WarningScope::CrossTU
+            ? diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound
+            : diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound;
+    S.Diag(PVDDecl->getBeginLoc(), DiagID) << PVDDecl->getSourceRange();
 
-    S.Diag(Attr->getLocation(),
-           diag::note_lifetime_safety_definition_lifetimebound_attribute_here)
+    S.Diag(Attr->getLocation(), diag::note_lifetime_safety_lifetimebound_here)
         << Attr->getRange();
   }
 
-  void suggestLifetimeboundToImplicitThis(SuggestionScope Scope,
+  void suggestLifetimeboundToImplicitThis(WarningScope Scope,
                                           const CXXMethodDecl *MD,
                                           const Expr *EscapeExpr) override {
-    unsigned DiagID = (Scope == SuggestionScope::CrossTU)
+    unsigned DiagID = (Scope == WarningScope::CrossTU)
                           ? diag::warn_lifetime_safety_cross_tu_this_suggestion
                           : 
diag::warn_lifetime_safety_intra_tu_this_suggestion;
     const auto MDL = MD->getTypeSourceInfo()->getTypeLoc();
diff --git a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp 
b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp
index 6c0c1949931c9..d759368f7d4e1 100644
--- a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp
@@ -1,40 +1,114 @@
-// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety-validations -Wno-dangling 
-verify %s
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-intra-tu-misplaced-lifetimebound -Wno-dangling -verify=intra 
%t/test.cpp
+// RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-cross-tu-misplaced-lifetimebound -Wno-dangling -I%t 
-verify=cross %t/cross.cpp
 
+//--- test.cpp
 struct MyObj {
   ~MyObj() {}
 };
 
+MyObj &free_param(MyObj &obj); // intra-warning {{'lifetimebound' attribute on 
an intra-TU definition is not visible to callers; add it to the declaration 
instead}}
+MyObj &free_param(MyObj &obj [[clang::lifetimebound]]) { // intra-note 
{{'lifetimebound' attribute appears here on the definition}}
+  return obj;
+}
+
 struct S {
   MyObj data;
-  const MyObj &implicit_this_only(); // expected-warning {{'lifetimebound' 
attribute on the definition is not visible to callers; add it to this 
declaration instead}}
-  const MyObj &param_only(const MyObj &obj); // expected-warning 
{{'lifetimebound' attribute on the definition is not visible to callers; add it 
to this declaration instead}}
-  const MyObj &both(const MyObj &obj, bool); // expected-warning 2 
{{'lifetimebound' attribute on the definition is not visible to callers; add it 
to this declaration instead}}
+  const MyObj &implicit_this_only(); // intra-warning {{'lifetimebound' 
attribute on an intra-TU definition is not visible to callers; add it to the 
declaration instead}}
+  const MyObj &param_only(const MyObj &obj); // intra-warning 
{{'lifetimebound' attribute on an intra-TU definition is not visible to 
callers; add it to the declaration instead}}
+  const MyObj &both(const MyObj &obj, bool); // intra-warning 2 
{{'lifetimebound' attribute on an intra-TU definition is not visible to 
callers; add it to the declaration instead}}
 };
 
-const MyObj &S::implicit_this_only() [[clang::lifetimebound]] { // 
expected-note {{'lifetimebound' attribute written on the definition is here}}
+const MyObj &S::implicit_this_only() [[clang::lifetimebound]] { // intra-note 
{{'lifetimebound' attribute appears here on the definition}}
   return data;
 }
 
 const MyObj &S::param_only(
-    const MyObj &obj [[clang::lifetimebound]]) { // expected-note 
{{'lifetimebound' attribute written on the definition is here}}
+    const MyObj &obj [[clang::lifetimebound]]) { // intra-note 
{{'lifetimebound' attribute appears here on the definition}}
   return obj;
 }
 
 const MyObj &S::both(
-    const MyObj &obj [[clang::lifetimebound]], bool use_obj) // expected-note 
{{'lifetimebound' attribute written on the definition is here}}
-    [[clang::lifetimebound]] { // expected-note {{'lifetimebound' attribute 
written on the definition is here}}
+    const MyObj &obj [[clang::lifetimebound]], bool use_obj) // intra-note 
{{'lifetimebound' attribute appears here on the definition}}
+    [[clang::lifetimebound]] { // intra-note {{'lifetimebound' attribute 
appears here on the definition}}
   return use_obj ? obj : data;
 }
 
 template <class T>
 struct MixedSpecializations {
   T data;
-  T &both(T &arg, bool); // expected-warning 2 {{'lifetimebound' attribute on 
the definition is not visible to callers; add it to this declaration instead}}
+  T &both(T &arg, bool); // intra-warning 2 {{'lifetimebound' attribute on an 
intra-TU definition is not visible to callers; add it to the declaration 
instead}}
 };
 
 template <>
 MyObj &MixedSpecializations<MyObj>::both(
-    MyObj &arg [[clang::lifetimebound]], bool use_arg) // expected-note 
{{'lifetimebound' attribute written on the definition is here}}
-    [[clang::lifetimebound]] { // expected-note {{'lifetimebound' attribute 
written on the definition is here}}
+    MyObj &arg [[clang::lifetimebound]], bool use_arg) // intra-note 
{{'lifetimebound' attribute appears here on the definition}}
+    [[clang::lifetimebound]] { // intra-note {{'lifetimebound' attribute 
appears here on the definition}}
   return use_arg ? arg : data;
 }
+
+struct InternalObj {
+  ~InternalObj() {}
+};
+
+namespace {
+InternalObj &anon_param(InternalObj &obj);
+InternalObj &anon_param(InternalObj &obj [[clang::lifetimebound]]) {
+  return obj;
+}
+
+struct AnonS {
+  InternalObj data;
+  InternalObj &anon_this();
+};
+
+InternalObj &AnonS::anon_this() [[clang::lifetimebound]] {
+  return data;
+}
+} // namespace
+
+static InternalObj &static_param(InternalObj &obj);
+static InternalObj &static_param(InternalObj &obj [[clang::lifetimebound]]) {
+  return obj;
+}
+
+struct IntraSuppressedObj {
+  ~IntraSuppressedObj() {}
+};
+
+IntraSuppressedObj &intra_suppressed(IntraSuppressedObj &obj); // 
intra-warning {{'lifetimebound' attribute on an intra-TU definition is not 
visible to callers; add it to the declaration instead}}
+IntraSuppressedObj &intra_suppressed(
+    IntraSuppressedObj &obj [[clang::lifetimebound]]) { // intra-note 
{{'lifetimebound' attribute appears here on the definition}}
+  return obj;
+}
+
+//--- cross.h
+struct HeaderObj {
+  ~HeaderObj() {}
+};
+
+HeaderObj &header_param(HeaderObj &obj); // cross-warning {{'lifetimebound' 
attribute on a cross-TU definition is not visible to callers; add it to the 
declaration instead}}
+
+HeaderObj &header_then_source_redecl(HeaderObj &obj); // cross-warning 
{{'lifetimebound' attribute on a cross-TU definition is not visible to callers; 
add it to the declaration instead}}
+
+struct HeaderS {
+  HeaderObj data;
+  HeaderObj &header_this(); // cross-warning {{'lifetimebound' attribute on a 
cross-TU definition is not visible to callers; add it to the declaration 
instead}}
+};
+
+//--- cross.cpp
+#include "cross.h"
+
+HeaderObj &header_param(HeaderObj &obj [[clang::lifetimebound]]) { // 
cross-note {{'lifetimebound' attribute appears here on the definition}}
+  return obj;
+}
+
+HeaderObj &header_then_source_redecl(HeaderObj &obj);
+HeaderObj &header_then_source_redecl(HeaderObj &obj [[clang::lifetimebound]]) 
{ // cross-note {{'lifetimebound' attribute appears here on the definition}}
+  return obj;
+}
+
+HeaderObj &HeaderS::header_this() [[clang::lifetimebound]] { // cross-note 
{{'lifetimebound' attribute appears here on the definition}}
+  return data;
+}

>From a6af555a4c98522db7a0fea28c1d903ede887ffd Mon Sep 17 00:00:00 2001
From: NeKon69 <[email protected]>
Date: Fri, 15 May 2026 22:13:30 +0300
Subject: [PATCH 4/7] resort includes...

---
 .../Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h     | 3 ++-
 clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp      | 1 +
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index 72ab6d1ffbf4c..bf5a960c1c67d 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -12,10 +12,11 @@
 
 #include "clang/AST/Attr.h"
 #include "clang/AST/DeclCXX.h"
-#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
 
 namespace clang ::lifetimes {
 
+enum class WarningScope;
+
 // This function is needed because Decl::isInStdNamespace will return false for
 // iterators in some STL implementations due to them being defined in a
 // namespace outside of the std namespace.
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp 
b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 88639e2c60eb5..f989477642dc4 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -13,6 +13,7 @@
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Type.h"
 #include "clang/AST/TypeLoc.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
 #include "clang/Basic/OperatorKinds.h"
 #include "clang/Basic/SourceManager.h"
 #include "llvm/ADT/StringSet.h"

>From 4297a2beb3a886f73bcea67dbabd6ff3d3570a71 Mon Sep 17 00:00:00 2001
From: NeKon69 <[email protected]>
Date: Mon, 18 May 2026 17:58:03 +0300
Subject: [PATCH 5/7] address PR comments

---
 .../LifetimeSafety/LifetimeAnnotations.h      |  5 --
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 64 ++++++++++++++-----
 .../LifetimeSafety/LifetimeAnnotations.cpp    | 38 -----------
 ...afety-misplaced-lifetimebound-cross-tu.cpp | 26 ++++++++
 ...fety-misplaced-lifetimebound-intra-tu.cpp} | 36 +----------
 5 files changed, 75 insertions(+), 94 deletions(-)
 create mode 100644 
clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-cross-tu.cpp
 rename clang/test/Sema/{warn-lifetime-safety-misplaced-lifetimebound.cpp => 
warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp} (68%)

diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index bf5a960c1c67d..ec05e67b05853 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -15,8 +15,6 @@
 
 namespace clang ::lifetimes {
 
-enum class WarningScope;
-
 // This function is needed because Decl::isInStdNamespace will return false for
 // iterators in some STL implementations due to them being defined in a
 // namespace outside of the std namespace.
@@ -48,9 +46,6 @@ bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl 
*CMD);
 const LifetimeBoundAttr *
 getDirectImplicitObjectLifetimeBoundAttr(const FunctionDecl *FD);
 
-const std::pair<const Decl *, WarningScope>
-getUnannotatedDeclBestMatch(const FunctionDecl *FD,
-                            const ParmVarDecl *PVD = nullptr);
 /// Returns the lifetimebound attribute for the implicit this parameter, if it
 /// exists on any redeclaration.
 const LifetimeBoundAttr *
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 5078def06e9c1..a9ec4694501f6 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -61,7 +61,6 @@ class LifetimeChecker {
   llvm::DenseMap<AnnotationTarget, EscapingTarget> AnnotationWarningsMap;
   llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap;
   llvm::DenseSet<const Decl *> VerifiedLiftimeboundEscapes;
-  llvm::DenseMap<const Decl *, WarningScope> DeclarationsToAnnotate;
   const LoanPropagationAnalysis &LoanPropagation;
   const MovedLoansAnalysis &MovedLoans;
   const LiveOriginsAnalysis &LiveOrigins;
@@ -136,10 +135,6 @@ class LifetimeChecker {
       if (PVD->hasAttr<LifetimeBoundAttr>()) {
         // Track that this lifetimebound parameter correctly escapes.
         VerifiedLiftimeboundEscapes.insert(PVD);
-        if (auto [UnannotatedFDecl, Scope] =
-                getUnannotatedDeclBestMatch(cast<FunctionDecl>(FD), PVD);
-            UnannotatedFDecl)
-          DeclarationsToAnnotate.try_emplace(UnannotatedFDecl, Scope);
       } else {
         // Otherwise, suggest lifetimebound for parameter escaping through
         // return or a field in constructor.
@@ -157,10 +152,6 @@ class LifetimeChecker {
         VerifiedLiftimeboundEscapes.insert(MD);
       else if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
         AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
-      if (getDirectImplicitObjectLifetimeBoundAttr(MD))
-        if (auto [UnannotatedFDecl, Scope] = getUnannotatedDeclBestMatch(MD);
-            UnannotatedFDecl)
-          DeclarationsToAnnotate.try_emplace(UnannotatedFDecl, Scope);
     };
     auto MovedAtEscape = MovedLoans.getMovedLoans(OEF);
     for (LoanID LID : EscapedLoans) {
@@ -325,6 +316,42 @@ class LifetimeChecker {
     }
   }
 
+  const std::pair<const FunctionDecl *, WarningScope>
+  getCanonicalFunctionDeclForAttr(const FunctionDecl *FD) {
+    if (!FD || !FD->isExternallyVisible())
+      return {nullptr, WarningScope::IntraTU};
+
+    const auto &SM = FD->getASTContext().getSourceManager();
+    const FileID DefFile = SM.getFileID(SM.getExpansionLoc(FD->getLocation()));
+    const FunctionDecl *FirstDecl = nullptr;
+    WarningScope Scope = WarningScope::IntraTU;
+
+    for (const FunctionDecl *D = FD->getPreviousDecl(); D;
+         D = D->getPreviousDecl()) {
+      if (D->isThisDeclarationADefinition())
+        continue;
+      FirstDecl = D;
+      Scope = SM.getFileID(SM.getExpansionLoc(D->getLocation())) != DefFile
+                  ? WarningScope::CrossTU
+                  : WarningScope::IntraTU;
+    }
+    return {FirstDecl, Scope};
+  }
+
+  const std::pair<const CXXMethodDecl *, WarningScope>
+  getCanonicalDeclForAttr(const CXXMethodDecl *MD) {
+    auto [CanonicalFD, Scope] = getCanonicalFunctionDeclForAttr(MD);
+    return {cast_or_null<CXXMethodDecl>(CanonicalFD), Scope};
+  }
+
+  const std::pair<const ParmVarDecl *, WarningScope>
+  getCanonicalDeclForAttr(const FunctionDecl *FD, const ParmVarDecl *PVD) {
+    auto [CanonicalFD, Scope] = getCanonicalFunctionDeclForAttr(FD);
+    if (!CanonicalFD)
+      return {nullptr, Scope};
+    return {CanonicalFD->getParamDecl(PVD->getFunctionScopeIndex()), Scope};
+  }
+
   /// Returns the declaration of a function that is visible across translation
   /// units, if such a declaration exists and is different from the definition.
   static const FunctionDecl *getCrossTUDecl(const FunctionDecl &FD,
@@ -428,13 +455,18 @@ class LifetimeChecker {
     const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD);
     if (!FDef)
       return;
-    for (auto [Decl, Scope] : DeclarationsToAnnotate) {
-      if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl))
-        SemaHelper->reportMisplacedLifetimebound(Scope,
-                                                 cast<CXXMethodDecl>(FDef), 
MD);
-      else if (const auto *PVD = dyn_cast<ParmVarDecl>(Decl))
-        SemaHelper->reportMisplacedLifetimebound(
-            Scope, FDef->getParamDecl(PVD->getFunctionScopeIndex()), PVD);
+    if (const auto *MDef = dyn_cast<CXXMethodDecl>(FDef);
+        MDef && getDirectImplicitObjectLifetimeBoundAttr(MDef))
+      if (auto [MDecl, Scope] = getCanonicalDeclForAttr(MDef);
+          MDecl && !getDirectImplicitObjectLifetimeBoundAttr(MDecl))
+        SemaHelper->reportMisplacedLifetimebound(Scope, MDef, MDecl);
+    for (const auto *PDef : FDef->parameters()) {
+      const auto *Attr = PDef->getAttr<LifetimeBoundAttr>();
+      if (!Attr || Attr->isImplicit())
+        continue;
+      if (auto [PDecl, Scope] = getCanonicalDeclForAttr(FDef, PDef);
+          PDecl && !PDecl->hasAttr<LifetimeBoundAttr>())
+        SemaHelper->reportMisplacedLifetimebound(Scope, PDef, PDecl);
     }
   }
 
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp 
b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index f989477642dc4..8f2b392fff514 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -13,9 +13,7 @@
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Type.h"
 #include "clang/AST/TypeLoc.h"
-#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
 #include "clang/Basic/OperatorKinds.h"
-#include "clang/Basic/SourceManager.h"
 #include "llvm/ADT/StringSet.h"
 
 namespace clang::lifetimes {
@@ -107,42 +105,6 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl 
*FD) {
   return isNormalAssignmentOperator(FD);
 }
 
-const std::pair<const Decl *, WarningScope>
-getUnannotatedDeclBestMatch(const FunctionDecl *FD, const ParmVarDecl *PVD) {
-  if (!FD || !FD->isExternallyVisible())
-    return {nullptr, WarningScope::IntraTU};
-
-  auto HasAttr = [PVD](const FunctionDecl *D) -> bool {
-    if (PVD)
-      return D->getParamDecl(PVD->getFunctionScopeIndex())
-          ->hasAttr<LifetimeBoundAttr>();
-    return getDirectImplicitObjectLifetimeBoundAttr(D);
-  };
-
-  auto GetAnnotatedDecl = [PVD](const FunctionDecl *D) -> const Decl * {
-    if (!D)
-      return nullptr;
-    if (PVD)
-      return D->getParamDecl(PVD->getFunctionScopeIndex());
-    return D;
-  };
-
-  const auto &SM = FD->getASTContext().getSourceManager();
-  const FileID DefFile = SM.getFileID(SM.getExpansionLoc(FD->getLocation()));
-  const FunctionDecl *Fallback = nullptr;
-
-  for (const FunctionDecl *D = FD->getPreviousDecl(); D;
-       D = D->getPreviousDecl()) {
-    if (D->isThisDeclarationADefinition() || HasAttr(D))
-      continue;
-    if (!Fallback)
-      Fallback = D;
-    if (SM.getFileID(SM.getExpansionLoc(D->getLocation())) != DefFile)
-      return {GetAnnotatedDecl(D), WarningScope::CrossTU};
-  }
-  return {GetAnnotatedDecl(Fallback), WarningScope::IntraTU};
-}
-
 bool isInStlNamespace(const Decl *D) {
   const DeclContext *DC = D->getDeclContext();
   if (!DC)
diff --git 
a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-cross-tu.cpp 
b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-cross-tu.cpp
new file mode 100644
index 0000000000000..701f4f8bcbaa4
--- /dev/null
+++ b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-cross-tu.cpp
@@ -0,0 +1,26 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-cross-tu-misplaced-lifetimebound -Wno-dangling -I%t 
-verify=cross %t/cross.cpp
+
+//--- cross.h
+struct HeaderObj {
+  ~HeaderObj() {}
+};
+
+HeaderObj &header_param(HeaderObj &obj); // cross-warning {{'lifetimebound' 
attribute on a cross-TU definition is not visible to callers; add it to the 
declaration instead}}
+
+struct HeaderS {
+  HeaderObj data;
+  HeaderObj &header_this(); // cross-warning {{'lifetimebound' attribute on a 
cross-TU definition is not visible to callers; add it to the declaration 
instead}}
+};
+
+//--- cross.cpp
+#include "cross.h"
+
+HeaderObj &header_param(HeaderObj &obj [[clang::lifetimebound]]) { // 
cross-note {{'lifetimebound' attribute appears here on the definition}}
+  return obj;
+}
+
+HeaderObj &HeaderS::header_this() [[clang::lifetimebound]] { // cross-note 
{{'lifetimebound' attribute appears here on the definition}}
+  return data;
+}
diff --git a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp 
b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
similarity index 68%
rename from clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp
rename to 
clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
index d759368f7d4e1..fc44dc5f59658 100644
--- a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
@@ -1,9 +1,5 @@
-// RUN: rm -rf %t
-// RUN: split-file %s %t
-// RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-intra-tu-misplaced-lifetimebound -Wno-dangling -verify=intra 
%t/test.cpp
-// RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-cross-tu-misplaced-lifetimebound -Wno-dangling -I%t 
-verify=cross %t/cross.cpp
+// RUN: %clang_cc1 -fsyntax-only 
-Wlifetime-safety-intra-tu-misplaced-lifetimebound -Wno-dangling -verify=intra 
%s
 
-//--- test.cpp
 struct MyObj {
   ~MyObj() {}
 };
@@ -82,33 +78,3 @@ IntraSuppressedObj &intra_suppressed(
     IntraSuppressedObj &obj [[clang::lifetimebound]]) { // intra-note 
{{'lifetimebound' attribute appears here on the definition}}
   return obj;
 }
-
-//--- cross.h
-struct HeaderObj {
-  ~HeaderObj() {}
-};
-
-HeaderObj &header_param(HeaderObj &obj); // cross-warning {{'lifetimebound' 
attribute on a cross-TU definition is not visible to callers; add it to the 
declaration instead}}
-
-HeaderObj &header_then_source_redecl(HeaderObj &obj); // cross-warning 
{{'lifetimebound' attribute on a cross-TU definition is not visible to callers; 
add it to the declaration instead}}
-
-struct HeaderS {
-  HeaderObj data;
-  HeaderObj &header_this(); // cross-warning {{'lifetimebound' attribute on a 
cross-TU definition is not visible to callers; add it to the declaration 
instead}}
-};
-
-//--- cross.cpp
-#include "cross.h"
-
-HeaderObj &header_param(HeaderObj &obj [[clang::lifetimebound]]) { // 
cross-note {{'lifetimebound' attribute appears here on the definition}}
-  return obj;
-}
-
-HeaderObj &header_then_source_redecl(HeaderObj &obj);
-HeaderObj &header_then_source_redecl(HeaderObj &obj [[clang::lifetimebound]]) 
{ // cross-note {{'lifetimebound' attribute appears here on the definition}}
-  return obj;
-}
-
-HeaderObj &HeaderS::header_this() [[clang::lifetimebound]] { // cross-note 
{{'lifetimebound' attribute appears here on the definition}}
-  return data;
-}

>From aa84793248a636ce629e99fb68598a0eee6ebd7d Mon Sep 17 00:00:00 2001
From: NeKon69 <[email protected]>
Date: Mon, 18 May 2026 18:13:36 +0300
Subject: [PATCH 6/7] add a new test

---
 ...fetime-safety-misplaced-lifetimebound-intra-tu.cpp | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git 
a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp 
b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
index fc44dc5f59658..f308630115eb6 100644
--- a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
@@ -78,3 +78,14 @@ IntraSuppressedObj &intra_suppressed(
     IntraSuppressedObj &obj [[clang::lifetimebound]]) { // intra-note 
{{'lifetimebound' attribute appears here on the definition}}
   return obj;
 }
+
+struct View {
+  friend View friend_redecl(MyObj &obj); // intra-warning {{'lifetimebound' 
attribute on an intra-TU definition is not visible to callers; add it to the 
declaration instead}}
+};
+
+// FIXME: Fix warning location, add a note pointing to this declaration saying 
"attribute inherited from this declaration"
+View friend_redecl(MyObj &obj [[clang::lifetimebound]]); // intra-note 
{{'lifetimebound' attribute appears here on the definition}}
+
+View friend_redecl(MyObj &obj) {
+  return View{};
+}

>From efd6d1248fd6adafc54b6648a25d5ac06f1157ef Mon Sep 17 00:00:00 2001
From: NeKon69 <[email protected]>
Date: Mon, 18 May 2026 18:22:32 +0300
Subject: [PATCH 7/7] change comment

---
 .../warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git 
a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp 
b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
index f308630115eb6..d0f9f158f78e9 100644
--- a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-intra-tu.cpp
@@ -83,7 +83,7 @@ struct View {
   friend View friend_redecl(MyObj &obj); // intra-warning {{'lifetimebound' 
attribute on an intra-TU definition is not visible to callers; add it to the 
declaration instead}}
 };
 
-// FIXME: Fix warning location, add a note pointing to this declaration saying 
"attribute inherited from this declaration"
+// FIXME: This diagnoses an attribute inherited from another redeclaration, 
not one written on the definition. Once we enforce that redeclarations agree on 
lifetimebound, handle this with a dedicated warning and note.
 View friend_redecl(MyObj &obj [[clang::lifetimebound]]); // intra-note 
{{'lifetimebound' attribute appears here on the definition}}
 
 View friend_redecl(MyObj &obj) {

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

Reply via email to