blob: dcc6672261d8c690091937ea190a0ae97f15c78b [file] [log] [blame]
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedLambdaCapturesChecker -verify %s
#include "objc-mock-types.h"
namespace std {
template <typename T>
class unique_ptr {
private:
T *t;
public:
unique_ptr() : t(nullptr) { }
unique_ptr(T *t) : t(t) { }
~unique_ptr() {
if (t)
delete t;
}
template <typename U> unique_ptr(unique_ptr<U>&& 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<typename Out, typename... In>
class CallableWrapperBase {
public:
virtual ~CallableWrapperBase() { }
virtual Out call(In...) = 0;
};
template<typename, typename, typename...> class CallableWrapper;
template<typename CallableType, typename Out, typename... In>
class CallableWrapper : public CallableWrapperBase<Out, In...> {
public:
explicit CallableWrapper(CallableType& callable)
: m_callable(callable) { }
Out call(In... in) final { return m_callable(in...); }
private:
CallableType m_callable;
};
} // namespace Detail
template<typename> class Function;
template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>*);
template <typename Out, typename... In>
class Function<Out(In...)> {
public:
using Impl = Detail::CallableWrapperBase<Out, In...>;
Function() = default;
template<typename FunctionType>
Function(FunctionType f)
: m_callableWrapper(new Detail::CallableWrapper<FunctionType, Out, In...>(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<Out, In...>(Impl*);
std::unique_ptr<Impl> m_callableWrapper;
};
template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>* impl)
{
return Function<Out(In...)>(impl, Function<Out(In...)>::Adopt);
}
template <typename KeyType, typename ValueType>
class HashMap {
public:
HashMap();
HashMap([[clang::noescape]] const Function<ValueType()>&);
void ensure(const KeyType&, [[clang::noescape]] const Function<ValueType()>&);
bool operator+([[clang::noescape]] const Function<ValueType()>&) const;
static void ifAny(HashMap, [[clang::noescape]] const Function<bool(ValueType)>&);
private:
ValueType* m_table { nullptr };
};
} // namespace WTF
struct A {
static void b();
};
SomeObj* make_obj();
CFMutableArrayRef make_cf();
void someFunction();
template <typename Callback> void call(Callback callback) {
someFunction();
callback();
}
void callAsync(const WTF::Function<void()>&);
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]}}
};
call(foo1);
call(foo2);
call(foo3);
call(foo4);
call(bar1);
call(bar2);
call(bar3);
call(bar4);
// Confirm that the checker respects [[clang::suppress]].
SomeObj* suppressed_obj = nullptr;
[[clang::suppress]] auto foo5 = [suppressed_obj](){
[suppressed_obj doWork];
};
// no warning.
call(foo5);
// Confirm that the checker respects [[clang::suppress]].
CFMutableArrayRef suppressed_cf = nullptr;
[[clang::suppress]] auto bar5 = [suppressed_cf](){
CFArrayAppendValue(suppressed_cf, nullptr);
};
// no warning.
call(bar5);
}
void quiet() {
// This code is not expected to trigger any warnings.
SomeObj *obj;
auto foo3 = [&]() {};
auto foo4 = [=]() {};
call(foo3);
call(foo4);
obj = nullptr;
}
template <typename Callback>
void map(SomeObj* start, [[clang::noescape]] Callback&& callback)
{
while (start) {
callback(start);
start = [start next];
}
}
template <typename Callback1, typename Callback2>
void doubleMap(SomeObj* start, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2)
{
while (start) {
callback1(start);
callback2(start);
start = [start next];
}
}
template <typename Callback1, typename Callback2>
void get_count_cf(CFArrayRef array, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2)
{
auto count = CFArrayGetCount(array);
callback1(count);
callback2(count);
}
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]}}
});
}
void callFunctionOpaque(WTF::Function<void()>&&);
void callFunction(WTF::Function<void()>&& function) {
someFunction();
function();
}
void lambda_converted_to_function(SomeObj* obj, CFMutableArrayRef cf)
{
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]}}
});
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]}}
});
}
@interface ObjWithSelf : NSObject {
RetainPtr<id> delegate;
}
-(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];
};
callFunctionOpaque(doWork);
callFunctionOpaque(doMoreWork);
callFunctionOpaque(doExtraWork);
}
-(void)doMoreWork {
auto doWork = [self, protectedSelf = retainPtr(self)] {
someFunction();
[self doWork];
};
callFunctionOpaque(doWork);
}
-(void)run {
someFunction();
}
@end