Skip to content

Commit 71b81e9

Browse files
authored
[alpha.webkit.UncountedLocalVarsChecker] Recursive functions are erroneously treated as non-trivial (#110973)
This PR fixes the bug that alpha.webkit.UncountedLocalVarsChecker erroneously treats a trivial recursive function as non-trivial. This was caused by TrivialFunctionAnalysis::isTrivialImpl which takes a statement as an argument populating the cache with "false" while traversing the statement to determine its triviality within a recursive function in TrivialFunctionAnalysisVisitor's WithCachedResult. Because IsFunctionTrivial honors an entry in the cache, this resulted in the whole function to be treated as non-trivial. Thankfully, TrivialFunctionAnalysisVisitor::IsFunctionTrivial already handles recursive functions correctly so this PR applies the same logic to TrivialFunctionAnalysisVisitor::WithCachedResult by sharing code between the two functions. This avoids the cache to be pre-populated with "false" while traversing statements in a recurisve function.
1 parent 5033ea7 commit 71b81e9

File tree

3 files changed

+73
-36
lines changed

3 files changed

+73
-36
lines changed

clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -273,16 +273,29 @@ class TrivialFunctionAnalysisVisitor
273273
return true;
274274
}
275275

276-
template <typename CheckFunction>
277-
bool WithCachedResult(const Stmt *S, CheckFunction Function) {
278-
// If the statement isn't in the cache, conservatively assume that
279-
// it's not trivial until analysis completes. Insert false to the cache
280-
// first to avoid infinite recursion.
281-
auto [It, IsNew] = Cache.insert(std::make_pair(S, false));
276+
template <typename StmtOrDecl, typename CheckFunction>
277+
bool WithCachedResult(const StmtOrDecl *S, CheckFunction Function) {
278+
auto CacheIt = Cache.find(S);
279+
if (CacheIt != Cache.end())
280+
return CacheIt->second;
281+
282+
// Treat a recursive statement to be trivial until proven otherwise.
283+
auto [RecursiveIt, IsNew] = RecursiveFn.insert(std::make_pair(S, true));
282284
if (!IsNew)
283-
return It->second;
285+
return RecursiveIt->second;
286+
284287
bool Result = Function();
288+
289+
if (!Result) {
290+
for (auto &It : RecursiveFn)
291+
It.second = false;
292+
}
293+
RecursiveIt = RecursiveFn.find(S);
294+
assert(RecursiveIt != RecursiveFn.end());
295+
Result = RecursiveIt->second;
296+
RecursiveFn.erase(RecursiveIt);
285297
Cache[S] = Result;
298+
286299
return Result;
287300
}
288301

@@ -292,16 +305,7 @@ class TrivialFunctionAnalysisVisitor
292305
TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
293306

294307
bool IsFunctionTrivial(const Decl *D) {
295-
auto CacheIt = Cache.find(D);
296-
if (CacheIt != Cache.end())
297-
return CacheIt->second;
298-
299-
// Treat a recursive function call to be trivial until proven otherwise.
300-
auto [RecursiveIt, IsNew] = RecursiveFn.insert(std::make_pair(D, true));
301-
if (!IsNew)
302-
return RecursiveIt->second;
303-
304-
bool Result = [&]() {
308+
return WithCachedResult(D, [&]() {
305309
if (auto *CtorDecl = dyn_cast<CXXConstructorDecl>(D)) {
306310
for (auto *CtorInit : CtorDecl->inits()) {
307311
if (!Visit(CtorInit->getInit()))
@@ -312,20 +316,7 @@ class TrivialFunctionAnalysisVisitor
312316
if (!Body)
313317
return false;
314318
return Visit(Body);
315-
}();
316-
317-
if (!Result) {
318-
// D and its mutually recursive callers are all non-trivial.
319-
for (auto &It : RecursiveFn)
320-
It.second = false;
321-
}
322-
RecursiveIt = RecursiveFn.find(D);
323-
assert(RecursiveIt != RecursiveFn.end());
324-
Result = RecursiveIt->second;
325-
RecursiveFn.erase(RecursiveIt);
326-
Cache[D] = Result;
327-
328-
return Result;
319+
});
329320
}
330321

331322
bool VisitStmt(const Stmt *S) {
@@ -586,11 +577,6 @@ bool TrivialFunctionAnalysis::isTrivialImpl(
586577

587578
bool TrivialFunctionAnalysis::isTrivialImpl(
588579
const Stmt *S, TrivialFunctionAnalysis::CacheTy &Cache) {
589-
// If the statement isn't in the cache, conservatively assume that
590-
// it's not trivial until analysis completes. Unlike a function case,
591-
// we don't insert an entry into the cache until Visit returns
592-
// since Visit* functions themselves make use of the cache.
593-
594580
TrivialFunctionAnalysisVisitor V(Cache);
595581
bool Result = V.Visit(S);
596582
assert(Cache.contains(S) && "Top-level statement not properly cached!");

clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,3 +289,42 @@ void foo() {
289289
}
290290

291291
} // namespace local_assignment_to_global
292+
293+
namespace local_var_in_recursive_function {
294+
295+
struct TreeNode {
296+
Ref<TreeNode> create() { return Ref(*new TreeNode); }
297+
298+
void ref() const { ++refCount; }
299+
void deref() const {
300+
if (!--refCount)
301+
delete this;
302+
}
303+
304+
int recursiveCost();
305+
int recursiveWeight();
306+
int weight();
307+
308+
int cost { 0 };
309+
mutable unsigned refCount { 0 };
310+
TreeNode* nextSibling { nullptr };
311+
TreeNode* firstChild { nullptr };
312+
};
313+
314+
int TreeNode::recursiveCost() {
315+
// no warnings
316+
unsigned totalCost = cost;
317+
for (TreeNode* node = firstChild; node; node = node->nextSibling)
318+
totalCost += recursiveCost();
319+
return totalCost;
320+
}
321+
322+
int TreeNode::recursiveWeight() {
323+
unsigned totalCost = weight();
324+
for (TreeNode* node = firstChild; node; node = node->nextSibling)
325+
// expected-warning@-1{{Local variable 'node' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
326+
totalCost += recursiveWeight();
327+
return totalCost;
328+
}
329+
330+
} // namespace local_var_in_recursive_function

clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,15 @@ class RefCounted {
259259
void mutuallyRecursive8() { mutuallyRecursive9(); someFunction(); }
260260
void mutuallyRecursive9() { mutuallyRecursive8(); }
261261

262+
int recursiveCost() {
263+
unsigned totalCost = 0;
264+
for (unsigned i = 0; i < sizeof(children)/sizeof(*children); ++i) {
265+
if (auto* child = children[i])
266+
totalCost += child->recursiveCost();
267+
}
268+
return totalCost;
269+
}
270+
262271
int trivial1() { return 123; }
263272
float trivial2() { return 0.3; }
264273
float trivial3() { return (float)0.4; }
@@ -448,6 +457,7 @@ class RefCounted {
448457
Number* number { nullptr };
449458
ComplexNumber complex;
450459
Enum enumValue { Enum::Value1 };
460+
RefCounted* children[4];
451461
};
452462

453463
unsigned RefCounted::s_v = 0;
@@ -558,6 +568,8 @@ class UnrelatedClass {
558568
getFieldTrivial().mutuallyRecursive9();
559569
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
560570

571+
getFieldTrivial().recursiveCost(); // no-warning
572+
561573
getFieldTrivial().someFunction();
562574
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
563575
getFieldTrivial().nonTrivial1();

0 commit comments

Comments
 (0)