summaryrefslogtreecommitdiff
path: root/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/test/SemaCXX/warn-thread-safety-analysis.cpp')
-rw-r--r--clang/test/SemaCXX/warn-thread-safety-analysis.cpp2243
1 files changed, 2243 insertions, 0 deletions
diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
new file mode 100644
index 0000000..566e5c1
--- /dev/null
+++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -0,0 +1,2243 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety %s
+
+#define LOCKABLE __attribute__ ((lockable))
+#define SCOPED_LOCKABLE __attribute__ ((scoped_lockable))
+#define GUARDED_BY(x) __attribute__ ((guarded_by(x)))
+#define GUARDED_VAR __attribute__ ((guarded_var))
+#define PT_GUARDED_BY(x) __attribute__ ((pt_guarded_by(x)))
+#define PT_GUARDED_VAR __attribute__ ((pt_guarded_var))
+#define ACQUIRED_AFTER(...) __attribute__ ((acquired_after(__VA_ARGS__)))
+#define ACQUIRED_BEFORE(...) __attribute__ ((acquired_before(__VA_ARGS__)))
+#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__ ((exclusive_lock_function(__VA_ARGS__)))
+#define SHARED_LOCK_FUNCTION(...) __attribute__ ((shared_lock_function(__VA_ARGS__)))
+#define EXCLUSIVE_TRYLOCK_FUNCTION(...) __attribute__ ((exclusive_trylock_function(__VA_ARGS__)))
+#define SHARED_TRYLOCK_FUNCTION(...) __attribute__ ((shared_trylock_function(__VA_ARGS__)))
+#define UNLOCK_FUNCTION(...) __attribute__ ((unlock_function(__VA_ARGS__)))
+#define LOCK_RETURNED(x) __attribute__ ((lock_returned(x)))
+#define LOCKS_EXCLUDED(...) __attribute__ ((locks_excluded(__VA_ARGS__)))
+#define EXCLUSIVE_LOCKS_REQUIRED(...) \
+ __attribute__ ((exclusive_locks_required(__VA_ARGS__)))
+#define SHARED_LOCKS_REQUIRED(...) \
+ __attribute__ ((shared_locks_required(__VA_ARGS__)))
+#define NO_THREAD_SAFETY_ANALYSIS __attribute__ ((no_thread_safety_analysis))
+
+//-----------------------------------------//
+// Helper fields
+//-----------------------------------------//
+
+
+class __attribute__((lockable)) Mutex {
+ public:
+ void Lock() __attribute__((exclusive_lock_function));
+ void ReaderLock() __attribute__((shared_lock_function));
+ void Unlock() __attribute__((unlock_function));
+ bool TryLock() __attribute__((exclusive_trylock_function(true)));
+ bool ReaderTryLock() __attribute__((shared_trylock_function(true)));
+ void LockWhen(const int &cond) __attribute__((exclusive_lock_function));
+};
+
+class __attribute__((scoped_lockable)) MutexLock {
+ public:
+ MutexLock(Mutex *mu) __attribute__((exclusive_lock_function(mu)));
+ ~MutexLock() __attribute__((unlock_function));
+};
+
+class __attribute__((scoped_lockable)) ReaderMutexLock {
+ public:
+ ReaderMutexLock(Mutex *mu) __attribute__((exclusive_lock_function(mu)));
+ ~ReaderMutexLock() __attribute__((unlock_function));
+};
+
+
+Mutex sls_mu;
+
+Mutex sls_mu2 __attribute__((acquired_after(sls_mu)));
+int sls_guard_var __attribute__((guarded_var)) = 0;
+int sls_guardby_var __attribute__((guarded_by(sls_mu))) = 0;
+
+bool getBool();
+
+class MutexWrapper {
+public:
+ Mutex mu;
+ int x __attribute__((guarded_by(mu)));
+ void MyLock() __attribute__((exclusive_lock_function(mu)));
+};
+
+MutexWrapper sls_mw;
+
+void sls_fun_0() {
+ sls_mw.mu.Lock();
+ sls_mw.x = 5;
+ sls_mw.mu.Unlock();
+}
+
+void sls_fun_2() {
+ sls_mu.Lock();
+ int x = sls_guard_var;
+ sls_mu.Unlock();
+}
+
+void sls_fun_3() {
+ sls_mu.Lock();
+ sls_guard_var = 2;
+ sls_mu.Unlock();
+}
+
+void sls_fun_4() {
+ sls_mu2.Lock();
+ sls_guard_var = 2;
+ sls_mu2.Unlock();
+}
+
+void sls_fun_5() {
+ sls_mu.Lock();
+ int x = sls_guardby_var;
+ sls_mu.Unlock();
+}
+
+void sls_fun_6() {
+ sls_mu.Lock();
+ sls_guardby_var = 2;
+ sls_mu.Unlock();
+}
+
+void sls_fun_7() {
+ sls_mu.Lock();
+ sls_mu2.Lock();
+ sls_mu2.Unlock();
+ sls_mu.Unlock();
+}
+
+void sls_fun_8() {
+ sls_mu.Lock();
+ if (getBool())
+ sls_mu.Unlock();
+ else
+ sls_mu.Unlock();
+}
+
+void sls_fun_9() {
+ if (getBool())
+ sls_mu.Lock();
+ else
+ sls_mu.Lock();
+ sls_mu.Unlock();
+}
+
+void sls_fun_good_6() {
+ if (getBool()) {
+ sls_mu.Lock();
+ } else {
+ if (getBool()) {
+ getBool(); // EMPTY
+ } else {
+ getBool(); // EMPTY
+ }
+ sls_mu.Lock();
+ }
+ sls_mu.Unlock();
+}
+
+void sls_fun_good_7() {
+ sls_mu.Lock();
+ while (getBool()) {
+ sls_mu.Unlock();
+ if (getBool()) {
+ if (getBool()) {
+ sls_mu.Lock();
+ continue;
+ }
+ }
+ sls_mu.Lock();
+ }
+ sls_mu.Unlock();
+}
+
+void sls_fun_good_8() {
+ sls_mw.MyLock();
+ sls_mw.mu.Unlock();
+}
+
+void sls_fun_bad_1() {
+ sls_mu.Unlock(); // \
+ // expected-warning{{unlocking 'sls_mu' that was not locked}}
+}
+
+void sls_fun_bad_2() {
+ sls_mu.Lock();
+ sls_mu.Lock(); // \
+ // expected-warning{{locking 'sls_mu' that is already locked}}
+ sls_mu.Unlock();
+}
+
+void sls_fun_bad_3() {
+ sls_mu.Lock(); // expected-note {{mutex acquired here}}
+} // expected-warning{{mutex 'sls_mu' is still locked at the end of function}}
+
+void sls_fun_bad_4() {
+ if (getBool())
+ sls_mu.Lock(); // \
+ expected-warning{{mutex 'sls_mu2' is not locked on every path through here}} \
+ expected-note{{mutex acquired here}}
+
+ else
+ sls_mu2.Lock(); // \
+ expected-note{{mutex acquired here}}
+} // expected-warning{{mutex 'sls_mu' is not locked on every path through here}}
+
+void sls_fun_bad_5() {
+ sls_mu.Lock(); // expected-note {{mutex acquired here}}
+ if (getBool())
+ sls_mu.Unlock();
+} // expected-warning{{mutex 'sls_mu' is not locked on every path through here}}
+
+void sls_fun_bad_6() {
+ if (getBool()) {
+ sls_mu.Lock(); // expected-note {{mutex acquired here}}
+ } else {
+ if (getBool()) {
+ getBool(); // EMPTY
+ } else {
+ getBool(); // EMPTY
+ }
+ }
+ sls_mu.Unlock(); // \
+ expected-warning{{mutex 'sls_mu' is not locked on every path through here}}\
+ expected-warning{{unlocking 'sls_mu' that was not locked}}
+}
+
+void sls_fun_bad_7() {
+ sls_mu.Lock();
+ while (getBool()) {
+ sls_mu.Unlock();
+ if (getBool()) {
+ if (getBool()) {
+ continue; // \
+ expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}}
+ }
+ }
+ sls_mu.Lock(); // expected-note {{mutex acquired here}}
+ }
+ sls_mu.Unlock();
+}
+
+void sls_fun_bad_8() {
+ sls_mu.Lock(); // expected-note{{mutex acquired here}}
+
+ // FIXME: TERRIBLE SOURCE LOCATION!
+ do { // expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}}
+ sls_mu.Unlock();
+ } while (getBool());
+}
+
+void sls_fun_bad_9() {
+ do {
+ sls_mu.Lock(); // \
+ // expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}} \
+ // expected-note{{mutex acquired here}}
+ } while (getBool());
+ sls_mu.Unlock();
+}
+
+void sls_fun_bad_10() {
+ sls_mu.Lock(); // expected-note 2{{mutex acquired here}}
+ while(getBool()) {
+ sls_mu.Unlock(); // expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}}
+ }
+} // expected-warning{{mutex 'sls_mu' is still locked at the end of function}}
+
+void sls_fun_bad_11() {
+ while (getBool()) { // \
+ expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}}
+ sls_mu.Lock(); // expected-note {{mutex acquired here}}
+ }
+ sls_mu.Unlock(); // \
+ // expected-warning{{unlocking 'sls_mu' that was not locked}}
+}
+
+void sls_fun_bad_12() {
+ sls_mu.Lock(); // expected-note {{mutex acquired here}}
+ while (getBool()) {
+ sls_mu.Unlock();
+ if (getBool()) {
+ if (getBool()) {
+ break; // expected-warning{{mutex 'sls_mu' is not locked on every path through here}}
+ }
+ }
+ sls_mu.Lock();
+ }
+ sls_mu.Unlock();
+}
+
+//-----------------------------------------//
+// Handling lock expressions in attribute args
+// -------------------------------------------//
+
+Mutex aa_mu;
+
+class GlobalLocker {
+public:
+ void globalLock() __attribute__((exclusive_lock_function(aa_mu)));
+ void globalUnlock() __attribute__((unlock_function(aa_mu)));
+};
+
+GlobalLocker glock;
+
+void aa_fun_1() {
+ glock.globalLock();
+ glock.globalUnlock();
+}
+
+void aa_fun_bad_1() {
+ glock.globalUnlock(); // \
+ // expected-warning{{unlocking 'aa_mu' that was not locked}}
+}
+
+void aa_fun_bad_2() {
+ glock.globalLock();
+ glock.globalLock(); // \
+ // expected-warning{{locking 'aa_mu' that is already locked}}
+ glock.globalUnlock();
+}
+
+void aa_fun_bad_3() {
+ glock.globalLock(); // expected-note{{mutex acquired here}}
+} // expected-warning{{mutex 'aa_mu' is still locked at the end of function}}
+
+//--------------------------------------------------//
+// Regression tests for unusual method names
+//--------------------------------------------------//
+
+Mutex wmu;
+
+// Test diagnostics for other method names.
+class WeirdMethods {
+ // FIXME: can't currently check inside constructors and destructors.
+ WeirdMethods() {
+ wmu.Lock(); // EXPECTED-NOTE {{mutex acquired here}}
+ } // EXPECTED-WARNING {{mutex 'wmu' is still locked at the end of function}}
+ ~WeirdMethods() {
+ wmu.Lock(); // EXPECTED-NOTE {{mutex acquired here}}
+ } // EXPECTED-WARNING {{mutex 'wmu' is still locked at the end of function}}
+ void operator++() {
+ wmu.Lock(); // expected-note {{mutex acquired here}}
+ } // expected-warning {{mutex 'wmu' is still locked at the end of function}}
+ operator int*() {
+ wmu.Lock(); // expected-note {{mutex acquired here}}
+ return 0;
+ } // expected-warning {{mutex 'wmu' is still locked at the end of function}}
+};
+
+//-----------------------------------------------//
+// Errors for guarded by or guarded var variables
+// ----------------------------------------------//
+
+int *pgb_gvar __attribute__((pt_guarded_var));
+int *pgb_var __attribute__((pt_guarded_by(sls_mu)));
+
+class PGBFoo {
+ public:
+ int x;
+ int *pgb_field __attribute__((guarded_by(sls_mu2)))
+ __attribute__((pt_guarded_by(sls_mu)));
+ void testFoo() {
+ pgb_field = &x; // \
+ // expected-warning {{writing variable 'pgb_field' requires locking 'sls_mu2' exclusively}}
+ *pgb_field = x; // expected-warning {{reading variable 'pgb_field' requires locking 'sls_mu2'}} \
+ // expected-warning {{writing the value pointed to by 'pgb_field' requires locking 'sls_mu' exclusively}}
+ x = *pgb_field; // expected-warning {{reading variable 'pgb_field' requires locking 'sls_mu2'}} \
+ // expected-warning {{reading the value pointed to by 'pgb_field' requires locking 'sls_mu'}}
+ (*pgb_field)++; // expected-warning {{reading variable 'pgb_field' requires locking 'sls_mu2'}} \
+ // expected-warning {{writing the value pointed to by 'pgb_field' requires locking 'sls_mu' exclusively}}
+ }
+};
+
+class GBFoo {
+ public:
+ int gb_field __attribute__((guarded_by(sls_mu)));
+
+ void testFoo() {
+ gb_field = 0; // \
+ // expected-warning {{writing variable 'gb_field' requires locking 'sls_mu' exclusively}}
+ }
+
+ void testNoAnal() __attribute__((no_thread_safety_analysis)) {
+ gb_field = 0;
+ }
+};
+
+GBFoo GlobalGBFoo __attribute__((guarded_by(sls_mu)));
+
+void gb_fun_0() {
+ sls_mu.Lock();
+ int x = *pgb_var;
+ sls_mu.Unlock();
+}
+
+void gb_fun_1() {
+ sls_mu.Lock();
+ *pgb_var = 2;
+ sls_mu.Unlock();
+}
+
+void gb_fun_2() {
+ int x;
+ pgb_var = &x;
+}
+
+void gb_fun_3() {
+ int *x = pgb_var;
+}
+
+void gb_bad_0() {
+ sls_guard_var = 1; // \
+ // expected-warning{{writing variable 'sls_guard_var' requires locking any mutex exclusively}}
+}
+
+void gb_bad_1() {
+ int x = sls_guard_var; // \
+ // expected-warning{{reading variable 'sls_guard_var' requires locking any mutex}}
+}
+
+void gb_bad_2() {
+ sls_guardby_var = 1; // \
+ // expected-warning {{writing variable 'sls_guardby_var' requires locking 'sls_mu' exclusively}}
+}
+
+void gb_bad_3() {
+ int x = sls_guardby_var; // \
+ // expected-warning {{reading variable 'sls_guardby_var' requires locking 'sls_mu'}}
+}
+
+void gb_bad_4() {
+ *pgb_gvar = 1; // \
+ // expected-warning {{writing the value pointed to by 'pgb_gvar' requires locking any mutex exclusively}}
+}
+
+void gb_bad_5() {
+ int x = *pgb_gvar; // \
+ // expected-warning {{reading the value pointed to by 'pgb_gvar' requires locking any mutex}}
+}
+
+void gb_bad_6() {
+ *pgb_var = 1; // \
+ // expected-warning {{writing the value pointed to by 'pgb_var' requires locking 'sls_mu' exclusively}}
+}
+
+void gb_bad_7() {
+ int x = *pgb_var; // \
+ // expected-warning {{reading the value pointed to by 'pgb_var' requires locking 'sls_mu'}}
+}
+
+void gb_bad_8() {
+ GBFoo G;
+ G.gb_field = 0; // \
+ // expected-warning {{writing variable 'gb_field' requires locking 'sls_mu'}}
+}
+
+void gb_bad_9() {
+ sls_guard_var++; // \
+ // expected-warning{{writing variable 'sls_guard_var' requires locking any mutex exclusively}}
+ sls_guard_var--; // \
+ // expected-warning{{writing variable 'sls_guard_var' requires locking any mutex exclusively}}
+ ++sls_guard_var; // \
+ // expected-warning{{writing variable 'sls_guard_var' requires locking any mutex exclusively}}
+ --sls_guard_var;// \
+ // expected-warning{{writing variable 'sls_guard_var' requires locking any mutex exclusively}}
+}
+
+//-----------------------------------------------//
+// Warnings on variables with late parsed attributes
+// ----------------------------------------------//
+
+class LateFoo {
+public:
+ int a __attribute__((guarded_by(mu)));
+ int b;
+
+ void foo() __attribute__((exclusive_locks_required(mu))) { }
+
+ void test() {
+ a = 0; // \
+ // expected-warning{{writing variable 'a' requires locking 'mu' exclusively}}
+ b = a; // \
+ // expected-warning {{reading variable 'a' requires locking 'mu'}}
+ c = 0; // \
+ // expected-warning {{writing variable 'c' requires locking 'mu' exclusively}}
+ }
+
+ int c __attribute__((guarded_by(mu)));
+
+ Mutex mu;
+};
+
+class LateBar {
+ public:
+ int a_ __attribute__((guarded_by(mu1_)));
+ int b_;
+ int *q __attribute__((pt_guarded_by(mu)));
+ Mutex mu1_;
+ Mutex mu;
+ LateFoo Foo;
+ LateFoo Foo2;
+ LateFoo *FooPointer;
+};
+
+LateBar b1, *b3;
+
+void late_0() {
+ LateFoo FooA;
+ LateFoo FooB;
+ FooA.mu.Lock();
+ FooA.a = 5;
+ FooA.mu.Unlock();
+}
+
+void late_1() {
+ LateBar BarA;
+ BarA.FooPointer->mu.Lock();
+ BarA.FooPointer->a = 2;
+ BarA.FooPointer->mu.Unlock();
+}
+
+void late_bad_0() {
+ LateFoo fooA;
+ LateFoo fooB;
+ fooA.mu.Lock();
+ fooB.a = 5; // \
+ // expected-warning{{writing variable 'a' requires locking 'mu' exclusively}}
+ fooA.mu.Unlock();
+}
+
+void late_bad_1() {
+ Mutex mu;
+ mu.Lock();
+ b1.mu1_.Lock();
+ int res = b1.a_ + b3->b_;
+ b3->b_ = *b1.q; // \
+ // expected-warning{{reading the value pointed to by 'q' requires locking 'mu'}}
+ b1.mu1_.Unlock();
+ b1.b_ = res;
+ mu.Unlock();
+}
+
+void late_bad_2() {
+ LateBar BarA;
+ BarA.FooPointer->mu.Lock();
+ BarA.Foo.a = 2; // \
+ // expected-warning{{writing variable 'a' requires locking 'mu' exclusively}}
+ BarA.FooPointer->mu.Unlock();
+}
+
+void late_bad_3() {
+ LateBar BarA;
+ BarA.Foo.mu.Lock();
+ BarA.FooPointer->a = 2; // \
+ // expected-warning{{writing variable 'a' requires locking 'mu' exclusively}}
+ BarA.Foo.mu.Unlock();
+}
+
+void late_bad_4() {
+ LateBar BarA;
+ BarA.Foo.mu.Lock();
+ BarA.Foo2.a = 2; // \
+ // expected-warning{{writing variable 'a' requires locking 'mu' exclusively}}
+ BarA.Foo.mu.Unlock();
+}
+
+//-----------------------------------------------//
+// Extra warnings for shared vs. exclusive locks
+// ----------------------------------------------//
+
+void shared_fun_0() {
+ sls_mu.Lock();
+ do {
+ sls_mu.Unlock();
+ sls_mu.Lock();
+ } while (getBool());
+ sls_mu.Unlock();
+}
+
+void shared_fun_1() {
+ sls_mu.ReaderLock(); // \
+ // expected-warning {{mutex 'sls_mu' is locked exclusively and shared in the same scope}}
+ do {
+ sls_mu.Unlock();
+ sls_mu.Lock(); // \
+ // expected-note {{the other lock of mutex 'sls_mu' is here}}
+ } while (getBool());
+ sls_mu.Unlock();
+}
+
+void shared_fun_3() {
+ if (getBool())
+ sls_mu.Lock();
+ else
+ sls_mu.Lock();
+ *pgb_var = 1;
+ sls_mu.Unlock();
+}
+
+void shared_fun_4() {
+ if (getBool())
+ sls_mu.ReaderLock();
+ else
+ sls_mu.ReaderLock();
+ int x = sls_guardby_var;
+ sls_mu.Unlock();
+}
+
+void shared_fun_8() {
+ if (getBool())
+ sls_mu.Lock(); // \
+ // expected-warning {{mutex 'sls_mu' is locked exclusively and shared in the same scope}}
+ else
+ sls_mu.ReaderLock(); // \
+ // expected-note {{the other lock of mutex 'sls_mu' is here}}
+ sls_mu.Unlock();
+}
+
+void shared_bad_0() {
+ sls_mu.Lock(); // \
+ // expected-warning {{mutex 'sls_mu' is locked exclusively and shared in the same scope}}
+ do {
+ sls_mu.Unlock();
+ sls_mu.ReaderLock(); // \
+ // expected-note {{the other lock of mutex 'sls_mu' is here}}
+ } while (getBool());
+ sls_mu.Unlock();
+}
+
+void shared_bad_1() {
+ if (getBool())
+ sls_mu.Lock(); // \
+ // expected-warning {{mutex 'sls_mu' is locked exclusively and shared in the same scope}}
+ else
+ sls_mu.ReaderLock(); // \
+ // expected-note {{the other lock of mutex 'sls_mu' is here}}
+ *pgb_var = 1;
+ sls_mu.Unlock();
+}
+
+void shared_bad_2() {
+ if (getBool())
+ sls_mu.ReaderLock(); // \
+ // expected-warning {{mutex 'sls_mu' is locked exclusively and shared in the same scope}}
+ else
+ sls_mu.Lock(); // \
+ // expected-note {{the other lock of mutex 'sls_mu' is here}}
+ *pgb_var = 1;
+ sls_mu.Unlock();
+}
+
+// FIXME: Add support for functions (not only methods)
+class LRBar {
+ public:
+ void aa_elr_fun() __attribute__((exclusive_locks_required(aa_mu)));
+ void aa_elr_fun_s() __attribute__((shared_locks_required(aa_mu)));
+ void le_fun() __attribute__((locks_excluded(sls_mu)));
+};
+
+class LRFoo {
+ public:
+ void test() __attribute__((exclusive_locks_required(sls_mu)));
+ void testShared() __attribute__((shared_locks_required(sls_mu2)));
+};
+
+void elr_fun() __attribute__((exclusive_locks_required(sls_mu)));
+void elr_fun() {}
+
+LRFoo MyLRFoo;
+LRBar Bar;
+
+void es_fun_0() {
+ aa_mu.Lock();
+ Bar.aa_elr_fun();
+ aa_mu.Unlock();
+}
+
+void es_fun_1() {
+ aa_mu.Lock();
+ Bar.aa_elr_fun_s();
+ aa_mu.Unlock();
+}
+
+void es_fun_2() {
+ aa_mu.ReaderLock();
+ Bar.aa_elr_fun_s();
+ aa_mu.Unlock();
+}
+
+void es_fun_3() {
+ sls_mu.Lock();
+ MyLRFoo.test();
+ sls_mu.Unlock();
+}
+
+void es_fun_4() {
+ sls_mu2.Lock();
+ MyLRFoo.testShared();
+ sls_mu2.Unlock();
+}
+
+void es_fun_5() {
+ sls_mu2.ReaderLock();
+ MyLRFoo.testShared();
+ sls_mu2.Unlock();
+}
+
+void es_fun_6() {
+ Bar.le_fun();
+}
+
+void es_fun_7() {
+ sls_mu.Lock();
+ elr_fun();
+ sls_mu.Unlock();
+}
+
+void es_fun_8() __attribute__((no_thread_safety_analysis));
+
+void es_fun_8() {
+ Bar.aa_elr_fun_s();
+}
+
+void es_fun_9() __attribute__((shared_locks_required(aa_mu)));
+void es_fun_9() {
+ Bar.aa_elr_fun_s();
+}
+
+void es_fun_10() __attribute__((exclusive_locks_required(aa_mu)));
+void es_fun_10() {
+ Bar.aa_elr_fun_s();
+}
+
+void es_bad_0() {
+ Bar.aa_elr_fun(); // \
+ // expected-warning {{calling function 'aa_elr_fun' requires exclusive lock on 'aa_mu'}}
+}
+
+void es_bad_1() {
+ aa_mu.ReaderLock();
+ Bar.aa_elr_fun(); // \
+ // expected-warning {{calling function 'aa_elr_fun' requires exclusive lock on 'aa_mu'}}
+ aa_mu.Unlock();
+}
+
+void es_bad_2() {
+ Bar.aa_elr_fun_s(); // \
+ // expected-warning {{calling function 'aa_elr_fun_s' requires shared lock on 'aa_mu'}}
+}
+
+void es_bad_3() {
+ MyLRFoo.test(); // \
+ // expected-warning {{calling function 'test' requires exclusive lock on 'sls_mu'}}
+}
+
+void es_bad_4() {
+ MyLRFoo.testShared(); // \
+ // expected-warning {{calling function 'testShared' requires shared lock on 'sls_mu2'}}
+}
+
+void es_bad_5() {
+ sls_mu.ReaderLock();
+ MyLRFoo.test(); // \
+ // expected-warning {{calling function 'test' requires exclusive lock on 'sls_mu'}}
+ sls_mu.Unlock();
+}
+
+void es_bad_6() {
+ sls_mu.Lock();
+ Bar.le_fun(); // \
+ // expected-warning {{cannot call function 'le_fun' while mutex 'sls_mu' is locked}}
+ sls_mu.Unlock();
+}
+
+void es_bad_7() {
+ sls_mu.ReaderLock();
+ Bar.le_fun(); // \
+ // expected-warning {{cannot call function 'le_fun' while mutex 'sls_mu' is locked}}
+ sls_mu.Unlock();
+}
+
+
+//-----------------------------------------------//
+// Unparseable lock expressions
+// ----------------------------------------------//
+
+// FIXME -- derive new tests for unhandled expressions
+
+
+//----------------------------------------------------------------------------//
+// The following test cases are ported from the gcc thread safety implementation
+// They are each wrapped inside a namespace with the test number of the gcc test
+//
+// FIXME: add all the gcc tests, once this analysis passes them.
+//----------------------------------------------------------------------------//
+
+//-----------------------------------------//
+// Good testcases (no errors)
+//-----------------------------------------//
+
+namespace thread_annot_lock_20 {
+class Bar {
+ public:
+ static int func1() EXCLUSIVE_LOCKS_REQUIRED(mu1_);
+ static int b_ GUARDED_BY(mu1_);
+ static Mutex mu1_;
+ static int a_ GUARDED_BY(mu1_);
+};
+
+Bar b1;
+
+int Bar::func1()
+{
+ int res = 5;
+
+ if (a_ == 4)
+ res = b_;
+ return res;
+}
+} // end namespace thread_annot_lock_20
+
+namespace thread_annot_lock_22 {
+// Test various usage of GUARDED_BY and PT_GUARDED_BY annotations, especially
+// uses in class definitions.
+Mutex mu;
+
+class Bar {
+ public:
+ int a_ GUARDED_BY(mu1_);
+ int b_;
+ int *q PT_GUARDED_BY(mu);
+ Mutex mu1_ ACQUIRED_AFTER(mu);
+};
+
+Bar b1, *b3;
+int *p GUARDED_BY(mu) PT_GUARDED_BY(mu);
+int res GUARDED_BY(mu) = 5;
+
+int func(int i)
+{
+ int x;
+ mu.Lock();
+ b1.mu1_.Lock();
+ res = b1.a_ + b3->b_;
+ *p = i;
+ b1.a_ = res + b3->b_;
+ b3->b_ = *b1.q;
+ b1.mu1_.Unlock();
+ b1.b_ = res;
+ x = res;
+ mu.Unlock();
+ return x;
+}
+} // end namespace thread_annot_lock_22
+
+namespace thread_annot_lock_27_modified {
+// test lock annotations applied to function definitions
+// Modified: applied annotations only to function declarations
+Mutex mu1;
+Mutex mu2 ACQUIRED_AFTER(mu1);
+
+class Foo {
+ public:
+ int method1(int i) SHARED_LOCKS_REQUIRED(mu2) EXCLUSIVE_LOCKS_REQUIRED(mu1);
+};
+
+int Foo::method1(int i) {
+ return i;
+}
+
+
+int foo(int i) EXCLUSIVE_LOCKS_REQUIRED(mu2) SHARED_LOCKS_REQUIRED(mu1);
+int foo(int i) {
+ return i;
+}
+
+static int bar(int i) EXCLUSIVE_LOCKS_REQUIRED(mu1);
+static int bar(int i) {
+ return i;
+}
+
+void main() {
+ Foo a;
+
+ mu1.Lock();
+ mu2.Lock();
+ a.method1(1);
+ foo(2);
+ mu2.Unlock();
+ bar(3);
+ mu1.Unlock();
+}
+} // end namespace thread_annot_lock_27_modified
+
+
+namespace thread_annot_lock_38 {
+// Test the case where a template member function is annotated with lock
+// attributes in a non-template class.
+class Foo {
+ public:
+ void func1(int y) LOCKS_EXCLUDED(mu_);
+ template <typename T> void func2(T x) LOCKS_EXCLUDED(mu_);
+ private:
+ Mutex mu_;
+};
+
+Foo *foo;
+
+void main()
+{
+ foo->func1(5);
+ foo->func2(5);
+}
+} // end namespace thread_annot_lock_38
+
+namespace thread_annot_lock_43 {
+// Tests lock canonicalization
+class Foo {
+ public:
+ Mutex *mu_;
+};
+
+class FooBar {
+ public:
+ Foo *foo_;
+ int GetA() EXCLUSIVE_LOCKS_REQUIRED(foo_->mu_) { return a_; }
+ int a_ GUARDED_BY(foo_->mu_);
+};
+
+FooBar *fb;
+
+void main()
+{
+ int x;
+ fb->foo_->mu_->Lock();
+ x = fb->GetA();
+ fb->foo_->mu_->Unlock();
+}
+} // end namespace thread_annot_lock_43
+
+namespace thread_annot_lock_49 {
+// Test the support for use of lock expression in the annotations
+class Foo {
+ public:
+ Mutex foo_mu_;
+};
+
+class Bar {
+ private:
+ Foo *foo;
+ Mutex bar_mu_ ACQUIRED_AFTER(foo->foo_mu_);
+
+ public:
+ void Test1() {
+ foo->foo_mu_.Lock();
+ bar_mu_.Lock();
+ bar_mu_.Unlock();
+ foo->foo_mu_.Unlock();
+ }
+};
+
+void main() {
+ Bar bar;
+ bar.Test1();
+}
+} // end namespace thread_annot_lock_49
+
+namespace thread_annot_lock_61_modified {
+ // Modified to fix the compiler errors
+ // Test the fix for a bug introduced by the support of pass-by-reference
+ // paramters.
+ struct Foo { Foo &operator<< (bool) {return *this;} };
+ Foo &getFoo();
+ struct Bar { Foo &func () {return getFoo();} };
+ struct Bas { void operator& (Foo &) {} };
+ void mumble()
+ {
+ Bas() & Bar().func() << "" << "";
+ Bas() & Bar().func() << "";
+ }
+} // end namespace thread_annot_lock_61_modified
+
+
+namespace thread_annot_lock_65 {
+// Test the fix for a bug in the support of allowing reader locks for
+// non-const, non-modifying overload functions. (We didn't handle the builtin
+// properly.)
+enum MyFlags {
+ Zero,
+ One,
+ Two,
+ Three,
+ Four,
+ Five,
+ Six,
+ Seven,
+ Eight,
+ Nine
+};
+
+inline MyFlags
+operator|(MyFlags a, MyFlags b)
+{
+ return MyFlags(static_cast<int>(a) | static_cast<int>(b));
+}
+
+inline MyFlags&
+operator|=(MyFlags& a, MyFlags b)
+{
+ return a = a | b;
+}
+} // end namespace thread_annot_lock_65
+
+namespace thread_annot_lock_66_modified {
+// Modified: Moved annotation to function defn
+// Test annotations on out-of-line definitions of member functions where the
+// annotations refer to locks that are also data members in the class.
+Mutex mu;
+
+class Foo {
+ public:
+ int method1(int i) SHARED_LOCKS_REQUIRED(mu1, mu, mu2);
+ int data GUARDED_BY(mu1);
+ Mutex *mu1;
+ Mutex *mu2;
+};
+
+int Foo::method1(int i)
+{
+ return data + i;
+}
+
+void main()
+{
+ Foo a;
+
+ a.mu2->Lock();
+ a.mu1->Lock();
+ mu.Lock();
+ a.method1(1);
+ mu.Unlock();
+ a.mu1->Unlock();
+ a.mu2->Unlock();
+}
+} // end namespace thread_annot_lock_66_modified
+
+namespace thread_annot_lock_68_modified {
+// Test a fix to a bug in the delayed name binding with nested template
+// instantiation. We use a stack to make sure a name is not resolved to an
+// inner context.
+template <typename T>
+class Bar {
+ Mutex mu_;
+};
+
+template <typename T>
+class Foo {
+ public:
+ void func(T x) {
+ mu_.Lock();
+ count_ = x;
+ mu_.Unlock();
+ }
+
+ private:
+ T count_ GUARDED_BY(mu_);
+ Bar<T> bar_;
+ Mutex mu_;
+};
+
+void main()
+{
+ Foo<int> *foo;
+ foo->func(5);
+}
+} // end namespace thread_annot_lock_68_modified
+
+namespace thread_annot_lock_30_modified {
+// Test delay parsing of lock attribute arguments with nested classes.
+// Modified: trylocks replaced with exclusive_lock_fun
+int a = 0;
+
+class Bar {
+ struct Foo;
+
+ public:
+ void MyLock() EXCLUSIVE_LOCK_FUNCTION(mu);
+
+ int func() {
+ MyLock();
+// if (foo == 0) {
+// return 0;
+// }
+ a = 5;
+ mu.Unlock();
+ return 1;
+ }
+
+ class FooBar {
+ int x;
+ int y;
+ };
+
+ private:
+ Mutex mu;
+};
+
+Bar *bar;
+
+void main()
+{
+ bar->func();
+}
+} // end namespace thread_annot_lock_30_modified
+
+namespace thread_annot_lock_47 {
+// Test the support for annotations on virtual functions.
+// This is a good test case. (i.e. There should be no warning emitted by the
+// compiler.)
+class Base {
+ public:
+ virtual void func1() EXCLUSIVE_LOCKS_REQUIRED(mu_);
+ virtual void func2() LOCKS_EXCLUDED(mu_);
+ Mutex mu_;
+};
+
+class Child : public Base {
+ public:
+ virtual void func1() EXCLUSIVE_LOCKS_REQUIRED(mu_);
+ virtual void func2() LOCKS_EXCLUDED(mu_);
+};
+
+void main() {
+ Child *c;
+ Base *b = c;
+
+ b->mu_.Lock();
+ b->func1();
+ b->mu_.Unlock();
+ b->func2();
+
+ c->mu_.Lock();
+ c->func1();
+ c->mu_.Unlock();
+ c->func2();
+}
+} // end namespace thread_annot_lock_47
+
+//-----------------------------------------//
+// Tests which produce errors
+//-----------------------------------------//
+
+namespace thread_annot_lock_13 {
+Mutex mu1;
+Mutex mu2;
+
+int g GUARDED_BY(mu1);
+int w GUARDED_BY(mu2);
+
+class Foo {
+ public:
+ void bar() LOCKS_EXCLUDED(mu_, mu1);
+ int foo() SHARED_LOCKS_REQUIRED(mu_) EXCLUSIVE_LOCKS_REQUIRED(mu2);
+
+ private:
+ int a_ GUARDED_BY(mu_);
+ public:
+ Mutex mu_ ACQUIRED_AFTER(mu1);
+};
+
+int Foo::foo()
+{
+ int res;
+ w = 5;
+ res = a_ + 5;
+ return res;
+}
+
+void Foo::bar()
+{
+ int x;
+ mu_.Lock();
+ x = foo(); // expected-warning {{calling function 'foo' requires exclusive lock on 'mu2'}}
+ a_ = x + 1;
+ mu_.Unlock();
+ if (x > 5) {
+ mu1.Lock();
+ g = 2;
+ mu1.Unlock();
+ }
+}
+
+void main()
+{
+ Foo f1, *f2;
+ f1.mu_.Lock();
+ f1.bar(); // expected-warning {{cannot call function 'bar' while mutex 'mu_' is locked}}
+ mu2.Lock();
+ f1.foo();
+ mu2.Unlock();
+ f1.mu_.Unlock();
+ f2->mu_.Lock();
+ f2->bar(); // expected-warning {{cannot call function 'bar' while mutex 'mu_' is locked}}
+ f2->mu_.Unlock();
+ mu2.Lock();
+ w = 2;
+ mu2.Unlock();
+}
+} // end namespace thread_annot_lock_13
+
+namespace thread_annot_lock_18_modified {
+// Modified: Trylocks removed
+// Test the ability to distnguish between the same lock field of
+// different objects of a class.
+ class Bar {
+ public:
+ bool MyLock() EXCLUSIVE_LOCK_FUNCTION(mu1_);
+ void MyUnlock() UNLOCK_FUNCTION(mu1_);
+ int a_ GUARDED_BY(mu1_);
+
+ private:
+ Mutex mu1_;
+};
+
+Bar *b1, *b2;
+
+void func()
+{
+ b1->MyLock();
+ b1->a_ = 5;
+ b2->a_ = 3; // expected-warning {{writing variable 'a_' requires locking 'mu1_' exclusively}}
+ b2->MyLock();
+ b2->MyUnlock();
+ b1->MyUnlock();
+}
+} // end namespace thread_annot_lock_18_modified
+
+namespace thread_annot_lock_21 {
+// Test various usage of GUARDED_BY and PT_GUARDED_BY annotations, especially
+// uses in class definitions.
+Mutex mu;
+
+class Bar {
+ public:
+ int a_ GUARDED_BY(mu1_);
+ int b_;
+ int *q PT_GUARDED_BY(mu);
+ Mutex mu1_ ACQUIRED_AFTER(mu);
+};
+
+Bar b1, *b3;
+int *p GUARDED_BY(mu) PT_GUARDED_BY(mu);
+
+int res GUARDED_BY(mu) = 5;
+
+int func(int i)
+{
+ int x;
+ b3->mu1_.Lock();
+ res = b1.a_ + b3->b_; // expected-warning {{reading variable 'a_' requires locking 'mu1_'}} \
+ // expected-warning {{writing variable 'res' requires locking 'mu' exclusively}}
+ *p = i; // expected-warning {{reading variable 'p' requires locking 'mu'}} \
+ // expected-warning {{writing the value pointed to by 'p' requires locking 'mu' exclusively}}
+ b1.a_ = res + b3->b_; // expected-warning {{reading variable 'res' requires locking 'mu'}} \
+ // expected-warning {{writing variable 'a_' requires locking 'mu1_' exclusively}}
+ b3->b_ = *b1.q; // expected-warning {{reading the value pointed to by 'q' requires locking 'mu'}}
+ b3->mu1_.Unlock();
+ b1.b_ = res; // expected-warning {{reading variable 'res' requires locking 'mu'}}
+ x = res; // expected-warning {{reading variable 'res' requires locking 'mu'}}
+ return x;
+}
+} // end namespace thread_annot_lock_21
+
+namespace thread_annot_lock_35_modified {
+// Test the analyzer's ability to distinguish the lock field of different
+// objects.
+class Foo {
+ private:
+ Mutex lock_;
+ int a_ GUARDED_BY(lock_);
+
+ public:
+ void Func(Foo* child) LOCKS_EXCLUDED(lock_) {
+ Foo *new_foo = new Foo;
+
+ lock_.Lock();
+
+ child->Func(new_foo); // There shouldn't be any warning here as the
+ // acquired lock is not in child.
+ child->bar(7); // expected-warning {{calling function 'bar' requires exclusive lock on 'lock_'}}
+ child->a_ = 5; // expected-warning {{writing variable 'a_' requires locking 'lock_' exclusively}}
+ lock_.Unlock();
+ }
+
+ void bar(int y) EXCLUSIVE_LOCKS_REQUIRED(lock_) {
+ a_ = y;
+ }
+};
+
+Foo *x;
+
+void main() {
+ Foo *child = new Foo;
+ x->Func(child);
+}
+} // end namespace thread_annot_lock_35_modified
+
+namespace thread_annot_lock_36_modified {
+// Modified to move the annotations to function defns.
+// Test the analyzer's ability to distinguish the lock field of different
+// objects
+class Foo {
+ private:
+ Mutex lock_;
+ int a_ GUARDED_BY(lock_);
+
+ public:
+ void Func(Foo* child) LOCKS_EXCLUDED(lock_);
+ void bar(int y) EXCLUSIVE_LOCKS_REQUIRED(lock_);
+};
+
+void Foo::Func(Foo* child) {
+ Foo *new_foo = new Foo;
+
+ lock_.Lock();
+
+ child->lock_.Lock();
+ child->Func(new_foo); // expected-warning {{cannot call function 'Func' while mutex 'lock_' is locked}}
+ child->bar(7);
+ child->a_ = 5;
+ child->lock_.Unlock();
+
+ lock_.Unlock();
+}
+
+void Foo::bar(int y) {
+ a_ = y;
+}
+
+
+Foo *x;
+
+void main() {
+ Foo *child = new Foo;
+ x->Func(child);
+}
+} // end namespace thread_annot_lock_36_modified
+
+
+namespace thread_annot_lock_42 {
+// Test support of multiple lock attributes of the same kind on a decl.
+class Foo {
+ private:
+ Mutex mu1, mu2, mu3;
+ int x GUARDED_BY(mu1) GUARDED_BY(mu2);
+ int y GUARDED_BY(mu2);
+
+ void f2() LOCKS_EXCLUDED(mu1) LOCKS_EXCLUDED(mu2) LOCKS_EXCLUDED(mu3) {
+ mu2.Lock();
+ y = 2;
+ mu2.Unlock();
+ }
+
+ public:
+ void f1() EXCLUSIVE_LOCKS_REQUIRED(mu2) EXCLUSIVE_LOCKS_REQUIRED(mu1) {
+ x = 5;
+ f2(); // expected-warning {{cannot call function 'f2' while mutex 'mu1' is locked}} \
+ // expected-warning {{cannot call function 'f2' while mutex 'mu2' is locked}}
+ }
+};
+
+Foo *foo;
+
+void func()
+{
+ foo->f1(); // expected-warning {{calling function 'f1' requires exclusive lock on 'mu2'}} \
+ // expected-warning {{calling function 'f1' requires exclusive lock on 'mu1'}}
+}
+} // end namespace thread_annot_lock_42
+
+namespace thread_annot_lock_46 {
+// Test the support for annotations on virtual functions.
+class Base {
+ public:
+ virtual void func1() EXCLUSIVE_LOCKS_REQUIRED(mu_);
+ virtual void func2() LOCKS_EXCLUDED(mu_);
+ Mutex mu_;
+};
+
+class Child : public Base {
+ public:
+ virtual void func1() EXCLUSIVE_LOCKS_REQUIRED(mu_);
+ virtual void func2() LOCKS_EXCLUDED(mu_);
+};
+
+void main() {
+ Child *c;
+ Base *b = c;
+
+ b->func1(); // expected-warning {{calling function 'func1' requires exclusive lock on 'mu_'}}
+ b->mu_.Lock();
+ b->func2(); // expected-warning {{cannot call function 'func2' while mutex 'mu_' is locked}}
+ b->mu_.Unlock();
+
+ c->func1(); // expected-warning {{calling function 'func1' requires exclusive lock on 'mu_'}}
+ c->mu_.Lock();
+ c->func2(); // expected-warning {{cannot call function 'func2' while mutex 'mu_' is locked}}
+ c->mu_.Unlock();
+}
+} // end namespace thread_annot_lock_46
+
+namespace thread_annot_lock_67_modified {
+// Modified: attributes on definitions moved to declarations
+// Test annotations on out-of-line definitions of member functions where the
+// annotations refer to locks that are also data members in the class.
+Mutex mu;
+Mutex mu3;
+
+class Foo {
+ public:
+ int method1(int i) SHARED_LOCKS_REQUIRED(mu1, mu, mu2, mu3);
+ int data GUARDED_BY(mu1);
+ Mutex *mu1;
+ Mutex *mu2;
+};
+
+int Foo::method1(int i) {
+ return data + i;
+}
+
+void main()
+{
+ Foo a;
+ a.method1(1); // expected-warning {{calling function 'method1' requires shared lock on 'mu1'}} \
+ // expected-warning {{calling function 'method1' requires shared lock on 'mu'}} \
+ // expected-warning {{calling function 'method1' requires shared lock on 'mu2'}} \
+ // expected-warning {{calling function 'method1' requires shared lock on 'mu3'}}
+}
+} // end namespace thread_annot_lock_67_modified
+
+
+namespace substitution_test {
+ class MyData {
+ public:
+ Mutex mu;
+
+ void lockData() __attribute__((exclusive_lock_function(mu))) { }
+ void unlockData() __attribute__((unlock_function(mu))) { }
+
+ void doSomething() __attribute__((exclusive_locks_required(mu))) { }
+ };
+
+
+ class DataLocker {
+ public:
+ void lockData (MyData *d) __attribute__((exclusive_lock_function(d->mu))) { }
+ void unlockData(MyData *d) __attribute__((unlock_function(d->mu))) { }
+ };
+
+
+ class Foo {
+ public:
+ void foo(MyData* d) __attribute__((exclusive_locks_required(d->mu))) { }
+
+ void bar1(MyData* d) {
+ d->lockData();
+ foo(d);
+ d->unlockData();
+ }
+
+ void bar2(MyData* d) {
+ DataLocker dlr;
+ dlr.lockData(d);
+ foo(d);
+ dlr.unlockData(d);
+ }
+
+ void bar3(MyData* d1, MyData* d2) {
+ DataLocker dlr;
+ dlr.lockData(d1); // expected-note {{mutex acquired here}}
+ dlr.unlockData(d2); // \
+ // expected-warning {{unlocking 'mu' that was not locked}}
+ } // expected-warning {{mutex 'mu' is still locked at the end of function}}
+
+ void bar4(MyData* d1, MyData* d2) {
+ DataLocker dlr;
+ dlr.lockData(d1);
+ foo(d2); // \
+ // expected-warning {{calling function 'foo' requires exclusive lock on 'mu'}}
+ dlr.unlockData(d1);
+ }
+ };
+} // end namespace substituation_test
+
+
+
+namespace constructor_destructor_tests {
+ Mutex fooMu;
+ int myVar GUARDED_BY(fooMu);
+
+ class Foo {
+ public:
+ Foo() __attribute__((exclusive_lock_function(fooMu))) { }
+ ~Foo() __attribute__((unlock_function(fooMu))) { }
+ };
+
+ void fooTest() {
+ Foo foo;
+ myVar = 0;
+ }
+}
+
+
+namespace invalid_lock_expression_test {
+
+class LOCKABLE MyLockable {
+public:
+ MyLockable() __attribute__((exclusive_lock_function)) { }
+ ~MyLockable() { }
+};
+
+// create an empty lock expression
+void foo() {
+ MyLockable lock; // \
+ // expected-warning {{cannot resolve lock expression}}
+}
+
+} // end namespace invalid_lock_expression_test
+
+namespace template_member_test {
+
+ struct S { int n; };
+ struct T {
+ Mutex m;
+ S *s GUARDED_BY(this->m);
+ };
+ Mutex m;
+ struct U {
+ union {
+ int n;
+ };
+ } *u GUARDED_BY(m);
+
+ template<typename U>
+ struct IndirectLock {
+ int DoNaughtyThings(T *t) {
+ u->n = 0; // expected-warning {{reading variable 'u' requires locking 'm'}}
+ return t->s->n; // expected-warning {{reading variable 's' requires locking 'm'}}
+ }
+ };
+
+ template struct IndirectLock<int>; // expected-note {{here}}
+
+ struct V {
+ void f(int);
+ void f(double);
+
+ Mutex m;
+ V *p GUARDED_BY(this->m);
+ };
+ template<typename U> struct W {
+ V v;
+ void f(U u) {
+ v.p->f(u); // expected-warning {{reading variable 'p' requires locking 'm'}}
+ }
+ };
+ template struct W<int>; // expected-note {{here}}
+
+}
+
+namespace test_scoped_lockable {
+
+struct TestScopedLockable {
+ Mutex mu1;
+ Mutex mu2;
+ int a __attribute__((guarded_by(mu1)));
+ int b __attribute__((guarded_by(mu2)));
+
+ bool getBool();
+
+ void foo1() {
+ MutexLock mulock(&mu1);
+ a = 5;
+ }
+
+ void foo2() {
+ ReaderMutexLock mulock1(&mu1);
+ if (getBool()) {
+ MutexLock mulock2a(&mu2);
+ b = a + 1;
+ }
+ else {
+ MutexLock mulock2b(&mu2);
+ b = a + 2;
+ }
+ }
+
+ void foo3() {
+ MutexLock mulock_a(&mu1);
+ MutexLock mulock_b(&mu1); // \
+ // expected-warning {{locking 'mu1' that is already locked}}
+ } // expected-warning {{unlocking 'mu1' that was not locked}}
+
+ void foo4() {
+ MutexLock mulock1(&mu1), mulock2(&mu2);
+ a = b+1;
+ b = a+1;
+ }
+};
+
+} // end namespace test_scoped_lockable
+
+
+namespace FunctionAttrTest {
+
+class Foo {
+public:
+ Mutex mu_;
+ int a GUARDED_BY(mu_);
+};
+
+Foo fooObj;
+
+void foo() EXCLUSIVE_LOCKS_REQUIRED(fooObj.mu_);
+
+void bar() {
+ foo(); // expected-warning {{calling function 'foo' requires exclusive lock on 'mu_'}}
+ fooObj.mu_.Lock();
+ foo();
+ fooObj.mu_.Unlock();
+}
+
+}; // end namespace FunctionAttrTest
+
+
+struct TestTryLock {
+ Mutex mu;
+ int a GUARDED_BY(mu);
+ bool cond;
+
+ void foo1() {
+ if (mu.TryLock()) {
+ a = 1;
+ mu.Unlock();
+ }
+ }
+
+ void foo2() {
+ if (!mu.TryLock()) return;
+ a = 2;
+ mu.Unlock();
+ }
+
+ void foo3() {
+ bool b = mu.TryLock();
+ if (b) {
+ a = 3;
+ mu.Unlock();
+ }
+ }
+
+ void foo4() {
+ bool b = mu.TryLock();
+ if (!b) return;
+ a = 4;
+ mu.Unlock();
+ }
+
+ void foo5() {
+ while (mu.TryLock()) {
+ a = a + 1;
+ mu.Unlock();
+ }
+ }
+
+ void foo6() {
+ bool b = mu.TryLock();
+ b = !b;
+ if (b) return;
+ a = 6;
+ mu.Unlock();
+ }
+
+ void foo7() {
+ bool b1 = mu.TryLock();
+ bool b2 = !b1;
+ bool b3 = !b2;
+ if (b3) {
+ a = 7;
+ mu.Unlock();
+ }
+ }
+
+ // Test use-def chains: join points
+ void foo8() {
+ bool b = mu.TryLock();
+ bool b2 = b;
+ if (cond)
+ b = true;
+ if (b) { // b should be unknown at this point, becuase of the join point
+ a = 8; // expected-warning {{writing variable 'a' requires locking 'mu' exclusively}}
+ }
+ if (b2) { // b2 should be known at this point.
+ a = 8;
+ mu.Unlock();
+ }
+ }
+
+ // Test use-def-chains: back edges
+ void foo9() {
+ bool b = mu.TryLock();
+
+ for (int i = 0; i < 10; ++i);
+
+ if (b) { // b is still known, because the loop doesn't alter it
+ a = 9;
+ mu.Unlock();
+ }
+ }
+
+ // Test use-def chains: back edges
+ void foo10() {
+ bool b = mu.TryLock();
+
+ while (cond) {
+ if (b) { // b should be uknown at this point b/c of the loop
+ a = 10; // expected-warning {{writing variable 'a' requires locking 'mu' exclusively}}
+ }
+ b = !b;
+ }
+ }
+}; // end TestTrylock
+
+
+namespace TestTemplateAttributeInstantiation {
+
+class Foo1 {
+public:
+ Mutex mu_;
+ int a GUARDED_BY(mu_);
+};
+
+class Foo2 {
+public:
+ int a GUARDED_BY(mu_);
+ Mutex mu_;
+};
+
+
+class Bar {
+public:
+ // Test non-dependent expressions in attributes on template functions
+ template <class T>
+ void barND(Foo1 *foo, T *fooT) EXCLUSIVE_LOCKS_REQUIRED(foo->mu_) {
+ foo->a = 0;
+ }
+
+ // Test dependent expressions in attributes on template functions
+ template <class T>
+ void barD(Foo1 *foo, T *fooT) EXCLUSIVE_LOCKS_REQUIRED(fooT->mu_) {
+ fooT->a = 0;
+ }
+};
+
+
+template <class T>
+class BarT {
+public:
+ Foo1 fooBase;
+ T fooBaseT;
+
+ // Test non-dependent expression in ordinary method on template class
+ void barND() EXCLUSIVE_LOCKS_REQUIRED(fooBase.mu_) {
+ fooBase.a = 0;
+ }
+
+ // Test dependent expressions in ordinary methods on template class
+ void barD() EXCLUSIVE_LOCKS_REQUIRED(fooBaseT.mu_) {
+ fooBaseT.a = 0;
+ }
+
+ // Test dependent expressions in template method in template class
+ template <class T2>
+ void barTD(T2 *fooT) EXCLUSIVE_LOCKS_REQUIRED(fooBaseT.mu_, fooT->mu_) {
+ fooBaseT.a = 0;
+ fooT->a = 0;
+ }
+};
+
+template <class T>
+class Cell {
+public:
+ Mutex mu_;
+ // Test dependent guarded_by
+ T data GUARDED_BY(mu_);
+
+ void fooEx() EXCLUSIVE_LOCKS_REQUIRED(mu_) {
+ data = 0;
+ }
+
+ void foo() {
+ mu_.Lock();
+ data = 0;
+ mu_.Unlock();
+ }
+};
+
+void test() {
+ Bar b;
+ BarT<Foo2> bt;
+ Foo1 f1;
+ Foo2 f2;
+
+ f1.mu_.Lock();
+ f2.mu_.Lock();
+ bt.fooBase.mu_.Lock();
+ bt.fooBaseT.mu_.Lock();
+
+ b.barND(&f1, &f2);
+ b.barD(&f1, &f2);
+ bt.barND();
+ bt.barD();
+ bt.barTD(&f2);
+
+ f1.mu_.Unlock();
+ bt.barTD(&f1); // \
+ // expected-warning {{calling function 'barTD' requires exclusive lock on 'mu_'}}
+
+ bt.fooBase.mu_.Unlock();
+ bt.fooBaseT.mu_.Unlock();
+ f2.mu_.Unlock();
+
+ Cell<int> cell;
+ cell.data = 0; // \
+ // expected-warning {{writing variable 'data' requires locking 'mu_' exclusively}}
+ cell.foo();
+ cell.mu_.Lock();
+ cell.fooEx();
+ cell.mu_.Unlock();
+}
+
+
+template <class T>
+class CellDelayed {
+public:
+ // Test dependent guarded_by
+ T data GUARDED_BY(mu_);
+ static T static_data GUARDED_BY(static_mu_);
+
+ void fooEx(CellDelayed<T> *other) EXCLUSIVE_LOCKS_REQUIRED(mu_, other->mu_) {
+ this->data = other->data;
+ }
+
+ template <class T2>
+ void fooExT(CellDelayed<T2> *otherT) EXCLUSIVE_LOCKS_REQUIRED(mu_, otherT->mu_) {
+ this->data = otherT->data;
+ }
+
+ void foo() {
+ mu_.Lock();
+ data = 0;
+ mu_.Unlock();
+ }
+
+ Mutex mu_;
+ static Mutex static_mu_;
+};
+
+void testDelayed() {
+ CellDelayed<int> celld;
+ CellDelayed<int> celld2;
+ celld.foo();
+ celld.mu_.Lock();
+ celld2.mu_.Lock();
+
+ celld.fooEx(&celld2);
+ celld.fooExT(&celld2);
+
+ celld2.mu_.Unlock();
+ celld.mu_.Unlock();
+}
+
+}; // end namespace TestTemplateAttributeInstantiation
+
+
+namespace FunctionDeclDefTest {
+
+class Foo {
+public:
+ Mutex mu_;
+ int a GUARDED_BY(mu_);
+
+ virtual void foo1(Foo *f_declared) EXCLUSIVE_LOCKS_REQUIRED(f_declared->mu_);
+};
+
+// EXCLUSIVE_LOCKS_REQUIRED should be applied, and rewritten to f_defined->mu_
+void Foo::foo1(Foo *f_defined) {
+ f_defined->a = 0;
+};
+
+void test() {
+ Foo myfoo;
+ myfoo.foo1(&myfoo); // \
+ // expected-warning {{calling function 'foo1' requires exclusive lock on 'mu_'}}
+ myfoo.mu_.Lock();
+ myfoo.foo1(&myfoo);
+ myfoo.mu_.Unlock();
+}
+
+};
+
+namespace GoingNative {
+
+ struct __attribute__((lockable)) mutex {
+ void lock() __attribute__((exclusive_lock_function));
+ void unlock() __attribute__((unlock_function));
+ // ...
+ };
+ bool foo();
+ bool bar();
+ mutex m;
+ void test() {
+ m.lock();
+ while (foo()) {
+ m.unlock();
+ // ...
+ if (bar()) {
+ // ...
+ if (foo())
+ continue; // expected-warning {{expecting mutex 'm' to be locked at start of each loop}}
+ //...
+ }
+ // ...
+ m.lock(); // expected-note {{mutex acquired here}}
+ }
+ m.unlock();
+ }
+
+}
+
+
+
+namespace FunctionDefinitionTest {
+
+class Foo {
+public:
+ void foo1();
+ void foo2();
+ void foo3(Foo *other);
+
+ template<class T>
+ void fooT1(const T& dummy1);
+
+ template<class T>
+ void fooT2(const T& dummy2) EXCLUSIVE_LOCKS_REQUIRED(mu_);
+
+ Mutex mu_;
+ int a GUARDED_BY(mu_);
+};
+
+template<class T>
+class FooT {
+public:
+ void foo();
+
+ Mutex mu_;
+ T a GUARDED_BY(mu_);
+};
+
+
+void Foo::foo1() NO_THREAD_SAFETY_ANALYSIS {
+ a = 1;
+}
+
+void Foo::foo2() EXCLUSIVE_LOCKS_REQUIRED(mu_) {
+ a = 2;
+}
+
+void Foo::foo3(Foo *other) EXCLUSIVE_LOCKS_REQUIRED(other->mu_) {
+ other->a = 3;
+}
+
+template<class T>
+void Foo::fooT1(const T& dummy1) EXCLUSIVE_LOCKS_REQUIRED(mu_) {
+ a = dummy1;
+}
+
+/* TODO -- uncomment with template instantiation of attributes.
+template<class T>
+void Foo::fooT2(const T& dummy2) {
+ a = dummy2;
+}
+*/
+
+void fooF1(Foo *f) EXCLUSIVE_LOCKS_REQUIRED(f->mu_) {
+ f->a = 1;
+}
+
+void fooF2(Foo *f);
+void fooF2(Foo *f) EXCLUSIVE_LOCKS_REQUIRED(f->mu_) {
+ f->a = 2;
+}
+
+void fooF3(Foo *f) EXCLUSIVE_LOCKS_REQUIRED(f->mu_);
+void fooF3(Foo *f) {
+ f->a = 3;
+}
+
+template<class T>
+void FooT<T>::foo() EXCLUSIVE_LOCKS_REQUIRED(mu_) {
+ a = 0;
+}
+
+void test() {
+ int dummy = 0;
+ Foo myFoo;
+
+ myFoo.foo2(); // \
+ // expected-warning {{calling function 'foo2' requires exclusive lock on 'mu_'}}
+ myFoo.foo3(&myFoo); // \
+ // expected-warning {{calling function 'foo3' requires exclusive lock on 'mu_'}}
+ myFoo.fooT1(dummy); // \
+ // expected-warning {{calling function 'fooT1' requires exclusive lock on 'mu_'}}
+
+ // FIXME: uncomment with template instantiation of attributes patch
+ // myFoo.fooT2(dummy); // expected warning
+
+ fooF1(&myFoo); // \
+ // expected-warning {{calling function 'fooF1' requires exclusive lock on 'mu_'}}
+ fooF2(&myFoo); // \
+ // expected-warning {{calling function 'fooF2' requires exclusive lock on 'mu_'}}
+ fooF3(&myFoo); // \
+ // expected-warning {{calling function 'fooF3' requires exclusive lock on 'mu_'}}
+
+ myFoo.mu_.Lock();
+ myFoo.foo2();
+ myFoo.foo3(&myFoo);
+ myFoo.fooT1(dummy);
+
+ // FIXME: uncomment with template instantiation of attributes patch
+ // myFoo.fooT2(dummy);
+
+ fooF1(&myFoo);
+ fooF2(&myFoo);
+ fooF3(&myFoo);
+ myFoo.mu_.Unlock();
+
+ FooT<int> myFooT;
+ myFooT.foo(); // \
+ // expected-warning {{calling function 'foo' requires exclusive lock on 'mu_'}}
+}
+
+} // end namespace FunctionDefinitionTest
+
+
+namespace SelfLockingTest {
+
+class LOCKABLE MyLock {
+public:
+ int foo GUARDED_BY(this);
+
+ void lock() EXCLUSIVE_LOCK_FUNCTION();
+ void unlock() UNLOCK_FUNCTION();
+
+ void doSomething() {
+ this->lock(); // allow 'this' as a lock expression
+ foo = 0;
+ doSomethingElse();
+ this->unlock();
+ }
+
+ void doSomethingElse() EXCLUSIVE_LOCKS_REQUIRED(this) {
+ foo = 1;
+ };
+
+ void test() {
+ foo = 2; // \
+ // expected-warning {{writing variable 'foo' requires locking 'this' exclusively}}
+ }
+};
+
+
+class LOCKABLE MyLock2 {
+public:
+ Mutex mu_;
+ int foo GUARDED_BY(this);
+
+ // don't check inside lock and unlock functions
+ void lock() EXCLUSIVE_LOCK_FUNCTION() { mu_.Lock(); }
+ void unlock() UNLOCK_FUNCTION() { mu_.Unlock(); }
+
+ // don't check inside constructors and destructors
+ MyLock2() { foo = 1; }
+ ~MyLock2() { foo = 0; }
+};
+
+
+} // end namespace SelfLockingTest
+
+
+namespace InvalidNonstatic {
+
+// Forward decl here causes bogus "invalid use of non-static data member"
+// on reference to mutex_ in guarded_by attribute.
+class Foo;
+
+class Foo {
+ Mutex* mutex_;
+
+ int foo __attribute__((guarded_by(mutex_)));
+};
+
+} // end namespace InvalidNonStatic
+
+
+namespace NoReturnTest {
+
+bool condition();
+void fatal() __attribute__((noreturn));
+
+Mutex mu_;
+
+void test1() {
+ MutexLock lock(&mu_);
+ if (condition()) {
+ fatal();
+ return;
+ }
+}
+
+} // end namespace NoReturnTest
+
+
+namespace TestMultiDecl {
+
+class Foo {
+public:
+ int GUARDED_BY(mu_) a;
+ int GUARDED_BY(mu_) b, c;
+
+ void foo() {
+ a = 0; // \
+ // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ b = 0; // \
+ // expected-warning {{writing variable 'b' requires locking 'mu_' exclusively}}
+ c = 0; // \
+ // expected-warning {{writing variable 'c' requires locking 'mu_' exclusively}}
+ }
+
+private:
+ Mutex mu_;
+};
+
+} // end namespace TestMultiDecl
+
+
+namespace WarnNoDecl {
+
+class Foo {
+ void foo(int a); __attribute__(( // \
+ // expected-warning {{declaration does not declare anything}}
+ exclusive_locks_required(a))); // \
+ // expected-warning {{attribute exclusive_locks_required ignored}}
+};
+
+} // end namespace WarnNoDecl
+
+
+
+namespace MoreLockExpressions {
+
+class Foo {
+public:
+ Mutex mu_;
+ int a GUARDED_BY(mu_);
+};
+
+class Bar {
+public:
+ int b;
+ Foo* f;
+
+ Foo& getFoo() { return *f; }
+ Foo& getFoo2(int c) { return *f; }
+ Foo& getFoo3(int c, int d) { return *f; }
+
+ Foo& getFooey() { return *f; }
+};
+
+Foo& getBarFoo(Bar &bar, int c) { return bar.getFoo2(c); }
+
+void test() {
+ Foo foo;
+ Foo *fooArray;
+ Bar bar;
+ int a;
+ int b;
+ int c;
+
+ bar.getFoo().mu_.Lock();
+ bar.getFoo().a = 0;
+ bar.getFoo().mu_.Unlock();
+
+ (bar.getFoo().mu_).Lock(); // test parenthesis
+ bar.getFoo().a = 0;
+ (bar.getFoo().mu_).Unlock();
+
+ bar.getFoo2(a).mu_.Lock();
+ bar.getFoo2(a).a = 0;
+ bar.getFoo2(a).mu_.Unlock();
+
+ bar.getFoo3(a, b).mu_.Lock();
+ bar.getFoo3(a, b).a = 0;
+ bar.getFoo3(a, b).mu_.Unlock();
+
+ getBarFoo(bar, a).mu_.Lock();
+ getBarFoo(bar, a).a = 0;
+ getBarFoo(bar, a).mu_.Unlock();
+
+ bar.getFoo2(10).mu_.Lock();
+ bar.getFoo2(10).a = 0;
+ bar.getFoo2(10).mu_.Unlock();
+
+ bar.getFoo2(a + 1).mu_.Lock();
+ bar.getFoo2(a + 1).a = 0;
+ bar.getFoo2(a + 1).mu_.Unlock();
+
+ (a > 0 ? fooArray[1] : fooArray[b]).mu_.Lock();
+ (a > 0 ? fooArray[1] : fooArray[b]).a = 0;
+ (a > 0 ? fooArray[1] : fooArray[b]).mu_.Unlock();
+
+ bar.getFoo().mu_.Lock();
+ bar.getFooey().a = 0; // \
+ // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ bar.getFoo().mu_.Unlock();
+
+ bar.getFoo2(a).mu_.Lock();
+ bar.getFoo2(b).a = 0; // \
+ // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ bar.getFoo2(a).mu_.Unlock();
+
+ bar.getFoo3(a, b).mu_.Lock();
+ bar.getFoo3(a, c).a = 0; // \
+ // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ bar.getFoo3(a, b).mu_.Unlock();
+
+ getBarFoo(bar, a).mu_.Lock();
+ getBarFoo(bar, b).a = 0; // \
+ // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ getBarFoo(bar, a).mu_.Unlock();
+
+ (a > 0 ? fooArray[1] : fooArray[b]).mu_.Lock();
+ (a > 0 ? fooArray[b] : fooArray[c]).a = 0; // \
+ // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}}
+ (a > 0 ? fooArray[1] : fooArray[b]).mu_.Unlock();
+}
+
+
+} // end namespace
+
+