diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2023-12-18 20:30:12 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2024-04-06 20:11:55 +0000 |
| commit | 5f757f3ff9144b609b3c433dfd370cc6bdc191ad (patch) | |
| tree | 1b4e980b866cd26a00af34c0a653eb640bd09caf /contrib/llvm-project/clang/lib/Analysis/ThreadSafety.cpp | |
| parent | 3e1c8a35f741a5d114d0ba670b15191355711fe9 (diff) | |
| parent | 312c0ed19cc5276a17bacf2120097bec4515b0f1 (diff) | |
Diffstat (limited to 'contrib/llvm-project/clang/lib/Analysis/ThreadSafety.cpp')
| -rw-r--r-- | contrib/llvm-project/clang/lib/Analysis/ThreadSafety.cpp | 259 |
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); |
