// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.RefCntblBaseVirtualDtor -verify %s
#include "mock-types.h"
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(WTFMove(callable)) { }
CallableWrapper(const CallableWrapper&) = delete;
CallableWrapper& operator=(const CallableWrapper&) = delete;
Out call(In... in) final;
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);
Out operator()(In... in) const;
explicit operator bool() const { return !!m_callableWrapper; }
private:
enum AdoptTag { Adopt };
Function(Impl* impl, AdoptTag)
: m_callableWrapper(impl)
{
}
friend Function adopt<Out, In...>(Impl*);
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 T, typename PtrTraits = RawPtrTraits<T>, typename RefDerefTraits = DefaultRefDerefTraits<T>> Ref<T, PtrTraits, RefDerefTraits> adoptRef(T&);
template<typename T, typename _PtrTraits, typename RefDerefTraits>
inline Ref<T, _PtrTraits, RefDerefTraits> adoptRef(T& reference)
{
return Ref<T, _PtrTraits, RefDerefTraits>(reference);
}
enum class DestructionThread : unsigned char { Any, Main, MainRunLoop };
void ensureOnMainThread(Function<void()>&&); // Sync if called on main thread, async otherwise.
void ensureOnMainRunLoop(Function<void()>&&); // Sync if called on main run loop, async otherwise.
class ThreadSafeRefCountedBase {
public:
ThreadSafeRefCountedBase() = default;
void ref() const
{
++m_refCount;
}
bool hasOneRef() const
{
return refCount() == 1;
}
unsigned refCount() const
{
return m_refCount;
}
protected:
bool derefBase() const
{
if (!--m_refCount) {
m_refCount = 1;
return true;
}
return false;
}
private:
mutable unsigned m_refCount { 1 };
};
template<class T, DestructionThread destructionThread = DestructionThread::Any> class ThreadSafeRefCounted : public ThreadSafeRefCountedBase {
public:
void deref() const
{
if (!derefBase())
return;
if constexpr (destructionThread == DestructionThread::Any) {
delete static_cast<const T*>(this);
} else if constexpr (destructionThread == DestructionThread::Main) {
ensureOnMainThread([this] {
delete static_cast<const T*>(this);
});
} else if constexpr (destructionThread == DestructionThread::MainRunLoop) {
auto deleteThis = [this] {
delete static_cast<const T*>(this);
};
ensureOnMainThread(deleteThis);
}
}
protected:
ThreadSafeRefCounted() = default;
};
class FancyRefCountedClass final : public ThreadSafeRefCounted<FancyRefCountedClass, DestructionThread::Main> {
public:
static Ref<FancyRefCountedClass> create()
{
return adoptRef(*new FancyRefCountedClass());
}
virtual ~FancyRefCountedClass();
private:
FancyRefCountedClass();
};
template<class T, DestructionThread destructionThread = DestructionThread::Any> class BadThreadSafeRefCounted : public ThreadSafeRefCountedBase {
public:
void deref() const
{
if (!derefBase())
return;
[this] {
delete static_cast<const T*>(this);
};
}
protected:
BadThreadSafeRefCounted() = default;
};
class FancyRefCountedClass2 final : public ThreadSafeRefCounted<FancyRefCountedClass, DestructionThread::Main> {
// expected-warning@-1{{Class 'ThreadSafeRefCounted<FancyRefCountedClass, DestructionThread::Main>' is used as a base of class 'FancyRefCountedClass2' but doesn't have virtual destructor}}
public:
static Ref<FancyRefCountedClass2> create()
{
return adoptRef(*new FancyRefCountedClass2());
}
virtual ~FancyRefCountedClass2();
private:
FancyRefCountedClass2();
};
template<class T, DestructionThread destructionThread = DestructionThread::Any> class NestedThreadSafeRefCounted : public ThreadSafeRefCountedBase {
public:
void deref() const
{
if (!derefBase())
return;
ensureOnMainRunLoop([&] {
auto destroyThis = [&] {
delete static_cast<const T*>(this);
};
destroyThis();
});
}
protected:
NestedThreadSafeRefCounted() = default;
};
class FancyRefCountedClass3 final : public NestedThreadSafeRefCounted<FancyRefCountedClass3, DestructionThread::Main> {
public:
static Ref<FancyRefCountedClass3> create()
{
return adoptRef(*new FancyRefCountedClass3());
}
virtual ~FancyRefCountedClass3();
private:
FancyRefCountedClass3();
};
template<class T, DestructionThread destructionThread = DestructionThread::Any> class BadNestedThreadSafeRefCounted : public ThreadSafeRefCountedBase {
public:
void deref() const
{
if (!derefBase())
return;
ensureOnMainThread([&] {
auto destroyThis = [&] {
delete static_cast<const T*>(this);
};
});
}
protected:
BadNestedThreadSafeRefCounted() = default;
};
class FancyRefCountedClass4 final : public BadNestedThreadSafeRefCounted<FancyRefCountedClass4, DestructionThread::Main> {
// expected-warning@-1{{Class 'BadNestedThreadSafeRefCounted<FancyRefCountedClass4, DestructionThread::Main>' is used as a base of class 'FancyRefCountedClass4' but doesn't have virtual destructor}}
public:
static Ref<FancyRefCountedClass4> create()
{
return adoptRef(*new FancyRefCountedClass4());
}
virtual ~FancyRefCountedClass4();
private:
FancyRefCountedClass4();
};
class FancyRefCountedClass5 final : public ThreadSafeRefCounted<FancyRefCountedClass5, DestructionThread::MainRunLoop> {
public:
static Ref<FancyRefCountedClass5> create()
{
return adoptRef(*new FancyRefCountedClass5());
}
virtual ~FancyRefCountedClass5();
private:
FancyRefCountedClass5();
};