655 lines · cpp
1// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.UncountedLambdaCapturesChecker -verify %s2 3#include "mock-types.h"4 5namespace std {6 7template <typename T>8T&& move(T& t) {9 return static_cast<T&&>(t);10}11 12namespace ranges {13 14template<typename IteratorType, typename CallbackType>15void for_each(IteratorType first, IteratorType last, CallbackType callback) {16 for (auto it = first; !(it == last); ++it)17 callback(*it);18}19 20struct all_of_impl {21 template <typename Collection, typename Predicate>22 constexpr bool operator()(const Collection& collection, Predicate predicate) const {23 for (auto it = collection.begin(); it != collection.end(); ++it) {24 if (!predicate(*it))25 return false;26 }27 return true;28 }29};30inline constexpr auto all_of = all_of_impl {};31 32}33 34}35 36namespace WTF {37 38namespace Detail {39 40template<typename Out, typename... In>41class CallableWrapperBase {42public:43 virtual ~CallableWrapperBase() { }44 virtual Out call(In...) = 0;45};46 47template<typename, typename, typename...> class CallableWrapper;48 49template<typename CallableType, typename Out, typename... In>50class CallableWrapper : public CallableWrapperBase<Out, In...> {51public:52 explicit CallableWrapper(CallableType& callable)53 : m_callable(callable) { }54 Out call(In... in) final { return m_callable(in...); }55 56private:57 CallableType m_callable;58};59 60} // namespace Detail61 62template<typename> class Function;63 64template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>*);65 66template <typename Out, typename... In>67class Function<Out(In...)> {68public:69 using Impl = Detail::CallableWrapperBase<Out, In...>;70 71 Function() = default;72 73 template<typename FunctionType>74 Function(FunctionType f)75 : m_callableWrapper(new Detail::CallableWrapper<FunctionType, Out, In...>(f)) { }76 77 Out operator()(In... in) const { return m_callableWrapper->call(in...); }78 explicit operator bool() const { return !!m_callableWrapper; }79 80private:81 enum AdoptTag { Adopt };82 Function(Impl* impl, AdoptTag)83 : m_callableWrapper(impl)84 {85 }86 87 friend Function adopt<Out, In...>(Impl*);88 89 std::unique_ptr<Impl> m_callableWrapper;90};91 92template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>* impl)93{94 return Function<Out(In...)>(impl, Function<Out(In...)>::Adopt);95}96 97template <typename KeyType, typename ValueType>98class HashMap {99public:100 HashMap();101 HashMap([[clang::noescape]] const Function<ValueType()>&);102 void ensure(const KeyType&, [[clang::noescape]] const Function<ValueType()>&);103 bool operator+([[clang::noescape]] const Function<ValueType()>&) const;104 static void ifAny(HashMap, [[clang::noescape]] const Function<bool(ValueType)>&);105 106private:107 ValueType* m_table { nullptr };108};109 110class ScopeExit final {111public:112 template<typename ExitFunctionParameter>113 explicit ScopeExit(ExitFunctionParameter&& exitFunction)114 : m_exitFunction(std::move(exitFunction)) {115 }116 117 ScopeExit(ScopeExit&& other)118 : m_exitFunction(std::move(other.m_exitFunction))119 , m_execute(std::move(other.m_execute)) {120 }121 122 ~ScopeExit() {123 if (m_execute)124 m_exitFunction();125 }126 127 WTF::Function<void()> take() {128 m_execute = false;129 return std::move(m_exitFunction);130 }131 132 void release() { m_execute = false; }133 134 ScopeExit(const ScopeExit&) = delete;135 ScopeExit& operator=(const ScopeExit&) = delete;136 ScopeExit& operator=(ScopeExit&&) = delete;137 138private:139 WTF::Function<void()> m_exitFunction;140 bool m_execute { true };141};142 143template<typename ExitFunction> ScopeExit makeScopeExit(ExitFunction&&);144template<typename ExitFunction>145ScopeExit makeScopeExit(ExitFunction&& exitFunction)146{147 return ScopeExit(std::move(exitFunction));148}149 150// Visitor adapted from http://stackoverflow.com/questions/25338795/is-there-a-name-for-this-tuple-creation-idiom151 152template<class A, class... B> struct Visitor : Visitor<A>, Visitor<B...> {153 Visitor(A a, B... b)154 : Visitor<A>(a)155 , Visitor<B...>(b...)156 {157 }158 159 using Visitor<A>::operator ();160 using Visitor<B...>::operator ();161};162 163template<class A> struct Visitor<A> : A {164 Visitor(A a)165 : A(a)166 {167 }168 169 using A::operator();170};171 172template<class... F> Visitor<F...> makeVisitor(F... f)173{174 return Visitor<F...>(f...);175}176 177void opaqueFunction();178template <typename Visitor, typename... Variants> void visit(Visitor&& v, Variants&&... values)179{180 opaqueFunction();181}182 183} // namespace WTF184 185struct A {186 static void b();187};188 189RefCountable* make_obj();190 191void someFunction();192template <typename Callback> void call(Callback callback) {193 someFunction();194 callback();195}196void callAsync(const WTF::Function<void()>&);197 198void raw_ptr() {199 RefCountable* ref_countable = make_obj();200 auto foo1 = [ref_countable](){201 // expected-warning@-1{{Captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}202 ref_countable->method();203 };204 auto foo2 = [&ref_countable](){205 // expected-warning@-1{{Captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}206 ref_countable->method();207 };208 auto foo3 = [&](){209 ref_countable->method();210 // expected-warning@-1{{Implicitly captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}211 ref_countable = nullptr;212 };213 214 auto foo4 = [=](){215 ref_countable->method();216 // expected-warning@-1{{Implicitly captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}217 };218 219 call(foo1);220 call(foo2);221 call(foo3);222 call(foo4);223 224 // Confirm that the checker respects [[clang::suppress]].225 RefCountable* suppressed_ref_countable = nullptr;226 [[clang::suppress]] auto foo5 = [suppressed_ref_countable](){};227 // no warning.228 call(foo5);229}230 231void references() {232 RefCountable automatic;233 RefCountable& ref_countable_ref = automatic;234 auto foo1 = [ref_countable_ref](){ ref_countable_ref.constMethod(); };235 auto foo2 = [&ref_countable_ref](){ ref_countable_ref.method(); };236 // expected-warning@-1{{Captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}237 auto foo3 = [&](){ ref_countable_ref.method(); };238 // expected-warning@-1{{Implicitly captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}239 auto foo4 = [=](){ ref_countable_ref.constMethod(); };240 241 call(foo1);242 call(foo2);243 call(foo3);244 call(foo4);245}246 247void quiet() {248// This code is not expected to trigger any warnings.249 {250 RefCountable automatic;251 RefCountable &ref_countable_ref = automatic;252 }253 254 auto foo3 = [&]() {};255 auto foo4 = [=]() {};256 257 call(foo3);258 call(foo4);259 260 RefCountable *ref_countable = nullptr;261}262 263template <typename Callback>264void map(RefCountable* start, [[clang::noescape]] Callback&& callback)265{266 while (start) {267 callback(*start);268 start = start->next();269 }270}271 272template <typename Callback1, typename Callback2>273void doubleMap(RefCountable* start, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2)274{275 while (start) {276 callback1(*start);277 callback2(*start);278 start = start->next();279 }280}281 282void noescape_lambda() {283 RefCountable* someObj = make_obj();284 RefCountable* otherObj = make_obj();285 map(make_obj(), [&](RefCountable& obj) {286 otherObj->method();287 });288 doubleMap(make_obj(), [&](RefCountable& obj) {289 otherObj->method();290 }, [&](RefCountable& obj) {291 otherObj->method();292 // expected-warning@-1{{Implicitly captured raw-pointer 'otherObj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}293 });294 ([&] {295 someObj->method();296 })();297}298 299void lambda_capture_param(RefCountable* obj) {300 auto someLambda = [&]() {301 obj->method();302 };303 someLambda();304 someLambda();305}306 307struct RefCountableWithLambdaCapturingThis {308 void ref() const;309 void deref() const;310 void nonTrivial();311 312 void method_captures_this_safe() {313 auto lambda = [&]() {314 nonTrivial();315 };316 lambda();317 }318 319 void method_captures_this_unsafe() {320 auto lambda = [&]() {321 nonTrivial();322 // expected-warning@-1{{Implicitly captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}323 };324 call(lambda);325 }326 327 void method_captures_this_unsafe_capture_local_var_explicitly() {328 RefCountable* x = make_obj();329 call([this, protectedThis = RefPtr { this }, x]() {330 // expected-warning@-1{{Captured raw-pointer 'x' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}331 nonTrivial();332 x->method();333 });334 }335 336 void method_captures_this_with_other_protected_var() {337 RefCountable* x = make_obj();338 call([this, protectedX = RefPtr { x }]() {339 // expected-warning@-1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}340 nonTrivial();341 protectedX->method();342 });343 }344 345 void method_captures_this_unsafe_capture_local_var_explicitly_with_deref() {346 RefCountable* x = make_obj();347 call([this, protectedThis = Ref { *this }, x]() {348 // expected-warning@-1{{Captured raw-pointer 'x' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}349 nonTrivial();350 x->method();351 });352 }353 354 void method_captures_this_unsafe_local_var_via_vardecl() {355 RefCountable* x = make_obj();356 auto lambda = [this, protectedThis = Ref { *this }, x]() {357 // expected-warning@-1{{Captured raw-pointer 'x' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}358 nonTrivial();359 x->method();360 };361 call(lambda);362 }363 364 void method_captures_this_with_guardian() {365 auto lambda = [this, protectedThis = Ref { *this }]() {366 nonTrivial();367 };368 call(lambda);369 }370 371 void method_captures_this_with_guardian_refptr() {372 auto lambda = [this, protectedThis = RefPtr { &*this }]() {373 nonTrivial();374 };375 call(lambda);376 }377 378 void forEach(const WTF::Function<void(RefCountable&)>&);379 void method_captures_this_with_lambda_with_no_escape() {380 auto run = [&]([[clang::noescape]] const WTF::Function<void(RefCountable&)>& func) {381 forEach(func);382 };383 run([&](RefCountable&) {384 nonTrivial();385 });386 }387 388 static void callLambda([[clang::noescape]] const WTF::Function<RefPtr<RefCountable>()>&);389 void method_captures_this_in_template_method() {390 RefCountable* obj = make_obj();391 WTF::HashMap<int, RefPtr<RefCountable>> nextMap;392 nextMap.ensure(3, [&] {393 return obj->next();394 });395 nextMap+[&] {396 return obj->next();397 };398 WTF::HashMap<int, RefPtr<RefCountable>>::ifAny(nextMap, [&](auto& item) -> bool {399 return item->next() && obj->next();400 });401 callLambda([&]() -> RefPtr<RefCountable> {402 return obj->next();403 });404 WTF::HashMap<int, RefPtr<RefCountable>> anotherMap([&] {405 return obj->next();406 });407 }408 409 void callAsyncNoescape([[clang::noescape]] WTF::Function<bool(RefCountable&)>&&);410 void method_temp_lambda(RefCountable* obj) {411 callAsyncNoescape([this, otherObj = RefPtr { obj }](auto& obj) {412 return otherObj == &obj;413 });414 }415 416 void method_nested_lambda() {417 callAsync([this, protectedThis = Ref { *this }] {418 callAsync([this, protectedThis = static_cast<const Ref<RefCountableWithLambdaCapturingThis>&&>(protectedThis)] {419 nonTrivial();420 });421 });422 }423 424 void method_nested_lambda2() {425 callAsync([this, protectedThis = RefPtr { this }] {426 callAsync([this, protectedThis = std::move(*protectedThis)] {427 nonTrivial();428 });429 });430 }431 432 void method_nested_lambda3() {433 callAsync([this, protectedThis = RefPtr { this }] {434 callAsync([this] {435 // expected-warning@-1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}436 nonTrivial();437 });438 });439 }440};441 442struct NonRefCountableWithLambdaCapturingThis {443 void nonTrivial();444 445 void method_captures_this_safe() {446 auto lambda = [&]() {447 nonTrivial();448 };449 lambda();450 }451 452 void method_captures_this_unsafe() {453 auto lambda = [&]() {454 nonTrivial();455 };456 call(lambda);457 }458};459 460void trivial_lambda() {461 RefCountable* ref_countable = make_obj();462 auto trivial_lambda = [&]() {463 return ref_countable->trivial();464 };465 trivial_lambda();466}467 468bool call_lambda_var_decl() {469 RefCountable* ref_countable = make_obj();470 auto lambda1 = [&]() -> bool {471 return ref_countable->next();472 };473 auto lambda2 = [=]() -> bool {474 return ref_countable->next();475 };476 return lambda1() && lambda2();477}478 479void lambda_with_args(RefCountable* obj) {480 auto trivial_lambda = [&](int v) {481 obj->method();482 };483 trivial_lambda(1);484}485 486void callFunctionOpaque(WTF::Function<void()>&&);487void callFunction(WTF::Function<void()>&& function) {488 someFunction();489 function();490}491 492void lambda_converted_to_function(RefCountable* obj)493{494 callFunction([&]() {495 obj->method();496 // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}497 });498 callFunctionOpaque([&]() {499 obj->method();500 // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}501 });502}503 504void capture_copy_in_lambda(CheckedObj& checked) {505 callFunctionOpaque([checked]() mutable {506 checked.method();507 });508 auto* ptr = &checked;509 callFunctionOpaque([ptr]() mutable {510 // expected-warning@-1{{Captured raw-pointer 'ptr' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}511 ptr->method();512 });513}514 515class Iterator {516public:517 Iterator(void* array, unsigned long sizeOfElement, unsigned int index);518 Iterator(const Iterator&);519 Iterator& operator=(const Iterator&);520 bool operator==(const Iterator&);521 522 Iterator& operator++();523 int& operator*();524 525private:526 void* current { nullptr };527 unsigned long sizeOfElement { 0 };528};529 530void ranges_for_each(RefCountable* obj) {531 int array[] = { 1, 2, 3, 4, 5 };532 std::ranges::for_each(Iterator(array, sizeof(*array), 0), Iterator(array, sizeof(*array), 5), [&](int& item) {533 obj->method();534 ++item;535 });536}537 538class IntCollection {539public:540 int* begin();541 int* end();542 const int* begin() const;543 const int* end() const;544};545 546class RefCountedObj {547public:548 void ref();549 void deref();550 551 bool allOf(const IntCollection&);552 bool isMatch(int);553 554 void call() const;555 void callLambda([[clang::noescape]] const WTF::Function<void ()>& callback) const;556 void doSomeWork() const;557};558 559bool RefCountedObj::allOf(const IntCollection& collection) {560 return std::ranges::all_of(collection, [&](auto& number) {561 return isMatch(number);562 });563}564 565void RefCountedObj::callLambda([[clang::noescape]] const WTF::Function<void ()>& callback) const566{567 callback();568}569 570void RefCountedObj::call() const571{572 auto lambda = [&] {573 doSomeWork();574 };575 callLambda(lambda);576}577 578void scope_exit(RefCountable* obj) {579 auto scope = WTF::makeScopeExit([&] {580 obj->method();581 });582 someFunction();583 WTF::ScopeExit scope2([&] {584 obj->method();585 });586 someFunction();587}588 589void doWhateverWith(WTF::ScopeExit& obj);590 591void scope_exit_with_side_effect(RefCountable* obj) {592 auto scope = WTF::makeScopeExit([&] {593 obj->method();594 // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}595 });596 doWhateverWith(scope);597}598 599void scope_exit_static(RefCountable* obj) {600 static auto scope = WTF::makeScopeExit([&] {601 obj->method();602 // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}603 });604}605 606WTF::Function<void()> scope_exit_take_lambda(RefCountable* obj) {607 auto scope = WTF::makeScopeExit([&] {608 obj->method();609 // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}610 });611 return scope.take();612}613 614// FIXME: Ideally, we treat release() as a trivial function.615void scope_exit_release(RefCountable* obj) {616 auto scope = WTF::makeScopeExit([&] {617 obj->method();618 // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}619 });620 scope.release();621}622 623void make_visitor(RefCountable* obj) {624 auto visitor = WTF::makeVisitor([&] {625 obj->method();626 });627}628 629void use_visitor(RefCountable* obj) {630 auto visitor = WTF::makeVisitor([&] {631 obj->method();632 });633 WTF::visit(visitor, obj);634}635 636template <typename Visitor, typename ObjectType>637void bad_visit(Visitor&, ObjectType*) {638 someFunction();639}640 641void static_visitor(RefCountable* obj) {642 static auto visitor = WTF::makeVisitor([&] {643 obj->method();644 // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}645 });646}647 648void bad_use_visitor(RefCountable* obj) {649 auto visitor = WTF::makeVisitor([&] {650 obj->method();651 // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}652 });653 bad_visit(visitor, obj);654}655