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

>From dd24f1a80cbcace6cfdef14b96503eb2f453e8b0 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <[email protected]>
Date: Mon, 19 Jan 2026 10:46:44 +0000
Subject: [PATCH] track use of dangling references

---
 .../LifetimeSafety/FactsGenerator.cpp         |  7 ++--
 clang/test/Sema/Inputs/lifetime-analysis.h    |  3 +-
 .../Sema/warn-lifetime-analysis-nocfg.cpp     | 18 ++++++---
 clang/test/Sema/warn-lifetime-safety.cpp      | 39 +++++++++++++++++++
 4 files changed, 58 insertions(+), 9 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 54e19b58bd7d5..5d1afd15c795d 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -589,9 +589,10 @@ void FactsGenerator::handleUse(const DeclRefExpr *DRE) {
   OriginList *List = getOriginsList(*DRE);
   if (!List)
     return;
-  // Remove the outer layer of origin which borrows from the decl directly. 
This
-  // is a use of the underlying decl.
-  List = getRValueOrigins(DRE, List);
+  // Remove the outer layer of origin which borrows from the decl directly
+  // (e.g., when this is not a reference). This is a use of the underlying 
decl.
+  if (!DRE->getDecl()->getType()->isReferenceType())
+    List = getRValueOrigins(DRE, List);
   // Skip if there is no inner origin (e.g., when it is not a pointer type).
   if (!List)
     return;
diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h 
b/clang/test/Sema/Inputs/lifetime-analysis.h
index 918547dfcec51..b47fe78bdf0fb 100644
--- a/clang/test/Sema/Inputs/lifetime-analysis.h
+++ b/clang/test/Sema/Inputs/lifetime-analysis.h
@@ -49,7 +49,8 @@ struct vector {
   template<typename InputIterator>
        vector(InputIterator first, InputIterator __last);
 
-  T &at(int n);
+  T &  at(int n) &;
+  T && at(int n) &&;
 
   void push_back(const T&);
   void push_back(T&&);
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp 
b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 441f9fc602916..77fb39ebb10f3 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -280,13 +280,21 @@ std::string_view danglingRefToOptionalFromTemp4() {
 }
 
 void danglingReferenceFromTempOwner() {
+  int &&r = *std::optional<int>();          // 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}}
   // 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}}
+
+  // FIXME: Detect this using the CFG-based lifetime analysis.
+  //        https://github.com/llvm/llvm-project/issues/175893
   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);
+
+  const int &r4 = std::vector<int>().at(3); // 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}}
+  int &&r5 = std::vector<int>().at(3);      // 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(r, r2, r3, r4, r5);                   // cfg-note 3 {{later used here}}
 
   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}}
@@ -299,8 +307,8 @@ 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}}
+  for (auto i : *getTempOptVec()) // 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}} cfg-note 
{{later used here}}
     ;
 }
 
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index 24ac72e7aad4d..13666b0402522 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1453,3 +1453,42 @@ void bar() {
     (void)x; // expected-note {{used here}}
 }
 }
+
+namespace reference_type_decl_ref_expr {
+struct S {
+  S();
+  ~S();
+  const std::string& x() const [[clang::lifetimebound]];
+};
+
+const std::string& identity(const std::string& in [[clang::lifetimebound]]);
+const S& identity(const S& in [[clang::lifetimebound]]);
+
+void test_temporary() {
+  const std::string& x = S().x(); // expected-warning {{object whose reference 
is captured does not live long enough}} expected-note {{destroyed here}}
+  (void)x; // expected-note {{later used here}}
+
+  const std::string& y = identity(S().x()); // expected-warning {{object whose 
reference is captured does not live long enough}} expected-note {{destroyed 
here}}
+  (void)y; // expected-note {{later used here}}
+
+  std::string_view z;
+  {
+    S s;
+    const std::string& zz = s.x(); // expected-warning {{object whose 
reference is captured does not live long enough}}
+    z = zz;
+  } // expected-note {{destroyed here}}
+  (void)z; // expected-note {{later used here}}
+}
+
+void test_lifetime_extension_ok() {
+  const S& x = S();
+  (void)x;
+  const S& y = identity(S()); // expected-warning {{object whose reference is 
captured does not live long enough}} expected-note {{destroyed here}}
+  (void)y; // expected-note {{later used here}}
+}
+
+const std::string& test_return() {
+  const std::string& x = S().x(); // expected-warning {{object whose reference 
is captured does not live long enough}} expected-note {{destroyed here}}
+  return x; // expected-note {{later used here}}
+}
+} // namespace reference_type_decl_ref_expr

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

Reply via email to