ASDenysPetrov updated this revision to Diff 308781.
ASDenysPetrov added a comment.

@NoQ , considered your suggestions about the case of recursive mutexes. 
Implemented this feature. Now this works like below:

  recursive_mutex rm;
  rm.lock();
  rm.lock(); // no-warning
  recursive_timed_mutex rtm;
  rtm.lock();
  rtm.lock(); // no-warning

Please look.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D85984/new/

https://reviews.llvm.org/D85984

Files:
  clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
  clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
  clang/test/Analysis/Checkers/CPlusPlus11LockChecker.cpp

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 <typename T>
+struct lock_guard {
+  T &t;
+  lock_guard(T &m) : t(m) {
+    t.lock();
+  }
+  ~lock_guard() {
+    t.unlock();
+  }
+};
+
+template <typename T>
+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<std::mutex> 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<std::timed_mutex> 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<std::recursive_mutex> 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<std::recursive_timed_mutex> 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<std::shared_mutex> 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<std::shared_mutex> 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<std::shared_timed_mutex> 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<std::shared_timed_mutex> 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}}
+}
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<FnCheck> 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<BugType> 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<CXXMemberCall>(&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<CXXMemberCall>(&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<CXXMemberCall>(&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<CXXMemberCall>(&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<CXXMemberCall>(&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<CXXMemberCall>(&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<LockMap>(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<DefinedSVal>()) {
@@ -489,7 +644,8 @@
 
   // Record that the lock was acquired.
   lockSucc = lockSucc->add<LockSet>(lockR);
-  auto State = IsShared ? LockState::getSharedLocked() : LockState::getLocked();
+  auto State = hasModeFlag(Flags, MF_Shared) ? LockState::getSharedLocked()
+                                             : LockState::getLocked();
   lockSucc = lockSucc->set<LockMap>(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<CXXMemberCall>(&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<CXXMemberCall>(&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<LockMap>(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/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<HasAlphaDocumentation>;
 
+def CPlusPlus11LockChecker : Checker<"CPlusPlus11Lock">,
+  HelpText<"Simple C++11 lock -> unlock checker">,
+  Dependencies<[PthreadLockBase]>,
+  Documentation<HasAlphaDocumentation>;
+
 } // end: "alpha.cplusplus"
 
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D85984: [analyzer] Ad... Denys Petrov via Phabricator via cfe-commits

Reply via email to