Skip to content

Commit 0937081

Browse files
authored
Merge pull request swiftlang#8198 from haoNoQ/static-analyzer-cherrypicks-2024-04
🍒 Static analyzer cherrypicks
2 parents 24bd3dd + 2418636 commit 0937081

File tree

9 files changed

+531
-20
lines changed

9 files changed

+531
-20
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,13 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
6666
E = call->getArg(0);
6767
continue;
6868
}
69+
6970
if (isReturnValueRefCounted(callee))
7071
return {E, true};
7172

73+
if (isSingleton(callee))
74+
return {E, true};
75+
7276
if (isPtrConversion(callee)) {
7377
E = call->getArg(0);
7478
continue;

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

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "clang/AST/Decl.h"
1313
#include "clang/AST/DeclCXX.h"
1414
#include "clang/AST/ExprCXX.h"
15+
#include "clang/AST/StmtVisitor.h"
1516
#include <optional>
1617

1718
using namespace clang;
@@ -222,4 +223,216 @@ bool isPtrConversion(const FunctionDecl *F) {
222223
return false;
223224
}
224225

226+
bool isSingleton(const FunctionDecl *F) {
227+
assert(F);
228+
// FIXME: check # of params == 1
229+
if (auto *MethodDecl = dyn_cast<CXXMethodDecl>(F)) {
230+
if (!MethodDecl->isStatic())
231+
return false;
232+
}
233+
const auto &Name = safeGetName(F);
234+
std::string SingletonStr = "singleton";
235+
auto index = Name.find(SingletonStr);
236+
return index != std::string::npos &&
237+
index == Name.size() - SingletonStr.size();
238+
}
239+
240+
// We only care about statements so let's use the simple
241+
// (non-recursive) visitor.
242+
class TrivialFunctionAnalysisVisitor
243+
: public ConstStmtVisitor<TrivialFunctionAnalysisVisitor, bool> {
244+
245+
// Returns false if at least one child is non-trivial.
246+
bool VisitChildren(const Stmt *S) {
247+
for (const Stmt *Child : S->children()) {
248+
if (Child && !Visit(Child))
249+
return false;
250+
}
251+
252+
return true;
253+
}
254+
255+
public:
256+
using CacheTy = TrivialFunctionAnalysis::CacheTy;
257+
258+
TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
259+
260+
bool VisitStmt(const Stmt *S) {
261+
// All statements are non-trivial unless overriden later.
262+
// Don't even recurse into children by default.
263+
return false;
264+
}
265+
266+
bool VisitCompoundStmt(const CompoundStmt *CS) {
267+
// A compound statement is allowed as long each individual sub-statement
268+
// is trivial.
269+
return VisitChildren(CS);
270+
}
271+
272+
bool VisitReturnStmt(const ReturnStmt *RS) {
273+
// A return statement is allowed as long as the return value is trivial.
274+
if (auto *RV = RS->getRetValue())
275+
return Visit(RV);
276+
return true;
277+
}
278+
279+
bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
280+
bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
281+
bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
282+
bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
283+
bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
284+
bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); }
285+
286+
bool VisitUnaryOperator(const UnaryOperator *UO) {
287+
// Operator '*' and '!' are allowed as long as the operand is trivial.
288+
if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot)
289+
return Visit(UO->getSubExpr());
290+
291+
// Other operators are non-trivial.
292+
return false;
293+
}
294+
295+
bool VisitBinaryOperator(const BinaryOperator *BO) {
296+
// Binary operators are trivial if their operands are trivial.
297+
return Visit(BO->getLHS()) && Visit(BO->getRHS());
298+
}
299+
300+
bool VisitConditionalOperator(const ConditionalOperator *CO) {
301+
// Ternary operators are trivial if their conditions & values are trivial.
302+
return VisitChildren(CO);
303+
}
304+
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+
}
312+
313+
bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) {
314+
// Any static_assert is considered trivial.
315+
return true;
316+
}
317+
318+
bool VisitCallExpr(const CallExpr *CE) {
319+
if (!checkArguments(CE))
320+
return false;
321+
322+
auto *Callee = CE->getDirectCallee();
323+
if (!Callee)
324+
return false;
325+
const auto &Name = safeGetName(Callee);
326+
327+
if (Name == "WTFCrashWithInfo" || Name == "WTFBreakpointTrap" ||
328+
Name == "compilerFenceForCrash" || Name == "__builtin_unreachable")
329+
return true;
330+
331+
return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache);
332+
}
333+
334+
bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
335+
if (!checkArguments(MCE))
336+
return false;
337+
338+
bool TrivialThis = Visit(MCE->getImplicitObjectArgument());
339+
if (!TrivialThis)
340+
return false;
341+
342+
auto *Callee = MCE->getMethodDecl();
343+
if (!Callee)
344+
return false;
345+
346+
std::optional<bool> IsGetterOfRefCounted = isGetterOfRefCounted(Callee);
347+
if (IsGetterOfRefCounted && *IsGetterOfRefCounted)
348+
return true;
349+
350+
// Recursively descend into the callee to confirm that it's trivial as well.
351+
return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache);
352+
}
353+
354+
bool checkArguments(const CallExpr *CE) {
355+
for (const Expr *Arg : CE->arguments()) {
356+
if (Arg && !Visit(Arg))
357+
return false;
358+
}
359+
return true;
360+
}
361+
362+
bool VisitCXXConstructExpr(const CXXConstructExpr *CE) {
363+
for (const Expr *Arg : CE->arguments()) {
364+
if (Arg && !Visit(Arg))
365+
return false;
366+
}
367+
368+
// Recursively descend into the callee to confirm that it's trivial.
369+
return TrivialFunctionAnalysis::isTrivialImpl(CE->getConstructor(), Cache);
370+
}
371+
372+
bool VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
373+
return Visit(ICE->getSubExpr());
374+
}
375+
376+
bool VisitExplicitCastExpr(const ExplicitCastExpr *ECE) {
377+
return Visit(ECE->getSubExpr());
378+
}
379+
380+
bool VisitParenExpr(const ParenExpr *PE) { return Visit(PE->getSubExpr()); }
381+
382+
bool VisitInitListExpr(const InitListExpr *ILE) {
383+
for (const Expr *Child : ILE->inits()) {
384+
if (Child && !Visit(Child))
385+
return false;
386+
}
387+
return true;
388+
}
389+
390+
bool VisitMemberExpr(const MemberExpr *ME) {
391+
// Field access is allowed but the base pointer may itself be non-trivial.
392+
return Visit(ME->getBase());
393+
}
394+
395+
bool VisitCXXThisExpr(const CXXThisExpr *CTE) {
396+
// The expression 'this' is always trivial, be it explicit or implicit.
397+
return true;
398+
}
399+
400+
// Constant literal expressions are always trivial
401+
bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; }
402+
bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; }
403+
bool VisitFixedPointLiteral(const FixedPointLiteral *E) { return true; }
404+
bool VisitCharacterLiteral(const CharacterLiteral *E) { return true; }
405+
bool VisitStringLiteral(const StringLiteral *E) { return true; }
406+
407+
bool VisitConstantExpr(const ConstantExpr *CE) {
408+
// Constant expressions are trivial.
409+
return true;
410+
}
411+
412+
private:
413+
CacheTy Cache;
414+
};
415+
416+
bool TrivialFunctionAnalysis::isTrivialImpl(
417+
const Decl *D, TrivialFunctionAnalysis::CacheTy &Cache) {
418+
// If the function isn't in the cache, conservatively assume that
419+
// it's not trivial until analysis completes. This makes every recursive
420+
// function non-trivial. This also guarantees that each function
421+
// will be scanned at most once.
422+
auto [It, IsNew] = Cache.insert(std::make_pair(D, false));
423+
if (!IsNew)
424+
return It->second;
425+
426+
const Stmt *Body = D->getBody();
427+
if (!Body)
428+
return false;
429+
430+
TrivialFunctionAnalysisVisitor V(Cache);
431+
bool Result = V.Visit(Body);
432+
if (Result)
433+
Cache[D] = true;
434+
435+
return Result;
436+
}
437+
225438
} // namespace clang

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
#define LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H
1111

1212
#include "llvm/ADT/APInt.h"
13+
#include "llvm/ADT/DenseMap.h"
1314
#include <optional>
1415

1516
namespace clang {
1617
class CXXBaseSpecifier;
1718
class CXXMethodDecl;
1819
class CXXRecordDecl;
20+
class Decl;
1921
class FunctionDecl;
2022
class Type;
2123

@@ -60,6 +62,25 @@ std::optional<bool> isGetterOfRefCounted(const clang::CXXMethodDecl* Method);
6062
/// pointer types.
6163
bool isPtrConversion(const FunctionDecl *F);
6264

65+
/// \returns true if \p F is a static singleton function.
66+
bool isSingleton(const FunctionDecl *F);
67+
68+
/// An inter-procedural analysis facility that detects functions with "trivial"
69+
/// behavior with respect to reference counting, such as simple field getters.
70+
class TrivialFunctionAnalysis {
71+
public:
72+
/// \returns true if \p D is a "trivial" function.
73+
bool isTrivial(const Decl *D) const { return isTrivialImpl(D, TheCache); }
74+
75+
private:
76+
friend class TrivialFunctionAnalysisVisitor;
77+
78+
using CacheTy = llvm::DenseMap<const Decl *, bool>;
79+
mutable CacheTy TheCache{};
80+
81+
static bool isTrivialImpl(const Decl *D, CacheTy &Cache);
82+
};
83+
6384
} // namespace clang
6485

6586
#endif

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class UncountedCallArgsChecker
3333
"WebKit coding guidelines"};
3434
mutable BugReporter *BR;
3535

36+
TrivialFunctionAnalysis TFA;
37+
3638
public:
3739

3840
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
@@ -135,6 +137,11 @@ class UncountedCallArgsChecker
135137
}
136138

137139
bool shouldSkipCall(const CallExpr *CE) const {
140+
const auto *Callee = CE->getDirectCallee();
141+
142+
if (Callee && TFA.isTrivial(Callee))
143+
return true;
144+
138145
if (CE->getNumArgs() == 0)
139146
return false;
140147

@@ -156,7 +163,6 @@ class UncountedCallArgsChecker
156163
return false;
157164
}
158165

159-
const auto *Callee = CE->getDirectCallee();
160166
if (!Callee)
161167
return false;
162168

0 commit comments

Comments
 (0)