40 #include "llvm/ADT/ArrayRef.h" 41 #include "llvm/ADT/DenseMap.h" 42 #include "llvm/ADT/ImmutableMap.h" 43 #include "llvm/ADT/Optional.h" 44 #include "llvm/ADT/STLExtras.h" 45 #include "llvm/ADT/SmallVector.h" 46 #include "llvm/ADT/StringRef.h" 47 #include "llvm/Support/Allocator.h" 48 #include "llvm/Support/Casting.h" 49 #include "llvm/Support/ErrorHandling.h" 50 #include "llvm/Support/raw_ostream.h" 57 #include <type_traits> 61 using namespace clang;
62 using namespace threadSafety;
77 const Expr *DeclExp, StringRef
Kind) {
91 class CapExprSet :
public SmallVector<CapabilityExpr, 4> {
95 iterator It = std::find_if(begin(), end(),
129 bool Asrt,
bool Declrd =
false)
132 virtual ~FactEntry() =
default;
136 bool asserted()
const {
return Asserted; }
137 bool declared()
const {
return Declared; }
139 void setDeclared(
bool D) { Declared = D; }
142 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
145 virtual void handleUnlock(FactSet &FSet, FactManager &FactMan,
148 StringRef DiagKind)
const = 0;
156 using FactID =
unsigned short;
162 std::vector<std::unique_ptr<FactEntry>> Facts;
165 FactID newFact(std::unique_ptr<FactEntry> Entry) {
166 Facts.push_back(std::move(Entry));
167 return static_cast<unsigned short>(Facts.size() - 1);
170 const FactEntry &operator[](FactID F)
const {
return *Facts[F]; }
171 FactEntry &operator[](FactID F) {
return *Facts[F]; }
188 using iterator = FactVec::iterator;
189 using const_iterator = FactVec::const_iterator;
191 iterator begin() {
return FactIDs.begin(); }
192 const_iterator begin()
const {
return FactIDs.begin(); }
194 iterator end() {
return FactIDs.end(); }
195 const_iterator end()
const {
return FactIDs.end(); }
197 bool isEmpty()
const {
return FactIDs.size() == 0; }
200 bool isEmpty(FactManager &FactMan)
const {
201 for (
const auto FID : *
this) {
202 if (!FactMan[FID].negative())
208 void addLockByID(FactID
ID) { FactIDs.push_back(ID); }
210 FactID addLock(FactManager &FM, std::unique_ptr<FactEntry> Entry) {
211 FactID F = FM.newFact(std::move(Entry));
212 FactIDs.push_back(F);
217 unsigned n = FactIDs.size();
221 for (
unsigned i = 0; i < n-1; ++i) {
222 if (FM[FactIDs[i]].
matches(CapE)) {
223 FactIDs[i] = FactIDs[n-1];
228 if (FM[FactIDs[n-1]].
matches(CapE)) {
235 iterator findLockIter(FactManager &FM,
const CapabilityExpr &CapE) {
236 return std::find_if(begin(), end(), [&](FactID ID) {
237 return FM[
ID].matches(CapE);
241 FactEntry *findLock(FactManager &FM,
const CapabilityExpr &CapE)
const {
242 auto I = std::find_if(begin(), end(), [&](FactID ID) {
243 return FM[
ID].matches(CapE);
245 return I != end() ? &FM[*I] :
nullptr;
248 FactEntry *findLockUniv(FactManager &FM,
const CapabilityExpr &CapE)
const {
249 auto I = std::find_if(begin(), end(), [&](FactID ID) ->
bool {
250 return FM[
ID].matchesUniv(CapE);
252 return I != end() ? &FM[*I] :
nullptr;
255 FactEntry *findPartialMatch(FactManager &FM,
257 auto I = std::find_if(begin(), end(), [&](FactID ID) ->
bool {
258 return FM[
ID].partiallyMatches(CapE);
260 return I != end() ? &FM[*I] :
nullptr;
263 bool containsMutexDecl(FactManager &FM,
const ValueDecl* Vd)
const {
264 auto I = std::find_if(begin(), end(), [&](FactID ID) ->
bool {
265 return FM[
ID].valueDecl() == Vd;
271 class ThreadSafetyAnalyzer;
276 namespace threadSafety {
286 BeforeInfo() =
default;
287 BeforeInfo(BeforeInfo &&) =
default;
291 llvm::DenseMap<const ValueDecl *, std::unique_ptr<BeforeInfo>>;
292 using CycleMap = llvm::DenseMap<const ValueDecl *, bool>;
297 BeforeInfo* insertAttrExprs(
const ValueDecl* Vd,
298 ThreadSafetyAnalyzer& Analyzer);
300 BeforeInfo *getBeforeInfoForDecl(
const ValueDecl *Vd,
301 ThreadSafetyAnalyzer &Analyzer);
303 void checkBeforeAfter(
const ValueDecl* Vd,
305 ThreadSafetyAnalyzer& Analyzer,
318 class LocalVariableMap;
320 using LocalVarContext = llvm::ImmutableMap<const NamedDecl *, unsigned>;
328 struct CFGBlockInfo {
336 LocalVarContext EntryContext;
339 LocalVarContext ExitContext;
351 bool Reachable =
false;
354 return Side == CBS_Entry ? EntrySet : ExitSet;
358 return Side == CBS_Entry ? EntryLoc : ExitLoc;
362 CFGBlockInfo(LocalVarContext EmptyCtx)
363 : EntryContext(EmptyCtx), ExitContext(EmptyCtx) {}
366 static CFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M);
382 class LocalVariableMap {
384 using Context = LocalVarContext;
390 struct VarDefinition {
392 friend class LocalVariableMap;
398 const Expr *Exp =
nullptr;
406 bool isReference() {
return !Exp; }
411 : Dec(D), Exp(E), Ctx(C) {}
414 VarDefinition(
const NamedDecl *D,
unsigned R, Context C)
415 : Dec(D), Ref(R), Ctx(C) {}
420 std::vector<VarDefinition> VarDefinitions;
421 std::vector<unsigned> CtxIndices;
422 std::vector<std::pair<Stmt *, Context>> SavedContexts;
427 VarDefinitions.push_back(VarDefinition(
nullptr, 0u, getEmptyContext()));
431 const VarDefinition* lookup(
const NamedDecl *D, Context Ctx) {
432 const unsigned *i = Ctx.lookup(D);
435 assert(*i < VarDefinitions.size());
436 return &VarDefinitions[*i];
443 const unsigned *
P = Ctx.lookup(D);
449 if (VarDefinitions[i].Exp) {
450 Ctx = VarDefinitions[i].Ctx;
451 return VarDefinitions[i].Exp;
453 i = VarDefinitions[i].Ref;
458 Context getEmptyContext() {
return ContextFactory.getEmptyMap(); }
463 Context getNextContext(
unsigned &CtxIndex,
Stmt *S, Context
C) {
464 if (SavedContexts[CtxIndex+1].first == S) {
466 Context Result = SavedContexts[CtxIndex].second;
472 void dumpVarDefinitionName(
unsigned i) {
474 llvm::errs() <<
"Undefined";
477 const NamedDecl *Dec = VarDefinitions[i].Dec;
479 llvm::errs() <<
"<<NULL>>";
483 llvm::errs() <<
"." << i <<
" " << ((
const void*) Dec);
488 for (
unsigned i = 1, e = VarDefinitions.size(); i < e; ++i) {
489 const Expr *Exp = VarDefinitions[i].Exp;
490 unsigned Ref = VarDefinitions[i].Ref;
492 dumpVarDefinitionName(i);
493 llvm::errs() <<
" = ";
494 if (Exp) Exp->
dump();
496 dumpVarDefinitionName(Ref);
497 llvm::errs() <<
"\n";
503 void dumpContext(Context C) {
504 for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) {
507 const unsigned *i = C.lookup(D);
508 llvm::errs() <<
" -> ";
509 dumpVarDefinitionName(*i);
510 llvm::errs() <<
"\n";
516 std::vector<CFGBlockInfo> &BlockInfo);
519 friend class VarMapBuilder;
522 unsigned getContextIndex() {
return SavedContexts.size()-1; }
525 void saveContext(
Stmt *S, Context C) {
526 SavedContexts.push_back(std::make_pair(S, C));
531 Context addDefinition(
const NamedDecl *D,
const Expr *Exp, Context Ctx) {
532 assert(!Ctx.contains(D));
533 unsigned newID = VarDefinitions.size();
534 Context NewCtx = ContextFactory.add(Ctx, D, newID);
535 VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
540 Context addReference(
const NamedDecl *D,
unsigned i, Context Ctx) {
541 unsigned newID = VarDefinitions.size();
542 Context NewCtx = ContextFactory.add(Ctx, D, newID);
543 VarDefinitions.push_back(VarDefinition(D, i, Ctx));
549 Context updateDefinition(
const NamedDecl *D,
Expr *Exp, Context Ctx) {
550 if (Ctx.contains(D)) {
551 unsigned newID = VarDefinitions.size();
552 Context NewCtx = ContextFactory.remove(Ctx, D);
553 NewCtx = ContextFactory.add(NewCtx, D, newID);
554 VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
562 Context clearDefinition(
const NamedDecl *D, Context Ctx) {
563 Context NewCtx = Ctx;
564 if (NewCtx.contains(D)) {
565 NewCtx = ContextFactory.remove(NewCtx, D);
566 NewCtx = ContextFactory.add(NewCtx, D, 0);
572 Context removeDefinition(
const NamedDecl *D, Context Ctx) {
573 Context NewCtx = Ctx;
574 if (NewCtx.contains(D)) {
575 NewCtx = ContextFactory.remove(NewCtx, D);
580 Context intersectContexts(Context C1, Context C2);
581 Context createReferenceContext(Context C);
582 void intersectBackEdge(Context C1, Context C2);
588 CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) {
589 return CFGBlockInfo(M.getEmptyContext());
595 class VarMapBuilder :
public StmtVisitor<VarMapBuilder> {
597 LocalVariableMap* VMap;
598 LocalVariableMap::Context Ctx;
600 VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context C)
601 : VMap(VM), Ctx(C) {}
610 void VarMapBuilder::VisitDeclStmt(
DeclStmt *S) {
611 bool modifiedCtx =
false;
613 for (
const auto *D : DGrp) {
614 if (
const auto *VD = dyn_cast_or_null<VarDecl>(D)) {
615 const Expr *E = VD->getInit();
620 Ctx = VMap->addDefinition(VD, E, Ctx);
626 VMap->saveContext(S, Ctx);
637 if (
const auto *DRE = dyn_cast<DeclRefExpr>(LHSExp)) {
639 if (Ctx.lookup(VDec)) {
641 Ctx = VMap->updateDefinition(VDec, BO->
getRHS(), Ctx);
644 Ctx = VMap->clearDefinition(VDec, Ctx);
645 VMap->saveContext(BO, Ctx);
653 LocalVariableMap::Context
654 LocalVariableMap::intersectContexts(Context C1, Context C2) {
656 for (
const auto &
P : C1) {
658 const unsigned *i2 = C2.lookup(Dec);
660 Result = removeDefinition(Dec, Result);
661 else if (*i2 !=
P.second)
662 Result = clearDefinition(Dec, Result);
670 LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) {
671 Context Result = getEmptyContext();
672 for (
const auto &
P : C)
673 Result = addReference(
P.first,
P.second, Result);
680 void LocalVariableMap::intersectBackEdge(Context C1, Context C2) {
681 for (
const auto &
P : C1) {
682 unsigned i1 =
P.second;
683 VarDefinition *VDef = &VarDefinitions[i1];
684 assert(VDef->isReference());
686 const unsigned *i2 = C2.lookup(
P.first);
687 if (!i2 || (*i2 != i1))
729 void LocalVariableMap::traverseCFG(
CFG *CFGraph,
731 std::vector<CFGBlockInfo> &BlockInfo) {
736 for (
const auto *CurrBlock : *SortedGraph) {
737 int CurrBlockID = CurrBlock->getBlockID();
738 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
740 VisitedBlocks.
insert(CurrBlock);
743 bool HasBackEdges =
false;
746 PE = CurrBlock->pred_end(); PI != PE; ++PI) {
748 if (*PI ==
nullptr || !VisitedBlocks.
alreadySet(*PI)) {
753 int PrevBlockID = (*PI)->getBlockID();
754 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
757 CurrBlockInfo->EntryContext = PrevBlockInfo->ExitContext;
761 CurrBlockInfo->EntryContext =
762 intersectContexts(CurrBlockInfo->EntryContext,
763 PrevBlockInfo->ExitContext);
770 CurrBlockInfo->EntryContext =
771 createReferenceContext(CurrBlockInfo->EntryContext);
774 saveContext(
nullptr, CurrBlockInfo->EntryContext);
775 CurrBlockInfo->EntryIndex = getContextIndex();
778 VarMapBuilder VMapBuilder(
this, CurrBlockInfo->EntryContext);
779 for (
const auto &BI : *CurrBlock) {
780 switch (BI.getKind()) {
783 VMapBuilder.Visit(const_cast<Stmt *>(CS.
getStmt()));
790 CurrBlockInfo->ExitContext = VMapBuilder.Ctx;
794 SE = CurrBlock->succ_end(); SI != SE; ++SI) {
796 if (*SI ==
nullptr || !VisitedBlocks.
alreadySet(*SI))
800 Context LoopBegin = BlockInfo[FirstLoopBlock->
getBlockID()].EntryContext;
801 Context LoopEnd = CurrBlockInfo->ExitContext;
802 intersectBackEdge(LoopBegin, LoopEnd);
808 saveContext(
nullptr, BlockInfo[exitID].ExitContext);
815 std::vector<CFGBlockInfo> &BlockInfo) {
816 for (
const auto *CurrBlock : *SortedGraph) {
817 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()];
821 if (
const Stmt *S = CurrBlock->getTerminator()) {
822 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->
getLocStart();
825 BE = CurrBlock->rend(); BI != BE; ++BI) {
828 CurrBlockInfo->ExitLoc = CS->getStmt()->getLocStart();
834 if (CurrBlockInfo->ExitLoc.isValid()) {
837 for (
const auto &BI : *CurrBlock) {
840 CurrBlockInfo->EntryLoc = CS->getStmt()->getLocStart();
844 }
else if (CurrBlock->pred_size() == 1 && *CurrBlock->pred_begin() &&
845 CurrBlock != &CFGraph->
getExit()) {
848 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =
849 BlockInfo[(*CurrBlock->pred_begin())->getBlockID()].ExitLoc;
856 class LockableFactEntry :
public FactEntry {
863 bool Mng =
false,
bool Asrt =
false)
864 : FactEntry(CE, LK, Loc, Asrt), Managed(Mng) {}
867 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
870 if (!Managed && !asserted() && !negative() && !isUniversal()) {
876 void handleUnlock(FactSet &FSet, FactManager &FactMan,
879 StringRef DiagKind)
const override {
880 FSet.removeLock(FactMan, Cp);
882 FSet.addLock(FactMan, llvm::make_unique<LockableFactEntry>(
888 class ScopedLockableFactEntry :
public FactEntry {
894 const CapExprSet &Excl,
const CapExprSet &Shrd)
896 for (
const auto &M : Excl)
897 UnderlyingMutexes.push_back(M.sexpr());
898 for (
const auto &M : Shrd)
899 UnderlyingMutexes.push_back(M.sexpr());
903 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
906 for (
const auto *UnderlyingMutex : UnderlyingMutexes) {
907 if (FSet.findLock(FactMan,
CapabilityExpr(UnderlyingMutex,
false))) {
911 "mutex",
sx::toString(UnderlyingMutex), loc(), JoinLoc, LEK);
916 void handleUnlock(FactSet &FSet, FactManager &FactMan,
919 StringRef DiagKind)
const override {
920 assert(!Cp.
negative() &&
"Managing object cannot be negative.");
921 for (
const auto *UnderlyingMutex : UnderlyingMutexes) {
923 auto UnderEntry = llvm::make_unique<LockableFactEntry>(
929 if (FSet.findLock(FactMan, UnderCp)) {
930 FSet.removeLock(FactMan, UnderCp);
931 FSet.addLock(FactMan, std::move(UnderEntry));
936 if (!FSet.findLock(FactMan, UnderCp)) {
940 FSet.removeLock(FactMan, UnderCp);
941 FSet.addLock(FactMan, std::move(UnderEntry));
945 FSet.removeLock(FactMan, Cp);
950 class ThreadSafetyAnalyzer {
951 friend class BuildLockset;
954 llvm::BumpPtrAllocator Bpa;
960 LocalVariableMap LocalVarMap;
962 std::vector<CFGBlockInfo> BlockInfo;
968 : Arena(&Bpa), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}
972 void addLock(FactSet &FSet, std::unique_ptr<FactEntry> Entry,
973 StringRef DiagKind,
bool ReqAttr =
false);
978 template <
typename AttrType>
979 void getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
Expr *Exp,
982 template <
class AttrType>
983 void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
Expr *Exp,
986 Expr *BrE,
bool Neg);
988 const CallExpr* getTrylockCallExpr(
const Stmt *Cond, LocalVarContext
C,
991 void getEdgeLockset(FactSet &Result,
const FactSet &ExitSet,
995 void intersectAndWarn(FactSet &FSet1,
const FactSet &FSet2,
1000 void intersectAndWarn(FactSet &FSet1,
const FactSet &FSet2,
1003 intersectAndWarn(FSet1, FSet2, JoinLoc, LEK1, LEK1, Modify);
1013 ThreadSafetyAnalyzer& Analyzer) {
1015 BeforeInfo *Info =
nullptr;
1019 std::unique_ptr<BeforeInfo> &InfoPtr = BMap[Vd];
1021 InfoPtr.reset(
new BeforeInfo());
1022 Info = InfoPtr.get();
1025 for (
const auto *At : Vd->
attrs()) {
1026 switch (At->getKind()) {
1027 case attr::AcquiredBefore: {
1028 const auto *A = cast<AcquiredBeforeAttr>(At);
1031 for (
const auto *Arg : A->args()) {
1033 Analyzer.SxBuilder.translateAttrExpr(Arg,
nullptr);
1035 Info->Vect.push_back(Cpvd);
1036 const auto It = BMap.find(Cpvd);
1037 if (It == BMap.end())
1038 insertAttrExprs(Cpvd, Analyzer);
1043 case attr::AcquiredAfter: {
1044 const auto *A = cast<AcquiredAfterAttr>(At);
1047 for (
const auto *Arg : A->args()) {
1049 Analyzer.SxBuilder.translateAttrExpr(Arg,
nullptr);
1052 BeforeInfo *ArgInfo = getBeforeInfoForDecl(ArgVd, Analyzer);
1053 ArgInfo->Vect.push_back(Vd);
1066 BeforeSet::BeforeInfo *
1068 ThreadSafetyAnalyzer &Analyzer) {
1069 auto It = BMap.find(Vd);
1070 BeforeInfo *Info =
nullptr;
1071 if (It == BMap.end())
1072 Info = insertAttrExprs(Vd, Analyzer);
1074 Info = It->second.get();
1075 assert(Info &&
"BMap contained nullptr?");
1081 const FactSet& FSet,
1082 ThreadSafetyAnalyzer& Analyzer,
1088 std::function<bool (const ValueDecl*)> traverse = [&](
const ValueDecl* Vd) {
1092 BeforeSet::BeforeInfo *Info = getBeforeInfoForDecl(Vd, Analyzer);
1094 if (Info->Visited == 1)
1097 if (Info->Visited == 2)
1100 if (Info->Vect.empty())
1103 InfoVect.push_back(Info);
1105 for (
const auto *Vdb : Info->Vect) {
1107 if (FSet.containsMutexDecl(Analyzer.FactMan, Vdb)) {
1108 StringRef L1 = StartVd->
getName();
1109 StringRef L2 = Vdb->getName();
1110 Analyzer.Handler.handleLockAcquiredBefore(CapKind, L1, L2, Loc);
1113 if (traverse(Vdb)) {
1114 if (CycMap.find(Vd) == CycMap.end()) {
1115 CycMap.insert(std::make_pair(Vd,
true));
1116 StringRef L1 = Vd->getName();
1117 Analyzer.Handler.handleBeforeAfterCycle(L1, Vd->getLocation());
1127 for (
auto *Info : InfoVect)
1133 if (
const auto *CE = dyn_cast<ImplicitCastExpr>(Exp))
1136 if (
const auto *DR = dyn_cast<DeclRefExpr>(Exp))
1137 return DR->getDecl();
1139 if (
const auto *ME = dyn_cast<MemberExpr>(Exp))
1140 return ME->getMemberDecl();
1147 template <
typename Ty>
1148 class has_arg_iterator_range {
1149 using yes =
char[1];
1152 template <
typename Inner>
1153 static yes& test(Inner *I, decltype(I->args()) * =
nullptr);
1156 static no& test(...);
1159 static const bool value =
sizeof(test<Ty>(
nullptr)) ==
sizeof(yes);
1165 return A->getName();
1173 if (
const auto *RD = RT->getDecl())
1174 if (
const auto *CA = RD->getAttr<CapabilityAttr>())
1177 if (
const auto *TD = TT->getDecl())
1178 if (
const auto *CA = TD->getAttr<CapabilityAttr>())
1187 assert(VD &&
"No ValueDecl passed");
1193 template <
typename AttrTy>
1194 static typename std::enable_if<!has_arg_iterator_range<AttrTy>::value,
1202 template <
typename AttrTy>
1203 static typename std::enable_if<has_arg_iterator_range<AttrTy>::value,
1206 for (
const auto *Arg : A->args()) {
1213 bool ThreadSafetyAnalyzer::inCurrentScope(
const CapabilityExpr &CapE) {
1216 if (
const auto *
P = dyn_cast_or_null<til::Project>(CapE.
sexpr())) {
1217 const auto *VD =
P->clangDecl();
1219 return VD->getDeclContext() == CurrentMethod->getDeclContext();
1226 void ThreadSafetyAnalyzer::addLock(FactSet &FSet,
1227 std::unique_ptr<FactEntry> Entry,
1228 StringRef DiagKind,
bool ReqAttr) {
1229 if (Entry->shouldIgnore())
1232 if (!ReqAttr && !Entry->negative()) {
1235 FactEntry *Nen = FSet.findLock(FactMan, NegC);
1237 FSet.removeLock(FactMan, NegC);
1240 if (inCurrentScope(*Entry) && !Entry->asserted())
1241 Handler.handleNegativeNotHeld(DiagKind, Entry->toString(),
1247 if (Handler.issueBetaWarnings() &&
1248 !Entry->asserted() && !Entry->declared()) {
1249 GlobalBeforeSet->checkBeforeAfter(Entry->valueDecl(), FSet, *
this,
1250 Entry->loc(), DiagKind);
1254 if (FSet.findLock(FactMan, *Entry)) {
1255 if (!Entry->asserted())
1256 Handler.handleDoubleLock(DiagKind, Entry->toString(), Entry->loc());
1258 FSet.addLock(FactMan, std::move(Entry));
1264 void ThreadSafetyAnalyzer::removeLock(FactSet &FSet,
const CapabilityExpr &Cp,
1266 bool FullyRemove,
LockKind ReceivedKind,
1267 StringRef DiagKind) {
1271 const FactEntry *LDat = FSet.findLock(FactMan, Cp);
1273 Handler.handleUnmatchedUnlock(DiagKind, Cp.
toString(), UnlockLoc);
1279 if (ReceivedKind !=
LK_Generic && LDat->kind() != ReceivedKind) {
1280 Handler.handleIncorrectUnlockKind(DiagKind, Cp.
toString(),
1281 LDat->kind(), ReceivedKind, UnlockLoc);
1284 LDat->handleUnlock(FSet, FactMan, Cp, UnlockLoc, FullyRemove, Handler,
1290 template <
typename AttrType>
1291 void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
1294 if (
Attr->args_size() == 0) {
1296 CapabilityExpr Cp = SxBuilder.translateAttrExpr(
nullptr, D, Exp, SelfDecl);
1303 Mtxs.push_back_nodup(Cp);
1307 for (
const auto *Arg :
Attr->args()) {
1308 CapabilityExpr Cp = SxBuilder.translateAttrExpr(Arg, D, Exp, SelfDecl);
1315 Mtxs.push_back_nodup(Cp);
1322 template <
class AttrType>
1323 void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
1327 Expr *BrE,
bool Neg) {
1329 bool branch =
false;
1330 if (
const auto *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE))
1331 branch = BLE->getValue();
1332 else if (
const auto *ILE = dyn_cast_or_null<IntegerLiteral>(BrE))
1333 branch = ILE->getValue().getBoolValue();
1335 int branchnum = branch ? 0 : 1;
1337 branchnum = !branchnum;
1342 SE = PredBlock->
succ_end(); SI != SE && i < 2; ++SI, ++i) {
1343 if (*SI == CurrBlock && i == branchnum)
1344 getMutexIDs(Mtxs,
Attr, Exp, D);
1349 if (isa<CXXNullPtrLiteralExpr>(E) || isa<GNUNullExpr>(E)) {
1352 }
else if (
const auto *BLE = dyn_cast<CXXBoolLiteralExpr>(E)) {
1353 TCond = BLE->getValue();
1355 }
else if (
const auto *ILE = dyn_cast<IntegerLiteral>(E)) {
1356 TCond = ILE->getValue().getBoolValue();
1358 }
else if (
auto *CE = dyn_cast<ImplicitCastExpr>(E))
1366 const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(
const Stmt *Cond,
1372 if (
const auto *CallExp = dyn_cast<CallExpr>(Cond))
1374 else if (
const auto *PE = dyn_cast<ParenExpr>(Cond))
1375 return getTrylockCallExpr(PE->getSubExpr(), C, Negate);
1376 else if (
const auto *CE = dyn_cast<ImplicitCastExpr>(Cond))
1377 return getTrylockCallExpr(CE->getSubExpr(), C, Negate);
1378 else if (
const auto *EWC = dyn_cast<ExprWithCleanups>(Cond))
1379 return getTrylockCallExpr(EWC->getSubExpr(), C, Negate);
1380 else if (
const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) {
1381 const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C);
1382 return getTrylockCallExpr(E, C, Negate);
1384 else if (
const auto *UOP = dyn_cast<UnaryOperator>(Cond)) {
1385 if (UOP->getOpcode() == UO_LNot) {
1387 return getTrylockCallExpr(UOP->getSubExpr(), C, Negate);
1391 else if (
const auto *BOP = dyn_cast<BinaryOperator>(Cond)) {
1392 if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) {
1393 if (BOP->getOpcode() == BO_NE)
1398 if (!TCond) Negate = !Negate;
1399 return getTrylockCallExpr(BOP->getLHS(), C, Negate);
1403 if (!TCond) Negate = !Negate;
1404 return getTrylockCallExpr(BOP->getRHS(), C, Negate);
1408 if (BOP->getOpcode() == BO_LAnd) {
1410 return getTrylockCallExpr(BOP->getRHS(), C, Negate);
1412 if (BOP->getOpcode() == BO_LOr)
1413 return getTrylockCallExpr(BOP->getRHS(), C, Negate);
1422 void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
1423 const FactSet &ExitSet,
1432 bool Negate =
false;
1433 const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->
getBlockID()];
1434 const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
1435 StringRef CapDiagKind =
"mutex";
1437 auto *Exp =
const_cast<CallExpr *
>(getTrylockCallExpr(Cond, LVarCtx, Negate));
1441 auto *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
1442 if(!FunDecl || !FunDecl->hasAttrs())
1445 CapExprSet ExclusiveLocksToAdd;
1446 CapExprSet SharedLocksToAdd;
1449 for (
const auto *
Attr : FunDecl->attrs()) {
1451 case attr::TryAcquireCapability: {
1452 auto *A = cast<TryAcquireCapabilityAttr>(
Attr);
1453 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
1454 Exp, FunDecl, PredBlock, CurrBlock, A->getSuccessValue(),
1459 case attr::ExclusiveTrylockFunction: {
1460 const auto *A = cast<ExclusiveTrylockFunctionAttr>(
Attr);
1461 getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl,
1462 PredBlock, CurrBlock, A->getSuccessValue(), Negate);
1466 case attr::SharedTrylockFunction: {
1467 const auto *A = cast<SharedTrylockFunctionAttr>(
Attr);
1468 getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl,
1469 PredBlock, CurrBlock, A->getSuccessValue(), Negate);
1480 for (
const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)
1481 addLock(Result, llvm::make_unique<LockableFactEntry>(ExclusiveLockToAdd,
1484 for (
const auto &SharedLockToAdd : SharedLocksToAdd)
1485 addLock(Result, llvm::make_unique<LockableFactEntry>(SharedLockToAdd,
1497 class BuildLockset :
public StmtVisitor<BuildLockset> {
1498 friend class ThreadSafetyAnalyzer;
1500 ThreadSafetyAnalyzer *Analyzer;
1502 LocalVariableMap::Context LVarCtx;
1510 StringRef DiagKind);
1520 BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info)
1522 LVarCtx(Info.EntryContext), CtxIndex(Info.EntryIndex) {}
1536 void BuildLockset::warnIfMutexNotHeld(
const NamedDecl *D,
const Expr *Exp,
1542 CapabilityExpr Cp = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
1552 FactEntry *LDat = FSet.findLock(Analyzer->FactMan, !Cp);
1554 Analyzer->Handler.handleFunExcludesLock(
1561 if (!Analyzer->inCurrentScope(Cp))
1565 LDat = FSet.findLock(Analyzer->FactMan, Cp);
1567 Analyzer->Handler.handleMutexNotHeld(
"", D, POK, Cp.
toString(),
1573 FactEntry* LDat = FSet.findLockUniv(Analyzer->FactMan, Cp);
1574 bool NoError =
true;
1577 LDat = FSet.findPartialMatch(Analyzer->FactMan, Cp);
1580 std::string PartMatchStr = LDat->toString();
1581 StringRef PartMatchName(PartMatchStr);
1582 Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Cp.
toString(),
1583 LK, Loc, &PartMatchName);
1586 Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Cp.
toString(),
1592 if (NoError && LDat && !LDat->isAtLeast(LK)) {
1593 Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Cp.
toString(),
1599 void BuildLockset::warnIfMutexHeld(
const NamedDecl *D,
const Expr *Exp,
1600 Expr *MutexExp, StringRef DiagKind) {
1601 CapabilityExpr Cp = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
1609 FactEntry* LDat = FSet.findLock(Analyzer->FactMan, Cp);
1611 Analyzer->Handler.handleFunExcludesLock(
1629 while (
const auto *DRE = dyn_cast<DeclRefExpr>(Exp)) {
1630 const auto *VD = dyn_cast<
VarDecl>(DRE->getDecl()->getCanonicalDecl());
1631 if (VD && VD->isLocalVarDecl() && VD->getType()->isReferenceType()) {
1632 if (
const auto *E = VD->getInit()) {
1640 if (
const auto *UO = dyn_cast<UnaryOperator>(Exp)) {
1642 if (UO->getOpcode() == UO_Deref)
1643 checkPtAccess(UO->getSubExpr(), AK, POK);
1647 if (
const auto *AE = dyn_cast<ArraySubscriptExpr>(Exp)) {
1648 checkPtAccess(AE->getLHS(), AK, POK);
1652 if (
const auto *ME = dyn_cast<MemberExpr>(Exp)) {
1654 checkPtAccess(ME->getBase(), AK, POK);
1656 checkAccess(ME->getBase(), AK, POK);
1660 if (!D || !D->hasAttrs())
1663 if (D->hasAttr<GuardedVarAttr>() && FSet.isEmpty(Analyzer->FactMan)) {
1664 Analyzer->Handler.handleNoMutexHeld(
"mutex", D, POK, AK, Loc);
1667 for (
const auto *I : D->specific_attrs<GuardedByAttr>())
1668 warnIfMutexNotHeld(D, Exp, AK, I->getArg(), POK,
1677 if (
const auto *PE = dyn_cast<ParenExpr>(Exp)) {
1678 Exp = PE->getSubExpr();
1681 if (
const auto *CE = dyn_cast<CastExpr>(Exp)) {
1682 if (CE->getCastKind() == CK_ArrayToPointerDecay) {
1685 checkAccess(CE->getSubExpr(), AK, POK);
1688 Exp = CE->getSubExpr();
1702 if (D->
hasAttr<PtGuardedVarAttr>() && FSet.isEmpty(Analyzer->FactMan))
1703 Analyzer->Handler.handleNoMutexHeld(
"mutex", D, PtPOK, AK,
1707 warnIfMutexNotHeld(D, Exp, AK, I->getArg(), PtPOK,
1723 CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;
1724 CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
1725 CapExprSet ScopedExclusiveReqs, ScopedSharedReqs;
1726 StringRef CapDiagKind =
"mutex";
1729 bool isScopedVar =
false;
1731 if (
const auto *CD = dyn_cast<const CXXConstructorDecl>(D)) {
1733 if (PD && PD->
hasAttr<ScopedLockableAttr>())
1739 switch (At->getKind()) {
1742 case attr::AcquireCapability: {
1743 const auto *A = cast<AcquireCapabilityAttr>(At);
1744 Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd
1745 : ExclusiveLocksToAdd,
1755 case attr::AssertExclusiveLock: {
1756 const auto *A = cast<AssertExclusiveLockAttr>(At);
1758 CapExprSet AssertLocks;
1759 Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
1760 for (
const auto &AssertLock : AssertLocks)
1761 Analyzer->addLock(FSet,
1762 llvm::make_unique<LockableFactEntry>(
1767 case attr::AssertSharedLock: {
1768 const auto *A = cast<AssertSharedLockAttr>(At);
1770 CapExprSet AssertLocks;
1771 Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
1772 for (
const auto &AssertLock : AssertLocks)
1773 Analyzer->addLock(FSet,
1774 llvm::make_unique<LockableFactEntry>(
1775 AssertLock,
LK_Shared, Loc,
false,
true),
1780 case attr::AssertCapability: {
1781 const auto *A = cast<AssertCapabilityAttr>(At);
1782 CapExprSet AssertLocks;
1783 Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
1784 for (
const auto &AssertLock : AssertLocks)
1785 Analyzer->addLock(FSet,
1786 llvm::make_unique<LockableFactEntry>(
1796 case attr::ReleaseCapability: {
1797 const auto *A = cast<ReleaseCapabilityAttr>(At);
1799 Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp, D, VD);
1800 else if (A->isShared())
1801 Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp, D, VD);
1803 Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp, D, VD);
1809 case attr::RequiresCapability: {
1810 const auto *A = cast<RequiresCapabilityAttr>(At);
1811 for (
auto *Arg : A->args()) {
1817 Analyzer->getMutexIDs(A->isShared() ? ScopedSharedReqs
1818 : ScopedExclusiveReqs,
1825 case attr::LocksExcluded: {
1826 const auto *A = cast<LocksExcludedAttr>(At);
1827 for (
auto *Arg : A->args())
1840 bool Dtor = isa<CXXDestructorDecl>(D);
1841 for (
const auto &M : ExclusiveLocksToRemove)
1842 Analyzer->removeLock(FSet, M, Loc, Dtor,
LK_Exclusive, CapDiagKind);
1843 for (
const auto &M : SharedLocksToRemove)
1844 Analyzer->removeLock(FSet, M, Loc, Dtor,
LK_Shared, CapDiagKind);
1845 for (
const auto &M : GenericLocksToRemove)
1846 Analyzer->removeLock(FSet, M, Loc, Dtor,
LK_Generic, CapDiagKind);
1849 for (
const auto &M : ExclusiveLocksToAdd)
1850 Analyzer->addLock(FSet, llvm::make_unique<LockableFactEntry>(
1853 for (
const auto &M : SharedLocksToAdd)
1854 Analyzer->addLock(FSet, llvm::make_unique<LockableFactEntry>(
1863 CapabilityExpr Scp = Analyzer->SxBuilder.translateAttrExpr(&DRE,
nullptr);
1865 std::copy(ScopedExclusiveReqs.begin(), ScopedExclusiveReqs.end(),
1866 std::back_inserter(ExclusiveLocksToAdd));
1867 std::copy(ScopedSharedReqs.begin(), ScopedSharedReqs.end(),
1868 std::back_inserter(SharedLocksToAdd));
1869 Analyzer->addLock(FSet,
1870 llvm::make_unique<ScopedLockableFactEntry>(
1871 Scp, MLoc, ExclusiveLocksToAdd, SharedLocksToAdd),
1900 LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);
1908 void BuildLockset::VisitCastExpr(
CastExpr *CE) {
1914 void BuildLockset::VisitCallExpr(
CallExpr *Exp) {
1915 bool ExamineArgs =
true;
1916 bool OperatorFun =
false;
1918 if (
const auto *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
1919 const auto *ME = dyn_cast<
MemberExpr>(CE->getCallee());
1924 if (ME->isArrow()) {
1926 checkPtAccess(CE->getImplicitObjectArgument(),
AK_Read);
1928 checkPtAccess(CE->getImplicitObjectArgument(),
AK_Read);
1931 checkAccess(CE->getImplicitObjectArgument(),
AK_Read);
1933 checkAccess(CE->getImplicitObjectArgument(),
AK_Read);
1936 }
else if (
const auto *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) {
1939 auto OEop = OE->getOperator();
1942 ExamineArgs =
false;
1944 const Expr *Source = OE->getArg(1);
1951 case OO_Subscript: {
1952 const Expr *Obj = OE->getArg(0);
1954 if (!(OEop == OO_Star && OE->getNumArgs() > 1)) {
1962 const Expr *Obj = OE->getArg(0);
1976 if (!FD->hasAttr<NoThreadSafetyAnalysisAttr>()) {
1977 unsigned Fn = FD->getNumParams();
1983 if (isa<CXXMethodDecl>(FD)) {
1994 unsigned n = (Fn < Cn) ? Fn : Cn;
1996 for (; i < n; ++i) {
2007 auto *D = dyn_cast_or_null<NamedDecl>(Exp->
getCalleeDecl());
2029 for (
auto *Ctor : RD->
ctors()) {
2030 if (Ctor->isDeleted())
2032 if (Ctor->isMoveConstructor())
2044 CD,
true, Args,
false,
false,
false,
false,
2049 void BuildLockset::VisitDeclStmt(
DeclStmt *S) {
2051 LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
2054 if (
auto *VD = dyn_cast_or_null<VarDecl>(D)) {
2061 if (
auto *EWC = dyn_cast<ExprWithCleanups>(E))
2062 E = EWC->getSubExpr();
2063 if (
auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E))
2064 E = BTE->getSubExpr();
2066 if (
const auto *CE = dyn_cast<CXXConstructExpr>(E)) {
2067 const auto *CtorD = dyn_cast_or_null<NamedDecl>(CE->getConstructor());
2068 if (!CtorD || !CtorD->hasAttrs())
2070 handleCall(E, CtorD, VD);
2071 }
else if (isa<CallExpr>(E) && E->
isRValue()) {
2078 if (!RD || !RD->hasAttr<ScopedLockableAttr>())
2103 void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,
2104 const FactSet &FSet2,
2109 FactSet FSet1Orig = FSet1;
2112 for (
const auto &Fact : FSet2) {
2113 const FactEntry *LDat1 =
nullptr;
2114 const FactEntry *LDat2 = &FactMan[Fact];
2115 FactSet::iterator Iter1 = FSet1.findLockIter(FactMan, *LDat2);
2116 if (Iter1 != FSet1.end()) LDat1 = &FactMan[*Iter1];
2119 if (LDat1->kind() != LDat2->kind()) {
2120 Handler.handleExclusiveAndShared(
"mutex", LDat2->toString(),
2121 LDat2->loc(), LDat1->loc());
2127 else if (Modify && LDat1->asserted() && !LDat2->asserted()) {
2132 LDat2->handleRemovalFromIntersection(FSet2, FactMan, JoinLoc, LEK1,
2138 for (
const auto &Fact : FSet1Orig) {
2139 const FactEntry *LDat1 = &FactMan[Fact];
2140 const FactEntry *LDat2 = FSet2.findLock(FactMan, *LDat1);
2143 LDat1->handleRemovalFromIntersection(FSet1Orig, FactMan, JoinLoc, LEK2,
2146 FSet1.removeLock(FactMan, *LDat1);
2160 if (isa<CXXThrowExpr>(S->getStmt()))
2175 if (!walker.
init(AC))
2183 const auto *CurrentFunction = dyn_cast<
FunctionDecl>(D);
2186 if (D->
hasAttr<NoThreadSafetyAnalysisAttr>())
2193 if (isa<CXXConstructorDecl>(D))
2195 if (isa<CXXDestructorDecl>(D))
2198 Handler.enterFunction(CurrentFunction);
2201 CFGBlockInfo::getEmptyBlockInfo(LocalVarMap));
2213 LocalVarMap.traverseCFG(CFGraph, SortedGraph, BlockInfo);
2218 CapExprSet ExclusiveLocksAcquired;
2219 CapExprSet SharedLocksAcquired;
2220 CapExprSet LocksReleased;
2227 FactSet &InitialLockset = BlockInfo[FirstBlock->
getBlockID()].EntrySet;
2229 CapExprSet ExclusiveLocksToAdd;
2230 CapExprSet SharedLocksToAdd;
2231 StringRef CapDiagKind =
"mutex";
2236 if (
const auto *A = dyn_cast<RequiresCapabilityAttr>(
Attr)) {
2237 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2240 }
else if (
const auto *A = dyn_cast<ReleaseCapabilityAttr>(
Attr)) {
2243 if (A->args_size() == 0)
2246 getMutexIDs(ExclusiveLocksToAdd, A,
nullptr, D);
2247 getMutexIDs(LocksReleased, A,
nullptr, D);
2249 }
else if (
const auto *A = dyn_cast<AcquireCapabilityAttr>(
Attr)) {
2250 if (A->args_size() == 0)
2252 getMutexIDs(A->isShared() ? SharedLocksAcquired
2253 : ExclusiveLocksAcquired,
2256 }
else if (isa<ExclusiveTrylockFunctionAttr>(
Attr)) {
2259 }
else if (isa<SharedTrylockFunctionAttr>(
Attr)) {
2262 }
else if (isa<TryAcquireCapabilityAttr>(
Attr)) {
2269 for (
const auto &Mu : ExclusiveLocksToAdd) {
2270 auto Entry = llvm::make_unique<LockableFactEntry>(Mu,
LK_Exclusive, Loc);
2271 Entry->setDeclared(
true);
2272 addLock(InitialLockset, std::move(Entry), CapDiagKind,
true);
2274 for (
const auto &Mu : SharedLocksToAdd) {
2275 auto Entry = llvm::make_unique<LockableFactEntry>(Mu,
LK_Shared, Loc);
2276 Entry->setDeclared(
true);
2277 addLock(InitialLockset, std::move(Entry), CapDiagKind,
true);
2281 for (
const auto *CurrBlock : *SortedGraph) {
2283 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
2286 VisitedBlocks.
insert(CurrBlock);
2301 bool LocksetInitialized =
false;
2304 PE = CurrBlock->
pred_end(); PI != PE; ++PI) {
2306 if (*PI ==
nullptr || !VisitedBlocks.
alreadySet(*PI))
2309 int PrevBlockID = (*PI)->getBlockID();
2310 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
2317 CurrBlockInfo->Reachable =
true;
2323 if (
const Stmt *Terminator = (*PI)->getTerminator()) {
2324 if (isa<ContinueStmt>(Terminator) || isa<BreakStmt>(Terminator)) {
2325 SpecialBlocks.push_back(*PI);
2330 FactSet PrevLockset;
2331 getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, *PI, CurrBlock);
2333 if (!LocksetInitialized) {
2334 CurrBlockInfo->EntrySet = PrevLockset;
2335 LocksetInitialized =
true;
2337 intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset,
2338 CurrBlockInfo->EntryLoc,
2344 if (!CurrBlockInfo->Reachable)
2349 for (
const auto *PrevBlock : SpecialBlocks) {
2350 int PrevBlockID = PrevBlock->getBlockID();
2351 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
2353 if (!LocksetInitialized) {
2354 CurrBlockInfo->EntrySet = PrevBlockInfo->ExitSet;
2355 LocksetInitialized =
true;
2362 const Stmt *Terminator = PrevBlock->getTerminator();
2363 bool IsLoop = Terminator && isa<ContinueStmt>(Terminator);
2365 FactSet PrevLockset;
2366 getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet,
2367 PrevBlock, CurrBlock);
2370 intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset,
2371 PrevBlockInfo->ExitLoc,
2378 BuildLockset LocksetBuilder(
this, *CurrBlockInfo);
2381 for (
const auto &BI : *CurrBlock) {
2382 switch (BI.getKind()) {
2385 LocksetBuilder.Visit(const_cast<Stmt *>(CS.
getStmt()));
2393 if (!DD->hasAttrs())
2400 LocksetBuilder.handleCall(&DRE, DD);
2407 CurrBlockInfo->ExitSet = LocksetBuilder.FSet;
2414 SE = CurrBlock->succ_end(); SI != SE; ++SI) {
2416 if (*SI ==
nullptr || !VisitedBlocks.
alreadySet(*SI))
2420 CFGBlockInfo *PreLoop = &BlockInfo[FirstLoopBlock->
getBlockID()];
2421 CFGBlockInfo *LoopEnd = &BlockInfo[CurrBlockID];
2422 intersectAndWarn(LoopEnd->ExitSet, PreLoop->EntrySet,
2433 if (!Final->Reachable)
2437 FactSet ExpectedExitSet = Initial->EntrySet;
2443 for (
const auto &Lock : ExclusiveLocksAcquired)
2444 ExpectedExitSet.addLock(FactMan, llvm::make_unique<LockableFactEntry>(
2446 for (
const auto &Lock : SharedLocksAcquired)
2447 ExpectedExitSet.addLock(FactMan, llvm::make_unique<LockableFactEntry>(
2449 for (
const auto &Lock : LocksReleased)
2450 ExpectedExitSet.removeLock(FactMan, Lock);
2453 intersectAndWarn(ExpectedExitSet, Final->ExitSet,
2459 Handler.leaveFunction(CurrentFunction);
2472 ThreadSafetyAnalyzer Analyzer(Handler, *BSet);
2473 Analyzer.runAnalysis(AC);
2487 llvm_unreachable(
"Unknown AccessKind");
const PostOrderCFGView * getSortedGraph() const
Represents a function declaration or definition.
Passing a guarded variable by reference.
bool equals(const CapabilityExpr &other) const
A (possibly-)qualified type.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
AdjacentBlocks::const_iterator const_pred_iterator
const Stmt * getStmt() const
const CFG * getGraph() const
succ_iterator succ_begin()
TypePropertyCache< Private > Cache
Stmt - This represents one statement.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
virtual void handleUnmatchedUnlock(StringRef Kind, Name LockName, SourceLocation Loc)
Warn about unlock function calls that do not have a prior matching lock expression.
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee...
C Language Family Type Representation.
static StringRef ClassifyDiagnostic(const CapabilityAttr *A)
unsigned getBlockID() const
QualType getNonReferenceType() const
If Type is a reference type (e.g., const int&), returns the type that the reference refers to ("const...
Represents a call to a C++ constructor.
T castAs() const
Convert to the specified CFGElement type, asserting that this CFGElement is of the desired type...
LockKind getLockKindFromAccessKind(AccessKind AK)
Helper function that returns a LockKind required for the given level of access.
Represents a C++ constructor within a class.
std::string toString() const
const CXXDestructorDecl * getDestructorDecl(ASTContext &astContext) const
Exclusive/writer lock of a mutex.
ProtectedOperationKind
This enum distinguishes between different kinds of operations that may need to be protected by locks...
bool isTrivialType(const ASTContext &Context) const
Return true if this is a trivial type per (C++0x [basic.types]p9)
Represents a variable declaration or definition.
ASTContext & getASTContext() const
const T * getAs() const
Member-template getAs<specific type>'.
Expr * IgnoreImplicit() LLVM_READONLY
IgnoreImplicit - Skip past any implicit AST nodes which might surround this expression.
const Stmt * getTriggerStmt() const
static void warnInvalidLock(ThreadSafetyHandler &Handler, const Expr *MutexExp, const NamedDecl *D, const Expr *DeclExp, StringRef Kind)
Issue a warning about an invalid lock expression.
static bool isAssignmentOp(Opcode Opc)
Represents a parameter to a function.
Defines the clang::Expr interface and subclasses for C++ expressions.
void threadSafetyCleanup(BeforeSet *Cache)
static const ValueDecl * getValueDecl(const Expr *Exp)
Gets the value decl pointer from DeclRefExprs or MemberExprs.
LockKind
This enum distinguishes between different kinds of lock actions.
CFGBlockSide
A side (entry or exit) of a CFG node.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
const til::SExpr * sexpr() const
static CXXConstructExpr * Create(const ASTContext &C, QualType T, SourceLocation Loc, CXXConstructorDecl *Ctor, bool Elidable, ArrayRef< Expr *> Args, bool HadMultipleCandidates, bool ListInitialization, bool StdInitListInitialization, bool ZeroInitialization, ConstructionKind ConstructKind, SourceRange ParenOrBraceRange)
AnalysisDeclContext contains the context data for the function or method under analysis.
Represents C++ object destructor implicitly generated for automatic object or temporary bound to cons...
bool isReferenceType() const
const DeclGroupRef getDeclGroup() const
static bool neverReturns(const CFGBlock *B)
AdjacentBlocks::const_iterator const_succ_iterator
bool alreadySet(const CFGBlock *Block)
Check if the bit for a CFGBlock has been already set.
virtual void handleInvalidLockExp(StringRef Kind, SourceLocation Loc)
Warn about lock expressions which fail to resolve to lockable objects.
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified...
Implements a set of CFGBlocks using a BitVector.
A builtin binary operation expression such as "x + y" or "x <= y".
Expr * IgnoreParenCasts() LLVM_READONLY
IgnoreParenCasts - Ignore parentheses and casts.
const VarDecl * getVarDecl() const
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Shared/reader lock of a mutex.
virtual ~ThreadSafetyHandler()
Passing a pt-guarded variable by reference.
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
bool init(AnalysisDeclContext &AC)
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
Handler class for thread safety warnings.
Represents a single basic block in a source-level CFG.
Dereferencing a variable (e.g. p in *p = 5;)
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Expr - This represents one expression.
Stmt * getTerminatorCondition(bool StripParens=true)
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt...
Represents a C++ destructor within a class.
Defines an enumeration for C++ overloaded operators.
void checkBeforeAfter(const ValueDecl *Vd, const FactSet &FSet, ThreadSafetyAnalyzer &Analyzer, SourceLocation Loc, StringRef CapKind)
Return true if any mutexes in FSet are in the acquired_before set of Vd.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
AccessKind
This enum distinguishes between different ways to access (read or write) a variable.
QualType getRecordType(const RecordDecl *Decl) const
UnaryOperator - This represents the unary-expression's (except sizeof and alignof), the postinc/postdec operators from postfix-expression, and various extensions.
Making a function call (e.g. fool())
virtual void printName(raw_ostream &os) const
Reading or writing a variable (e.g. x in x = 5;)
std::pair< llvm::NoneType, bool > insert(const CFGBlock *Block)
Set the bit associated with a particular CFGBlock.
BeforeInfo * insertAttrExprs(const ValueDecl *Vd, ThreadSafetyAnalyzer &Analyzer)
Process acquired_before and acquired_after attributes on Vd.
Encodes a location in the source.
Expr * getSubExpr() const
CastKind getCastKind() const
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
ASTContext & getASTContext() const LLVM_READONLY
SourceLocation getLocStart() const LLVM_READONLY
DeclStmt - Adaptor class for mixing declarations with statements and expressions. ...
Represents a static or instance method of a struct/union/class.
StmtVisitor - This class implements a simple visitor for Stmt subclasses.
SourceLocation getLocation() const
unsigned getNumBlockIDs() const
Returns the total number of BlockIDs allocated (which start at 0).
SourceLocation getLocEnd() const LLVM_READONLY
static bool getStaticBooleanValue(Expr *E, bool &TCond)
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Optional< T > getAs() const
Convert to the specified CFGElement type, returning None if this CFGElement is not of the desired typ...
bool shouldIgnore() const
Defines various enumerations that describe declaration and type specifiers.
pred_iterator pred_begin()
static CXXConstructorDecl * findConstructorForByValueReturn(const CXXRecordDecl *RD)
Dataflow Directional Tag Classes.
SourceLocation getLocStart() const LLVM_READONLY
bool isValid() const
Return true if this is a valid SourceLocation object.
BeforeInfo * getBeforeInfoForDecl(const ValueDecl *Vd, ThreadSafetyAnalyzer &Analyzer)
void runThreadSafetyAnalysis(AnalysisDeclContext &AC, ThreadSafetyHandler &Handler, BeforeSet **Bset)
Check a function's CFG for thread-safety violations.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return 0.
bool isCopyConstructor(unsigned &TypeQuals) const
Whether this constructor is a copy constructor (C++ [class.copy]p2, which can be used to copy the cla...
const Expr * getInit() const
const CXXRecordDecl * getParent() const
Returns the parent of this method declaration, which is the class in which this method is defined...
std::string toString(const til::SExpr *E)
A helper class that allows the use of isa/cast/dyncast to detect TagType objects of structs/unions/cl...
void dump() const
Dumps the specified AST fragment and all subtrees to llvm::errs().
static Expr * buildFakeCtorCall(CXXConstructorDecl *CD, ArrayRef< Expr *> Args, SourceLocation Loc)
Expr * getArg(unsigned Arg)
Return the specified argument.
const NamedDecl * getDecl() const
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
bool hasNoReturnElement() const
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate.h) and friends (in DeclFriend.h).
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
Defines the clang::SourceLocation class and associated facilities.
Represents a C++ struct/union/class.
Represents a top-level expression in a basic block.
unsigned kind
All of the diagnostics that can be emitted by the frontend.
virtual void handleMutexHeldEndOfScope(StringRef Kind, Name LockName, SourceLocation LocLocked, SourceLocation LocEndOfScope, LockErrorKind LEK)
Warn about situations where a mutex is sometimes held and sometimes not.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
A reference to a declared variable, function, enum, etc.
const ValueDecl * valueDecl() const
bool isPointerType() const
static llvm::ImmutableListFactory< const FieldRegion * > Factory
bool matches(const til::SExpr *E1, const til::SExpr *E2)
An l-value expression is a reference to an object with independent storage.
A trivial tuple used to represent a source range.
This represents a decl that may have a name.
static void findBlockLocations(CFG *CFGraph, const PostOrderCFGView *SortedGraph, std::vector< CFGBlockInfo > &BlockInfo)
Find the appropriate source locations to use when producing diagnostics for each block in the CFG...
attr::Kind getKind() const
Attr - This represents one attribute.
SourceLocation getLocation() const
Expr * IgnoreParens() LLVM_READONLY
IgnoreParens - Ignore parentheses.
Can be either Shared or Exclusive.