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 @@ -83,6 +83,7 @@ CK_PthreadLockChecker, CK_FuchsiaLockChecker, CK_C11LockChecker, + CK_CPlusPlus11LockChecker, CK_NumCheckKinds }; DefaultBool ChecksEnabled[CK_NumCheckKinds]; @@ -172,6 +173,79 @@ {{"mtx_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock}, }; + CallDescriptionMap CPlusPlus11Callbacks = { + + // Acquire. + {{{"std", "mutex", "lock"}, 0}, + &PthreadLockChecker::AcquireCPlusPlus11Lock}, + {{{"std", "timed_mutex", "lock"}, 0}, + &PthreadLockChecker::AcquireCPlusPlus11Lock}, + {{{"std", "recursive_mutex", "lock"}, 0}, + &PthreadLockChecker::AcquireCPlusPlus11Lock}, + {{{"std", "recursive_timed_mutex", "lock"}, 0}, + &PthreadLockChecker::AcquireCPlusPlus11Lock}, + {{{"std", "shared_mutex", "lock"}, 0}, + &PthreadLockChecker::AcquireCPlusPlus11Lock}, + {{{"std", "shared_mutex", "lock_shared"}, 0}, + &PthreadLockChecker::AcquireCPlusPlus11LockShared}, + {{{"std", "shared_timed_mutex", "lock"}, 0}, + &PthreadLockChecker::AcquireCPlusPlus11Lock}, + {{{"std", "shared_timed_mutex", "lock_shared"}, 0}, + &PthreadLockChecker::AcquireCPlusPlus11LockShared}, + + // Try. + {{{"std", "mutex", "try_lock"}, 0}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "timed_mutex", "try_lock"}, 0}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "timed_mutex", "try_lock_for"}, 1}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "timed_mutex", "try_lock_until"}, 1}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "recursive_mutex", "try_lock"}, 0}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "recursive_timed_mutex", "try_lock"}, 0}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "recursive_timed_mutex", "try_lock_for"}, 1}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "recursive_timed_mutex", "try_lock_until"}, 1}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "shared_mutex", "try_lock"}, 0}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "shared_mutex", "try_lock_shared"}, 0}, + &PthreadLockChecker::TryCPlusPlus11LockShared}, + {{{"std", "shared_timed_mutex", "try_lock"}, 0}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "shared_timed_mutex", "try_lock_for"}, 1}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "shared_timed_mutex", "try_lock_until"}, 1}, + &PthreadLockChecker::TryCPlusPlus11Lock}, + {{{"std", "shared_timed_mutex", "try_lock_shared"}, 0}, + &PthreadLockChecker::TryCPlusPlus11LockShared}, + {{{"std", "shared_timed_mutex", "try_lock_shared_for"}, 1}, + &PthreadLockChecker::TryCPlusPlus11LockShared}, + {{{"std", "shared_timed_mutex", "try_lock_shared_until"}, 1}, + &PthreadLockChecker::TryCPlusPlus11LockShared}, + + // Release. + {{{"std", "mutex", "unlock"}, 0}, + &PthreadLockChecker::ReleaseCPlusPlus11Lock}, + {{{"std", "timed_mutex", "unlock"}, 0}, + &PthreadLockChecker::ReleaseCPlusPlus11Lock}, + {{{"std", "recursive_mutex", "unlock"}, 0}, + &PthreadLockChecker::ReleaseCPlusPlus11Lock}, + {{{"std", "recursive_timed_mutex", "unlock"}, 0}, + &PthreadLockChecker::ReleaseCPlusPlus11Lock}, + {{{"std", "shared_mutex", "unlock"}, 0}, + &PthreadLockChecker::ReleaseCPlusPlus11Lock}, + {{{"std", "shared_mutex", "unlock_shared"}, 0}, + &PthreadLockChecker::ReleaseCPlusPlus11LockShared}, + {{{"std", "shared_timed_mutex", "unlock"}, 0}, + &PthreadLockChecker::ReleaseCPlusPlus11Lock}, + {{{"std", "shared_timed_mutex", "unlock_shared"}, 0}, + &PthreadLockChecker::ReleaseCPlusPlus11LockShared}, + }; + ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const; @@ -193,6 +267,10 @@ CheckerKind CheckKind) const; void AcquireXNULockShared(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const; + void AcquireCPlusPlus11Lock(const CallEvent &Call, CheckerContext &C, + CheckerKind CheckKind) const; + void AcquireCPlusPlus11LockShared(const CallEvent &Call, CheckerContext &C, + CheckerKind CheckKind) const; void TryPthreadLock(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const; void TryXNULock(const CallEvent &Call, CheckerContext &C, @@ -203,6 +281,10 @@ CheckerKind CheckKind) const; void TryC11Lock(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const; + void TryCPlusPlus11Lock(const CallEvent &Call, CheckerContext &C, + CheckerKind CheckKind) const; + void TryCPlusPlus11LockShared(const CallEvent &Call, CheckerContext &C, + CheckerKind CheckKind) const; void AcquireLockAux(const CallEvent &Call, CheckerContext &C, const Expr *MtxExpr, SVal MtxVal, bool IsTryLock, bool IsShared, LockingSemantics Semantics, @@ -213,6 +295,10 @@ CheckerKind CheckKind) const; void ReleaseAnyLockShared(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const; + void ReleaseCPlusPlus11Lock(const CallEvent &Call, CheckerContext &C, + CheckerKind CheckKind) const; + void ReleaseCPlusPlus11LockShared(const CallEvent &Call, CheckerContext &C, + CheckerKind CheckKind) const; void ReleaseLockAux(const CallEvent &Call, CheckerContext &C, const Expr *MtxExpr, SVal MtxVal, bool IsShared, enum LockingSemantics Semantics, @@ -282,7 +368,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)) @@ -291,6 +377,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 @@ -386,6 +474,25 @@ 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, XNUSemantics, + CheckKind); +} + +void PthreadLockChecker::AcquireCPlusPlus11LockShared( + 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, XNUSemantics, + CheckKind); +} + void PthreadLockChecker::TryPthreadLock(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const { @@ -419,6 +526,26 @@ 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, XNUSemantics, + CheckKind); +} + +void PthreadLockChecker::TryCPlusPlus11LockShared(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, XNUSemantics, + CheckKind); +} + void PthreadLockChecker::AcquireLockAux(const CallEvent &Call, CheckerContext &C, const Expr *MtxExpr, SVal MtxVal, bool IsTryLock, @@ -508,6 +635,23 @@ 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, false, XNUSemantics, CheckKind); +} + +void PthreadLockChecker::ReleaseCPlusPlus11LockShared( + 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, true, XNUSemantics, CheckKind); +} + void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call, CheckerContext &C, const Expr *MtxExpr, SVal MtxVal, bool IsShared, @@ -720,10 +864,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()) @@ -769,3 +913,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,1111 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.CPlusPlus11Lock -verify %s + +namespace std { + +namespace chrono { +using duration = int; +using time_point = int; +} // namespace chrono + +struct mutex { + void lock(); + bool try_lock(); + void unlock(); +}; + +struct timed_mutex { + void lock(); + bool try_lock(); + bool try_lock_for(std::chrono::duration); + bool try_lock_until(std::chrono::time_point); + void unlock(); +}; + +struct recursive_mutex { + void lock(); + bool try_lock(); + void unlock(); +}; + +struct recursive_timed_mutex { + void lock(); + bool try_lock(); + bool try_lock_for(std::chrono::duration); + bool try_lock_until(std::chrono::time_point); + void unlock(); +}; + +struct shared_mutex { + void lock(); + bool try_lock(); + void unlock(); + void lock_shared(); + bool try_lock_shared(); + void unlock_shared(); +}; + +struct shared_timed_mutex { + void lock(); + bool try_lock(); + bool try_lock_for(std::chrono::duration); + bool try_lock_until(std::chrono::time_point); + void unlock(); + void lock_shared(); + bool try_lock_shared(); + bool try_lock_shared_for(std::chrono::duration); + bool try_lock_shared_until(std::chrono::time_point); + void unlock_shared(); +}; + +template +struct lock_guard { + T &t; + lock_guard(T &m) : t(m) { + t.lock(); + } + ~lock_guard() { + t.unlock(); + } +}; + +template +struct shared_lock { + T &t; + shared_lock(T &m) : t(m) { + t.lock_shared(); + } + ~shared_lock() { + t.unlock_shared(); + } +}; +} // 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 gl(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(void) { + if (tm1.try_lock_for(std::chrono::duration(1))) // no-warning + tm1.unlock(); // no-warning +} + +void tm_ok11(void) { + tm1.unlock(); // no-warning + if (tm1.try_lock_for(std::chrono::duration(1))) // no-warning + tm1.unlock(); // no-warning +} + +void tm_ok12(void) { + if (!tm1.try_lock_for(std::chrono::duration(1))) // no-warning + tm1.lock(); // no-warning + tm1.unlock(); // no-warning +} + +void tm_ok13(void) { + if (tm1.try_lock_until(std::chrono::time_point(1))) // no-warning + tm1.unlock(); // no-warning +} + +void tm_ok14(void) { + tm1.unlock(); // no-warning + if (tm1.try_lock_until(std::chrono::time_point(1))) // no-warning + tm1.unlock(); // no-warning +} + +void tm_ok15(void) { + if (!tm1.try_lock_until(std::chrono::time_point(1))) // no-warning + tm1.lock(); // no-warning + tm1.unlock(); // no-warning +} + +void tm_ok16() { + 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}} +} + +void tm_bad8() { + if (tm1.try_lock_for(std::chrono::duration(1))) // no-warning + tm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void tm_bad9() { + if (tm1.try_lock_until(std::chrono::time_point(1))) // 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(void) { + if (rtm1.try_lock_for(std::chrono::duration(1))) // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok11(void) { + rtm1.unlock(); // no-warning + if (rtm1.try_lock_for(std::chrono::duration(1))) // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok12(void) { + if (!rtm1.try_lock_for(std::chrono::duration(1))) // no-warning + rtm1.lock(); // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok13(void) { + if (rtm1.try_lock_until(std::chrono::time_point(1))) // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok14(void) { + rtm1.unlock(); // no-warning + if (rtm1.try_lock_until(std::chrono::time_point(1))) // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok15(void) { + if (!rtm1.try_lock_until(std::chrono::time_point(1))) // no-warning + rtm1.lock(); // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok16() { + 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}} +} + +void rtm_bad8() { + if (rtm1.try_lock_for(std::chrono::duration(1))) // no-warning + rtm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void rtm_bad9() { + if (rtm1.try_lock_until(std::chrono::time_point(1))) // 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}} +} + +// shared_mutex ok (shared semantics) + +void sms_ok1() { + sm1.lock_shared(); // no-warning +} + +void sms_ok2() { + sm1.unlock_shared(); // no-warning +} + +void sms_ok3() { + sm1.lock_shared(); // no-warning + sm1.unlock_shared(); // no-warning +} + +void sms_ok4() { + sm1.lock_shared(); // no-warning + sm1.unlock_shared(); // no-warning + sm1.lock_shared(); // no-warning + sm1.unlock_shared(); // no-warning +} + +void sms_ok5() { + sm1.lock_shared(); // no-warning + sm1.unlock_shared(); // no-warning + sm2.lock_shared(); // no-warning + sm2.unlock_shared(); // no-warning +} + +void sms_ok6(void) { + sm1.lock_shared(); // no-warning + sm2.lock_shared(); // no-warning + sm2.unlock_shared(); // no-warning + sm1.unlock_shared(); // no-warning +} + +void sms_ok7(void) { + if (sm1.try_lock_shared()) // no-warning + sm1.unlock_shared(); // no-warning +} + +void sms_ok8(void) { + sm1.unlock_shared(); // no-warning + if (sm1.try_lock_shared()) // no-warning + sm1.unlock_shared(); // no-warning +} + +void sms_ok9(void) { + if (!sm1.try_lock_shared()) // no-warning + sm1.lock_shared(); // no-warning + sm1.unlock_shared(); // no-warning +} + +void sms_ok10() { + std::shared_lock gl(sm1); // no-warning +} + +// shared_mutex bad (shared semantics) + +void sms_bad1() { + sm1.lock_shared(); // no-warning + sm1.lock_shared(); // expected-warning{{This lock has already been acquired}} +} + +void sms_bad2() { + sm1.lock(); // no-warning + sm1.lock_shared(); // expected-warning{{This lock has already been acquired}} +} + +void sms_bad3() { + sm1.lock_shared(); // no-warning + sm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void sms_bad4() { + sm1.lock_shared(); // no-warning + sm1.unlock_shared(); // no-warning + sm1.unlock_shared(); // expected-warning {{This lock has already been unlocked}} +} + +void sms_bad5() { + sm1.unlock_shared(); // no-warning + sm1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void sms_bad6() { + sm1.unlock(); // no-warning + sm1.unlock_shared(); // expected-warning {{This lock has already been unlocked}} +} + +void sms_bad7() { + sm1.lock_shared(); // no-warning + sm2.lock_shared(); // no-warning + sm1.unlock_shared(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}} + sm2.unlock_shared(); // no-warning +} + +void sms_bad8() { + while (true) + sm1.unlock_shared(); // expected-warning {{This lock has already been unlocked}} +} + +void sms_bad9() { + while (true) + sm1.lock_shared(); // expected-warning{{This lock has already been acquired}} +} + +void sms_bad10() { + if (sm1.try_lock_shared()) // no-warning + sm1.lock_shared(); // expected-warning{{This lock has already been acquired}} +} + +void sms_bad11() { + sm1.lock_shared(); // no-warning + sm1.unlock(); // expected-warning{{This lock has been acquired as shared, but exclusive unlock used}} +} + +void sms_bad12() { + sm1.lock(); // no-warning + sm1.unlock_shared(); // expected-warning{{This lock has been acquired exclusively, but shared unlock used}} +} + +void sms_bad13() { + if (sm1.try_lock_shared()) // no-warning + sm1.unlock(); // expected-warning{{This lock has been acquired as shared, but exclusive unlock used}} +} + +void sms_bad14() { + if (sm1.try_lock()) // no-warning + sm1.unlock_shared(); // expected-warning{{This lock has been acquired exclusively, but shared unlock used}} +} + +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(void) { + if (stm1.try_lock_for(std::chrono::duration(1))) // no-warning + stm1.unlock(); // no-warning +} + +void stm_ok11(void) { + stm1.unlock(); // no-warning + if (stm1.try_lock_for(std::chrono::duration(1))) // no-warning + stm1.unlock(); // no-warning +} + +void stm_ok12(void) { + if (!stm1.try_lock_for(std::chrono::duration(1))) // no-warning + stm1.lock(); // no-warning + stm1.unlock(); // no-warning +} + +void stm_ok13(void) { + if (stm1.try_lock_until(std::chrono::time_point(1))) // no-warning + stm1.unlock(); // no-warning +} + +void stm_ok14(void) { + stm1.unlock(); // no-warning + if (stm1.try_lock_until(std::chrono::time_point(1))) // no-warning + stm1.unlock(); // no-warning +} + +void stm_ok15(void) { + if (!stm1.try_lock_until(std::chrono::time_point(1))) // no-warning + stm1.lock(); // no-warning + stm1.unlock(); // no-warning +} + +void stm_ok16() { + 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}} +} + +void stm_bad8() { + if (stm1.try_lock_for(std::chrono::duration(1))) // no-warning + stm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void stm_bad9() { + if (stm1.try_lock_until(std::chrono::time_point(1))) // no-warning + stm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +// shared_timed_mutex ok (shared semantics) + +void stms_ok1() { + stm1.lock_shared(); // no-warning +} + +void stms_ok2() { + stm1.unlock_shared(); // no-warning +} + +void stms_ok3() { + stm1.lock_shared(); // no-warning + stm1.unlock_shared(); // no-warning +} + +void stms_ok4() { + stm1.lock_shared(); // no-warning + stm1.unlock_shared(); // no-warning + stm1.lock_shared(); // no-warning + stm1.unlock_shared(); // no-warning +} + +void stms_ok5() { + stm1.lock_shared(); // no-warning + stm1.unlock_shared(); // no-warning + stm2.lock_shared(); // no-warning + stm2.unlock_shared(); // no-warning +} + +void stms_ok6(void) { + stm1.lock_shared(); // no-warning + stm2.lock_shared(); // no-warning + stm2.unlock_shared(); // no-warning + stm1.unlock_shared(); // no-warning +} + +void stms_ok7(void) { + if (stm1.try_lock_shared()) // no-warning + stm1.unlock_shared(); // no-warning +} + +void stms_ok8(void) { + stm1.unlock_shared(); // no-warning + if (stm1.try_lock_shared()) // no-warning + stm1.unlock_shared(); // no-warning +} + +void stms_ok9(void) { + if (!stm1.try_lock_shared()) // no-warning + stm1.lock_shared(); // no-warning + stm1.unlock_shared(); // no-warning +} + +void stms_ok10(void) { + if (stm1.try_lock_shared_for(std::chrono::duration(1))) // no-warning + stm1.unlock_shared(); // no-warning +} + +void stms_ok11(void) { + stm1.unlock_shared(); // no-warning + if (stm1.try_lock_shared_for(std::chrono::duration(1))) // no-warning + stm1.unlock_shared(); // no-warning +} + +void stms_ok12(void) { + if (!stm1.try_lock_shared_for(std::chrono::duration(1))) // no-warning + stm1.lock_shared(); // no-warning + stm1.unlock_shared(); // no-warning +} + +void stms_ok13(void) { + if (stm1.try_lock_shared_until(std::chrono::time_point(1))) // no-warning + stm1.unlock_shared(); // no-warning +} + +void stms_ok14(void) { + stm1.unlock_shared(); // no-warning + if (stm1.try_lock_shared_until(std::chrono::time_point(1))) // no-warning + stm1.unlock_shared(); // no-warning +} + +void stms_ok15(void) { + if (!stm1.try_lock_shared_until(std::chrono::time_point(1))) // no-warning + stm1.lock_shared(); // no-warning + stm1.unlock_shared(); // no-warning +} + +void stms_ok16() { + std::shared_lock gl(stm1); // no-warning +} + +// shared_timed_mutex bad (shared semantics) + +void stms_bad1() { + stm1.lock_shared(); // no-warning + stm1.lock_shared(); // expected-warning{{This lock has already been acquired}} +} + +void stms_bad2() { + stm1.lock(); // no-warning + stm1.lock_shared(); // expected-warning{{This lock has already been acquired}} +} + +void stms_bad3() { + stm1.lock_shared(); // no-warning + stm1.lock(); // expected-warning{{This lock has already been acquired}} +} + +void stms_bad4() { + stm1.lock_shared(); // no-warning + stm1.unlock_shared(); // no-warning + stm1.unlock_shared(); // expected-warning {{This lock has already been unlocked}} +} + +void stms_bad5() { + stm1.unlock_shared(); // no-warning + stm1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void stms_bad6() { + stm1.unlock(); // no-warning + stm1.unlock_shared(); // expected-warning {{This lock has already been unlocked}} +} + +void stms_bad7() { + stm1.lock_shared(); // no-warning + stm2.lock_shared(); // no-warning + stm1.unlock_shared(); // expected-warning {{This was not the most recently acquired lock. Possible lock order reversal}} + stm2.unlock_shared(); // no-warning +} + +void stms_bad8() { + while (true) + stm1.unlock_shared(); // expected-warning {{This lock has already been unlocked}} +} + +void stms_bad9() { + while (true) + stm1.lock_shared(); // expected-warning{{This lock has already been acquired}} +} + +void stms_bad10() { + if (stm1.try_lock_shared()) // no-warning + stm1.lock_shared(); // expected-warning{{This lock has already been acquired}} +} + +void stms_bad11() { + if (stm1.try_lock_shared_for(std::chrono::duration(1))) // no-warning + stm1.lock_shared(); // expected-warning{{This lock has already been acquired}} +} + +void stms_bad12() { + if (stm1.try_lock_shared_until(std::chrono::time_point(1))) // no-warning + stm1.lock_shared(); // expected-warning{{This lock has already been acquired}} +} + +void stms_bad13() { + stm1.lock_shared(); // no-warning + stm1.unlock(); // expected-warning{{This lock has been acquired as shared, but exclusive unlock used}} +} + +void stms_bad14() { + stm1.lock(); // no-warning + stm1.unlock_shared(); // expected-warning{{This lock has been acquired exclusively, but shared unlock used}} +} + +void stms_bad15() { + if (stm1.try_lock_shared()) // no-warning + stm1.unlock(); // expected-warning{{This lock has been acquired as shared, but exclusive unlock used}} +} + +void stms_bad16() { + if (stm1.try_lock_shared_for(std::chrono::duration(1))) // no-warning + stm1.unlock(); // expected-warning{{This lock has been acquired as shared, but exclusive unlock used}} +} + +void stms_bad17() { + if (stm1.try_lock_shared_until(std::chrono::time_point(1))) // no-warning + stm1.unlock(); // expected-warning{{This lock has been acquired as shared, but exclusive unlock used}} +} + +void stms_bad18() { + if (stm1.try_lock()) // no-warning + stm1.unlock_shared(); // expected-warning{{This lock has been acquired exclusively, but shared unlock used}} +} + +void stms_bad19() { + if (stm1.try_lock_for(std::chrono::duration(1))) // no-warning + stm1.unlock_shared(); // expected-warning{{This lock has been acquired exclusively, but shared unlock used}} +} + +void stms_bad20() { + if (stm1.try_lock_until(std::chrono::time_point(1))) // no-warning + stm1.unlock_shared(); // expected-warning{{This lock has been acquired exclusively, but shared unlock used}} +}