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,82 @@ {{"mtx_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock}, }; + CallDescriptionMap CPlusPlus11Callbacks = { + + // Acquire. + {{{"std", "mutex", "lock"}, 0, 0}, + &PthreadLockChecker::AcquireCPlusPlus11Lock}, + {{{"std", "recursive_mutex", "lock"}, 0, 0}, + &PthreadLockChecker::AcquireCPlusPlus11RecursiveLock}, + + // Try. + {{{"std", "mutex", "try_lock"}, 0, 0}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "recursive_mutex", "try_lock"}, 0, 0}, + &PthreadLockChecker::TryCPlusPlus11RecursiveLock}, + + // Release. + {{{"std", "mutex", "unlock"}, 0, 0}, + &PthreadLockChecker::ReleaseCPlusPlus11Lock}, + {{{"std", "recursive_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 AcquireCPlusPlus11RecursiveLock(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 TryCPlusPlus11RecursiveLock(const CallEvent &Call, CheckerContext &C, + CheckerKind CheckKind) const; + void AcquireLockAux(const CallEvent &Call, CheckerContext &C, + const Expr *MtxExpr, SVal MtxVal, bool IsTryLock, + bool IsRecursive, 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 +265,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 +299,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 +308,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 +382,92 @@ 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, 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, 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, false, CPlusPlusSemantics, + CheckKind); +} + +void PthreadLockChecker::AcquireCPlusPlus11RecursiveLock( + 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, true, 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, false, + 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, false, + 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, false, + 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, false, + 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, false, CPlusPlusSemantics, + CheckKind); +} + +void PthreadLockChecker::TryCPlusPlus11RecursiveLock( + 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, 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, + bool IsRecursive, + 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,26 @@ if (const LockState *LState = state->get(lockR)) { if (LState->isLocked()) { + // Ignore twice recursive lock + if (IsRecursive) + return; 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 +506,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 +530,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 +544,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 +579,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 +600,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 +620,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 +686,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 +732,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 +785,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 +834,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,207 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.CPlusPlus11Lock -verify %s + +namespace std { + +struct mutex { + void lock(); + bool try_lock(); + void unlock(); +}; + +struct recursive_mutex { + void lock(); + bool try_lock(); + void unlock(); +}; + +template +struct guard_lock { + T &t; + guard_lock(T &m) : t(m) { + t.lock(); + } + ~guard_lock() { + 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::guard_lock 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}} +} + +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::guard_lock gl2(rm1); // no-warning +} + +void rm_ok11() { + rm1.lock(); // no-warning + rm1.lock(); // no-warning +} + +void rm_ok12() { + while (true) + rm1.lock(); // no-warning +} + +// recursive_mutex bad + +void rm_bad1() { + rm1.lock(); // no-warning + rm1.unlock(); // no-warning + rm1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void rm_bad2() { + 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_bad3() { + while (true) + rm1.unlock(); // expected-warning {{This lock has already been unlocked}} +}