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