380 lines · plain
1// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedLambdaCapturesChecker -verify %s2 3#include "objc-mock-types.h"4 5namespace std {6 7template <typename T>8class unique_ptr {9private:10 T *t;11 12public:13 unique_ptr() : t(nullptr) { }14 unique_ptr(T *t) : t(t) { }15 ~unique_ptr() {16 if (t)17 delete t;18 }19 template <typename U> unique_ptr(unique_ptr<U>&& u)20 : t(u.t)21 {22 u.t = nullptr;23 }24 T *get() const { return t; }25 T *operator->() const { return t; }26 T &operator*() const { return *t; }27 unique_ptr &operator=(T *) { return *this; }28 explicit operator bool() const { return !!t; }29};30 31};32 33namespace WTF {34 35namespace Detail {36 37template<typename Out, typename... In>38class CallableWrapperBase {39public:40 virtual ~CallableWrapperBase() { }41 virtual Out call(In...) = 0;42};43 44template<typename, typename, typename...> class CallableWrapper;45 46template<typename CallableType, typename Out, typename... In>47class CallableWrapper : public CallableWrapperBase<Out, In...> {48public:49 explicit CallableWrapper(CallableType& callable)50 : m_callable(callable) { }51 Out call(In... in) final { return m_callable(in...); }52 53private:54 CallableType m_callable;55};56 57} // namespace Detail58 59template<typename> class Function;60 61template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>*);62 63template <typename Out, typename... In>64class Function<Out(In...)> {65public:66 using Impl = Detail::CallableWrapperBase<Out, In...>;67 68 Function() = default;69 70 template<typename FunctionType>71 Function(FunctionType f)72 : m_callableWrapper(new Detail::CallableWrapper<FunctionType, Out, In...>(f)) { }73 74 Out operator()(In... in) const { return m_callableWrapper->call(in...); }75 explicit operator bool() const { return !!m_callableWrapper; }76 77private:78 enum AdoptTag { Adopt };79 Function(Impl* impl, AdoptTag)80 : m_callableWrapper(impl)81 {82 }83 84 friend Function adopt<Out, In...>(Impl*);85 86 std::unique_ptr<Impl> m_callableWrapper;87};88 89template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>* impl)90{91 return Function<Out(In...)>(impl, Function<Out(In...)>::Adopt);92}93 94template <typename KeyType, typename ValueType>95class HashMap {96public:97 HashMap();98 HashMap([[clang::noescape]] const Function<ValueType()>&);99 void ensure(const KeyType&, [[clang::noescape]] const Function<ValueType()>&);100 bool operator+([[clang::noescape]] const Function<ValueType()>&) const;101 static void ifAny(HashMap, [[clang::noescape]] const Function<bool(ValueType)>&);102 103private:104 ValueType* m_table { nullptr };105};106 107} // namespace WTF108 109struct A {110 static void b();111};112 113SomeObj* make_obj();114CFMutableArrayRef make_cf();115dispatch_queue_t make_os();116 117void someFunction();118template <typename Callback> void call(Callback callback) {119 someFunction();120 callback();121}122void callAsync(const WTF::Function<void()>&);123 124void raw_ptr() {125 SomeObj* obj = make_obj();126 auto foo1 = [obj](){127 // expected-warning@-1{{Captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}128 [obj doWork];129 };130 call(foo1);131 132 auto foo2 = [&obj](){133 // expected-warning@-1{{Captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}134 [obj doWork];135 };136 auto foo3 = [&](){137 [obj doWork];138 // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}139 obj = nullptr;140 };141 auto foo4 = [=](){142 [obj doWork];143 // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}144 };145 146 auto cf = make_cf();147 auto bar1 = [cf](){148 // expected-warning@-1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}149 CFArrayAppendValue(cf, nullptr);150 };151 auto bar2 = [&cf](){152 // expected-warning@-1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}153 CFArrayAppendValue(cf, nullptr);154 };155 auto bar3 = [&](){156 CFArrayAppendValue(cf, nullptr);157 // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}158 cf = nullptr;159 };160 auto bar4 = [=](){161 CFArrayAppendValue(cf, nullptr);162 // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}163 };164 165 auto os = make_os();166 auto baz1 = [os](){167 // expected-warning@-1{{Captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}168 dispatch_queue_get_label(os);169 };170 auto baz2 = [&os](){171 // expected-warning@-1{{Captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}172 dispatch_queue_get_label(os);173 };174 auto baz3 = [&](){175 dispatch_queue_get_label(os);176 // expected-warning@-1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}177 os = nullptr;178 };179 auto baz4 = [=](){180 dispatch_queue_get_label(os);181 // expected-warning@-1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}182 };183 184 call(foo1);185 call(foo2);186 call(foo3);187 call(foo4);188 189 call(bar1);190 call(bar2);191 call(bar3);192 call(bar4);193 194 call(baz1);195 call(baz2);196 call(baz3);197 call(baz4);198 199 // Confirm that the checker respects [[clang::suppress]].200 SomeObj* suppressed_obj = make_obj();201 [[clang::suppress]] auto foo5 = [suppressed_obj](){202 [suppressed_obj doWork];203 };204 // no warning.205 call(foo5);206 207 // Confirm that the checker respects [[clang::suppress]].208 CFMutableArrayRef suppressed_cf = make_cf();209 [[clang::suppress]] auto bar5 = [suppressed_cf](){210 CFArrayAppendValue(suppressed_cf, nullptr);211 };212 // no warning.213 call(bar5);214 215 // Confirm that the checker respects [[clang::suppress]].216 dispatch_queue_t suppressed_os = make_os();217 [[clang::suppress]] auto baz5 = [suppressed_os](){218 dispatch_queue_get_label(suppressed_os);219 };220 // no warning.221 call(baz5);222}223 224void quiet() {225// This code is not expected to trigger any warnings.226 SomeObj *obj;227 228 auto foo3 = [&]() {};229 auto foo4 = [=]() {};230 231 call(foo3);232 call(foo4);233 234 obj = nullptr;235}236 237template <typename Callback>238void map(SomeObj* start, [[clang::noescape]] Callback&& callback)239{240 while (start) {241 callback(start);242 start = [start next];243 }244}245 246template <typename Callback1, typename Callback2>247void doubleMap(SomeObj* start, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2)248{249 while (start) {250 callback1(start);251 callback2(start);252 start = [start next];253 }254}255 256template <typename Callback1, typename Callback2>257void get_count_cf(CFArrayRef array, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2)258{259 auto count = CFArrayGetCount(array);260 callback1(count);261 callback2(count);262}263 264template <typename Callback1, typename Callback2>265void get_count_os(dispatch_queue_t queue, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2)266{267 auto* label = dispatch_queue_get_label(queue);268 callback1(label);269 callback2(label);270}271 272void noescape_lambda() {273 SomeObj* someObj = make_obj();274 SomeObj* otherObj = make_obj();275 map(make_obj(), [&](SomeObj *obj) {276 [otherObj doWork];277 });278 doubleMap(make_obj(), [&](SomeObj *obj) {279 [otherObj doWork];280 }, [&](SomeObj *obj) {281 [otherObj doWork];282 // expected-warning@-1{{Implicitly captured raw-pointer 'otherObj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}283 });284 ([&] {285 [someObj doWork];286 })();287 288 CFMutableArrayRef someCF = make_cf();289 get_count_cf(make_cf(), [&](CFIndex count) {290 CFArrayAppendValue(someCF, nullptr);291 }, [&](CFIndex count) {292 CFArrayAppendValue(someCF, nullptr);293 // expected-warning@-1{{Implicitly captured reference 'someCF' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}294 });295 296 dispatch_queue_t someOS = make_os();297 get_count_os(make_os(), [&](const char* label) {298 dispatch_queue_get_label(someOS);299 }, [&](const char* label) {300 dispatch_queue_get_label(someOS);301 // expected-warning@-1{{Implicitly captured reference 'someOS' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}302 });303}304 305void callFunctionOpaque(WTF::Function<void()>&&);306void callFunction(WTF::Function<void()>&& function) {307 someFunction();308 function();309}310 311void lambda_converted_to_function(SomeObj* obj, CFMutableArrayRef cf, dispatch_queue_t os)312{313 callFunction([&]() {314 [obj doWork];315 // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}316 CFArrayAppendValue(cf, nullptr);317 // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}318 dispatch_queue_get_label(os);319 // expected-warning@-1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}320 });321 callFunctionOpaque([&]() {322 [obj doWork];323 // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}324 CFArrayAppendValue(cf, nullptr);325 // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}326 dispatch_queue_get_label(os);327 // expected-warning@-1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}328 });329}330 331@interface ObjWithSelf : NSObject {332 RetainPtr<id> delegate;333 OSObjectPtr<dispatch_queue_t> queue;334}335-(void)doWork;336-(void)doMoreWork;337-(void)run;338@end339 340@implementation ObjWithSelf341-(void)doWork {342 auto doWork = [&] {343 // expected-warning@-1{{Implicitly captured raw-pointer 'self' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}344 someFunction();345 [delegate doWork];346 };347 auto doMoreWork = [=] {348 // expected-warning@-1{{Implicitly captured raw-pointer 'self' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}349 someFunction();350 [delegate doWork];351 };352 auto doExtraWork = [&, protectedSelf = retainPtr(self)] {353 someFunction();354 [delegate doWork];355 };356 auto* queuePtr = queue.get();357 auto doAdditionalWork = [&] {358 someFunction();359 dispatch_queue_get_label(queuePtr);360 // expected-warning@-1{{Implicitly captured raw-pointer 'queuePtr' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}361 };362 callFunctionOpaque(doWork);363 callFunctionOpaque(doMoreWork);364 callFunctionOpaque(doExtraWork);365 callFunctionOpaque(doAdditionalWork);366}367 368-(void)doMoreWork {369 auto doWork = [self, protectedSelf = retainPtr(self)] {370 someFunction();371 [self doWork];372 };373 callFunctionOpaque(doWork);374}375 376-(void)run {377 someFunction();378}379@end380