Skip to content

Commit 7ce1cfe

Browse files
authored
[alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (#82229)
This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and pointers to a ref counted type which appears within "trival" statements. To do this, this PR extends TrivialFunctionAnalysis so that it can also analyze "triviality" of statements as well as that of functions Each Visit* function is now augmented with withCachedResult, which is responsible for looking up and updating the cache for each Visit* functions. As this PR dramatically improves the false positive rate of the checker, it also deletes the code to ignore raw pointers and references within if and for statements.
1 parent c59129a commit 7ce1cfe

File tree

5 files changed

+203
-65
lines changed

5 files changed

+203
-65
lines changed

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

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,19 @@ class TrivialFunctionAnalysisVisitor
253253
return true;
254254
}
255255

256+
template <typename CheckFunction>
257+
bool WithCachedResult(const Stmt *S, CheckFunction Function) {
258+
// If the statement isn't in the cache, conservatively assume that
259+
// it's not trivial until analysis completes. Insert false to the cache
260+
// first to avoid infinite recursion.
261+
auto [It, IsNew] = Cache.insert(std::make_pair(S, false));
262+
if (!IsNew)
263+
return It->second;
264+
bool Result = Function();
265+
Cache[S] = Result;
266+
return Result;
267+
}
268+
256269
public:
257270
using CacheTy = TrivialFunctionAnalysis::CacheTy;
258271

@@ -267,7 +280,7 @@ class TrivialFunctionAnalysisVisitor
267280
bool VisitCompoundStmt(const CompoundStmt *CS) {
268281
// A compound statement is allowed as long each individual sub-statement
269282
// is trivial.
270-
return VisitChildren(CS);
283+
return WithCachedResult(CS, [&]() { return VisitChildren(CS); });
271284
}
272285

273286
bool VisitReturnStmt(const ReturnStmt *RS) {
@@ -279,17 +292,36 @@ class TrivialFunctionAnalysisVisitor
279292

280293
bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
281294
bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
282-
bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
295+
bool VisitIfStmt(const IfStmt *IS) {
296+
return WithCachedResult(IS, [&]() { return VisitChildren(IS); });
297+
}
298+
bool VisitForStmt(const ForStmt *FS) {
299+
return WithCachedResult(FS, [&]() { return VisitChildren(FS); });
300+
}
301+
bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) {
302+
return WithCachedResult(FS, [&]() { return VisitChildren(FS); });
303+
}
304+
bool VisitWhileStmt(const WhileStmt *WS) {
305+
return WithCachedResult(WS, [&]() { return VisitChildren(WS); });
306+
}
283307
bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
284308
bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
285309
bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); }
286310

287311
bool VisitUnaryOperator(const UnaryOperator *UO) {
288312
// Operator '*' and '!' are allowed as long as the operand is trivial.
289-
if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
290-
UO->getOpcode() == UO_LNot)
313+
auto op = UO->getOpcode();
314+
if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot)
291315
return Visit(UO->getSubExpr());
292316

317+
if (UO->isIncrementOp() || UO->isDecrementOp()) {
318+
// Allow increment or decrement of a POD type.
319+
if (auto *RefExpr = dyn_cast<DeclRefExpr>(UO->getSubExpr())) {
320+
if (auto *Decl = dyn_cast<VarDecl>(RefExpr->getDecl()))
321+
return Decl->isLocalVarDeclOrParm() &&
322+
Decl->getType().isPODType(Decl->getASTContext());
323+
}
324+
}
293325
// Other operators are non-trivial.
294326
return false;
295327
}
@@ -304,22 +336,6 @@ class TrivialFunctionAnalysisVisitor
304336
return VisitChildren(CO);
305337
}
306338

307-
bool VisitDeclRefExpr(const DeclRefExpr *DRE) {
308-
if (auto *decl = DRE->getDecl()) {
309-
if (isa<ParmVarDecl>(decl))
310-
return true;
311-
if (isa<EnumConstantDecl>(decl))
312-
return true;
313-
if (auto *VD = dyn_cast<VarDecl>(decl)) {
314-
if (VD->hasConstantInitialization() && VD->getEvaluatedValue())
315-
return true;
316-
auto *Init = VD->getInit();
317-
return !Init || Visit(Init);
318-
}
319-
}
320-
return false;
321-
}
322-
323339
bool VisitAtomicExpr(const AtomicExpr *E) { return VisitChildren(E); }
324340

325341
bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) {
@@ -436,6 +452,11 @@ class TrivialFunctionAnalysisVisitor
436452
return true;
437453
}
438454

455+
bool VisitDeclRefExpr(const DeclRefExpr *DRE) {
456+
// The use of a variable is trivial.
457+
return true;
458+
}
459+
439460
// Constant literal expressions are always trivial
440461
bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; }
441462
bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; }
@@ -449,7 +470,7 @@ class TrivialFunctionAnalysisVisitor
449470
}
450471

451472
private:
452-
CacheTy Cache;
473+
CacheTy &Cache;
453474
};
454475

455476
bool TrivialFunctionAnalysis::isTrivialImpl(
@@ -474,4 +495,17 @@ bool TrivialFunctionAnalysis::isTrivialImpl(
474495
return Result;
475496
}
476497

498+
bool TrivialFunctionAnalysis::isTrivialImpl(
499+
const Stmt *S, TrivialFunctionAnalysis::CacheTy &Cache) {
500+
// If the statement isn't in the cache, conservatively assume that
501+
// it's not trivial until analysis completes. Unlike a function case,
502+
// we don't insert an entry into the cache until Visit returns
503+
// since Visit* functions themselves make use of the cache.
504+
505+
TrivialFunctionAnalysisVisitor V(Cache);
506+
bool Result = V.Visit(S);
507+
assert(Cache.contains(S) && "Top-level statement not properly cached!");
508+
return Result;
509+
}
510+
477511
} // namespace clang

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "llvm/ADT/APInt.h"
1313
#include "llvm/ADT/DenseMap.h"
14+
#include "llvm/ADT/PointerUnion.h"
1415
#include <optional>
1516

1617
namespace clang {
@@ -19,6 +20,7 @@ class CXXMethodDecl;
1920
class CXXRecordDecl;
2021
class Decl;
2122
class FunctionDecl;
23+
class Stmt;
2224
class Type;
2325

2426
// Ref-countability of a type is implicitly defined by Ref<T> and RefPtr<T>
@@ -71,14 +73,17 @@ class TrivialFunctionAnalysis {
7173
public:
7274
/// \returns true if \p D is a "trivial" function.
7375
bool isTrivial(const Decl *D) const { return isTrivialImpl(D, TheCache); }
76+
bool isTrivial(const Stmt *S) const { return isTrivialImpl(S, TheCache); }
7477

7578
private:
7679
friend class TrivialFunctionAnalysisVisitor;
7780

78-
using CacheTy = llvm::DenseMap<const Decl *, bool>;
81+
using CacheTy =
82+
llvm::DenseMap<llvm::PointerUnion<const Decl *, const Stmt *>, bool>;
7983
mutable CacheTy TheCache{};
8084

8185
static bool isTrivialImpl(const Decl *D, CacheTy &Cache);
86+
static bool isTrivialImpl(const Stmt *S, CacheTy &Cache);
8287
};
8388

8489
} // namespace clang

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

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -26,28 +26,6 @@ using namespace ento;
2626

2727
namespace {
2828

29-
// for ( int a = ...) ... true
30-
// for ( int a : ...) ... true
31-
// if ( int* a = ) ... true
32-
// anything else ... false
33-
bool isDeclaredInForOrIf(const VarDecl *Var) {
34-
assert(Var);
35-
auto &ASTCtx = Var->getASTContext();
36-
auto parent = ASTCtx.getParents(*Var);
37-
38-
if (parent.size() == 1) {
39-
if (auto *DS = parent.begin()->get<DeclStmt>()) {
40-
DynTypedNodeList grandParent = ASTCtx.getParents(*DS);
41-
if (grandParent.size() == 1) {
42-
return grandParent.begin()->get<ForStmt>() ||
43-
grandParent.begin()->get<IfStmt>() ||
44-
grandParent.begin()->get<CXXForRangeStmt>();
45-
}
46-
}
47-
}
48-
return false;
49-
}
50-
5129
// FIXME: should be defined by anotations in the future
5230
bool isRefcountedStringsHack(const VarDecl *V) {
5331
assert(V);
@@ -143,6 +121,11 @@ class UncountedLocalVarsChecker
143121
// want to visit those, so we make our own RecursiveASTVisitor.
144122
struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
145123
const UncountedLocalVarsChecker *Checker;
124+
125+
TrivialFunctionAnalysis TFA;
126+
127+
using Base = RecursiveASTVisitor<LocalVisitor>;
128+
146129
explicit LocalVisitor(const UncountedLocalVarsChecker *Checker)
147130
: Checker(Checker) {
148131
assert(Checker);
@@ -155,6 +138,36 @@ class UncountedLocalVarsChecker
155138
Checker->visitVarDecl(V);
156139
return true;
157140
}
141+
142+
bool TraverseIfStmt(IfStmt *IS) {
143+
if (!TFA.isTrivial(IS))
144+
return Base::TraverseIfStmt(IS);
145+
return true;
146+
}
147+
148+
bool TraverseForStmt(ForStmt *FS) {
149+
if (!TFA.isTrivial(FS))
150+
return Base::TraverseForStmt(FS);
151+
return true;
152+
}
153+
154+
bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) {
155+
if (!TFA.isTrivial(FRS))
156+
return Base::TraverseCXXForRangeStmt(FRS);
157+
return true;
158+
}
159+
160+
bool TraverseWhileStmt(WhileStmt *WS) {
161+
if (!TFA.isTrivial(WS))
162+
return Base::TraverseWhileStmt(WS);
163+
return true;
164+
}
165+
166+
bool TraverseCompoundStmt(CompoundStmt *CS) {
167+
if (!TFA.isTrivial(CS))
168+
return Base::TraverseCompoundStmt(CS);
169+
return true;
170+
}
158171
};
159172

160173
LocalVisitor visitor(this);
@@ -189,18 +202,16 @@ class UncountedLocalVarsChecker
189202
dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
190203
const auto *MaybeGuardianArgType =
191204
MaybeGuardian->getType().getTypePtr();
192-
if (!MaybeGuardianArgType)
193-
return;
194-
const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
195-
MaybeGuardianArgType->getAsCXXRecordDecl();
196-
if (!MaybeGuardianArgCXXRecord)
197-
return;
198-
199-
if (MaybeGuardian->isLocalVarDecl() &&
200-
(isRefCounted(MaybeGuardianArgCXXRecord) ||
201-
isRefcountedStringsHack(MaybeGuardian)) &&
202-
isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) {
203-
return;
205+
if (MaybeGuardianArgType) {
206+
const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
207+
MaybeGuardianArgType->getAsCXXRecordDecl();
208+
if (MaybeGuardianArgCXXRecord) {
209+
if (MaybeGuardian->isLocalVarDecl() &&
210+
(isRefCounted(MaybeGuardianArgCXXRecord) ||
211+
isRefcountedStringsHack(MaybeGuardian)) &&
212+
isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian))
213+
return;
214+
}
204215
}
205216

206217
// Parameters are guaranteed to be safe for the duration of the call
@@ -219,9 +230,6 @@ class UncountedLocalVarsChecker
219230
if (!V->isLocalVarDecl())
220231
return true;
221232

222-
if (isDeclaredInForOrIf(V))
223-
return true;
224-
225233
return false;
226234
}
227235

clang/test/Analysis/Checkers/WebKit/mock-types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ struct RefCountable {
6262
static Ref<RefCountable> create();
6363
void ref() {}
6464
void deref() {}
65+
void method();
66+
int trivial() { return 123; }
6567
};
6668

6769
template <typename T> T *downcast(T *t) { return t; }

0 commit comments

Comments
 (0)