23class RawPtrRefLambdaCapturesChecker
24 :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
27 mutable BugReporter *BR =
nullptr;
28 TrivialFunctionAnalysis TFA;
31 mutable std::optional<RetainTypeChecker> RTC;
34 RawPtrRefLambdaCapturesChecker(
const char *description)
35 : Bug(this, description,
"WebKit coding guidelines") {}
37 virtual std::optional<bool> isUnsafePtr(QualType)
const = 0;
38 virtual bool isPtrType(
const std::string &)
const = 0;
39 virtual const char *ptrKind(QualType QT)
const = 0;
41 void checkASTDecl(
const TranslationUnitDecl *TUD, AnalysisManager &MGR,
42 BugReporter &BRArg)
const {
49 const RawPtrRefLambdaCapturesChecker *Checker;
50 llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore;
51 llvm::DenseSet<const LambdaExpr *> LambdasToIgnore;
52 llvm::DenseSet<const ValueDecl *> ProtectedThisDecls;
53 llvm::DenseSet<const CallExpr *> CallToIgnore;
54 llvm::DenseSet<const CXXConstructExpr *> ConstructToIgnore;
55 llvm::DenseMap<const VarDecl *, const LambdaExpr *> LambdaOwnerMap;
59 explicit LocalVisitor(
const RawPtrRefLambdaCapturesChecker *Checker)
62 ShouldVisitTemplateInstantiations =
true;
63 ShouldVisitImplicitCode =
false;
66 bool TraverseCXXMethodDecl(CXXMethodDecl *CXXMD)
override {
67 llvm::SaveAndRestore SavedDecl(ClsType);
70 return DynamicRecursiveASTVisitor::TraverseCXXMethodDecl(CXXMD);
73 bool TraverseObjCMethodDecl(ObjCMethodDecl *OCMD)
override {
74 llvm::SaveAndRestore SavedDecl(ClsType);
77 ClsType = ImplParamDecl->getType();
79 return DynamicRecursiveASTVisitor::TraverseObjCMethodDecl(OCMD);
82 bool VisitTypedefDecl(TypedefDecl *TD)
override {
84 Checker->RTC->visitTypedef(TD);
88 bool shouldCheckThis() {
90 !ClsType.
isNull() ? Checker->isUnsafePtr(ClsType) : std::nullopt;
91 return result && *result;
95 if (LambdasToIgnore.contains(L))
97 Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L),
102 bool VisitVarDecl(VarDecl *VD)
override {
106 if (
auto *L = dyn_cast_or_null<LambdaExpr>(
Init->IgnoreParenCasts())) {
107 LambdasToIgnore.insert(L);
112 if (
auto *E = dyn_cast<ExprWithCleanups>(
Init))
113 Init = E->getSubExpr();
114 if (
auto *E = dyn_cast<CXXBindTemporaryExpr>(
Init))
115 Init = E->getSubExpr();
116 if (
auto *CE = dyn_cast<CallExpr>(
Init)) {
117 if (
auto *Callee = CE->getDirectCallee()) {
119 unsigned ArgCnt = CE->getNumArgs();
120 if (FnName ==
"makeScopeExit" && ArgCnt == 1) {
121 auto *Arg = CE->getArg(0);
122 if (
auto *E = dyn_cast<MaterializeTemporaryExpr>(Arg))
123 Arg = E->getSubExpr();
124 if (
auto *L = dyn_cast<LambdaExpr>(Arg)) {
125 LambdaOwnerMap.insert(std::make_pair(VD, L));
126 CallToIgnore.insert(CE);
127 LambdasToIgnore.insert(L);
129 }
else if (FnName ==
"makeVisitor") {
130 for (
unsigned ArgIndex = 0; ArgIndex < ArgCnt; ++ArgIndex) {
131 auto *Arg = CE->getArg(ArgIndex);
132 if (
auto *E = dyn_cast<MaterializeTemporaryExpr>(Arg))
133 Arg = E->getSubExpr();
134 if (
auto *L = dyn_cast<LambdaExpr>(Arg)) {
135 LambdaOwnerMap.insert(std::make_pair(VD, L));
136 CallToIgnore.insert(CE);
137 LambdasToIgnore.insert(L);
142 }
else if (
auto *CE = dyn_cast<CXXConstructExpr>(
Init)) {
143 if (
auto *Ctor = CE->getConstructor()) {
144 if (
auto *Cls = Ctor->getParent()) {
146 unsigned ArgCnt = CE->getNumArgs();
147 if (FnName ==
"ScopeExit" && ArgCnt == 1) {
148 auto *Arg = CE->getArg(0);
149 if (
auto *E = dyn_cast<MaterializeTemporaryExpr>(Arg))
150 Arg = E->getSubExpr();
151 if (
auto *L = dyn_cast<LambdaExpr>(Arg)) {
152 LambdaOwnerMap.insert(std::make_pair(VD, L));
153 ConstructToIgnore.insert(CE);
154 LambdasToIgnore.insert(L);
163 bool VisitDeclRefExpr(DeclRefExpr *DRE)
override {
164 if (DeclRefExprsToIgnore.contains(DRE))
166 auto *VD = dyn_cast_or_null<VarDecl>(DRE->
getDecl());
169 if (
auto It = LambdaOwnerMap.find(VD); It != LambdaOwnerMap.end()) {
170 auto *L = It->second;
171 Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L),
178 auto *L = dyn_cast_or_null<LambdaExpr>(
Init->IgnoreParenCasts());
181 LambdasToIgnore.insert(L);
182 Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L),
187 bool shouldTreatAllArgAsNoEscape(FunctionDecl *FDecl) {
189 for (
auto *Decl = FDecl->
getParent(); Decl; Decl =
Decl->getParent()) {
196 if (Name ==
"WTF" && PreviousName ==
"switchOn")
199 if (Name ==
"std" && PreviousName ==
"ranges")
206 bool VisitCXXConstructExpr(CXXConstructExpr *CE)
override {
207 if (ConstructToIgnore.contains(CE))
210 unsigned ArgIndex = 0;
211 for (
auto *Param :
Callee->parameters()) {
215 if (
auto *L = findLambdaInArg(Arg)) {
216 LambdasToIgnore.insert(L);
217 if (!Param->hasAttr<NoEscapeAttr>())
218 Checker->visitLambdaExpr(
219 L, shouldCheckThis() && !hasProtectedThis(L), ClsType);
227 bool VisitCallExpr(CallExpr *CE)
override {
228 if (CallToIgnore.contains(CE))
230 checkCalleeLambda(CE);
232 if (isVisitFunction(CE, Callee))
234 checkParameters(CE, Callee);
235 }
else if (
auto *CalleeE = CE->
getCallee()) {
236 if (
auto *DRE = dyn_cast<DeclRefExpr>(CalleeE->IgnoreParenCasts())) {
237 if (
auto *Callee = dyn_cast_or_null<FunctionDecl>(DRE->
getDecl()))
238 checkParameters(CE, Callee);
244 bool isVisitFunction(CallExpr *CallExpr, FunctionDecl *FnDecl) {
255 if (NsName !=
"WTF" && NsName !=
"std")
257 auto *Arg = CallExpr->
getArg(0);
260 auto *DRE = dyn_cast<DeclRefExpr>(Arg->IgnoreParenCasts());
263 auto *VD = dyn_cast<VarDecl>(DRE->
getDecl());
266 if (!LambdaOwnerMap.contains(VD))
268 DeclRefExprsToIgnore.insert(DRE);
272 void checkParameters(CallExpr *CE, FunctionDecl *Callee) {
274 bool TreatAllArgsAsNoEscape = shouldTreatAllArgAsNoEscape(Callee);
275 for (
auto *Param :
Callee->parameters()) {
279 if (
auto *L = findLambdaInArg(Arg)) {
280 LambdasToIgnore.insert(L);
281 if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape)
282 Checker->visitLambdaExpr(
283 L, shouldCheckThis() && !hasProtectedThis(L), ClsType);
290 if (
auto *Lambda = dyn_cast_or_null<LambdaExpr>(E))
292 auto *TempExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(E);
298 if (
auto *Lambda = dyn_cast<LambdaExpr>(E))
300 auto *CE = dyn_cast_or_null<CXXConstructExpr>(E);
306 auto *InnerCE = dyn_cast_or_null<CXXConstructExpr>(CtorArg);
307 if (InnerCE && InnerCE->getNumArgs())
308 CtorArg = InnerCE->getArg(0)->IgnoreParenCasts();
309 auto updateIgnoreList = [&] {
310 ConstructToIgnore.insert(CE);
312 ConstructToIgnore.insert(InnerCE);
314 if (
auto *Lambda = dyn_cast<LambdaExpr>(CtorArg)) {
318 if (
auto *TempExpr = dyn_cast<CXXBindTemporaryExpr>(CtorArg)) {
320 if (
auto *Lambda = dyn_cast<LambdaExpr>(E)) {
325 auto *DRE = dyn_cast<DeclRefExpr>(CtorArg);
328 auto *VD = dyn_cast_or_null<VarDecl>(DRE->
getDecl());
334 if (
auto *Lambda = dyn_cast<LambdaExpr>(
Init)) {
335 DeclRefExprsToIgnore.insert(DRE);
342 void checkCalleeLambda(CallExpr *CE) {
347 if (
auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Callee)) {
348 Callee = MTE->getSubExpr();
353 if (
auto *L = dyn_cast<LambdaExpr>(Callee)) {
354 LambdasToIgnore.insert(L);
357 auto *DRE = dyn_cast<DeclRefExpr>(
Callee->IgnoreParenCasts());
360 auto *MD = dyn_cast_or_null<CXXMethodDecl>(DRE->
getDecl());
364 if (
auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) {
365 LambdasToIgnore.insert(L);
368 auto *ArgRef = dyn_cast<DeclRefExpr>(Arg);
371 auto *VD = dyn_cast_or_null<VarDecl>(ArgRef->getDecl());
377 auto *L = dyn_cast_or_null<LambdaExpr>(
Init->IgnoreParenCasts());
380 DeclRefExprsToIgnore.insert(ArgRef);
381 LambdasToIgnore.insert(L);
385 for (
const LambdaCapture &OtherCapture : L->
captures()) {
386 if (!OtherCapture.capturesVariable())
388 if (
auto *ValueDecl = OtherCapture.getCapturedVar()) {
389 if (declProtectsThis(ValueDecl)) {
390 ProtectedThisDecls.insert(ValueDecl);
398 bool declProtectsThis(
const ValueDecl *ValueDecl)
const {
399 auto *VD = dyn_cast<VarDecl>(ValueDecl);
407 if (
auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Arg))
409 if (
auto *CE = dyn_cast<CXXConstructExpr>(Arg)) {
410 auto *Ctor = CE->getConstructor();
414 if (Checker->isPtrType(clsName) && CE->
getNumArgs()) {
419 if (
auto *CXXR =
Type->getPointeeCXXRecordDecl()) {
420 if (CXXR == Ctor->getParent() && Ctor->isMoveConstructor() &&
429 if (
auto *CE = dyn_cast<CallExpr>(Arg)) {
438 if (
auto *OpCE = dyn_cast<CXXOperatorCallExpr>(Arg)) {
439 auto OpCode = OpCE->getOperator();
440 if (OpCode == OO_Star || OpCode == OO_Amp) {
441 auto *
Callee = OpCE->getDirectCallee();
445 if (!Checker->isPtrType(clsName) || !OpCE->getNumArgs())
451 if (
auto *UO = dyn_cast<UnaryOperator>(Arg)) {
452 auto OpCode = UO->getOpcode();
453 if (OpCode == UO_Deref || OpCode == UO_AddrOf) {
460 if (
auto *DRE = dyn_cast<DeclRefExpr>(Arg)) {
462 if (
auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(Decl)) {
463 auto kind = ImplicitParam->getParameterKind();
464 return kind == ImplicitParamKind::ObjCSelf ||
465 kind == ImplicitParamKind::CXXThis;
467 return ProtectedThisDecls.contains(Decl);
473 LocalVisitor visitor(
this);
475 RTC->visitTranslationUnitDecl(TUD);
476 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
479 void visitLambdaExpr(
const LambdaExpr *L,
bool shouldCheckThis,
481 bool ignoreParamVarDecl =
false)
const {
482 if (TFA.isTrivial(L->
getBody()))
484 for (
const LambdaCapture &
C : L->
captures()) {
485 if (
C.capturesVariable()) {
486 ValueDecl *CapturedVar =
C.getCapturedVar();
489 if (
auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(CapturedVar)) {
490 auto kind = ImplicitParam->getParameterKind();
491 if ((kind == ImplicitParamKind::ObjCSelf ||
492 kind == ImplicitParamKind::CXXThis) &&
496 QualType CapturedVarQualType = CapturedVar->
getType();
497 auto IsUncountedPtr = isUnsafePtr(CapturedVar->
getType());
501 if (IsUncountedPtr && *IsUncountedPtr)
502 reportBug(
C, CapturedVar, CapturedVarQualType, L);
503 }
else if (
C.capturesThis() && shouldCheckThis) {
504 if (ignoreParamVarDecl)
506 reportBugOnThisPtr(
C,
T);
511 void reportBug(
const LambdaCapture &
Capture, ValueDecl *CapturedVar,
515 auto Location =
Capture.getLocation();
519 SmallString<100> Buf;
520 llvm::raw_svector_ostream Os(Buf);
525 Os <<
"Implicitly captured ";
528 Os <<
"raw-pointer ";
534 Os <<
" to " << ptrKind(
T) <<
" type is unsafe.";
536 PathDiagnosticLocation BSLoc(Location, BR->getSourceManager());
537 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
538 BR->emitReport(std::move(
Report));
541 void reportBugOnThisPtr(
const LambdaCapture &
Capture,
542 const QualType
T)
const {
543 SmallString<100> Buf;
544 llvm::raw_svector_ostream Os(Buf);
549 Os <<
"Implicitly captured ";
552 Os <<
"raw-pointer 'this' to " << ptrKind(
T) <<
" type is unsafe.";
554 PathDiagnosticLocation BSLoc(
Capture.getLocation(), BR->getSourceManager());
555 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
556 BR->emitReport(std::move(
Report));
560class UncountedLambdaCapturesChecker :
public RawPtrRefLambdaCapturesChecker {
562 UncountedLambdaCapturesChecker()
563 : RawPtrRefLambdaCapturesChecker(
"Lambda capture of uncounted or "
564 "unchecked variable") {}
566 std::optional<bool> isUnsafePtr(QualType QT)
const final {
569 if (result1 && *result1)
571 if (result2 && *result2)
578 virtual bool isPtrType(
const std::string &Name)
const final {
582 const char *ptrKind(QualType QT)
const final {
589class UnretainedLambdaCapturesChecker :
public RawPtrRefLambdaCapturesChecker {
591 UnretainedLambdaCapturesChecker()
592 : RawPtrRefLambdaCapturesChecker(
"Lambda capture of unretained "
594 RTC = RetainTypeChecker();
597 std::optional<bool> isUnsafePtr(QualType QT)
const final {
598 if (QT.hasStrongOrWeakObjCLifetime())
600 return RTC->isUnretained(QT);
603 virtual bool isPtrType(
const std::string &Name)
const final {
607 const char *ptrKind(QualType QT)
const final {
return "unretained"; }
612void ento::registerUncountedLambdaCapturesChecker(
CheckerManager &Mgr) {
616bool ento::shouldRegisterUncountedLambdaCapturesChecker(
621void ento::registerUnretainedLambdaCapturesChecker(
CheckerManager &Mgr) {
625bool ento::shouldRegisterUnretainedLambdaCapturesChecker(
Expr * getArg(unsigned Arg)
Return the specified argument.
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
unsigned getNumArgs() const
Return the number of arguments to the constructor call.
QualType getThisType() const
Return the type of the this pointer.
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.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Stmt * getBody() const
Retrieve the body of the lambda.
SourceLocation getBeginLoc() const LLVM_READONLY
capture_range captures() const
Retrieve this lambda's captures.
ImplicitParamDecl * getSelfDecl() const
bool isInstanceMethod() const
bool isNull() const
Return true if this QualType doesn't point to a type yet.
const Type * getTypePtrOrNull() const
bool isReferenceType() const
const Expr * getInit() const
bool hasLocalStorage() const
Returns true if a variable with function scope is a non-static local variable.
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.
unsigned kind
All of the diagnostics that can be emitted by the frontend.
constexpr bool isPtrType(PrimType 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 isa(CodeGen::Address addr)
@ LCK_ByCopy
Capturing by copy (a.k.a., by value)
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
const FunctionProtoType * T
@ Type
The name was classified as a type.
bool isRetainPtrOrOSPtr(const std::string &Name)
bool isRefType(const std::string &Name)
std::optional< bool > isUncountedPtr(const QualType T)
std::string safeGetName(const T *ASTNode)
bool isCheckedPtr(const std::string &Name)
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
bool isStdOrWTFMove(const clang::FunctionDecl *F)
std::optional< bool > isUncounted(const QualType T)
std::optional< bool > isUncheckedPtr(const QualType T)