| // 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 |
| } |
| |