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/PointerIntPair.h" 45 #include "llvm/ADT/STLExtras.h" 46 #include "llvm/ADT/SmallVector.h" 47 #include "llvm/ADT/StringRef.h" 48 #include "llvm/Support/Allocator.h" 49 #include "llvm/Support/Casting.h" 50 #include "llvm/Support/ErrorHandling.h" 51 #include "llvm/Support/raw_ostream.h" 58 #include <type_traits> 62 using namespace clang;
63 using namespace threadSafety;
71 const Expr *DeclExp, StringRef
Kind) {
85 class CapExprSet :
public SmallVector<CapabilityExpr, 4> {
89 iterator It = std::find_if(begin(), end(),
123 bool Asrt,
bool Declrd =
false)
126 virtual ~FactEntry() =
default;
130 bool asserted()
const {
return Asserted; }
131 bool declared()
const {
return Declared; }
133 void setDeclared(
bool D) { Declared = D; }
136 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
139 virtual void handleLock(FactSet &FSet, FactManager &FactMan,
141 StringRef DiagKind)
const = 0;
142 virtual void handleUnlock(FactSet &FSet, FactManager &FactMan,
145 StringRef DiagKind)
const = 0;
153 using FactID =
unsigned short;
159 std::vector<std::unique_ptr<const FactEntry>> Facts;
162 FactID newFact(std::unique_ptr<FactEntry> Entry) {
163 Facts.push_back(std::move(Entry));
164 return static_cast<unsigned short>(Facts.size() - 1);
167 const FactEntry &operator[](FactID F)
const {
return *Facts[F]; }
185 using const_iterator = FactVec::const_iterator;
187 iterator begin() {
return FactIDs.begin(); }
188 const_iterator begin()
const {
return FactIDs.begin(); }
190 iterator end() {
return FactIDs.end(); }
191 const_iterator end()
const {
return FactIDs.end(); }
193 bool isEmpty()
const {
return FactIDs.size() == 0; }
196 bool isEmpty(FactManager &FactMan)
const {
197 for (
const auto FID : *
this) {
198 if (!FactMan[FID].negative())
204 void addLockByID(FactID
ID) { FactIDs.push_back(ID); }
206 FactID addLock(FactManager &FM, std::unique_ptr<FactEntry> Entry) {
207 FactID F = FM.newFact(std::move(Entry));
208 FactIDs.push_back(F);
213 unsigned n = FactIDs.size();
217 for (
unsigned i = 0; i < n-1; ++i) {
218 if (FM[FactIDs[i]].
matches(CapE)) {
219 FactIDs[i] = FactIDs[n-1];
224 if (FM[FactIDs[n-1]].
matches(CapE)) {
232 return std::find_if(begin(), end(), [&](FactID ID) {
233 return FM[
ID].matches(CapE);
237 const FactEntry *findLock(FactManager &FM,
const CapabilityExpr &CapE)
const {
238 auto I = std::find_if(begin(), end(), [&](FactID ID) {
239 return FM[
ID].matches(CapE);
241 return I != end() ? &FM[*I] :
nullptr;
244 const FactEntry *findLockUniv(FactManager &FM,
246 auto I = std::find_if(begin(), end(), [&](FactID ID) ->
bool {
247 return FM[
ID].matchesUniv(CapE);
249 return I != end() ? &FM[*I] :
nullptr;
252 const FactEntry *findPartialMatch(FactManager &FM,
254 auto I = std::find_if(begin(), end(), [&](FactID ID) ->
bool {
255 return FM[
ID].partiallyMatches(CapE);
257 return I != end() ? &FM[*I] :
nullptr;
260 bool containsMutexDecl(FactManager &FM,
const ValueDecl* Vd)
const {
261 auto I = std::find_if(begin(), end(), [&](FactID ID) ->
bool {
262 return FM[
ID].valueDecl() == Vd;
268 class ThreadSafetyAnalyzer;
273 namespace threadSafety {
283 BeforeInfo() =
default;
284 BeforeInfo(BeforeInfo &&) =
default;
288 llvm::DenseMap<const ValueDecl *, std::unique_ptr<BeforeInfo>>;
289 using CycleMap = llvm::DenseMap<const ValueDecl *, bool>;
294 BeforeInfo* insertAttrExprs(
const ValueDecl* Vd,
295 ThreadSafetyAnalyzer& Analyzer);
297 BeforeInfo *getBeforeInfoForDecl(
const ValueDecl *Vd,
298 ThreadSafetyAnalyzer &Analyzer);
300 void checkBeforeAfter(
const ValueDecl* Vd,
302 ThreadSafetyAnalyzer& Analyzer,
315 class LocalVariableMap;
317 using LocalVarContext = llvm::ImmutableMap<const NamedDecl *, unsigned>;
325 struct CFGBlockInfo {
333 LocalVarContext EntryContext;
336 LocalVarContext ExitContext;
348 bool Reachable =
false;
351 return Side == CBS_Entry ? EntrySet : ExitSet;
355 return Side == CBS_Entry ? EntryLoc : ExitLoc;
359 CFGBlockInfo(LocalVarContext EmptyCtx)
360 : EntryContext(EmptyCtx), ExitContext(EmptyCtx) {}
363 static CFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M);
379 class LocalVariableMap {
381 using Context = LocalVarContext;
387 struct VarDefinition {
389 friend class LocalVariableMap;
395 const Expr *Exp =
nullptr;
403 bool isReference() {
return !Exp; }
408 : Dec(D), Exp(E), Ctx(C) {}
411 VarDefinition(
const NamedDecl *D,
unsigned R, Context C)
412 : Dec(D), Ref(R), Ctx(C) {}
416 Context::Factory ContextFactory;
417 std::vector<VarDefinition> VarDefinitions;
418 std::vector<unsigned> CtxIndices;
419 std::vector<std::pair<const Stmt *, Context>> SavedContexts;
424 VarDefinitions.push_back(VarDefinition(
nullptr, 0u, getEmptyContext()));
428 const VarDefinition* lookup(
const NamedDecl *D, Context Ctx) {
429 const unsigned *i = Ctx.lookup(D);
432 assert(*i < VarDefinitions.size());
433 return &VarDefinitions[*i];
440 const unsigned *
P = Ctx.lookup(D);
446 if (VarDefinitions[i].Exp) {
447 Ctx = VarDefinitions[i].Ctx;
448 return VarDefinitions[i].Exp;
450 i = VarDefinitions[i].Ref;
455 Context getEmptyContext() {
return ContextFactory.getEmptyMap(); }
460 Context getNextContext(
unsigned &CtxIndex,
const Stmt *S, Context
C) {
461 if (SavedContexts[CtxIndex+1].first == S) {
463 Context Result = SavedContexts[CtxIndex].second;
469 void dumpVarDefinitionName(
unsigned i) {
471 llvm::errs() <<
"Undefined";
474 const NamedDecl *Dec = VarDefinitions[i].Dec;
476 llvm::errs() <<
"<<NULL>>";
480 llvm::errs() <<
"." << i <<
" " << ((
const void*) Dec);
485 for (
unsigned i = 1, e = VarDefinitions.size(); i < e; ++i) {
486 const Expr *Exp = VarDefinitions[i].Exp;
487 unsigned Ref = VarDefinitions[i].Ref;
489 dumpVarDefinitionName(i);
490 llvm::errs() <<
" = ";
491 if (Exp) Exp->
dump();
493 dumpVarDefinitionName(Ref);
494 llvm::errs() <<
"\n";
500 void dumpContext(Context C) {
501 for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) {
504 const unsigned *i = C.lookup(D);
505 llvm::errs() <<
" -> ";
506 dumpVarDefinitionName(*i);
507 llvm::errs() <<
"\n";
513 std::vector<CFGBlockInfo> &BlockInfo);
516 friend class VarMapBuilder;
519 unsigned getContextIndex() {
return SavedContexts.size()-1; }
522 void saveContext(
const Stmt *S, Context C) {
523 SavedContexts.push_back(std::make_pair(S, C));
528 Context addDefinition(
const NamedDecl *D,
const Expr *Exp, Context Ctx) {
529 assert(!Ctx.contains(D));
530 unsigned newID = VarDefinitions.size();
531 Context NewCtx = ContextFactory.add(Ctx, D, newID);
532 VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
537 Context addReference(
const NamedDecl *D,
unsigned i, Context Ctx) {
538 unsigned newID = VarDefinitions.size();
539 Context NewCtx = ContextFactory.add(Ctx, D, newID);
540 VarDefinitions.push_back(VarDefinition(D, i, Ctx));
546 Context updateDefinition(
const NamedDecl *D,
Expr *Exp, Context Ctx) {
547 if (Ctx.contains(D)) {
548 unsigned newID = VarDefinitions.size();
549 Context NewCtx = ContextFactory.remove(Ctx, D);
550 NewCtx = ContextFactory.add(NewCtx, D, newID);
551 VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
559 Context clearDefinition(
const NamedDecl *D, Context Ctx) {
560 Context NewCtx = Ctx;
561 if (NewCtx.contains(D)) {
562 NewCtx = ContextFactory.remove(NewCtx, D);
563 NewCtx = ContextFactory.add(NewCtx, D, 0);
569 Context removeDefinition(
const NamedDecl *D, Context Ctx) {
570 Context NewCtx = Ctx;
571 if (NewCtx.contains(D)) {
572 NewCtx = ContextFactory.remove(NewCtx, D);
577 Context intersectContexts(Context C1, Context C2);
578 Context createReferenceContext(Context C);
579 void intersectBackEdge(Context C1, Context C2);
585 CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) {
586 return CFGBlockInfo(M.getEmptyContext());
594 LocalVariableMap* VMap;
595 LocalVariableMap::Context Ctx;
597 VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context C)
598 : VMap(VM), Ctx(C) {}
600 void VisitDeclStmt(
const DeclStmt *S);
607 void VarMapBuilder::VisitDeclStmt(
const DeclStmt *S) {
608 bool modifiedCtx =
false;
610 for (
const auto *D : DGrp) {
611 if (
const auto *VD = dyn_cast_or_null<VarDecl>(D)) {
612 const Expr *E = VD->getInit();
617 Ctx = VMap->addDefinition(VD, E, Ctx);
623 VMap->saveContext(S, Ctx);
627 void VarMapBuilder::VisitBinaryOperator(
const BinaryOperator *BO) {
634 if (
const auto *DRE = dyn_cast<DeclRefExpr>(LHSExp)) {
636 if (Ctx.lookup(VDec)) {
638 Ctx = VMap->updateDefinition(VDec, BO->
getRHS(), Ctx);
641 Ctx = VMap->clearDefinition(VDec, Ctx);
642 VMap->saveContext(BO, Ctx);
650 LocalVariableMap::Context
651 LocalVariableMap::intersectContexts(Context C1, Context C2) {
653 for (
const auto &
P : C1) {
655 const unsigned *i2 = C2.lookup(Dec);
657 Result = removeDefinition(Dec, Result);
658 else if (*i2 !=
P.second)
659 Result = clearDefinition(Dec, Result);
667 LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) {
668 Context Result = getEmptyContext();
669 for (
const auto &
P : C)
670 Result = addReference(
P.first,
P.second, Result);
677 void LocalVariableMap::intersectBackEdge(Context C1, Context C2) {
678 for (
const auto &
P : C1) {
679 unsigned i1 =
P.second;
680 VarDefinition *VDef = &VarDefinitions[i1];
681 assert(VDef->isReference());
683 const unsigned *i2 = C2.lookup(
P.first);
684 if (!i2 || (*i2 != i1))
726 void LocalVariableMap::traverseCFG(
CFG *CFGraph,
728 std::vector<CFGBlockInfo> &BlockInfo) {
733 for (
const auto *CurrBlock : *SortedGraph) {
734 unsigned CurrBlockID = CurrBlock->getBlockID();
735 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
737 VisitedBlocks.
insert(CurrBlock);
740 bool HasBackEdges =
false;
743 PE = CurrBlock->pred_end(); PI != PE; ++PI) {
745 if (*PI ==
nullptr || !VisitedBlocks.
alreadySet(*PI)) {
750 unsigned PrevBlockID = (*PI)->getBlockID();
751 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
754 CurrBlockInfo->EntryContext = PrevBlockInfo->ExitContext;
758 CurrBlockInfo->EntryContext =
759 intersectContexts(CurrBlockInfo->EntryContext,
760 PrevBlockInfo->ExitContext);
767 CurrBlockInfo->EntryContext =
768 createReferenceContext(CurrBlockInfo->EntryContext);
771 saveContext(
nullptr, CurrBlockInfo->EntryContext);
772 CurrBlockInfo->EntryIndex = getContextIndex();
775 VarMapBuilder VMapBuilder(
this, CurrBlockInfo->EntryContext);
776 for (
const auto &BI : *CurrBlock) {
777 switch (BI.getKind()) {
780 VMapBuilder.Visit(CS.
getStmt());
787 CurrBlockInfo->ExitContext = VMapBuilder.Ctx;
791 SE = CurrBlock->succ_end(); SI != SE; ++SI) {
793 if (*SI ==
nullptr || !VisitedBlocks.
alreadySet(*SI))
797 Context LoopBegin = BlockInfo[FirstLoopBlock->
getBlockID()].EntryContext;
798 Context LoopEnd = CurrBlockInfo->ExitContext;
799 intersectBackEdge(LoopBegin, LoopEnd);
805 saveContext(
nullptr, BlockInfo[exitID].ExitContext);
812 std::vector<CFGBlockInfo> &BlockInfo) {
813 for (
const auto *CurrBlock : *SortedGraph) {
814 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()];
818 if (
const Stmt *S = CurrBlock->getTerminatorStmt()) {
819 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->
getBeginLoc();
822 BE = CurrBlock->rend(); BI != BE; ++BI) {
825 CurrBlockInfo->ExitLoc = CS->getStmt()->getBeginLoc();
831 if (CurrBlockInfo->ExitLoc.isValid()) {
834 for (
const auto &BI : *CurrBlock) {
837 CurrBlockInfo->EntryLoc = CS->getStmt()->getBeginLoc();
841 }
else if (CurrBlock->pred_size() == 1 && *CurrBlock->pred_begin() &&
842 CurrBlock != &CFGraph->
getExit()) {
845 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =
846 BlockInfo[(*CurrBlock->pred_begin())->getBlockID()].ExitLoc;
853 class LockableFactEntry :
public FactEntry {
860 bool Mng =
false,
bool Asrt =
false)
861 : FactEntry(CE, LK, Loc, Asrt), Managed(Mng) {}
864 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
867 if (!Managed && !asserted() && !negative() && !isUniversal()) {
873 void handleLock(FactSet &FSet, FactManager &FactMan,
const FactEntry &entry,
875 StringRef DiagKind)
const override {
879 void handleUnlock(FactSet &FSet, FactManager &FactMan,
882 StringRef DiagKind)
const override {
883 FSet.removeLock(FactMan, Cp);
885 FSet.addLock(FactMan, std::make_unique<LockableFactEntry>(
891 class ScopedLockableFactEntry :
public FactEntry {
893 enum UnderlyingCapabilityKind {
896 UCK_ReleasedExclusive,
899 using UnderlyingCapability =
900 llvm::PointerIntPair<const til::SExpr *, 2, UnderlyingCapabilityKind>;
909 UnderlyingMutexes.emplace_back(M.
sexpr(), UCK_Acquired);
913 UnderlyingMutexes.emplace_back(M.
sexpr(), UCK_Acquired);
917 UnderlyingMutexes.emplace_back(M.
sexpr(), UCK_ReleasedExclusive);
921 UnderlyingMutexes.emplace_back(M.
sexpr(), UCK_ReleasedShared);
925 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
928 for (
const auto &UnderlyingMutex : UnderlyingMutexes) {
929 const auto *Entry = FSet.findLock(
931 if ((UnderlyingMutex.getInt() == UCK_Acquired && Entry) ||
932 (UnderlyingMutex.getInt() != UCK_Acquired && !Entry)) {
936 "mutex",
sx::toString(UnderlyingMutex.getPointer()), loc(), JoinLoc,
942 void handleLock(FactSet &FSet, FactManager &FactMan,
const FactEntry &entry,
944 StringRef DiagKind)
const override {
945 for (
const auto &UnderlyingMutex : UnderlyingMutexes) {
948 if (UnderlyingMutex.getInt() == UCK_Acquired)
949 lock(FSet, FactMan, UnderCp, entry.kind(), entry.loc(), &Handler,
952 unlock(FSet, FactMan, UnderCp, entry.loc(), &Handler, DiagKind);
956 void handleUnlock(FactSet &FSet, FactManager &FactMan,
959 StringRef DiagKind)
const override {
960 assert(!Cp.
negative() &&
"Managing object cannot be negative.");
961 for (
const auto &UnderlyingMutex : UnderlyingMutexes) {
967 if (UnderlyingMutex.getInt() == UCK_Acquired) {
968 unlock(FSet, FactMan, UnderCp, UnlockLoc, TSHandler, DiagKind);
970 LockKind kind = UnderlyingMutex.getInt() == UCK_ReleasedShared
973 lock(FSet, FactMan, UnderCp, kind, UnlockLoc, TSHandler, DiagKind);
977 FSet.removeLock(FactMan, Cp);
981 void lock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr &Cp,
983 StringRef DiagKind)
const {
984 if (
const FactEntry *Fact = FSet.findLock(FactMan, Cp)) {
988 FSet.removeLock(FactMan, !Cp);
989 FSet.addLock(FactMan,
990 std::make_unique<LockableFactEntry>(Cp, kind, loc));
994 void unlock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr &Cp,
996 StringRef DiagKind)
const {
997 if (FSet.findLock(FactMan, Cp)) {
998 FSet.removeLock(FactMan, Cp);
999 FSet.addLock(FactMan, std::make_unique<LockableFactEntry>(
1001 }
else if (Handler) {
1008 class ThreadSafetyAnalyzer {
1009 friend class BuildLockset;
1012 llvm::BumpPtrAllocator Bpa;
1018 LocalVariableMap LocalVarMap;
1019 FactManager FactMan;
1020 std::vector<CFGBlockInfo> BlockInfo;
1026 : Arena(&Bpa), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}
1030 void addLock(FactSet &FSet, std::unique_ptr<FactEntry> Entry,
1031 StringRef DiagKind,
bool ReqAttr =
false);
1034 StringRef DiagKind);
1036 template <
typename AttrType>
1037 void getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
const Expr *Exp,
1040 template <
class AttrType>
1041 void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
const Expr *Exp,
1044 Expr *BrE,
bool Neg);
1046 const CallExpr* getTrylockCallExpr(
const Stmt *Cond, LocalVarContext
C,
1049 void getEdgeLockset(FactSet &Result,
const FactSet &ExitSet,
1053 void intersectAndWarn(FactSet &FSet1,
const FactSet &FSet2,
1058 void intersectAndWarn(FactSet &FSet1,
const FactSet &FSet2,
1061 intersectAndWarn(FSet1, FSet2, JoinLoc, LEK1, LEK1, Modify);
1071 ThreadSafetyAnalyzer& Analyzer) {
1073 BeforeInfo *Info =
nullptr;
1077 std::unique_ptr<BeforeInfo> &InfoPtr = BMap[Vd];
1079 InfoPtr.reset(
new BeforeInfo());
1080 Info = InfoPtr.get();
1083 for (
const auto *At : Vd->
attrs()) {
1084 switch (At->getKind()) {
1085 case attr::AcquiredBefore: {
1086 const auto *A = cast<AcquiredBeforeAttr>(At);
1089 for (
const auto *Arg : A->args()) {
1091 Analyzer.SxBuilder.translateAttrExpr(Arg,
nullptr);
1093 Info->Vect.push_back(Cpvd);
1094 const auto It = BMap.find(Cpvd);
1095 if (It == BMap.end())
1096 insertAttrExprs(Cpvd, Analyzer);
1101 case attr::AcquiredAfter: {
1102 const auto *A = cast<AcquiredAfterAttr>(At);
1105 for (
const auto *Arg : A->args()) {
1107 Analyzer.SxBuilder.translateAttrExpr(Arg,
nullptr);
1110 BeforeInfo *ArgInfo = getBeforeInfoForDecl(ArgVd, Analyzer);
1111 ArgInfo->Vect.push_back(Vd);
1124 BeforeSet::BeforeInfo *
1126 ThreadSafetyAnalyzer &Analyzer) {
1127 auto It = BMap.find(Vd);
1128 BeforeInfo *Info =
nullptr;
1129 if (It == BMap.end())
1130 Info = insertAttrExprs(Vd, Analyzer);
1132 Info = It->second.get();
1133 assert(Info &&
"BMap contained nullptr?");
1139 const FactSet& FSet,
1140 ThreadSafetyAnalyzer& Analyzer,
1150 BeforeSet::BeforeInfo *Info = getBeforeInfoForDecl(Vd, Analyzer);
1152 if (Info->Visited == 1)
1155 if (Info->Visited == 2)
1158 if (Info->Vect.empty())
1161 InfoVect.push_back(Info);
1163 for (
const auto *Vdb : Info->Vect) {
1165 if (FSet.containsMutexDecl(Analyzer.FactMan, Vdb)) {
1166 StringRef L1 = StartVd->
getName();
1167 StringRef L2 = Vdb->getName();
1168 Analyzer.Handler.handleLockAcquiredBefore(CapKind, L1, L2, Loc);
1172 if (CycMap.find(Vd) == CycMap.end()) {
1173 CycMap.insert(std::make_pair(Vd,
true));
1174 StringRef L1 = Vd->getName();
1175 Analyzer.Handler.handleBeforeAfterCycle(L1, Vd->getLocation());
1185 for (
auto *Info : InfoVect)
1191 if (
const auto *CE = dyn_cast<ImplicitCastExpr>(Exp))
1194 if (
const auto *DR = dyn_cast<DeclRefExpr>(Exp))
1195 return DR->getDecl();
1197 if (
const auto *ME = dyn_cast<MemberExpr>(Exp))
1198 return ME->getMemberDecl();
1205 template <
typename Ty>
1206 class has_arg_iterator_range {
1207 using yes =
char[1];
1210 template <
typename Inner>
1211 static yes& test(Inner *I, decltype(I->args()) * =
nullptr);
1214 static no& test(...);
1217 static const bool value =
sizeof(test<Ty>(
nullptr)) ==
sizeof(yes);
1223 return A->getName();
1231 if (
const auto *RD = RT->getDecl())
1232 if (
const auto *CA = RD->getAttr<CapabilityAttr>())
1235 if (
const auto *TD = TT->getDecl())
1236 if (
const auto *CA = TD->getAttr<CapabilityAttr>())
1245 assert(VD &&
"No ValueDecl passed");
1251 template <
typename AttrTy>
1252 static typename std::enable_if<!has_arg_iterator_range<AttrTy>::value,
1260 template <
typename AttrTy>
1261 static typename std::enable_if<has_arg_iterator_range<AttrTy>::value,
1264 for (
const auto *Arg : A->args()) {
1271 bool ThreadSafetyAnalyzer::inCurrentScope(
const CapabilityExpr &CapE) {
1274 if (
const auto *
P = dyn_cast_or_null<til::Project>(CapE.
sexpr())) {
1275 const auto *VD =
P->clangDecl();
1277 return VD->getDeclContext() == CurrentMethod->getDeclContext();
1284 void ThreadSafetyAnalyzer::addLock(FactSet &FSet,
1285 std::unique_ptr<FactEntry> Entry,
1286 StringRef DiagKind,
bool ReqAttr) {
1287 if (Entry->shouldIgnore())
1290 if (!ReqAttr && !Entry->negative()) {
1293 const FactEntry *Nen = FSet.findLock(FactMan, NegC);
1295 FSet.removeLock(FactMan, NegC);
1298 if (inCurrentScope(*Entry) && !Entry->asserted())
1299 Handler.handleNegativeNotHeld(DiagKind, Entry->toString(),
1305 if (Handler.issueBetaWarnings() &&
1306 !Entry->asserted() && !Entry->declared()) {
1307 GlobalBeforeSet->checkBeforeAfter(Entry->valueDecl(), FSet, *
this,
1308 Entry->loc(), DiagKind);
1312 if (
const FactEntry *Cp = FSet.findLock(FactMan, *Entry)) {
1313 if (!Entry->asserted())
1314 Cp->handleLock(FSet, FactMan, *Entry, Handler, DiagKind);
1316 FSet.addLock(FactMan, std::move(Entry));
1322 void ThreadSafetyAnalyzer::removeLock(FactSet &FSet,
const CapabilityExpr &Cp,
1324 bool FullyRemove,
LockKind ReceivedKind,
1325 StringRef DiagKind) {
1329 const FactEntry *LDat = FSet.findLock(FactMan, Cp);
1331 Handler.handleUnmatchedUnlock(DiagKind, Cp.
toString(), UnlockLoc);
1337 if (ReceivedKind !=
LK_Generic && LDat->kind() != ReceivedKind) {
1338 Handler.handleIncorrectUnlockKind(DiagKind, Cp.
toString(), LDat->kind(),
1339 ReceivedKind, LDat->loc(), UnlockLoc);
1342 LDat->handleUnlock(FSet, FactMan, Cp, UnlockLoc, FullyRemove, Handler,
1348 template <
typename AttrType>
1349 void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
1352 if (
Attr->args_size() == 0) {
1354 CapabilityExpr Cp = SxBuilder.translateAttrExpr(
nullptr, D, Exp, SelfDecl);
1361 Mtxs.push_back_nodup(Cp);
1365 for (
const auto *Arg :
Attr->args()) {
1366 CapabilityExpr Cp = SxBuilder.translateAttrExpr(Arg, D, Exp, SelfDecl);
1373 Mtxs.push_back_nodup(Cp);
1380 template <
class AttrType>
1381 void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
1385 Expr *BrE,
bool Neg) {
1387 bool branch =
false;
1388 if (
const auto *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE))
1389 branch = BLE->getValue();
1390 else if (
const auto *ILE = dyn_cast_or_null<IntegerLiteral>(BrE))
1391 branch = ILE->getValue().getBoolValue();
1393 int branchnum = branch ? 0 : 1;
1395 branchnum = !branchnum;
1400 SE = PredBlock->
succ_end(); SI != SE && i < 2; ++SI, ++i) {
1401 if (*SI == CurrBlock && i == branchnum)
1402 getMutexIDs(Mtxs,
Attr, Exp, D);
1407 if (isa<CXXNullPtrLiteralExpr>(E) || isa<GNUNullExpr>(E)) {
1410 }
else if (
const auto *BLE = dyn_cast<CXXBoolLiteralExpr>(E)) {
1411 TCond = BLE->getValue();
1413 }
else if (
const auto *ILE = dyn_cast<IntegerLiteral>(E)) {
1414 TCond = ILE->getValue().getBoolValue();
1416 }
else if (
auto *CE = dyn_cast<ImplicitCastExpr>(E))
1424 const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(
const Stmt *Cond,
1430 if (
const auto *CallExp = dyn_cast<CallExpr>(Cond)) {
1431 if (CallExp->getBuiltinCallee() == Builtin::BI__builtin_expect)
1432 return getTrylockCallExpr(CallExp->getArg(0), C, Negate);
1435 else if (
const auto *PE = dyn_cast<ParenExpr>(Cond))
1436 return getTrylockCallExpr(PE->getSubExpr(), C, Negate);
1437 else if (
const auto *CE = dyn_cast<ImplicitCastExpr>(Cond))
1438 return getTrylockCallExpr(CE->getSubExpr(), C, Negate);
1439 else if (
const auto *FE = dyn_cast<FullExpr>(Cond))
1440 return getTrylockCallExpr(FE->getSubExpr(), C, Negate);
1441 else if (
const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) {
1442 const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C);
1443 return getTrylockCallExpr(E, C, Negate);
1445 else if (
const auto *UOP = dyn_cast<UnaryOperator>(Cond)) {
1446 if (UOP->getOpcode() == UO_LNot) {
1448 return getTrylockCallExpr(UOP->getSubExpr(), C, Negate);
1452 else if (
const auto *BOP = dyn_cast<BinaryOperator>(Cond)) {
1453 if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) {
1454 if (BOP->getOpcode() == BO_NE)
1459 if (!TCond) Negate = !Negate;
1460 return getTrylockCallExpr(BOP->getLHS(), C, Negate);
1464 if (!TCond) Negate = !Negate;
1465 return getTrylockCallExpr(BOP->getRHS(), C, Negate);
1469 if (BOP->getOpcode() == BO_LAnd) {
1471 return getTrylockCallExpr(BOP->getRHS(), C, Negate);
1473 if (BOP->getOpcode() == BO_LOr)
1474 return getTrylockCallExpr(BOP->getRHS(), C, Negate);
1476 }
else if (
const auto *COP = dyn_cast<ConditionalOperator>(Cond)) {
1480 if (TCond && !FCond)
1481 return getTrylockCallExpr(COP->getCond(), C, Negate);
1482 if (!TCond && FCond) {
1484 return getTrylockCallExpr(COP->getCond(), C, Negate);
1494 void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
1495 const FactSet &ExitSet,
1505 bool Negate =
false;
1506 const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->
getBlockID()];
1507 const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
1508 StringRef CapDiagKind =
"mutex";
1510 const auto *Exp = getTrylockCallExpr(Cond, LVarCtx, Negate);
1514 auto *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
1515 if(!FunDecl || !FunDecl->hasAttrs())
1518 CapExprSet ExclusiveLocksToAdd;
1519 CapExprSet SharedLocksToAdd;
1522 for (
const auto *
Attr : FunDecl->attrs()) {
1524 case attr::TryAcquireCapability: {
1525 auto *A = cast<TryAcquireCapabilityAttr>(
Attr);
1526 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
1527 Exp, FunDecl, PredBlock, CurrBlock, A->getSuccessValue(),
1532 case attr::ExclusiveTrylockFunction: {
1533 const auto *A = cast<ExclusiveTrylockFunctionAttr>(
Attr);
1534 getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl,
1535 PredBlock, CurrBlock, A->getSuccessValue(), Negate);
1539 case attr::SharedTrylockFunction: {
1540 const auto *A = cast<SharedTrylockFunctionAttr>(
Attr);
1541 getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl,
1542 PredBlock, CurrBlock, A->getSuccessValue(), Negate);
1553 for (
const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)
1554 addLock(Result, std::make_unique<LockableFactEntry>(ExclusiveLockToAdd,
1557 for (
const auto &SharedLockToAdd : SharedLocksToAdd)
1558 addLock(Result, std::make_unique<LockableFactEntry>(SharedLockToAdd,
1571 friend class ThreadSafetyAnalyzer;
1573 ThreadSafetyAnalyzer *Analyzer;
1575 LocalVariableMap::Context LVarCtx;
1583 StringRef DiagKind);
1594 bool SkipFirstParam =
false);
1597 BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info)
1599 LVarCtx(Info.EntryContext), CtxIndex(Info.EntryIndex) {}
1603 void VisitCastExpr(
const CastExpr *CE);
1604 void VisitCallExpr(
const CallExpr *Exp);
1606 void VisitDeclStmt(
const DeclStmt *S);
1613 void BuildLockset::warnIfMutexNotHeld(
const NamedDecl *D,
const Expr *Exp,
1619 CapabilityExpr Cp = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
1629 const FactEntry *LDat = FSet.findLock(Analyzer->FactMan, !Cp);
1631 Analyzer->Handler.handleFunExcludesLock(
1638 if (!Analyzer->inCurrentScope(Cp))
1642 LDat = FSet.findLock(Analyzer->FactMan, Cp);
1644 Analyzer->Handler.handleMutexNotHeld(
"", D, POK, Cp.
toString(),
1650 const FactEntry *LDat = FSet.findLockUniv(Analyzer->FactMan, Cp);
1651 bool NoError =
true;
1654 LDat = FSet.findPartialMatch(Analyzer->FactMan, Cp);
1657 std::string PartMatchStr = LDat->toString();
1658 StringRef PartMatchName(PartMatchStr);
1659 Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Cp.
toString(),
1660 LK, Loc, &PartMatchName);
1663 Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Cp.
toString(),
1669 if (NoError && LDat && !LDat->isAtLeast(LK)) {
1670 Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Cp.
toString(),
1676 void BuildLockset::warnIfMutexHeld(
const NamedDecl *D,
const Expr *Exp,
1677 Expr *MutexExp, StringRef DiagKind) {
1678 CapabilityExpr Cp = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
1686 const FactEntry *LDat = FSet.findLock(Analyzer->FactMan, Cp);
1688 Analyzer->Handler.handleFunExcludesLock(
1706 while (
const auto *DRE = dyn_cast<DeclRefExpr>(Exp)) {
1707 const auto *VD = dyn_cast<
VarDecl>(DRE->getDecl()->getCanonicalDecl());
1708 if (VD && VD->isLocalVarDecl() && VD->getType()->isReferenceType()) {
1709 if (
const auto *E = VD->getInit()) {
1720 if (
const auto *UO = dyn_cast<UnaryOperator>(Exp)) {
1722 if (UO->getOpcode() == UO_Deref)
1723 checkPtAccess(UO->getSubExpr(), AK, POK);
1727 if (
const auto *AE = dyn_cast<ArraySubscriptExpr>(Exp)) {
1728 checkPtAccess(AE->getLHS(), AK, POK);
1732 if (
const auto *ME = dyn_cast<MemberExpr>(Exp)) {
1734 checkPtAccess(ME->getBase(), AK, POK);
1736 checkAccess(ME->getBase(), AK, POK);
1740 if (!D || !D->hasAttrs())
1743 if (D->hasAttr<GuardedVarAttr>() && FSet.isEmpty(Analyzer->FactMan)) {
1744 Analyzer->Handler.handleNoMutexHeld(
"mutex", D, POK, AK, Loc);
1747 for (
const auto *I : D->specific_attrs<GuardedByAttr>())
1748 warnIfMutexNotHeld(D, Exp, AK, I->getArg(), POK,
1757 if (
const auto *PE = dyn_cast<ParenExpr>(Exp)) {
1758 Exp = PE->getSubExpr();
1761 if (
const auto *CE = dyn_cast<CastExpr>(Exp)) {
1762 if (CE->getCastKind() == CK_ArrayToPointerDecay) {
1765 checkAccess(CE->getSubExpr(), AK, POK);
1768 Exp = CE->getSubExpr();
1782 if (D->
hasAttr<PtGuardedVarAttr>() && FSet.isEmpty(Analyzer->FactMan))
1783 Analyzer->Handler.handleNoMutexHeld(
"mutex", D, PtPOK, AK,
1787 warnIfMutexNotHeld(D, Exp, AK, I->getArg(), PtPOK,
1801 void BuildLockset::handleCall(
const Expr *Exp,
const NamedDecl *D,
1804 CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;
1805 CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
1806 CapExprSet ScopedExclusiveReqs, ScopedSharedReqs;
1807 StringRef CapDiagKind =
"mutex";
1810 bool isScopedVar =
false;
1812 if (
const auto *CD = dyn_cast<const CXXConstructorDecl>(D)) {
1814 if (PD && PD->
hasAttr<ScopedLockableAttr>())
1820 switch (At->getKind()) {
1823 case attr::AcquireCapability: {
1824 const auto *A = cast<AcquireCapabilityAttr>(At);
1825 Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd
1826 : ExclusiveLocksToAdd,
1836 case attr::AssertExclusiveLock: {
1837 const auto *A = cast<AssertExclusiveLockAttr>(At);
1839 CapExprSet AssertLocks;
1840 Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
1841 for (
const auto &AssertLock : AssertLocks)
1842 Analyzer->addLock(FSet,
1843 std::make_unique<LockableFactEntry>(
1848 case attr::AssertSharedLock: {
1849 const auto *A = cast<AssertSharedLockAttr>(At);
1851 CapExprSet AssertLocks;
1852 Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
1853 for (
const auto &AssertLock : AssertLocks)
1854 Analyzer->addLock(FSet,
1855 std::make_unique<LockableFactEntry>(
1856 AssertLock,
LK_Shared, Loc,
false,
true),
1861 case attr::AssertCapability: {
1862 const auto *A = cast<AssertCapabilityAttr>(At);
1863 CapExprSet AssertLocks;
1864 Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
1865 for (
const auto &AssertLock : AssertLocks)
1866 Analyzer->addLock(FSet,
1867 std::make_unique<LockableFactEntry>(
1877 case attr::ReleaseCapability: {
1878 const auto *A = cast<ReleaseCapabilityAttr>(At);
1880 Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp, D, VD);
1881 else if (A->isShared())
1882 Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp, D, VD);
1884 Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp, D, VD);
1890 case attr::RequiresCapability: {
1891 const auto *A = cast<RequiresCapabilityAttr>(At);
1892 for (
auto *Arg : A->args()) {
1898 Analyzer->getMutexIDs(A->isShared() ? ScopedSharedReqs
1899 : ScopedExclusiveReqs,
1906 case attr::LocksExcluded: {
1907 const auto *A = cast<LocksExcludedAttr>(At);
1908 for (
auto *Arg : A->args())
1921 bool Dtor = isa<CXXDestructorDecl>(D);
1922 for (
const auto &M : ExclusiveLocksToRemove)
1923 Analyzer->removeLock(FSet, M, Loc, Dtor,
LK_Exclusive, CapDiagKind);
1924 for (
const auto &M : SharedLocksToRemove)
1925 Analyzer->removeLock(FSet, M, Loc, Dtor,
LK_Shared, CapDiagKind);
1926 for (
const auto &M : GenericLocksToRemove)
1927 Analyzer->removeLock(FSet, M, Loc, Dtor,
LK_Generic, CapDiagKind);
1930 for (
const auto &M : ExclusiveLocksToAdd)
1931 Analyzer->addLock(FSet, std::make_unique<LockableFactEntry>(
1934 for (
const auto &M : SharedLocksToAdd)
1935 Analyzer->addLock(FSet, std::make_unique<LockableFactEntry>(
1945 CapabilityExpr Scp = Analyzer->SxBuilder.translateAttrExpr(&DRE,
nullptr);
1947 auto ScopedEntry = std::make_unique<ScopedLockableFactEntry>(Scp, MLoc);
1948 for (
const auto &M : ExclusiveLocksToAdd)
1949 ScopedEntry->addExclusiveLock(M);
1950 for (
const auto &M : ScopedExclusiveReqs)
1951 ScopedEntry->addExclusiveLock(M);
1952 for (
const auto &M : SharedLocksToAdd)
1953 ScopedEntry->addSharedLock(M);
1954 for (
const auto &M : ScopedSharedReqs)
1955 ScopedEntry->addSharedLock(M);
1956 for (
const auto &M : ExclusiveLocksToRemove)
1957 ScopedEntry->addExclusiveUnlock(M);
1958 for (
const auto &M : SharedLocksToRemove)
1959 ScopedEntry->addSharedUnlock(M);
1960 Analyzer->addLock(FSet, std::move(ScopedEntry), CapDiagKind);
1967 void BuildLockset::VisitUnaryOperator(
const UnaryOperator *UO) {
1983 void BuildLockset::VisitBinaryOperator(
const BinaryOperator *BO) {
1988 LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);
1996 void BuildLockset::VisitCastExpr(
const CastExpr *CE) {
2002 void BuildLockset::examineArguments(
const FunctionDecl *FD,
2005 bool SkipFirstParam) {
2015 if (FD->
hasAttr<NoThreadSafetyAnalysisAttr>())
2019 auto Param = Params.begin();
2024 for (
auto Arg = ArgBegin; Param != Params.end() && Arg != ArgEnd;
2032 void BuildLockset::VisitCallExpr(
const CallExpr *Exp) {
2033 if (
const auto *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
2034 const auto *ME = dyn_cast<
MemberExpr>(CE->getCallee());
2039 if (ME->isArrow()) {
2041 checkPtAccess(CE->getImplicitObjectArgument(),
AK_Read);
2043 checkPtAccess(CE->getImplicitObjectArgument(),
AK_Read);
2046 checkAccess(CE->getImplicitObjectArgument(),
AK_Read);
2048 checkAccess(CE->getImplicitObjectArgument(),
AK_Read);
2052 examineArguments(CE->getDirectCallee(), CE->arg_begin(), CE->arg_end());
2053 }
else if (
const auto *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) {
2054 auto OEop = OE->getOperator();
2058 const Expr *Source = OE->getArg(1);
2066 if (!(OEop == OO_Star && OE->getNumArgs() > 1)) {
2068 checkPtAccess(OE->getArg(0),
AK_Read);
2073 const Expr *Obj = OE->getArg(0);
2079 examineArguments(FD, std::next(OE->arg_begin()), OE->arg_end(),
2080 !isa<CXXMethodDecl>(FD));
2088 auto *D = dyn_cast_or_null<NamedDecl>(Exp->
getCalleeDecl());
2089 if(!D || !D->hasAttrs())
2111 for (
auto *Ctor : RD->
ctors()) {
2112 if (Ctor->isDeleted())
2114 if (Ctor->isMoveConstructor())
2126 CD,
true, Args,
false,
false,
false,
false,
2131 void BuildLockset::VisitDeclStmt(
const DeclStmt *S) {
2133 LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
2136 if (
auto *VD = dyn_cast_or_null<VarDecl>(D)) {
2143 if (
auto *EWC = dyn_cast<ExprWithCleanups>(E))
2144 E = EWC->getSubExpr();
2145 if (
auto *ICE = dyn_cast<ImplicitCastExpr>(E))
2146 if (ICE->getCastKind() == CK_NoOp)
2147 E = ICE->getSubExpr();
2148 if (
auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E))
2149 E = BTE->getSubExpr();
2151 if (
const auto *CE = dyn_cast<CXXConstructExpr>(E)) {
2152 const auto *CtorD = dyn_cast_or_null<NamedDecl>(CE->getConstructor());
2153 if (!CtorD || !CtorD->hasAttrs())
2155 handleCall(E, CtorD, VD);
2156 }
else if (isa<CallExpr>(E) && E->
isRValue()) {
2163 if (!RD || !RD->hasAttr<ScopedLockableAttr>())
2188 void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,
2189 const FactSet &FSet2,
2194 FactSet FSet1Orig = FSet1;
2197 for (
const auto &Fact : FSet2) {
2198 const FactEntry *LDat1 =
nullptr;
2199 const FactEntry *LDat2 = &FactMan[Fact];
2200 FactSet::iterator Iter1 = FSet1.findLockIter(FactMan, *LDat2);
2201 if (Iter1 != FSet1.end()) LDat1 = &FactMan[*Iter1];
2204 if (LDat1->kind() != LDat2->kind()) {
2205 Handler.handleExclusiveAndShared(
"mutex", LDat2->toString(),
2206 LDat2->loc(), LDat1->loc());
2212 else if (Modify && LDat1->asserted() && !LDat2->asserted()) {
2217 LDat2->handleRemovalFromIntersection(FSet2, FactMan, JoinLoc, LEK1,
2223 for (
const auto &Fact : FSet1Orig) {
2224 const FactEntry *LDat1 = &FactMan[Fact];
2225 const FactEntry *LDat2 = FSet2.findLock(FactMan, *LDat1);
2228 LDat1->handleRemovalFromIntersection(FSet1Orig, FactMan, JoinLoc, LEK2,
2231 FSet1.removeLock(FactMan, *LDat1);
2245 if (isa<CXXThrowExpr>(S->getStmt()))
2260 if (!walker.
init(AC))
2268 const auto *CurrentFunction = dyn_cast<
FunctionDecl>(D);
2271 if (D->
hasAttr<NoThreadSafetyAnalysisAttr>())
2278 if (isa<CXXConstructorDecl>(D))
2280 if (isa<CXXDestructorDecl>(D))
2283 Handler.enterFunction(CurrentFunction);
2286 CFGBlockInfo::getEmptyBlockInfo(LocalVarMap));
2298 LocalVarMap.traverseCFG(CFGraph, SortedGraph, BlockInfo);
2303 CapExprSet ExclusiveLocksAcquired;
2304 CapExprSet SharedLocksAcquired;
2305 CapExprSet LocksReleased;
2312 FactSet &InitialLockset = BlockInfo[FirstBlock->
getBlockID()].EntrySet;
2314 CapExprSet ExclusiveLocksToAdd;
2315 CapExprSet SharedLocksToAdd;
2316 StringRef CapDiagKind =
"mutex";
2321 if (
const auto *A = dyn_cast<RequiresCapabilityAttr>(
Attr)) {
2322 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2325 }
else if (
const auto *A = dyn_cast<ReleaseCapabilityAttr>(
Attr)) {
2328 if (A->args_size() == 0)
2330 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2332 getMutexIDs(LocksReleased, A,
nullptr, D);
2334 }
else if (
const auto *A = dyn_cast<AcquireCapabilityAttr>(
Attr)) {
2335 if (A->args_size() == 0)
2337 getMutexIDs(A->isShared() ? SharedLocksAcquired
2338 : ExclusiveLocksAcquired,
2341 }
else if (isa<ExclusiveTrylockFunctionAttr>(
Attr)) {
2344 }
else if (isa<SharedTrylockFunctionAttr>(
Attr)) {
2347 }
else if (isa<TryAcquireCapabilityAttr>(
Attr)) {
2354 for (
const auto &Mu : ExclusiveLocksToAdd) {
2355 auto Entry = std::make_unique<LockableFactEntry>(Mu,
LK_Exclusive, Loc);
2356 Entry->setDeclared(
true);
2357 addLock(InitialLockset, std::move(Entry), CapDiagKind,
true);
2359 for (
const auto &Mu : SharedLocksToAdd) {
2360 auto Entry = std::make_unique<LockableFactEntry>(Mu,
LK_Shared, Loc);
2361 Entry->setDeclared(
true);
2362 addLock(InitialLockset, std::move(Entry), CapDiagKind,
true);
2366 for (
const auto *CurrBlock : *SortedGraph) {
2367 unsigned CurrBlockID = CurrBlock->
getBlockID();
2368 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
2371 VisitedBlocks.
insert(CurrBlock);
2386 bool LocksetInitialized =
false;
2389 PE = CurrBlock->
pred_end(); PI != PE; ++PI) {
2391 if (*PI ==
nullptr || !VisitedBlocks.
alreadySet(*PI))
2394 unsigned PrevBlockID = (*PI)->getBlockID();
2395 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
2402 CurrBlockInfo->Reachable =
true;
2408 if (
const Stmt *Terminator = (*PI)->getTerminatorStmt()) {
2409 if (isa<ContinueStmt>(Terminator) || isa<BreakStmt>(Terminator)) {
2410 SpecialBlocks.push_back(*PI);
2415 FactSet PrevLockset;
2416 getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, *PI, CurrBlock);
2418 if (!LocksetInitialized) {
2419 CurrBlockInfo->EntrySet = PrevLockset;
2420 LocksetInitialized =
true;
2422 intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset,
2423 CurrBlockInfo->EntryLoc,
2429 if (!CurrBlockInfo->Reachable)
2434 for (
const auto *PrevBlock : SpecialBlocks) {
2435 unsigned PrevBlockID = PrevBlock->getBlockID();
2436 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
2438 if (!LocksetInitialized) {
2439 CurrBlockInfo->EntrySet = PrevBlockInfo->ExitSet;
2440 LocksetInitialized =
true;
2447 const Stmt *Terminator = PrevBlock->getTerminatorStmt();
2448 bool IsLoop = Terminator && isa<ContinueStmt>(Terminator);
2450 FactSet PrevLockset;
2451 getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet,
2452 PrevBlock, CurrBlock);
2455 intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset,
2456 PrevBlockInfo->ExitLoc,
2463 BuildLockset LocksetBuilder(
this, *CurrBlockInfo);
2466 for (
const auto &BI : *CurrBlock) {
2467 switch (BI.getKind()) {
2470 LocksetBuilder.Visit(CS.
getStmt());
2477 if (!DD->hasAttrs())
2485 LocksetBuilder.handleCall(&DRE, DD);
2492 CurrBlockInfo->ExitSet = LocksetBuilder.FSet;
2499 SE = CurrBlock->succ_end(); SI != SE; ++SI) {
2501 if (*SI ==
nullptr || !VisitedBlocks.
alreadySet(*SI))
2505 CFGBlockInfo *PreLoop = &BlockInfo[FirstLoopBlock->
getBlockID()];
2506 CFGBlockInfo *LoopEnd = &BlockInfo[CurrBlockID];
2507 intersectAndWarn(LoopEnd->ExitSet, PreLoop->EntrySet,
2518 if (!Final->Reachable)
2522 FactSet ExpectedExitSet = Initial->EntrySet;
2528 for (
const auto &Lock : ExclusiveLocksAcquired)
2529 ExpectedExitSet.addLock(FactMan, std::make_unique<LockableFactEntry>(
2531 for (
const auto &Lock : SharedLocksAcquired)
2532 ExpectedExitSet.addLock(FactMan, std::make_unique<LockableFactEntry>(
2534 for (
const auto &Lock : LocksReleased)
2535 ExpectedExitSet.removeLock(FactMan, Lock);
2538 intersectAndWarn(ExpectedExitSet, Final->ExitSet,
2544 Handler.leaveFunction(CurrentFunction);
2557 ThreadSafetyAnalyzer Analyzer(Handler, *BSet);
2558 Analyzer.runAnalysis(AC);
2572 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.
AdjacentBlocks::const_iterator const_pred_iterator
const Stmt * getStmt() const
const CFG * getGraph() const
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
succ_iterator succ_begin()
TypePropertyCache< Private > Cache
Stmt - This represents one statement.
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.
SourceLocation getBeginLoc() const LLVM_READONLY
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)
static CXXConstructExpr * Create(const ASTContext &Ctx, QualType Ty, SourceLocation Loc, CXXConstructorDecl *Ctor, bool Elidable, ArrayRef< Expr *> Args, bool HadMultipleCandidates, bool ListInitialization, bool StdInitListInitialization, bool ZeroInitialization, ConstructionKind ConstructKind, SourceRange ParenOrBraceRange)
Create a C++ construction expression.
Represents a variable declaration or definition.
ASTContext & getASTContext() const
const T * getAs() const
Member-template getAs<specific type>'.
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)
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 std::string toString(const clang::SanitizerSet &Sanitizers)
Produce a string containing comma-separated names of sanitizers in Sanitizers set.
SourceLocation getBeginLoc() const LLVM_READONLY
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)
ArrayRef< ParmVarDecl * > parameters() const
AdjacentBlocks::const_iterator const_succ_iterator
virtual void handleDoubleLock(StringRef Kind, Name LockName, SourceLocation LocLocked, SourceLocation LocDoubleLock)
Warn about lock function calls for locks which are already held.
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
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
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 ...
This represents one expression.
Stmt * getTerminatorCondition(bool StripParens=true)
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt...
Defines an enumeration for C++ overloaded operators.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
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())
SourceLocation getEndLoc() const LLVM_READONLY
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
DeclStmt - Adaptor class for mixing declarations with statements and expressions. ...
Represents a static or instance method of a struct/union/class.
SourceLocation getLocation() const
unsigned getNumBlockIDs() const
Returns the total number of BlockIDs allocated (which start at 0).
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.
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.
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
Return the parent of this method declaration, which is the class in which this method is defined...
Stmt * getTerminatorStmt()
std::string toString(const til::SExpr *E)
Expr * IgnoreImplicit() LLVM_READONLY
Skip past any implicit AST nodes which might surround this expression until reaching a fixed point...
A helper class that allows the use of isa/cast/dyncast to detect TagType objects of structs/unions/cl...
Iterator for iterating over Stmt * arrays that contain only T *.
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.
internal::Matcher< T > traverse(ast_type_traits::TraversalKind TK, const internal::Matcher< T > &InnerMatcher)
Causes all nested matchers to be matched with the specified traversal kind.
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
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
Defines enum values for all the target-independent builtin functions.
Attr - This represents one attribute.
SourceLocation getLocation() const
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point...
Can be either Shared or Exclusive.