21#include "llvm/Support/SaveAndRestore.h"
29class RawPtrRefCallArgsChecker
30 :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
33 TrivialFunctionAnalysis TFA;
34 EnsureFunctionAnalysis EFA;
37 mutable BugReporter *BR;
38 mutable std::optional<RetainTypeChecker> RTC;
41 RawPtrRefCallArgsChecker(
const char *description)
42 : Bug(this, description,
"WebKit coding guidelines") {}
44 virtual std::optional<bool> isUnsafeType(QualType)
const = 0;
45 virtual std::optional<bool> isUnsafePtr(QualType)
const = 0;
47 virtual bool isSafePtrType(
const QualType
type)
const = 0;
48 virtual bool isSafeExpr(
const Expr *)
const {
return false; }
49 virtual bool isSafeDecl(
const Decl *)
const {
return false; }
50 virtual const char *ptrKind()
const = 0;
52 void checkASTDecl(
const TranslationUnitDecl *TUD, AnalysisManager &MGR,
53 BugReporter &BRArg)
const {
60 const RawPtrRefCallArgsChecker *Checker;
61 Decl *DeclWithIssue{
nullptr};
63 explicit LocalVisitor(
const RawPtrRefCallArgsChecker *Checker)
66 ShouldVisitTemplateInstantiations =
true;
67 ShouldVisitImplicitCode =
false;
70 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl)
override {
73 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);
76 bool TraverseDecl(Decl *D)
override {
77 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
83 bool VisitCallExpr(CallExpr *CE)
override {
84 Checker->visitCallExpr(CE, DeclWithIssue);
88 bool VisitCXXConstructExpr(CXXConstructExpr *CE)
override {
89 Checker->visitConstructExpr(CE, DeclWithIssue);
93 bool VisitTypedefDecl(TypedefDecl *TD)
override {
95 Checker->RTC->visitTypedef(TD);
99 bool VisitObjCMessageExpr(ObjCMessageExpr *ObjCMsgExpr)
override {
100 Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
105 LocalVisitor visitor(
this);
107 RTC->visitTranslationUnitDecl(TUD);
108 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
111 template <
typename CallOrConstrcut>
112 void visitCallOrConstructExpr(
const CallOrConstrcut *CE,
113 const FunctionDecl *F,
const Decl *D)
const {
120 if (
auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE))
121 checkThisArg(MemberCallExpr, D);
124 auto *Arg = CE->getArg(0);
125 QualType ArgType = Arg->getType().getCanonicalType();
126 std::optional<bool> IsUnsafe = isUnsafeType(ArgType);
127 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Arg))
128 reportBugOnThis(Arg, D);
132 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
136 checkArg(CE->getArg(ArgIdx), (*P)->getType(), *P, D);
138 for (; ArgIdx < CE->getNumArgs(); ++ArgIdx) {
139 auto *Arg = CE->getArg(ArgIdx);
140 checkArg(Arg, Arg->getType(),
nullptr, D);
145 void visitCallExpr(
const CallExpr *CE,
const Decl *D)
const {
147 if (shouldSkipCall(CE, Callee))
151 visitCallOrConstructExpr(CE, Callee, D);
153 if (
auto *FnType =
Decl->getFunctionType()) {
154 if (
auto *ProtoType = dyn_cast<FunctionProtoType>(FnType)) {
155 if (
auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE))
156 checkThisArg(MemberCallExpr, D);
158 for (
auto PT = ProtoType->param_type_begin();
159 PT < ProtoType->param_type_end() && ArgIdx < CE->getNumArgs();
161 checkArg(CE->
getArg(ArgIdx), *PT,
nullptr, D);
163 auto *Arg = CE->
getArg(ArgIdx);
164 checkArg(Arg, Arg->getType(),
nullptr, D);
171 void visitConstructExpr(
const CXXConstructExpr *CE,
const Decl *D)
const {
179 void visitObjCMessageExpr(
const ObjCMessageExpr *E,
const Decl *D)
const {
180 if (BR->getSourceManager().isInSystemHeader(E->
getExprLoc()))
185 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) {
189 if (SelectorName ==
"isEqual" || SelectorName ==
"isEqualToString")
191 reportBugOnReceiver(Receiver, D);
200 for (
unsigned i = 0; i < ArgCount; ++i) {
202 bool hasParam = i < MethodDecl->param_size();
203 auto *Param = hasParam ? MethodDecl->getParamDecl(i) :
nullptr;
204 auto ArgType = Arg->getType();
205 std::optional<bool> IsUnsafe = isUnsafePtr(ArgType);
206 if (!IsUnsafe || !(*IsUnsafe))
208 if (isPtrOriginSafe(Arg))
210 reportBug(Arg, Param, D);
214 void checkThisArg(
const CXXMemberCallExpr *MemberCallExpr,
215 const Decl *DeclWithIssue)
const {
218 if (name ==
"ref" || name ==
"deref")
220 if (name ==
"incrementCheckedPtrCount" ||
221 name ==
"decrementCheckedPtrCount")
226 std::optional<bool> IsUnsafe = isUnsafeType(ArgType);
227 if (!IsUnsafe || !*IsUnsafe)
230 if (isPtrOriginSafe(ThisExpr))
233 reportBugOnThis(MemberCallExpr, DeclWithIssue);
236 void checkArg(
const Expr *Arg, QualType ParamType,
const ParmVarDecl *Param,
237 const Decl *DeclWithIssue)
const {
238 std::optional<bool> IsUncounted = isUnsafePtr(ParamType);
239 if (!IsUncounted || !(*IsUncounted))
242 if (
auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
243 Arg = DefaultArg->getExpr();
245 if (isPtrOriginSafe(Arg))
248 reportBug(Arg, Param, DeclWithIssue);
251 bool isPtrOriginSafe(
const Expr *Arg)
const {
255 [&](
const clang::QualType T) {
return isSafePtrType(T); },
256 [&](
const clang::Decl *D) {
return isSafeDecl(D); },
257 [&](
const clang::Expr *ArgOrigin,
bool IsSafe) {
273 if (EFA.isACallToEnsureFn(ArgOrigin)) {
274 auto *MCE = dyn_cast<CXXMemberCallExpr>(ArgOrigin);
276 if (isPtrOriginSafe(MCE->getImplicitObjectArgument()))
279 if (isSafeExpr(ArgOrigin))
285 template <
typename CallOrConstruct>
286 bool shouldSkipCall(
const CallOrConstruct *CE,
287 const FunctionDecl *Callee)
const {
288 if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
291 if (Callee && TFA.isTrivial(Callee))
297 if (CE->getNumArgs() == 0)
302 if (
auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
304 if (MemberOp->getOperator() ==
306 auto *callee = MemberOp->getDirectCallee();
307 if (
auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
308 if (
const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
314 if (MemberOp->isAssignmentOp())
321 if (isMethodOnWTFContainerType(Callee))
324 auto overloadedOperatorType =
Callee->getOverloadedOperator();
325 if (overloadedOperatorType == OO_EqualEqual ||
326 overloadedOperatorType == OO_ExclaimEqual ||
327 overloadedOperatorType == OO_LessEqual ||
328 overloadedOperatorType == OO_GreaterEqual ||
329 overloadedOperatorType == OO_Spaceship ||
330 overloadedOperatorType == OO_AmpAmp ||
331 overloadedOperatorType == OO_PipePipe)
338 if (name ==
"adoptRef" || name ==
"getPtr" || name ==
"WeakPtr" ||
339 name ==
"is" || name ==
"equal" || name ==
"hash" || name ==
"isType" ||
341 name ==
"CFEqual" || name ==
"equalIgnoringASCIICase" ||
342 name ==
"equalIgnoringASCIICaseCommon" ||
343 name ==
"equalIgnoringNullity" || name ==
"toString")
349 bool isMethodOnWTFContainerType(
const FunctionDecl *Decl)
const {
352 auto *ClassDecl =
Decl->getParent();
356 auto *NsDecl = ClassDecl->getParent();
362 StringRef ClsName = ClsNameStr;
365 return NamespaceName ==
"WTF" &&
366 (MethodName ==
"find" || MethodName ==
"findIf" ||
367 MethodName ==
"reverseFind" || MethodName ==
"reverseFindIf" ||
368 MethodName ==
"findIgnoringASCIICase" || MethodName ==
"get" ||
369 MethodName ==
"inlineGet" || MethodName ==
"contains" ||
370 MethodName ==
"containsIf" ||
371 MethodName ==
"containsIgnoringASCIICase" ||
372 MethodName ==
"startsWith" || MethodName ==
"endsWith" ||
373 MethodName ==
"startsWithIgnoringASCIICase" ||
374 MethodName ==
"endsWithIgnoringASCIICase" ||
375 MethodName ==
"substring") &&
376 (ClsName.ends_with(
"Vector") || ClsName.ends_with(
"Set") ||
377 ClsName.ends_with(
"Map") || ClsName ==
"StringImpl" ||
378 ClsName.ends_with(
"String"));
381 void reportBug(
const Expr *CallArg,
const ParmVarDecl *Param,
382 const Decl *DeclWithIssue)
const {
385 SmallString<100> Buf;
386 llvm::raw_svector_ostream Os(Buf);
389 Os <<
"Call argument";
390 if (!paramName.empty()) {
391 Os <<
" for parameter ";
394 Os <<
" is " << ptrKind() <<
" and unsafe.";
397 const SourceLocation SrcLocToReport =
401 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
402 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
404 Report->setDeclWithIssue(DeclWithIssue);
405 BR->emitReport(std::move(
Report));
408 void reportBugOnThis(
const Expr *CallArg,
const Decl *DeclWithIssue)
const {
413 SmallString<100> Buf;
414 llvm::raw_svector_ostream Os(Buf);
415 Os <<
"Call argument for 'this' parameter is " << ptrKind();
416 Os <<
" and unsafe.";
418 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
419 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
421 Report->setDeclWithIssue(DeclWithIssue);
422 BR->emitReport(std::move(
Report));
425 void reportBugOnReceiver(
const Expr *CallArg,
426 const Decl *DeclWithIssue)
const {
431 SmallString<100> Buf;
432 llvm::raw_svector_ostream Os(Buf);
433 Os <<
"Receiver is " << ptrKind() <<
" and unsafe.";
435 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
436 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
438 Report->setDeclWithIssue(DeclWithIssue);
439 BR->emitReport(std::move(
Report));
443class UncountedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
445 UncountedCallArgsChecker()
446 : RawPtrRefCallArgsChecker(
"Uncounted call argument for a raw "
447 "pointer/reference parameter") {}
449 std::optional<bool> isUnsafeType(QualType QT)
const final {
453 std::optional<bool> isUnsafePtr(QualType QT)
const final {
461 bool isSafePtrType(
const QualType
type)
const final {
465 const char *ptrKind() const final {
return "uncounted"; }
468class UncheckedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
470 UncheckedCallArgsChecker()
471 : RawPtrRefCallArgsChecker(
"Unchecked call argument for a raw "
472 "pointer/reference parameter") {}
474 std::optional<bool> isUnsafeType(QualType QT)
const final {
478 std::optional<bool> isUnsafePtr(QualType QT)
const final {
486 bool isSafePtrType(
const QualType
type)
const final {
490 bool isSafeExpr(
const Expr *E)
const final {
494 const char *ptrKind() const final {
return "unchecked"; }
497class UnretainedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
499 UnretainedCallArgsChecker()
500 : RawPtrRefCallArgsChecker(
"Unretained call argument for a raw "
501 "pointer/reference parameter") {
502 RTC = RetainTypeChecker();
505 std::optional<bool> isUnsafeType(QualType QT)
const final {
506 return RTC->isUnretained(QT);
509 std::optional<bool> isUnsafePtr(QualType QT)
const final {
510 return RTC->isUnretained(QT);
517 bool isSafePtrType(
const QualType
type)
const final {
521 bool isSafeDecl(
const Decl *D)
const final {
526 const char *ptrKind() const final {
return "unretained"; }
535bool ento::shouldRegisterUncountedCallArgsChecker(
const CheckerManager &) {
543bool ento::shouldRegisterUncheckedCallArgsChecker(
const CheckerManager &) {
547void ento::registerUnretainedCallArgsChecker(
CheckerManager &Mgr) {
551bool ento::shouldRegisterUnretainedCallArgsChecker(
const CheckerManager &) {
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
llvm::MachO::Record Record
Defines the clang::SourceLocation class and associated facilities.
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
CXXMethodDecl * getMethodDecl() const
Retrieve the declaration of the called method.
Expr * getImplicitObjectArgument() const
Retrieve the implicit object argument for the member call.
QualType getObjectType() const
Retrieve the type of the object argument.
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.
SourceLocation getLocation() const
virtual bool TraverseDecl(MaybeConst< Decl > *D)
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
param_iterator param_begin()
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Expr * getInstanceReceiver()
Returns the object expression (receiver) for an instance message, or null for a message that is not a...
Selector getSelector() const
const ObjCMethodDecl * getMethodDecl() const
QualType getReceiverType() const
Retrieve the receiver type to which this message is being directed.
unsigned getNumArgs() const
Return the number of actual arguments in this message, not counting the receiver.
QualType getCanonicalType() const
StringRef getNameForSlot(unsigned argIndex) const
Retrieve the name at a given position in the selector.
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...
const SourceManager & getSourceManager()
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
const internal::VariadicAllOfMatcher< Type > type
Matches Types in the clang AST.
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
The JSON file list parser is used to communicate input to InstallAPI.
bool isCtorOfSafePtr(const clang::FunctionDecl *F)
bool isTrivialBuiltinFunction(const FunctionDecl *F)
bool isa(CodeGen::Address addr)
bool isExprToGetCheckedPtrCapableMember(const clang::Expr *E)
bool isPtrConversion(const FunctionDecl *F)
std::optional< bool > isUnchecked(const QualType T)
bool isRefOrCheckedPtrType(const clang::QualType T)
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
bool isRetainPtrOrOSPtrType(const clang::QualType T)
bool tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj, std::function< bool(const clang::CXXRecordDecl *)> isSafePtr, std::function< bool(const clang::QualType)> isSafePtrType, std::function< bool(const clang::Decl *)> isSafeGlobalDecl, std::function< bool(const clang::Expr *, bool)> callback)
This function de-facto defines a set of transformations that we consider safe (in heuristical sense).
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 isSmartPtrClass(const std::string &Name)
bool isRefCounted(const CXXRecordDecl *R)
bool isRetainPtrOrOSPtr(const std::string &Name)
std::optional< bool > isUncountedPtr(const QualType T)
bool isSafePtr(clang::CXXRecordDecl *Decl)
std::string safeGetName(const T *ASTNode)
bool isNullPtr(const clang::Expr *E)
bool isCheckedPtr(const std::string &Name)
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
bool isAllocInit(const Expr *E, const Expr **InnerExpr)
std::optional< bool > isUncounted(const QualType T)
std::optional< bool > isUncheckedPtr(const QualType T)