xref: /llvm-project/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp (revision a71f9e6986b80fa90c453219795a1369b8ea7b6e)
1 // RUN: %clang_analyze_cc1 -analyzer-checker=webkit.UncountedLambdaCapturesChecker -verify %s
2 
3 #include "mock-types.h"
4 
5 namespace WTF {
6 
7 namespace Detail {
8 
9 template<typename Out, typename... In>
10 class CallableWrapperBase {
11 public:
12     virtual ~CallableWrapperBase() { }
13     virtual Out call(In...) = 0;
14 };
15 
16 template<typename, typename, typename...> class CallableWrapper;
17 
18 template<typename CallableType, typename Out, typename... In>
19 class CallableWrapper : public CallableWrapperBase<Out, In...> {
20 public:
21     explicit CallableWrapper(CallableType& callable)
22         : m_callable(callable) { }
23     Out call(In... in) final { return m_callable(in...); }
24 
25 private:
26     CallableType m_callable;
27 };
28 
29 } // namespace Detail
30 
31 template<typename> class Function;
32 
33 template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>*);
34 
35 template <typename Out, typename... In>
36 class Function<Out(In...)> {
37 public:
38     using Impl = Detail::CallableWrapperBase<Out, In...>;
39 
40     Function() = default;
41 
42     template<typename FunctionType>
43     Function(FunctionType f)
44         : m_callableWrapper(new Detail::CallableWrapper<FunctionType, Out, In...>(f)) { }
45 
46     Out operator()(In... in) const { return m_callableWrapper->call(in...); }
47     explicit operator bool() const { return !!m_callableWrapper; }
48 
49 private:
50     enum AdoptTag { Adopt };
51     Function(Impl* impl, AdoptTag)
52         : m_callableWrapper(impl)
53     {
54     }
55 
56     friend Function adopt<Out, In...>(Impl*);
57 
58     std::unique_ptr<Impl> m_callableWrapper;
59 };
60 
61 template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>* impl)
62 {
63     return Function<Out(In...)>(impl, Function<Out(In...)>::Adopt);
64 }
65 
66 } // namespace WTF
67 
68 struct A {
69   static void b();
70 };
71 
72 RefCountable* make_obj();
73 
74 void someFunction();
75 template <typename Callback> void call(Callback callback) {
76   someFunction();
77   callback();
78 }
79 
80 void raw_ptr() {
81   RefCountable* ref_countable = make_obj();
82   auto foo1 = [ref_countable](){
83     // expected-warning@-1{{Captured raw-pointer 'ref_countable' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
84     ref_countable->method();
85   };
86   auto foo2 = [&ref_countable](){
87     // expected-warning@-1{{Captured raw-pointer 'ref_countable' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
88     ref_countable->method();
89   };
90   auto foo3 = [&](){
91     ref_countable->method();
92     // expected-warning@-1{{Implicitly captured raw-pointer 'ref_countable' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
93     ref_countable = nullptr;
94   };
95 
96   auto foo4 = [=](){
97     ref_countable->method();
98     // expected-warning@-1{{Implicitly captured raw-pointer 'ref_countable' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
99   };
100 
101   call(foo1);
102   call(foo2);
103   call(foo3);
104   call(foo4);
105 
106   // Confirm that the checker respects [[clang::suppress]].
107   RefCountable* suppressed_ref_countable = nullptr;
108   [[clang::suppress]] auto foo5 = [suppressed_ref_countable](){};
109   // no warning.
110   call(foo5);
111 }
112 
113 void references() {
114   RefCountable automatic;
115   RefCountable& ref_countable_ref = automatic;
116   auto foo1 = [ref_countable_ref](){ ref_countable_ref.constMethod(); };
117   // expected-warning@-1{{Captured reference 'ref_countable_ref' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
118   auto foo2 = [&ref_countable_ref](){ ref_countable_ref.method(); };
119   // expected-warning@-1{{Captured reference 'ref_countable_ref' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
120   auto foo3 = [&](){ ref_countable_ref.method(); };
121   // expected-warning@-1{{Implicitly captured reference 'ref_countable_ref' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
122   auto foo4 = [=](){ ref_countable_ref.constMethod(); };
123   // expected-warning@-1{{Implicitly captured reference 'ref_countable_ref' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
124 
125   call(foo1);
126   call(foo2);
127   call(foo3);
128   call(foo4);
129 }
130 
131 void quiet() {
132 // This code is not expected to trigger any warnings.
133   {
134     RefCountable automatic;
135     RefCountable &ref_countable_ref = automatic;
136   }
137 
138   auto foo3 = [&]() {};
139   auto foo4 = [=]() {};
140 
141   call(foo3);
142   call(foo4);
143 
144   RefCountable *ref_countable = nullptr;
145 }
146 
147 template <typename Callback>
148 void map(RefCountable* start, [[clang::noescape]] Callback&& callback)
149 {
150   while (start) {
151     callback(*start);
152     start = start->next();
153   }
154 }
155 
156 template <typename Callback1, typename Callback2>
157 void doubleMap(RefCountable* start, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2)
158 {
159   while (start) {
160     callback1(*start);
161     callback2(*start);
162     start = start->next();
163   }
164 }
165 
166 void noescape_lambda() {
167   RefCountable* someObj = make_obj();
168   RefCountable* otherObj = make_obj();
169   map(make_obj(), [&](RefCountable& obj) {
170     otherObj->method();
171   });
172   doubleMap(make_obj(), [&](RefCountable& obj) {
173     otherObj->method();
174   }, [&](RefCountable& obj) {
175     otherObj->method();
176     // expected-warning@-1{{Implicitly captured raw-pointer 'otherObj' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
177   });
178   ([&] {
179     someObj->method();
180   })();
181 }
182 
183 void lambda_capture_param(RefCountable* obj) {
184   auto someLambda = [&]() {
185     obj->method();
186   };
187   someLambda();
188   someLambda();
189 }
190 
191 struct RefCountableWithLambdaCapturingThis {
192   void ref() const;
193   void deref() const;
194   void nonTrivial();
195 
196   void method_captures_this_safe() {
197     auto lambda = [&]() {
198       nonTrivial();
199     };
200     lambda();
201   }
202 
203   void method_captures_this_unsafe() {
204     auto lambda = [&]() {
205       nonTrivial();
206       // expected-warning@-1{{Implicitly captured raw-pointer 'this' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
207     };
208     call(lambda);
209   }
210 
211   void method_captures_this_unsafe_capture_local_var_explicitly() {
212     RefCountable* x = make_obj();
213     call([this, protectedThis = RefPtr { this }, x]() {
214       // expected-warning@-1{{Captured raw-pointer 'x' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
215       nonTrivial();
216       x->method();
217     });
218   }
219 
220   void method_captures_this_with_other_protected_var() {
221     RefCountable* x = make_obj();
222     call([this, protectedX = RefPtr { x }]() {
223       // expected-warning@-1{{Captured raw-pointer 'this' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
224       nonTrivial();
225       protectedX->method();
226     });
227   }
228 
229   void method_captures_this_unsafe_capture_local_var_explicitly_with_deref() {
230     RefCountable* x = make_obj();
231     call([this, protectedThis = Ref { *this }, x]() {
232       // expected-warning@-1{{Captured raw-pointer 'x' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
233       nonTrivial();
234       x->method();
235     });
236   }
237 
238   void method_captures_this_unsafe_local_var_via_vardecl() {
239     RefCountable* x = make_obj();
240     auto lambda = [this, protectedThis = Ref { *this }, x]() {
241       // expected-warning@-1{{Captured raw-pointer 'x' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
242       nonTrivial();
243       x->method();
244     };
245     call(lambda);
246   }
247 
248   void method_captures_this_with_guardian() {
249     auto lambda = [this, protectedThis = Ref { *this }]() {
250       nonTrivial();
251     };
252     call(lambda);
253   }
254 
255   void method_captures_this_with_guardian_refPtr() {
256     auto lambda = [this, protectedThis = RefPtr { &*this }]() {
257       nonTrivial();
258     };
259     call(lambda);
260   }
261 
262 };
263 
264 struct NonRefCountableWithLambdaCapturingThis {
265   void nonTrivial();
266 
267   void method_captures_this_safe() {
268     auto lambda = [&]() {
269       nonTrivial();
270     };
271     lambda();
272   }
273 
274   void method_captures_this_unsafe() {
275     auto lambda = [&]() {
276       nonTrivial();
277     };
278     call(lambda);
279   }
280 };
281 
282 void trivial_lambda() {
283   RefCountable* ref_countable = make_obj();
284   auto trivial_lambda = [&]() {
285     return ref_countable->trivial();
286   };
287   trivial_lambda();
288 }
289 
290 void lambda_with_args(RefCountable* obj) {
291   auto trivial_lambda = [&](int v) {
292     obj->method();
293   };
294   trivial_lambda(1);
295 }
296 
297 void callFunctionOpaque(WTF::Function<void()>&&);
298 void callFunction(WTF::Function<void()>&& function) {
299   someFunction();
300   function();
301 }
302 
303 void lambda_converted_to_function(RefCountable* obj)
304 {
305   callFunction([&]() {
306     obj->method();
307     // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
308   });
309   callFunctionOpaque([&]() {
310     obj->method();
311     // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
312   });
313 }
314