// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.UncountedLambdaCapturesChecker -verify %s #include "mock-types.h" namespace std { template T&& move(T& t) { return static_cast(t); } namespace ranges { template void for_each(IteratorType first, IteratorType last, CallbackType callback) { for (auto it = first; !(it == last); ++it) callback(*it); } struct all_of_impl { template constexpr bool operator()(const Collection& collection, Predicate predicate) const { for (auto it = collection.begin(); it != collection.end(); ++it) { if (!predicate(*it)) return false; } return true; } }; inline constexpr auto all_of = all_of_impl {}; } } namespace WTF { namespace Detail { template class CallableWrapperBase { public: virtual ~CallableWrapperBase() { } virtual Out call(In...) = 0; }; template class CallableWrapper; template class CallableWrapper : public CallableWrapperBase { public: explicit CallableWrapper(CallableType& callable) : m_callable(callable) { } Out call(In... in) final { return m_callable(in...); } private: CallableType m_callable; }; } // namespace Detail template class Function; template Function adopt(Detail::CallableWrapperBase*); template class Function { public: using Impl = Detail::CallableWrapperBase; Function() = default; template Function(FunctionType f) : m_callableWrapper(new Detail::CallableWrapper(f)) { } Out operator()(In... in) const { return m_callableWrapper->call(in...); } explicit operator bool() const { return !!m_callableWrapper; } private: enum AdoptTag { Adopt }; Function(Impl* impl, AdoptTag) : m_callableWrapper(impl) { } friend Function adopt(Impl*); std::unique_ptr m_callableWrapper; }; template Function adopt(Detail::CallableWrapperBase* impl) { return Function(impl, Function::Adopt); } template class HashMap { public: HashMap(); HashMap([[clang::noescape]] const Function&); void ensure(const KeyType&, [[clang::noescape]] const Function&); bool operator+([[clang::noescape]] const Function&) const; static void ifAny(HashMap, [[clang::noescape]] const Function&); private: ValueType* m_table { nullptr }; }; } // namespace WTF struct A { static void b(); }; RefCountable* make_obj(); void someFunction(); template void call(Callback callback) { someFunction(); callback(); } void callAsync(const WTF::Function&); void raw_ptr() { RefCountable* ref_countable = make_obj(); auto foo1 = [ref_countable](){ // expected-warning@-1{{Captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} ref_countable->method(); }; auto foo2 = [&ref_countable](){ // expected-warning@-1{{Captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} ref_countable->method(); }; auto foo3 = [&](){ ref_countable->method(); // expected-warning@-1{{Implicitly captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} ref_countable = nullptr; }; auto foo4 = [=](){ ref_countable->method(); // expected-warning@-1{{Implicitly captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} }; call(foo1); call(foo2); call(foo3); call(foo4); // Confirm that the checker respects [[clang::suppress]]. RefCountable* suppressed_ref_countable = nullptr; [[clang::suppress]] auto foo5 = [suppressed_ref_countable](){}; // no warning. call(foo5); } void references() { RefCountable automatic; RefCountable& ref_countable_ref = automatic; auto foo1 = [ref_countable_ref](){ ref_countable_ref.constMethod(); }; auto foo2 = [&ref_countable_ref](){ ref_countable_ref.method(); }; // expected-warning@-1{{Captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} auto foo3 = [&](){ ref_countable_ref.method(); }; // expected-warning@-1{{Implicitly captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} auto foo4 = [=](){ ref_countable_ref.constMethod(); }; call(foo1); call(foo2); call(foo3); call(foo4); } void quiet() { // This code is not expected to trigger any warnings. { RefCountable automatic; RefCountable &ref_countable_ref = automatic; } auto foo3 = [&]() {}; auto foo4 = [=]() {}; call(foo3); call(foo4); RefCountable *ref_countable = nullptr; } template void map(RefCountable* start, [[clang::noescape]] Callback&& callback) { while (start) { callback(*start); start = start->next(); } } template void doubleMap(RefCountable* start, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2) { while (start) { callback1(*start); callback2(*start); start = start->next(); } } void noescape_lambda() { RefCountable* someObj = make_obj(); RefCountable* otherObj = make_obj(); map(make_obj(), [&](RefCountable& obj) { otherObj->method(); }); doubleMap(make_obj(), [&](RefCountable& obj) { otherObj->method(); }, [&](RefCountable& obj) { otherObj->method(); // expected-warning@-1{{Implicitly captured raw-pointer 'otherObj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} }); ([&] { someObj->method(); })(); } void lambda_capture_param(RefCountable* obj) { auto someLambda = [&]() { obj->method(); }; someLambda(); someLambda(); } struct RefCountableWithLambdaCapturingThis { void ref() const; void deref() const; void nonTrivial(); void method_captures_this_safe() { auto lambda = [&]() { nonTrivial(); }; lambda(); } void method_captures_this_unsafe() { auto lambda = [&]() { nonTrivial(); // expected-warning@-1{{Implicitly captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} }; call(lambda); } void method_captures_this_unsafe_capture_local_var_explicitly() { RefCountable* x = make_obj(); call([this, protectedThis = RefPtr { this }, x]() { // expected-warning@-1{{Captured raw-pointer 'x' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} nonTrivial(); x->method(); }); } void method_captures_this_with_other_protected_var() { RefCountable* x = make_obj(); call([this, protectedX = RefPtr { x }]() { // expected-warning@-1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} nonTrivial(); protectedX->method(); }); } void method_captures_this_unsafe_capture_local_var_explicitly_with_deref() { RefCountable* x = make_obj(); call([this, protectedThis = Ref { *this }, x]() { // expected-warning@-1{{Captured raw-pointer 'x' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} nonTrivial(); x->method(); }); } void method_captures_this_unsafe_local_var_via_vardecl() { RefCountable* x = make_obj(); auto lambda = [this, protectedThis = Ref { *this }, x]() { // expected-warning@-1{{Captured raw-pointer 'x' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} nonTrivial(); x->method(); }; call(lambda); } void method_captures_this_with_guardian() { auto lambda = [this, protectedThis = Ref { *this }]() { nonTrivial(); }; call(lambda); } void method_captures_this_with_guardian_refptr() { auto lambda = [this, protectedThis = RefPtr { &*this }]() { nonTrivial(); }; call(lambda); } void forEach(const WTF::Function&); void method_captures_this_with_lambda_with_no_escape() { auto run = [&]([[clang::noescape]] const WTF::Function& func) { forEach(func); }; run([&](RefCountable&) { nonTrivial(); }); } static void callLambda([[clang::noescape]] const WTF::Function()>&); void method_captures_this_in_template_method() { RefCountable* obj = make_obj(); WTF::HashMap> nextMap; nextMap.ensure(3, [&] { return obj->next(); }); nextMap+[&] { return obj->next(); }; WTF::HashMap>::ifAny(nextMap, [&](auto& item) -> bool { return item->next() && obj->next(); }); callLambda([&]() -> RefPtr { return obj->next(); }); WTF::HashMap> anotherMap([&] { return obj->next(); }); } void callAsyncNoescape([[clang::noescape]] WTF::Function&&); void method_temp_lambda(RefCountable* obj) { callAsyncNoescape([this, otherObj = RefPtr { obj }](auto& obj) { return otherObj == &obj; }); } void method_nested_lambda() { callAsync([this, protectedThis = Ref { *this }] { callAsync([this, protectedThis = static_cast&&>(protectedThis)] { nonTrivial(); }); }); } void method_nested_lambda2() { callAsync([this, protectedThis = RefPtr { this }] { callAsync([this, protectedThis = std::move(*protectedThis)] { nonTrivial(); }); }); } void method_nested_lambda3() { callAsync([this, protectedThis = RefPtr { this }] { callAsync([this] { // expected-warning@-1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} nonTrivial(); }); }); } }; struct NonRefCountableWithLambdaCapturingThis { void nonTrivial(); void method_captures_this_safe() { auto lambda = [&]() { nonTrivial(); }; lambda(); } void method_captures_this_unsafe() { auto lambda = [&]() { nonTrivial(); }; call(lambda); } }; void trivial_lambda() { RefCountable* ref_countable = make_obj(); auto trivial_lambda = [&]() { return ref_countable->trivial(); }; trivial_lambda(); } bool call_lambda_var_decl() { RefCountable* ref_countable = make_obj(); auto lambda1 = [&]() -> bool { return ref_countable->next(); }; auto lambda2 = [=]() -> bool { return ref_countable->next(); }; return lambda1() && lambda2(); } void lambda_with_args(RefCountable* obj) { auto trivial_lambda = [&](int v) { obj->method(); }; trivial_lambda(1); } void callFunctionOpaque(WTF::Function&&); void callFunction(WTF::Function&& function) { someFunction(); function(); } void lambda_converted_to_function(RefCountable* obj) { callFunction([&]() { obj->method(); // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} }); callFunctionOpaque([&]() { obj->method(); // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} }); } void capture_copy_in_lambda(CheckedObj& checked) { callFunctionOpaque([checked]() mutable { checked.method(); }); auto* ptr = &checked; callFunctionOpaque([ptr]() mutable { // expected-warning@-1{{Captured raw-pointer 'ptr' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} ptr->method(); }); } class Iterator { public: Iterator(void* array, unsigned long sizeOfElement, unsigned int index); Iterator(const Iterator&); Iterator& operator=(const Iterator&); bool operator==(const Iterator&); Iterator& operator++(); int& operator*(); private: void* current { nullptr }; unsigned long sizeOfElement { 0 }; }; void ranges_for_each(RefCountable* obj) { int array[] = { 1, 2, 3, 4, 5 }; std::ranges::for_each(Iterator(array, sizeof(*array), 0), Iterator(array, sizeof(*array), 5), [&](int& item) { obj->method(); ++item; }); } class IntCollection { public: int* begin(); int* end(); const int* begin() const; const int* end() const; }; class RefCountedObj { public: void ref(); void deref(); bool allOf(const IntCollection&); bool isMatch(int); void call() const; void callLambda([[clang::noescape]] const WTF::Function& callback) const; void doSomeWork() const; }; bool RefCountedObj::allOf(const IntCollection& collection) { return std::ranges::all_of(collection, [&](auto& number) { return isMatch(number); }); } void RefCountedObj::callLambda([[clang::noescape]] const WTF::Function& callback) const { callback(); } void RefCountedObj::call() const { auto lambda = [&] { doSomeWork(); }; callLambda(lambda); }