Author: Utkarsh Saxena
Date: 2025-07-22T12:10:47Z
New Revision: 54b50681ca0fd1c0c6ddb059c88981a45e2f1b19

URL: 
https://github.com/llvm/llvm-project/commit/54b50681ca0fd1c0c6ddb059c88981a45e2f1b19
DIFF: 
https://github.com/llvm/llvm-project/commit/54b50681ca0fd1c0c6ddb059c88981a45e2f1b19.diff

LOG: Revert "[LifetimeSafety] Revamp test suite using unittests (#149158)"

This reverts commit 688ea048affe8e79221ea1a8c376bcf20ef8f3bb.

Added: 
    

Modified: 
    clang/include/clang/Analysis/Analyses/LifetimeSafety.h
    clang/lib/Analysis/LifetimeSafety.cpp
    clang/lib/Sema/AnalysisBasedWarnings.cpp
    clang/test/Sema/warn-lifetime-safety-dataflow.cpp
    clang/unittests/Analysis/CMakeLists.txt

Removed: 
    clang/unittests/Analysis/LifetimeSafetyTest.cpp


################################################################################
diff  --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
index beeb0aaba5d0d..9998702a41cab 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -17,96 +17,14 @@
 
//===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H
 #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H
+#include "clang/AST/DeclBase.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
-#include "llvm/ADT/ImmutableSet.h"
-#include "llvm/ADT/StringMap.h"
-#include <memory>
+namespace clang {
 
-namespace clang::lifetimes {
+void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg,
+                               AnalysisDeclContext &AC);
 
-/// The main entry point for the analysis.
-void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC);
-
-namespace internal {
-// Forward declarations of internal types.
-class Fact;
-class FactManager;
-class LoanPropagationAnalysis;
-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>;
-
-// 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>;
-
-/// 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 {
-public:
-  LifetimeSafetyAnalysis(AnalysisDeclContext &AC);
-  ~LifetimeSafetyAnalysis();
-
-  void run();
-
-  /// Returns the set of loans an origin holds at a specific program point.
-  LoanSet getLoansAtPoint(OriginID OID, ProgramPoint PP) const;
-
-  /// Finds the OriginID for a given declaration.
-  /// Returns a null optional if not found.
-  std::optional<OriginID> getOriginIDForDecl(const ValueDecl *D) const;
-
-  /// Finds the LoanID's for the loan created with the specific variable as
-  /// their Path.
-  std::vector<LoanID> getLoanIDForVar(const VarDecl *VD) const;
-
-  /// Retrieves program points that were specially marked in the source code
-  /// for testing.
-  ///
-  /// The analysis recognizes special function calls of the form
-  /// `void("__lifetime_test_point_<name>")` as test points. This method 
returns
-  /// a map from the annotation string (<name>) to the corresponding
-  /// `ProgramPoint`. This allows test harnesses to query the analysis state at
-  /// user-defined locations in the code.
-  /// \note This is intended for testing only.
-  llvm::StringMap<ProgramPoint> getTestPoints() const;
-
-private:
-  AnalysisDeclContext &AC;
-  std::unique_ptr<LifetimeFactory> Factory;
-  std::unique_ptr<FactManager> FactMgr;
-  std::unique_ptr<LoanPropagationAnalysis> LoanPropagation;
-};
-} // namespace internal
-} // namespace clang::lifetimes
+} // namespace clang
 
 #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H

diff  --git a/clang/lib/Analysis/LifetimeSafety.cpp 
b/clang/lib/Analysis/LifetimeSafety.cpp
index ae6ec9f76cbf6..a95db6d8013bd 100644
--- a/clang/lib/Analysis/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -24,14 +24,8 @@
 #include "llvm/Support/TimeProfiler.h"
 #include <cstdint>
 
-namespace clang::lifetimes {
-namespace internal {
+namespace clang {
 namespace {
-template <typename Tag>
-inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {
-  return OS << ID.Value;
-}
-} // namespace
 
 /// Represents the storage location being borrowed, e.g., a specific stack
 /// variable.
@@ -42,6 +36,32 @@ struct AccessPath {
   AccessPath(const clang::ValueDecl *D) : D(D) {}
 };
 
+/// 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);
+  }
+};
+
+template <typename Tag>
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {
+  return OS << ID.Value;
+}
+
+using LoanID = ID<struct LoanTag>;
+using OriginID = ID<struct OriginTag>;
+
 /// Information about a single borrow, or "Loan". A loan is created when a
 /// reference or pointer is created.
 struct Loan {
@@ -203,9 +223,7 @@ class Fact {
     /// An origin is propagated from a source to a destination (e.g., p = q).
     AssignOrigin,
     /// An origin escapes the function by flowing into the return value.
-    ReturnOfOrigin,
-    /// A marker for a specific point in the code, for testing.
-    TestPoint,
+    ReturnOfOrigin
   };
 
 private:
@@ -292,24 +310,6 @@ class ReturnOfOriginFact : public Fact {
   }
 };
 
-/// 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 override {
-    OS << "TestPoint (Annotation: \"" << getAnnotation() << "\")\n";
-  }
-};
-
 class FactManager {
 public:
   llvm::ArrayRef<const Fact *> getFacts(const CFGBlock *B) const {
@@ -363,7 +363,6 @@ class FactManager {
 };
 
 class FactGenerator : public ConstStmtVisitor<FactGenerator> {
-  using Base = ConstStmtVisitor<FactGenerator>;
 
 public:
   FactGenerator(FactManager &FactMgr, AnalysisDeclContext &AC)
@@ -459,15 +458,6 @@ class FactGenerator : public 
ConstStmtVisitor<FactGenerator> {
     }
   }
 
-  void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE) {
-    // Check if this is a test point marker. If so, we are done with this
-    // expression.
-    if (VisitTestPoint(FCE))
-      return;
-    // Visit as normal otherwise.
-    Base::VisitCXXFunctionalCastExpr(FCE);
-  }
-
 private:
   // Check if a type has an origin.
   bool hasOrigin(QualType QT) { return QT->isPointerOrReferenceType(); }
@@ -501,27 +491,6 @@ class FactGenerator : public 
ConstStmtVisitor<FactGenerator> {
     }
   }
 
-  /// Checks if the expression is a `void("__lifetime_test_point_...")` cast.
-  /// If so, creates a `TestPointFact` and returns true.
-  bool VisitTestPoint(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;
-  }
-
   FactManager &FactMgr;
   AnalysisDeclContext &AC;
   llvm::SmallVector<Fact *> CurrentBlockFacts;
@@ -668,8 +637,6 @@ class DataflowAnalysis {
       return D->transfer(In, *F->getAs<AssignOriginFact>());
     case Fact::Kind::ReturnOfOrigin:
       return D->transfer(In, *F->getAs<ReturnOfOriginFact>());
-    case Fact::Kind::TestPoint:
-      return D->transfer(In, *F->getAs<TestPointFact>());
     }
     llvm_unreachable("Unknown fact kind");
   }
@@ -679,16 +646,14 @@ class DataflowAnalysis {
   Lattice transfer(Lattice In, const ExpireFact &) { return In; }
   Lattice transfer(Lattice In, const AssignOriginFact &) { return In; }
   Lattice transfer(Lattice In, const ReturnOfOriginFact &) { 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) {
+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)
@@ -701,7 +666,7 @@ static llvm::ImmutableSet<T> join(llvm::ImmutableSet<T> A,
 // 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>
+llvm::ImmutableMap<K, V>
 join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
      typename llvm::ImmutableMap<K, V>::Factory &F, Joiner joinValues) {
   if (A.getHeight() < B.getHeight())
@@ -725,6 +690,10 @@ join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> 
B,
 //                          Loan Propagation Analysis
 // ========================================================================= //
 
+// 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>;
 
 /// An object to hold the factories for immutable collections, ensuring
@@ -838,28 +807,17 @@ class LoanPropagationAnalysis
 // - Modify origin liveness analysis to answer `bool isLive(Origin O, Point P)`
 // - Using the above three to perform the final error reporting.
 // ========================================================================= //
+} // anonymous namespace
 
-// ========================================================================= //
-//                  LifetimeSafetyAnalysis Class Implementation
-// ========================================================================= //
-
-// We need this here for unique_ptr with forward declared class.
-LifetimeSafetyAnalysis::~LifetimeSafetyAnalysis() = default;
-
-LifetimeSafetyAnalysis::LifetimeSafetyAnalysis(AnalysisDeclContext &AC)
-    : AC(AC), Factory(std::make_unique<LifetimeFactory>()),
-      FactMgr(std::make_unique<FactManager>()) {}
-
-void LifetimeSafetyAnalysis::run() {
+void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg,
+                               AnalysisDeclContext &AC) {
   llvm::TimeTraceScope TimeProfile("LifetimeSafetyAnalysis");
-
-  const CFG &Cfg = *AC.getCFG();
   DEBUG_WITH_TYPE("PrintCFG", Cfg.dump(AC.getASTContext().getLangOpts(),
                                        /*ShowColors=*/true));
-
-  FactGenerator FactGen(*FactMgr, AC);
+  FactManager FactMgr;
+  FactGenerator 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.
@@ -870,56 +828,9 @@ void LifetimeSafetyAnalysis::run() {
   ///    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.
-  LoanPropagation =
-      std::make_unique<LoanPropagationAnalysis>(Cfg, AC, *FactMgr, *Factory);
-  LoanPropagation->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;
-}
-
-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) {
-  internal::LifetimeSafetyAnalysis Analysis(AC);
-  Analysis.run();
+  LifetimeFactory Factory;
+  LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, Factory);
+  LoanPropagation.run();
+  DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump());
 }
-} // namespace clang::lifetimes
+} // namespace clang

diff  --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp 
b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index d4d640bf988b1..d1400cbfc884d 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -3029,8 +3029,8 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
   // TODO: Enable lifetime safety analysis for other languages once it is
   // stable.
   if (EnableLifetimeSafetyAnalysis && S.getLangOpts().CPlusPlus) {
-    if (AC.getCFG())
-      lifetimes::runLifetimeSafetyAnalysis(AC);
+    if (CFG *cfg = AC.getCFG())
+      runLifetimeSafetyAnalysis(*cast<DeclContext>(D), *cfg, AC);
   }
   // Check for violations of "called once" parameter properties.
   if (S.getLangOpts().ObjC && !S.getLangOpts().CPlusPlus &&

diff  --git a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp 
b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
index 0eb3bda918f82..0e98904ade86a 100644
--- a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -mllvm -debug-only=LifetimeFacts 
-Wexperimental-lifetime-safety %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -mllvm -debug-only=LifetimeFacts,LifetimeLoanPropagation 
-Wexperimental-lifetime-safety %s 2>&1 | FileCheck %s
 // REQUIRES: asserts
 
 struct MyObj {
@@ -19,6 +19,10 @@ MyObj* return_local_addr() {
 // CHECK:   ReturnOfOrigin (OriginID: [[O_RET_VAL]])
 // CHECK:   Expire (LoanID: [[L_X]])
 }
+// CHECK: LoanPropagation results:
+// CHECK-DAG: Origin [[O_ADDR_X]] contains Loan [[L_X]]
+// CHECK-DAG: Origin [[O_P]] contains Loan [[L_X]]
+// CHECK-DAG: Origin [[O_RET_VAL]] contains Loan [[L_X]]
 
 
 // Pointer Assignment and Return
@@ -43,6 +47,15 @@ MyObj* assign_and_return_local_addr() {
 // CHECK: ReturnOfOrigin (OriginID: [[O_PTR2_RVAL_2]])
 // CHECK: Expire (LoanID: [[L_Y]])
 }
+// CHECK: LoanPropagation results:
+// CHECK-DAG: Origin [[O_ADDR_Y]] contains Loan [[L_Y]]
+// CHECK-DAG: Origin [[O_PTR1]] contains Loan [[L_Y]]
+// CHECK-DAG: Origin [[O_PTR2]] contains Loan [[L_Y]]
+// CHECK-DAG: Origin [[O_PTR1_RVAL]] contains Loan [[L_Y]]
+// CHECK-DAG: Origin [[O_PTR1_RVAL_2]] contains Loan [[L_Y]]
+// CHECK-DAG: Origin [[O_PTR2_RVAL]] contains Loan [[L_Y]]
+// CHECK-DAG: Origin [[O_PTR2_RVAL_2]] contains Loan [[L_Y]]
+
 
 // Return of Non-Pointer Type
 // CHECK-LABEL: Function: return_int_val
@@ -52,6 +65,8 @@ int return_int_val() {
   return x;
 }
 // CHECK-NEXT: End of Block
+// CHECK: LoanPropagation results:
+// CHECK:  <empty>
 
 
 // Loan Expiration (Automatic Variable, C++)
@@ -64,6 +79,9 @@ void loan_expires_cpp() {
 // CHECK: AssignOrigin (DestID: [[O_POBJ:[0-9]+]], SrcID: [[O_ADDR_OBJ]])
 // CHECK: Expire (LoanID: [[L_OBJ]])
 }
+// CHECK: LoanPropagation results:
+// CHECK-DAG: Origin [[O_ADDR_OBJ]] contains Loan [[L_OBJ]]
+// CHECK-DAG: Origin [[O_POBJ]] contains Loan [[L_OBJ]]
 
 
 // FIXME: No expire for Trivial Destructors
@@ -78,6 +96,10 @@ void loan_expires_trivial() {
 // CHECK-NEXT: End of Block
   // FIXME: Add check for Expire once trivial destructors are handled for 
expiration.
 }
+// CHECK: LoanPropagation results:
+// CHECK-DAG: Origin [[O_ADDR_TRIVIAL_OBJ]] contains Loan [[L_TRIVIAL_OBJ]]
+// CHECK-DAG: Origin [[O_PTOBJ]] contains Loan [[L_TRIVIAL_OBJ]]
+
 
 // CHECK-LABEL: Function: conditional
 void conditional(bool condition) {
@@ -97,6 +119,13 @@ void conditional(bool condition) {
   // CHECK: AssignOrigin (DestID: [[O_P_RVAL:[0-9]+]], SrcID: [[O_P]])
   // CHECK: AssignOrigin (DestID: [[O_Q:[0-9]+]], SrcID: [[O_P_RVAL]])
 }
+// CHECK: LoanPropagation results:
+// CHECK-DAG: Origin [[O_ADDR_A]] contains Loan [[L_A]]
+// CHECK-DAG: Origin [[O_ADDR_B]] contains Loan [[L_B]]
+// CHECK-DAG: Origin [[O_P]] contains Loan [[L_A]]
+// CHECK-DAG: Origin [[O_P]] contains Loan [[L_B]]
+// CHECK-DAG: Origin [[O_Q]] contains Loan [[L_A]]
+// CHECK-DAG: Origin [[O_Q]] contains Loan [[L_B]]
 
 
 // CHECK-LABEL: Function: pointers_in_a_cycle
@@ -132,6 +161,25 @@ void pointers_in_a_cycle(bool condition) {
 // CHECK:   AssignOrigin (DestID: [[O_P3]], SrcID: [[O_TEMP_RVAL]])
   }
 }
+// At the end of the analysis, the origins for the pointers involved in the 
cycle
+// (p1, p2, p3, temp) should all contain the loans from v1, v2, and v3 at the 
fixed point.
+// CHECK: LoanPropagation results:
+// CHECK-DAG: Origin [[O_P1]] contains Loan [[L_V1]]
+// CHECK-DAG: Origin [[O_P1]] contains Loan [[L_V2]]
+// CHECK-DAG: Origin [[O_P1]] contains Loan [[L_V3]]
+// CHECK-DAG: Origin [[O_P2]] contains Loan [[L_V1]]
+// CHECK-DAG: Origin [[O_P2]] contains Loan [[L_V2]]
+// CHECK-DAG: Origin [[O_P2]] contains Loan [[L_V3]]
+// CHECK-DAG: Origin [[O_P3]] contains Loan [[L_V1]]
+// CHECK-DAG: Origin [[O_P3]] contains Loan [[L_V2]]
+// CHECK-DAG: Origin [[O_P3]] contains Loan [[L_V3]]
+// CHECK-DAG: Origin [[O_TEMP]] contains Loan [[L_V1]]
+// CHECK-DAG: Origin [[O_TEMP]] contains Loan [[L_V2]]
+// CHECK-DAG: Origin [[O_TEMP]] contains Loan [[L_V3]]
+// CHECK-DAG: Origin [[O_ADDR_V1]] contains Loan [[L_V1]]
+// CHECK-DAG: Origin [[O_ADDR_V2]] contains Loan [[L_V2]]
+// CHECK-DAG: Origin [[O_ADDR_V3]] contains Loan [[L_V3]]
+
 
 // CHECK-LABEL: Function: overwrite_origin
 void overwrite_origin() {
@@ -147,6 +195,10 @@ void overwrite_origin() {
 // CHECK:   Expire (LoanID: [[L_S2]])
 // CHECK:   Expire (LoanID: [[L_S1]])
 }
+// CHECK: LoanPropagation results:
+// CHECK:     Origin [[O_P]] contains Loan [[L_S2]]
+// CHECK-NOT: Origin [[O_P]] contains Loan [[L_S1]]
+
 
 // CHECK-LABEL: Function: reassign_to_null
 void reassign_to_null() {
@@ -161,6 +213,8 @@ void reassign_to_null() {
 }
 // FIXME: Have a better representation for nullptr than just an empty origin. 
 //        It should be a separate loan and origin kind.
+// CHECK: LoanPropagation results:
+// CHECK: Origin [[O_P]] contains no loans
 
 
 // CHECK-LABEL: Function: reassign_in_if
@@ -181,6 +235,11 @@ void reassign_in_if(bool condition) {
 // CHECK:   Expire (LoanID: [[L_S2]])
 // CHECK:   Expire (LoanID: [[L_S1]])
 }
+// CHECK: LoanPropagation results:
+// CHECK-DAG: Origin [[O_P]] contains Loan [[L_S1]]
+// CHECK-DAG: Origin [[O_P]] contains Loan [[L_S2]]
+// CHECK-DAG: Origin [[O_ADDR_S1]] contains Loan [[L_S1]]
+// CHECK-DAG: Origin [[O_ADDR_S2]] contains Loan [[L_S2]]
 
 
 // CHECK-LABEL: Function: assign_in_switch
@@ -217,6 +276,14 @@ void assign_in_switch(int mode) {
 // CHECK-DAG:   Expire (LoanID: [[L_S2]])
 // CHECK-DAG:   Expire (LoanID: [[L_S1]])
 }
+// CHECK: LoanPropagation results:
+// CHECK-DAG: Origin [[O_P]] contains Loan [[L_S1]]
+// CHECK-DAG: Origin [[O_P]] contains Loan [[L_S2]]
+// CHECK-DAG: Origin [[O_P]] contains Loan [[L_S3]]
+// CHECK-DAG: Origin [[O_ADDR_S1]] contains Loan [[L_S1]]
+// CHECK-DAG: Origin [[O_ADDR_S2]] contains Loan [[L_S2]]
+// CHECK-DAG: Origin [[O_ADDR_S3]] contains Loan [[L_S3]]
+
 
 // CHECK-LABEL: Function: loan_in_loop
 void loan_in_loop(bool condition) {
@@ -232,6 +299,10 @@ void loan_in_loop(bool condition) {
 // CHECK:   Expire (LoanID: [[L_INNER]])
   }
 }
+// CHECK: LoanPropagation results:
+// CHECK-DAG: Origin [[O_P]] contains Loan [[L_INNER]]
+// CHECK-DAG: Origin [[O_ADDR_INNER]] contains Loan [[L_INNER]]
+
 
 // CHECK-LABEL: Function: loop_with_break
 void loop_with_break(int count) {
@@ -255,6 +326,13 @@ void loop_with_break(int count) {
 // CHECK:   Expire (LoanID: [[L_S1]])
 }
 
+// CHECK-LABEL: LoanPropagation results:
+// CHECK-DAG: Origin [[O_P]] contains Loan [[L_S1]]
+// CHECK-DAG: Origin [[O_P]] contains Loan [[L_S2]]
+// CHECK-DAG: Origin [[O_ADDR_S1]] contains Loan [[L_S1]]
+// CHECK-DAG: Origin [[O_ADDR_S2]] contains Loan [[L_S2]]
+
+
 // CHECK-LABEL: Function: nested_scopes
 void nested_scopes() {
   MyObj* p = nullptr;
@@ -277,6 +355,13 @@ void nested_scopes() {
 // CHECK:   Expire (LoanID: [[L_OUTER]])
 }
 
+// CHECK-LABEL: LoanPropagation results:
+// CHECK-DAG: Origin [[O_P]] contains Loan [[L_INNER]]
+// CHECK-DAG: Origin [[O_ADDR_INNER]] contains Loan [[L_INNER]]
+// CHECK-DAG: Origin [[O_ADDR_OUTER]] contains Loan [[L_OUTER]]
+// CHECK-NOT: Origin [[O_P]] contains Loan [[L_OUTER]]
+
+
 // CHECK-LABEL: Function: pointer_indirection
 void pointer_indirection() {
   int a;

diff  --git a/clang/unittests/Analysis/CMakeLists.txt 
b/clang/unittests/Analysis/CMakeLists.txt
index 52e7d2854633d..059a74843155c 100644
--- a/clang/unittests/Analysis/CMakeLists.txt
+++ b/clang/unittests/Analysis/CMakeLists.txt
@@ -4,7 +4,6 @@ add_clang_unittest(ClangAnalysisTests
   CloneDetectionTest.cpp
   ExprMutationAnalyzerTest.cpp
   IntervalPartitionTest.cpp
-  LifetimeSafetyTest.cpp
   MacroExpansionContextTest.cpp
   UnsafeBufferUsageTest.cpp
   CLANG_LIBS

diff  --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp 
b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
deleted file mode 100644
index af4d63a38211e..0000000000000
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ /dev/null
@@ -1,439 +0,0 @@
-//===- LifetimeSafetyTest.cpp - Lifetime Safety Tests -*---------- 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/ASTMatchers/ASTMatchFinder.h"
-#include "clang/ASTMatchers/ASTMatchers.h"
-#include "clang/Testing/TestAST.h"
-#include "llvm/ADT/StringMap.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include <optional>
-#include <vector>
-
-namespace clang::lifetimes::internal {
-namespace {
-
-using namespace ast_matchers;
-using ::testing::UnorderedElementsAreArray;
-
-// A helper class to run the full lifetime analysis on a piece of code
-// and provide an interface for querying the results.
-class LifetimeTestRunner {
-public:
-  LifetimeTestRunner(llvm::StringRef Code) {
-    std::string FullCode = R"(
-      #define POINT(name) void("__lifetime_test_point_" #name)
-      struct MyObj { ~MyObj() {} int i; };
-    )";
-    FullCode += Code.str();
-
-    TestAST = std::make_unique<clang::TestAST>(FullCode);
-    ASTCtx = &TestAST->context();
-
-    // Find the target function using AST matchers.
-    auto MatchResult =
-        match(functionDecl(hasName("target")).bind("target"), *ASTCtx);
-    auto *FD = selectFirst<FunctionDecl>("target", MatchResult);
-    if (!FD) {
-      ADD_FAILURE() << "Test case must have a function named 'target'";
-      return;
-    }
-    AnalysisCtx = std::make_unique<AnalysisDeclContext>(nullptr, FD);
-    AnalysisCtx->getCFGBuildOptions().setAllAlwaysAdd();
-
-    // Run the main analysis.
-    Analysis = std::make_unique<LifetimeSafetyAnalysis>(*AnalysisCtx);
-    Analysis->run();
-
-    AnnotationToPointMap = Analysis->getTestPoints();
-  }
-
-  LifetimeSafetyAnalysis &getAnalysis() { return *Analysis; }
-  ASTContext &getASTContext() { return *ASTCtx; }
-
-  ProgramPoint getProgramPoint(llvm::StringRef Annotation) {
-    auto It = AnnotationToPointMap.find(Annotation);
-    if (It == AnnotationToPointMap.end()) {
-      ADD_FAILURE() << "Annotation '" << Annotation << "' not found.";
-      return nullptr;
-    }
-    return It->second;
-  }
-
-private:
-  std::unique_ptr<TestAST> TestAST;
-  ASTContext *ASTCtx = nullptr;
-  std::unique_ptr<AnalysisDeclContext> AnalysisCtx;
-  std::unique_ptr<LifetimeSafetyAnalysis> Analysis;
-  llvm::StringMap<ProgramPoint> AnnotationToPointMap;
-};
-
-// A convenience wrapper that uses the LifetimeSafetyAnalysis public API.
-class LifetimeTestHelper {
-public:
-  LifetimeTestHelper(LifetimeTestRunner &Runner)
-      : Runner(Runner), Analysis(Runner.getAnalysis()) {}
-
-  std::optional<OriginID> getOriginForDecl(llvm::StringRef VarName) {
-    auto *VD = findDecl<ValueDecl>(VarName);
-    if (!VD)
-      return std::nullopt;
-    auto OID = Analysis.getOriginIDForDecl(VD);
-    if (!OID)
-      ADD_FAILURE() << "Origin for '" << VarName << "' not found.";
-    return OID;
-  }
-
-  std::optional<LoanID> getLoanForVar(llvm::StringRef VarName) {
-    auto *VD = findDecl<VarDecl>(VarName);
-    if (!VD)
-      return std::nullopt;
-    std::vector<LoanID> LID = Analysis.getLoanIDForVar(VD);
-    if (LID.empty()) {
-      ADD_FAILURE() << "Loan for '" << VarName << "' not found.";
-      return std::nullopt;
-    }
-    // TODO: Support retrieving more than one loans to a var.
-    if (LID.size() > 1) {
-      ADD_FAILURE() << "More than 1 loans found for '" << VarName;
-      return std::nullopt;
-    }
-    return LID[0];
-  }
-
-  std::optional<LoanSet> getLoansAtPoint(OriginID OID,
-                                         llvm::StringRef Annotation) {
-    ProgramPoint PP = Runner.getProgramPoint(Annotation);
-    if (!PP)
-      return std::nullopt;
-    return Analysis.getLoansAtPoint(OID, PP);
-  }
-
-private:
-  template <typename DeclT> DeclT *findDecl(llvm::StringRef Name) {
-    auto &Ctx = Runner.getASTContext();
-    auto Results = match(valueDecl(hasName(Name)).bind("v"), Ctx);
-    if (Results.empty()) {
-      ADD_FAILURE() << "Declaration '" << Name << "' not found in AST.";
-      return nullptr;
-    }
-    return const_cast<DeclT *>(selectFirst<DeclT>("v", Results));
-  }
-
-  LifetimeTestRunner &Runner;
-  LifetimeSafetyAnalysis &Analysis;
-};
-
-// ========================================================================= //
-//                         GTest Matchers & Fixture
-// ========================================================================= //
-
-// It holds the name of the origin variable and a reference to the helper.
-class OriginInfo {
-public:
-  OriginInfo(llvm::StringRef OriginVar, LifetimeTestHelper &Helper)
-      : OriginVar(OriginVar), Helper(Helper) {}
-  llvm::StringRef OriginVar;
-  LifetimeTestHelper &Helper;
-};
-
-/// Matcher to verify the set of loans held by an origin at a specific
-/// program point.
-///
-/// This matcher is intended to be used with an \c OriginInfo object.
-///
-/// \param LoanVars A vector of strings, where each string is the name of a
-/// variable expected to be the source of a loan.
-/// \param Annotation A string identifying the program point (created with
-/// POINT()) where the check should be performed.
-MATCHER_P2(HasLoansToImpl, LoanVars, Annotation, "") {
-  const OriginInfo &Info = arg;
-  std::optional<OriginID> OIDOpt = 
Info.Helper.getOriginForDecl(Info.OriginVar);
-  if (!OIDOpt) {
-    *result_listener << "could not find origin for '" << Info.OriginVar.str()
-                     << "'";
-    return false;
-  }
-
-  std::optional<LoanSet> ActualLoansSetOpt =
-      Info.Helper.getLoansAtPoint(*OIDOpt, Annotation);
-  if (!ActualLoansSetOpt) {
-    *result_listener << "could not get a valid loan set at point '"
-                     << Annotation << "'";
-    return false;
-  }
-  std::vector<LoanID> ActualLoans(ActualLoansSetOpt->begin(),
-                                  ActualLoansSetOpt->end());
-
-  std::vector<LoanID> ExpectedLoans;
-  for (const auto &LoanVar : LoanVars) {
-    std::optional<LoanID> ExpectedLIDOpt = Info.Helper.getLoanForVar(LoanVar);
-    if (!ExpectedLIDOpt) {
-      *result_listener << "could not find loan for var '" << LoanVar << "'";
-      return false;
-    }
-    ExpectedLoans.push_back(*ExpectedLIDOpt);
-  }
-
-  return ExplainMatchResult(UnorderedElementsAreArray(ExpectedLoans),
-                            ActualLoans, result_listener);
-}
-
-// Base test fixture to manage the runner and helper.
-class LifetimeAnalysisTest : public ::testing::Test {
-protected:
-  void SetupTest(llvm::StringRef Code) {
-    Runner = std::make_unique<LifetimeTestRunner>(Code);
-    Helper = std::make_unique<LifetimeTestHelper>(*Runner);
-  }
-
-  OriginInfo Origin(llvm::StringRef OriginVar) {
-    return OriginInfo(OriginVar, *Helper);
-  }
-
-  // Factory function that hides the std::vector creation.
-  auto HasLoansTo(std::initializer_list<std::string> LoanVars,
-                  const char *Annotation) {
-    return HasLoansToImpl(std::vector<std::string>(LoanVars), Annotation);
-  }
-
-  std::unique_ptr<LifetimeTestRunner> Runner;
-  std::unique_ptr<LifetimeTestHelper> Helper;
-};
-
-// ========================================================================= //
-//                                 TESTS
-// ========================================================================= //
-
-TEST_F(LifetimeAnalysisTest, SimpleLoanAndOrigin) {
-  SetupTest(R"(
-    void target() {
-      int x;
-      int* p = &x;
-      POINT(p1);
-    }
-  )");
-  EXPECT_THAT(Origin("p"), HasLoansTo({"x"}, "p1"));
-}
-
-TEST_F(LifetimeAnalysisTest, OverwriteOrigin) {
-  SetupTest(R"(
-    void target() {
-      MyObj s1, s2;
-
-      MyObj* p = &s1;
-      POINT(after_s1);
-
-      p = &s2;
-      POINT(after_s2);
-    }
-  )");
-  EXPECT_THAT(Origin("p"), HasLoansTo({"s1"}, "after_s1"));
-  EXPECT_THAT(Origin("p"), HasLoansTo({"s2"}, "after_s2"));
-}
-
-TEST_F(LifetimeAnalysisTest, ConditionalLoan) {
-  SetupTest(R"(
-    void target(bool cond) {
-      int a, b;
-      int *p = nullptr;
-      if (cond) {
-        p = &a;
-        POINT(after_then);
-      } else {
-        p = &b;
-        POINT(after_else);
-      }
-      POINT(after_if);
-    }
-  )");
-  EXPECT_THAT(Origin("p"), HasLoansTo({"a"}, "after_then"));
-  EXPECT_THAT(Origin("p"), HasLoansTo({"b"}, "after_else"));
-  EXPECT_THAT(Origin("p"), HasLoansTo({"a", "b"}, "after_if"));
-}
-
-TEST_F(LifetimeAnalysisTest, PointerChain) {
-  SetupTest(R"(
-    void target() {
-      MyObj y;
-      MyObj* ptr1 = &y;
-      POINT(p1);
-
-      MyObj* ptr2 = ptr1;
-      POINT(p2);
-
-      ptr2 = ptr1;
-      POINT(p3);
-
-      ptr2 = ptr2; // Self assignment
-      POINT(p4);
-    }
-  )");
-  EXPECT_THAT(Origin("ptr1"), HasLoansTo({"y"}, "p1"));
-  EXPECT_THAT(Origin("ptr2"), HasLoansTo({"y"}, "p2"));
-  EXPECT_THAT(Origin("ptr2"), HasLoansTo({"y"}, "p3"));
-  EXPECT_THAT(Origin("ptr2"), HasLoansTo({"y"}, "p4"));
-}
-
-TEST_F(LifetimeAnalysisTest, ReassignToNull) {
-  SetupTest(R"(
-    void target() {
-      MyObj s1;
-      MyObj* p = &s1;
-      POINT(before_null);
-      p = nullptr;
-      POINT(after_null);
-    }
-  )");
-  EXPECT_THAT(Origin("p"), HasLoansTo({"s1"}, "before_null"));
-  // After assigning to null, the origin for `p` should have no loans.
-  EXPECT_THAT(Origin("p"), HasLoansTo({}, "after_null"));
-}
-
-TEST_F(LifetimeAnalysisTest, ReassignInIf) {
-  SetupTest(R"(
-    void target(bool condition) {
-      MyObj s1, s2;
-      MyObj* p = &s1;
-      POINT(before_if);
-      if (condition) {
-        p = &s2;
-        POINT(after_reassign);
-      }
-      POINT(after_if);
-    }
-  )");
-  EXPECT_THAT(Origin("p"), HasLoansTo({"s1"}, "before_if"));
-  EXPECT_THAT(Origin("p"), HasLoansTo({"s2"}, "after_reassign"));
-  EXPECT_THAT(Origin("p"), HasLoansTo({"s1", "s2"}, "after_if"));
-}
-
-TEST_F(LifetimeAnalysisTest, AssignInSwitch) {
-  SetupTest(R"(
-    void target(int mode) {
-      MyObj s1, s2, s3;
-      MyObj* p = nullptr;
-      switch (mode) {
-        case 1:
-          p = &s1;
-          POINT(case1);
-          break;
-        case 2:
-          p = &s2;
-          POINT(case2);
-          break;
-        default:
-          p = &s3;
-          POINT(case3);
-          break;
-      }
-      POINT(after_switch);
-    }
-  )");
-  EXPECT_THAT(Origin("p"), HasLoansTo({"s1"}, "case1"));
-  EXPECT_THAT(Origin("p"), HasLoansTo({"s2"}, "case2"));
-  EXPECT_THAT(Origin("p"), HasLoansTo({"s3"}, "case3"));
-  EXPECT_THAT(Origin("p"), HasLoansTo({"s1", "s2", "s3"}, "after_switch"));
-}
-
-TEST_F(LifetimeAnalysisTest, LoanInLoop) {
-  SetupTest(R"(
-    void target(bool condition) {
-      MyObj* p = nullptr;
-      while (condition) {
-        MyObj inner;
-        p = &inner;
-        POINT(in_loop);
-      }
-      POINT(after_loop);
-    }
-  )");
-  EXPECT_THAT(Origin("p"), HasLoansTo({"inner"}, "in_loop"));
-  EXPECT_THAT(Origin("p"), HasLoansTo({"inner"}, "after_loop"));
-}
-
-TEST_F(LifetimeAnalysisTest, LoopWithBreak) {
-  SetupTest(R"(
-    void target(int count) {
-      MyObj s1;
-      MyObj s2;
-      MyObj* p = &s1;
-      POINT(before_loop);
-      for (int i = 0; i < count; ++i) {
-        if (i == 5) {
-          p = &s2;
-          POINT(inside_if);
-          break;
-        }
-        POINT(after_if);
-      }
-      POINT(after_loop);
-    }
-  )");
-  EXPECT_THAT(Origin("p"), HasLoansTo({"s1"}, "before_loop"));
-  EXPECT_THAT(Origin("p"), HasLoansTo({"s2"}, "inside_if"));
-  // At the join point after if, s2 cannot make it to p without the if.
-  EXPECT_THAT(Origin("p"), HasLoansTo({"s1"}, "after_if"));
-  // At the join point after the loop, p could hold a loan to s1 (if the loop
-  // completed normally) or to s2 (if the loop was broken).
-  EXPECT_THAT(Origin("p"), HasLoansTo({"s1", "s2"}, "after_loop"));
-}
-
-TEST_F(LifetimeAnalysisTest, PointersInACycle) {
-  SetupTest(R"(
-    void target(bool condition) {
-      MyObj v1, v2, v3;
-      MyObj *p1 = &v1, *p2 = &v2, *p3 = &v3;
-
-      POINT(before_while);
-      while (condition) {
-        MyObj* temp = p1;
-        p1 = p2;
-        p2 = p3;
-        p3 = temp;
-      }
-      POINT(after_loop);
-    }
-  )");
-  EXPECT_THAT(Origin("p1"), HasLoansTo({"v1"}, "before_while"));
-  EXPECT_THAT(Origin("p2"), HasLoansTo({"v2"}, "before_while"));
-  EXPECT_THAT(Origin("p3"), HasLoansTo({"v3"}, "before_while"));
-
-  // At the fixed point after the loop, all pointers could point to any of
-  // the three variables.
-  EXPECT_THAT(Origin("p1"), HasLoansTo({"v1", "v2", "v3"}, "after_loop"));
-  EXPECT_THAT(Origin("p2"), HasLoansTo({"v1", "v2", "v3"}, "after_loop"));
-  EXPECT_THAT(Origin("p3"), HasLoansTo({"v1", "v2", "v3"}, "after_loop"));
-  EXPECT_THAT(Origin("temp"), HasLoansTo({"v1", "v2", "v3"}, "after_loop"));
-}
-
-TEST_F(LifetimeAnalysisTest, NestedScopes) {
-  SetupTest(R"(
-    void target() {
-      MyObj* p = nullptr;
-      {
-        MyObj outer;
-        p = &outer;
-        POINT(before_inner_scope);
-        {
-          MyObj inner;
-          p = &inner;
-          POINT(inside_inner_scope);
-        } // inner expires
-        POINT(after_inner_scope);
-      } // outer expires
-    }
-  )");
-  EXPECT_THAT(Origin("p"), HasLoansTo({"outer"}, "before_inner_scope"));
-  EXPECT_THAT(Origin("p"), HasLoansTo({"inner"}, "inside_inner_scope"));
-  EXPECT_THAT(Origin("p"), HasLoansTo({"inner"}, "after_inner_scope"));
-}
-
-} // anonymous namespace
-} // namespace clang::lifetimes::internal


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to