[clang] [LifetimeSafety] Test lifetime safety on stmt-local analysis test suite (PR #175906)
Utkarsh Saxena via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 14 10:17:05 PST 2026
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 <usx at google.com>
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();
More information about the cfe-commits
mailing list