22#include "llvm/Support/SaveAndRestore.h"
30class RawPtrRefCallArgsChecker
31 :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
34 TrivialFunctionAnalysis TFA;
35 EnsureFunctionAnalysis EFA;
38 mutable BugReporter *BR;
39 mutable std::optional<RetainTypeChecker> RTC;
42 RawPtrRefCallArgsChecker(
const char *description)
43 : Bug(this, description,
"WebKit coding guidelines") {}
45 virtual std::optional<bool> isUnsafeType(QualType)
const = 0;
46 virtual std::optional<bool> isUnsafePtr(QualType)
const = 0;
48 virtual bool isSafePtrType(
const QualType
type)
const = 0;
49 virtual bool isSafeExpr(
const Expr *)
const {
return false; }
50 virtual bool isSafeDecl(
const Decl *)
const {
return false; }
51 virtual const char *typeName()
const = 0;
53 void checkASTDecl(
const TranslationUnitDecl *TUD, AnalysisManager &MGR,
54 BugReporter &BRArg)
const {
61 const RawPtrRefCallArgsChecker *Checker;
62 Decl *DeclWithIssue{
nullptr};
64 explicit LocalVisitor(
const RawPtrRefCallArgsChecker *Checker)
67 ShouldVisitTemplateInstantiations =
true;
68 ShouldVisitImplicitCode =
false;
71 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl)
override {
74 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);
77 bool TraverseDecl(Decl *D)
override {
78 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
84 bool VisitCallExpr(CallExpr *CE)
override {
85 Checker->visitCallExpr(CE, DeclWithIssue);
89 bool VisitCXXConstructExpr(CXXConstructExpr *CE)
override {
90 Checker->visitConstructExpr(CE, DeclWithIssue);
94 bool VisitTypedefDecl(TypedefDecl *TD)
override {
96 Checker->RTC->visitTypedef(TD);
100 bool VisitObjCMessageExpr(ObjCMessageExpr *ObjCMsgExpr)
override {
101 Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
106 LocalVisitor visitor(
this);
108 RTC->visitTranslationUnitDecl(TUD);
109 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
112 template <
typename CallOrConstrcut>
113 void visitCallOrConstructExpr(
const CallOrConstrcut *CE,
114 const FunctionDecl *F,
const Decl *D)
const {
121 if (
auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE))
122 checkThisArg(F, MemberCallExpr, D);
125 auto *Arg = CE->getArg(0);
126 QualType ArgType = Arg->getType().getCanonicalType();
127 std::optional<bool> IsUnsafe = isUnsafeType(ArgType);
128 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Arg))
129 reportBugOnThis(F, Arg, D);
133 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
137 checkArg(F, CE->getArg(ArgIdx), (*P)->getType(), *P, D);
139 for (; ArgIdx < CE->getNumArgs(); ++ArgIdx) {
140 auto *Arg = CE->getArg(ArgIdx);
141 checkArg(F, Arg, Arg->getType(),
nullptr, D);
146 void visitCallExpr(
const CallExpr *CE,
const Decl *D)
const {
148 if (shouldSkipCall(CE, Callee))
152 visitCallOrConstructExpr(CE, Callee, D);
154 if (
auto *FnType =
Decl->getFunctionType()) {
155 if (
auto *ProtoType = dyn_cast<FunctionProtoType>(FnType)) {
156 if (
auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE))
157 checkThisArg(
nullptr, MemberCallExpr, D);
159 for (
auto PT = ProtoType->param_type_begin();
160 PT < ProtoType->param_type_end() && ArgIdx < CE->getNumArgs();
162 checkArg(
nullptr, CE->
getArg(ArgIdx), *PT,
nullptr, D);
164 auto *Arg = CE->
getArg(ArgIdx);
165 checkArg(
nullptr, Arg, Arg->getType(),
nullptr, D);
172 void visitConstructExpr(
const CXXConstructExpr *CE,
const Decl *D)
const {
180 void visitObjCMessageExpr(
const ObjCMessageExpr *E,
const Decl *D)
const {
181 if (BR->getSourceManager().isInSystemHeader(E->
getExprLoc()))
186 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) {
190 if (SelectorName ==
"isEqual" || SelectorName ==
"isEqualToString")
201 for (
unsigned i = 0; i < ArgCount; ++i) {
203 bool hasParam = i < MethodDecl->param_size();
204 auto *Param = hasParam ? MethodDecl->getParamDecl(i) :
nullptr;
205 auto ArgType = Arg->getType();
206 std::optional<bool> IsUnsafe = isUnsafePtr(ArgType);
207 if (!IsUnsafe || !(*IsUnsafe))
209 if (isPtrOriginSafe(Arg))
211 reportBug(MethodDecl, Arg, Param, D);
215 void checkThisArg(
const NamedDecl *Callee,
216 const CXXMemberCallExpr *MemberCallExpr,
217 const Decl *DeclWithIssue)
const {
220 if (name ==
"ref" || name ==
"deref")
222 if (name ==
"incrementCheckedPtrCount" ||
223 name ==
"decrementCheckedPtrCount")
228 std::optional<bool> IsUnsafe = isUnsafeType(ArgType);
229 if (!IsUnsafe || !*IsUnsafe)
232 if (isPtrOriginSafe(ThisExpr))
235 reportBugOnThis(Callee, ThisExpr, DeclWithIssue);
238 void checkArg(
const NamedDecl *Callee,
const Expr *Arg, QualType ParamType,
239 const ParmVarDecl *Param,
const Decl *DeclWithIssue)
const {
240 std::optional<bool> IsUncounted = isUnsafePtr(ParamType);
241 if (!IsUncounted || !(*IsUncounted))
244 if (
auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
245 Arg = DefaultArg->getExpr();
247 if (isPtrOriginSafe(Arg))
250 reportBug(Callee, Arg, Param, DeclWithIssue);
253 bool isPtrOriginSafe(
const Expr *Arg)
const {
257 [&](
const clang::QualType T) {
return isSafePtrType(T); },
258 [&](
const clang::Decl *D) {
return isSafeDecl(D); },
259 [&](
const clang::Expr *ArgOrigin,
bool IsSafe) {
275 if (EFA.isACallToEnsureFn(ArgOrigin)) {
276 auto *MCE = dyn_cast<CXXMemberCallExpr>(ArgOrigin);
278 if (isPtrOriginSafe(MCE->getImplicitObjectArgument()))
281 if (isSafeExpr(ArgOrigin))
287 template <
typename CallOrConstruct>
288 bool shouldSkipCall(
const CallOrConstruct *CE,
289 const FunctionDecl *Callee)
const {
290 if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
293 if (Callee && TFA.isTrivial(Callee))
299 if (CE->getNumArgs() == 0)
304 if (
auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
306 if (MemberOp->getOperator() ==
308 auto *callee = MemberOp->getDirectCallee();
309 if (
auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
310 if (
const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
316 if (MemberOp->isAssignmentOp())
323 if (isMethodOnWTFContainerType(Callee))
326 auto overloadedOperatorType =
Callee->getOverloadedOperator();
327 if (overloadedOperatorType == OO_EqualEqual ||
328 overloadedOperatorType == OO_ExclaimEqual ||
329 overloadedOperatorType == OO_LessEqual ||
330 overloadedOperatorType == OO_GreaterEqual ||
331 overloadedOperatorType == OO_Spaceship ||
332 overloadedOperatorType == OO_AmpAmp ||
333 overloadedOperatorType == OO_PipePipe)
340 if (name ==
"adoptRef" || name ==
"getPtr" || name ==
"WeakPtr" ||
341 name ==
"is" || name ==
"equal" || name ==
"hash" || name ==
"isType" ||
343 name ==
"CFEqual" || name ==
"equalIgnoringASCIICase" ||
344 name ==
"equalIgnoringASCIICaseCommon" ||
345 name ==
"equalIgnoringNullity" || name ==
"toString")
351 bool isMethodOnWTFContainerType(
const FunctionDecl *Decl)
const {
354 auto *ClassDecl =
Decl->getParent();
358 auto *NsDecl = ClassDecl->getParent();
364 StringRef ClsName = ClsNameStr;
367 return NamespaceName ==
"WTF" &&
368 (MethodName ==
"find" || MethodName ==
"findIf" ||
369 MethodName ==
"reverseFind" || MethodName ==
"reverseFindIf" ||
370 MethodName ==
"findIgnoringASCIICase" || MethodName ==
"get" ||
371 MethodName ==
"inlineGet" || MethodName ==
"contains" ||
372 MethodName ==
"containsIf" ||
373 MethodName ==
"containsIgnoringASCIICase" ||
374 MethodName ==
"startsWith" || MethodName ==
"endsWith" ||
375 MethodName ==
"startsWithIgnoringASCIICase" ||
376 MethodName ==
"endsWithIgnoringASCIICase" ||
377 MethodName ==
"substring") &&
378 (ClsName.ends_with(
"Vector") || ClsName.ends_with(
"Set") ||
379 ClsName.ends_with(
"Map") || ClsName ==
"StringImpl" ||
380 ClsName.ends_with(
"String"));
383 void reportBug(
const NamedDecl *Callee,
const Expr *CallArg,
384 const ParmVarDecl *Param,
const Decl *DeclWithIssue)
const {
387 SmallString<100> Buf;
388 llvm::raw_svector_ostream Os(Buf);
391 Os <<
"Function argument";
393 if (!paramName.empty() || Callee)
395 if (!paramName.empty()) {
400 if (!paramName.empty())
405 if (!paramName.empty() || Callee)
410 if (printPointer(Os, ArgType) == PrintDeclKind::Pointer) {
412 if (
auto *Decl = RTC->getCanonicalDecl(CallArg->
getType())) {
425 const SourceLocation SrcLocToReport =
429 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
430 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
432 Report->setDeclWithIssue(DeclWithIssue);
433 BR->emitReport(std::move(
Report));
436 void reportBugOnThis(
const NamedDecl *Callee,
const Expr *CallArg,
437 const Decl *DeclWithIssue)
const {
442 SmallString<100> Buf;
443 llvm::raw_svector_ostream Os(Buf);
444 Os <<
"Function argument";
446 Os <<
" (parameter 'this'";
451 Os <<
") is a raw pointer to " << typeName() <<
" ";
454 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
455 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
457 Report->setDeclWithIssue(DeclWithIssue);
458 BR->emitReport(std::move(
Report));
461 void reportBugOnReceiver(
const NamedDecl *Callee,
const Expr *CallArg,
462 const Decl *DeclWithIssue)
const {
467 SmallString<100> Buf;
468 llvm::raw_svector_ostream Os(Buf);
476 Os <<
" is a raw pointer to " << typeName() <<
" ";
479 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
480 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
482 Report->setDeclWithIssue(DeclWithIssue);
483 BR->emitReport(std::move(
Report));
486 void printArgument(llvm::raw_svector_ostream &Os,
const Expr *Arg,
487 const Decl *D)
const {
488 SmallString<100> Buf;
489 llvm::raw_svector_ostream ArgOs(Buf);
492 StringRef ArgCode = ArgOs.str();
493 if (ArgCode.contains(
'\n'))
495 ArgCode = ArgCode.take_front(50);
496 if (ArgCode.size() == 50)
497 Os <<
" '" << ArgCode <<
"...'";
499 Os <<
" '" << ArgCode <<
"'";
502 enum class PrintDeclKind { Pointee,
Pointer };
503 virtual PrintDeclKind printPointer(llvm::raw_svector_ostream &Os,
504 const Type *T)
const {
507 Os <<
"raw " << (IsPtr ?
"pointer" :
"reference") <<
" to " << typeName();
508 return PrintDeclKind::Pointee;
512class UncountedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
514 UncountedCallArgsChecker()
515 : RawPtrRefCallArgsChecker(
"Uncounted call argument for a raw "
516 "pointer/reference parameter") {}
518 std::optional<bool> isUnsafeType(QualType QT)
const final {
522 std::optional<bool> isUnsafePtr(QualType QT)
const final {
530 bool isSafePtrType(
const QualType
type)
const final {
534 const char *typeName() const final {
return "RefPtr-capable type"; }
537class UncheckedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
539 UncheckedCallArgsChecker()
540 : RawPtrRefCallArgsChecker(
"Unchecked call argument for a raw "
541 "pointer/reference parameter") {}
543 std::optional<bool> isUnsafeType(QualType QT)
const final {
547 std::optional<bool> isUnsafePtr(QualType QT)
const final {
555 bool isSafePtrType(
const QualType
type)
const final {
559 bool isSafeExpr(
const Expr *E)
const final {
563 const char *typeName() const final {
return "CheckedPtr-capable type"; }
566class UnretainedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
568 UnretainedCallArgsChecker()
569 : RawPtrRefCallArgsChecker(
"Unretained call argument for a raw "
570 "pointer/reference parameter") {
571 RTC = RetainTypeChecker();
574 std::optional<bool> isUnsafeType(QualType QT)
const final {
575 return RTC->isUnretained(QT);
578 std::optional<bool> isUnsafePtr(QualType QT)
const final {
579 return RTC->isUnretained(QT);
586 bool isSafePtrType(
const QualType
type)
const final {
590 bool isSafeDecl(
const Decl *D)
const final {
595 PrintDeclKind printPointer(llvm::raw_svector_ostream &Os,
596 const Type *T)
const final {
598 Os << typeName() <<
" ";
599 return PrintDeclKind::Pointer;
601 return RawPtrRefCallArgsChecker::printPointer(Os, T);
604 const char *typeName() const final {
return "RetainPtr-capable type"; }
613bool ento::shouldRegisterUncountedCallArgsChecker(
const CheckerManager &) {
621bool ento::shouldRegisterUncheckedCallArgsChecker(
const CheckerManager &) {
625void ento::registerUnretainedCallArgsChecker(
CheckerManager &Mgr) {
629bool 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.
static void printArgument(const TemplateArgument &A, const PrintingPolicy &PP, llvm::raw_ostream &OS, bool IncludeType)
const clang::PrintingPolicy & getPrintingPolicy() const
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.
ASTContext & getASTContext() const LLVM_READONLY
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.
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
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
void printPretty(raw_ostream &OS, PrinterHelper *Helper, const PrintingPolicy &Policy, unsigned Indentation=0, StringRef NewlineSymbol="\n", const ASTContext *Context=nullptr) const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
const T * getAs() const
Member-template getAs<specific type>'.
const Type * getUnqualifiedDesugaredType() const
Return the specified type with any "sugar" removed from the type, removing any typedefs,...
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)
@ Type
The name was classified as a type.
bool isRetainPtrOrOSPtr(const std::string &Name)
void printTypeName(llvm::raw_ostream &Os, const QualType QT)
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)