Skip to content

Commit 4560980

Browse files
authored
Merge pull request #8349 from rniwa/merge-webkit-checker-improvements
Merge WebKit checker improvements
2 parents f9fc88d + 0e19fc4 commit 4560980

11 files changed

+636
-85
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
2323
E = tempExpr->getSubExpr();
2424
continue;
2525
}
26+
if (auto *tempExpr = dyn_cast<CXXBindTemporaryExpr>(E)) {
27+
E = tempExpr->getSubExpr();
28+
continue;
29+
}
2630
if (auto *cast = dyn_cast<CastExpr>(E)) {
2731
if (StopAtFirstRefCountedObj) {
2832
if (auto *ConversionFunc =

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

Lines changed: 98 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,18 @@ std::optional<bool> isRefCountable(const CXXRecordDecl* R)
103103
return hasRef && hasDeref;
104104
}
105105

106+
bool isRefType(const std::string &Name) {
107+
return Name == "Ref" || Name == "RefAllowingPartiallyDestroyed" ||
108+
Name == "RefPtr" || Name == "RefPtrAllowingPartiallyDestroyed";
109+
}
110+
106111
bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
107112
assert(F);
108-
const auto &FunctionName = safeGetName(F);
109-
110-
return FunctionName == "Ref" || FunctionName == "makeRef"
113+
const std::string &FunctionName = safeGetName(F);
111114

112-
|| FunctionName == "RefPtr" || FunctionName == "makeRefPtr"
113-
114-
|| FunctionName == "UniqueRef" || FunctionName == "makeUniqueRef" ||
115+
return isRefType(FunctionName) || FunctionName == "makeRef" ||
116+
FunctionName == "makeRefPtr" || FunctionName == "UniqueRef" ||
117+
FunctionName == "makeUniqueRef" ||
115118
FunctionName == "makeUniqueRefWithoutFastMallocCheck"
116119

117120
|| FunctionName == "String" || FunctionName == "AtomString" ||
@@ -131,7 +134,7 @@ bool isReturnValueRefCounted(const clang::FunctionDecl *F) {
131134
if (auto *specialT = type->getAs<TemplateSpecializationType>()) {
132135
if (auto *decl = specialT->getTemplateName().getAsTemplateDecl()) {
133136
auto name = decl->getNameAsString();
134-
return name == "Ref" || name == "RefPtr";
137+
return isRefType(name);
135138
}
136139
return false;
137140
}
@@ -172,20 +175,18 @@ std::optional<bool> isGetterOfRefCounted(const CXXMethodDecl* M)
172175
if (isa<CXXMethodDecl>(M)) {
173176
const CXXRecordDecl *calleeMethodsClass = M->getParent();
174177
auto className = safeGetName(calleeMethodsClass);
175-
auto methodName = safeGetName(M);
178+
auto method = safeGetName(M);
176179

177-
if (((className == "Ref" || className == "RefPtr") &&
178-
methodName == "get") ||
179-
(className == "Ref" && methodName == "ptr") ||
180+
if ((isRefType(className) && (method == "get" || method == "ptr")) ||
180181
((className == "String" || className == "AtomString" ||
181182
className == "AtomStringImpl" || className == "UniqueString" ||
182183
className == "UniqueStringImpl" || className == "Identifier") &&
183-
methodName == "impl"))
184+
method == "impl"))
184185
return true;
185186

186187
// Ref<T> -> T conversion
187188
// FIXME: Currently allowing any Ref<T> -> whatever cast.
188-
if (className == "Ref" || className == "RefPtr") {
189+
if (isRefType(className)) {
189190
if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M)) {
190191
if (auto *targetConversionType =
191192
maybeRefToRawOperator->getConversionType().getTypePtrOrNull()) {
@@ -202,7 +203,7 @@ bool isRefCounted(const CXXRecordDecl *R) {
202203
if (auto *TmplR = R->getTemplateInstantiationPattern()) {
203204
// FIXME: String/AtomString/UniqueString
204205
const auto &ClassName = safeGetName(TmplR);
205-
return ClassName == "RefPtr" || ClassName == "Ref";
206+
return isRefType(ClassName);
206207
}
207208
return false;
208209
}
@@ -252,6 +253,19 @@ class TrivialFunctionAnalysisVisitor
252253
return true;
253254
}
254255

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+
255269
public:
256270
using CacheTy = TrivialFunctionAnalysis::CacheTy;
257271

@@ -266,7 +280,7 @@ class TrivialFunctionAnalysisVisitor
266280
bool VisitCompoundStmt(const CompoundStmt *CS) {
267281
// A compound statement is allowed as long each individual sub-statement
268282
// is trivial.
269-
return VisitChildren(CS);
283+
return WithCachedResult(CS, [&]() { return VisitChildren(CS); });
270284
}
271285

272286
bool VisitReturnStmt(const ReturnStmt *RS) {
@@ -278,16 +292,36 @@ class TrivialFunctionAnalysisVisitor
278292

279293
bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
280294
bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
281-
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+
}
282307
bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
283308
bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
284309
bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); }
285310

286311
bool VisitUnaryOperator(const UnaryOperator *UO) {
287312
// Operator '*' and '!' are allowed as long as the operand is trivial.
288-
if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot)
313+
auto op = UO->getOpcode();
314+
if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot)
289315
return Visit(UO->getSubExpr());
290316

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+
}
291325
// Other operators are non-trivial.
292326
return false;
293327
}
@@ -302,13 +336,7 @@ class TrivialFunctionAnalysisVisitor
302336
return VisitChildren(CO);
303337
}
304338

305-
bool VisitDeclRefExpr(const DeclRefExpr *DRE) {
306-
if (auto *decl = DRE->getDecl()) {
307-
if (isa<ParmVarDecl>(decl))
308-
return true;
309-
}
310-
return false;
311-
}
339+
bool VisitAtomicExpr(const AtomicExpr *E) { return VisitChildren(E); }
312340

313341
bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) {
314342
// Any static_assert is considered trivial.
@@ -325,12 +353,18 @@ class TrivialFunctionAnalysisVisitor
325353
const auto &Name = safeGetName(Callee);
326354

327355
if (Name == "WTFCrashWithInfo" || Name == "WTFBreakpointTrap" ||
356+
Name == "WTFReportAssertionFailure" ||
328357
Name == "compilerFenceForCrash" || Name == "__builtin_unreachable")
329358
return true;
330359

331360
return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache);
332361
}
333362

363+
bool VisitPredefinedExpr(const PredefinedExpr *E) {
364+
// A predefined identifier such as "func" is considered trivial.
365+
return true;
366+
}
367+
334368
bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
335369
if (!checkArguments(MCE))
336370
return false;
@@ -351,6 +385,14 @@ class TrivialFunctionAnalysisVisitor
351385
return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache);
352386
}
353387

388+
bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) {
389+
if (auto *Expr = E->getExpr()) {
390+
if (!Visit(Expr))
391+
return false;
392+
}
393+
return true;
394+
}
395+
354396
bool checkArguments(const CallExpr *CE) {
355397
for (const Expr *Arg : CE->arguments()) {
356398
if (Arg && !Visit(Arg))
@@ -377,6 +419,14 @@ class TrivialFunctionAnalysisVisitor
377419
return Visit(ECE->getSubExpr());
378420
}
379421

422+
bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *VMT) {
423+
return Visit(VMT->getSubExpr());
424+
}
425+
426+
bool VisitExprWithCleanups(const ExprWithCleanups *EWC) {
427+
return Visit(EWC->getSubExpr());
428+
}
429+
380430
bool VisitParenExpr(const ParenExpr *PE) { return Visit(PE->getSubExpr()); }
381431

382432
bool VisitInitListExpr(const InitListExpr *ILE) {
@@ -397,6 +447,16 @@ class TrivialFunctionAnalysisVisitor
397447
return true;
398448
}
399449

450+
bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E) {
451+
// nullptr is trivial.
452+
return true;
453+
}
454+
455+
bool VisitDeclRefExpr(const DeclRefExpr *DRE) {
456+
// The use of a variable is trivial.
457+
return true;
458+
}
459+
400460
// Constant literal expressions are always trivial
401461
bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; }
402462
bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; }
@@ -410,7 +470,7 @@ class TrivialFunctionAnalysisVisitor
410470
}
411471

412472
private:
413-
CacheTy Cache;
473+
CacheTy &Cache;
414474
};
415475

416476
bool TrivialFunctionAnalysis::isTrivialImpl(
@@ -435,4 +495,17 @@ bool TrivialFunctionAnalysis::isTrivialImpl(
435495
return Result;
436496
}
437497

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+
438511
} // 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/UncountedCallArgsChecker.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ class UncountedCallArgsChecker
7474
unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
7575

7676
if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {
77+
if (auto *MD = MemberCallExpr->getMethodDecl()) {
78+
auto name = safeGetName(MD);
79+
if (name == "ref" || name == "deref")
80+
return;
81+
}
7782
auto *E = MemberCallExpr->getImplicitObjectArgument();
7883
QualType ArgType = MemberCallExpr->getObjectType();
7984
std::optional<bool> IsUncounted =
@@ -166,6 +171,9 @@ class UncountedCallArgsChecker
166171
if (!Callee)
167172
return false;
168173

174+
if (isMethodOnWTFContainerType(Callee))
175+
return true;
176+
169177
auto overloadedOperatorType = Callee->getOverloadedOperator();
170178
if (overloadedOperatorType == OO_EqualEqual ||
171179
overloadedOperatorType == OO_ExclaimEqual ||
@@ -194,6 +202,31 @@ class UncountedCallArgsChecker
194202
return false;
195203
}
196204

205+
bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const {
206+
if (!isa<CXXMethodDecl>(Decl))
207+
return false;
208+
auto *ClassDecl = Decl->getParent();
209+
if (!ClassDecl || !isa<CXXRecordDecl>(ClassDecl))
210+
return false;
211+
212+
auto *NsDecl = ClassDecl->getParent();
213+
if (!NsDecl || !isa<NamespaceDecl>(NsDecl))
214+
return false;
215+
216+
auto MethodName = safeGetName(Decl);
217+
auto ClsNameStr = safeGetName(ClassDecl);
218+
StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef.
219+
auto NamespaceName = safeGetName(NsDecl);
220+
// FIXME: These should be implemented via attributes.
221+
return NamespaceName == "WTF" &&
222+
(MethodName == "find" || MethodName == "findIf" ||
223+
MethodName == "reverseFind" || MethodName == "reverseFindIf" ||
224+
MethodName == "get" || MethodName == "inlineGet" ||
225+
MethodName == "contains" || MethodName == "containsIf") &&
226+
(ClsName.ends_with("Vector") || ClsName.ends_with("Set") ||
227+
ClsName.ends_with("Map"));
228+
}
229+
197230
void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const {
198231
assert(CallArg);
199232

0 commit comments

Comments
 (0)