35 #include "llvm/ADT/ImmutableMap.h" 36 #include "llvm/ADT/PostOrderIterator.h" 37 #include "llvm/ADT/SmallVector.h" 38 #include "llvm/ADT/StringRef.h" 39 #include "llvm/Support/raw_ostream.h" 45 using namespace clang;
46 using namespace threadSafety;
59 const Expr *DeclExp, StringRef
Kind) {
71 class CapExprSet :
public SmallVector<CapabilityExpr, 4> {
75 iterator It = std::find_if(begin(), end(),
104 bool Asrt,
bool Declrd =
false)
108 virtual ~FactEntry() {}
112 bool asserted()
const {
return Asserted; }
113 bool declared()
const {
return Declared; }
115 void setDeclared(
bool D) { Declared = D; }
118 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
121 virtual void handleUnlock(FactSet &FSet, FactManager &FactMan,
124 StringRef DiagKind)
const = 0;
133 typedef unsigned short FactID;
139 std::vector<std::unique_ptr<FactEntry>> Facts;
142 FactID newFact(std::unique_ptr<FactEntry> Entry) {
143 Facts.push_back(std::move(Entry));
144 return static_cast<unsigned short>(Facts.size() - 1);
147 const FactEntry &operator[](FactID F)
const {
return *Facts[F]; }
148 FactEntry &operator[](FactID F) {
return *Facts[F]; }
166 typedef FactVec::iterator iterator;
167 typedef FactVec::const_iterator const_iterator;
169 iterator begin() {
return FactIDs.begin(); }
170 const_iterator begin()
const {
return FactIDs.begin(); }
172 iterator end() {
return FactIDs.end(); }
173 const_iterator end()
const {
return FactIDs.end(); }
175 bool isEmpty()
const {
return FactIDs.size() == 0; }
178 bool isEmpty(FactManager &FactMan)
const {
179 for (FactID FID : *
this) {
180 if (!FactMan[FID].negative())
186 void addLockByID(FactID
ID) { FactIDs.push_back(ID); }
188 FactID addLock(FactManager &FM, std::unique_ptr<FactEntry> Entry) {
189 FactID F = FM.newFact(std::move(Entry));
190 FactIDs.push_back(F);
195 unsigned n = FactIDs.size();
199 for (
unsigned i = 0; i < n-1; ++i) {
200 if (FM[FactIDs[i]].
matches(CapE)) {
201 FactIDs[i] = FactIDs[n-1];
206 if (FM[FactIDs[n-1]].
matches(CapE)) {
213 iterator findLockIter(FactManager &FM,
const CapabilityExpr &CapE) {
214 return std::find_if(begin(), end(), [&](FactID ID) {
215 return FM[
ID].matches(CapE);
219 FactEntry *findLock(FactManager &FM,
const CapabilityExpr &CapE)
const {
220 auto I = std::find_if(begin(), end(), [&](FactID ID) {
221 return FM[
ID].matches(CapE);
223 return I != end() ? &FM[*I] :
nullptr;
226 FactEntry *findLockUniv(FactManager &FM,
const CapabilityExpr &CapE)
const {
227 auto I = std::find_if(begin(), end(), [&](FactID ID) ->
bool {
228 return FM[
ID].matchesUniv(CapE);
230 return I != end() ? &FM[*I] :
nullptr;
233 FactEntry *findPartialMatch(FactManager &FM,
235 auto I = std::find_if(begin(), end(), [&](FactID ID) ->
bool {
236 return FM[
ID].partiallyMatches(CapE);
238 return I != end() ? &FM[*I] :
nullptr;
241 bool containsMutexDecl(FactManager &FM,
const ValueDecl* Vd)
const {
242 auto I = std::find_if(begin(), end(), [&](FactID ID) ->
bool {
243 return FM[
ID].valueDecl() == Vd;
249 class ThreadSafetyAnalyzer;
253 namespace threadSafety {
259 BeforeInfo() : Visited(0) {}
260 BeforeInfo(BeforeInfo &&) =
default;
266 typedef llvm::DenseMap<const ValueDecl *, std::unique_ptr<BeforeInfo>>
268 typedef llvm::DenseMap<const ValueDecl*, bool> CycleMap;
273 BeforeInfo* insertAttrExprs(
const ValueDecl* Vd,
274 ThreadSafetyAnalyzer& Analyzer);
276 BeforeInfo *getBeforeInfoForDecl(
const ValueDecl *Vd,
277 ThreadSafetyAnalyzer &Analyzer);
279 void checkBeforeAfter(
const ValueDecl* Vd,
281 ThreadSafetyAnalyzer& Analyzer,
293 class LocalVariableMap;
301 struct CFGBlockInfo {
304 LocalVarContext EntryContext;
305 LocalVarContext ExitContext;
312 return Side == CBS_Entry ? EntrySet : ExitSet;
315 return Side == CBS_Entry ? EntryLoc : ExitLoc;
319 CFGBlockInfo(LocalVarContext EmptyCtx)
320 : EntryContext(EmptyCtx), ExitContext(EmptyCtx), Reachable(
false)
324 static CFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M);
342 class LocalVariableMap {
344 typedef LocalVarContext Context;
350 struct VarDefinition {
352 friend class LocalVariableMap;
359 bool isReference() {
return !Exp; }
364 : Dec(D), Exp(E), Ref(0), Ctx(C)
368 VarDefinition(
const NamedDecl *D,
unsigned R, Context C)
369 : Dec(D), Exp(
nullptr), Ref(R), Ctx(C)
374 Context::Factory ContextFactory;
375 std::vector<VarDefinition> VarDefinitions;
376 std::vector<unsigned> CtxIndices;
377 std::vector<std::pair<Stmt*, Context> > SavedContexts;
382 VarDefinitions.push_back(VarDefinition(
nullptr, 0u, getEmptyContext()));
386 const VarDefinition* lookup(
const NamedDecl *D, Context Ctx) {
387 const unsigned *i = Ctx.lookup(D);
390 assert(*i < VarDefinitions.size());
391 return &VarDefinitions[*i];
398 const unsigned *
P = Ctx.lookup(D);
404 if (VarDefinitions[i].Exp) {
405 Ctx = VarDefinitions[i].Ctx;
406 return VarDefinitions[i].Exp;
408 i = VarDefinitions[i].Ref;
413 Context getEmptyContext() {
return ContextFactory.getEmptyMap(); }
418 Context getNextContext(
unsigned &CtxIndex,
Stmt *S, Context
C) {
419 if (SavedContexts[CtxIndex+1].first == S) {
421 Context Result = SavedContexts[CtxIndex].second;
427 void dumpVarDefinitionName(
unsigned i) {
429 llvm::errs() <<
"Undefined";
432 const NamedDecl *Dec = VarDefinitions[i].Dec;
434 llvm::errs() <<
"<<NULL>>";
438 llvm::errs() <<
"." << i <<
" " << ((
const void*) Dec);
443 for (
unsigned i = 1, e = VarDefinitions.size(); i < e; ++i) {
444 const Expr *Exp = VarDefinitions[i].Exp;
445 unsigned Ref = VarDefinitions[i].Ref;
447 dumpVarDefinitionName(i);
448 llvm::errs() <<
" = ";
449 if (Exp) Exp->
dump();
451 dumpVarDefinitionName(Ref);
452 llvm::errs() <<
"\n";
458 void dumpContext(Context C) {
459 for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) {
462 const unsigned *i = C.lookup(D);
463 llvm::errs() <<
" -> ";
464 dumpVarDefinitionName(*i);
465 llvm::errs() <<
"\n";
471 std::vector<CFGBlockInfo> &BlockInfo);
475 unsigned getContextIndex() {
return SavedContexts.size()-1; }
478 void saveContext(
Stmt *S, Context C) {
479 SavedContexts.push_back(std::make_pair(S,C));
484 Context addDefinition(
const NamedDecl *D,
const Expr *Exp, Context Ctx) {
485 assert(!Ctx.contains(D));
486 unsigned newID = VarDefinitions.size();
487 Context NewCtx = ContextFactory.add(Ctx, D, newID);
488 VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
493 Context addReference(
const NamedDecl *D,
unsigned i, Context Ctx) {
494 unsigned newID = VarDefinitions.size();
495 Context NewCtx = ContextFactory.add(Ctx, D, newID);
496 VarDefinitions.push_back(VarDefinition(D, i, Ctx));
502 Context updateDefinition(
const NamedDecl *D,
Expr *Exp, Context Ctx) {
503 if (Ctx.contains(D)) {
504 unsigned newID = VarDefinitions.size();
505 Context NewCtx = ContextFactory.remove(Ctx, D);
506 NewCtx = ContextFactory.add(NewCtx, D, newID);
507 VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
515 Context clearDefinition(
const NamedDecl *D, Context Ctx) {
516 Context NewCtx = Ctx;
517 if (NewCtx.contains(D)) {
518 NewCtx = ContextFactory.remove(NewCtx, D);
519 NewCtx = ContextFactory.add(NewCtx, D, 0);
525 Context removeDefinition(
const NamedDecl *D, Context Ctx) {
526 Context NewCtx = Ctx;
527 if (NewCtx.contains(D)) {
528 NewCtx = ContextFactory.remove(NewCtx, D);
533 Context intersectContexts(Context C1, Context C2);
534 Context createReferenceContext(Context C);
535 void intersectBackEdge(Context C1, Context C2);
537 friend class VarMapBuilder;
542 CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) {
543 return CFGBlockInfo(M.getEmptyContext());
548 class VarMapBuilder :
public StmtVisitor<VarMapBuilder> {
550 LocalVariableMap* VMap;
551 LocalVariableMap::Context Ctx;
553 VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context C)
554 : VMap(VM), Ctx(C) {}
562 void VarMapBuilder::VisitDeclStmt(
DeclStmt *S) {
563 bool modifiedCtx =
false;
565 for (
const auto *D : DGrp) {
566 if (
const auto *VD = dyn_cast_or_null<VarDecl>(D)) {
567 const Expr *E = VD->getInit();
572 Ctx = VMap->addDefinition(VD, E, Ctx);
578 VMap->saveContext(S, Ctx);
589 if (
DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(LHSExp)) {
591 if (Ctx.lookup(VDec)) {
593 Ctx = VMap->updateDefinition(VDec, BO->
getRHS(), Ctx);
596 Ctx = VMap->clearDefinition(VDec, Ctx);
597 VMap->saveContext(BO, Ctx);
606 LocalVariableMap::Context
607 LocalVariableMap::intersectContexts(Context C1, Context C2) {
609 for (
const auto &
P : C1) {
611 const unsigned *i2 = C2.lookup(Dec);
613 Result = removeDefinition(Dec, Result);
614 else if (*i2 !=
P.second)
615 Result = clearDefinition(Dec, Result);
623 LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) {
624 Context Result = getEmptyContext();
625 for (
const auto &
P : C)
626 Result = addReference(
P.first,
P.second, Result);
633 void LocalVariableMap::intersectBackEdge(Context C1, Context C2) {
634 for (
const auto &
P : C1) {
635 unsigned i1 =
P.second;
636 VarDefinition *VDef = &VarDefinitions[i1];
637 assert(VDef->isReference());
639 const unsigned *i2 = C2.lookup(
P.first);
640 if (!i2 || (*i2 != i1))
684 void LocalVariableMap::traverseCFG(
CFG *CFGraph,
686 std::vector<CFGBlockInfo> &BlockInfo) {
691 for (
const auto *CurrBlock : *SortedGraph) {
692 int CurrBlockID = CurrBlock->getBlockID();
693 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
695 VisitedBlocks.
insert(CurrBlock);
698 bool HasBackEdges =
false;
701 PE = CurrBlock->pred_end(); PI != PE; ++PI) {
703 if (*PI ==
nullptr || !VisitedBlocks.
alreadySet(*PI)) {
708 int PrevBlockID = (*PI)->getBlockID();
709 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
712 CurrBlockInfo->EntryContext = PrevBlockInfo->ExitContext;
716 CurrBlockInfo->EntryContext =
717 intersectContexts(CurrBlockInfo->EntryContext,
718 PrevBlockInfo->ExitContext);
725 CurrBlockInfo->EntryContext =
726 createReferenceContext(CurrBlockInfo->EntryContext);
729 saveContext(
nullptr, CurrBlockInfo->EntryContext);
730 CurrBlockInfo->EntryIndex = getContextIndex();
733 VarMapBuilder VMapBuilder(
this, CurrBlockInfo->EntryContext);
735 BE = CurrBlock->end(); BI != BE; ++BI) {
736 switch (BI->getKind()) {
739 VMapBuilder.Visit(const_cast<Stmt*>(CS.
getStmt()));
746 CurrBlockInfo->ExitContext = VMapBuilder.Ctx;
750 SE = CurrBlock->succ_end(); SI != SE; ++SI) {
752 if (*SI ==
nullptr || !VisitedBlocks.
alreadySet(*SI))
756 Context LoopBegin = BlockInfo[FirstLoopBlock->
getBlockID()].EntryContext;
757 Context LoopEnd = CurrBlockInfo->ExitContext;
758 intersectBackEdge(LoopBegin, LoopEnd);
764 saveContext(
nullptr, BlockInfo[exitID].ExitContext);
769 static void findBlockLocations(
CFG *CFGraph,
771 std::vector<CFGBlockInfo> &BlockInfo) {
772 for (
const auto *CurrBlock : *SortedGraph) {
773 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()];
777 if (
const Stmt *S = CurrBlock->getTerminator()) {
778 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->
getLocStart();
781 BE = CurrBlock->rend(); BI != BE; ++BI) {
784 CurrBlockInfo->ExitLoc = CS->getStmt()->getLocStart();
790 if (CurrBlockInfo->ExitLoc.isValid()) {
794 BE = CurrBlock->end(); BI != BE; ++BI) {
797 CurrBlockInfo->EntryLoc = CS->getStmt()->getLocStart();
801 }
else if (CurrBlock->pred_size() == 1 && *CurrBlock->pred_begin() &&
802 CurrBlock != &CFGraph->
getExit()) {
805 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =
806 BlockInfo[(*CurrBlock->pred_begin())->getBlockID()].ExitLoc;
811 class LockableFactEntry :
public FactEntry {
817 bool Mng =
false,
bool Asrt =
false)
818 : FactEntry(CE, LK, Loc, Asrt), Managed(Mng) {}
821 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
824 if (!Managed && !asserted() && !negative() && !isUniversal()) {
830 void handleUnlock(FactSet &FSet, FactManager &FactMan,
833 StringRef DiagKind)
const override {
834 FSet.removeLock(FactMan, Cp);
836 FSet.addLock(FactMan, llvm::make_unique<LockableFactEntry>(
842 class ScopedLockableFactEntry :
public FactEntry {
848 const CapExprSet &Excl,
const CapExprSet &Shrd)
850 for (
const auto &M : Excl)
851 UnderlyingMutexes.push_back(M.sexpr());
852 for (
const auto &M : Shrd)
853 UnderlyingMutexes.push_back(M.sexpr());
857 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
860 for (
const til::SExpr *UnderlyingMutex : UnderlyingMutexes) {
861 if (FSet.findLock(FactMan,
CapabilityExpr(UnderlyingMutex,
false))) {
865 "mutex",
sx::toString(UnderlyingMutex), loc(), JoinLoc, LEK);
870 void handleUnlock(FactSet &FSet, FactManager &FactMan,
873 StringRef DiagKind)
const override {
874 assert(!Cp.
negative() &&
"Managing object cannot be negative.");
875 for (
const til::SExpr *UnderlyingMutex : UnderlyingMutexes) {
877 auto UnderEntry = llvm::make_unique<LockableFactEntry>(
883 if (FSet.findLock(FactMan, UnderCp)) {
884 FSet.removeLock(FactMan, UnderCp);
885 FSet.addLock(FactMan, std::move(UnderEntry));
890 if (!FSet.findLock(FactMan, UnderCp)) {
894 FSet.removeLock(FactMan, UnderCp);
895 FSet.addLock(FactMan, std::move(UnderEntry));
899 FSet.removeLock(FactMan, Cp);
904 class ThreadSafetyAnalyzer {
905 friend class BuildLockset;
908 llvm::BumpPtrAllocator Bpa;
914 LocalVariableMap LocalVarMap;
916 std::vector<CFGBlockInfo> BlockInfo;
922 : Arena(&Bpa), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}
926 void addLock(FactSet &FSet, std::unique_ptr<FactEntry> Entry,
927 StringRef DiagKind,
bool ReqAttr =
false);
932 template <
typename AttrType>
933 void getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
Expr *Exp,
936 template <
class AttrType>
937 void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
Expr *Exp,
940 Expr *BrE,
bool Neg);
942 const CallExpr* getTrylockCallExpr(
const Stmt *Cond, LocalVarContext C,
945 void getEdgeLockset(FactSet &Result,
const FactSet &ExitSet,
949 void intersectAndWarn(FactSet &FSet1,
const FactSet &FSet2,
954 void intersectAndWarn(FactSet &FSet1,
const FactSet &FSet2,
957 intersectAndWarn(FSet1, FSet2, JoinLoc, LEK1, LEK1, Modify);
966 ThreadSafetyAnalyzer& Analyzer) {
968 BeforeInfo *Info =
nullptr;
972 std::unique_ptr<BeforeInfo> &InfoPtr = BMap[Vd];
974 InfoPtr.reset(
new BeforeInfo());
975 Info = InfoPtr.get();
979 switch (At->getKind()) {
980 case attr::AcquiredBefore: {
981 auto *A = cast<AcquiredBeforeAttr>(At);
984 for (
const auto *Arg : A->args()) {
986 Analyzer.SxBuilder.translateAttrExpr(Arg,
nullptr);
988 Info->Vect.push_back(Cpvd);
989 auto It = BMap.find(Cpvd);
990 if (It == BMap.end())
991 insertAttrExprs(Cpvd, Analyzer);
996 case attr::AcquiredAfter: {
997 auto *A = cast<AcquiredAfterAttr>(At);
1000 for (
const auto *Arg : A->args()) {
1002 Analyzer.SxBuilder.translateAttrExpr(Arg,
nullptr);
1005 BeforeInfo *ArgInfo = getBeforeInfoForDecl(ArgVd, Analyzer);
1006 ArgInfo->Vect.push_back(Vd);
1019 BeforeSet::BeforeInfo *
1021 ThreadSafetyAnalyzer &Analyzer) {
1022 auto It = BMap.find(Vd);
1023 BeforeInfo *Info =
nullptr;
1024 if (It == BMap.end())
1025 Info = insertAttrExprs(Vd, Analyzer);
1027 Info = It->second.get();
1028 assert(Info &&
"BMap contained nullptr?");
1034 const FactSet& FSet,
1035 ThreadSafetyAnalyzer& Analyzer,
1041 std::function<bool (const ValueDecl*)> traverse = [&](
const ValueDecl* Vd) {
1045 BeforeSet::BeforeInfo *Info = getBeforeInfoForDecl(Vd, Analyzer);
1047 if (Info->Visited == 1)
1050 if (Info->Visited == 2)
1053 if (Info->Vect.empty())
1056 InfoVect.push_back(Info);
1058 for (
auto *Vdb : Info->Vect) {
1060 if (FSet.containsMutexDecl(Analyzer.FactMan, Vdb)) {
1061 StringRef L1 = StartVd->
getName();
1062 StringRef L2 = Vdb->getName();
1063 Analyzer.Handler.handleLockAcquiredBefore(CapKind, L1, L2, Loc);
1066 if (traverse(Vdb)) {
1067 if (CycMap.find(Vd) == CycMap.end()) {
1068 CycMap.insert(std::make_pair(Vd,
true));
1069 StringRef L1 = Vd->getName();
1070 Analyzer.Handler.handleBeforeAfterCycle(L1, Vd->getLocation());
1080 for (
auto* Info : InfoVect)
1088 if (
const auto *CE = dyn_cast<ImplicitCastExpr>(Exp))
1091 if (
const auto *DR = dyn_cast<DeclRefExpr>(Exp))
1092 return DR->getDecl();
1094 if (
const auto *ME = dyn_cast<MemberExpr>(Exp))
1095 return ME->getMemberDecl();
1101 template <
typename Ty>
1102 class has_arg_iterator_range {
1103 typedef char yes[1];
1106 template <
typename Inner>
1107 static yes& test(Inner *I, decltype(I->args()) * =
nullptr);
1110 static no& test(...);
1113 static const bool value =
sizeof(test<Ty>(
nullptr)) ==
sizeof(yes);
1118 return A->getName();
1126 if (
const auto *RD = RT->getDecl())
1127 if (
const auto *CA = RD->getAttr<CapabilityAttr>())
1130 if (
const auto *TD = TT->getDecl())
1131 if (
const auto *CA = TD->getAttr<CapabilityAttr>())
1140 assert(VD &&
"No ValueDecl passed");
1146 template <
typename AttrTy>
1147 static typename std::enable_if<!has_arg_iterator_range<AttrTy>::value,
1155 template <
typename AttrTy>
1156 static typename std::enable_if<has_arg_iterator_range<AttrTy>::value,
1159 for (
const auto *Arg : A->args()) {
1167 inline bool ThreadSafetyAnalyzer::inCurrentScope(
const CapabilityExpr &CapE) {
1170 if (
auto *
P = dyn_cast_or_null<til::Project>(CapE.
sexpr())) {
1171 auto *VD =
P->clangDecl();
1173 return VD->getDeclContext() == CurrentMethod->getDeclContext();
1181 void ThreadSafetyAnalyzer::addLock(FactSet &FSet,
1182 std::unique_ptr<FactEntry> Entry,
1183 StringRef DiagKind,
bool ReqAttr) {
1184 if (Entry->shouldIgnore())
1187 if (!ReqAttr && !Entry->negative()) {
1190 FactEntry *Nen = FSet.findLock(FactMan, NegC);
1192 FSet.removeLock(FactMan, NegC);
1195 if (inCurrentScope(*Entry) && !Entry->asserted())
1196 Handler.handleNegativeNotHeld(DiagKind, Entry->toString(),
1202 if (Handler.issueBetaWarnings() &&
1203 !Entry->asserted() && !Entry->declared()) {
1204 GlobalBeforeSet->checkBeforeAfter(Entry->valueDecl(), FSet, *
this,
1205 Entry->loc(), DiagKind);
1209 if (FSet.findLock(FactMan, *Entry)) {
1210 if (!Entry->asserted())
1211 Handler.handleDoubleLock(DiagKind, Entry->toString(), Entry->loc());
1213 FSet.addLock(FactMan, std::move(Entry));
1220 void ThreadSafetyAnalyzer::removeLock(FactSet &FSet,
const CapabilityExpr &Cp,
1222 bool FullyRemove,
LockKind ReceivedKind,
1223 StringRef DiagKind) {
1227 const FactEntry *LDat = FSet.findLock(FactMan, Cp);
1229 Handler.handleUnmatchedUnlock(DiagKind, Cp.
toString(), UnlockLoc);
1235 if (ReceivedKind !=
LK_Generic && LDat->kind() != ReceivedKind) {
1236 Handler.handleIncorrectUnlockKind(DiagKind, Cp.
toString(),
1237 LDat->kind(), ReceivedKind, UnlockLoc);
1240 LDat->handleUnlock(FSet, FactMan, Cp, UnlockLoc, FullyRemove, Handler,
1247 template <
typename AttrType>
1248 void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
1251 if (
Attr->args_size() == 0) {
1253 CapabilityExpr Cp = SxBuilder.translateAttrExpr(
nullptr, D, Exp, SelfDecl);
1260 Mtxs.push_back_nodup(Cp);
1264 for (
const auto *Arg :
Attr->args()) {
1265 CapabilityExpr Cp = SxBuilder.translateAttrExpr(Arg, D, Exp, SelfDecl);
1272 Mtxs.push_back_nodup(Cp);
1280 template <
class AttrType>
1281 void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
1285 Expr *BrE,
bool Neg) {
1287 bool branch =
false;
1289 branch = BLE->getValue();
1290 else if (
IntegerLiteral *ILE = dyn_cast_or_null<IntegerLiteral>(BrE))
1291 branch = ILE->getValue().getBoolValue();
1293 int branchnum = branch ? 0 : 1;
1295 branchnum = !branchnum;
1300 SE = PredBlock->
succ_end(); SI != SE && i < 2; ++SI, ++i) {
1301 if (*SI == CurrBlock && i == branchnum)
1302 getMutexIDs(Mtxs,
Attr, Exp, D);
1307 if (isa<CXXNullPtrLiteralExpr>(E) || isa<GNUNullExpr>(E)) {
1311 TCond = BLE->getValue();
1314 TCond = ILE->getValue().getBoolValue();
1326 const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(
const Stmt *Cond,
1332 if (
const CallExpr *CallExp = dyn_cast<CallExpr>(Cond)) {
1335 else if (
const ParenExpr *PE = dyn_cast<ParenExpr>(Cond)) {
1336 return getTrylockCallExpr(PE->getSubExpr(), C, Negate);
1339 return getTrylockCallExpr(CE->getSubExpr(), C, Negate);
1342 return getTrylockCallExpr(EWC->getSubExpr(), C, Negate);
1344 else if (
const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Cond)) {
1345 const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C);
1346 return getTrylockCallExpr(E, C, Negate);
1348 else if (
const UnaryOperator *UOP = dyn_cast<UnaryOperator>(Cond)) {
1349 if (UOP->getOpcode() == UO_LNot) {
1351 return getTrylockCallExpr(UOP->getSubExpr(), C, Negate);
1355 else if (
const BinaryOperator *BOP = dyn_cast<BinaryOperator>(Cond)) {
1356 if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) {
1357 if (BOP->getOpcode() == BO_NE)
1362 if (!TCond) Negate = !Negate;
1363 return getTrylockCallExpr(BOP->getLHS(), C, Negate);
1367 if (!TCond) Negate = !Negate;
1368 return getTrylockCallExpr(BOP->getRHS(), C, Negate);
1372 if (BOP->getOpcode() == BO_LAnd) {
1374 return getTrylockCallExpr(BOP->getRHS(), C, Negate);
1376 if (BOP->getOpcode() == BO_LOr) {
1377 return getTrylockCallExpr(BOP->getRHS(), C, Negate);
1388 void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
1389 const FactSet &ExitSet,
1398 bool Negate =
false;
1399 const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->
getBlockID()];
1400 const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
1401 StringRef CapDiagKind =
"mutex";
1404 const_cast<CallExpr*
>(getTrylockCallExpr(Cond, LVarCtx, Negate));
1409 if(!FunDecl || !FunDecl->
hasAttrs())
1412 CapExprSet ExclusiveLocksToAdd;
1413 CapExprSet SharedLocksToAdd;
1418 case attr::ExclusiveTrylockFunction: {
1419 ExclusiveTrylockFunctionAttr *A =
1420 cast<ExclusiveTrylockFunctionAttr>(
Attr);
1421 getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl,
1422 PredBlock, CurrBlock, A->getSuccessValue(), Negate);
1426 case attr::SharedTrylockFunction: {
1427 SharedTrylockFunctionAttr *A =
1428 cast<SharedTrylockFunctionAttr>(
Attr);
1429 getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl,
1430 PredBlock, CurrBlock, A->getSuccessValue(), Negate);
1441 for (
const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)
1442 addLock(Result, llvm::make_unique<LockableFactEntry>(ExclusiveLockToAdd,
1445 for (
const auto &SharedLockToAdd : SharedLocksToAdd)
1446 addLock(Result, llvm::make_unique<LockableFactEntry>(SharedLockToAdd,
1457 class BuildLockset :
public StmtVisitor<BuildLockset> {
1458 friend class ThreadSafetyAnalyzer;
1460 ThreadSafetyAnalyzer *Analyzer;
1462 LocalVariableMap::Context LVarCtx;
1470 StringRef DiagKind);
1480 BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info)
1483 FSet(Info.EntrySet),
1484 LVarCtx(Info.EntryContext),
1485 CtxIndex(Info.EntryIndex)
1499 void BuildLockset::warnIfMutexNotHeld(
const NamedDecl *D,
const Expr *Exp,
1505 CapabilityExpr Cp = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
1507 warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, DiagKind);
1515 FactEntry *LDat = FSet.findLock(Analyzer->FactMan, !Cp);
1517 Analyzer->Handler.handleFunExcludesLock(
1524 if (!Analyzer->inCurrentScope(Cp))
1528 LDat = FSet.findLock(Analyzer->FactMan, Cp);
1530 Analyzer->Handler.handleMutexNotHeld(
"", D, POK, Cp.
toString(),
1536 FactEntry* LDat = FSet.findLockUniv(Analyzer->FactMan, Cp);
1537 bool NoError =
true;
1540 LDat = FSet.findPartialMatch(Analyzer->FactMan, Cp);
1543 std::string PartMatchStr = LDat->toString();
1544 StringRef PartMatchName(PartMatchStr);
1545 Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Cp.
toString(),
1546 LK, Loc, &PartMatchName);
1549 Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Cp.
toString(),
1555 if (NoError && LDat && !LDat->isAtLeast(LK)) {
1556 Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Cp.
toString(),
1562 void BuildLockset::warnIfMutexHeld(
const NamedDecl *D,
const Expr *Exp,
1563 Expr *MutexExp, StringRef DiagKind) {
1564 CapabilityExpr Cp = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
1566 warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, DiagKind);
1572 FactEntry* LDat = FSet.findLock(Analyzer->FactMan, Cp);
1574 Analyzer->Handler.handleFunExcludesLock(
1592 while (
const auto *DRE = dyn_cast<DeclRefExpr>(Exp)) {
1593 const auto *VD = dyn_cast<
VarDecl>(DRE->getDecl()->getCanonicalDecl());
1594 if (VD && VD->isLocalVarDecl() && VD->getType()->isReferenceType()) {
1595 if (
const auto *E = VD->getInit()) {
1603 if (
const UnaryOperator *UO = dyn_cast<UnaryOperator>(Exp)) {
1605 if (UO->getOpcode() == clang::UO_Deref)
1606 checkPtAccess(UO->getSubExpr(), AK, POK);
1611 checkPtAccess(AE->getLHS(), AK, POK);
1615 if (
const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
1617 checkPtAccess(ME->getBase(), AK, POK);
1619 checkAccess(ME->getBase(), AK, POK);
1623 if (!D || !D->hasAttrs())
1626 if (D->hasAttr<GuardedVarAttr>() && FSet.isEmpty(Analyzer->FactMan)) {
1627 Analyzer->Handler.handleNoMutexHeld(
"mutex", D, POK, AK, Loc);
1630 for (
const auto *I : D->specific_attrs<GuardedByAttr>())
1631 warnIfMutexNotHeld(D, Exp, AK, I->getArg(), POK,
1641 if (
const ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) {
1642 Exp = PE->getSubExpr();
1645 if (
const CastExpr *CE = dyn_cast<CastExpr>(Exp)) {
1646 if (CE->getCastKind() == CK_ArrayToPointerDecay) {
1649 checkAccess(CE->getSubExpr(), AK, POK);
1652 Exp = CE->getSubExpr();
1666 if (D->
hasAttr<PtGuardedVarAttr>() && FSet.isEmpty(Analyzer->FactMan))
1667 Analyzer->Handler.handleNoMutexHeld(
"mutex", D, PtPOK, AK,
1671 warnIfMutexNotHeld(D, Exp, AK, I->getArg(), PtPOK,
1687 CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;
1688 CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
1689 CapExprSet ScopedExclusiveReqs, ScopedSharedReqs;
1690 StringRef CapDiagKind =
"mutex";
1693 bool isScopedVar =
false;
1697 if (PD && PD->
hasAttr<ScopedLockableAttr>())
1703 Attr* At =
const_cast<Attr*
>(Atconst);
1707 case attr::AcquireCapability: {
1708 auto *A = cast<AcquireCapabilityAttr>(At);
1709 Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd
1710 : ExclusiveLocksToAdd,
1720 case attr::AssertExclusiveLock: {
1721 AssertExclusiveLockAttr *A = cast<AssertExclusiveLockAttr>(At);
1723 CapExprSet AssertLocks;
1724 Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
1725 for (
const auto &AssertLock : AssertLocks)
1726 Analyzer->addLock(FSet,
1727 llvm::make_unique<LockableFactEntry>(
1732 case attr::AssertSharedLock: {
1733 AssertSharedLockAttr *A = cast<AssertSharedLockAttr>(At);
1735 CapExprSet AssertLocks;
1736 Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
1737 for (
const auto &AssertLock : AssertLocks)
1738 Analyzer->addLock(FSet,
1739 llvm::make_unique<LockableFactEntry>(
1740 AssertLock,
LK_Shared, Loc,
false,
true),
1745 case attr::AssertCapability: {
1746 AssertCapabilityAttr *A = cast<AssertCapabilityAttr>(At);
1747 CapExprSet AssertLocks;
1748 Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
1749 for (
const auto &AssertLock : AssertLocks)
1750 Analyzer->addLock(FSet,
1751 llvm::make_unique<LockableFactEntry>(
1761 case attr::ReleaseCapability: {
1762 auto *A = cast<ReleaseCapabilityAttr>(At);
1764 Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp, D, VD);
1765 else if (A->isShared())
1766 Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp, D, VD);
1768 Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp, D, VD);
1774 case attr::RequiresCapability: {
1775 RequiresCapabilityAttr *A = cast<RequiresCapabilityAttr>(At);
1776 for (
auto *Arg : A->args()) {
1782 Analyzer->getMutexIDs(A->isShared() ? ScopedSharedReqs
1783 : ScopedExclusiveReqs,
1790 case attr::LocksExcluded: {
1791 LocksExcludedAttr *A = cast<LocksExcludedAttr>(At);
1792 for (
auto *Arg : A->args())
1804 for (
const auto &M : ExclusiveLocksToAdd)
1805 Analyzer->addLock(FSet, llvm::make_unique<LockableFactEntry>(
1808 for (
const auto &M : SharedLocksToAdd)
1809 Analyzer->addLock(FSet, llvm::make_unique<LockableFactEntry>(
1818 CapabilityExpr Scp = Analyzer->SxBuilder.translateAttrExpr(&DRE,
nullptr);
1820 std::copy(ScopedExclusiveReqs.begin(), ScopedExclusiveReqs.end(),
1821 std::back_inserter(ExclusiveLocksToAdd));
1822 std::copy(ScopedSharedReqs.begin(), ScopedSharedReqs.end(),
1823 std::back_inserter(SharedLocksToAdd));
1824 Analyzer->addLock(FSet,
1825 llvm::make_unique<ScopedLockableFactEntry>(
1826 Scp, MLoc, ExclusiveLocksToAdd, SharedLocksToAdd),
1832 bool Dtor = isa<CXXDestructorDecl>(D);
1833 for (
const auto &M : ExclusiveLocksToRemove)
1834 Analyzer->removeLock(FSet, M, Loc, Dtor,
LK_Exclusive, CapDiagKind);
1835 for (
const auto &M : SharedLocksToRemove)
1836 Analyzer->removeLock(FSet, M, Loc, Dtor,
LK_Shared, CapDiagKind);
1837 for (
const auto &M : GenericLocksToRemove)
1838 Analyzer->removeLock(FSet, M, Loc, Dtor,
LK_Generic, CapDiagKind);
1847 case clang::UO_PostDec:
1848 case clang::UO_PostInc:
1849 case clang::UO_PreDec:
1850 case clang::UO_PreInc: {
1867 LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);
1876 void BuildLockset::VisitCastExpr(
CastExpr *CE) {
1883 void BuildLockset::VisitCallExpr(
CallExpr *Exp) {
1884 bool ExamineArgs =
true;
1885 bool OperatorFun =
false;
1894 if (MD->isConst()) {
1895 checkPtAccess(CE->getImplicitObjectArgument(),
AK_Read);
1897 checkPtAccess(CE->getImplicitObjectArgument(),
AK_Read);
1901 checkAccess(CE->getImplicitObjectArgument(),
AK_Read);
1903 checkAccess(CE->getImplicitObjectArgument(),
AK_Read);
1909 auto OEop = OE->getOperator();
1912 ExamineArgs =
false;
1913 const Expr *Target = OE->getArg(0);
1914 const Expr *Source = OE->getArg(1);
1921 case OO_Subscript: {
1922 const Expr *Obj = OE->getArg(0);
1924 if (!(OEop == OO_Star && OE->getNumArgs() > 1)) {
1932 const Expr *Obj = OE->getArg(0);
1948 if (!FD->hasAttr<NoThreadSafetyAnalysisAttr>()) {
1949 unsigned Fn = FD->getNumParams();
1955 if (isa<CXXMethodDecl>(FD)) {
1966 unsigned n = (Fn < Cn) ? Fn : Cn;
1968 for (; i < n; ++i) {
1994 void BuildLockset::VisitDeclStmt(
DeclStmt *S) {
1996 LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
1999 if (
VarDecl *VD = dyn_cast_or_null<VarDecl>(D)) {
2003 E = EWC->getSubExpr();
2006 NamedDecl *CtorD = dyn_cast_or_null<NamedDecl>(CE->getConstructor());
2009 handleCall(CE, CtorD, VD);
2031 void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,
2032 const FactSet &FSet2,
2037 FactSet FSet1Orig = FSet1;
2040 for (
const auto &Fact : FSet2) {
2041 const FactEntry *LDat1 =
nullptr;
2042 const FactEntry *LDat2 = &FactMan[Fact];
2043 FactSet::iterator Iter1 = FSet1.findLockIter(FactMan, *LDat2);
2044 if (Iter1 != FSet1.end()) LDat1 = &FactMan[*Iter1];
2047 if (LDat1->kind() != LDat2->kind()) {
2048 Handler.handleExclusiveAndShared(
"mutex", LDat2->toString(),
2049 LDat2->loc(), LDat1->loc());
2055 else if (Modify && LDat1->asserted() && !LDat2->asserted()) {
2060 LDat2->handleRemovalFromIntersection(FSet2, FactMan, JoinLoc, LEK1,
2066 for (
const auto &Fact : FSet1Orig) {
2067 const FactEntry *LDat1 = &FactMan[Fact];
2068 const FactEntry *LDat2 = FSet2.findLock(FactMan, *LDat1);
2071 LDat1->handleRemovalFromIntersection(FSet1Orig, FactMan, JoinLoc, LEK2,
2074 FSet1.removeLock(FactMan, *LDat1);
2089 if (isa<CXXThrowExpr>(S->getStmt()))
2105 if (!walker.
init(AC))
2116 if (D->
hasAttr<NoThreadSafetyAnalysisAttr>())
2123 if (isa<CXXConstructorDecl>(D))
2125 if (isa<CXXDestructorDecl>(D))
2128 Handler.enterFunction(CurrentFunction);
2131 CFGBlockInfo::getEmptyBlockInfo(LocalVarMap));
2143 LocalVarMap.traverseCFG(CFGraph, SortedGraph, BlockInfo);
2146 findBlockLocations(CFGraph, SortedGraph, BlockInfo);
2148 CapExprSet ExclusiveLocksAcquired;
2149 CapExprSet SharedLocksAcquired;
2150 CapExprSet LocksReleased;
2157 FactSet &InitialLockset = BlockInfo[FirstBlock->
getBlockID()].EntrySet;
2159 CapExprSet ExclusiveLocksToAdd;
2160 CapExprSet SharedLocksToAdd;
2161 StringRef CapDiagKind =
"mutex";
2166 if (
const auto *A = dyn_cast<RequiresCapabilityAttr>(
Attr)) {
2167 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2170 }
else if (
const auto *A = dyn_cast<ReleaseCapabilityAttr>(
Attr)) {
2173 if (A->args_size() == 0)
2176 getMutexIDs(ExclusiveLocksToAdd, A,
nullptr, D);
2177 getMutexIDs(LocksReleased, A,
nullptr, D);
2179 }
else if (
const auto *A = dyn_cast<AcquireCapabilityAttr>(
Attr)) {
2180 if (A->args_size() == 0)
2182 getMutexIDs(A->isShared() ? SharedLocksAcquired
2183 : ExclusiveLocksAcquired,
2186 }
else if (isa<ExclusiveTrylockFunctionAttr>(
Attr)) {
2189 }
else if (isa<SharedTrylockFunctionAttr>(
Attr)) {
2196 for (
const auto &Mu : ExclusiveLocksToAdd) {
2197 auto Entry = llvm::make_unique<LockableFactEntry>(Mu,
LK_Exclusive, Loc);
2198 Entry->setDeclared(
true);
2199 addLock(InitialLockset, std::move(Entry), CapDiagKind,
true);
2201 for (
const auto &Mu : SharedLocksToAdd) {
2202 auto Entry = llvm::make_unique<LockableFactEntry>(Mu,
LK_Shared, Loc);
2203 Entry->setDeclared(
true);
2204 addLock(InitialLockset, std::move(Entry), CapDiagKind,
true);
2208 for (
const auto *CurrBlock : *SortedGraph) {
2210 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
2213 VisitedBlocks.
insert(CurrBlock);
2228 bool LocksetInitialized =
false;
2231 PE = CurrBlock->
pred_end(); PI != PE; ++PI) {
2234 if (*PI ==
nullptr || !VisitedBlocks.
alreadySet(*PI))
2237 int PrevBlockID = (*PI)->getBlockID();
2238 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
2245 CurrBlockInfo->Reachable =
true;
2251 if (
const Stmt *Terminator = (*PI)->getTerminator()) {
2252 if (isa<ContinueStmt>(Terminator) || isa<BreakStmt>(Terminator)) {
2253 SpecialBlocks.push_back(*PI);
2258 FactSet PrevLockset;
2259 getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, *PI, CurrBlock);
2261 if (!LocksetInitialized) {
2262 CurrBlockInfo->EntrySet = PrevLockset;
2263 LocksetInitialized =
true;
2265 intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset,
2266 CurrBlockInfo->EntryLoc,
2272 if (!CurrBlockInfo->Reachable)
2277 for (
const auto *PrevBlock : SpecialBlocks) {
2278 int PrevBlockID = PrevBlock->getBlockID();
2279 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
2281 if (!LocksetInitialized) {
2282 CurrBlockInfo->EntrySet = PrevBlockInfo->ExitSet;
2283 LocksetInitialized =
true;
2290 const Stmt *Terminator = PrevBlock->getTerminator();
2291 bool IsLoop = Terminator && isa<ContinueStmt>(Terminator);
2293 FactSet PrevLockset;
2294 getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet,
2295 PrevBlock, CurrBlock);
2298 intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset,
2299 PrevBlockInfo->ExitLoc,
2306 BuildLockset LocksetBuilder(
this, *CurrBlockInfo);
2310 BE = CurrBlock->
end(); BI != BE; ++BI) {
2311 switch (BI->getKind()) {
2314 LocksetBuilder.Visit(const_cast<Stmt*>(CS.
getStmt()));
2322 if (!DD->hasAttrs())
2329 LocksetBuilder.handleCall(&DRE, DD);
2336 CurrBlockInfo->ExitSet = LocksetBuilder.FSet;
2343 SE = CurrBlock->
succ_end(); SI != SE; ++SI) {
2346 if (*SI ==
nullptr || !VisitedBlocks.
alreadySet(*SI))
2350 CFGBlockInfo *PreLoop = &BlockInfo[FirstLoopBlock->
getBlockID()];
2351 CFGBlockInfo *LoopEnd = &BlockInfo[CurrBlockID];
2352 intersectAndWarn(LoopEnd->ExitSet, PreLoop->EntrySet,
2363 if (!Final->Reachable)
2367 FactSet ExpectedExitSet = Initial->EntrySet;
2373 for (
const auto &Lock : ExclusiveLocksAcquired)
2374 ExpectedExitSet.addLock(FactMan, llvm::make_unique<LockableFactEntry>(
2376 for (
const auto &Lock : SharedLocksAcquired)
2377 ExpectedExitSet.addLock(FactMan, llvm::make_unique<LockableFactEntry>(
2379 for (
const auto &Lock : LocksReleased)
2380 ExpectedExitSet.removeLock(FactMan, Lock);
2383 intersectAndWarn(ExpectedExitSet, Final->ExitSet,
2389 Handler.leaveFunction(CurrentFunction);
2403 ThreadSafetyAnalyzer Analyzer(Handler, *BSet);
2404 Analyzer.runAnalysis(AC);
2418 llvm_unreachable(
"Unknown AccessKind");
A call to an overloaded operator written using operator syntax.
const PostOrderCFGView * getSortedGraph() const
An instance of this class is created to represent 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...
static StringRef ClassifyDiagnostic(const CapabilityAttr *A)
Defines the SourceManager interface.
unsigned getBlockID() const
ParenExpr - This represents a parethesized expression, e.g.
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)
VarDecl - An instance of this class is created to represent 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
Represents an expression – generally a full-expression – that introduces cleanups to be run at the ...
static bool isAssignmentOp(Opcode Opc)
ParmVarDecl - 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.
const til::SExpr * sexpr() const
AnalysisDeclContext contains the context data for the function or method under analysis.
CFGAutomaticObjDtor - Represents C++ object destructor implicitly generated for automatic object or t...
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)
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.
bool init(AnalysisDeclContext &AC)
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
Handler class for thread safety warnings.
SourceLocation getLocEnd() const LLVM_READONLY
CFGBlock - Represents a single basic block in a source-level CFG.
Dereferencing a variable (e.g. p in *p = 5;)
ValueDecl - Represent the declaration of a variable (in which case it is an lvalue) a function (in wh...
Expr - This represents one expression.
Stmt * getTerminatorCondition(bool StripParens=true)
CFG - Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt...
const FunctionProtoType * T
Represents a C++ destructor within a class.
Defines an enumeration for C++ overloaded operators.
ElementList::const_iterator const_iterator
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.
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
getNameAsString - Get a human-readable name for the declaration, even if it is one of the special kin...
Represents a call to a member function that may be written either with member call syntax (e...
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
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
unsigned getNumBlockIDs() const
getNumBlockIDs - 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
pred_iterator pred_begin()
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
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().
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
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.
CFGElement - 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
getName - 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
Base class for AST nodes in the typed intermediate language.
bool matches(const til::SExpr *E1, const til::SExpr *E2)
An l-value expression is a reference to an object with independent storage.
NamedDecl - This represents a decl with a name.
A boolean literal, per ([C++ lex.bool] Boolean literals).
attr::Kind getKind() const
Attr - This represents one attribute.
SourceLocation getLocation() const
Can be either Shared or Exclusive.