https://github.com/kashika0112 created 
https://github.com/llvm/llvm-project/pull/204361

This PR implements support for `[[clang::lifetime_capture_by(X)]]` to enable 
tracking lifetimes for plain structs and containers like `std::vector` without 
requiring manual `[[gsl::Pointer]]` or `[[gsl::Owner]]` annotations. The 
implementation extends the `LifetimeAnnotatedOriginTypeCollector` to register 
types in capture_by contracts for origin tracking. 

This PR also enables the intra-procedural analysis in existing tests using 
-Wlifetime-safety and updated expectations to handle the more detailed 
flow-sensitive diagnostics.

```cpp
struct MyContainer {
  const char* stored_ptr;
};

void captureInto(std::string_view v [[clang::lifetime_capture_by(c)]], 
MyContainer& c);

void test_parameter_capture() {
  MyContainer container;
  {
    std::string local = "temporary data";
    captureInto(local, container); 
  }
  (void)container;
}
```

Warnings Generated:
```cpp
b10.cpp:14:17: warning: local variable 'local' does not live long enough 
[-Wlifetime-safety-use-after-scope]
   14 |     captureInto(local, container); 
      |                 ^~~~~
b10.cpp:15:3: note: destroyed here
   15 |   }
      |   ^
b10.cpp:16:9: note: later used here
   16 |   (void)container;
      |         ^~~~~~~~~
```

>From 038c9bc70f18059da3097899f5bb3639faaabb0b Mon Sep 17 00:00:00 2001
From: Kashika Akhouri <[email protected]>
Date: Wed, 17 Jun 2026 14:33:44 +0000
Subject: [PATCH] Add support for simple structs without annotation

---
 clang/lib/Analysis/LifetimeSafety/Origins.cpp |  23 +-
 clang/test/Sema/LifetimeSafety/capture-by.cpp | 362 ++++++++++++++----
 clang/test/Sema/LifetimeSafety/safety.cpp     |   7 +-
 3 files changed, 312 insertions(+), 80 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/Origins.cpp 
b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
index 55d3b36e3163a..7aa188d22b781 100644
--- a/clang/lib/Analysis/LifetimeSafety/Origins.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
@@ -81,9 +81,9 @@ class LifetimeAnnotatedOriginTypeCollector
     if (!FD)
       return;
     FD = getDeclWithMergedLifetimeBoundAttrs(FD);
+    const auto *MD = dyn_cast<CXXMethodDecl>(FD);
 
-    if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
-        MD && MD->isInstance() && !isa<CXXConstructorDecl>(MD) &&
+    if (MD && MD->isInstance() && !isa<CXXConstructorDecl>(MD) &&
         implicitObjectParamIsLifetimeBound(MD)) {
       CollectedTypes.push_back(RetType);
       return;
@@ -94,6 +94,25 @@ class LifetimeAnnotatedOriginTypeCollector
         CollectedTypes.push_back(RetType);
         return;
       }
+      if (auto *Attr = Param->getAttr<LifetimeCaptureByAttr>()) {
+        for (int Idx : Attr->params()) {
+          if (Idx == LifetimeCaptureByAttr::Global ||
+              Idx == LifetimeCaptureByAttr::Unknown ||
+              Idx == LifetimeCaptureByAttr::Invalid)
+            continue;
+          if (Idx == LifetimeCaptureByAttr::This) {
+            if (MD && MD->isInstance())
+              CollectedTypes.push_back(MD->getFunctionObjectParameterType());
+          } else if (int LogicalIdx =
+                         Idx -
+                         (MD && MD->isImplicitObjectMemberFunction() ? 1 : 0);
+                     LogicalIdx >= 0 &&
+                     (unsigned)LogicalIdx < FD->getNumParams()) {
+            CollectedTypes.push_back(
+                FD->getParamDecl(LogicalIdx)->getType().getNonReferenceType());
+          }
+        }
+      }
     }
   }
 };
diff --git a/clang/test/Sema/LifetimeSafety/capture-by.cpp 
b/clang/test/Sema/LifetimeSafety/capture-by.cpp
index 2877d8d6cd5f9..00571fa34d87a 100644
--- a/clang/test/Sema/LifetimeSafety/capture-by.cpp
+++ b/clang/test/Sema/LifetimeSafety/capture-by.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 --std=c++20 -fsyntax-only -verify -Wdangling-capture %s
+// RUN: %clang_cc1 --std=c++20 -fsyntax-only -Wno-dangling -verify=cfg 
-Wlifetime-safety %s
 
 #include "Inputs/lifetime-analysis.h"
 
@@ -11,13 +12,28 @@ void captureInt(const int &i 
[[clang::lifetime_capture_by(x)]], X &x);
 void captureRValInt(int &&i [[clang::lifetime_capture_by(x)]], X &x);
 void noCaptureInt(int i [[clang::lifetime_capture_by(x)]], X &x);
 
-void use() {
-  int local;
-  captureInt(1, // expected-warning {{object whose reference is captured by 
'x' will be destroyed at the end of the full-expression}}
-            x);
+void temporary_int_capture() {
+  captureInt(1,x); // expected-warning {{object whose reference is captured by 
'x' will be destroyed at the end of the full-expression}}
+                   // cfg-warning@-1 {{temporary object does not live long 
enough}}
+                   // cfg-note@-2 {{destroyed here}}
+  (void)x;         // cfg-note {{later used here}} 
   captureRValInt(1, x); // expected-warning {{object whose reference is 
captured by 'x' will be destroyed at the end of the full-expression}}
-  captureInt(local, x);
+                      // cfg-warning@-1 {{temporary object does not live long 
enough}}
+                      // cfg-note@-2 {{destroyed here}}
+( void)x;              // cfg-note {{later used here}} 
+}
+
+void local_int_capture() {
+  {
+    int local;
+    captureInt(local, x); // cfg-warning {{local variable 'local' does not 
live long enough}}
+  }                       // cfg-note {{destroyed here}}
+  (void)x;                // cfg-note {{later used here}} 
+}
+
+void safe_int_captures() {
   noCaptureInt(1, x);
+  int local;
   noCaptureInt(local, x);
 }
 } // namespace capture_int
@@ -30,12 +46,25 @@ struct X {} x;
 void captureString(const std::string &s [[clang::lifetime_capture_by(x)]], X 
&x);
 void captureRValString(std::string &&s [[clang::lifetime_capture_by(x)]], X 
&x);
 
-void use() {
-  std::string local_string;
+void temporary_string_capture() {
   captureString(std::string(), x); // expected-warning {{object whose 
reference is captured by 'x' will be destroyed at the end of the 
full-expression}}
-  captureString(local_string, x);
-  captureRValString(std::move(local_string), x);
+                                   // cfg-warning@-1 {{temporary object does 
not live long enough}}
+                                   // cfg-note@-2 {{destroyed here}}
+  (void)x;                         // cfg-note {{later used here}}
   captureRValString(std::string(), x); // expected-warning {{object whose 
reference is captured by 'x' will be destroyed at the end of the 
full-expression}}
+                                       // cfg-warning@-1 {{temporary object 
does not live long enough}}
+                                       // cfg-note@-2 {{destroyed here}}
+  (void)x;                             // cfg-note {{later used here}}   
+}
+
+void local_string_capture() {
+  {
+    std::string local_string1, local_string2;
+    captureString(local_string1, x);                // cfg-warning {{local 
variable 'local_string1' does not live long enough}}
+    captureRValString(std::move(local_string2), x); // cfg-warning {{local 
variable 'local_string2' does not live long enough}}
+                                    // cfg-note@-1 {{result of call to 
'move<std::basic_string<char> &>' aliases the storage of local variable 
'local_string2'}}
+  }                                 // cfg-note 2 {{destroyed here}}           
                      
+  (void)x;                          // cfg-note 2 {{later used here}}          
               
 }
 } // namespace capture_string
 
@@ -43,7 +72,7 @@ void use() {
 // Capture std::string_view (gsl pointer types)
 // ****************************************************************************
 namespace capture_string_view {
-struct X {} x;
+struct X {} x;   // cfg-note 2 {{this global dangles}}
 void captureStringView(std::string_view s [[clang::lifetime_capture_by(x)]], X 
&x);
 void captureRValStringView(std::string_view &&sv 
[[clang::lifetime_capture_by(x)]], X &x);
 void noCaptureStringView(std::string_view sv, X &x);
@@ -53,35 +82,80 @@ std::string_view getNotLifetimeBoundView(const std::string& 
s);
 const std::string& getLifetimeBoundString(const std::string &s 
[[clang::lifetimebound]]);
 const std::string& getLifetimeBoundString(std::string_view sv 
[[clang::lifetimebound]]);
 
-void use() {
-  std::string_view local_string_view;
-  std::string local_string;
-  captureStringView(local_string_view, x);
-  captureStringView(std::string(), // expected-warning {{object whose 
reference is captured by 'x' will be destroyed at the end of the 
full-expression}}
-            x);
-
-  captureStringView(getLifetimeBoundView(local_string), x);
-  captureStringView(getNotLifetimeBoundView(std::string()), x);
-  captureRValStringView(std::move(local_string_view), x);
+void temporary_string_capture() {
+  captureStringView(std::string(), x); // expected-warning {{object whose 
reference is captured by 'x' will be destroyed at the end of the 
full-expression}}
+                                       // cfg-warning@-1 {{temporary object 
does not live long enough}}
+                                       // cfg-note@-2 {{destroyed here}}
+  (void)x;                             // cfg-note {{later used here}}       
   captureRValStringView(std::string(), x); // expected-warning {{object whose 
reference is captured by 'x' will be destroyed at the end of the 
full-expression}}
-  captureRValStringView(std::string_view{"abcd"}, x);
+                                           // cfg-warning@-1 {{temporary 
object does not live long enough}}
+                                           // cfg-note@-2 {{destroyed here}}
+  (void)x;                                 // cfg-note {{later used here}}
+  captureRValStringView(std::string_view{"abcd"}, x); // cfg-warning 
{{temporary object does not live long enough}}
+                                                      // cfg-note@-1 
{{destroyed here}}
+  (void)x;                                            // cfg-note {{later used 
here}}                
+}
 
-  noCaptureStringView(local_string_view, x);
-  noCaptureStringView(std::string(), x);
+void local_string_capture() {
+  {
+    std::string local_string;
+    captureStringView(getLifetimeBoundView(local_string), x);  // cfg-warning 
{{local variable 'local_string' does not live long enough}}
+                                                               // cfg-note@-1 
{{result of call to 'getLifetimeBoundView' aliases the storage of local 
variable 'local_string'}}
+  }                                                            // cfg-note 
{{destroyed here}}
+  (void)x;      // cfg-note {{later used here}}
+  std::string_view local_string_view;
+  captureRValStringView(std::move(local_string_view), x); // cfg-warning 
{{stack memory associated with local variable 'local_string_view' escapes to 
the global variable 'x' which will dangle}}
+}
 
-  // With lifetimebound functions.
-  captureStringView(getLifetimeBoundView(
-  std::string() // expected-warning {{object whose reference is captured by 
'x' will be destroyed at the end of the full-expression}}
-  ), x);
-  captureRValStringView(getLifetimeBoundView(local_string), x);
+// Lifetimebound captures
+void temporary_string_view_lifetimebound_capture() {
+  captureStringView(getLifetimeBoundView( // cfg-note {{result of call to 
'getLifetimeBoundView' aliases the storage of temporary object}}
+  std::string()                           // expected-warning {{object whose 
reference is captured by 'x' will be destroyed at the end of the 
full-expression}}
+  ), x);                                  // cfg-warning@-1 {{temporary object 
does not live long enough}}
+                                          // cfg-note@-1 {{destroyed here}}
+  (void)x;                                // cfg-note {{later used here}}
+  captureStringView(getLifetimeBoundString(std::string()), x);   // 
expected-warning {{object whose reference is captured by 'x' will be destroyed 
at the end of the full-expression}}
+                                                                 // 
cfg-warning@-1 {{temporary object does not live long enough}}
+                                                                 // 
cfg-note@-2 {{destroyed here}}
+                                                                 // 
cfg-note@-3 {{result of call to 'getLifetimeBoundString' aliases the storage of 
temporary object}}
+  (void)x;                                                       // cfg-note 
{{later used here}}
   captureRValStringView(getLifetimeBoundView(std::string()), x); // 
expected-warning {{object whose reference is captured by 'x' will be destroyed 
at the end of the full-expression}}
-  captureRValStringView(getNotLifetimeBoundView(std::string()), x);
-  noCaptureStringView(getLifetimeBoundView(std::string()), x);
-  captureStringView(getLifetimeBoundString(std::string()), x); // 
expected-warning {{object whose reference is captured by 'x' will be destroyed 
at the end of the full-expression}}
+                                                                 // 
cfg-warning@-1 {{temporary object does not live long enough}}
+                                                                 // 
cfg-note@-2 {{destroyed here}}
+  (void)x;                                                       // cfg-note 
{{later used here}}                                                             
  
+  captureRValStringView(getNotLifetimeBoundView(std::string()), x);  // 
cfg-warning {{stack memory associated with temporary object escapes to the 
global variable 'x' which will dangle}}
+}
+
+void local_string_lifetimebound_capture() {
+ {
+    std::string local_string;
+    captureRValStringView(getLifetimeBoundView(local_string), x); // 
cfg-warning {{temporary object does not live long enough}}
+                                                                  // 
cfg-note@-1 {{destroyed here}}
+ }
+ (void)x;                                                         // cfg-note 
{{later used here}}
+}
+
+void temporary_nested_lifetimebound_capture() {
   
captureStringView(getLifetimeBoundString(getLifetimeBoundView(std::string())), 
x); // expected-warning {{object whose reference is captured by 'x' will be 
destroyed at the end of the full-expression}}
-  captureStringView(getLifetimeBoundString(getLifetimeBoundString(
+                                                                               
      // cfg-warning@-1 {{temporary object does not live long enough}}
+                                                                               
      // cfg-note@-2 {{destroyed here}}
+                                                                               
      // cfg-note@-3 {{result of call to 'getLifetimeBoundView' aliases the 
storage of temporary object}}
+                                                                               
      // cfg-note@-4 {{result of call to 'getLifetimeBoundString' aliases the 
storage of temporary object}}
+  (void)x;                                                                     
      // cfg-note {{later used here}}
+  captureStringView(getLifetimeBoundString(getLifetimeBoundString(             
      // cfg-note 2 {{result of call to 'getLifetimeBoundString' aliases the 
storage of temporary object}}
     std::string()  // expected-warning {{object whose reference is captured by 
'x' will be destroyed at the end of the full-expression}}
-    )), x);
+    )), x);        // cfg-warning@-1 {{temporary object does not live long 
enough}}
+                   // cfg-note@-1 {{destroyed here}}
+  (void)x;         // cfg-note {{later used here}}
+}
+
+void safe_captures() {
+  std::string_view local_string_view;
+  captureStringView(local_string_view, x);
+  captureStringView(getNotLifetimeBoundView(std::string()), x);
+  noCaptureStringView(local_string_view, x);
+  noCaptureStringView(std::string(), x);
+  noCaptureStringView(getLifetimeBoundView(std::string()), x);
 }
 } // namespace capture_string_view
 
@@ -92,15 +166,25 @@ const std::string* getLifetimeBoundPointer(const 
std::string &s [[clang::lifetim
 const std::string* getNotLifetimeBoundPointer(const std::string &s);
 
 namespace capture_pointer {
-struct X {} x;
+struct X {} x;   // cfg-note {{this global dangles}}
 void capturePointer(const std::string* sp [[clang::lifetime_capture_by(x)]], X 
&x);
-void use() {
+
+void temporary_pointer_lifetimebound_capture() {
   capturePointer(getLifetimeBoundPointer(std::string()), x); // 
expected-warning {{object whose reference is captured by 'x' will be destroyed 
at the end of the full-expression}}
+                                                             // cfg-warning@-1 
{{temporary object does not live long enough}}
+                                                             // cfg-note@-2 
{{destroyed here}}
+                                                             // cfg-note@-3 
{{result of call to 'getLifetimeBoundPointer' aliases the storage of temporary 
object}}
+  (void)x;                                                   // cfg-note 
{{later used here}}
+}
+
+void temporary_nested_lifetimebound_capture() {
   capturePointer(getLifetimeBoundPointer(*getLifetimeBoundPointer(
     std::string()  // expected-warning {{object whose reference is captured by 
'x' will be destroyed at the end of the full-expression}}
-    )), x);
-  capturePointer(getNotLifetimeBoundPointer(std::string()), x);
+    )), x);        // cfg-warning@-1 {{stack memory associated with temporary 
object escapes to the global variable 'x' which will dangle}}
+}
 
+void safe_capture() {
+  capturePointer(getNotLifetimeBoundPointer(std::string()), x);
 }
 } // namespace capture_pointer
 
@@ -108,23 +192,41 @@ void use() {
 // Arrays and initializer lists.
 // ****************************************************************************
 namespace init_lists {
-struct X {} x;
+struct X {} x;    // cfg-note {{this global dangles}}
 void captureVector(const std::vector<int> &a 
[[clang::lifetime_capture_by(x)]], X &x);
 void captureArray(int array [[clang::lifetime_capture_by(x)]] [2], X &x);
 void captureInitList(std::initializer_list<int> abc 
[[clang::lifetime_capture_by(x)]], X &x);
 
-
 std::initializer_list<int> getLifetimeBoundInitList(std::initializer_list<int> 
abc [[clang::lifetimebound]]);
 
-void use() {
+void temporary_vector_capture() {
   captureVector({1, 2, 3}, x); // expected-warning {{object whose reference is 
captured by 'x' will be destroyed at the end of the full-expression}}
+                               // cfg-warning@-1 {{temporary object does not 
live long enough}}
+                               // cfg-note@-2 {{destroyed here}}
+  (void)x;                     // cfg-note {{later used here}}       
   captureVector(std::vector<int>{}, x); // expected-warning {{object whose 
reference is captured by 'x' will be destroyed at the end of the 
full-expression}}
-  std::vector<int> local_vector;
-  captureVector(local_vector, x);
+                                        // cfg-warning@-1 {{temporary object 
does not live long enough}}
+                                        // cfg-note@-2 {{destroyed here}}
+  (void)x;                              // cfg-note {{later used here}}        
+}
+
+void local_vector_capture() {
+  {
+    std::vector<int> local_vector;
+    captureVector(local_vector, x);    // cfg-warning {{local variable 
'local_vector' does not live long enough}}
+  }                                    // cfg-note {{destroyed here}}
+  (void)x;                             // cfg-note {{later used here}}
+}
+
+void local_array_capture() {
   int local_array[2]; 
-  captureArray(local_array, x);
-  captureInitList({1, 2}, x); // expected-warning {{object whose reference is 
captured by 'x' will be destroyed at the end of the full-expression}}
-  captureInitList(getLifetimeBoundInitList({1, 2}), x); // expected-warning 
{{object whose reference is captured by 'x' will be destroyed at the end of the 
full-expression}}
+  captureArray(local_array, x);      // cfg-warning {{stack memory associated 
with local variable 'local_array' escapes to the global variable 'x' which will 
dangle}}
+}
+
+// FIXME: Add support for initializer lists in -Wlifetime-safety
+void initializer_list_capture() {
+  captureInitList({1, 2}, x); // expected-warning {{object whose reference is 
captured by 'x' will be destroyed at the end of the full-expression}}         
+  captureInitList(getLifetimeBoundInitList({1, 2}), x); // expected-warning 
{{object whose reference is captured by 'x' will be destroyed at the end of the 
full-expression}}                                     
 }
 } // namespace init_lists
 
@@ -136,6 +238,8 @@ struct X {} x;
 struct S {
   void capture(X &x) [[clang::lifetime_capture_by(x)]];
 };
+
+// FIXME: Add support for capture of method declarations in -Wlifetime-safety
 void use() {
   S{}.capture(x); // expected-warning {{object whose reference is captured by 
'x' will be destroyed at the end of the full-expression}}
   S s;
@@ -166,6 +270,7 @@ void captureByUnknown(std::string_view s 
[[clang::lifetime_capture_by(unknown)]]
 
 std::string_view getLifetimeBoundView(const std::string& s 
[[clang::lifetimebound]]);
 
+// FIXME: Add support for capture by global and unknown in -Wlifetime-safety
 void use() {  
   std::string_view local_string_view;
   std::string local_string;
@@ -195,12 +300,30 @@ std::string_view getLifetimeBoundView(const std::string& 
s [[clang::lifetimeboun
 std::string_view getNotLifetimeBoundView(const std::string& s);
 const std::string& getLifetimeBoundString(const std::string &s 
[[clang::lifetimebound]]);
 
-void use() {
+void temporary_capture_by_this() {
   S s;
   s.captureInt(1); // expected-warning {{object whose reference is captured by 
's' will be destroyed at the end of the full-expression}}
+                   // cfg-warning@-1 {{temporary object does not live long 
enough}}
+                   // cfg-note@-2 {{destroyed here}}
+  (void)s;         // cfg-note {{later used here}}       
   s.captureView(std::string()); // expected-warning {{object whose reference 
is captured by 's' will be destroyed at the end of the full-expression}}
-  s.captureView(getLifetimeBoundView(std::string())); // expected-warning 
{{object whose reference is captured by 's' will be destroyed at the end of the 
full-expression}}
+                                // cfg-warning@-1 {{temporary object does not 
live long enough}}
+                                // cfg-note@-2 {{destroyed here}}
+  (void)s;                      // cfg-note {{later used here}}                
              
+}
+
+void lifetimebound_capture_by_this() {
+  S s;
+  s.captureView(getLifetimeBoundView(std::string()));    // expected-warning 
{{object whose reference is captured by 's' will be destroyed at the end of the 
full-expression}}
+                                                         // cfg-warning@-1 
{{temporary object does not live long enough}}
+                                                         // cfg-note@-2 
{{destroyed here}}
+                                                         // cfg-note@-3 
{{esult of call to 'getLifetimeBoundView' aliases the storage of temporary 
object}}
+  (void)s;                                               // cfg-note {{later 
used here}}         
   s.captureView(getLifetimeBoundString(std::string()));  // expected-warning 
{{object whose reference is captured by 's' will be destroyed at the end of the 
full-expression}}
+                                                         // cfg-warning@-1 
{{temporary object does not live long enough}}
+                                                         // cfg-note@-2 
{{destroyed here}}
+                                                         // cfg-note@-3 
{{result of call to 'getLifetimeBoundString' aliases the storage of temporary 
object}}
+  (void)s;                                               // cfg-note {{later 
used here}}        
   s.captureView(getNotLifetimeBoundView(std::string()));
 }  
 } // namespace capture_by_this
@@ -254,9 +377,18 @@ struct MySet {
 void user_defined_containers() {
   MySet<int> set_of_int;
   set_of_int.insert(1); // expected-warning {{object whose reference is 
captured by 'set_of_int' will be destroyed at the end of the full-expression}}
+                        // cfg-warning@-1 {{temporary object does not live 
long enough}}
+                        // cfg-note@-2 {{destroyed here}}
+  (void)set_of_int;     // cfg-note {{later used here}}                   
   MySet<std::string_view> set_of_sv;
-  set_of_sv.insert(std::string());  // expected-warning {{object whose 
reference is captured by 'set_of_sv' will be destroyed at the end of the 
full-expression}}
-  set_of_sv.insert(std::string_view());
+  set_of_sv.insert(std::string());       // expected-warning {{object whose 
reference is captured by 'set_of_sv' will be destroyed at the end of the 
full-expression}}
+                                         // cfg-warning@-1 {{temporary object 
does not live long enough}}
+                                         // cfg-note@-2 {{destroyed here}}
+  (void)set_of_sv;                       // cfg-note {{later used here}}       
         
+  set_of_sv.insert(std::string_view());  // cfg-warning {{temporary object 
does not live long enough}}
+                                         // cfg-note@-1 {{destroyed here}}
+  (void)set_of_sv;                       // cfg-note {{later used here}}       
                                             
+                                    
 }
 } // namespace containers_no_distinction
 
@@ -286,13 +418,29 @@ void use_container() {
   
   MyVector<std::string_view> vector_of_view;
   vector_of_view.push_back(std::string()); // expected-warning {{object whose 
reference is captured by 'vector_of_view' will be destroyed at the end of the 
full-expression}}
+                                           // cfg-warning@-1 {{temporary 
object does not live long enough}}
+                                           // cfg-note@-2 {{destroyed here}}
+  (void)vector_of_view;                    // cfg-note {{later used here}}
   vector_of_view.push_back(getLifetimeBoundView(std::string())); // 
expected-warning {{object whose reference is captured by 'vector_of_view' will 
be destroyed at the end of the full-expression}}
-  
+                                                                 // 
cfg-warning@-1 {{temporary object does not live long enough}}
+                                                                 // 
cfg-note@-2 {{destroyed here}}
+  (void)vector_of_view;                                          // cfg-note 
{{later used here}}
+
   MyVector<const std::string*> vector_of_pointer;
   vector_of_pointer.push_back(getLifetimeBoundPointer(std::string())); // 
expected-warning {{object whose reference is captured by 'vector_of_pointer' 
will be destroyed at the end of the full-expression}}
+                                                                       // 
cfg-warning@-1 {{temporary object does not live long enough}}
+                                                                       // 
cfg-note@-2 {{destroyed here}}
+  (void)vector_of_pointer;                                             // 
cfg-note {{later used here}}
   
vector_of_pointer.push_back(getLifetimeBoundPointer(*getLifetimeBoundPointer(std::string())));
 // expected-warning {{object whose reference is captured by 
'vector_of_pointer' will be destroyed at the end of the full-expression}}
-  vector_of_pointer.push_back(getLifetimeBoundPointer(local));
-  vector_of_pointer.push_back(getNotLifetimeBoundPointer(std::string()));
+                                                                               
                  // cfg-warning@-1 {{temporary object does not live long 
enough}}
+                                                                               
                  // cfg-note@-2 {{destroyed here}}
+  (void)vector_of_pointer;                                                     
                  // cfg-note {{later used here}}
+  vector_of_pointer.push_back(getLifetimeBoundPointer(local));              // 
cfg-warning {{temporary object does not live long enough}}
+                                                                            // 
cfg-note@-1 {{destroyed here}}
+  (void)vector_of_pointer;                                                  // 
cfg-note {{later used here}}
+  vector_of_pointer.push_back(getNotLifetimeBoundPointer(std::string()));   // 
cfg-warning {{temporary object does not live long enough}}
+                                                                            // 
cfg-note@-1 {{destroyed here}}
+  (void)vector_of_pointer;                                                  // 
cfg-note {{later used here}}
 }
 
 // ****************************************************************************
@@ -323,18 +471,38 @@ const std::string& 
getLifetimeBoundString(std::string_view sv [[clang::lifetimeb
 void use_my_view() {
   std::string local;
   MyVector<MyStringView> vector_of_my_view;
-  vector_of_my_view.push_back(getMySV());
-  vector_of_my_view.push_back(MyStringView{});
-  vector_of_my_view.push_back(std::string_view{});
-  vector_of_my_view.push_back(std::string{}); // expected-warning {{object 
whose reference is captured by 'vector_of_my_view' will be destroyed at the end 
of the full-expression}}
+  vector_of_my_view.push_back(getMySV());           // cfg-warning {{temporary 
object does not live long enough}}
+                                                    // cfg-note@-1 {{destroyed 
here}}
+  (void)vector_of_my_view;                          // cfg-note {{later used 
here}}
+  vector_of_my_view.push_back(MyStringView{});      // cfg-warning {{temporary 
object does not live long enough}}
+                                                    // cfg-note@-1 {{destroyed 
here}}
+  (void)vector_of_my_view;                          // cfg-note {{later used 
here}}
+  vector_of_my_view.push_back(std::string_view{});  // cfg-warning {{temporary 
object does not live long enough}}
+                                                    // cfg-note@-1 {{destroyed 
here}}
+  (void)vector_of_my_view;                          // cfg-note {{later used 
here}}
+  vector_of_my_view.push_back(std::string{});       // expected-warning 
{{object whose reference is captured by 'vector_of_my_view' will be destroyed 
at the end of the full-expression}}
+                                                    // cfg-warning@-1 
{{temporary object does not live long enough}}
+                                                    // cfg-note@-2 {{destroyed 
here}}
+  (void)vector_of_my_view;                          // cfg-note {{later used 
here}}                                            
   vector_of_my_view.push_back(getLifetimeBoundView(std::string{})); // 
expected-warning {{object whose reference is captured by 'vector_of_my_view' 
will be destroyed at the end of the full-expression}}
+                                                    // cfg-warning@-1 
{{temporary object does not live long enough}}
+                                                    // cfg-note@-2 {{destroyed 
here}}
+  (void)vector_of_my_view;                          // cfg-note {{later used 
here}}                                                   
   
vector_of_my_view.push_back(getLifetimeBoundString(getLifetimeBoundView(std::string{})));
 // expected-warning {{object whose reference is captured by 
'vector_of_my_view' will be destroyed at the end of the full-expression}}
-  
vector_of_my_view.push_back(getNotLifetimeBoundView(getLifetimeBoundString(getLifetimeBoundView(std::string{}))));
+                                                    // cfg-warning@-1 
{{temporary object does not live long enough}}
+                                                    // cfg-note@-2 {{destroyed 
here}}
+  (void)vector_of_my_view;                          // cfg-note {{later used 
here}}                                                    
+  
vector_of_my_view.push_back(getNotLifetimeBoundView(getLifetimeBoundString(getLifetimeBoundView(std::string{}))));
 // cfg-warning {{temporary object does not live long enough}}
+                                                    // cfg-note@-1 {{destroyed 
here}}
+  (void)vector_of_my_view;                          // cfg-note {{later used 
here}}       
   
   // Use with container of other view types.
   MyVector<std::string_view> vector_of_view;
-  vector_of_view.push_back(getMySV());
+  vector_of_view.push_back(getMySV());              // cfg-warning {{temporary 
object does not live long enough}}
+                                                    // cfg-note@-1 {{destroyed 
here}}
+  (void)vector_of_view;                             // cfg-note {{later used 
here}}
   vector_of_view.push_back(getMySVNotP());
+  (void)vector_of_view;
 }
 
 // ****************************************************************************
@@ -345,10 +513,18 @@ void use_with_optional_view() {
 
   std::optional<std::string_view> optional_of_view;
   vector_of_view.push_back(optional_of_view.value());
-  vector_of_view.push_back(getOptionalS().value()); // expected-warning 
{{object whose reference is captured by 'vector_of_view' will be destroyed at 
the end of the full-expression}}
-  
-  vector_of_view.push_back(getOptionalSV().value());
-  vector_of_view.push_back(getOptionalMySV().value());
+  vector_of_view.push_back(getOptionalS().value());      // expected-warning 
{{object whose reference is captured by 'vector_of_view' will be destroyed at 
the end of the full-expression}}
+                                                         // cfg-warning@-1 
{{temporary object does not live long enough}}
+                                                         // cfg-note@-2 
{{destroyed here}}                                                         
+  (void)vector_of_view;                                  // cfg-note {{later 
used here}}  
+  vector_of_view.push_back(getOptionalSV().value());     // cfg-warning 
{{temporary object does not live long enough}}
+                                                         // cfg-note@-1 
{{destroyed here}}
+                                                         // cfg-note@-2 
{{result of call to 'value' aliases the storage of temporary object}}
+  (void)vector_of_view;                                  // cfg-note {{later 
used here}}  
+  vector_of_view.push_back(getOptionalMySV().value());   // cfg-warning 
{{temporary object does not live long enough}}
+                                                         // cfg-note@-1 
{{destroyed here}}
+                                                         // cfg-note@-2 
{{result of call to 'value' aliases the storage of temporary object}}
+  (void)vector_of_view;                                  // cfg-note {{later 
used here}}  
   vector_of_view.push_back(getOptionalMySVNotP().value());
 }
 } // namespace conatiners_with_different
@@ -367,17 +543,30 @@ void capture3(const std::string_view& s 
[[clang::lifetime_capture_by(x)]], std::
 void use() {
   std::vector<std::string_view> x1;
   capture1(std::string(), x1); // expected-warning {{object whose reference is 
captured by 'x1' will be destroyed at the end of the full-expression}}
+                               // cfg-warning@-1 {{temporary object does not 
live long enough}}
+                               // cfg-note@-2 {{destroyed here}}
+  (void)x1;                    // cfg-note {{later used here}}
   capture1(std::string_view(), x1);
 
   std::vector<std::string_view*> x2;
   // Clang considers 'const std::string_view&' to refer to the owner
   // 'std::string' and not 'std::string_view'. Therefore no diagnostic here.
-  capture2(std::string_view(), x2);
-  capture2(std::string(), x2); // expected-warning {{object whose reference is 
captured by 'x2' will be destroyed at the end of the full-expression}}
+  capture2(std::string_view(), x2);  // cfg-warning {{temporary object does 
not live long enough}} 
+                                     // cfg-note@-1 {{destroyed here}}
+  (void)x2;                          // cfg-note {{later used here}}
+  capture2(std::string(), x2);       // expected-warning {{object whose 
reference is captured by 'x2' will be destroyed at the end of the 
full-expression}}
+                                     // cfg-warning@-1 {{temporary object does 
not live long enough}} 
+                                     // cfg-note@-2 {{destroyed here}}
+  (void)x2;                          // cfg-note {{later used here}}
   
   std::vector<std::string_view> x3;
-  capture3(std::string_view(), x3);
-  capture3(std::string(), x3); // expected-warning {{object whose reference is 
captured by 'x3' will be destroyed at the end of the full-expression}}
+  capture3(std::string_view(), x3);  // cfg-warning {{temporary object does 
not live long enough}} 
+                                     // cfg-note@-1 {{destroyed here}}
+  (void)x3;                          // cfg-note {{later used here}}           
+  capture3(std::string(), x3);       // expected-warning {{object whose 
reference is captured by 'x3' will be destroyed at the end of the 
full-expression}}
+                                     // cfg-warning@-1 {{temporary object does 
not live long enough}} 
+                                     // cfg-note@-2 {{destroyed here}}
+  (void)x3;                          // cfg-note {{later used here}}       
 }
 } // namespace temporary_views
 
@@ -391,15 +580,32 @@ const std::string* getNotLifetimeBoundPointer(const 
std::string &s);
 std::string_view getLifetimeBoundView(const std::string& s 
[[clang::lifetimebound]]);
 std::string_view getNotLifetimeBoundView(const std::string& s);
 void use() {
-  std::string local;
   std::vector<std::string_view> views;
   views.push_back(std::string()); // expected-warning {{object whose reference 
is captured by 'views' will be destroyed at the end of the full-expression}}
+                                  // cfg-warning@-1 {{temporary object does 
not live long enough}}
+                                  // cfg-note@-2 {{destroyed here}}
+  (void)views;                    // cfg-note {{later used here}}             
   views.insert(views.begin(), 
-            std::string()); // expected-warning {{object whose reference is 
captured by 'views' will be destroyed at the end of the full-expression}}
-  views.push_back(getLifetimeBoundView(std::string())); // expected-warning 
{{object whose reference is captured by 'views' will be destroyed at the end of 
the full-expression}}
-  views.push_back(getNotLifetimeBoundView(std::string()));
-  views.push_back(local);
-  views.insert(views.end(), local);
+            std::string());       // expected-warning {{object whose reference 
is captured by 'views' will be destroyed at the end of the full-expression}}
+                                  // cfg-warning@-1 {{temporary object does 
not live long enough}}
+                                  // cfg-note@-2 {{destroyed here}}
+  (void)views;                    // cfg-note {{later used here}}              
               
+  views.push_back(getLifetimeBoundView(std::string()));    // expected-warning 
{{object whose reference is captured by 'views' will be destroyed at the end of 
the full-expression}}
+                                                           // cfg-warning@-1 
{{temporary object does not live long enough}}
+                                                           // cfg-note@-2 
{{destroyed here}}
+  (void)views;                                             // cfg-note {{later 
used here}}                                                             
+  views.push_back(getNotLifetimeBoundView(std::string())); // cfg-warning 
{{temporary object does not live long enough}}
+                                                           // cfg-note@-1 
{{destroyed here}}
+  (void)views;                                             // cfg-note {{later 
used here}}  
+  {
+      std::string local1, local2;
+      views.push_back(local1);            // cfg-warning {{temporary object 
does not live long enough}}
+                                          // cfg-note@-1 {{destroyed here}}
+      (void)views;                        // cfg-note {{later used here}}      
        
+      views.insert(views.end(), local2);  // cfg-warning {{temporary object 
does not live long enough}}
+                                          // cfg-note@-1 {{destroyed here}}
+  }                                       
+  (void)views;                            // cfg-note {{later used here}}
 
   std::vector<std::string> strings;
   strings.push_back(std::string());
@@ -407,6 +613,7 @@ void use() {
 
   std::vector<const std::string*> pointers;
   pointers.push_back(getLifetimeBoundPointer(std::string()));
+  std::string local;
   pointers.push_back(&local);
 }
 
@@ -420,8 +627,15 @@ struct [[gsl::Pointer]] Span {
 void use() {
   std::vector<Span<int>> spans;
   spans.push_back(std::vector<int>{1, 2, 3}); // expected-warning {{object 
whose reference is captured by 'spans' will be destroyed at the end of the 
full-expression}}
-  std::vector<int> local;
-  spans.push_back(local);
+                                              // cfg-warning@-1 {{temporary 
object does not live long enough}}
+                                              // cfg-note@-2 {{destroyed here}}
+  (void)spans;                                // cfg-note {{later used here}}  
                                  
+  {
+    std::vector<int> local;
+    spans.push_back(local);    // cfg-warning {{temporary object does not live 
long enough}}
+                               // cfg-note@-1 {{destroyed here}}
+  }                            
+  (void)spans;                 // cfg-note {{later used here}} 
 }
 } // namespace with_span
 } // namespace inferred_capture_by
diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp 
b/clang/test/Sema/LifetimeSafety/safety.cpp
index c838918eb556d..372eb48fdca6c 100644
--- a/clang/test/Sema/LifetimeSafety/safety.cpp
+++ b/clang/test/Sema/LifetimeSafety/safety.cpp
@@ -3644,7 +3644,6 @@ void member_capture() {
   (void)c.stored;   // expected-note {{later used here}}
 }
 
-// FIXME: Add support for simple containers without annotations.
 struct SimpleContainer {
   View stored;
   void set(View s [[clang::lifetime_capture_by(this)]]);
@@ -3654,9 +3653,9 @@ void member_capture_simple_container() {
   SimpleContainer c;
   {
     MyObj local;
-    c.set(local);   
-  }                 
-  (void)c.stored;   
+    c.set(local);   // expected-warning {{local variable 'local' does not live 
long enough}}
+  }                 // expected-note {{destroyed here}}   
+  (void)c.stored;   // expected-note {{later used here}}
 }
 
 void captureTwo(View& into, 

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

Reply via email to