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
