Thread safety analysis: expand set of expressions that can be used to denote locks.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@151956 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp
index 40ad79b..b2f89cc 100644
--- a/lib/Analysis/ThreadSafety.cpp
+++ b/lib/Analysis/ThreadSafety.cpp
@@ -126,12 +126,56 @@
         DeclSeq.push_back(0);  // Use 0 to represent 'this'.
         return;  // mutexID is still valid in this case
       }
-    } else if (UnaryOperator *UOE = dyn_cast<UnaryOperator>(Exp))
+    } else if (CXXMemberCallExpr *CMCE = dyn_cast<CXXMemberCallExpr>(Exp)) {
+      DeclSeq.push_back(CMCE->getMethodDecl()->getCanonicalDecl());
+      buildMutexID(CMCE->getImplicitObjectArgument(),
+                   D, Parent, NumArgs, FunArgs);
+      unsigned NumCallArgs = CMCE->getNumArgs();
+      Expr** CallArgs = CMCE->getArgs();
+      for (unsigned i = 0; i < NumCallArgs; ++i) {
+        buildMutexID(CallArgs[i], D, Parent, NumArgs, FunArgs);
+      }
+    } else if (CallExpr *CE = dyn_cast<CallExpr>(Exp)) {
+      buildMutexID(CE->getCallee(), D, Parent, NumArgs, FunArgs);
+      unsigned NumCallArgs = CE->getNumArgs();
+      Expr** CallArgs = CE->getArgs();
+      for (unsigned i = 0; i < NumCallArgs; ++i) {
+        buildMutexID(CallArgs[i], D, Parent, NumArgs, FunArgs);
+      }
+    } else if (BinaryOperator *BOE = dyn_cast<BinaryOperator>(Exp)) {
+      buildMutexID(BOE->getLHS(), D, Parent, NumArgs, FunArgs);
+      buildMutexID(BOE->getRHS(), D, Parent, NumArgs, FunArgs);
+    } else if (UnaryOperator *UOE = dyn_cast<UnaryOperator>(Exp)) {
       buildMutexID(UOE->getSubExpr(), D, Parent, NumArgs, FunArgs);
-    else if (CastExpr *CE = dyn_cast<CastExpr>(Exp))
+    } else if (ArraySubscriptExpr *ASE = dyn_cast<ArraySubscriptExpr>(Exp)) {
+      buildMutexID(ASE->getBase(), D, Parent, NumArgs, FunArgs);
+      buildMutexID(ASE->getIdx(), D, Parent, NumArgs, FunArgs);
+    } else if (AbstractConditionalOperator *CE =
+                 dyn_cast<AbstractConditionalOperator>(Exp)) {
+      buildMutexID(CE->getCond(), D, Parent, NumArgs, FunArgs);
+      buildMutexID(CE->getTrueExpr(), D, Parent, NumArgs, FunArgs);
+      buildMutexID(CE->getFalseExpr(), D, Parent, NumArgs, FunArgs);
+    } else if (ChooseExpr *CE = dyn_cast<ChooseExpr>(Exp)) {
+      buildMutexID(CE->getCond(), D, Parent, NumArgs, FunArgs);
+      buildMutexID(CE->getLHS(), D, Parent, NumArgs, FunArgs);
+      buildMutexID(CE->getRHS(), D, Parent, NumArgs, FunArgs);
+    } else if (CastExpr *CE = dyn_cast<CastExpr>(Exp)) {
       buildMutexID(CE->getSubExpr(), D, Parent, NumArgs, FunArgs);
-    else
-      DeclSeq.clear(); // Mark as invalid lock expression.
+    } else if (ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) {
+      buildMutexID(PE->getSubExpr(), D, Parent, NumArgs, FunArgs);
+    } else if (isa<CharacterLiteral>(Exp) ||
+             isa<CXXNullPtrLiteralExpr>(Exp) ||
+             isa<GNUNullExpr>(Exp) ||
+             isa<CXXBoolLiteralExpr>(Exp) ||
+             isa<FloatingLiteral>(Exp) ||
+             isa<ImaginaryLiteral>(Exp) ||
+             isa<IntegerLiteral>(Exp) ||
+             isa<StringLiteral>(Exp) ||
+             isa<ObjCStringLiteral>(Exp)) {
+      return;  // FIXME: Ignore literals for now
+    } else {
+      // Ignore.  FIXME: mark as invalid expression?
+    }
   }
 
   /// \brief Construct a MutexID from an expression.
@@ -233,11 +277,11 @@
   /// The caret will point unambiguously to the lock expression, so using this
   /// name in diagnostics is a way to get simple, and consistent, mutex names.
   /// We do not want to output the entire expression text for security reasons.
-  StringRef getName() const {
+  std::string getName() const {
     assert(isValid());
     if (!DeclSeq.front())
       return "this";  // Use 0 to represent 'this'.
-    return DeclSeq.front()->getName();
+    return DeclSeq.front()->getNameAsString();
   }
 
   void Profile(llvm::FoldingSetNodeID &ID) const {
diff --git a/test/SemaCXX/warn-thread-safety-analysis.cpp b/test/SemaCXX/warn-thread-safety-analysis.cpp
index 1bf00c2..715645c 100644
--- a/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ b/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -766,27 +766,7 @@
 // Unparseable lock expressions
 // ----------------------------------------------//
 
-Mutex UPmu;
-// FIXME: add support for lock expressions involving arrays.
-Mutex mua[5];
-
-int x __attribute__((guarded_by(UPmu = sls_mu)));
-int y __attribute__((guarded_by(mua[0])));
-
-
-void testUnparse() {
-  x = 5; // \
-    // expected-warning{{cannot resolve lock expression}}
-  y = 5; // \
-    // expected-warning{{cannot resolve lock expression}}
-}
-
-void testUnparse2() {
-  mua[0].Lock(); // \
-    // expected-warning{{cannot resolve lock expression}}
-  (&(mua[0]) + 4)->Lock(); // \
-    // expected-warning{{cannot resolve lock expression}}
-}
+// FIXME -- derive new tests for unhandled expressions
 
 
 //----------------------------------------------------------------------------//
@@ -2168,3 +2148,96 @@
 } // 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
+
+