[clang] [llvm] [LifetimeSafety] Reorganize code into modular components (PR #162474)

Utkarsh Saxena via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 9 06:10:56 PDT 2025


https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/162474

>From 6f87445434cda9ec128a9da9c034ea63ca206af7 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Wed, 8 Oct 2025 12:32:20 +0000
Subject: [PATCH 01/10] [LifetimeSafety] Refactor the analysis into smaller
 files

---
 .../Analyses/LifetimeSafety/Checker.h         |  129 ++
 .../Analyses/LifetimeSafety/Dataflow.h        |  180 ++
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  620 +++++++
 .../LifetimeAnnotations.h                     |    0
 .../{ => LifetimeSafety}/LifetimeSafety.h     |    0
 .../Analyses/LifetimeSafety/LiveOrigins.h     |  216 +++
 .../Analyses/LifetimeSafety/LoanPropagation.h |  133 ++
 .../Analysis/Analyses/LifetimeSafety/Loans.h  |   64 +
 .../Analyses/LifetimeSafety/Origins.h         |  131 ++
 .../Analysis/Analyses/LifetimeSafety/Utils.h  |   74 +
 clang/lib/Analysis/CMakeLists.txt             |    3 +-
 clang/lib/Analysis/LifetimeSafety.cpp         | 1546 -----------------
 .../Analysis/LifetimeSafety/CMakeLists.txt    |   14 +
 .../LifetimeAnnotations.cpp                   |    2 +-
 .../LifetimeSafety/LifetimeSafety.cpp         |  147 ++
 clang/lib/Sema/AnalysisBasedWarnings.cpp      |    2 +-
 clang/lib/Sema/CMakeLists.txt                 |    1 +
 clang/lib/Sema/CheckExprLifetime.cpp          |    2 +-
 clang/lib/Sema/SemaAPINotes.cpp               |    2 +-
 .../unittests/Analysis/LifetimeSafetyTest.cpp |    2 +-
 20 files changed, 1715 insertions(+), 1553 deletions(-)
 create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
 create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety/Dataflow.h
 create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
 rename clang/include/clang/Analysis/Analyses/{ => LifetimeSafety}/LifetimeAnnotations.h (100%)
 rename clang/include/clang/Analysis/Analyses/{ => LifetimeSafety}/LifetimeSafety.h (100%)
 create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h
 create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
 create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h
 create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
 create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
 delete mode 100644 clang/lib/Analysis/LifetimeSafety.cpp
 create mode 100644 clang/lib/Analysis/LifetimeSafety/CMakeLists.txt
 rename clang/lib/Analysis/{ => LifetimeSafety}/LifetimeAnnotations.cpp (97%)
 create mode 100644 clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
new file mode 100644
index 0000000000000..f5bcb83fbac89
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
@@ -0,0 +1,129 @@
+//===- Checker.h - C++ Lifetime Safety Analysis -*----------- C++-*-=========//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// TODO: Complete me.
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/TimeProfiler.h"
+
+namespace clang::lifetimes {
+namespace internal {
+
+// ========================================================================= //
+//                       Lifetime checker and Error reporter
+// ========================================================================= //
+
+/// Struct to store the complete context for a potential lifetime violation.
+struct PendingWarning {
+  SourceLocation ExpiryLoc; // Where the loan expired.
+  const Expr *UseExpr;      // Where the origin holding this loan was used.
+  Confidence ConfidenceLevel;
+};
+
+class LifetimeChecker {
+private:
+  llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
+  LoanPropagationAnalysis &LoanPropagation;
+  LiveOriginAnalysis &LiveOrigins;
+  FactManager &FactMgr;
+  AnalysisDeclContext &ADC;
+  LifetimeSafetyReporter *Reporter;
+
+public:
+  LifetimeChecker(LoanPropagationAnalysis &LPA, LiveOriginAnalysis &LOA,
+                  FactManager &FM, AnalysisDeclContext &ADC,
+                  LifetimeSafetyReporter *Reporter)
+      : LoanPropagation(LPA), LiveOrigins(LOA), FactMgr(FM), ADC(ADC),
+        Reporter(Reporter) {}
+
+  void run() {
+    llvm::TimeTraceScope TimeProfile("LifetimeChecker");
+    for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
+      for (const Fact *F : FactMgr.getFacts(B))
+        if (const auto *EF = F->getAs<ExpireFact>())
+          checkExpiry(EF);
+    issuePendingWarnings();
+  }
+
+  /// Checks for use-after-free errors when a loan expires.
+  ///
+  /// This method examines all live origins at the expiry point and determines
+  /// if any of them hold the expiring loan. If so, it creates a pending
+  /// warning with the appropriate confidence level based on the liveness
+  /// information. The confidence reflects whether the origin is definitely
+  /// or maybe live at this point.
+  ///
+  /// Note: This implementation considers only the confidence of origin
+  /// liveness. Future enhancements could also consider the confidence of loan
+  /// propagation (e.g., a loan may only be held on some execution paths).
+  void checkExpiry(const ExpireFact *EF) {
+    LoanID ExpiredLoan = EF->getLoanID();
+    LivenessMap Origins = LiveOrigins.getLiveOrigins(EF);
+    Confidence CurConfidence = Confidence::None;
+    const UseFact *BadUse = nullptr;
+    for (auto &[OID, LiveInfo] : Origins) {
+      LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
+      if (!HeldLoans.contains(ExpiredLoan))
+        continue;
+      // Loan is defaulted.
+      Confidence NewConfidence = livenessKindToConfidence(LiveInfo.Kind);
+      if (CurConfidence < NewConfidence) {
+        CurConfidence = NewConfidence;
+        BadUse = LiveInfo.CausingUseFact;
+      }
+    }
+    if (!BadUse)
+      return;
+    // We have a use-after-free.
+    Confidence LastConf = FinalWarningsMap.lookup(ExpiredLoan).ConfidenceLevel;
+    if (LastConf >= CurConfidence)
+      return;
+    FinalWarningsMap[ExpiredLoan] = {/*ExpiryLoc=*/EF->getExpiryLoc(),
+                                     /*UseExpr=*/BadUse->getUseExpr(),
+                                     /*ConfidenceLevel=*/CurConfidence};
+  }
+
+  static Confidence livenessKindToConfidence(LivenessKind K) {
+    switch (K) {
+    case LivenessKind::Must:
+      return Confidence::Definite;
+    case LivenessKind::Maybe:
+      return Confidence::Maybe;
+    case LivenessKind::Dead:
+      return Confidence::None;
+    }
+    llvm_unreachable("unknown liveness kind");
+  }
+
+  void issuePendingWarnings() {
+    if (!Reporter)
+      return;
+    for (const auto &[LID, Warning] : FinalWarningsMap) {
+      const Loan &L = FactMgr.getLoanMgr().getLoan(LID);
+      const Expr *IssueExpr = L.IssueExpr;
+      Reporter->reportUseAfterFree(IssueExpr, Warning.UseExpr,
+                                   Warning.ExpiryLoc, Warning.ConfidenceLevel);
+    }
+  }
+};
+} // namespace internal
+} // namespace clang::lifetimes
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Dataflow.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Dataflow.h
new file mode 100644
index 0000000000000..c4df15a93f740
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Dataflow.h
@@ -0,0 +1,180 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_DATAFLOW_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_DATAFLOW_H
+
+#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/TimeProfiler.h"
+#include <optional>
+
+namespace clang::lifetimes {
+namespace internal {
+
+// ========================================================================= //
+//                         Generic Dataflow Analysis
+// ========================================================================= //
+
+enum class Direction { Forward, Backward };
+
+/// A `ProgramPoint` identifies a location in the CFG by pointing to a specific
+/// `Fact`. identified by a lifetime-related event (`Fact`).
+///
+/// A `ProgramPoint` has "after" semantics: it represents the location
+/// immediately after its corresponding `Fact`.
+using ProgramPoint = const Fact *;
+
+/// A generic, policy-based driver for dataflow analyses. It combines
+/// the dataflow runner and the transferer logic into a single class hierarchy.
+///
+/// The derived class is expected to provide:
+/// - A `Lattice` type.
+/// - `StringRef getAnalysisName() const`
+/// - `Lattice getInitialState();` The initial state of the analysis.
+/// - `Lattice join(Lattice, Lattice);` Merges states from multiple CFG paths.
+/// - `Lattice transfer(Lattice, const FactType&);` Defines how a single
+///   lifetime-relevant `Fact` transforms the lattice state. Only overloads
+///   for facts relevant to the analysis need to be implemented.
+///
+/// \tparam Derived The CRTP derived class that implements the specific
+/// analysis.
+/// \tparam LatticeType The dataflow lattice used by the analysis.
+/// \tparam Dir The direction of the analysis (Forward or Backward).
+/// TODO: Maybe use the dataflow framework! The framework might need changes
+/// to support the current comparison done at block-entry.
+template <typename Derived, typename LatticeType, Direction Dir>
+class DataflowAnalysis {
+public:
+  using Lattice = LatticeType;
+  using Base = DataflowAnalysis<Derived, Lattice, Dir>;
+
+private:
+  const CFG &Cfg;
+  AnalysisDeclContext &AC;
+
+  /// The dataflow state before a basic block is processed.
+  llvm::DenseMap<const CFGBlock *, Lattice> InStates;
+  /// The dataflow state after a basic block is processed.
+  llvm::DenseMap<const CFGBlock *, Lattice> OutStates;
+  /// The dataflow state at a Program Point.
+  /// In a forward analysis, this is the state after the Fact at that point has
+  /// been applied, while in a backward analysis, it is the state before.
+  llvm::DenseMap<ProgramPoint, Lattice> PerPointStates;
+
+  static constexpr bool isForward() { return Dir == Direction::Forward; }
+
+protected:
+  FactManager &AllFacts;
+
+  explicit DataflowAnalysis(const CFG &C, AnalysisDeclContext &AC,
+                            FactManager &F)
+      : Cfg(C), AC(AC), AllFacts(F) {}
+
+public:
+  void run() {
+    Derived &D = static_cast<Derived &>(*this);
+    llvm::TimeTraceScope Time(D.getAnalysisName());
+
+    using Worklist =
+        std::conditional_t<Dir == Direction::Forward, ForwardDataflowWorklist,
+                           BackwardDataflowWorklist>;
+    Worklist W(Cfg, AC);
+
+    const CFGBlock *Start = isForward() ? &Cfg.getEntry() : &Cfg.getExit();
+    InStates[Start] = D.getInitialState();
+    W.enqueueBlock(Start);
+
+    while (const CFGBlock *B = W.dequeue()) {
+      Lattice StateIn = *getInState(B);
+      Lattice StateOut = transferBlock(B, StateIn);
+      OutStates[B] = StateOut;
+      for (const CFGBlock *AdjacentB : isForward() ? B->succs() : B->preds()) {
+        if (!AdjacentB)
+          continue;
+        std::optional<Lattice> OldInState = getInState(AdjacentB);
+        Lattice NewInState =
+            !OldInState ? StateOut : D.join(*OldInState, StateOut);
+        // Enqueue the adjacent block if its in-state has changed or if we have
+        // never seen it.
+        if (!OldInState || NewInState != *OldInState) {
+          InStates[AdjacentB] = NewInState;
+          W.enqueueBlock(AdjacentB);
+        }
+      }
+    }
+  }
+
+protected:
+  Lattice getState(ProgramPoint P) const { return PerPointStates.lookup(P); }
+
+  std::optional<Lattice> getInState(const CFGBlock *B) const {
+    auto It = InStates.find(B);
+    if (It == InStates.end())
+      return std::nullopt;
+    return It->second;
+  }
+
+  Lattice getOutState(const CFGBlock *B) const { return OutStates.lookup(B); }
+
+  void dump() const {
+    const Derived *D = static_cast<const Derived *>(this);
+    llvm::dbgs() << "==========================================\n";
+    llvm::dbgs() << D->getAnalysisName() << " results:\n";
+    llvm::dbgs() << "==========================================\n";
+    const CFGBlock &B = isForward() ? Cfg.getExit() : Cfg.getEntry();
+    getOutState(&B).dump(llvm::dbgs());
+  }
+
+private:
+  /// Computes the state at one end of a block by applying all its facts
+  /// sequentially to a given state from the other end.
+  Lattice transferBlock(const CFGBlock *Block, Lattice State) {
+    auto Facts = AllFacts.getFacts(Block);
+    if constexpr (isForward()) {
+      for (const Fact *F : Facts) {
+        State = transferFact(State, F);
+        PerPointStates[F] = State;
+      }
+    } else {
+      for (const Fact *F : llvm::reverse(Facts)) {
+        // In backward analysis, capture the state before applying the fact.
+        PerPointStates[F] = State;
+        State = transferFact(State, F);
+      }
+    }
+    return State;
+  }
+
+  Lattice transferFact(Lattice In, const Fact *F) {
+    assert(F);
+    Derived *D = static_cast<Derived *>(this);
+    switch (F->getKind()) {
+    case Fact::Kind::Issue:
+      return D->transfer(In, *F->getAs<IssueFact>());
+    case Fact::Kind::Expire:
+      return D->transfer(In, *F->getAs<ExpireFact>());
+    case Fact::Kind::OriginFlow:
+      return D->transfer(In, *F->getAs<OriginFlowFact>());
+    case Fact::Kind::ReturnOfOrigin:
+      return D->transfer(In, *F->getAs<ReturnOfOriginFact>());
+    case Fact::Kind::Use:
+      return D->transfer(In, *F->getAs<UseFact>());
+    case Fact::Kind::TestPoint:
+      return D->transfer(In, *F->getAs<TestPointFact>());
+    }
+    llvm_unreachable("Unknown fact kind");
+  }
+
+public:
+  Lattice transfer(Lattice In, const IssueFact &) { return In; }
+  Lattice transfer(Lattice In, const ExpireFact &) { return In; }
+  Lattice transfer(Lattice In, const OriginFlowFact &) { return In; }
+  Lattice transfer(Lattice In, const ReturnOfOriginFact &) { return In; }
+  Lattice transfer(Lattice In, const UseFact &) { return In; }
+  Lattice transfer(Lattice In, const TestPointFact &) { return In; }
+};
+} // namespace internal
+} // namespace clang::lifetimes
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_DATAFLOW_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
new file mode 100644
index 0000000000000..3866db466c617
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -0,0 +1,620 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H
+
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/TimeProfiler.h"
+#include <cstdint>
+#include <optional>
+
+namespace clang::lifetimes {
+namespace internal {
+/// An abstract base class for a single, atomic lifetime-relevant event.
+class Fact {
+
+public:
+  enum class Kind : uint8_t {
+    /// A new loan is issued from a borrow expression (e.g., &x).
+    Issue,
+    /// A loan expires as its underlying storage is freed (e.g., variable goes
+    /// out of scope).
+    Expire,
+    /// An origin is propagated from a source to a destination (e.g., p = q).
+    /// This can also optionally kill the destination origin before flowing into
+    /// it. Otherwise, the source's loan set is merged into the destination's
+    /// loan set.
+    OriginFlow,
+    /// An origin escapes the function by flowing into the return value.
+    ReturnOfOrigin,
+    /// An origin is used (eg. appears as l-value expression like DeclRefExpr).
+    Use,
+    /// A marker for a specific point in the code, for testing.
+    TestPoint,
+  };
+
+private:
+  Kind K;
+
+protected:
+  Fact(Kind K) : K(K) {}
+
+public:
+  virtual ~Fact() = default;
+  Kind getKind() const { return K; }
+
+  template <typename T> const T *getAs() const {
+    if (T::classof(this))
+      return static_cast<const T *>(this);
+    return nullptr;
+  }
+
+  virtual void dump(llvm::raw_ostream &OS, const LoanManager &,
+                    const OriginManager &) const {
+    OS << "Fact (Kind: " << static_cast<int>(K) << ")\n";
+  }
+};
+
+class IssueFact : public Fact {
+  LoanID LID;
+  OriginID OID;
+
+public:
+  static bool classof(const Fact *F) { return F->getKind() == Kind::Issue; }
+
+  IssueFact(LoanID LID, OriginID OID) : Fact(Kind::Issue), LID(LID), OID(OID) {}
+  LoanID getLoanID() const { return LID; }
+  OriginID getOriginID() const { return OID; }
+  void dump(llvm::raw_ostream &OS, const LoanManager &LM,
+            const OriginManager &OM) const override {
+    OS << "Issue (";
+    LM.getLoan(getLoanID()).dump(OS);
+    OS << ", ToOrigin: ";
+    OM.dump(getOriginID(), OS);
+    OS << ")\n";
+  }
+};
+
+class ExpireFact : public Fact {
+  LoanID LID;
+  SourceLocation ExpiryLoc;
+
+public:
+  static bool classof(const Fact *F) { return F->getKind() == Kind::Expire; }
+
+  ExpireFact(LoanID LID, SourceLocation ExpiryLoc)
+      : Fact(Kind::Expire), LID(LID), ExpiryLoc(ExpiryLoc) {}
+
+  LoanID getLoanID() const { return LID; }
+  SourceLocation getExpiryLoc() const { return ExpiryLoc; }
+
+  void dump(llvm::raw_ostream &OS, const LoanManager &LM,
+            const OriginManager &) const override {
+    OS << "Expire (";
+    LM.getLoan(getLoanID()).dump(OS);
+    OS << ")\n";
+  }
+};
+
+class OriginFlowFact : public Fact {
+  OriginID OIDDest;
+  OriginID OIDSrc;
+  // True if the destination origin should be killed (i.e., its current loans
+  // cleared) before the source origin's loans are flowed into it.
+  bool KillDest;
+
+public:
+  static bool classof(const Fact *F) {
+    return F->getKind() == Kind::OriginFlow;
+  }
+
+  OriginFlowFact(OriginID OIDDest, OriginID OIDSrc, bool KillDest)
+      : Fact(Kind::OriginFlow), OIDDest(OIDDest), OIDSrc(OIDSrc),
+        KillDest(KillDest) {}
+
+  OriginID getDestOriginID() const { return OIDDest; }
+  OriginID getSrcOriginID() const { return OIDSrc; }
+  bool getKillDest() const { return KillDest; }
+
+  void dump(llvm::raw_ostream &OS, const LoanManager &,
+            const OriginManager &OM) const override {
+    OS << "OriginFlow (Dest: ";
+    OM.dump(getDestOriginID(), OS);
+    OS << ", Src: ";
+    OM.dump(getSrcOriginID(), OS);
+    OS << (getKillDest() ? "" : ", Merge");
+    OS << ")\n";
+  }
+};
+
+class ReturnOfOriginFact : public Fact {
+  OriginID OID;
+
+public:
+  static bool classof(const Fact *F) {
+    return F->getKind() == Kind::ReturnOfOrigin;
+  }
+
+  ReturnOfOriginFact(OriginID OID) : Fact(Kind::ReturnOfOrigin), OID(OID) {}
+  OriginID getReturnedOriginID() const { return OID; }
+  void dump(llvm::raw_ostream &OS, const LoanManager &,
+            const OriginManager &OM) const override {
+    OS << "ReturnOfOrigin (";
+    OM.dump(getReturnedOriginID(), OS);
+    OS << ")\n";
+  }
+};
+
+class UseFact : public Fact {
+  const Expr *UseExpr;
+  // True if this use is a write operation (e.g., left-hand side of assignment).
+  // Write operations are exempted from use-after-free checks.
+  bool IsWritten = false;
+
+public:
+  static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
+
+  UseFact(const Expr *UseExpr) : Fact(Kind::Use), UseExpr(UseExpr) {}
+
+  OriginID getUsedOrigin(const OriginManager &OM) const {
+    // TODO: Remove const cast and make OriginManager::get as const.
+    return const_cast<OriginManager &>(OM).get(*UseExpr);
+  }
+  const Expr *getUseExpr() const { return UseExpr; }
+  void markAsWritten() { IsWritten = true; }
+  bool isWritten() const { return IsWritten; }
+
+  void dump(llvm::raw_ostream &OS, const LoanManager &,
+            const OriginManager &OM) const override {
+    OS << "Use (";
+    OM.dump(getUsedOrigin(OM), OS);
+    OS << ", " << (isWritten() ? "Write" : "Read") << ")\n";
+  }
+};
+
+/// A dummy-fact used to mark a specific point in the code for testing.
+/// It is generated by recognizing a `void("__lifetime_test_point_...")` cast.
+class TestPointFact : public Fact {
+  StringRef Annotation;
+
+public:
+  static bool classof(const Fact *F) { return F->getKind() == Kind::TestPoint; }
+
+  explicit TestPointFact(StringRef Annotation)
+      : Fact(Kind::TestPoint), Annotation(Annotation) {}
+
+  StringRef getAnnotation() const { return Annotation; }
+
+  void dump(llvm::raw_ostream &OS, const LoanManager &,
+            const OriginManager &) const override {
+    OS << "TestPoint (Annotation: \"" << getAnnotation() << "\")\n";
+  }
+};
+
+class FactManager {
+public:
+  llvm::ArrayRef<const Fact *> getFacts(const CFGBlock *B) const {
+    auto It = BlockToFactsMap.find(B);
+    if (It != BlockToFactsMap.end())
+      return It->second;
+    return {};
+  }
+
+  void addBlockFacts(const CFGBlock *B, llvm::ArrayRef<Fact *> NewFacts) {
+    if (!NewFacts.empty())
+      BlockToFactsMap[B].assign(NewFacts.begin(), NewFacts.end());
+  }
+
+  template <typename FactType, typename... Args>
+  FactType *createFact(Args &&...args) {
+    void *Mem = FactAllocator.Allocate<FactType>();
+    return new (Mem) FactType(std::forward<Args>(args)...);
+  }
+
+  void dump(const CFG &Cfg, AnalysisDeclContext &AC) const {
+    llvm::dbgs() << "==========================================\n";
+    llvm::dbgs() << "       Lifetime Analysis Facts:\n";
+    llvm::dbgs() << "==========================================\n";
+    if (const Decl *D = AC.getDecl())
+      if (const auto *ND = dyn_cast<NamedDecl>(D))
+        llvm::dbgs() << "Function: " << ND->getQualifiedNameAsString() << "\n";
+    // Print blocks in the order as they appear in code for a stable ordering.
+    for (const CFGBlock *B : *AC.getAnalysis<PostOrderCFGView>()) {
+      llvm::dbgs() << "  Block B" << B->getBlockID() << ":\n";
+      auto It = BlockToFactsMap.find(B);
+      if (It != BlockToFactsMap.end()) {
+        for (const Fact *F : It->second) {
+          llvm::dbgs() << "    ";
+          F->dump(llvm::dbgs(), LoanMgr, OriginMgr);
+        }
+      }
+      llvm::dbgs() << "  End of Block\n";
+    }
+  }
+
+  LoanManager &getLoanMgr() { return LoanMgr; }
+  OriginManager &getOriginMgr() { return OriginMgr; }
+
+private:
+  LoanManager LoanMgr;
+  OriginManager OriginMgr;
+  llvm::DenseMap<const clang::CFGBlock *, llvm::SmallVector<const Fact *>>
+      BlockToFactsMap;
+  llvm::BumpPtrAllocator FactAllocator;
+};
+
+class FactGenerator : public ConstStmtVisitor<FactGenerator> {
+  using Base = ConstStmtVisitor<FactGenerator>;
+
+public:
+  FactGenerator(FactManager &FactMgr, AnalysisDeclContext &AC)
+      : FactMgr(FactMgr), AC(AC) {}
+
+  void run() {
+    llvm::TimeTraceScope TimeProfile("FactGenerator");
+    // Iterate through the CFG blocks in reverse post-order to ensure that
+    // initializations and destructions are processed in the correct sequence.
+    for (const CFGBlock *Block : *AC.getAnalysis<PostOrderCFGView>()) {
+      CurrentBlockFacts.clear();
+      for (unsigned I = 0; I < Block->size(); ++I) {
+        const CFGElement &Element = Block->Elements[I];
+        if (std::optional<CFGStmt> CS = Element.getAs<CFGStmt>())
+          Visit(CS->getStmt());
+        else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
+                     Element.getAs<CFGAutomaticObjDtor>())
+          handleDestructor(*DtorOpt);
+      }
+      FactMgr.addBlockFacts(Block, CurrentBlockFacts);
+    }
+  }
+
+  void VisitDeclStmt(const DeclStmt *DS) {
+    for (const Decl *D : DS->decls())
+      if (const auto *VD = dyn_cast<VarDecl>(D))
+        if (hasOrigin(VD))
+          if (const Expr *InitExpr = VD->getInit())
+            killAndFlowOrigin(*VD, *InitExpr);
+  }
+
+  void VisitDeclRefExpr(const DeclRefExpr *DRE) {
+    handleUse(DRE);
+    // For non-pointer/non-view types, a reference to the variable's storage
+    // is a borrow. We create a loan for it.
+    // For pointer/view types, we stick to the existing model for now and do
+    // not create an extra origin for the l-value expression itself.
+
+    // TODO: A single origin for a `DeclRefExpr` for a pointer or view type is
+    // not sufficient to model the different levels of indirection. The current
+    // single-origin model cannot distinguish between a loan to the variable's
+    // storage and a loan to what it points to. A multi-origin model would be
+    // required for this.
+    if (!isPointerType(DRE->getType())) {
+      if (const Loan *L = createLoan(DRE)) {
+        OriginID ExprOID = FactMgr.getOriginMgr().getOrCreate(*DRE);
+        CurrentBlockFacts.push_back(
+            FactMgr.createFact<IssueFact>(L->ID, ExprOID));
+      }
+    }
+  }
+
+  void VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
+    if (isGslPointerType(CCE->getType())) {
+      handleGSLPointerConstruction(CCE);
+      return;
+    }
+  }
+
+  void VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
+    // Specifically for conversion operators,
+    // like `std::string_view p = std::string{};`
+    if (isGslPointerType(MCE->getType()) &&
+        isa<CXXConversionDecl>(MCE->getCalleeDecl())) {
+      // The argument is the implicit object itself.
+      handleFunctionCall(MCE, MCE->getMethodDecl(),
+                         {MCE->getImplicitObjectArgument()},
+                         /*IsGslConstruction=*/true);
+    }
+    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);
+    }
+  }
+
+  void VisitCallExpr(const CallExpr *CE) {
+    handleFunctionCall(CE, CE->getDirectCallee(),
+                       {CE->getArgs(), CE->getNumArgs()});
+  }
+
+  void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N) {
+    /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
+    /// pointers can use the same type of loan.
+    FactMgr.getOriginMgr().getOrCreate(*N);
+  }
+
+  void VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
+    if (!hasOrigin(ICE))
+      return;
+    // An ImplicitCastExpr node itself gets an origin, which flows from the
+    // origin of its sub-expression (after stripping its own parens/casts).
+    killAndFlowOrigin(*ICE, *ICE->getSubExpr());
+  }
+
+  void VisitUnaryOperator(const UnaryOperator *UO) {
+    if (UO->getOpcode() == UO_AddrOf) {
+      const Expr *SubExpr = UO->getSubExpr();
+      // Taking address of a pointer-type expression is not yet supported and
+      // will be supported in multi-origin model.
+      if (isPointerType(SubExpr->getType()))
+        return;
+      // The origin of an address-of expression (e.g., &x) is the origin of
+      // its sub-expression (x). This fact will cause the dataflow analysis
+      // to propagate any loans held by the sub-expression's origin to the
+      // origin of this UnaryOperator expression.
+      killAndFlowOrigin(*UO, *SubExpr);
+    }
+  }
+
+  void VisitReturnStmt(const ReturnStmt *RS) {
+    if (const Expr *RetExpr = RS->getRetValue()) {
+      if (hasOrigin(RetExpr)) {
+        OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);
+        CurrentBlockFacts.push_back(
+            FactMgr.createFact<ReturnOfOriginFact>(OID));
+      }
+    }
+  }
+
+  void VisitBinaryOperator(const BinaryOperator *BO) {
+    if (BO->isAssignmentOp())
+      handleAssignment(BO->getLHS(), BO->getRHS());
+  }
+
+  void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
+    // Assignment operators have special "kill-then-propagate" semantics
+    // and are handled separately.
+    if (OCE->isAssignmentOp() && OCE->getNumArgs() == 2) {
+      handleAssignment(OCE->getArg(0), OCE->getArg(1));
+      return;
+    }
+    handleFunctionCall(OCE, OCE->getDirectCallee(),
+                       {OCE->getArgs(), OCE->getNumArgs()},
+                       /*IsGslConstruction=*/false);
+  }
+
+  void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE) {
+    // Check if this is a test point marker. If so, we are done with this
+    // expression.
+    if (handleTestPoint(FCE))
+      return;
+    if (isGslPointerType(FCE->getType()))
+      killAndFlowOrigin(*FCE, *FCE->getSubExpr());
+  }
+
+  void VisitInitListExpr(const InitListExpr *ILE) {
+    if (!hasOrigin(ILE))
+      return;
+    // For list initialization with a single element, like `View{...}`, the
+    // origin of the list itself is the origin of its single element.
+    if (ILE->getNumInits() == 1)
+      killAndFlowOrigin(*ILE, *ILE->getInit(0));
+  }
+
+  void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE) {
+    if (!hasOrigin(MTE))
+      return;
+    // A temporary object's origin is the same as the origin of the
+    // expression that initializes it.
+    killAndFlowOrigin(*MTE, *MTE->getSubExpr());
+  }
+
+  void handleDestructor(const CFGAutomaticObjDtor &DtorOpt) {
+    /// TODO: Also handle trivial destructors (e.g., for `int`
+    /// variables) which will never have a CFGAutomaticObjDtor node.
+    /// TODO: Handle loans to temporaries.
+    /// TODO: Consider using clang::CFG::BuildOptions::AddLifetime to reuse the
+    /// lifetime ends.
+    const VarDecl *DestructedVD = DtorOpt.getVarDecl();
+    if (!DestructedVD)
+      return;
+    // Iterate through all loans to see if any expire.
+    /// TODO(opt): Do better than a linear search to find loans associated with
+    /// 'DestructedVD'.
+    for (const Loan &L : FactMgr.getLoanMgr().getLoans()) {
+      const AccessPath &LoanPath = L.Path;
+      // Check if the loan is for a stack variable and if that variable
+      // is the one being destructed.
+      if (LoanPath.D == DestructedVD)
+        CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
+            L.ID, DtorOpt.getTriggerStmt()->getEndLoc()));
+    }
+  }
+
+private:
+  static bool isGslPointerType(QualType QT) {
+    if (const auto *RD = QT->getAsCXXRecordDecl()) {
+      // We need to check the template definition for specializations.
+      if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
+        return CTSD->getSpecializedTemplate()
+            ->getTemplatedDecl()
+            ->hasAttr<PointerAttr>();
+      return RD->hasAttr<PointerAttr>();
+    }
+    return false;
+  }
+
+  static bool isPointerType(QualType QT) {
+    return QT->isPointerOrReferenceType() || isGslPointerType(QT);
+  }
+  // Check if a type has an origin.
+  static bool hasOrigin(const Expr *E) {
+    return E->isGLValue() || isPointerType(E->getType());
+  }
+
+  static bool hasOrigin(const VarDecl *VD) {
+    return isPointerType(VD->getType());
+  }
+
+  void handleGSLPointerConstruction(const CXXConstructExpr *CCE) {
+    assert(isGslPointerType(CCE->getType()));
+    if (CCE->getNumArgs() != 1)
+      return;
+    if (hasOrigin(CCE->getArg(0)))
+      killAndFlowOrigin(*CCE, *CCE->getArg(0));
+    else
+      // This could be a new borrow.
+      handleFunctionCall(CCE, CCE->getConstructor(),
+                         {CCE->getArgs(), CCE->getNumArgs()},
+                         /*IsGslConstruction=*/true);
+  }
+
+  /// Checks if a call-like expression creates a borrow by passing a value to a
+  /// reference parameter, creating an IssueFact if it does.
+  /// \param IsGslConstruction True if this is a GSL construction where all
+  ///   argument origins should flow to the returned origin.
+  void handleFunctionCall(const Expr *Call, const FunctionDecl *FD,
+                          ArrayRef<const Expr *> Args,
+                          bool IsGslConstruction = false) {
+    // Ignore functions returning values with no origin.
+    if (!FD || !hasOrigin(Call))
+      return;
+    auto IsArgLifetimeBound = [FD](unsigned I) -> bool {
+      const ParmVarDecl *PVD = nullptr;
+      if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
+          Method && Method->isInstance()) {
+        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;
+    };
+    if (Args.empty())
+      return;
+    bool killedSrc = false;
+    for (unsigned I = 0; I < Args.size(); ++I)
+      if (IsGslConstruction || IsArgLifetimeBound(I)) {
+        if (!killedSrc) {
+          killedSrc = true;
+          killAndFlowOrigin(*Call, *Args[I]);
+        } else
+          flowOrigin(*Call, *Args[I]);
+      }
+  }
+
+  /// Creates a loan for the storage path of a given declaration reference.
+  /// This function should be called whenever a DeclRefExpr represents a borrow.
+  /// \param DRE The declaration reference expression that initiates the borrow.
+  /// \return The new Loan on success, nullptr otherwise.
+  const Loan *createLoan(const DeclRefExpr *DRE) {
+    if (const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl())) {
+      AccessPath Path(VD);
+      // The loan is created at the location of the DeclRefExpr.
+      return &FactMgr.getLoanMgr().addLoan(Path, DRE);
+    }
+    return nullptr;
+  }
+
+  template <typename Destination, typename Source>
+  void flowOrigin(const Destination &D, const Source &S) {
+    OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
+    OriginID SrcOID = FactMgr.getOriginMgr().get(S);
+    CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
+        DestOID, SrcOID, /*KillDest=*/false));
+  }
+
+  template <typename Destination, typename Source>
+  void killAndFlowOrigin(const Destination &D, const Source &S) {
+    OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
+    OriginID SrcOID = FactMgr.getOriginMgr().get(S);
+    CurrentBlockFacts.push_back(
+        FactMgr.createFact<OriginFlowFact>(DestOID, SrcOID, /*KillDest=*/true));
+  }
+
+  /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
+  /// If so, creates a `TestPointFact` and returns true.
+  bool handleTestPoint(const CXXFunctionalCastExpr *FCE) {
+    if (!FCE->getType()->isVoidType())
+      return false;
+
+    const auto *SubExpr = FCE->getSubExpr()->IgnoreParenImpCasts();
+    if (const auto *SL = dyn_cast<StringLiteral>(SubExpr)) {
+      llvm::StringRef LiteralValue = SL->getString();
+      const std::string Prefix = "__lifetime_test_point_";
+
+      if (LiteralValue.starts_with(Prefix)) {
+        StringRef Annotation = LiteralValue.drop_front(Prefix.length());
+        CurrentBlockFacts.push_back(
+            FactMgr.createFact<TestPointFact>(Annotation));
+        return true;
+      }
+    }
+    return false;
+  }
+
+  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr) {
+    if (!hasOrigin(LHSExpr))
+      return;
+    // Find the underlying variable declaration for the left-hand side.
+    if (const auto *DRE_LHS =
+            dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) {
+      markUseAsWrite(DRE_LHS);
+      if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl())) {
+        // Kill the old loans of the destination origin and flow the new loans
+        // from the source origin.
+        killAndFlowOrigin(*VD_LHS, *RHSExpr);
+      }
+    }
+  }
+
+  // A DeclRefExpr will be treated as a use of the referenced decl. It will be
+  // checked for use-after-free unless it is later marked as being written to
+  // (e.g. on the left-hand side of an assignment).
+  void handleUse(const DeclRefExpr *DRE) {
+    if (isPointerType(DRE->getType())) {
+      UseFact *UF = FactMgr.createFact<UseFact>(DRE);
+      CurrentBlockFacts.push_back(UF);
+      assert(!UseFacts.contains(DRE));
+      UseFacts[DRE] = UF;
+    }
+  }
+
+  void markUseAsWrite(const DeclRefExpr *DRE) {
+    if (!isPointerType(DRE->getType()))
+      return;
+    assert(UseFacts.contains(DRE));
+    UseFacts[DRE]->markAsWritten();
+  }
+
+  FactManager &FactMgr;
+  AnalysisDeclContext &AC;
+  llvm::SmallVector<Fact *> CurrentBlockFacts;
+  // To distinguish between reads and writes for use-after-free checks, this map
+  // stores the `UseFact` for each `DeclRefExpr`. We initially identify all
+  // `DeclRefExpr`s as "read" uses. When an assignment is processed, the use
+  // corresponding to the left-hand side is updated to be a "write", thereby
+  // exempting it from the check.
+  llvm::DenseMap<const DeclRefExpr *, UseFact *> UseFacts;
+};
+} // namespace internal
+} // namespace clang::lifetimes
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeAnnotations.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
similarity index 100%
rename from clang/include/clang/Analysis/Analyses/LifetimeAnnotations.h
rename to clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
similarity index 100%
rename from clang/include/clang/Analysis/Analyses/LifetimeSafety.h
rename to clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h
new file mode 100644
index 0000000000000..9432b9c31f407
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h
@@ -0,0 +1,216 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LIVE_ORIGINS_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LIVE_ORIGINS_H
+
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Dataflow.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace clang::lifetimes {
+namespace internal {
+
+// ========================================================================= //
+//                         Live Origins Analysis
+// ========================================================================= //
+//
+// A backward dataflow analysis that determines which origins are "live" at each
+// program point. An origin is "live" at a program point if there's a potential
+// future use of the pointer it represents. Liveness is "generated" by a read of
+// origin's loan set (e.g., a `UseFact`) and is "killed" (i.e., it stops being
+// live) when its loan set is overwritten (e.g. a OriginFlow killing the
+// destination origin).
+//
+// This information is used for detecting use-after-free errors, as it allows us
+// to check if a live origin holds a loan to an object that has already expired.
+// ========================================================================= //
+
+/// Information about why an origin is live at a program point.
+struct LivenessInfo {
+  /// The use that makes the origin live. If liveness is propagated from
+  /// multiple uses along different paths, this will point to the use appearing
+  /// earlier in the translation unit.
+  /// This is 'null' when the origin is not live.
+  const UseFact *CausingUseFact;
+  /// The kind of liveness of the origin.
+  /// `Must`: The origin is live on all control-flow paths from the current
+  /// point to the function's exit (i.e. the current point is dominated by a set
+  /// of uses).
+  /// `Maybe`: indicates it is live on some but not all paths.
+  ///
+  /// This determines the diagnostic's confidence level.
+  /// `Must`-be-alive at expiration implies a definite use-after-free,
+  /// while `Maybe`-be-alive suggests a potential one on some paths.
+  LivenessKind Kind;
+
+  LivenessInfo() : CausingUseFact(nullptr), Kind(LivenessKind::Dead) {}
+  LivenessInfo(const UseFact *UF, LivenessKind K)
+      : CausingUseFact(UF), Kind(K) {}
+
+  bool operator==(const LivenessInfo &Other) const {
+    return CausingUseFact == Other.CausingUseFact && Kind == Other.Kind;
+  }
+  bool operator!=(const LivenessInfo &Other) const { return !(*this == Other); }
+
+  void Profile(llvm::FoldingSetNodeID &IDBuilder) const {
+    IDBuilder.AddPointer(CausingUseFact);
+    IDBuilder.Add(Kind);
+  }
+};
+
+using LivenessMap = llvm::ImmutableMap<OriginID, LivenessInfo>;
+
+/// The dataflow lattice for origin liveness analysis.
+/// It tracks which origins are live, why they're live (which UseFact),
+/// and the confidence level of that liveness.
+struct LivenessLattice {
+  LivenessMap LiveOrigins;
+
+  LivenessLattice() : LiveOrigins(nullptr) {};
+
+  explicit LivenessLattice(LivenessMap L) : LiveOrigins(L) {}
+
+  bool operator==(const LivenessLattice &Other) const {
+    return LiveOrigins == Other.LiveOrigins;
+  }
+
+  bool operator!=(const LivenessLattice &Other) const {
+    return !(*this == Other);
+  }
+
+  void dump(llvm::raw_ostream &OS, const OriginManager &OM) const {
+    if (LiveOrigins.isEmpty())
+      OS << "  <empty>\n";
+    for (const auto &Entry : LiveOrigins) {
+      OriginID OID = Entry.first;
+      const LivenessInfo &Info = Entry.second;
+      OS << "  ";
+      OM.dump(OID, OS);
+      OS << " is ";
+      switch (Info.Kind) {
+      case LivenessKind::Must:
+        OS << "definitely";
+        break;
+      case LivenessKind::Maybe:
+        OS << "maybe";
+        break;
+      case LivenessKind::Dead:
+        llvm_unreachable("liveness kind of live origins should not be dead.");
+      }
+      OS << " live at this point\n";
+    }
+  }
+};
+
+/// The analysis that tracks which origins are live, with granular information
+/// about the causing use fact and confidence level. This is a backward
+/// analysis.
+class LiveOriginAnalysis
+    : public DataflowAnalysis<LiveOriginAnalysis, LivenessLattice,
+                              Direction::Backward> {
+  FactManager &FactMgr;
+  LivenessMap::Factory &Factory;
+
+public:
+  LiveOriginAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
+                     LivenessMap::Factory &SF)
+      : DataflowAnalysis(C, AC, F), FactMgr(F), Factory(SF) {}
+  using DataflowAnalysis<LiveOriginAnalysis, Lattice,
+                         Direction::Backward>::transfer;
+
+  StringRef getAnalysisName() const { return "LiveOrigins"; }
+
+  Lattice getInitialState() { return Lattice(Factory.getEmptyMap()); }
+
+  /// Merges two lattices by combining liveness information.
+  /// When the same origin has different confidence levels, we take the lower
+  /// one.
+  Lattice join(Lattice L1, Lattice L2) const {
+    LivenessMap Merged = L1.LiveOrigins;
+    // Take the earliest UseFact to make the join hermetic and commutative.
+    auto CombineUseFact = [](const UseFact &A,
+                             const UseFact &B) -> const UseFact * {
+      return A.getUseExpr()->getExprLoc() < B.getUseExpr()->getExprLoc() ? &A
+                                                                         : &B;
+    };
+    auto CombineLivenessKind = [](LivenessKind K1,
+                                  LivenessKind K2) -> LivenessKind {
+      assert(K1 != LivenessKind::Dead && "LivenessKind should not be dead.");
+      assert(K2 != LivenessKind::Dead && "LivenessKind should not be dead.");
+      // Only return "Must" if both paths are "Must", otherwise Maybe.
+      if (K1 == LivenessKind::Must && K2 == LivenessKind::Must)
+        return LivenessKind::Must;
+      return LivenessKind::Maybe;
+    };
+    auto CombineLivenessInfo = [&](const LivenessInfo *L1,
+                                   const LivenessInfo *L2) -> LivenessInfo {
+      assert((L1 || L2) && "unexpectedly merging 2 empty sets");
+      if (!L1)
+        return LivenessInfo(L2->CausingUseFact, LivenessKind::Maybe);
+      if (!L2)
+        return LivenessInfo(L1->CausingUseFact, LivenessKind::Maybe);
+      return LivenessInfo(
+          CombineUseFact(*L1->CausingUseFact, *L2->CausingUseFact),
+          CombineLivenessKind(L1->Kind, L2->Kind));
+    };
+    return Lattice(utils::join(
+        L1.LiveOrigins, L2.LiveOrigins, Factory, CombineLivenessInfo,
+        // A symmetric join is required here. If an origin is live on one
+        // branch but not the other, its confidence must be demoted to `Maybe`.
+        utils::JoinKind::Symmetric));
+  }
+
+  /// A read operation makes the origin live with definite confidence, as it
+  /// dominates this program point. A write operation kills the liveness of
+  /// the origin since it overwrites the value.
+  Lattice transfer(Lattice In, const UseFact &UF) {
+    OriginID OID = UF.getUsedOrigin(FactMgr.getOriginMgr());
+    // Write kills liveness.
+    if (UF.isWritten())
+      return Lattice(Factory.remove(In.LiveOrigins, OID));
+    // Read makes origin live with definite confidence (dominates this point).
+    return Lattice(Factory.add(In.LiveOrigins, OID,
+                               LivenessInfo(&UF, LivenessKind::Must)));
+  }
+
+  /// Issuing a new loan to an origin kills its liveness.
+  Lattice transfer(Lattice In, const IssueFact &IF) {
+    return Lattice(Factory.remove(In.LiveOrigins, IF.getOriginID()));
+  }
+
+  /// An OriginFlow kills the liveness of the destination origin if `KillDest`
+  /// is true. Otherwise, it propagates liveness from destination to source.
+  Lattice transfer(Lattice In, const OriginFlowFact &OF) {
+    if (!OF.getKillDest())
+      return In;
+    return Lattice(Factory.remove(In.LiveOrigins, OF.getDestOriginID()));
+  }
+
+  LivenessMap getLiveOrigins(ProgramPoint P) const {
+    return getState(P).LiveOrigins;
+  }
+
+  // Dump liveness values on all test points in the program.
+  void dump(llvm::raw_ostream &OS, const LifetimeSafetyAnalysis &LSA) const {
+    llvm::dbgs() << "==========================================\n";
+    llvm::dbgs() << getAnalysisName() << " results:\n";
+    llvm::dbgs() << "==========================================\n";
+    for (const auto &Entry : LSA.getTestPoints()) {
+      OS << "TestPoint: " << Entry.getKey() << "\n";
+      getState(Entry.getValue()).dump(OS, FactMgr.getOriginMgr());
+    }
+  }
+};
+} // namespace internal
+} // namespace clang::lifetimes
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LIVE_ORIGINS_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
new file mode 100644
index 0000000000000..d49cf1c323c43
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -0,0 +1,133 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOAN_PROPAGATION_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOAN_PROPAGATION_H
+
+#include "clang/Analysis/Analyses/LifetimeSafety/Dataflow.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/Support/Debug.h"
+
+namespace clang::lifetimes {
+namespace internal {
+
+// ========================================================================= //
+//                          Loan Propagation Analysis
+// ========================================================================= //
+
+/// Represents the dataflow lattice for loan propagation.
+///
+/// This lattice tracks which loans each origin may hold at a given program
+/// point.The lattice has a finite height: An origin's loan set is bounded by
+/// the total number of loans in the function.
+/// TODO(opt): To reduce the lattice size, propagate origins of declarations,
+/// not expressions, because expressions are not visible across blocks.
+struct LoanPropagationLattice {
+  /// The map from an origin to the set of loans it contains.
+  OriginLoanMap Origins = OriginLoanMap(nullptr);
+
+  explicit LoanPropagationLattice(const OriginLoanMap &S) : Origins(S) {}
+  LoanPropagationLattice() = default;
+
+  bool operator==(const LoanPropagationLattice &Other) const {
+    return Origins == Other.Origins;
+  }
+  bool operator!=(const LoanPropagationLattice &Other) const {
+    return !(*this == Other);
+  }
+
+  void dump(llvm::raw_ostream &OS) const {
+    OS << "LoanPropagationLattice State:\n";
+    if (Origins.isEmpty())
+      OS << "  <empty>\n";
+    for (const auto &Entry : Origins) {
+      if (Entry.second.isEmpty())
+        OS << "  Origin " << Entry.first << " contains no loans\n";
+      for (const LoanID &LID : Entry.second)
+        OS << "  Origin " << Entry.first << " contains Loan " << LID << "\n";
+    }
+  }
+};
+
+/// The analysis that tracks which loans belong to which origins.
+class LoanPropagationAnalysis
+    : public DataflowAnalysis<LoanPropagationAnalysis, LoanPropagationLattice,
+                              Direction::Forward> {
+  OriginLoanMap::Factory &OriginLoanMapFactory;
+  LoanSet::Factory &LoanSetFactory;
+
+public:
+  LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
+                          OriginLoanMap::Factory &OriginLoanMapFactory,
+                          LoanSet::Factory &LoanSetFactory)
+      : DataflowAnalysis(C, AC, F), OriginLoanMapFactory(OriginLoanMapFactory),
+        LoanSetFactory(LoanSetFactory) {}
+
+  using Base::transfer;
+
+  StringRef getAnalysisName() const { return "LoanPropagation"; }
+
+  Lattice getInitialState() { return Lattice{}; }
+
+  /// Merges two lattices by taking the union of loans for each origin.
+  // TODO(opt): Keep the state small by removing origins which become dead.
+  Lattice join(Lattice A, Lattice B) {
+    OriginLoanMap JoinedOrigins = utils::join(
+        A.Origins, B.Origins, OriginLoanMapFactory,
+        [&](const LoanSet *S1, const LoanSet *S2) {
+          assert((S1 || S2) && "unexpectedly merging 2 empty sets");
+          if (!S1)
+            return *S2;
+          if (!S2)
+            return *S1;
+          return utils::join(*S1, *S2, LoanSetFactory);
+        },
+        // Asymmetric join is a performance win. For origins present only on one
+        // branch, the loan set can be carried over as-is.
+        utils::JoinKind::Asymmetric);
+    return Lattice(JoinedOrigins);
+  }
+
+  /// A new loan is issued to the origin. Old loans are erased.
+  Lattice transfer(Lattice In, const IssueFact &F) {
+    OriginID OID = F.getOriginID();
+    LoanID LID = F.getLoanID();
+    return LoanPropagationLattice(OriginLoanMapFactory.add(
+        In.Origins, OID,
+        LoanSetFactory.add(LoanSetFactory.getEmptySet(), LID)));
+  }
+
+  /// A flow from source to destination. If `KillDest` is true, this replaces
+  /// the destination's loans with the source's. Otherwise, the source's loans
+  /// are merged into the destination's.
+  Lattice transfer(Lattice In, const OriginFlowFact &F) {
+    OriginID DestOID = F.getDestOriginID();
+    OriginID SrcOID = F.getSrcOriginID();
+
+    LoanSet DestLoans =
+        F.getKillDest() ? LoanSetFactory.getEmptySet() : getLoans(In, DestOID);
+    LoanSet SrcLoans = getLoans(In, SrcOID);
+    LoanSet MergedLoans = utils::join(DestLoans, SrcLoans, LoanSetFactory);
+
+    return LoanPropagationLattice(
+        OriginLoanMapFactory.add(In.Origins, DestOID, MergedLoans));
+  }
+
+  LoanSet getLoans(OriginID OID, ProgramPoint P) const {
+    return getLoans(getState(P), OID);
+  }
+
+private:
+  LoanSet getLoans(Lattice L, OriginID OID) const {
+    if (auto *Loans = L.Origins.lookup(OID))
+      return *Loans;
+    return LoanSetFactory.getEmptySet();
+  }
+};
+} // namespace internal
+} // namespace clang::lifetimes
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOAN_PROPAGATION_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h
new file mode 100644
index 0000000000000..a5a800598b464
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h
@@ -0,0 +1,64 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOANS_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOANS_H
+#include "LifetimeSafety.h"
+
+namespace clang::lifetimes {
+namespace internal {
+
+/// Represents the storage location being borrowed, e.g., a specific stack
+/// variable.
+/// TODO: Model access paths of other types, e.g., s.field, heap and globals.
+struct AccessPath {
+  const clang::ValueDecl *D;
+
+  AccessPath(const clang::ValueDecl *D) : D(D) {}
+};
+
+/// Information about a single borrow, or "Loan". A loan is created when a
+/// reference or pointer is created.
+struct Loan {
+  /// TODO: Represent opaque loans.
+  /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
+  /// is represented as empty LoanSet
+  LoanID ID;
+  AccessPath Path;
+  /// The expression that creates the loan, e.g., &x.
+  const Expr *IssueExpr;
+
+  Loan(LoanID id, AccessPath path, const Expr *IssueExpr)
+      : ID(id), Path(path), IssueExpr(IssueExpr) {}
+
+  void dump(llvm::raw_ostream &OS) const {
+    OS << ID << " (Path: ";
+    OS << Path.D->getNameAsString() << ")";
+  }
+};
+
+/// Manages the creation, storage and retrieval of loans.
+class LoanManager {
+public:
+  LoanManager() = default;
+
+  Loan &addLoan(AccessPath Path, const Expr *IssueExpr) {
+    AllLoans.emplace_back(getNextLoanID(), Path, IssueExpr);
+    return AllLoans.back();
+  }
+
+  const Loan &getLoan(LoanID ID) const {
+    assert(ID.Value < AllLoans.size());
+    return AllLoans[ID.Value];
+  }
+  llvm::ArrayRef<Loan> getLoans() const { return AllLoans; }
+
+private:
+  LoanID getNextLoanID() { return NextLoanID++; }
+
+  LoanID NextLoanID{0};
+  /// TODO(opt): Profile and evaluate the usefullness of small buffer
+  /// optimisation.
+  llvm::SmallVector<Loan> AllLoans;
+};
+} // namespace internal
+} // namespace clang::lifetimes
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOANS_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
new file mode 100644
index 0000000000000..fccfc49fe4ca2
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
@@ -0,0 +1,131 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_ORIGINS_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_ORIGINS_H
+
+#include "LifetimeSafety.h"
+
+namespace clang::lifetimes {
+namespace internal {
+
+/// An Origin is a symbolic identifier that represents the set of possible
+/// loans a pointer-like object could hold at any given time.
+/// TODO: Enhance the origin model to handle complex types, pointer
+/// indirection and reborrowing. The plan is to move from a single origin per
+/// variable/expression to a "list of origins" governed by the Type.
+/// For example, the type 'int**' would have two origins.
+/// See discussion:
+/// https://github.com/llvm/llvm-project/pull/142313/commits/0cd187b01e61b200d92ca0b640789c1586075142#r2137644238
+struct Origin {
+  OriginID ID;
+  /// A pointer to the AST node that this origin represents. This union
+  /// distinguishes between origins from declarations (variables or parameters)
+  /// and origins from expressions.
+  llvm::PointerUnion<const clang::ValueDecl *, const clang::Expr *> Ptr;
+
+  Origin(OriginID ID, const clang::ValueDecl *D) : ID(ID), Ptr(D) {}
+  Origin(OriginID ID, const clang::Expr *E) : ID(ID), Ptr(E) {}
+
+  const clang::ValueDecl *getDecl() const {
+    return Ptr.dyn_cast<const clang::ValueDecl *>();
+  }
+  const clang::Expr *getExpr() const {
+    return Ptr.dyn_cast<const clang::Expr *>();
+  }
+};
+
+/// Manages the creation, storage, and retrieval of origins for pointer-like
+/// variables and expressions.
+class OriginManager {
+public:
+  OriginManager() = default;
+
+  Origin &addOrigin(OriginID ID, const clang::ValueDecl &D) {
+    AllOrigins.emplace_back(ID, &D);
+    return AllOrigins.back();
+  }
+  Origin &addOrigin(OriginID ID, const clang::Expr &E) {
+    AllOrigins.emplace_back(ID, &E);
+    return AllOrigins.back();
+  }
+
+  // TODO: Mark this method as const once we remove the call to getOrCreate.
+  OriginID get(const Expr &E) {
+    auto It = ExprToOriginID.find(&E);
+    if (It != ExprToOriginID.end())
+      return It->second;
+    // If the expression itself has no specific origin, and it's a reference
+    // to a declaration, its origin is that of the declaration it refers to.
+    // For pointer types, where we don't pre-emptively create an origin for the
+    // DeclRefExpr itself.
+    if (const auto *DRE = dyn_cast<DeclRefExpr>(&E))
+      return get(*DRE->getDecl());
+    // TODO: This should be an assert(It != ExprToOriginID.end()). The current
+    // implementation falls back to getOrCreate to avoid crashing on
+    // yet-unhandled pointer expressions, creating an empty origin for them.
+    return getOrCreate(E);
+  }
+
+  OriginID get(const ValueDecl &D) {
+    auto It = DeclToOriginID.find(&D);
+    // TODO: This should be an assert(It != DeclToOriginID.end()). The current
+    // implementation falls back to getOrCreate to avoid crashing on
+    // yet-unhandled pointer expressions, creating an empty origin for them.
+    if (It == DeclToOriginID.end())
+      return getOrCreate(D);
+
+    return It->second;
+  }
+
+  OriginID getOrCreate(const Expr &E) {
+    auto It = ExprToOriginID.find(&E);
+    if (It != ExprToOriginID.end())
+      return It->second;
+
+    OriginID NewID = getNextOriginID();
+    addOrigin(NewID, E);
+    ExprToOriginID[&E] = NewID;
+    return NewID;
+  }
+
+  const Origin &getOrigin(OriginID ID) const {
+    assert(ID.Value < AllOrigins.size());
+    return AllOrigins[ID.Value];
+  }
+
+  llvm::ArrayRef<Origin> getOrigins() const { return AllOrigins; }
+
+  OriginID getOrCreate(const ValueDecl &D) {
+    auto It = DeclToOriginID.find(&D);
+    if (It != DeclToOriginID.end())
+      return It->second;
+    OriginID NewID = getNextOriginID();
+    addOrigin(NewID, D);
+    DeclToOriginID[&D] = NewID;
+    return NewID;
+  }
+
+  void dump(OriginID OID, llvm::raw_ostream &OS) const {
+    OS << OID << " (";
+    Origin O = getOrigin(OID);
+    if (const ValueDecl *VD = O.getDecl())
+      OS << "Decl: " << VD->getNameAsString();
+    else if (const Expr *E = O.getExpr())
+      OS << "Expr: " << E->getStmtClassName();
+    else
+      OS << "Unknown";
+    OS << ")";
+  }
+
+private:
+  OriginID getNextOriginID() { return NextOriginID++; }
+
+  OriginID NextOriginID{0};
+  /// TODO(opt): Profile and evaluate the usefullness of small buffer
+  /// optimisation.
+  llvm::SmallVector<Origin> AllOrigins;
+  llvm::DenseMap<const clang::ValueDecl *, OriginID> DeclToOriginID;
+  llvm::DenseMap<const clang::Expr *, OriginID> ExprToOriginID;
+};
+} // namespace internal
+} // namespace clang::lifetimes
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_ORIGINS_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
new file mode 100644
index 0000000000000..744602b2ea0a7
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
@@ -0,0 +1,74 @@
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_UTILS_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_UTILS_H
+
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+
+namespace clang::lifetimes {
+namespace internal {
+
+namespace utils {
+
+/// Computes the union of two ImmutableSets.
+template <typename T>
+static llvm::ImmutableSet<T> join(llvm::ImmutableSet<T> A,
+                                  llvm::ImmutableSet<T> B,
+                                  typename llvm::ImmutableSet<T>::Factory &F) {
+  if (A.getHeight() < B.getHeight())
+    std::swap(A, B);
+  for (const T &E : B)
+    A = F.add(A, E);
+  return A;
+}
+
+/// Describes the strategy for joining two `ImmutableMap` instances, primarily
+/// differing in how they handle keys that are unique to one of the maps.
+///
+/// A `Symmetric` join is universally correct, while an `Asymmetric` join
+/// serves as a performance optimization. The latter is applicable only when the
+/// join operation possesses a left identity element, allowing for a more
+/// efficient, one-sided merge.
+enum class JoinKind {
+  /// A symmetric join applies the `JoinValues` operation to keys unique to
+  /// either map, ensuring that values from both maps contribute to the result.
+  Symmetric,
+  /// An asymmetric join preserves keys unique to the first map as-is, while
+  /// applying the `JoinValues` operation only to keys unique to the second map.
+  Asymmetric,
+};
+
+/// Computes the key-wise union of two ImmutableMaps.
+// TODO(opt): This key-wise join is a performance bottleneck. A more
+// efficient merge could be implemented using a Patricia Trie or HAMT
+// instead of the current AVL-tree-based ImmutableMap.
+template <typename K, typename V, typename Joiner>
+static llvm::ImmutableMap<K, V>
+join(const llvm::ImmutableMap<K, V> &A, const llvm::ImmutableMap<K, V> &B,
+     typename llvm::ImmutableMap<K, V>::Factory &F, Joiner JoinValues,
+     JoinKind Kind) {
+  if (A.getHeight() < B.getHeight())
+    return join(B, A, F, JoinValues, Kind);
+
+  // For each element in B, join it with the corresponding element in A
+  // (or with an empty value if it doesn't exist in A).
+  llvm::ImmutableMap<K, V> Res = A;
+  for (const auto &Entry : B) {
+    const K &Key = Entry.first;
+    const V &ValB = Entry.second;
+    Res = F.add(Res, Key, JoinValues(A.lookup(Key), &ValB));
+  }
+  if (Kind == JoinKind::Symmetric) {
+    for (const auto &Entry : A) {
+      const K &Key = Entry.first;
+      const V &ValA = Entry.second;
+      if (!B.contains(Key))
+        Res = F.add(Res, Key, JoinValues(&ValA, nullptr));
+    }
+  }
+  return Res;
+}
+} // namespace utils
+} // namespace internal
+} // namespace clang::lifetimes
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_UTILS_H
diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt
index 5a26f3eeea418..1dbd4153d856f 100644
--- a/clang/lib/Analysis/CMakeLists.txt
+++ b/clang/lib/Analysis/CMakeLists.txt
@@ -21,8 +21,6 @@ add_clang_library(clangAnalysis
   FixitUtil.cpp
   IntervalPartition.cpp
   IssueHash.cpp
-  LifetimeAnnotations.cpp
-  LifetimeSafety.cpp
   LiveVariables.cpp
   MacroExpansionContext.cpp
   ObjCNoReturn.cpp
@@ -51,3 +49,4 @@ add_clang_library(clangAnalysis
 
 add_subdirectory(plugins)
 add_subdirectory(FlowSensitive)
+add_subdirectory(LifetimeSafety)
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp
deleted file mode 100644
index 6196ec31fce1c..0000000000000
--- a/clang/lib/Analysis/LifetimeSafety.cpp
+++ /dev/null
@@ -1,1546 +0,0 @@
-//===- LifetimeSafety.cpp - C++ Lifetime Safety Analysis -*--------- C++-*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-#include "clang/Analysis/Analyses/LifetimeSafety.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/Expr.h"
-#include "clang/AST/StmtVisitor.h"
-#include "clang/AST/Type.h"
-#include "clang/Analysis/Analyses/LifetimeAnnotations.h"
-#include "clang/Analysis/Analyses/PostOrderCFGView.h"
-#include "clang/Analysis/AnalysisDeclContext.h"
-#include "clang/Analysis/CFG.h"
-#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
-#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/ImmutableMap.h"
-#include "llvm/ADT/ImmutableSet.h"
-#include "llvm/ADT/PointerUnion.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/TimeProfiler.h"
-#include <cstdint>
-#include <memory>
-#include <optional>
-
-namespace clang::lifetimes {
-namespace internal {
-
-/// Represents the storage location being borrowed, e.g., a specific stack
-/// variable.
-/// TODO: Model access paths of other types, e.g., s.field, heap and globals.
-struct AccessPath {
-  const clang::ValueDecl *D;
-
-  AccessPath(const clang::ValueDecl *D) : D(D) {}
-};
-
-/// Information about a single borrow, or "Loan". A loan is created when a
-/// reference or pointer is created.
-struct Loan {
-  /// TODO: Represent opaque loans.
-  /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
-  /// is represented as empty LoanSet
-  LoanID ID;
-  AccessPath Path;
-  /// The expression that creates the loan, e.g., &x.
-  const Expr *IssueExpr;
-
-  Loan(LoanID id, AccessPath path, const Expr *IssueExpr)
-      : ID(id), Path(path), IssueExpr(IssueExpr) {}
-
-  void dump(llvm::raw_ostream &OS) const {
-    OS << ID << " (Path: ";
-    OS << Path.D->getNameAsString() << ")";
-  }
-};
-
-/// An Origin is a symbolic identifier that represents the set of possible
-/// loans a pointer-like object could hold at any given time.
-/// TODO: Enhance the origin model to handle complex types, pointer
-/// indirection and reborrowing. The plan is to move from a single origin per
-/// variable/expression to a "list of origins" governed by the Type.
-/// For example, the type 'int**' would have two origins.
-/// See discussion:
-/// https://github.com/llvm/llvm-project/pull/142313/commits/0cd187b01e61b200d92ca0b640789c1586075142#r2137644238
-struct Origin {
-  OriginID ID;
-  /// A pointer to the AST node that this origin represents. This union
-  /// distinguishes between origins from declarations (variables or parameters)
-  /// and origins from expressions.
-  llvm::PointerUnion<const clang::ValueDecl *, const clang::Expr *> Ptr;
-
-  Origin(OriginID ID, const clang::ValueDecl *D) : ID(ID), Ptr(D) {}
-  Origin(OriginID ID, const clang::Expr *E) : ID(ID), Ptr(E) {}
-
-  const clang::ValueDecl *getDecl() const {
-    return Ptr.dyn_cast<const clang::ValueDecl *>();
-  }
-  const clang::Expr *getExpr() const {
-    return Ptr.dyn_cast<const clang::Expr *>();
-  }
-};
-
-/// Manages the creation, storage and retrieval of loans.
-class LoanManager {
-public:
-  LoanManager() = default;
-
-  Loan &addLoan(AccessPath Path, const Expr *IssueExpr) {
-    AllLoans.emplace_back(getNextLoanID(), Path, IssueExpr);
-    return AllLoans.back();
-  }
-
-  const Loan &getLoan(LoanID ID) const {
-    assert(ID.Value < AllLoans.size());
-    return AllLoans[ID.Value];
-  }
-  llvm::ArrayRef<Loan> getLoans() const { return AllLoans; }
-
-private:
-  LoanID getNextLoanID() { return NextLoanID++; }
-
-  LoanID NextLoanID{0};
-  /// TODO(opt): Profile and evaluate the usefullness of small buffer
-  /// optimisation.
-  llvm::SmallVector<Loan> AllLoans;
-};
-
-/// Manages the creation, storage, and retrieval of origins for pointer-like
-/// variables and expressions.
-class OriginManager {
-public:
-  OriginManager() = default;
-
-  Origin &addOrigin(OriginID ID, const clang::ValueDecl &D) {
-    AllOrigins.emplace_back(ID, &D);
-    return AllOrigins.back();
-  }
-  Origin &addOrigin(OriginID ID, const clang::Expr &E) {
-    AllOrigins.emplace_back(ID, &E);
-    return AllOrigins.back();
-  }
-
-  // TODO: Mark this method as const once we remove the call to getOrCreate.
-  OriginID get(const Expr &E) {
-    auto It = ExprToOriginID.find(&E);
-    if (It != ExprToOriginID.end())
-      return It->second;
-    // If the expression itself has no specific origin, and it's a reference
-    // to a declaration, its origin is that of the declaration it refers to.
-    // For pointer types, where we don't pre-emptively create an origin for the
-    // DeclRefExpr itself.
-    if (const auto *DRE = dyn_cast<DeclRefExpr>(&E))
-      return get(*DRE->getDecl());
-    // TODO: This should be an assert(It != ExprToOriginID.end()). The current
-    // implementation falls back to getOrCreate to avoid crashing on
-    // yet-unhandled pointer expressions, creating an empty origin for them.
-    return getOrCreate(E);
-  }
-
-  OriginID get(const ValueDecl &D) {
-    auto It = DeclToOriginID.find(&D);
-    // TODO: This should be an assert(It != DeclToOriginID.end()). The current
-    // implementation falls back to getOrCreate to avoid crashing on
-    // yet-unhandled pointer expressions, creating an empty origin for them.
-    if (It == DeclToOriginID.end())
-      return getOrCreate(D);
-
-    return It->second;
-  }
-
-  OriginID getOrCreate(const Expr &E) {
-    auto It = ExprToOriginID.find(&E);
-    if (It != ExprToOriginID.end())
-      return It->second;
-
-    OriginID NewID = getNextOriginID();
-    addOrigin(NewID, E);
-    ExprToOriginID[&E] = NewID;
-    return NewID;
-  }
-
-  const Origin &getOrigin(OriginID ID) const {
-    assert(ID.Value < AllOrigins.size());
-    return AllOrigins[ID.Value];
-  }
-
-  llvm::ArrayRef<Origin> getOrigins() const { return AllOrigins; }
-
-  OriginID getOrCreate(const ValueDecl &D) {
-    auto It = DeclToOriginID.find(&D);
-    if (It != DeclToOriginID.end())
-      return It->second;
-    OriginID NewID = getNextOriginID();
-    addOrigin(NewID, D);
-    DeclToOriginID[&D] = NewID;
-    return NewID;
-  }
-
-  void dump(OriginID OID, llvm::raw_ostream &OS) const {
-    OS << OID << " (";
-    Origin O = getOrigin(OID);
-    if (const ValueDecl *VD = O.getDecl())
-      OS << "Decl: " << VD->getNameAsString();
-    else if (const Expr *E = O.getExpr())
-      OS << "Expr: " << E->getStmtClassName();
-    else
-      OS << "Unknown";
-    OS << ")";
-  }
-
-private:
-  OriginID getNextOriginID() { return NextOriginID++; }
-
-  OriginID NextOriginID{0};
-  /// TODO(opt): Profile and evaluate the usefullness of small buffer
-  /// optimisation.
-  llvm::SmallVector<Origin> AllOrigins;
-  llvm::DenseMap<const clang::ValueDecl *, OriginID> DeclToOriginID;
-  llvm::DenseMap<const clang::Expr *, OriginID> ExprToOriginID;
-};
-
-/// An abstract base class for a single, atomic lifetime-relevant event.
-class Fact {
-
-public:
-  enum class Kind : uint8_t {
-    /// A new loan is issued from a borrow expression (e.g., &x).
-    Issue,
-    /// A loan expires as its underlying storage is freed (e.g., variable goes
-    /// out of scope).
-    Expire,
-    /// An origin is propagated from a source to a destination (e.g., p = q).
-    /// This can also optionally kill the destination origin before flowing into
-    /// it. Otherwise, the source's loan set is merged into the destination's
-    /// loan set.
-    OriginFlow,
-    /// An origin escapes the function by flowing into the return value.
-    ReturnOfOrigin,
-    /// An origin is used (eg. appears as l-value expression like DeclRefExpr).
-    Use,
-    /// A marker for a specific point in the code, for testing.
-    TestPoint,
-  };
-
-private:
-  Kind K;
-
-protected:
-  Fact(Kind K) : K(K) {}
-
-public:
-  virtual ~Fact() = default;
-  Kind getKind() const { return K; }
-
-  template <typename T> const T *getAs() const {
-    if (T::classof(this))
-      return static_cast<const T *>(this);
-    return nullptr;
-  }
-
-  virtual void dump(llvm::raw_ostream &OS, const LoanManager &,
-                    const OriginManager &) const {
-    OS << "Fact (Kind: " << static_cast<int>(K) << ")\n";
-  }
-};
-
-class IssueFact : public Fact {
-  LoanID LID;
-  OriginID OID;
-
-public:
-  static bool classof(const Fact *F) { return F->getKind() == Kind::Issue; }
-
-  IssueFact(LoanID LID, OriginID OID) : Fact(Kind::Issue), LID(LID), OID(OID) {}
-  LoanID getLoanID() const { return LID; }
-  OriginID getOriginID() const { return OID; }
-  void dump(llvm::raw_ostream &OS, const LoanManager &LM,
-            const OriginManager &OM) const override {
-    OS << "Issue (";
-    LM.getLoan(getLoanID()).dump(OS);
-    OS << ", ToOrigin: ";
-    OM.dump(getOriginID(), OS);
-    OS << ")\n";
-  }
-};
-
-class ExpireFact : public Fact {
-  LoanID LID;
-  SourceLocation ExpiryLoc;
-
-public:
-  static bool classof(const Fact *F) { return F->getKind() == Kind::Expire; }
-
-  ExpireFact(LoanID LID, SourceLocation ExpiryLoc)
-      : Fact(Kind::Expire), LID(LID), ExpiryLoc(ExpiryLoc) {}
-
-  LoanID getLoanID() const { return LID; }
-  SourceLocation getExpiryLoc() const { return ExpiryLoc; }
-
-  void dump(llvm::raw_ostream &OS, const LoanManager &LM,
-            const OriginManager &) const override {
-    OS << "Expire (";
-    LM.getLoan(getLoanID()).dump(OS);
-    OS << ")\n";
-  }
-};
-
-class OriginFlowFact : public Fact {
-  OriginID OIDDest;
-  OriginID OIDSrc;
-  // True if the destination origin should be killed (i.e., its current loans
-  // cleared) before the source origin's loans are flowed into it.
-  bool KillDest;
-
-public:
-  static bool classof(const Fact *F) {
-    return F->getKind() == Kind::OriginFlow;
-  }
-
-  OriginFlowFact(OriginID OIDDest, OriginID OIDSrc, bool KillDest)
-      : Fact(Kind::OriginFlow), OIDDest(OIDDest), OIDSrc(OIDSrc),
-        KillDest(KillDest) {}
-
-  OriginID getDestOriginID() const { return OIDDest; }
-  OriginID getSrcOriginID() const { return OIDSrc; }
-  bool getKillDest() const { return KillDest; }
-
-  void dump(llvm::raw_ostream &OS, const LoanManager &,
-            const OriginManager &OM) const override {
-    OS << "OriginFlow (Dest: ";
-    OM.dump(getDestOriginID(), OS);
-    OS << ", Src: ";
-    OM.dump(getSrcOriginID(), OS);
-    OS << (getKillDest() ? "" : ", Merge");
-    OS << ")\n";
-  }
-};
-
-class ReturnOfOriginFact : public Fact {
-  OriginID OID;
-
-public:
-  static bool classof(const Fact *F) {
-    return F->getKind() == Kind::ReturnOfOrigin;
-  }
-
-  ReturnOfOriginFact(OriginID OID) : Fact(Kind::ReturnOfOrigin), OID(OID) {}
-  OriginID getReturnedOriginID() const { return OID; }
-  void dump(llvm::raw_ostream &OS, const LoanManager &,
-            const OriginManager &OM) const override {
-    OS << "ReturnOfOrigin (";
-    OM.dump(getReturnedOriginID(), OS);
-    OS << ")\n";
-  }
-};
-
-class UseFact : public Fact {
-  const Expr *UseExpr;
-  // True if this use is a write operation (e.g., left-hand side of assignment).
-  // Write operations are exempted from use-after-free checks.
-  bool IsWritten = false;
-
-public:
-  static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
-
-  UseFact(const Expr *UseExpr) : Fact(Kind::Use), UseExpr(UseExpr) {}
-
-  OriginID getUsedOrigin(const OriginManager &OM) const {
-    // TODO: Remove const cast and make OriginManager::get as const.
-    return const_cast<OriginManager &>(OM).get(*UseExpr);
-  }
-  const Expr *getUseExpr() const { return UseExpr; }
-  void markAsWritten() { IsWritten = true; }
-  bool isWritten() const { return IsWritten; }
-
-  void dump(llvm::raw_ostream &OS, const LoanManager &,
-            const OriginManager &OM) const override {
-    OS << "Use (";
-    OM.dump(getUsedOrigin(OM), OS);
-    OS << ", " << (isWritten() ? "Write" : "Read") << ")\n";
-  }
-};
-
-/// A dummy-fact used to mark a specific point in the code for testing.
-/// It is generated by recognizing a `void("__lifetime_test_point_...")` cast.
-class TestPointFact : public Fact {
-  StringRef Annotation;
-
-public:
-  static bool classof(const Fact *F) { return F->getKind() == Kind::TestPoint; }
-
-  explicit TestPointFact(StringRef Annotation)
-      : Fact(Kind::TestPoint), Annotation(Annotation) {}
-
-  StringRef getAnnotation() const { return Annotation; }
-
-  void dump(llvm::raw_ostream &OS, const LoanManager &,
-            const OriginManager &) const override {
-    OS << "TestPoint (Annotation: \"" << getAnnotation() << "\")\n";
-  }
-};
-
-class FactManager {
-public:
-  llvm::ArrayRef<const Fact *> getFacts(const CFGBlock *B) const {
-    auto It = BlockToFactsMap.find(B);
-    if (It != BlockToFactsMap.end())
-      return It->second;
-    return {};
-  }
-
-  void addBlockFacts(const CFGBlock *B, llvm::ArrayRef<Fact *> NewFacts) {
-    if (!NewFacts.empty())
-      BlockToFactsMap[B].assign(NewFacts.begin(), NewFacts.end());
-  }
-
-  template <typename FactType, typename... Args>
-  FactType *createFact(Args &&...args) {
-    void *Mem = FactAllocator.Allocate<FactType>();
-    return new (Mem) FactType(std::forward<Args>(args)...);
-  }
-
-  void dump(const CFG &Cfg, AnalysisDeclContext &AC) const {
-    llvm::dbgs() << "==========================================\n";
-    llvm::dbgs() << "       Lifetime Analysis Facts:\n";
-    llvm::dbgs() << "==========================================\n";
-    if (const Decl *D = AC.getDecl())
-      if (const auto *ND = dyn_cast<NamedDecl>(D))
-        llvm::dbgs() << "Function: " << ND->getQualifiedNameAsString() << "\n";
-    // Print blocks in the order as they appear in code for a stable ordering.
-    for (const CFGBlock *B : *AC.getAnalysis<PostOrderCFGView>()) {
-      llvm::dbgs() << "  Block B" << B->getBlockID() << ":\n";
-      auto It = BlockToFactsMap.find(B);
-      if (It != BlockToFactsMap.end()) {
-        for (const Fact *F : It->second) {
-          llvm::dbgs() << "    ";
-          F->dump(llvm::dbgs(), LoanMgr, OriginMgr);
-        }
-      }
-      llvm::dbgs() << "  End of Block\n";
-    }
-  }
-
-  LoanManager &getLoanMgr() { return LoanMgr; }
-  OriginManager &getOriginMgr() { return OriginMgr; }
-
-private:
-  LoanManager LoanMgr;
-  OriginManager OriginMgr;
-  llvm::DenseMap<const clang::CFGBlock *, llvm::SmallVector<const Fact *>>
-      BlockToFactsMap;
-  llvm::BumpPtrAllocator FactAllocator;
-};
-
-class FactGenerator : public ConstStmtVisitor<FactGenerator> {
-  using Base = ConstStmtVisitor<FactGenerator>;
-
-public:
-  FactGenerator(FactManager &FactMgr, AnalysisDeclContext &AC)
-      : FactMgr(FactMgr), AC(AC) {}
-
-  void run() {
-    llvm::TimeTraceScope TimeProfile("FactGenerator");
-    // Iterate through the CFG blocks in reverse post-order to ensure that
-    // initializations and destructions are processed in the correct sequence.
-    for (const CFGBlock *Block : *AC.getAnalysis<PostOrderCFGView>()) {
-      CurrentBlockFacts.clear();
-      for (unsigned I = 0; I < Block->size(); ++I) {
-        const CFGElement &Element = Block->Elements[I];
-        if (std::optional<CFGStmt> CS = Element.getAs<CFGStmt>())
-          Visit(CS->getStmt());
-        else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
-                     Element.getAs<CFGAutomaticObjDtor>())
-          handleDestructor(*DtorOpt);
-      }
-      FactMgr.addBlockFacts(Block, CurrentBlockFacts);
-    }
-  }
-
-  void VisitDeclStmt(const DeclStmt *DS) {
-    for (const Decl *D : DS->decls())
-      if (const auto *VD = dyn_cast<VarDecl>(D))
-        if (hasOrigin(VD))
-          if (const Expr *InitExpr = VD->getInit())
-            killAndFlowOrigin(*VD, *InitExpr);
-  }
-
-  void VisitDeclRefExpr(const DeclRefExpr *DRE) {
-    handleUse(DRE);
-    // For non-pointer/non-view types, a reference to the variable's storage
-    // is a borrow. We create a loan for it.
-    // For pointer/view types, we stick to the existing model for now and do
-    // not create an extra origin for the l-value expression itself.
-
-    // TODO: A single origin for a `DeclRefExpr` for a pointer or view type is
-    // not sufficient to model the different levels of indirection. The current
-    // single-origin model cannot distinguish between a loan to the variable's
-    // storage and a loan to what it points to. A multi-origin model would be
-    // required for this.
-    if (!isPointerType(DRE->getType())) {
-      if (const Loan *L = createLoan(DRE)) {
-        OriginID ExprOID = FactMgr.getOriginMgr().getOrCreate(*DRE);
-        CurrentBlockFacts.push_back(
-            FactMgr.createFact<IssueFact>(L->ID, ExprOID));
-      }
-    }
-  }
-
-  void VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
-    if (isGslPointerType(CCE->getType())) {
-      handleGSLPointerConstruction(CCE);
-      return;
-    }
-  }
-
-  void VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
-    // Specifically for conversion operators,
-    // like `std::string_view p = std::string{};`
-    if (isGslPointerType(MCE->getType()) &&
-        isa<CXXConversionDecl>(MCE->getCalleeDecl())) {
-      // The argument is the implicit object itself.
-      handleFunctionCall(MCE, MCE->getMethodDecl(),
-                         {MCE->getImplicitObjectArgument()},
-                         /*IsGslConstruction=*/true);
-    }
-    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);
-    }
-  }
-
-  void VisitCallExpr(const CallExpr *CE) {
-    handleFunctionCall(CE, CE->getDirectCallee(),
-                       {CE->getArgs(), CE->getNumArgs()});
-  }
-
-  void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N) {
-    /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
-    /// pointers can use the same type of loan.
-    FactMgr.getOriginMgr().getOrCreate(*N);
-  }
-
-  void VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
-    if (!hasOrigin(ICE))
-      return;
-    // An ImplicitCastExpr node itself gets an origin, which flows from the
-    // origin of its sub-expression (after stripping its own parens/casts).
-    killAndFlowOrigin(*ICE, *ICE->getSubExpr());
-  }
-
-  void VisitUnaryOperator(const UnaryOperator *UO) {
-    if (UO->getOpcode() == UO_AddrOf) {
-      const Expr *SubExpr = UO->getSubExpr();
-      // Taking address of a pointer-type expression is not yet supported and
-      // will be supported in multi-origin model.
-      if (isPointerType(SubExpr->getType()))
-        return;
-      // The origin of an address-of expression (e.g., &x) is the origin of
-      // its sub-expression (x). This fact will cause the dataflow analysis
-      // to propagate any loans held by the sub-expression's origin to the
-      // origin of this UnaryOperator expression.
-      killAndFlowOrigin(*UO, *SubExpr);
-    }
-  }
-
-  void VisitReturnStmt(const ReturnStmt *RS) {
-    if (const Expr *RetExpr = RS->getRetValue()) {
-      if (hasOrigin(RetExpr)) {
-        OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);
-        CurrentBlockFacts.push_back(
-            FactMgr.createFact<ReturnOfOriginFact>(OID));
-      }
-    }
-  }
-
-  void VisitBinaryOperator(const BinaryOperator *BO) {
-    if (BO->isAssignmentOp())
-      handleAssignment(BO->getLHS(), BO->getRHS());
-  }
-
-  void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
-    // Assignment operators have special "kill-then-propagate" semantics
-    // and are handled separately.
-    if (OCE->isAssignmentOp() && OCE->getNumArgs() == 2) {
-      handleAssignment(OCE->getArg(0), OCE->getArg(1));
-      return;
-    }
-    handleFunctionCall(OCE, OCE->getDirectCallee(),
-                       {OCE->getArgs(), OCE->getNumArgs()},
-                       /*IsGslConstruction=*/false);
-  }
-
-  void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE) {
-    // Check if this is a test point marker. If so, we are done with this
-    // expression.
-    if (handleTestPoint(FCE))
-      return;
-    if (isGslPointerType(FCE->getType()))
-      killAndFlowOrigin(*FCE, *FCE->getSubExpr());
-  }
-
-  void VisitInitListExpr(const InitListExpr *ILE) {
-    if (!hasOrigin(ILE))
-      return;
-    // For list initialization with a single element, like `View{...}`, the
-    // origin of the list itself is the origin of its single element.
-    if (ILE->getNumInits() == 1)
-      killAndFlowOrigin(*ILE, *ILE->getInit(0));
-  }
-
-  void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE) {
-    if (!hasOrigin(MTE))
-      return;
-    // A temporary object's origin is the same as the origin of the
-    // expression that initializes it.
-    killAndFlowOrigin(*MTE, *MTE->getSubExpr());
-  }
-
-  void handleDestructor(const CFGAutomaticObjDtor &DtorOpt) {
-    /// TODO: Also handle trivial destructors (e.g., for `int`
-    /// variables) which will never have a CFGAutomaticObjDtor node.
-    /// TODO: Handle loans to temporaries.
-    /// TODO: Consider using clang::CFG::BuildOptions::AddLifetime to reuse the
-    /// lifetime ends.
-    const VarDecl *DestructedVD = DtorOpt.getVarDecl();
-    if (!DestructedVD)
-      return;
-    // Iterate through all loans to see if any expire.
-    /// TODO(opt): Do better than a linear search to find loans associated with
-    /// 'DestructedVD'.
-    for (const Loan &L : FactMgr.getLoanMgr().getLoans()) {
-      const AccessPath &LoanPath = L.Path;
-      // Check if the loan is for a stack variable and if that variable
-      // is the one being destructed.
-      if (LoanPath.D == DestructedVD)
-        CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
-            L.ID, DtorOpt.getTriggerStmt()->getEndLoc()));
-    }
-  }
-
-private:
-  static bool isGslPointerType(QualType QT) {
-    if (const auto *RD = QT->getAsCXXRecordDecl()) {
-      // We need to check the template definition for specializations.
-      if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
-        return CTSD->getSpecializedTemplate()
-            ->getTemplatedDecl()
-            ->hasAttr<PointerAttr>();
-      return RD->hasAttr<PointerAttr>();
-    }
-    return false;
-  }
-
-  static bool isPointerType(QualType QT) {
-    return QT->isPointerOrReferenceType() || isGslPointerType(QT);
-  }
-  // Check if a type has an origin.
-  static bool hasOrigin(const Expr *E) {
-    return E->isGLValue() || isPointerType(E->getType());
-  }
-
-  static bool hasOrigin(const VarDecl *VD) {
-    return isPointerType(VD->getType());
-  }
-
-  void handleGSLPointerConstruction(const CXXConstructExpr *CCE) {
-    assert(isGslPointerType(CCE->getType()));
-    if (CCE->getNumArgs() != 1)
-      return;
-    if (hasOrigin(CCE->getArg(0)))
-      killAndFlowOrigin(*CCE, *CCE->getArg(0));
-    else
-      // This could be a new borrow.
-      handleFunctionCall(CCE, CCE->getConstructor(),
-                         {CCE->getArgs(), CCE->getNumArgs()},
-                         /*IsGslConstruction=*/true);
-  }
-
-  /// Checks if a call-like expression creates a borrow by passing a value to a
-  /// reference parameter, creating an IssueFact if it does.
-  /// \param IsGslConstruction True if this is a GSL construction where all
-  ///   argument origins should flow to the returned origin.
-  void handleFunctionCall(const Expr *Call, const FunctionDecl *FD,
-                          ArrayRef<const Expr *> Args,
-                          bool IsGslConstruction = false) {
-    // Ignore functions returning values with no origin.
-    if (!FD || !hasOrigin(Call))
-      return;
-    auto IsArgLifetimeBound = [FD](unsigned I) -> bool {
-      const ParmVarDecl *PVD = nullptr;
-      if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
-          Method && Method->isInstance()) {
-        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;
-    };
-    if (Args.empty())
-      return;
-    bool killedSrc = false;
-    for (unsigned I = 0; I < Args.size(); ++I)
-      if (IsGslConstruction || IsArgLifetimeBound(I)) {
-        if (!killedSrc) {
-          killedSrc = true;
-          killAndFlowOrigin(*Call, *Args[I]);
-        } else
-          flowOrigin(*Call, *Args[I]);
-      }
-  }
-
-  /// Creates a loan for the storage path of a given declaration reference.
-  /// This function should be called whenever a DeclRefExpr represents a borrow.
-  /// \param DRE The declaration reference expression that initiates the borrow.
-  /// \return The new Loan on success, nullptr otherwise.
-  const Loan *createLoan(const DeclRefExpr *DRE) {
-    if (const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl())) {
-      AccessPath Path(VD);
-      // The loan is created at the location of the DeclRefExpr.
-      return &FactMgr.getLoanMgr().addLoan(Path, DRE);
-    }
-    return nullptr;
-  }
-
-  template <typename Destination, typename Source>
-  void flowOrigin(const Destination &D, const Source &S) {
-    OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-    OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-    CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
-        DestOID, SrcOID, /*KillDest=*/false));
-  }
-
-  template <typename Destination, typename Source>
-  void killAndFlowOrigin(const Destination &D, const Source &S) {
-    OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-    OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-    CurrentBlockFacts.push_back(
-        FactMgr.createFact<OriginFlowFact>(DestOID, SrcOID, /*KillDest=*/true));
-  }
-
-  /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
-  /// If so, creates a `TestPointFact` and returns true.
-  bool handleTestPoint(const CXXFunctionalCastExpr *FCE) {
-    if (!FCE->getType()->isVoidType())
-      return false;
-
-    const auto *SubExpr = FCE->getSubExpr()->IgnoreParenImpCasts();
-    if (const auto *SL = dyn_cast<StringLiteral>(SubExpr)) {
-      llvm::StringRef LiteralValue = SL->getString();
-      const std::string Prefix = "__lifetime_test_point_";
-
-      if (LiteralValue.starts_with(Prefix)) {
-        StringRef Annotation = LiteralValue.drop_front(Prefix.length());
-        CurrentBlockFacts.push_back(
-            FactMgr.createFact<TestPointFact>(Annotation));
-        return true;
-      }
-    }
-    return false;
-  }
-
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr) {
-    if (!hasOrigin(LHSExpr))
-      return;
-    // Find the underlying variable declaration for the left-hand side.
-    if (const auto *DRE_LHS =
-            dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) {
-      markUseAsWrite(DRE_LHS);
-      if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl())) {
-        // Kill the old loans of the destination origin and flow the new loans
-        // from the source origin.
-        killAndFlowOrigin(*VD_LHS, *RHSExpr);
-      }
-    }
-  }
-
-  // A DeclRefExpr will be treated as a use of the referenced decl. It will be
-  // checked for use-after-free unless it is later marked as being written to
-  // (e.g. on the left-hand side of an assignment).
-  void handleUse(const DeclRefExpr *DRE) {
-    if (isPointerType(DRE->getType())) {
-      UseFact *UF = FactMgr.createFact<UseFact>(DRE);
-      CurrentBlockFacts.push_back(UF);
-      assert(!UseFacts.contains(DRE));
-      UseFacts[DRE] = UF;
-    }
-  }
-
-  void markUseAsWrite(const DeclRefExpr *DRE) {
-    if (!isPointerType(DRE->getType()))
-      return;
-    assert(UseFacts.contains(DRE));
-    UseFacts[DRE]->markAsWritten();
-  }
-
-  FactManager &FactMgr;
-  AnalysisDeclContext &AC;
-  llvm::SmallVector<Fact *> CurrentBlockFacts;
-  // To distinguish between reads and writes for use-after-free checks, this map
-  // stores the `UseFact` for each `DeclRefExpr`. We initially identify all
-  // `DeclRefExpr`s as "read" uses. When an assignment is processed, the use
-  // corresponding to the left-hand side is updated to be a "write", thereby
-  // exempting it from the check.
-  llvm::DenseMap<const DeclRefExpr *, UseFact *> UseFacts;
-};
-
-// ========================================================================= //
-//                         Generic Dataflow Analysis
-// ========================================================================= //
-
-enum class Direction { Forward, Backward };
-
-/// A `ProgramPoint` identifies a location in the CFG by pointing to a specific
-/// `Fact`. identified by a lifetime-related event (`Fact`).
-///
-/// A `ProgramPoint` has "after" semantics: it represents the location
-/// immediately after its corresponding `Fact`.
-using ProgramPoint = const Fact *;
-
-/// A generic, policy-based driver for dataflow analyses. It combines
-/// the dataflow runner and the transferer logic into a single class hierarchy.
-///
-/// The derived class is expected to provide:
-/// - A `Lattice` type.
-/// - `StringRef getAnalysisName() const`
-/// - `Lattice getInitialState();` The initial state of the analysis.
-/// - `Lattice join(Lattice, Lattice);` Merges states from multiple CFG paths.
-/// - `Lattice transfer(Lattice, const FactType&);` Defines how a single
-///   lifetime-relevant `Fact` transforms the lattice state. Only overloads
-///   for facts relevant to the analysis need to be implemented.
-///
-/// \tparam Derived The CRTP derived class that implements the specific
-/// analysis.
-/// \tparam LatticeType The dataflow lattice used by the analysis.
-/// \tparam Dir The direction of the analysis (Forward or Backward).
-/// TODO: Maybe use the dataflow framework! The framework might need changes
-/// to support the current comparison done at block-entry.
-template <typename Derived, typename LatticeType, Direction Dir>
-class DataflowAnalysis {
-public:
-  using Lattice = LatticeType;
-  using Base = DataflowAnalysis<Derived, Lattice, Dir>;
-
-private:
-  const CFG &Cfg;
-  AnalysisDeclContext &AC;
-
-  /// The dataflow state before a basic block is processed.
-  llvm::DenseMap<const CFGBlock *, Lattice> InStates;
-  /// The dataflow state after a basic block is processed.
-  llvm::DenseMap<const CFGBlock *, Lattice> OutStates;
-  /// The dataflow state at a Program Point.
-  /// In a forward analysis, this is the state after the Fact at that point has
-  /// been applied, while in a backward analysis, it is the state before.
-  llvm::DenseMap<ProgramPoint, Lattice> PerPointStates;
-
-  static constexpr bool isForward() { return Dir == Direction::Forward; }
-
-protected:
-  FactManager &AllFacts;
-
-  explicit DataflowAnalysis(const CFG &C, AnalysisDeclContext &AC,
-                            FactManager &F)
-      : Cfg(C), AC(AC), AllFacts(F) {}
-
-public:
-  void run() {
-    Derived &D = static_cast<Derived &>(*this);
-    llvm::TimeTraceScope Time(D.getAnalysisName());
-
-    using Worklist =
-        std::conditional_t<Dir == Direction::Forward, ForwardDataflowWorklist,
-                           BackwardDataflowWorklist>;
-    Worklist W(Cfg, AC);
-
-    const CFGBlock *Start = isForward() ? &Cfg.getEntry() : &Cfg.getExit();
-    InStates[Start] = D.getInitialState();
-    W.enqueueBlock(Start);
-
-    while (const CFGBlock *B = W.dequeue()) {
-      Lattice StateIn = *getInState(B);
-      Lattice StateOut = transferBlock(B, StateIn);
-      OutStates[B] = StateOut;
-      for (const CFGBlock *AdjacentB : isForward() ? B->succs() : B->preds()) {
-        if (!AdjacentB)
-          continue;
-        std::optional<Lattice> OldInState = getInState(AdjacentB);
-        Lattice NewInState =
-            !OldInState ? StateOut : D.join(*OldInState, StateOut);
-        // Enqueue the adjacent block if its in-state has changed or if we have
-        // never seen it.
-        if (!OldInState || NewInState != *OldInState) {
-          InStates[AdjacentB] = NewInState;
-          W.enqueueBlock(AdjacentB);
-        }
-      }
-    }
-  }
-
-protected:
-  Lattice getState(ProgramPoint P) const { return PerPointStates.lookup(P); }
-
-  std::optional<Lattice> getInState(const CFGBlock *B) const {
-    auto It = InStates.find(B);
-    if (It == InStates.end())
-      return std::nullopt;
-    return It->second;
-  }
-
-  Lattice getOutState(const CFGBlock *B) const { return OutStates.lookup(B); }
-
-  void dump() const {
-    const Derived *D = static_cast<const Derived *>(this);
-    llvm::dbgs() << "==========================================\n";
-    llvm::dbgs() << D->getAnalysisName() << " results:\n";
-    llvm::dbgs() << "==========================================\n";
-    const CFGBlock &B = isForward() ? Cfg.getExit() : Cfg.getEntry();
-    getOutState(&B).dump(llvm::dbgs());
-  }
-
-private:
-  /// Computes the state at one end of a block by applying all its facts
-  /// sequentially to a given state from the other end.
-  Lattice transferBlock(const CFGBlock *Block, Lattice State) {
-    auto Facts = AllFacts.getFacts(Block);
-    if constexpr (isForward()) {
-      for (const Fact *F : Facts) {
-        State = transferFact(State, F);
-        PerPointStates[F] = State;
-      }
-    } else {
-      for (const Fact *F : llvm::reverse(Facts)) {
-        // In backward analysis, capture the state before applying the fact.
-        PerPointStates[F] = State;
-        State = transferFact(State, F);
-      }
-    }
-    return State;
-  }
-
-  Lattice transferFact(Lattice In, const Fact *F) {
-    assert(F);
-    Derived *D = static_cast<Derived *>(this);
-    switch (F->getKind()) {
-    case Fact::Kind::Issue:
-      return D->transfer(In, *F->getAs<IssueFact>());
-    case Fact::Kind::Expire:
-      return D->transfer(In, *F->getAs<ExpireFact>());
-    case Fact::Kind::OriginFlow:
-      return D->transfer(In, *F->getAs<OriginFlowFact>());
-    case Fact::Kind::ReturnOfOrigin:
-      return D->transfer(In, *F->getAs<ReturnOfOriginFact>());
-    case Fact::Kind::Use:
-      return D->transfer(In, *F->getAs<UseFact>());
-    case Fact::Kind::TestPoint:
-      return D->transfer(In, *F->getAs<TestPointFact>());
-    }
-    llvm_unreachable("Unknown fact kind");
-  }
-
-public:
-  Lattice transfer(Lattice In, const IssueFact &) { return In; }
-  Lattice transfer(Lattice In, const ExpireFact &) { return In; }
-  Lattice transfer(Lattice In, const OriginFlowFact &) { return In; }
-  Lattice transfer(Lattice In, const ReturnOfOriginFact &) { return In; }
-  Lattice transfer(Lattice In, const UseFact &) { return In; }
-  Lattice transfer(Lattice In, const TestPointFact &) { return In; }
-};
-
-namespace utils {
-
-/// Computes the union of two ImmutableSets.
-template <typename T>
-static llvm::ImmutableSet<T> join(llvm::ImmutableSet<T> A,
-                                  llvm::ImmutableSet<T> B,
-                                  typename llvm::ImmutableSet<T>::Factory &F) {
-  if (A.getHeight() < B.getHeight())
-    std::swap(A, B);
-  for (const T &E : B)
-    A = F.add(A, E);
-  return A;
-}
-
-/// Describes the strategy for joining two `ImmutableMap` instances, primarily
-/// differing in how they handle keys that are unique to one of the maps.
-///
-/// A `Symmetric` join is universally correct, while an `Asymmetric` join
-/// serves as a performance optimization. The latter is applicable only when the
-/// join operation possesses a left identity element, allowing for a more
-/// efficient, one-sided merge.
-enum class JoinKind {
-  /// A symmetric join applies the `JoinValues` operation to keys unique to
-  /// either map, ensuring that values from both maps contribute to the result.
-  Symmetric,
-  /// An asymmetric join preserves keys unique to the first map as-is, while
-  /// applying the `JoinValues` operation only to keys unique to the second map.
-  Asymmetric,
-};
-
-/// Computes the key-wise union of two ImmutableMaps.
-// TODO(opt): This key-wise join is a performance bottleneck. A more
-// efficient merge could be implemented using a Patricia Trie or HAMT
-// instead of the current AVL-tree-based ImmutableMap.
-template <typename K, typename V, typename Joiner>
-static llvm::ImmutableMap<K, V>
-join(const llvm::ImmutableMap<K, V> &A, const llvm::ImmutableMap<K, V> &B,
-     typename llvm::ImmutableMap<K, V>::Factory &F, Joiner JoinValues,
-     JoinKind Kind) {
-  if (A.getHeight() < B.getHeight())
-    return join(B, A, F, JoinValues, Kind);
-
-  // For each element in B, join it with the corresponding element in A
-  // (or with an empty value if it doesn't exist in A).
-  llvm::ImmutableMap<K, V> Res = A;
-  for (const auto &Entry : B) {
-    const K &Key = Entry.first;
-    const V &ValB = Entry.second;
-    Res = F.add(Res, Key, JoinValues(A.lookup(Key), &ValB));
-  }
-  if (Kind == JoinKind::Symmetric) {
-    for (const auto &Entry : A) {
-      const K &Key = Entry.first;
-      const V &ValA = Entry.second;
-      if (!B.contains(Key))
-        Res = F.add(Res, Key, JoinValues(&ValA, nullptr));
-    }
-  }
-  return Res;
-}
-} // namespace utils
-
-// ========================================================================= //
-//                          Loan Propagation Analysis
-// ========================================================================= //
-
-/// Represents the dataflow lattice for loan propagation.
-///
-/// This lattice tracks which loans each origin may hold at a given program
-/// point.The lattice has a finite height: An origin's loan set is bounded by
-/// the total number of loans in the function.
-/// TODO(opt): To reduce the lattice size, propagate origins of declarations,
-/// not expressions, because expressions are not visible across blocks.
-struct LoanPropagationLattice {
-  /// The map from an origin to the set of loans it contains.
-  OriginLoanMap Origins = OriginLoanMap(nullptr);
-
-  explicit LoanPropagationLattice(const OriginLoanMap &S) : Origins(S) {}
-  LoanPropagationLattice() = default;
-
-  bool operator==(const LoanPropagationLattice &Other) const {
-    return Origins == Other.Origins;
-  }
-  bool operator!=(const LoanPropagationLattice &Other) const {
-    return !(*this == Other);
-  }
-
-  void dump(llvm::raw_ostream &OS) const {
-    OS << "LoanPropagationLattice State:\n";
-    if (Origins.isEmpty())
-      OS << "  <empty>\n";
-    for (const auto &Entry : Origins) {
-      if (Entry.second.isEmpty())
-        OS << "  Origin " << Entry.first << " contains no loans\n";
-      for (const LoanID &LID : Entry.second)
-        OS << "  Origin " << Entry.first << " contains Loan " << LID << "\n";
-    }
-  }
-};
-
-/// The analysis that tracks which loans belong to which origins.
-class LoanPropagationAnalysis
-    : public DataflowAnalysis<LoanPropagationAnalysis, LoanPropagationLattice,
-                              Direction::Forward> {
-  OriginLoanMap::Factory &OriginLoanMapFactory;
-  LoanSet::Factory &LoanSetFactory;
-
-public:
-  LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
-                          OriginLoanMap::Factory &OriginLoanMapFactory,
-                          LoanSet::Factory &LoanSetFactory)
-      : DataflowAnalysis(C, AC, F), OriginLoanMapFactory(OriginLoanMapFactory),
-        LoanSetFactory(LoanSetFactory) {}
-
-  using Base::transfer;
-
-  StringRef getAnalysisName() const { return "LoanPropagation"; }
-
-  Lattice getInitialState() { return Lattice{}; }
-
-  /// Merges two lattices by taking the union of loans for each origin.
-  // TODO(opt): Keep the state small by removing origins which become dead.
-  Lattice join(Lattice A, Lattice B) {
-    OriginLoanMap JoinedOrigins = utils::join(
-        A.Origins, B.Origins, OriginLoanMapFactory,
-        [&](const LoanSet *S1, const LoanSet *S2) {
-          assert((S1 || S2) && "unexpectedly merging 2 empty sets");
-          if (!S1)
-            return *S2;
-          if (!S2)
-            return *S1;
-          return utils::join(*S1, *S2, LoanSetFactory);
-        },
-        // Asymmetric join is a performance win. For origins present only on one
-        // branch, the loan set can be carried over as-is.
-        utils::JoinKind::Asymmetric);
-    return Lattice(JoinedOrigins);
-  }
-
-  /// A new loan is issued to the origin. Old loans are erased.
-  Lattice transfer(Lattice In, const IssueFact &F) {
-    OriginID OID = F.getOriginID();
-    LoanID LID = F.getLoanID();
-    return LoanPropagationLattice(OriginLoanMapFactory.add(
-        In.Origins, OID,
-        LoanSetFactory.add(LoanSetFactory.getEmptySet(), LID)));
-  }
-
-  /// A flow from source to destination. If `KillDest` is true, this replaces
-  /// the destination's loans with the source's. Otherwise, the source's loans
-  /// are merged into the destination's.
-  Lattice transfer(Lattice In, const OriginFlowFact &F) {
-    OriginID DestOID = F.getDestOriginID();
-    OriginID SrcOID = F.getSrcOriginID();
-
-    LoanSet DestLoans =
-        F.getKillDest() ? LoanSetFactory.getEmptySet() : getLoans(In, DestOID);
-    LoanSet SrcLoans = getLoans(In, SrcOID);
-    LoanSet MergedLoans = utils::join(DestLoans, SrcLoans, LoanSetFactory);
-
-    return LoanPropagationLattice(
-        OriginLoanMapFactory.add(In.Origins, DestOID, MergedLoans));
-  }
-
-  LoanSet getLoans(OriginID OID, ProgramPoint P) const {
-    return getLoans(getState(P), OID);
-  }
-
-private:
-  LoanSet getLoans(Lattice L, OriginID OID) const {
-    if (auto *Loans = L.Origins.lookup(OID))
-      return *Loans;
-    return LoanSetFactory.getEmptySet();
-  }
-};
-
-// ========================================================================= //
-//                         Live Origins Analysis
-// ========================================================================= //
-//
-// A backward dataflow analysis that determines which origins are "live" at each
-// program point. An origin is "live" at a program point if there's a potential
-// future use of the pointer it represents. Liveness is "generated" by a read of
-// origin's loan set (e.g., a `UseFact`) and is "killed" (i.e., it stops being
-// live) when its loan set is overwritten (e.g. a OriginFlow killing the
-// destination origin).
-//
-// This information is used for detecting use-after-free errors, as it allows us
-// to check if a live origin holds a loan to an object that has already expired.
-// ========================================================================= //
-
-/// Information about why an origin is live at a program point.
-struct LivenessInfo {
-  /// The use that makes the origin live. If liveness is propagated from
-  /// multiple uses along different paths, this will point to the use appearing
-  /// earlier in the translation unit.
-  /// This is 'null' when the origin is not live.
-  const UseFact *CausingUseFact;
-  /// The kind of liveness of the origin.
-  /// `Must`: The origin is live on all control-flow paths from the current
-  /// point to the function's exit (i.e. the current point is dominated by a set
-  /// of uses).
-  /// `Maybe`: indicates it is live on some but not all paths.
-  ///
-  /// This determines the diagnostic's confidence level.
-  /// `Must`-be-alive at expiration implies a definite use-after-free,
-  /// while `Maybe`-be-alive suggests a potential one on some paths.
-  LivenessKind Kind;
-
-  LivenessInfo() : CausingUseFact(nullptr), Kind(LivenessKind::Dead) {}
-  LivenessInfo(const UseFact *UF, LivenessKind K)
-      : CausingUseFact(UF), Kind(K) {}
-
-  bool operator==(const LivenessInfo &Other) const {
-    return CausingUseFact == Other.CausingUseFact && Kind == Other.Kind;
-  }
-  bool operator!=(const LivenessInfo &Other) const { return !(*this == Other); }
-
-  void Profile(llvm::FoldingSetNodeID &IDBuilder) const {
-    IDBuilder.AddPointer(CausingUseFact);
-    IDBuilder.Add(Kind);
-  }
-};
-
-using LivenessMap = llvm::ImmutableMap<OriginID, LivenessInfo>;
-
-/// The dataflow lattice for origin liveness analysis.
-/// It tracks which origins are live, why they're live (which UseFact),
-/// and the confidence level of that liveness.
-struct LivenessLattice {
-  LivenessMap LiveOrigins;
-
-  LivenessLattice() : LiveOrigins(nullptr) {};
-
-  explicit LivenessLattice(LivenessMap L) : LiveOrigins(L) {}
-
-  bool operator==(const LivenessLattice &Other) const {
-    return LiveOrigins == Other.LiveOrigins;
-  }
-
-  bool operator!=(const LivenessLattice &Other) const {
-    return !(*this == Other);
-  }
-
-  void dump(llvm::raw_ostream &OS, const OriginManager &OM) const {
-    if (LiveOrigins.isEmpty())
-      OS << "  <empty>\n";
-    for (const auto &Entry : LiveOrigins) {
-      OriginID OID = Entry.first;
-      const LivenessInfo &Info = Entry.second;
-      OS << "  ";
-      OM.dump(OID, OS);
-      OS << " is ";
-      switch (Info.Kind) {
-      case LivenessKind::Must:
-        OS << "definitely";
-        break;
-      case LivenessKind::Maybe:
-        OS << "maybe";
-        break;
-      case LivenessKind::Dead:
-        llvm_unreachable("liveness kind of live origins should not be dead.");
-      }
-      OS << " live at this point\n";
-    }
-  }
-};
-
-/// The analysis that tracks which origins are live, with granular information
-/// about the causing use fact and confidence level. This is a backward
-/// analysis.
-class LiveOriginAnalysis
-    : public DataflowAnalysis<LiveOriginAnalysis, LivenessLattice,
-                              Direction::Backward> {
-  FactManager &FactMgr;
-  LivenessMap::Factory &Factory;
-
-public:
-  LiveOriginAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
-                     LivenessMap::Factory &SF)
-      : DataflowAnalysis(C, AC, F), FactMgr(F), Factory(SF) {}
-  using DataflowAnalysis<LiveOriginAnalysis, Lattice,
-                         Direction::Backward>::transfer;
-
-  StringRef getAnalysisName() const { return "LiveOrigins"; }
-
-  Lattice getInitialState() { return Lattice(Factory.getEmptyMap()); }
-
-  /// Merges two lattices by combining liveness information.
-  /// When the same origin has different confidence levels, we take the lower
-  /// one.
-  Lattice join(Lattice L1, Lattice L2) const {
-    LivenessMap Merged = L1.LiveOrigins;
-    // Take the earliest UseFact to make the join hermetic and commutative.
-    auto CombineUseFact = [](const UseFact &A,
-                             const UseFact &B) -> const UseFact * {
-      return A.getUseExpr()->getExprLoc() < B.getUseExpr()->getExprLoc() ? &A
-                                                                         : &B;
-    };
-    auto CombineLivenessKind = [](LivenessKind K1,
-                                  LivenessKind K2) -> LivenessKind {
-      assert(K1 != LivenessKind::Dead && "LivenessKind should not be dead.");
-      assert(K2 != LivenessKind::Dead && "LivenessKind should not be dead.");
-      // Only return "Must" if both paths are "Must", otherwise Maybe.
-      if (K1 == LivenessKind::Must && K2 == LivenessKind::Must)
-        return LivenessKind::Must;
-      return LivenessKind::Maybe;
-    };
-    auto CombineLivenessInfo = [&](const LivenessInfo *L1,
-                                   const LivenessInfo *L2) -> LivenessInfo {
-      assert((L1 || L2) && "unexpectedly merging 2 empty sets");
-      if (!L1)
-        return LivenessInfo(L2->CausingUseFact, LivenessKind::Maybe);
-      if (!L2)
-        return LivenessInfo(L1->CausingUseFact, LivenessKind::Maybe);
-      return LivenessInfo(
-          CombineUseFact(*L1->CausingUseFact, *L2->CausingUseFact),
-          CombineLivenessKind(L1->Kind, L2->Kind));
-    };
-    return Lattice(utils::join(
-        L1.LiveOrigins, L2.LiveOrigins, Factory, CombineLivenessInfo,
-        // A symmetric join is required here. If an origin is live on one
-        // branch but not the other, its confidence must be demoted to `Maybe`.
-        utils::JoinKind::Symmetric));
-  }
-
-  /// A read operation makes the origin live with definite confidence, as it
-  /// dominates this program point. A write operation kills the liveness of
-  /// the origin since it overwrites the value.
-  Lattice transfer(Lattice In, const UseFact &UF) {
-    OriginID OID = UF.getUsedOrigin(FactMgr.getOriginMgr());
-    // Write kills liveness.
-    if (UF.isWritten())
-      return Lattice(Factory.remove(In.LiveOrigins, OID));
-    // Read makes origin live with definite confidence (dominates this point).
-    return Lattice(Factory.add(In.LiveOrigins, OID,
-                               LivenessInfo(&UF, LivenessKind::Must)));
-  }
-
-  /// Issuing a new loan to an origin kills its liveness.
-  Lattice transfer(Lattice In, const IssueFact &IF) {
-    return Lattice(Factory.remove(In.LiveOrigins, IF.getOriginID()));
-  }
-
-  /// An OriginFlow kills the liveness of the destination origin if `KillDest`
-  /// is true. Otherwise, it propagates liveness from destination to source.
-  Lattice transfer(Lattice In, const OriginFlowFact &OF) {
-    if (!OF.getKillDest())
-      return In;
-    return Lattice(Factory.remove(In.LiveOrigins, OF.getDestOriginID()));
-  }
-
-  LivenessMap getLiveOrigins(ProgramPoint P) const {
-    return getState(P).LiveOrigins;
-  }
-
-  // Dump liveness values on all test points in the program.
-  void dump(llvm::raw_ostream &OS, const LifetimeSafetyAnalysis &LSA) const {
-    llvm::dbgs() << "==========================================\n";
-    llvm::dbgs() << getAnalysisName() << " results:\n";
-    llvm::dbgs() << "==========================================\n";
-    for (const auto &Entry : LSA.getTestPoints()) {
-      OS << "TestPoint: " << Entry.getKey() << "\n";
-      getState(Entry.getValue()).dump(OS, FactMgr.getOriginMgr());
-    }
-  }
-};
-
-// ========================================================================= //
-//                       Lifetime checker and Error reporter
-// ========================================================================= //
-
-/// Struct to store the complete context for a potential lifetime violation.
-struct PendingWarning {
-  SourceLocation ExpiryLoc; // Where the loan expired.
-  const Expr *UseExpr;      // Where the origin holding this loan was used.
-  Confidence ConfidenceLevel;
-};
-
-class LifetimeChecker {
-private:
-  llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
-  LoanPropagationAnalysis &LoanPropagation;
-  LiveOriginAnalysis &LiveOrigins;
-  FactManager &FactMgr;
-  AnalysisDeclContext &ADC;
-  LifetimeSafetyReporter *Reporter;
-
-public:
-  LifetimeChecker(LoanPropagationAnalysis &LPA, LiveOriginAnalysis &LOA,
-                  FactManager &FM, AnalysisDeclContext &ADC,
-                  LifetimeSafetyReporter *Reporter)
-      : LoanPropagation(LPA), LiveOrigins(LOA), FactMgr(FM), ADC(ADC),
-        Reporter(Reporter) {}
-
-  void run() {
-    llvm::TimeTraceScope TimeProfile("LifetimeChecker");
-    for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
-      for (const Fact *F : FactMgr.getFacts(B))
-        if (const auto *EF = F->getAs<ExpireFact>())
-          checkExpiry(EF);
-    issuePendingWarnings();
-  }
-
-  /// Checks for use-after-free errors when a loan expires.
-  ///
-  /// This method examines all live origins at the expiry point and determines
-  /// if any of them hold the expiring loan. If so, it creates a pending
-  /// warning with the appropriate confidence level based on the liveness
-  /// information. The confidence reflects whether the origin is definitely
-  /// or maybe live at this point.
-  ///
-  /// Note: This implementation considers only the confidence of origin
-  /// liveness. Future enhancements could also consider the confidence of loan
-  /// propagation (e.g., a loan may only be held on some execution paths).
-  void checkExpiry(const ExpireFact *EF) {
-    LoanID ExpiredLoan = EF->getLoanID();
-    LivenessMap Origins = LiveOrigins.getLiveOrigins(EF);
-    Confidence CurConfidence = Confidence::None;
-    const UseFact *BadUse = nullptr;
-    for (auto &[OID, LiveInfo] : Origins) {
-      LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
-      if (!HeldLoans.contains(ExpiredLoan))
-        continue;
-      // Loan is defaulted.
-      Confidence NewConfidence = livenessKindToConfidence(LiveInfo.Kind);
-      if (CurConfidence < NewConfidence) {
-        CurConfidence = NewConfidence;
-        BadUse = LiveInfo.CausingUseFact;
-      }
-    }
-    if (!BadUse)
-      return;
-    // We have a use-after-free.
-    Confidence LastConf = FinalWarningsMap.lookup(ExpiredLoan).ConfidenceLevel;
-    if (LastConf >= CurConfidence)
-      return;
-    FinalWarningsMap[ExpiredLoan] = {/*ExpiryLoc=*/EF->getExpiryLoc(),
-                                     /*UseExpr=*/BadUse->getUseExpr(),
-                                     /*ConfidenceLevel=*/CurConfidence};
-  }
-
-  static Confidence livenessKindToConfidence(LivenessKind K) {
-    switch (K) {
-    case LivenessKind::Must:
-      return Confidence::Definite;
-    case LivenessKind::Maybe:
-      return Confidence::Maybe;
-    case LivenessKind::Dead:
-      return Confidence::None;
-    }
-    llvm_unreachable("unknown liveness kind");
-  }
-
-  void issuePendingWarnings() {
-    if (!Reporter)
-      return;
-    for (const auto &[LID, Warning] : FinalWarningsMap) {
-      const Loan &L = FactMgr.getLoanMgr().getLoan(LID);
-      const Expr *IssueExpr = L.IssueExpr;
-      Reporter->reportUseAfterFree(IssueExpr, Warning.UseExpr,
-                                   Warning.ExpiryLoc, Warning.ConfidenceLevel);
-    }
-  }
-};
-
-// ========================================================================= //
-//                  LifetimeSafetyAnalysis Class Implementation
-// ========================================================================= //
-
-/// An object to hold the factories for immutable collections, ensuring
-/// that all created states share the same underlying memory management.
-struct LifetimeFactory {
-  llvm::BumpPtrAllocator Allocator;
-  OriginLoanMap::Factory OriginMapFactory{Allocator, /*canonicalize=*/false};
-  LoanSet::Factory LoanSetFactory{Allocator, /*canonicalize=*/false};
-  LivenessMap::Factory LivenessMapFactory{Allocator, /*canonicalize=*/false};
-};
-
-// We need this here for unique_ptr with forward declared class.
-LifetimeSafetyAnalysis::~LifetimeSafetyAnalysis() = default;
-
-LifetimeSafetyAnalysis::LifetimeSafetyAnalysis(AnalysisDeclContext &AC,
-                                               LifetimeSafetyReporter *Reporter)
-    : AC(AC), Reporter(Reporter), Factory(std::make_unique<LifetimeFactory>()),
-      FactMgr(std::make_unique<FactManager>()) {}
-
-void LifetimeSafetyAnalysis::run() {
-  llvm::TimeTraceScope TimeProfile("LifetimeSafetyAnalysis");
-
-  const CFG &Cfg = *AC.getCFG();
-  DEBUG_WITH_TYPE("PrintCFG", Cfg.dump(AC.getASTContext().getLangOpts(),
-                                       /*ShowColors=*/true));
-
-  FactGenerator FactGen(*FactMgr, AC);
-  FactGen.run();
-  DEBUG_WITH_TYPE("LifetimeFacts", FactMgr->dump(Cfg, AC));
-
-  /// TODO(opt): Consider optimizing individual blocks before running the
-  /// dataflow analysis.
-  /// 1. Expression Origins: These are assigned once and read at most once,
-  ///    forming simple chains. These chains can be compressed into a single
-  ///    assignment.
-  /// 2. Block-Local Loans: Origins of expressions are never read by other
-  ///    blocks; only Decls are visible.  Therefore, loans in a block that
-  ///    never reach an Origin associated with a Decl can be safely dropped by
-  ///    the analysis.
-  /// 3. Collapse ExpireFacts belonging to same source location into a single
-  ///    Fact.
-  LoanPropagation = std::make_unique<LoanPropagationAnalysis>(
-      Cfg, AC, *FactMgr, Factory->OriginMapFactory, Factory->LoanSetFactory);
-  LoanPropagation->run();
-
-  LiveOrigins = std::make_unique<LiveOriginAnalysis>(
-      Cfg, AC, *FactMgr, Factory->LivenessMapFactory);
-  LiveOrigins->run();
-  DEBUG_WITH_TYPE("LiveOrigins", LiveOrigins->dump(llvm::dbgs(), *this));
-
-  LifetimeChecker Checker(*LoanPropagation, *LiveOrigins, *FactMgr, AC,
-                          Reporter);
-  Checker.run();
-}
-
-LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID,
-                                                ProgramPoint PP) const {
-  assert(LoanPropagation && "Analysis has not been run.");
-  return LoanPropagation->getLoans(OID, PP);
-}
-
-std::optional<OriginID>
-LifetimeSafetyAnalysis::getOriginIDForDecl(const ValueDecl *D) const {
-  assert(FactMgr && "FactManager not initialized");
-  // This assumes the OriginManager's `get` can find an existing origin.
-  // We might need a `find` method on OriginManager to avoid `getOrCreate` logic
-  // in a const-query context if that becomes an issue.
-  return FactMgr->getOriginMgr().get(*D);
-}
-
-std::vector<LoanID>
-LifetimeSafetyAnalysis::getLoanIDForVar(const VarDecl *VD) const {
-  assert(FactMgr && "FactManager not initialized");
-  std::vector<LoanID> Result;
-  for (const Loan &L : FactMgr->getLoanMgr().getLoans())
-    if (L.Path.D == VD)
-      Result.push_back(L.ID);
-  return Result;
-}
-
-std::vector<std::pair<OriginID, LivenessKind>>
-LifetimeSafetyAnalysis::getLiveOriginsAtPoint(ProgramPoint PP) const {
-  assert(LiveOrigins && "LiveOriginAnalysis has not been run.");
-  std::vector<std::pair<OriginID, LivenessKind>> Result;
-  for (auto &[OID, Info] : LiveOrigins->getLiveOrigins(PP))
-    Result.push_back({OID, Info.Kind});
-  return Result;
-}
-
-llvm::StringMap<ProgramPoint> LifetimeSafetyAnalysis::getTestPoints() const {
-  assert(FactMgr && "FactManager not initialized");
-  llvm::StringMap<ProgramPoint> AnnotationToPointMap;
-  for (const CFGBlock *Block : *AC.getCFG()) {
-    for (const Fact *F : FactMgr->getFacts(Block)) {
-      if (const auto *TPF = F->getAs<TestPointFact>()) {
-        StringRef PointName = TPF->getAnnotation();
-        assert(AnnotationToPointMap.find(PointName) ==
-                   AnnotationToPointMap.end() &&
-               "more than one test points with the same name");
-        AnnotationToPointMap[PointName] = F;
-      }
-    }
-  }
-  return AnnotationToPointMap;
-}
-} // namespace internal
-
-void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
-                               LifetimeSafetyReporter *Reporter) {
-  internal::LifetimeSafetyAnalysis Analysis(AC, Reporter);
-  Analysis.run();
-}
-} // namespace clang::lifetimes
diff --git a/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt b/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt
new file mode 100644
index 0000000000000..5a0a0f776163d
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt
@@ -0,0 +1,14 @@
+add_clang_library(clangAnalysisLifetimeSafety
+  LifetimeAnnotations.cpp
+  LifetimeSafety.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+
+  DEPENDS
+  omp_gen
+  ClangDriverOptions
+  )
diff --git a/clang/lib/Analysis/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
similarity index 97%
rename from clang/lib/Analysis/LifetimeAnnotations.cpp
rename to clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index e79122475625e..87d3db77cc65e 100644
--- a/clang/lib/Analysis/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-#include "clang/Analysis/Analyses/LifetimeAnnotations.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Attr.h"
 #include "clang/AST/Decl.h"
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
new file mode 100644
index 0000000000000..d4cf0a6b63c55
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
@@ -0,0 +1,147 @@
+//===- LifetimeSafety.cpp - C++ Lifetime Safety Analysis -*--------- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Checker.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Dataflow.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/TimeProfiler.h"
+#include <memory>
+#include <optional>
+
+namespace clang::lifetimes {
+namespace internal {
+
+// ========================================================================= //
+//                  LifetimeSafetyAnalysis Class Implementation
+// ========================================================================= //
+
+/// An object to hold the factories for immutable collections, ensuring
+/// that all created states share the same underlying memory management.
+struct LifetimeFactory {
+  llvm::BumpPtrAllocator Allocator;
+  OriginLoanMap::Factory OriginMapFactory{Allocator, /*canonicalize=*/false};
+  LoanSet::Factory LoanSetFactory{Allocator, /*canonicalize=*/false};
+  LivenessMap::Factory LivenessMapFactory{Allocator, /*canonicalize=*/false};
+};
+
+// We need this here for unique_ptr with forward declared class.
+LifetimeSafetyAnalysis::~LifetimeSafetyAnalysis() = default;
+
+LifetimeSafetyAnalysis::LifetimeSafetyAnalysis(AnalysisDeclContext &AC,
+                                               LifetimeSafetyReporter *Reporter)
+    : AC(AC), Reporter(Reporter), Factory(std::make_unique<LifetimeFactory>()),
+      FactMgr(std::make_unique<FactManager>()) {}
+
+void LifetimeSafetyAnalysis::run() {
+  llvm::TimeTraceScope TimeProfile("LifetimeSafetyAnalysis");
+
+  const CFG &Cfg = *AC.getCFG();
+  DEBUG_WITH_TYPE("PrintCFG", Cfg.dump(AC.getASTContext().getLangOpts(),
+                                       /*ShowColors=*/true));
+
+  FactGenerator FactGen(*FactMgr, AC);
+  FactGen.run();
+  DEBUG_WITH_TYPE("LifetimeFacts", FactMgr->dump(Cfg, AC));
+
+  /// TODO(opt): Consider optimizing individual blocks before running the
+  /// dataflow analysis.
+  /// 1. Expression Origins: These are assigned once and read at most once,
+  ///    forming simple chains. These chains can be compressed into a single
+  ///    assignment.
+  /// 2. Block-Local Loans: Origins of expressions are never read by other
+  ///    blocks; only Decls are visible.  Therefore, loans in a block that
+  ///    never reach an Origin associated with a Decl can be safely dropped by
+  ///    the analysis.
+  /// 3. Collapse ExpireFacts belonging to same source location into a single
+  ///    Fact.
+  LoanPropagation = std::make_unique<LoanPropagationAnalysis>(
+      Cfg, AC, *FactMgr, Factory->OriginMapFactory, Factory->LoanSetFactory);
+  LoanPropagation->run();
+
+  LiveOrigins = std::make_unique<LiveOriginAnalysis>(
+      Cfg, AC, *FactMgr, Factory->LivenessMapFactory);
+  LiveOrigins->run();
+  DEBUG_WITH_TYPE("LiveOrigins", LiveOrigins->dump(llvm::dbgs(), *this));
+
+  LifetimeChecker Checker(*LoanPropagation, *LiveOrigins, *FactMgr, AC,
+                          Reporter);
+  Checker.run();
+}
+
+LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID,
+                                                ProgramPoint PP) const {
+  assert(LoanPropagation && "Analysis has not been run.");
+  return LoanPropagation->getLoans(OID, PP);
+}
+
+std::optional<OriginID>
+LifetimeSafetyAnalysis::getOriginIDForDecl(const ValueDecl *D) const {
+  assert(FactMgr && "FactManager not initialized");
+  // This assumes the OriginManager's `get` can find an existing origin.
+  // We might need a `find` method on OriginManager to avoid `getOrCreate` logic
+  // in a const-query context if that becomes an issue.
+  return FactMgr->getOriginMgr().get(*D);
+}
+
+std::vector<LoanID>
+LifetimeSafetyAnalysis::getLoanIDForVar(const VarDecl *VD) const {
+  assert(FactMgr && "FactManager not initialized");
+  std::vector<LoanID> Result;
+  for (const Loan &L : FactMgr->getLoanMgr().getLoans())
+    if (L.Path.D == VD)
+      Result.push_back(L.ID);
+  return Result;
+}
+
+std::vector<std::pair<OriginID, LivenessKind>>
+LifetimeSafetyAnalysis::getLiveOriginsAtPoint(ProgramPoint PP) const {
+  assert(LiveOrigins && "LiveOriginAnalysis has not been run.");
+  std::vector<std::pair<OriginID, LivenessKind>> Result;
+  for (auto &[OID, Info] : LiveOrigins->getLiveOrigins(PP))
+    Result.push_back({OID, Info.Kind});
+  return Result;
+}
+
+llvm::StringMap<ProgramPoint> LifetimeSafetyAnalysis::getTestPoints() const {
+  assert(FactMgr && "FactManager not initialized");
+  llvm::StringMap<ProgramPoint> AnnotationToPointMap;
+  for (const CFGBlock *Block : *AC.getCFG()) {
+    for (const Fact *F : FactMgr->getFacts(Block)) {
+      if (const auto *TPF = F->getAs<TestPointFact>()) {
+        StringRef PointName = TPF->getAnnotation();
+        assert(AnnotationToPointMap.find(PointName) ==
+                   AnnotationToPointMap.end() &&
+               "more than one test points with the same name");
+        AnnotationToPointMap[PointName] = F;
+      }
+    }
+  }
+  return AnnotationToPointMap;
+}
+} // namespace internal
+
+void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
+                               LifetimeSafetyReporter *Reporter) {
+  internal::LifetimeSafetyAnalysis Analysis(AC, Reporter);
+  Analysis.run();
+}
+} // namespace clang::lifetimes
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index e9ca8cef07fa1..9abaf79bed7db 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -29,7 +29,7 @@
 #include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
 #include "clang/Analysis/Analyses/CalledOnceCheck.h"
 #include "clang/Analysis/Analyses/Consumed.h"
-#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
 #include "clang/Analysis/Analyses/ReachableCode.h"
 #include "clang/Analysis/Analyses/ThreadSafety.h"
 #include "clang/Analysis/Analyses/UninitializedValues.h"
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 51e0ee10b080b..0ebf56ecffe69 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -111,6 +111,7 @@ add_clang_library(clangSema
   clangAPINotes
   clangAST
   clangAnalysis
+  clangAnalysisLifetimeSafety
   clangBasic
   clangEdit
   clangLex
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index e8a7ad3bd355a..8aebf53c0e76f 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -10,7 +10,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/Type.h"
-#include "clang/Analysis/Analyses/LifetimeAnnotations.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
 #include "clang/Basic/DiagnosticSema.h"
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/Sema.h"
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index 35cdfbf8bf390..0d8d0faa59abd 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -17,7 +17,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/TypeLoc.h"
-#include "clang/Analysis/Analyses/LifetimeAnnotations.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Lex/Lexer.h"
 #include "clang/Sema/SemaObjC.h"
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index 169b2d2d5e9b4..a280222b0cbfc 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/Analysis/Analyses/LifetimeSafety.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Testing/TestAST.h"

>From 5d811c20e7919bfd1cd4a3a279b1192a3eb82f11 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Wed, 8 Oct 2025 14:36:22 +0200
Subject: [PATCH 02/10] Apply changes from code browser

Apply changes from code browser
---
 .../clang/Analysis/Analyses/LifetimeSafety/Checker.h       | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
index f5bcb83fbac89..7fa1da47697a4 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
@@ -5,7 +5,12 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// TODO: Complete me.
+//
+// This file defines the LifetimeChecker, which detects use-after-free errors
+// by checking if live origins hold loans that have expired. It combines the
+// results of loan propagation and liveness analysis to report violations with
+// appropriate confidence levels.
+//
 //===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H

>From 92eb3771178b7578e163abab14239cfc06ca316d Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Wed, 8 Oct 2025 14:45:45 +0200
Subject: [PATCH 03/10] Apply changes from code browser

Apply changes from code browser
---
 .../Analyses/LifetimeSafety/Checker.h         |  2 +-
 .../Analyses/LifetimeSafety/Dataflow.h        | 18 +++-
 .../Analysis/Analyses/LifetimeSafety/Facts.h  | 21 +++-
 .../Analyses/LifetimeSafety/LifetimeSafety.h  | 99 +------------------
 .../Analyses/LifetimeSafety/LiveOrigins.h     | 29 +++++-
 .../Analyses/LifetimeSafety/LoanPropagation.h | 21 +++-
 .../Analysis/Analyses/LifetimeSafety/Loans.h  | 23 ++++-
 .../Analyses/LifetimeSafety/Origins.h         | 22 ++++-
 .../Analyses/LifetimeSafety/Reporter.h        | 35 +++++++
 .../Analysis/Analyses/LifetimeSafety/Utils.h  | 52 ++++++++++
 .../LifetimeSafety/LifetimeSafety.cpp         |  9 +-
 11 files changed, 224 insertions(+), 107 deletions(-)
 create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety/Reporter.h

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
index 7fa1da47697a4..79db9f9d6aac8 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
@@ -18,10 +18,10 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
-#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Reporter.h"
 #include "clang/Analysis/Analyses/PostOrderCFGView.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Dataflow.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Dataflow.h
index c4df15a93f740..e818b26739e18 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Dataflow.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Dataflow.h
@@ -1,3 +1,17 @@
+//===- Dataflow.h - Generic Dataflow Analysis Framework --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a generic, policy-based driver for dataflow analyses.
+// It provides a flexible framework that combines the dataflow runner and
+// transfer functions, allowing derived classes to implement specific analyses
+// by defining their lattice, join, and transfer functions.
+//
+//===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_DATAFLOW_H
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_DATAFLOW_H
 
@@ -13,10 +27,6 @@
 namespace clang::lifetimes {
 namespace internal {
 
-// ========================================================================= //
-//                         Generic Dataflow Analysis
-// ========================================================================= //
-
 enum class Direction { Forward, Backward };
 
 /// A `ProgramPoint` identifies a location in the CFG by pointing to a specific
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 3866db466c617..535320e12b5f6 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -1,9 +1,21 @@
+//===- Facts.h - Lifetime Analysis Facts and Fact Generation ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines Facts, which are atomic lifetime-relevant events (such as
+// loan issuance, loan expiration, origin flow, and use), and the FactGenerator,
+// which traverses the AST to generate these facts from CFG statements.
+//
+//===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H
 
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
-#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
 #include "clang/Analysis/Analyses/PostOrderCFGView.h"
@@ -64,6 +76,13 @@ class Fact {
   }
 };
 
+/// A `ProgramPoint` identifies a location in the CFG by pointing to a specific
+/// `Fact`. identified by a lifetime-related event (`Fact`).
+///
+/// A `ProgramPoint` has "after" semantics: it represents the location
+/// immediately after its corresponding `Fact`.
+using ProgramPoint = const Fact *;
+
 class IssueFact : public Fact {
   LoanID LID;
   OriginID OID;
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index e54fc26df28ea..0745eeba9f53a 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -17,94 +17,26 @@
 //===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H
+
+#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Reporter.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
-#include "clang/Basic/SourceLocation.h"
-#include "llvm/ADT/DenseMapInfo.h"
-#include "llvm/ADT/ImmutableMap.h"
-#include "llvm/ADT/ImmutableSet.h"
 #include "llvm/ADT/StringMap.h"
 #include <memory>
 
 namespace clang::lifetimes {
 
-/// Enum to track the confidence level of a potential error.
-enum class Confidence : uint8_t {
-  None,
-  Maybe,   // Reported as a potential error (-Wlifetime-safety-strict)
-  Definite // Reported as a definite error (-Wlifetime-safety-permissive)
-};
-
-enum class LivenessKind : uint8_t {
-  Dead,  // Not alive
-  Maybe, // Live on some path but not all paths (may-be-live)
-  Must   // Live on all paths (must-be-live)
-};
-
-class LifetimeSafetyReporter {
-public:
-  LifetimeSafetyReporter() = default;
-  virtual ~LifetimeSafetyReporter() = default;
-
-  virtual void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr,
-                                  SourceLocation FreeLoc,
-                                  Confidence Confidence) {}
-};
-
 /// The main entry point for the analysis.
 void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
                                LifetimeSafetyReporter *Reporter);
 
 namespace internal {
 // Forward declarations of internal types.
-class Fact;
-class FactManager;
-class LoanPropagationAnalysis;
-class ExpiredLoansAnalysis;
-class LiveOriginAnalysis;
 struct LifetimeFactory;
 
-/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
-/// Used for giving ID to loans and origins.
-template <typename Tag> struct ID {
-  uint32_t Value = 0;
-
-  bool operator==(const ID<Tag> &Other) const { return Value == Other.Value; }
-  bool operator!=(const ID<Tag> &Other) const { return !(*this == Other); }
-  bool operator<(const ID<Tag> &Other) const { return Value < Other.Value; }
-  ID<Tag> operator++(int) {
-    ID<Tag> Tmp = *this;
-    ++Value;
-    return Tmp;
-  }
-  void Profile(llvm::FoldingSetNodeID &IDBuilder) const {
-    IDBuilder.AddInteger(Value);
-  }
-};
-
-using LoanID = ID<struct LoanTag>;
-using OriginID = ID<struct OriginTag>;
-inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, LoanID ID) {
-  return OS << ID.Value;
-}
-inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, OriginID ID) {
-  return OS << ID.Value;
-}
-
-// Using LLVM's immutable collections is efficient for dataflow analysis
-// as it avoids deep copies during state transitions.
-// TODO(opt): Consider using a bitset to represent the set of loans.
-using LoanSet = llvm::ImmutableSet<LoanID>;
-using OriginSet = llvm::ImmutableSet<OriginID>;
-using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
-
-/// A `ProgramPoint` identifies a location in the CFG by pointing to a specific
-/// `Fact`. identified by a lifetime-related event (`Fact`).
-///
-/// A `ProgramPoint` has "after" semantics: it represents the location
-/// immediately after its corresponding `Fact`.
-using ProgramPoint = const Fact *;
-
 /// Running the lifetime safety analysis and querying its results. It
 /// encapsulates the various dataflow analyses.
 class LifetimeSafetyAnalysis {
@@ -159,25 +91,4 @@ class LifetimeSafetyAnalysis {
 } // namespace internal
 } // namespace clang::lifetimes
 
-namespace llvm {
-template <typename Tag>
-struct DenseMapInfo<clang::lifetimes::internal::ID<Tag>> {
-  using ID = clang::lifetimes::internal::ID<Tag>;
-
-  static inline ID getEmptyKey() {
-    return {DenseMapInfo<uint32_t>::getEmptyKey()};
-  }
-
-  static inline ID getTombstoneKey() {
-    return {DenseMapInfo<uint32_t>::getTombstoneKey()};
-  }
-
-  static unsigned getHashValue(const ID &Val) {
-    return DenseMapInfo<uint32_t>::getHashValue(Val.Value);
-  }
-
-  static bool isEqual(const ID &LHS, const ID &RHS) { return LHS == RHS; }
-};
-} // namespace llvm
-
 #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h
index 9432b9c31f407..6b0bb7b52875e 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h
@@ -1,7 +1,21 @@
+//===- LiveOrigins.h - Live Origins Analysis -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the LiveOriginAnalysis, a backward dataflow analysis that
+// determines which origins are "live" at each program point. An origin is live
+// if there's a potential future use of the pointer it represents. This
+// information is used to detect use-after-free errors by checking if live
+// origins hold loans to objects that have already expired.
+//
+//===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LIVE_ORIGINS_H
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LIVE_ORIGINS_H
 
-#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/Type.h"
@@ -19,6 +33,14 @@
 namespace clang::lifetimes {
 namespace internal {
 
+using OriginSet = llvm::ImmutableSet<OriginID>;
+
+enum class LivenessKind : uint8_t {
+  Dead,  // Not alive
+  Maybe, // Live on some path but not all paths (may-be-live)
+  Must   // Live on all paths (must-be-live)
+};
+
 // ========================================================================= //
 //                         Live Origins Analysis
 // ========================================================================= //
@@ -200,11 +222,12 @@ class LiveOriginAnalysis
   }
 
   // Dump liveness values on all test points in the program.
-  void dump(llvm::raw_ostream &OS, const LifetimeSafetyAnalysis &LSA) const {
+  void dump(llvm::raw_ostream &OS,
+            llvm::StringMap<ProgramPoint> TestPoints) const {
     llvm::dbgs() << "==========================================\n";
     llvm::dbgs() << getAnalysisName() << " results:\n";
     llvm::dbgs() << "==========================================\n";
-    for (const auto &Entry : LSA.getTestPoints()) {
+    for (const auto &Entry : TestPoints) {
       OS << "TestPoint: " << Entry.getKey() << "\n";
       getState(Entry.getValue()).dump(OS, FactMgr.getOriginMgr());
     }
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index d49cf1c323c43..8510f40637b84 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -1,9 +1,22 @@
+//===- LoanPropagation.h - Loan Propagation Analysis -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the LoanPropagationAnalysis, a forward dataflow analysis
+// that tracks which loans each origin holds at each program point. Loans
+// represent borrows of storage locations and are propagated through the
+// program as pointers are copied or assigned.
+//
+//===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOAN_PROPAGATION_H
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOAN_PROPAGATION_H
 
 #include "clang/Analysis/Analyses/LifetimeSafety/Dataflow.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
-#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
@@ -14,6 +27,12 @@
 namespace clang::lifetimes {
 namespace internal {
 
+// Using LLVM's immutable collections is efficient for dataflow analysis
+// as it avoids deep copies during state transitions.
+// TODO(opt): Consider using a bitset to represent the set of loans.
+using LoanSet = llvm::ImmutableSet<LoanID>;
+using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
+
 // ========================================================================= //
 //                          Loan Propagation Analysis
 // ========================================================================= //
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h
index a5a800598b464..eb7b62abbdad6 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h
@@ -1,10 +1,31 @@
+//===- Loans.h - Loan and Access Path Definitions --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the Loan and AccessPath structures, which represent
+// borrows of storage locations, and the LoanManager, which manages the
+// creation and retrieval of loans during lifetime analysis.
+//
+//===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOANS_H
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOANS_H
-#include "LifetimeSafety.h"
+
+#include "clang/AST/Decl.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
+#include "llvm/Support/raw_ostream.h"
 
 namespace clang::lifetimes {
 namespace internal {
 
+using LoanID = utils::ID<struct LoanTag>;
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, LoanID ID) {
+  return OS << ID.Value;
+}
+
 /// Represents the storage location being borrowed, e.g., a specific stack
 /// variable.
 /// TODO: Model access paths of other types, e.g., s.field, heap and globals.
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
index fccfc49fe4ca2..d159d986e5cf0 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
@@ -1,11 +1,31 @@
+//===- Origins.h - Origin and Origin Management ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines Origins, which represent the set of possible loans a
+// pointer-like object could hold, and the OriginManager, which manages the
+// creation, storage, and retrieval of origins for variables and expressions.
+//
+//===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_ORIGINS_H
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_ORIGINS_H
 
-#include "LifetimeSafety.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
 
 namespace clang::lifetimes {
 namespace internal {
 
+using OriginID = utils::ID<struct OriginTag>;
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, OriginID ID) {
+  return OS << ID.Value;
+}
+
 /// An Origin is a symbolic identifier that represents the set of possible
 /// loans a pointer-like object could hold at any given time.
 /// TODO: Enhance the origin model to handle complex types, pointer
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Reporter.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Reporter.h
new file mode 100644
index 0000000000000..642e5c26cc1c6
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Reporter.h
@@ -0,0 +1,35 @@
+//===- Reporter.h - TODO: FILL ME -*----------- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// TODO: FILLME
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_REPORTER_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_REPORTER_H
+
+#include "clang/AST/Expr.h"
+#include "clang/Basic/SourceLocation.h"
+
+namespace clang::lifetimes {
+
+/// Enum to track the confidence level of a potential error.
+enum class Confidence : uint8_t {
+  None,
+  Maybe,   // Reported as a potential error (-Wlifetime-safety-strict)
+  Definite // Reported as a definite error (-Wlifetime-safety-permissive)
+};
+
+class LifetimeSafetyReporter {
+public:
+  LifetimeSafetyReporter() = default;
+  virtual ~LifetimeSafetyReporter() = default;
+
+  virtual void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr,
+                                  SourceLocation FreeLoc,
+                                  Confidence Confidence) {}
+};
+} // namespace clang::lifetimes
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_REPORTER_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
index 744602b2ea0a7..0d5e2121f101a 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
@@ -1,3 +1,16 @@
+//===- Utils.h - Utility Functions for Immutable Collections ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides utility functions for working with LLVM's immutable data
+// structures, including join operations for ImmutableSet and ImmutableMap
+// used throughout the lifetime safety analysis.
+//
+//===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_UTILS_H
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_UTILS_H
 
@@ -9,6 +22,24 @@ namespace internal {
 
 namespace utils {
 
+/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
+/// Used for giving ID to loans and origins.
+template <typename Tag> struct ID {
+  uint32_t Value = 0;
+
+  bool operator==(const ID<Tag> &Other) const { return Value == Other.Value; }
+  bool operator!=(const ID<Tag> &Other) const { return !(*this == Other); }
+  bool operator<(const ID<Tag> &Other) const { return Value < Other.Value; }
+  ID<Tag> operator++(int) {
+    ID<Tag> Tmp = *this;
+    ++Value;
+    return Tmp;
+  }
+  void Profile(llvm::FoldingSetNodeID &IDBuilder) const {
+    IDBuilder.AddInteger(Value);
+  }
+};
+
 /// Computes the union of two ImmutableSets.
 template <typename T>
 static llvm::ImmutableSet<T> join(llvm::ImmutableSet<T> A,
@@ -71,4 +102,25 @@ join(const llvm::ImmutableMap<K, V> &A, const llvm::ImmutableMap<K, V> &B,
 } // namespace internal
 } // namespace clang::lifetimes
 
+namespace llvm {
+template <typename Tag>
+struct DenseMapInfo<clang::lifetimes::internal::utils::ID<Tag>> {
+  using ID = clang::lifetimes::internal::utils::ID<Tag>;
+
+  static inline ID getEmptyKey() {
+    return {DenseMapInfo<uint32_t>::getEmptyKey()};
+  }
+
+  static inline ID getTombstoneKey() {
+    return {DenseMapInfo<uint32_t>::getTombstoneKey()};
+  }
+
+  static unsigned getHashValue(const ID &Val) {
+    return DenseMapInfo<uint32_t>::getHashValue(Val.Value);
+  }
+
+  static bool isEqual(const ID &LHS, const ID &RHS) { return LHS == RHS; }
+};
+} // namespace llvm
+
 #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_UTILS_H
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
index d4cf0a6b63c55..141d7fa054cc4 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
@@ -5,6 +5,12 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+//
+// This file implements the main LifetimeSafetyAnalysis class, which coordinates
+// the various components (fact generation, loan propagation, live origins
+// analysis, and checking) to detect lifetime safety violations in C++ code.
+//
+//===----------------------------------------------------------------------===//
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
@@ -80,7 +86,8 @@ void LifetimeSafetyAnalysis::run() {
   LiveOrigins = std::make_unique<LiveOriginAnalysis>(
       Cfg, AC, *FactMgr, Factory->LivenessMapFactory);
   LiveOrigins->run();
-  DEBUG_WITH_TYPE("LiveOrigins", LiveOrigins->dump(llvm::dbgs(), *this));
+  DEBUG_WITH_TYPE("LiveOrigins",
+                  LiveOrigins->dump(llvm::dbgs(), getTestPoints()));
 
   LifetimeChecker Checker(*LoanPropagation, *LiveOrigins, *FactMgr, AC,
                           Reporter);

>From 39689c6ca28255eb272d9585a9833e4d5f04b702 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Wed, 8 Oct 2025 15:35:07 +0200
Subject: [PATCH 04/10] Apply changes from code browser

Apply changes from code browser
---
 .../clang/Analysis/Analyses/LifetimeSafety/Utils.h       | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
index 0d5e2121f101a..cd038a6ba3296 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
@@ -1,14 +1,11 @@
-//===- Utils.h - Utility Functions for Immutable Collections ---*- C++ -*-===//
+//===- Utils.h - Utility Functions for Lifetime Safety --------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
-//===----------------------------------------------------------------------===//
-//
-// This file provides utility functions for working with LLVM's immutable data
-// structures, including join operations for ImmutableSet and ImmutableMap
-// used throughout the lifetime safety analysis.
+// This file provides utilities for the lifetime safety analysis, including 
+// join operations for LLVM's immutable data structures.
 //
 //===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_UTILS_H

>From 081859976f8d951ef4507c7df4956482b9df8a2f Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Wed, 8 Oct 2025 15:36:40 +0200
Subject: [PATCH 05/10] Apply suggested changes

Apply suggested changes
---
 .../clang/Analysis/Analyses/LifetimeSafety/Reporter.h      | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Reporter.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Reporter.h
index 642e5c26cc1c6..185f1d8d6e158 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Reporter.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Reporter.h
@@ -1,11 +1,14 @@
-//===- Reporter.h - TODO: FILL ME -*----------- C++-*-===//
+//===- Reporter.h - Lifetime Safety Error Reporter -------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// TODO: FILLME
+//
+// This file defines the LifetimeSafetyReporter interface for reporting
+// lifetime safety violations and the Confidence enum for diagnostic severity.
+//
 //===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_REPORTER_H
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_REPORTER_H

>From a677b11098b2e125b1f1f02423da685e425b0ce2 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Wed, 8 Oct 2025 15:47:21 +0200
Subject: [PATCH 06/10] split into implementation files

---
 .../Analyses/LifetimeSafety/Checker.h         | 124 +-----
 .../Analysis/Analyses/LifetimeSafety/Facts.h  | 369 ------------------
 .../Analyses/LifetimeSafety/FactsGenerator.h  | 105 +++++
 .../Analyses/LifetimeSafety/LiveOrigins.h     | 137 +------
 .../Analyses/LifetimeSafety/LoanPropagation.h |  63 +--
 .../Analysis/Analyses/LifetimeSafety/Utils.h  |   2 +-
 .../Analysis/LifetimeSafety/CMakeLists.txt    |  15 +-
 clang/lib/Analysis/LifetimeSafety/Checker.cpp | 145 +++++++
 .../LifetimeSafety/FactsGenerator.cpp         | 340 ++++++++++++++++
 .../LifetimeSafety/LifetimeSafety.cpp         |   7 +-
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   | 109 ++++++
 .../LifetimeSafety/LoanPropagation.cpp        |  63 +++
 12 files changed, 807 insertions(+), 672 deletions(-)
 create mode 100644 clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
 create mode 100644 clang/lib/Analysis/LifetimeSafety/Checker.cpp
 create mode 100644 clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
 create mode 100644 clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
 create mode 100644 clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
index 79db9f9d6aac8..b059e00d7bdaf 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
@@ -6,129 +6,33 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This file defines the LifetimeChecker, which detects use-after-free errors
-// by checking if live origins hold loans that have expired. It combines the
-// results of loan propagation and liveness analysis to report violations with
-// appropriate confidence levels.
+// This file provides the entry point for lifetime checking, which detects
+// use-after-free errors by checking if live origins hold loans that have
+// expired.
 //
 //===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H
 
-#include "clang/AST/Decl.h"
-#include "clang/AST/Expr.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
-#include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Reporter.h"
-#include "clang/Analysis/Analyses/PostOrderCFGView.h"
-#include "clang/Analysis/AnalysisDeclContext.h"
-#include "clang/Analysis/CFG.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/TimeProfiler.h"
 
-namespace clang::lifetimes {
+namespace clang {
+namespace lifetimes {
 namespace internal {
 
-// ========================================================================= //
-//                       Lifetime checker and Error reporter
-// ========================================================================= //
+/// Runs the lifetime checker, which detects use-after-free errors by
+/// examining loan expiration points and checking if any live origins hold
+/// the expired loan.
+void runLifetimeChecker(LoanPropagationAnalysis &LoanPropagation,
+                        LiveOriginAnalysis &LiveOrigins, FactManager &FactMgr,
+                        AnalysisDeclContext &ADC,
+                        LifetimeSafetyReporter *Reporter);
 
-/// Struct to store the complete context for a potential lifetime violation.
-struct PendingWarning {
-  SourceLocation ExpiryLoc; // Where the loan expired.
-  const Expr *UseExpr;      // Where the origin holding this loan was used.
-  Confidence ConfidenceLevel;
-};
-
-class LifetimeChecker {
-private:
-  llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
-  LoanPropagationAnalysis &LoanPropagation;
-  LiveOriginAnalysis &LiveOrigins;
-  FactManager &FactMgr;
-  AnalysisDeclContext &ADC;
-  LifetimeSafetyReporter *Reporter;
-
-public:
-  LifetimeChecker(LoanPropagationAnalysis &LPA, LiveOriginAnalysis &LOA,
-                  FactManager &FM, AnalysisDeclContext &ADC,
-                  LifetimeSafetyReporter *Reporter)
-      : LoanPropagation(LPA), LiveOrigins(LOA), FactMgr(FM), ADC(ADC),
-        Reporter(Reporter) {}
-
-  void run() {
-    llvm::TimeTraceScope TimeProfile("LifetimeChecker");
-    for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
-      for (const Fact *F : FactMgr.getFacts(B))
-        if (const auto *EF = F->getAs<ExpireFact>())
-          checkExpiry(EF);
-    issuePendingWarnings();
-  }
-
-  /// Checks for use-after-free errors when a loan expires.
-  ///
-  /// This method examines all live origins at the expiry point and determines
-  /// if any of them hold the expiring loan. If so, it creates a pending
-  /// warning with the appropriate confidence level based on the liveness
-  /// information. The confidence reflects whether the origin is definitely
-  /// or maybe live at this point.
-  ///
-  /// Note: This implementation considers only the confidence of origin
-  /// liveness. Future enhancements could also consider the confidence of loan
-  /// propagation (e.g., a loan may only be held on some execution paths).
-  void checkExpiry(const ExpireFact *EF) {
-    LoanID ExpiredLoan = EF->getLoanID();
-    LivenessMap Origins = LiveOrigins.getLiveOrigins(EF);
-    Confidence CurConfidence = Confidence::None;
-    const UseFact *BadUse = nullptr;
-    for (auto &[OID, LiveInfo] : Origins) {
-      LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
-      if (!HeldLoans.contains(ExpiredLoan))
-        continue;
-      // Loan is defaulted.
-      Confidence NewConfidence = livenessKindToConfidence(LiveInfo.Kind);
-      if (CurConfidence < NewConfidence) {
-        CurConfidence = NewConfidence;
-        BadUse = LiveInfo.CausingUseFact;
-      }
-    }
-    if (!BadUse)
-      return;
-    // We have a use-after-free.
-    Confidence LastConf = FinalWarningsMap.lookup(ExpiredLoan).ConfidenceLevel;
-    if (LastConf >= CurConfidence)
-      return;
-    FinalWarningsMap[ExpiredLoan] = {/*ExpiryLoc=*/EF->getExpiryLoc(),
-                                     /*UseExpr=*/BadUse->getUseExpr(),
-                                     /*ConfidenceLevel=*/CurConfidence};
-  }
-
-  static Confidence livenessKindToConfidence(LivenessKind K) {
-    switch (K) {
-    case LivenessKind::Must:
-      return Confidence::Definite;
-    case LivenessKind::Maybe:
-      return Confidence::Maybe;
-    case LivenessKind::Dead:
-      return Confidence::None;
-    }
-    llvm_unreachable("unknown liveness kind");
-  }
-
-  void issuePendingWarnings() {
-    if (!Reporter)
-      return;
-    for (const auto &[LID, Warning] : FinalWarningsMap) {
-      const Loan &L = FactMgr.getLoanMgr().getLoan(LID);
-      const Expr *IssueExpr = L.IssueExpr;
-      Reporter->reportUseAfterFree(IssueExpr, Warning.UseExpr,
-                                   Warning.ExpiryLoc, Warning.ConfidenceLevel);
-    }
-  }
-};
 } // namespace internal
-} // namespace clang::lifetimes
+} // namespace lifetimes
+} // namespace clang
 
 #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 535320e12b5f6..222f861837e09 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -14,20 +14,14 @@
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H
 
-#include "clang/AST/StmtVisitor.h"
-#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
 #include "clang/Analysis/Analyses/PostOrderCFGView.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
-#include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Debug.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/TimeProfiler.h"
 #include <cstdint>
-#include <optional>
 
 namespace clang::lifetimes {
 namespace internal {
@@ -270,369 +264,6 @@ class FactManager {
       BlockToFactsMap;
   llvm::BumpPtrAllocator FactAllocator;
 };
-
-class FactGenerator : public ConstStmtVisitor<FactGenerator> {
-  using Base = ConstStmtVisitor<FactGenerator>;
-
-public:
-  FactGenerator(FactManager &FactMgr, AnalysisDeclContext &AC)
-      : FactMgr(FactMgr), AC(AC) {}
-
-  void run() {
-    llvm::TimeTraceScope TimeProfile("FactGenerator");
-    // Iterate through the CFG blocks in reverse post-order to ensure that
-    // initializations and destructions are processed in the correct sequence.
-    for (const CFGBlock *Block : *AC.getAnalysis<PostOrderCFGView>()) {
-      CurrentBlockFacts.clear();
-      for (unsigned I = 0; I < Block->size(); ++I) {
-        const CFGElement &Element = Block->Elements[I];
-        if (std::optional<CFGStmt> CS = Element.getAs<CFGStmt>())
-          Visit(CS->getStmt());
-        else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
-                     Element.getAs<CFGAutomaticObjDtor>())
-          handleDestructor(*DtorOpt);
-      }
-      FactMgr.addBlockFacts(Block, CurrentBlockFacts);
-    }
-  }
-
-  void VisitDeclStmt(const DeclStmt *DS) {
-    for (const Decl *D : DS->decls())
-      if (const auto *VD = dyn_cast<VarDecl>(D))
-        if (hasOrigin(VD))
-          if (const Expr *InitExpr = VD->getInit())
-            killAndFlowOrigin(*VD, *InitExpr);
-  }
-
-  void VisitDeclRefExpr(const DeclRefExpr *DRE) {
-    handleUse(DRE);
-    // For non-pointer/non-view types, a reference to the variable's storage
-    // is a borrow. We create a loan for it.
-    // For pointer/view types, we stick to the existing model for now and do
-    // not create an extra origin for the l-value expression itself.
-
-    // TODO: A single origin for a `DeclRefExpr` for a pointer or view type is
-    // not sufficient to model the different levels of indirection. The current
-    // single-origin model cannot distinguish between a loan to the variable's
-    // storage and a loan to what it points to. A multi-origin model would be
-    // required for this.
-    if (!isPointerType(DRE->getType())) {
-      if (const Loan *L = createLoan(DRE)) {
-        OriginID ExprOID = FactMgr.getOriginMgr().getOrCreate(*DRE);
-        CurrentBlockFacts.push_back(
-            FactMgr.createFact<IssueFact>(L->ID, ExprOID));
-      }
-    }
-  }
-
-  void VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
-    if (isGslPointerType(CCE->getType())) {
-      handleGSLPointerConstruction(CCE);
-      return;
-    }
-  }
-
-  void VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
-    // Specifically for conversion operators,
-    // like `std::string_view p = std::string{};`
-    if (isGslPointerType(MCE->getType()) &&
-        isa<CXXConversionDecl>(MCE->getCalleeDecl())) {
-      // The argument is the implicit object itself.
-      handleFunctionCall(MCE, MCE->getMethodDecl(),
-                         {MCE->getImplicitObjectArgument()},
-                         /*IsGslConstruction=*/true);
-    }
-    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);
-    }
-  }
-
-  void VisitCallExpr(const CallExpr *CE) {
-    handleFunctionCall(CE, CE->getDirectCallee(),
-                       {CE->getArgs(), CE->getNumArgs()});
-  }
-
-  void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N) {
-    /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
-    /// pointers can use the same type of loan.
-    FactMgr.getOriginMgr().getOrCreate(*N);
-  }
-
-  void VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
-    if (!hasOrigin(ICE))
-      return;
-    // An ImplicitCastExpr node itself gets an origin, which flows from the
-    // origin of its sub-expression (after stripping its own parens/casts).
-    killAndFlowOrigin(*ICE, *ICE->getSubExpr());
-  }
-
-  void VisitUnaryOperator(const UnaryOperator *UO) {
-    if (UO->getOpcode() == UO_AddrOf) {
-      const Expr *SubExpr = UO->getSubExpr();
-      // Taking address of a pointer-type expression is not yet supported and
-      // will be supported in multi-origin model.
-      if (isPointerType(SubExpr->getType()))
-        return;
-      // The origin of an address-of expression (e.g., &x) is the origin of
-      // its sub-expression (x). This fact will cause the dataflow analysis
-      // to propagate any loans held by the sub-expression's origin to the
-      // origin of this UnaryOperator expression.
-      killAndFlowOrigin(*UO, *SubExpr);
-    }
-  }
-
-  void VisitReturnStmt(const ReturnStmt *RS) {
-    if (const Expr *RetExpr = RS->getRetValue()) {
-      if (hasOrigin(RetExpr)) {
-        OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);
-        CurrentBlockFacts.push_back(
-            FactMgr.createFact<ReturnOfOriginFact>(OID));
-      }
-    }
-  }
-
-  void VisitBinaryOperator(const BinaryOperator *BO) {
-    if (BO->isAssignmentOp())
-      handleAssignment(BO->getLHS(), BO->getRHS());
-  }
-
-  void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
-    // Assignment operators have special "kill-then-propagate" semantics
-    // and are handled separately.
-    if (OCE->isAssignmentOp() && OCE->getNumArgs() == 2) {
-      handleAssignment(OCE->getArg(0), OCE->getArg(1));
-      return;
-    }
-    handleFunctionCall(OCE, OCE->getDirectCallee(),
-                       {OCE->getArgs(), OCE->getNumArgs()},
-                       /*IsGslConstruction=*/false);
-  }
-
-  void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE) {
-    // Check if this is a test point marker. If so, we are done with this
-    // expression.
-    if (handleTestPoint(FCE))
-      return;
-    if (isGslPointerType(FCE->getType()))
-      killAndFlowOrigin(*FCE, *FCE->getSubExpr());
-  }
-
-  void VisitInitListExpr(const InitListExpr *ILE) {
-    if (!hasOrigin(ILE))
-      return;
-    // For list initialization with a single element, like `View{...}`, the
-    // origin of the list itself is the origin of its single element.
-    if (ILE->getNumInits() == 1)
-      killAndFlowOrigin(*ILE, *ILE->getInit(0));
-  }
-
-  void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE) {
-    if (!hasOrigin(MTE))
-      return;
-    // A temporary object's origin is the same as the origin of the
-    // expression that initializes it.
-    killAndFlowOrigin(*MTE, *MTE->getSubExpr());
-  }
-
-  void handleDestructor(const CFGAutomaticObjDtor &DtorOpt) {
-    /// TODO: Also handle trivial destructors (e.g., for `int`
-    /// variables) which will never have a CFGAutomaticObjDtor node.
-    /// TODO: Handle loans to temporaries.
-    /// TODO: Consider using clang::CFG::BuildOptions::AddLifetime to reuse the
-    /// lifetime ends.
-    const VarDecl *DestructedVD = DtorOpt.getVarDecl();
-    if (!DestructedVD)
-      return;
-    // Iterate through all loans to see if any expire.
-    /// TODO(opt): Do better than a linear search to find loans associated with
-    /// 'DestructedVD'.
-    for (const Loan &L : FactMgr.getLoanMgr().getLoans()) {
-      const AccessPath &LoanPath = L.Path;
-      // Check if the loan is for a stack variable and if that variable
-      // is the one being destructed.
-      if (LoanPath.D == DestructedVD)
-        CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
-            L.ID, DtorOpt.getTriggerStmt()->getEndLoc()));
-    }
-  }
-
-private:
-  static bool isGslPointerType(QualType QT) {
-    if (const auto *RD = QT->getAsCXXRecordDecl()) {
-      // We need to check the template definition for specializations.
-      if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
-        return CTSD->getSpecializedTemplate()
-            ->getTemplatedDecl()
-            ->hasAttr<PointerAttr>();
-      return RD->hasAttr<PointerAttr>();
-    }
-    return false;
-  }
-
-  static bool isPointerType(QualType QT) {
-    return QT->isPointerOrReferenceType() || isGslPointerType(QT);
-  }
-  // Check if a type has an origin.
-  static bool hasOrigin(const Expr *E) {
-    return E->isGLValue() || isPointerType(E->getType());
-  }
-
-  static bool hasOrigin(const VarDecl *VD) {
-    return isPointerType(VD->getType());
-  }
-
-  void handleGSLPointerConstruction(const CXXConstructExpr *CCE) {
-    assert(isGslPointerType(CCE->getType()));
-    if (CCE->getNumArgs() != 1)
-      return;
-    if (hasOrigin(CCE->getArg(0)))
-      killAndFlowOrigin(*CCE, *CCE->getArg(0));
-    else
-      // This could be a new borrow.
-      handleFunctionCall(CCE, CCE->getConstructor(),
-                         {CCE->getArgs(), CCE->getNumArgs()},
-                         /*IsGslConstruction=*/true);
-  }
-
-  /// Checks if a call-like expression creates a borrow by passing a value to a
-  /// reference parameter, creating an IssueFact if it does.
-  /// \param IsGslConstruction True if this is a GSL construction where all
-  ///   argument origins should flow to the returned origin.
-  void handleFunctionCall(const Expr *Call, const FunctionDecl *FD,
-                          ArrayRef<const Expr *> Args,
-                          bool IsGslConstruction = false) {
-    // Ignore functions returning values with no origin.
-    if (!FD || !hasOrigin(Call))
-      return;
-    auto IsArgLifetimeBound = [FD](unsigned I) -> bool {
-      const ParmVarDecl *PVD = nullptr;
-      if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
-          Method && Method->isInstance()) {
-        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;
-    };
-    if (Args.empty())
-      return;
-    bool killedSrc = false;
-    for (unsigned I = 0; I < Args.size(); ++I)
-      if (IsGslConstruction || IsArgLifetimeBound(I)) {
-        if (!killedSrc) {
-          killedSrc = true;
-          killAndFlowOrigin(*Call, *Args[I]);
-        } else
-          flowOrigin(*Call, *Args[I]);
-      }
-  }
-
-  /// Creates a loan for the storage path of a given declaration reference.
-  /// This function should be called whenever a DeclRefExpr represents a borrow.
-  /// \param DRE The declaration reference expression that initiates the borrow.
-  /// \return The new Loan on success, nullptr otherwise.
-  const Loan *createLoan(const DeclRefExpr *DRE) {
-    if (const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl())) {
-      AccessPath Path(VD);
-      // The loan is created at the location of the DeclRefExpr.
-      return &FactMgr.getLoanMgr().addLoan(Path, DRE);
-    }
-    return nullptr;
-  }
-
-  template <typename Destination, typename Source>
-  void flowOrigin(const Destination &D, const Source &S) {
-    OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-    OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-    CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
-        DestOID, SrcOID, /*KillDest=*/false));
-  }
-
-  template <typename Destination, typename Source>
-  void killAndFlowOrigin(const Destination &D, const Source &S) {
-    OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
-    OriginID SrcOID = FactMgr.getOriginMgr().get(S);
-    CurrentBlockFacts.push_back(
-        FactMgr.createFact<OriginFlowFact>(DestOID, SrcOID, /*KillDest=*/true));
-  }
-
-  /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
-  /// If so, creates a `TestPointFact` and returns true.
-  bool handleTestPoint(const CXXFunctionalCastExpr *FCE) {
-    if (!FCE->getType()->isVoidType())
-      return false;
-
-    const auto *SubExpr = FCE->getSubExpr()->IgnoreParenImpCasts();
-    if (const auto *SL = dyn_cast<StringLiteral>(SubExpr)) {
-      llvm::StringRef LiteralValue = SL->getString();
-      const std::string Prefix = "__lifetime_test_point_";
-
-      if (LiteralValue.starts_with(Prefix)) {
-        StringRef Annotation = LiteralValue.drop_front(Prefix.length());
-        CurrentBlockFacts.push_back(
-            FactMgr.createFact<TestPointFact>(Annotation));
-        return true;
-      }
-    }
-    return false;
-  }
-
-  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr) {
-    if (!hasOrigin(LHSExpr))
-      return;
-    // Find the underlying variable declaration for the left-hand side.
-    if (const auto *DRE_LHS =
-            dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) {
-      markUseAsWrite(DRE_LHS);
-      if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl())) {
-        // Kill the old loans of the destination origin and flow the new loans
-        // from the source origin.
-        killAndFlowOrigin(*VD_LHS, *RHSExpr);
-      }
-    }
-  }
-
-  // A DeclRefExpr will be treated as a use of the referenced decl. It will be
-  // checked for use-after-free unless it is later marked as being written to
-  // (e.g. on the left-hand side of an assignment).
-  void handleUse(const DeclRefExpr *DRE) {
-    if (isPointerType(DRE->getType())) {
-      UseFact *UF = FactMgr.createFact<UseFact>(DRE);
-      CurrentBlockFacts.push_back(UF);
-      assert(!UseFacts.contains(DRE));
-      UseFacts[DRE] = UF;
-    }
-  }
-
-  void markUseAsWrite(const DeclRefExpr *DRE) {
-    if (!isPointerType(DRE->getType()))
-      return;
-    assert(UseFacts.contains(DRE));
-    UseFacts[DRE]->markAsWritten();
-  }
-
-  FactManager &FactMgr;
-  AnalysisDeclContext &AC;
-  llvm::SmallVector<Fact *> CurrentBlockFacts;
-  // To distinguish between reads and writes for use-after-free checks, this map
-  // stores the `UseFact` for each `DeclRefExpr`. We initially identify all
-  // `DeclRefExpr`s as "read" uses. When an assignment is processed, the use
-  // corresponding to the left-hand side is updated to be a "write", thereby
-  // exempting it from the check.
-  llvm::DenseMap<const DeclRefExpr *, UseFact *> UseFacts;
-};
 } // namespace internal
 } // namespace clang::lifetimes
 
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
new file mode 100644
index 0000000000000..6a59a53daa071
--- /dev/null
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -0,0 +1,105 @@
+//===- TODO ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// TODO
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTSGENERATOR_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTSGENERATOR_H
+
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace clang::lifetimes {
+namespace internal {
+
+class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
+  using Base = ConstStmtVisitor<FactsGenerator>;
+
+public:
+  FactsGenerator(FactManager &FactMgr, AnalysisDeclContext &AC)
+      : FactMgr(FactMgr), AC(AC) {}
+
+  void run();
+
+  void VisitDeclStmt(const DeclStmt *DS);
+  void VisitDeclRefExpr(const DeclRefExpr *DRE);
+  void VisitCXXConstructExpr(const CXXConstructExpr *CCE);
+  void VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE);
+  void VisitCallExpr(const CallExpr *CE);
+  void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N);
+  void VisitImplicitCastExpr(const ImplicitCastExpr *ICE);
+  void VisitUnaryOperator(const UnaryOperator *UO);
+  void VisitReturnStmt(const ReturnStmt *RS);
+  void VisitBinaryOperator(const BinaryOperator *BO);
+  void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE);
+  void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE);
+  void VisitInitListExpr(const InitListExpr *ILE);
+  void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
+
+private:
+  void handleDestructor(const CFGAutomaticObjDtor &DtorOpt);
+
+  void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
+
+  /// Checks if a call-like expression creates a borrow by passing a value to a
+  /// reference parameter, creating an IssueFact if it does.
+  /// \param IsGslConstruction True if this is a GSL construction where all
+  ///   argument origins should flow to the returned origin.
+  void handleFunctionCall(const Expr *Call, const FunctionDecl *FD,
+                          ArrayRef<const Expr *> Args,
+                          bool IsGslConstruction = false);
+
+  template <typename Destination, typename Source>
+  void flowOrigin(const Destination &D, const Source &S) {
+    OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
+    OriginID SrcOID = FactMgr.getOriginMgr().get(S);
+    CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
+        DestOID, SrcOID, /*KillDest=*/false));
+  }
+
+  template <typename Destination, typename Source>
+  void killAndFlowOrigin(const Destination &D, const Source &S) {
+    OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
+    OriginID SrcOID = FactMgr.getOriginMgr().get(S);
+    CurrentBlockFacts.push_back(
+        FactMgr.createFact<OriginFlowFact>(DestOID, SrcOID, /*KillDest=*/true));
+  }
+
+  /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
+  /// If so, creates a `TestPointFact` and returns true.
+  bool handleTestPoint(const CXXFunctionalCastExpr *FCE);
+
+  void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
+
+  // A DeclRefExpr will be treated as a use of the referenced decl. It will be
+  // checked for use-after-free unless it is later marked as being written to
+  // (e.g. on the left-hand side of an assignment).
+  void handleUse(const DeclRefExpr *DRE);
+
+  void markUseAsWrite(const DeclRefExpr *DRE);
+
+  FactManager &FactMgr;
+  AnalysisDeclContext &AC;
+  llvm::SmallVector<Fact *> CurrentBlockFacts;
+  // To distinguish between reads and writes for use-after-free checks, this map
+  // stores the `UseFact` for each `DeclRefExpr`. We initially identify all
+  // `DeclRefExpr`s as "read" uses. When an assignment is processed, the use
+  // corresponding to the left-hand side is updated to be a "write", thereby
+  // exempting it from the check.
+  llvm::DenseMap<const DeclRefExpr *, UseFact *> UseFacts;
+};
+
+} // namespace internal
+} // namespace clang::lifetimes
+
+#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTSGENERATOR_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h
index 6b0bb7b52875e..f3d49adcef0ec 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h
@@ -7,28 +7,27 @@
 //===----------------------------------------------------------------------===//
 //
 // This file defines the LiveOriginAnalysis, a backward dataflow analysis that
-// determines which origins are "live" at each program point. An origin is live
-// if there's a potential future use of the pointer it represents. This
-// information is used to detect use-after-free errors by checking if live
-// origins hold loans to objects that have already expired.
+// determines which origins are "live" at each program point. An origin is
+// "live" at a program point if there's a potential future use of the pointer it
+// represents. Liveness is "generated" by a read of origin's loan set (e.g., a
+// `UseFact`) and is "killed" (i.e., it stops being live) when its loan set is
+// overwritten (e.g. a OriginFlow killing the destination origin).
+//
+// This information is used for detecting use-after-free errors, as it allows us
+// to check if a live origin holds a loan to an object that has already expired.
 //
 //===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LIVE_ORIGINS_H
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LIVE_ORIGINS_H
 
-#include "clang/AST/Decl.h"
-#include "clang/AST/Expr.h"
-#include "clang/AST/Type.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Dataflow.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
-#include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/ImmutableMap.h"
 #include "llvm/Support/Debug.h"
-#include "llvm/Support/ErrorHandling.h"
 
 namespace clang::lifetimes {
 namespace internal {
@@ -41,21 +40,6 @@ enum class LivenessKind : uint8_t {
   Must   // Live on all paths (must-be-live)
 };
 
-// ========================================================================= //
-//                         Live Origins Analysis
-// ========================================================================= //
-//
-// A backward dataflow analysis that determines which origins are "live" at each
-// program point. An origin is "live" at a program point if there's a potential
-// future use of the pointer it represents. Liveness is "generated" by a read of
-// origin's loan set (e.g., a `UseFact`) and is "killed" (i.e., it stops being
-// live) when its loan set is overwritten (e.g. a OriginFlow killing the
-// destination origin).
-//
-// This information is used for detecting use-after-free errors, as it allows us
-// to check if a live origin holds a loan to an object that has already expired.
-// ========================================================================= //
-
 /// Information about why an origin is live at a program point.
 struct LivenessInfo {
   /// The use that makes the origin live. If liveness is propagated from
@@ -109,28 +93,7 @@ struct LivenessLattice {
     return !(*this == Other);
   }
 
-  void dump(llvm::raw_ostream &OS, const OriginManager &OM) const {
-    if (LiveOrigins.isEmpty())
-      OS << "  <empty>\n";
-    for (const auto &Entry : LiveOrigins) {
-      OriginID OID = Entry.first;
-      const LivenessInfo &Info = Entry.second;
-      OS << "  ";
-      OM.dump(OID, OS);
-      OS << " is ";
-      switch (Info.Kind) {
-      case LivenessKind::Must:
-        OS << "definitely";
-        break;
-      case LivenessKind::Maybe:
-        OS << "maybe";
-        break;
-      case LivenessKind::Dead:
-        llvm_unreachable("liveness kind of live origins should not be dead.");
-      }
-      OS << " live at this point\n";
-    }
-  }
+  void dump(llvm::raw_ostream &OS, const OriginManager &OM) const;
 };
 
 /// The analysis that tracks which origins are live, with granular information
@@ -139,8 +102,6 @@ struct LivenessLattice {
 class LiveOriginAnalysis
     : public DataflowAnalysis<LiveOriginAnalysis, LivenessLattice,
                               Direction::Backward> {
-  FactManager &FactMgr;
-  LivenessMap::Factory &Factory;
 
 public:
   LiveOriginAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
@@ -153,69 +114,11 @@ class LiveOriginAnalysis
 
   Lattice getInitialState() { return Lattice(Factory.getEmptyMap()); }
 
-  /// Merges two lattices by combining liveness information.
-  /// When the same origin has different confidence levels, we take the lower
-  /// one.
-  Lattice join(Lattice L1, Lattice L2) const {
-    LivenessMap Merged = L1.LiveOrigins;
-    // Take the earliest UseFact to make the join hermetic and commutative.
-    auto CombineUseFact = [](const UseFact &A,
-                             const UseFact &B) -> const UseFact * {
-      return A.getUseExpr()->getExprLoc() < B.getUseExpr()->getExprLoc() ? &A
-                                                                         : &B;
-    };
-    auto CombineLivenessKind = [](LivenessKind K1,
-                                  LivenessKind K2) -> LivenessKind {
-      assert(K1 != LivenessKind::Dead && "LivenessKind should not be dead.");
-      assert(K2 != LivenessKind::Dead && "LivenessKind should not be dead.");
-      // Only return "Must" if both paths are "Must", otherwise Maybe.
-      if (K1 == LivenessKind::Must && K2 == LivenessKind::Must)
-        return LivenessKind::Must;
-      return LivenessKind::Maybe;
-    };
-    auto CombineLivenessInfo = [&](const LivenessInfo *L1,
-                                   const LivenessInfo *L2) -> LivenessInfo {
-      assert((L1 || L2) && "unexpectedly merging 2 empty sets");
-      if (!L1)
-        return LivenessInfo(L2->CausingUseFact, LivenessKind::Maybe);
-      if (!L2)
-        return LivenessInfo(L1->CausingUseFact, LivenessKind::Maybe);
-      return LivenessInfo(
-          CombineUseFact(*L1->CausingUseFact, *L2->CausingUseFact),
-          CombineLivenessKind(L1->Kind, L2->Kind));
-    };
-    return Lattice(utils::join(
-        L1.LiveOrigins, L2.LiveOrigins, Factory, CombineLivenessInfo,
-        // A symmetric join is required here. If an origin is live on one
-        // branch but not the other, its confidence must be demoted to `Maybe`.
-        utils::JoinKind::Symmetric));
-  }
-
-  /// A read operation makes the origin live with definite confidence, as it
-  /// dominates this program point. A write operation kills the liveness of
-  /// the origin since it overwrites the value.
-  Lattice transfer(Lattice In, const UseFact &UF) {
-    OriginID OID = UF.getUsedOrigin(FactMgr.getOriginMgr());
-    // Write kills liveness.
-    if (UF.isWritten())
-      return Lattice(Factory.remove(In.LiveOrigins, OID));
-    // Read makes origin live with definite confidence (dominates this point).
-    return Lattice(Factory.add(In.LiveOrigins, OID,
-                               LivenessInfo(&UF, LivenessKind::Must)));
-  }
-
-  /// Issuing a new loan to an origin kills its liveness.
-  Lattice transfer(Lattice In, const IssueFact &IF) {
-    return Lattice(Factory.remove(In.LiveOrigins, IF.getOriginID()));
-  }
+  Lattice join(Lattice L1, Lattice L2) const;
 
-  /// An OriginFlow kills the liveness of the destination origin if `KillDest`
-  /// is true. Otherwise, it propagates liveness from destination to source.
-  Lattice transfer(Lattice In, const OriginFlowFact &OF) {
-    if (!OF.getKillDest())
-      return In;
-    return Lattice(Factory.remove(In.LiveOrigins, OF.getDestOriginID()));
-  }
+  Lattice transfer(Lattice In, const UseFact &UF);
+  Lattice transfer(Lattice In, const IssueFact &IF);
+  Lattice transfer(Lattice In, const OriginFlowFact &OF);
 
   LivenessMap getLiveOrigins(ProgramPoint P) const {
     return getState(P).LiveOrigins;
@@ -223,15 +126,11 @@ class LiveOriginAnalysis
 
   // Dump liveness values on all test points in the program.
   void dump(llvm::raw_ostream &OS,
-            llvm::StringMap<ProgramPoint> TestPoints) const {
-    llvm::dbgs() << "==========================================\n";
-    llvm::dbgs() << getAnalysisName() << " results:\n";
-    llvm::dbgs() << "==========================================\n";
-    for (const auto &Entry : TestPoints) {
-      OS << "TestPoint: " << Entry.getKey() << "\n";
-      getState(Entry.getValue()).dump(OS, FactMgr.getOriginMgr());
-    }
-  }
+            llvm::StringMap<ProgramPoint> TestPoints) const;
+
+private:
+  FactManager &FactMgr;
+  LivenessMap::Factory &Factory;
 };
 } // namespace internal
 } // namespace clang::lifetimes
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index 8510f40637b84..40a591604b8f3 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -17,7 +17,6 @@
 
 #include "clang/Analysis/Analyses/LifetimeSafety/Dataflow.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
-#include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
 #include "llvm/ADT/ImmutableMap.h"
@@ -33,10 +32,6 @@ namespace internal {
 using LoanSet = llvm::ImmutableSet<LoanID>;
 using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
 
-// ========================================================================= //
-//                          Loan Propagation Analysis
-// ========================================================================= //
-
 /// Represents the dataflow lattice for loan propagation.
 ///
 /// This lattice tracks which loans each origin may hold at a given program
@@ -58,20 +53,9 @@ struct LoanPropagationLattice {
     return !(*this == Other);
   }
 
-  void dump(llvm::raw_ostream &OS) const {
-    OS << "LoanPropagationLattice State:\n";
-    if (Origins.isEmpty())
-      OS << "  <empty>\n";
-    for (const auto &Entry : Origins) {
-      if (Entry.second.isEmpty())
-        OS << "  Origin " << Entry.first << " contains no loans\n";
-      for (const LoanID &LID : Entry.second)
-        OS << "  Origin " << Entry.first << " contains Loan " << LID << "\n";
-    }
-  }
+  void dump(llvm::raw_ostream &OS) const;
 };
 
-/// The analysis that tracks which loans belong to which origins.
 class LoanPropagationAnalysis
     : public DataflowAnalysis<LoanPropagationAnalysis, LoanPropagationLattice,
                               Direction::Forward> {
@@ -91,49 +75,10 @@ class LoanPropagationAnalysis
 
   Lattice getInitialState() { return Lattice{}; }
 
-  /// Merges two lattices by taking the union of loans for each origin.
-  // TODO(opt): Keep the state small by removing origins which become dead.
-  Lattice join(Lattice A, Lattice B) {
-    OriginLoanMap JoinedOrigins = utils::join(
-        A.Origins, B.Origins, OriginLoanMapFactory,
-        [&](const LoanSet *S1, const LoanSet *S2) {
-          assert((S1 || S2) && "unexpectedly merging 2 empty sets");
-          if (!S1)
-            return *S2;
-          if (!S2)
-            return *S1;
-          return utils::join(*S1, *S2, LoanSetFactory);
-        },
-        // Asymmetric join is a performance win. For origins present only on one
-        // branch, the loan set can be carried over as-is.
-        utils::JoinKind::Asymmetric);
-    return Lattice(JoinedOrigins);
-  }
-
-  /// A new loan is issued to the origin. Old loans are erased.
-  Lattice transfer(Lattice In, const IssueFact &F) {
-    OriginID OID = F.getOriginID();
-    LoanID LID = F.getLoanID();
-    return LoanPropagationLattice(OriginLoanMapFactory.add(
-        In.Origins, OID,
-        LoanSetFactory.add(LoanSetFactory.getEmptySet(), LID)));
-  }
-
-  /// A flow from source to destination. If `KillDest` is true, this replaces
-  /// the destination's loans with the source's. Otherwise, the source's loans
-  /// are merged into the destination's.
-  Lattice transfer(Lattice In, const OriginFlowFact &F) {
-    OriginID DestOID = F.getDestOriginID();
-    OriginID SrcOID = F.getSrcOriginID();
+  Lattice join(Lattice A, Lattice B);
 
-    LoanSet DestLoans =
-        F.getKillDest() ? LoanSetFactory.getEmptySet() : getLoans(In, DestOID);
-    LoanSet SrcLoans = getLoans(In, SrcOID);
-    LoanSet MergedLoans = utils::join(DestLoans, SrcLoans, LoanSetFactory);
-
-    return LoanPropagationLattice(
-        OriginLoanMapFactory.add(In.Origins, DestOID, MergedLoans));
-  }
+  Lattice transfer(Lattice In, const IssueFact &F);
+  Lattice transfer(Lattice In, const OriginFlowFact &F);
 
   LoanSet getLoans(OriginID OID, ProgramPoint P) const {
     return getLoans(getState(P), OID);
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
index cd038a6ba3296..443ddd44b358c 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
@@ -4,7 +4,7 @@
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
-// This file provides utilities for the lifetime safety analysis, including 
+// This file provides utilities for the lifetime safety analysis, including
 // join operations for LLVM's immutable data structures.
 //
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt b/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt
index 5a0a0f776163d..629ab00419638 100644
--- a/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt
+++ b/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt
@@ -1,14 +1,9 @@
 add_clang_library(clangAnalysisLifetimeSafety
+  Checker.cpp
+  FactsGenerator.cpp
+  LiveOrigins.cpp
   LifetimeAnnotations.cpp
   LifetimeSafety.cpp
-
-  LINK_LIBS
-  clangAST
-  clangASTMatchers
-  clangBasic
-  clangLex
-
-  DEPENDS
-  omp_gen
-  ClangDriverOptions
+  LoanPropagation.cpp
   )
+
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
new file mode 100644
index 0000000000000..56f0549b1e003
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -0,0 +1,145 @@
+//===- Checker.cpp - C++ Lifetime Safety Checker ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the LifetimeChecker, which detects use-after-free
+// errors by checking if live origins hold loans that have expired.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Analyses/LifetimeSafety/Checker.h"
+#include "clang/AST/Expr.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Reporter.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/TimeProfiler.h"
+
+namespace clang::lifetimes {
+namespace internal {
+
+/// Struct to store the complete context for a potential lifetime violation.
+struct PendingWarning {
+  SourceLocation ExpiryLoc; // Where the loan expired.
+  const Expr *UseExpr;      // Where the origin holding this loan was used.
+  Confidence ConfidenceLevel;
+};
+
+class LifetimeChecker {
+private:
+  llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
+  LoanPropagationAnalysis &LoanPropagation;
+  LiveOriginAnalysis &LiveOrigins;
+  FactManager &FactMgr;
+  AnalysisDeclContext &ADC;
+  LifetimeSafetyReporter *Reporter;
+
+  void checkExpiry(const ExpireFact *EF);
+  void issuePendingWarnings();
+  static Confidence livenessKindToConfidence(LivenessKind K);
+
+public:
+  LifetimeChecker(LoanPropagationAnalysis &LPA, LiveOriginAnalysis &LOA,
+                  FactManager &FM, AnalysisDeclContext &ADC,
+                  LifetimeSafetyReporter *Reporter);
+
+  void run();
+};
+
+LifetimeChecker::LifetimeChecker(LoanPropagationAnalysis &LPA,
+                                 LiveOriginAnalysis &LOA, FactManager &FM,
+                                 AnalysisDeclContext &ADC,
+                                 LifetimeSafetyReporter *Reporter)
+    : LoanPropagation(LPA), LiveOrigins(LOA), FactMgr(FM), ADC(ADC),
+      Reporter(Reporter) {}
+
+void LifetimeChecker::run() {
+  for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
+    for (const Fact *F : FactMgr.getFacts(B))
+      if (const auto *EF = F->getAs<ExpireFact>())
+        checkExpiry(EF);
+  issuePendingWarnings();
+}
+
+/// Checks for use-after-free errors when a loan expires.
+///
+/// This method examines all live origins at the expiry point and determines
+/// if any of them hold the expiring loan. If so, it creates a pending
+/// warning with the appropriate confidence level based on the liveness
+/// information. The confidence reflects whether the origin is definitely
+/// or maybe live at this point.
+///
+/// Note: This implementation considers only the confidence of origin
+/// liveness. Future enhancements could also consider the confidence of loan
+/// propagation (e.g., a loan may only be held on some execution paths).
+void LifetimeChecker::checkExpiry(const ExpireFact *EF) {
+  LoanID ExpiredLoan = EF->getLoanID();
+  LivenessMap Origins = LiveOrigins.getLiveOrigins(EF);
+  Confidence CurConfidence = Confidence::None;
+  const UseFact *BadUse = nullptr;
+  for (auto &[OID, LiveInfo] : Origins) {
+    LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
+    if (!HeldLoans.contains(ExpiredLoan))
+      continue;
+    // Loan is defaulted.
+    Confidence NewConfidence = livenessKindToConfidence(LiveInfo.Kind);
+    if (CurConfidence < NewConfidence) {
+      CurConfidence = NewConfidence;
+      BadUse = LiveInfo.CausingUseFact;
+    }
+  }
+  if (!BadUse)
+    return;
+  // We have a use-after-free.
+  Confidence LastConf = FinalWarningsMap.lookup(ExpiredLoan).ConfidenceLevel;
+  if (LastConf >= CurConfidence)
+    return;
+  FinalWarningsMap[ExpiredLoan] = {/*ExpiryLoc=*/EF->getExpiryLoc(),
+                                   /*UseExpr=*/BadUse->getUseExpr(),
+                                   /*ConfidenceLevel=*/CurConfidence};
+}
+
+Confidence LifetimeChecker::livenessKindToConfidence(LivenessKind K) {
+  switch (K) {
+  case LivenessKind::Must:
+    return Confidence::Definite;
+  case LivenessKind::Maybe:
+    return Confidence::Maybe;
+  case LivenessKind::Dead:
+    return Confidence::None;
+  }
+  llvm_unreachable("unknown liveness kind");
+}
+
+void LifetimeChecker::issuePendingWarnings() {
+  if (!Reporter)
+    return;
+  for (const auto &[LID, Warning] : FinalWarningsMap) {
+    const Loan &L = FactMgr.getLoanMgr().getLoan(LID);
+    const Expr *IssueExpr = L.IssueExpr;
+    Reporter->reportUseAfterFree(IssueExpr, Warning.UseExpr, Warning.ExpiryLoc,
+                                 Warning.ConfidenceLevel);
+  }
+}
+
+void runLifetimeChecker(LoanPropagationAnalysis &LoanPropagation,
+                        LiveOriginAnalysis &LiveOrigins, FactManager &FactMgr,
+                        AnalysisDeclContext &ADC,
+                        LifetimeSafetyReporter *Reporter) {
+  llvm::TimeTraceScope TimeProfile("LifetimeChecker");
+  LifetimeChecker Checker(LoanPropagation, LiveOrigins, FactMgr, ADC, Reporter);
+  Checker.run();
+}
+
+} // namespace internal
+} // namespace clang::lifetimes
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
new file mode 100644
index 0000000000000..3f4836fd13c18
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -0,0 +1,340 @@
+#include "clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
+#include "llvm/Support/TimeProfiler.h"
+
+namespace clang::lifetimes {
+namespace internal {
+static bool isGslPointerType(QualType QT) {
+  if (const auto *RD = QT->getAsCXXRecordDecl()) {
+    // We need to check the template definition for specializations.
+    if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
+      return CTSD->getSpecializedTemplate()
+          ->getTemplatedDecl()
+          ->hasAttr<PointerAttr>();
+    return RD->hasAttr<PointerAttr>();
+  }
+  return false;
+}
+
+static bool isPointerType(QualType QT) {
+  return QT->isPointerOrReferenceType() || isGslPointerType(QT);
+}
+// Check if a type has an origin.
+static bool hasOrigin(const Expr *E) {
+  return E->isGLValue() || isPointerType(E->getType());
+}
+
+static bool hasOrigin(const VarDecl *VD) {
+  return isPointerType(VD->getType());
+}
+
+/// Creates a loan for the storage path of a given declaration reference.
+/// This function should be called whenever a DeclRefExpr represents a borrow.
+/// \param DRE The declaration reference expression that initiates the borrow.
+/// \return The new Loan on success, nullptr otherwise.
+static const Loan *createLoan(FactManager &FactMgr, const DeclRefExpr *DRE) {
+  if (const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl())) {
+    AccessPath Path(VD);
+    // The loan is created at the location of the DeclRefExpr.
+    return &FactMgr.getLoanMgr().addLoan(Path, DRE);
+  }
+  return nullptr;
+}
+
+void FactsGenerator::run() {
+  llvm::TimeTraceScope TimeProfile("FactGenerator");
+  // Iterate through the CFG blocks in reverse post-order to ensure that
+  // initializations and destructions are processed in the correct sequence.
+  for (const CFGBlock *Block : *AC.getAnalysis<PostOrderCFGView>()) {
+    CurrentBlockFacts.clear();
+    for (unsigned I = 0; I < Block->size(); ++I) {
+      const CFGElement &Element = Block->Elements[I];
+      if (std::optional<CFGStmt> CS = Element.getAs<CFGStmt>())
+        Visit(CS->getStmt());
+      else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
+                   Element.getAs<CFGAutomaticObjDtor>())
+        handleDestructor(*DtorOpt);
+    }
+    FactMgr.addBlockFacts(Block, CurrentBlockFacts);
+  }
+}
+
+void FactsGenerator::VisitDeclStmt(const DeclStmt *DS) {
+  for (const Decl *D : DS->decls())
+    if (const auto *VD = dyn_cast<VarDecl>(D))
+      if (hasOrigin(VD))
+        if (const Expr *InitExpr = VD->getInit())
+          killAndFlowOrigin(*VD, *InitExpr);
+}
+
+void FactsGenerator::VisitDeclRefExpr(const DeclRefExpr *DRE) {
+  handleUse(DRE);
+  // For non-pointer/non-view types, a reference to the variable's storage
+  // is a borrow. We create a loan for it.
+  // For pointer/view types, we stick to the existing model for now and do
+  // not create an extra origin for the l-value expression itself.
+
+  // TODO: A single origin for a `DeclRefExpr` for a pointer or view type is
+  // not sufficient to model the different levels of indirection. The current
+  // single-origin model cannot distinguish between a loan to the variable's
+  // storage and a loan to what it points to. A multi-origin model would be
+  // required for this.
+  if (!isPointerType(DRE->getType())) {
+    if (const Loan *L = createLoan(FactMgr, DRE)) {
+      OriginID ExprOID = FactMgr.getOriginMgr().getOrCreate(*DRE);
+      CurrentBlockFacts.push_back(
+          FactMgr.createFact<IssueFact>(L->ID, ExprOID));
+    }
+  }
+}
+
+void FactsGenerator::VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
+  if (isGslPointerType(CCE->getType())) {
+    handleGSLPointerConstruction(CCE);
+    return;
+  }
+}
+
+void FactsGenerator::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
+  // Specifically for conversion operators,
+  // like `std::string_view p = std::string{};`
+  if (isGslPointerType(MCE->getType()) &&
+      isa<CXXConversionDecl>(MCE->getCalleeDecl())) {
+    // The argument is the implicit object itself.
+    handleFunctionCall(MCE, MCE->getMethodDecl(),
+                       {MCE->getImplicitObjectArgument()},
+                       /*IsGslConstruction=*/true);
+  }
+  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);
+  }
+}
+
+void FactsGenerator::VisitCallExpr(const CallExpr *CE) {
+  handleFunctionCall(CE, CE->getDirectCallee(),
+                     {CE->getArgs(), CE->getNumArgs()});
+}
+
+void FactsGenerator::VisitCXXNullPtrLiteralExpr(
+    const CXXNullPtrLiteralExpr *N) {
+  /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
+  /// pointers can use the same type of loan.
+  FactMgr.getOriginMgr().getOrCreate(*N);
+}
+
+void FactsGenerator::VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
+  if (!hasOrigin(ICE))
+    return;
+  // An ImplicitCastExpr node itself gets an origin, which flows from the
+  // origin of its sub-expression (after stripping its own parens/casts).
+  killAndFlowOrigin(*ICE, *ICE->getSubExpr());
+}
+
+void FactsGenerator::VisitUnaryOperator(const UnaryOperator *UO) {
+  if (UO->getOpcode() == UO_AddrOf) {
+    const Expr *SubExpr = UO->getSubExpr();
+    // Taking address of a pointer-type expression is not yet supported and
+    // will be supported in multi-origin model.
+    if (isPointerType(SubExpr->getType()))
+      return;
+    // The origin of an address-of expression (e.g., &x) is the origin of
+    // its sub-expression (x). This fact will cause the dataflow analysis
+    // to propagate any loans held by the sub-expression's origin to the
+    // origin of this UnaryOperator expression.
+    killAndFlowOrigin(*UO, *SubExpr);
+  }
+}
+
+void FactsGenerator::VisitReturnStmt(const ReturnStmt *RS) {
+  if (const Expr *RetExpr = RS->getRetValue()) {
+    if (hasOrigin(RetExpr)) {
+      OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);
+      CurrentBlockFacts.push_back(FactMgr.createFact<ReturnOfOriginFact>(OID));
+    }
+  }
+}
+
+void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) {
+  if (BO->isAssignmentOp())
+    handleAssignment(BO->getLHS(), BO->getRHS());
+}
+
+void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
+  // Assignment operators have special "kill-then-propagate" semantics
+  // and are handled separately.
+  if (OCE->isAssignmentOp() && OCE->getNumArgs() == 2) {
+    handleAssignment(OCE->getArg(0), OCE->getArg(1));
+    return;
+  }
+  handleFunctionCall(OCE, OCE->getDirectCallee(),
+                     {OCE->getArgs(), OCE->getNumArgs()},
+                     /*IsGslConstruction=*/false);
+}
+
+void FactsGenerator::VisitCXXFunctionalCastExpr(
+    const CXXFunctionalCastExpr *FCE) {
+  // Check if this is a test point marker. If so, we are done with this
+  // expression.
+  if (handleTestPoint(FCE))
+    return;
+  if (isGslPointerType(FCE->getType()))
+    killAndFlowOrigin(*FCE, *FCE->getSubExpr());
+}
+
+void FactsGenerator::VisitInitListExpr(const InitListExpr *ILE) {
+  if (!hasOrigin(ILE))
+    return;
+  // For list initialization with a single element, like `View{...}`, the
+  // origin of the list itself is the origin of its single element.
+  if (ILE->getNumInits() == 1)
+    killAndFlowOrigin(*ILE, *ILE->getInit(0));
+}
+
+void FactsGenerator::VisitMaterializeTemporaryExpr(
+    const MaterializeTemporaryExpr *MTE) {
+  if (!hasOrigin(MTE))
+    return;
+  // A temporary object's origin is the same as the origin of the
+  // expression that initializes it.
+  killAndFlowOrigin(*MTE, *MTE->getSubExpr());
+}
+
+void FactsGenerator::handleDestructor(const CFGAutomaticObjDtor &DtorOpt) {
+  /// TODO: Also handle trivial destructors (e.g., for `int`
+  /// variables) which will never have a CFGAutomaticObjDtor node.
+  /// TODO: Handle loans to temporaries.
+  /// TODO: Consider using clang::CFG::BuildOptions::AddLifetime to reuse the
+  /// lifetime ends.
+  const VarDecl *DestructedVD = DtorOpt.getVarDecl();
+  if (!DestructedVD)
+    return;
+  // Iterate through all loans to see if any expire.
+  /// TODO(opt): Do better than a linear search to find loans associated with
+  /// 'DestructedVD'.
+  for (const Loan &L : FactMgr.getLoanMgr().getLoans()) {
+    const AccessPath &LoanPath = L.Path;
+    // Check if the loan is for a stack variable and if that variable
+    // is the one being destructed.
+    if (LoanPath.D == DestructedVD)
+      CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
+          L.ID, DtorOpt.getTriggerStmt()->getEndLoc()));
+  }
+}
+
+void FactsGenerator::handleGSLPointerConstruction(const CXXConstructExpr *CCE) {
+  assert(isGslPointerType(CCE->getType()));
+  if (CCE->getNumArgs() != 1)
+    return;
+  if (hasOrigin(CCE->getArg(0)))
+    killAndFlowOrigin(*CCE, *CCE->getArg(0));
+  else
+    // This could be a new borrow.
+    handleFunctionCall(CCE, CCE->getConstructor(),
+                       {CCE->getArgs(), CCE->getNumArgs()},
+                       /*IsGslConstruction=*/true);
+}
+
+/// Checks if a call-like expression creates a borrow by passing a value to a
+/// reference parameter, creating an IssueFact if it does.
+/// \param IsGslConstruction True if this is a GSL construction where all
+///   argument origins should flow to the returned origin.
+void FactsGenerator::handleFunctionCall(const Expr *Call,
+                                        const FunctionDecl *FD,
+                                        ArrayRef<const Expr *> Args,
+                                        bool IsGslConstruction) {
+  // Ignore functions returning values with no origin.
+  if (!FD || !hasOrigin(Call))
+    return;
+  auto IsArgLifetimeBound = [FD](unsigned I) -> bool {
+    const ParmVarDecl *PVD = nullptr;
+    if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
+        Method && Method->isInstance()) {
+      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;
+  };
+  if (Args.empty())
+    return;
+  bool killedSrc = false;
+  for (unsigned I = 0; I < Args.size(); ++I)
+    if (IsGslConstruction || IsArgLifetimeBound(I)) {
+      if (!killedSrc) {
+        killedSrc = true;
+        killAndFlowOrigin(*Call, *Args[I]);
+      } else
+        flowOrigin(*Call, *Args[I]);
+    }
+}
+
+/// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
+/// If so, creates a `TestPointFact` and returns true.
+bool FactsGenerator::handleTestPoint(const CXXFunctionalCastExpr *FCE) {
+  if (!FCE->getType()->isVoidType())
+    return false;
+
+  const auto *SubExpr = FCE->getSubExpr()->IgnoreParenImpCasts();
+  if (const auto *SL = dyn_cast<StringLiteral>(SubExpr)) {
+    llvm::StringRef LiteralValue = SL->getString();
+    const std::string Prefix = "__lifetime_test_point_";
+
+    if (LiteralValue.starts_with(Prefix)) {
+      StringRef Annotation = LiteralValue.drop_front(Prefix.length());
+      CurrentBlockFacts.push_back(
+          FactMgr.createFact<TestPointFact>(Annotation));
+      return true;
+    }
+  }
+  return false;
+}
+
+void FactsGenerator::handleAssignment(const Expr *LHSExpr,
+                                      const Expr *RHSExpr) {
+  if (!hasOrigin(LHSExpr))
+    return;
+  // Find the underlying variable declaration for the left-hand side.
+  if (const auto *DRE_LHS =
+          dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) {
+    markUseAsWrite(DRE_LHS);
+    if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl())) {
+      // Kill the old loans of the destination origin and flow the new loans
+      // from the source origin.
+      killAndFlowOrigin(*VD_LHS, *RHSExpr);
+    }
+  }
+}
+
+// A DeclRefExpr will be treated as a use of the referenced decl. It will be
+// checked for use-after-free unless it is later marked as being written to
+// (e.g. on the left-hand side of an assignment).
+void FactsGenerator::handleUse(const DeclRefExpr *DRE) {
+  if (isPointerType(DRE->getType())) {
+    UseFact *UF = FactMgr.createFact<UseFact>(DRE);
+    CurrentBlockFacts.push_back(UF);
+    assert(!UseFacts.contains(DRE));
+    UseFacts[DRE] = UF;
+  }
+}
+
+void FactsGenerator::markUseAsWrite(const DeclRefExpr *DRE) {
+  if (!isPointerType(DRE->getType()))
+    return;
+  assert(UseFacts.contains(DRE));
+  UseFacts[DRE]->markAsWritten();
+}
+
+} // namespace internal
+} // namespace clang::lifetimes
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
index 141d7fa054cc4..5b2e826b402ec 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
@@ -18,6 +18,7 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/Checker.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Dataflow.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
@@ -64,7 +65,7 @@ void LifetimeSafetyAnalysis::run() {
   DEBUG_WITH_TYPE("PrintCFG", Cfg.dump(AC.getASTContext().getLangOpts(),
                                        /*ShowColors=*/true));
 
-  FactGenerator FactGen(*FactMgr, AC);
+  FactsGenerator FactGen(*FactMgr, AC);
   FactGen.run();
   DEBUG_WITH_TYPE("LifetimeFacts", FactMgr->dump(Cfg, AC));
 
@@ -89,9 +90,7 @@ void LifetimeSafetyAnalysis::run() {
   DEBUG_WITH_TYPE("LiveOrigins",
                   LiveOrigins->dump(llvm::dbgs(), getTestPoints()));
 
-  LifetimeChecker Checker(*LoanPropagation, *LiveOrigins, *FactMgr, AC,
-                          Reporter);
-  Checker.run();
+  runLifetimeChecker(*LoanPropagation, *LiveOrigins, *FactMgr, AC, Reporter);
 }
 
 LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID,
diff --git a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
new file mode 100644
index 0000000000000..dd20eb4b0fbe2
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
@@ -0,0 +1,109 @@
+#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace clang::lifetimes {
+namespace internal {
+
+using Lattice = LiveOriginAnalysis::Lattice;
+
+void LivenessLattice::dump(llvm::raw_ostream &OS,
+                           const OriginManager &OM) const {
+  if (LiveOrigins.isEmpty())
+    OS << "  <empty>\n";
+  for (const auto &Entry : LiveOrigins) {
+    OriginID OID = Entry.first;
+    const LivenessInfo &Info = Entry.second;
+    OS << "  ";
+    OM.dump(OID, OS);
+    OS << " is ";
+    switch (Info.Kind) {
+    case LivenessKind::Must:
+      OS << "definitely";
+      break;
+    case LivenessKind::Maybe:
+      OS << "maybe";
+      break;
+    case LivenessKind::Dead:
+      llvm_unreachable("liveness kind of live origins should not be dead.");
+    }
+    OS << " live at this point\n";
+  }
+}
+
+/// Merges two lattices by combining liveness information.
+/// When the same origin has different confidence levels, we take the lower
+/// one.
+Lattice LiveOriginAnalysis::join(Lattice L1, Lattice L2) const {
+  LivenessMap Merged = L1.LiveOrigins;
+  // Take the earliest UseFact to make the join hermetic and commutative.
+  auto CombineUseFact = [](const UseFact &A,
+                           const UseFact &B) -> const UseFact * {
+    return A.getUseExpr()->getExprLoc() < B.getUseExpr()->getExprLoc() ? &A
+                                                                       : &B;
+  };
+  auto CombineLivenessKind = [](LivenessKind K1,
+                                LivenessKind K2) -> LivenessKind {
+    assert(K1 != LivenessKind::Dead && "LivenessKind should not be dead.");
+    assert(K2 != LivenessKind::Dead && "LivenessKind should not be dead.");
+    // Only return "Must" if both paths are "Must", otherwise Maybe.
+    if (K1 == LivenessKind::Must && K2 == LivenessKind::Must)
+      return LivenessKind::Must;
+    return LivenessKind::Maybe;
+  };
+  auto CombineLivenessInfo = [&](const LivenessInfo *L1,
+                                 const LivenessInfo *L2) -> LivenessInfo {
+    assert((L1 || L2) && "unexpectedly merging 2 empty sets");
+    if (!L1)
+      return LivenessInfo(L2->CausingUseFact, LivenessKind::Maybe);
+    if (!L2)
+      return LivenessInfo(L1->CausingUseFact, LivenessKind::Maybe);
+    return LivenessInfo(
+        CombineUseFact(*L1->CausingUseFact, *L2->CausingUseFact),
+        CombineLivenessKind(L1->Kind, L2->Kind));
+  };
+  return Lattice(utils::join(
+      L1.LiveOrigins, L2.LiveOrigins, Factory, CombineLivenessInfo,
+      // A symmetric join is required here. If an origin is live on one
+      // branch but not the other, its confidence must be demoted to `Maybe`.
+      utils::JoinKind::Symmetric));
+}
+
+/// A read operation makes the origin live with definite confidence, as it
+/// dominates this program point. A write operation kills the liveness of
+/// the origin since it overwrites the value.
+Lattice LiveOriginAnalysis::transfer(Lattice In, const UseFact &UF) {
+  OriginID OID = UF.getUsedOrigin(FactMgr.getOriginMgr());
+  // Write kills liveness.
+  if (UF.isWritten())
+    return Lattice(Factory.remove(In.LiveOrigins, OID));
+  // Read makes origin live with definite confidence (dominates this point).
+  return Lattice(
+      Factory.add(In.LiveOrigins, OID, LivenessInfo(&UF, LivenessKind::Must)));
+}
+
+/// Issuing a new loan to an origin kills its liveness.
+Lattice LiveOriginAnalysis::transfer(Lattice In, const IssueFact &IF) {
+  return Lattice(Factory.remove(In.LiveOrigins, IF.getOriginID()));
+}
+
+/// An OriginFlow kills the liveness of the destination origin if `KillDest`
+/// is true. Otherwise, it propagates liveness from destination to source.
+Lattice LiveOriginAnalysis::transfer(Lattice In, const OriginFlowFact &OF) {
+  if (!OF.getKillDest())
+    return In;
+  return Lattice(Factory.remove(In.LiveOrigins, OF.getDestOriginID()));
+}
+
+void LiveOriginAnalysis::dump(llvm::raw_ostream &OS,
+                              llvm::StringMap<ProgramPoint> TestPoints) const {
+  llvm::dbgs() << "==========================================\n";
+  llvm::dbgs() << getAnalysisName() << " results:\n";
+  llvm::dbgs() << "==========================================\n";
+  for (const auto &Entry : TestPoints) {
+    OS << "TestPoint: " << Entry.getKey() << "\n";
+    getState(Entry.getValue()).dump(OS, FactMgr.getOriginMgr());
+  }
+}
+
+} // namespace internal
+} // namespace clang::lifetimes
\ No newline at end of file
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
new file mode 100644
index 0000000000000..d3fdb54eaf51f
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -0,0 +1,63 @@
+#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
+
+namespace clang::lifetimes {
+namespace internal {
+
+void LoanPropagationLattice::dump(llvm::raw_ostream &OS) const {
+  OS << "LoanPropagationLattice State:\n";
+  if (Origins.isEmpty())
+    OS << "  <empty>\n";
+  for (const auto &Entry : Origins) {
+    if (Entry.second.isEmpty())
+      OS << "  Origin " << Entry.first << " contains no loans\n";
+    for (const LoanID &LID : Entry.second)
+      OS << "  Origin " << Entry.first << " contains Loan " << LID << "\n";
+  }
+}
+
+using Lattice = LoanPropagationAnalysis::Lattice;
+
+/// Merges two lattices by taking the union of loans for each origin.
+// TODO(opt): Keep the state small by removing origins which become dead.
+Lattice LoanPropagationAnalysis::join(Lattice A, Lattice B) {
+  OriginLoanMap JoinedOrigins = utils::join(
+      A.Origins, B.Origins, OriginLoanMapFactory,
+      [&](const LoanSet *S1, const LoanSet *S2) {
+        assert((S1 || S2) && "unexpectedly merging 2 empty sets");
+        if (!S1)
+          return *S2;
+        if (!S2)
+          return *S1;
+        return utils::join(*S1, *S2, LoanSetFactory);
+      },
+      // Asymmetric join is a performance win. For origins present only on one
+      // branch, the loan set can be carried over as-is.
+      utils::JoinKind::Asymmetric);
+  return Lattice(JoinedOrigins);
+}
+
+/// A new loan is issued to the origin. Old loans are erased.
+Lattice LoanPropagationAnalysis::transfer(Lattice In, const IssueFact &F) {
+  OriginID OID = F.getOriginID();
+  LoanID LID = F.getLoanID();
+  return LoanPropagationLattice(OriginLoanMapFactory.add(
+      In.Origins, OID, LoanSetFactory.add(LoanSetFactory.getEmptySet(), LID)));
+}
+
+/// A flow from source to destination. If `KillDest` is true, this replaces
+/// the destination's loans with the source's. Otherwise, the source's loans
+/// are merged into the destination's.
+Lattice LoanPropagationAnalysis::transfer(Lattice In, const OriginFlowFact &F) {
+  OriginID DestOID = F.getDestOriginID();
+  OriginID SrcOID = F.getSrcOriginID();
+
+  LoanSet DestLoans =
+      F.getKillDest() ? LoanSetFactory.getEmptySet() : getLoans(In, DestOID);
+  LoanSet SrcLoans = getLoans(In, SrcOID);
+  LoanSet MergedLoans = utils::join(DestLoans, SrcLoans, LoanSetFactory);
+
+  return LoanPropagationLattice(
+      OriginLoanMapFactory.add(In.Origins, DestOID, MergedLoans));
+}
+} // namespace internal
+} // namespace clang::lifetimes

>From 52464b328577e36bb0a324e41a06e0999976b2bd Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Wed, 8 Oct 2025 21:54:38 +0200
Subject: [PATCH 07/10] Apply changes from code browser

Apply changes from code browser
---
 .../Analysis/Analyses/LifetimeSafety/FactsGenerator.h      | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 6a59a53daa071..a904f3dc64a06 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -1,4 +1,4 @@
-//===- TODO ---*- C++ -*-===//
+//===- FactsGenerator.h - Lifetime Facts Generation -------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,7 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// TODO
+// This file defines the FactsGenerator, which traverses the AST to generate
+// lifetime-relevant facts (such as loan issuance, expiration, origin flow,
+// and use) from CFG statements. These facts are used by the dataflow analyses
+// to track pointer lifetimes and detect use-after-free errors.
 //
 //===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTSGENERATOR_H

>From 98bb91d3d680c82d0945f1d1eb2d82980c83c8c3 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Wed, 8 Oct 2025 21:56:41 +0200
Subject: [PATCH 08/10] Apply changes from code browser

Apply changes from code browser
---
 .github/new-prs-labeler.yml                   |  4 +-
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |  8 ++--
 .../Analyses/LifetimeSafety/LifetimeSafety.h  | 15 +++++---
 .../LifetimeSafety/LifetimeSafety.cpp         | 37 +++++--------------
 4 files changed, 26 insertions(+), 38 deletions(-)

diff --git a/.github/new-prs-labeler.yml b/.github/new-prs-labeler.yml
index c49fd1dd3cc7f..efdc42d349195 100644
--- a/.github/new-prs-labeler.yml
+++ b/.github/new-prs-labeler.yml
@@ -1096,8 +1096,8 @@ clang:openmp:
   - llvm/test/Transforms/OpenMP/**
 
 clang:temporal-safety:
-  - clang/include/clang/Analysis/Analyses/LifetimeSafety*
-  - clang/lib/Analysis/LifetimeSafety*
+  - clang/include/clang/Analysis/Analyses/LifetimeSafety/**
+  - clang/lib/Analysis/LifetimeSafety/**
   - clang/unittests/Analysis/LifetimeSafety*
   - clang/test/Sema/*lifetime-safety*
   - clang/test/Sema/*lifetime-analysis*
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 222f861837e09..5cfc1ed67c406 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -1,4 +1,4 @@
-//===- Facts.h - Lifetime Analysis Facts and Fact Generation ---*- C++ -*-===//
+//===- Facts.h - Lifetime Analysis Facts and Fact Manager ------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -7,8 +7,8 @@
 //===----------------------------------------------------------------------===//
 //
 // This file defines Facts, which are atomic lifetime-relevant events (such as
-// loan issuance, loan expiration, origin flow, and use), and the FactGenerator,
-// which traverses the AST to generate these facts from CFG statements.
+// loan issuance, loan expiration, origin flow, and use), and the FactManager,
+// which manages the storage and retrieval of facts for each CFG block.
 //
 //===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H
@@ -255,7 +255,9 @@ class FactManager {
   }
 
   LoanManager &getLoanMgr() { return LoanMgr; }
+  const LoanManager &getLoanMgr() const { return LoanMgr; }
   OriginManager &getOriginMgr() { return OriginMgr; }
+  const OriginManager &getOriginMgr() const { return OriginMgr; }
 
 private:
   LoanManager LoanMgr;
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 0745eeba9f53a..1228148d00586 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -25,7 +25,6 @@
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
 #include "llvm/ADT/StringMap.h"
-#include <memory>
 
 namespace clang::lifetimes {
 
@@ -34,8 +33,14 @@ void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
                                LifetimeSafetyReporter *Reporter);
 
 namespace internal {
-// Forward declarations of internal types.
-struct LifetimeFactory;
+/// An object to hold the factories for immutable collections, ensuring
+/// that all created states share the same underlying memory management.
+struct LifetimeFactory {
+  llvm::BumpPtrAllocator Allocator;
+  OriginLoanMap::Factory OriginMapFactory{Allocator, /*canonicalize=*/false};
+  LoanSet::Factory LoanSetFactory{Allocator, /*canonicalize=*/false};
+  LivenessMap::Factory LivenessMapFactory{Allocator, /*canonicalize=*/false};
+};
 
 /// Running the lifetime safety analysis and querying its results. It
 /// encapsulates the various dataflow analyses.
@@ -83,8 +88,8 @@ class LifetimeSafetyAnalysis {
 private:
   AnalysisDeclContext &AC;
   LifetimeSafetyReporter *Reporter;
-  std::unique_ptr<LifetimeFactory> Factory;
-  std::unique_ptr<FactManager> FactMgr;
+  LifetimeFactory Factory;
+  FactManager FactMgr;
   std::unique_ptr<LoanPropagationAnalysis> LoanPropagation;
   std::unique_ptr<LiveOriginAnalysis> LiveOrigins;
 };
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
index 5b2e826b402ec..74058c3e54711 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
@@ -26,8 +26,6 @@
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
 #include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/ImmutableMap.h"
-#include "llvm/ADT/ImmutableSet.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/TimeProfiler.h"
@@ -37,26 +35,12 @@
 namespace clang::lifetimes {
 namespace internal {
 
-// ========================================================================= //
-//                  LifetimeSafetyAnalysis Class Implementation
-// ========================================================================= //
-
-/// An object to hold the factories for immutable collections, ensuring
-/// that all created states share the same underlying memory management.
-struct LifetimeFactory {
-  llvm::BumpPtrAllocator Allocator;
-  OriginLoanMap::Factory OriginMapFactory{Allocator, /*canonicalize=*/false};
-  LoanSet::Factory LoanSetFactory{Allocator, /*canonicalize=*/false};
-  LivenessMap::Factory LivenessMapFactory{Allocator, /*canonicalize=*/false};
-};
-
 // We need this here for unique_ptr with forward declared class.
 LifetimeSafetyAnalysis::~LifetimeSafetyAnalysis() = default;
 
 LifetimeSafetyAnalysis::LifetimeSafetyAnalysis(AnalysisDeclContext &AC,
                                                LifetimeSafetyReporter *Reporter)
-    : AC(AC), Reporter(Reporter), Factory(std::make_unique<LifetimeFactory>()),
-      FactMgr(std::make_unique<FactManager>()) {}
+    : AC(AC), Reporter(Reporter) {}
 
 void LifetimeSafetyAnalysis::run() {
   llvm::TimeTraceScope TimeProfile("LifetimeSafetyAnalysis");
@@ -65,9 +49,9 @@ void LifetimeSafetyAnalysis::run() {
   DEBUG_WITH_TYPE("PrintCFG", Cfg.dump(AC.getASTContext().getLangOpts(),
                                        /*ShowColors=*/true));
 
-  FactsGenerator FactGen(*FactMgr, AC);
+  FactsGenerator FactGen(FactMgr, AC);
   FactGen.run();
-  DEBUG_WITH_TYPE("LifetimeFacts", FactMgr->dump(Cfg, AC));
+  DEBUG_WITH_TYPE("LifetimeFacts", FactMgr.dump(Cfg, AC));
 
   /// TODO(opt): Consider optimizing individual blocks before running the
   /// dataflow analysis.
@@ -81,16 +65,16 @@ void LifetimeSafetyAnalysis::run() {
   /// 3. Collapse ExpireFacts belonging to same source location into a single
   ///    Fact.
   LoanPropagation = std::make_unique<LoanPropagationAnalysis>(
-      Cfg, AC, *FactMgr, Factory->OriginMapFactory, Factory->LoanSetFactory);
+      Cfg, AC, FactMgr, Factory.OriginMapFactory, Factory.LoanSetFactory);
   LoanPropagation->run();
 
   LiveOrigins = std::make_unique<LiveOriginAnalysis>(
-      Cfg, AC, *FactMgr, Factory->LivenessMapFactory);
+      Cfg, AC, FactMgr, Factory.LivenessMapFactory);
   LiveOrigins->run();
   DEBUG_WITH_TYPE("LiveOrigins",
                   LiveOrigins->dump(llvm::dbgs(), getTestPoints()));
 
-  runLifetimeChecker(*LoanPropagation, *LiveOrigins, *FactMgr, AC, Reporter);
+  runLifetimeChecker(*LoanPropagation, *LiveOrigins, FactMgr, AC, Reporter);
 }
 
 LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID,
@@ -101,18 +85,16 @@ LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID,
 
 std::optional<OriginID>
 LifetimeSafetyAnalysis::getOriginIDForDecl(const ValueDecl *D) const {
-  assert(FactMgr && "FactManager not initialized");
   // This assumes the OriginManager's `get` can find an existing origin.
   // We might need a `find` method on OriginManager to avoid `getOrCreate` logic
   // in a const-query context if that becomes an issue.
-  return FactMgr->getOriginMgr().get(*D);
+  return const_cast<OriginManager &>(FactMgr.getOriginMgr()).get(*D);
 }
 
 std::vector<LoanID>
 LifetimeSafetyAnalysis::getLoanIDForVar(const VarDecl *VD) const {
-  assert(FactMgr && "FactManager not initialized");
   std::vector<LoanID> Result;
-  for (const Loan &L : FactMgr->getLoanMgr().getLoans())
+  for (const Loan &L : FactMgr.getLoanMgr().getLoans())
     if (L.Path.D == VD)
       Result.push_back(L.ID);
   return Result;
@@ -128,10 +110,9 @@ LifetimeSafetyAnalysis::getLiveOriginsAtPoint(ProgramPoint PP) const {
 }
 
 llvm::StringMap<ProgramPoint> LifetimeSafetyAnalysis::getTestPoints() const {
-  assert(FactMgr && "FactManager not initialized");
   llvm::StringMap<ProgramPoint> AnnotationToPointMap;
   for (const CFGBlock *Block : *AC.getCFG()) {
-    for (const Fact *F : FactMgr->getFacts(Block)) {
+    for (const Fact *F : FactMgr.getFacts(Block)) {
       if (const auto *TPF = F->getAs<TestPointFact>()) {
         StringRef PointName = TPF->getAnnotation();
         assert(AnnotationToPointMap.find(PointName) ==

>From feeb663f6c7f591e89d54581f4473a3467dc3093 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Thu, 9 Oct 2025 12:53:24 +0200
Subject: [PATCH 09/10] nested namespaces

---
 .../Analyses/LifetimeSafety/Checker.h         |  8 +-
 .../Analyses/LifetimeSafety/Dataflow.h        |  6 +-
 .../Analysis/Analyses/LifetimeSafety/Facts.h  | 71 +++------------
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  6 +-
 .../Analyses/LifetimeSafety/LifetimeSafety.h  | 15 +++-
 .../Analyses/LifetimeSafety/LiveOrigins.h     |  6 +-
 .../Analyses/LifetimeSafety/LoanPropagation.h |  6 +-
 .../Analysis/Analyses/LifetimeSafety/Loans.h  | 11 +--
 .../Analyses/LifetimeSafety/Origins.h         | 18 +---
 .../Analysis/Analyses/LifetimeSafety/Utils.h  |  9 +-
 .../Analysis/LifetimeSafety/CMakeLists.txt    |  5 +-
 clang/lib/Analysis/LifetimeSafety/Checker.cpp |  6 +-
 clang/lib/Analysis/LifetimeSafety/Facts.cpp   | 86 +++++++++++++++++++
 .../LifetimeSafety/FactsGenerator.cpp         |  9 +-
 .../LifetimeSafety/LifetimeAnnotations.cpp    |  6 +-
 .../LifetimeSafety/LifetimeSafety.cpp         |  6 --
 .../Analysis/LifetimeSafety/LiveOrigins.cpp   |  7 +-
 .../LifetimeSafety/LoanPropagation.cpp        |  7 +-
 clang/lib/Analysis/LifetimeSafety/Loans.cpp   | 18 ++++
 clang/lib/Analysis/LifetimeSafety/Origins.cpp | 25 ++++++
 .../unittests/Analysis/LifetimeSafetyTest.cpp |  2 +-
 21 files changed, 190 insertions(+), 143 deletions(-)
 create mode 100644 clang/lib/Analysis/LifetimeSafety/Facts.cpp
 create mode 100644 clang/lib/Analysis/LifetimeSafety/Loans.cpp
 create mode 100644 clang/lib/Analysis/LifetimeSafety/Origins.cpp

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
index b059e00d7bdaf..a8584ec5188d4 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h
@@ -19,9 +19,7 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Reporter.h"
 
-namespace clang {
-namespace lifetimes {
-namespace internal {
+namespace clang::lifetimes::internal {
 
 /// Runs the lifetime checker, which detects use-after-free errors by
 /// examining loan expiration points and checking if any live origins hold
@@ -31,8 +29,6 @@ void runLifetimeChecker(LoanPropagationAnalysis &LoanPropagation,
                         AnalysisDeclContext &ADC,
                         LifetimeSafetyReporter *Reporter);
 
-} // namespace internal
-} // namespace lifetimes
-} // namespace clang
+} // namespace clang::lifetimes::internal
 
 #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_CHECKER_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Dataflow.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Dataflow.h
index e818b26739e18..16927f199869b 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Dataflow.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Dataflow.h
@@ -24,8 +24,7 @@
 #include "llvm/Support/TimeProfiler.h"
 #include <optional>
 
-namespace clang::lifetimes {
-namespace internal {
+namespace clang::lifetimes::internal {
 
 enum class Direction { Forward, Backward };
 
@@ -185,6 +184,5 @@ class DataflowAnalysis {
   Lattice transfer(Lattice In, const UseFact &) { return In; }
   Lattice transfer(Lattice In, const TestPointFact &) { return In; }
 };
-} // namespace internal
-} // namespace clang::lifetimes
+} // namespace clang::lifetimes::internal
 #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_DATAFLOW_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 5cfc1ed67c406..80b9b8ef89d5f 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -16,15 +16,13 @@
 
 #include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
-#include "clang/Analysis/Analyses/PostOrderCFGView.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Debug.h"
 #include <cstdint>
 
-namespace clang::lifetimes {
-namespace internal {
+namespace clang::lifetimes::internal {
 /// An abstract base class for a single, atomic lifetime-relevant event.
 class Fact {
 
@@ -65,9 +63,7 @@ class Fact {
   }
 
   virtual void dump(llvm::raw_ostream &OS, const LoanManager &,
-                    const OriginManager &) const {
-    OS << "Fact (Kind: " << static_cast<int>(K) << ")\n";
-  }
+                    const OriginManager &) const;
 };
 
 /// A `ProgramPoint` identifies a location in the CFG by pointing to a specific
@@ -88,13 +84,7 @@ class IssueFact : public Fact {
   LoanID getLoanID() const { return LID; }
   OriginID getOriginID() const { return OID; }
   void dump(llvm::raw_ostream &OS, const LoanManager &LM,
-            const OriginManager &OM) const override {
-    OS << "Issue (";
-    LM.getLoan(getLoanID()).dump(OS);
-    OS << ", ToOrigin: ";
-    OM.dump(getOriginID(), OS);
-    OS << ")\n";
-  }
+            const OriginManager &OM) const override;
 };
 
 class ExpireFact : public Fact {
@@ -111,11 +101,7 @@ class ExpireFact : public Fact {
   SourceLocation getExpiryLoc() const { return ExpiryLoc; }
 
   void dump(llvm::raw_ostream &OS, const LoanManager &LM,
-            const OriginManager &) const override {
-    OS << "Expire (";
-    LM.getLoan(getLoanID()).dump(OS);
-    OS << ")\n";
-  }
+            const OriginManager &) const override;
 };
 
 class OriginFlowFact : public Fact {
@@ -139,14 +125,7 @@ class OriginFlowFact : public Fact {
   bool getKillDest() const { return KillDest; }
 
   void dump(llvm::raw_ostream &OS, const LoanManager &,
-            const OriginManager &OM) const override {
-    OS << "OriginFlow (Dest: ";
-    OM.dump(getDestOriginID(), OS);
-    OS << ", Src: ";
-    OM.dump(getSrcOriginID(), OS);
-    OS << (getKillDest() ? "" : ", Merge");
-    OS << ")\n";
-  }
+            const OriginManager &OM) const override;
 };
 
 class ReturnOfOriginFact : public Fact {
@@ -160,11 +139,7 @@ class ReturnOfOriginFact : public Fact {
   ReturnOfOriginFact(OriginID OID) : Fact(Kind::ReturnOfOrigin), OID(OID) {}
   OriginID getReturnedOriginID() const { return OID; }
   void dump(llvm::raw_ostream &OS, const LoanManager &,
-            const OriginManager &OM) const override {
-    OS << "ReturnOfOrigin (";
-    OM.dump(getReturnedOriginID(), OS);
-    OS << ")\n";
-  }
+            const OriginManager &OM) const override;
 };
 
 class UseFact : public Fact {
@@ -187,11 +162,7 @@ class UseFact : public Fact {
   bool isWritten() const { return IsWritten; }
 
   void dump(llvm::raw_ostream &OS, const LoanManager &,
-            const OriginManager &OM) const override {
-    OS << "Use (";
-    OM.dump(getUsedOrigin(OM), OS);
-    OS << ", " << (isWritten() ? "Write" : "Read") << ")\n";
-  }
+            const OriginManager &OM) const override;
 };
 
 /// A dummy-fact used to mark a specific point in the code for testing.
@@ -208,9 +179,7 @@ class TestPointFact : public Fact {
   StringRef getAnnotation() const { return Annotation; }
 
   void dump(llvm::raw_ostream &OS, const LoanManager &,
-            const OriginManager &) const override {
-    OS << "TestPoint (Annotation: \"" << getAnnotation() << "\")\n";
-  }
+            const OriginManager &) const override;
 };
 
 class FactManager {
@@ -233,26 +202,7 @@ class FactManager {
     return new (Mem) FactType(std::forward<Args>(args)...);
   }
 
-  void dump(const CFG &Cfg, AnalysisDeclContext &AC) const {
-    llvm::dbgs() << "==========================================\n";
-    llvm::dbgs() << "       Lifetime Analysis Facts:\n";
-    llvm::dbgs() << "==========================================\n";
-    if (const Decl *D = AC.getDecl())
-      if (const auto *ND = dyn_cast<NamedDecl>(D))
-        llvm::dbgs() << "Function: " << ND->getQualifiedNameAsString() << "\n";
-    // Print blocks in the order as they appear in code for a stable ordering.
-    for (const CFGBlock *B : *AC.getAnalysis<PostOrderCFGView>()) {
-      llvm::dbgs() << "  Block B" << B->getBlockID() << ":\n";
-      auto It = BlockToFactsMap.find(B);
-      if (It != BlockToFactsMap.end()) {
-        for (const Fact *F : It->second) {
-          llvm::dbgs() << "    ";
-          F->dump(llvm::dbgs(), LoanMgr, OriginMgr);
-        }
-      }
-      llvm::dbgs() << "  End of Block\n";
-    }
-  }
+  void dump(const CFG &Cfg, AnalysisDeclContext &AC) const;
 
   LoanManager &getLoanMgr() { return LoanMgr; }
   const LoanManager &getLoanMgr() const { return LoanMgr; }
@@ -266,7 +216,6 @@ class FactManager {
       BlockToFactsMap;
   llvm::BumpPtrAllocator FactAllocator;
 };
-} // namespace internal
-} // namespace clang::lifetimes
+} // namespace clang::lifetimes::internal
 
 #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index a904f3dc64a06..5e58abee2bbb3 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -22,8 +22,7 @@
 #include "clang/Analysis/CFG.h"
 #include "llvm/ADT/SmallVector.h"
 
-namespace clang::lifetimes {
-namespace internal {
+namespace clang::lifetimes::internal {
 
 class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
   using Base = ConstStmtVisitor<FactsGenerator>;
@@ -102,7 +101,6 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
   llvm::DenseMap<const DeclRefExpr *, UseFact *> UseFacts;
 };
 
-} // namespace internal
-} // namespace clang::lifetimes
+} // namespace clang::lifetimes::internal
 
 #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTSGENERATOR_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 1228148d00586..b9d1436c4ff6f 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -52,8 +52,19 @@ class LifetimeSafetyAnalysis {
 
   void run();
 
-  /// Returns the set of loans an origin holds at a specific program point.
-  LoanSet getLoansAtPoint(OriginID OID, ProgramPoint PP) const;
+  /// Returns the loan propagation analysis object.
+  /// \note This is intended for testing only.
+  LoanPropagationAnalysis &getLoanPropagationAnalysis() const {
+    assert(LoanPropagation && "Analysis has not been run.");
+    return *LoanPropagation;
+  }
+
+  /// Returns the live origin analysis object.
+  /// \note This is intended for testing only.
+  LiveOriginAnalysis &getLiveOriginAnalysis() const {
+    assert(LiveOrigins && "Analysis has not been run.");
+    return *LiveOrigins;
+  }
 
   /// Returns the set of origins that are live at a specific program point,
   /// along with the confidence level of their liveness.
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h
index f3d49adcef0ec..89d275f1c13b2 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h
@@ -29,8 +29,7 @@
 #include "llvm/ADT/ImmutableMap.h"
 #include "llvm/Support/Debug.h"
 
-namespace clang::lifetimes {
-namespace internal {
+namespace clang::lifetimes::internal {
 
 using OriginSet = llvm::ImmutableSet<OriginID>;
 
@@ -132,7 +131,6 @@ class LiveOriginAnalysis
   FactManager &FactMgr;
   LivenessMap::Factory &Factory;
 };
-} // namespace internal
-} // namespace clang::lifetimes
+} // namespace clang::lifetimes::internal
 
 #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LIVE_ORIGINS_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
index 40a591604b8f3..66a4143c87307 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h
@@ -23,8 +23,7 @@
 #include "llvm/ADT/ImmutableSet.h"
 #include "llvm/Support/Debug.h"
 
-namespace clang::lifetimes {
-namespace internal {
+namespace clang::lifetimes::internal {
 
 // Using LLVM's immutable collections is efficient for dataflow analysis
 // as it avoids deep copies during state transitions.
@@ -91,7 +90,6 @@ class LoanPropagationAnalysis
     return LoanSetFactory.getEmptySet();
   }
 };
-} // namespace internal
-} // namespace clang::lifetimes
+} // namespace clang::lifetimes::internal
 
 #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOAN_PROPAGATION_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h
index eb7b62abbdad6..7f5cf03fd3e5f 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h
@@ -18,8 +18,7 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
 #include "llvm/Support/raw_ostream.h"
 
-namespace clang::lifetimes {
-namespace internal {
+namespace clang::lifetimes::internal {
 
 using LoanID = utils::ID<struct LoanTag>;
 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, LoanID ID) {
@@ -49,10 +48,7 @@ struct Loan {
   Loan(LoanID id, AccessPath path, const Expr *IssueExpr)
       : ID(id), Path(path), IssueExpr(IssueExpr) {}
 
-  void dump(llvm::raw_ostream &OS) const {
-    OS << ID << " (Path: ";
-    OS << Path.D->getNameAsString() << ")";
-  }
+  void dump(llvm::raw_ostream &OS) const;
 };
 
 /// Manages the creation, storage and retrieval of loans.
@@ -79,7 +75,6 @@ class LoanManager {
   /// optimisation.
   llvm::SmallVector<Loan> AllLoans;
 };
-} // namespace internal
-} // namespace clang::lifetimes
+} // namespace clang::lifetimes::internal
 
 #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOANS_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
index d159d986e5cf0..d858b3fa8f9fa 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
@@ -18,8 +18,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
 
-namespace clang::lifetimes {
-namespace internal {
+namespace clang::lifetimes::internal {
 
 using OriginID = utils::ID<struct OriginTag>;
 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, OriginID ID) {
@@ -123,17 +122,7 @@ class OriginManager {
     return NewID;
   }
 
-  void dump(OriginID OID, llvm::raw_ostream &OS) const {
-    OS << OID << " (";
-    Origin O = getOrigin(OID);
-    if (const ValueDecl *VD = O.getDecl())
-      OS << "Decl: " << VD->getNameAsString();
-    else if (const Expr *E = O.getExpr())
-      OS << "Expr: " << E->getStmtClassName();
-    else
-      OS << "Unknown";
-    OS << ")";
-  }
+  void dump(OriginID OID, llvm::raw_ostream &OS) const;
 
 private:
   OriginID getNextOriginID() { return NextOriginID++; }
@@ -145,7 +134,6 @@ class OriginManager {
   llvm::DenseMap<const clang::ValueDecl *, OriginID> DeclToOriginID;
   llvm::DenseMap<const clang::Expr *, OriginID> ExprToOriginID;
 };
-} // namespace internal
-} // namespace clang::lifetimes
+} // namespace clang::lifetimes::internal
 
 #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_ORIGINS_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
index 443ddd44b358c..4183cabe860a7 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Utils.h
@@ -14,10 +14,7 @@
 #include "llvm/ADT/ImmutableMap.h"
 #include "llvm/ADT/ImmutableSet.h"
 
-namespace clang::lifetimes {
-namespace internal {
-
-namespace utils {
+namespace clang::lifetimes::internal::utils {
 
 /// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
 /// Used for giving ID to loans and origins.
@@ -95,9 +92,7 @@ join(const llvm::ImmutableMap<K, V> &A, const llvm::ImmutableMap<K, V> &B,
   }
   return Res;
 }
-} // namespace utils
-} // namespace internal
-} // namespace clang::lifetimes
+} // namespace clang::lifetimes::internal::utils
 
 namespace llvm {
 template <typename Tag>
diff --git a/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt b/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt
index 629ab00419638..85842928770cd 100644
--- a/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt
+++ b/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt
@@ -1,9 +1,12 @@
 add_clang_library(clangAnalysisLifetimeSafety
   Checker.cpp
+  Facts.cpp
   FactsGenerator.cpp
-  LiveOrigins.cpp
   LifetimeAnnotations.cpp
   LifetimeSafety.cpp
+  LiveOrigins.cpp
+  Loans.cpp
   LoanPropagation.cpp
+  Origins.cpp
   )
 
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 56f0549b1e003..51341c0d17303 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -25,8 +25,7 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/TimeProfiler.h"
 
-namespace clang::lifetimes {
-namespace internal {
+namespace clang::lifetimes::internal {
 
 /// Struct to store the complete context for a potential lifetime violation.
 struct PendingWarning {
@@ -141,5 +140,4 @@ void runLifetimeChecker(LoanPropagationAnalysis &LoanPropagation,
   Checker.run();
 }
 
-} // namespace internal
-} // namespace clang::lifetimes
+} // namespace clang::lifetimes::internal
\ No newline at end of file
diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
new file mode 100644
index 0000000000000..dc7c999534ec7
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
@@ -0,0 +1,86 @@
+//===- Facts.cpp - Lifetime Analysis Facts Implementation -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/AST/Decl.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+
+namespace clang::lifetimes::internal {
+
+void Fact::dump(llvm::raw_ostream &OS, const LoanManager &,
+                const OriginManager &) const {
+  OS << "Fact (Kind: " << static_cast<int>(K) << ")\n";
+}
+
+void IssueFact::dump(llvm::raw_ostream &OS, const LoanManager &LM,
+                     const OriginManager &OM) const {
+  OS << "Issue (";
+  LM.getLoan(getLoanID()).dump(OS);
+  OS << ", ToOrigin: ";
+  OM.dump(getOriginID(), OS);
+  OS << ")\n";
+}
+
+void ExpireFact::dump(llvm::raw_ostream &OS, const LoanManager &LM,
+                      const OriginManager &) const {
+  OS << "Expire (";
+  LM.getLoan(getLoanID()).dump(OS);
+  OS << ")\n";
+}
+
+void OriginFlowFact::dump(llvm::raw_ostream &OS, const LoanManager &,
+                          const OriginManager &OM) const {
+  OS << "OriginFlow (Dest: ";
+  OM.dump(getDestOriginID(), OS);
+  OS << ", Src: ";
+  OM.dump(getSrcOriginID(), OS);
+  OS << (getKillDest() ? "" : ", Merge");
+  OS << ")\n";
+}
+
+void ReturnOfOriginFact::dump(llvm::raw_ostream &OS, const LoanManager &,
+                              const OriginManager &OM) const {
+  OS << "ReturnOfOrigin (";
+  OM.dump(getReturnedOriginID(), OS);
+  OS << ")\n";
+}
+
+void UseFact::dump(llvm::raw_ostream &OS, const LoanManager &,
+                   const OriginManager &OM) const {
+  OS << "Use (";
+  OM.dump(getUsedOrigin(OM), OS);
+  OS << ", " << (isWritten() ? "Write" : "Read") << ")\n";
+}
+
+void TestPointFact::dump(llvm::raw_ostream &OS, const LoanManager &,
+                         const OriginManager &) const {
+  OS << "TestPoint (Annotation: \"" << getAnnotation() << "\")\n";
+}
+
+void FactManager::dump(const CFG &Cfg, AnalysisDeclContext &AC) const {
+  llvm::dbgs() << "==========================================\n";
+  llvm::dbgs() << "       Lifetime Analysis Facts:\n";
+  llvm::dbgs() << "==========================================\n";
+  if (const Decl *D = AC.getDecl())
+    if (const auto *ND = dyn_cast<NamedDecl>(D))
+      llvm::dbgs() << "Function: " << ND->getQualifiedNameAsString() << "\n";
+  // Print blocks in the order as they appear in code for a stable ordering.
+  for (const CFGBlock *B : *AC.getAnalysis<PostOrderCFGView>()) {
+    llvm::dbgs() << "  Block B" << B->getBlockID() << ":\n";
+    auto It = BlockToFactsMap.find(B);
+    if (It != BlockToFactsMap.end()) {
+      for (const Fact *F : It->second) {
+        llvm::dbgs() << "    ";
+        F->dump(llvm::dbgs(), LoanMgr, OriginMgr);
+      }
+    }
+    llvm::dbgs() << "  End of Block\n";
+  }
+}
+
+} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 3f4836fd13c18..7fce59ecc8add 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -1,9 +1,11 @@
+// TODO: Add file header
 #include "clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
 #include "llvm/Support/TimeProfiler.h"
 
-namespace clang::lifetimes {
-namespace internal {
+namespace clang::lifetimes::internal {
+
 static bool isGslPointerType(QualType QT) {
   if (const auto *RD = QT->getAsCXXRecordDecl()) {
     // We need to check the template definition for specializations.
@@ -336,5 +338,4 @@ void FactsGenerator::markUseAsWrite(const DeclRefExpr *DRE) {
   UseFacts[DRE]->markAsWritten();
 }
 
-} // namespace internal
-} // namespace clang::lifetimes
+} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 87d3db77cc65e..ad61a42c0eaeb 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -13,8 +13,7 @@
 #include "clang/AST/Type.h"
 #include "clang/AST/TypeLoc.h"
 
-namespace clang {
-namespace lifetimes {
+namespace clang::lifetimes {
 
 const FunctionDecl *
 getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD) {
@@ -71,5 +70,4 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
   return isNormalAssignmentOperator(FD);
 }
 
-} // namespace lifetimes
-} // namespace clang
+} // namespace clang::lifetimes
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
index 74058c3e54711..ae6ef3619ba11 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
@@ -77,12 +77,6 @@ void LifetimeSafetyAnalysis::run() {
   runLifetimeChecker(*LoanPropagation, *LiveOrigins, FactMgr, AC, Reporter);
 }
 
-LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID,
-                                                ProgramPoint PP) const {
-  assert(LoanPropagation && "Analysis has not been run.");
-  return LoanPropagation->getLoans(OID, PP);
-}
-
 std::optional<OriginID>
 LifetimeSafetyAnalysis::getOriginIDForDecl(const ValueDecl *D) const {
   // This assumes the OriginManager's `get` can find an existing origin.
diff --git a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
index dd20eb4b0fbe2..b4b1da72353e9 100644
--- a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
@@ -1,8 +1,8 @@
+// TODO: Add file header
 #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
 #include "llvm/Support/ErrorHandling.h"
 
-namespace clang::lifetimes {
-namespace internal {
+namespace clang::lifetimes::internal {
 
 using Lattice = LiveOriginAnalysis::Lattice;
 
@@ -105,5 +105,4 @@ void LiveOriginAnalysis::dump(llvm::raw_ostream &OS,
   }
 }
 
-} // namespace internal
-} // namespace clang::lifetimes
\ No newline at end of file
+} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index d3fdb54eaf51f..1b49e024d3540 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -1,7 +1,7 @@
+// TODO: Add file header
 #include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
 
-namespace clang::lifetimes {
-namespace internal {
+namespace clang::lifetimes::internal {
 
 void LoanPropagationLattice::dump(llvm::raw_ostream &OS) const {
   OS << "LoanPropagationLattice State:\n";
@@ -59,5 +59,4 @@ Lattice LoanPropagationAnalysis::transfer(Lattice In, const OriginFlowFact &F) {
   return LoanPropagationLattice(
       OriginLoanMapFactory.add(In.Origins, DestOID, MergedLoans));
 }
-} // namespace internal
-} // namespace clang::lifetimes
+} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Analysis/LifetimeSafety/Loans.cpp b/clang/lib/Analysis/LifetimeSafety/Loans.cpp
new file mode 100644
index 0000000000000..2c85a3c6083f3
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety/Loans.cpp
@@ -0,0 +1,18 @@
+//===- Loans.cpp - Loan Implementation --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
+
+namespace clang::lifetimes::internal {
+
+void Loan::dump(llvm::raw_ostream &OS) const {
+  OS << ID << " (Path: ";
+  OS << Path.D->getNameAsString() << ")";
+}
+
+} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Analysis/LifetimeSafety/Origins.cpp b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
new file mode 100644
index 0000000000000..56645680e3cef
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
@@ -0,0 +1,25 @@
+//===- Origins.cpp - Origin Implementation -----------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
+
+namespace clang::lifetimes::internal {
+
+void OriginManager::dump(OriginID OID, llvm::raw_ostream &OS) const {
+  OS << OID << " (";
+  Origin O = getOrigin(OID);
+  if (const ValueDecl *VD = O.getDecl())
+    OS << "Decl: " << VD->getNameAsString();
+  else if (const Expr *E = O.getExpr())
+    OS << "Expr: " << E->getStmtClassName();
+  else
+    OS << "Unknown";
+  OS << ")";
+}
+
+} // namespace clang::lifetimes::internal
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index a280222b0cbfc..c7f8d56278772 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -123,7 +123,7 @@ class LifetimeTestHelper {
     ProgramPoint PP = Runner.getProgramPoint(Annotation);
     if (!PP)
       return std::nullopt;
-    return Analysis.getLoansAtPoint(OID, PP);
+    return Analysis.getLoanPropagationAnalysis().getLoans(OID, PP);
   }
 
   std::optional<std::vector<std::pair<OriginID, LivenessKind>>>

>From 146a15cc6778339ad278d331364dd19a4afdd089 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Thu, 9 Oct 2025 15:10:41 +0200
Subject: [PATCH 10/10] Apply changes from code browser

Apply changes from code browser
---
 clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp  | 9 ++++++++-
 clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp     | 9 ++++++++-
 clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp | 8 +++++++-
 3 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 7fce59ecc8add..485308f5b2c3b 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -1,4 +1,11 @@
-// TODO: Add file header
+//===- FactsGenerator.cpp - Lifetime Facts Generation -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
 #include "clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
 #include "clang/Analysis/Analyses/PostOrderCFGView.h"
diff --git a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
index b4b1da72353e9..78a11b9fcc78c 100644
--- a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
@@ -1,4 +1,11 @@
-// TODO: Add file header
+//===- LiveOrigins.cpp - Live Origins Analysis -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
 #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
 #include "llvm/Support/ErrorHandling.h"
 
diff --git a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
index 1b49e024d3540..a6c427262a988 100644
--- a/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp
@@ -1,4 +1,10 @@
-// TODO: Add file header
+//===- LoanPropagation.cpp - Loan Propagation Analysis ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
 #include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
 
 namespace clang::lifetimes::internal {



More information about the llvm-commits mailing list