// RUN: %clang_cc1 -fsyntax-only -Wdangling -Wdangling-field -Wreturn-stack-address -verify %s struct [[gsl::Owner(int)]] MyIntOwner { 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(); operator MyLongPointerFromConversion(); long &operator*(); MyIntPointer releaseAsMyPointer(); long *releaseAsRawPointer(); }; 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; // TODO (void)p; } struct DanglingGslPtrField { MyIntPointer p; // expected-note {{pointer member declared here}} MyLongPointerFromConversion p2; // expected-note {{pointer member declared here}} DanglingGslPtrField(int i) : p(&i) {} // TODO 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}} 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}} }; MyIntPointer danglingGslPtrFromLocal() { int j; return &j; // TODO } 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}} } MyLongPointerFromConversion daglingGslPtrFromLocalOwnerConv() { MyLongOwnerWithConversion localOwner; return localOwner; // expected-warning {{address of stack memory associated with local variable 'localOwner' returned}} } MyIntPointer danglingGslPtrFromTemporary() { return MyIntOwner{}; // expected-warning {{returning address of local temporary object}} } MyIntOwner makeTempOwner(); MyIntPointer danglingGslPtrFromTemporary2() { return makeTempOwner(); // expected-warning {{returning address of local temporary object}} } MyLongPointerFromConversion danglingGslPtrFromTemporaryConv() { return MyLongOwnerWithConversion{}; // expected-warning {{returning address of local temporary object}} } 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}} p = MyIntOwner{}; // TODO ? global = MyIntOwner{}; // TODO ? MyLongPointerFromConversion p2 = MyLongOwnerWithConversion{}; // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} p2 = MyLongOwnerWithConversion{}; // TODO ? global2 = MyLongOwnerWithConversion{}; // TODO ? } namespace __gnu_cxx { template struct basic_iterator { basic_iterator operator++(); T& operator*() const; T* operator->() const; }; template bool operator!=(basic_iterator, basic_iterator); } namespace std { template struct remove_reference { typedef T type; }; template struct remove_reference { typedef T type; }; template struct remove_reference { typedef T type; }; template typename remove_reference::type &&move(T &&t) noexcept; template auto data(const C &c) -> decltype(c.data()); template auto begin(C &c) -> decltype(c.begin()); template T *begin(T (&array)[N]); template struct vector { typedef __gnu_cxx::basic_iterator iterator; iterator begin(); iterator end(); const T *data() const; T &at(int n); }; template struct basic_string_view { basic_string_view(const T *); const T *begin() const; }; template struct basic_string { basic_string(); basic_string(const T *); const T *c_str() const; operator basic_string_view () const; }; template struct unique_ptr { T &operator*(); T *get() const; }; template struct optional { optional(); optional(const T&); T &operator*() &; T &&operator*() &&; T &value() &; T &&value() &&; }; template struct stack { T &top(); }; struct any {}; template T any_cast(const any& operand); template struct reference_wrapper { template reference_wrapper(U &&); }; template reference_wrapper ref(T& t) noexcept; } 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}} (void)it; } std::vector::iterator modelIteratorReturn() { return std::vector().begin(); // expected-warning {{returning address of local temporary object}} } const int *modelFreeFunctions() { return std::data(std::vector()); // expected-warning {{returning address of local temporary object}} } int &modelAnyCast() { return std::any_cast(std::any{}); // expected-warning {{returning reference to local temporary object}} } 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}} } int &danglingRawPtrFromLocal2() { std::optional o; return o.value(); // expected-warning {{reference to stack memory associated with local variable 'o' returned}} } int &danglingRawPtrFromLocal3() { std::optional o; return *o; // expected-warning {{reference to stack memory associated with local variable 'o' returned}} } const char *danglingRawPtrFromTemp() { return std::basic_string().c_str(); // expected-warning {{returning address of local temporary object}} } std::unique_ptr getUniquePtr(); int *danglingUniquePtrFromTemp() { return getUniquePtr().get(); // expected-warning {{returning address of local temporary object}} } int *danglingUniquePtrFromTemp2() { return std::unique_ptr().get(); // expected-warning {{returning address of local temporary object}} } void danglingReferenceFromTempOwner() { int &&r = *std::optional(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} int &&r2 = *std::optional(5); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} int &&r3 = std::optional(5).value(); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} int &r4 = std::vector().at(3); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}} } 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}} ; } int &usedToBeFalsePositive(std::vector &v) { std::vector::iterator it = v.begin(); int& value = *it; return value; // ok } int &doNotFollowReferencesForLocalOwner() { std::unique_ptr localOwner; int &p = *localOwner.get(); // In real world code localOwner is usually moved here. return p; // ok } const char *trackThroughMultiplePointer() { return std::basic_string_view(std::basic_string()).begin(); // expected-warning {{returning address of local temporary object}} } struct X { X(std::unique_ptr up) : pointee(*up), pointee2(up.get()), pointer(std::move(up)) {} int &pointee; int *pointee2; std::unique_ptr pointer; }; 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}} } 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 } 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); }