[clang] [Lifetime Safety] Highlight lifetimebound calls in alias chain diagnostics (PR #206337)

via cfe-commits cfe-commits at lists.llvm.org
Fri Jul 17 04:38:07 PDT 2026


https://github.com/iitianpushkar updated https://github.com/llvm/llvm-project/pull/206337

>From 666b03577b08a11d6c0c5df8a4daab2c4080cf2c Mon Sep 17 00:00:00 2001
From: iitianpushkar <pushkarsingh0587 at gmail.com>
Date: Tue, 7 Jul 2026 02:01:05 +0530
Subject: [PATCH 1/8] rebased branch on latest main and updated patch
 accordingly

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  19 +++
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |   9 +-
 .../Analyses/LifetimeSafety/LoanPropagation.h |   9 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |   2 +
 clang/lib/Analysis/LifetimeSafety/Checker.cpp |  20 +--
 .../LifetimeSafety/FactsGenerator.cpp         |  57 ++++++--
 .../LifetimeSafety/LoanPropagation.cpp        |  62 +++++++-
 clang/lib/Sema/SemaLifetimeSafety.h           |  49 +++++--
 .../LifetimeSafety/annotation-suggestions.cpp |  12 +-
 clang/test/Sema/LifetimeSafety/nocfg.cpp      |  26 ++--
 clang/test/Sema/LifetimeSafety/safety-c.c     |   6 +-
 clang/test/Sema/LifetimeSafety/safety.cpp     | 137 +++++++++---------
 12 files changed, 273 insertions(+), 135 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 94db2a7f311ae..2e426105d1c84 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -20,6 +20,7 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Debug.h"
@@ -33,6 +34,11 @@ class LoanPropagationAnalysis;
 
 using FactID = utils::ID<struct FactTag>;
 
+struct LifetimeBoundParamInfo {
+  const ParmVarDecl *Param = nullptr;
+  bool IsImplicitObject = false;
+};
+
 /// An abstract base class for a single, atomic lifetime-relevant event.
 class Fact {
 
@@ -393,6 +399,17 @@ class FactManager {
   const LoanManager &getLoanMgr() const { return LoanMgr; }
   OriginManager &getOriginMgr() { return OriginMgr; }
   const OriginManager &getOriginMgr() const { return OriginMgr; }
+  void setLifetimeBoundParamInfo(const OriginFlowFact *F,
+                                 LifetimeBoundParamInfo Info) {
+    LifetimeBoundParamInfos.try_emplace(F, Info);
+  }
+  std::optional<LifetimeBoundParamInfo>
+  getLifetimeBoundParamInfo(const OriginFlowFact *F) const {
+    auto It = LifetimeBoundParamInfos.find(F);
+    if (It == LifetimeBoundParamInfos.end())
+      return std::nullopt;
+    return It->second;
+  }
 
 private:
   FactID NextFactID{0};
@@ -400,6 +417,8 @@ class FactManager {
   OriginManager OriginMgr;
   /// Facts for each CFG block, indexed by block ID.
   llvm::SmallVector<llvm::SmallVector<const Fact *>> BlockToFacts;
+  llvm::DenseMap<const OriginFlowFact *, LifetimeBoundParamInfo>
+      LifetimeBoundParamInfos;
   llvm::BumpPtrAllocator FactAllocator;
 };
 } // namespace clang::lifetimes::internal
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 80a23f18baebd..6204381a873ee 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -31,6 +31,7 @@
 #include "llvm/ADT/PointerUnion.h"
 #include <cstddef>
 #include <memory>
+#include <optional>
 
 namespace clang::lifetimes {
 
@@ -49,6 +50,11 @@ enum class WarningScope {
   IntraTU  // For warnings on functions local to a Translation Unit.
 };
 
+struct AliasChainEntry {
+  const Expr *E = nullptr;
+  std::optional<internal::LifetimeBoundParamInfo> LifetimeBound;
+};
+
 /// Abstract interface for operations requiring Sema access.
 ///
 /// This class exists to break a circular dependency: the LifetimeSafety
@@ -67,7 +73,8 @@ class LifetimeSafetySemaHelper {
   virtual void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
                                    const Expr *MovedExpr,
                                    SourceLocation FreeLoc,
-                                   llvm::ArrayRef<const Expr *> ExprChain) {}
+                                   llvm::ArrayRef<AliasChainEntry> AliasChain) {
+  }
 
   virtual void reportUseAfterReturn(const Expr *IssueExpr,
                                     const Expr *ReturnExpr,
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index 838daa024c953..616394314056f 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -56,6 +56,15 @@ class LoanPropagationAnalysis {
                                                    const LoanID TargetLoan,
                                                    const CFG *Cfg) const;
 
+  llvm::SmallVector<const OriginFlowFact *>
+  buildOriginFlowChainWithFacts(ProgramPoint StartPoint,
+                                const OriginID StartOID,
+                                const LoanID TargetLoan, const CFG *Cfg) const;
+
+  llvm::SmallVector<const OriginFlowFact *>
+  buildOriginFlowChainWithFacts(const UseFact *UF, const LoanID TargetLoan,
+                                const CFG *Cfg) const;
+
 private:
   class Impl;
   std::unique_ptr<Impl> PImpl;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 05f49a4dbd1ca..ed1e4350890b5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11078,6 +11078,8 @@ def note_lifetime_safety_escapes_to_global_here: Note<"escapes to this global st
 def note_lifetime_safety_escapes_to_static_storage_here: Note<"escapes to this static storage">;
 def note_lifetime_safety_lifetimebound_here: Note<"'lifetimebound' attribute appears here on the definition">;
 def note_lifetime_safety_aliases_storage : Note<"%0 aliases the storage of %1">;
+def note_lifetime_safety_aliases_storage_lifetimebound : Note<
+  "%0 aliases the storage of %1 because %2 is marked 'lifetimebound'">;
 
 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 f72f7f80abbc0..2a3acdeb4b1ca 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -274,7 +274,8 @@ class LifetimeChecker {
           // Scope-based expiry (use-after-scope).
           SemaHelper->reportUseAfterScope(
               IssueExpr, UF->getUseExpr(), MovedExpr, ExpiryLoc,
-              getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, Cfg)));
+              getAliasChain(
+                  LoanPropagation.buildOriginFlowChainWithFacts(UF, LID, Cfg)));
 
       } else if (const auto *OEF =
                      CausingFact.dyn_cast<const OriginEscapesFact *>()) {
@@ -526,14 +527,15 @@ class LifetimeChecker {
   /// Given a chain of origins that shows how a loan propagates, this function
   /// extracts the corresponding expressions for each origin. Origins that refer
   /// to declarations (rather than expressions) are skipped.
-  llvm::SmallVector<const Expr *>
-  getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
-    llvm::SmallVector<const Expr *> rs;
-    for (const OriginID CurrOID : OriginFlowChain)
-      if (const Expr *CurrExpr =
-              FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr())
-        rs.push_back(CurrExpr);
-    return rs;
+  llvm::SmallVector<AliasChainEntry>
+  getAliasChain(llvm::ArrayRef<const OriginFlowFact *> OriginFlowChain) {
+    llvm::SmallVector<AliasChainEntry> Result;
+    for (const OriginFlowFact *Flow : OriginFlowChain)
+      if (const Expr *CurrExpr = FactMgr.getOriginMgr()
+                                     .getOrigin(Flow->getSrcOriginID())
+                                     .getExpr())
+        Result.push_back({CurrExpr, FactMgr.getLifetimeBoundParamInfo(Flow)});
+    return Result;
   }
 };
 } // namespace
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 095bee8135c20..83ff0d1265658 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -1096,28 +1096,45 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
     flow(CallList, getOriginsList(*Args[0]), /*Kill=*/true);
     return;
   }
-  auto IsArgLifetimeBound = [FD, &Args](unsigned I) -> bool {
+  auto GetLifetimeBoundParamInfo =
+      [FD](unsigned I) -> std::optional<LifetimeBoundParamInfo> {
     const ParmVarDecl *PVD = nullptr;
     if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
         Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) {
       if (I == 0)
         // For the 'this' argument, the attribute is on the method itself.
-        return implicitObjectParamIsLifetimeBound(Method) ||
-               shouldTrackImplicitObjectArg(
-                   *Args[0], Method, /*RunningUnderLifetimeSafety=*/true);
+        return implicitObjectParamIsLifetimeBound(Method)
+                   ? std::optional<LifetimeBoundParamInfo>(
+                         LifetimeBoundParamInfo{/*Param=*/nullptr,
+                                                /*IsImplicitObject=*/true})
+                   : std::nullopt;
       if ((I - 1) < Method->getNumParams())
         // For explicit arguments, find the corresponding parameter
         // declaration.
         PVD = Method->getParamDecl(I - 1);
-    } else if (I == 0 && shouldTrackFirstArgument(FD)) {
-      return true;
-    } else if (I == 1 && shouldTrackSecondArgument(FD)) {
-      return true;
     } else if (I < FD->getNumParams()) {
       // For free functions or static methods.
       PVD = FD->getParamDecl(I);
     }
-    return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() : false;
+    if (PVD && PVD->hasAttr<clang::LifetimeBoundAttr>())
+      return LifetimeBoundParamInfo{PVD, /*IsImplicitObject=*/false};
+    return std::nullopt;
+  };
+  auto ShouldTrackArg =
+      [FD, &Args](unsigned I,
+                  std::optional<LifetimeBoundParamInfo> Info) -> bool {
+    if (Info)
+      return true;
+    if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
+        Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD))
+      if (I == 0)
+        return shouldTrackImplicitObjectArg(
+            *Args[0], Method, /*RunningUnderLifetimeSafety=*/true);
+    if (I == 0 && shouldTrackFirstArgument(FD))
+      return true;
+    if (I == 1 && shouldTrackSecondArgument(FD))
+      return true;
+    return false;
   };
   auto shouldTrackPointerImplicitObjectArg = [FD, &Args](unsigned I) -> bool {
     const auto *Method = dyn_cast<CXXMethodDecl>(FD);
@@ -1135,6 +1152,9 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
     OriginList *ArgList = getOriginsList(*Args[I]);
     if (!ArgList)
       continue;
+    std::optional<LifetimeBoundParamInfo> LifetimeBoundInfo =
+        GetLifetimeBoundParamInfo(I);
+    bool TrackArg = ShouldTrackArg(I, LifetimeBoundInfo);
     if (IsGslConstruction) {
       // TODO: document with code example.
       // std::string_view(const std::string_view& from)
@@ -1150,16 +1170,18 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
             CallList->getOuterOriginID(), ArgList->getOuterOriginID(),
             KillSrc));
         KillSrc = false;
-      } else if (IsArgLifetimeBound(I)) {
+      } else if (TrackArg) {
         // Only flow the outer origin here. For lifetimebound args in
         // gsl::Pointer construction, we do not have enough information to
         // safely match inner origins, so the source and
         // destination origin lists may have different lengths.
         // FIXME: Handle origin-shape mismatches gracefully so we can also flow
         // inner origins.
-        CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
-            CallList->getOuterOriginID(), ArgList->getOuterOriginID(),
-            KillSrc));
+        auto *F = FactMgr.createFact<OriginFlowFact>(
+            CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc);
+        if (LifetimeBoundInfo)
+          FactMgr.setLifetimeBoundParamInfo(F, *LifetimeBoundInfo);
+        CurrentBlockFacts.push_back(F);
         KillSrc = false;
       }
     } else if (shouldTrackPointerImplicitObjectArg(I)) {
@@ -1170,12 +1192,15 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
           CallList->getOuterOriginID(),
           ArgList->peelOuterOrigin()->getOuterOriginID(), KillSrc));
       KillSrc = false;
-    } else if (IsArgLifetimeBound(I)) {
+    } else if (TrackArg) {
       // Lifetimebound on a non-GSL-ctor function means the returned
       // pointer/reference itself must not outlive the arguments. This
       // only constrains the top-level origin.
-      CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
-          CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc));
+      auto *F = FactMgr.createFact<OriginFlowFact>(
+          CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc);
+      if (LifetimeBoundInfo)
+        FactMgr.setLifetimeBoundParamInfo(F, *LifetimeBoundInfo);
+      CurrentBlockFacts.push_back(F);
       KillSrc = false;
     }
   }
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 078892bd48c10..5744479737b0a 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -207,6 +207,17 @@ class AnalysisImpl
                                                    const OriginID StartOID,
                                                    const LoanID TargetLoan,
                                                    const CFG *Cfg) const {
+    llvm::SmallVector<OriginID> OriginFlowChain;
+    for (const OriginFlowFact *Flow :
+         buildOriginFlowChainWithFacts(StartPoint, StartOID, TargetLoan, Cfg))
+      OriginFlowChain.push_back(Flow->getSrcOriginID());
+    return OriginFlowChain;
+  }
+
+  llvm::SmallVector<const OriginFlowFact *>
+  buildOriginFlowChainWithFacts(ProgramPoint StartPoint,
+                                const OriginID StartOID,
+                                const LoanID TargetLoan, const CFG *Cfg) const {
     assert(getLoans(StartOID, StartPoint).contains(TargetLoan) &&
            "TargetLoan must be present in the StartOID at the StartPoint");
 
@@ -225,7 +236,7 @@ class AnalysisImpl
     using SearchState = std::pair<const CFGBlock *, OriginID>;
     struct DFSNode {
       SearchState CurrState;
-      llvm::SmallVector<OriginID> OriginFlowChain;
+      llvm::SmallVector<const OriginFlowFact *> OriginFlowChain;
     };
 
     llvm::SmallVector<DFSNode> PendingStates;
@@ -239,10 +250,10 @@ class AnalysisImpl
 
       // Trace origins within the current block
       const auto [BuildResult, Complete] =
-          buildOriginFlowChain(CurrBlock, CurrOID, TargetLoan);
+          buildOriginFlowChainWithFacts(CurrBlock, CurrOID, TargetLoan);
       if (!BuildResult.empty()) {
         CurrNode.OriginFlowChain.append(BuildResult);
-        CurrOID = BuildResult.back();
+        CurrOID = BuildResult.back()->getSrcOriginID();
       }
 
       // If we found the IssueFact, we're done
@@ -266,11 +277,21 @@ class AnalysisImpl
   llvm::SmallVector<OriginID> buildOriginFlowChain(const UseFact *UF,
                                                    const LoanID TargetLoan,
                                                    const CFG *Cfg) const {
+    llvm::SmallVector<OriginID> OriginFlowChain;
+    for (const OriginFlowFact *Flow :
+         buildOriginFlowChainWithFacts(UF, TargetLoan, Cfg))
+      OriginFlowChain.push_back(Flow->getSrcOriginID());
+    return OriginFlowChain;
+  }
+
+  llvm::SmallVector<const OriginFlowFact *>
+  buildOriginFlowChainWithFacts(const UseFact *UF, const LoanID TargetLoan,
+                                const CFG *Cfg) const {
     for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
          Cur = Cur->peelOuterOrigin())
       if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan))
-        return buildOriginFlowChain(UF, Cur->getOuterOriginID(), TargetLoan,
-                                    Cfg);
+        return buildOriginFlowChainWithFacts(UF, Cur->getOuterOriginID(),
+                                             TargetLoan, Cfg);
 
     return {};
   }
@@ -308,8 +329,19 @@ class AnalysisImpl
   std::pair<llvm::SmallVector<OriginID>, bool>
   buildOriginFlowChain(const CFGBlock *Block, const OriginID StartOID,
                        const LoanID TargetLoan) const {
-    OriginID CurrOID = StartOID;
     llvm::SmallVector<OriginID> OriginFlowChain;
+    auto [FlowChain, Complete] =
+        buildOriginFlowChainWithFacts(Block, StartOID, TargetLoan);
+    for (const OriginFlowFact *Flow : FlowChain)
+      OriginFlowChain.push_back(Flow->getSrcOriginID());
+    return {OriginFlowChain, Complete};
+  }
+
+  std::pair<llvm::SmallVector<const OriginFlowFact *>, bool>
+  buildOriginFlowChainWithFacts(const CFGBlock *Block, const OriginID StartOID,
+                                const LoanID TargetLoan) const {
+    OriginID CurrOID = StartOID;
+    llvm::SmallVector<const OriginFlowFact *> OriginFlowChain;
 
     for (const Fact *F : llvm::reverse(FactMgr.getFacts(Block))) {
       if (const auto *IF = F->getAs<IssueFact>())
@@ -324,7 +356,7 @@ class AnalysisImpl
       if (!getLoans(SrcOriginID, OFF).contains(TargetLoan))
         continue;
 
-      OriginFlowChain.push_back(SrcOriginID);
+      OriginFlowChain.push_back(OFF);
       CurrOID = SrcOriginID;
     }
 
@@ -369,4 +401,20 @@ llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
     const UseFact *UF, const LoanID TargetLoan, const CFG *Cfg) const {
   return PImpl->buildOriginFlowChain(UF, TargetLoan, Cfg);
 }
+
+llvm::SmallVector<const OriginFlowFact *>
+LoanPropagationAnalysis::buildOriginFlowChainWithFacts(ProgramPoint StartPoint,
+                                                       const OriginID StartOID,
+                                                       const LoanID TargetLoan,
+                                                       const CFG *Cfg) const {
+  return PImpl->buildOriginFlowChainWithFacts(StartPoint, StartOID, TargetLoan,
+                                              Cfg);
+}
+
+llvm::SmallVector<const OriginFlowFact *>
+LoanPropagationAnalysis::buildOriginFlowChainWithFacts(const UseFact *UF,
+                                                       const LoanID TargetLoan,
+                                                       const CFG *Cfg) const {
+  return PImpl->buildOriginFlowChainWithFacts(UF, TargetLoan, Cfg);
+}
 } // namespace clang::lifetimes::internal
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 48edf5f3f070f..7b628669ed0cf 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -105,9 +105,10 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
 public:
   LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {}
 
-  void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
-                           const Expr *MovedExpr, SourceLocation FreeLoc,
-                           llvm::ArrayRef<const Expr *> ExprChain) override {
+  void
+  reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
+                      const Expr *MovedExpr, SourceLocation FreeLoc,
+                      llvm::ArrayRef<AliasChainEntry> AliasChain) override {
     unsigned DiagID = MovedExpr
                           ? diag::warn_lifetime_safety_use_after_scope_moved
                           : diag::warn_lifetime_safety_use_after_scope;
@@ -121,7 +122,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
     S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here)
         << DestroyedSubject;
 
-    reportAliasingChain(ExprChain);
+    reportAliasingChain(AliasChain);
 
     S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
         << UseExpr->getSourceRange();
@@ -664,20 +665,44 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
     return CurrExpr->getSourceRange() != LastExpr->getSourceRange();
   }
 
-  void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {
+  void reportAliasingChain(llvm::ArrayRef<AliasChainEntry> OriginExprChain) {
     if (OriginExprChain.empty())
       return;
 
-    const Expr *LastExpr = OriginExprChain.back();
+    AliasChainEntry Last = OriginExprChain.back();
+    const Expr *LastExpr = Last.E;
     std::string IssueStr = getDiagSubjectDescription(LastExpr);
 
-    for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
-      if (!shouldShowInAliasChain(CurrExpr, LastExpr))
+    for (AliasChainEntry Curr : reverse(OriginExprChain.drop_back())) {
+      const Expr *CurrExpr = Curr.E;
+      if (!shouldShowInAliasChain(CurrExpr, LastExpr)) {
+        if (!Last.LifetimeBound && Curr.LifetimeBound)
+          Last.LifetimeBound = Curr.LifetimeBound;
         continue;
-      S.Diag(CurrExpr->getBeginLoc(),
-             diag::note_lifetime_safety_aliases_storage)
-          << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr)
-          << IssueStr;
+      }
+      if (Last.LifetimeBound) {
+        std::string LifetimeBoundSubject;
+        if (Last.LifetimeBound->IsImplicitObject) {
+          LifetimeBoundSubject = "the implicit object parameter";
+        } else {
+          LifetimeBoundSubject = "parameter ";
+          if (Last.LifetimeBound->Param &&
+              Last.LifetimeBound->Param->getIdentifier())
+            LifetimeBoundSubject +=
+                "'" + Last.LifetimeBound->Param->getNameAsString() + "'";
+          else
+            LifetimeBoundSubject += "'<unnamed>'";
+        }
+        S.Diag(CurrExpr->getBeginLoc(),
+               diag::note_lifetime_safety_aliases_storage_lifetimebound)
+            << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr)
+            << IssueStr << LifetimeBoundSubject;
+      } else
+        S.Diag(CurrExpr->getBeginLoc(),
+               diag::note_lifetime_safety_aliases_storage)
+            << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr)
+            << IssueStr;
+      Last = Curr;
       LastExpr = CurrExpr;
     }
   }
diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
index 56ed4a6dc7106..f744e958c4d0b 100644
--- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
+++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
@@ -278,21 +278,21 @@ View return_view_field(const ViewProvider& v) {    // expected-warning {{paramet
 void test_get_on_temporary_pointer() {
   const ReturnsSelf* s_ref = &ReturnsSelf().get(); // expected-warning {{temporary object does not live long enough}}.
                                                    // expected-note at -1 {{temporary object is destroyed here}}
-                                                   // expected-note at -2 {{result of call to 'get' aliases the storage of temporary object}}
+                                                   // expected-note at -2 {{result of call to 'get' aliases the storage of temporary object because the implicit object parameter is marked 'lifetimebound'}}
   (void)s_ref;                                     // expected-note {{later used here}}
 }
 
 void test_get_on_temporary_ref() {
   const ReturnsSelf& s_ref = ReturnsSelf().get();  // expected-warning {{temporary object does not live long enough}}.
                                                    // expected-note at -1 {{temporary object is destroyed here}}
-                                                   // expected-note at -2 {{result of call to 'get' aliases the storage of temporary object}}
+                                                   // expected-note at -2 {{result of call to 'get' aliases the storage of temporary object because the implicit object parameter is marked 'lifetimebound'}}
   (void)s_ref;                                     // expected-note {{later used here}}
 }
 
 void test_getView_on_temporary() {
   View sv = ViewProvider{1}.getView();      // expected-warning {{temporary object does not live long enough}}.
                                             // expected-note at -1 {{temporary object is destroyed here}}
-                                            // expected-note at -2 {{result of call to 'getView' aliases the storage of temporary object}}
+                                            // expected-note at -2 {{result of call to 'getView' aliases the storage of temporary object because the implicit object parameter is marked 'lifetimebound'}}
   (void)sv;                                 // expected-note {{later used here}}
 }
 
@@ -603,7 +603,7 @@ void uaf_via_inferred_lifetimebound() {
   {
     int local;
     f = return_lambda_capturing_param(local); // expected-warning {{local variable 'local' does not live long enough}} \
-                                              // expected-note {{result of call to 'return_lambda_capturing_param' aliases the storage of local variable 'local'}}
+                                              // expected-note {{result of call to 'return_lambda_capturing_param' aliases the storage of local variable 'local' because parameter 'x' is marked 'lifetimebound'}}
   } // expected-note {{local variable 'local' is destroyed here}}
   (void)f; // expected-note {{later used here}}
 }
@@ -627,7 +627,7 @@ void test_inference() {
   {
     MyObj obj;
     ptr = create_target(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
-                              // expected-note {{result of call to 'create_target' aliases the storage of local variable 'obj'}}
+                              // expected-note {{result of call to 'create_target' aliases the storage of local variable 'obj' because parameter 'obj' is marked 'lifetimebound'}}
   } // expected-note {{local variable 'obj' is destroyed here}}
   (void)ptr; // expected-note {{later used here}}
 }
@@ -642,7 +642,7 @@ View* MakeView(const MyObj& in) { // expected-warning {{parameter in intra-TU fu
 void test_new_allocation() {
   View* v = MakeView(MyObj{}); // expected-warning {{temporary object does not live long enough}} \
                                // expected-note {{temporary object is destroyed here}} \
-                               // expected-note {{result of call to 'MakeView' aliases the storage of temporary object}}
+                               // expected-note {{result of call to 'MakeView' aliases the storage of temporary object because parameter 'in' is marked 'lifetimebound'}}
   (void)v;                     // expected-note {{later used here}}
 }
 
diff --git a/clang/test/Sema/LifetimeSafety/nocfg.cpp b/clang/test/Sema/LifetimeSafety/nocfg.cpp
index b9a6150daf712..d7f3b52460fe9 100644
--- a/clang/test/Sema/LifetimeSafety/nocfg.cpp
+++ b/clang/test/Sema/LifetimeSafety/nocfg.cpp
@@ -621,7 +621,7 @@ std::string_view ReturnStringView(std::string_view abc [[clang::lifetimebound]])
 void test() {
   std::string_view svjkk1 = ReturnStringView(StrCat("bar", "x")); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \
                                                                   // cfg-warning {{temporary object does not live long enough}} cfg-note {{destroyed here}} \
-                                                                  // cfg-note {{result of call to 'ReturnStringView' aliases the storage of temporary object}}
+                                                                  // cfg-note {{result of call to 'ReturnStringView' aliases the storage of temporary object because parameter 'abc' is marked 'lifetimebound'}}
   use(svjkk1);                                                    // cfg-note {{later used here}}
 }
 } // namespace GH100549
@@ -872,11 +872,11 @@ std::string_view TakeStr(std::string abc [[clang::lifetimebound]]);
 std::string_view test1_1() {
   std::string_view t1 = Ref(std::string()); // expected-warning {{object backing}} \
                                             // cfg-warning {{temporary object does not live long enough}} cfg-note {{destroyed here}} \
-                                            // cfg-note {{result of call to 'Ref' aliases the storage of temporary object}}
+                                            // cfg-note {{result of call to 'Ref' aliases the storage of temporary object because parameter 'abc' is marked 'lifetimebound'}}
   use(t1);                                  // cfg-note {{later used here}}
   t1 = Ref(std::string()); // expected-warning {{object backing}} \
                            // cfg-warning {{temporary object does not live long enough}} cfg-note {{destroyed here}} \
-                           // cfg-note {{result of call to 'Ref' aliases the storage of temporary object}}
+                           // cfg-note {{result of call to 'Ref' aliases the storage of temporary object because parameter 'abc' is marked 'lifetimebound'}}
   use(t1);                 // cfg-note {{later used here}}
   return Ref(std::string()); // expected-warning {{returning address}} \
                              // cfg-warning {{stack memory associated with temporary object is returned}} cfg-note {{returned here}}
@@ -885,11 +885,11 @@ std::string_view test1_1() {
 std::string_view test1_2() {
   std::string_view t2 = TakeSv(std::string()); // expected-warning {{object backing}} \
                                             // cfg-warning {{temporary object does not live long enough}} cfg-note {{destroyed here}} \
-                                            // cfg-note {{result of call to 'TakeSv' aliases the storage of temporary object}}
+                                            // cfg-note {{result of call to 'TakeSv' aliases the storage of temporary object because parameter 'abc' is marked 'lifetimebound'}}
   use(t2);                                  // cfg-note {{later used here}}
   t2 = TakeSv(std::string()); // expected-warning {{object backing}} \
                               // cfg-warning {{temporary object does not live long enough}} cfg-note {{destroyed here}} \
-                              // cfg-note {{result of call to 'TakeSv' aliases the storage of temporary object}}
+                              // cfg-note {{result of call to 'TakeSv' aliases the storage of temporary object because parameter 'abc' is marked 'lifetimebound'}}
   use(t2);                    // cfg-note {{later used here}}
 
   return TakeSv(std::string()); // expected-warning {{returning address}} \
@@ -899,11 +899,11 @@ std::string_view test1_2() {
 std::string_view test1_3() {
   std::string_view t3 = TakeStrRef(std::string()); // expected-warning {{temporary}} \
                                                    // cfg-warning {{temporary object does not live long enough}} cfg-note {{destroyed here}} \
-                                                   // cfg-note {{result of call to 'TakeStrRef' aliases the storage of temporary object}}
+                                                   // cfg-note {{result of call to 'TakeStrRef' aliases the storage of temporary object because parameter 'abc' is marked 'lifetimebound'}}
   use(t3);                                         // cfg-note {{later used here}}
   t3 = TakeStrRef(std::string()); // expected-warning {{object backing}} \
                                   // cfg-warning {{temporary object does not live long enough}} cfg-note {{destroyed here}} \
-                                  // cfg-note {{result of call to 'TakeStrRef' aliases the storage of temporary object}}
+                                  // cfg-note {{result of call to 'TakeStrRef' aliases the storage of temporary object because parameter 'abc' is marked 'lifetimebound'}}
   use(t3);                        // cfg-note {{later used here}}
   return TakeStrRef(std::string()); // expected-warning {{returning address}} \
                                     // cfg-warning {{stack memory associated with temporary object is returned}} cfg-note {{returned here}}
@@ -926,11 +926,11 @@ struct Foo {
 std::string_view test2_1(Foo<std::string> r1, Foo<std::string_view> r2) {
   std::string_view t1 = Foo<std::string>().get(); // expected-warning {{object backing}} \
                                                   // cfg-warning {{temporary object does not live long enough}} cfg-note {{destroyed here}} \
-                                                  // cfg-note {{result of call to 'get' aliases the storage of temporary object}}
+                                                  // cfg-note {{result of call to 'get' aliases the storage of temporary object because the implicit object parameter is marked 'lifetimebound'}}
   use(t1);                                        // cfg-note {{later used here}}
   t1 = Foo<std::string>().get(); // expected-warning {{object backing}} \
                                  // cfg-warning {{temporary object does not live long enough}} cfg-note {{destroyed here}} \
-                                 // cfg-note {{result of call to 'get' aliases the storage of temporary object}}
+                                 // cfg-note {{result of call to 'get' aliases the storage of temporary object because the implicit object parameter is marked 'lifetimebound'}}
   use(t1);                       // cfg-note {{later used here}}
   return r1.get(); // expected-warning {{address of stack}} \
                    // cfg-warning {{stack memory associated with parameter 'r1' is returned}} cfg-note {{returned here}}
@@ -1136,19 +1136,19 @@ void test1() {
   std::string_view k3 = Q().get()->sv; // OK
   std::string_view k4  = Q().get()->s; // expected-warning {{object backing the pointer will}} \
                                        // cfg-warning {{temporary object does not live long enough}} cfg-note {{destroyed here}} \
-                                       // cfg-note {{result of call to 'get' aliases the storage of temporary object}} \
+                                       // cfg-note {{result of call to 'get' aliases the storage of temporary object because the implicit object parameter is marked 'lifetimebound'}} \
                                        // cfg-note {{expression aliases the storage of temporary object}}
 
 
   std::string_view lb1 = foo(S().s); // expected-warning {{object backing the pointer will}} \
                                      // cfg-warning {{temporary object does not live long enough}} cfg-note {{destroyed here}} \
                                      // cfg-note {{expression aliases the storage of temporary object}} \
-                                     // cfg-note {{result of call to 'foo' aliases the storage of temporary object}}
+                                     // cfg-note {{result of call to 'foo' aliases the storage of temporary object because parameter 'sv' is marked 'lifetimebound'}}
   std::string_view lb2 = foo(Q().get()->s); // expected-warning {{object backing the pointer will}} \
                                             // cfg-warning {{temporary object does not live long enough}} cfg-note {{destroyed here}} \
-                                            // cfg-note {{result of call to 'get' aliases the storage of temporary object}} \
+                                            // cfg-note {{result of call to 'get' aliases the storage of temporary object because the implicit object parameter is marked 'lifetimebound'}} \
                                             // cfg-note {{expression aliases the storage of temporary object}} \
-                                            // cfg-note {{result of call to 'foo' aliases the storage of temporary object}}
+                                            // cfg-note {{result of call to 'foo' aliases the storage of temporary object because parameter 'sv' is marked 'lifetimebound'}}
 
   use(k1, k2, k3, k4, lb1, lb2);  // cfg-note 4 {{later used here}}
 }
diff --git a/clang/test/Sema/LifetimeSafety/safety-c.c b/clang/test/Sema/LifetimeSafety/safety-c.c
index 42adbbb3f0628..a5a98170a2c9e 100644
--- a/clang/test/Sema/LifetimeSafety/safety-c.c
+++ b/clang/test/Sema/LifetimeSafety/safety-c.c
@@ -63,7 +63,7 @@ void lifetimebound_call(void) {
   {
     int i;
     p = identity(&i); // expected-warning {{local variable 'i' does not live long enough}} \
-                      // expected-note {{result of call to 'identity' aliases the storage of local variable 'i'}}
+                      // expected-note {{result of call to 'identity' aliases the storage of local variable 'i' because parameter 'p' is marked 'lifetimebound'}}
   }                   // expected-note {{local variable 'i' is destroyed here}}
   (void)*p;           // expected-note {{later used here}}
 }
@@ -94,8 +94,8 @@ void conditional_operator_lifetimebound(int cond) {
   {
     int a, b;
     p = identity(cond ? &a    // expected-warning {{local variable 'a' does not live long enough}} \
-                              // expected-note {{result of call to 'identity' aliases the storage of local variable 'a'}} \
-                              // expected-note {{result of call to 'identity' aliases the storage of local variable 'b'}}
+                              // expected-note {{result of call to 'identity' aliases the storage of local variable 'a' because parameter 'p' is marked 'lifetimebound'}} \
+                              // expected-note {{result of call to 'identity' aliases the storage of local variable 'b' because parameter 'p' is marked 'lifetimebound'}}
                       : &b);  // expected-warning {{local variable 'b' does not live long enough}}
   }                           // expected-note {{local variable 'a' is destroyed here}} \
                               // expected-note {{local variable 'b' is destroyed here}}
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index 3c9b58c4e4957..3b626cb234796 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -697,7 +697,7 @@ void test_lifetimebound_multi_level() {
     int** pp = &p;  
     int*** ppp = &pp; // expected-warning {{local variable 'pp' does not live long enough}}
     result = return_inner_ptr_addr(ppp); // expected-note {{local variable 'ppp' aliases the storage of local variable 'pp'}} \
-                                         // expected-note {{result of call to 'return_inner_ptr_addr' aliases the storage of local variable 'pp'}}
+                                         // expected-note {{result of call to 'return_inner_ptr_addr' aliases the storage of local variable 'pp' because parameter 'ppp' is marked 'lifetimebound'}}
   }                   // expected-note {{local variable 'pp' is destroyed here}}
   (void)**result;     // expected-note {{used here}}
 }
@@ -821,7 +821,7 @@ void lifetimebound_simple_function() {
   {
     MyObj obj;
     v = Identity(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
-                       // expected-note {{result of call to 'Identity' aliases the storage of local variable 'obj'}}
+                       // expected-note {{result of call to 'Identity' aliases the storage of local variable 'obj' because parameter 'v' is marked 'lifetimebound'}}
   }                    // expected-note {{local variable 'obj' is destroyed here}}
   v.use();             // expected-note {{later used here}}
 }
@@ -830,8 +830,8 @@ void lifetimebound_multiple_args_definite() {
   View v;
   {
     MyObj obj1, obj2;
-    v = Choose(true,  // expected-note {{result of call to 'Choose' aliases the storage of local variable 'obj1'}} \
-                      // expected-note {{result of call to 'Choose' aliases the storage of local variable 'obj2'}}
+    v = Choose(true,  // expected-note {{result of call to 'Choose' aliases the storage of local variable 'obj1' because parameter 'a' is marked 'lifetimebound'}} \
+                      // expected-note {{result of call to 'Choose' aliases the storage of local variable 'obj2' because parameter 'b' is marked 'lifetimebound'}}
                obj1,  // expected-warning {{local variable 'obj1' does not live long enough}}
                obj2); // expected-warning {{local variable 'obj2' does not live long enough}}
   }                              // expected-note {{local variable 'obj1' is destroyed here}} \
@@ -846,8 +846,8 @@ void lifetimebound_multiple_args_potential(bool cond) {
     MyObj obj1;
     if (cond) {
       MyObj obj2;
-      v = Choose(true,             // expected-note {{result of call to 'Choose' aliases the storage of local variable 'obj1'}} \
-                                   // expected-note {{result of call to 'Choose' aliases the storage of local variable 'obj2'}}
+      v = Choose(true,             // expected-note {{result of call to 'Choose' aliases the storage of local variable 'obj1' because parameter 'a' is marked 'lifetimebound'}} \
+                                   // expected-note {{result of call to 'Choose' aliases the storage of local variable 'obj2' because parameter 'b' is marked 'lifetimebound'}}
                  obj1,             // expected-warning {{local variable 'obj1' does not live long enough}}
                  obj2);            // expected-warning {{local variable 'obj2' does not live long enough}}
     }                              // expected-note {{local variable 'obj2' is destroyed here}}
@@ -873,7 +873,7 @@ void lifetimebound_mixed_args() {
   {
     MyObj obj1, obj2;
     v = SelectFirst(obj1,        // expected-warning {{local variable 'obj1' does not live long enough}} \
-                                 // expected-note {{result of call to 'SelectFirst' aliases the storage of local variable 'obj1'}}
+                                 // expected-note {{result of call to 'SelectFirst' aliases the storage of local variable 'obj1' because parameter 'a' is marked 'lifetimebound'}}
                     obj2);
   }                              // expected-note {{local variable 'obj1' is destroyed here}}
   v.use();                       // expected-note {{later used here}}
@@ -890,7 +890,7 @@ void lifetimebound_member_function() {
   {
     MyObj obj;
     v  = obj.getView(); // expected-warning {{local variable 'obj' does not live long enough}} \
-                        // expected-note {{result of call to 'getView' aliases the storage of local variable 'obj'}}
+                        // expected-note {{result of call to 'getView' aliases the storage of local variable 'obj' because the implicit object parameter is marked 'lifetimebound'}}
   }                     // expected-note {{local variable 'obj' is destroyed here}}
   v.use();              // expected-note {{later used here}}
 }
@@ -915,7 +915,7 @@ void lifetimebound_chained_calls() {
   {
     MyObj obj;
     v = Identity(Identity(Identity(obj))); // expected-warning {{local variable 'obj' does not live long enough}} \
-                                           // expected-note 3 {{result of call to 'Identity' aliases the storage of local variable 'obj'}}
+                                           // expected-note 3 {{result of call to 'Identity' aliases the storage of local variable 'obj' because parameter 'v' is marked 'lifetimebound'}}
   }                                        // expected-note {{local variable 'obj' is destroyed here}}
   v.use();                                 // expected-note {{later used here}}
 }
@@ -925,7 +925,7 @@ void lifetimebound_with_pointers() {
   {
     MyObj obj;
     ptr = GetPointer(obj); // expected-warning {{local variable 'obj' does not live long enough}} \
-                           // expected-note {{result of call to 'GetPointer' aliases the storage of local variable 'obj'}}
+                           // expected-note {{result of call to 'GetPointer' aliases the storage of local variable 'obj' because parameter 'obj' is marked 'lifetimebound'}}
   }                        // expected-note {{local variable 'obj' is destroyed here}}
   (void)*ptr;              // expected-note {{later used here}}
 }
@@ -935,7 +935,7 @@ void chained_assignment_lifetimebound_call() {
   {
     MyObj s;
     p = Identity(obj = &s); // expected-warning {{does not live long enough}} \
-                            // expected-note {{result of call to 'Identity' aliases the storage of local variable 's'}}
+                            // expected-note {{result of call to 'Identity' aliases the storage of local variable 's' because parameter 'v' is marked 'lifetimebound'}}
   }                         // expected-note {{local variable 's' is destroyed here}}
   (void)*p;                 // expected-note {{later used here}}
 }
@@ -953,7 +953,7 @@ void lifetimebound_partial_safety(bool cond) {
   
   if (cond) {
     MyObj temp_obj;
-    v = Choose(true,      // expected-note {{result of call to 'Choose' aliases the storage of local variable 'temp_obj'}}
+    v = Choose(true,      // expected-note {{result of call to 'Choose' aliases the storage of local variable 'temp_obj' because parameter 'b' is marked 'lifetimebound'}}
                safe_obj,
                temp_obj); // expected-warning {{local variable 'temp_obj' does not live long enough}}
   }                       // expected-note {{local variable 'temp_obj' is destroyed here}}
@@ -968,7 +968,7 @@ void lifetimebound_return_reference() {
     MyObj obj;
     View temp_v = obj;  // expected-warning {{local variable 'obj' does not live long enough}}
     const MyObj& ref = GetObject(temp_v); // expected-note {{local variable 'temp_v' aliases the storage of local variable 'obj'}} \
-                                          // expected-note {{result of call to 'GetObject' aliases the storage of local variable 'obj'}}
+                                          // expected-note {{result of call to 'GetObject' aliases the storage of local variable 'obj' because parameter 'v' is marked 'lifetimebound'}}
     ptr = &ref;
   }                       // expected-note {{local variable 'obj' is destroyed here}}
   (void)*ptr;             // expected-note {{later used here}}
@@ -1023,7 +1023,7 @@ void lifetimebound_make_unique() {
   {
     MyObj obj;
     ptr = std::make_unique<LifetimeBoundCtor>(obj); // tu-warning {{local variable 'obj' does not live long enough}} \
-                                                    // tu-note {{result of call to 'make_unique<LifetimeBoundCtor, MyObj &>' aliases the storage of local variable 'obj'}}
+                                                    // tu-note {{result of call to 'make_unique<LifetimeBoundCtor, MyObj &>' aliases the storage of local variable 'obj' because parameter 'args' is marked 'lifetimebound'}}
   }                                                 // tu-note {{local variable 'obj' is destroyed here}}
   (void)ptr;                                        // tu-note {{later used here}}
 }
@@ -1041,7 +1041,7 @@ void non_lifetimebound_make_unique() {
 void lifetimebound_make_unique_temp() {
   std::unique_ptr<LifetimeBoundCtor> ptr = std::make_unique<LifetimeBoundCtor>(MyObj()); // tu-warning {{temporary object does not live long enough}} \
                                                                                          // tu-note {{temporary object is destroyed here}} \
-                                                                                         // tu-note {{result of call to 'make_unique<LifetimeBoundCtor, MyObj>' aliases the storage of temporary object}}
+                                                                                         // tu-note {{result of call to 'make_unique<LifetimeBoundCtor, MyObj>' aliases the storage of temporary object because parameter 'args' is marked 'lifetimebound'}}
   (void)ptr; // tu-note {{later used here}}
 }
 
@@ -1079,7 +1079,7 @@ void lifetimebound_make_unique_multi_params() {
   {
     MyObj obj_short;
     ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long); // tu-warning {{local variable 'obj_short' does not live long enough}} \
-                                                                         // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &>' aliases the storage of local variable 'obj_short'}}
+                                                                         // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &>' aliases the storage of local variable 'obj_short' because parameter 'args' is marked 'lifetimebound'}}
   } // tu-note {{local variable 'obj_short' is destroyed here}}
   (void)ptr; // tu-note {{later used here}}
 }
@@ -1090,7 +1090,7 @@ void lifetimebound_make_unique_multi_params2() {
   {
     MyObj obj_short;
     ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1); // tu-warning {{local variable 'obj_short' does not live long enough}} \
-                                                                            // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &, int>' aliases the storage of local variable 'obj_short'}}
+                                                                            // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &, int>' aliases the storage of local variable 'obj_short' because parameter 'args' is marked 'lifetimebound'}}
   } // tu-note {{local variable 'obj_short' is destroyed here}}
   (void)ptr; // tu-note {{later used here}}
 }
@@ -1111,7 +1111,7 @@ void lifetimebound_make_unique_multi_params3_1() {
   {
     MyObj obj_short;
     ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_short, obj_long, 1.0); // tu-warning {{local variable 'obj_short' does not live long enough}} \
-                                                                              // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &, double>' aliases the storage of local variable 'obj_short'}}
+                                                                              // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &, double>' aliases the storage of local variable 'obj_short' because parameter 'args' is marked 'lifetimebound'}}
   } // tu-note {{local variable 'obj_short' is destroyed here}}
   (void)ptr; // tu-note {{later used here}}
 }
@@ -1122,7 +1122,7 @@ void lifetimebound_make_unique_multi_params3_2() {
   {
     MyObj obj_short;
     ptr = std::make_unique<MultiLifetimeBoundCtor>(obj_long, obj_short, 1.0); // tu-warning {{local variable 'obj_short' does not live long enough}} \
-                                                                              // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &, double>' aliases the storage of local variable 'obj_short'}}
+                                                                              // tu-note {{result of call to 'make_unique<MultiLifetimeBoundCtor, MyObj &, MyObj &, double>' aliases the storage of local variable 'obj_short' because parameter 'args' is marked 'lifetimebound'}}
   } // tu-note {{local variable 'obj_short' is destroyed here}}
   (void)ptr; // tu-note {{later used here}}
 }
@@ -1237,8 +1237,8 @@ void conditional_operator_lifetimebound(bool cond) {
   {
     MyObj a, b;
     p = Identity(cond ? &a    // expected-warning {{local variable 'a' does not live long enough}} \
-                              // expected-note {{result of call to 'Identity' aliases the storage of local variable 'a'}} \
-                              // expected-note {{result of call to 'Identity' aliases the storage of local variable 'b'}}
+                              // expected-note {{result of call to 'Identity' aliases the storage of local variable 'a' because parameter 'v' is marked 'lifetimebound'}} \
+                              // expected-note {{result of call to 'Identity' aliases the storage of local variable 'b' because parameter 'v' is marked 'lifetimebound'}}
                       : &b);  // expected-warning {{local variable 'b' does not live long enough}}
   }  // expected-note {{local variable 'b' is destroyed here}} expected-note {{local variable 'a' is destroyed here}}
   (void)*p;  // expected-note 2 {{later used here}}
@@ -1249,10 +1249,10 @@ void conditional_operator_lifetimebound_nested(bool cond) {
   {
     MyObj a, b;
     p = Identity(cond ? Identity(&a)    // expected-warning {{local variable 'a' does not live long enough}} \
-                                        // expected-note 2 {{result of call to 'Identity' aliases the storage of local variable 'a'}} \
-                                        // expected-note {{result of call to 'Identity' aliases the storage of local variable 'b'}}
+                                        // expected-note 2 {{result of call to 'Identity' aliases the storage of local variable 'a' because parameter 'v' is marked 'lifetimebound'}} \
+                                        // expected-note {{result of call to 'Identity' aliases the storage of local variable 'b' because parameter 'v' is marked 'lifetimebound'}}
                       : Identity(&b));  // expected-warning {{local variable 'b' does not live long enough}} \
-                                        // expected-note {{result of call to 'Identity' aliases the storage of local variable 'b'}}
+                                        // expected-note {{result of call to 'Identity' aliases the storage of local variable 'b' because parameter 'v' is marked 'lifetimebound'}}
   }  // expected-note {{local variable 'b' is destroyed here}} expected-note {{local variable 'a' is destroyed here}}
   (void)*p;  // expected-note 2 {{later used here}}
 }
@@ -1262,14 +1262,14 @@ void conditional_operator_lifetimebound_nested_deep(bool cond) {
   {
     MyObj a, b, c, d;
     p = Identity(cond ? Identity(cond ? &a     // expected-warning {{local variable 'a' does not live long enough}} \
-                                               // expected-note 2 {{result of call to 'Identity' aliases the storage of local variable 'a'}} \
-                                               // expected-note 2 {{result of call to 'Identity' aliases the storage of local variable 'b'}} \
-                                               // expected-note {{result of call to 'Identity' aliases the storage of local variable 'c'}} \
-                                               // expected-note {{result of call to 'Identity' aliases the storage of local variable 'd'}}
+                                               // expected-note 2 {{result of call to 'Identity' aliases the storage of local variable 'a' because parameter 'v' is marked 'lifetimebound'}} \
+                                               // expected-note 2 {{result of call to 'Identity' aliases the storage of local variable 'b' because parameter 'v' is marked 'lifetimebound'}} \
+                                               // expected-note {{result of call to 'Identity' aliases the storage of local variable 'c' because parameter 'v' is marked 'lifetimebound'}} \
+                                               // expected-note {{result of call to 'Identity' aliases the storage of local variable 'd' because parameter 'v' is marked 'lifetimebound'}}
                                       : &b)    // expected-warning {{local variable 'b' does not live long enough}}
                       : Identity(cond ? &c     // expected-warning {{local variable 'c' does not live long enough}} \
-                                               // expected-note {{result of call to 'Identity' aliases the storage of local variable 'c'}} \
-                                               // expected-note {{result of call to 'Identity' aliases the storage of local variable 'd'}}
+                                               // expected-note {{result of call to 'Identity' aliases the storage of local variable 'c' because parameter 'v' is marked 'lifetimebound'}} \
+                                               // expected-note {{result of call to 'Identity' aliases the storage of local variable 'd' because parameter 'v' is marked 'lifetimebound'}}
                                       : &d));  // expected-warning {{local variable 'd' does not live long enough}}
   }  // expected-note {{local variable 'a' is destroyed here}} expected-note {{local variable 'd' is destroyed here}} expected-note {{local variable 'b' is destroyed here}} expected-note {{local variable 'c' is destroyed here}}
   (void)*p;  // expected-note 4 {{later used here}}
@@ -1464,7 +1464,7 @@ void parentheses(bool cond) {
   {
     MyObj a;
     p = ((GetPointer((a))));  // expected-warning {{local variable 'a' does not live long enough}} \
-                              // expected-note {{result of call to 'GetPointer' aliases the storage of local variable 'a'}}
+                              // expected-note {{result of call to 'GetPointer' aliases the storage of local variable 'a' because parameter 'obj' is marked 'lifetimebound'}}
   }                           // expected-note {{local variable 'a' is destroyed here}}
   (void)*p;                   // expected-note {{later used here}}
 
@@ -1496,7 +1496,7 @@ void use_temporary_after_destruction() {
 void passing_temporary_to_lifetime_bound_function() {
   View a = construct_view(non_trivially_destructed_temporary()); // expected-warning {{temporary object does not live long enough}} \
                 expected-note {{temporary object is destroyed here}} \
-                expected-note {{result of call to 'construct_view' aliases the storage of temporary object}}
+                expected-note {{result of call to 'construct_view' aliases the storage of temporary object because parameter 'obj' is marked 'lifetimebound'}}
   use(a); // expected-note {{later used here}}
 }
 
@@ -1538,7 +1538,7 @@ int **bit_cast_multilevel() {
 namespace FullExprCleanupLoc {
 void var_initializer() {
   View v = non_trivially_destructed_temporary() // expected-warning {{temporary object does not live long enough}} \
-                                                // expected-note {{result of call to 'getView' aliases the storage of temporary object}}
+                                                // expected-note {{result of call to 'getView' aliases the storage of temporary object because the implicit object parameter is marked 'lifetimebound'}}
                .getView(); // expected-note {{temporary object is destroyed here}}
   v.use(); // expected-note {{later used here}}
 }
@@ -1546,7 +1546,7 @@ void var_initializer() {
 void expr_statement() {
   View v;
   v = non_trivially_destructed_temporary() // expected-warning {{temporary object does not live long enough}} \
-                                           // expected-note {{result of call to 'getView' aliases the storage of temporary object}}
+                                           // expected-note {{result of call to 'getView' aliases the storage of temporary object because the implicit object parameter is marked 'lifetimebound'}}
           .getView(); // expected-note {{temporary object is destroyed here}}
   v.use(); // expected-note {{later used here}}
 }
@@ -1590,7 +1590,7 @@ void foobar() {
   {
     StatusOr<MyObj> string_or = getStringOr();
     view = string_or. // expected-warning {{local variable 'string_or' does not live long enough}} \
-                      // expected-note {{result of call to 'value' aliases the storage of local variable 'string_or'}}
+                      // expected-note {{result of call to 'value' aliases the storage of local variable 'string_or' because the implicit object parameter is marked 'lifetimebound'}}
             value();
   }                     // expected-note {{local variable 'string_or' is destroyed here}}
   (void)view;           // expected-note {{later used here}}
@@ -1666,7 +1666,7 @@ void test_user_defined_deref_uaf() {
     MyObj obj;
     SmartPtr<MyObj> smart_ptr(&obj);
     p = &(*smart_ptr);  // expected-warning {{local variable 'smart_ptr' does not live long enough}} \
-                        // expected-note {{expression aliases the storage of local variable 'smart_ptr'}}
+                        // expected-note {{expression aliases the storage of local variable 'smart_ptr' because the implicit object parameter is marked 'lifetimebound'}}
   }                     // expected-note {{local variable 'smart_ptr' is destroyed here}}
   (void)*p;             // expected-note {{later used here}}
 }
@@ -1684,7 +1684,7 @@ void test_user_defined_deref_with_view() {
     MyObj obj;
     SmartPtr<MyObj> smart_ptr(&obj);
     v = *smart_ptr;  // expected-warning {{local variable 'smart_ptr' does not live long enough}} \
-                     // expected-note {{expression aliases the storage of local variable 'smart_ptr'}}
+                     // expected-note {{expression aliases the storage of local variable 'smart_ptr' because the implicit object parameter is marked 'lifetimebound'}}
   }                  // expected-note {{local variable 'smart_ptr' is destroyed here}}
   v.use();           // expected-note {{later used here}}
 }
@@ -1695,7 +1695,7 @@ void test_user_defined_deref_arrow() {
     MyObj obj;
     SmartPtr<MyObj> smart_ptr(&obj);
     p = smart_ptr.operator->();  // expected-warning {{local variable 'smart_ptr' does not live long enough}} \
-                                 // expected-note {{expression aliases the storage of local variable 'smart_ptr'}}
+                                 // expected-note {{expression aliases the storage of local variable 'smart_ptr' because the implicit object parameter is marked 'lifetimebound'}}
   }                              // expected-note {{local variable 'smart_ptr' is destroyed here}}
   (void)*p;                      // expected-note {{later used here}}
 }
@@ -1706,7 +1706,7 @@ void test_user_defined_deref_chained() {
     MyObj obj;
     SmartPtr<SmartPtr<MyObj>> double_ptr;
     p = &(**double_ptr);  // expected-warning {{local variable 'double_ptr' does not live long enough}} \
-                          // expected-note 2 {{expression aliases the storage of local variable 'double_ptr'}}
+                          // expected-note 2 {{expression aliases the storage of local variable 'double_ptr' because the implicit object parameter is marked 'lifetimebound'}}
   }                       // expected-note {{local variable 'double_ptr' is destroyed here}}
   (void)*p;               // expected-note {{later used here}}
 }
@@ -1901,7 +1901,7 @@ void wrong_use_of_move_is_permissive() {
     MyObj a;
     p = std::move(a).getData(); // expected-warning {{local variable 'a' does not live long enough}} \
                                 // expected-note {{result of call to 'move<MyObj &>' aliases the storage of local variable 'a'}} \
-                                // expected-note {{result of call to 'getData' aliases the storage of local variable 'a'}}
+                                // expected-note {{result of call to 'getData' aliases the storage of local variable 'a' because the implicit object parameter is marked 'lifetimebound'}}
   }         // expected-note {{local variable 'a' is destroyed here}}
   (void)p;  // expected-note {{later used here}}
 }
@@ -1936,10 +1936,10 @@ void bar() {
     {
         S s;
         x = s.x(); // expected-warning {{local variable 's' does not live long enough}} \
-                   // expected-note {{result of call to 'x' aliases the storage of local variable 's'}}
+                   // expected-note {{result of call to 'x' aliases the storage of local variable 's' because the implicit object parameter is marked 'lifetimebound'}}
         View y = S().x(); // expected-warning {{temporary object does not live long enough}} \
                              expected-note {{temporary object is destroyed here}} \
-                             expected-note {{result of call to 'x' aliases the storage of temporary object}}
+                             expected-note {{result of call to 'x' aliases the storage of temporary object because the implicit object parameter is marked 'lifetimebound'}}
         (void)y; // expected-note {{used here}}
     } // expected-note {{local variable 's' is destroyed here}}
     (void)x; // expected-note {{used here}}
@@ -2028,19 +2028,19 @@ const S& identity(const S& in [[clang::lifetimebound]]);
 
 void test_temporary() {
   const std::string& x = S().x(); // expected-warning {{temporary object does not live long enough}} expected-note {{temporary object is destroyed here}} \
-                                  // expected-note {{result of call to 'x' aliases the storage of temporary object}}
+                                  // expected-note {{result of call to 'x' aliases the storage of temporary object because the implicit object parameter is marked 'lifetimebound'}}
   (void)x; // expected-note {{later used here}}
 
   const std::string& y = identity(S().x()); // expected-warning {{temporary object does not live long enough}} expected-note {{temporary object is destroyed here}} \
-                                            // expected-note {{result of call to 'x' aliases the storage of temporary object}} \
-                                            // expected-note {{result of call to 'identity' aliases the storage of temporary object}}
+                                            // expected-note {{result of call to 'x' aliases the storage of temporary object because the implicit object parameter is marked 'lifetimebound'}} \
+                                            // expected-note {{result of call to 'identity' aliases the storage of temporary object because parameter 'in' is marked 'lifetimebound'}}
   (void)y; // expected-note {{later used here}}
 
   std::string_view z;
   {
     S s;
     const std::string& zz = s.x(); // expected-warning {{local variable 's' does not live long enough}} \
-                                   // expected-note {{result of call to 'x' aliases the storage of local variable 's'}}
+                                   // expected-note {{result of call to 'x' aliases the storage of local variable 's' because the implicit object parameter is marked 'lifetimebound'}}
     z = zz;                        // expected-note {{expression aliases the storage of local variable 's'}}
   } // expected-note {{local variable 's' is destroyed here}}
   (void)z; // expected-note {{later used here}}
@@ -2050,13 +2050,13 @@ void test_lifetime_extension_ok() {
   const S& x = S();
   (void)x;
   const S& y = identity(S()); // expected-warning {{temporary object does not live long enough}} expected-note {{temporary object is destroyed here}} \
-                              // expected-note {{result of call to 'identity' aliases the storage of temporary object}}
+                              // expected-note {{result of call to 'identity' aliases the storage of temporary object because parameter 'in' is marked 'lifetimebound'}}
   (void)y; // expected-note {{later used here}}
 }
 
 const std::string& test_return() {
   const std::string& x = S().x(); // expected-warning {{temporary object does not live long enough}} expected-note {{temporary object is destroyed here}} \
-                                  // expected-note {{result of call to 'x' aliases the storage of temporary object}}
+                                  // expected-note {{result of call to 'x' aliases the storage of temporary object because the implicit object parameter is marked 'lifetimebound'}}
   return x; // expected-note {{later used here}}
 }
 } // namespace reference_type_decl_ref_expr
@@ -2237,11 +2237,11 @@ const T* MemberFuncsTpl<T>::memberC(const T& x [[clang::lifetimebound]]) {
 void test() {
   MemberFuncsTpl<MyObj> mtf;
   const MyObj* pTMA = mtf.memberA(MyObj()); // expected-warning {{temporary object does not live long enough}} // expected-note {{temporary object is destroyed here}} \
-                                            // expected-note {{result of call to 'memberA' aliases the storage of temporary object}}
+                                            // expected-note {{result of call to 'memberA' aliases the storage of temporary object because parameter 'x' is marked 'lifetimebound'}}
   const MyObj* pTMB = mtf.memberB(MyObj()); // tu-warning {{temporary object does not live long enough}} // tu-note {{temporary object is destroyed here}} \
-                                            // tu-note {{result of call to 'memberB' aliases the storage of temporary object}}
+                                            // tu-note {{result of call to 'memberB' aliases the storage of temporary object because parameter 'x' is marked 'lifetimebound'}}
   const MyObj* pTMC = mtf.memberC(MyObj()); // expected-warning {{temporary object does not live long enough}} // expected-note {{temporary object is destroyed here}} \
-                                            // expected-note {{result of call to 'memberC' aliases the storage of temporary object}}
+                                            // expected-note {{result of call to 'memberC' aliases the storage of temporary object because parameter 'x' is marked 'lifetimebound'}}
   (void)pTMA; // expected-note {{later used here}}
   (void)pTMB; // tu-note {{later used here}}
   (void)pTMC; // expected-note {{later used here}}
@@ -2289,7 +2289,7 @@ void test_optional_arrow_lifetimebound() {
     std::optional<MyObj> opt;
     v = opt->getView();  // expected-warning {{local variable 'opt' does not live long enough}} \
                          // expected-note {{expression aliases the storage of local variable 'opt'}} \
-                         // expected-note {{result of call to 'getView' aliases the storage of local variable 'opt'}}
+                         // expected-note {{result of call to 'getView' aliases the storage of local variable 'opt' because the implicit object parameter is marked 'lifetimebound'}}
   }                      // expected-note {{local variable 'opt' is destroyed here}}
   v.use();               // expected-note {{later used here}}
 }
@@ -2690,7 +2690,8 @@ struct S {
 
 void indexing_with_static_operator() {
   S()(1, 2);
-  S& x = S()("1", // expected-note 2 {{expression aliases the storage of temporary object}}
+  S& x = S()("1", // expected-note {{expression aliases the storage of temporary object because parameter 'a' is marked 'lifetimebound'}} \
+             // expected-note {{expression aliases the storage of temporary object because parameter 'b' is marked 'lifetimebound'}}
              2,   // expected-warning {{temporary object does not live long enough}}
              3);  // expected-warning {{temporary object does not live long enough}} expected-note 2 {{temporary object is destroyed here}}
 
@@ -2716,7 +2717,7 @@ S getS(const std::string &s [[clang::lifetimebound]]);
 void from_free_function() {
   S s = getS(std::string("temp")); // expected-warning {{temporary object does not live long enough}} \
                                    // expected-note {{temporary object is destroyed here}} \
-                                   // expected-note {{result of call to 'getS' aliases the storage of temporary object}}
+                                   // expected-note {{result of call to 'getS' aliases the storage of temporary object because parameter 's' is marked 'lifetimebound'}}
   use(s);                          // expected-note {{later used here}}
 }
 
@@ -2736,14 +2737,14 @@ void from_method() {
   Factory f;
   S s = f.make(std::string("temp")); // expected-warning {{temporary object does not live long enough}} \
                                      // expected-note {{temporary object is destroyed here}} \
-                                     // expected-note {{result of call to 'make' aliases the storage of temporary object}}
+                                     // expected-note {{result of call to 'make' aliases the storage of temporary object because parameter 's' is marked 'lifetimebound'}}
   use(s);                            // expected-note {{later used here}}
 }
 
 void from_static_method() {
   S s = Factory::create(std::string("temp")); // expected-warning {{temporary object does not live long enough}} \
                                               // expected-note {{temporary object is destroyed here}} \
-                                              // expected-note {{result of call to 'create' aliases the storage of temporary object}}
+                                              // expected-note {{result of call to 'create' aliases the storage of temporary object because parameter 's' is marked 'lifetimebound'}}
   use(s);                                     // expected-note {{later used here}}
 }
 
@@ -2752,7 +2753,7 @@ void from_lifetimebound_this_method() {
   {
     Factory f;
     value = f.makeThis(); // expected-warning {{local variable 'f' does not live long enough}} \
-                          // expected-note {{result of call to 'makeThis' aliases the storage of local variable 'f'}}
+                          // expected-note {{result of call to 'makeThis' aliases the storage of local variable 'f' because the implicit object parameter is marked 'lifetimebound'}}
   }                       // expected-note {{local variable 'f' is destroyed here}}
   use(value);             // expected-note {{later used here}}
 }
@@ -2762,7 +2763,7 @@ void across_scope() {
   {
     std::string str{"abc"};
     s = getS(str); // expected-warning {{local variable 'str' does not live long enough}} \
-                   // expected-note {{result of call to 'getS' aliases the storage of local variable 'str'}}
+                   // expected-note {{result of call to 'getS' aliases the storage of local variable 'str' because parameter 's' is marked 'lifetimebound'}}
   }                // expected-note {{local variable 'str' is destroyed here}}
   use(s);          // expected-note {{later used here}}
 }
@@ -2785,7 +2786,7 @@ void assignment_propagation() {
   {
     std::string str{"abc"};
     a = getS(str); // expected-warning {{local variable 'str' does not live long enough}} \
-                   // expected-note {{result of call to 'getS' aliases the storage of local variable 'str'}}
+                   // expected-note {{result of call to 'getS' aliases the storage of local variable 'str' because parameter 's' is marked 'lifetimebound'}}
     b = a;         // expected-note {{local variable 'a' aliases the storage of local variable 'str'}}
   }                // expected-note {{local variable 'str' is destroyed here}}
   use(b);          // expected-note {{later used here}}
@@ -2796,7 +2797,7 @@ void chained_defaulted_assignment_propagation() {
   {
     std::string str{"abc"};
     S a = getS(str); // expected-warning {{local variable 'str' does not live long enough}} \
-                     // expected-note {{result of call to 'getS' aliases the storage of local variable 'str'}}
+                     // expected-note {{result of call to 'getS' aliases the storage of local variable 'str' because parameter 's' is marked 'lifetimebound'}}
     c = b = a;       // expected-note {{local variable 'a' aliases the storage of local variable 'str'}} \
                      // expected-note {{expression aliases the storage of local variable 'str'}}
   }                  // expected-note {{local variable 'str' is destroyed here}}
@@ -2813,7 +2814,7 @@ void no_annotation() {
 void mix_annotated_and_not() {
   S s1 = getS(std::string("temp")); // expected-warning {{temporary object does not live long enough}} \
                                     // expected-note {{temporary object is destroyed here}} \
-                                    // expected-note {{result of call to 'getS' aliases the storage of temporary object}}
+                                    // expected-note {{result of call to 'getS' aliases the storage of temporary object because parameter 's' is marked 'lifetimebound'}}
   S s2 = getSNoAnnotation(std::string("temp"));
   use(s1); // expected-note {{later used here}}
   use(s2);
@@ -2825,7 +2826,7 @@ S multiple_lifetimebound_params() {
   std::string str{"abc"};
   S s = getS2(str, std::string("temp")); // expected-warning {{stack memory associated with local variable 'str' is returned}} \
                                          // expected-warning {{temporary object does not live long enough}} \
-                                         // expected-note {{result of call to 'getS2' aliases the storage of temporary object}} \
+                                         // expected-note {{result of call to 'getS2' aliases the storage of temporary object because parameter 'b' is marked 'lifetimebound'}} \
                                          // expected-note {{temporary object is destroyed here}}
   return s;                              // expected-note {{returned here}} \
                                          // expected-note {{later used here}}
@@ -2846,7 +2847,7 @@ T make(const std::string &s [[clang::lifetimebound]]);
 void from_template_instantiation() {
   S s = make<S>(std::string("temp")); // expected-warning {{temporary object does not live long enough}} \
                                       // expected-note {{temporary object is destroyed here}} \
-                                      // expected-note {{result of call to 'make<track_origins_for_lifetimebound_record_type::S>' aliases the storage of temporary object}}
+                                      // expected-note {{result of call to 'make<track_origins_for_lifetimebound_record_type::S>' aliases the storage of temporary object because parameter 's' is marked 'lifetimebound'}}
   use(s);                             // expected-note {{later used here}}
 }
 
@@ -2910,7 +2911,7 @@ SAlias getSAlias(const std::string &s [[clang::lifetimebound]]);
 void from_typedef_return() {
   SAlias s = getSAlias(std::string("temp")); // expected-warning {{temporary object does not live long enough}} \
                                              // expected-note {{temporary object is destroyed here}} \
-                                             // expected-note {{result of call to 'getSAlias' aliases the storage of temporary object}}
+                                             // expected-note {{result of call to 'getSAlias' aliases the storage of temporary object because parameter 's' is marked 'lifetimebound'}}
   use(s);                                    // expected-note {{later used here}}
 }
 
@@ -2985,7 +2986,7 @@ std::unique_ptr<S> getUniqueS(const std::string &s [[clang::lifetimebound]]);
 void owner_return_unique_ptr_s() {
   auto ptr = getUniqueS(std::string("temp")); // expected-warning {{temporary object does not live long enough}} \
                                               // expected-note {{temporary object is destroyed here}} \
-                                              // expected-note {{result of call to 'getUniqueS' aliases the storage of temporary object}}
+                                              // expected-note {{result of call to 'getUniqueS' aliases the storage of temporary object because parameter 's' is marked 'lifetimebound'}}
   (void)ptr;                                  // expected-note {{later used here}}
 }
 
@@ -3002,7 +3003,7 @@ void owner_outlives_lifetimebound_source() {
   {
     std::string local;
     ups = getUniqueS(local); // expected-warning {{local variable 'local' does not live long enough}} \
-                             // expected-note {{result of call to 'getUniqueS' aliases the storage of local variable 'local'}}
+                             // expected-note {{result of call to 'getUniqueS' aliases the storage of local variable 'local' because parameter 's' is marked 'lifetimebound'}}
   } // expected-note {{local variable 'local' is destroyed here}}
   (void)ups; // expected-note {{later used here}}
 }
@@ -3037,7 +3038,7 @@ void nested_local_pointer() {
   {
     Bar v;
     p = Pointer(v);     // expected-warning {{local variable 'v' does not live long enough}}
-    pp = Pointer(p);    // expected-note {{local variable 'p' aliases the storage of local variable 'v'}}
+    pp = Pointer(p);    // expected-note {{local variable 'p' aliases the storage of local variable 'v' because parameter 'bar' is marked 'lifetimebound'}}
     ppp = Pointer(pp);  // expected-note {{local variable 'pp' aliases the storage of local variable 'v'}}
   }                     // expected-note {{local variable 'v' is destroyed here}}
   use(***ppp);          // expected-note {{later used here}}
@@ -3795,7 +3796,7 @@ void uaf_via_lifetimebound() {
   {
     int local;
     f = capture_lifetimebound_param(local); // expected-warning {{local variable 'local' does not live long enough}} \
-                                            // expected-note {{result of call to 'capture_lifetimebound_param' aliases the storage of local variable 'local'}}
+                                            // expected-note {{result of call to 'capture_lifetimebound_param' aliases the storage of local variable 'local' because parameter 'x' is marked 'lifetimebound'}}
   } // expected-note {{local variable 'local' is destroyed here}}
   (void)f; // expected-note {{later used here}}
 }

>From ad1a2be77ddb923c3c1020985224e9b0b6ff3c5c Mon Sep 17 00:00:00 2001
From: iitianpushkar <pushkarsingh0587 at gmail.com>
Date: Wed, 8 Jul 2026 20:50:04 +0530
Subject: [PATCH 2/8] implemented re-derive design flow using PointerUnion

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  | 19 ----
 .../Analyses/LifetimeSafety/LifetimeSafety.h  |  6 +-
 .../Analyses/LifetimeSafety/LoanPropagation.h |  9 --
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 +-
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 97 +++++++++++++++++--
 .../LifetimeSafety/FactsGenerator.cpp         | 38 +++-----
 .../LifetimeSafety/LoanPropagation.cpp        | 62 ++----------
 clang/lib/Sema/SemaLifetimeSafety.h           | 23 +++--
 8 files changed, 126 insertions(+), 131 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 2e426105d1c84..94db2a7f311ae 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -20,7 +20,6 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
-#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Debug.h"
@@ -34,11 +33,6 @@ class LoanPropagationAnalysis;
 
 using FactID = utils::ID<struct FactTag>;
 
-struct LifetimeBoundParamInfo {
-  const ParmVarDecl *Param = nullptr;
-  bool IsImplicitObject = false;
-};
-
 /// An abstract base class for a single, atomic lifetime-relevant event.
 class Fact {
 
@@ -399,17 +393,6 @@ class FactManager {
   const LoanManager &getLoanMgr() const { return LoanMgr; }
   OriginManager &getOriginMgr() { return OriginMgr; }
   const OriginManager &getOriginMgr() const { return OriginMgr; }
-  void setLifetimeBoundParamInfo(const OriginFlowFact *F,
-                                 LifetimeBoundParamInfo Info) {
-    LifetimeBoundParamInfos.try_emplace(F, Info);
-  }
-  std::optional<LifetimeBoundParamInfo>
-  getLifetimeBoundParamInfo(const OriginFlowFact *F) const {
-    auto It = LifetimeBoundParamInfos.find(F);
-    if (It == LifetimeBoundParamInfos.end())
-      return std::nullopt;
-    return It->second;
-  }
 
 private:
   FactID NextFactID{0};
@@ -417,8 +400,6 @@ class FactManager {
   OriginManager OriginMgr;
   /// Facts for each CFG block, indexed by block ID.
   llvm::SmallVector<llvm::SmallVector<const Fact *>> BlockToFacts;
-  llvm::DenseMap<const OriginFlowFact *, LifetimeBoundParamInfo>
-      LifetimeBoundParamInfos;
   llvm::BumpPtrAllocator FactAllocator;
 };
 } // namespace clang::lifetimes::internal
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 6204381a873ee..7a4577e890b2f 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -21,6 +21,7 @@
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H
 
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
@@ -50,9 +51,12 @@ enum class WarningScope {
   IntraTU  // For warnings on functions local to a Translation Unit.
 };
 
+using LifetimeBoundParamInfo =
+    llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
+
 struct AliasChainEntry {
   const Expr *E = nullptr;
-  std::optional<internal::LifetimeBoundParamInfo> LifetimeBound;
+  std::optional<LifetimeBoundParamInfo> LifetimeBound;
 };
 
 /// Abstract interface for operations requiring Sema access.
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index 616394314056f..838daa024c953 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -56,15 +56,6 @@ class LoanPropagationAnalysis {
                                                    const LoanID TargetLoan,
                                                    const CFG *Cfg) const;
 
-  llvm::SmallVector<const OriginFlowFact *>
-  buildOriginFlowChainWithFacts(ProgramPoint StartPoint,
-                                const OriginID StartOID,
-                                const LoanID TargetLoan, const CFG *Cfg) const;
-
-  llvm::SmallVector<const OriginFlowFact *>
-  buildOriginFlowChainWithFacts(const UseFact *UF, const LoanID TargetLoan,
-                                const CFG *Cfg) const;
-
 private:
   class Impl;
   std::unique_ptr<Impl> PImpl;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index ed1e4350890b5..e55e6ee1aa939 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11079,7 +11079,8 @@ def note_lifetime_safety_escapes_to_static_storage_here: Note<"escapes to this s
 def note_lifetime_safety_lifetimebound_here: Note<"'lifetimebound' attribute appears here on the definition">;
 def note_lifetime_safety_aliases_storage : Note<"%0 aliases the storage of %1">;
 def note_lifetime_safety_aliases_storage_lifetimebound : Note<
-  "%0 aliases the storage of %1 because %2 is marked 'lifetimebound'">;
+  "%0 aliases the storage of %1 because %select{parameter %3|the implicit "
+  "object parameter}2 is marked 'lifetimebound'">;
 
 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 2a3acdeb4b1ca..93dbdd602df92 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -14,6 +14,7 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/Checker.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
@@ -40,6 +41,12 @@ static bool causingFactDominatesExpiry(LivenessKind K) {
   llvm_unreachable("unknown liveness kind");
 }
 
+static OriginList *getRValueOrigins(const Expr *E, OriginList *List) {
+  if (!List)
+    return nullptr;
+  return E->isGLValue() ? List->peelOuterOrigin() : List;
+}
+
 namespace {
 
 /// Struct to store the complete context for a potential lifetime violation.
@@ -275,7 +282,7 @@ class LifetimeChecker {
           SemaHelper->reportUseAfterScope(
               IssueExpr, UF->getUseExpr(), MovedExpr, ExpiryLoc,
               getAliasChain(
-                  LoanPropagation.buildOriginFlowChainWithFacts(UF, LID, Cfg)));
+                  LoanPropagation.buildOriginFlowChain(UF, LID, Cfg)));
 
       } else if (const auto *OEF =
                      CausingFact.dyn_cast<const OriginEscapesFact *>()) {
@@ -522,19 +529,95 @@ class LifetimeChecker {
     }
   }
 
+  std::optional<LifetimeBoundParamInfo>
+  getLifetimeBoundParamInfo(const FunctionDecl *FD, unsigned I) {
+    const ParmVarDecl *PVD = nullptr;
+    if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
+        Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) {
+      if (I == 0)
+        return implicitObjectParamIsLifetimeBound(Method)
+                   ? std::optional<LifetimeBoundParamInfo>(
+                         LifetimeBoundParamInfo(Method))
+                   : std::nullopt;
+      if ((I - 1) < Method->getNumParams())
+        PVD = Method->getParamDecl(I - 1);
+    } else if (I < FD->getNumParams()) {
+      PVD = FD->getParamDecl(I);
+    }
+
+    if (PVD && PVD->hasAttr<clang::LifetimeBoundAttr>())
+      return LifetimeBoundParamInfo(PVD);
+    return std::nullopt;
+  }
+
+  std::optional<LifetimeBoundParamInfo>
+  getLifetimeBoundParamInfo(const Expr *Call, OriginID SrcOriginID) {
+    Call = Call->IgnoreParenImpCasts();
+
+    const FunctionDecl *FD = nullptr;
+    llvm::SmallVector<const Expr *, 4> Args;
+
+    if (const auto *CCE = dyn_cast<CXXConstructExpr>(Call)) {
+      FD = CCE->getConstructor();
+      Args.append(CCE->getArgs(), CCE->getArgs() + CCE->getNumArgs());
+    } else if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) {
+      FD = MCE->getMethodDecl();
+      Args.push_back(MCE->getImplicitObjectArgument());
+      Args.append(MCE->getArgs(), MCE->getArgs() + MCE->getNumArgs());
+    } else if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(Call)) {
+      FD = OCE->getDirectCallee();
+      Args.append(OCE->getArgs(), OCE->getArgs() + OCE->getNumArgs());
+      if (FD && OCE->getOperator() == OO_Call && FD->isStatic())
+        Args.erase(Args.begin());
+    } else if (const auto *CE = dyn_cast<CallExpr>(Call)) {
+      FD = CE->getDirectCallee();
+      Args.append(CE->getArgs(), CE->getArgs() + CE->getNumArgs());
+    }
+
+    FD = getDeclWithMergedLifetimeBoundAttrs(FD);
+    if (!FD)
+      return std::nullopt;
+
+    for (unsigned I = 0; I < Args.size(); ++I) {
+      std::optional<LifetimeBoundParamInfo> Info =
+          getLifetimeBoundParamInfo(FD, I);
+      if (!Info)
+        continue;
+
+      OriginList *ArgList = FactMgr.getOriginMgr().getOrCreateList(Args[I]);
+      if (ArgList && ArgList->getOuterOriginID() == SrcOriginID)
+        return Info;
+
+      OriginList *RValueList = getRValueOrigins(Args[I], ArgList);
+      if (RValueList && RValueList->getOuterOriginID() == SrcOriginID)
+        return Info;
+    }
+
+    return std::nullopt;
+  }
+
   /// Extract expressions from the origin flow chain for diagnostic purposes.
   ///
   /// Given a chain of origins that shows how a loan propagates, this function
   /// extracts the corresponding expressions for each origin. Origins that refer
   /// to declarations (rather than expressions) are skipped.
   llvm::SmallVector<AliasChainEntry>
-  getAliasChain(llvm::ArrayRef<const OriginFlowFact *> OriginFlowChain) {
+  getAliasChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
+    llvm::SmallVector<std::pair<OriginID, AliasChainEntry>> Entries;
+    for (OriginID OID : OriginFlowChain)
+      if (const Expr *CurrExpr =
+              FactMgr.getOriginMgr().getOrigin(OID).getExpr())
+        Entries.push_back({OID, {CurrExpr, std::nullopt}});
+
+    for (unsigned I = 0; I + 1 < Entries.size(); ++I)
+      if (std::optional<LifetimeBoundParamInfo> Info =
+              getLifetimeBoundParamInfo(Entries[I].second.E,
+                                        Entries[I + 1].first))
+        Entries[I + 1].second.LifetimeBound = Info;
+
     llvm::SmallVector<AliasChainEntry> Result;
-    for (const OriginFlowFact *Flow : OriginFlowChain)
-      if (const Expr *CurrExpr = FactMgr.getOriginMgr()
-                                     .getOrigin(Flow->getSrcOriginID())
-                                     .getExpr())
-        Result.push_back({CurrExpr, FactMgr.getLifetimeBoundParamInfo(Flow)});
+    for (const auto &Entry : Entries)
+      Result.push_back(Entry.second);
     return Result;
   }
 };
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 83ff0d1265658..ca88986975299 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -1096,18 +1096,13 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
     flow(CallList, getOriginsList(*Args[0]), /*Kill=*/true);
     return;
   }
-  auto GetLifetimeBoundParamInfo =
-      [FD](unsigned I) -> std::optional<LifetimeBoundParamInfo> {
+  auto IsArgLifetimeBound = [FD](unsigned I) -> bool {
     const ParmVarDecl *PVD = nullptr;
     if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
         Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) {
       if (I == 0)
         // For the 'this' argument, the attribute is on the method itself.
-        return implicitObjectParamIsLifetimeBound(Method)
-                   ? std::optional<LifetimeBoundParamInfo>(
-                         LifetimeBoundParamInfo{/*Param=*/nullptr,
-                                                /*IsImplicitObject=*/true})
-                   : std::nullopt;
+        return implicitObjectParamIsLifetimeBound(Method);
       if ((I - 1) < Method->getNumParams())
         // For explicit arguments, find the corresponding parameter
         // declaration.
@@ -1116,14 +1111,10 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
       // For free functions or static methods.
       PVD = FD->getParamDecl(I);
     }
-    if (PVD && PVD->hasAttr<clang::LifetimeBoundAttr>())
-      return LifetimeBoundParamInfo{PVD, /*IsImplicitObject=*/false};
-    return std::nullopt;
+    return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() : false;
   };
-  auto ShouldTrackArg =
-      [FD, &Args](unsigned I,
-                  std::optional<LifetimeBoundParamInfo> Info) -> bool {
-    if (Info)
+  auto ShouldTrackArg = [FD, &Args, IsArgLifetimeBound](unsigned I) -> bool {
+    if (IsArgLifetimeBound(I))
       return true;
     if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
         Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD))
@@ -1152,9 +1143,7 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
     OriginList *ArgList = getOriginsList(*Args[I]);
     if (!ArgList)
       continue;
-    std::optional<LifetimeBoundParamInfo> LifetimeBoundInfo =
-        GetLifetimeBoundParamInfo(I);
-    bool TrackArg = ShouldTrackArg(I, LifetimeBoundInfo);
+    bool TrackArg = ShouldTrackArg(I);
     if (IsGslConstruction) {
       // TODO: document with code example.
       // std::string_view(const std::string_view& from)
@@ -1177,11 +1166,9 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
         // destination origin lists may have different lengths.
         // FIXME: Handle origin-shape mismatches gracefully so we can also flow
         // inner origins.
-        auto *F = FactMgr.createFact<OriginFlowFact>(
-            CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc);
-        if (LifetimeBoundInfo)
-          FactMgr.setLifetimeBoundParamInfo(F, *LifetimeBoundInfo);
-        CurrentBlockFacts.push_back(F);
+        CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
+            CallList->getOuterOriginID(), ArgList->getOuterOriginID(),
+            KillSrc));
         KillSrc = false;
       }
     } else if (shouldTrackPointerImplicitObjectArg(I)) {
@@ -1196,11 +1183,8 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
       // Lifetimebound on a non-GSL-ctor function means the returned
       // pointer/reference itself must not outlive the arguments. This
       // only constrains the top-level origin.
-      auto *F = FactMgr.createFact<OriginFlowFact>(
-          CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc);
-      if (LifetimeBoundInfo)
-        FactMgr.setLifetimeBoundParamInfo(F, *LifetimeBoundInfo);
-      CurrentBlockFacts.push_back(F);
+      CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
+          CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc));
       KillSrc = false;
     }
   }
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 5744479737b0a..078892bd48c10 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -207,17 +207,6 @@ class AnalysisImpl
                                                    const OriginID StartOID,
                                                    const LoanID TargetLoan,
                                                    const CFG *Cfg) const {
-    llvm::SmallVector<OriginID> OriginFlowChain;
-    for (const OriginFlowFact *Flow :
-         buildOriginFlowChainWithFacts(StartPoint, StartOID, TargetLoan, Cfg))
-      OriginFlowChain.push_back(Flow->getSrcOriginID());
-    return OriginFlowChain;
-  }
-
-  llvm::SmallVector<const OriginFlowFact *>
-  buildOriginFlowChainWithFacts(ProgramPoint StartPoint,
-                                const OriginID StartOID,
-                                const LoanID TargetLoan, const CFG *Cfg) const {
     assert(getLoans(StartOID, StartPoint).contains(TargetLoan) &&
            "TargetLoan must be present in the StartOID at the StartPoint");
 
@@ -236,7 +225,7 @@ class AnalysisImpl
     using SearchState = std::pair<const CFGBlock *, OriginID>;
     struct DFSNode {
       SearchState CurrState;
-      llvm::SmallVector<const OriginFlowFact *> OriginFlowChain;
+      llvm::SmallVector<OriginID> OriginFlowChain;
     };
 
     llvm::SmallVector<DFSNode> PendingStates;
@@ -250,10 +239,10 @@ class AnalysisImpl
 
       // Trace origins within the current block
       const auto [BuildResult, Complete] =
-          buildOriginFlowChainWithFacts(CurrBlock, CurrOID, TargetLoan);
+          buildOriginFlowChain(CurrBlock, CurrOID, TargetLoan);
       if (!BuildResult.empty()) {
         CurrNode.OriginFlowChain.append(BuildResult);
-        CurrOID = BuildResult.back()->getSrcOriginID();
+        CurrOID = BuildResult.back();
       }
 
       // If we found the IssueFact, we're done
@@ -277,21 +266,11 @@ class AnalysisImpl
   llvm::SmallVector<OriginID> buildOriginFlowChain(const UseFact *UF,
                                                    const LoanID TargetLoan,
                                                    const CFG *Cfg) const {
-    llvm::SmallVector<OriginID> OriginFlowChain;
-    for (const OriginFlowFact *Flow :
-         buildOriginFlowChainWithFacts(UF, TargetLoan, Cfg))
-      OriginFlowChain.push_back(Flow->getSrcOriginID());
-    return OriginFlowChain;
-  }
-
-  llvm::SmallVector<const OriginFlowFact *>
-  buildOriginFlowChainWithFacts(const UseFact *UF, const LoanID TargetLoan,
-                                const CFG *Cfg) const {
     for (const OriginList *Cur = UF->getUsedOrigins(); Cur;
          Cur = Cur->peelOuterOrigin())
       if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan))
-        return buildOriginFlowChainWithFacts(UF, Cur->getOuterOriginID(),
-                                             TargetLoan, Cfg);
+        return buildOriginFlowChain(UF, Cur->getOuterOriginID(), TargetLoan,
+                                    Cfg);
 
     return {};
   }
@@ -329,19 +308,8 @@ class AnalysisImpl
   std::pair<llvm::SmallVector<OriginID>, bool>
   buildOriginFlowChain(const CFGBlock *Block, const OriginID StartOID,
                        const LoanID TargetLoan) const {
-    llvm::SmallVector<OriginID> OriginFlowChain;
-    auto [FlowChain, Complete] =
-        buildOriginFlowChainWithFacts(Block, StartOID, TargetLoan);
-    for (const OriginFlowFact *Flow : FlowChain)
-      OriginFlowChain.push_back(Flow->getSrcOriginID());
-    return {OriginFlowChain, Complete};
-  }
-
-  std::pair<llvm::SmallVector<const OriginFlowFact *>, bool>
-  buildOriginFlowChainWithFacts(const CFGBlock *Block, const OriginID StartOID,
-                                const LoanID TargetLoan) const {
     OriginID CurrOID = StartOID;
-    llvm::SmallVector<const OriginFlowFact *> OriginFlowChain;
+    llvm::SmallVector<OriginID> OriginFlowChain;
 
     for (const Fact *F : llvm::reverse(FactMgr.getFacts(Block))) {
       if (const auto *IF = F->getAs<IssueFact>())
@@ -356,7 +324,7 @@ class AnalysisImpl
       if (!getLoans(SrcOriginID, OFF).contains(TargetLoan))
         continue;
 
-      OriginFlowChain.push_back(OFF);
+      OriginFlowChain.push_back(SrcOriginID);
       CurrOID = SrcOriginID;
     }
 
@@ -401,20 +369,4 @@ llvm::SmallVector<OriginID> LoanPropagationAnalysis::buildOriginFlowChain(
     const UseFact *UF, const LoanID TargetLoan, const CFG *Cfg) const {
   return PImpl->buildOriginFlowChain(UF, TargetLoan, Cfg);
 }
-
-llvm::SmallVector<const OriginFlowFact *>
-LoanPropagationAnalysis::buildOriginFlowChainWithFacts(ProgramPoint StartPoint,
-                                                       const OriginID StartOID,
-                                                       const LoanID TargetLoan,
-                                                       const CFG *Cfg) const {
-  return PImpl->buildOriginFlowChainWithFacts(StartPoint, StartOID, TargetLoan,
-                                              Cfg);
-}
-
-llvm::SmallVector<const OriginFlowFact *>
-LoanPropagationAnalysis::buildOriginFlowChainWithFacts(const UseFact *UF,
-                                                       const LoanID TargetLoan,
-                                                       const CFG *Cfg) const {
-  return PImpl->buildOriginFlowChainWithFacts(UF, TargetLoan, Cfg);
-}
 } // namespace clang::lifetimes::internal
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 7b628669ed0cf..205ff2f2ae054 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -21,6 +21,7 @@
 #include "clang/Lex/Lexer.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/Sema.h"
+#include <cassert>
 #include <string>
 
 namespace clang::lifetimes {
@@ -681,22 +682,20 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
         continue;
       }
       if (Last.LifetimeBound) {
-        std::string LifetimeBoundSubject;
-        if (Last.LifetimeBound->IsImplicitObject) {
-          LifetimeBoundSubject = "the implicit object parameter";
-        } else {
-          LifetimeBoundSubject = "parameter ";
-          if (Last.LifetimeBound->Param &&
-              Last.LifetimeBound->Param->getIdentifier())
-            LifetimeBoundSubject +=
-                "'" + Last.LifetimeBound->Param->getNameAsString() + "'";
-          else
-            LifetimeBoundSubject += "'<unnamed>'";
+        bool IsImplicitObject = isa<const CXXMethodDecl *>(*Last.LifetimeBound);
+        std::string ParamName;
+        if (!IsImplicitObject) {
+          const auto *Param = cast<const ParmVarDecl *>(*Last.LifetimeBound);
+          assert(Param &&
+                 "lifetimebound parameter info should identify a parameter");
+          ParamName = Param->getIdentifier()
+                          ? "'" + Param->getNameAsString() + "'"
+                          : "'<unnamed>'";
         }
         S.Diag(CurrExpr->getBeginLoc(),
                diag::note_lifetime_safety_aliases_storage_lifetimebound)
             << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr)
-            << IssueStr << LifetimeBoundSubject;
+            << IssueStr << IsImplicitObject << ParamName;
       } else
         S.Diag(CurrExpr->getBeginLoc(),
                diag::note_lifetime_safety_aliases_storage)

>From 10dc54a3575b8dcad63f5050b754928b730808be Mon Sep 17 00:00:00 2001
From: iitianpushkar <pushkarsingh0587 at gmail.com>
Date: Fri, 10 Jul 2026 20:40:29 +0530
Subject: [PATCH 3/8] created a shared helper flow for rederive design

---
 .../LifetimeSafety/LifetimeAnnotations.h      | 17 +++-
 .../Analyses/LifetimeSafety/LifetimeSafety.h  | 12 +--
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 95 +------------------
 .../LifetimeSafety/FactsGenerator.cpp         | 21 +---
 .../LifetimeSafety/LifetimeAnnotations.cpp    | 83 ++++++++++++++++
 clang/lib/Sema/SemaLifetimeSafety.h           | 39 ++++----
 6 files changed, 127 insertions(+), 140 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index 47fcd5dbfd569..85ff329774c7b 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -12,8 +12,10 @@
 
 #include "clang/AST/Attr.h"
 #include "clang/AST/DeclCXX.h"
+#include "llvm/ADT/PointerUnion.h"
+#include <optional>
 
-namespace clang ::lifetimes {
+namespace clang::lifetimes {
 
 // This function is needed because Decl::isInStdNamespace will return false for
 // iterators in some STL implementations due to them being defined in a
@@ -56,6 +58,19 @@ getImplicitObjectParamLifetimeBoundAttr(const FunctionDecl *FD);
 /// method or because it's a normal assignment operator.
 bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD);
 
+using LifetimeBoundParamInfo =
+    llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
+
+/// Returns the lifetimebound parameter corresponding to argument I.
+/// For instance methods, argument 0 is the implicit object argument.
+std::optional<LifetimeBoundParamInfo>
+getLifetimeBoundParamInfo(const FunctionDecl *FD, unsigned I);
+
+/// Returns the lifetimebound parameter in Call whose argument corresponds to
+/// Source, if any.
+std::optional<LifetimeBoundParamInfo>
+getLifetimeBoundParamInfoForCallArg(const Expr *Call, const Expr *Source);
+
 // Returns true if the implicit object argument (this) of a method call should
 // be tracked for GSL lifetime analysis. This applies to STL methods that return
 // pointers or references that depend on the lifetime of the object, such as
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 7a4577e890b2f..52d199c51ae24 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -32,7 +32,6 @@
 #include "llvm/ADT/PointerUnion.h"
 #include <cstddef>
 #include <memory>
-#include <optional>
 
 namespace clang::lifetimes {
 
@@ -51,14 +50,6 @@ enum class WarningScope {
   IntraTU  // For warnings on functions local to a Translation Unit.
 };
 
-using LifetimeBoundParamInfo =
-    llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
-
-struct AliasChainEntry {
-  const Expr *E = nullptr;
-  std::optional<LifetimeBoundParamInfo> LifetimeBound;
-};
-
 /// Abstract interface for operations requiring Sema access.
 ///
 /// This class exists to break a circular dependency: the LifetimeSafety
@@ -77,8 +68,7 @@ class LifetimeSafetySemaHelper {
   virtual void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
                                    const Expr *MovedExpr,
                                    SourceLocation FreeLoc,
-                                   llvm::ArrayRef<AliasChainEntry> AliasChain) {
-  }
+                                   llvm::ArrayRef<const Expr *> ExprChain) {}
 
   virtual void reportUseAfterReturn(const Expr *IssueExpr,
                                     const Expr *ReturnExpr,
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 93dbdd602df92..1276b4824d4b1 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -14,7 +14,6 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/Checker.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
-#include "clang/AST/ExprCXX.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
@@ -41,12 +40,6 @@ static bool causingFactDominatesExpiry(LivenessKind K) {
   llvm_unreachable("unknown liveness kind");
 }
 
-static OriginList *getRValueOrigins(const Expr *E, OriginList *List) {
-  if (!List)
-    return nullptr;
-  return E->isGLValue() ? List->peelOuterOrigin() : List;
-}
-
 namespace {
 
 /// Struct to store the complete context for a potential lifetime violation.
@@ -281,8 +274,7 @@ class LifetimeChecker {
           // Scope-based expiry (use-after-scope).
           SemaHelper->reportUseAfterScope(
               IssueExpr, UF->getUseExpr(), MovedExpr, ExpiryLoc,
-              getAliasChain(
-                  LoanPropagation.buildOriginFlowChain(UF, LID, Cfg)));
+              getExprChain(LoanPropagation.buildOriginFlowChain(UF, LID, Cfg)));
 
       } else if (const auto *OEF =
                      CausingFact.dyn_cast<const OriginEscapesFact *>()) {
@@ -529,95 +521,18 @@ class LifetimeChecker {
     }
   }
 
-  std::optional<LifetimeBoundParamInfo>
-  getLifetimeBoundParamInfo(const FunctionDecl *FD, unsigned I) {
-    const ParmVarDecl *PVD = nullptr;
-    if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
-        Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) {
-      if (I == 0)
-        return implicitObjectParamIsLifetimeBound(Method)
-                   ? std::optional<LifetimeBoundParamInfo>(
-                         LifetimeBoundParamInfo(Method))
-                   : std::nullopt;
-      if ((I - 1) < Method->getNumParams())
-        PVD = Method->getParamDecl(I - 1);
-    } else if (I < FD->getNumParams()) {
-      PVD = FD->getParamDecl(I);
-    }
-
-    if (PVD && PVD->hasAttr<clang::LifetimeBoundAttr>())
-      return LifetimeBoundParamInfo(PVD);
-    return std::nullopt;
-  }
-
-  std::optional<LifetimeBoundParamInfo>
-  getLifetimeBoundParamInfo(const Expr *Call, OriginID SrcOriginID) {
-    Call = Call->IgnoreParenImpCasts();
-
-    const FunctionDecl *FD = nullptr;
-    llvm::SmallVector<const Expr *, 4> Args;
-
-    if (const auto *CCE = dyn_cast<CXXConstructExpr>(Call)) {
-      FD = CCE->getConstructor();
-      Args.append(CCE->getArgs(), CCE->getArgs() + CCE->getNumArgs());
-    } else if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) {
-      FD = MCE->getMethodDecl();
-      Args.push_back(MCE->getImplicitObjectArgument());
-      Args.append(MCE->getArgs(), MCE->getArgs() + MCE->getNumArgs());
-    } else if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(Call)) {
-      FD = OCE->getDirectCallee();
-      Args.append(OCE->getArgs(), OCE->getArgs() + OCE->getNumArgs());
-      if (FD && OCE->getOperator() == OO_Call && FD->isStatic())
-        Args.erase(Args.begin());
-    } else if (const auto *CE = dyn_cast<CallExpr>(Call)) {
-      FD = CE->getDirectCallee();
-      Args.append(CE->getArgs(), CE->getArgs() + CE->getNumArgs());
-    }
-
-    FD = getDeclWithMergedLifetimeBoundAttrs(FD);
-    if (!FD)
-      return std::nullopt;
-
-    for (unsigned I = 0; I < Args.size(); ++I) {
-      std::optional<LifetimeBoundParamInfo> Info =
-          getLifetimeBoundParamInfo(FD, I);
-      if (!Info)
-        continue;
-
-      OriginList *ArgList = FactMgr.getOriginMgr().getOrCreateList(Args[I]);
-      if (ArgList && ArgList->getOuterOriginID() == SrcOriginID)
-        return Info;
-
-      OriginList *RValueList = getRValueOrigins(Args[I], ArgList);
-      if (RValueList && RValueList->getOuterOriginID() == SrcOriginID)
-        return Info;
-    }
-
-    return std::nullopt;
-  }
-
   /// Extract expressions from the origin flow chain for diagnostic purposes.
   ///
   /// Given a chain of origins that shows how a loan propagates, this function
   /// extracts the corresponding expressions for each origin. Origins that refer
   /// to declarations (rather than expressions) are skipped.
-  llvm::SmallVector<AliasChainEntry>
-  getAliasChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
-    llvm::SmallVector<std::pair<OriginID, AliasChainEntry>> Entries;
+  llvm::SmallVector<const Expr *>
+  getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
+    llvm::SmallVector<const Expr *> Result;
     for (OriginID OID : OriginFlowChain)
       if (const Expr *CurrExpr =
               FactMgr.getOriginMgr().getOrigin(OID).getExpr())
-        Entries.push_back({OID, {CurrExpr, std::nullopt}});
-
-    for (unsigned I = 0; I + 1 < Entries.size(); ++I)
-      if (std::optional<LifetimeBoundParamInfo> Info =
-              getLifetimeBoundParamInfo(Entries[I].second.E,
-                                        Entries[I + 1].first))
-        Entries[I + 1].second.LifetimeBound = Info;
-
-    llvm::SmallVector<AliasChainEntry> Result;
-    for (const auto &Entry : Entries)
-      Result.push_back(Entry.second);
+        Result.push_back(CurrExpr);
     return Result;
   }
 };
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index ca88986975299..475c57cf38eb4 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -1096,25 +1096,8 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
     flow(CallList, getOriginsList(*Args[0]), /*Kill=*/true);
     return;
   }
-  auto IsArgLifetimeBound = [FD](unsigned I) -> bool {
-    const ParmVarDecl *PVD = nullptr;
-    if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
-        Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) {
-      if (I == 0)
-        // For the 'this' argument, the attribute is on the method itself.
-        return implicitObjectParamIsLifetimeBound(Method);
-      if ((I - 1) < Method->getNumParams())
-        // For explicit arguments, find the corresponding parameter
-        // declaration.
-        PVD = Method->getParamDecl(I - 1);
-    } else if (I < FD->getNumParams()) {
-      // For free functions or static methods.
-      PVD = FD->getParamDecl(I);
-    }
-    return PVD ? PVD->hasAttr<clang::LifetimeBoundAttr>() : false;
-  };
-  auto ShouldTrackArg = [FD, &Args, IsArgLifetimeBound](unsigned I) -> bool {
-    if (IsArgLifetimeBound(I))
+  auto ShouldTrackArg = [FD, &Args](unsigned I) -> bool {
+    if (getLifetimeBoundParamInfo(FD, I))
       return true;
     if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
         Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD))
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 6a52616c5d590..6543009cd38b0 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -11,6 +11,8 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
 #include "clang/AST/Type.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/OperatorKinds.h"
@@ -105,6 +107,87 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
   return isNormalAssignmentOperator(FD);
 }
 
+std::optional<LifetimeBoundParamInfo>
+getLifetimeBoundParamInfo(const FunctionDecl *FD, unsigned I) {
+  FD = getDeclWithMergedLifetimeBoundAttrs(FD);
+  if (!FD)
+    return std::nullopt;
+
+  const ParmVarDecl *PVD = nullptr;
+  if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
+      Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) {
+    if (I == 0)
+      return implicitObjectParamIsLifetimeBound(Method)
+                 ? std::optional<LifetimeBoundParamInfo>(
+                       LifetimeBoundParamInfo(Method))
+                 : std::nullopt;
+    if ((I - 1) < Method->getNumParams())
+      PVD = Method->getParamDecl(I - 1);
+  } else if (I < FD->getNumParams()) {
+    PVD = FD->getParamDecl(I);
+  }
+
+  if (PVD && PVD->hasAttr<clang::LifetimeBoundAttr>())
+    return LifetimeBoundParamInfo(PVD);
+  return std::nullopt;
+}
+
+static bool isExprInCallArg(const Expr *Arg, const Expr *Source) {
+  if (!Arg || !Source)
+    return false;
+
+  Arg = Arg->IgnoreParenImpCasts();
+  Source = Source->IgnoreParenImpCasts();
+  if (Arg == Source || Arg->getSourceRange() == Source->getSourceRange())
+    return true;
+
+  for (const Stmt *Child : Arg->children())
+    if (const auto *ChildExpr = dyn_cast_or_null<Expr>(Child))
+      if (isExprInCallArg(ChildExpr, Source))
+        return true;
+
+  return false;
+}
+
+std::optional<LifetimeBoundParamInfo>
+getLifetimeBoundParamInfoForCallArg(const Expr *Call, const Expr *Source) {
+  if (!Call || !Source)
+    return std::nullopt;
+
+  Call = Call->IgnoreParenImpCasts();
+
+  const FunctionDecl *FD = nullptr;
+  llvm::SmallVector<const Expr *, 4> Args;
+
+  if (const auto *CCE = dyn_cast<CXXConstructExpr>(Call)) {
+    FD = CCE->getConstructor();
+    Args.append(CCE->getArgs(), CCE->getArgs() + CCE->getNumArgs());
+  } else if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) {
+    FD = MCE->getMethodDecl();
+    Args.push_back(MCE->getImplicitObjectArgument());
+    Args.append(MCE->getArgs(), MCE->getArgs() + MCE->getNumArgs());
+  } else if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(Call)) {
+    FD = OCE->getDirectCallee();
+    Args.append(OCE->getArgs(), OCE->getArgs() + OCE->getNumArgs());
+    if (FD && OCE->getOperator() == OO_Call && FD->isStatic())
+      Args.erase(Args.begin());
+  } else if (const auto *CE = dyn_cast<CallExpr>(Call)) {
+    FD = CE->getDirectCallee();
+    Args.append(CE->getArgs(), CE->getArgs() + CE->getNumArgs());
+  }
+
+  if (!FD)
+    return std::nullopt;
+
+  for (unsigned I = 0; I < Args.size(); ++I)
+    if (isExprInCallArg(Args[I], Source))
+      if (std::optional<LifetimeBoundParamInfo> Info =
+              getLifetimeBoundParamInfo(FD, I))
+        return Info;
+
+  return std::nullopt;
+}
+
 bool isInStlNamespace(const Decl *D) {
   for (const DeclContext *DC = D->getDeclContext(); DC; DC = DC->getParent()) {
     if (DC->isStdNamespace())
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 205ff2f2ae054..f916bcc4db372 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -21,7 +21,6 @@
 #include "clang/Lex/Lexer.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/Sema.h"
-#include <cassert>
 #include <string>
 
 namespace clang::lifetimes {
@@ -106,10 +105,9 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
 public:
   LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {}
 
-  void
-  reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
-                      const Expr *MovedExpr, SourceLocation FreeLoc,
-                      llvm::ArrayRef<AliasChainEntry> AliasChain) override {
+  void reportUseAfterScope(const Expr *IssueExpr, const Expr *UseExpr,
+                           const Expr *MovedExpr, SourceLocation FreeLoc,
+                           llvm::ArrayRef<const Expr *> ExprChain) override {
     unsigned DiagID = MovedExpr
                           ? diag::warn_lifetime_safety_use_after_scope_moved
                           : diag::warn_lifetime_safety_use_after_scope;
@@ -123,7 +121,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
     S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here)
         << DestroyedSubject;
 
-    reportAliasingChain(AliasChain);
+    reportAliasingChain(ExprChain);
 
     S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here)
         << UseExpr->getSourceRange();
@@ -666,28 +664,31 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
     return CurrExpr->getSourceRange() != LastExpr->getSourceRange();
   }
 
-  void reportAliasingChain(llvm::ArrayRef<AliasChainEntry> OriginExprChain) {
+  void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {
     if (OriginExprChain.empty())
       return;
 
-    AliasChainEntry Last = OriginExprChain.back();
-    const Expr *LastExpr = Last.E;
+    const Expr *LastExpr = OriginExprChain.back();
     std::string IssueStr = getDiagSubjectDescription(LastExpr);
+    std::optional<LifetimeBoundParamInfo> HiddenLifetimeBound;
 
-    for (AliasChainEntry Curr : reverse(OriginExprChain.drop_back())) {
-      const Expr *CurrExpr = Curr.E;
+    for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
+      std::optional<LifetimeBoundParamInfo> LifetimeBound =
+          getLifetimeBoundParamInfoForCallArg(CurrExpr, LastExpr);
       if (!shouldShowInAliasChain(CurrExpr, LastExpr)) {
-        if (!Last.LifetimeBound && Curr.LifetimeBound)
-          Last.LifetimeBound = Curr.LifetimeBound;
+        if (!HiddenLifetimeBound && LifetimeBound)
+          HiddenLifetimeBound = LifetimeBound;
         continue;
       }
-      if (Last.LifetimeBound) {
-        bool IsImplicitObject = isa<const CXXMethodDecl *>(*Last.LifetimeBound);
+
+      if (!LifetimeBound)
+        LifetimeBound = HiddenLifetimeBound;
+
+      if (LifetimeBound) {
+        bool IsImplicitObject = isa<const CXXMethodDecl *>(*LifetimeBound);
         std::string ParamName;
         if (!IsImplicitObject) {
-          const auto *Param = cast<const ParmVarDecl *>(*Last.LifetimeBound);
-          assert(Param &&
-                 "lifetimebound parameter info should identify a parameter");
+          const auto *Param = cast<const ParmVarDecl *>(*LifetimeBound);
           ParamName = Param->getIdentifier()
                           ? "'" + Param->getNameAsString() + "'"
                           : "'<unnamed>'";
@@ -701,7 +702,7 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
                diag::note_lifetime_safety_aliases_storage)
             << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr)
             << IssueStr;
-      Last = Curr;
+      HiddenLifetimeBound.reset();
       LastExpr = CurrExpr;
     }
   }

>From a8aaa978b0154c25cba5d20081afd981d7f3be4b Mon Sep 17 00:00:00 2001
From: iitianpushkar <pushkarsingh0587 at gmail.com>
Date: Fri, 10 Jul 2026 20:55:03 +0530
Subject: [PATCH 4/8] diff cleanup

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

diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 1276b4824d4b1..f72f7f80abbc0 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -528,12 +528,12 @@ class LifetimeChecker {
   /// to declarations (rather than expressions) are skipped.
   llvm::SmallVector<const Expr *>
   getExprChain(llvm::ArrayRef<OriginID> OriginFlowChain) {
-    llvm::SmallVector<const Expr *> Result;
-    for (OriginID OID : OriginFlowChain)
+    llvm::SmallVector<const Expr *> rs;
+    for (const OriginID CurrOID : OriginFlowChain)
       if (const Expr *CurrExpr =
-              FactMgr.getOriginMgr().getOrigin(OID).getExpr())
-        Result.push_back(CurrExpr);
-    return Result;
+              FactMgr.getOriginMgr().getOrigin(CurrOID).getExpr())
+        rs.push_back(CurrExpr);
+    return rs;
   }
 };
 } // namespace

>From 05f8ef93446d2848bc31885d446015e8786dbba0 Mon Sep 17 00:00:00 2001
From: iitianpushkar <pushkarsingh0587 at gmail.com>
Date: Fri, 10 Jul 2026 20:57:13 +0530
Subject: [PATCH 5/8] minor diff cleanup

---
 .../Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index 85ff329774c7b..55f9e7bc171fd 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -15,7 +15,7 @@
 #include "llvm/ADT/PointerUnion.h"
 #include <optional>
 
-namespace clang::lifetimes {
+namespace clang ::lifetimes {
 
 // This function is needed because Decl::isInStdNamespace will return false for
 // iterators in some STL implementations due to them being defined in a

>From d1948901ea6ad4c1b74fdf696ce9409b75316d66 Mon Sep 17 00:00:00 2001
From: iitianpushkar <pushkarsingh0587 at gmail.com>
Date: Sun, 12 Jul 2026 13:17:19 +0530
Subject: [PATCH 6/8] created a shared helper for args collection logic and
 narrowed hiddenlifetimebound forwarding

---
 .../LifetimeSafety/LifetimeAnnotations.h      |  7 +++-
 .../LifetimeSafety/FactsGenerator.cpp         | 27 +++++-----------
 .../LifetimeSafety/LifetimeAnnotations.cpp    | 32 ++++++++++++++-----
 clang/lib/Sema/SemaLifetimeSafety.h           | 23 ++++++++++++-
 4 files changed, 60 insertions(+), 29 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index 55f9e7bc171fd..7ba4571eb69b9 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -13,9 +13,10 @@
 #include "clang/AST/Attr.h"
 #include "clang/AST/DeclCXX.h"
 #include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
 #include <optional>
 
-namespace clang ::lifetimes {
+namespace clang::lifetimes {
 
 // This function is needed because Decl::isInStdNamespace will return false for
 // iterators in some STL implementations due to them being defined in a
@@ -61,6 +62,10 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD);
 using LifetimeBoundParamInfo =
     llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
 
+/// Returns the arguments corresponding to Call, including the implicit object
+/// argument as argument 0 for instance member calls.
+llvm::SmallVector<const Expr *, 4> getLifetimeSafetyCallArgs(const Expr *Call);
+
 /// Returns the lifetimebound parameter corresponding to argument I.
 /// For instance methods, argument 0 is the implicit object argument.
 std::optional<LifetimeBoundParamInfo>
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 475c57cf38eb4..bb5ff58fa73cb 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -232,8 +232,7 @@ void FactsGenerator::VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
       return;
     }
   }
-  handleFunctionCall(CCE, CCE->getConstructor(),
-                     {CCE->getArgs(), CCE->getNumArgs()},
+  handleFunctionCall(CCE, CCE->getConstructor(), getLifetimeSafetyCallArgs(CCE),
                      /*IsGslConstruction=*/false);
 }
 
@@ -257,18 +256,13 @@ void FactsGenerator::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
       isGslOwnerType(MCE->getImplicitObjectArgument()->getType())) {
     // The argument is the implicit object itself.
     handleFunctionCall(MCE, MCE->getMethodDecl(),
-                       {MCE->getImplicitObjectArgument()},
+                       getLifetimeSafetyCallArgs(MCE),
                        /*IsGslConstruction=*/true);
     return;
   }
   if (const CXXMethodDecl *Method = MCE->getMethodDecl()) {
-    // Construct the argument list, with the implicit 'this' object as the
-    // first argument.
-    llvm::SmallVector<const Expr *, 4> Args;
-    Args.push_back(MCE->getImplicitObjectArgument());
-    Args.append(MCE->getArgs(), MCE->getArgs() + MCE->getNumArgs());
-
-    handleFunctionCall(MCE, Method, Args, /*IsGslConstruction=*/false);
+    handleFunctionCall(MCE, Method, getLifetimeSafetyCallArgs(MCE),
+                       /*IsGslConstruction=*/false);
   }
 }
 
@@ -289,8 +283,7 @@ void FactsGenerator::VisitMemberExpr(const MemberExpr *ME) {
 }
 
 void FactsGenerator::VisitCallExpr(const CallExpr *CE) {
-  handleFunctionCall(CE, CE->getDirectCallee(),
-                     {CE->getArgs(), CE->getNumArgs()});
+  handleFunctionCall(CE, CE->getDirectCallee(), getLifetimeSafetyCallArgs(CE));
 }
 
 void FactsGenerator::VisitCXXNullPtrLiteralExpr(
@@ -637,12 +630,8 @@ void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
     }
   }
 
-  ArrayRef<const Expr *> Args(OCE->getArgs(), OCE->getNumArgs());
-  // For `static operator()`, the first argument is the object argument,
-  // remove it from the argument list to avoid off-by-one errors.
-  if (OCE->getOperator() == OO_Call && OCE->getDirectCallee()->isStatic())
-    Args = Args.slice(1);
-  handleFunctionCall(OCE, OCE->getDirectCallee(), Args);
+  handleFunctionCall(OCE, OCE->getDirectCallee(),
+                     getLifetimeSafetyCallArgs(OCE));
 }
 
 void FactsGenerator::VisitCXXFunctionalCastExpr(
@@ -899,7 +888,7 @@ void FactsGenerator::handleGSLPointerConstruction(const CXXConstructExpr *CCE) {
     // This could be a new borrow.
     // TODO: Add code example here.
     handleFunctionCall(CCE, CCE->getConstructor(),
-                       {CCE->getArgs(), CCE->getNumArgs()},
+                       getLifetimeSafetyCallArgs(CCE),
                        /*IsGslConstruction=*/true);
   }
 }
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 6543009cd38b0..86d67fda6c587 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -149,6 +149,29 @@ static bool isExprInCallArg(const Expr *Arg, const Expr *Source) {
   return false;
 }
 
+llvm::SmallVector<const Expr *, 4> getLifetimeSafetyCallArgs(const Expr *Call) {
+  llvm::SmallVector<const Expr *, 4> Args;
+  if (!Call)
+    return Args;
+
+  Call = Call->IgnoreParenImpCasts();
+  if (const auto *CCE = dyn_cast<CXXConstructExpr>(Call)) {
+    Args.append(CCE->getArgs(), CCE->getArgs() + CCE->getNumArgs());
+  } else if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) {
+    Args.push_back(MCE->getImplicitObjectArgument());
+    Args.append(MCE->getArgs(), MCE->getArgs() + MCE->getNumArgs());
+  } else if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(Call)) {
+    Args.append(OCE->getArgs(), OCE->getArgs() + OCE->getNumArgs());
+    if (const FunctionDecl *FD = OCE->getDirectCallee();
+        FD && OCE->getOperator() == OO_Call && FD->isStatic())
+      Args.erase(Args.begin());
+  } else if (const auto *CE = dyn_cast<CallExpr>(Call)) {
+    Args.append(CE->getArgs(), CE->getArgs() + CE->getNumArgs());
+  }
+
+  return Args;
+}
+
 std::optional<LifetimeBoundParamInfo>
 getLifetimeBoundParamInfoForCallArg(const Expr *Call, const Expr *Source) {
   if (!Call || !Source)
@@ -157,28 +180,21 @@ getLifetimeBoundParamInfoForCallArg(const Expr *Call, const Expr *Source) {
   Call = Call->IgnoreParenImpCasts();
 
   const FunctionDecl *FD = nullptr;
-  llvm::SmallVector<const Expr *, 4> Args;
 
   if (const auto *CCE = dyn_cast<CXXConstructExpr>(Call)) {
     FD = CCE->getConstructor();
-    Args.append(CCE->getArgs(), CCE->getArgs() + CCE->getNumArgs());
   } else if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) {
     FD = MCE->getMethodDecl();
-    Args.push_back(MCE->getImplicitObjectArgument());
-    Args.append(MCE->getArgs(), MCE->getArgs() + MCE->getNumArgs());
   } else if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(Call)) {
     FD = OCE->getDirectCallee();
-    Args.append(OCE->getArgs(), OCE->getArgs() + OCE->getNumArgs());
-    if (FD && OCE->getOperator() == OO_Call && FD->isStatic())
-      Args.erase(Args.begin());
   } else if (const auto *CE = dyn_cast<CallExpr>(Call)) {
     FD = CE->getDirectCallee();
-    Args.append(CE->getArgs(), CE->getArgs() + CE->getNumArgs());
   }
 
   if (!FD)
     return std::nullopt;
 
+  llvm::SmallVector<const Expr *, 4> Args = getLifetimeSafetyCallArgs(Call);
   for (unsigned I = 0; I < Args.size(); ++I)
     if (isExprInCallArg(Args[I], Source))
       if (std::optional<LifetimeBoundParamInfo> Info =
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index f916bcc4db372..97f8f3ebb3885 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -664,6 +664,26 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
     return CurrExpr->getSourceRange() != LastExpr->getSourceRange();
   }
 
+  bool shouldCarryHiddenLifetimeBound(const Expr *HiddenExpr,
+                                      const Expr *LastExpr) {
+    HiddenExpr = HiddenExpr->IgnoreImpCasts();
+    LastExpr = LastExpr->IgnoreImpCasts();
+
+    SourceRange HiddenRange = HiddenExpr->getSourceRange();
+    SourceRange LastRange = LastExpr->getSourceRange();
+    if (HiddenRange == LastRange)
+      return true;
+    if (HiddenRange.isInvalid() || LastRange.isInvalid())
+      return false;
+
+    const SourceManager &SM = S.getSourceManager();
+    auto IsBeforeOrEqual = [&SM](SourceLocation LHS, SourceLocation RHS) {
+      return LHS == RHS || SM.isBeforeInTranslationUnit(LHS, RHS);
+    };
+    return IsBeforeOrEqual(HiddenRange.getBegin(), LastRange.getBegin()) &&
+           IsBeforeOrEqual(LastRange.getEnd(), HiddenRange.getEnd());
+  }
+
   void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {
     if (OriginExprChain.empty())
       return;
@@ -676,7 +696,8 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
       std::optional<LifetimeBoundParamInfo> LifetimeBound =
           getLifetimeBoundParamInfoForCallArg(CurrExpr, LastExpr);
       if (!shouldShowInAliasChain(CurrExpr, LastExpr)) {
-        if (!HiddenLifetimeBound && LifetimeBound)
+        if (!HiddenLifetimeBound && LifetimeBound &&
+            shouldCarryHiddenLifetimeBound(CurrExpr, LastExpr))
           HiddenLifetimeBound = LifetimeBound;
         continue;
       }

>From bd405d56f68bce33bd7efcb702b1260e23ff9204 Mon Sep 17 00:00:00 2001
From: iitianpushkar <pushkarsingh0587 at gmail.com>
Date: Mon, 13 Jul 2026 13:02:46 +0530
Subject: [PATCH 7/8] shared helper now returns callee with args

---
 .../LifetimeSafety/LifetimeAnnotations.h      | 11 +++-
 .../LifetimeSafety/FactsGenerator.cpp         | 23 ++++----
 .../LifetimeSafety/LifetimeAnnotations.cpp    | 55 ++++++++-----------
 3 files changed, 43 insertions(+), 46 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index 7ba4571eb69b9..9195f9a0122cb 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -62,9 +62,14 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD);
 using LifetimeBoundParamInfo =
     llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
 
-/// Returns the arguments corresponding to Call, including the implicit object
-/// argument as argument 0 for instance member calls.
-llvm::SmallVector<const Expr *, 4> getLifetimeSafetyCallArgs(const Expr *Call);
+struct LifetimeSafetyCallInfo {
+  const FunctionDecl *FD = nullptr;
+  llvm::SmallVector<const Expr *, 4> Args;
+};
+
+/// Returns the callee and arguments corresponding to Call. For instance member
+/// calls, Args includes the implicit object argument as argument 0.
+LifetimeSafetyCallInfo getLifetimeSafetyCallInfo(const Expr *Call);
 
 /// Returns the lifetimebound parameter corresponding to argument I.
 /// For instance methods, argument 0 is the implicit object argument.
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index bb5ff58fa73cb..7673569593088 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -232,7 +232,8 @@ void FactsGenerator::VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
       return;
     }
   }
-  handleFunctionCall(CCE, CCE->getConstructor(), getLifetimeSafetyCallArgs(CCE),
+  LifetimeSafetyCallInfo CallInfo = getLifetimeSafetyCallInfo(CCE);
+  handleFunctionCall(CCE, CallInfo.FD, CallInfo.Args,
                      /*IsGslConstruction=*/false);
 }
 
@@ -255,13 +256,14 @@ void FactsGenerator::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
       isa_and_present<CXXConversionDecl>(MCE->getCalleeDecl()) &&
       isGslOwnerType(MCE->getImplicitObjectArgument()->getType())) {
     // The argument is the implicit object itself.
-    handleFunctionCall(MCE, MCE->getMethodDecl(),
-                       getLifetimeSafetyCallArgs(MCE),
+    LifetimeSafetyCallInfo CallInfo = getLifetimeSafetyCallInfo(MCE);
+    handleFunctionCall(MCE, CallInfo.FD, CallInfo.Args,
                        /*IsGslConstruction=*/true);
     return;
   }
-  if (const CXXMethodDecl *Method = MCE->getMethodDecl()) {
-    handleFunctionCall(MCE, Method, getLifetimeSafetyCallArgs(MCE),
+  if (MCE->getMethodDecl()) {
+    LifetimeSafetyCallInfo CallInfo = getLifetimeSafetyCallInfo(MCE);
+    handleFunctionCall(MCE, CallInfo.FD, CallInfo.Args,
                        /*IsGslConstruction=*/false);
   }
 }
@@ -283,7 +285,8 @@ void FactsGenerator::VisitMemberExpr(const MemberExpr *ME) {
 }
 
 void FactsGenerator::VisitCallExpr(const CallExpr *CE) {
-  handleFunctionCall(CE, CE->getDirectCallee(), getLifetimeSafetyCallArgs(CE));
+  LifetimeSafetyCallInfo CallInfo = getLifetimeSafetyCallInfo(CE);
+  handleFunctionCall(CE, CallInfo.FD, CallInfo.Args);
 }
 
 void FactsGenerator::VisitCXXNullPtrLiteralExpr(
@@ -630,8 +633,8 @@ void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
     }
   }
 
-  handleFunctionCall(OCE, OCE->getDirectCallee(),
-                     getLifetimeSafetyCallArgs(OCE));
+  LifetimeSafetyCallInfo CallInfo = getLifetimeSafetyCallInfo(OCE);
+  handleFunctionCall(OCE, CallInfo.FD, CallInfo.Args);
 }
 
 void FactsGenerator::VisitCXXFunctionalCastExpr(
@@ -887,8 +890,8 @@ void FactsGenerator::handleGSLPointerConstruction(const CXXConstructExpr *CCE) {
   } else {
     // This could be a new borrow.
     // TODO: Add code example here.
-    handleFunctionCall(CCE, CCE->getConstructor(),
-                       getLifetimeSafetyCallArgs(CCE),
+    LifetimeSafetyCallInfo CallInfo = getLifetimeSafetyCallInfo(CCE);
+    handleFunctionCall(CCE, CallInfo.FD, CallInfo.Args,
                        /*IsGslConstruction=*/true);
   }
 }
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 86d67fda6c587..d26857a2a0bd7 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -149,27 +149,30 @@ static bool isExprInCallArg(const Expr *Arg, const Expr *Source) {
   return false;
 }
 
-llvm::SmallVector<const Expr *, 4> getLifetimeSafetyCallArgs(const Expr *Call) {
-  llvm::SmallVector<const Expr *, 4> Args;
+LifetimeSafetyCallInfo getLifetimeSafetyCallInfo(const Expr *Call) {
+  LifetimeSafetyCallInfo Info;
   if (!Call)
-    return Args;
+    return Info;
 
   Call = Call->IgnoreParenImpCasts();
   if (const auto *CCE = dyn_cast<CXXConstructExpr>(Call)) {
-    Args.append(CCE->getArgs(), CCE->getArgs() + CCE->getNumArgs());
+    Info.FD = CCE->getConstructor();
+    Info.Args.append(CCE->getArgs(), CCE->getArgs() + CCE->getNumArgs());
   } else if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) {
-    Args.push_back(MCE->getImplicitObjectArgument());
-    Args.append(MCE->getArgs(), MCE->getArgs() + MCE->getNumArgs());
+    Info.FD = MCE->getMethodDecl();
+    Info.Args.push_back(MCE->getImplicitObjectArgument());
+    Info.Args.append(MCE->getArgs(), MCE->getArgs() + MCE->getNumArgs());
   } else if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(Call)) {
-    Args.append(OCE->getArgs(), OCE->getArgs() + OCE->getNumArgs());
-    if (const FunctionDecl *FD = OCE->getDirectCallee();
-        FD && OCE->getOperator() == OO_Call && FD->isStatic())
-      Args.erase(Args.begin());
+    Info.FD = OCE->getDirectCallee();
+    Info.Args.append(OCE->getArgs(), OCE->getArgs() + OCE->getNumArgs());
+    if (Info.FD && OCE->getOperator() == OO_Call && Info.FD->isStatic())
+      Info.Args.erase(Info.Args.begin());
   } else if (const auto *CE = dyn_cast<CallExpr>(Call)) {
-    Args.append(CE->getArgs(), CE->getArgs() + CE->getNumArgs());
+    Info.FD = CE->getDirectCallee();
+    Info.Args.append(CE->getArgs(), CE->getArgs() + CE->getNumArgs());
   }
 
-  return Args;
+  return Info;
 }
 
 std::optional<LifetimeBoundParamInfo>
@@ -177,29 +180,15 @@ getLifetimeBoundParamInfoForCallArg(const Expr *Call, const Expr *Source) {
   if (!Call || !Source)
     return std::nullopt;
 
-  Call = Call->IgnoreParenImpCasts();
-
-  const FunctionDecl *FD = nullptr;
-
-  if (const auto *CCE = dyn_cast<CXXConstructExpr>(Call)) {
-    FD = CCE->getConstructor();
-  } else if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) {
-    FD = MCE->getMethodDecl();
-  } else if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(Call)) {
-    FD = OCE->getDirectCallee();
-  } else if (const auto *CE = dyn_cast<CallExpr>(Call)) {
-    FD = CE->getDirectCallee();
-  }
-
-  if (!FD)
+  LifetimeSafetyCallInfo Info = getLifetimeSafetyCallInfo(Call);
+  if (!Info.FD)
     return std::nullopt;
 
-  llvm::SmallVector<const Expr *, 4> Args = getLifetimeSafetyCallArgs(Call);
-  for (unsigned I = 0; I < Args.size(); ++I)
-    if (isExprInCallArg(Args[I], Source))
-      if (std::optional<LifetimeBoundParamInfo> Info =
-              getLifetimeBoundParamInfo(FD, I))
-        return Info;
+  for (unsigned I = 0; I < Info.Args.size(); ++I)
+    if (isExprInCallArg(Info.Args[I], Source))
+      if (std::optional<LifetimeBoundParamInfo> ParamInfo =
+              getLifetimeBoundParamInfo(Info.FD, I))
+        return ParamInfo;
 
   return std::nullopt;
 }

>From 12916cbc23ba46cbb5ff59186f1765e84eeb24e0 Mon Sep 17 00:00:00 2001
From: iitianpushkar <pushkarsingh0587 at gmail.com>
Date: Fri, 17 Jul 2026 16:56:59 +0530
Subject: [PATCH 8/8] factored the full ShouldTrackArg logic

---
 .../LifetimeSafety/LifetimeAnnotations.h      | 40 +++++----
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 +-
 .../LifetimeSafety/FactsGenerator.cpp         | 49 ++++-------
 .../LifetimeSafety/LifetimeAnnotations.cpp    | 86 +++++++++++++------
 clang/lib/Sema/SemaLifetimeSafety.h           | 44 ++--------
 clang/test/Sema/LifetimeSafety/safety.cpp     |  2 +-
 6 files changed, 110 insertions(+), 114 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index 9195f9a0122cb..9a5928c016cc2 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -12,11 +12,13 @@
 
 #include "clang/AST/Attr.h"
 #include "clang/AST/DeclCXX.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/PointerUnion.h"
 #include "llvm/ADT/SmallVector.h"
 #include <optional>
+#include <utility>
 
-namespace clang::lifetimes {
+namespace clang ::lifetimes {
 
 // This function is needed because Decl::isInStdNamespace will return false for
 // iterators in some STL implementations due to them being defined in a
@@ -62,24 +64,32 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD);
 using LifetimeBoundParamInfo =
     llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
 
-struct LifetimeSafetyCallInfo {
-  const FunctionDecl *FD = nullptr;
-  llvm::SmallVector<const Expr *, 4> Args;
+enum class TrackedArgKind {
+  None,
+  ExplicitLifetimeBound,
+  Inferred,
+};
+
+struct TrackedArgInfo {
+  TrackedArgKind Kind = TrackedArgKind::None;
+  std::optional<LifetimeBoundParamInfo> ParamInfo;
 };
 
 /// Returns the callee and arguments corresponding to Call. For instance member
 /// calls, Args includes the implicit object argument as argument 0.
-LifetimeSafetyCallInfo getLifetimeSafetyCallInfo(const Expr *Call);
-
-/// Returns the lifetimebound parameter corresponding to argument I.
-/// For instance methods, argument 0 is the implicit object argument.
-std::optional<LifetimeBoundParamInfo>
-getLifetimeBoundParamInfo(const FunctionDecl *FD, unsigned I);
-
-/// Returns the lifetimebound parameter in Call whose argument corresponds to
-/// Source, if any.
-std::optional<LifetimeBoundParamInfo>
-getLifetimeBoundParamInfoForCallArg(const Expr *Call, const Expr *Source);
+std::pair<const FunctionDecl *, llvm::SmallVector<const Expr *, 4>>
+getFunctionCallInfo(const Expr *Call);
+
+/// Returns whether argument I should be tracked for lifetime safety, and
+/// whether the tracking reason is an explicit lifetimebound annotation or an
+/// inferred/heuristic rule.
+TrackedArgInfo getTrackedArgInfo(const FunctionDecl *FD,
+                                 llvm::ArrayRef<const Expr *> Args, unsigned I);
+
+/// Returns lifetime safety tracking info for the call argument containing
+/// Source.
+std::optional<TrackedArgInfo> getTrackingInfoForCallArg(const Expr *Call,
+                                                        const Expr *Source);
 
 // Returns true if the implicit object argument (this) of a method call should
 // be tracked for GSL lifetime analysis. This applies to STL methods that return
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e55e6ee1aa939..b6cbd52fdbd16 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11079,8 +11079,7 @@ def note_lifetime_safety_escapes_to_static_storage_here: Note<"escapes to this s
 def note_lifetime_safety_lifetimebound_here: Note<"'lifetimebound' attribute appears here on the definition">;
 def note_lifetime_safety_aliases_storage : Note<"%0 aliases the storage of %1">;
 def note_lifetime_safety_aliases_storage_lifetimebound : Note<
-  "%0 aliases the storage of %1 because %select{parameter %3|the implicit "
-  "object parameter}2 is marked 'lifetimebound'">;
+  "%0 aliases the storage of %1 because %select{parameter %3|the implicit object parameter}2 is marked 'lifetimebound'">;
 
 def warn_lifetime_safety_intra_tu_param_suggestion
     : Warning<"parameter in intra-TU function should be marked "
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 7673569593088..c3b0a06543678 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -232,8 +232,8 @@ void FactsGenerator::VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
       return;
     }
   }
-  LifetimeSafetyCallInfo CallInfo = getLifetimeSafetyCallInfo(CCE);
-  handleFunctionCall(CCE, CallInfo.FD, CallInfo.Args,
+  auto [FD, Args] = getFunctionCallInfo(CCE);
+  handleFunctionCall(CCE, FD, Args,
                      /*IsGslConstruction=*/false);
 }
 
@@ -255,17 +255,13 @@ void FactsGenerator::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
   if (isGslPointerType(MCE->getType()) &&
       isa_and_present<CXXConversionDecl>(MCE->getCalleeDecl()) &&
       isGslOwnerType(MCE->getImplicitObjectArgument()->getType())) {
-    // The argument is the implicit object itself.
-    LifetimeSafetyCallInfo CallInfo = getLifetimeSafetyCallInfo(MCE);
-    handleFunctionCall(MCE, CallInfo.FD, CallInfo.Args,
+    auto [FD, Args] = getFunctionCallInfo(MCE);
+    handleFunctionCall(MCE, FD, Args,
                        /*IsGslConstruction=*/true);
     return;
   }
-  if (MCE->getMethodDecl()) {
-    LifetimeSafetyCallInfo CallInfo = getLifetimeSafetyCallInfo(MCE);
-    handleFunctionCall(MCE, CallInfo.FD, CallInfo.Args,
-                       /*IsGslConstruction=*/false);
-  }
+  auto [FD, Args] = getFunctionCallInfo(MCE);
+  handleFunctionCall(MCE, FD, Args, /*IsGslConstruction=*/false);
 }
 
 void FactsGenerator::VisitMemberExpr(const MemberExpr *ME) {
@@ -285,8 +281,8 @@ void FactsGenerator::VisitMemberExpr(const MemberExpr *ME) {
 }
 
 void FactsGenerator::VisitCallExpr(const CallExpr *CE) {
-  LifetimeSafetyCallInfo CallInfo = getLifetimeSafetyCallInfo(CE);
-  handleFunctionCall(CE, CallInfo.FD, CallInfo.Args);
+  auto [FD, Args] = getFunctionCallInfo(CE);
+  handleFunctionCall(CE, FD, Args);
 }
 
 void FactsGenerator::VisitCXXNullPtrLiteralExpr(
@@ -633,8 +629,8 @@ void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
     }
   }
 
-  LifetimeSafetyCallInfo CallInfo = getLifetimeSafetyCallInfo(OCE);
-  handleFunctionCall(OCE, CallInfo.FD, CallInfo.Args);
+  auto [FD, Args] = getFunctionCallInfo(OCE);
+  handleFunctionCall(OCE, FD, Args);
 }
 
 void FactsGenerator::VisitCXXFunctionalCastExpr(
@@ -890,8 +886,8 @@ void FactsGenerator::handleGSLPointerConstruction(const CXXConstructExpr *CCE) {
   } else {
     // This could be a new borrow.
     // TODO: Add code example here.
-    LifetimeSafetyCallInfo CallInfo = getLifetimeSafetyCallInfo(CCE);
-    handleFunctionCall(CCE, CallInfo.FD, CallInfo.Args,
+    auto [FD, Args] = getFunctionCallInfo(CCE);
+    handleFunctionCall(CCE, FD, Args,
                        /*IsGslConstruction=*/true);
   }
 }
@@ -1088,20 +1084,6 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
     flow(CallList, getOriginsList(*Args[0]), /*Kill=*/true);
     return;
   }
-  auto ShouldTrackArg = [FD, &Args](unsigned I) -> bool {
-    if (getLifetimeBoundParamInfo(FD, I))
-      return true;
-    if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
-        Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD))
-      if (I == 0)
-        return shouldTrackImplicitObjectArg(
-            *Args[0], Method, /*RunningUnderLifetimeSafety=*/true);
-    if (I == 0 && shouldTrackFirstArgument(FD))
-      return true;
-    if (I == 1 && shouldTrackSecondArgument(FD))
-      return true;
-    return false;
-  };
   auto shouldTrackPointerImplicitObjectArg = [FD, &Args](unsigned I) -> bool {
     const auto *Method = dyn_cast<CXXMethodDecl>(FD);
     if (!Method || !Method->isInstance())
@@ -1118,7 +1100,8 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
     OriginList *ArgList = getOriginsList(*Args[I]);
     if (!ArgList)
       continue;
-    bool TrackArg = ShouldTrackArg(I);
+    TrackedArgInfo ArgInfo = getTrackedArgInfo(FD, Args, I);
+    bool ShouldTrackArg = ArgInfo.Kind != TrackedArgKind::None;
     if (IsGslConstruction) {
       // TODO: document with code example.
       // std::string_view(const std::string_view& from)
@@ -1134,7 +1117,7 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
             CallList->getOuterOriginID(), ArgList->getOuterOriginID(),
             KillSrc));
         KillSrc = false;
-      } else if (TrackArg) {
+      } else if (ShouldTrackArg) {
         // Only flow the outer origin here. For lifetimebound args in
         // gsl::Pointer construction, we do not have enough information to
         // safely match inner origins, so the source and
@@ -1154,7 +1137,7 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
           CallList->getOuterOriginID(),
           ArgList->peelOuterOrigin()->getOuterOriginID(), KillSrc));
       KillSrc = false;
-    } else if (TrackArg) {
+    } else if (ShouldTrackArg) {
       // Lifetimebound on a non-GSL-ctor function means the returned
       // pointer/reference itself must not outlive the arguments. This
       // only constrains the top-level origin.
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index d26857a2a0bd7..87551febbd3e0 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -107,8 +107,8 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
   return isNormalAssignmentOperator(FD);
 }
 
-std::optional<LifetimeBoundParamInfo>
-getLifetimeBoundParamInfo(const FunctionDecl *FD, unsigned I) {
+static std::optional<LifetimeBoundParamInfo>
+getExplicitLifetimeBoundParamInfo(const FunctionDecl *FD, unsigned I) {
   FD = getDeclWithMergedLifetimeBoundAttrs(FD);
   if (!FD)
     return std::nullopt;
@@ -117,7 +117,7 @@ getLifetimeBoundParamInfo(const FunctionDecl *FD, unsigned I) {
   if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
       Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) {
     if (I == 0)
-      return implicitObjectParamIsLifetimeBound(Method)
+      return getImplicitObjectParamLifetimeBoundAttr(Method)
                  ? std::optional<LifetimeBoundParamInfo>(
                        LifetimeBoundParamInfo(Method))
                  : std::nullopt;
@@ -149,46 +149,78 @@ static bool isExprInCallArg(const Expr *Arg, const Expr *Source) {
   return false;
 }
 
-LifetimeSafetyCallInfo getLifetimeSafetyCallInfo(const Expr *Call) {
-  LifetimeSafetyCallInfo Info;
+std::pair<const FunctionDecl *, llvm::SmallVector<const Expr *, 4>>
+getFunctionCallInfo(const Expr *Call) {
+  const FunctionDecl *FD = nullptr;
+  llvm::SmallVector<const Expr *, 4> Args;
   if (!Call)
-    return Info;
+    return {FD, Args};
 
   Call = Call->IgnoreParenImpCasts();
   if (const auto *CCE = dyn_cast<CXXConstructExpr>(Call)) {
-    Info.FD = CCE->getConstructor();
-    Info.Args.append(CCE->getArgs(), CCE->getArgs() + CCE->getNumArgs());
+    FD = CCE->getConstructor();
+    Args.append(CCE->getArgs(), CCE->getArgs() + CCE->getNumArgs());
   } else if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) {
-    Info.FD = MCE->getMethodDecl();
-    Info.Args.push_back(MCE->getImplicitObjectArgument());
-    Info.Args.append(MCE->getArgs(), MCE->getArgs() + MCE->getNumArgs());
+    FD = MCE->getMethodDecl();
+    Args.push_back(MCE->getImplicitObjectArgument());
+    Args.append(MCE->getArgs(), MCE->getArgs() + MCE->getNumArgs());
   } else if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(Call)) {
-    Info.FD = OCE->getDirectCallee();
-    Info.Args.append(OCE->getArgs(), OCE->getArgs() + OCE->getNumArgs());
-    if (Info.FD && OCE->getOperator() == OO_Call && Info.FD->isStatic())
-      Info.Args.erase(Info.Args.begin());
+    FD = OCE->getDirectCallee();
+    Args.append(OCE->getArgs(), OCE->getArgs() + OCE->getNumArgs());
+    // For `static operator()`, the first argument is the object argument,
+    // remove it from the argument list to avoid off-by-one errors.
+    if (FD && OCE->getOperator() == OO_Call && FD->isStatic())
+      Args.erase(Args.begin());
   } else if (const auto *CE = dyn_cast<CallExpr>(Call)) {
-    Info.FD = CE->getDirectCallee();
-    Info.Args.append(CE->getArgs(), CE->getArgs() + CE->getNumArgs());
+    FD = CE->getDirectCallee();
+    Args.append(CE->getArgs(), CE->getArgs() + CE->getNumArgs());
   }
 
-  return Info;
+  return {FD, Args};
 }
 
-std::optional<LifetimeBoundParamInfo>
-getLifetimeBoundParamInfoForCallArg(const Expr *Call, const Expr *Source) {
+TrackedArgInfo getTrackedArgInfo(const FunctionDecl *FD,
+                                 llvm::ArrayRef<const Expr *> Args,
+                                 unsigned I) {
+  FD = getDeclWithMergedLifetimeBoundAttrs(FD);
+  if (!FD || I >= Args.size())
+    return {};
+
+  if (std::optional<LifetimeBoundParamInfo> Info =
+          getExplicitLifetimeBoundParamInfo(FD, I))
+    return {TrackedArgKind::ExplicitLifetimeBound, Info};
+
+  if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
+      Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD))
+    if (I == 0 &&
+        (isNormalAssignmentOperator(Method) ||
+         shouldTrackImplicitObjectArg(*Args[0], Method,
+                                      /*RunningUnderLifetimeSafety=*/true)))
+      return {TrackedArgKind::Inferred, std::nullopt};
+
+  if (I == 0 && shouldTrackFirstArgument(FD))
+    return {TrackedArgKind::Inferred, std::nullopt};
+  if (I == 1 && shouldTrackSecondArgument(FD))
+    return {TrackedArgKind::Inferred, std::nullopt};
+
+  return {};
+}
+
+std::optional<TrackedArgInfo> getTrackingInfoForCallArg(const Expr *Call,
+                                                        const Expr *Source) {
   if (!Call || !Source)
     return std::nullopt;
 
-  LifetimeSafetyCallInfo Info = getLifetimeSafetyCallInfo(Call);
-  if (!Info.FD)
+  auto [FD, Args] = getFunctionCallInfo(Call);
+  if (!FD)
     return std::nullopt;
 
-  for (unsigned I = 0; I < Info.Args.size(); ++I)
-    if (isExprInCallArg(Info.Args[I], Source))
-      if (std::optional<LifetimeBoundParamInfo> ParamInfo =
-              getLifetimeBoundParamInfo(Info.FD, I))
-        return ParamInfo;
+  for (unsigned I = 0; I < Args.size(); ++I)
+    if (isExprInCallArg(Args[I], Source)) {
+      TrackedArgInfo Info = getTrackedArgInfo(FD, Args, I);
+      if (Info.Kind != TrackedArgKind::None)
+        return Info;
+    }
 
   return std::nullopt;
 }
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 97f8f3ebb3885..0d9170f0973c7 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -664,52 +664,25 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
     return CurrExpr->getSourceRange() != LastExpr->getSourceRange();
   }
 
-  bool shouldCarryHiddenLifetimeBound(const Expr *HiddenExpr,
-                                      const Expr *LastExpr) {
-    HiddenExpr = HiddenExpr->IgnoreImpCasts();
-    LastExpr = LastExpr->IgnoreImpCasts();
-
-    SourceRange HiddenRange = HiddenExpr->getSourceRange();
-    SourceRange LastRange = LastExpr->getSourceRange();
-    if (HiddenRange == LastRange)
-      return true;
-    if (HiddenRange.isInvalid() || LastRange.isInvalid())
-      return false;
-
-    const SourceManager &SM = S.getSourceManager();
-    auto IsBeforeOrEqual = [&SM](SourceLocation LHS, SourceLocation RHS) {
-      return LHS == RHS || SM.isBeforeInTranslationUnit(LHS, RHS);
-    };
-    return IsBeforeOrEqual(HiddenRange.getBegin(), LastRange.getBegin()) &&
-           IsBeforeOrEqual(LastRange.getEnd(), HiddenRange.getEnd());
-  }
-
   void reportAliasingChain(llvm::ArrayRef<const Expr *> OriginExprChain) {
     if (OriginExprChain.empty())
       return;
 
     const Expr *LastExpr = OriginExprChain.back();
     std::string IssueStr = getDiagSubjectDescription(LastExpr);
-    std::optional<LifetimeBoundParamInfo> HiddenLifetimeBound;
 
     for (const Expr *CurrExpr : reverse(OriginExprChain.drop_back())) {
-      std::optional<LifetimeBoundParamInfo> LifetimeBound =
-          getLifetimeBoundParamInfoForCallArg(CurrExpr, LastExpr);
-      if (!shouldShowInAliasChain(CurrExpr, LastExpr)) {
-        if (!HiddenLifetimeBound && LifetimeBound &&
-            shouldCarryHiddenLifetimeBound(CurrExpr, LastExpr))
-          HiddenLifetimeBound = LifetimeBound;
+      if (!shouldShowInAliasChain(CurrExpr, LastExpr))
         continue;
-      }
-
-      if (!LifetimeBound)
-        LifetimeBound = HiddenLifetimeBound;
-
-      if (LifetimeBound) {
-        bool IsImplicitObject = isa<const CXXMethodDecl *>(*LifetimeBound);
+      std::optional<TrackedArgInfo> ArgInfo =
+          getTrackingInfoForCallArg(CurrExpr, LastExpr);
+      if (ArgInfo && ArgInfo->Kind == TrackedArgKind::ExplicitLifetimeBound &&
+          ArgInfo->ParamInfo) {
+        LifetimeBoundParamInfo ParamInfo = *ArgInfo->ParamInfo;
+        bool IsImplicitObject = isa<const CXXMethodDecl *>(ParamInfo);
         std::string ParamName;
         if (!IsImplicitObject) {
-          const auto *Param = cast<const ParmVarDecl *>(*LifetimeBound);
+          const auto *Param = cast<const ParmVarDecl *>(ParamInfo);
           ParamName = Param->getIdentifier()
                           ? "'" + Param->getNameAsString() + "'"
                           : "'<unnamed>'";
@@ -723,7 +696,6 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
                diag::note_lifetime_safety_aliases_storage)
             << CurrExpr->getSourceRange() << getDiagSubjectDescription(CurrExpr)
             << IssueStr;
-      HiddenLifetimeBound.reset();
       LastExpr = CurrExpr;
     }
   }
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp
index 3b626cb234796..bd737698a9808 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -3038,7 +3038,7 @@ void nested_local_pointer() {
   {
     Bar v;
     p = Pointer(v);     // expected-warning {{local variable 'v' does not live long enough}}
-    pp = Pointer(p);    // expected-note {{local variable 'p' aliases the storage of local variable 'v' because parameter 'bar' is marked 'lifetimebound'}}
+    pp = Pointer(p);    // expected-note {{local variable 'p' aliases the storage of local variable 'v'}}
     ppp = Pointer(pp);  // expected-note {{local variable 'pp' aliases the storage of local variable 'v'}}
   }                     // expected-note {{local variable 'v' is destroyed here}}
   use(***ppp);          // expected-note {{later used here}}



More information about the cfe-commits mailing list