https://github.com/kashika0112 updated 
https://github.com/llvm/llvm-project/pull/168855

>From 24c7b4b1ccb7dceca06938e23dadea8b11ebb051 Mon Sep 17 00:00:00 2001
From: Kashika Akhouri <[email protected]>
Date: Thu, 20 Nov 2025 11:08:26 +0000
Subject: [PATCH 1/5] Support trivial desctructors

---
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  1 +
 .../LifetimeSafety/FactsGenerator.cpp         | 18 +++--
 clang/lib/Sema/AnalysisBasedWarnings.cpp      |  1 +
 .../Sema/warn-lifetime-safety-dataflow.cpp    | 24 ++++++-
 clang/test/Sema/warn-lifetime-safety.cpp      | 44 +++++++++++--
 .../unittests/Analysis/LifetimeSafetyTest.cpp | 66 +++++++++++++++++++
 6 files changed, 143 insertions(+), 11 deletions(-)

diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 8ea37259c570b..85192fdff5067 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -51,6 +51,7 @@ class FactsGenerator : public 
ConstStmtVisitor<FactsGenerator> {
 
 private:
   void handleDestructor(const CFGAutomaticObjDtor &DtorOpt);
+  void handleTrivialDestructors(const CFGLifetimeEnds &LifetimeEnds);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
 
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index cb9a202b08968..fc92aba773187 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -66,6 +66,9 @@ void FactsGenerator::run() {
       else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
                    Element.getAs<CFGAutomaticObjDtor>())
         handleDestructor(*DtorOpt);
+      else if (std::optional<CFGLifetimeEnds> LifetimeEnds =
+                   Element.getAs<CFGLifetimeEnds>())
+        handleTrivialDestructors(*LifetimeEnds);
     }
     CurrentBlockFacts.append(EscapesInCurrentBlock.begin(),
                              EscapesInCurrentBlock.end());
@@ -230,11 +233,7 @@ void FactsGenerator::VisitMaterializeTemporaryExpr(
 }
 
 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;
@@ -251,6 +250,17 @@ void FactsGenerator::handleDestructor(const 
CFGAutomaticObjDtor &DtorOpt) {
   }
 }
 
+void FactsGenerator::handleTrivialDestructors(
+    const CFGLifetimeEnds &LifetimeEnds) {
+  for (const auto &Loan : FactMgr.getLoanMgr().getLoans()) {
+    if (Loan.Path.D == LifetimeEnds.getVarDecl()) {
+      CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
+          Loan.ID, LifetimeEnds.getTriggerStmt()->getEndLoc()));
+      break;
+    }
+  }
+}
+
 void FactsGenerator::handleGSLPointerConstruction(const CXXConstructExpr *CCE) 
{
   assert(isGslPointerType(CCE->getType()));
   if (CCE->getNumArgs() != 1)
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp 
b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 0fa75a24db4f3..fd6abc2874cb7 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2987,6 +2987,7 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
   AC.getCFGBuildOptions().AddInitializers = true;
   AC.getCFGBuildOptions().AddImplicitDtors = true;
   AC.getCFGBuildOptions().AddTemporaryDtors = true;
+  AC.getCFGBuildOptions().AddLifetime = true;
   AC.getCFGBuildOptions().AddCXXNewAllocator = false;
   AC.getCFGBuildOptions().AddCXXDefaultInitExprInCtors = true;
 
diff --git a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp 
b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
index 11d3b836db3e7..4d9d88fbc294d 100644
--- a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
@@ -59,6 +59,7 @@ int return_int_val() {
   int x = 10;
 // CHECK: Block B{{[0-9]+}}:
 // CHECK:   Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: {{[0-9]+}} (Expr: 
DeclRefExpr))
+// CHECK:   Expire (0 (Path: x))
   return x;
 }
 // CHECK-NEXT: End of Block
@@ -77,7 +78,6 @@ void loan_expires_cpp() {
 }
 
 
-// FIXME: No expire for Trivial Destructors
 // CHECK-LABEL: Function: loan_expires_trivial
 void loan_expires_trivial() {
   int trivial_obj = 1;
@@ -86,11 +86,29 @@ void loan_expires_trivial() {
 // CHECK:   OriginFlow (Dest: [[O_ADDR_TRIVIAL_OBJ:[0-9]+]] (Expr: 
UnaryOperator), Src: [[O_DRE_TRIVIAL]] (Expr: DeclRefExpr))
   int* pTrivialObj = &trivial_obj;
 // CHECK:   OriginFlow (Dest: {{[0-9]+}} (Decl: pTrivialObj), Src: 
[[O_ADDR_TRIVIAL_OBJ]] (Expr: UnaryOperator))
-// CHECK-NOT: Expire
+// CHECK:   Expire (0 (Path: trivial_obj))
 // CHECK-NEXT: End of Block
-  // FIXME: Add check for Expire once trivial destructors are handled for 
expiration.
 }
 
+// Trivial Destructors
+// CHECK-LABEL: Function: return_int_pointer
+int* return_int_pointer() {
+  int* ptr;
+// CHECK: Block B{{[0-9]+}}:
+  int x = 1;
+// CHECK:   Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: [[O_DRE_X:[0-9]+]] 
(Expr: DeclRefExpr))
+// CHECK:   OriginFlow (Dest: [[O_ADDR_X:[0-9]+]] (Expr: UnaryOperator), Src: 
[[O_DRE_X]] (Expr: DeclRefExpr))
+  ptr = &x;
+// CHECK:   Use ([[O_PTR:[0-9]+]] (Decl: ptr), Write)
+// CHECK:   OriginFlow (Dest: [[O_PTR]] (Decl: ptr), Src: [[O_ADDR_X]] (Expr: 
UnaryOperator))
+// CHECK:   Use ([[O_PTR]] (Decl: ptr), Read)
+// CHECK:   OriginFlow (Dest: [[O_RET_VAL:[0-9]+]] (Expr: ImplicitCastExpr), 
Src: [[O_PTR]] (Decl: ptr))
+// CHECK:   Expire ([[L_X]] (Path: x))
+// CHECK:   OriginEscapes ([[O_RET_VAL]] (Expr: ImplicitCastExpr))
+  return ptr;
+}
+// CHECK-NEXT: End of Block
+
 // CHECK-LABEL: Function: conditional
 void conditional(bool condition) {
   int a = 5;
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index 2803e73b5aee2..88c8d5076e5b5 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -12,6 +12,10 @@ struct [[gsl::Pointer()]] View {
   void use() const;
 };
 
+class S {
+  View a, b;
+};
+
 
//===----------------------------------------------------------------------===//
 // Basic Definite Use-After-Free (-W...permissive)
 // These are cases where the pointer is guaranteed to be dangling at the use 
site.
@@ -396,6 +400,24 @@ void loan_from_previous_iteration(MyObj safe, bool 
condition) {
   }             // expected-note {{destroyed here}}
 }
 
+void trivial_uaf(){
+  int * a;
+    {
+        int b = 1;
+        a = &b; // expected-warning {{object whose reference is captured does 
not live long enough}}
+    }           // expected-note {{destroyed here}}
+    (void)*a;   // expected-note {{later used here}}
+}
+
+void trivial_class_uaf() {
+  S* ptr;
+  {
+      S s;
+      ptr = &s; // expected-warning {{object whose reference is captured does 
not live long enough}}
+  }             // expected-note {{destroyed here}}
+  (void)ptr;    // expected-note {{later used here}}
+}
+
 
//===----------------------------------------------------------------------===//
 // Basic Definite Use-After-Return (Return-Stack-Address) (-W...permissive)
 // These are cases where the pointer is guaranteed to be dangling at the use 
site.
@@ -493,6 +515,20 @@ MyObj& reference_return_of_local() {
                     // expected-note@-1 {{returned here}}
 }
 
+int* trivial_uar() {
+  int *a;
+  int b = 1;
+  a = &b;          // expected-warning {{address of stack memory is returned 
later}}
+  return a;        // expected-note {{returned here}}
+}
+
+S* trivial_class_uar () {
+  S *ptr;
+  S s;
+  ptr = &s;       // expected-warning {{address of stack memory is returned 
later}}
+  return ptr;     // expected-note {{returned here}}
+}
+
 
//===----------------------------------------------------------------------===//
 // Use-After-Scope & Use-After-Return (Return-Stack-Address) Combined
 // These are cases where the diagnostic kind is determined by location
@@ -696,10 +732,10 @@ void lifetimebound_return_reference() {
   {
     MyObj obj;
     View temp_v = obj;
-    const MyObj& ref = GetObject(temp_v);
-    ptr = &ref;
-  }
-  (void)*ptr;
+    const MyObj& ref = GetObject(temp_v);   
+    ptr = &ref;           // expected-warning {{object whose reference is 
captured does not live long enough}}
+  }                       // expected-note {{destroyed here}}
+  (void)*ptr;             // expected-note {{later used here}}
 }
 
 // FIXME: No warning for non gsl::Pointer types. Origin tracking is only 
supported for pointer types.
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp 
b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index 558a22af72572..a895475013c98 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -59,6 +59,7 @@ class LifetimeTestRunner {
     BuildOptions.setAllAlwaysAdd();
     BuildOptions.AddImplicitDtors = true;
     BuildOptions.AddTemporaryDtors = true;
+    BuildOptions.AddLifetime = true;
 
     // Run the main analysis.
     Analysis = std::make_unique<LifetimeSafetyAnalysis>(*AnalysisCtx, nullptr);
@@ -1307,6 +1308,42 @@ TEST_F(LifetimeAnalysisTest, LivenessOutsideLoop) {
   EXPECT_THAT(Origins({"p"}), MaybeLiveAt("p1"));
 }
 
+TEST_F(LifetimeAnalysisTest, TrivialDestructorsUAF) {
+  SetupTest(R"(
+    void target() {
+      int *ptr;
+      {
+          int s = 1;
+          ptr = &s;
+      }
+      POINT(p1);    
+      (void)*ptr;
+    }
+  )");
+  EXPECT_THAT(Origin("ptr"), HasLoansTo({"s"}, "p1"));
+  EXPECT_THAT(Origins({"ptr"}), MustBeLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, TrivialClassDestructorsUAF) {
+  SetupTest(R"(
+    class S {
+      View a, b;
+    };
+
+    void target() {
+      S* ptr;
+      {
+          S s;
+          ptr = &s;
+      }
+      POINT(p1);
+      (void)ptr;
+    }
+  )");
+  EXPECT_THAT(Origin("ptr"), HasLoansTo({"s"}, "p1"));
+  EXPECT_THAT(Origins({"ptr"}), MustBeLiveAt("p1"));
+}
+
 TEST_F(LifetimeAnalysisTest, SimpleReturnStackAddress) {
   SetupTest(R"(
     MyObj* target() {
@@ -1506,5 +1543,34 @@ TEST_F(LifetimeAnalysisTest, ReturnBeforeUseAfterScope) {
   EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p1"));
 }
 
+TEST_F(LifetimeAnalysisTest, TrivialDestructorsUAR) {
+  SetupTest(R"(
+    int* target() {
+      int s = 10;
+      int* p = &s;
+      POINT(p1);
+      return p;
+    }
+  )");
+  EXPECT_THAT("s", HasLiveLoanAtExpiry("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, TrivialClassDestructorsUAR) {
+  SetupTest(R"(
+    class S {
+      View a, b;
+    };
+
+    S* target() {
+      S *ptr;
+      S s;
+      ptr = &s;
+      POINT(p1);
+      return ptr;
+    }
+  )");
+  EXPECT_THAT("s", HasLiveLoanAtExpiry("p1"));
+}
+
 } // anonymous namespace
 } // namespace clang::lifetimes::internal

>From a88ac13cb38b7ceffa95d0ca97ed1404072423fe Mon Sep 17 00:00:00 2001
From: Kashika Akhouri <[email protected]>
Date: Thu, 20 Nov 2025 16:16:51 +0000
Subject: [PATCH 2/5] add EnableLifetimeSafetyAnalysis check

---
 clang/lib/Sema/AnalysisBasedWarnings.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp 
b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index fd6abc2874cb7..25799c20eb55e 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2987,7 +2987,6 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
   AC.getCFGBuildOptions().AddInitializers = true;
   AC.getCFGBuildOptions().AddImplicitDtors = true;
   AC.getCFGBuildOptions().AddTemporaryDtors = true;
-  AC.getCFGBuildOptions().AddLifetime = true;
   AC.getCFGBuildOptions().AddCXXNewAllocator = false;
   AC.getCFGBuildOptions().AddCXXDefaultInitExprInCtors = true;
 
@@ -3003,6 +3002,7 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
       P.enableConsumedAnalysis || EnableLifetimeSafetyAnalysis) {
     // Unreachable code analysis and thread safety require a linearized CFG.
     AC.getCFGBuildOptions().setAllAlwaysAdd();
+    AC.getCFGBuildOptions().AddLifetime = true;
   } else {
     AC.getCFGBuildOptions()
       .setAlwaysAdd(Stmt::BinaryOperatorClass)

>From 7df3c505cf12b5dd5280938e23ddf38260f8f272 Mon Sep 17 00:00:00 2001
From: Kashika Akhouri <[email protected]>
Date: Fri, 21 Nov 2025 09:20:19 +0000
Subject: [PATCH 3/5] Address comments and remove handleDestructor

---
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  3 +-
 .../LifetimeSafety/FactsGenerator.cpp         | 27 +-----------
 .../Sema/warn-lifetime-safety-dataflow.cpp    |  4 +-
 clang/test/Sema/warn-lifetime-safety.cpp      | 44 ++++++++++++++-----
 4 files changed, 39 insertions(+), 39 deletions(-)

diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 85192fdff5067..878cb90b685f9 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -50,8 +50,7 @@ class FactsGenerator : public 
ConstStmtVisitor<FactsGenerator> {
   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
 
 private:
-  void handleDestructor(const CFGAutomaticObjDtor &DtorOpt);
-  void handleTrivialDestructors(const CFGLifetimeEnds &LifetimeEnds);
+  void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);
 
   void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
 
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index fc92aba773187..7116bece1fe9f 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -63,12 +63,9 @@ void FactsGenerator::run() {
       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);
       else if (std::optional<CFGLifetimeEnds> LifetimeEnds =
                    Element.getAs<CFGLifetimeEnds>())
-        handleTrivialDestructors(*LifetimeEnds);
+        handleLifetimeEnds(*LifetimeEnds);
     }
     CurrentBlockFacts.append(EscapesInCurrentBlock.begin(),
                              EscapesInCurrentBlock.end());
@@ -232,31 +229,11 @@ void FactsGenerator::VisitMaterializeTemporaryExpr(
   killAndFlowOrigin(*MTE, *MTE->getSubExpr());
 }
 
-void FactsGenerator::handleDestructor(const CFGAutomaticObjDtor &DtorOpt) {
-  /// TODO: Handle loans to temporaries.
-  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::handleTrivialDestructors(
-    const CFGLifetimeEnds &LifetimeEnds) {
+void FactsGenerator::handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds) {
   for (const auto &Loan : FactMgr.getLoanMgr().getLoans()) {
     if (Loan.Path.D == LifetimeEnds.getVarDecl()) {
       CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
           Loan.ID, LifetimeEnds.getTriggerStmt()->getEndLoc()));
-      break;
     }
   }
 }
diff --git a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp 
b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
index 4d9d88fbc294d..7b6fc9201af6d 100644
--- a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp
@@ -59,7 +59,7 @@ int return_int_val() {
   int x = 10;
 // CHECK: Block B{{[0-9]+}}:
 // CHECK:   Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: {{[0-9]+}} (Expr: 
DeclRefExpr))
-// CHECK:   Expire (0 (Path: x))
+// CHECK:   Expire ([[L_X:[0-9]+]] (Path: x))
   return x;
 }
 // CHECK-NEXT: End of Block
@@ -86,7 +86,7 @@ void loan_expires_trivial() {
 // CHECK:   OriginFlow (Dest: [[O_ADDR_TRIVIAL_OBJ:[0-9]+]] (Expr: 
UnaryOperator), Src: [[O_DRE_TRIVIAL]] (Expr: DeclRefExpr))
   int* pTrivialObj = &trivial_obj;
 // CHECK:   OriginFlow (Dest: {{[0-9]+}} (Decl: pTrivialObj), Src: 
[[O_ADDR_TRIVIAL_OBJ]] (Expr: UnaryOperator))
-// CHECK:   Expire (0 (Path: trivial_obj))
+// CHECK:   Expire ([[L_TRIVIAL_OBJ:[0-9]+]] (Path: trivial_obj))
 // CHECK-NEXT: End of Block
 }
 
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index 88c8d5076e5b5..8a04a4686d667 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -12,7 +12,7 @@ struct [[gsl::Pointer()]] View {
   void use() const;
 };
 
-class S {
+class TriviallyDestructedClass {
   View a, b;
 };
 
@@ -400,7 +400,7 @@ void loan_from_previous_iteration(MyObj safe, bool 
condition) {
   }             // expected-note {{destroyed here}}
 }
 
-void trivial_uaf(){
+void trivial_int_uaf(){
   int * a;
     {
         int b = 1;
@@ -410,9 +410,9 @@ void trivial_uaf(){
 }
 
 void trivial_class_uaf() {
-  S* ptr;
+  TriviallyDestructedClass* ptr;
   {
-      S s;
+      TriviallyDestructedClass s;
       ptr = &s; // expected-warning {{object whose reference is captured does 
not live long enough}}
   }             // expected-note {{destroyed here}}
   (void)ptr;    // expected-note {{later used here}}
@@ -515,20 +515,43 @@ MyObj& reference_return_of_local() {
                     // expected-note@-1 {{returned here}}
 }
 
-int* trivial_uar() {
+int* trivial_int_uar() {
   int *a;
   int b = 1;
   a = &b;          // expected-warning {{address of stack memory is returned 
later}}
   return a;        // expected-note {{returned here}}
 }
 
-S* trivial_class_uar () {
-  S *ptr;
-  S s;
+TriviallyDestructedClass* trivial_class_uar () {
+  TriviallyDestructedClass *ptr;
+  TriviallyDestructedClass s;
   ptr = &s;       // expected-warning {{address of stack memory is returned 
later}}
   return ptr;     // expected-note {{returned here}}
 }
 
+// FIXME: No lifetime warning for this as no loans are issued for paramters
+const int& return_parameter(int a) { 
+  return a; 
+}
+
+// FIXME: No lifetime warning for this as no loans are issued for paramters
+int* return_pointer_to_parameter(int a) {
+    return &a;
+}
+
+const int& return_reference_to_parameter(int a)
+{
+    const int &b = a; 
+    return b;         // expected-warning {{address of stack memory is 
returned later}}
+                      // expected-note@-1 {{returned here}}
+}
+
+const int& get_ref_to_local() {
+    int local_var = 42;
+    return local_var;  // expected-warning {{address of stack memory is 
returned later}}
+                       // expected-note@-1 {{returned here}}
+}
+
 
//===----------------------------------------------------------------------===//
 // Use-After-Scope & Use-After-Return (Return-Stack-Address) Combined
 // These are cases where the diagnostic kind is determined by location
@@ -724,7 +747,8 @@ void lifetimebound_partial_safety(bool cond) {
   v.use();                // expected-note {{later used here}}
 }
 
-// FIXME: Creating reference from lifetimebound call doesn't propagate loans.
+// FIXME: Warning should be on the 'GetObject' call, not the assignment to 
'ptr'. 
+// The loan from the lifetimebound argument is not propagated to the call 
expression itself.
 const MyObj& GetObject(View v [[clang::lifetimebound]]);
 void lifetimebound_return_reference() {
   View v;
@@ -732,7 +756,7 @@ void lifetimebound_return_reference() {
   {
     MyObj obj;
     View temp_v = obj;
-    const MyObj& ref = GetObject(temp_v);   
+    const MyObj& ref = GetObject(temp_v);
     ptr = &ref;           // expected-warning {{object whose reference is 
captured does not live long enough}}
   }                       // expected-note {{destroyed here}}
   (void)*ptr;             // expected-note {{later used here}}

>From 3975956c12363b8c0831a9693572061821625963 Mon Sep 17 00:00:00 2001
From: Kashika Akhouri <[email protected]>
Date: Fri, 21 Nov 2025 11:46:11 +0000
Subject: [PATCH 4/5] Add comments in handleLifetimeEnds

---
 .../LifetimeSafety/FactsGenerator.cpp         | 10 ++++++++-
 clang/lib/Sema/AnalysisBasedWarnings.cpp      |  5 ++++-
 clang/test/Sema/warn-lifetime-safety.cpp      | 22 +++++++++----------
 3 files changed, 24 insertions(+), 13 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 7116bece1fe9f..15de702e21bce 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -230,8 +230,16 @@ void FactsGenerator::VisitMaterializeTemporaryExpr(
 }
 
 void FactsGenerator::handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds) {
+  /// TODO: Handle loans to temporaries.
+  const VarDecl *LifetimeEndsVD = LifetimeEnds.getVarDecl();
+  if (!LifetimeEndsVD)
+    return;
+  // Iterate through all loans to see if any expire.
   for (const auto &Loan : FactMgr.getLoanMgr().getLoans()) {
-    if (Loan.Path.D == LifetimeEnds.getVarDecl()) {
+    const AccessPath &LoanPath = Loan.Path;
+    // Check if the loan is for a stack variable and if that variable
+    // is the one being destructed.
+    if (LoanPath.D == LifetimeEndsVD) {
       CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
           Loan.ID, LifetimeEnds.getTriggerStmt()->getEndLoc()));
     }
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp 
b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 25799c20eb55e..bb512710c8a35 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2992,6 +2992,10 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
 
   bool EnableLifetimeSafetyAnalysis = S.getLangOpts().EnableLifetimeSafety;
 
+  if (EnableLifetimeSafetyAnalysis) {
+    AC.getCFGBuildOptions().AddLifetime = true;
+  }
+
   // Force that certain expressions appear as CFGElements in the CFG.  This
   // is used to speed up various analyses.
   // FIXME: This isn't the right factoring.  This is here for initial
@@ -3002,7 +3006,6 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
       P.enableConsumedAnalysis || EnableLifetimeSafetyAnalysis) {
     // Unreachable code analysis and thread safety require a linearized CFG.
     AC.getCFGBuildOptions().setAllAlwaysAdd();
-    AC.getCFGBuildOptions().AddLifetime = true;
   } else {
     AC.getCFGBuildOptions()
       .setAlwaysAdd(Stmt::BinaryOperatorClass)
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index 8a04a4686d667..e80a05860389c 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -400,13 +400,13 @@ void loan_from_previous_iteration(MyObj safe, bool 
condition) {
   }             // expected-note {{destroyed here}}
 }
 
-void trivial_int_uaf(){
+void trivial_int_uaf() {
   int * a;
-    {
-        int b = 1;
-        a = &b; // expected-warning {{object whose reference is captured does 
not live long enough}}
-    }           // expected-note {{destroyed here}}
-    (void)*a;   // expected-note {{later used here}}
+  {
+      int b = 1;
+      a = &b;  // expected-warning {{object whose reference is captured does 
not live long enough}}
+  }            // expected-note {{destroyed here}}
+  (void)*a;    // expected-note {{later used here}}
 }
 
 void trivial_class_uaf() {
@@ -529,12 +529,12 @@ TriviallyDestructedClass* trivial_class_uar () {
   return ptr;     // expected-note {{returned here}}
 }
 
-// FIXME: No lifetime warning for this as no loans are issued for paramters
+// FIXME: No lifetime warning for this as no expire facts are generated for 
parameters
 const int& return_parameter(int a) { 
   return a; 
 }
 
-// FIXME: No lifetime warning for this as no loans are issued for paramters
+// FIXME: No lifetime warning for this as no expire facts are generated for 
parameters
 int* return_pointer_to_parameter(int a) {
     return &a;
 }
@@ -547,9 +547,9 @@ const int& return_reference_to_parameter(int a)
 }
 
 const int& get_ref_to_local() {
-    int local_var = 42;
-    return local_var;  // expected-warning {{address of stack memory is 
returned later}}
-                       // expected-note@-1 {{returned here}}
+    int a = 42;
+    return a;         // expected-warning {{address of stack memory is 
returned later}}
+                      // expected-note@-1 {{returned here}}
 }
 
 
//===----------------------------------------------------------------------===//

>From d5aad764152c1c3c61e2f87c10f2b02478d7440e Mon Sep 17 00:00:00 2001
From: Kashika Akhouri <[email protected]>
Date: Fri, 21 Nov 2025 11:49:52 +0000
Subject: [PATCH 5/5] Addressing nit comments

---
 clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp | 3 +--
 clang/lib/Sema/AnalysisBasedWarnings.cpp             | 3 +--
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 15de702e21bce..f7be472ed15b5 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -239,10 +239,9 @@ void FactsGenerator::handleLifetimeEnds(const 
CFGLifetimeEnds &LifetimeEnds) {
     const AccessPath &LoanPath = Loan.Path;
     // Check if the loan is for a stack variable and if that variable
     // is the one being destructed.
-    if (LoanPath.D == LifetimeEndsVD) {
+    if (LoanPath.D == LifetimeEndsVD)
       CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
           Loan.ID, LifetimeEnds.getTriggerStmt()->getEndLoc()));
-    }
   }
 }
 
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp 
b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index bb512710c8a35..43d2b9a829545 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2992,9 +2992,8 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
 
   bool EnableLifetimeSafetyAnalysis = S.getLangOpts().EnableLifetimeSafety;
 
-  if (EnableLifetimeSafetyAnalysis) {
+  if (EnableLifetimeSafetyAnalysis)
     AC.getCFGBuildOptions().AddLifetime = true;
-  }
 
   // Force that certain expressions appear as CFGElements in the CFG.  This
   // is used to speed up various analyses.

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

Reply via email to