Skip to content

Commit 0e19fc4

Browse files
committed
[alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (llvm#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 0f896a2 commit 0e19fc4

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
@@ -27,28 +27,6 @@ using namespace ento;
2727

2828
namespace {
2929

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

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

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

223-
if (isDeclaredInForOrIf(V))
224-
return true;
225-
226234
return false;
227235
}
228236

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)