1 | // RUN: %clang_analyze_cc1 -analyzer-checker=core,optin.performance.GCDAntipattern %s -fblocks -verify |
2 | typedef signed char BOOL; |
3 | @protocol NSObject - (BOOL)isEqual:(id)object; @end |
4 | @interface NSObject <NSObject> {} |
5 | +(id)alloc; |
6 | -(id)init; |
7 | -(id)autorelease; |
8 | -(id)copy; |
9 | -(id)retain; |
10 | @end |
11 | |
12 | typedef int dispatch_semaphore_t; |
13 | typedef int dispatch_group_t; |
14 | typedef void (^block_t)(); |
15 | |
16 | dispatch_semaphore_t dispatch_semaphore_create(int); |
17 | dispatch_group_t dispatch_group_create(); |
18 | void dispatch_group_enter(dispatch_group_t); |
19 | void dispatch_group_leave(dispatch_group_t); |
20 | void dispatch_group_wait(dispatch_group_t, int); |
21 | |
22 | |
23 | void dispatch_semaphore_wait(dispatch_semaphore_t, int); |
24 | void dispatch_semaphore_signal(dispatch_semaphore_t); |
25 | |
26 | void func(void (^)(void)); |
27 | void func_w_typedef(block_t); |
28 | |
29 | int coin(); |
30 | |
31 | void use_semaphor_antipattern() { |
32 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
33 | |
34 | func(^{ |
35 | dispatch_semaphore_signal(sema); |
36 | }); |
37 | dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback using a semaphore}} |
38 | } |
39 | |
40 | // It's OK to use pattern in tests. |
41 | // We simply match the containing function name against ^test. |
42 | void test_no_warning() { |
43 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
44 | |
45 | func(^{ |
46 | dispatch_semaphore_signal(sema); |
47 | }); |
48 | dispatch_semaphore_wait(sema, 100); |
49 | } |
50 | |
51 | void use_semaphor_antipattern_multiple_times() { |
52 | dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); |
53 | |
54 | func(^{ |
55 | dispatch_semaphore_signal(sema1); |
56 | }); |
57 | dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a callback using a semaphore}} |
58 | |
59 | dispatch_semaphore_t sema2 = dispatch_semaphore_create(0); |
60 | |
61 | func(^{ |
62 | dispatch_semaphore_signal(sema2); |
63 | }); |
64 | dispatch_semaphore_wait(sema2, 100); // expected-warning{{Waiting on a callback using a semaphore}} |
65 | } |
66 | |
67 | void use_semaphor_antipattern_multiple_wait() { |
68 | dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); |
69 | |
70 | func(^{ |
71 | dispatch_semaphore_signal(sema1); |
72 | }); |
73 | // FIXME: multiple waits on same semaphor should not raise a warning. |
74 | dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a callback using a semaphore}} |
75 | dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a callback using a semaphore}} |
76 | } |
77 | |
78 | void warn_incorrect_order() { |
79 | // FIXME: ASTMatchers do not allow ordered matching, so would match even |
80 | // if out of order. |
81 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
82 | |
83 | dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback}} |
84 | func(^{ |
85 | dispatch_semaphore_signal(sema); |
86 | }); |
87 | } |
88 | |
89 | void warn_w_typedef() { |
90 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
91 | |
92 | func_w_typedef(^{ |
93 | dispatch_semaphore_signal(sema); |
94 | }); |
95 | dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback using a semaphore}} |
96 | } |
97 | |
98 | void warn_nested_ast() { |
99 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
100 | |
101 | if (coin()) { |
102 | func(^{ |
103 | dispatch_semaphore_signal(sema); |
104 | }); |
105 | } else { |
106 | func(^{ |
107 | dispatch_semaphore_signal(sema); |
108 | }); |
109 | } |
110 | dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback using a semaphore}} |
111 | } |
112 | |
113 | void use_semaphore_assignment() { |
114 | dispatch_semaphore_t sema; |
115 | sema = dispatch_semaphore_create(0); |
116 | |
117 | func(^{ |
118 | dispatch_semaphore_signal(sema); |
119 | }); |
120 | dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback using a semaphore}} |
121 | } |
122 | |
123 | void use_semaphore_assignment_init() { |
124 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
125 | sema = dispatch_semaphore_create(1); |
126 | |
127 | func(^{ |
128 | dispatch_semaphore_signal(sema); |
129 | }); |
130 | dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback using a semaphore}} |
131 | } |
132 | |
133 | void differentsemaphoreok() { |
134 | dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); |
135 | dispatch_semaphore_t sema2 = dispatch_semaphore_create(0); |
136 | |
137 | func(^{ |
138 | dispatch_semaphore_signal(sema1); |
139 | }); |
140 | dispatch_semaphore_wait(sema2, 100); // no-warning |
141 | } |
142 | |
143 | void nosignalok() { |
144 | dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); |
145 | dispatch_semaphore_wait(sema1, 100); |
146 | } |
147 | |
148 | void nowaitok() { |
149 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
150 | func(^{ |
151 | dispatch_semaphore_signal(sema); |
152 | }); |
153 | } |
154 | |
155 | void noblockok() { |
156 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
157 | dispatch_semaphore_signal(sema); |
158 | dispatch_semaphore_wait(sema, 100); |
159 | } |
160 | |
161 | void storedblockok() { |
162 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
163 | block_t b = ^{ |
164 | dispatch_semaphore_signal(sema); |
165 | }; |
166 | dispatch_semaphore_wait(sema, 100); |
167 | } |
168 | |
169 | void passed_semaphore_ok(dispatch_semaphore_t sema) { |
170 | func(^{ |
171 | dispatch_semaphore_signal(sema); |
172 | }); |
173 | dispatch_semaphore_wait(sema, 100); |
174 | } |
175 | |
176 | void warn_with_cast() { |
177 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
178 | |
179 | func(^{ |
180 | dispatch_semaphore_signal((int)sema); |
181 | }); |
182 | dispatch_semaphore_wait((int)sema, 100); // expected-warning{{Waiting on a callback using a semaphore}} |
183 | } |
184 | |
185 | @interface MyInterface1 : NSObject |
186 | -(void)use_method_warn; |
187 | -(void) pass_block_as_second_param_warn; |
188 | -(void)use_objc_callback_warn; |
189 | -(void) use_dispatch_group; |
190 | -(void)testNoWarn; |
191 | -(void)acceptBlock:(block_t)callback; |
192 | -(void)flag:(int)flag acceptBlock:(block_t)callback; |
193 | @end |
194 | |
195 | @implementation MyInterface1 |
196 | |
197 | -(void)use_method_warn { |
198 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
199 | |
200 | func(^{ |
201 | dispatch_semaphore_signal(sema); |
202 | }); |
203 | dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback}} |
204 | } |
205 | |
206 | -(void) pass_block_as_second_param_warn { |
207 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
208 | |
209 | [self flag:1 acceptBlock:^{ |
210 | dispatch_semaphore_signal(sema); |
211 | }]; |
212 | dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback}} |
213 | } |
214 | |
215 | -(void)testNoWarn { |
216 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
217 | |
218 | func(^{ |
219 | dispatch_semaphore_signal(sema); |
220 | }); |
221 | dispatch_semaphore_wait(sema, 100); |
222 | } |
223 | |
224 | -(void)acceptBlock:(block_t) callback { |
225 | callback(); |
226 | } |
227 | |
228 | -(void)flag:(int)flag acceptBlock:(block_t)callback { |
229 | callback(); |
230 | } |
231 | |
232 | -(void)use_objc_callback_warn { |
233 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
234 | |
235 | [self acceptBlock:^{ |
236 | dispatch_semaphore_signal(sema); |
237 | }]; |
238 | dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback}} |
239 | } |
240 | |
241 | -(void)use_dispatch_group { |
242 | dispatch_group_t group = dispatch_group_create(); |
243 | dispatch_group_enter(group); |
244 | [self acceptBlock:^{ |
245 | dispatch_group_leave(group); |
246 | }]; |
247 | dispatch_group_wait(group, 100); // expected-warning{{Waiting on a callback using a group}} |
248 | |
249 | } |
250 | |
251 | void use_objc_and_c_callback(MyInterface1 *t) { |
252 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
253 | |
254 | func(^{ |
255 | dispatch_semaphore_signal(sema); |
256 | }); |
257 | dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback using a semaphore}} |
258 | |
259 | dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); |
260 | |
261 | [t acceptBlock:^{ |
262 | dispatch_semaphore_signal(sema1); |
263 | }]; |
264 | dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a callback}} |
265 | } |
266 | @end |
267 | |
268 | // No warnings: class name contains "test" |
269 | @interface Test1 : NSObject |
270 | -(void)use_method_warn; |
271 | @end |
272 | |
273 | @implementation Test1 |
274 | -(void)use_method_warn { |
275 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
276 | |
277 | func(^{ |
278 | dispatch_semaphore_signal(sema); |
279 | }); |
280 | dispatch_semaphore_wait(sema, 100); |
281 | } |
282 | @end |
283 | |
284 | |
285 | // No warnings: class name contains "mock" |
286 | @interface Mock1 : NSObject |
287 | -(void)use_method_warn; |
288 | @end |
289 | |
290 | @implementation Mock1 |
291 | -(void)use_method_warn { |
292 | dispatch_semaphore_t sema = dispatch_semaphore_create(0); |
293 | |
294 | func(^{ |
295 | dispatch_semaphore_signal(sema); |
296 | }); |
297 | dispatch_semaphore_wait(sema, 100); |
298 | } |
299 | @end |
300 | |
301 | void dispatch_group_wait_func(MyInterface1 *M) { |
302 | dispatch_group_t group = dispatch_group_create(); |
303 | dispatch_group_enter(group); |
304 | |
305 | func(^{ |
306 | dispatch_group_leave(group); |
307 | }); |
308 | dispatch_group_wait(group, 100); // expected-warning{{Waiting on a callback using a group}} |
309 | } |
310 | |
311 | |
312 | void dispatch_group_wait_cfunc(MyInterface1 *M) { |
313 | dispatch_group_t group = dispatch_group_create(); |
314 | dispatch_group_enter(group); |
315 | [M acceptBlock:^{ |
316 | dispatch_group_leave(group); |
317 | }]; |
318 | dispatch_group_wait(group, 100); // expected-warning{{Waiting on a callback using a group}} |
319 | } |
320 | |
321 | void dispatch_group_and_semaphore_use(MyInterface1 *M) { |
322 | dispatch_group_t group = dispatch_group_create(); |
323 | dispatch_group_enter(group); |
324 | [M acceptBlock:^{ |
325 | dispatch_group_leave(group); |
326 | }]; |
327 | dispatch_group_wait(group, 100); // expected-warning{{Waiting on a callback using a group}} |
328 | |
329 | dispatch_semaphore_t sema1 = dispatch_semaphore_create(0); |
330 | |
331 | [M acceptBlock:^{ |
332 | dispatch_semaphore_signal(sema1); |
333 | }]; |
334 | dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a callback using a semaphore}} |
335 | } |
336 | |
337 | void no_warn_on_nonzero_semaphore(MyInterface1 *M) { |
338 | dispatch_semaphore_t sema1 = dispatch_semaphore_create(1); |
339 | |
340 | [M acceptBlock:^{ |
341 | dispatch_semaphore_signal(sema1); |
342 | }]; |
343 | dispatch_semaphore_wait(sema1, 100); // no-warning |
344 | } |
345 | |
346 | |