| // 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); |
| } |
| |
| 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(); |
| }; |