// RUN: %clang_cc1 -fsyntax-only -Wdangling -Wdangling-field -Wreturn-stack-address -verify %s // RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wno-dangling -verify=cfg %s // RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety -Wno-dangling -verify=cfg,tu %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*(); }; struct [[gsl::Pointer(int)]] MyIntPointer { MyIntPointer(int *p = nullptr); // Conversion operator and constructor conversion will result in two // different ASTs. The former is tested with another owner and // pointer type. MyIntPointer(const MyIntOwner &); int &operator*(); MyIntOwner toOwner(); }; struct MySpecialIntPointer : MyIntPointer { }; // We did see examples in the wild when a derived class changes // the ownership model. So we have a test for it. struct [[gsl::Owner(int)]] MyOwnerIntPointer : MyIntPointer { }; struct [[gsl::Pointer(long)]] MyLongPointerFromConversion { MyLongPointerFromConversion(long *p = nullptr); long &operator*(); }; struct [[gsl::Owner(long)]] MyLongOwnerWithConversion { MyLongOwnerWithConversion(); // TODO: Do this behind a macro and run tests without this dtor to verify trivial dtor cases. ~MyLongOwnerWithConversion(); operator MyLongPointerFromConversion(); long &operator*(); MyIntPointer releaseAsMyPointer(); long *releaseAsRawPointer(); }; template 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}} } void intentionalFalseNegative() { int i; MyIntPointer p{&i}; // In this case we do not have enough information in a statement local // analysis to detect the problem. new MyIntPointer(p); new MyIntPointer(MyIntPointer{p}); } MyIntPointer ownershipTransferToMyPointer() { MyLongOwnerWithConversion t; return t.releaseAsMyPointer(); // ok } long *ownershipTransferToRawPointer() { MyLongOwnerWithConversion t; return t.releaseAsRawPointer(); // ok } struct Y { int a[4]; }; void dangligGslPtrFromTemporary() { MyIntPointer p = Y{}.a; // cfg-warning {{local temporary object does not live long enough}} \ // cfg-note {{destroyed here}} (void)p; // cfg-note {{later used here}} } struct DanglingGslPtrField { MyIntPointer p; // expected-note {{pointer member declared here}} \ // cfg-note 3 {{this field dangles}} MyLongPointerFromConversion p2; // expected-note {{pointer member declared here}} \ // cfg-note 2 {{this field dangles}} DanglingGslPtrField(int i) : p(&i) {} // cfg-warning {{stack memory associated with parameter 'i' escapes to the field 'p' which will dangle}} DanglingGslPtrField() : p2(MyLongOwnerWithConversion{}) {} // expected-warning {{initializing pointer member 'p2' to point to a temporary object whose lifetime is shorter than the lifetime of the constructed object}} \ // cfg-warning {{stack memory associated with local temporary object escapes to the field 'p2' which will dangle}} DanglingGslPtrField(double) : p(MyIntOwner{}) {} // expected-warning {{initializing pointer member 'p' to point to a temporary object whose lifetime is shorter than the lifetime of the constructed object}} \ // cfg-warning {{stack memory associated with local temporary object escapes to the field 'p' which will dangle}} DanglingGslPtrField(MyIntOwner io) : p(io) {} // cfg-warning {{stack memory associated with parameter 'io' escapes to the field 'p' which will dangle}} DanglingGslPtrField(MyLongOwnerWithConversion lo) : p2(lo) {} // cfg-warning {{stack memory associated with parameter 'lo' escapes to the field 'p2' which will dangle}} }; MyIntPointer danglingGslPtrFromLocal() { int j; // Detected only by CFG analysis. return &j; // cfg-warning {{stack memory associated with local variable 'j' is returned}} cfg-note {{returned here}} } MyIntPointer returningLocalPointer() { MyIntPointer localPointer; return localPointer; // ok } MyIntPointer daglingGslPtrFromLocalOwner() { MyIntOwner localOwner; return localOwner; // expected-warning {{address of stack memory associated with local variable 'localOwner' returned}} \ // cfg-warning {{stack memory associated with local variable 'localOwner' is returned}} cfg-note {{returned here}} } MyLongPointerFromConversion daglingGslPtrFromLocalOwnerConv() { MyLongOwnerWithConversion localOwner; return localOwner; // expected-warning {{address of stack memory associated with local variable 'localOwner' returned}} \ // cfg-warning {{stack memory associated with local variable 'localOwner' is returned}} cfg-note {{returned here}} } MyIntPointer danglingGslPtrFromTemporary() { return MyIntOwner{}; // expected-warning {{returning address of local temporary object}} \ // cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} } MyIntOwner makeTempOwner(); MyIntPointer danglingGslPtrFromTemporary2() { return makeTempOwner(); // expected-warning {{returning address of local temporary object}} \ // cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} } MyLongPointerFromConversion danglingGslPtrFromTemporaryConv() { return MyLongOwnerWithConversion{}; // expected-warning {{returning address of local temporary object}} \ // cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} } int *noFalsePositive(MyIntOwner &o) { MyIntPointer p = o; return &*p; // ok } 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}} \ // cfg-warning {{local temporary object 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 {{local temporary object 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 {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(p); // cfg-note {{later used here}} pp = p; // no warning use(p, pp); global = MyIntOwner{}; // expected-warning {{object backing the pointer 'global' }} \ // cfg-warning {{local temporary object 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}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(p2); // cfg-note {{later used here}} p2 = MyLongOwnerWithConversion{}; // expected-warning {{object backing the pointer 'p2' }} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} global2 = MyLongOwnerWithConversion{}; // expected-warning {{object backing the pointer 'global2' }} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(global2, p2); // cfg-note 2 {{later used here}} } struct LifetimeBoundCtor { LifetimeBoundCtor(const MyIntOwner& obj1 [[clang::lifetimebound]]); LifetimeBoundCtor(std::string_view sv [[clang::lifetimebound]]); }; auto lifetimebound_make_unique_single_param() { return std::make_unique(MyIntOwner{}); // tu-warning {{stack memory associated with local temporary object is returned}} tu-note {{returned here}} } struct Unannotated { typedef std::vector::iterator iterator; iterator begin(); operator iterator() const; }; void modelIterators() { std::vector::iterator it = std::vector().begin(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} (void)it; // cfg-note {{later used here}} } std::vector::iterator modelIteratorReturn() { return std::vector().begin(); // expected-warning {{returning address of local temporary object}} \ // cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} } const int *modelFreeFunctions() { return std::data(std::vector()); // expected-warning {{returning address of local temporary object}} \ // cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} } int &modelAnyCast() { return std::any_cast(std::any{}); // expected-warning {{returning reference to local temporary object}} \ // cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} } int modelAnyCast2() { return std::any_cast(std::any{}); // ok } int modelAnyCast3() { return std::any_cast(std::any{}); // ok } const char *danglingRawPtrFromLocal() { std::basic_string s; return s.c_str(); // expected-warning {{address of stack memory associated with local variable 's' returned}} \ // cfg-warning {{stack memory associated with local variable 's' is returned}} cfg-note {{returned here}} } int &danglingRawPtrFromLocal2() { std::optional o; return o.value(); // expected-warning {{reference to stack memory associated with local variable 'o' returned}} \ // cfg-warning {{stack memory associated with local variable 'o' is returned}} cfg-note {{returned here}} } int &danglingRawPtrFromLocal3() { std::optional o; return *o; // expected-warning {{reference to stack memory associated with local variable 'o' returned}} \ // cfg-warning {{stack memory associated with local variable 'o' is returned}} cfg-note {{returned here}} } // GH100384 std::string_view containerWithAnnotatedElements() { std::string_view c1 = std::vector().at(0); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(c1); // cfg-note {{later used here}} c1 = std::vector().at(0); // expected-warning {{object backing the pointer}} \ // cfg-warning {{local temporary object 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().at(0); use(c2); std::vector local; return local.at(0); // expected-warning {{address of stack memory associated with local variable}} \ // cfg-warning {{stack memory associated with local variable 'local' is returned}} cfg-note {{returned here}} } std::string_view localUniquePtr(int i) { std::unique_ptr c1; if (i) return *c1; // expected-warning {{address of stack memory associated with local variable}} \ // cfg-warning {{stack memory associated with local variable 'c1' is returned}} cfg-note {{returned here}} std::unique_ptr c2; return *c2; // expect no-warning. } std::string_view localOptional(int i) { std::optional o; if (i) return o.value(); // expected-warning {{address of stack memory associated with local variable}} \ // cfg-warning {{stack memory associated with local variable 'o' is returned}} cfg-note {{returned here}} std::optional abc; return abc.value(); // expect no warning } const char *danglingRawPtrFromTemp() { return std::basic_string().c_str(); // expected-warning {{returning address of local temporary object}} \ // cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} } std::unique_ptr getUniquePtr(); int *danglingUniquePtrFromTemp() { return getUniquePtr().get(); // expected-warning {{returning address of local temporary object}} \ // cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} } int *danglingUniquePtrFromTemp2() { return std::unique_ptr().get(); // expected-warning {{returning address of local temporary object}} \ // cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} } const int& danglingRefToOptionalFromTemp3() { return std::optional().value(); // expected-warning {{returning reference to local temporary object}} \ // cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} } std::optional getTempOptStr(); std::string_view danglingRefToOptionalFromTemp4() { return getTempOptStr().value(); // expected-warning {{returning address of local temporary object}} \ // cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} } void danglingReferenceFromTempOwner() { int &&r = *std::optional(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} // https://github.com/llvm/llvm-project/issues/175893 int &&r2 = *std::optional(5); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} // https://github.com/llvm/llvm-project/issues/175893 int &&r3 = std::optional(5).value(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} const int &r4 = std::vector().at(3); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} int &&r5 = std::vector().at(3); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(r, r2, r3, r4, r5); // cfg-note 5 {{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 {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(sv); // cfg-note {{later used here}} } std::vector getTempVec(); std::optional> getTempOptVec(); void testLoops() { for (auto i : getTempVec()) // ok ; for (auto i : *getTempOptVec()) // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} cfg-note {{later used here}} ; } int &usedToBeFalsePositive(std::vector &v) { std::vector::iterator it = v.begin(); int& value = *it; return value; // ok } int &doNotFollowReferencesForLocalOwner() { // Warning caught by CFG analysis. std::unique_ptr localOwner; int &p = *localOwner // cfg-warning {{stack memory associated with local variable 'localOwner' is returned}} .get(); return p; // cfg-note {{returned here}} } const char *trackThroughMultiplePointer() { return std::basic_string_view(std::basic_string()).begin(); // expected-warning {{returning address of local temporary object}} \ // cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} } struct X { X(std::unique_ptr up) : pointee(*up), // cfg-warning {{may have been moved.}} pointee2(up.get()), // cfg-warning {{may have been moved.}} pointer(std::move(up)) {} // cfg-note 2 {{potentially moved here}} int &pointee; // cfg-note {{this field dangles}} int *pointee2; // cfg-note {{this field dangles}} std::unique_ptr pointer; }; struct [[gsl::Owner]] XOwner { int* get() const [[clang::lifetimebound]]; }; struct X2 { // A common usage that moves the passing owner to the class. // verify a strict warning on this case. X2(XOwner owner) : pointee(owner.get()), // cfg-warning {{may have been moved.}} owner(std::move(owner)) {} // cfg-note {{potentially moved here}} int* pointee; // cfg-note {{this field dangles}} XOwner owner; }; std::vector::iterator getIt(); std::vector getVec(); const int &handleGslPtrInitsThroughReference() { const auto &it = getIt(); // Ok, it is lifetime extended. return *it; } void handleGslPtrInitsThroughReference2() { const std::vector &v = getVec(); const int *val = v.data(); // Ok, it is lifetime extended. } void handleTernaryOperator(bool cond) { std::basic_string def; std::basic_string_view v = cond ? def : ""; // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(v); // cfg-note {{later used here}} } 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}} \ // cfg-warning {{local temporary object 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 {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(a2); // cfg-note {{later used here}} } std::reference_wrapper danglingPtrFromNonOwnerLocal() { int i = 5; return i; // TODO } std::reference_wrapper danglingPtrFromNonOwnerLocal2() { int i = 5; return std::ref(i); // TODO } std::reference_wrapper danglingPtrFromNonOwnerLocal3() { int i = 5; return std::reference_wrapper(i); // TODO } std::reference_wrapper danglingPtrFromNonOwnerLocal4() { Unannotated i; return std::reference_wrapper(i); // TODO } std::reference_wrapper danglingPtrFromNonOwnerLocal5() { Unannotated i; return std::ref(i); // TODO } int *returnPtrToLocalArray() { int a[5]; return std::begin(a); // TODO } namespace lifetimebound_stl_algorithms { std::vector GetTemporaryString(); std::vector GetTemporaryView(); std::string_view test_str_local() { std::vector v; return *std::find(v.begin(), // cfg-warning {{stack memory associated with local variable 'v' is returned}} cfg-note {{returned here}} v.end(), "42"); } std::string_view test_str_temporary() { return *std::find(GetTemporaryString().begin(), // cfg-warning {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} GetTemporaryString().end(), "42"); } std::string_view test_view() { std::vector v; return *std::find(v.begin(), v.end(), "42"); } std::string_view test_view_local() { return *std::find(GetTemporaryView().begin(), GetTemporaryView().end(), "42"); } } // namespace lifetimebound_stl_algorithms struct ptr_wrapper { std::vector::iterator member; }; ptr_wrapper getPtrWrapper(); std::vector::iterator returnPtrFromWrapper() { ptr_wrapper local = getPtrWrapper(); return local.member; } std::vector::iterator returnPtrFromWrapperThroughRef() { ptr_wrapper local = getPtrWrapper(); ptr_wrapper &local2 = local; return local2.member; } std::vector::iterator returnPtrFromWrapperThroughRef2() { ptr_wrapper local = getPtrWrapper(); std::vector::iterator &local2 = local.member; return local2; } void checkPtrMemberFromAggregate() { std::vector::iterator local = getPtrWrapper().member; // OK. } std::vector::iterator doNotInterferWithUnannotated() { Unannotated value; // Conservative choice for now. Probably not ok, but we do not warn. return std::begin(value); } std::vector::iterator doNotInterferWithUnannotated2() { Unannotated value; return value; } std::vector::iterator supportDerefAddrofChain(int a, std::vector::iterator value) { switch (a) { default: return value; case 1: return *&value; case 2: return *&*&value; case 3: return *&*&*&value; } } int &supportDerefAddrofChain2(int a, std::vector::iterator value) { switch (a) { default: return *value; case 1: return **&value; case 2: return **&*&value; case 3: return **&*&*&value; } } int *supportDerefAddrofChain3(int a, std::vector::iterator value) { switch (a) { default: return &*value; case 1: return &*&*value; case 2: return &*&**&value; case 3: return &*&**&*&value; } } MyIntPointer handleDerivedToBaseCast1(MySpecialIntPointer ptr) { return ptr; } MyIntPointer handleDerivedToBaseCast2(MyOwnerIntPointer ptr) { return ptr; // expected-warning {{address of stack memory associated with parameter 'ptr' returned}} } std::vector::iterator noFalsePositiveWithVectorOfPointers() { std::vector::iterator> iters; return iters.at(0); } void testForBug49342() { auto it = std::iter{} - 2; // Used to be false positive. } namespace GH93386 { // verify no duplicated diagnostics are emitted. struct [[gsl::Pointer]] S { S(const std::vector& abc [[clang::lifetimebound]]); }; S test(std::vector a) { return S(a); // expected-warning {{address of stack memory associated with}} \ // cfg-warning {{stack memory associated with parameter 'a' is returned}} cfg-note {{returned here}} } // FIXME: Detect this using the CFG-based lifetime analysis (global initialisation). auto s = S(std::vector()); // expected-warning {{temporary whose address is used as value of local variable}} // Verify no regression on the follow case. std::string_view test2(int i, std::optional a) { if (i) return std::move(*a); return std::move(a.value()); } struct Foo; struct FooView { FooView(const Foo& foo [[clang::lifetimebound]]); }; FooView test3(int i, std::optional a) { if (i) return *a; // expected-warning {{address of stack memory}} \ // cfg-warning {{stack memory associated with parameter 'a' is returned}} \ // cfg-note {{returned here}} return a.value(); // expected-warning {{address of stack memory}} \ // cfg-warning {{stack memory associated with parameter 'a' is returned}} \ // cfg-note {{returned here}} } } // namespace GH93386 namespace GH100549 { struct UrlAnalyzed { UrlAnalyzed(std::string_view url [[clang::lifetimebound]]); }; std::string StrCat(std::string_view, std::string_view); void test1() { UrlAnalyzed url(StrCat("abc", "bcd")); // expected-warning {{object backing the pointer will be destroyed}} \ // cfg-warning {{local temporary object does not live long enough}} \ // cfg-note {{destroyed here}} use(url); // cfg-note {{later used here}} } 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}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(svjkk1); // cfg-note {{later used here}} } } // namespace GH100549 namespace GH108272 { template 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 class Wrapper1 { public: operator V() const; V value; }; std::string_view test1() { StatusOr> k; // Be conservative in this case, as there is not enough information available // to infer the lifetime relationship for the Wrapper1 type. std::string_view good = StatusOr>().value(); return k.value(); } template class Wrapper2 { public: operator V() const [[clang::lifetimebound]]; V value; }; std::string_view test2() { StatusOr> k; // We expect dangling issues as the conversion operator is lifetimebound。 std::string_view bad = StatusOr>().value(); // expected-warning {{temporary whose address is used as value of}} return k.value(); // expected-warning {{address of stack memory associated}} \ // cfg-warning {{stack memory associated with local variable 'k' is returned}} 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 v1({std::string()}); // expected-warning {{object backing the pointer will be destroyed at the end}} use(v1); std::vector v2({ std::string(), // expected-warning {{object backing the pointer will be destroyed at the end}} std::string_view() }); use(v2); std::vector v3({ std::string_view(), std::string() // expected-warning {{object backing the pointer will be destroyed at the end}} }); use(v3); std::optional 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: // 1. make_optional creates a temporary "optional"" object // 2. the temporary object owns the underlying string which is copied from s. // 3. the t3 object holds the view to the underlying string of the temporary object. std::optional o2 = std::make_optional(s); // expected-warning {{object backing the pointer}} std::optional o3 = std::optional(s); // expected-warning {{object backing the pointer}} std::optional o4 = std::optional(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 n1 = {std::string_view()}; std::optional n2 = {std::string_view()}; std::optional n3 = std::string_view(); std::optional n4 = std::make_optional(std::string_view()); const char* b = ""; std::optional n5 = std::make_optional(b); std::optional n6 = std::make_optional("test"); use(n1, n2, n3, n4, n5, n6); } std::vector test2(int i) { std::vector t; if (i) return t; // this is fine, no dangling return std::vector(t.begin(), t.end()); } class Foo { public: operator std::string_view() const { return ""; } }; class [[gsl::Owner]] FooOwner { public: operator std::string_view() const { return ""; } }; std::optional GetFoo(); std::optional GetFooOwner(); template struct [[gsl::Owner]] Container1 { Container1(); }; template struct [[gsl::Owner]] Container2 { template Container2(const Container1& C2); }; std::optional test3(int i) { std::string s; std::string_view sv; if (i) return s; // expected-warning {{address of stack memory associated}} return sv; // fine Container2 c1 = Container1(); // no diagnostic as Foo is not an Owner. Container2 c2 = Container1(); // expected-warning {{object backing the pointer will be destroyed}} return GetFoo(); // fine, we don't know Foo is owner or not, be conservative. return GetFooOwner(); // expected-warning {{returning address of local temporary object}} } std::optional test4(int a) { return std::make_optional(nullptr); // fine } template struct [[gsl::Owner]] StatusOr { const T &valueLB() const [[clang::lifetimebound]]; const T &valueNoLB() const; }; template struct [[gsl::Pointer]] Span { Span(const std::vector &V); const int& getFieldLB() const [[clang::lifetimebound]]; const int& getFieldNoLB() const; }; /////// From Owner /////// // Pointer from Owner std::string_view test5() { // The Owner doesn't own the object which its inner pointer points to. std::string_view a = StatusOr().valueLB(); // OK return StatusOr().valueLB(); // OK // No dangling diagnostics on non-lifetimebound methods. std::string_view b = StatusOr().valueNoLB(); return StatusOr().valueNoLB(); } // Pointer from Owner // Prevent regression GH108463 Span test6(std::vector v) { Span dangling = std::vector(); // expected-warning {{object backing the pointer}} dangling = std::vector(); // expected-warning {{object backing the pointer}} return v; // expected-warning {{address of stack memory}} \ // cfg-warning {{stack memory associated with parameter 'v' is returned}} cfg-note {{returned here}} } /////// From Owner> /////// // Pointer from Owner> int* test7(StatusOr> aa) { // No dangling diagnostic on pointer. return aa.valueLB().valueLB(); // OK. } // Owner from Owner> std::vector test8(StatusOr> aa) { return aa.valueLB(); // OK, no pointer being construct on this case. return aa.valueNoLB(); } // Pointer from Owner> Span test9(StatusOr> aa) { return aa.valueLB(); // expected-warning {{address of stack memory associated}} \ // cfg-warning {{stack memory associated with parameter 'aa' is returned}} cfg-note {{returned here}} return aa.valueNoLB(); // OK. } /////// From Owner /////// // Pointer> from Owner Span test10(StatusOr> aa) { return aa.valueLB(); // expected-warning {{address of stack memory}} \ // cfg-warning {{stack memory associated with parameter 'aa' is returned}} cfg-note {{returned here}} return aa.valueNoLB(); // OK. } /////// From Owner> /////// // Pointer> from Owner> Span test11(StatusOr> aa) { return aa.valueLB(); // OK return aa.valueNoLB(); // OK. } // Lifetimebound and gsl::Pointer. const int& test12(Span a) { return a.getFieldLB(); // expected-warning {{reference to stack memory associated}} \ // cfg-warning {{stack memory associated with parameter 'a' is returned}} cfg-note {{returned here}} return a.getFieldNoLB(); // OK. } void test13() { // FIXME: RHS is Owner, we skip this case to avoid false positives. std::optional> abc = std::vector{}; // FIXME: Detect this using the CFG-based lifetime analysis (container of pointer). std::optional> t = std::vector {}; // expected-warning {{object backing the pointer will be destroyed}} use(t); } } // namespace GH100526 namespace GH118064{ void test() { auto y = std::set{}.begin(); // expected-warning {{object backing the pointer}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(y); // cfg-note {{later used here}} } } // namespace GH118064 namespace LifetimeboundInterleave { const std::string& Ref(const std::string& abc [[clang::lifetimebound]]); 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_1() { std::string_view t1 = Ref(std::string()); // expected-warning {{object backing}} \ // cfg-warning {{local temporary object 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 {{local temporary object 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 {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} } std::string_view test1_2() { std::string_view t2 = TakeSv(std::string()); // expected-warning {{object backing}} \ // cfg-warning {{local temporary object 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 {{local temporary object 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 {{stack memory associated with local temporary object is returned}} cfg-note {{returned here}} } std::string_view test1_3() { std::string_view t3 = TakeStrRef(std::string()); // expected-warning {{temporary}} \ // cfg-warning {{local temporary object 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 {{local temporary object 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 {{stack memory associated with local temporary object is returned}} 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()); } template 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_1(Foo r1, Foo r2) { std::string_view t1 = Foo().get(); // expected-warning {{object backing}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(t1); // cfg-note {{later used here}} t1 = Foo().get(); // expected-warning {{object backing}} \ // cfg-warning {{local temporary object 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 {{stack memory associated with parameter 'r1' is returned}} cfg-note {{returned here}} } std::string_view test2_2(Foo r1, Foo r2) { std::string_view t2 = Foo().get(); use(t2); t2 = Foo().get(); use(t2); return r2.get(); } std::string_view test2_3(Foo r1, Foo r2) { // no warning on no-LB-annotated method. std::string_view t3 = Foo().getNoLB(); use(t3); t3 = Foo().getNoLB(); use(t3); return r1.getNoLB(); } 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) { Pointer p = Pointer(Bar()); // expected-warning {{temporary}} cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(p); // cfg-note {{later used here}} p = Pointer(Bar()); // expected-warning {{object backing}} cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(p); // cfg-note {{later used here}} return bar; // expected-warning {{address of stack}} cfg-warning {{stack memory associated with parameter 'bar' is returned}} cfg-note {{returned here}} } template struct MySpan { MySpan(const std::vector& v); ~MySpan(); using iterator = std::iterator; // FIXME: It is not possible to annotate accessor methods of non-owning view types. // Clang should provide another annotation to mark such functions as 'transparent'. iterator begin() const; }; // FIXME: Same as above. template typename MySpan::iterator ReturnFirstIt(const MySpan& v); void test4() { std::vector v{1}; // MySpan doesn't own any underlying T objects, the pointee object of // the MySpan iterator is still alive when the whole span is destroyed, thus // no diagnostic. const int& t1 = *MySpan(v).begin(); const int& t2 = *ReturnFirstIt(MySpan(v)); // Ideally, we would diagnose the following case, but due to implementation // constraints, we do not. const int& t4 = *MySpan(std::vector{}).begin(); use(t1, t2, t4); auto it1 = MySpan(v).begin(); auto it2 = ReturnFirstIt(MySpan(v)); use(it1, it2); } } // namespace LifetimeboundInterleave namespace range_based_for_loop_variables { std::string_view test_view_loop_var(std::vector strings) { for (std::string_view s : strings) { // cfg-warning {{stack memory associated with parameter 'strings' is returned}} return s; //cfg-note {{returned here}} } return ""; } const char* test_view_loop_var_with_data(std::vector strings) { for (std::string_view s : strings) { // cfg-warning {{stack memory associated with parameter 'strings' is returned}} return s.data(); //cfg-note {{returned here}} } return ""; } std::string_view test_no_error_for_views(std::vector views) { for (std::string_view s : views) { return s; } return ""; } std::string_view test_string_ref_var(std::vector strings) { for (const std::string& s : strings) { // cfg-warning {{stack memory associated with parameter 'strings' is returned}} return s; //cfg-note {{returned here}} } return ""; } std::string_view test_opt_strings(std::optional> strings_or) { for (const std::string& s : *strings_or) { // cfg-warning {{stack memory associated with parameter 'strings_or' is returned}} return s; //cfg-note {{returned here}} } return ""; } } // namespace range_based_for_loop_variables namespace iterator_arrow { std::string_view test() { std::vector strings; return strings.begin()->data(); // cfg-warning {{stack memory associated with local variable 'strings' is returned}} cfg-note {{returned here}} } void operator_star_arrow_reference() { std::vector v; const char* p = v.begin()->data(); const char* q = (*v.begin()).data(); const std::string& r = *v.begin(); auto temporary = []() { return std::vector{{"1"}}; }; const char* x = temporary().begin()->data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} const char* y = (*temporary().begin()).data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} const std::string& z = (*temporary().begin()); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(p, q, r, x, y, z); // cfg-note 3 {{later used here}} } void operator_star_arrow_of_iterators_false_positive_no_cfg_analysis() { std::vector> v; const char* p = v.begin()->second.data(); const char* q = (*v.begin()).second.data(); const std::string& r = (*v.begin()).second; auto temporary = []() { return std::vector>{{1, "1"}}; }; const char* x = temporary().begin()->second.data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} const char* y = (*temporary().begin()).second.data(); // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} const std::string& z = (*temporary().begin()).second; // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(p, q, r, x, y, z); // cfg-note 3 {{later used here}} } } // namespace iterator_arrow namespace GH120206 { struct S { std::string_view s; }; struct [[gsl::Owner]] Q1 { const S* get() const [[clang::lifetimebound]]; }; std::string_view test1(int c, std::string_view sv) { std::string_view k = c > 1 ? Q1().get()->s : sv; if (c == 1) return c > 1 ? Q1().get()->s : sv; Q1 q; return c > 1 ? q.get()->s : sv; } struct Q2 { const S* get() const [[clang::lifetimebound]]; }; std::string_view test2(int c, std::string_view sv) { std::string_view k = c > 1 ? Q2().get()->s : sv; if (c == 1) return c > 1 ? Q2().get()->s : sv; Q2 q; return c > 1 ? q.get()->s : sv; } } // namespace GH120206 namespace GH120543 { struct S { std::string_view sv; std::string s; }; struct Q { const S* get() const [[clang::lifetimebound]]; ~Q(); }; std::string_view foo(std::string_view sv [[clang::lifetimebound]]); void test1() { std::string_view k1 = S().sv; // OK std::string_view k2 = S().s; // expected-warning {{object backing the pointer will}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} std::string_view k3 = Q().get()->sv; // OK std::string_view k4 = Q().get()->s; // expected-warning {{object backing the pointer will}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} std::string_view lb1 = foo(S().s); // expected-warning {{object backing the pointer will}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} std::string_view lb2 = foo(Q().get()->s); // expected-warning {{object backing the pointer will}} \ // cfg-warning {{local temporary object does not live long enough}} cfg-note {{destroyed here}} use(k1, k2, k3, k4, lb1, lb2); // cfg-note 4 {{later used here}} } struct Bar {}; struct Foo { std::vector v; }; Foo getFoo(); void test2() { const Foo& foo = getFoo(); const Bar& bar = foo.v.back(); // OK } struct Foo2 { std::unique_ptr bar; }; struct Test { Test(Foo2 foo) : bar(foo.bar.get()), // cfg-warning-re {{stack memory associated with parameter 'foo' may escape to the field 'bar' which will dangle. {{.*}} may have been moved}} storage(std::move(foo.bar)) {}; // cfg-note {{potentially moved here}} Bar* bar; // cfg-note {{this field dangles}} std::unique_ptr storage; }; } // namespace GH120543 namespace GH127195 { template struct StatusOr { T* operator->() [[clang::lifetimebound]]; T* value() [[clang::lifetimebound]]; }; const char* foo() { StatusOr s; return s->data(); // expected-warning {{address of stack memory associated with local variable}} \ // cfg-warning {{stack memory associated with local variable 's' is returned}} cfg-note {{returned here}} StatusOr s2; return s2->data(); StatusOr> s3; return s3.value()->value()->data(); // FIXME: nested cases are not supported now. StatusOr> s4; return s4.value()->value()->data(); } } // namespace GH127195 // Lifetimebound on definition vs declaration on implicit this param. namespace GH175391 { // Version A: Attribute on declaration only class StringA { public: const char* data() const [[clang::lifetimebound]]; // Declaration with attribute private: char buffer[32] = "hello"; }; inline const char* StringA::data() const { // Definition WITHOUT attribute return buffer; } // Version B: Attribute on definition only class StringB { public: const char* data() const; // No attribute private: char buffer[32] = "hello"; }; inline const char* StringB::data() const [[clang::lifetimebound]] { return buffer; } // Version C: Attribute on BOTH declaration and definition class StringC { public: const char* data() const [[clang::lifetimebound]]; private: char buffer[32] = "hello"; }; inline const char* StringC::data() const [[clang::lifetimebound]] { return buffer; } // TEMPLATED VERSIONS // Template Version A: Attribute on declaration only template class StringTemplateA { public: const T* data() const [[clang::lifetimebound]]; // Declaration with attribute private: T buffer[32]; }; template inline const T* StringTemplateA::data() const { // Definition WITHOUT attribute return buffer; } // Template Version B: Attribute on definition only template class StringTemplateB { public: const T* data() const; // No attribute private: T buffer[32]; }; template inline const T* StringTemplateB::data() const [[clang::lifetimebound]] { return buffer; } // Template Version C: Attribute on BOTH declaration and definition template class StringTemplateC { public: const T* data() const [[clang::lifetimebound]]; private: T buffer[32]; }; template inline const T* StringTemplateC::data() const [[clang::lifetimebound]] { return buffer; } // TEMPLATE SPECIALIZATION VERSIONS // Template predeclarations for specializations template class StringTemplateSpecA; template class StringTemplateSpecB; template class StringTemplateSpecC; // Template Specialization Version A: Attribute on declaration only - specialization template<> class StringTemplateSpecA { public: const char* data() const [[clang::lifetimebound]]; // Declaration with attribute private: char buffer[32] = "hello"; }; inline const char* StringTemplateSpecA::data() const { // Definition WITHOUT attribute return buffer; } // Template Specialization Version B: Attribute on definition only - specialization template<> class StringTemplateSpecB { public: const char* data() const; // No attribute private: char buffer[32] = "hello"; }; inline const char* StringTemplateSpecB::data() const [[clang::lifetimebound]] { return buffer; } // Template Specialization Version C: Attribute on BOTH declaration and definition - specialization template<> class StringTemplateSpecC { public: const char* data() const [[clang::lifetimebound]]; private: char buffer[32] = "hello"; }; inline const char* StringTemplateSpecC::data() const [[clang::lifetimebound]] { return buffer; } void test() { // Non-templated tests const auto ptrA = StringA().data(); // Declaration-only attribute // expected-warning {{temporary whose address is used}} const auto ptrB = StringB().data(); // Definition-only attribute // expected-warning {{temporary whose address is used}} const auto ptrC = StringC().data(); // Both have attribute // expected-warning {{temporary whose address is used}} // Templated tests (generic templates) const auto ptrTA = StringTemplateA().data(); // Declaration-only attribute // expected-warning {{temporary whose address is used}} const auto ptrTB = StringTemplateB().data(); // Definition-only attribute // expected-warning {{temporary whose address is used}} const auto ptrTC = StringTemplateC().data(); // Both have attribute // expected-warning {{temporary whose address is used}} // Template specialization tests const auto ptrTSA = StringTemplateSpecA().data(); // Declaration-only attribute // expected-warning {{temporary whose address is used}} const auto ptrTSB = StringTemplateSpecB().data(); // Definition-only attribute // expected-warning {{temporary whose address is used}} const auto ptrTSC = StringTemplateSpecC().data(); // Both have attribute // expected-warning {{temporary whose address is used}} } } // namespace GH175391 void string_insert_GH_186817() { std::string msg; msg.insert(0, std::string_view(std::string("a temporary"))); }