21#include "llvm/ADT/DenseSet.h"
22#include "llvm/Support/SaveAndRestore.h"
30class RawPtrRefCallArgsChecker
31 :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
39 RawPtrRefCallArgsChecker(
const char *description)
40 : Bug(this, description,
"WebKit coding guidelines") {}
42 virtual std::optional<bool> isUnsafeType(
QualType)
const = 0;
44 virtual const char *ptrKind()
const = 0;
54 const RawPtrRefCallArgsChecker *
Checker;
55 Decl *DeclWithIssue{
nullptr};
57 explicit LocalVisitor(
const RawPtrRefCallArgsChecker *
Checker)
60 ShouldVisitTemplateInstantiations =
true;
61 ShouldVisitImplicitCode =
false;
67 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(
Decl);
70 bool TraverseDecl(
Decl *
D)
override {
72 if (
D && (isa<FunctionDecl>(
D) || isa<ObjCMethodDecl>(
D)))
77 bool VisitCallExpr(
CallExpr *CE)
override {
78 Checker->visitCallExpr(CE, DeclWithIssue);
83 LocalVisitor visitor(
this);
88 if (shouldSkipCall(CE))
95 isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
97 if (
auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {
98 if (
auto *MD = MemberCallExpr->getMethodDecl()) {
100 if (name ==
"ref" || name ==
"deref")
102 if (name ==
"incrementCheckedPtrCount" ||
103 name ==
"decrementCheckedPtrCount")
106 auto *
E = MemberCallExpr->getImplicitObjectArgument();
107 QualType ArgType = MemberCallExpr->getObjectType().getCanonicalType();
108 std::optional<bool> IsUnsafe = isUnsafeType(ArgType);
109 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(
E))
110 reportBugOnThis(
E,
D);
113 for (
auto P = F->param_begin();
118 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++
P, ++ArgIdx) {
123 QualType ArgType = (*P)->getType().getCanonicalType();
125 std::optional<bool> IsUncounted =
isUnsafePtr(ArgType);
126 if (!IsUncounted || !(*IsUncounted))
129 const auto *Arg = CE->
getArg(ArgIdx);
131 if (
auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
132 Arg = defaultArg->getExpr();
134 if (isPtrOriginSafe(Arg))
137 reportBug(Arg, *
P,
D);
142 bool isPtrOriginSafe(
const Expr *Arg)
const {
147 if (isa<CXXNullPtrLiteralExpr>(ArgOrigin)) {
151 if (isa<IntegerLiteral>(ArgOrigin)) {
164 bool shouldSkipCall(
const CallExpr *CE)
const {
178 if (
auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
180 if (MemberOp->getOperator() ==
182 auto *callee = MemberOp->getDirectCallee();
183 if (
auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
190 if (MemberOp->isAssignmentOp())
197 if (isMethodOnWTFContainerType(Callee))
200 auto overloadedOperatorType =
Callee->getOverloadedOperator();
201 if (overloadedOperatorType == OO_EqualEqual ||
202 overloadedOperatorType == OO_ExclaimEqual ||
203 overloadedOperatorType == OO_LessEqual ||
204 overloadedOperatorType == OO_GreaterEqual ||
205 overloadedOperatorType == OO_Spaceship ||
206 overloadedOperatorType == OO_AmpAmp ||
207 overloadedOperatorType == OO_PipePipe)
214 if (name ==
"adoptRef" || name ==
"getPtr" || name ==
"WeakPtr" ||
215 name ==
"dynamicDowncast" || name ==
"downcast" ||
216 name ==
"checkedDowncast" || name ==
"uncheckedDowncast" ||
217 name ==
"bitwise_cast" || name ==
"is" || name ==
"equal" ||
218 name ==
"hash" || name ==
"isType" ||
220 name ==
"equalIgnoringASCIICase" ||
221 name ==
"equalIgnoringASCIICaseCommon" ||
222 name ==
"equalIgnoringNullity" || name ==
"toString")
229 if (!isa<CXXMethodDecl>(
Decl))
231 auto *ClassDecl =
Decl->getParent();
232 if (!ClassDecl || !isa<CXXRecordDecl>(ClassDecl))
235 auto *NsDecl = ClassDecl->getParent();
236 if (!NsDecl || !isa<NamespaceDecl>(NsDecl))
241 StringRef ClsName = ClsNameStr;
244 return NamespaceName ==
"WTF" &&
245 (MethodName ==
"find" || MethodName ==
"findIf" ||
246 MethodName ==
"reverseFind" || MethodName ==
"reverseFindIf" ||
247 MethodName ==
"findIgnoringASCIICase" || MethodName ==
"get" ||
248 MethodName ==
"inlineGet" || MethodName ==
"contains" ||
249 MethodName ==
"containsIf" ||
250 MethodName ==
"containsIgnoringASCIICase" ||
251 MethodName ==
"startsWith" || MethodName ==
"endsWith" ||
252 MethodName ==
"startsWithIgnoringASCIICase" ||
253 MethodName ==
"endsWithIgnoringASCIICase" ||
254 MethodName ==
"substring") &&
255 (ClsName.ends_with(
"Vector") || ClsName.ends_with(
"Set") ||
256 ClsName.ends_with(
"Map") || ClsName ==
"StringImpl" ||
257 ClsName.ends_with(
"String"));
261 const Decl *DeclWithIssue)
const {
265 llvm::raw_svector_ostream Os(Buf);
268 Os <<
"Call argument";
269 if (!paramName.empty()) {
270 Os <<
" for parameter ";
273 Os <<
" is " << ptrKind() <<
" and unsafe.";
280 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
282 Report->setDeclWithIssue(DeclWithIssue);
286 void reportBugOnThis(
const Expr *CallArg,
const Decl *DeclWithIssue)
const {
292 llvm::raw_svector_ostream Os(Buf);
293 Os <<
"Call argument for 'this' parameter is " << ptrKind();
294 Os <<
" and unsafe.";
297 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
299 Report->setDeclWithIssue(DeclWithIssue);
304class UncountedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
306 UncountedCallArgsChecker()
307 : RawPtrRefCallArgsChecker(
"Uncounted call argument for a raw "
308 "pointer/reference parameter") {}
310 std::optional<bool> isUnsafeType(
QualType QT)
const final {
318 const char *ptrKind() const final {
return "uncounted"; }
321class UncheckedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
323 UncheckedCallArgsChecker()
324 : RawPtrRefCallArgsChecker(
"Unchecked call argument for a raw "
325 "pointer/reference parameter") {}
327 std::optional<bool> isUnsafeType(
QualType QT)
const final {
335 const char *ptrKind() const final {
return "unchecked"; }
344bool ento::shouldRegisterUncountedCallArgsChecker(
const CheckerManager &) {
352bool ento::shouldRegisterUncheckedCallArgsChecker(
const CheckerManager &) {
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::SourceLocation class and associated facilities.
Represents a C++ struct/union/class.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Declaration of a class template.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Decl - This represents one declaration (or definition), e.g.
Recursive AST visitor that supports extension via dynamic dispatch.
virtual bool TraverseDecl(Decl *D)
Recursively visit a declaration, by dispatching to Traverse*Decl() based on the argument's dynamic ty...
bool isACallToEnsureFn(const Expr *E) const
This represents one expression.
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Represents a function declaration or definition.
Represents a parameter to a function.
A (possibly-)qualified type.
Encodes a location in the source.
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
SourceLocation getBegin() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
The top declaration context.
An inter-procedural analysis facility that detects functions with "trivial" behavior with respect to ...
bool isTrivial(const Decl *D) const
BugReporter is a utility class for generating PathDiagnostics for analysis.
const SourceManager & getSourceManager()
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
The JSON file list parser is used to communicate input to InstallAPI.
std::optional< bool > isUnchecked(const QualType T)
bool isCtorOfRefCounted(const clang::FunctionDecl *F)
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
bool isASafeCallArg(const Expr *E)
For E referring to a ref-countable/-counted pointer/reference we return whether it's a safe call argu...
bool isRefCounted(const CXXRecordDecl *R)
bool isRefType(const std::string &Name)
std::optional< bool > isUncountedPtr(const QualType T)
bool tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj, std::function< bool(const clang::Expr *, bool)> callback)
This function de-facto defines a set of transformations that we consider safe (in heuristical sense).
std::string safeGetName(const T *ASTNode)
std::optional< bool > isUnsafePtr(const QualType T)
std::optional< bool > isUncounted(const QualType T)
std::optional< bool > isUncheckedPtr(const QualType T)