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

>From f95dcf6ef9be88c85f4a94735e93077046ecfe68 Mon Sep 17 00:00:00 2001
From: NeKon69 <[email protected]>
Date: Wed, 20 May 2026 16:30:14 +0300
Subject: [PATCH 1/6] remove function

---
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 57 ++++---------------
 .../test/Sema/warn-lifetime-safety-fixits.cpp |  9 ++-
 .../Sema/warn-lifetime-safety-suggestions.cpp | 24 ++++----
 3 files changed, 27 insertions(+), 63 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index d6a15139aa4ea..4b9d195411179 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -353,66 +353,31 @@ class LifetimeChecker {
             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,
-                                            SourceManager &SM) {
-    if (!FD.isExternallyVisible())
-      return nullptr;
-    const FileID DefinitionFile = SM.getFileID(FD.getLocation());
-    for (const FunctionDecl *Redecl : FD.redecls())
-      if (SM.getFileID(Redecl->getLocation()) != DefinitionFile)
-        return Redecl;
-
-    return nullptr;
-  }
-
-  static const FunctionDecl *getCrossTUDecl(const ParmVarDecl &PVD,
-                                            SourceManager &SM) {
-    if (const auto *FD = dyn_cast<FunctionDecl>(PVD.getDeclContext()))
-      return getCrossTUDecl(*FD, SM);
-    return nullptr;
-  }
-
-  static void suggestWithScopeForParmVar(LifetimeSafetySemaHelper *SemaHelper,
-                                         const ParmVarDecl *PVD,
-                                         SourceManager &SM,
-                                         EscapingTarget EscapeTarget) {
+  void suggestWithScopeForParmVar(const ParmVarDecl *PVD,
+                                  EscapingTarget EscapeTarget) {
     if (llvm::isa<const VarDecl *>(EscapeTarget))
       return;
 
-    if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD, SM))
-      SemaHelper->suggestLifetimeboundToParmVar(
-          WarningScope::CrossTU,
-          CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()),
-          EscapeTarget);
-    else
-      SemaHelper->suggestLifetimeboundToParmVar(WarningScope::IntraTU, PVD,
-                                                EscapeTarget);
+    auto [Parm, Scope] = getCanonicalDeclForAttr(cast<FunctionDecl>(FD), PVD);
+    SemaHelper->suggestLifetimeboundToParmVar(Scope, Parm, EscapeTarget);
   }
 
-  static void
-  suggestWithScopeForImplicitThis(LifetimeSafetySemaHelper *SemaHelper,
-                                  const CXXMethodDecl *MD, SourceManager &SM,
-                                  const Expr *EscapeExpr) {
-    if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD, SM))
-      SemaHelper->suggestLifetimeboundToImplicitThis(
-          WarningScope::CrossTU, cast<CXXMethodDecl>(CrossTUDecl), EscapeExpr);
-    else
-      SemaHelper->suggestLifetimeboundToImplicitThis(WarningScope::IntraTU, MD,
-                                                     EscapeExpr);
+  void suggestWithScopeForImplicitThis(const CXXMethodDecl *MD,
+                                       const Expr *EscapeExpr) {
+    auto [MethodDecl, Scope] = getCanonicalDeclForAttr(MD);
+    SemaHelper->suggestLifetimeboundToImplicitThis(Scope, MethodDecl,
+                                                   EscapeExpr);
   }
 
   void suggestAnnotations() {
     if (!SemaHelper)
       return;
-    SourceManager &SM = AST.getSourceManager();
     for (auto [Target, EscapeTarget] : AnnotationWarningsMap) {
       if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>())
-        suggestWithScopeForParmVar(SemaHelper, PVD, SM, EscapeTarget);
+        suggestWithScopeForParmVar(PVD, EscapeTarget);
       else if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) {
         if (const auto *EscapeExpr = EscapeTarget.dyn_cast<const Expr *>())
-          suggestWithScopeForImplicitThis(SemaHelper, MD, SM, EscapeExpr);
+          suggestWithScopeForImplicitThis(MD, EscapeExpr);
         else
           llvm_unreachable("Implicit this can only escape via Expr (return)");
       }
diff --git a/clang/test/Sema/warn-lifetime-safety-fixits.cpp 
b/clang/test/Sema/warn-lifetime-safety-fixits.cpp
index 88d8bd379de8b..d9c7e8d3f0519 100644
--- a/clang/test/Sema/warn-lifetime-safety-fixits.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-fixits.cpp
@@ -69,12 +69,11 @@ int *arr_default(int a[2] = nullptr) {
   return a;
 }
 
-// FIXME: Iterate over redecls and add [[clang::lifetimebound]]
 View multi_decl(View a);
+// CHECK: :[[@LINE-1]]:17: warning: parameter in intra-TU function should be 
marked
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:23-[[@LINE-2]]:23}:" 
{{\[\[}}clang::lifetimebound]]"
 View multi_decl(View a);
 View multi_decl(View a) {
-  // CHECK: :[[@LINE-1]]:17: warning: parameter in intra-TU function should be 
marked
-  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:23-[[@LINE-2]]:23}:" 
{{\[\[}}clang::lifetimebound]]"
   return a;
 }
 
@@ -145,10 +144,10 @@ struct OutOfLine {
   OutOfLine() {}
   ~OutOfLine() {}
   const OutOfLine &get() const;
+  // CHECK: :[[@LINE-1]]:31: warning: implicit this in intra-TU function 
should be marked
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:31-[[@LINE-2]]:31}:" 
{{\[\[}}clang::lifetimebound]]"
 };
 const OutOfLine &OutOfLine::get() const {
-  // CHECK: :[[@LINE-1]]:40: warning: implicit this in intra-TU function 
should be marked
-  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:40-[[@LINE-2]]:40}:" 
{{\[\[}}clang::lifetimebound]]"
   return *this;
 }
 
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp 
b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index 973c610eb58ab..5122e634e594f 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -46,8 +46,8 @@ inline View inline_header_return_view(View a) {  // 
expected-warning {{parameter
   return a;                                      // expected-note {{param 
returned here}}
 }
 
-View redeclared_in_header(View a);
-inline View redeclared_in_header(View a) {  // expected-warning {{parameter in 
intra-TU function should be marked [[clang::lifetimebound]]}}
+View redeclared_in_header(View a);          // expected-warning {{parameter in 
intra-TU function should be marked [[clang::lifetimebound]]}}
+inline View redeclared_in_header(View a) {
   return a;                                 // expected-note {{param returned 
here}}
 }
 
@@ -177,8 +177,8 @@ View reassigned_to_another_parameter(
   return a;       // expected-note {{param returned here}} 
 }
 
-View intra_tu_func_redecl(View a);
-View intra_tu_func_redecl(View a) {   // expected-warning {{parameter in 
intra-TU function should be marked [[clang::lifetimebound]]}}.
+View intra_tu_func_redecl(View a);    // expected-warning {{parameter in 
intra-TU function should be marked [[clang::lifetimebound]]}}.
+View intra_tu_func_redecl(View a) {
   return a;                           // expected-note {{param returned here}} 
 }
 }
@@ -282,25 +282,25 @@ MyObj* return_pointer_by_func(MyObj* a) {         // 
expected-warning {{paramete
 } // namespace correct_order_inference
 
 namespace incorrect_order_inference_view {
-View return_view_callee(View a);
+View return_view_callee(View a);      // expected-warning {{parameter in 
intra-TU function should be marked [[clang::lifetimebound]]}}.
 
 View return_view_caller(View a) {     // expected-warning {{parameter in 
intra-TU function should be marked [[clang::lifetimebound]]}}.
   return return_view_callee(a);       // expected-note {{param returned here}}
 }
 
-View return_view_callee(View a) {     // expected-warning {{parameter in 
intra-TU function should be marked [[clang::lifetimebound]]}}.
+View return_view_callee(View a) {
   return a;                           // expected-note {{param returned here}}
 }   
 } // namespace incorrect_order_inference_view
 
 namespace incorrect_order_inference_object {
-MyObj* return_object_callee(MyObj* a);
+MyObj* return_object_callee(MyObj* a);       // expected-warning {{parameter 
in intra-TU function should be marked [[clang::lifetimebound]]}}.
 
 MyObj* return_object_caller(MyObj* a) {      // expected-warning {{parameter 
in intra-TU function should be marked [[clang::lifetimebound]]}}.
   return return_object_callee(a);            // expected-note {{param returned 
here}}
 }
 
-MyObj* return_object_callee(MyObj* a) {      // expected-warning {{parameter 
in intra-TU function should be marked [[clang::lifetimebound]]}}.
+MyObj* return_object_callee(MyObj* a) {
   return a;                                  // expected-note {{param returned 
here}}
 }   
 } // namespace incorrect_order_inference_object
@@ -322,13 +322,13 @@ View inference_top_level_return_stack_view() {
 } // namespace simple_annotation_inference
 
 namespace inference_in_order_with_redecls {
-View inference_callee_return_identity(View a);
-View inference_callee_return_identity(View a) {   // expected-warning 
{{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}.
+View inference_callee_return_identity(View a);    // expected-warning 
{{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}.
+View inference_callee_return_identity(View a) {
   return a;                                       // expected-note {{param 
returned here}}
 }
 
-View inference_caller_forwards_callee(View a);
-View inference_caller_forwards_callee(View a) {   // expected-warning 
{{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}.
+View inference_caller_forwards_callee(View a);    // expected-warning 
{{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}.
+View inference_caller_forwards_callee(View a) {
   return inference_callee_return_identity(a);     // expected-note {{param 
returned here}}
 }
   

>From 8c31e0f4a28d0c2dd91d91e23fd97131f45d2808 Mon Sep 17 00:00:00 2001
From: NeKon69 <[email protected]>
Date: Wed, 20 May 2026 17:55:45 +0300
Subject: [PATCH 2/6] revert the change and warn on both crosstu declarations
 and canonical declarations

---
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 43 +++++++++++++++++--
 .../Sema/warn-lifetime-safety-suggestions.cpp |  9 ++++
 2 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 4b9d195411179..f6b116b2d03a6 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -353,20 +353,55 @@ class LifetimeChecker {
             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,
+                                            SourceManager &SM) {
+    if (!FD.isExternallyVisible())
+      return nullptr;
+    const FileID DefinitionFile = SM.getFileID(FD.getLocation());
+    for (const FunctionDecl *Redecl : FD.redecls())
+      if (SM.getFileID(Redecl->getLocation()) != DefinitionFile)
+        return Redecl;
+
+    return nullptr;
+  }
+
+  static const FunctionDecl *getCrossTUDecl(const ParmVarDecl &PVD,
+                                            SourceManager &SM) {
+    if (const auto *FD = dyn_cast<FunctionDecl>(PVD.getDeclContext()))
+      return getCrossTUDecl(*FD, SM);
+    return nullptr;
+  }
+
   void suggestWithScopeForParmVar(const ParmVarDecl *PVD,
                                   EscapingTarget EscapeTarget) {
     if (llvm::isa<const VarDecl *>(EscapeTarget))
       return;
 
-    auto [Parm, Scope] = getCanonicalDeclForAttr(cast<FunctionDecl>(FD), PVD);
-    SemaHelper->suggestLifetimeboundToParmVar(Scope, Parm, EscapeTarget);
+    auto [CanonicalPVD, Scope] =
+        getCanonicalDeclForAttr(cast<FunctionDecl>(FD), PVD);
+    const auto *CrossTUFD = getCrossTUDecl(*PVD, AST.getSourceManager());
+    const auto *CrossTUParm =
+        CrossTUFD ? CrossTUFD->getParamDecl(PVD->getFunctionScopeIndex())
+                  : nullptr;
+
+    SemaHelper->suggestLifetimeboundToParmVar(Scope, CanonicalPVD,
+                                              EscapeTarget);
+    if (CrossTUParm && CrossTUParm != CanonicalPVD)
+      SemaHelper->suggestLifetimeboundToParmVar(WarningScope::CrossTU,
+                                                CrossTUParm, EscapeTarget);
   }
 
   void suggestWithScopeForImplicitThis(const CXXMethodDecl *MD,
                                        const Expr *EscapeExpr) {
-    auto [MethodDecl, Scope] = getCanonicalDeclForAttr(MD);
-    SemaHelper->suggestLifetimeboundToImplicitThis(Scope, MethodDecl,
+    auto [CanonicalDecl, Scope] = getCanonicalDeclForAttr(MD);
+    const auto *CrossTUDecl = getCrossTUDecl(*MD, AST.getSourceManager());
+    SemaHelper->suggestLifetimeboundToImplicitThis(Scope, CanonicalDecl,
                                                    EscapeExpr);
+    if (CrossTUDecl && CrossTUDecl != CanonicalDecl)
+      SemaHelper->suggestLifetimeboundToImplicitThis(
+          WarningScope::CrossTU, cast<CXXMethodDecl>(CrossTUDecl), EscapeExpr);
   }
 
   void suggestAnnotations() {
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp 
b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index 5122e634e594f..30d1896450236 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -31,6 +31,8 @@ struct [[gsl::Pointer()]] View {
 
 View definition_before_header(View a); // expected-warning {{parameter in 
cross-TU function should be marked [[clang::lifetimebound]]}}
 
+View redeclared_before_header_include(View a); // expected-warning {{parameter 
in cross-TU function should be marked [[clang::lifetimebound]]}}
+
 View return_view_directly(View a); // expected-warning {{parameter in cross-TU 
function should be marked [[clang::lifetimebound]]}}
 
 View conditional_return_view(
@@ -67,6 +69,9 @@ struct ReturnThisPointer {
 
 //--- test_source.cpp
 
+struct View;
+View redeclared_before_header_include(View a); // expected-warning {{parameter 
in intra-TU function should be marked [[clang::lifetimebound]]}}
+
 #include "test_header.h"
 #include "Inputs/lifetime-analysis.h"
 
@@ -74,6 +79,10 @@ View definition_before_header(View a) {
   return a;                               // expected-note {{param returned 
here}}
 }
 
+View redeclared_before_header_include(View a) {
+  return a;                               // expected-note 2 {{param returned 
here}}
+}
+
 View return_view_directly(View a) {
   return a;                             // expected-note {{param returned 
here}}
 }

>From 73adbd7bc23c7d7407e977fcd489282237913e0e Mon Sep 17 00:00:00 2001
From: NeKon69 <[email protected]>
Date: Thu, 21 May 2026 21:36:36 +0300
Subject: [PATCH 3/6] change impl and update tests

---
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 128 ++++++++----------
 ...afety-misplaced-lifetimebound-cross-tu.cpp |  14 ++
 ...afety-misplaced-lifetimebound-intra-tu.cpp |   6 +
 .../Sema/warn-lifetime-safety-suggestions.cpp |  46 +++++++
 4 files changed, 126 insertions(+), 68 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index f6b116b2d03a6..38ad62b37df60 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -316,62 +316,57 @@ class LifetimeChecker {
     }
   }
 
-  std::pair<const FunctionDecl *, WarningScope>
-  getCanonicalFunctionDeclForAttr(const FunctionDecl *FDef) {
+  // Returns diagnostic targets for annotations on FDef: the canonical
+  // declaration and the earliest redeclaration in each other file. Each
+  // target is paired with the warning scope appropriate for its file.
+  llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2>
+  getTargetDeclsForAttr(const FunctionDecl *FDef) {
     if (!FDef)
-      return {nullptr, WarningScope::IntraTU};
+      return {};
 
     assert(FDef->isThisDeclarationADefinition() &&
            "Expected FunctionDecl to be a definition");
 
     const auto &SM = FDef->getASTContext().getSourceManager();
-    const FileID DefFile =
-        SM.getFileID(SM.getExpansionLoc(FDef->getLocation()));
-    const FunctionDecl *CanonicalDecl = FDef->getCanonicalDecl();
-    WarningScope Scope = WarningScope::IntraTU;
 
-    Scope = SM.getFileID(SM.getExpansionLoc(CanonicalDecl->getLocation())) !=
-                    DefFile
-                ? WarningScope::CrossTU
-                : WarningScope::IntraTU;
+    auto GetLoc = [&SM](const FunctionDecl *FD) {
+      return SM.getExpansionLoc(FD->getLocation());
+    };
+    auto GetFile = [&SM, &GetLoc](const FunctionDecl *FD) {
+      return SM.getFileID(GetLoc(FD));
+    };
 
-    return {CanonicalDecl, Scope};
-  }
+    const FileID DefFile = GetFile(FDef);
+    llvm::SmallVector<std::pair<FileID, const FunctionDecl *>, 2>
+        EarliestDeclForFile{
+            {GetFile(FDef->getCanonicalDecl()), FDef->getCanonicalDecl()}};
 
-  std::pair<const CXXMethodDecl *, WarningScope>
-  getCanonicalDeclForAttr(const CXXMethodDecl *MDef) {
-    auto [CanonicalFDecl, Scope] = getCanonicalFunctionDeclForAttr(MDef);
-    return {cast_or_null<CXXMethodDecl>(CanonicalFDecl), Scope};
-  }
+    auto AddCrossTUDecl = [&](const FunctionDecl *FD) {
+      FileID File = GetFile(FD);
+      if (File == DefFile)
+        return;
+      for (auto &[SeenFile, SeenFD] : EarliestDeclForFile) {
+        if (SeenFile != File)
+          continue;
+        if (SM.isBeforeInTranslationUnit(GetLoc(FD), GetLoc(SeenFD)))
+          SeenFD = FD;
+        return;
+      }
+      EarliestDeclForFile.push_back({File, FD});
+    };
 
-  std::pair<const ParmVarDecl *, WarningScope>
-  getCanonicalDeclForAttr(const FunctionDecl *FDef, const ParmVarDecl *PVDDef) 
{
-    auto [CanonicalFDecl, Scope] = getCanonicalFunctionDeclForAttr(FDef);
-    if (!CanonicalFDecl)
-      return {nullptr, Scope};
-    return {CanonicalFDecl->getParamDecl(PVDDef->getFunctionScopeIndex()),
-            Scope};
-  }
+    for (const FunctionDecl *Redecl : FDef->redecls()) {
+      AddCrossTUDecl(Redecl);
+    }
 
-  /// 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,
-                                            SourceManager &SM) {
-    if (!FD.isExternallyVisible())
-      return nullptr;
-    const FileID DefinitionFile = SM.getFileID(FD.getLocation());
-    for (const FunctionDecl *Redecl : FD.redecls())
-      if (SM.getFileID(Redecl->getLocation()) != DefinitionFile)
-        return Redecl;
-
-    return nullptr;
-  }
+    llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2> 
Targets;
+    for (auto [File, FD] : EarliestDeclForFile) {
+      WarningScope Scope =
+          File == DefFile ? WarningScope::IntraTU : WarningScope::CrossTU;
+      Targets.push_back({FD, Scope});
+    }
 
-  static const FunctionDecl *getCrossTUDecl(const ParmVarDecl &PVD,
-                                            SourceManager &SM) {
-    if (const auto *FD = dyn_cast<FunctionDecl>(PVD.getDeclContext()))
-      return getCrossTUDecl(*FD, SM);
-    return nullptr;
+    return Targets;
   }
 
   void suggestWithScopeForParmVar(const ParmVarDecl *PVD,
@@ -379,29 +374,20 @@ class LifetimeChecker {
     if (llvm::isa<const VarDecl *>(EscapeTarget))
       return;
 
-    auto [CanonicalPVD, Scope] =
-        getCanonicalDeclForAttr(cast<FunctionDecl>(FD), PVD);
-    const auto *CrossTUFD = getCrossTUDecl(*PVD, AST.getSourceManager());
-    const auto *CrossTUParm =
-        CrossTUFD ? CrossTUFD->getParamDecl(PVD->getFunctionScopeIndex())
-                  : nullptr;
-
-    SemaHelper->suggestLifetimeboundToParmVar(Scope, CanonicalPVD,
-                                              EscapeTarget);
-    if (CrossTUParm && CrossTUParm != CanonicalPVD)
-      SemaHelper->suggestLifetimeboundToParmVar(WarningScope::CrossTU,
-                                                CrossTUParm, EscapeTarget);
+    for (auto [Decl, Scope] : getTargetDeclsForAttr(cast<FunctionDecl>(FD))) {
+      const auto *ParmToAnnotate =
+          Decl->getParamDecl(PVD->getFunctionScopeIndex());
+      SemaHelper->suggestLifetimeboundToParmVar(Scope, ParmToAnnotate,
+                                                EscapeTarget);
+    }
   }
 
   void suggestWithScopeForImplicitThis(const CXXMethodDecl *MD,
                                        const Expr *EscapeExpr) {
-    auto [CanonicalDecl, Scope] = getCanonicalDeclForAttr(MD);
-    const auto *CrossTUDecl = getCrossTUDecl(*MD, AST.getSourceManager());
-    SemaHelper->suggestLifetimeboundToImplicitThis(Scope, CanonicalDecl,
-                                                   EscapeExpr);
-    if (CrossTUDecl && CrossTUDecl != CanonicalDecl)
+    for (auto [Decl, Scope] : getTargetDeclsForAttr(MD)) {
       SemaHelper->suggestLifetimeboundToImplicitThis(
-          WarningScope::CrossTU, cast<CXXMethodDecl>(CrossTUDecl), EscapeExpr);
+          Scope, cast<CXXMethodDecl>(Decl), EscapeExpr);
+    }
   }
 
   void suggestAnnotations() {
@@ -458,13 +444,17 @@ class LifetimeChecker {
     const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD);
     if (!FDef)
       return;
+
+    auto TargetDecls = getTargetDeclsForAttr(FDef);
     // Check if implicit 'this' has lifetimebound on definition but not on
     // declaration.
     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 (auto [Decl, Scope] : TargetDecls) {
+        const auto *MDecl = cast<CXXMethodDecl>(Decl);
+        if (!getDirectImplicitObjectLifetimeBoundAttr(MDecl))
+          SemaHelper->reportMisplacedLifetimebound(Scope, MDef, MDecl);
+      }
 
     // Check each parameter for explicit lifetimebound on definition but not on
     // declaration.
@@ -472,9 +462,11 @@ class LifetimeChecker {
       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);
+      for (auto [Decl, Scope] : TargetDecls) {
+        const auto *PDecl = Decl->getParamDecl(PDef->getFunctionScopeIndex());
+        if (!PDecl->hasAttr<LifetimeBoundAttr>())
+          SemaHelper->reportMisplacedLifetimebound(Scope, PDef, PDecl);
+      }
     }
   }
 
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
index 5fd49023a042a..583fba6988573 100644
--- a/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-cross-tu.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-misplaced-lifetimebound-cross-tu.cpp
@@ -14,8 +14,18 @@ struct HeaderS {
   HeaderObj &header_this(); // expected-warning {{'lifetimebound' attribute on 
this definition is not visible to callers in other translation units; add it to 
the declaration instead}}
 };
 
+//--- cross_1.h
+struct HeaderObj;
+HeaderObj &multi_header_param(HeaderObj &obj); // expected-warning 
{{'lifetimebound' attribute on this definition is not visible to callers in 
other translation units; add it to the declaration instead}}
+
+//--- cross_2.h
+struct HeaderObj;
+HeaderObj &multi_header_param(HeaderObj &obj); // expected-warning 
{{'lifetimebound' attribute on this definition is not visible to callers in 
other translation units; add it to the declaration instead}}
+
 //--- cross.cpp
 #include "cross.h"
+#include "cross_1.h"
+#include "cross_2.h"
 
 HeaderObj &header_param(HeaderObj &obj [[clang::lifetimebound]]) { // 
expected-note {{'lifetimebound' attribute appears here on the definition}}
   return obj;
@@ -24,3 +34,7 @@ HeaderObj &header_param(HeaderObj &obj 
[[clang::lifetimebound]]) { // expected-n
 HeaderObj &HeaderS::header_this() [[clang::lifetimebound]] { // expected-note 
{{'lifetimebound' attribute appears here on the definition}}
   return data;
 }
+
+HeaderObj &multi_header_param(HeaderObj &obj [[clang::lifetimebound]]) { // 
expected-note 2 {{'lifetimebound' attribute appears here on the definition}}
+  return obj;
+}
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 caf805cea5416..998e92b467819 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
@@ -9,6 +9,12 @@ MyObj &free_param(MyObj &obj [[clang::lifetimebound]]) { // 
expected-note {{'lif
   return obj;
 }
 
+MyObj &earliest_redecl_param(MyObj &obj); // expected-warning 
{{'lifetimebound' attribute on this definition is not visible to callers before 
the definition; add it to the declaration instead}}
+MyObj &earliest_redecl_param(MyObj &obj);
+MyObj &earliest_redecl_param(MyObj &obj [[clang::lifetimebound]]) { // 
expected-note {{'lifetimebound' attribute appears here on the definition}}
+  return obj;
+}
+
 struct S {
   MyObj data;
   const MyObj &implicit_this_only();         // expected-warning 
{{'lifetimebound' attribute on this definition is not visible to callers before 
the definition; add it to the declaration instead}}
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp 
b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index 30d1896450236..d9aa308986913 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -67,14 +67,40 @@ struct ReturnThisPointer {
 
 #endif // TEST_HEADER_H
 
+//--- test_redecls_header.h
+
+View earliest_decl_in_header(View a); // expected-warning {{parameter in 
cross-TU function should be marked [[clang::lifetimebound]]}}
+View earliest_decl_in_header(View a);
+
+View multi_redecl_one_file(View a); // expected-warning {{parameter in 
cross-TU function should be marked [[clang::lifetimebound]]}}
+View multi_redecl_one_file(View a);
+View multi_redecl_one_file(View a);
+
+View source_and_header(View a); // expected-warning {{parameter in cross-TU 
function should be marked [[clang::lifetimebound]]}}
+
+//--- test_redecls_header_1.h
+
+View in_two_headers(View a); // expected-warning {{parameter in cross-TU 
function should be marked [[clang::lifetimebound]]}}
+
+//--- test_redecls_header_2.h
+
+View in_two_headers(View a); // expected-warning {{parameter in cross-TU 
function should be marked [[clang::lifetimebound]]}}
+
 //--- test_source.cpp
 
 struct View;
 View redeclared_before_header_include(View a); // expected-warning {{parameter 
in intra-TU function should be marked [[clang::lifetimebound]]}}
+View source_and_header(View a); // expected-warning {{parameter in intra-TU 
function should be marked [[clang::lifetimebound]]}}
 
 #include "test_header.h"
+#include "test_redecls_header.h"
+#include "test_redecls_header_1.h"
+#include "test_redecls_header_2.h"
 #include "Inputs/lifetime-analysis.h"
 
+View earliest_decl_in_source(View a); // expected-warning {{parameter in 
intra-TU function should be marked [[clang::lifetimebound]]}}
+View earliest_decl_in_source(View a);
+
 View definition_before_header(View a) {
   return a;                               // expected-note {{param returned 
here}}
 }
@@ -112,6 +138,26 @@ MyObj& return_unnamed_ref(MyObj& a, bool c) {
   return a;                               // expected-note {{param returned 
here}}
 }
 
+View earliest_decl_in_header(View a) {
+  return a;                               // expected-note {{param returned 
here}}
+}
+
+View earliest_decl_in_source(View a) {
+  return a;                               // expected-note {{param returned 
here}}
+}
+
+View multi_redecl_one_file(View a) {
+  return a;                               // expected-note {{param returned 
here}}
+}
+
+View in_two_headers(View a) {
+  return a;                               // expected-note 2 {{param returned 
here}}
+}
+
+View source_and_header(View a) {
+  return a;                               // expected-note 2 {{param returned 
here}}
+}
+
 MyObj& return_reference(MyObj& a, // expected-warning {{parameter in intra-TU 
function should be marked [[clang::lifetimebound]]}}
                         MyObj& b, // expected-warning {{parameter in intra-TU 
function should be marked [[clang::lifetimebound]]}}
                         bool c) {

>From 614c4efd151add2afaa01e2b05deac31a48e78c0 Mon Sep 17 00:00:00 2001
From: NeKon69 <[email protected]>
Date: Fri, 22 May 2026 11:48:55 +0300
Subject: [PATCH 4/6] cleanup logic a bit and a few nits

---
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 22 +++++++++----------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 38ad62b37df60..388f9c116c645 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -316,9 +316,11 @@ class LifetimeChecker {
     }
   }
 
-  // Returns diagnostic targets for annotations on FDef: the canonical
-  // declaration and the earliest redeclaration in each other file. Each
-  // target is paired with the warning scope appropriate for its file.
+  // Returns declarations that should be annotated with lifetime attributes
+  // in order to annotate FDef: the canonical declaration and the earliest
+  // redeclarations in each other file. This defines the placement policy for
+  // lifetime annotations. Each target is paired with its corresponding warning
+  // scope.
   llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2>
   getTargetDeclsForAttr(const FunctionDecl *FDef) {
     if (!FDef)
@@ -345,19 +347,15 @@ class LifetimeChecker {
       FileID File = GetFile(FD);
       if (File == DefFile)
         return;
-      for (auto &[SeenFile, SeenFD] : EarliestDeclForFile) {
-        if (SeenFile != File)
-          continue;
-        if (SM.isBeforeInTranslationUnit(GetLoc(FD), GetLoc(SeenFD)))
-          SeenFD = FD;
-        return;
-      }
+      for (auto [SeenFile, SeenFD] : EarliestDeclForFile)
+        if (SeenFile == File)
+          return;
       EarliestDeclForFile.push_back({File, FD});
     };
 
-    for (const FunctionDecl *Redecl : FDef->redecls()) {
+    llvm::SmallVector<const FunctionDecl *, 4> Redecls(FDef->redecls());
+    for (const FunctionDecl *Redecl : llvm::reverse(Redecls))
       AddCrossTUDecl(Redecl);
-    }
 
     llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2> 
Targets;
     for (auto [File, FD] : EarliestDeclForFile) {

>From 65974ebc5fbbc0af718c0ead8b2d16af64305bf7 Mon Sep 17 00:00:00 2001
From: NeKon69 <[email protected]>
Date: Fri, 22 May 2026 12:49:15 +0300
Subject: [PATCH 5/6] simplify code

---
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 21 +++++++------------
 1 file changed, 8 insertions(+), 13 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 388f9c116c645..adb104fa978c1 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -339,31 +339,26 @@ class LifetimeChecker {
     };
 
     const FileID DefFile = GetFile(FDef);
-    llvm::SmallVector<std::pair<FileID, const FunctionDecl *>, 2>
-        EarliestDeclForFile{
-            {GetFile(FDef->getCanonicalDecl()), FDef->getCanonicalDecl()}};
+    const FunctionDecl *CanonicalDecl = FDef->getCanonicalDecl();
+    llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2> 
Targets{
+        {CanonicalDecl, GetFile(CanonicalDecl) == DefFile
+                            ? WarningScope::IntraTU
+                            : WarningScope::CrossTU}};
 
     auto AddCrossTUDecl = [&](const FunctionDecl *FD) {
       FileID File = GetFile(FD);
       if (File == DefFile)
         return;
-      for (auto [SeenFile, SeenFD] : EarliestDeclForFile)
-        if (SeenFile == File)
+      for (auto [SeenFD, _] : Targets)
+        if (GetFile(SeenFD) == File)
           return;
-      EarliestDeclForFile.push_back({File, FD});
+      Targets.push_back({FD, WarningScope::CrossTU});
     };
 
     llvm::SmallVector<const FunctionDecl *, 4> Redecls(FDef->redecls());
     for (const FunctionDecl *Redecl : llvm::reverse(Redecls))
       AddCrossTUDecl(Redecl);
 
-    llvm::SmallVector<std::pair<const FunctionDecl *, WarningScope>, 2> 
Targets;
-    for (auto [File, FD] : EarliestDeclForFile) {
-      WarningScope Scope =
-          File == DefFile ? WarningScope::IntraTU : WarningScope::CrossTU;
-      Targets.push_back({FD, Scope});
-    }
-
     return Targets;
   }
 

>From bff670379d131b5f00bc27807f47261120835fab Mon Sep 17 00:00:00 2001
From: NeKon69 <[email protected]>
Date: Fri, 22 May 2026 14:49:31 +0300
Subject: [PATCH 6/6] add comments and remove lambda

---
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp 
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index adb104fa978c1..53899251b9643 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -331,11 +331,8 @@ class LifetimeChecker {
 
     const auto &SM = FDef->getASTContext().getSourceManager();
 
-    auto GetLoc = [&SM](const FunctionDecl *FD) {
-      return SM.getExpansionLoc(FD->getLocation());
-    };
-    auto GetFile = [&SM, &GetLoc](const FunctionDecl *FD) {
-      return SM.getFileID(GetLoc(FD));
+    auto GetFile = [&SM](const FunctionDecl *FD) {
+      return SM.getFileID(SM.getExpansionLoc(FD->getLocation()));
     };
 
     const FileID DefFile = GetFile(FDef);
@@ -345,6 +342,8 @@ class LifetimeChecker {
                             ? WarningScope::IntraTU
                             : WarningScope::CrossTU}};
 
+    // Find the earliest redeclaration in each file other than the definition
+    // file.
     auto AddCrossTUDecl = [&](const FunctionDecl *FD) {
       FileID File = GetFile(FD);
       if (File == DefFile)
@@ -355,8 +354,10 @@ class LifetimeChecker {
       Targets.push_back({FD, WarningScope::CrossTU});
     };
 
-    llvm::SmallVector<const FunctionDecl *, 4> Redecls(FDef->redecls());
-    for (const FunctionDecl *Redecl : llvm::reverse(Redecls))
+    // We iterate in reverse order (from most recent to oldest) to find
+    // the first declaration in each file.
+    for (const FunctionDecl *Redecl :
+         llvm::reverse(llvm::to_vector(FDef->redecls())))
       AddCrossTUDecl(Redecl);
 
     return Targets;

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

Reply via email to