| // RUN: %clang_analyze_cc1 -analyzer-checker=webkit.RefCntblBaseVirtualDtor -verify %s |
| |
| struct RefCntblBase { |
| void ref() {} |
| void deref() {} |
| }; |
| |
| template<class T> |
| struct DerivedClassTmpl1 : T { }; |
| // expected-warning@-1{{Struct 'RefCntblBase' is used as a base of struct 'DerivedClassTmpl1<RefCntblBase>' but doesn't have virtual destructor}} |
| |
| DerivedClassTmpl1<RefCntblBase> a; |
| void foo(DerivedClassTmpl1<RefCntblBase>& obj) { obj.deref(); } |
| |
| template<class T> |
| struct DerivedClassTmpl2 : T { }; |
| // expected-warning@-1{{Struct 'RefCntblBase' is used as a base of struct 'DerivedClassTmpl2<RefCntblBase>' but doesn't have virtual destructor}} |
| |
| template<class T> int foo(T) { DerivedClassTmpl2<T> f; return 42; } |
| int b = foo(RefCntblBase{}); |
| |
| |
| template<class T> |
| struct DerivedClassTmpl3 : T { }; |
| // expected-warning@-1{{Struct 'RefCntblBase' is used as a base of struct 'DerivedClassTmpl3<RefCntblBase>' but doesn't have virtual destructor}} |
| |
| typedef DerivedClassTmpl3<RefCntblBase> Foo; |
| Foo c; |
| |
| namespace WTF { |
| |
| class RefCountedBase { |
| public: |
| void ref() const { ++count; } |
| |
| protected: |
| bool derefBase() const |
| { |
| return !--count; |
| } |
| |
| private: |
| mutable unsigned count; |
| }; |
| |
| template <typename T> |
| class RefCounted : public RefCountedBase { |
| public: |
| void deref() const { |
| if (derefBase()) |
| delete const_cast<T*>(static_cast<const T*>(this)); |
| } |
| |
| protected: |
| RefCounted() { } |
| }; |
| |
| template <typename X, typename T> |
| class ExoticRefCounted : public RefCountedBase { |
| public: |
| void deref() const { |
| if (derefBase()) |
| delete (const_cast<T*>(static_cast<const T*>(this))); |
| } |
| }; |
| |
| template <typename X, typename T> |
| class BadBase : RefCountedBase { |
| public: |
| void deref() const { |
| if (derefBase()) |
| delete (const_cast<X*>(static_cast<const X*>(this))); |
| } |
| }; |
| |
| template <typename T> |
| class FancyDeref { |
| public: |
| void ref() const |
| { |
| ++refCount; |
| } |
| |
| void deref() const |
| { |
| --refCount; |
| if (refCount) |
| return; |
| auto deleteThis = [this] { |
| delete static_cast<const T*>(this); |
| }; |
| deleteThis(); |
| } |
| private: |
| mutable unsigned refCount { 0 }; |
| }; |
| |
| 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 { return m_callable(in...); } |
| private: |
| CallableType m_callable; |
| }; |
| |
| } // namespace Detail |
| |
| template<typename> class Function; |
| |
| template <typename Out, typename... In> |
| class Function<Out(In...)> { |
| public: |
| using Impl = Detail::CallableWrapperBase<Out, In...>; |
| |
| Function() = default; |
| |
| template<typename CallableType> |
| Function(CallableType&& callable) |
| : m_callableWrapper(new Detail::CallableWrapper<CallableType, Out, In...>>(callable)) { } |
| |
| template<typename FunctionType> |
| Function(FunctionType f) |
| : m_callableWrapper(new Detail::CallableWrapper<FunctionType, Out, In...>>(f)) { } |
| |
| ~Function() { |
| } |
| |
| Out operator()(In... in) const { |
| ASSERT(m_callableWrapper); |
| return m_callableWrapper->call(in...); |
| } |
| |
| explicit operator bool() const { return !!m_callableWrapper; } |
| |
| private: |
| Impl* m_callableWrapper; |
| }; |
| |
| void ensureOnMainThread(const Function<void()>&& function); |
| |
| enum class DestructionThread { Any, MainThread }; |
| |
| template <typename T, DestructionThread destructionThread = DestructionThread::Any> |
| class FancyDeref2 { |
| public: |
| void ref() const |
| { |
| ++refCount; |
| } |
| |
| void deref() const |
| { |
| --refCount; |
| if (refCount) |
| return; |
| const_cast<FancyDeref2<T, destructionThread>*>(this)->destroy(); |
| } |
| |
| private: |
| void destroy() { |
| delete static_cast<T*>(this); |
| } |
| mutable unsigned refCount { 0 }; |
| }; |
| |
| template <typename S> |
| class DerivedFancyDeref2 : public FancyDeref2<S> { |
| }; |
| |
| template <typename T> |
| class BadFancyDeref { |
| public: |
| void ref() const |
| { |
| ++refCount; |
| } |
| |
| void deref() const |
| { |
| --refCount; |
| if (refCount) |
| return; |
| auto deleteThis = [this] { |
| delete static_cast<const T*>(this); |
| }; |
| delete this; |
| } |
| private: |
| mutable unsigned refCount { 0 }; |
| }; |
| |
| template <typename T> |
| class ThreadSafeRefCounted { |
| public: |
| void ref() const { ++refCount; } |
| void deref() const { |
| if (!--refCount) |
| delete const_cast<T*>(static_cast<const T*>(this)); |
| } |
| private: |
| mutable unsigned refCount { 0 }; |
| }; |
| |
| template <typename T> |
| class ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr { |
| public: |
| void ref() const { ++refCount; } |
| void deref() const { |
| if (!--refCount) |
| delete const_cast<T*>(static_cast<const T*>(this)); |
| } |
| private: |
| mutable unsigned refCount { 0 }; |
| }; |
| |
| } // namespace WTF |
| |
| class DerivedClass4 : public WTF::RefCounted<DerivedClass4> { }; |
| |
| class DerivedClass4b : public WTF::ExoticRefCounted<int, DerivedClass4b> { }; |
| |
| class DerivedClass4cSub; |
| class DerivedClass4c : public WTF::BadBase<DerivedClass4cSub, DerivedClass4c> { }; |
| // expected-warning@-1{{Class 'WTF::BadBase<DerivedClass4cSub, DerivedClass4c>' is used as a base of class 'DerivedClass4c' but doesn't have virtual destructor}} |
| class DerivedClass4cSub : public DerivedClass4c { }; |
| void UseDerivedClass4c(DerivedClass4c &obj) { obj.deref(); } |
| |
| class DerivedClass4d : public WTF::RefCounted<DerivedClass4d> { |
| public: |
| virtual ~DerivedClass4d() { } |
| }; |
| class DerivedClass4dSub : public DerivedClass4d { }; |
| |
| class DerivedClass5 : public DerivedClass4 { }; |
| // expected-warning@-1{{Class 'DerivedClass4' is used as a base of class 'DerivedClass5' but doesn't have virtual destructor}} |
| void UseDerivedClass5(DerivedClass5 &obj) { obj.deref(); } |
| |
| class DerivedClass6 : public WTF::ThreadSafeRefCounted<DerivedClass6> { }; |
| void UseDerivedClass6(DerivedClass6 &obj) { obj.deref(); } |
| |
| class DerivedClass7 : public DerivedClass6 { }; |
| // expected-warning@-1{{Class 'DerivedClass6' is used as a base of class 'DerivedClass7' but doesn't have virtual destructor}} |
| void UseDerivedClass7(DerivedClass7 &obj) { obj.deref(); } |
| |
| class DerivedClass8 : public WTF::ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<DerivedClass8> { }; |
| void UseDerivedClass8(DerivedClass8 &obj) { obj.deref(); } |
| |
| class DerivedClass9 : public DerivedClass8 { }; |
| // expected-warning@-1{{Class 'DerivedClass8' is used as a base of class 'DerivedClass9' but doesn't have virtual destructor}} |
| void UseDerivedClass9(DerivedClass9 &obj) { obj.deref(); } |
| |
| class DerivedClass10 : public WTF::FancyDeref<DerivedClass10> { }; |
| void UseDerivedClass10(DerivedClass10 &obj) { obj.deref(); } |
| |
| class DerivedClass10b : public WTF::DerivedFancyDeref2<DerivedClass10b> { }; |
| void UseDerivedClass10b(DerivedClass10b &obj) { obj.deref(); } |
| |
| class DerivedClass10c : public WTF::BadFancyDeref<DerivedClass10c> { }; |
| // expected-warning@-1{{Class 'WTF::BadFancyDeref<DerivedClass10c>' is used as a base of class 'DerivedClass10c' but doesn't have virtual destructor}} |
| void UseDerivedClass10c(DerivedClass10c &obj) { obj.deref(); } |
| |
| class BaseClass1 { |
| public: |
| void ref() const { ++refCount; } |
| void deref() const; |
| private: |
| enum class Type { Base, Derived } type { Type::Base }; |
| mutable unsigned refCount { 0 }; |
| }; |
| |
| class DerivedClass11 : public BaseClass1 { }; |
| |
| void BaseClass1::deref() const |
| { |
| --refCount; |
| if (refCount) |
| return; |
| switch (type) { |
| case Type::Base: |
| delete const_cast<BaseClass1*>(this); |
| break; |
| case Type::Derived: |
| delete const_cast<DerivedClass11*>(static_cast<const DerivedClass11*>(this)); |
| break; |
| } |
| } |
| |
| void UseDerivedClass11(DerivedClass11& obj) { obj.deref(); } |
| |
| class BaseClass2; |
| static void deleteBase2(BaseClass2*); |
| |
| class BaseClass2 { |
| public: |
| void ref() const { ++refCount; } |
| void deref() const |
| { |
| if (!--refCount) |
| deleteBase2(const_cast<BaseClass2*>(this)); |
| } |
| virtual bool isDerived() { return false; } |
| private: |
| mutable unsigned refCount { 0 }; |
| }; |
| |
| class DerivedClass12 : public BaseClass2 { |
| bool isDerived() final { return true; } |
| }; |
| |
| void UseDerivedClass11(DerivedClass12& obj) { obj.deref(); } |
| |
| void deleteBase2(BaseClass2* obj) { |
| if (obj->isDerived()) |
| delete static_cast<DerivedClass12*>(obj); |
| else |
| delete obj; |
| } |
| |
| class BaseClass3 { |
| public: |
| void ref() const { ++refCount; } |
| void deref() const |
| { |
| if (!--refCount) |
| const_cast<BaseClass3*>(this)->destroy(); |
| } |
| virtual bool isDerived() { return false; } |
| |
| private: |
| void destroy(); |
| |
| mutable unsigned refCount { 0 }; |
| }; |
| |
| class DerivedClass13 : public BaseClass3 { |
| bool isDerived() final { return true; } |
| }; |
| |
| void UseDerivedClass11(DerivedClass13& obj) { obj.deref(); } |
| |
| void BaseClass3::destroy() { |
| if (isDerived()) |
| delete static_cast<DerivedClass13*>(this); |
| else |
| delete this; |
| } |
| |
| class RecursiveBaseClass { |
| public: |
| void ref() const { |
| if (otherObject) |
| otherObject->ref(); |
| else |
| ++refCount; |
| } |
| void deref() const { |
| if (otherObject) |
| otherObject->deref(); |
| else { |
| --refCount; |
| if (refCount) |
| return; |
| delete this; |
| } |
| } |
| private: |
| RecursiveBaseClass* otherObject { nullptr }; |
| mutable unsigned refCount { 0 }; |
| }; |
| |
| class RecursiveDerivedClass : public RecursiveBaseClass { }; |
| // expected-warning@-1{{Class 'RecursiveBaseClass' is used as a base of class 'RecursiveDerivedClass' but doesn't have virtual destructor}} |
| |
| class DerivedClass14 : public WTF::RefCounted<DerivedClass14> { |
| public: |
| virtual ~DerivedClass14() { } |
| }; |
| |
| void UseDerivedClass14(DerivedClass14& obj) { obj.deref(); } |
| |
| class DerivedClass15 : public DerivedClass14 { }; |
| |
| void UseDerivedClass15(DerivedClass15& obj) { obj.deref(); } |