https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/176728
>From c01304ccac8483f7136d102b04c3052b6fa23696 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
