xref: /llvm-project/clang/test/Analysis/Checkers/WebKit/ref-cntbl-crtp-base-no-virtual-dtor.cpp (revision d3c4637cbbd5f0a84811abe195098ce714a2cc32)
1 // RUN: %clang_analyze_cc1 -analyzer-checker=webkit.RefCntblBaseVirtualDtor -verify %s
2 
3 #include "mock-types.h"
4 
5 namespace Detail {
6 
7 template<typename Out, typename... In>
8 class CallableWrapperBase {
9 public:
10     virtual ~CallableWrapperBase() { }
11     virtual Out call(In...) = 0;
12 };
13 
14 template<typename, typename, typename...> class CallableWrapper;
15 
16 template<typename CallableType, typename Out, typename... In>
17 class CallableWrapper : public CallableWrapperBase<Out, In...> {
18 public:
19     explicit CallableWrapper(CallableType&& callable)
20         : m_callable(WTFMove(callable)) { }
21     CallableWrapper(const CallableWrapper&) = delete;
22     CallableWrapper& operator=(const CallableWrapper&) = delete;
23     Out call(In... in) final;
24 private:
25     CallableType m_callable;
26 };
27 
28 } // namespace Detail
29 
30 template<typename> class Function;
31 
32 template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>*);
33 
34 template <typename Out, typename... In>
35 class Function<Out(In...)> {
36 public:
37     using Impl = Detail::CallableWrapperBase<Out, In...>;
38 
39     Function() = default;
40 
41     template<typename FunctionType>
42     Function(FunctionType f);
43 
44     Out operator()(In... in) const;
45     explicit operator bool() const { return !!m_callableWrapper; }
46 
47 private:
48     enum AdoptTag { Adopt };
49     Function(Impl* impl, AdoptTag)
50         : m_callableWrapper(impl)
51     {
52     }
53 
54     friend Function adopt<Out, In...>(Impl*);
55 
56     Impl* m_callableWrapper;
57 };
58 
59 template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>* impl)
60 {
61     return Function<Out(In...)>(impl, Function<Out(In...)>::Adopt);
62 }
63 
64 enum class DestructionThread : unsigned char { Any, Main, MainRunLoop };
65 void ensureOnMainThread(Function<void()>&&); // Sync if called on main thread, async otherwise.
66 void ensureOnMainRunLoop(Function<void()>&&); // Sync if called on main run loop, async otherwise.
67 
68 class ThreadSafeRefCountedBase {
69 public:
70     ThreadSafeRefCountedBase() = default;
71 
72     void ref() const
73     {
74         ++m_refCount;
75     }
76 
77     bool hasOneRef() const
78     {
79         return refCount() == 1;
80     }
81 
82     unsigned refCount() const
83     {
84         return m_refCount;
85     }
86 
87 protected:
88     bool derefBase() const
89     {
90       if (!--m_refCount) {
91           m_refCount = 1;
92           return true;
93       }
94       return false;
95     }
96 
97 private:
98     mutable unsigned m_refCount { 1 };
99 };
100 
101 template<class T, DestructionThread destructionThread = DestructionThread::Any> class ThreadSafeRefCounted : public ThreadSafeRefCountedBase {
102 public:
103     void deref() const
104     {
105         if (!derefBase())
106             return;
107 
108         if constexpr (destructionThread == DestructionThread::Any) {
109             delete static_cast<const T*>(this);
110         } else if constexpr (destructionThread == DestructionThread::Main) {
111             ensureOnMainThread([this] {
112                 delete static_cast<const T*>(this);
113             });
114         } else if constexpr (destructionThread == DestructionThread::MainRunLoop) {
115             auto deleteThis = [this] {
116                 delete static_cast<const T*>(this);
117             };
118             ensureOnMainThread(deleteThis);
119         }
120     }
121 
122 protected:
123     ThreadSafeRefCounted() = default;
124 };
125 
126 class FancyRefCountedClass final : public ThreadSafeRefCounted<FancyRefCountedClass, DestructionThread::Main> {
127 public:
128     static Ref<FancyRefCountedClass> create()
129     {
130         return adoptRef(*new FancyRefCountedClass());
131     }
132 
133     virtual ~FancyRefCountedClass();
134 
135 private:
136     FancyRefCountedClass();
137 };
138 
139 template<class T, DestructionThread destructionThread = DestructionThread::Any> class BadThreadSafeRefCounted : public ThreadSafeRefCountedBase {
140 public:
141     void deref() const
142     {
143         if (!derefBase())
144             return;
145 
146         [this] {
147           delete static_cast<const T*>(this);
148         };
149     }
150 
151 protected:
152     BadThreadSafeRefCounted() = default;
153 };
154 
155 class FancyRefCountedClass2 final : public ThreadSafeRefCounted<FancyRefCountedClass, DestructionThread::Main> {
156 // expected-warning@-1{{Class 'ThreadSafeRefCounted<FancyRefCountedClass, DestructionThread::Main>' is used as a base of class 'FancyRefCountedClass2' but doesn't have virtual destructor}}
157 public:
158     static Ref<FancyRefCountedClass2> create()
159     {
160         return adoptRef(*new FancyRefCountedClass2());
161     }
162 
163     virtual ~FancyRefCountedClass2();
164 
165 private:
166     FancyRefCountedClass2();
167 };
168 
169 template<class T, DestructionThread destructionThread = DestructionThread::Any> class NestedThreadSafeRefCounted : public ThreadSafeRefCountedBase {
170 public:
171     void deref() const
172     {
173         if (!derefBase())
174             return;
175         ensureOnMainRunLoop([&] {
176           auto destroyThis = [&] {
177             delete static_cast<const T*>(this);
178           };
179           destroyThis();
180         });
181     }
182 
183 protected:
184     NestedThreadSafeRefCounted() = default;
185 };
186 
187 class FancyRefCountedClass3 final : public NestedThreadSafeRefCounted<FancyRefCountedClass3, DestructionThread::Main> {
188 public:
189     static Ref<FancyRefCountedClass3> create()
190     {
191         return adoptRef(*new FancyRefCountedClass3());
192     }
193 
194     virtual ~FancyRefCountedClass3();
195 
196 private:
197     FancyRefCountedClass3();
198 };
199 
200 template<class T, DestructionThread destructionThread = DestructionThread::Any> class BadNestedThreadSafeRefCounted : public ThreadSafeRefCountedBase {
201 public:
202     void deref() const
203     {
204         if (!derefBase())
205             return;
206         ensureOnMainThread([&] {
207           auto destroyThis = [&] {
208             delete static_cast<const T*>(this);
209           };
210         });
211     }
212 
213 protected:
214     BadNestedThreadSafeRefCounted() = default;
215 };
216 
217 class FancyRefCountedClass4 final : public BadNestedThreadSafeRefCounted<FancyRefCountedClass4, DestructionThread::Main> {
218 // expected-warning@-1{{Class 'BadNestedThreadSafeRefCounted<FancyRefCountedClass4, DestructionThread::Main>' is used as a base of class 'FancyRefCountedClass4' but doesn't have virtual destructor}}
219 public:
220     static Ref<FancyRefCountedClass4> create()
221     {
222         return adoptRef(*new FancyRefCountedClass4());
223     }
224 
225     virtual ~FancyRefCountedClass4();
226 
227 private:
228     FancyRefCountedClass4();
229 };
230 
231 class FancyRefCountedClass5 final : public ThreadSafeRefCounted<FancyRefCountedClass5, DestructionThread::MainRunLoop> {
232 public:
233     static Ref<FancyRefCountedClass5> create()
234     {
235         return adoptRef(*new FancyRefCountedClass5());
236     }
237 
238     virtual ~FancyRefCountedClass5();
239 
240 private:
241     FancyRefCountedClass5();
242 };
243