aboutsummaryrefslogtreecommitdiff
path: root/test/Analysis/gcdantipatternchecker_test.m
blob: 24ffe8975dd9e480460343979c2f79e734d73292 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
// RUN: %clang_analyze_cc1 -analyzer-checker=core,optin.performance.GCDAntipattern %s -fblocks -verify
typedef signed char BOOL;
@protocol NSObject  - (BOOL)isEqual:(id)object; @end
@interface NSObject <NSObject> {}
+(id)alloc;
-(id)init;
-(id)autorelease;
-(id)copy;
-(id)retain;
@end

typedef int dispatch_semaphore_t;
typedef int dispatch_group_t;
typedef void (^block_t)();

dispatch_semaphore_t dispatch_semaphore_create(int);
dispatch_group_t dispatch_group_create();
void dispatch_group_enter(dispatch_group_t);
void dispatch_group_leave(dispatch_group_t);
void dispatch_group_wait(dispatch_group_t, int);


void dispatch_semaphore_wait(dispatch_semaphore_t, int);
void dispatch_semaphore_signal(dispatch_semaphore_t);

void func(void (^)(void));
void func_w_typedef(block_t);

int coin();

void use_semaphor_antipattern() {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);

  func(^{
      dispatch_semaphore_signal(sema);
  });
  dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback using a semaphore}}
}

// It's OK to use pattern in tests.
// We simply match the containing function name against ^test.
void test_no_warning() {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);

  func(^{
      dispatch_semaphore_signal(sema);
  });
  dispatch_semaphore_wait(sema, 100);
}

void use_semaphor_antipattern_multiple_times() {
  dispatch_semaphore_t sema1 = dispatch_semaphore_create(0);

  func(^{
      dispatch_semaphore_signal(sema1);
  });
  dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a callback using a semaphore}}

  dispatch_semaphore_t sema2 = dispatch_semaphore_create(0);

  func(^{
      dispatch_semaphore_signal(sema2);
  });
  dispatch_semaphore_wait(sema2, 100); // expected-warning{{Waiting on a callback using a semaphore}}
}

void use_semaphor_antipattern_multiple_wait() {
  dispatch_semaphore_t sema1 = dispatch_semaphore_create(0);

  func(^{
      dispatch_semaphore_signal(sema1);
  });
  // FIXME: multiple waits on same semaphor should not raise a warning.
  dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a callback using a semaphore}}
  dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a callback using a semaphore}}
}

void warn_incorrect_order() {
  // FIXME: ASTMatchers do not allow ordered matching, so would match even
  // if out of order.
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);

  dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback}}
  func(^{
      dispatch_semaphore_signal(sema);
  });
}

void warn_w_typedef() {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);

  func_w_typedef(^{
      dispatch_semaphore_signal(sema);
  });
  dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback using a semaphore}}
}

void warn_nested_ast() {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);

  if (coin()) {
    func(^{
         dispatch_semaphore_signal(sema);
         });
  } else {
    func(^{
         dispatch_semaphore_signal(sema);
         });
  }
  dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback using a semaphore}}
}

void use_semaphore_assignment() {
  dispatch_semaphore_t sema;
  sema = dispatch_semaphore_create(0);

  func(^{
      dispatch_semaphore_signal(sema);
  });
  dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback using a semaphore}}
}

void use_semaphore_assignment_init() {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);
  sema = dispatch_semaphore_create(1);

  func(^{
      dispatch_semaphore_signal(sema);
  });
  dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback using a semaphore}}
}

void differentsemaphoreok() {
  dispatch_semaphore_t sema1 = dispatch_semaphore_create(0);
  dispatch_semaphore_t sema2 = dispatch_semaphore_create(0);

  func(^{
      dispatch_semaphore_signal(sema1);
  });
  dispatch_semaphore_wait(sema2, 100); // no-warning
}

void nosignalok() {
  dispatch_semaphore_t sema1 = dispatch_semaphore_create(0);
  dispatch_semaphore_wait(sema1, 100);
}

void nowaitok() {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);
  func(^{
      dispatch_semaphore_signal(sema);
  });
}

void noblockok() {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);
  dispatch_semaphore_signal(sema);
  dispatch_semaphore_wait(sema, 100);
}

void storedblockok() {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);
  block_t b = ^{
      dispatch_semaphore_signal(sema);
  };
  dispatch_semaphore_wait(sema, 100);
}

void passed_semaphore_ok(dispatch_semaphore_t sema) {
  func(^{
      dispatch_semaphore_signal(sema);
  });
  dispatch_semaphore_wait(sema, 100);
}

void warn_with_cast() {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);

  func(^{
      dispatch_semaphore_signal((int)sema);
  });
  dispatch_semaphore_wait((int)sema, 100); // expected-warning{{Waiting on a callback using a semaphore}}
}

@interface MyInterface1 : NSObject
-(void)use_method_warn;
-(void) pass_block_as_second_param_warn;
-(void)use_objc_callback_warn;
-(void) use_dispatch_group;
-(void)testNoWarn;
-(void)acceptBlock:(block_t)callback;
-(void)flag:(int)flag acceptBlock:(block_t)callback;
@end

@implementation MyInterface1

-(void)use_method_warn {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);

  func(^{
      dispatch_semaphore_signal(sema);
  });
  dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback}}
}

-(void) pass_block_as_second_param_warn {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);

  [self flag:1 acceptBlock:^{
      dispatch_semaphore_signal(sema);
  }];
  dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback}}
}

-(void)testNoWarn {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);

  func(^{
      dispatch_semaphore_signal(sema);
  });
  dispatch_semaphore_wait(sema, 100);
}

-(void)acceptBlock:(block_t) callback {
  callback();
}

-(void)flag:(int)flag acceptBlock:(block_t)callback {
  callback();
}

-(void)use_objc_callback_warn {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);

  [self acceptBlock:^{
      dispatch_semaphore_signal(sema);
  }];
  dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback}}
}

-(void)use_dispatch_group {
  dispatch_group_t group = dispatch_group_create();
  dispatch_group_enter(group);
  [self acceptBlock:^{
    dispatch_group_leave(group);
  }];
  dispatch_group_wait(group, 100); // expected-warning{{Waiting on a callback using a group}}

}

void use_objc_and_c_callback(MyInterface1 *t) {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);

  func(^{
      dispatch_semaphore_signal(sema);
  });
  dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a callback using a semaphore}}

  dispatch_semaphore_t sema1 = dispatch_semaphore_create(0);

  [t acceptBlock:^{
      dispatch_semaphore_signal(sema1);
  }];
  dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a callback}}
}
@end

// No warnings: class name contains "test"
@interface Test1 : NSObject
-(void)use_method_warn;
@end

@implementation Test1
-(void)use_method_warn {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);

  func(^{
      dispatch_semaphore_signal(sema);
  });
  dispatch_semaphore_wait(sema, 100);
}
@end


// No warnings: class name contains "mock"
@interface Mock1 : NSObject
-(void)use_method_warn;
@end

@implementation Mock1
-(void)use_method_warn {
  dispatch_semaphore_t sema = dispatch_semaphore_create(0);

  func(^{
      dispatch_semaphore_signal(sema);
  });
  dispatch_semaphore_wait(sema, 100);
}
@end

void dispatch_group_wait_func(MyInterface1 *M) {
  dispatch_group_t group = dispatch_group_create();
  dispatch_group_enter(group);

  func(^{
      dispatch_group_leave(group);
  });
  dispatch_group_wait(group, 100); // expected-warning{{Waiting on a callback using a group}}
}


void dispatch_group_wait_cfunc(MyInterface1 *M) {
  dispatch_group_t group = dispatch_group_create();
  dispatch_group_enter(group);
  [M acceptBlock:^{
    dispatch_group_leave(group);
  }];
  dispatch_group_wait(group, 100); // expected-warning{{Waiting on a callback using a group}}
}

void dispatch_group_and_semaphore_use(MyInterface1 *M) {
  dispatch_group_t group = dispatch_group_create();
  dispatch_group_enter(group);
  [M acceptBlock:^{
    dispatch_group_leave(group);
  }];
  dispatch_group_wait(group, 100); // expected-warning{{Waiting on a callback using a group}}

  dispatch_semaphore_t sema1 = dispatch_semaphore_create(0);

  [M acceptBlock:^{
      dispatch_semaphore_signal(sema1);
  }];
  dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a callback using a semaphore}}
}

void no_warn_on_nonzero_semaphore(MyInterface1 *M) {
  dispatch_semaphore_t sema1 = dispatch_semaphore_create(1);

  [M acceptBlock:^{
      dispatch_semaphore_signal(sema1);
  }];
  dispatch_semaphore_wait(sema1, 100); // no-warning
}