diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2014-11-24 09:15:30 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2014-11-24 09:15:30 +0000 | 
| commit | 9f4dbff6669c8037f3b036bcf580d14f1a4f12a5 (patch) | |
| tree | 47df2c12b57214af6c31e47404b005675b8b7ffc /lib/Analysis/ThreadSafety.cpp | |
| parent | f73d5f23a889b93d89ddef61ac0995df40286bb8 (diff) | |
Notes
Diffstat (limited to 'lib/Analysis/ThreadSafety.cpp')
| -rw-r--r-- | lib/Analysis/ThreadSafety.cpp | 791 | 
1 files changed, 393 insertions, 398 deletions
| diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp index 6e0e1732bae9..11df61f80fa0 100644 --- a/lib/Analysis/ThreadSafety.cpp +++ b/lib/Analysis/ThreadSafety.cpp @@ -10,18 +10,22 @@  // A intra-procedural analysis for thread safety (e.g. deadlocks and race  // conditions), based off of an annotation system.  // -// See http://clang.llvm.org/docs/LanguageExtensions.html#thread-safety-annotation-checking +// See http://clang.llvm.org/docs/ThreadSafetyAnalysis.html  // for more information.  //  //===----------------------------------------------------------------------===// -#include "clang/Analysis/Analyses/ThreadSafety.h"  #include "clang/AST/Attr.h"  #include "clang/AST/DeclCXX.h"  #include "clang/AST/ExprCXX.h"  #include "clang/AST/StmtCXX.h"  #include "clang/AST/StmtVisitor.h"  #include "clang/Analysis/Analyses/PostOrderCFGView.h" +#include "clang/Analysis/Analyses/ThreadSafety.h" +#include "clang/Analysis/Analyses/ThreadSafetyLogical.h" +#include "clang/Analysis/Analyses/ThreadSafetyTIL.h" +#include "clang/Analysis/Analyses/ThreadSafetyTraverse.h" +#include "clang/Analysis/Analyses/ThreadSafetyCommon.h"  #include "clang/Analysis/AnalysisContext.h"  #include "clang/Analysis/CFG.h"  #include "clang/Analysis/CFGStmtMap.h" @@ -172,12 +176,9 @@ private:      const Expr* const* FunArgs;    // Function arguments      CallingContext*    PrevCtx;    // The previous context; or 0 if none. -    CallingContext(const NamedDecl *D = 0, const Expr *S = 0, -                   unsigned N = 0, const Expr* const *A = 0, -                   CallingContext *P = 0) -      : AttrDecl(D), SelfArg(S), SelfArrow(false), -        NumArgs(N), FunArgs(A), PrevCtx(P) -    { } +    CallingContext(const NamedDecl *D) +        : AttrDecl(D), SelfArg(nullptr), SelfArrow(false), NumArgs(0), +          FunArgs(nullptr), PrevCtx(nullptr) {}    };    typedef SmallVector<SExprNode, 4> NodeVector; @@ -188,44 +189,41 @@ private:    NodeVector NodeVec;  private: +  unsigned make(ExprOp O, unsigned F = 0, const void *D = nullptr) { +    NodeVec.push_back(SExprNode(O, F, D)); +    return NodeVec.size() - 1; +  } +    unsigned makeNop() { -    NodeVec.push_back(SExprNode(EOP_Nop, 0, 0)); -    return NodeVec.size()-1; +    return make(EOP_Nop);    }    unsigned makeWildcard() { -    NodeVec.push_back(SExprNode(EOP_Wildcard, 0, 0)); -    return NodeVec.size()-1; +    return make(EOP_Wildcard);    }    unsigned makeUniversal() { -    NodeVec.push_back(SExprNode(EOP_Universal, 0, 0)); -    return NodeVec.size()-1; +    return make(EOP_Universal);    }    unsigned makeNamedVar(const NamedDecl *D) { -    NodeVec.push_back(SExprNode(EOP_NVar, 0, D)); -    return NodeVec.size()-1; +    return make(EOP_NVar, 0, D);    }    unsigned makeLocalVar(const NamedDecl *D) { -    NodeVec.push_back(SExprNode(EOP_LVar, 0, D)); -    return NodeVec.size()-1; +    return make(EOP_LVar, 0, D);    }    unsigned makeThis() { -    NodeVec.push_back(SExprNode(EOP_This, 0, 0)); -    return NodeVec.size()-1; +    return make(EOP_This);    }    unsigned makeDot(const NamedDecl *D, bool Arrow) { -    NodeVec.push_back(SExprNode(EOP_Dot, Arrow ? 1 : 0, D)); -    return NodeVec.size()-1; +    return make(EOP_Dot, Arrow ? 1 : 0, D);    }    unsigned makeCall(unsigned NumArgs, const NamedDecl *D) { -    NodeVec.push_back(SExprNode(EOP_Call, NumArgs, D)); -    return NodeVec.size()-1; +    return make(EOP_Call, NumArgs, D);    }    // Grab the very first declaration of virtual method D @@ -238,32 +236,27 @@ private:          return D;  // Method does not override anything        D = *I;      // FIXME: this does not work with multiple inheritance.      } -    return 0; +    return nullptr;    }    unsigned makeMCall(unsigned NumArgs, const CXXMethodDecl *D) { -    NodeVec.push_back(SExprNode(EOP_MCall, NumArgs, getFirstVirtualDecl(D))); -    return NodeVec.size()-1; +    return make(EOP_MCall, NumArgs, getFirstVirtualDecl(D));    }    unsigned makeIndex() { -    NodeVec.push_back(SExprNode(EOP_Index, 0, 0)); -    return NodeVec.size()-1; +    return make(EOP_Index);    }    unsigned makeUnary() { -    NodeVec.push_back(SExprNode(EOP_Unary, 0, 0)); -    return NodeVec.size()-1; +    return make(EOP_Unary);    }    unsigned makeBinary() { -    NodeVec.push_back(SExprNode(EOP_Binary, 0, 0)); -    return NodeVec.size()-1; +    return make(EOP_Binary);    }    unsigned makeUnknown(unsigned Arity) { -    NodeVec.push_back(SExprNode(EOP_Unknown, Arity, 0)); -    return NodeVec.size()-1; +    return make(EOP_Unknown, Arity);    }    inline bool isCalleeArrow(const Expr *E) { @@ -277,10 +270,10 @@ private:    /// ensure that the original expression is a valid mutex expression.    ///    /// NDeref returns the number of Derefence and AddressOf operations -  /// preceeding the Expr; this is used to decide whether to pretty-print +  /// preceding the Expr; this is used to decide whether to pretty-print    /// SExprs with . or ->. -  unsigned buildSExpr(const Expr *Exp, CallingContext* CallCtx, -                      int* NDeref = 0) { +  unsigned buildSExpr(const Expr *Exp, CallingContext *CallCtx, +                      int *NDeref = nullptr) {      if (!Exp)        return 0; @@ -377,7 +370,7 @@ private:          }        }        unsigned NumCallArgs = CE->getNumArgs(); -      unsigned Root = makeCall(NumCallArgs, 0); +      unsigned Root = makeCall(NumCallArgs, nullptr);        unsigned Sz = buildSExpr(CE->getCallee(), CallCtx);        const Expr* const* CallArgs = CE->getArgs();        for (unsigned i = 0; i < NumCallArgs; ++i) { @@ -470,7 +463,7 @@ private:    ///        occurs.    /// \param D  The declaration to which the lock/unlock attribute is attached.    void buildSExprFromExpr(const Expr *MutexExp, const Expr *DeclExp, -                          const NamedDecl *D, VarDecl *SelfDecl = 0) { +                          const NamedDecl *D, VarDecl *SelfDecl = nullptr) {      CallingContext CallCtx(D);      if (MutexExp) { @@ -487,8 +480,8 @@ private:      }      // If we are processing a raw attribute expression, with no substitutions. -    if (DeclExp == 0) { -      buildSExpr(MutexExp, 0); +    if (!DeclExp) { +      buildSExpr(MutexExp, nullptr);        return;      } @@ -508,7 +501,7 @@ private:        CallCtx.FunArgs = CE->getArgs();      } else if (const CXXConstructExpr *CE =                 dyn_cast<CXXConstructExpr>(DeclExp)) { -      CallCtx.SelfArg = 0;  // Will be set below +      CallCtx.SelfArg = nullptr;  // Will be set below        CallCtx.NumArgs = CE->getNumArgs();        CallCtx.FunArgs = CE->getArgs();      } else if (D && isa<CXXDestructorDecl>(D)) { @@ -524,16 +517,16 @@ private:        CallCtx.SelfArg = &SelfDRE;        // If the attribute has no arguments, then assume the argument is "this". -      if (MutexExp == 0) -        buildSExpr(CallCtx.SelfArg, 0); +      if (!MutexExp) +        buildSExpr(CallCtx.SelfArg, nullptr);        else  // For most attributes.          buildSExpr(MutexExp, &CallCtx);        return;      }      // If the attribute has no arguments, then assume the argument is "this". -    if (MutexExp == 0) -      buildSExpr(CallCtx.SelfArg, 0); +    if (!MutexExp) +      buildSExpr(CallCtx.SelfArg, nullptr);      else  // For most attributes.        buildSExpr(MutexExp, &CallCtx);    } @@ -551,8 +544,8 @@ public:    ///        occurs.    /// \param D  The declaration to which the lock/unlock attribute is attached.    /// Caller must check isValid() after construction. -  SExpr(const Expr* MutexExp, const Expr *DeclExp, const NamedDecl* D, -        VarDecl *SelfDecl=0) { +  SExpr(const Expr *MutexExp, const Expr *DeclExp, const NamedDecl *D, +        VarDecl *SelfDecl = nullptr) {      buildSExprFromExpr(MutexExp, DeclExp, D, SelfDecl);    } @@ -575,15 +568,15 @@ public:    /// Issue a warning about an invalid lock expression    static void warnInvalidLock(ThreadSafetyHandler &Handler, -                              const Expr *MutexExp, -                              const Expr *DeclExp, const NamedDecl* D) { +                              const Expr *MutexExp, const Expr *DeclExp, +                              const NamedDecl *D, StringRef Kind) {      SourceLocation Loc;      if (DeclExp)        Loc = DeclExp->getExprLoc();      // FIXME: add a note about the attribute location in MutexExp or D      if (Loc.isValid()) -      Handler.handleInvalidLockExp(Loc); +      Handler.handleInvalidLockExp(Kind, Loc);    }    bool operator==(const SExpr &other) const { @@ -716,27 +709,16 @@ public:    }  }; - -  /// \brief A short list of SExprs  class MutexIDList : public SmallVector<SExpr, 3> {  public: -  /// \brief Return true if the list contains the specified SExpr -  /// Performs a linear search, because these lists are almost always very small. -  bool contains(const SExpr& M) { -    for (iterator I=begin(),E=end(); I != E; ++I) -      if ((*I) == M) return true; -    return false; -  } - -  /// \brief Push M onto list, bud discard duplicates +  /// \brief Push M onto list, but discard duplicates.    void push_back_nodup(const SExpr& M) { -    if (!contains(M)) push_back(M); +    if (end() == std::find(begin(), end(), M)) +      push_back(M);    }  }; - -  /// \brief This is a helper class that stores info about the most recent  /// accquire of a Lock.  /// @@ -867,44 +849,37 @@ public:      return false;    } -  // Returns an iterator    iterator findLockIter(FactManager &FM, const SExpr &M) { -    for (iterator I = begin(), E = end(); I != E; ++I) { -      const SExpr &Exp = FM[*I].MutID; -      if (Exp.matches(M)) -        return I; -    } -    return end(); +    return std::find_if(begin(), end(), [&](FactID ID) { +      return FM[ID].MutID.matches(M); +    });    } -  LockData* findLock(FactManager &FM, const SExpr &M) const { -    for (const_iterator I = begin(), E = end(); I != E; ++I) { -      const SExpr &Exp = FM[*I].MutID; -      if (Exp.matches(M)) -        return &FM[*I].LDat; -    } -    return 0; -  } +  LockData *findLock(FactManager &FM, const SExpr &M) const { +    auto I = std::find_if(begin(), end(), [&](FactID ID) { +      return FM[ID].MutID.matches(M); +    }); -  LockData* findLockUniv(FactManager &FM, const SExpr &M) const { -    for (const_iterator I = begin(), E = end(); I != E; ++I) { -      const SExpr &Exp = FM[*I].MutID; -      if (Exp.matches(M) || Exp.isUniversal()) -        return &FM[*I].LDat; -    } -    return 0; +    return I != end() ? &FM[*I].LDat : nullptr;    } -  FactEntry* findPartialMatch(FactManager &FM, const SExpr &M) const { -    for (const_iterator I=begin(), E=end(); I != E; ++I) { -      const SExpr& Exp = FM[*I].MutID; -      if (Exp.partiallyMatches(M)) return &FM[*I]; -    } -    return 0; +  LockData *findLockUniv(FactManager &FM, const SExpr &M) const { +    auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool { +      const SExpr &Expr = FM[ID].MutID; +      return Expr.isUniversal() || Expr.matches(M); +    }); + +    return I != end() ? &FM[*I].LDat : nullptr;    } -}; +  FactEntry *findPartialMatch(FactManager &FM, const SExpr &M) const { +    auto I = std::find_if(begin(), end(), [&](FactID ID) { +      return FM[ID].MutID.partiallyMatches(M); +    }); +    return I != end() ? &FM[*I] : nullptr; +  } +};  /// A Lockset maps each SExpr (defined above) to information about how it has  /// been locked. @@ -987,7 +962,7 @@ public:      // Create reference to previous definition      VarDefinition(const NamedDecl *D, unsigned R, Context C) -      : Dec(D), Exp(0), Ref(R), Ctx(C) +      : Dec(D), Exp(nullptr), Ref(R), Ctx(C)      { }    }; @@ -1000,14 +975,14 @@ private:  public:    LocalVariableMap() {      // index 0 is a placeholder for undefined variables (aka phi-nodes). -    VarDefinitions.push_back(VarDefinition(0, 0u, getEmptyContext())); +    VarDefinitions.push_back(VarDefinition(nullptr, 0u, getEmptyContext()));    }    /// Look up a definition, within the given context.    const VarDefinition* lookup(const NamedDecl *D, Context Ctx) {      const unsigned *i = Ctx.lookup(D);      if (!i) -      return 0; +      return nullptr;      assert(*i < VarDefinitions.size());      return &VarDefinitions[*i];    } @@ -1018,7 +993,7 @@ public:    const Expr* lookupExpr(const NamedDecl *D, Context &Ctx) {      const unsigned *P = Ctx.lookup(D);      if (!P) -      return 0; +      return nullptr;      unsigned i = *P;      while (i > 0) { @@ -1028,7 +1003,7 @@ public:        }        i = VarDefinitions[i].Ref;      } -    return 0; +    return nullptr;    }    Context getEmptyContext() { return ContextFactory.getEmptyMap(); } @@ -1088,8 +1063,8 @@ public:    }    /// Builds the variable map. -  void traverseCFG(CFG *CFGraph, PostOrderCFGView *SortedGraph, -                     std::vector<CFGBlockInfo> &BlockInfo); +  void traverseCFG(CFG *CFGraph, const PostOrderCFGView *SortedGraph, +                   std::vector<CFGBlockInfo> &BlockInfo);  protected:    // Get the current context index @@ -1102,7 +1077,7 @@ protected:    // Adds a new definition to the given context, and returns a new context.    // This method should be called when declaring a new variable. -  Context addDefinition(const NamedDecl *D, Expr *Exp, Context Ctx) { +  Context addDefinition(const NamedDecl *D, const Expr *Exp, Context Ctx) {      assert(!Ctx.contains(D));      unsigned newID = VarDefinitions.size();      Context NewCtx = ContextFactory.add(Ctx, D, newID); @@ -1183,9 +1158,9 @@ public:  void VarMapBuilder::VisitDeclStmt(DeclStmt *S) {    bool modifiedCtx = false;    DeclGroupRef DGrp = S->getDeclGroup(); -  for (DeclGroupRef::iterator I = DGrp.begin(), E = DGrp.end(); I != E; ++I) { -    if (VarDecl *VD = dyn_cast_or_null<VarDecl>(*I)) { -      Expr *E = VD->getInit(); +  for (const auto *D : DGrp) { +    if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) { +      const Expr *E = VD->getInit();        // Add local variables with trivial type to the variable map        QualType T = VD->getType(); @@ -1227,13 +1202,12 @@ void VarMapBuilder::VisitBinaryOperator(BinaryOperator *BO) {  LocalVariableMap::Context  LocalVariableMap::intersectContexts(Context C1, Context C2) {    Context Result = C1; -  for (Context::iterator I = C1.begin(), E = C1.end(); I != E; ++I) { -    const NamedDecl *Dec = I.getKey(); -    unsigned i1 = I.getData(); +  for (const auto &P : C1) { +    const NamedDecl *Dec = P.first;      const unsigned *i2 = C2.lookup(Dec);      if (!i2)             // variable doesn't exist on second path        Result = removeDefinition(Dec, Result); -    else if (*i2 != i1)  // variable exists, but has different definition +    else if (*i2 != P.second)  // variable exists, but has different definition        Result = clearDefinition(Dec, Result);    }    return Result; @@ -1244,11 +1218,8 @@ LocalVariableMap::intersectContexts(Context C1, Context C2) {  // (We use this for a naive implementation of SSA on loop back-edges.)  LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) {    Context Result = getEmptyContext(); -  for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) { -    const NamedDecl *Dec = I.getKey(); -    unsigned i = I.getData(); -    Result = addReference(Dec, i, Result); -  } +  for (const auto &P : C) +    Result = addReference(P.first, P.second, Result);    return Result;  } @@ -1256,13 +1227,12 @@ LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) {  // altering the VarDefinitions.  C1 must be the result of an earlier call to  // createReferenceContext.  void LocalVariableMap::intersectBackEdge(Context C1, Context C2) { -  for (Context::iterator I = C1.begin(), E = C1.end(); I != E; ++I) { -    const NamedDecl *Dec = I.getKey(); -    unsigned i1 = I.getData(); +  for (const auto &P : C1) { +    unsigned i1 = P.second;      VarDefinition *VDef = &VarDefinitions[i1];      assert(VDef->isReference()); -    const unsigned *i2 = C2.lookup(Dec); +    const unsigned *i2 = C2.lookup(P.first);      if (!i2 || (*i2 != i1))        VDef->Ref = 0;    // Mark this variable as undefined    } @@ -1308,15 +1278,13 @@ void LocalVariableMap::intersectBackEdge(Context C1, Context C2) {  //   ...                 { y -> y1           | x3 = 2, x2 = 1, ... }  //  void LocalVariableMap::traverseCFG(CFG *CFGraph, -                                   PostOrderCFGView *SortedGraph, +                                   const PostOrderCFGView *SortedGraph,                                     std::vector<CFGBlockInfo> &BlockInfo) {    PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph);    CtxIndices.resize(CFGraph->getNumBlockIDs()); -  for (PostOrderCFGView::iterator I = SortedGraph->begin(), -       E = SortedGraph->end(); I!= E; ++I) { -    const CFGBlock *CurrBlock = *I; +  for (const auto *CurrBlock : *SortedGraph) {      int CurrBlockID = CurrBlock->getBlockID();      CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID]; @@ -1328,7 +1296,7 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph,      for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),           PE  = CurrBlock->pred_end(); PI != PE; ++PI) {        // if *PI -> CurrBlock is a back edge, so skip it -      if (*PI == 0 || !VisitedBlocks.alreadySet(*PI)) { +      if (*PI == nullptr || !VisitedBlocks.alreadySet(*PI)) {          HasBackEdges = true;          continue;        } @@ -1354,7 +1322,7 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph,          createReferenceContext(CurrBlockInfo->EntryContext);      // Create a starting context index for the current block -    saveContext(0, CurrBlockInfo->EntryContext); +    saveContext(nullptr, CurrBlockInfo->EntryContext);      CurrBlockInfo->EntryIndex = getContextIndex();      // Visit all the statements in the basic block. @@ -1377,7 +1345,7 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph,      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 (*SI == 0 || !VisitedBlocks.alreadySet(*SI)) +      if (*SI == nullptr || !VisitedBlocks.alreadySet(*SI))          continue;        CFGBlock *FirstLoopBlock = *SI; @@ -1389,17 +1357,15 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph,    // Put an extra entry at the end of the indexed context array    unsigned exitID = CFGraph->getExit().getBlockID(); -  saveContext(0, BlockInfo[exitID].ExitContext); +  saveContext(nullptr, BlockInfo[exitID].ExitContext);  }  /// Find the appropriate source locations to use when producing diagnostics for  /// each block in the CFG.  static void findBlockLocations(CFG *CFGraph, -                               PostOrderCFGView *SortedGraph, +                               const PostOrderCFGView *SortedGraph,                                 std::vector<CFGBlockInfo> &BlockInfo) { -  for (PostOrderCFGView::iterator I = SortedGraph->begin(), -       E = SortedGraph->end(); I!= E; ++I) { -    const CFGBlock *CurrBlock = *I; +  for (const auto *CurrBlock : *SortedGraph) {      CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()];      // Find the source location of the last statement in the block, if the @@ -1450,13 +1416,14 @@ class ThreadSafetyAnalyzer {  public:    ThreadSafetyAnalyzer(ThreadSafetyHandler &H) : Handler(H) {} -  void addLock(FactSet &FSet, const SExpr &Mutex, const LockData &LDat); -  void removeLock(FactSet &FSet, const SExpr &Mutex, -                  SourceLocation UnlockLoc, bool FullyRemove=false); +  void addLock(FactSet &FSet, const SExpr &Mutex, const LockData &LDat, +               StringRef DiagKind); +  void removeLock(FactSet &FSet, const SExpr &Mutex, SourceLocation UnlockLoc, +                  bool FullyRemove, LockKind Kind, StringRef DiagKind);    template <typename AttrType>    void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp, -                   const NamedDecl *D, VarDecl *SelfDecl=0); +                   const NamedDecl *D, VarDecl *SelfDecl = nullptr);    template <class AttrType>    void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp, @@ -1485,12 +1452,89 @@ public:    void runAnalysis(AnalysisDeclContext &AC);  }; +/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs. +static const ValueDecl *getValueDecl(const Expr *Exp) { +  if (const auto *CE = dyn_cast<ImplicitCastExpr>(Exp)) +    return getValueDecl(CE->getSubExpr()); + +  if (const auto *DR = dyn_cast<DeclRefExpr>(Exp)) +    return DR->getDecl(); + +  if (const auto *ME = dyn_cast<MemberExpr>(Exp)) +    return ME->getMemberDecl(); + +  return nullptr; +} + +template <typename Ty> +class has_arg_iterator_range { +  typedef char yes[1]; +  typedef char no[2]; + +  template <typename Inner> +  static yes& test(Inner *I, decltype(I->args()) * = nullptr); + +  template <typename> +  static no& test(...); + +public: +  static const bool value = sizeof(test<Ty>(nullptr)) == sizeof(yes); +}; + +static StringRef ClassifyDiagnostic(const CapabilityAttr *A) { +  return A->getName(); +} + +static StringRef ClassifyDiagnostic(QualType VDT) { +  // We need to look at the declaration of the type of the value to determine +  // which it is. The type should either be a record or a typedef, or a pointer +  // or reference thereof. +  if (const auto *RT = VDT->getAs<RecordType>()) { +    if (const auto *RD = RT->getDecl()) +      if (const auto *CA = RD->getAttr<CapabilityAttr>()) +        return ClassifyDiagnostic(CA); +  } else if (const auto *TT = VDT->getAs<TypedefType>()) { +    if (const auto *TD = TT->getDecl()) +      if (const auto *CA = TD->getAttr<CapabilityAttr>()) +        return ClassifyDiagnostic(CA); +  } else if (VDT->isPointerType() || VDT->isReferenceType()) +    return ClassifyDiagnostic(VDT->getPointeeType()); + +  return "mutex"; +} + +static StringRef ClassifyDiagnostic(const ValueDecl *VD) { +  assert(VD && "No ValueDecl passed"); + +  // The ValueDecl is the declaration of a mutex or role (hopefully). +  return ClassifyDiagnostic(VD->getType()); +} + +template <typename AttrTy> +static typename std::enable_if<!has_arg_iterator_range<AttrTy>::value, +                               StringRef>::type +ClassifyDiagnostic(const AttrTy *A) { +  if (const ValueDecl *VD = getValueDecl(A->getArg())) +    return ClassifyDiagnostic(VD); +  return "mutex"; +} + +template <typename AttrTy> +static typename std::enable_if<has_arg_iterator_range<AttrTy>::value, +                               StringRef>::type +ClassifyDiagnostic(const AttrTy *A) { +  for (const auto *Arg : A->args()) { +    if (const ValueDecl *VD = getValueDecl(Arg)) +      return ClassifyDiagnostic(VD); +  } +  return "mutex"; +}  /// \brief Add a new lock to the lockset, warning if the lock is already there.  /// \param Mutex -- the Mutex expression for the lock  /// \param LDat  -- the LockData for the lock  void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex, -                                   const LockData &LDat) { +                                   const LockData &LDat, StringRef DiagKind) {    // FIXME: deal with acquired before/after annotations.    // FIXME: Don't always warn when we have support for reentrant locks.    if (Mutex.shouldIgnore()) @@ -1498,7 +1542,7 @@ void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex,    if (FSet.findLock(FactMan, Mutex)) {      if (!LDat.Asserted) -      Handler.handleDoubleLock(Mutex.toString(), LDat.AcquireLoc); +      Handler.handleDoubleLock(DiagKind, Mutex.toString(), LDat.AcquireLoc);    } else {      FSet.addLock(FactMan, Mutex, LDat);    } @@ -1508,16 +1552,24 @@ void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex,  /// \brief Remove a lock from the lockset, warning if the lock is not there.  /// \param Mutex The lock expression corresponding to the lock to be removed  /// \param UnlockLoc The source location of the unlock (only used in error msg) -void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, -                                      const SExpr &Mutex, +void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const SExpr &Mutex,                                        SourceLocation UnlockLoc, -                                      bool FullyRemove) { +                                      bool FullyRemove, LockKind ReceivedKind, +                                      StringRef DiagKind) {    if (Mutex.shouldIgnore())      return;    const LockData *LDat = FSet.findLock(FactMan, Mutex);    if (!LDat) { -    Handler.handleUnmatchedUnlock(Mutex.toString(), UnlockLoc); +    Handler.handleUnmatchedUnlock(DiagKind, Mutex.toString(), UnlockLoc); +    return; +  } + +  // Generic lock removal doesn't care about lock kind mismatches, but +  // otherwise diagnose when the lock kinds are mismatched. +  if (ReceivedKind != LK_Generic && LDat->LKind != ReceivedKind) { +    Handler.handleIncorrectUnlockKind(DiagKind, Mutex.toString(), LDat->LKind, +                                      ReceivedKind, UnlockLoc);      return;    } @@ -1532,8 +1584,8 @@ void ThreadSafetyAnalyzer::removeLock(FactSet &FSet,        // We're releasing the underlying mutex, but not destroying the        // managing object.  Warn on dual release.        if (!FSet.findLock(FactMan, LDat->UnderlyingMutex)) { -        Handler.handleUnmatchedUnlock(LDat->UnderlyingMutex.toString(), -                                      UnlockLoc); +        Handler.handleUnmatchedUnlock( +            DiagKind, LDat->UnderlyingMutex.toString(), UnlockLoc);        }        FSet.removeLock(FactMan, LDat->UnderlyingMutex);        return; @@ -1549,22 +1601,21 @@ template <typename AttrType>  void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr,                                         Expr *Exp, const NamedDecl *D,                                         VarDecl *SelfDecl) { -  typedef typename AttrType::args_iterator iterator_type; -    if (Attr->args_size() == 0) {      // The mutex held is the "this" object. -    SExpr Mu(0, Exp, D, SelfDecl); +    SExpr Mu(nullptr, Exp, D, SelfDecl);      if (!Mu.isValid()) -      SExpr::warnInvalidLock(Handler, 0, Exp, D); +      SExpr::warnInvalidLock(Handler, nullptr, Exp, D, +                             ClassifyDiagnostic(Attr));      else        Mtxs.push_back_nodup(Mu);      return;    } -  for (iterator_type I=Attr->args_begin(), E=Attr->args_end(); I != E; ++I) { -    SExpr Mu(*I, Exp, D, SelfDecl); +  for (const auto *Arg : Attr->args()) { +    SExpr Mu(Arg, Exp, D, SelfDecl);      if (!Mu.isValid()) -      SExpr::warnInvalidLock(Handler, *I, Exp, D); +      SExpr::warnInvalidLock(Handler, Arg, Exp, D, ClassifyDiagnostic(Attr));      else        Mtxs.push_back_nodup(Mu);    } @@ -1581,23 +1632,22 @@ void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr,                                         const CFGBlock *CurrBlock,                                         Expr *BrE, bool Neg) {    // Find out which branch has the lock -  bool branch = 0; -  if (CXXBoolLiteralExpr *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE)) { +  bool branch = false; +  if (CXXBoolLiteralExpr *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE))      branch = BLE->getValue(); -  } -  else if (IntegerLiteral *ILE = dyn_cast_or_null<IntegerLiteral>(BrE)) { +  else if (IntegerLiteral *ILE = dyn_cast_or_null<IntegerLiteral>(BrE))      branch = ILE->getValue().getBoolValue(); -  } +    int branchnum = branch ? 0 : 1; -  if (Neg) branchnum = !branchnum; +  if (Neg) +    branchnum = !branchnum;    // If we've taken the trylock branch, then add the lock    int i = 0;    for (CFGBlock::const_succ_iterator SI = PredBlock->succ_begin(),         SE = PredBlock->succ_end(); SI != SE && i < 2; ++SI, ++i) { -    if (*SI == CurrBlock && i == branchnum) { +    if (*SI == CurrBlock && i == branchnum)        getMutexIDs(Mtxs, Attr, Exp, D); -    }    }  } @@ -1626,7 +1676,7 @@ const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond,                                                           LocalVarContext C,                                                           bool &Negate) {    if (!Cond) -    return 0; +    return nullptr;    if (const CallExpr *CallExp = dyn_cast<CallExpr>(Cond)) {      return CallExp; @@ -1649,7 +1699,7 @@ const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond,        Negate = !Negate;        return getTrylockCallExpr(UOP->getSubExpr(), C, Negate);      } -    return 0; +    return nullptr;    }    else if (const BinaryOperator *BOP = dyn_cast<BinaryOperator>(Cond)) {      if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) { @@ -1666,7 +1716,7 @@ const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond,          if (!TCond) Negate = !Negate;          return getTrylockCallExpr(BOP->getRHS(), C, Negate);        } -      return 0; +      return nullptr;      }      if (BOP->getOpcode() == BO_LAnd) {        // LHS must have been evaluated in a different block. @@ -1675,9 +1725,9 @@ const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond,      if (BOP->getOpcode() == BO_LOr) {        return getTrylockCallExpr(BOP->getRHS(), C, Negate);      } -    return 0; +    return nullptr;    } -  return 0; +  return nullptr;  } @@ -1697,6 +1747,7 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,    bool Negate = false;    const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->getBlockID()];    const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext; +  StringRef CapDiagKind = "mutex";    CallExpr *Exp =      const_cast<CallExpr*>(getTrylockCallExpr(Cond, LVarCtx, Negate)); @@ -1711,15 +1762,14 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,    MutexIDList SharedLocksToAdd;    // If the condition is a call to a Trylock function, then grab the attributes -  AttrVec &ArgAttrs = FunDecl->getAttrs(); -  for (unsigned i = 0; i < ArgAttrs.size(); ++i) { -    Attr *Attr = ArgAttrs[i]; +  for (auto *Attr : FunDecl->getAttrs()) {      switch (Attr->getKind()) {        case attr::ExclusiveTrylockFunction: {          ExclusiveTrylockFunctionAttr *A =            cast<ExclusiveTrylockFunctionAttr>(Attr);          getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl,                      PredBlock, CurrBlock, A->getSuccessValue(), Negate); +        CapDiagKind = ClassifyDiagnostic(A);          break;        }        case attr::SharedTrylockFunction: { @@ -1727,6 +1777,7 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,            cast<SharedTrylockFunctionAttr>(Attr);          getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl,                      PredBlock, CurrBlock, A->getSuccessValue(), Negate); +        CapDiagKind = ClassifyDiagnostic(A);          break;        }        default: @@ -1736,17 +1787,13 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,    // Add and remove locks.    SourceLocation Loc = Exp->getExprLoc(); -  for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { -    addLock(Result, ExclusiveLocksToAdd[i], -            LockData(Loc, LK_Exclusive)); -  } -  for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { -    addLock(Result, SharedLocksToAdd[i], -            LockData(Loc, LK_Shared)); -  } +  for (const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd) +    addLock(Result, ExclusiveLockToAdd, LockData(Loc, LK_Exclusive), +            CapDiagKind); +  for (const auto &SharedLockToAdd : SharedLocksToAdd) +    addLock(Result, SharedLockToAdd, LockData(Loc, LK_Shared), CapDiagKind);  } -  /// \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 @@ -1761,16 +1808,17 @@ class BuildLockset : public StmtVisitor<BuildLockset> {    unsigned CtxIndex;    // Helper functions -  const ValueDecl *getValueDecl(const Expr *Exp);    void warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, AccessKind AK, -                          Expr *MutexExp, ProtectedOperationKind POK); -  void warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, Expr *MutexExp); +                          Expr *MutexExp, ProtectedOperationKind POK, +                          StringRef DiagKind); +  void warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, Expr *MutexExp, +                       StringRef DiagKind);    void checkAccess(const Expr *Exp, AccessKind AK);    void checkPtAccess(const Expr *Exp, AccessKind AK); -  void handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD = 0); +  void handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD = nullptr);  public:    BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info) @@ -1789,31 +1837,17 @@ public:    void VisitDeclStmt(DeclStmt *S);  }; - -/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs -const ValueDecl *BuildLockset::getValueDecl(const Expr *Exp) { -  if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(Exp)) -    return getValueDecl(CE->getSubExpr()); - -  if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Exp)) -    return DR->getDecl(); - -  if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) -    return ME->getMemberDecl(); - -  return 0; -} -  /// \brief Warn if the LSet does not contain a lock sufficient to protect access  /// of at least the passed in AccessKind.  void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,                                        AccessKind AK, Expr *MutexExp, -                                      ProtectedOperationKind POK) { +                                      ProtectedOperationKind POK, +                                      StringRef DiagKind) {    LockKind LK = getLockKindFromAccessKind(AK);    SExpr Mutex(MutexExp, Exp, D);    if (!Mutex.isValid()) { -    SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D); +    SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D, DiagKind);      return;    } else if (Mutex.shouldIgnore()) {      return; @@ -1829,40 +1863,38 @@ void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,        LDat = &FEntry->LDat;        std::string PartMatchStr = FEntry->MutID.toString();        StringRef   PartMatchName(PartMatchStr); -      Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK, -                                           Exp->getExprLoc(), &PartMatchName); +      Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(), +                                           LK, Exp->getExprLoc(), +                                           &PartMatchName);      } else {        // Warn that there's no match at all. -      Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK, -                                           Exp->getExprLoc()); +      Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(), +                                           LK, Exp->getExprLoc());      }      NoError = false;    }    // Make sure the mutex we found is the right kind.    if (NoError && LDat && !LDat->isAtLeast(LK)) -    Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK, +    Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(), LK,                                           Exp->getExprLoc());  }  /// \brief Warn if the LSet contains the given lock. -void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr* Exp, -                                   Expr *MutexExp) { +void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, +                                   Expr *MutexExp, +                                   StringRef DiagKind) {    SExpr Mutex(MutexExp, Exp, D);    if (!Mutex.isValid()) { -    SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D); +    SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D, DiagKind);      return;    }    LockData* LDat = FSet.findLock(Analyzer->FactMan, Mutex); -  if (LDat) { -    std::string DeclName = D->getNameAsString(); -    StringRef   DeclNameSR (DeclName); -    Analyzer->Handler.handleFunExcludesLock(DeclNameSR, Mutex.toString(), -                                            Exp->getExprLoc()); -  } +  if (LDat) +    Analyzer->Handler.handleFunExcludesLock( +        DiagKind, D->getNameAsString(), Mutex.toString(), Exp->getExprLoc());  } -  /// \brief Checks guarded_by and pt_guarded_by attributes.  /// Whenever we identify an access (read or write) to a DeclRefExpr that is  /// marked with guarded_by, we must ensure the appropriate mutexes are held. @@ -1879,10 +1911,8 @@ void BuildLockset::checkAccess(const Expr *Exp, AccessKind AK) {    }    if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(Exp)) { -    if (Analyzer->Handler.issueBetaWarnings()) { -      checkPtAccess(AE->getLHS(), AK); -      return; -    } +    checkPtAccess(AE->getLHS(), AK); +    return;    }    if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) { @@ -1896,55 +1926,48 @@ void BuildLockset::checkAccess(const Expr *Exp, AccessKind AK) {    if (!D || !D->hasAttrs())      return; -  if (D->getAttr<GuardedVarAttr>() && FSet.isEmpty()) -    Analyzer->Handler.handleNoMutexHeld(D, POK_VarAccess, AK, +  if (D->hasAttr<GuardedVarAttr>() && FSet.isEmpty()) +    Analyzer->Handler.handleNoMutexHeld("mutex", D, POK_VarAccess, AK,                                          Exp->getExprLoc()); -  const AttrVec &ArgAttrs = D->getAttrs(); -  for (unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i) -    if (GuardedByAttr *GBAttr = dyn_cast<GuardedByAttr>(ArgAttrs[i])) -      warnIfMutexNotHeld(D, Exp, AK, GBAttr->getArg(), POK_VarAccess); +  for (const auto *I : D->specific_attrs<GuardedByAttr>()) +    warnIfMutexNotHeld(D, Exp, AK, I->getArg(), POK_VarAccess, +                       ClassifyDiagnostic(I));  }  /// \brief Checks pt_guarded_by and pt_guarded_var attributes.  void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK) { -  if (Analyzer->Handler.issueBetaWarnings()) { -    while (true) { -      if (const ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) { -        Exp = PE->getSubExpr(); -        continue; -      } -      if (const CastExpr *CE = dyn_cast<CastExpr>(Exp)) { -        if (CE->getCastKind() == CK_ArrayToPointerDecay) { -          // If it's an actual array, and not a pointer, then it's elements -          // are protected by GUARDED_BY, not PT_GUARDED_BY; -          checkAccess(CE->getSubExpr(), AK); -          return; -        } -        Exp = CE->getSubExpr(); -        continue; +  while (true) { +    if (const ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) { +      Exp = PE->getSubExpr(); +      continue; +    } +    if (const CastExpr *CE = dyn_cast<CastExpr>(Exp)) { +      if (CE->getCastKind() == CK_ArrayToPointerDecay) { +        // If it's an actual array, and not a pointer, then it's elements +        // are protected by GUARDED_BY, not PT_GUARDED_BY; +        checkAccess(CE->getSubExpr(), AK); +        return;        } -      break; +      Exp = CE->getSubExpr(); +      continue;      } +    break;    } -  else -    Exp = Exp->IgnoreParenCasts();    const ValueDecl *D = getValueDecl(Exp);    if (!D || !D->hasAttrs())      return; -  if (D->getAttr<PtGuardedVarAttr>() && FSet.isEmpty()) -    Analyzer->Handler.handleNoMutexHeld(D, POK_VarDereference, AK, +  if (D->hasAttr<PtGuardedVarAttr>() && FSet.isEmpty()) +    Analyzer->Handler.handleNoMutexHeld("mutex", D, POK_VarDereference, AK,                                          Exp->getExprLoc()); -  const AttrVec &ArgAttrs = D->getAttrs(); -  for (unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i) -    if (PtGuardedByAttr *GBAttr = dyn_cast<PtGuardedByAttr>(ArgAttrs[i])) -      warnIfMutexNotHeld(D, Exp, AK, GBAttr->getArg(), POK_VarDereference); +  for (auto const *I : D->specific_attrs<PtGuardedByAttr>()) +    warnIfMutexNotHeld(D, Exp, AK, I->getArg(), POK_VarDereference, +                       ClassifyDiagnostic(I));  } -  /// \brief Process a function call, method call, constructor call,  /// or destructor call.  This involves looking at the attributes on the  /// corresponding function/method/constructor/destructor, issuing warnings, @@ -1958,26 +1981,22 @@ void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK) {  void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {    SourceLocation Loc = Exp->getExprLoc();    const AttrVec &ArgAttrs = D->getAttrs(); -  MutexIDList ExclusiveLocksToAdd; -  MutexIDList SharedLocksToAdd; -  MutexIDList LocksToRemove; +  MutexIDList ExclusiveLocksToAdd, SharedLocksToAdd; +  MutexIDList ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove; +  StringRef CapDiagKind = "mutex";    for(unsigned i = 0; i < ArgAttrs.size(); ++i) {      Attr *At = const_cast<Attr*>(ArgAttrs[i]);      switch (At->getKind()) { -      // When we encounter an exclusive lock function, we need to add the lock -      // to our lockset with kind exclusive. -      case attr::ExclusiveLockFunction: { -        ExclusiveLockFunctionAttr *A = cast<ExclusiveLockFunctionAttr>(At); -        Analyzer->getMutexIDs(ExclusiveLocksToAdd, A, Exp, D, VD); -        break; -      } - -      // When we encounter a shared lock function, we need to add the lock -      // to our lockset with kind shared. -      case attr::SharedLockFunction: { -        SharedLockFunctionAttr *A = cast<SharedLockFunctionAttr>(At); -        Analyzer->getMutexIDs(SharedLocksToAdd, A, Exp, D, VD); +      // When we encounter a lock function, we need to add the lock to our +      // lockset. +      case attr::AcquireCapability: { +        auto *A = cast<AcquireCapabilityAttr>(At); +        Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd +                                            : ExclusiveLocksToAdd, +                              A, Exp, D, VD); + +        CapDiagKind = ClassifyDiagnostic(A);          break;        } @@ -1989,10 +2008,10 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {          MutexIDList AssertLocks;          Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD); -        for (unsigned i=0,n=AssertLocks.size(); i<n; ++i) { -          Analyzer->addLock(FSet, AssertLocks[i], -                            LockData(Loc, LK_Exclusive, false, true)); -        } +        for (const auto &AssertLock : AssertLocks) +          Analyzer->addLock(FSet, AssertLock, +                            LockData(Loc, LK_Exclusive, false, true), +                            ClassifyDiagnostic(A));          break;        }        case attr::AssertSharedLock: { @@ -2000,50 +2019,44 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {          MutexIDList AssertLocks;          Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD); -        for (unsigned i=0,n=AssertLocks.size(); i<n; ++i) { -          Analyzer->addLock(FSet, AssertLocks[i], -                            LockData(Loc, LK_Shared, false, true)); -        } +        for (const auto &AssertLock : AssertLocks) +          Analyzer->addLock(FSet, AssertLock, +                            LockData(Loc, LK_Shared, false, true), +                            ClassifyDiagnostic(A));          break;        }        // When we encounter an unlock function, we need to remove unlocked        // mutexes from the lockset, and flag a warning if they are not there. -      case attr::UnlockFunction: { -        UnlockFunctionAttr *A = cast<UnlockFunctionAttr>(At); -        Analyzer->getMutexIDs(LocksToRemove, A, Exp, D, VD); -        break; -      } - -      case attr::ExclusiveLocksRequired: { -        ExclusiveLocksRequiredAttr *A = cast<ExclusiveLocksRequiredAttr>(At); +      case attr::ReleaseCapability: { +        auto *A = cast<ReleaseCapabilityAttr>(At); +        if (A->isGeneric()) +          Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp, D, VD); +        else if (A->isShared()) +          Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp, D, VD); +        else +          Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp, D, VD); -        for (ExclusiveLocksRequiredAttr::args_iterator -             I = A->args_begin(), E = A->args_end(); I != E; ++I) -          warnIfMutexNotHeld(D, Exp, AK_Written, *I, POK_FunctionCall); +        CapDiagKind = ClassifyDiagnostic(A);          break;        } -      case attr::SharedLocksRequired: { -        SharedLocksRequiredAttr *A = cast<SharedLocksRequiredAttr>(At); - -        for (SharedLocksRequiredAttr::args_iterator I = A->args_begin(), -             E = A->args_end(); I != E; ++I) -          warnIfMutexNotHeld(D, Exp, AK_Read, *I, POK_FunctionCall); +      case attr::RequiresCapability: { +        RequiresCapabilityAttr *A = cast<RequiresCapabilityAttr>(At); +        for (auto *Arg : A->args()) +          warnIfMutexNotHeld(D, Exp, A->isShared() ? AK_Read : AK_Written, Arg, +                             POK_FunctionCall, ClassifyDiagnostic(A));          break;        }        case attr::LocksExcluded: {          LocksExcludedAttr *A = cast<LocksExcludedAttr>(At); - -        for (LocksExcludedAttr::args_iterator I = A->args_begin(), -            E = A->args_end(); I != E; ++I) { -          warnIfMutexHeld(D, Exp, *I); -        } +        for (auto *Arg : A->args()) +          warnIfMutexHeld(D, Exp, Arg, ClassifyDiagnostic(A));          break;        } -      // Ignore other (non thread-safety) attributes +      // Ignore attributes unrelated to thread-safety        default:          break;      } @@ -2054,44 +2067,43 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {    if (VD) {      if (const CXXConstructorDecl *CD = dyn_cast<const CXXConstructorDecl>(D)) {        const CXXRecordDecl* PD = CD->getParent(); -      if (PD && PD->getAttr<ScopedLockableAttr>()) +      if (PD && PD->hasAttr<ScopedLockableAttr>())          isScopedVar = true;      }    }    // Add locks. -  for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { -    Analyzer->addLock(FSet, ExclusiveLocksToAdd[i], -                            LockData(Loc, LK_Exclusive, isScopedVar)); -  } -  for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { -    Analyzer->addLock(FSet, SharedLocksToAdd[i], -                            LockData(Loc, LK_Shared, isScopedVar)); -  } +  for (const auto &M : ExclusiveLocksToAdd) +    Analyzer->addLock(FSet, M, LockData(Loc, LK_Exclusive, isScopedVar), +                      CapDiagKind); +  for (const auto &M : SharedLocksToAdd) +    Analyzer->addLock(FSet, M, LockData(Loc, LK_Shared, isScopedVar), +                      CapDiagKind);    // Add the managing object as a dummy mutex, mapped to the underlying mutex.    // FIXME -- this doesn't work if we acquire multiple locks.    if (isScopedVar) {      SourceLocation MLoc = VD->getLocation();      DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation()); -    SExpr SMutex(&DRE, 0, 0); +    SExpr SMutex(&DRE, nullptr, nullptr); -    for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { -      Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive, -                                               ExclusiveLocksToAdd[i])); -    } -    for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { -      Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared, -                                               SharedLocksToAdd[i])); -    } +    for (const auto &M : ExclusiveLocksToAdd) +      Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive, M), +                        CapDiagKind); +    for (const auto &M : SharedLocksToAdd) +      Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared, M), +                        CapDiagKind);    }    // Remove locks.    // FIXME -- should only fully remove if the attribute refers to 'this'.    bool Dtor = isa<CXXDestructorDecl>(D); -  for (unsigned i=0,n=LocksToRemove.size(); i<n; ++i) { -    Analyzer->removeLock(FSet, LocksToRemove[i], Loc, Dtor); -  } +  for (const auto &M : ExclusiveLocksToRemove) +    Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Exclusive, CapDiagKind); +  for (const auto &M : SharedLocksToRemove) +    Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Shared, CapDiagKind); +  for (const auto &M : GenericLocksToRemove) +    Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Generic, CapDiagKind);  } @@ -2168,11 +2180,9 @@ void BuildLockset::VisitCallExpr(CallExpr *Exp) {        case OO_Star:        case OO_Arrow:        case OO_Subscript: { -        if (Analyzer->Handler.issueBetaWarnings()) { -          const Expr *Obj = OE->getArg(0); -          checkAccess(Obj, AK_Read); -          checkPtAccess(Obj, AK_Read); -        } +        const Expr *Obj = OE->getArg(0); +        checkAccess(Obj, AK_Read); +        checkPtAccess(Obj, AK_Read);          break;        }        default: { @@ -2201,9 +2211,7 @@ void BuildLockset::VisitDeclStmt(DeclStmt *S) {    // adjust the context    LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx); -  DeclGroupRef DGrp = S->getDeclGroup(); -  for (DeclGroupRef::iterator I = DGrp.begin(), E = DGrp.end(); I != E; ++I) { -    Decl *D = *I; +  for (auto *D : S->getDeclGroup()) {      if (VarDecl *VD = dyn_cast_or_null<VarDecl>(D)) {        Expr *E = VD->getInit();        // handle constructors that involve temporaries @@ -2245,26 +2253,24 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,    FactSet FSet1Orig = FSet1;    // Find locks in FSet2 that conflict or are not in FSet1, and warn. -  for (FactSet::const_iterator I = FSet2.begin(), E = FSet2.end(); -       I != E; ++I) { -    const SExpr &FSet2Mutex = FactMan[*I].MutID; -    const LockData &LDat2 = FactMan[*I].LDat; +  for (const auto &Fact : FSet2) { +    const SExpr &FSet2Mutex = FactMan[Fact].MutID; +    const LockData &LDat2 = FactMan[Fact].LDat;      FactSet::iterator I1 = FSet1.findLockIter(FactMan, FSet2Mutex);      if (I1 != FSet1.end()) {        const LockData* LDat1 = &FactMan[*I1].LDat;        if (LDat1->LKind != LDat2.LKind) { -        Handler.handleExclusiveAndShared(FSet2Mutex.toString(), -                                         LDat2.AcquireLoc, -                                         LDat1->AcquireLoc); +        Handler.handleExclusiveAndShared("mutex", FSet2Mutex.toString(), +                                         LDat2.AcquireLoc, LDat1->AcquireLoc);          if (Modify && LDat1->LKind != LK_Exclusive) {            // Take the exclusive lock, which is the one in FSet2. -          *I1 = *I; +          *I1 = Fact;          }        }        else if (LDat1->Asserted && !LDat2.Asserted) {          // The non-asserted lock in FSet2 is the one we want to track. -        *I1 = *I; +        *I1 = Fact;        }      } else {        if (LDat2.UnderlyingMutex.isValid()) { @@ -2272,23 +2278,21 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,            // If this is a scoped lock that manages another mutex, and if the            // underlying mutex is still held, then warn about the underlying            // mutex. -          Handler.handleMutexHeldEndOfScope(LDat2.UnderlyingMutex.toString(), -                                            LDat2.AcquireLoc, -                                            JoinLoc, LEK1); +          Handler.handleMutexHeldEndOfScope("mutex", +                                            LDat2.UnderlyingMutex.toString(), +                                            LDat2.AcquireLoc, JoinLoc, LEK1);          }        }        else if (!LDat2.Managed && !FSet2Mutex.isUniversal() && !LDat2.Asserted) -        Handler.handleMutexHeldEndOfScope(FSet2Mutex.toString(), -                                          LDat2.AcquireLoc, -                                          JoinLoc, LEK1); +        Handler.handleMutexHeldEndOfScope("mutex", FSet2Mutex.toString(), +                                          LDat2.AcquireLoc, JoinLoc, LEK1);      }    }    // Find locks in FSet1 that are not in FSet2, and remove them. -  for (FactSet::const_iterator I = FSet1Orig.begin(), E = FSet1Orig.end(); -       I != E; ++I) { -    const SExpr &FSet1Mutex = FactMan[*I].MutID; -    const LockData &LDat1 = FactMan[*I].LDat; +  for (const auto &Fact : FSet1Orig) { +    const SExpr &FSet1Mutex = FactMan[Fact].MutID; +    const LockData &LDat1 = FactMan[Fact].LDat;      if (!FSet2.findLock(FactMan, FSet1Mutex)) {        if (LDat1.UnderlyingMutex.isValid()) { @@ -2296,15 +2300,14 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,            // If this is a scoped lock that manages another mutex, and if the            // underlying mutex is still held, then warn about the underlying            // mutex. -          Handler.handleMutexHeldEndOfScope(LDat1.UnderlyingMutex.toString(), -                                            LDat1.AcquireLoc, -                                            JoinLoc, LEK1); +          Handler.handleMutexHeldEndOfScope("mutex", +                                            LDat1.UnderlyingMutex.toString(), +                                            LDat1.AcquireLoc, JoinLoc, LEK1);          }        }        else if (!LDat1.Managed && !FSet1Mutex.isUniversal() && !LDat1.Asserted) -        Handler.handleMutexHeldEndOfScope(FSet1Mutex.toString(), -                                          LDat1.AcquireLoc, -                                          JoinLoc, LEK2); +        Handler.handleMutexHeldEndOfScope("mutex", FSet1Mutex.toString(), +                                          LDat1.AcquireLoc, JoinLoc, LEK2);        if (Modify)          FSet1.removeLock(FactMan, FSet1Mutex);      } @@ -2334,16 +2337,21 @@ inline bool neverReturns(const CFGBlock* B) {  /// at the end of each block, and issue warnings for thread safety violations.  /// Each block in the CFG is traversed exactly once.  void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { -  CFG *CFGraph = AC.getCFG(); -  if (!CFGraph) return; -  const NamedDecl *D = dyn_cast_or_null<NamedDecl>(AC.getDecl()); +  // TODO: this whole function needs be rewritten as a visitor for CFGWalker. +  // For now, we just use the walker to set things up. +  threadSafety::CFGWalker walker; +  if (!walker.init(AC)) +    return;    // AC.dumpCFG(true); +  // threadSafety::printSCFG(walker); -  if (!D) -    return;  // Ignore anonymous functions for now. -  if (D->getAttr<NoThreadSafetyAnalysisAttr>()) +  CFG *CFGraph = walker.getGraph(); +  const NamedDecl *D = walker.getDecl(); + +  if (D->hasAttr<NoThreadSafetyAnalysisAttr>())      return; +    // FIXME: Do something a bit more intelligent inside constructor and    // destructor code.  Constructors and destructors must assume unique access    // to 'this', so checks on member variable access is disabled, but we should @@ -2359,7 +2367,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {    // 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. -  PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>(); +  const PostOrderCFGView *SortedGraph = walker.getSortedGraph();    PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph);    // Mark entry block as reachable @@ -2385,35 +2393,31 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {      MutexIDList ExclusiveLocksToAdd;      MutexIDList SharedLocksToAdd; +    StringRef CapDiagKind = "mutex";      SourceLocation Loc = D->getLocation(); -    for (unsigned i = 0; i < ArgAttrs.size(); ++i) { -      Attr *Attr = ArgAttrs[i]; +    for (const auto *Attr : ArgAttrs) {        Loc = Attr->getLocation(); -      if (ExclusiveLocksRequiredAttr *A -            = dyn_cast<ExclusiveLocksRequiredAttr>(Attr)) { -        getMutexIDs(ExclusiveLocksToAdd, A, (Expr*) 0, D); -      } else if (SharedLocksRequiredAttr *A -                   = dyn_cast<SharedLocksRequiredAttr>(Attr)) { -        getMutexIDs(SharedLocksToAdd, A, (Expr*) 0, D); -      } else if (UnlockFunctionAttr *A = dyn_cast<UnlockFunctionAttr>(Attr)) { +      if (const auto *A = dyn_cast<RequiresCapabilityAttr>(Attr)) { +        getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A, +                    nullptr, D); +        CapDiagKind = ClassifyDiagnostic(A); +      } else if (const auto *A = dyn_cast<ReleaseCapabilityAttr>(Attr)) {          // UNLOCK_FUNCTION() is used to hide the underlying lock implementation.          // We must ignore such methods.          if (A->args_size() == 0)            return;          // FIXME -- deal with exclusive vs. shared unlock functions? -        getMutexIDs(ExclusiveLocksToAdd, A, (Expr*) 0, D); -        getMutexIDs(LocksReleased, A, (Expr*) 0, D); -      } else if (ExclusiveLockFunctionAttr *A -                   = dyn_cast<ExclusiveLockFunctionAttr>(Attr)) { +        getMutexIDs(ExclusiveLocksToAdd, A, nullptr, D); +        getMutexIDs(LocksReleased, A, nullptr, D); +        CapDiagKind = ClassifyDiagnostic(A); +      } else if (const auto *A = dyn_cast<AcquireCapabilityAttr>(Attr)) {          if (A->args_size() == 0)            return; -        getMutexIDs(ExclusiveLocksAcquired, A, (Expr*) 0, D); -      } else if (SharedLockFunctionAttr *A -                   = dyn_cast<SharedLockFunctionAttr>(Attr)) { -        if (A->args_size() == 0) -          return; -        getMutexIDs(SharedLocksAcquired, A, (Expr*) 0, D); +        getMutexIDs(A->isShared() ? SharedLocksAcquired +                                  : ExclusiveLocksAcquired, +                    A, nullptr, D); +        CapDiagKind = ClassifyDiagnostic(A);        } else if (isa<ExclusiveTrylockFunctionAttr>(Attr)) {          // Don't try to check trylock functions for now          return; @@ -2424,19 +2428,15 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {      }      // FIXME -- Loc can be wrong here. -    for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { -      addLock(InitialLockset, ExclusiveLocksToAdd[i], -              LockData(Loc, LK_Exclusive)); -    } -    for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { -      addLock(InitialLockset, SharedLocksToAdd[i], -              LockData(Loc, LK_Shared)); -    } +    for (const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd) +      addLock(InitialLockset, ExclusiveLockToAdd, LockData(Loc, LK_Exclusive), +              CapDiagKind); +    for (const auto &SharedLockToAdd : SharedLocksToAdd) +      addLock(InitialLockset, SharedLockToAdd, LockData(Loc, LK_Shared), +              CapDiagKind);    } -  for (PostOrderCFGView::iterator I = SortedGraph->begin(), -       E = SortedGraph->end(); I!= E; ++I) { -    const CFGBlock *CurrBlock = *I; +  for (const auto *CurrBlock : *SortedGraph) {      int CurrBlockID = CurrBlock->getBlockID();      CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID]; @@ -2462,7 +2462,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {           PE  = CurrBlock->pred_end(); PI != PE; ++PI) {        // if *PI -> CurrBlock is a back edge -      if (*PI == 0 || !VisitedBlocks.alreadySet(*PI)) +      if (*PI == nullptr || !VisitedBlocks.alreadySet(*PI))          continue;        int PrevBlockID = (*PI)->getBlockID(); @@ -2505,9 +2505,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {      // Process continue and break blocks. Assume that the lockset for the      // resulting block is unaffected by any discrepancies in them. -    for (unsigned SpecialI = 0, SpecialN = SpecialBlocks.size(); -         SpecialI < SpecialN; ++SpecialI) { -      CFGBlock *PrevBlock = SpecialBlocks[SpecialI]; +    for (const auto *PrevBlock : SpecialBlocks) {        int PrevBlockID = PrevBlock->getBlockID();        CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID]; @@ -2576,7 +2574,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {           SE  = CurrBlock->succ_end(); SI != SE; ++SI) {        // if CurrBlock -> *SI is *not* a back edge -      if (*SI == 0 || !VisitedBlocks.alreadySet(*SI)) +      if (*SI == nullptr || !VisitedBlocks.alreadySet(*SI))          continue;        CFGBlock *FirstLoopBlock = *SI; @@ -2603,17 +2601,14 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {    // by *-LOCK_FUNCTION and UNLOCK_FUNCTION.  The intersect below will then    // issue the appropriate warning.    // FIXME: the location here is not quite right. -  for (unsigned i=0,n=ExclusiveLocksAcquired.size(); i<n; ++i) { -    ExpectedExitSet.addLock(FactMan, ExclusiveLocksAcquired[i], +  for (const auto &Lock : ExclusiveLocksAcquired) +    ExpectedExitSet.addLock(FactMan, Lock,                              LockData(D->getLocation(), LK_Exclusive)); -  } -  for (unsigned i=0,n=SharedLocksAcquired.size(); i<n; ++i) { -    ExpectedExitSet.addLock(FactMan, SharedLocksAcquired[i], +  for (const auto &Lock : SharedLocksAcquired) +    ExpectedExitSet.addLock(FactMan, Lock,                              LockData(D->getLocation(), LK_Shared)); -  } -  for (unsigned i=0,n=LocksReleased.size(); i<n; ++i) { -    ExpectedExitSet.removeLock(FactMan, LocksReleased[i]); -  } +  for (const auto &Lock : LocksReleased) +    ExpectedExitSet.removeLock(FactMan, Lock);    // FIXME: Should we call this function for all blocks which exit the function?    intersectAndWarn(ExpectedExitSet, Final->ExitSet, | 
