aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/Analysis/ThreadSafety.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2023-12-18 20:30:12 +0000
committerDimitry Andric <dim@FreeBSD.org>2024-04-06 20:11:55 +0000
commit5f757f3ff9144b609b3c433dfd370cc6bdc191ad (patch)
tree1b4e980b866cd26a00af34c0a653eb640bd09caf /contrib/llvm-project/clang/lib/Analysis/ThreadSafety.cpp
parent3e1c8a35f741a5d114d0ba670b15191355711fe9 (diff)
parent312c0ed19cc5276a17bacf2120097bec4515b0f1 (diff)
Diffstat (limited to 'contrib/llvm-project/clang/lib/Analysis/ThreadSafety.cpp')
-rw-r--r--contrib/llvm-project/clang/lib/Analysis/ThreadSafety.cpp259
1 files changed, 154 insertions, 105 deletions
diff --git a/contrib/llvm-project/clang/lib/Analysis/ThreadSafety.cpp b/contrib/llvm-project/clang/lib/Analysis/ThreadSafety.cpp
index 087994e6ebd7..e25b843c9bf8 100644
--- a/contrib/llvm-project/clang/lib/Analysis/ThreadSafety.cpp
+++ b/contrib/llvm-project/clang/lib/Analysis/ThreadSafety.cpp
@@ -1008,8 +1008,10 @@ class ThreadSafetyAnalyzer {
threadSafety::SExprBuilder SxBuilder;
ThreadSafetyHandler &Handler;
- const CXXMethodDecl *CurrentMethod;
+ const FunctionDecl *CurrentFunction;
LocalVariableMap LocalVarMap;
+ // Maps constructed objects to `this` placeholder prior to initialization.
+ llvm::SmallDenseMap<const Expr *, til::LiteralPtr *> ConstructedObjects;
FactManager FactMan;
std::vector<CFGBlockInfo> BlockInfo;
@@ -1055,6 +1057,19 @@ public:
}
void runAnalysis(AnalysisDeclContext &AC);
+
+ void warnIfMutexNotHeld(const FactSet &FSet, const NamedDecl *D,
+ const Expr *Exp, AccessKind AK, Expr *MutexExp,
+ ProtectedOperationKind POK, til::LiteralPtr *Self,
+ SourceLocation Loc);
+ void warnIfMutexHeld(const FactSet &FSet, const NamedDecl *D, const Expr *Exp,
+ Expr *MutexExp, til::LiteralPtr *Self,
+ SourceLocation Loc);
+
+ void checkAccess(const FactSet &FSet, const Expr *Exp, AccessKind AK,
+ ProtectedOperationKind POK);
+ void checkPtAccess(const FactSet &FSet, const Expr *Exp, AccessKind AK,
+ ProtectedOperationKind POK);
};
} // namespace
@@ -1230,10 +1245,10 @@ bool ThreadSafetyAnalyzer::inCurrentScope(const CapabilityExpr &CapE) {
// Members are in scope from methods of the same class.
if (const auto *P = dyn_cast<til::Project>(SExp)) {
- if (!CurrentMethod)
+ if (!isa_and_nonnull<CXXMethodDecl>(CurrentFunction))
return false;
const ValueDecl *VD = P->clangDecl();
- return VD->getDeclContext() == CurrentMethod->getDeclContext();
+ return VD->getDeclContext() == CurrentFunction->getDeclContext();
}
return false;
@@ -1528,22 +1543,21 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
ThreadSafetyAnalyzer *Analyzer;
FactSet FSet;
- /// Maps constructed objects to `this` placeholder prior to initialization.
- llvm::SmallDenseMap<const Expr *, til::LiteralPtr *> ConstructedObjects;
+ // The fact set for the function on exit.
+ const FactSet &FunctionExitFSet;
LocalVariableMap::Context LVarCtx;
unsigned CtxIndex;
// helper functions
- void warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, AccessKind AK,
- Expr *MutexExp, ProtectedOperationKind POK,
- til::LiteralPtr *Self, SourceLocation Loc);
- void warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, Expr *MutexExp,
- til::LiteralPtr *Self, SourceLocation Loc);
void checkAccess(const Expr *Exp, AccessKind AK,
- ProtectedOperationKind POK = POK_VarAccess);
+ ProtectedOperationKind POK = POK_VarAccess) {
+ Analyzer->checkAccess(FSet, Exp, AK, POK);
+ }
void checkPtAccess(const Expr *Exp, AccessKind AK,
- ProtectedOperationKind POK = POK_VarAccess);
+ ProtectedOperationKind POK = POK_VarAccess) {
+ Analyzer->checkPtAccess(FSet, Exp, AK, POK);
+ }
void handleCall(const Expr *Exp, const NamedDecl *D,
til::LiteralPtr *Self = nullptr,
@@ -1554,9 +1568,11 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
bool SkipFirstParam = false);
public:
- BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info)
+ BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info,
+ const FactSet &FunctionExitFSet)
: ConstStmtVisitor<BuildLockset>(), Analyzer(Anlzr), FSet(Info.EntrySet),
- LVarCtx(Info.EntryContext), CtxIndex(Info.EntryIndex) {}
+ FunctionExitFSet(FunctionExitFSet), LVarCtx(Info.EntryContext),
+ CtxIndex(Info.EntryIndex) {}
void VisitUnaryOperator(const UnaryOperator *UO);
void VisitBinaryOperator(const BinaryOperator *BO);
@@ -1565,23 +1581,21 @@ public:
void VisitCXXConstructExpr(const CXXConstructExpr *Exp);
void VisitDeclStmt(const DeclStmt *S);
void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Exp);
+ void VisitReturnStmt(const ReturnStmt *S);
};
} // namespace
/// Warn if the LSet does not contain a lock sufficient to protect access
/// of at least the passed in AccessKind.
-void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,
- AccessKind AK, Expr *MutexExp,
- ProtectedOperationKind POK,
- til::LiteralPtr *Self,
- SourceLocation Loc) {
+void ThreadSafetyAnalyzer::warnIfMutexNotHeld(
+ const FactSet &FSet, const NamedDecl *D, const Expr *Exp, AccessKind AK,
+ Expr *MutexExp, ProtectedOperationKind POK, til::LiteralPtr *Self,
+ SourceLocation Loc) {
LockKind LK = getLockKindFromAccessKind(AK);
-
- CapabilityExpr Cp =
- Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp, Self);
+ CapabilityExpr Cp = SxBuilder.translateAttrExpr(MutexExp, D, Exp, Self);
if (Cp.isInvalid()) {
- warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, Cp.getKind());
+ warnInvalidLock(Handler, MutexExp, D, Exp, Cp.getKind());
return;
} else if (Cp.shouldIgnore()) {
return;
@@ -1589,68 +1603,67 @@ void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,
if (Cp.negative()) {
// Negative capabilities act like locks excluded
- const FactEntry *LDat = FSet.findLock(Analyzer->FactMan, !Cp);
+ const FactEntry *LDat = FSet.findLock(FactMan, !Cp);
if (LDat) {
- Analyzer->Handler.handleFunExcludesLock(
- Cp.getKind(), D->getNameAsString(), (!Cp).toString(), Loc);
+ Handler.handleFunExcludesLock(Cp.getKind(), D->getNameAsString(),
+ (!Cp).toString(), Loc);
return;
}
// If this does not refer to a negative capability in the same class,
// then stop here.
- if (!Analyzer->inCurrentScope(Cp))
+ if (!inCurrentScope(Cp))
return;
// Otherwise the negative requirement must be propagated to the caller.
- LDat = FSet.findLock(Analyzer->FactMan, Cp);
+ LDat = FSet.findLock(FactMan, Cp);
if (!LDat) {
- Analyzer->Handler.handleNegativeNotHeld(D, Cp.toString(), Loc);
+ Handler.handleNegativeNotHeld(D, Cp.toString(), Loc);
}
return;
}
- const FactEntry *LDat = FSet.findLockUniv(Analyzer->FactMan, Cp);
+ const FactEntry *LDat = FSet.findLockUniv(FactMan, Cp);
bool NoError = true;
if (!LDat) {
// No exact match found. Look for a partial match.
- LDat = FSet.findPartialMatch(Analyzer->FactMan, Cp);
+ LDat = FSet.findPartialMatch(FactMan, Cp);
if (LDat) {
// Warn that there's no precise match.
std::string PartMatchStr = LDat->toString();
StringRef PartMatchName(PartMatchStr);
- Analyzer->Handler.handleMutexNotHeld(Cp.getKind(), D, POK, Cp.toString(),
- LK, Loc, &PartMatchName);
+ Handler.handleMutexNotHeld(Cp.getKind(), D, POK, Cp.toString(), LK, Loc,
+ &PartMatchName);
} else {
// Warn that there's no match at all.
- Analyzer->Handler.handleMutexNotHeld(Cp.getKind(), D, POK, Cp.toString(),
- LK, Loc);
+ Handler.handleMutexNotHeld(Cp.getKind(), D, POK, Cp.toString(), LK, Loc);
}
NoError = false;
}
// Make sure the mutex we found is the right kind.
if (NoError && LDat && !LDat->isAtLeast(LK)) {
- Analyzer->Handler.handleMutexNotHeld(Cp.getKind(), D, POK, Cp.toString(),
- LK, Loc);
+ Handler.handleMutexNotHeld(Cp.getKind(), D, POK, Cp.toString(), LK, Loc);
}
}
/// Warn if the LSet contains the given lock.
-void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr *Exp,
- Expr *MutexExp, til::LiteralPtr *Self,
- SourceLocation Loc) {
- CapabilityExpr Cp =
- Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp, Self);
+void ThreadSafetyAnalyzer::warnIfMutexHeld(const FactSet &FSet,
+ const NamedDecl *D, const Expr *Exp,
+ Expr *MutexExp,
+ til::LiteralPtr *Self,
+ SourceLocation Loc) {
+ CapabilityExpr Cp = SxBuilder.translateAttrExpr(MutexExp, D, Exp, Self);
if (Cp.isInvalid()) {
- warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, Cp.getKind());
+ warnInvalidLock(Handler, MutexExp, D, Exp, Cp.getKind());
return;
} else if (Cp.shouldIgnore()) {
return;
}
- const FactEntry *LDat = FSet.findLock(Analyzer->FactMan, Cp);
+ const FactEntry *LDat = FSet.findLock(FactMan, Cp);
if (LDat) {
- Analyzer->Handler.handleFunExcludesLock(Cp.getKind(), D->getNameAsString(),
- Cp.toString(), Loc);
+ Handler.handleFunExcludesLock(Cp.getKind(), D->getNameAsString(),
+ Cp.toString(), Loc);
}
}
@@ -1659,8 +1672,9 @@ void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr *Exp,
/// marked with guarded_by, we must ensure the appropriate mutexes are held.
/// Similarly, we check if the access is to an expression that dereferences
/// a pointer marked with pt_guarded_by.
-void BuildLockset::checkAccess(const Expr *Exp, AccessKind AK,
- ProtectedOperationKind POK) {
+void ThreadSafetyAnalyzer::checkAccess(const FactSet &FSet, const Expr *Exp,
+ AccessKind AK,
+ ProtectedOperationKind POK) {
Exp = Exp->IgnoreImplicit()->IgnoreParenCasts();
SourceLocation Loc = Exp->getExprLoc();
@@ -1684,49 +1698,50 @@ void BuildLockset::checkAccess(const Expr *Exp, AccessKind AK,
if (const auto *UO = dyn_cast<UnaryOperator>(Exp)) {
// For dereferences
if (UO->getOpcode() == UO_Deref)
- checkPtAccess(UO->getSubExpr(), AK, POK);
+ checkPtAccess(FSet, UO->getSubExpr(), AK, POK);
return;
}
if (const auto *BO = dyn_cast<BinaryOperator>(Exp)) {
switch (BO->getOpcode()) {
case BO_PtrMemD: // .*
- return checkAccess(BO->getLHS(), AK, POK);
+ return checkAccess(FSet, BO->getLHS(), AK, POK);
case BO_PtrMemI: // ->*
- return checkPtAccess(BO->getLHS(), AK, POK);
+ return checkPtAccess(FSet, BO->getLHS(), AK, POK);
default:
return;
}
}
if (const auto *AE = dyn_cast<ArraySubscriptExpr>(Exp)) {
- checkPtAccess(AE->getLHS(), AK, POK);
+ checkPtAccess(FSet, AE->getLHS(), AK, POK);
return;
}
if (const auto *ME = dyn_cast<MemberExpr>(Exp)) {
if (ME->isArrow())
- checkPtAccess(ME->getBase(), AK, POK);
+ checkPtAccess(FSet, ME->getBase(), AK, POK);
else
- checkAccess(ME->getBase(), AK, POK);
+ checkAccess(FSet, ME->getBase(), AK, POK);
}
const ValueDecl *D = getValueDecl(Exp);
if (!D || !D->hasAttrs())
return;
- if (D->hasAttr<GuardedVarAttr>() && FSet.isEmpty(Analyzer->FactMan)) {
- Analyzer->Handler.handleNoMutexHeld(D, POK, AK, Loc);
+ if (D->hasAttr<GuardedVarAttr>() && FSet.isEmpty(FactMan)) {
+ Handler.handleNoMutexHeld(D, POK, AK, Loc);
}
for (const auto *I : D->specific_attrs<GuardedByAttr>())
- warnIfMutexNotHeld(D, Exp, AK, I->getArg(), POK, nullptr, Loc);
+ warnIfMutexNotHeld(FSet, D, Exp, AK, I->getArg(), POK, nullptr, Loc);
}
/// Checks pt_guarded_by and pt_guarded_var attributes.
/// POK is the same operationKind that was passed to checkAccess.
-void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK,
- ProtectedOperationKind POK) {
+void ThreadSafetyAnalyzer::checkPtAccess(const FactSet &FSet, const Expr *Exp,
+ AccessKind AK,
+ ProtectedOperationKind POK) {
while (true) {
if (const auto *PE = dyn_cast<ParenExpr>(Exp)) {
Exp = PE->getSubExpr();
@@ -1736,7 +1751,7 @@ void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK,
if (CE->getCastKind() == CK_ArrayToPointerDecay) {
// If it's an actual array, and not a pointer, then it's elements
// are protected by GUARDED_BY, not PT_GUARDED_BY;
- checkAccess(CE->getSubExpr(), AK, POK);
+ checkAccess(FSet, CE->getSubExpr(), AK, POK);
return;
}
Exp = CE->getSubExpr();
@@ -1748,16 +1763,18 @@ void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK,
// Pass by reference warnings are under a different flag.
ProtectedOperationKind PtPOK = POK_VarDereference;
if (POK == POK_PassByRef) PtPOK = POK_PtPassByRef;
+ if (POK == POK_ReturnByRef)
+ PtPOK = POK_PtReturnByRef;
const ValueDecl *D = getValueDecl(Exp);
if (!D || !D->hasAttrs())
return;
- if (D->hasAttr<PtGuardedVarAttr>() && FSet.isEmpty(Analyzer->FactMan))
- Analyzer->Handler.handleNoMutexHeld(D, PtPOK, AK, Exp->getExprLoc());
+ if (D->hasAttr<PtGuardedVarAttr>() && FSet.isEmpty(FactMan))
+ Handler.handleNoMutexHeld(D, PtPOK, AK, Exp->getExprLoc());
for (auto const *I : D->specific_attrs<PtGuardedByAttr>())
- warnIfMutexNotHeld(D, Exp, AK, I->getArg(), PtPOK, nullptr,
+ warnIfMutexNotHeld(FSet, D, Exp, AK, I->getArg(), PtPOK, nullptr,
Exp->getExprLoc());
}
@@ -1773,7 +1790,8 @@ void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK,
///
/// \param Exp The call expression.
/// \param D The callee declaration.
-/// \param Self If \p Exp = nullptr, the implicit this argument.
+/// \param Self If \p Exp = nullptr, the implicit this argument or the argument
+/// of an implicitly called cleanup function.
/// \param Loc If \p Exp = nullptr, the location.
void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
til::LiteralPtr *Self, SourceLocation Loc) {
@@ -1790,7 +1808,7 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
std::pair<til::LiteralPtr *, StringRef> Placeholder =
Analyzer->SxBuilder.createThisPlaceholder(Exp);
[[maybe_unused]] auto inserted =
- ConstructedObjects.insert({Exp, Placeholder.first});
+ Analyzer->ConstructedObjects.insert({Exp, Placeholder.first});
assert(inserted.second && "Are we visiting the same expression again?");
if (isa<CXXConstructExpr>(Exp))
Self = Placeholder.first;
@@ -1868,8 +1886,9 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
case attr::RequiresCapability: {
const auto *A = cast<RequiresCapabilityAttr>(At);
for (auto *Arg : A->args()) {
- warnIfMutexNotHeld(D, Exp, A->isShared() ? AK_Read : AK_Written, Arg,
- POK_FunctionCall, Self, Loc);
+ Analyzer->warnIfMutexNotHeld(FSet, D, Exp,
+ A->isShared() ? AK_Read : AK_Written,
+ Arg, POK_FunctionCall, Self, Loc);
// use for adopting a lock
if (!Scp.shouldIgnore())
Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp, D, Self);
@@ -1880,7 +1899,7 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
case attr::LocksExcluded: {
const auto *A = cast<LocksExcludedAttr>(At);
for (auto *Arg : A->args()) {
- warnIfMutexHeld(D, Exp, Arg, Self, Loc);
+ Analyzer->warnIfMutexHeld(FSet, D, Exp, Arg, Self, Loc);
// use for deferring a lock
if (!Scp.shouldIgnore())
Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp, D, Self);
@@ -2109,10 +2128,10 @@ void BuildLockset::VisitDeclStmt(const DeclStmt *S) {
E = EWC->getSubExpr()->IgnoreParens();
E = UnpackConstruction(E);
- if (auto Object = ConstructedObjects.find(E);
- Object != ConstructedObjects.end()) {
+ if (auto Object = Analyzer->ConstructedObjects.find(E);
+ Object != Analyzer->ConstructedObjects.end()) {
Object->second->setClangDecl(VD);
- ConstructedObjects.erase(Object);
+ Analyzer->ConstructedObjects.erase(Object);
}
}
}
@@ -2121,15 +2140,34 @@ void BuildLockset::VisitDeclStmt(const DeclStmt *S) {
void BuildLockset::VisitMaterializeTemporaryExpr(
const MaterializeTemporaryExpr *Exp) {
if (const ValueDecl *ExtD = Exp->getExtendingDecl()) {
- if (auto Object =
- ConstructedObjects.find(UnpackConstruction(Exp->getSubExpr()));
- Object != ConstructedObjects.end()) {
+ if (auto Object = Analyzer->ConstructedObjects.find(
+ UnpackConstruction(Exp->getSubExpr()));
+ Object != Analyzer->ConstructedObjects.end()) {
Object->second->setClangDecl(ExtD);
- ConstructedObjects.erase(Object);
+ Analyzer->ConstructedObjects.erase(Object);
}
}
}
+void BuildLockset::VisitReturnStmt(const ReturnStmt *S) {
+ if (Analyzer->CurrentFunction == nullptr)
+ return;
+ const Expr *RetVal = S->getRetValue();
+ if (!RetVal)
+ return;
+
+ // If returning by reference, check that the function requires the appropriate
+ // capabilities.
+ const QualType ReturnType =
+ Analyzer->CurrentFunction->getReturnType().getCanonicalType();
+ if (ReturnType->isLValueReferenceType()) {
+ Analyzer->checkAccess(
+ FunctionExitFSet, RetVal,
+ ReturnType->getPointeeType().isConstQualified() ? AK_Read : AK_Written,
+ POK_ReturnByRef);
+ }
+}
+
/// Given two facts merging on a join point, possibly warn and decide whether to
/// keep or replace.
///
@@ -2239,8 +2277,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
CFG *CFGraph = walker.getGraph();
const NamedDecl *D = walker.getDecl();
- const auto *CurrentFunction = dyn_cast<FunctionDecl>(D);
- CurrentMethod = dyn_cast<CXXMethodDecl>(D);
+ CurrentFunction = dyn_cast<FunctionDecl>(D);
if (D->hasAttr<NoThreadSafetyAnalysisAttr>())
return;
@@ -2265,8 +2302,11 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
const PostOrderCFGView *SortedGraph = walker.getSortedGraph();
PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph);
+ CFGBlockInfo &Initial = BlockInfo[CFGraph->getEntry().getBlockID()];
+ CFGBlockInfo &Final = BlockInfo[CFGraph->getExit().getBlockID()];
+
// Mark entry block as reachable
- BlockInfo[CFGraph->getEntry().getBlockID()].Reachable = true;
+ Initial.Reachable = true;
// Compute SSA names for local variables
LocalVarMap.traverseCFG(CFGraph, SortedGraph, BlockInfo);
@@ -2282,8 +2322,8 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
// to initial lockset. Also turn off checking for lock and unlock functions.
// FIXME: is there a more intelligent way to check lock/unlock functions?
if (!SortedGraph->empty() && D->hasAttrs()) {
- const CFGBlock *FirstBlock = *SortedGraph->begin();
- FactSet &InitialLockset = BlockInfo[FirstBlock->getBlockID()].EntrySet;
+ assert(*SortedGraph->begin() == &CFGraph->getEntry());
+ FactSet &InitialLockset = Initial.EntrySet;
CapExprSet ExclusiveLocksToAdd;
CapExprSet SharedLocksToAdd;
@@ -2333,6 +2373,25 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
}
}
+ // Compute the expected exit set.
+ // By default, we expect all locks held on entry to be held on exit.
+ FactSet ExpectedFunctionExitSet = Initial.EntrySet;
+
+ // Adjust the expected exit set by adding or removing locks, as declared
+ // by *-LOCK_FUNCTION and UNLOCK_FUNCTION. The intersect below will then
+ // issue the appropriate warning.
+ // FIXME: the location here is not quite right.
+ for (const auto &Lock : ExclusiveLocksAcquired)
+ ExpectedFunctionExitSet.addLock(
+ FactMan, std::make_unique<LockableFactEntry>(Lock, LK_Exclusive,
+ D->getLocation()));
+ for (const auto &Lock : SharedLocksAcquired)
+ ExpectedFunctionExitSet.addLock(
+ FactMan,
+ std::make_unique<LockableFactEntry>(Lock, LK_Shared, D->getLocation()));
+ for (const auto &Lock : LocksReleased)
+ ExpectedFunctionExitSet.removeLock(FactMan, Lock);
+
for (const auto *CurrBlock : *SortedGraph) {
unsigned CurrBlockID = CurrBlock->getBlockID();
CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
@@ -2392,7 +2451,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
if (!CurrBlockInfo->Reachable)
continue;
- BuildLockset LocksetBuilder(this, *CurrBlockInfo);
+ BuildLockset LocksetBuilder(this, *CurrBlockInfo, ExpectedFunctionExitSet);
// Visit all the statements in the basic block.
for (const auto &BI : *CurrBlock) {
@@ -2414,20 +2473,29 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
AD.getTriggerStmt()->getEndLoc());
break;
}
+
+ case CFGElement::CleanupFunction: {
+ const CFGCleanupFunction &CF = BI.castAs<CFGCleanupFunction>();
+ LocksetBuilder.handleCall(/*Exp=*/nullptr, CF.getFunctionDecl(),
+ SxBuilder.createVariable(CF.getVarDecl()),
+ CF.getVarDecl()->getLocation());
+ break;
+ }
+
case CFGElement::TemporaryDtor: {
auto TD = BI.castAs<CFGTemporaryDtor>();
// Clean up constructed object even if there are no attributes to
// keep the number of objects in limbo as small as possible.
- if (auto Object = LocksetBuilder.ConstructedObjects.find(
+ if (auto Object = ConstructedObjects.find(
TD.getBindTemporaryExpr()->getSubExpr());
- Object != LocksetBuilder.ConstructedObjects.end()) {
+ Object != ConstructedObjects.end()) {
const auto *DD = TD.getDestructorDecl(AC.getASTContext());
if (DD->hasAttrs())
// TODO: the location here isn't quite correct.
LocksetBuilder.handleCall(nullptr, DD, Object->second,
TD.getBindTemporaryExpr()->getEndLoc());
- LocksetBuilder.ConstructedObjects.erase(Object);
+ ConstructedObjects.erase(Object);
}
break;
}
@@ -2455,31 +2523,12 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
}
}
- CFGBlockInfo *Initial = &BlockInfo[CFGraph->getEntry().getBlockID()];
- CFGBlockInfo *Final = &BlockInfo[CFGraph->getExit().getBlockID()];
-
// Skip the final check if the exit block is unreachable.
- if (!Final->Reachable)
+ if (!Final.Reachable)
return;
- // By default, we expect all locks held on entry to be held on exit.
- FactSet ExpectedExitSet = Initial->EntrySet;
-
- // Adjust the expected exit set by adding or removing locks, as declared
- // by *-LOCK_FUNCTION and UNLOCK_FUNCTION. The intersect below will then
- // issue the appropriate warning.
- // FIXME: the location here is not quite right.
- for (const auto &Lock : ExclusiveLocksAcquired)
- ExpectedExitSet.addLock(FactMan, std::make_unique<LockableFactEntry>(
- Lock, LK_Exclusive, D->getLocation()));
- for (const auto &Lock : SharedLocksAcquired)
- ExpectedExitSet.addLock(FactMan, std::make_unique<LockableFactEntry>(
- Lock, LK_Shared, D->getLocation()));
- for (const auto &Lock : LocksReleased)
- ExpectedExitSet.removeLock(FactMan, Lock);
-
// FIXME: Should we call this function for all blocks which exit the function?
- intersectAndWarn(ExpectedExitSet, Final->ExitSet, Final->ExitLoc,
+ intersectAndWarn(ExpectedFunctionExitSet, Final.ExitSet, Final.ExitLoc,
LEK_LockedAtEndOfFunction, LEK_NotLockedAtEndOfFunction);
Handler.leaveFunction(CurrentFunction);