Skip to content

[webkit.RefCntblBaseVirtualDtor] Make ThreadSafeRefCounted not generate warnings #107676

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -67,6 +67,32 @@ class DerefFuncDeleteExprVisitor
const Decl *D = CE->getCalleeDecl();
if (D && D->hasBody())
return VisitBody(D->getBody());
else {
auto name = safeGetName(D);
if (name == "ensureOnMainThread" || name == "ensureOnMainRunLoop") {
for (unsigned i = 0; i < CE->getNumArgs(); ++i) {
auto *Arg = CE->getArg(i);
if (VisitLabmdaArgument(Arg))
return true;
}
}
}
return false;
}

bool VisitLabmdaArgument(const Expr *E) {
E = E->IgnoreParenCasts();
if (auto *TempE = dyn_cast<CXXBindTemporaryExpr>(E))
E = TempE->getSubExpr();
if (auto *ConstructE = dyn_cast<CXXConstructExpr>(E)) {
for (unsigned i = 0; i < ConstructE->getNumArgs(); ++i) {
auto *Arg = ConstructE->getArg(i);
if (auto *Lambda = dyn_cast<LambdaExpr>(Arg)) {
if (VisitBody(Lambda->getBody()))
return true;
}
}
}
return false;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
// 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);
});
}
}

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