//===- GCDAntipatternChecker.cpp ---------------------------------*- C++ -*-==// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines GCDAntipatternChecker which checks against a common // antipattern when synchronous API is emulated from asynchronous callbacks // using a semaphore: // // dispatch_semaphore_t sema = dispatch_semaphore_create(0); // // AnyCFunctionCall(^{ // // code… // dispatch_semaphore_signal(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 semaphores is used. // Good code would either use asynchronous API (when available), or perform // the necessary action in asynchronous callback. // // Currently, the check is performed using a simple heuristical AST pattern // matching. // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/Support/Debug.h" usingnamespaceclang; usingnamespaceento; usingnamespaceast_matchers; namespace { // ID of a node at which the diagnostic would be emitted. const char *WarnAtNode = …; class GCDAntipatternChecker : public Checker<check::ASTCodeBody> { … }; decltype(auto) callsName(const char *FunctionName) { … } decltype(auto) equalsBoundArgDecl(int ArgIdx, const char *DeclName) { … } decltype(auto) bindAssignmentToDecl(const char *DeclName) { … } /// The pattern is very common in tests, and it is OK to use it there. /// We have to heuristics for detecting tests: method name starts with "test" /// (used in XCTest), and a class name contains "mock" or "test" (used in /// helpers which are not tests themselves, but used exclusively in tests). static bool isTest(const Decl *D) { … } static auto findGCDAntiPatternWithSemaphore() -> decltype(compoundStmt()) { … } static auto findGCDAntiPatternWithGroup() -> decltype(compoundStmt()) { … } static void emitDiagnostics(const BoundNodes &Nodes, const char* Type, BugReporter &BR, AnalysisDeclContext *ADC, const GCDAntipatternChecker *Checker) { … } void GCDAntipatternChecker::checkASTCodeBody(const Decl *D, AnalysisManager &AM, BugReporter &BR) const { … } } // end of anonymous namespace void ento::registerGCDAntipattern(CheckerManager &Mgr) { … } bool ento::shouldRegisterGCDAntipattern(const CheckerManager &mgr) { … }