Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -749,6 +749,11 @@ Dependencies<[SmartPtrModeling]>, Documentation; +def CPlusPlus11LockChecker : Checker<"CPlusPlus11Lock">, + HelpText<"Simple C++11 lock -> unlock checker">, + Dependencies<[PthreadLockBase]>, + Documentation; + } // end: "alpha.cplusplus" Index: clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -70,11 +70,17 @@ class PthreadLockChecker : public Checker { public: - enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics }; + enum LockingSemantics { + NotApplicable = 0, + PthreadSemantics, + XNUSemantics, + CPlusPlusSemantics, + }; enum CheckerKind { CK_PthreadLockChecker, CK_FuchsiaLockChecker, CK_C11LockChecker, + CK_CPlusPlus11LockChecker, CK_NumCheckKinds }; DefaultBool ChecksEnabled[CK_NumCheckKinds]; @@ -83,7 +89,7 @@ private: typedef void (PthreadLockChecker::*FnCheck)(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; CallDescriptionMap PThreadCallbacks = { // Init. {{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock}, @@ -164,49 +170,101 @@ {{"mtx_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock}, }; + CallDescriptionMap CPlusPlus11Callbacks = { + + // Acquire. + {{{"std", "mutex", "lock"}, 0, 0}, + &PthreadLockChecker::AcquireCPlusPlus11Lock}, + {{{"std", "timed_mutex", "lock"}, 0, 0}, + &PthreadLockChecker::AcquireCPlusPlus11Lock}, + {{{"std", "recursive_mutex", "lock"}, 0, 0}, + &PthreadLockChecker::AcquireCPlusPlus11Lock}, + {{{"std", "recursive_timed_mutex", "lock"}, 0, 0}, + &PthreadLockChecker::AcquireCPlusPlus11Lock}, + {{{"std", "shared_mutex", "lock"}, 0, 0}, + &PthreadLockChecker::AcquireCPlusPlus11Lock}, + {{{"std", "shared_timed_mutex", "lock"}, 0, 0}, + &PthreadLockChecker::AcquireCPlusPlus11Lock}, + + // Try. + {{{"std", "mutex", "try_lock"}, 0, 0}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "timed_mutex", "try_lock"}, 0, 0}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "recursive_mutex", "try_lock"}, 0, 0}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "recursive_timed_mutex", "try_lock"}, 0, 0}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "shared_mutex", "try_lock"}, 0, 0}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "shared_timed_mutex", "try_lock"}, 0, 0}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + + // Release. + {{{"std", "mutex", "unlock"}, 0, 0}, + &PthreadLockChecker::ReleaseCPlusPlus11Lock}, + {{{"std", "timed_mutex", "unlock"}, 0, 0}, + &PthreadLockChecker::ReleaseCPlusPlus11Lock}, + {{{"std", "recursive_mutex", "unlock"}, 0, 0}, + &PthreadLockChecker::ReleaseCPlusPlus11Lock}, + {{{"std", "recursive_timed_mutex", "unlock"}, 0, 0}, + &PthreadLockChecker::ReleaseCPlusPlus11Lock}, + {{{"std", "shared_mutex", "unlock"}, 0, 0}, + &PthreadLockChecker::ReleaseCPlusPlus11Lock}, + {{{"std", "shared_timed_mutex", "unlock"}, 0, 0}, + &PthreadLockChecker::ReleaseCPlusPlus11Lock}, + }; + ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const; void reportUseDestroyedBug(const CallEvent &Call, CheckerContext &C, - unsigned ArgNo, CheckerKind checkKind) const; + const Expr *MtxExpr, CheckerKind CheckKind) const; // Init. void InitAnyLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; void InitLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, - SVal Lock, CheckerKind checkkind) const; + SVal Lock, CheckerKind CheckKind) const; // Lock, Try-lock. void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; void AcquireXNULock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; + void AcquireCPlusPlus11Lock(const CallEvent &Call, CheckerContext &C, + CheckerKind CheckKind) const; void TryPthreadLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; void TryXNULock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; void TryFuchsiaLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; void TryC11Lock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; - void AcquireLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, - SVal lock, bool isTryLock, LockingSemantics semantics, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; + void TryCPlusPlus11Lock(const CallEvent &Call, CheckerContext &C, + CheckerKind CheckKind) const; + void AcquireLockAux(const CallEvent &Call, CheckerContext &C, + const Expr *MtxExpr, SVal MtxVal, bool IsTryLock, + LockingSemantics semantics, CheckerKind CheckKind) const; // Release. void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; - void ReleaseLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, - SVal lock, CheckerKind checkkind) const; + CheckerKind CheckKind) const; + void ReleaseCPlusPlus11Lock(const CallEvent &Call, CheckerContext &C, + CheckerKind CheckKind) const; + void ReleaseLockAux(const CallEvent &Call, CheckerContext &C, + const Expr *MtxExpr, SVal MtxVal, + LockingSemantics semantics, CheckerKind CheckKind) const; // Destroy. void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; void DestroyXNULock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; void DestroyLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, SVal Lock, LockingSemantics semantics, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; public: void checkPostCall(const CallEvent &Call, CheckerContext &C) const; @@ -226,18 +284,18 @@ mutable std::unique_ptr BT_initlock[CK_NumCheckKinds]; mutable std::unique_ptr BT_lor[CK_NumCheckKinds]; - void initBugType(CheckerKind checkKind) const { - if (BT_doublelock[checkKind]) + void initBugType(CheckerKind CheckKind) const { + if (BT_doublelock[CheckKind]) return; - BT_doublelock[checkKind].reset( - new BugType{CheckNames[checkKind], "Double locking", "Lock checker"}); - BT_doubleunlock[checkKind].reset( - new BugType{CheckNames[checkKind], "Double unlocking", "Lock checker"}); - BT_destroylock[checkKind].reset(new BugType{ - CheckNames[checkKind], "Use destroyed lock", "Lock checker"}); - BT_initlock[checkKind].reset(new BugType{ - CheckNames[checkKind], "Init invalid lock", "Lock checker"}); - BT_lor[checkKind].reset(new BugType{CheckNames[checkKind], + BT_doublelock[CheckKind].reset( + new BugType{CheckNames[CheckKind], "Double locking", "Lock checker"}); + BT_doubleunlock[CheckKind].reset( + new BugType{CheckNames[CheckKind], "Double unlocking", "Lock checker"}); + BT_destroylock[CheckKind].reset(new BugType{ + CheckNames[CheckKind], "Use destroyed lock", "Lock checker"}); + BT_initlock[CheckKind].reset(new BugType{ + CheckNames[CheckKind], "Init invalid lock", "Lock checker"}); + BT_lor[CheckKind].reset(new BugType{CheckNames[CheckKind], "Lock order reversal", "Lock checker"}); } }; @@ -260,7 +318,7 @@ // with exactly one identifier? // FIXME: Try to handle cases when the implementation was inlined rather // than just giving up. - if (!Call.isGlobalCFunction() || C.wasInlined) + if (C.wasInlined) return; if (const FnCheck *Callback = PThreadCallbacks.lookup(Call)) @@ -269,6 +327,8 @@ (this->**Callback)(Call, C, CK_FuchsiaLockChecker); else if (const FnCheck *Callback = C11Callbacks.lookup(Call)) (this->**Callback)(Call, C, CK_C11LockChecker); + else if (const FnCheck *Callback = CPlusPlus11Callbacks.lookup(Call)) + (this->**Callback)(Call, C, CK_CPlusPlus11LockChecker); } // When a lock is destroyed, in some semantics(like PthreadSemantics) we are not @@ -341,53 +401,73 @@ void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, PthreadSemantics, - checkKind); + CheckerKind CheckKind) const { + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false, + PthreadSemantics, CheckKind); } void PthreadLockChecker::AcquireXNULock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, XNUSemantics, - checkKind); + CheckerKind CheckKind) const { + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false, + XNUSemantics, CheckKind); +} + +void PthreadLockChecker::AcquireCPlusPlus11Lock(const CallEvent &Call, + CheckerContext &C, + CheckerKind CheckKind) const { + auto MemberCall = cast(&Call); + auto ThisExpr = MemberCall->getCXXThisExpr(); + auto ThisVal = MemberCall->getCXXThisVal(); + AcquireLockAux(Call, C, ThisExpr, ThisVal, false, CPlusPlusSemantics, + CheckKind); } void PthreadLockChecker::TryPthreadLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics, - checkKind); + CheckerKind CheckKind) const { + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, + PthreadSemantics, CheckKind); } void PthreadLockChecker::TryXNULock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics, - checkKind); + CheckerKind CheckKind) const { + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, + PthreadSemantics, CheckKind); } void PthreadLockChecker::TryFuchsiaLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics, - checkKind); + CheckerKind CheckKind) const { + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, + PthreadSemantics, CheckKind); } void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics, - checkKind); + CheckerKind CheckKind) const { + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, + PthreadSemantics, CheckKind); +} + +void PthreadLockChecker::TryCPlusPlus11Lock(const CallEvent &Call, + CheckerContext &C, + CheckerKind CheckKind) const { + auto MemberCall = cast(&Call); + auto ThisExpr = MemberCall->getCXXThisExpr(); + auto ThisVal = MemberCall->getCXXThisVal(); + AcquireLockAux(Call, C, ThisExpr, ThisVal, true, CPlusPlusSemantics, + CheckKind); } void PthreadLockChecker::AcquireLockAux(const CallEvent &Call, - CheckerContext &C, unsigned ArgNo, - SVal lock, bool isTryLock, - enum LockingSemantics semantics, - CheckerKind checkKind) const { - if (!ChecksEnabled[checkKind]) + CheckerContext &C, const Expr *MtxExpr, + SVal MtxVal, bool IsTryLock, + LockingSemantics semantics, + CheckerKind CheckKind) const { + if (!ChecksEnabled[CheckKind]) return; - const MemRegion *lockR = lock.getAsRegion(); + const MemRegion *lockR = MtxVal.getAsRegion(); if (!lockR) return; @@ -398,23 +478,24 @@ if (const LockState *LState = state->get(lockR)) { if (LState->isLocked()) { + // Ignore twice recursive lock ExplodedNode *N = C.generateErrorNode(); if (!N) return; - initBugType(checkKind); + initBugType(CheckKind); auto report = std::make_unique( - *BT_doublelock[checkKind], "This lock has already been acquired", N); - report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); + *BT_doublelock[CheckKind], "This lock has already been acquired", N); + report->addRange(MtxExpr->getSourceRange()); C.emitReport(std::move(report)); return; } else if (LState->isDestroyed()) { - reportUseDestroyedBug(Call, C, ArgNo, checkKind); + reportUseDestroyedBug(Call, C, MtxExpr, CheckKind); return; } } ProgramStateRef lockSucc = state; - if (isTryLock) { + if (IsTryLock) { // Bifurcate the state, and allow a mode where the lock acquisition fails. SVal RetVal = Call.getReturnValue(); if (auto DefinedRetVal = RetVal.getAs()) { @@ -423,6 +504,7 @@ case PthreadSemantics: std::tie(lockFail, lockSucc) = state->assume(*DefinedRetVal); break; + case CPlusPlusSemantics: case XNUSemantics: std::tie(lockSucc, lockFail) = state->assume(*DefinedRetVal); break; @@ -446,8 +528,9 @@ // We might want to handle the case when the mutex lock function was inlined // and returned an Unknown or Undefined value. } else { - // XNU locking semantics return void on non-try locks - assert((semantics == XNUSemantics) && "Unknown locking semantics"); + // XNU and C++ locking semantics return void on non-try locks + assert((semantics == CPlusPlusSemantics || semantics == XNUSemantics) && + "Unknown locking semantics"); lockSucc = state; } @@ -459,18 +542,28 @@ void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - ReleaseLockAux(Call, C, 0, Call.getArgSVal(0), checkKind); + CheckerKind CheckKind) const { + ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), NotApplicable, + CheckKind); +} + +void PthreadLockChecker::ReleaseCPlusPlus11Lock(const CallEvent &Call, + CheckerContext &C, + CheckerKind CheckKind) const { + auto MemberCall = cast(&Call); + auto ThisExpr = MemberCall->getCXXThisExpr(); + auto ThisVal = MemberCall->getCXXThisVal(); + ReleaseLockAux(Call, C, ThisExpr, ThisVal, CPlusPlusSemantics, CheckKind); } void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call, - CheckerContext &C, unsigned ArgNo, - SVal lock, - CheckerKind checkKind) const { - if (!ChecksEnabled[checkKind]) + CheckerContext &C, const Expr *MtxExpr, + SVal MtxVal, LockingSemantics semantics, + CheckerKind CheckKind) const { + if (!ChecksEnabled[CheckKind]) return; - const MemRegion *lockR = lock.getAsRegion(); + const MemRegion *lockR = MtxVal.getAsRegion(); if (!lockR) return; @@ -484,15 +577,15 @@ ExplodedNode *N = C.generateErrorNode(); if (!N) return; - initBugType(checkKind); + initBugType(CheckKind); auto Report = std::make_unique( - *BT_doubleunlock[checkKind], "This lock has already been unlocked", + *BT_doubleunlock[CheckKind], "This lock has already been unlocked", N); - Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); + Report->addRange(MtxExpr->getSourceRange()); C.emitReport(std::move(Report)); return; } else if (LState->isDestroyed()) { - reportUseDestroyedBug(Call, C, ArgNo, checkKind); + reportUseDestroyedBug(Call, C, MtxExpr, CheckKind); return; } } @@ -505,13 +598,13 @@ ExplodedNode *N = C.generateErrorNode(); if (!N) return; - initBugType(checkKind); + initBugType(CheckKind); auto report = std::make_unique( - *BT_lor[checkKind], + *BT_lor[CheckKind], "This was not the most recently acquired lock. Possible " "lock order reversal", N); - report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); + report->addRange(MtxExpr->getSourceRange()); C.emitReport(std::move(report)); return; } @@ -525,22 +618,21 @@ void PthreadLockChecker::DestroyPthreadLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics, checkKind); + CheckerKind CheckKind) const { + DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics, CheckKind); } void PthreadLockChecker::DestroyXNULock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - DestroyLockAux(Call, C, 0, Call.getArgSVal(0), XNUSemantics, checkKind); + CheckerKind CheckKind) const { + DestroyLockAux(Call, C, 0, Call.getArgSVal(0), XNUSemantics, CheckKind); } void PthreadLockChecker::DestroyLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, - SVal Lock, - enum LockingSemantics semantics, - CheckerKind checkKind) const { - if (!ChecksEnabled[checkKind]) + SVal Lock, LockingSemantics semantics, + CheckerKind CheckKind) const { + if (!ChecksEnabled[CheckKind]) return; const MemRegion *LockR = Lock.getAsRegion(); @@ -592,22 +684,22 @@ ExplodedNode *N = C.generateErrorNode(); if (!N) return; - initBugType(checkKind); + initBugType(CheckKind); auto Report = std::make_unique( - *BT_destroylock[checkKind], Message, N); + *BT_destroylock[CheckKind], Message, N); Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); C.emitReport(std::move(Report)); } void PthreadLockChecker::InitAnyLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - InitLockAux(Call, C, 0, Call.getArgSVal(0), checkKind); + CheckerKind CheckKind) const { + InitLockAux(Call, C, 0, Call.getArgSVal(0), CheckKind); } void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, SVal Lock, - CheckerKind checkKind) const { - if (!ChecksEnabled[checkKind]) + CheckerKind CheckKind) const { + if (!ChecksEnabled[CheckKind]) return; const MemRegion *LockR = Lock.getAsRegion(); @@ -638,24 +730,24 @@ ExplodedNode *N = C.generateErrorNode(); if (!N) return; - initBugType(checkKind); + initBugType(CheckKind); auto Report = std::make_unique( - *BT_initlock[checkKind], Message, N); + *BT_initlock[CheckKind], Message, N); Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); C.emitReport(std::move(Report)); } void PthreadLockChecker::reportUseDestroyedBug(const CallEvent &Call, CheckerContext &C, - unsigned ArgNo, - CheckerKind checkKind) const { + const Expr *MtxExpr, + CheckerKind CheckKind) const { ExplodedNode *N = C.generateErrorNode(); if (!N) return; - initBugType(checkKind); + initBugType(CheckKind); auto Report = std::make_unique( - *BT_destroylock[checkKind], "This lock has already been destroyed", N); - Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); + *BT_destroylock[CheckKind], "This lock has already been destroyed", N); + Report->addRange(MtxExpr->getSourceRange()); C.emitReport(std::move(Report)); } @@ -691,10 +783,10 @@ const CallEvent *Call) const { bool IsLibraryFunction = false; - if (Call && Call->isGlobalCFunction()) { + if (Call) { // Avoid invalidating mutex state when a known supported function is called. if (PThreadCallbacks.lookup(*Call) || FuchsiaCallbacks.lookup(*Call) || - C11Callbacks.lookup(*Call)) + C11Callbacks.lookup(*Call) || CPlusPlus11Callbacks.lookup(*Call)) return State; if (Call->isInSystemHeader()) @@ -740,3 +832,4 @@ REGISTER_CHECKER(PthreadLockChecker) REGISTER_CHECKER(FuchsiaLockChecker) REGISTER_CHECKER(C11LockChecker) +REGISTER_CHECKER(CPlusPlus11LockChecker) Index: clang/test/Analysis/Checkers/CPlusPlus11LockChecker.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/Checkers/CPlusPlus11LockChecker.cpp @@ -0,0 +1,621 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.CPlusPlus11Lock -verify %s + +namespace std { + +struct mutex { + void lock(); + bool try_lock(); + void unlock(); +}; + +struct timed_mutex { + void lock(); + bool try_lock(); + void unlock(); +}; + +struct recursive_mutex { + void lock(); + bool try_lock(); + void unlock(); +}; + +struct recursive_timed_mutex { + void lock(); + bool try_lock(); + void unlock(); +}; + +struct shared_mutex { + void lock(); + bool try_lock(); + void unlock(); +}; + +struct shared_timed_mutex { + void lock(); + bool try_lock(); + void unlock(); +}; + +template +struct lock_guard { + T &t; + lock_guard(T &m) : t(m) { + t.lock(); + } + ~lock_guard() { + t.unlock(); + } +}; +} // namespace std + +std::mutex m1; +std::mutex m2; + +// mutex ok + +void m_ok1() { + m1.lock(); // no-warning +} + +void m_ok2() { + m1.unlock(); // no-warning +} + +void m_ok3() { + m1.lock(); // no-warning + m1.unlock(); // no-warning +} + +void m_ok4() { + m1.lock(); // no-warning + m1.unlock(); // no-warning + m1.lock(); // no-warning + m1.unlock(); // no-warning +} + +void m_ok5() { + m1.lock(); // no-warning + m1.unlock(); // no-warning + m2.lock(); // no-warning + m2.unlock(); // no-warning +} + +void m_ok6(void) { + m1.lock(); // no-warning + m2.lock(); // no-warning + m2.unlock(); // no-warning + m1.unlock(); // no-warning +} + +void m_ok7(void) { + if (m1.try_lock()) // no-warning + m1.unlock(); // no-warning +} + +void m_ok8(void) { + m1.unlock(); // no-warning + if (m1.try_lock()) // no-warning + m1.unlock(); // no-warning +} + +void m_ok9(void) { + if (!m1.try_lock()) // no-warning + m1.lock(); // no-warning + m1.unlock(); // no-warning +} + +void m_ok10() { + std::lock_guard gl1(m1); // no-warning +} + +// mutex bad + +void m_bad1() { + m1.lock(); // no-warning + m1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void m_bad2() { + m1.lock(); // no-warning + m1.unlock(); // no-warning + m1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void m_bad3() { + m1.lock(); // no-warning + m2.lock(); // no-warning + m1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}} + m2.unlock(); // no-warning +} + +void m_bad5() { + while (true) + m1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void m_bad6() { + while (true) + m1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void m_bad7() { + if (m1.try_lock()) // no-warning + m1.lock(); // expected-warning{{This lock has already been acquired}} +} + +std::timed_mutex tm1; +std::timed_mutex tm2; + +// timed_mutex ok + +void tm_ok1() { + tm1.lock(); // no-warning +} + +void tm_ok2() { + tm1.unlock(); // no-warning +} + +void tm_ok3() { + tm1.lock(); // no-warning + tm1.unlock(); // no-warning +} + +void tm_ok4() { + tm1.lock(); // no-warning + tm1.unlock(); // no-warning + tm1.lock(); // no-warning + tm1.unlock(); // no-warning +} + +void tm_ok5() { + tm1.lock(); // no-warning + tm1.unlock(); // no-warning + tm2.lock(); // no-warning + tm2.unlock(); // no-warning +} + +void tm_ok6(void) { + tm1.lock(); // no-warning + tm2.lock(); // no-warning + tm2.unlock(); // no-warning + tm1.unlock(); // no-warning +} + +void tm_ok7(void) { + if (tm1.try_lock()) // no-warning + tm1.unlock(); // no-warning +} + +void tm_ok8(void) { + tm1.unlock(); // no-warning + if (tm1.try_lock()) // no-warning + tm1.unlock(); // no-warning +} + +void tm_ok9(void) { + if (!tm1.try_lock()) // no-warning + tm1.lock(); // no-warning + tm1.unlock(); // no-warning +} + +void tm_ok10() { + std::lock_guard gl(tm1); // no-warning +} + +// timed_mutex bad + +void tm_bad1() { + tm1.lock(); // no-warning + tm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void tm_bad2() { + tm1.lock(); // no-warning + tm1.unlock(); // no-warning + tm1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void tm_bad3() { + tm1.lock(); // no-warning + tm2.lock(); // no-warning + tm1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}} + tm2.unlock(); // no-warning +} + +void tm_bad5() { + while (true) + tm1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void tm_bad6() { + while (true) + tm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void tm_bad7() { + if (tm1.try_lock()) // no-warning + tm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +std::recursive_mutex rm1; +std::recursive_mutex rm2; + +// recursive_mutex ok + +void rm_ok1() { + rm1.lock(); // no-warning +} + +void rm_ok2() { + rm1.unlock(); // no-warning +} + +void rm_ok3() { + rm1.lock(); // no-warning + rm1.unlock(); // no-warning +} + +void rm_ok4() { + rm1.lock(); // no-warning + rm1.unlock(); // no-warning + rm1.lock(); // no-warning + rm1.unlock(); // no-warning +} + +void rm_ok5() { + rm1.lock(); // no-warning + rm1.unlock(); // no-warning + rm2.lock(); // no-warning + rm2.unlock(); // no-warning +} + +void rm_ok6(void) { + rm1.lock(); // no-warning + rm2.lock(); // no-warning + rm2.unlock(); // no-warning + rm1.unlock(); // no-warning +} + +void rm_ok7(void) { + if (rm1.try_lock()) // no-warning + rm1.unlock(); // no-warning +} + +void rm_ok8(void) { + rm1.unlock(); // no-warning + if (rm1.try_lock()) // no-warning + rm1.unlock(); // no-warning +} + +void rm_ok9(void) { + if (!rm1.try_lock()) // no-warning + rm1.lock(); // no-warning + rm1.unlock(); // no-warning +} + +void rm_ok10() { + std::lock_guard gl(rm1); // no-warning +} + +// recursive_mutex bad + +void rm_bad1() { + rm1.lock(); // no-warning + rm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void rm_bad2() { + rm1.lock(); // no-warning + rm1.unlock(); // no-warning + rm1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void rm_bad3() { + rm1.lock(); // no-warning + rm2.lock(); // no-warning + rm1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}} + rm2.unlock(); // no-warning +} + +void rm_bad5() { + while (true) + rm1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void rm_bad6() { + while (true) + rm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void rm_bad7() { + if (rm1.try_lock()) // no-warning + rm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +std::recursive_timed_mutex rtm1; +std::recursive_timed_mutex rtm2; + +// recursive_timed_mutex ok + +void rtm_ok1() { + rtm1.lock(); // no-warning +} + +void rtm_ok2() { + rtm1.unlock(); // no-warning +} + +void rtm_ok3() { + rtm1.lock(); // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok4() { + rtm1.lock(); // no-warning + rtm1.unlock(); // no-warning + rtm1.lock(); // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok5() { + rtm1.lock(); // no-warning + rtm1.unlock(); // no-warning + rtm2.lock(); // no-warning + rtm2.unlock(); // no-warning +} + +void rtm_ok6(void) { + rtm1.lock(); // no-warning + rtm2.lock(); // no-warning + rtm2.unlock(); // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok7(void) { + if (rtm1.try_lock()) // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok8(void) { + rtm1.unlock(); // no-warning + if (rtm1.try_lock()) // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok9(void) { + if (!rtm1.try_lock()) // no-warning + rtm1.lock(); // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok10() { + std::lock_guard gl(rtm1); // no-warning +} + +// recursive_timed_mutex bad + +void rtm_bad1() { + rtm1.lock(); // no-warning + rtm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void rtm_bad2() { + rtm1.lock(); // no-warning + rtm1.unlock(); // no-warning + rtm1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void rtm_bad3() { + rtm1.lock(); // no-warning + rtm2.lock(); // no-warning + rtm1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}} + rtm2.unlock(); // no-warning +} + +void rtm_bad5() { + while (true) + rtm1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void rtm_bad6() { + while (true) + rtm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void rtm_bad7() { + if (rtm1.try_lock()) // no-warning + rtm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +std::shared_mutex sm1; +std::shared_mutex sm2; + +// shared_mutex ok + +void sm_ok1() { + sm1.lock(); // no-warning +} + +void sm_ok2() { + sm1.unlock(); // no-warning +} + +void sm_ok3() { + sm1.lock(); // no-warning + sm1.unlock(); // no-warning +} + +void sm_ok4() { + sm1.lock(); // no-warning + sm1.unlock(); // no-warning + sm1.lock(); // no-warning + sm1.unlock(); // no-warning +} + +void sm_ok5() { + sm1.lock(); // no-warning + sm1.unlock(); // no-warning + sm2.lock(); // no-warning + sm2.unlock(); // no-warning +} + +void sm_ok6(void) { + sm1.lock(); // no-warning + sm2.lock(); // no-warning + sm2.unlock(); // no-warning + sm1.unlock(); // no-warning +} + +void sm_ok7(void) { + if (sm1.try_lock()) // no-warning + sm1.unlock(); // no-warning +} + +void sm_ok8(void) { + sm1.unlock(); // no-warning + if (sm1.try_lock()) // no-warning + sm1.unlock(); // no-warning +} + +void sm_ok9(void) { + if (!sm1.try_lock()) // no-warning + sm1.lock(); // no-warning + sm1.unlock(); // no-warning +} + +void sm_ok10() { + std::lock_guard gl(sm1); // no-warning +} + +// shared_mutex bad + +void sm_bad1() { + sm1.lock(); // no-warning + sm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void sm_bad2() { + sm1.lock(); // no-warning + sm1.unlock(); // no-warning + sm1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void sm_bad3() { + sm1.lock(); // no-warning + sm2.lock(); // no-warning + sm1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}} + sm2.unlock(); // no-warning +} + +void sm_bad5() { + while (true) + sm1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void sm_bad6() { + while (true) + sm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void sm_bad7() { + if (sm1.try_lock()) // no-warning + sm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +std::shared_timed_mutex stm1; +std::shared_timed_mutex stm2; + +// shared_timed_mutex ok + +void stm_ok1() { + stm1.lock(); // no-warning +} + +void stm_ok2() { + stm1.unlock(); // no-warning +} + +void stm_ok3() { + stm1.lock(); // no-warning + stm1.unlock(); // no-warning +} + +void stm_ok4() { + stm1.lock(); // no-warning + stm1.unlock(); // no-warning + stm1.lock(); // no-warning + stm1.unlock(); // no-warning +} + +void stm_ok5() { + stm1.lock(); // no-warning + stm1.unlock(); // no-warning + stm2.lock(); // no-warning + stm2.unlock(); // no-warning +} + +void stm_ok6(void) { + stm1.lock(); // no-warning + stm2.lock(); // no-warning + stm2.unlock(); // no-warning + stm1.unlock(); // no-warning +} + +void stm_ok7(void) { + if (stm1.try_lock()) // no-warning + stm1.unlock(); // no-warning +} + +void stm_ok8(void) { + stm1.unlock(); // no-warning + if (stm1.try_lock()) // no-warning + stm1.unlock(); // no-warning +} + +void stm_ok9(void) { + if (!stm1.try_lock()) // no-warning + stm1.lock(); // no-warning + stm1.unlock(); // no-warning +} + +void stm_ok10() { + std::lock_guard gl(stm1); // no-warning +} + +// shared_timed_mutex bad + +void stm_bad1() { + stm1.lock(); // no-warning + stm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void stm_bad2() { + stm1.lock(); // no-warning + stm1.unlock(); // no-warning + stm1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void stm_bad3() { + stm1.lock(); // no-warning + stm2.lock(); // no-warning + stm1.unlock(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}} + stm2.unlock(); // no-warning +} + +void stm_bad5() { + while (true) + stm1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void stm_bad6() { + while (true) + stm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void stm_bad7() { + if (stm1.try_lock()) // no-warning + stm1.lock(); // expected-warning{{This lock has already been acquired}} +}