-//===- GCDAsyncSemaphoreChecker.cpp -----------------------------*- C++ -*-==//
+//===- GCDAntipatternChecker.cpp ---------------------------------*- C++ -*-==//
//
// The LLVM Compiler Infrastructure
//
//
//===----------------------------------------------------------------------===//
//
-// This file defines GCDAsyncSemaphoreChecker which checks against a common
+// This file defines GCDAntipatternChecker which checks against a common
// antipattern when synchronous API is emulated from asynchronous callbacks
-// using a semaphor:
+// using a semaphore:
+//
+// dispatch_semaphore_t sema = dispatch_semaphore_create(0);
//
-// dispatch_semapshore_t sema = dispatch_semaphore_create(0);
-
// AnyCFunctionCall(^{
// // codeā¦
-// dispatch_semapshore_signal(sema);
+// dispatch_semaphore_signal(sema);
// })
-// dispatch_semapshore_wait(sema, *)
+// dispatch_semaphore_wait(sema, *)
//
// Such code is a common performance problem, due to inability of GCD to
-// properly handle QoS when a combination of queues and semaphors is used.
+// properly handle QoS when a combination of queues and semaphores is used.
// Good code would either use asynchronous API (when available), or perform
// the necessary action in asynchronous callback.
//
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "llvm/Support/Debug.h"
-#define DEBUG_TYPE "gcdasyncsemaphorechecker"
-
using namespace clang;
using namespace ento;
using namespace ast_matchers;
const char *WarningBinding = "semaphore_wait";
-class GCDAsyncSemaphoreChecker : public Checker<check::ASTCodeBody> {
+class GCDAntipatternChecker : public Checker<check::ASTCodeBody> {
public:
void checkASTCodeBody(const Decl *D,
AnalysisManager &AM,
class Callback : public MatchFinder::MatchCallback {
BugReporter &BR;
- const GCDAsyncSemaphoreChecker *C;
+ const GCDAntipatternChecker *C;
AnalysisDeclContext *ADC;
public:
Callback(BugReporter &BR,
AnalysisDeclContext *ADC,
- const GCDAsyncSemaphoreChecker *C) : BR(BR), C(C), ADC(ADC) {}
+ const GCDAntipatternChecker *C) : BR(BR), C(C), ADC(ADC) {}
virtual void run(const MatchFinder::MatchResult &Result) override;
};
declRefExpr(to(varDecl().bind(DeclName)))));
}
-void GCDAsyncSemaphoreChecker::checkASTCodeBody(const Decl *D,
+void GCDAntipatternChecker::checkASTCodeBody(const Decl *D,
AnalysisManager &AM,
BugReporter &BR) const {
if (StringRef(DeclName).startswith("test"))
return;
}
+ if (const auto *OD = dyn_cast<ObjCMethodDecl>(D)) {
+ if (const auto *CD = dyn_cast<ObjCContainerDecl>(OD->getParent())) {
+ std::string ContainerName = CD->getNameAsString();
+ StringRef CN(ContainerName);
+ if (CN.contains_lower("test") || CN.contains_lower("mock"))
+ return;
+ }
+ }
const char *SemaphoreBinding = "semaphore_name";
auto SemaphoreCreateM = callExpr(callsName("dispatch_semaphore_create"));
ADC->getDecl(), C,
/*Name=*/"Semaphore performance anti-pattern",
/*Category=*/"Performance",
- "Possible semaphore performance anti-pattern: wait on a semaphore "
- "signalled to in a callback",
+ "Waiting on a semaphore with Grand Central Dispatch creates useless "
+ "threads and is subject to priority inversion; consider "
+ "using a synchronous API or changing the caller to be asynchronous",
PathDiagnosticLocation::createBegin(SW, BR.getSourceManager(), ADC),
SW->getSourceRange());
}
}
-void ento::registerGCDAsyncSemaphoreChecker(CheckerManager &Mgr) {
- Mgr.registerChecker<GCDAsyncSemaphoreChecker>();
+void ento::registerGCDAntipattern(CheckerManager &Mgr) {
+ Mgr.registerChecker<GCDAntipatternChecker>();
}
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.osx.GCDAsyncSemaphore %s -fblocks -verify
+// 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> {}
func(^{
dispatch_semaphore_signal(sema);
});
- dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}}
+ dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}}
}
// It's OK to use pattern in tests.
func(^{
dispatch_semaphore_signal(sema1);
});
- dispatch_semaphore_wait(sema1, 100); // expected-warning{{Possible semaphore performance anti-pattern}}
+ dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}}
dispatch_semaphore_t sema2 = dispatch_semaphore_create(0);
func(^{
dispatch_semaphore_signal(sema2);
});
- dispatch_semaphore_wait(sema2, 100); // expected-warning{{Possible semaphore performance anti-pattern}}
+ dispatch_semaphore_wait(sema2, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}}
}
void use_semaphor_antipattern_multiple_wait() {
dispatch_semaphore_signal(sema1);
});
// FIXME: multiple waits on same semaphor should not raise a warning.
- dispatch_semaphore_wait(sema1, 100); // expected-warning{{Possible semaphore performance anti-pattern}}
- dispatch_semaphore_wait(sema1, 100); // expected-warning{{Possible semaphore performance anti-pattern}}
+ dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}}
+ dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}}
}
void warn_incorrect_order() {
// if out of order.
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
- dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}}
+ dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}}
func(^{
dispatch_semaphore_signal(sema);
});
func_w_typedef(^{
dispatch_semaphore_signal(sema);
});
- dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}}
+ dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}}
}
void warn_nested_ast() {
dispatch_semaphore_signal(sema);
});
}
- dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}}
+ dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}}
}
void use_semaphore_assignment() {
func(^{
dispatch_semaphore_signal(sema);
});
- dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}}
+ dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}}
}
void use_semaphore_assignment_init() {
func(^{
dispatch_semaphore_signal(sema);
});
- dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}}
+ dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}}
}
void differentsemaphoreok() {
func(^{
dispatch_semaphore_signal((int)sema);
});
- dispatch_semaphore_wait((int)sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}}
+ dispatch_semaphore_wait((int)sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}}
}
-@interface Test1 : NSObject
+@interface MyInterface1 : NSObject
-(void)use_method_warn;
-(void)use_objc_callback_warn;
-(void)testNoWarn;
-(void)acceptBlock:(block_t)callback;
@end
-@implementation Test1
+@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{{Possible semaphore performance anti-pattern}}
+ dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}}
}
-(void)testNoWarn {
[self acceptBlock:^{
dispatch_semaphore_signal(sema);
}];
- dispatch_semaphore_wait(sema, 100); // expected-warning{{Possible semaphore performance anti-pattern}}
+ dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}}
}
-void use_objc_and_c_callback(Test1 *t) {
+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{{Possible semaphore performance anti-pattern}}
+ dispatch_semaphore_wait(sema, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}}
dispatch_semaphore_t sema1 = dispatch_semaphore_create(0);
[t acceptBlock:^{
dispatch_semaphore_signal(sema1);
}];
- dispatch_semaphore_wait(sema1, 100); // expected-warning{{Possible semaphore performance anti-pattern}}
+ dispatch_semaphore_wait(sema1, 100); // expected-warning{{Waiting on a semaphore with Grand Central Dispatch creates useless threads and is subject to priority inversion}}
+}
+@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