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)) {
188 reportBugOnReceiver(Receiver, D);
197 for (
unsigned i = 0; i < ArgCount; ++i) {
199 bool hasParam = i < MethodDecl->param_size();
200 auto *Param = hasParam ? MethodDecl->getParamDecl(i) :
nullptr;
201 auto ArgType = Arg->getType();
202 std::optional<bool> IsUnsafe = isUnsafePtr(ArgType);
203 if (!IsUnsafe || !(*IsUnsafe))
205 if (isPtrOriginSafe(Arg))
207 reportBug(Arg, Param, D);
211 void checkThisArg(
const CXXMemberCallExpr *MemberCallExpr,
212 const Decl *DeclWithIssue)
const {
215 if (name ==
"ref" || name ==
"deref")
217 if (name ==
"incrementCheckedPtrCount" ||
218 name ==
"decrementCheckedPtrCount")
223 std::optional<bool> IsUnsafe = isUnsafeType(ArgType);
224 if (!IsUnsafe || !*IsUnsafe)
227 if (isPtrOriginSafe(ThisExpr))
230 reportBugOnThis(MemberCallExpr, DeclWithIssue);
233 void checkArg(
const Expr *Arg, QualType ParamType,
const ParmVarDecl *Param,
234 const Decl *DeclWithIssue)
const {
235 std::optional<bool> IsUncounted = isUnsafePtr(ParamType);
236 if (!IsUncounted || !(*IsUncounted))
239 if (
auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
240 Arg = DefaultArg->getExpr();
242 if (isPtrOriginSafe(Arg))
245 reportBug(Arg, Param, DeclWithIssue);
248 bool isPtrOriginSafe(
const Expr *Arg)
const {
252 [&](
const clang::QualType T) {
return isSafePtrType(T); },
253 [&](
const clang::Decl *D) {
return isSafeDecl(D); },
254 [&](
const clang::Expr *ArgOrigin,
bool IsSafe) {
270 if (EFA.isACallToEnsureFn(ArgOrigin))
272 if (isSafeExpr(ArgOrigin))
278 template <
typename CallOrConstruct>
279 bool shouldSkipCall(
const CallOrConstruct *CE,
280 const FunctionDecl *Callee)
const {
281 if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
284 if (Callee && TFA.isTrivial(Callee))
290 if (CE->getNumArgs() == 0)
295 if (
auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
297 if (MemberOp->getOperator() ==
299 auto *callee = MemberOp->getDirectCallee();
300 if (
auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
301 if (
const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
307 if (MemberOp->isAssignmentOp())
314 if (isMethodOnWTFContainerType(Callee))
317 auto overloadedOperatorType =
Callee->getOverloadedOperator();
318 if (overloadedOperatorType == OO_EqualEqual ||
319 overloadedOperatorType == OO_ExclaimEqual ||
320 overloadedOperatorType == OO_LessEqual ||
321 overloadedOperatorType == OO_GreaterEqual ||
322 overloadedOperatorType == OO_Spaceship ||
323 overloadedOperatorType == OO_AmpAmp ||
324 overloadedOperatorType == OO_PipePipe)
331 if (name ==
"adoptRef" || name ==
"getPtr" || name ==
"WeakPtr" ||
332 name ==
"is" || name ==
"equal" || name ==
"hash" || name ==
"isType" ||
334 name ==
"CFEqual" || name ==
"equalIgnoringASCIICase" ||
335 name ==
"equalIgnoringASCIICaseCommon" ||
336 name ==
"equalIgnoringNullity" || name ==
"toString")
342 bool isMethodOnWTFContainerType(
const FunctionDecl *Decl)
const {
345 auto *ClassDecl =
Decl->getParent();
349 auto *NsDecl = ClassDecl->getParent();
355 StringRef ClsName = ClsNameStr;
358 return NamespaceName ==
"WTF" &&
359 (MethodName ==
"find" || MethodName ==
"findIf" ||
360 MethodName ==
"reverseFind" || MethodName ==
"reverseFindIf" ||
361 MethodName ==
"findIgnoringASCIICase" || MethodName ==
"get" ||
362 MethodName ==
"inlineGet" || MethodName ==
"contains" ||
363 MethodName ==
"containsIf" ||
364 MethodName ==
"containsIgnoringASCIICase" ||
365 MethodName ==
"startsWith" || MethodName ==
"endsWith" ||
366 MethodName ==
"startsWithIgnoringASCIICase" ||
367 MethodName ==
"endsWithIgnoringASCIICase" ||
368 MethodName ==
"substring") &&
369 (ClsName.ends_with(
"Vector") || ClsName.ends_with(
"Set") ||
370 ClsName.ends_with(
"Map") || ClsName ==
"StringImpl" ||
371 ClsName.ends_with(
"String"));
374 void reportBug(
const Expr *CallArg,
const ParmVarDecl *Param,
375 const Decl *DeclWithIssue)
const {
378 SmallString<100> Buf;
379 llvm::raw_svector_ostream Os(Buf);
382 Os <<
"Call argument";
383 if (!paramName.empty()) {
384 Os <<
" for parameter ";
387 Os <<
" is " << ptrKind() <<
" and unsafe.";
390 const SourceLocation SrcLocToReport =
394 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
395 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
397 Report->setDeclWithIssue(DeclWithIssue);
398 BR->emitReport(std::move(
Report));
401 void reportBugOnThis(
const Expr *CallArg,
const Decl *DeclWithIssue)
const {
406 SmallString<100> Buf;
407 llvm::raw_svector_ostream Os(Buf);
408 Os <<
"Call argument for 'this' parameter is " << ptrKind();
409 Os <<
" and unsafe.";
411 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
412 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
414 Report->setDeclWithIssue(DeclWithIssue);
415 BR->emitReport(std::move(
Report));
418 void reportBugOnReceiver(
const Expr *CallArg,
419 const Decl *DeclWithIssue)
const {
424 SmallString<100> Buf;
425 llvm::raw_svector_ostream Os(Buf);
426 Os <<
"Receiver is " << ptrKind() <<
" and unsafe.";
428 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
429 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
431 Report->setDeclWithIssue(DeclWithIssue);
432 BR->emitReport(std::move(
Report));
436class UncountedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
438 UncountedCallArgsChecker()
439 : RawPtrRefCallArgsChecker(
"Uncounted call argument for a raw "
440 "pointer/reference parameter") {}
442 std::optional<bool> isUnsafeType(QualType QT)
const final {
446 std::optional<bool> isUnsafePtr(QualType QT)
const final {
454 bool isSafePtrType(
const QualType
type)
const final {
458 const char *ptrKind() const final {
return "uncounted"; }
461class UncheckedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
463 UncheckedCallArgsChecker()
464 : RawPtrRefCallArgsChecker(
"Unchecked call argument for a raw "
465 "pointer/reference parameter") {}
467 std::optional<bool> isUnsafeType(QualType QT)
const final {
471 std::optional<bool> isUnsafePtr(QualType QT)
const final {
479 bool isSafePtrType(
const QualType
type)
const final {
483 bool isSafeExpr(
const Expr *E)
const final {
487 const char *ptrKind() const final {
return "unchecked"; }
490class UnretainedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
492 UnretainedCallArgsChecker()
493 : RawPtrRefCallArgsChecker(
"Unretained call argument for a raw "
494 "pointer/reference parameter") {
495 RTC = RetainTypeChecker();
498 std::optional<bool> isUnsafeType(QualType QT)
const final {
499 return RTC->isUnretained(QT);
502 std::optional<bool> isUnsafePtr(QualType QT)
const final {
503 return RTC->isUnretained(QT);
510 bool isSafePtrType(
const QualType
type)
const final {
514 bool isSafeExpr(
const Expr *E)
const final {
519 bool isSafeDecl(
const Decl *D)
const final {
524 const char *ptrKind() const final {
return "unretained"; }
533bool ento::shouldRegisterUncountedCallArgsChecker(
const CheckerManager &) {
541bool ento::shouldRegisterUncheckedCallArgsChecker(
const CheckerManager &) {
545void ento::registerUnretainedCallArgsChecker(
CheckerManager &Mgr) {
549bool 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...
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
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.
bool isCocoaObjectRef(QualType T)
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)