| // RUN: %clang_cc1 --std=c++20 -fsyntax-only -verify -Wdangling-capture %s |
| |
| #include "Inputs/lifetime-analysis.h" |
| |
| // **************************************************************************** |
| // Capture an integer |
| // **************************************************************************** |
| namespace capture_int { |
| struct X {} x; |
| void captureInt(const int &i [[clang::lifetime_capture_by(x)]], X &x); |
| void captureRValInt(int &&i [[clang::lifetime_capture_by(x)]], X &x); |
| void noCaptureInt(int i [[clang::lifetime_capture_by(x)]], X &x); |
| |
| void use() { |
| int local; |
| captureInt(1, // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| x); |
| captureRValInt(1, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| captureInt(local, x); |
| noCaptureInt(1, x); |
| noCaptureInt(local, x); |
| } |
| } // namespace capture_int |
| |
| // **************************************************************************** |
| // Capture std::string (gsl owner types) |
| // **************************************************************************** |
| namespace capture_string { |
| struct X {} x; |
| void captureString(const std::string &s [[clang::lifetime_capture_by(x)]], X &x); |
| void captureRValString(std::string &&s [[clang::lifetime_capture_by(x)]], X &x); |
| |
| void use() { |
| std::string local_string; |
| captureString(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| captureString(local_string, x); |
| captureRValString(std::move(local_string), x); |
| captureRValString(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| } |
| } // namespace capture_string |
| |
| // **************************************************************************** |
| // Capture std::string_view (gsl pointer types) |
| // **************************************************************************** |
| namespace capture_string_view { |
| struct X {} x; |
| void captureStringView(std::string_view s [[clang::lifetime_capture_by(x)]], X &x); |
| void captureRValStringView(std::string_view &&sv [[clang::lifetime_capture_by(x)]], X &x); |
| void noCaptureStringView(std::string_view sv, X &x); |
| |
| std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); |
| std::string_view getNotLifetimeBoundView(const std::string& s); |
| const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]); |
| const std::string& getLifetimeBoundString(std::string_view sv [[clang::lifetimebound]]); |
| |
| void use() { |
| std::string_view local_string_view; |
| std::string local_string; |
| captureStringView(local_string_view, x); |
| captureStringView(std::string(), // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| x); |
| |
| captureStringView(getLifetimeBoundView(local_string), x); |
| captureStringView(getNotLifetimeBoundView(std::string()), x); |
| captureRValStringView(std::move(local_string_view), x); |
| captureRValStringView(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| captureRValStringView(std::string_view{"abcd"}, x); |
| |
| noCaptureStringView(local_string_view, x); |
| noCaptureStringView(std::string(), x); |
| |
| // With lifetimebound functions. |
| captureStringView(getLifetimeBoundView( |
| std::string() // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| ), x); |
| captureRValStringView(getLifetimeBoundView(local_string), x); |
| captureRValStringView(getLifetimeBoundView(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| captureRValStringView(getNotLifetimeBoundView(std::string()), x); |
| noCaptureStringView(getLifetimeBoundView(std::string()), x); |
| captureStringView(getLifetimeBoundString(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| captureStringView(getLifetimeBoundString(getLifetimeBoundView(std::string())), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| captureStringView(getLifetimeBoundString(getLifetimeBoundString( |
| std::string() // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| )), x); |
| } |
| } // namespace capture_string_view |
| |
| // **************************************************************************** |
| // Capture pointer (eg: std::string*) |
| // **************************************************************************** |
| const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]); |
| const std::string* getNotLifetimeBoundPointer(const std::string &s); |
| |
| namespace capture_pointer { |
| struct X {} x; |
| void capturePointer(const std::string* sp [[clang::lifetime_capture_by(x)]], X &x); |
| void use() { |
| capturePointer(getLifetimeBoundPointer(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| capturePointer(getLifetimeBoundPointer(*getLifetimeBoundPointer( |
| std::string() // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| )), x); |
| capturePointer(getNotLifetimeBoundPointer(std::string()), x); |
| |
| } |
| } // namespace capture_pointer |
| |
| // **************************************************************************** |
| // Arrays and initializer lists. |
| // **************************************************************************** |
| namespace init_lists { |
| struct X {} x; |
| void captureVector(const std::vector<int> &a [[clang::lifetime_capture_by(x)]], X &x); |
| void captureArray(int array [[clang::lifetime_capture_by(x)]] [2], X &x); |
| void captureInitList(std::initializer_list<int> abc [[clang::lifetime_capture_by(x)]], X &x); |
| |
| |
| std::initializer_list<int> getLifetimeBoundInitList(std::initializer_list<int> abc [[clang::lifetimebound]]); |
| |
| void use() { |
| captureVector({1, 2, 3}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| captureVector(std::vector<int>{}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| std::vector<int> local_vector; |
| captureVector(local_vector, x); |
| int local_array[2]; |
| captureArray(local_array, x); |
| captureInitList({1, 2}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| captureInitList(getLifetimeBoundInitList({1, 2}), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| } |
| } // namespace init_lists |
| |
| // **************************************************************************** |
| // Implicit object param 'this' is captured |
| // **************************************************************************** |
| namespace this_is_captured { |
| struct X {} x; |
| struct S { |
| void capture(X &x) [[clang::lifetime_capture_by(x)]]; |
| }; |
| void use() { |
| S{}.capture(x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| S s; |
| s.capture(x); |
| } |
| } // namespace this_is_captured |
| |
| namespace temporary_capturing_object { |
| struct S { |
| void add(const int& x [[clang::lifetime_capture_by(this)]]); |
| }; |
| |
| void test() { |
| // We still give an warning even the capturing object is a temoprary. |
| // It is possible that the capturing object uses the captured object in its |
| // destructor. |
| S().add(1); // expected-warning {{object whose reference is captured}} |
| S{}.add(1); // expected-warning {{object whose reference is captured}} |
| } |
| } // namespace ignore_temporary_class_object |
| |
| // **************************************************************************** |
| // Capture by Global and Unknown. |
| // **************************************************************************** |
| namespace capture_by_global_unknown { |
| void captureByGlobal(std::string_view s [[clang::lifetime_capture_by(global)]]); |
| void captureByUnknown(std::string_view s [[clang::lifetime_capture_by(unknown)]]); |
| |
| std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); |
| |
| void use() { |
| std::string_view local_string_view; |
| std::string local_string; |
| // capture by global. |
| captureByGlobal(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}} |
| captureByGlobal(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}} |
| captureByGlobal(local_string); |
| captureByGlobal(local_string_view); |
| |
| // capture by unknown. |
| captureByUnknown(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}} |
| captureByUnknown(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}} |
| captureByUnknown(local_string); |
| captureByUnknown(local_string_view); |
| } |
| } // namespace capture_by_global_unknown |
| |
| // **************************************************************************** |
| // Member functions: Capture by 'this' |
| // **************************************************************************** |
| namespace capture_by_this { |
| struct S { |
| void captureInt(const int& x [[clang::lifetime_capture_by(this)]]); |
| void captureView(std::string_view sv [[clang::lifetime_capture_by(this)]]); |
| }; |
| std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); |
| std::string_view getNotLifetimeBoundView(const std::string& s); |
| const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]); |
| |
| void use() { |
| S s; |
| s.captureInt(1); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}} |
| s.captureView(std::string()); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}} |
| s.captureView(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}} |
| s.captureView(getLifetimeBoundString(std::string())); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}} |
| s.captureView(getNotLifetimeBoundView(std::string())); |
| } |
| } // namespace capture_by_this |
| |
| // **************************************************************************** |
| // Struct with field as a reference |
| // **************************************************************************** |
| namespace reference_field { |
| struct X {} x; |
| struct Foo { |
| const int& b; |
| }; |
| void captureField(Foo param [[clang::lifetime_capture_by(x)]], X &x); |
| void use() { |
| captureField(Foo{ |
| 1 // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| }, x); |
| int local; |
| captureField(Foo{local}, x); |
| } |
| } // namespace reference_field |
| |
| // **************************************************************************** |
| // Capture default argument. |
| // **************************************************************************** |
| namespace default_arg { |
| struct X {} x; |
| void captureDefaultArg(X &x, std::string_view s [[clang::lifetime_capture_by(x)]] = std::string()); |
| |
| std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); |
| |
| void useCaptureDefaultArg() { |
| X x; |
| captureDefaultArg(x); // FIXME: Diagnose temporary default arg. |
| captureDefaultArg(x, std::string("temp")); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| captureDefaultArg(x, getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}} |
| std::string local; |
| captureDefaultArg(x, local); |
| } |
| } // namespace default_arg |
| |
| // **************************************************************************** |
| // Container: *No* distinction between pointer-like and other element type |
| // **************************************************************************** |
| namespace containers_no_distinction { |
| template<class T> |
| struct MySet { |
| void insert(T&& t [[clang::lifetime_capture_by(this)]]); |
| void insert(const T& t [[clang::lifetime_capture_by(this)]]); |
| }; |
| void user_defined_containers() { |
| MySet<int> set_of_int; |
| set_of_int.insert(1); // expected-warning {{object whose reference is captured by 'set_of_int' will be destroyed at the end of the full-expression}} |
| MySet<std::string_view> set_of_sv; |
| set_of_sv.insert(std::string()); // expected-warning {{object whose reference is captured by 'set_of_sv' will be destroyed at the end of the full-expression}} |
| set_of_sv.insert(std::string_view()); |
| } |
| } // namespace containers_no_distinction |
| |
| // **************************************************************************** |
| // Container: Different for pointer-like and other element type. |
| // **************************************************************************** |
| namespace conatiners_with_different { |
| template<typename T> struct IsPointerLikeTypeImpl : std::false_type {}; |
| template<> struct IsPointerLikeTypeImpl<std::string_view> : std::true_type {}; |
| template<typename T> concept IsPointerLikeType = std::is_pointer<T>::value || IsPointerLikeTypeImpl<T>::value; |
| |
| template<class T> struct MyVector { |
| void push_back(T&& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>; |
| void push_back(const T& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>; |
| |
| void push_back(T&& t) requires (!IsPointerLikeType<T>); |
| void push_back(const T& t) requires (!IsPointerLikeType<T>); |
| }; |
| |
| std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); |
| |
| void use_container() { |
| std::string local; |
| |
| MyVector<std::string> vector_of_string; |
| vector_of_string.push_back(std::string()); // Ok. |
| |
| MyVector<std::string_view> vector_of_view; |
| vector_of_view.push_back(std::string()); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}} |
| vector_of_view.push_back(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}} |
| |
| MyVector<const std::string*> vector_of_pointer; |
| vector_of_pointer.push_back(getLifetimeBoundPointer(std::string())); // expected-warning {{object whose reference is captured by 'vector_of_pointer' will be destroyed at the end of the full-expression}} |
| vector_of_pointer.push_back(getLifetimeBoundPointer(*getLifetimeBoundPointer(std::string()))); // expected-warning {{object whose reference is captured by 'vector_of_pointer' will be destroyed at the end of the full-expression}} |
| vector_of_pointer.push_back(getLifetimeBoundPointer(local)); |
| vector_of_pointer.push_back(getNotLifetimeBoundPointer(std::string())); |
| } |
| |
| // **************************************************************************** |
| // Container: For user defined view types |
| // **************************************************************************** |
| struct [[gsl::Pointer()]] MyStringView : public std::string_view { |
| MyStringView(); |
| MyStringView(std::string_view&&); |
| MyStringView(const MyStringView&); |
| MyStringView(const std::string&); |
| }; |
| template<> struct IsPointerLikeTypeImpl<MyStringView> : std::true_type {}; |
| |
| std::optional<std::string_view> getOptionalSV(); |
| std::optional<std::string> getOptionalS(); |
| std::optional<MyStringView> getOptionalMySV(); |
| MyStringView getMySV(); |
| |
| class MyStringViewNotPointer : public std::string_view {}; |
| std::optional<MyStringViewNotPointer> getOptionalMySVNotP(); |
| MyStringViewNotPointer getMySVNotP(); |
| |
| std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); |
| std::string_view getNotLifetimeBoundView(const std::string& s); |
| const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]); |
| const std::string& getLifetimeBoundString(std::string_view sv [[clang::lifetimebound]]); |
| |
| void use_my_view() { |
| std::string local; |
| MyVector<MyStringView> vector_of_my_view; |
| vector_of_my_view.push_back(getMySV()); |
| vector_of_my_view.push_back(MyStringView{}); |
| vector_of_my_view.push_back(std::string_view{}); |
| vector_of_my_view.push_back(std::string{}); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}} |
| vector_of_my_view.push_back(getLifetimeBoundView(std::string{})); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}} |
| vector_of_my_view.push_back(getLifetimeBoundString(getLifetimeBoundView(std::string{}))); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}} |
| vector_of_my_view.push_back(getNotLifetimeBoundView(getLifetimeBoundString(getLifetimeBoundView(std::string{})))); |
| |
| // Use with container of other view types. |
| MyVector<std::string_view> vector_of_view; |
| vector_of_view.push_back(getMySV()); |
| vector_of_view.push_back(getMySVNotP()); |
| } |
| |
| // **************************************************************************** |
| // Container: Use with std::optional<view> (owner<pointer> types) |
| // **************************************************************************** |
| void use_with_optional_view() { |
| MyVector<std::string_view> vector_of_view; |
| |
| std::optional<std::string_view> optional_of_view; |
| vector_of_view.push_back(optional_of_view.value()); |
| vector_of_view.push_back(getOptionalS().value()); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}} |
| |
| vector_of_view.push_back(getOptionalSV().value()); |
| vector_of_view.push_back(getOptionalMySV().value()); |
| vector_of_view.push_back(getOptionalMySVNotP().value()); |
| } |
| } // namespace conatiners_with_different |
| |
| // **************************************************************************** |
| // Capture 'temporary' views |
| // **************************************************************************** |
| namespace temporary_views { |
| void capture1(std::string_view s [[clang::lifetime_capture_by(x)]], std::vector<std::string_view>& x); |
| |
| // Intended to capture the "string_view" itself |
| void capture2(const std::string_view& s [[clang::lifetime_capture_by(x)]], std::vector<std::string_view*>& x); |
| // Intended to capture the pointee of the "string_view" |
| void capture3(const std::string_view& s [[clang::lifetime_capture_by(x)]], std::vector<std::string_view>& x); |
| |
| void use() { |
| std::vector<std::string_view> x1; |
| capture1(std::string(), x1); // expected-warning {{object whose reference is captured by 'x1' will be destroyed at the end of the full-expression}} |
| capture1(std::string_view(), x1); |
| |
| std::vector<std::string_view*> x2; |
| // Clang considers 'const std::string_view&' to refer to the owner |
| // 'std::string' and not 'std::string_view'. Therefore no diagnostic here. |
| capture2(std::string_view(), x2); |
| capture2(std::string(), x2); // expected-warning {{object whose reference is captured by 'x2' will be destroyed at the end of the full-expression}} |
| |
| std::vector<std::string_view> x3; |
| capture3(std::string_view(), x3); |
| capture3(std::string(), x3); // expected-warning {{object whose reference is captured by 'x3' will be destroyed at the end of the full-expression}} |
| } |
| } // namespace temporary_views |
| |
| // **************************************************************************** |
| // Inferring annotation for STL containers |
| // **************************************************************************** |
| namespace inferred_capture_by { |
| const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]); |
| const std::string* getNotLifetimeBoundPointer(const std::string &s); |
| |
| std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); |
| std::string_view getNotLifetimeBoundView(const std::string& s); |
| void use() { |
| std::string local; |
| std::vector<std::string_view> views; |
| views.push_back(std::string()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}} |
| views.insert(views.begin(), |
| std::string()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}} |
| views.push_back(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}} |
| views.push_back(getNotLifetimeBoundView(std::string())); |
| views.push_back(local); |
| views.insert(views.end(), local); |
| |
| std::vector<std::string> strings; |
| strings.push_back(std::string()); |
| strings.insert(strings.begin(), std::string()); |
| |
| std::vector<const std::string*> pointers; |
| pointers.push_back(getLifetimeBoundPointer(std::string())); |
| pointers.push_back(&local); |
| } |
| |
| namespace with_span { |
| // Templated view types. |
| template<typename T> |
| struct [[gsl::Pointer]] Span { |
| Span(const std::vector<T> &V); |
| }; |
| |
| void use() { |
| std::vector<Span<int>> spans; |
| spans.push_back(std::vector<int>{1, 2, 3}); // expected-warning {{object whose reference is captured by 'spans' will be destroyed at the end of the full-expression}} |
| std::vector<int> local; |
| spans.push_back(local); |
| } |
| } // namespace with_span |
| } // namespace inferred_capture_by |
| |
| namespace on_constructor { |
| struct T { |
| T(const int& t [[clang::lifetime_capture_by(this)]]); |
| }; |
| struct T2 { |
| T2(const int& t [[clang::lifetime_capture_by(x)]], int& x); |
| }; |
| struct T3 { |
| T3(const T& t [[clang::lifetime_capture_by(this)]]); |
| }; |
| |
| int foo(const T& t); |
| int bar(const T& t[[clang::lifetimebound]]); |
| |
| void test() { |
| auto x = foo(T(1)); // OK. no diagnosic |
| T(1); // OK. no diagnostic |
| T t(1); // expected-warning {{temporary whose address is used}} |
| auto y = bar(T(1)); // expected-warning {{temporary whose address is used}} |
| T3 t3(T(1)); // expected-warning {{temporary whose address is used}} |
| |
| int a; |
| T2(1, a); // expected-warning {{object whose reference is captured by}} |
| } |
| } // namespace on_constructor |
| |
| namespace GH121391 { |
| |
| struct Foo {}; |
| |
| template <typename T> |
| struct Container { |
| const T& tt() [[clang::lifetimebound]]; |
| }; |
| template<typename T> |
| struct StatusOr { |
| T* get() [[clang::lifetimebound]]; |
| }; |
| StatusOr<Container<const Foo*>> getContainer(); |
| |
| void test() { |
| std::vector<const Foo*> vv; |
| vv.push_back(getContainer().get()->tt()); // OK |
| } |
| |
| } // namespace GH121391 |