@@ -1008,7 +1008,7 @@ class ThreadSafetyAnalyzer {
10081008 threadSafety::SExprBuilder SxBuilder;
10091009
10101010 ThreadSafetyHandler &Handler;
1011- const CXXMethodDecl *CurrentMethod = nullptr ;
1011+ const FunctionDecl *CurrentFunction ;
10121012 LocalVariableMap LocalVarMap;
10131013 FactManager FactMan;
10141014 std::vector<CFGBlockInfo> BlockInfo;
@@ -1243,10 +1243,10 @@ bool ThreadSafetyAnalyzer::inCurrentScope(const CapabilityExpr &CapE) {
12431243
12441244 // Members are in scope from methods of the same class.
12451245 if (const auto *P = dyn_cast<til::Project>(SExp)) {
1246- if (!CurrentMethod )
1246+ if (!isa_and_nonnull<CXXMethodDecl>(CurrentFunction) )
12471247 return false ;
12481248 const ValueDecl *VD = P->clangDecl ();
1249- return VD->getDeclContext () == CurrentMethod ->getDeclContext ();
1249+ return VD->getDeclContext () == CurrentFunction ->getDeclContext ();
12501250 }
12511251
12521252 return false ;
@@ -1541,6 +1541,8 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
15411541
15421542 ThreadSafetyAnalyzer *Analyzer;
15431543 FactSet FSet;
1544+ // The fact set for the function on exit.
1545+ const FactSet &FunctionExitFSet;
15441546 // / Maps constructed objects to `this` placeholder prior to initialization.
15451547 llvm::SmallDenseMap<const Expr *, til::LiteralPtr *> ConstructedObjects;
15461548 LocalVariableMap::Context LVarCtx;
@@ -1566,9 +1568,11 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
15661568 bool SkipFirstParam = false );
15671569
15681570public:
1569- BuildLockset (ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info)
1571+ BuildLockset (ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info,
1572+ const FactSet &FunctionExitFSet)
15701573 : ConstStmtVisitor<BuildLockset>(), Analyzer(Anlzr), FSet(Info.EntrySet),
1571- LVarCtx (Info.EntryContext), CtxIndex(Info.EntryIndex) {}
1574+ FunctionExitFSet (FunctionExitFSet), LVarCtx(Info.EntryContext),
1575+ CtxIndex (Info.EntryIndex) {}
15721576
15731577 void VisitUnaryOperator (const UnaryOperator *UO);
15741578 void VisitBinaryOperator (const BinaryOperator *BO);
@@ -1577,6 +1581,7 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
15771581 void VisitCXXConstructExpr (const CXXConstructExpr *Exp);
15781582 void VisitDeclStmt (const DeclStmt *S);
15791583 void VisitMaterializeTemporaryExpr (const MaterializeTemporaryExpr *Exp);
1584+ void VisitReturnStmt (const ReturnStmt *S);
15801585};
15811586
15821587} // namespace
@@ -1758,6 +1763,8 @@ void ThreadSafetyAnalyzer::checkPtAccess(const FactSet &FSet, const Expr *Exp,
17581763 // Pass by reference warnings are under a different flag.
17591764 ProtectedOperationKind PtPOK = POK_VarDereference;
17601765 if (POK == POK_PassByRef) PtPOK = POK_PtPassByRef;
1766+ if (POK == POK_ReturnByRef)
1767+ PtPOK = POK_PtReturnByRef;
17611768
17621769 const ValueDecl *D = getValueDecl (Exp);
17631770 if (!D || !D->hasAttrs ())
@@ -2142,6 +2149,25 @@ void BuildLockset::VisitMaterializeTemporaryExpr(
21422149 }
21432150}
21442151
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+
21452171// / Given two facts merging on a join point, possibly warn and decide whether to
21462172// / keep or replace.
21472173// /
@@ -2251,8 +2277,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
22512277
22522278 CFG *CFGraph = walker.getGraph ();
22532279 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);
22562281
22572282 if (D->hasAttr <NoThreadSafetyAnalysisAttr>())
22582283 return ;
@@ -2278,7 +2303,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
22782303 PostOrderCFGView::CFGBlockSet VisitedBlocks (CFGraph);
22792304
22802305 CFGBlockInfo &Initial = BlockInfo[CFGraph->getEntry ().getBlockID ()];
2281- CFGBlockInfo &Final = BlockInfo[CFGraph->getExit ().getBlockID ()];
2306+ CFGBlockInfo &Final = BlockInfo[CFGraph->getExit ().getBlockID ()];
22822307
22832308 // Mark entry block as reachable
22842309 Initial.Reachable = true ;
@@ -2348,6 +2373,25 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
23482373 }
23492374 }
23502375
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+
23512395 for (const auto *CurrBlock : *SortedGraph) {
23522396 unsigned CurrBlockID = CurrBlock->getBlockID ();
23532397 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
@@ -2407,7 +2451,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
24072451 if (!CurrBlockInfo->Reachable )
24082452 continue ;
24092453
2410- BuildLockset LocksetBuilder (this , *CurrBlockInfo);
2454+ BuildLockset LocksetBuilder (this , *CurrBlockInfo, ExpectedFunctionExitSet );
24112455
24122456 // Visit all the statements in the basic block.
24132457 for (const auto &BI : *CurrBlock) {
@@ -2483,24 +2527,8 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
24832527 if (!Final.Reachable )
24842528 return ;
24852529
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-
25022530 // 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 ,
25042532 LEK_LockedAtEndOfFunction, LEK_NotLockedAtEndOfFunction);
25052533
25062534 Handler.leaveFunction (CurrentFunction);
0 commit comments