// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedLambdaCapturesChecker -verify %s #include "objc-mock-types.h" namespace std { template class unique_ptr { private: T *t; public: unique_ptr() : t(nullptr) { } unique_ptr(T *t) : t(t) { } ~unique_ptr() { if (t) delete t; } template unique_ptr(unique_ptr&& u) : t(u.t) { u.t = nullptr; } T *get() const { return t; } T *operator->() const { return t; } T &operator*() const { return *t; } unique_ptr &operator=(T *) { return *this; } explicit operator bool() const { return !!t; } }; }; 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(); }; SomeObj* make_obj(); CFMutableArrayRef make_cf(); dispatch_queue_t make_os(); void someFunction(); template void call(Callback callback) { someFunction(); callback(); } void callAsync(const WTF::Function&); void raw_ptr() { SomeObj* obj = make_obj(); auto foo1 = [obj](){ // expected-warning@-1{{Captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} [obj doWork]; }; call(foo1); auto foo2 = [&obj](){ // expected-warning@-1{{Captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} [obj doWork]; }; auto foo3 = [&](){ [obj doWork]; // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} obj = nullptr; }; auto foo4 = [=](){ [obj doWork]; // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} }; auto cf = make_cf(); auto bar1 = [cf](){ // expected-warning@-1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} CFArrayAppendValue(cf, nullptr); }; auto bar2 = [&cf](){ // expected-warning@-1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} CFArrayAppendValue(cf, nullptr); }; auto bar3 = [&](){ CFArrayAppendValue(cf, nullptr); // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} cf = nullptr; }; auto bar4 = [=](){ CFArrayAppendValue(cf, nullptr); // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} }; auto os = make_os(); auto baz1 = [os](){ // expected-warning@-1{{Captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} dispatch_queue_get_label(os); }; auto baz2 = [&os](){ // expected-warning@-1{{Captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} dispatch_queue_get_label(os); }; auto baz3 = [&](){ dispatch_queue_get_label(os); // expected-warning@-1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} os = nullptr; }; auto baz4 = [=](){ dispatch_queue_get_label(os); // expected-warning@-1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} }; call(foo1); call(foo2); call(foo3); call(foo4); call(bar1); call(bar2); call(bar3); call(bar4); call(baz1); call(baz2); call(baz3); call(baz4); // Confirm that the checker respects [[clang::suppress]]. SomeObj* suppressed_obj = make_obj(); [[clang::suppress]] auto foo5 = [suppressed_obj](){ [suppressed_obj doWork]; }; // no warning. call(foo5); // Confirm that the checker respects [[clang::suppress]]. CFMutableArrayRef suppressed_cf = make_cf(); [[clang::suppress]] auto bar5 = [suppressed_cf](){ CFArrayAppendValue(suppressed_cf, nullptr); }; // no warning. call(bar5); // Confirm that the checker respects [[clang::suppress]]. dispatch_queue_t suppressed_os = make_os(); [[clang::suppress]] auto baz5 = [suppressed_os](){ dispatch_queue_get_label(suppressed_os); }; // no warning. call(baz5); } void quiet() { // This code is not expected to trigger any warnings. SomeObj *obj; auto foo3 = [&]() {}; auto foo4 = [=]() {}; call(foo3); call(foo4); obj = nullptr; } template void map(SomeObj* start, [[clang::noescape]] Callback&& callback) { while (start) { callback(start); start = [start next]; } } template void doubleMap(SomeObj* start, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2) { while (start) { callback1(start); callback2(start); start = [start next]; } } template void get_count_cf(CFArrayRef array, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2) { auto count = CFArrayGetCount(array); callback1(count); callback2(count); } template void get_count_os(dispatch_queue_t queue, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2) { auto* label = dispatch_queue_get_label(queue); callback1(label); callback2(label); } void noescape_lambda() { SomeObj* someObj = make_obj(); SomeObj* otherObj = make_obj(); map(make_obj(), [&](SomeObj *obj) { [otherObj doWork]; }); doubleMap(make_obj(), [&](SomeObj *obj) { [otherObj doWork]; }, [&](SomeObj *obj) { [otherObj doWork]; // expected-warning@-1{{Implicitly captured raw-pointer 'otherObj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} }); ([&] { [someObj doWork]; })(); CFMutableArrayRef someCF = make_cf(); get_count_cf(make_cf(), [&](CFIndex count) { CFArrayAppendValue(someCF, nullptr); }, [&](CFIndex count) { CFArrayAppendValue(someCF, nullptr); // expected-warning@-1{{Implicitly captured reference 'someCF' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} }); dispatch_queue_t someOS = make_os(); get_count_os(make_os(), [&](const char* label) { dispatch_queue_get_label(someOS); }, [&](const char* label) { dispatch_queue_get_label(someOS); // expected-warning@-1{{Implicitly captured reference 'someOS' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} }); } void callFunctionOpaque(WTF::Function&&); void callFunction(WTF::Function&& function) { someFunction(); function(); } void lambda_converted_to_function(SomeObj* obj, CFMutableArrayRef cf, dispatch_queue_t os) { callFunction([&]() { [obj doWork]; // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} CFArrayAppendValue(cf, nullptr); // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} dispatch_queue_get_label(os); // expected-warning@-1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} }); callFunctionOpaque([&]() { [obj doWork]; // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} CFArrayAppendValue(cf, nullptr); // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} dispatch_queue_get_label(os); // expected-warning@-1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} }); } @interface ObjWithSelf : NSObject { RetainPtr delegate; OSObjectPtr queue; } -(void)doWork; -(void)doMoreWork; -(void)run; @end @implementation ObjWithSelf -(void)doWork { auto doWork = [&] { // expected-warning@-1{{Implicitly captured raw-pointer 'self' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} someFunction(); [delegate doWork]; }; auto doMoreWork = [=] { // expected-warning@-1{{Implicitly captured raw-pointer 'self' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} someFunction(); [delegate doWork]; }; auto doExtraWork = [&, protectedSelf = retainPtr(self)] { someFunction(); [delegate doWork]; }; auto* queuePtr = queue.get(); auto doAdditionalWork = [&] { someFunction(); dispatch_queue_get_label(queuePtr); // expected-warning@-1{{Implicitly captured raw-pointer 'queuePtr' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} }; callFunctionOpaque(doWork); callFunctionOpaque(doMoreWork); callFunctionOpaque(doExtraWork); callFunctionOpaque(doAdditionalWork); } -(void)doMoreWork { auto doWork = [self, protectedSelf = retainPtr(self)] { someFunction(); [self doWork]; }; callFunctionOpaque(doWork); } -(void)run { someFunction(); } @end