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

>From 49b12d8f75a72821a29c953b9d92aae7fdfc412d Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <[email protected]>
Date: Wed, 14 Jan 2026 09:15:58 +0000
Subject: [PATCH] [LifetimeSafety] Test lifetime safety on stmt-local analysis
 test suite

---
 clang/test/Sema/Inputs/lifetime-analysis.h    |  10 +-
 .../Sema/warn-lifetime-analysis-nocfg.cpp     | 278 ++++++++++++++----
 2 files changed, 228 insertions(+), 60 deletions(-)

diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h 
b/clang/test/Sema/Inputs/lifetime-analysis.h
index 2072e4603cead..94220b15cea7a 100644
--- a/clang/test/Sema/Inputs/lifetime-analysis.h
+++ b/clang/test/Sema/Inputs/lifetime-analysis.h
@@ -42,6 +42,7 @@ struct vector {
   iterator end();
   const T *data() const;
   vector();
+  ~vector();
   vector(initializer_list<T> __l,
          const Alloc& alloc = Alloc());
 
@@ -78,6 +79,7 @@ template<typename T>
 struct basic_string {
   basic_string();
   basic_string(const T *);
+  ~basic_string();
   const T *c_str() const;
   operator basic_string_view<T> () const;
   using const_iterator = iter<T>;
@@ -87,6 +89,7 @@ using string = basic_string<char>;
 
 template<typename T>
 struct unique_ptr {
+  ~unique_ptr();
   T &operator*();
   T *get() const;
 };
@@ -96,6 +99,8 @@ struct optional {
   optional();
   optional(const T&);
 
+  ~optional();
+
   template<typename U = T>
   optional(U&& t);
 
@@ -116,7 +121,10 @@ struct stack {
   T &top();
 };
 
-struct any {};
+struct any {
+  any();
+  ~any();
+};
 
 template<typename T>
 T any_cast(const any& operand);
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp 
b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 7634dbf2f6733..86d7d18a02087 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -1,7 +1,12 @@
 // RUN: %clang_cc1 -fsyntax-only -Wdangling -Wdangling-field 
-Wreturn-stack-address -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety 
-Wexperimental-lifetime-safety -Wno-dangling -verify=cfg %s
+
 #include "Inputs/lifetime-analysis.h"
+
 struct [[gsl::Owner(int)]] MyIntOwner {
   MyIntOwner();
+  // TODO: Do this behind a macro and run tests without this dtor to verify 
trivial dtor cases.
+  ~MyIntOwner();
   int &operator*();
 };
 
@@ -36,6 +41,8 @@ struct [[gsl::Owner(long)]] MyLongOwnerWithConversion {
   long *releaseAsRawPointer();
 };
 
+template<class... T> void use(T... arg);
+
 void danglingHeapObject() {
   new MyLongPointerFromConversion(MyLongOwnerWithConversion{}); // 
expected-warning {{object backing the pointer will be destroyed at the end of 
the full-expression}}
   new MyIntPointer(MyIntOwner{}); // expected-warning {{object backing the 
pointer will be destroyed at the end of the full-expression}}
@@ -89,22 +96,26 @@ MyIntPointer returningLocalPointer() {
 
 MyIntPointer daglingGslPtrFromLocalOwner() {
   MyIntOwner localOwner;
-  return localOwner; // expected-warning {{address of stack memory associated 
with local variable 'localOwner' returned}}
+  return localOwner; // expected-warning {{address of stack memory associated 
with local variable 'localOwner' returned}} \
+                     // cfg-warning {{address of stack memory is returned 
later}} cfg-note {{returned here}}
 }
 
 MyLongPointerFromConversion daglingGslPtrFromLocalOwnerConv() {
   MyLongOwnerWithConversion localOwner;
-  return localOwner; // expected-warning {{address of stack memory associated 
with local variable 'localOwner' returned}}
+  return localOwner; // expected-warning {{address of stack memory associated 
with local variable 'localOwner' returned}} \
+                     // cfg-warning {{address of stack memory is returned 
later}} cfg-note {{returned here}}
 }
 
 MyIntPointer danglingGslPtrFromTemporary() {
-  return MyIntOwner{}; // expected-warning {{returning address of local 
temporary object}}
+  return MyIntOwner{}; // expected-warning {{returning address of local 
temporary object}} \
+                       // cfg-warning {{address of stack memory is returned 
later}} cfg-note {{returned here}}
 }
 
 MyIntOwner makeTempOwner();
 
 MyIntPointer danglingGslPtrFromTemporary2() {
-  return makeTempOwner(); // expected-warning {{returning address of local 
temporary object}}
+  return makeTempOwner(); // expected-warning {{returning address of local 
temporary object}} \
+                          // cfg-warning {{address of stack memory is returned 
later}} cfg-note {{returned here}}
 }
 
 MyLongPointerFromConversion danglingGslPtrFromTemporaryConv() {
@@ -120,14 +131,31 @@ MyIntPointer global;
 MyLongPointerFromConversion global2;
 
 void initLocalGslPtrWithTempOwner() {
-  MyIntPointer p = MyIntOwner{}; // expected-warning {{object backing the 
pointer will be destroyed at the end of the full-expression}}
-  MyIntPointer pp = p = MyIntOwner{}; // expected-warning {{object backing the 
pointer 'p' will be}}
-  p = MyIntOwner{}; // expected-warning {{object backing the pointer 'p' }}
+  MyIntPointer p = MyIntOwner{}; // expected-warning {{object backing the 
pointer will be destroyed at the end of the full-expression}} \
+                                 // cfg-warning {{object whose reference is 
captured does not live long enough}} cfg-note {{destroyed here}}
+  use(p);                        // cfg-note {{later used here}}
+
+  MyIntPointer pp = p = MyIntOwner{}; // expected-warning {{object backing the 
pointer 'p' will be}} \
+                                      // cfg-warning {{object whose reference 
is captured does not live long enough}} cfg-note {{destroyed here}}
+  use(p, pp);                         // cfg-note {{later used here}}
+
+  p = MyIntOwner{}; // expected-warning {{object backing the pointer 'p' }} \
+                    // cfg-warning {{object whose reference is captured does 
not live long enough}} cfg-note {{destroyed here}}
+  use(p);           // cfg-note {{later used here}}
+
   pp = p; // no warning
-  global = MyIntOwner{}; // expected-warning {{object backing the pointer 
'global' }}
+  use(p, pp);
+
+  global = MyIntOwner{}; // expected-warning {{object backing the pointer 
'global' }} \
+                         // cfg-warning {{object whose reference is captured 
does not live long enough}} cfg-note {{destroyed here}}
+  use(global);           // cfg-note {{later used here}}
+
   MyLongPointerFromConversion p2 = MyLongOwnerWithConversion{}; // 
expected-warning {{object backing the pointer will be destroyed at the end of 
the full-expression}}
+  use(p2);
+
   p2 = MyLongOwnerWithConversion{}; // expected-warning {{object backing the 
pointer 'p2' }}
   global2 = MyLongOwnerWithConversion{}; // expected-warning {{object backing 
the pointer 'global2' }}
+  use(global2, p2);
 }
 
 
@@ -138,20 +166,24 @@ struct Unannotated {
 };
 
 void modelIterators() {
-  std::vector<int>::iterator it = std::vector<int>().begin(); // 
expected-warning {{object backing the pointer will be destroyed at the end of 
the full-expression}}
-  (void)it;
+  std::vector<int>::iterator it = std::vector<int>().begin(); // 
expected-warning {{object backing the pointer will be destroyed at the end of 
the full-expression}} \
+                                                              // cfg-warning 
{{object whose reference is captured does not live long enough}} cfg-note 
{{destroyed here}}
+  (void)it; // cfg-note {{later used here}}
 }
 
 std::vector<int>::iterator modelIteratorReturn() {
-  return std::vector<int>().begin(); // expected-warning {{returning address 
of local temporary object}}
+  return std::vector<int>().begin(); // expected-warning {{returning address 
of local temporary object}} \
+                                     // cfg-warning {{address of stack memory 
is returned later}} cfg-note {{returned here}}
 }
 
 const int *modelFreeFunctions() {
-  return std::data(std::vector<int>()); // expected-warning {{returning 
address of local temporary object}}
+  return std::data(std::vector<int>()); // expected-warning {{returning 
address of local temporary object}} \
+                                        // cfg-warning {{address of stack 
memory is returned later}} cfg-note {{returned here}}
 }
 
 int &modelAnyCast() {
-  return std::any_cast<int&>(std::any{}); // expected-warning {{returning 
reference to local temporary object}}
+  return std::any_cast<int&>(std::any{}); // expected-warning {{returning 
reference to local temporary object}} \
+                                          // cfg-warning {{address of stack 
memory is returned later}} cfg-note {{returned here}}
 }
 
 int modelAnyCast2() {
@@ -164,35 +196,46 @@ int modelAnyCast3() {
 
 const char *danglingRawPtrFromLocal() {
   std::basic_string<char> s;
-  return s.c_str(); // expected-warning {{address of stack memory associated 
with local variable 's' returned}}
+  return s.c_str(); // expected-warning {{address of stack memory associated 
with local variable 's' returned}} \
+                    // cfg-warning {{address of stack memory is returned 
later}} cfg-note {{returned here}}
 }
 
 int &danglingRawPtrFromLocal2() {
   std::optional<int> o;
-  return o.value(); // expected-warning {{reference to stack memory associated 
with local variable 'o' returned}}
+  return o.value(); // expected-warning {{reference to stack memory associated 
with local variable 'o' returned}} \
+                    // cfg-warning {{address of stack memory is returned 
later}} cfg-note {{returned here}}
 }
 
 int &danglingRawPtrFromLocal3() {
   std::optional<int> o;
-  return *o; // expected-warning {{reference to stack memory associated with 
local variable 'o' returned}}
+  return *o; // expected-warning {{reference to stack memory associated with 
local variable 'o' returned}} \
+             // cfg-warning {{address of stack memory is returned later}} 
cfg-note {{returned here}}
 }
 
 // GH100384
 std::string_view containerWithAnnotatedElements() {
-  std::string_view c1 = std::vector<std::string>().at(0); // expected-warning 
{{object backing the pointer will be destroyed at the end of the 
full-expression}}
-  c1 = std::vector<std::string>().at(0); // expected-warning {{object backing 
the pointer}}
+  std::string_view c1 = std::vector<std::string>().at(0); // expected-warning 
{{object backing the pointer will be destroyed at the end of the 
full-expression}} \
+                                                          // cfg-warning 
{{object whose reference is captured does not live long enough}} cfg-note 
{{destroyed here}}
+  use(c1);                                                // cfg-note {{later 
used here}}
+
+  c1 = std::vector<std::string>().at(0); // expected-warning {{object backing 
the pointer}} \
+                                         // cfg-warning {{object whose 
reference is captured does not live long enough}} cfg-note {{destroyed here}}
+  use(c1);                               // cfg-note {{later used here}}
 
   // no warning on constructing from gsl-pointer
   std::string_view c2 = std::vector<std::string_view>().at(0);
+  use(c2);
 
   std::vector<std::string> local;
-  return local.at(0); // expected-warning {{address of stack memory associated 
with local variable}}
+  return local.at(0); // expected-warning {{address of stack memory associated 
with local variable}} \
+                      // cfg-warning {{address of stack memory is returned 
later}} cfg-note {{returned here}}
 }
 
 std::string_view localUniquePtr(int i) {
   std::unique_ptr<std::string> c1;
   if (i)
-    return *c1; // expected-warning {{address of stack memory associated with 
local variable}}
+    return *c1; // expected-warning {{address of stack memory associated with 
local variable}} \
+                // cfg-warning {{address of stack memory is returned later}} 
cfg-note {{returned here}}
   std::unique_ptr<std::string_view> c2;
   return *c2; // expect no-warning.
 }
@@ -200,30 +243,53 @@ std::string_view localUniquePtr(int i) {
 std::string_view localOptional(int i) {
   std::optional<std::string> o;
   if (i)
-    return o.value(); // expected-warning {{address of stack memory associated 
with local variable}}
+    return o.value(); // expected-warning {{address of stack memory associated 
with local variable}} \
+                      // cfg-warning {{address of stack memory is returned 
later}} cfg-note {{returned here}}
   std::optional<std::string_view> abc;
   return abc.value(); // expect no warning
 }
 
 const char *danglingRawPtrFromTemp() {
-  return std::basic_string<char>().c_str(); // expected-warning {{returning 
address of local temporary object}}
+  return std::basic_string<char>().c_str(); // expected-warning {{returning 
address of local temporary object}} \
+                                            // cfg-warning {{address of stack 
memory is returned later}} cfg-note {{returned here}}
 }
 
 std::unique_ptr<int> getUniquePtr();
 
 int *danglingUniquePtrFromTemp() {
-  return getUniquePtr().get(); // expected-warning {{returning address of 
local temporary object}}
+  return getUniquePtr().get(); // expected-warning {{returning address of 
local temporary object}} \
+                               // cfg-warning {{address of stack memory is 
returned later}} cfg-note {{returned here}}
 }
 
 int *danglingUniquePtrFromTemp2() {
-  return std::unique_ptr<int>().get(); // expected-warning {{returning address 
of local temporary object}}
+  return std::unique_ptr<int>().get(); // expected-warning {{returning address 
of local temporary object}} \
+                                       // cfg-warning {{address of stack 
memory is returned later}} cfg-note {{returned here}}
+}
+
+const int& danglingRefToOptionalFromTemp3() {
+  return std::optional<int>().value(); // expected-warning {{returning 
reference to local temporary object}} \
+                                       // cfg-warning {{address of stack 
memory is returned later}} cfg-note {{returned here}}
+}
+
+std::optional<std::string> getTempOptStr();
+
+std::string_view danglingRefToOptionalFromTemp4() {
+  return getTempOptStr().value(); // expected-warning {{returning address of 
local temporary object}} \
+                                  // cfg-warning {{address of stack memory is 
returned later}} cfg-note {{returned here}}
 }
 
 void danglingReferenceFromTempOwner() {
+  // FIXME: Detect this using the CFG-based lifetime analysis.
+  //        https://github.com/llvm/llvm-project/issues/175893
   int &&r = *std::optional<int>();          // expected-warning {{object 
backing the pointer will be destroyed at the end of the full-expression}}
   int &&r2 = *std::optional<int>(5);        // expected-warning {{object 
backing the pointer will be destroyed at the end of the full-expression}}
   int &&r3 = std::optional<int>(5).value(); // expected-warning {{object 
backing the pointer will be destroyed at the end of the full-expression}}
   int &r4 = std::vector<int>().at(3);       // expected-warning {{object 
backing the pointer will be destroyed at the end of the full-expression}}
+  use(r, r2, r3, r4);
+
+  std::string_view sv = *getTempOptStr();  // expected-warning {{object 
backing the pointer will be destroyed at the end of the full-expression}} \
+                                           // cfg-warning {{object whose 
reference is captured does not live long enough}} cfg-note {{destroyed here}}
+  use(sv);                                 // cfg-note {{later used here}}
 }
 
 std::vector<int> getTempVec();
@@ -232,6 +298,7 @@ std::optional<std::vector<int>> getTempOptVec();
 void testLoops() {
   for (auto i : getTempVec()) // ok
     ;
+  // FIXME: Detect this using the CFG-based lifetime analysis.
   for (auto i : *getTempOptVec()) // expected-warning {{object backing the 
pointer will be destroyed at the end of the full-expression}}
     ;
 }
@@ -243,14 +310,16 @@ int &usedToBeFalsePositive(std::vector<int> &v) {
 }
 
 int &doNotFollowReferencesForLocalOwner() {
+// Warning caught by CFG analysis.
   std::unique_ptr<int> localOwner;
-  int &p = *localOwner.get();
-  // In real world code localOwner is usually moved here.
-  return p; // ok
+  int &p = *localOwner // cfg-warning {{address of stack memory is returned 
later}}
+            .get();
+  return p; // cfg-note {{returned here}}
 }
 
 const char *trackThroughMultiplePointer() {
-  return std::basic_string_view<char>(std::basic_string<char>()).begin(); // 
expected-warning {{returning address of local temporary object}}
+  return std::basic_string_view<char>(std::basic_string<char>()).begin(); // 
expected-warning {{returning address of local temporary object}} \
+         // cfg-warning {{address of stack memory is returned later}} cfg-note 
{{returned here}}
 }
 
 struct X {
@@ -289,13 +358,20 @@ void handleGslPtrInitsThroughReference2() {
 
 void handleTernaryOperator(bool cond) {
     std::basic_string<char> def;
+    // FIXME: Detect this using the CFG-based lifetime analysis.
     std::basic_string_view<char> v = cond ? def : ""; // expected-warning 
{{object backing the pointer will be destroyed at the end of the 
full-expression}}
+    use(v);
 }
 
 std::string operator+(std::string_view s1, std::string_view s2);
 void danglingStringviewAssignment(std::string_view a1, std::string_view a2) {
-  a1 = std::string(); // expected-warning {{object backing}}
-  a2 = a1 + a1; // expected-warning {{object backing}}
+  a1 = std::string(); // expected-warning {{object backing}} \
+                      // cfg-warning {{object whose reference is captured does 
not live long enough}} cfg-note {{destroyed here}}
+  use(a1);            // cfg-note {{later used here}}
+
+  a2 = a1 + a1; // expected-warning {{object backing}} \
+                // cfg-warning {{object whose reference is captured does not 
live long enough}} cfg-note {{destroyed here}}
+  use(a2);      // cfg-note {{later used here}}
 }
 
 std::reference_wrapper<int> danglingPtrFromNonOwnerLocal() {
@@ -430,9 +506,11 @@ struct [[gsl::Pointer]] S {
 };
 
 S test(std::vector<int> a) {
-  return S(a);  // expected-warning {{address of stack memory associated with}}
+  return S(a);  // expected-warning {{address of stack memory associated 
with}} \
+                // cfg-warning {{address of stack memory is returned later}} 
cfg-note {{returned here}}
 }
 
+// FIXME: Detect this using the CFG-based lifetime analysis (global 
initialisation).
 auto s = S(std::vector<int>()); // expected-warning {{temporary whose address 
is used as value of local variable}}
 
 // Verify no regression on the follow case.
@@ -447,6 +525,9 @@ struct FooView {
   FooView(const Foo& foo [[clang::lifetimebound]]);
 };
 FooView test3(int i, std::optional<Foo> a) {
+  // FIXME: Detect this using the CFG-based lifetime analysis.
+  //        Origin tracking for non-pointers type retured from lifetimebound 
fn is missing.
+  //        https://github.com/llvm/llvm-project/issues/163600
   if (i)
     return *a; // expected-warning {{address of stack memory}}
   return a.value(); // expected-warning {{address of stack memory}}
@@ -459,13 +540,19 @@ struct UrlAnalyzed {
 };
 std::string StrCat(std::string_view, std::string_view);
 void test1() {
+  // FIXME: Detect this using the CFG-based lifetime analysis.
+  //        Origin tracking for non-pointers type retured from lifetimebound 
fn is missing.
+  //        https://github.com/llvm/llvm-project/issues/163600
   UrlAnalyzed url(StrCat("abc", "bcd")); // expected-warning {{object backing 
the pointer will be destroyed}}
+  use(url);
 }
 
 std::string_view ReturnStringView(std::string_view abc 
[[clang::lifetimebound]]);
 
 void test() {
-  std::string_view svjkk1 = ReturnStringView(StrCat("bar", "x")); // 
expected-warning {{object backing the pointer will be destroyed at the end of 
the full-expression}}
+  std::string_view svjkk1 = ReturnStringView(StrCat("bar", "x")); // 
expected-warning {{object backing the pointer will be destroyed at the end of 
the full-expression}} \
+                                                                  // 
cfg-warning {{object whose reference is captured does not live long enough}} 
cfg-note {{destroyed here}}
+  use(svjkk1);                                                    // cfg-note 
{{later used here}}
 }
 } // namespace GH100549
 
@@ -473,6 +560,8 @@ namespace GH108272 {
 template <typename T>
 struct [[gsl::Owner]] StatusOr {
   const T &value() [[clang::lifetimebound]];
+  // TODO: Do this behind a macro and run tests without this dtor to verify 
trivial dtor cases.
+  ~StatusOr();
 };
 
 template <typename V>
@@ -499,23 +588,34 @@ std::string_view test2() {
   StatusOr<Wrapper2<std::string_view>> k;
   // We expect dangling issues as the conversion operator is lifetimebound。
   std::string_view bad = StatusOr<Wrapper2<std::string_view>>().value(); // 
expected-warning {{temporary whose address is used as value of}}
-  return k.value(); // expected-warning {{address of stack memory associated}}
+  
+  return k.value(); // expected-warning {{address of stack memory associated}} 
\
+                    // cfg-warning {{address of stack memory is returned 
later}} cfg-note {{returned here}}
 }
 } // namespace GH108272
 
 namespace GH100526 {
+// FIXME: Detect this using the CFG-based lifetime analysis.
+//        Container of pointers
+//        https://github.com/llvm/llvm-project/issues/175025
 void test() {
   std::vector<std::string_view> v1({std::string()}); // expected-warning 
{{object backing the pointer will be destroyed at the end}}
+  use(v1);
+
   std::vector<std::string_view> v2({
     std::string(), // expected-warning {{object backing the pointer will be 
destroyed at the end}}
     std::string_view()
   });
+  use(v2);
+
   std::vector<std::string_view> v3({
     std::string_view(),
     std::string()  // expected-warning {{object backing the pointer will be 
destroyed at the end}}
   });
+  use(v3);
 
   std::optional<std::string_view> o1 = std::string(); // expected-warning 
{{object backing the pointer}}
+  use(o1);
 
   std::string s;
   // This is a tricky use-after-free case, what it does:
@@ -525,10 +625,12 @@ void test() {
   std::optional<std::string_view> o2 = std::make_optional(s); // 
expected-warning {{object backing the pointer}}
   std::optional<std::string_view> o3 = std::optional<std::string>(s); // 
expected-warning {{object backing the pointer}}
   std::optional<std::string_view> o4 = std::optional<std::string_view>(s);
+  use(o2, o3, o4);
 
   // FIXME: should work for assignment cases
   v1 = {std::string()};
   o1 = std::string();
+  use(o1, v1);
 
   // no warning on copying pointers.
   std::vector<std::string_view> n1 = {std::string_view()};
@@ -538,6 +640,7 @@ void test() {
   const char* b = "";
   std::optional<std::string_view> n5 = std::make_optional(b);
   std::optional<std::string_view> n6 = std::make_optional("test");
+  use(n1, n2, n3, n4, n5, n6);
 }
 
 std::vector<std::string_view> test2(int i) {
@@ -618,7 +721,8 @@ std::string_view test5() {
 Span<int*> test6(std::vector<int*> v) {
   Span<int *> dangling = std::vector<int*>(); // expected-warning {{object 
backing the pointer}}
   dangling = std::vector<int*>(); // expected-warning {{object backing the 
pointer}}
-  return v; // expected-warning {{address of stack memory}}
+  return v; // expected-warning {{address of stack memory}} \
+            // cfg-warning {{address of stack memory is returned later}} 
cfg-note {{returned here}}
 }
 
 /////// From Owner<Owner<Pointer>> ///////
@@ -637,7 +741,8 @@ std::vector<int*> test8(StatusOr<std::vector<int*>> aa) {
 
 // Pointer<Pointer> from Owner<Owner<Pointer>>
 Span<int*> test9(StatusOr<std::vector<int*>> aa) {
-  return aa.valueLB(); // expected-warning {{address of stack memory 
associated}}
+  return aa.valueLB(); // expected-warning {{address of stack memory 
associated}} \
+                       // cfg-warning {{address of stack memory is returned 
later}} cfg-note {{returned here}}
   return aa.valueNoLB(); // OK.
 }
 
@@ -645,7 +750,8 @@ Span<int*> test9(StatusOr<std::vector<int*>> aa) {
 
 // Pointer<Owner>> from Owner<Owner>
 Span<std::string> test10(StatusOr<std::vector<std::string>> aa) {
-  return aa.valueLB(); // expected-warning {{address of stack memory}}
+  return aa.valueLB(); // expected-warning {{address of stack memory}} \
+                       // cfg-warning {{address of stack memory is returned 
later}} cfg-note {{returned here}}
   return aa.valueNoLB(); // OK.
 }
 
@@ -659,7 +765,8 @@ Span<std::string> test11(StatusOr<Span<std::string>> aa) {
 
 // Lifetimebound and gsl::Pointer.
 const int& test12(Span<int> a) {
-  return a.getFieldLB(); // expected-warning {{reference to stack memory 
associated}}
+  return a.getFieldLB(); // expected-warning {{reference to stack memory 
associated}} \
+                         // cfg-warning {{address of stack memory is returned 
later}} cfg-note {{returned here}}
   return a.getFieldNoLB(); // OK.
 }
 
@@ -667,7 +774,9 @@ void test13() {
   // FIXME: RHS is Owner<Pointer>, we skip this case to avoid false positives.
   std::optional<Span<int*>> abc = std::vector<int*>{};
 
+  // FIXME: Detect this using the CFG-based lifetime analysis (container of 
pointer).
   std::optional<Span<int>> t = std::vector<int> {}; // expected-warning 
{{object backing the pointer will be destroyed}}
+  use(t);
 }
 
 } // namespace GH100526
@@ -684,6 +793,7 @@ struct BB {
 template <typename T>
 class set {
 public:
+  ~set();
   typedef typename BB<T>::iterator iterator;
   iterator begin() const;
 };
@@ -692,6 +802,7 @@ namespace GH118064{
 
 void test() {
   auto y = std::set<int>{}.begin(); // expected-warning {{object backing the 
pointer}}
+  use(y);
 }
 } // namespace GH118064
 
@@ -703,22 +814,44 @@ std::string_view TakeSv(std::string_view abc 
[[clang::lifetimebound]]);
 std::string_view TakeStrRef(const std::string& abc [[clang::lifetimebound]]);
 std::string_view TakeStr(std::string abc [[clang::lifetimebound]]);
 
-std::string_view test1() {
-  std::string_view t1 = Ref(std::string()); // expected-warning {{object 
backing}}
-  t1 = Ref(std::string()); // expected-warning {{object backing}}
-  return Ref(std::string()); // expected-warning {{returning address}}
-
-  std::string_view t2 = TakeSv(std::string()); // expected-warning {{object 
backing}}
-  t2 = TakeSv(std::string()); // expected-warning {{object backing}}
-  return TakeSv(std::string()); // expected-warning {{returning address}}
-
-  std::string_view t3 = TakeStrRef(std::string()); // expected-warning 
{{temporary}}
-  t3 = TakeStrRef(std::string()); // expected-warning {{object backing}}
-  return TakeStrRef(std::string()); // expected-warning {{returning address}}
-
-
+std::string_view test1_1() {
+  std::string_view t1 = Ref(std::string()); // expected-warning {{object 
backing}} \
+                                            // cfg-warning {{object whose 
reference is captured does not live long enough}} cfg-note {{destroyed here}}
+  use(t1);                                  // cfg-note {{later used here}}
+  t1 = Ref(std::string()); // expected-warning {{object backing}} \
+                           // cfg-warning {{object whose reference is captured 
does not live long enough}} cfg-note {{destroyed here}}
+  use(t1);                 // cfg-note {{later used here}}
+  return Ref(std::string()); // expected-warning {{returning address}} \
+                             // cfg-warning {{address of stack memory is 
returned later}} cfg-note {{returned here}}
+}
+
+std::string_view test1_2() {
+  std::string_view t2 = TakeSv(std::string()); // expected-warning {{object 
backing}} \
+                                            // cfg-warning {{object whose 
reference is captured does not live long enough}} cfg-note {{destroyed here}}
+  use(t2);                                  // cfg-note {{later used here}}
+  t2 = TakeSv(std::string()); // expected-warning {{object backing}} \
+                              // cfg-warning {{object whose reference is 
captured does not live long enough}} cfg-note {{destroyed here}}
+  use(t2);                    // cfg-note {{later used here}}
+
+  return TakeSv(std::string()); // expected-warning {{returning address}} \
+                                // cfg-warning {{address of stack memory is 
returned later}} cfg-note {{returned here}}
+}
+
+std::string_view test1_3() {
+  std::string_view t3 = TakeStrRef(std::string()); // expected-warning 
{{temporary}} \
+                                                   // cfg-warning {{object 
whose reference is captured does not live long enough}} cfg-note {{destroyed 
here}}
+  use(t3);                                         // cfg-note {{later used 
here}}
+  t3 = TakeStrRef(std::string()); // expected-warning {{object backing}} \
+                                  // cfg-warning {{object whose reference is 
captured does not live long enough}} cfg-note {{destroyed here}}
+  use(t3);                        // cfg-note {{later used here}}
+  return TakeStrRef(std::string()); // expected-warning {{returning address}} \
+                                    // cfg-warning {{address of stack memory 
is returned later}} cfg-note {{returned here}}
+}
+
+std::string_view test1_4() {
   std::string_view t4 = TakeStr(std::string());
   t4 = TakeStr(std::string());
+  use(t4);
   return TakeStr(std::string());
 }
 
@@ -726,35 +859,56 @@ template <typename T>
 struct Foo {
   const T& get() const [[clang::lifetimebound]];
   const T& getNoLB() const;
+  // TODO: Do this behind a macro and run tests without this dtor to verify 
trivial dtor cases.
+  ~Foo();
 };
-std::string_view test2(Foo<std::string> r1, Foo<std::string_view> r2) {
-  std::string_view t1 = Foo<std::string>().get(); // expected-warning {{object 
backing}}
-  t1 = Foo<std::string>().get(); // expected-warning {{object backing}}
-  return r1.get(); // expected-warning {{address of stack}}
-
+std::string_view test2_1(Foo<std::string> r1, Foo<std::string_view> r2) {
+  std::string_view t1 = Foo<std::string>().get(); // expected-warning {{object 
backing}} \
+                                                  // cfg-warning {{object 
whose reference is captured does not live long enough}} cfg-note {{destroyed 
here}}
+  use(t1);                                        // cfg-note {{later used 
here}}
+  t1 = Foo<std::string>().get(); // expected-warning {{object backing}} \
+                                 // cfg-warning {{object whose reference is 
captured does not live long enough}} cfg-note {{destroyed here}}
+  use(t1);                       // cfg-note {{later used here}}
+  return r1.get(); // expected-warning {{address of stack}} \
+                   // cfg-warning {{address of stack memory is returned 
later}} cfg-note {{returned here}}
+}
+std::string_view test2_2(Foo<std::string> r1, Foo<std::string_view> r2) {
   std::string_view t2 = Foo<std::string_view>().get();
+  use(t2);
   t2 = Foo<std::string_view>().get();
+  use(t2);
   return r2.get();
-
+}
+std::string_view test2_3(Foo<std::string> r1, Foo<std::string_view> r2) {
   // no warning on no-LB-annotated method.
   std::string_view t3 = Foo<std::string>().getNoLB();
+  use(t3);
   t3 = Foo<std::string>().getNoLB();
+  use(t3);
   return r1.getNoLB();
 }
 
-struct Bar {};
+struct Bar {
+  // TODO: Do this behind a macro and run tests without this dtor to verify 
trivial dtor cases.
+  ~Bar();
+};
 struct [[gsl::Pointer]] Pointer {
   Pointer(const Bar & bar [[clang::lifetimebound]]);
 };
 Pointer test3(Bar bar) {
+  // FIXME: Detect this using the CFG-based lifetime analysis (constructor of 
a pointer).
+  //        https://github.com/llvm/llvm-project/issues/175898
   Pointer p = Pointer(Bar()); // expected-warning {{temporary}}
+  use(p);
   p = Pointer(Bar()); // expected-warning {{object backing}}
+  use(p);
   return bar; // expected-warning {{address of stack}}
 }
 
 template<typename T>
 struct MySpan {
   MySpan(const std::vector<T>& v);
+  ~MySpan();
   using iterator = std::iterator<T>;
   iterator begin() const [[clang::lifetimebound]];
 };
@@ -772,8 +926,10 @@ void test4() {
   // constraints, we do not.
   const int& t4 = *MySpan<int>(std::vector<int>{}).begin();
 
+  // FIXME: Detect this using the CFG-based lifetime analysis (constructor of 
a pointer).
   auto it1 = MySpan<int>(v).begin(); // expected-warning {{temporary whose 
address is use}}
   auto it2 = ReturnFirstIt(MySpan<int>(v)); // expected-warning {{temporary 
whose address is used}}
+  use(it1, it2);
 }
 
 } // namespace LifetimeboundInterleave
@@ -818,6 +974,7 @@ struct Q {
 
 std::string_view foo(std::string_view sv [[clang::lifetimebound]]);
 
+// TODO: file bug
 void test1() {
   std::string_view k1 = S().sv; // OK
   std::string_view k2 = S().s; // expected-warning {{object backing the 
pointer will}}
@@ -827,6 +984,8 @@ void test1() {
 
   std::string_view lb1 = foo(S().s); // expected-warning {{object backing the 
pointer will}}
   std::string_view lb2 = foo(Q().get()->s); // expected-warning {{object 
backing the pointer will}}
+
+  use(k1, k2, k3, k4, lb1, lb2);
 }
 
 struct Bar {};
@@ -862,7 +1021,8 @@ struct StatusOr {
 
 const char* foo() {
   StatusOr<std::string> s;
-  return s->data(); // expected-warning {{address of stack memory associated 
with local variable}}
+  return s->data(); // expected-warning {{address of stack memory associated 
with local variable}} \
+                    // cfg-warning {{address of stack memory is returned 
later}} cfg-note {{returned here}}
 
   StatusOr<std::string_view> s2;
   return s2->data();

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

Reply via email to