Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -751,6 +751,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,8 +83,16 @@ CK_PthreadLockChecker, CK_FuchsiaLockChecker, CK_C11LockChecker, + CK_CPlusPlus11LockChecker, CK_NumCheckKinds }; + using ModeFlags = int8_t; + enum : ModeFlags { + MF_None = 0b0000, + MF_Try = 0b0001, + MF_Shared = 0b0010, + MF_Recursive = 0b0100, + }; DefaultBool ChecksEnabled[CK_NumCheckKinds]; CheckerNameRef CheckNames[CK_NumCheckKinds]; @@ -172,12 +180,88 @@ {{"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::AcquireCPlusPlus11LockRecursive}, + {{{"std", "recursive_timed_mutex", "lock"}, 0}, + &PthreadLockChecker::AcquireCPlusPlus11LockRecursive}, + {{{"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::TryCPlusPlus11LockRecursive}, + {{{"std", "recursive_timed_mutex", "try_lock"}, 0}, + &PthreadLockChecker::TryCPlusPlus11LockRecursive}, + {{{"std", "recursive_timed_mutex", "try_lock_for"}, 1}, + &PthreadLockChecker::TryCPlusPlus11LockRecursive}, + {{{"std", "recursive_timed_mutex", "try_lock_until"}, 1}, + &PthreadLockChecker::TryCPlusPlus11LockRecursive}, + {{{"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; void reportBug(CheckerContext &C, std::unique_ptr BT[], const Expr *MtxExpr, CheckerKind CheckKind, StringRef Desc) const; + bool hasModeFlag(const ModeFlags flags, const ModeFlags flag) const { + return (flags & flag) == flag; + } // Init. void InitAnyLock(const CallEvent &Call, CheckerContext &C, @@ -193,6 +277,12 @@ CheckerKind CheckKind) const; void AcquireXNULockShared(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const; + void AcquireCPlusPlus11Lock(const CallEvent &Call, CheckerContext &C, + CheckerKind CheckKind) const; + void AcquireCPlusPlus11LockRecursive(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,18 +293,27 @@ CheckerKind CheckKind) const; void TryC11Lock(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const; + void TryCPlusPlus11Lock(const CallEvent &Call, CheckerContext &C, + CheckerKind CheckKind) const; + void TryCPlusPlus11LockRecursive(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, - CheckerKind CheckKind) const; + const Expr *MtxExpr, SVal MtxVal, ModeFlags Flags, + LockingSemantics Semantics, CheckerKind CheckKind) const; // Release. void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C, 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, + const Expr *MtxExpr, SVal MtxVal, ModeFlags Flags, enum LockingSemantics Semantics, CheckerKind CheckKind) const; @@ -282,7 +381,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 +390,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 @@ -368,61 +469,115 @@ void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const { - AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false, false, + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_None, PthreadSemantics, CheckKind); } void PthreadLockChecker::AcquireXNULock(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const { - AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false, false, + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_None, XNUSemantics, CheckKind); } void PthreadLockChecker::AcquireXNULockShared(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const { - AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false, true, + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_Shared, 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, MF_None, XNUSemantics, CheckKind); +} + +void PthreadLockChecker::AcquireCPlusPlus11LockRecursive( + 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, MF_Recursive, 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, MF_Shared, XNUSemantics, + CheckKind); +} + void PthreadLockChecker::TryPthreadLock(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const { - AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, false, + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_Try, PthreadSemantics, CheckKind); } void PthreadLockChecker::TryXNULock(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const { - AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, false, + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_Try, XNUSemantics, CheckKind); } void PthreadLockChecker::TryXNULockShared(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const { - AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, true, - XNUSemantics, CheckKind); + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), + MF_Try | MF_Shared, XNUSemantics, CheckKind); } void PthreadLockChecker::TryFuchsiaLock(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const { - AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, false, + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_Try, PthreadSemantics, CheckKind); } void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const { - AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, false, + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_Try, 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, MF_Try, XNUSemantics, CheckKind); +} + +void PthreadLockChecker::TryCPlusPlus11LockRecursive( + 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, MF_Try | MF_Recursive, + 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, MF_Try | MF_Shared, XNUSemantics, + CheckKind); +} + void PthreadLockChecker::AcquireLockAux(const CallEvent &Call, CheckerContext &C, const Expr *MtxExpr, - SVal MtxVal, bool IsTryLock, - bool IsShared, + SVal MtxVal, ModeFlags Flags, enum LockingSemantics Semantics, CheckerKind CheckKind) const { if (!ChecksEnabled[CheckKind]) @@ -438,7 +593,7 @@ state = resolvePossiblyDestroyedMutex(state, lockR, sym); if (const LockState *LState = state->get(lockR)) { - if (LState->isAnyLocked()) { + if (LState->isAnyLocked() && !hasModeFlag(Flags, MF_Recursive)) { reportBug(C, BT_doublelock, MtxExpr, CheckKind, "This lock has already been acquired"); return; @@ -450,7 +605,7 @@ } ProgramStateRef lockSucc = state; - if (IsTryLock) { + if (hasModeFlag(Flags, MF_Try)) { // Bifurcate the state, and allow a mode where the lock acquisition fails. SVal RetVal = Call.getReturnValue(); if (auto DefinedRetVal = RetVal.getAs()) { @@ -489,7 +644,8 @@ // Record that the lock was acquired. lockSucc = lockSucc->add(lockR); - auto State = IsShared ? LockState::getSharedLocked() : LockState::getLocked(); + auto State = hasModeFlag(Flags, MF_Shared) ? LockState::getSharedLocked() + : LockState::getLocked(); lockSucc = lockSucc->set(lockR, State); C.addTransition(lockSucc); } @@ -497,20 +653,38 @@ void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const { - ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false, + ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_None, NotApplicable, CheckKind); } void PthreadLockChecker::ReleaseAnyLockShared(const CallEvent &Call, CheckerContext &C, CheckerKind CheckKind) const { - ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, + ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), MF_Shared, 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, MF_None, 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, MF_Shared, XNUSemantics, + CheckKind); +} + void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call, CheckerContext &C, const Expr *MtxExpr, - SVal MtxVal, bool IsShared, + SVal MtxVal, ModeFlags Flags, enum LockingSemantics Semantics, CheckerKind CheckKind) const { if (!ChecksEnabled[CheckKind]) @@ -525,29 +699,30 @@ if (sym) state = resolvePossiblyDestroyedMutex(state, lockR, sym); + const bool IsShared = hasModeFlag(Flags, MF_Shared); + if (const LockState *LState = state->get(lockR)) { if (LState->isAnyUnlocked()) { reportBug(C, BT_doubleunlock, MtxExpr, CheckKind, "This lock has already been unlocked"); return; - } else if (LState->isDestroyed()) { + } + if (LState->isDestroyed()) { reportBug(C, BT_destroylock, MtxExpr, CheckKind, "This lock has already been destroyed"); return; - } else if (LState->isLocked()) { - if (IsShared) { - reportBug( - C, BT_shared, MtxExpr, CheckKind, - "This lock has been acquired exclusively, but shared unlock used"); - return; - } - } else if (LState->isSharedLocked()) { - if (!IsShared) { - reportBug( - C, BT_shared, MtxExpr, CheckKind, - "This lock has been acquired as shared, but exclusive unlock used"); - return; - } + } + if (LState->isLocked() && IsShared) { + reportBug(C, BT_shared, MtxExpr, CheckKind, + "This lock has been acquired exclusively, but shared " + "unlock used"); + return; + } + if (LState->isSharedLocked() && !IsShared) { + reportBug(C, BT_shared, MtxExpr, CheckKind, + "This lock has been acquired exclusively, but shared " + "unlock used"); + return; } } @@ -720,10 +895,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 +944,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.lock(); // no-warning +} + +void rm_ok5() { + rm1.lock(); // no-warning + rm1.unlock(); // no-warning + rm1.lock(); // no-warning + rm1.unlock(); // no-warning +} + +void rm_ok6() { + rm1.lock(); // no-warning + rm1.unlock(); // no-warning + rm2.lock(); // no-warning + rm2.unlock(); // no-warning +} + +void rm_ok7(void) { + rm1.lock(); // no-warning + rm2.lock(); // no-warning + rm2.unlock(); // no-warning + rm1.unlock(); // no-warning +} + +void rm_ok8(void) { + if (rm1.try_lock()) // no-warning + rm1.unlock(); // no-warning +} + +void rm_ok9(void) { + rm1.unlock(); // no-warning + if (rm1.try_lock()) // no-warning + rm1.unlock(); // no-warning +} + +void rm_ok10(void) { + if (!rm1.try_lock()) // no-warning + rm1.lock(); // no-warning + rm1.unlock(); // no-warning +} + +void rm_ok11() { + std::lock_guard gl(rm1); // no-warning +} + +void rm_ok12() { + while (true) + rm1.lock(); // no-warning +} + +void rm_ok13() { + if (rm1.try_lock()) // no-warning + 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}} +} + +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.lock(); // no-warning +} + +void rtm_ok5() { + rtm1.lock(); // no-warning + rtm1.unlock(); // no-warning + rtm1.lock(); // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok6() { + rtm1.lock(); // no-warning + rtm1.unlock(); // no-warning + rtm2.lock(); // no-warning + rtm2.unlock(); // no-warning +} + +void rtm_ok7(void) { + rtm1.lock(); // no-warning + rtm2.lock(); // no-warning + rtm2.unlock(); // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok8(void) { + if (rtm1.try_lock()) // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok9(void) { + rtm1.unlock(); // no-warning + if (rtm1.try_lock()) // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok10(void) { + if (!rtm1.try_lock()) // no-warning + rtm1.lock(); // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok11(void) { + if (rtm1.try_lock_for(std::chrono::duration(1))) // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok12(void) { + rtm1.unlock(); // no-warning + if (rtm1.try_lock_for(std::chrono::duration(1))) // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok13(void) { + if (!rtm1.try_lock_for(std::chrono::duration(1))) // no-warning + rtm1.lock(); // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok14(void) { + if (rtm1.try_lock_until(std::chrono::time_point(1))) // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok15(void) { + rtm1.unlock(); // no-warning + if (rtm1.try_lock_until(std::chrono::time_point(1))) // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok16(void) { + if (!rtm1.try_lock_until(std::chrono::time_point(1))) // no-warning + rtm1.lock(); // no-warning + rtm1.unlock(); // no-warning +} + +void rtm_ok17() { + std::lock_guard gl(rtm1); // no-warning +} + +void rtm_ok18() { + while (true) + rtm1.lock(); // no-warning +} + +void rtm_ok19() { + if (rtm1.try_lock()) // no-warning + rtm1.lock(); // no-warning +} + +void rtm_ok20() { + if (rtm1.try_lock_for(std::chrono::duration(1))) // no-warning + rtm1.lock(); // no-warning +} + +void rtm_ok21() { + if (rtm1.try_lock_until(std::chrono::time_point(1))) // no-warning + rtm1.lock(); // no-warning +} + +// recursive_timed_mutex bad + +void rtm_bad1() { + rtm1.lock(); // no-warning + rtm1.unlock(); // no-warning + rtm1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +void rtm_bad2() { + 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_bad3() { + while (true) + rtm1.unlock(); // expected-warning {{This lock has already been unlocked}} +} + +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}} +}