@@ -1008,7 +1008,7 @@ class ThreadSafetyAnalyzer {
1008
1008
threadSafety::SExprBuilder SxBuilder;
1009
1009
1010
1010
ThreadSafetyHandler &Handler;
1011
- const CXXMethodDecl *CurrentMethod = nullptr ;
1011
+ const FunctionDecl *CurrentFunction ;
1012
1012
LocalVariableMap LocalVarMap;
1013
1013
FactManager FactMan;
1014
1014
std::vector<CFGBlockInfo> BlockInfo;
@@ -1243,10 +1243,10 @@ bool ThreadSafetyAnalyzer::inCurrentScope(const CapabilityExpr &CapE) {
1243
1243
1244
1244
// Members are in scope from methods of the same class.
1245
1245
if (const auto *P = dyn_cast<til::Project>(SExp)) {
1246
- if (!CurrentMethod )
1246
+ if (!isa_and_nonnull<CXXMethodDecl>(CurrentFunction) )
1247
1247
return false ;
1248
1248
const ValueDecl *VD = P->clangDecl ();
1249
- return VD->getDeclContext () == CurrentMethod ->getDeclContext ();
1249
+ return VD->getDeclContext () == CurrentFunction ->getDeclContext ();
1250
1250
}
1251
1251
1252
1252
return false ;
@@ -1541,6 +1541,8 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
1541
1541
1542
1542
ThreadSafetyAnalyzer *Analyzer;
1543
1543
FactSet FSet;
1544
+ // The fact set for the function on exit.
1545
+ const FactSet &FunctionExitFSet;
1544
1546
// / Maps constructed objects to `this` placeholder prior to initialization.
1545
1547
llvm::SmallDenseMap<const Expr *, til::LiteralPtr *> ConstructedObjects;
1546
1548
LocalVariableMap::Context LVarCtx;
@@ -1566,9 +1568,11 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
1566
1568
bool SkipFirstParam = false );
1567
1569
1568
1570
public:
1569
- BuildLockset (ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info)
1571
+ BuildLockset (ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info,
1572
+ const FactSet &FunctionExitFSet)
1570
1573
: ConstStmtVisitor<BuildLockset>(), Analyzer(Anlzr), FSet(Info.EntrySet),
1571
- LVarCtx (Info.EntryContext), CtxIndex(Info.EntryIndex) {}
1574
+ FunctionExitFSet (FunctionExitFSet), LVarCtx(Info.EntryContext),
1575
+ CtxIndex (Info.EntryIndex) {}
1572
1576
1573
1577
void VisitUnaryOperator (const UnaryOperator *UO);
1574
1578
void VisitBinaryOperator (const BinaryOperator *BO);
@@ -1577,6 +1581,7 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
1577
1581
void VisitCXXConstructExpr (const CXXConstructExpr *Exp);
1578
1582
void VisitDeclStmt (const DeclStmt *S);
1579
1583
void VisitMaterializeTemporaryExpr (const MaterializeTemporaryExpr *Exp);
1584
+ void VisitReturnStmt (const ReturnStmt *S);
1580
1585
};
1581
1586
1582
1587
} // namespace
@@ -1758,6 +1763,8 @@ void ThreadSafetyAnalyzer::checkPtAccess(const FactSet &FSet, const Expr *Exp,
1758
1763
// Pass by reference warnings are under a different flag.
1759
1764
ProtectedOperationKind PtPOK = POK_VarDereference;
1760
1765
if (POK == POK_PassByRef) PtPOK = POK_PtPassByRef;
1766
+ if (POK == POK_ReturnByRef)
1767
+ PtPOK = POK_PtReturnByRef;
1761
1768
1762
1769
const ValueDecl *D = getValueDecl (Exp);
1763
1770
if (!D || !D->hasAttrs ())
@@ -2142,6 +2149,25 @@ void BuildLockset::VisitMaterializeTemporaryExpr(
2142
2149
}
2143
2150
}
2144
2151
2152
+ void BuildLockset::VisitReturnStmt (const ReturnStmt *S) {
2153
+ if (Analyzer->CurrentFunction == nullptr )
2154
+ return ;
2155
+ const Expr *RetVal = S->getRetValue ();
2156
+ if (!RetVal)
2157
+ return ;
2158
+
2159
+ // If returning by reference, check that the function requires the appropriate
2160
+ // capabilities.
2161
+ const QualType ReturnType =
2162
+ Analyzer->CurrentFunction ->getReturnType ().getCanonicalType ();
2163
+ if (ReturnType->isLValueReferenceType ()) {
2164
+ Analyzer->checkAccess (
2165
+ FunctionExitFSet, RetVal,
2166
+ ReturnType->getPointeeType ().isConstQualified () ? AK_Read : AK_Written,
2167
+ POK_ReturnByRef);
2168
+ }
2169
+ }
2170
+
2145
2171
// / Given two facts merging on a join point, possibly warn and decide whether to
2146
2172
// / keep or replace.
2147
2173
// /
@@ -2251,8 +2277,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
2251
2277
2252
2278
CFG *CFGraph = walker.getGraph ();
2253
2279
const NamedDecl *D = walker.getDecl ();
2254
- const auto *CurrentFunction = dyn_cast<FunctionDecl>(D);
2255
- CurrentMethod = dyn_cast<CXXMethodDecl>(D);
2280
+ CurrentFunction = dyn_cast<FunctionDecl>(D);
2256
2281
2257
2282
if (D->hasAttr <NoThreadSafetyAnalysisAttr>())
2258
2283
return ;
@@ -2278,7 +2303,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
2278
2303
PostOrderCFGView::CFGBlockSet VisitedBlocks (CFGraph);
2279
2304
2280
2305
CFGBlockInfo &Initial = BlockInfo[CFGraph->getEntry ().getBlockID ()];
2281
- CFGBlockInfo &Final = BlockInfo[CFGraph->getExit ().getBlockID ()];
2306
+ CFGBlockInfo &Final = BlockInfo[CFGraph->getExit ().getBlockID ()];
2282
2307
2283
2308
// Mark entry block as reachable
2284
2309
Initial.Reachable = true ;
@@ -2348,6 +2373,25 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
2348
2373
}
2349
2374
}
2350
2375
2376
+ // Compute the expected exit set.
2377
+ // By default, we expect all locks held on entry to be held on exit.
2378
+ FactSet ExpectedFunctionExitSet = Initial.EntrySet ;
2379
+
2380
+ // Adjust the expected exit set by adding or removing locks, as declared
2381
+ // by *-LOCK_FUNCTION and UNLOCK_FUNCTION. The intersect below will then
2382
+ // issue the appropriate warning.
2383
+ // FIXME: the location here is not quite right.
2384
+ for (const auto &Lock : ExclusiveLocksAcquired)
2385
+ ExpectedFunctionExitSet.addLock (
2386
+ FactMan, std::make_unique<LockableFactEntry>(Lock, LK_Exclusive,
2387
+ D->getLocation ()));
2388
+ for (const auto &Lock : SharedLocksAcquired)
2389
+ ExpectedFunctionExitSet.addLock (
2390
+ FactMan,
2391
+ std::make_unique<LockableFactEntry>(Lock, LK_Shared, D->getLocation ()));
2392
+ for (const auto &Lock : LocksReleased)
2393
+ ExpectedFunctionExitSet.removeLock (FactMan, Lock);
2394
+
2351
2395
for (const auto *CurrBlock : *SortedGraph) {
2352
2396
unsigned CurrBlockID = CurrBlock->getBlockID ();
2353
2397
CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
@@ -2407,7 +2451,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
2407
2451
if (!CurrBlockInfo->Reachable )
2408
2452
continue ;
2409
2453
2410
- BuildLockset LocksetBuilder (this , *CurrBlockInfo);
2454
+ BuildLockset LocksetBuilder (this , *CurrBlockInfo, ExpectedFunctionExitSet );
2411
2455
2412
2456
// Visit all the statements in the basic block.
2413
2457
for (const auto &BI : *CurrBlock) {
@@ -2483,24 +2527,8 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
2483
2527
if (!Final.Reachable )
2484
2528
return ;
2485
2529
2486
- // By default, we expect all locks held on entry to be held on exit.
2487
- FactSet ExpectedExitSet = Initial.EntrySet ;
2488
-
2489
- // Adjust the expected exit set by adding or removing locks, as declared
2490
- // by *-LOCK_FUNCTION and UNLOCK_FUNCTION. The intersect below will then
2491
- // issue the appropriate warning.
2492
- // FIXME: the location here is not quite right.
2493
- for (const auto &Lock : ExclusiveLocksAcquired)
2494
- ExpectedExitSet.addLock (FactMan, std::make_unique<LockableFactEntry>(
2495
- Lock, LK_Exclusive, D->getLocation ()));
2496
- for (const auto &Lock : SharedLocksAcquired)
2497
- ExpectedExitSet.addLock (FactMan, std::make_unique<LockableFactEntry>(
2498
- Lock, LK_Shared, D->getLocation ()));
2499
- for (const auto &Lock : LocksReleased)
2500
- ExpectedExitSet.removeLock (FactMan, Lock);
2501
-
2502
2530
// FIXME: Should we call this function for all blocks which exit the function?
2503
- intersectAndWarn (ExpectedExitSet , Final.ExitSet , Final.ExitLoc ,
2531
+ intersectAndWarn (ExpectedFunctionExitSet , Final.ExitSet , Final.ExitLoc ,
2504
2532
LEK_LockedAtEndOfFunction, LEK_NotLockedAtEndOfFunction);
2505
2533
2506
2534
Handler.leaveFunction (CurrentFunction);
0 commit comments