[cfe-commits] r138350 - in /cfe/trunk: include/clang/Basic/Attr.td include/clang/Basic/DiagnosticSemaKinds.td include/clang/Sema/AnalysisBasedWarnings.h lib/Sema/AnalysisBasedWarnings.cpp lib/Sema/SemaDeclAttr.cpp test/SemaCXX/warn-thread-safety-analysis.cpp test/SemaCXX/warn-thread-safety-parsing.cpp test/SemaCXX/warn-thread-safety.cpp

Caitlin Sadowski supertri at google.com
Tue Aug 23 11:46:35 PDT 2011


Author: supertri
Date: Tue Aug 23 13:46:34 2011
New Revision: 138350

URL: http://llvm.org/viewvc/llvm-project?rev=138350&view=rev
Log:
Thread-safety analysis: adding in a basic lockset tracking system. This
system flags an error when unlocking a lock which was not held, locking
the same lock twice, having a different lockset on each iteration of a
loop, or going out of scope while still holding a lock. In order to
successfully use the lockset, this patch also makes sure that attribute
arguments are attached correctly for later parsing.

This patch was also worked on by DeLesley Hutchins.

Note: This patch has been reviewed by Chandler Carruth and Jeffrey
Yasskin. Feel free to provide post-commit review comments for a
subsequent patch.

Added:
    cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp
    cfe/trunk/test/SemaCXX/warn-thread-safety-parsing.cpp
      - copied, changed from r138343, cfe/trunk/test/SemaCXX/warn-thread-safety.cpp
Removed:
    cfe/trunk/test/SemaCXX/warn-thread-safety.cpp
Modified:
    cfe/trunk/include/clang/Basic/Attr.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/AnalysisBasedWarnings.h
    cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
    cfe/trunk/lib/Sema/SemaDeclAttr.cpp

Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=138350&r1=138349&r2=138350&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Tue Aug 23 13:46:34 2011
@@ -55,6 +55,7 @@
 class TypeArgument<string name> : Argument<name>;
 class UnsignedArgument<string name> : Argument<name>;
 class VariadicUnsignedArgument<string name> : Argument<name>;
+class VariadicExprArgument<string name> : Argument<name>;
 
 // A version of the form major.minor[.subminor].
 class VersionArgument<string name> : Argument<name>;
@@ -564,52 +565,65 @@
 
 def GuardedBy : InheritableAttr {
   let Spellings = ["guarded_by"];
+  let Args = [ExprArgument<"Arg">];
 }
 
 def PtGuardedBy : InheritableAttr {
   let Spellings = ["pt_guarded_by"];
+  let Args = [ExprArgument<"Arg">];
 }
 
 def AcquiredAfter : InheritableAttr {
   let Spellings = ["acquired_after"];
+  let Args = [VariadicExprArgument<"Args">];
 }
 
 def AcquiredBefore : InheritableAttr {
   let Spellings = ["acquired_before"];
+  let Args = [VariadicExprArgument<"Args">];
 }
 
 def ExclusiveLockFunction : InheritableAttr {
   let Spellings = ["exclusive_lock_function"];
+  let Args = [VariadicExprArgument<"Args">];
 }
 
 def SharedLockFunction : InheritableAttr {
   let Spellings = ["shared_lock_function"];
+  let Args = [VariadicExprArgument<"Args">];
 }
 
 def ExclusiveTrylockFunction : InheritableAttr {
   let Spellings = ["exclusive_trylock_function"];
+  let Args = [VariadicExprArgument<"Args">];
 }
 
 def SharedTrylockFunction : InheritableAttr {
   let Spellings = ["shared_trylock_function"];
+  let Args = [VariadicExprArgument<"Args">];
 }
 
 def UnlockFunction : InheritableAttr {
   let Spellings = ["unlock_function"];
+  let Args = [VariadicExprArgument<"Args">];
 }
 
 def LockReturned : InheritableAttr {
   let Spellings = ["lock_returned"];
+  let Args = [ExprArgument<"Arg">];
 }
 
 def LocksExcluded : InheritableAttr {
   let Spellings = ["locks_excluded"];
+  let Args = [VariadicExprArgument<"Args">];
 }
 
 def ExclusiveLocksRequired : InheritableAttr {
   let Spellings = ["exclusive_locks_required"];
+  let Args = [VariadicExprArgument<"Args">];
 }
 
 def SharedLocksRequired : InheritableAttr {
   let Spellings = ["shared_locks_required"];
+  let Args = [VariadicExprArgument<"Args">];
 }

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=138350&r1=138349&r2=138350&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Aug 23 13:46:34 2011
@@ -1383,7 +1383,24 @@
 def err_attribute_decl_not_lockable : Error<
   "%0 attribute can only be applied in a context annotated "
   "with 'lockable' attribute">;
-
+def warn_unlock_but_no_acquire : Warning<
+  "unlocking '%0' that was not acquired">,
+  InGroup<DiagGroup<"thread-safety">>, DefaultIgnore;
+def warn_double_lock : Warning<
+  "locking '%0' that is already acquired">,
+  InGroup<DiagGroup<"thread-safety">>, DefaultIgnore;
+def warn_function_requires_lock : Warning<
+  "calling function '%0' requires lock '%0'">,
+  InGroup<DiagGroup<"thread-safety">>, DefaultIgnore;
+def warn_locks_not_released : Warning<
+  "lock '%0' is not released at the end of function '%1'">,
+  InGroup<DiagGroup<"thread-safety">>, DefaultIgnore;
+def warn_lock_not_released_in_scope : Warning<
+  "lock '%0' is not released at the end of its scope">,
+  InGroup<DiagGroup<"thread-safety">>, DefaultIgnore;
+def warn_expecting_lock_held_on_loop : Warning<
+  "expecting lock '%0' to be held at start of each loop">,
+  InGroup<DiagGroup<"thread-safety">>, DefaultIgnore;
 
 def warn_impcast_vector_scalar : Warning<
   "implicit conversion turns vector to scalar: %0 to %1">,

Modified: cfe/trunk/include/clang/Sema/AnalysisBasedWarnings.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/AnalysisBasedWarnings.h?rev=138350&r1=138349&r2=138350&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/AnalysisBasedWarnings.h (original)
+++ cfe/trunk/include/clang/Sema/AnalysisBasedWarnings.h Tue Aug 23 13:46:34 2011
@@ -37,6 +37,7 @@
     // The warnings to run.
     unsigned enableCheckFallThrough : 1;
     unsigned enableCheckUnreachable : 1;
+    unsigned enableThreadSafetyAnalysis : 1;
   public:
     Policy();
     void disableCheckFallThrough() { enableCheckFallThrough = 0; }

Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=138350&r1=138349&r2=138350&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Tue Aug 23 13:46:34 2011
@@ -25,6 +25,7 @@
 #include "clang/AST/StmtObjC.h"
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/EvaluatedExprVisitor.h"
+#include "clang/AST/StmtVisitor.h"
 #include "clang/Analysis/AnalysisContext.h"
 #include "clang/Analysis/CFG.h"
 #include "clang/Analysis/Analyses/ReachableCode.h"
@@ -32,7 +33,13 @@
 #include "clang/Analysis/CFGStmtMap.h"
 #include "clang/Analysis/Analyses/UninitializedValues.h"
 #include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Casting.h"
+#include <algorithm>
+#include <vector>
 
 using namespace clang;
 
@@ -585,6 +592,551 @@
 };
 }
 
+
+//===----------------------------------------------------------------------===//
+// -Wthread-safety
+//===----------------------------------------------------------------------===//
+
+namespace {
+/// \brief Implements a set of CFGBlocks using a BitVector.
+///
+/// This class contains a minimal interface, primarily dictated by the SetType
+/// template parameter of the llvm::po_iterator template, as used with external
+/// storage. We also use this set to keep track of which CFGBlocks we visit
+/// during the analysis.
+class CFGBlockSet {
+  llvm::BitVector VisitedBlockIDs;
+
+public:
+  // po_iterator requires this iterator, but the only interface needed is the
+  // value_type typedef.
+  struct iterator {
+    typedef const CFGBlock *value_type;
+  };
+
+  CFGBlockSet() {}
+  CFGBlockSet(const CFG *G) : VisitedBlockIDs(G->getNumBlockIDs(), false) {}
+
+  /// \brief Set the bit associated with a particular CFGBlock.
+  /// This is the important method for the SetType template parameter.
+  bool insert(const CFGBlock *Block) {
+    if (VisitedBlockIDs.test(Block->getBlockID()))
+      return false;
+    VisitedBlockIDs.set(Block->getBlockID());
+    return true;
+  }
+
+  /// \brief Check if the bit for a CFGBlock has been already set.
+  /// This mehtod is for tracking visited blocks in the main threadsafety loop.
+  bool alreadySet(const CFGBlock *Block) {
+    return VisitedBlockIDs.test(Block->getBlockID());
+  }
+};
+
+/// \brief We create a helper class which we use to iterate through CFGBlocks in
+/// the topological order.
+class TopologicallySortedCFG {
+  typedef llvm::po_iterator<const CFG*, CFGBlockSet, true>  po_iterator;
+
+  std::vector<const CFGBlock*> Blocks;
+
+public:
+  typedef std::vector<const CFGBlock*>::reverse_iterator iterator;
+
+  TopologicallySortedCFG(const CFG *CFGraph) {
+    Blocks.reserve(CFGraph->getNumBlockIDs());
+    CFGBlockSet BSet(CFGraph);
+
+    for (po_iterator I = po_iterator::begin(CFGraph, BSet),
+         E = po_iterator::end(CFGraph, BSet); I != E; ++I) {
+      Blocks.push_back(*I);
+    }
+  }
+
+  iterator begin() {
+    return Blocks.rbegin();
+  }
+
+  iterator end() {
+    return Blocks.rend();
+  }
+};
+
+/// \brief A Lock object uniquely identifies a particular lock acquired, and is
+/// built from an Expr* (i.e. calling a lock function).
+///
+/// Thread-safety analysis works by comparing lock expressions.  Within the
+/// body of a function, an expression such as "x->foo->bar.mu" will resolve to
+/// a particular lock object at run-time.  Subsequent occurrences of the same
+/// expression (where "same" means syntactic equality) will refer to the same
+/// run-time object if three conditions hold:
+/// (1) Local variables in the expression, such as "x" have not changed.
+/// (2) Values on the heap that affect the expression have not changed.
+/// (3) The expression involves only pure function calls.
+/// The current implementation assumes, but does not verify, that multiple uses
+/// of the same lock expression satisfies these criteria.
+///
+/// Clang introduces an additional wrinkle, which is that it is difficult to
+/// derive canonical expressions, or compare expressions directly for equality.
+/// Thus, we identify a lock not by an Expr, but by the set of named
+/// declarations that are referenced by the Expr.  In other words,
+/// x->foo->bar.mu will be a four element vector with the Decls for
+/// mu, bar, and foo, and x.  The vector will uniquely identify the expression
+/// for all practical purposes.
+///
+/// Note we will need to perform substitution on "this" and function parameter
+/// names when constructing a lock expression.
+///
+/// For example:
+/// class C { Mutex Mu;  void lock() EXCLUSIVE_LOCK_FUNCTION(this->Mu); };
+/// void myFunc(C *X) { ... X->lock() ... }
+/// The original expression for the lock acquired by myFunc is "this->Mu", but
+/// "X" is substituted for "this" so we get X->Mu();
+///
+/// For another example:
+/// foo(MyList *L) EXCLUSIVE_LOCKS_REQUIRED(L->Mu) { ... }
+/// MyList *MyL;
+/// foo(MyL);  // requires lock MyL->Mu to be held
+///
+/// FIXME: In C++0x Mutexes are the objects that control access to shared
+/// variables, while Locks are the objects that acquire and release Mutexes. We
+/// may want to switch to this new terminology soon, in which case we should
+/// rename this class "Mutex" and rename "LockId" to "MutexId", as well as
+/// making sure that the terms Lock and Mutex throughout this code are
+/// consistent with C++0x
+///
+/// FIXME: We should also pick one and canonicalize all usage of lock vs acquire
+/// and unlock vs release as verbs.
+class LockID {
+  SmallVector<NamedDecl*, 2> DeclSeq;
+ 
+  /// Build a Decl sequence representing the lock from the given expression.
+  /// Recursive function that bottoms out when the final DeclRefExpr is reached.
+  void buildLock(Expr *Exp) {
+    if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) {
+      NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl());
+      DeclSeq.push_back(ND);
+    } else if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
+      NamedDecl *ND = ME->getMemberDecl();
+      DeclSeq.push_back(ND);
+      buildLock(ME->getBase());
+    } else {
+      // FIXME: add diagnostic
+      llvm::report_fatal_error("Expected lock expression!");
+    }
+  }
+
+public:
+  LockID(Expr *LExpr) {
+    buildLock(LExpr);
+    assert(!DeclSeq.empty());
+  }
+
+  bool operator==(const LockID &other) const {
+    return DeclSeq == other.DeclSeq;
+  }
+
+  bool operator!=(const LockID &other) const {
+    return !(*this == other);
+  }
+
+  // SmallVector overloads Operator< to do lexicographic ordering. Note that
+  // we use pointer equality (and <) to compare NamedDecls. This means the order
+  // of locks in a lockset is nondeterministic. In order to output
+  // diagnostics in a deterministic ordering, we must order all diagnostics to
+  // output by SourceLocation when iterating through this lockset.
+  bool operator<(const LockID &other) const {
+    return DeclSeq < other.DeclSeq;
+  }
+
+  /// \brief Returns the name of the first Decl in the list for a given LockId;
+  /// e.g. the lock expression foo.bar() has name "bar".
+  /// The caret will point unambiguously to the lock expression, so using this
+  /// name in diagnostics is a way to get simple, and consistent, lock names.
+  /// We do not want to output the entire expression text for security reasons.
+  StringRef getName() const {
+    return DeclSeq.front()->getName();
+  }
+
+  void Profile(llvm::FoldingSetNodeID &ID) const {
+    for (SmallVectorImpl<NamedDecl*>::const_iterator I = DeclSeq.begin(),
+         E = DeclSeq.end(); I != E; ++I) {
+      ID.AddPointer(*I);
+    }
+  }
+};
+
+/// \brief This is a helper class that stores info about the most recent
+/// accquire of a Lock.
+///
+/// The main body of the analysis maps Locks to LockDatas.
+struct LockData {
+  SourceLocation AcquireLoc;
+
+  LockData(SourceLocation Loc) : AcquireLoc(Loc) {}
+
+  bool operator==(const LockData &other) const {
+    return AcquireLoc == other.AcquireLoc;
+  }
+
+  bool operator!=(const LockData &other) const {
+    return !(*this == other);
+  }
+
+  void Profile(llvm::FoldingSetNodeID &ID) const {
+    ID.AddInteger(AcquireLoc.getRawEncoding());
+  }
+};
+
+ /// A Lockset maps each lock (defined above) to information about how it has
+/// been locked.
+typedef llvm::ImmutableMap<LockID, LockData> Lockset;
+
+/// \brief We use this class to visit different types of expressions in
+/// CFGBlocks, and build up the lockset.
+/// An expression may cause us to add or remove locks from the lockset, or else
+/// output error messages related to missing locks.
+/// FIXME: In future, we may be able to not inherit from a visitor.
+class BuildLockset : public StmtVisitor<BuildLockset> {
+  Sema &S;
+  Lockset LSet;
+  Lockset::Factory &LocksetFactory;
+
+  // Helper functions
+  void RemoveLock(SourceLocation UnlockLoc, Expr *LockExp);
+  void AddLock(SourceLocation LockLoc, Expr *LockExp);
+
+public:
+  BuildLockset(Sema &S, Lockset LS, Lockset::Factory &F)
+    : StmtVisitor<BuildLockset>(), S(S), LSet(LS),
+      LocksetFactory(F) {}
+
+  Lockset getLockset() {
+    return LSet;
+  }
+
+  void VisitDeclRefExpr(DeclRefExpr *Exp);
+  void VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp);
+};
+
+/// \brief Add a new lock to the lockset, warning if the lock is already there.
+/// \param LockExp The lock expression corresponding to the lock to be added
+/// \param LockLoc The source location of the acquire
+void BuildLockset::AddLock(SourceLocation LockLoc, Expr *LockExp) {
+  LockID Lock(LockExp);
+  LockData NewLockData(LockLoc);
+
+  if (LSet.contains(Lock))
+    S.Diag(LockLoc, diag::warn_double_lock) << Lock.getName();
+
+  LSet = LocksetFactory.add(LSet, Lock, NewLockData);
+}
+
+/// \brief Remove a lock from the lockset, warning if the lock is not there.
+/// \param LockExp The lock expression corresponding to the lock to be removed
+/// \param UnlockLoc The source location of the unlock (only used in error msg)
+void BuildLockset::RemoveLock(SourceLocation UnlockLoc, Expr *LockExp) {
+  LockID Lock(LockExp);
+
+  Lockset NewLSet = LocksetFactory.remove(LSet, Lock);
+  if(NewLSet == LSet)
+    S.Diag(UnlockLoc, diag::warn_unlock_but_no_acquire) << Lock.getName();
+
+  LSet = NewLSet;
+}
+
+void BuildLockset::VisitDeclRefExpr(DeclRefExpr *Exp) {
+  // FIXME: checking for guarded_by/var and pt_guarded_by/var
+}
+
+/// \brief When visiting CXXMemberCallExprs we need to examine the attributes on
+/// the method that is being called and add, remove or check locks in the
+/// lockset accordingly.
+void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) {
+  NamedDecl *D = dyn_cast<NamedDecl>(Exp->getCalleeDecl());
+  SourceLocation ExpLocation = Exp->getExprLoc();
+  Expr *Parent = Exp->getImplicitObjectArgument();
+
+  if(!D || !D->hasAttrs())
+    return;
+
+  AttrVec &ArgAttrs = D->getAttrs();
+  for(unsigned i = 0; i < ArgAttrs.size(); ++i) {
+    Attr *Attr = ArgAttrs[i];
+    switch (Attr->getKind()) {
+      // When we encounter an exclusive lock function, we need to add the lock
+      // to our lockset.
+      case attr::ExclusiveLockFunction: {
+        ExclusiveLockFunctionAttr *ELFAttr =
+          cast<ExclusiveLockFunctionAttr>(Attr);
+
+        if (ELFAttr->args_size() == 0) {// The lock held is the "this" object.
+          AddLock(ExpLocation, Parent);
+          break;
+        }
+
+        for (ExclusiveLockFunctionAttr::args_iterator I = ELFAttr->args_begin(),
+             E = ELFAttr->args_end(); I != E; ++I)
+          AddLock(ExpLocation, *I);
+        // FIXME: acquired_after/acquired_before annotations
+        break;
+      }
+
+      // When we encounter an unlock function, we need to remove unlocked locks
+      // from the lockset, and flag a warning if they are not there.
+      case attr::UnlockFunction: {
+        UnlockFunctionAttr *UFAttr = cast<UnlockFunctionAttr>(Attr);
+
+        if (UFAttr->args_size() == 0) { // The lock held is the "this" object.
+          RemoveLock(ExpLocation, Parent);
+          break;
+        }
+
+        for (UnlockFunctionAttr::args_iterator I = UFAttr->args_begin(),
+            E = UFAttr->args_end(); I != E; ++I)
+          RemoveLock(ExpLocation, *I);
+        break;
+      }
+
+      // Ignore other (non thread-safety) attributes
+      default:
+        break;
+    }
+  }
+}
+
+typedef std::pair<SourceLocation, PartialDiagnostic> DelayedDiag;
+typedef llvm::SmallVector<DelayedDiag, 4> DiagList;
+
+struct SortDiagBySourceLocation {
+  Sema &S;
+
+  SortDiagBySourceLocation(Sema &S) : S(S) {}
+
+  bool operator()(const DelayedDiag &left, const DelayedDiag &right) {
+    // Although this call will be slow, this is only called when outputting
+    // multiple warnings.
+    return S.getSourceManager().isBeforeInTranslationUnit(left.first,
+                                                          right.first);
+  }
+};
+} // end anonymous namespace
+
+/// \brief Emit all buffered diagnostics in order of sourcelocation.
+/// We need to output diagnostics produced while iterating through
+/// the lockset in deterministic order, so this function orders diagnostics
+/// and outputs them.
+static void EmitDiagnostics(Sema &S, DiagList &D) {
+  SortDiagBySourceLocation SortDiagBySL(S);
+  sort(D.begin(), D.end(), SortDiagBySL);
+  for (DiagList::iterator I = D.begin(), E = D.end(); I != E; ++I)
+    S.Diag(I->first, I->second);
+}
+
+/// \brief Compute the intersection of two locksets and issue warnings for any
+/// locks in the symmetric difference.
+///
+/// This function is used at a merge point in the CFG when comparing the lockset
+/// of each branch being merged. For example, given the following sequence:
+/// A; if () then B; else C; D; we need to check that the lockset after B and C
+/// are the same. In the event of a difference, we use the intersection of these
+/// two locksets at the start of D.
+static Lockset intersectAndWarn(Sema &S, Lockset LSet1, Lockset LSet2,
+                                Lockset::Factory &Fact) {
+  Lockset Intersection = LSet1;
+  DiagList Warnings;
+
+  for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I) {
+    if (!LSet1.contains(I.getKey())) {
+      const LockID &MissingLock = I.getKey();
+      const LockData &MissingLockData = I.getData();
+      PartialDiagnostic Warning =
+        S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName();
+      Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
+    }
+  }
+
+  for (Lockset::iterator I = LSet1.begin(), E = LSet1.end(); I != E; ++I) {
+    if (!LSet2.contains(I.getKey())) {
+      const LockID &MissingLock = I.getKey();
+      const LockData &MissingLockData = I.getData();
+      PartialDiagnostic Warning =
+        S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName();
+      Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
+      Intersection = Fact.remove(Intersection, MissingLock);
+    }
+  }
+
+  EmitDiagnostics(S, Warnings);
+  return Intersection;
+}
+
+/// \brief Returns the location of the first Stmt in a Block.
+static SourceLocation getFirstStmtLocation(CFGBlock *Block) {
+  for (CFGBlock::const_iterator BI = Block->begin(), BE = Block->end();
+       BI != BE; ++BI) {
+    if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&(*BI)))
+      return CfgStmt->getStmt()->getLocStart();
+  }
+  return SourceLocation();
+}
+
+/// \brief Warn about different locksets along backedges of loops.
+/// This function is called when we encounter a back edge. At that point,
+/// we need to verify that the lockset before taking the backedge is the
+/// same as the lockset before entering the loop.
+///
+/// \param LoopEntrySet Locks held before starting the loop
+/// \param LoopReentrySet Locks held in the last CFG block of the loop
+static void warnBackEdgeUnequalLocksets(Sema &S, const Lockset LoopReentrySet,
+                                        const Lockset LoopEntrySet,
+                                        SourceLocation FirstLocInLoop) {
+  assert(FirstLocInLoop.isValid());
+  DiagList Warnings;
+
+  // Warn for locks held at the start of the loop, but not the end.
+  for (Lockset::iterator I = LoopEntrySet.begin(), E = LoopEntrySet.end();
+       I != E; ++I) {
+    if (!LoopReentrySet.contains(I.getKey())) {
+      const LockID &MissingLock = I.getKey();
+      // We report this error at the location of the first statement in a loop
+      PartialDiagnostic Warning =
+        S.PDiag(diag::warn_expecting_lock_held_on_loop)
+          << MissingLock.getName();
+      Warnings.push_back(DelayedDiag(FirstLocInLoop, Warning));
+    }
+  }
+
+  // Warn for locks held at the end of the loop, but not at the start.
+  for (Lockset::iterator I = LoopReentrySet.begin(), E = LoopReentrySet.end();
+       I != E; ++I) {
+    if (!LoopEntrySet.contains(I.getKey())) {
+      const LockID &MissingLock = I.getKey();
+      const LockData &MissingLockData = I.getData();
+      PartialDiagnostic Warning =
+        S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName();
+      Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
+    }
+  }
+
+  EmitDiagnostics(S, Warnings);
+}
+
+/// \brief Check a function's CFG for thread-safety violations.
+///
+/// We traverse the blocks in the CFG, compute the set of locks that are held
+/// at the end of each block, and issue warnings for thread safety violations.
+/// Each block in the CFG is traversed exactly once.
+static void checkThreadSafety(Sema &S, AnalysisContext &AC) {
+  CFG *CFGraph = AC.getCFG();
+  if (!CFGraph) return;
+
+  StringRef FunName;
+  if (const NamedDecl *ContextDecl = dyn_cast<NamedDecl>(AC.getDecl()))
+    FunName = ContextDecl->getName();
+
+  Lockset::Factory LocksetFactory;
+
+  // FIXME: Swith to SmallVector? Otherwise improve performance impact?
+  std::vector<Lockset> EntryLocksets(CFGraph->getNumBlockIDs(),
+                                     LocksetFactory.getEmptyMap());
+  std::vector<Lockset> ExitLocksets(CFGraph->getNumBlockIDs(),
+                                    LocksetFactory.getEmptyMap());
+
+  // We need to explore the CFG via a "topological" ordering.
+  // That way, we will be guaranteed to have information about required
+  // predecessor locksets when exploring a new block.
+  TopologicallySortedCFG SortedGraph(CFGraph);
+  CFGBlockSet VisitedBlocks(CFGraph);
+
+  for (TopologicallySortedCFG::iterator I = SortedGraph.begin(),
+       E = SortedGraph.end(); I!= E; ++I) {
+    const CFGBlock *CurrBlock = *I;
+    int CurrBlockID = CurrBlock->getBlockID();
+
+    VisitedBlocks.insert(CurrBlock);
+
+    // Use the default initial lockset in case there are no predecessors.
+    Lockset &Entryset = EntryLocksets[CurrBlockID];
+    Lockset &Exitset = ExitLocksets[CurrBlockID];
+
+    // Iterate through the predecessor blocks and warn if the lockset for all
+    // predecessors is not the same. We take the entry lockset of the current
+    // block to be the intersection of all previous locksets.
+    // FIXME: By keeping the intersection, we may output more errors in future
+    // for a lock which is not in the intersection, but was in the union. We
+    // may want to also keep the union in future. As an example, let's say
+    // the intersection contains Lock L, and the union contains L and M.
+    // Later we unlock M. At this point, we would output an error because we
+    // never locked M; although the real error is probably that we forgot to
+    // lock M on all code paths. Conversely, let's say that later we lock M.
+    // In this case, we should compare against the intersection instead of the
+    // union because the real error is probably that we forgot to unlock M on
+    // all code paths.
+    bool LocksetInitialized = false;
+    for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),
+         PE  = CurrBlock->pred_end(); PI != PE; ++PI) {
+
+      // if *PI -> CurrBlock is a back edge
+      if (!VisitedBlocks.alreadySet(*PI))
+        continue;
+
+      int PrevBlockID = (*PI)->getBlockID();
+      if (!LocksetInitialized) {
+        Entryset = ExitLocksets[PrevBlockID];
+        LocksetInitialized = true;
+      } else {
+        Entryset = intersectAndWarn(S, Entryset, ExitLocksets[PrevBlockID],
+                                LocksetFactory);
+      }
+    }
+
+    BuildLockset LocksetBuilder(S, Entryset, LocksetFactory);
+    for (CFGBlock::const_iterator BI = CurrBlock->begin(),
+         BE = CurrBlock->end(); BI != BE; ++BI) {
+      if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&*BI)) {
+        LocksetBuilder.Visit(CfgStmt->getStmt());
+      }
+    }
+    Exitset = LocksetBuilder.getLockset();
+
+    // For every back edge from CurrBlock (the end of the loop) to another block
+    // (FirstLoopBlock) we need to check that the Lockset of Block is equal to
+    // the one held at the beginning of FirstLoopBlock. We can look up the
+    // Lockset held at the beginning of FirstLoopBlock in the EntryLockSets map.
+    for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
+         SE  = CurrBlock->succ_end(); SI != SE; ++SI) {
+
+      // if CurrBlock -> *SI is *not* a back edge
+      if (!VisitedBlocks.alreadySet(*SI))
+        continue;
+
+      CFGBlock *FirstLoopBlock = *SI;
+      SourceLocation FirstLoopLocation = getFirstStmtLocation(FirstLoopBlock);
+
+      Lockset PreLoop = EntryLocksets[FirstLoopBlock->getBlockID()];
+      Lockset LoopEnd = ExitLocksets[CurrBlockID];
+      warnBackEdgeUnequalLocksets(S, LoopEnd, PreLoop, FirstLoopLocation);
+    }
+  }
+
+  Lockset FinalLockset = ExitLocksets[CFGraph->getExit().getBlockID()];
+  if (!FinalLockset.isEmpty()) {
+    DiagList Warnings;
+    for (Lockset::iterator I=FinalLockset.begin(), E=FinalLockset.end();
+         I != E; ++I) {
+      const LockID &MissingLock = I.getKey();
+      const LockData &MissingLockData = I.getData();
+      PartialDiagnostic Warning =
+        S.PDiag(diag::warn_locks_not_released)
+          << MissingLock.getName() << FunName;
+      Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
+    }
+    EmitDiagnostics(S, Warnings);
+  }
+}
+
+
 //===----------------------------------------------------------------------===//
 // AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based
 //  warnings on a function, method, or block.
@@ -593,6 +1145,7 @@
 clang::sema::AnalysisBasedWarnings::Policy::Policy() {
   enableCheckFallThrough = 1;
   enableCheckUnreachable = 0;
+  enableThreadSafetyAnalysis = 0;
 }
 
 clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
@@ -610,6 +1163,10 @@
   DefaultPolicy.enableCheckUnreachable = (unsigned)
     (D.getDiagnosticLevel(diag::warn_unreachable, SourceLocation()) !=
         Diagnostic::Ignored);
+  DefaultPolicy.enableThreadSafetyAnalysis = (unsigned)
+    (D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) !=
+     Diagnostic::Ignored);
+
 }
 
 static void flushDiagnostics(Sema &S, sema::FunctionScopeInfo *fscope) {
@@ -735,6 +1292,10 @@
   if (P.enableCheckUnreachable)
     CheckUnreachable(S, AC);
   
+  // Check for thread safety violations
+  if (P.enableThreadSafetyAnalysis)
+    checkThreadSafety(S, AC);
+
   if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart())
       != Diagnostic::Ignored ||
       Diags.getDiagnosticLevel(diag::warn_maybe_uninit_var, D->getLocStart())

Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=138350&r1=138349&r2=138350&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Tue Aug 23 13:46:34 2011
@@ -272,55 +272,69 @@
   return 0;
 }
 
+/// \brief Thread Safety Analysis: Checks that the passed in RecordType
+/// resolves to a lockable object. May flag an error.
+bool checkForLockableRecord(Sema &S, Decl *D, const AttributeList &Attr,
+                            const RecordType *RT) {
+  // Flag error if could not get record type for this argument.
+  if(!RT) {
+    S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_class)
+      << Attr.getName();
+    return false;
+  }
+  // Flag error if the type is not lockable.
+  if(!RT->getDecl()->getAttr<LockableAttr>()) {
+    S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_lockable)
+      << Attr.getName();
+    return false;
+  }
+  return true;
+}
+
 /// \brief Thread Safety Analysis: Checks that all attribute arguments, starting
 /// from Sidx, resolve to a lockable object. May flag an error.
-static bool checkAttrArgsAreLockableObjs(Sema & S, Decl *D,
-                                         const AttributeList & Attr,
+/// \param Sidx The attribute argument index to start checking with.
+/// \param ParamIdxOk Whether an argument can be indexing into a function
+/// parameter list.
+static bool checkAttrArgsAreLockableObjs(Sema &S, Decl *D,
+                                         const AttributeList &Attr,
+                                         SmallVectorImpl<Expr*> &Args,
                                          int Sidx = 0,
                                          bool ParamIdxOk = false) {
-  for(unsigned int Idx = Sidx; Idx < Attr.getNumArgs(); ++Idx) {
+  for(unsigned Idx = Sidx; Idx < Attr.getNumArgs(); ++Idx) {
     Expr *ArgExp = Attr.getArg(Idx);
+
     if (ArgExp->isTypeDependent())
-      continue;
+     continue;
 
-    QualType Arg_QT = ArgExp->getType();
+    QualType ArgTy = ArgExp->getType();
 
-    // Get record type.
-    // first see if we can just cast to record type, or point to record type
-    const RecordType *RT = getRecordType(Arg_QT);
-
-    // now check if we idx into a record type function param
-    if (!RT && ParamIdxOk) {
-      FunctionDecl *FD = dyn_cast <FunctionDecl>(D);
+    // First see if we can just cast to record type, or point to record type.
+    const RecordType *RT = getRecordType(ArgTy);
+
+    // Now check if we index into a record type function param.
+    if(!RT && ParamIdxOk) {
+      FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
       IntegerLiteral *IL = dyn_cast<IntegerLiteral>(ArgExp);
       if(FD && IL) {
         unsigned int NumParams = FD->getNumParams();
         llvm::APInt ArgValue = IL->getValue();
-        uint64_t ParamIdx_from1 = ArgValue.getZExtValue();
-        uint64_t ParamIdx_from0 = ParamIdx_from1 - 1;
-        if(!ArgValue.isStrictlyPositive() || ParamIdx_from1 > NumParams) {
+        uint64_t ParamIdxFromOne = ArgValue.getZExtValue();
+        uint64_t ParamIdxFromZero = ParamIdxFromOne - 1;
+        if(!ArgValue.isStrictlyPositive() || ParamIdxFromOne > NumParams) {
           S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_range)
             << Attr.getName() << Idx + 1 << NumParams;
           return false;
         }
-        Arg_QT = FD->getParamDecl(ParamIdx_from0)->getType();
-        RT = getRecordType(Arg_QT);
+        ArgTy = FD->getParamDecl(ParamIdxFromZero)->getType();
+        RT = getRecordType(ArgTy);
       }
     }
 
-    //  Flag error if could not get record type for this argument
-    if (!RT) {
-      S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_class)
-        << Attr.getName();
+    if (!checkForLockableRecord(S, D, Attr, RT))
       return false;
-    }
 
-    // Flag error if the type is not lockable
-    if (!RT->getDecl()->getAttr<LockableAttr>()) {
-      S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_lockable)
-        << Attr.getName();
-      return false;
-    }
+    Args.push_back(ArgExp);
   }
   return true;
 }
@@ -363,6 +377,8 @@
   if (!checkAttributeNumArgs(S, Attr, 1))
     return;
 
+  Expr *Arg = Attr.getArg(0);
+
   // D must be either a member field or global (potentially shared) variable.
   if (!mayBeSharedVariable(D)) {
     S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
@@ -373,14 +389,18 @@
   if (pointer && !checkIsPointer(S, D, Attr))
     return;
 
-  // check that all arguments are lockable objects
-  if (!checkAttrArgsAreLockableObjs(S, D, Attr))
+  if (Arg->isTypeDependent())
+    return;
+
+  // check that the argument is lockable object
+  if (!checkForLockableRecord(S, D, Attr, getRecordType(Arg->getType())))
     return;
 
   if (pointer)
-    D->addAttr(::new (S.Context) PtGuardedByAttr(Attr.getLoc(), S.Context));
+    D->addAttr(::new (S.Context) PtGuardedByAttr(Attr.getLoc(),
+                                                 S.Context, Arg));
   else
-    D->addAttr(::new (S.Context) GuardedByAttr(Attr.getLoc(), S.Context));
+    D->addAttr(::new (S.Context) GuardedByAttr(Attr.getLoc(), S.Context, Arg));
 }
 
 
@@ -446,14 +466,21 @@
     }
   }
 
+  SmallVector<Expr*, 1> Args;
   // check that all arguments are lockable objects
-  if (!checkAttrArgsAreLockableObjs(S, D, Attr))
+  if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args))
     return;
 
+  unsigned Size = Args.size();
+  assert(Size == Attr.getNumArgs());
+  Expr **StartArg = Size == 0 ? 0 : &Args[0];
+
   if (before)
-    D->addAttr(::new (S.Context) AcquiredBeforeAttr(Attr.getLoc(), S.Context));
+    D->addAttr(::new (S.Context) AcquiredBeforeAttr(Attr.getLoc(), S.Context,
+                                                    StartArg, Size));
   else
-    D->addAttr(::new (S.Context) AcquiredAfterAttr(Attr.getLoc(), S.Context));
+    D->addAttr(::new (S.Context) AcquiredAfterAttr(Attr.getLoc(), S.Context,
+                                                   StartArg, Size));
 }
 
 static void handleLockFunAttr(Sema &S, Decl *D, const AttributeList &Attr,
@@ -470,15 +497,22 @@
   }
 
   // check that all arguments are lockable objects
-  if (!checkAttrArgsAreLockableObjs(S, D, Attr, 0, /*ParamIdxOk=*/true))
+  SmallVector<Expr*, 1> Args;
+  if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args, 0, /*ParamIdxOk=*/true))
     return;
 
+  unsigned Size = Args.size();
+  assert(Size == Attr.getNumArgs());
+  Expr **StartArg = Size == 0 ? 0 : &Args[0];
+
   if (exclusive)
     D->addAttr(::new (S.Context) ExclusiveLockFunctionAttr(Attr.getLoc(),
-                                                           S.Context));
+                                                           S.Context, StartArg,
+                                                           Size));
   else
     D->addAttr(::new (S.Context) SharedLockFunctionAttr(Attr.getLoc(),
-                                                        S.Context));
+                                                        S.Context, StartArg,
+                                                        Size));
 }
 
 static void handleTrylockFunAttr(Sema &S, Decl *D, const AttributeList &Attr,
@@ -501,16 +535,24 @@
     return;
   }
 
+  SmallVector<Expr*, 2> Args;
+  Args.push_back(Attr.getArg(0)); //FIXME
   // check that all arguments are lockable objects
-  if (!checkAttrArgsAreLockableObjs(S, D, Attr, 1))
+  if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args, 1))
     return;
 
+  unsigned Size = Args.size();
+  assert(Size == Attr.getNumArgs());
+  Expr **StartArg = Size == 0 ? 0 : &Args[0];
+
   if (exclusive)
     D->addAttr(::new (S.Context) ExclusiveTrylockFunctionAttr(Attr.getLoc(),
-                                                              S.Context));
+                                                              S.Context,
+                                                              StartArg, Size));
   else
     D->addAttr(::new (S.Context) SharedTrylockFunctionAttr(Attr.getLoc(),
-                                                           S.Context));
+                                                           S.Context, StartArg,
+                                                           Size));
 }
 
 static void handleLocksRequiredAttr(Sema &S, Decl *D, const AttributeList &Attr,
@@ -527,15 +569,22 @@
   }
 
   // check that all arguments are lockable objects
-  if (!checkAttrArgsAreLockableObjs(S, D, Attr))
+  SmallVector<Expr*, 1> Args;
+  if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args))
     return;
 
+  unsigned Size = Args.size();
+  assert(Size == Attr.getNumArgs());
+  Expr **StartArg = Size == 0 ? 0 : &Args[0];
+
   if (exclusive)
     D->addAttr(::new (S.Context) ExclusiveLocksRequiredAttr(Attr.getLoc(),
-                                                            S.Context));
+                                                            S.Context, StartArg,
+                                                            Size));
   else
     D->addAttr(::new (S.Context) SharedLocksRequiredAttr(Attr.getLoc(),
-                                                         S.Context));
+                                                         S.Context, StartArg,
+                                                         Size));
 }
 
 static void handleUnlockFunAttr(Sema &S, Decl *D,
@@ -551,10 +600,16 @@
   }
 
   // check that all arguments are lockable objects
-  if (!checkAttrArgsAreLockableObjs(S, D, Attr, 0, /*ParamIdxOk=*/true))
+  SmallVector<Expr*, 1> Args;
+  if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args, 0, /*ParamIdxOk=*/true))
     return;
 
-  D->addAttr(::new (S.Context) UnlockFunctionAttr(Attr.getLoc(), S.Context));
+  unsigned Size = Args.size();
+  assert(Size == Attr.getNumArgs());
+  Expr **StartArg = Size == 0 ? 0 : &Args[0];
+
+  D->addAttr(::new (S.Context) UnlockFunctionAttr(Attr.getLoc(), S.Context,
+                                                  StartArg, Size));
 }
 
 static void handleLockReturnedAttr(Sema &S, Decl *D,
@@ -563,6 +618,7 @@
 
   if (!checkAttributeNumArgs(S, Attr, 1))
     return;
+  Expr *Arg = Attr.getArg(0);
 
   if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) {
     S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
@@ -570,11 +626,14 @@
     return;
   }
 
-  // check that all arguments are lockable objects
-  if (!checkAttrArgsAreLockableObjs(S, D, Attr))
+  if (Arg->isTypeDependent())
+    return;
+
+  // check that the argument is lockable object
+  if (!checkForLockableRecord(S, D, Attr, getRecordType(Arg->getType())))
     return;
 
-  D->addAttr(::new (S.Context) LockReturnedAttr(Attr.getLoc(), S.Context));
+  D->addAttr(::new (S.Context) LockReturnedAttr(Attr.getLoc(), S.Context, Arg));
 }
 
 static void handleLocksExcludedAttr(Sema &S, Decl *D,
@@ -591,10 +650,16 @@
   }
 
   // check that all arguments are lockable objects
-  if (!checkAttrArgsAreLockableObjs(S, D, Attr))
+  SmallVector<Expr*, 1> Args;
+  if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args))
     return;
 
-  D->addAttr(::new (S.Context) LocksExcludedAttr(Attr.getLoc(), S.Context));
+  unsigned Size = Args.size();
+  assert(Size == Attr.getNumArgs());
+  Expr **StartArg = Size == 0 ? 0 : &Args[0];
+
+  D->addAttr(::new (S.Context) LocksExcludedAttr(Attr.getLoc(), S.Context,
+                                                 StartArg, Size));
 }
 
 

Added: cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp?rev=138350&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp (added)
+++ cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp Tue Aug 23 13:46:34 2011
@@ -0,0 +1,267 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety %s
+
+
+//-----------------------------------------//
+//  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));
+};
+
+
+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))); // FIXME: scoping error
+};
+
+MutexWrapper sls_mw;
+
+void sls_fun_0() {
+  sls_mw.mu.Lock();
+  // sls_mw.x = 5; // FIXME: turn mu into sls_mw.mu
+  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_bad_1() {
+  sls_mu.Unlock(); // \
+    expected-warning {{unlocking 'sls_mu' that was not acquired}}
+}
+
+void sls_fun_bad_2() {
+  sls_mu.Lock();
+  sls_mu.Lock(); // \
+    expected-warning {{locking 'sls_mu' that is already acquired}}
+  sls_mu.Unlock();
+}
+
+void sls_fun_bad_3() {
+  sls_mu.Lock(); // \
+    expected-warning {{lock 'sls_mu' is not released at the end of function 'sls_fun_bad_3'}}
+}
+
+void sls_fun_bad_4() {
+  if (getBool())
+    sls_mu.Lock(); // \
+      expected-warning {{lock 'sls_mu' is not released at the end of its scope}}
+  else
+    sls_mu2.Lock(); // \
+      expected-warning {{lock 'sls_mu2' is not released at the end of its scope}}
+}
+
+void sls_fun_bad_5() {
+  sls_mu.Lock(); // \
+    expected-warning {{lock 'sls_mu' is not released at the end of its scope}}
+  if (getBool())
+    sls_mu.Unlock();
+}
+
+void sls_fun_bad_6() {
+  if (getBool()) {
+    sls_mu.Lock(); // \
+      expected-warning {{lock 'sls_mu' is not released at the end of its scope}}
+  } else {
+    if (getBool()) {
+      getBool(); // EMPTY
+    } else {
+      getBool(); // EMPTY
+    }
+  }
+  sls_mu.Unlock(); // \
+    expected-warning {{unlocking 'sls_mu' that was not acquired}}
+}
+
+void sls_fun_bad_7() {
+  sls_mu.Lock();
+  while (getBool()) { // \
+      expected-warning {{expecting lock 'sls_mu' to be held at start of each loop}}
+    sls_mu.Unlock();
+    if (getBool()) {
+      if (getBool()) {
+        continue;
+      }
+    }
+    sls_mu.Lock(); // \
+      expected-warning {{lock 'sls_mu' is not released at the end of its scope}}
+  }
+  sls_mu.Unlock();
+}
+
+void sls_fun_bad_8() {
+  sls_mu.Lock();
+  do {
+    sls_mu.Unlock();  // \
+      expected-warning {{expecting lock 'sls_mu' to be held at start of each loop}}
+  } while (getBool());
+}
+
+void sls_fun_bad_9() {
+  do {
+    sls_mu.Lock(); // \
+      expected-warning {{lock 'sls_mu' is not released at the end of its scope}}
+  } while (getBool());
+  sls_mu.Unlock();
+}
+
+void sls_fun_bad_10() {
+  sls_mu.Lock(); // \
+    expected-warning {{lock 'sls_mu' is not released at the end of function 'sls_fun_bad_10'}}
+  while(getBool()) { // \
+      expected-warning {{expecting lock 'sls_mu' to be held at start of each loop}}
+    sls_mu.Unlock();
+  }
+}
+
+void sls_fun_bad_11() {
+  while (getBool()) {
+    sls_mu.Lock(); // \
+      expected-warning {{lock 'sls_mu' is not released at the end of its scope}}
+  }
+  sls_mu.Unlock(); // \
+    expected-warning {{unlocking 'sls_mu' that was not acquired}}
+}
+
+
+//-----------------------------------------//
+// 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_elr_fun() __attribute__((exclusive_locks_required(aa_mu)));
+void aa_elr_fun() { }
+
+void aa_fun_1() {
+  glock.globalLock();
+  glock.globalUnlock();
+}
+
+void aa_fun_2() {
+  aa_mu.Lock();
+  aa_elr_fun();
+  aa_mu.Unlock();
+}
+
+void aa_fun_bad_1() {
+  glock.globalUnlock(); // \
+    expected-warning {{unlocking 'aa_mu' that was not acquired}}
+}
+
+void aa_fun_bad_2() {
+  glock.globalLock();
+  glock.globalLock(); // \
+    expected-warning {{locking 'aa_mu' that is already acquired}}
+  glock.globalUnlock();
+}
+
+void aa_fun_bad_3() {
+  glock.globalLock(); // \
+    expected-warning {{lock 'aa_mu' is not released at the end of function 'aa_fun_bad_3'}}
+}

Copied: cfe/trunk/test/SemaCXX/warn-thread-safety-parsing.cpp (from r138343, cfe/trunk/test/SemaCXX/warn-thread-safety.cpp)
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-thread-safety-parsing.cpp?p2=cfe/trunk/test/SemaCXX/warn-thread-safety-parsing.cpp&p1=cfe/trunk/test/SemaCXX/warn-thread-safety.cpp&r1=138343&r2=138350&rev=138350&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-thread-safety.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-thread-safety-parsing.cpp Tue Aug 23 13:46:34 2011
@@ -51,9 +51,9 @@
 #error "Should support no_thread_safety_analysis attribute"
 #endif
 
-void noanal_function() __attribute__((no_thread_safety_analysis));
+void noanal_fun() __attribute__((no_thread_safety_analysis));
 
-void noanal_function() __attribute__((no_thread_safety_analysis(1))); // \
+void noanal_fun_args() __attribute__((no_thread_safety_analysis(1))); // \
   expected-error {{attribute takes no arguments}}
 
 int noanal_testfn(int y) __attribute__((no_thread_safety_analysis));

Removed: cfe/trunk/test/SemaCXX/warn-thread-safety.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-thread-safety.cpp?rev=138349&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-thread-safety.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-thread-safety.cpp (removed)
@@ -1,1141 +0,0 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety %s
-
-
-//-----------------------------------------//
-//  Helper fields
-//-----------------------------------------//
-
-class __attribute__((lockable)) Mu {
-};
-
-class UnlockableMu{
-};
-
-class MuWrapper {
-  public:
-  Mu mu;
-  Mu getMu() {
-    return mu;
-  }
-  Mu * getMuPointer() {
-    return μ
-  }
-};
-
-
-class MuDoubleWrapper {
-  public:
-  MuWrapper* muWrapper;
-  MuWrapper* getWrapper() {
-    return muWrapper;
-  }
-};
-
-Mu mu1;
-UnlockableMu umu;
-Mu mu2;
-MuWrapper muWrapper;
-MuDoubleWrapper muDoubleWrapper;
-Mu* muPointer;
-Mu ** muDoublePointer = & muPointer;
-Mu& muRef = mu1;
-
-//-----------------------------------------//
-//   No Thread Safety Analysis (noanal)    //
-//-----------------------------------------//
-
-// FIXME: Right now we cannot parse attributes put on function definitions
-// We would like to patch this at some point.
-
-#if !__has_attribute(no_thread_safety_analysis)
-#error "Should support no_thread_safety_analysis attribute"
-#endif
-
-void noanal_function() __attribute__((no_thread_safety_analysis));
-
-void noanal_function() __attribute__((no_thread_safety_analysis(1))); // \
-  expected-error {{attribute takes no arguments}}
-
-int noanal_testfn(int y) __attribute__((no_thread_safety_analysis));
-
-int noanal_testfn(int y) {
-  int x __attribute__((no_thread_safety_analysis)) = y; // \
-    expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
-  return x;
-};
-
-int noanal_test_var __attribute__((no_thread_safety_analysis)); // \
-  expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
-
-class NoanalFoo {
- private:
-  int test_field __attribute__((no_thread_safety_analysis)); // \
-    expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
-  void test_method() __attribute__((no_thread_safety_analysis));
-};
-
-class __attribute__((no_thread_safety_analysis)) NoanalTestClass { // \
-    expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
-};
-
-void noanal_fun_params(int lvar __attribute__((no_thread_safety_analysis))); // \
-  expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
-
-
-//-----------------------------------------//
-//  Guarded Var Attribute (gv)
-//-----------------------------------------//
-
-#if !__has_attribute(guarded_var)
-#error "Should support guarded_var attribute"
-#endif
-
-int gv_var_noargs __attribute__((guarded_var));
-
-int gv_var_args __attribute__((guarded_var(1))); // \
-    expected-error {{attribute takes no arguments}}
-
-class GVFoo {
- private:
-  int gv_field_noargs __attribute__((guarded_var));
-  int gv_field_args __attribute__((guarded_var(1))); // \
-      expected-error {{attribute takes no arguments}}
-};
-
-class __attribute__((guarded_var)) GV { // \
-      expected-warning {{'guarded_var' attribute only applies to fields and global variables}}
-};
-
-void gv_function() __attribute__((guarded_var)); // \
-    expected-warning {{'guarded_var' attribute only applies to fields and global variables}}
-
-void gv_function_params(int gv_lvar __attribute__((guarded_var))); // \
-    expected-warning {{'guarded_var' attribute only applies to fields and global variables}}
-
-int gv_testfn(int y){
-  int x __attribute__((guarded_var)) = y; // \
-      expected-warning {{'guarded_var' attribute only applies to fields and global variables}}
-  return x;
-}
-
-//-----------------------------------------//
-//   Pt Guarded Var Attribute (pgv)
-//-----------------------------------------//
-
-//FIXME: add support for boost::scoped_ptr<int> fancyptr  and references
-
-#if !__has_attribute(pt_guarded_var)
-#error "Should support pt_guarded_var attribute"
-#endif
-
-int *pgv_pt_var_noargs __attribute__((pt_guarded_var));
-
-int pgv_var_noargs __attribute__((pt_guarded_var)); // \
-    expected-warning {{'pt_guarded_var' only applies to pointer types; type here is 'int'}}
-
-class PGVFoo {
- private:
-  int *pt_field_noargs __attribute__((pt_guarded_var));
-  int field_noargs __attribute__((pt_guarded_var)); // \
-    expected-warning {{'pt_guarded_var' only applies to pointer types; type here is 'int'}}
-  int *gv_field_args __attribute__((pt_guarded_var(1))); // \
-    expected-error {{attribute takes no arguments}}
-};
-
-class __attribute__((pt_guarded_var)) PGV { // \
-    expected-warning {{'pt_guarded_var' attribute only applies to fields and global variables}}
-};
-
-int *pgv_var_args __attribute__((pt_guarded_var(1))); // \
-  expected-error {{attribute takes no arguments}}
-
-
-void pgv_function() __attribute__((pt_guarded_var)); // \
-  expected-warning {{'pt_guarded_var' attribute only applies to fields and global variables}}
-
-void pgv_function_params(int *gv_lvar __attribute__((pt_guarded_var))); // \
-  expected-warning {{'pt_guarded_var' attribute only applies to fields and global variables}}
-
-void pgv_testfn(int y){
-  int *x __attribute__((pt_guarded_var)) = new int(0); // \
-    expected-warning {{'pt_guarded_var' attribute only applies to fields and global variables}}
-  delete x;
-}
-
-//-----------------------------------------//
-//  Lockable Attribute (l)
-//-----------------------------------------//
-
-//FIXME: In future we may want to add support for structs, ObjC classes, etc.
-
-#if !__has_attribute(lockable)
-#error "Should support lockable attribute"
-#endif
-
-class __attribute__((lockable)) LTestClass {
-};
-
-class __attribute__((lockable (1))) LTestClass_args { // \
-    expected-error {{attribute takes no arguments}}
-};
-
-void l_test_function() __attribute__((lockable));  // \
-  expected-warning {{'lockable' attribute only applies to classes}}
-
-int l_testfn(int y) {
-  int x __attribute__((lockable)) = y; // \
-    expected-warning {{'lockable' attribute only applies to classes}}
-  return x;
-}
-
-int l_test_var __attribute__((lockable)); // \
-  expected-warning {{'lockable' attribute only applies to classes}}
-
-class LFoo {
- private:
-  int test_field __attribute__((lockable)); // \
-    expected-warning {{'lockable' attribute only applies to classes}}
-  void test_method() __attribute__((lockable)); // \
-    expected-warning {{'lockable' attribute only applies to classes}}
-};
-
-
-void l_function_params(int lvar __attribute__((lockable))); // \
-  expected-warning {{'lockable' attribute only applies to classes}}
-
-
-//-----------------------------------------//
-//  Scoped Lockable Attribute (sl)
-//-----------------------------------------//
-
-#if !__has_attribute(scoped_lockable)
-#error "Should support scoped_lockable attribute"
-#endif
-
-class __attribute__((scoped_lockable)) SLTestClass {
-};
-
-class __attribute__((scoped_lockable (1))) SLTestClass_args { // \
-    expected-error {{attribute takes no arguments}}
-};
-
-void sl_test_function() __attribute__((scoped_lockable));  // \
-  expected-warning {{'scoped_lockable' attribute only applies to classes}}
-
-int sl_testfn(int y) {
-  int x __attribute__((scoped_lockable)) = y; // \
-    expected-warning {{'scoped_lockable' attribute only applies to classes}}
-  return x;
-}
-
-int sl_test_var __attribute__((scoped_lockable)); // \
-  expected-warning {{'scoped_lockable' attribute only applies to classes}}
-
-class SLFoo {
- private:
-  int test_field __attribute__((scoped_lockable)); // \
-    expected-warning {{'scoped_lockable' attribute only applies to classes}}
-  void test_method() __attribute__((scoped_lockable)); // \
-    expected-warning {{'scoped_lockable' attribute only applies to classes}}
-};
-
-
-void sl_function_params(int lvar __attribute__((scoped_lockable))); // \
-  expected-warning {{'scoped_lockable' attribute only applies to classes}}
-
-
-//-----------------------------------------//
-//  Guarded By Attribute (gb)
-//-----------------------------------------//
-
-// FIXME: Eventually, would we like this attribute to take more than 1 arg?
-
-#if !__has_attribute(guarded_by)
-#error "Should support guarded_by attribute"
-#endif
-
-//1. Check applied to the right types & argument number
-
-int gb_var_arg __attribute__((guarded_by(mu1)));
-
-int gb_var_args __attribute__((guarded_by(mu1, mu2))); // \
-  expected-error {{attribute takes one argument}}
-
-int gb_var_noargs __attribute__((guarded_by)); // \
-  expected-error {{attribute takes one argument}}
-
-class GBFoo {
- private:
-  int gb_field_noargs __attribute__((guarded_by)); // \
-    expected-error {{attribute takes one argument}}
-  int gb_field_args __attribute__((guarded_by(mu1)));
-};
-
-class __attribute__((guarded_by(mu1))) GB { // \
-      expected-warning {{'guarded_by' attribute only applies to fields and global variables}}
-};
-
-void gb_function() __attribute__((guarded_by(mu1))); // \
-    expected-warning {{'guarded_by' attribute only applies to fields and global variables}}
-
-void gb_function_params(int gv_lvar __attribute__((guarded_by(mu1)))); // \
-    expected-warning {{'guarded_by' attribute only applies to fields and global variables}}
-
-int gb_testfn(int y){
-  int x __attribute__((guarded_by(mu1))) = y; // \
-      expected-warning {{'guarded_by' attribute only applies to fields and global variables}}
-  return x;
-}
-
-//2. Check argument parsing.
-
-// legal attribute arguments
-int gb_var_arg_1 __attribute__((guarded_by(muWrapper.mu)));
-int gb_var_arg_2 __attribute__((guarded_by(muDoubleWrapper.muWrapper->mu)));
-int gb_var_arg_3 __attribute__((guarded_by(muWrapper.getMu())));
-int gb_var_arg_4 __attribute__((guarded_by(*muWrapper.getMuPointer())));
-int gb_var_arg_5 __attribute__((guarded_by(&mu1)));
-int gb_var_arg_6 __attribute__((guarded_by(muRef)));
-int gb_var_arg_7 __attribute__((guarded_by(muDoubleWrapper.getWrapper()->getMu())));
-int gb_var_arg_8 __attribute__((guarded_by(muPointer)));
-
-
-// illegal attribute arguments
-int gb_var_arg_bad_1 __attribute__((guarded_by(1))); // \
-  expected-error {{'guarded_by' attribute requires arguments that are class type or point to class type}}
-int gb_var_arg_bad_2 __attribute__((guarded_by("mu"))); // \
-  expected-error {{'guarded_by' attribute requires arguments that are class type or point to class type}}
-int gb_var_arg_bad_3 __attribute__((guarded_by(muDoublePointer))); // \
-  expected-error {{'guarded_by' attribute requires arguments that are class type or point to class type}}
-int gb_var_arg_bad_4 __attribute__((guarded_by(umu))); // \
-  expected-error {{'guarded_by' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-//3.
-// Thread Safety analysis tests
-
-
-//-----------------------------------------//
-//  Pt Guarded By Attribute (pgb)
-//-----------------------------------------//
-
-#if !__has_attribute(pt_guarded_by)
-#error "Should support pt_guarded_by attribute"
-#endif
-
-//1. Check applied to the right types & argument number
-
-int *pgb_var_noargs __attribute__((pt_guarded_by)); // \
-  expected-error {{attribute takes one argument}}
-
-int *pgb_ptr_var_arg __attribute__((pt_guarded_by(mu1)));
-
-int *pgb_ptr_var_args __attribute__((guarded_by(mu1, mu2))); // \
-  expected-error {{attribute takes one argument}}
-
-int pgb_var_args __attribute__((pt_guarded_by(mu1))); // \
-    expected-warning {{'pt_guarded_by' only applies to pointer types; type here is 'int'}}
-
-class PGBFoo {
- private:
-  int *pgb_field_noargs __attribute__((pt_guarded_by)); // \
-    expected-error {{attribute takes one argument}}
-  int *pgb_field_args __attribute__((pt_guarded_by(mu1)));
-};
-
-class __attribute__((pt_guarded_by(mu1))) PGB { // \
-      expected-warning {{'pt_guarded_by' attribute only applies to fields and global variables}}
-};
-
-void pgb_function() __attribute__((pt_guarded_by(mu1))); // \
-    expected-warning {{'pt_guarded_by' attribute only applies to fields and global variables}}
-
-void pgb_function_params(int gv_lvar __attribute__((pt_guarded_by(mu1)))); // \
-    expected-warning {{'pt_guarded_by' attribute only applies to fields and global variables}}
-
-void pgb_testfn(int y){
-  int *x __attribute__((pt_guarded_by(mu1))) = new int(0); // \
-      expected-warning {{'pt_guarded_by' attribute only applies to fields and global variables}}
-  delete x;
-}
-
-//2. Check argument parsing.
-
-// legal attribute arguments
-int * pgb_var_arg_1 __attribute__((pt_guarded_by(muWrapper.mu)));
-int * pgb_var_arg_2 __attribute__((pt_guarded_by(muDoubleWrapper.muWrapper->mu)));
-int * pgb_var_arg_3 __attribute__((pt_guarded_by(muWrapper.getMu())));
-int * pgb_var_arg_4 __attribute__((pt_guarded_by(*muWrapper.getMuPointer())));
-int * pgb_var_arg_5 __attribute__((pt_guarded_by(&mu1)));
-int * pgb_var_arg_6 __attribute__((pt_guarded_by(muRef)));
-int * pgb_var_arg_7 __attribute__((pt_guarded_by(muDoubleWrapper.getWrapper()->getMu())));
-int * pgb_var_arg_8 __attribute__((pt_guarded_by(muPointer)));
-
-
-// illegal attribute arguments
-int * pgb_var_arg_bad_1 __attribute__((pt_guarded_by(1))); // \
-  expected-error {{'pt_guarded_by' attribute requires arguments that are class type or point to class type}}
-int * pgb_var_arg_bad_2 __attribute__((pt_guarded_by("mu"))); // \
-  expected-error {{'pt_guarded_by' attribute requires arguments that are class type or point to class type}}
-int * pgb_var_arg_bad_3 __attribute__((pt_guarded_by(muDoublePointer))); // \
-  expected-error {{'pt_guarded_by' attribute requires arguments that are class type or point to class type}}
-int * pgb_var_arg_bad_4 __attribute__((pt_guarded_by(umu))); // \
-  expected-error {{'pt_guarded_by' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-
-//-----------------------------------------//
-//  Acquired After (aa)
-//-----------------------------------------//
-
-// FIXME: Would we like this attribute to take more than 1 arg?
-
-#if !__has_attribute(acquired_after)
-#error "Should support acquired_after attribute"
-#endif
-
-Mu mu_aa __attribute__((acquired_after(mu1)));
-
-Mu aa_var_noargs __attribute__((acquired_after)); // \
-  expected-error {{attribute takes at least 1 argument}}
-
-class AAFoo {
- private:
-  Mu aa_field_noargs __attribute__((acquired_after)); // \
-    expected-error {{attribute takes at least 1 argument}}
-  Mu aa_field_args __attribute__((acquired_after(mu1)));
-};
-
-class __attribute__((acquired_after(mu1))) AA { // \
-      expected-warning {{'acquired_after' attribute only applies to fields and global variables}}
-};
-
-void aa_function() __attribute__((acquired_after(mu1))); // \
-    expected-warning {{'acquired_after' attribute only applies to fields and global variables}}
-
-void aa_function_params(int gv_lvar __attribute__((acquired_after(mu1)))); // \
-    expected-warning {{'acquired_after' attribute only applies to fields and global variables}}
-
-void aa_testfn(int y){
-  Mu x __attribute__((acquired_after(mu1))) = Mu(); // \
-      expected-warning {{'acquired_after' attribute only applies to fields and global variables}}
-}
-
-//Check argument parsing.
-
-// legal attribute arguments
-Mu aa_var_arg_1 __attribute__((acquired_after(muWrapper.mu)));
-Mu aa_var_arg_2 __attribute__((acquired_after(muDoubleWrapper.muWrapper->mu)));
-Mu aa_var_arg_3 __attribute__((acquired_after(muWrapper.getMu())));
-Mu aa_var_arg_4 __attribute__((acquired_after(*muWrapper.getMuPointer())));
-Mu aa_var_arg_5 __attribute__((acquired_after(&mu1)));
-Mu aa_var_arg_6 __attribute__((acquired_after(muRef)));
-Mu aa_var_arg_7 __attribute__((acquired_after(muDoubleWrapper.getWrapper()->getMu())));
-Mu aa_var_arg_8 __attribute__((acquired_after(muPointer)));
-
-
-// illegal attribute arguments
-Mu aa_var_arg_bad_1 __attribute__((acquired_after(1))); // \
-  expected-error {{'acquired_after' attribute requires arguments that are class type or point to class type}}
-Mu aa_var_arg_bad_2 __attribute__((acquired_after("mu"))); // \
-  expected-error {{'acquired_after' attribute requires arguments that are class type or point to class type}}
-Mu aa_var_arg_bad_3 __attribute__((acquired_after(muDoublePointer))); // \
-  expected-error {{'acquired_after' attribute requires arguments that are class type or point to class type}}
-Mu aa_var_arg_bad_4 __attribute__((acquired_after(umu))); // \
-  expected-error {{'acquired_after' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-UnlockableMu aa_var_arg_bad_5 __attribute__((acquired_after(mu_aa))); // \
-  expected-error {{'acquired_after' attribute can only be applied in a context annotated with 'lockable' attribute}}
-
-//-----------------------------------------//
-//  Acquired Before (ab)
-//-----------------------------------------//
-
-#if !__has_attribute(acquired_before)
-#error "Should support acquired_before attribute"
-#endif
-
-Mu mu_ab __attribute__((acquired_before(mu1)));
-
-Mu ab_var_noargs __attribute__((acquired_before)); // \
-  expected-error {{attribute takes at least 1 argument}}
-
-class ABFoo {
- private:
-  Mu ab_field_noargs __attribute__((acquired_before)); // \
-    expected-error {{attribute takes at least 1 argument}}
-  Mu ab_field_args __attribute__((acquired_before(mu1)));
-};
-
-class __attribute__((acquired_before(mu1))) AB { // \
-      expected-warning {{'acquired_before' attribute only applies to fields and global variables}}
-};
-
-void ab_function() __attribute__((acquired_before(mu1))); // \
-    expected-warning {{'acquired_before' attribute only applies to fields and global variables}}
-
-void ab_function_params(int gv_lvar __attribute__((acquired_before(mu1)))); // \
-    expected-warning {{'acquired_before' attribute only applies to fields and global variables}}
-
-void ab_testfn(int y){
-  Mu x __attribute__((acquired_before(mu1))) = Mu(); // \
-      expected-warning {{'acquired_before' attribute only applies to fields and global variables}}
-}
-
-// Note: illegal int ab_int __attribute__((acquired_before(mu1))) will
-// be taken care of by warnings that ab__int is not lockable.
-
-//Check argument parsing.
-
-// legal attribute arguments
-Mu ab_var_arg_1 __attribute__((acquired_before(muWrapper.mu)));
-Mu ab_var_arg_2 __attribute__((acquired_before(muDoubleWrapper.muWrapper->mu)));
-Mu ab_var_arg_3 __attribute__((acquired_before(muWrapper.getMu())));
-Mu ab_var_arg_4 __attribute__((acquired_before(*muWrapper.getMuPointer())));
-Mu ab_var_arg_5 __attribute__((acquired_before(&mu1)));
-Mu ab_var_arg_6 __attribute__((acquired_before(muRef)));
-Mu ab_var_arg_7 __attribute__((acquired_before(muDoubleWrapper.getWrapper()->getMu())));
-Mu ab_var_arg_8 __attribute__((acquired_before(muPointer)));
-
-
-// illegal attribute arguments
-Mu ab_var_arg_bad_1 __attribute__((acquired_before(1))); // \
-  expected-error {{'acquired_before' attribute requires arguments that are class type or point to class type}}
-Mu ab_var_arg_bad_2 __attribute__((acquired_before("mu"))); // \
-  expected-error {{'acquired_before' attribute requires arguments that are class type or point to class type}}
-Mu ab_var_arg_bad_3 __attribute__((acquired_before(muDoublePointer))); // \
-  expected-error {{'acquired_before' attribute requires arguments that are class type or point to class type}}
-Mu ab_var_arg_bad_4 __attribute__((acquired_before(umu))); // \
-  expected-error {{'acquired_before' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-UnlockableMu ab_var_arg_bad_5 __attribute__((acquired_before(mu_ab))); // \
-  expected-error {{'acquired_before' attribute can only be applied in a context annotated with 'lockable' attribute}}
-
-
-//-----------------------------------------//
-//  Exclusive Lock Function (elf)
-//-----------------------------------------//
-
-#if !__has_attribute(exclusive_lock_function)
-#error "Should support exclusive_lock_function attribute"
-#endif
-
-// takes zero or more arguments, all locks (vars/fields)
-
-void elf_function() __attribute__((exclusive_lock_function));
-
-void elf_function_args() __attribute__((exclusive_lock_function(mu1, mu2)));
-
-int elf_testfn(int y) __attribute__((exclusive_lock_function));
-
-int elf_testfn(int y) {
-  int x __attribute__((exclusive_lock_function)) = y; // \
-    expected-warning {{'exclusive_lock_function' attribute only applies to functions and methods}}
-  return x;
-};
-
-int elf_test_var __attribute__((exclusive_lock_function)); // \
-  expected-warning {{'exclusive_lock_function' attribute only applies to functions and methods}}
-
-class ElfFoo {
- private:
-  int test_field __attribute__((exclusive_lock_function)); // \
-    expected-warning {{'exclusive_lock_function' attribute only applies to functions and methods}}
-  void test_method() __attribute__((exclusive_lock_function));
-};
-
-class __attribute__((exclusive_lock_function)) ElfTestClass { // \
-    expected-warning {{'exclusive_lock_function' attribute only applies to functions and methods}}
-};
-
-void elf_fun_params(int lvar __attribute__((exclusive_lock_function))); // \
-  expected-warning {{'exclusive_lock_function' attribute only applies to functions and methods}}
-
-// Check argument parsing.
-
-// legal attribute arguments
-int elf_function_1() __attribute__((exclusive_lock_function(muWrapper.mu)));
-int elf_function_2() __attribute__((exclusive_lock_function(muDoubleWrapper.muWrapper->mu)));
-int elf_function_3() __attribute__((exclusive_lock_function(muWrapper.getMu())));
-int elf_function_4() __attribute__((exclusive_lock_function(*muWrapper.getMuPointer())));
-int elf_function_5() __attribute__((exclusive_lock_function(&mu1)));
-int elf_function_6() __attribute__((exclusive_lock_function(muRef)));
-int elf_function_7() __attribute__((exclusive_lock_function(muDoubleWrapper.getWrapper()->getMu())));
-int elf_function_8() __attribute__((exclusive_lock_function(muPointer)));
-int elf_function_9(Mu x) __attribute__((exclusive_lock_function(1)));
-int elf_function_9(Mu x, Mu y) __attribute__((exclusive_lock_function(1,2)));
-
-
-// illegal attribute arguments
-int elf_function_bad_2() __attribute__((exclusive_lock_function("mu"))); // \
-  expected-error {{'exclusive_lock_function' attribute requires arguments that are class type or point to class type}}
-int elf_function_bad_3() __attribute__((exclusive_lock_function(muDoublePointer))); // \
-  expected-error {{'exclusive_lock_function' attribute requires arguments that are class type or point to class type}}
-int elf_function_bad_4() __attribute__((exclusive_lock_function(umu))); // \
-  expected-error {{'exclusive_lock_function' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-int elf_function_bad_1() __attribute__((exclusive_lock_function(1))); // \
-  expected-error {{'exclusive_lock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
-int elf_function_bad_5(Mu x) __attribute__((exclusive_lock_function(0))); // \
-  expected-error {{'exclusive_lock_function' attribute parameter 1 is out of bounds: can only be 1, since there is one parameter}}
-int elf_function_bad_6(Mu x, Mu y) __attribute__((exclusive_lock_function(0))); // \
-  expected-error {{'exclusive_lock_function' attribute parameter 1 is out of bounds: must be between 1 and 2}}
-int elf_function_bad_7() __attribute__((exclusive_lock_function(0))); // \
-  expected-error {{'exclusive_lock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
-
-
-//-----------------------------------------//
-//  Shared Lock Function (slf)
-//-----------------------------------------//
-
-#if !__has_attribute(shared_lock_function)
-#error "Should support shared_lock_function attribute"
-#endif
-
-// takes zero or more arguments, all locks (vars/fields)
-
-void slf_function() __attribute__((shared_lock_function));
-
-void slf_function_args() __attribute__((shared_lock_function(mu1, mu2)));
-
-int slf_testfn(int y) __attribute__((shared_lock_function));
-
-int slf_testfn(int y) {
-  int x __attribute__((shared_lock_function)) = y; // \
-    expected-warning {{'shared_lock_function' attribute only applies to functions and methods}}
-  return x;
-};
-
-int slf_test_var __attribute__((shared_lock_function)); // \
-  expected-warning {{'shared_lock_function' attribute only applies to functions and methods}}
-
-void slf_fun_params(int lvar __attribute__((shared_lock_function))); // \
-  expected-warning {{'shared_lock_function' attribute only applies to functions and methods}}
-
-class SlfFoo {
- private:
-  int test_field __attribute__((shared_lock_function)); // \
-    expected-warning {{'shared_lock_function' attribute only applies to functions and methods}}
-  void test_method() __attribute__((shared_lock_function));
-};
-
-class __attribute__((shared_lock_function)) SlfTestClass { // \
-    expected-warning {{'shared_lock_function' attribute only applies to functions and methods}}
-};
-
-// Check argument parsing.
-
-// legal attribute arguments
-int slf_function_1() __attribute__((shared_lock_function(muWrapper.mu)));
-int slf_function_2() __attribute__((shared_lock_function(muDoubleWrapper.muWrapper->mu)));
-int slf_function_3() __attribute__((shared_lock_function(muWrapper.getMu())));
-int slf_function_4() __attribute__((shared_lock_function(*muWrapper.getMuPointer())));
-int slf_function_5() __attribute__((shared_lock_function(&mu1)));
-int slf_function_6() __attribute__((shared_lock_function(muRef)));
-int slf_function_7() __attribute__((shared_lock_function(muDoubleWrapper.getWrapper()->getMu())));
-int slf_function_8() __attribute__((shared_lock_function(muPointer)));
-int slf_function_9(Mu x) __attribute__((shared_lock_function(1)));
-int slf_function_9(Mu x, Mu y) __attribute__((shared_lock_function(1,2)));
-
-
-// illegal attribute arguments
-int slf_function_bad_2() __attribute__((shared_lock_function("mu"))); // \
-  expected-error {{'shared_lock_function' attribute requires arguments that are class type or point to class type}}
-int slf_function_bad_3() __attribute__((shared_lock_function(muDoublePointer))); // \
-  expected-error {{'shared_lock_function' attribute requires arguments that are class type or point to class type}}
-int slf_function_bad_4() __attribute__((shared_lock_function(umu))); // \
-  expected-error {{'shared_lock_function' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-int slf_function_bad_1() __attribute__((shared_lock_function(1))); // \
-  expected-error {{'shared_lock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
-int slf_function_bad_5(Mu x) __attribute__((shared_lock_function(0))); // \
-  expected-error {{'shared_lock_function' attribute parameter 1 is out of bounds: can only be 1, since there is one parameter}}
-int slf_function_bad_6(Mu x, Mu y) __attribute__((shared_lock_function(0))); // \
-  expected-error {{'shared_lock_function' attribute parameter 1 is out of bounds: must be between 1 and 2}}
-int slf_function_bad_7() __attribute__((shared_lock_function(0))); // \
-  expected-error {{'shared_lock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
-
-
-//-----------------------------------------//
-//  Exclusive TryLock Function (etf)
-//-----------------------------------------//
-
-#if !__has_attribute(exclusive_trylock_function)
-#error "Should support exclusive_trylock_function attribute"
-#endif
-
-// takes a mandatory boolean or integer argument specifying the retval
-// plus an optional list of locks (vars/fields)
-
-void etf_function() __attribute__((exclusive_trylock_function));  // \
-  expected-error {{attribute takes attribute takes at least 1 argument arguments}}
-
-void etf_function_args() __attribute__((exclusive_trylock_function(1, mu2)));
-
-void etf_function_arg() __attribute__((exclusive_trylock_function(1)));
-
-int etf_testfn(int y) __attribute__((exclusive_trylock_function(1)));
-
-int etf_testfn(int y) {
-  int x __attribute__((exclusive_trylock_function(1))) = y; // \
-    expected-warning {{'exclusive_trylock_function' attribute only applies to functions and methods}}
-  return x;
-};
-
-int etf_test_var __attribute__((exclusive_trylock_function(1))); // \
-  expected-warning {{'exclusive_trylock_function' attribute only applies to functions and methods}}
-
-class EtfFoo {
- private:
-  int test_field __attribute__((exclusive_trylock_function(1))); // \
-    expected-warning {{'exclusive_trylock_function' attribute only applies to functions and methods}}
-  void test_method() __attribute__((exclusive_trylock_function(1)));
-};
-
-class __attribute__((exclusive_trylock_function(1))) EtfTestClass { // \
-    expected-warning {{'exclusive_trylock_function' attribute only applies to functions and methods}}
-};
-
-void etf_fun_params(int lvar __attribute__((exclusive_trylock_function(1)))); // \
-  expected-warning {{'exclusive_trylock_function' attribute only applies to functions and methods}}
-
-// Check argument parsing.
-
-// legal attribute arguments
-int etf_function_1() __attribute__((exclusive_trylock_function(1, muWrapper.mu)));
-int etf_function_2() __attribute__((exclusive_trylock_function(1, muDoubleWrapper.muWrapper->mu)));
-int etf_function_3() __attribute__((exclusive_trylock_function(1, muWrapper.getMu())));
-int etf_function_4() __attribute__((exclusive_trylock_function(1, *muWrapper.getMuPointer())));
-int etf_function_5() __attribute__((exclusive_trylock_function(1, &mu1)));
-int etf_function_6() __attribute__((exclusive_trylock_function(1, muRef)));
-int etf_function_7() __attribute__((exclusive_trylock_function(1, muDoubleWrapper.getWrapper()->getMu())));
-int etf_functetfn_8() __attribute__((exclusive_trylock_function(1, muPointer)));
-int etf_function_9() __attribute__((exclusive_trylock_function(true)));
-
-
-// illegal attribute arguments
-int etf_function_bad_1() __attribute__((exclusive_trylock_function(mu1))); // \
-  expected-error {{'exclusive_trylock_function' attribute first argument must be of int or bool type}}
-int etf_function_bad_2() __attribute__((exclusive_trylock_function("mu"))); // \
-  expected-error {{'exclusive_trylock_function' attribute first argument must be of int or bool type}}
-int etf_function_bad_3() __attribute__((exclusive_trylock_function(muDoublePointer))); // \
-  expected-error {{'exclusive_trylock_function' attribute first argument must be of int or bool type}}
-
-int etf_function_bad_4() __attribute__((exclusive_trylock_function(1, "mu"))); // \
-  expected-error {{'exclusive_trylock_function' attribute requires arguments that are class type or point to class type}}
-int etf_function_bad_5() __attribute__((exclusive_trylock_function(1, muDoublePointer))); // \
-  expected-error {{'exclusive_trylock_function' attribute requires arguments that are class type or point to class type}}
-int etf_function_bad_6() __attribute__((exclusive_trylock_function(1, umu))); // \
-  expected-error {{'exclusive_trylock_function' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-
-//-----------------------------------------//
-//  Shared TryLock Function (stf)
-//-----------------------------------------//
-
-#if !__has_attribute(shared_trylock_function)
-#error "Should support shared_trylock_function attribute"
-#endif
-
-// takes a mandatory boolean or integer argument specifying the retval
-// plus an optional list of locks (vars/fields)
-
-void stf_function() __attribute__((shared_trylock_function));  // \
-  expected-error {{attribute takes at least 1 argument}}
-
-void stf_function_args() __attribute__((shared_trylock_function(1, mu2)));
-
-void stf_function_arg() __attribute__((shared_trylock_function(1)));
-
-int stf_testfn(int y) __attribute__((shared_trylock_function(1)));
-
-int stf_testfn(int y) {
-  int x __attribute__((shared_trylock_function(1))) = y; // \
-    expected-warning {{'shared_trylock_function' attribute only applies to functions and methods}}
-  return x;
-};
-
-int stf_test_var __attribute__((shared_trylock_function(1))); // \
-  expected-warning {{'shared_trylock_function' attribute only applies to functions and methods}}
-
-void stf_fun_params(int lvar __attribute__((shared_trylock_function(1)))); // \
-  expected-warning {{'shared_trylock_function' attribute only applies to functions and methods}}
-
-
-class StfFoo {
- private:
-  int test_field __attribute__((shared_trylock_function(1))); // \
-    expected-warning {{'shared_trylock_function' attribute only applies to functions and methods}}
-  void test_method() __attribute__((shared_trylock_function(1)));
-};
-
-class __attribute__((shared_trylock_function(1))) StfTestClass { // \
-    expected-warning {{'shared_trylock_function' attribute only applies to functions and methods}}
-};
-
-// Check argument parsing.
-
-// legal attribute arguments
-int stf_function_1() __attribute__((shared_trylock_function(1, muWrapper.mu)));
-int stf_function_2() __attribute__((shared_trylock_function(1, muDoubleWrapper.muWrapper->mu)));
-int stf_function_3() __attribute__((shared_trylock_function(1, muWrapper.getMu())));
-int stf_function_4() __attribute__((shared_trylock_function(1, *muWrapper.getMuPointer())));
-int stf_function_5() __attribute__((shared_trylock_function(1, &mu1)));
-int stf_function_6() __attribute__((shared_trylock_function(1, muRef)));
-int stf_function_7() __attribute__((shared_trylock_function(1, muDoubleWrapper.getWrapper()->getMu())));
-int stf_function_8() __attribute__((shared_trylock_function(1, muPointer)));
-int stf_function_9() __attribute__((shared_trylock_function(true)));
-
-
-// illegal attribute arguments
-int stf_function_bad_1() __attribute__((shared_trylock_function(mu1))); // \
-  expected-error {{'shared_trylock_function' attribute first argument must be of int or bool type}}
-int stf_function_bad_2() __attribute__((shared_trylock_function("mu"))); // \
-  expected-error {{'shared_trylock_function' attribute first argument must be of int or bool type}}
-int stf_function_bad_3() __attribute__((shared_trylock_function(muDoublePointer))); // \
-  expected-error {{'shared_trylock_function' attribute first argument must be of int or bool type}}
-
-int stf_function_bad_4() __attribute__((shared_trylock_function(1, "mu"))); // \
-  expected-error {{'shared_trylock_function' attribute requires arguments that are class type or point to class type}}
-int stf_function_bad_5() __attribute__((shared_trylock_function(1, muDoublePointer))); // \
-  expected-error {{'shared_trylock_function' attribute requires arguments that are class type or point to class type}}
-int stf_function_bad_6() __attribute__((shared_trylock_function(1, umu))); // \
-  expected-error {{'shared_trylock_function' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-
-//-----------------------------------------//
-//  Unlock Function (uf)
-//-----------------------------------------//
-
-#if !__has_attribute(unlock_function)
-#error "Should support unlock_function attribute"
-#endif
-
-// takes zero or more arguments, all locks (vars/fields)
-
-void uf_function() __attribute__((unlock_function));
-
-void uf_function_args() __attribute__((unlock_function(mu1, mu2)));
-
-int uf_testfn(int y) __attribute__((unlock_function));
-
-int uf_testfn(int y) {
-  int x __attribute__((unlock_function)) = y; // \
-    expected-warning {{'unlock_function' attribute only applies to functions and methods}}
-  return x;
-};
-
-int uf_test_var __attribute__((unlock_function)); // \
-  expected-warning {{'unlock_function' attribute only applies to functions and methods}}
-
-class UfFoo {
- private:
-  int test_field __attribute__((unlock_function)); // \
-    expected-warning {{'unlock_function' attribute only applies to functions and methods}}
-  void test_method() __attribute__((unlock_function));
-};
-
-class __attribute__((no_thread_safety_analysis)) UfTestClass { // \
-    expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
-};
-
-void uf_fun_params(int lvar __attribute__((unlock_function))); // \
-  expected-warning {{'unlock_function' attribute only applies to functions and methods}}
-
-// Check argument parsing.
-
-// legal attribute arguments
-int uf_function_1() __attribute__((unlock_function(muWrapper.mu)));
-int uf_function_2() __attribute__((unlock_function(muDoubleWrapper.muWrapper->mu)));
-int uf_function_3() __attribute__((unlock_function(muWrapper.getMu())));
-int uf_function_4() __attribute__((unlock_function(*muWrapper.getMuPointer())));
-int uf_function_5() __attribute__((unlock_function(&mu1)));
-int uf_function_6() __attribute__((unlock_function(muRef)));
-int uf_function_7() __attribute__((unlock_function(muDoubleWrapper.getWrapper()->getMu())));
-int uf_function_8() __attribute__((unlock_function(muPointer)));
-int uf_function_9(Mu x) __attribute__((unlock_function(1)));
-int uf_function_9(Mu x, Mu y) __attribute__((unlock_function(1,2)));
-
-
-// illegal attribute arguments
-int uf_function_bad_2() __attribute__((unlock_function("mu"))); // \
-  expected-error {{'unlock_function' attribute requires arguments that are class type or point to class type}}
-int uf_function_bad_3() __attribute__((unlock_function(muDoublePointer))); // \
-expected-error {{'unlock_function' attribute requires arguments that are class type or point to class type}}
-int uf_function_bad_4() __attribute__((unlock_function(umu))); // \
-  expected-error {{'unlock_function' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-int uf_function_bad_1() __attribute__((unlock_function(1))); // \
-  expected-error {{'unlock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
-int uf_function_bad_5(Mu x) __attribute__((unlock_function(0))); // \
-  expected-error {{'unlock_function' attribute parameter 1 is out of bounds: can only be 1, since there is one parameter}}
-int uf_function_bad_6(Mu x, Mu y) __attribute__((unlock_function(0))); // \
-  expected-error {{'unlock_function' attribute parameter 1 is out of bounds: must be between 1 and 2}}
-int uf_function_bad_7() __attribute__((unlock_function(0))); // \
-  expected-error {{'unlock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
-
-
-//-----------------------------------------//
-//  Lock Returned (lr)
-//-----------------------------------------//
-
-#if !__has_attribute(lock_returned)
-#error "Should support lock_returned attribute"
-#endif
-
-// Takes exactly one argument, a var/field
-
-void lr_function() __attribute__((lock_returned)); // \
-  expected-error {{attribute takes one argument}}
-
-void lr_function_arg() __attribute__((lock_returned(mu1)));
-
-void lr_function_args() __attribute__((lock_returned(mu1, mu2))); // \
-  expected-error {{attribute takes one argument}}
-
-int lr_testfn(int y) __attribute__((lock_returned(mu1)));
-
-int lr_testfn(int y) {
-  int x __attribute__((lock_returned(mu1))) = y; // \
-    expected-warning {{'lock_returned' attribute only applies to functions and methods}}
-  return x;
-};
-
-int lr_test_var __attribute__((lock_returned(mu1))); // \
-  expected-warning {{'lock_returned' attribute only applies to functions and methods}}
-
-void lr_fun_params(int lvar __attribute__((lock_returned(mu1)))); // \
-  expected-warning {{'lock_returned' attribute only applies to functions and methods}}
-
-class LrFoo {
- private:
-  int test_field __attribute__((lock_returned(mu1))); // \
-    expected-warning {{'lock_returned' attribute only applies to functions and methods}}
-  void test_method() __attribute__((lock_returned(mu1)));
-};
-
-class __attribute__((lock_returned(mu1))) LrTestClass { // \
-    expected-warning {{'lock_returned' attribute only applies to functions and methods}}
-};
-
-// Check argument parsing.
-
-// legal attribute arguments
-int lr_function_1() __attribute__((lock_returned(muWrapper.mu)));
-int lr_function_2() __attribute__((lock_returned(muDoubleWrapper.muWrapper->mu)));
-int lr_function_3() __attribute__((lock_returned(muWrapper.getMu())));
-int lr_function_4() __attribute__((lock_returned(*muWrapper.getMuPointer())));
-int lr_function_5() __attribute__((lock_returned(&mu1)));
-int lr_function_6() __attribute__((lock_returned(muRef)));
-int lr_function_7() __attribute__((lock_returned(muDoubleWrapper.getWrapper()->getMu())));
-int lr_function_8() __attribute__((lock_returned(muPointer)));
-
-
-// illegal attribute arguments
-int lr_function_bad_1() __attribute__((lock_returned(1))); // \
-  expected-error {{'lock_returned' attribute requires arguments that are class type or point to class type}}
-int lr_function_bad_2() __attribute__((lock_returned("mu"))); // \
-  expected-error {{'lock_returned' attribute requires arguments that are class type or point to class type}}
-int lr_function_bad_3() __attribute__((lock_returned(muDoublePointer))); // \
-  expected-error {{'lock_returned' attribute requires arguments that are class type or point to class type}}
-int lr_function_bad_4() __attribute__((lock_returned(umu))); // \
-  expected-error {{'lock_returned' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-
-
-//-----------------------------------------//
-//  Locks Excluded (le)
-//-----------------------------------------//
-
-#if !__has_attribute(locks_excluded)
-#error "Should support locks_excluded attribute"
-#endif
-
-// takes one or more arguments, all locks (vars/fields)
-
-void le_function() __attribute__((locks_excluded)); // \
-  expected-error {{attribute takes at least 1 argument}}
-
-void le_function_arg() __attribute__((locks_excluded(mu1)));
-
-void le_function_args() __attribute__((locks_excluded(mu1, mu2)));
-
-int le_testfn(int y) __attribute__((locks_excluded(mu1)));
-
-int le_testfn(int y) {
-  int x __attribute__((locks_excluded(mu1))) = y; // \
-    expected-warning {{'locks_excluded' attribute only applies to functions and methods}}
-  return x;
-};
-
-int le_test_var __attribute__((locks_excluded(mu1))); // \
-  expected-warning {{'locks_excluded' attribute only applies to functions and methods}}
-
-void le_fun_params(int lvar __attribute__((locks_excluded(mu1)))); // \
-  expected-warning {{'locks_excluded' attribute only applies to functions and methods}}
-
-class LeFoo {
- private:
-  int test_field __attribute__((locks_excluded(mu1))); // \
-    expected-warning {{'locks_excluded' attribute only applies to functions and methods}}
-  void test_method() __attribute__((locks_excluded(mu1)));
-};
-
-class __attribute__((locks_excluded(mu1))) LeTestClass { // \
-    expected-warning {{'locks_excluded' attribute only applies to functions and methods}}
-};
-
-// Check argument parsing.
-
-// legal attribute arguments
-int le_function_1() __attribute__((locks_excluded(muWrapper.mu)));
-int le_function_2() __attribute__((locks_excluded(muDoubleWrapper.muWrapper->mu)));
-int le_function_3() __attribute__((locks_excluded(muWrapper.getMu())));
-int le_function_4() __attribute__((locks_excluded(*muWrapper.getMuPointer())));
-int le_function_5() __attribute__((locks_excluded(&mu1)));
-int le_function_6() __attribute__((locks_excluded(muRef)));
-int le_function_7() __attribute__((locks_excluded(muDoubleWrapper.getWrapper()->getMu())));
-int le_function_8() __attribute__((locks_excluded(muPointer)));
-
-
-// illegal attribute arguments
-int le_function_bad_1() __attribute__((locks_excluded(1))); // \
-  expected-error {{'locks_excluded' attribute requires arguments that are class type or point to class type}}
-int le_function_bad_2() __attribute__((locks_excluded("mu"))); // \
-  expected-error {{'locks_excluded' attribute requires arguments that are class type or point to class type}}
-int le_function_bad_3() __attribute__((locks_excluded(muDoublePointer))); // \
-  expected-error {{'locks_excluded' attribute requires arguments that are class type or point to class type}}
-int le_function_bad_4() __attribute__((locks_excluded(umu))); // \
-  expected-error {{'locks_excluded' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-
-
-//-----------------------------------------//
-//  Exclusive Locks Required (elr)
-//-----------------------------------------//
-
-#if !__has_attribute(exclusive_locks_required)
-#error "Should support exclusive_locks_required attribute"
-#endif
-
-// takes one or more arguments, all locks (vars/fields)
-
-void elr_function() __attribute__((exclusive_locks_required)); // \
-  expected-error {{attribute takes at least 1 argument}}
-
-void elr_function_arg() __attribute__((exclusive_locks_required(mu1)));
-
-void elr_function_args() __attribute__((exclusive_locks_required(mu1, mu2)));
-
-int elr_testfn(int y) __attribute__((exclusive_locks_required(mu1)));
-
-int elr_testfn(int y) {
-  int x __attribute__((exclusive_locks_required(mu1))) = y; // \
-    expected-warning {{'exclusive_locks_required' attribute only applies to functions and methods}}
-  return x;
-};
-
-int elr_test_var __attribute__((exclusive_locks_required(mu1))); // \
-  expected-warning {{'exclusive_locks_required' attribute only applies to functions and methods}}
-
-void elr_fun_params(int lvar __attribute__((exclusive_locks_required(mu1)))); // \
-  expected-warning {{'exclusive_locks_required' attribute only applies to functions and methods}}
-
-class ElrFoo {
- private:
-  int test_field __attribute__((exclusive_locks_required(mu1))); // \
-    expected-warning {{'exclusive_locks_required' attribute only applies to functions and methods}}
-  void test_method() __attribute__((exclusive_locks_required(mu1)));
-};
-
-class __attribute__((exclusive_locks_required(mu1))) ElrTestClass { // \
-    expected-warning {{'exclusive_locks_required' attribute only applies to functions and methods}}
-};
-
-// Check argument parsing.
-
-// legal attribute arguments
-int elr_function_1() __attribute__((exclusive_locks_required(muWrapper.mu)));
-int elr_function_2() __attribute__((exclusive_locks_required(muDoubleWrapper.muWrapper->mu)));
-int elr_function_3() __attribute__((exclusive_locks_required(muWrapper.getMu())));
-int elr_function_4() __attribute__((exclusive_locks_required(*muWrapper.getMuPointer())));
-int elr_function_5() __attribute__((exclusive_locks_required(&mu1)));
-int elr_function_6() __attribute__((exclusive_locks_required(muRef)));
-int elr_function_7() __attribute__((exclusive_locks_required(muDoubleWrapper.getWrapper()->getMu())));
-int elr_function_8() __attribute__((exclusive_locks_required(muPointer)));
-
-
-// illegal attribute arguments
-int elr_function_bad_1() __attribute__((exclusive_locks_required(1))); // \
-  expected-error {{'exclusive_locks_required' attribute requires arguments that are class type or point to class type}}
-int elr_function_bad_2() __attribute__((exclusive_locks_required("mu"))); // \
-  expected-error {{'exclusive_locks_required' attribute requires arguments that are class type or point to class type}}
-int elr_function_bad_3() __attribute__((exclusive_locks_required(muDoublePointer))); // \
-  expected-error {{'exclusive_locks_required' attribute requires arguments that are class type or point to class type}}
-int elr_function_bad_4() __attribute__((exclusive_locks_required(umu))); // \
-  expected-error {{'exclusive_locks_required' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-
-
-
-//-----------------------------------------//
-//  Shared Locks Required (slr)
-//-----------------------------------------//
-
-#if !__has_attribute(shared_locks_required)
-#error "Should support shared_locks_required attribute"
-#endif
-
-// takes one or more arguments, all locks (vars/fields)
-
-void slr_function() __attribute__((shared_locks_required)); // \
-  expected-error {{attribute takes at least 1 argument}}
-
-void slr_function_arg() __attribute__((shared_locks_required(mu1)));
-
-void slr_function_args() __attribute__((shared_locks_required(mu1, mu2)));
-
-int slr_testfn(int y) __attribute__((shared_locks_required(mu1)));
-
-int slr_testfn(int y) {
-  int x __attribute__((shared_locks_required(mu1))) = y; // \
-    expected-warning {{'shared_locks_required' attribute only applies to functions and methods}}
-  return x;
-};
-
-int slr_test_var __attribute__((shared_locks_required(mu1))); // \
-  expected-warning {{'shared_locks_required' attribute only applies to functions and methods}}
-
-void slr_fun_params(int lvar __attribute__((shared_locks_required(mu1)))); // \
-  expected-warning {{'shared_locks_required' attribute only applies to functions and methods}}
-
-class SlrFoo {
- private:
-  int test_field __attribute__((shared_locks_required(mu1))); // \
-    expected-warning {{'shared_locks_required' attribute only applies to functions and methods}}
-  void test_method() __attribute__((shared_locks_required(mu1)));
-};
-
-class __attribute__((shared_locks_required(mu1))) SlrTestClass { // \
-    expected-warning {{'shared_locks_required' attribute only applies to functions and methods}}
-};
-
-// Check argument parsing.
-
-// legal attribute arguments
-int slr_function_1() __attribute__((shared_locks_required(muWrapper.mu)));
-int slr_function_2() __attribute__((shared_locks_required(muDoubleWrapper.muWrapper->mu)));
-int slr_function_3() __attribute__((shared_locks_required(muWrapper.getMu())));
-int slr_function_4() __attribute__((shared_locks_required(*muWrapper.getMuPointer())));
-int slr_function_5() __attribute__((shared_locks_required(&mu1)));
-int slr_function_6() __attribute__((shared_locks_required(muRef)));
-int slr_function_7() __attribute__((shared_locks_required(muDoubleWrapper.getWrapper()->getMu())));
-int slr_function_8() __attribute__((shared_locks_required(muPointer)));
-
-
-// illegal attribute arguments
-int slr_function_bad_1() __attribute__((shared_locks_required(1))); // \
-  expected-error {{'shared_locks_required' attribute requires arguments that are class type or point to class type}}
-int slr_function_bad_2() __attribute__((shared_locks_required("mu"))); // \
-  expected-error {{'shared_locks_required' attribute requires arguments that are class type or point to class type}}
-int slr_function_bad_3() __attribute__((shared_locks_required(muDoublePointer))); // \
-  expected-error {{'shared_locks_required' attribute requires arguments that are class type or point to class type}}
-int slr_function_bad_4() __attribute__((shared_locks_required(umu))); // \
-  expected-error {{'shared_locks_required' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-





More information about the cfe-commits mailing list