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 *, SmallVector<const LambdaExpr *>>
60 explicit LocalVisitor(
const RawPtrRefLambdaCapturesChecker *Checker)
63 ShouldVisitTemplateInstantiations =
true;
64 ShouldVisitImplicitCode =
false;
67 bool TraverseCXXConstructorDecl(CXXConstructorDecl *Ctor)
override {
68 llvm::SaveAndRestore SavedDecl(ClsType);
70 return DynamicRecursiveASTVisitor::TraverseCXXConstructorDecl(Ctor);
73 bool TraverseCXXDestructorDecl(CXXDestructorDecl *Dtor)
override {
74 llvm::SaveAndRestore SavedDecl(ClsType);
76 return DynamicRecursiveASTVisitor::TraverseCXXDestructorDecl(Dtor);
79 bool TraverseCXXMethodDecl(CXXMethodDecl *CXXMD)
override {
80 llvm::SaveAndRestore SavedDecl(ClsType);
83 return DynamicRecursiveASTVisitor::TraverseCXXMethodDecl(CXXMD);
86 bool TraverseObjCMethodDecl(ObjCMethodDecl *OCMD)
override {
87 llvm::SaveAndRestore SavedDecl(ClsType);
90 ClsType = ImplParamDecl->getType();
92 return DynamicRecursiveASTVisitor::TraverseObjCMethodDecl(OCMD);
95 bool VisitTypedefDecl(TypedefDecl *TD)
override {
97 Checker->RTC->visitTypedef(TD);
101 bool shouldCheckThis() {
103 !ClsType.
isNull() ? Checker->isUnsafePtr(ClsType) : std::nullopt;
104 return result && *result;
107 bool VisitLambdaExpr(
LambdaExpr *L)
override {
108 if (LambdasToIgnore.contains(L))
110 Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L),
115 bool VisitVarDecl(VarDecl *VD)
override {
119 if (
auto *L = dyn_cast_or_null<LambdaExpr>(
Init->IgnoreParenCasts())) {
120 LambdasToIgnore.insert(L);
125 if (
auto *E = dyn_cast<ExprWithCleanups>(
Init))
126 Init = E->getSubExpr();
127 if (
auto *E = dyn_cast<CXXBindTemporaryExpr>(
Init))
128 Init = E->getSubExpr();
129 if (
auto *CE = dyn_cast<CallExpr>(
Init)) {
130 if (
auto *Callee = CE->getDirectCallee()) {
132 unsigned ArgCnt = CE->getNumArgs();
133 if (FnName ==
"makeScopeExit" && ArgCnt == 1) {
134 auto *Arg = CE->getArg(0);
135 if (
auto *E = dyn_cast<MaterializeTemporaryExpr>(Arg))
136 Arg = E->getSubExpr();
137 if (
auto *L = dyn_cast<LambdaExpr>(Arg))
138 addLambdaOwner(VD, CE, L);
139 }
else if (FnName ==
"makeVisitor") {
140 for (
unsigned ArgIndex = 0; ArgIndex < ArgCnt; ++ArgIndex) {
141 auto *Arg = CE->getArg(ArgIndex);
142 if (
auto *E = dyn_cast<MaterializeTemporaryExpr>(Arg))
143 Arg = E->getSubExpr();
144 if (
auto *L = dyn_cast<LambdaExpr>(Arg))
145 addLambdaOwner(VD, CE, L);
149 }
else if (
auto *CE = dyn_cast<CXXConstructExpr>(
Init)) {
150 if (
auto *Ctor = CE->getConstructor()) {
153 unsigned ArgCnt = CE->getNumArgs();
154 if (FnName ==
"ScopeExit" && ArgCnt == 1) {
155 auto *Arg = CE->getArg(0);
156 if (
auto *E = dyn_cast<MaterializeTemporaryExpr>(Arg))
157 Arg = E->getSubExpr();
158 if (
auto *L = dyn_cast<LambdaExpr>(Arg))
159 addLambdaOwner(VD, CE, L);
167 void addLambdaOwner(VarDecl *VD, CallExpr *CE,
LambdaExpr *L) {
168 auto result = LambdaOwnerMap.insert(
169 std::make_pair(VD, SmallVector<const LambdaExpr *>{L}));
171 result.first->second.push_back(L);
172 CallToIgnore.insert(CE);
173 LambdasToIgnore.insert(L);
176 void addLambdaOwner(VarDecl *VD, CXXConstructExpr *CE,
LambdaExpr *L) {
177 auto result = LambdaOwnerMap.insert(
178 std::make_pair(VD, SmallVector<const LambdaExpr *>{L}));
180 result.first->second.push_back(L);
181 ConstructToIgnore.insert(CE);
182 LambdasToIgnore.insert(L);
185 bool VisitDeclRefExpr(DeclRefExpr *DRE)
override {
186 if (DeclRefExprsToIgnore.contains(DRE))
188 auto *VD = dyn_cast_or_null<VarDecl>(DRE->
getDecl());
191 if (
auto It = LambdaOwnerMap.find(VD); It != LambdaOwnerMap.end()) {
192 for (
auto *L : It->second) {
193 Checker->visitLambdaExpr(
194 L, shouldCheckThis() && !hasProtectedThis(L), ClsType);
201 auto *L = dyn_cast_or_null<LambdaExpr>(
Init->IgnoreParenCasts());
204 LambdasToIgnore.insert(L);
205 Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L),
210 bool shouldTreatAllArgAsNoEscape(FunctionDecl *FDecl) {
212 for (
auto *Decl = FDecl->
getParent(); Decl; Decl =
Decl->getParent()) {
219 if (Name ==
"WTF" && PreviousName ==
"switchOn")
222 if (Name ==
"std" && PreviousName ==
"ranges")
229 bool VisitCXXConstructExpr(CXXConstructExpr *CE)
override {
230 if (ConstructToIgnore.contains(CE))
233 unsigned ArgIndex = 0;
234 for (
auto *Param :
Callee->parameters()) {
238 if (
auto *L = findLambdaInArg(Arg)) {
239 LambdasToIgnore.insert(L);
240 if (!Param->hasAttr<NoEscapeAttr>())
241 Checker->visitLambdaExpr(
242 L, shouldCheckThis() && !hasProtectedThis(L), ClsType);
250 bool VisitCallExpr(CallExpr *CE)
override {
251 if (CallToIgnore.contains(CE))
253 checkCalleeLambda(CE);
255 if (isVisitFunction(CE, Callee))
257 checkParameters(CE, Callee);
258 }
else if (
auto *CalleeE = CE->
getCallee()) {
259 if (
auto *DRE = dyn_cast<DeclRefExpr>(CalleeE->IgnoreParenCasts())) {
260 if (
auto *Callee = dyn_cast_or_null<FunctionDecl>(DRE->
getDecl()))
261 checkParameters(CE, Callee);
267 bool isVisitFunction(CallExpr *CallExpr, FunctionDecl *FnDecl) {
278 if (NsName !=
"WTF" && NsName !=
"std")
280 auto *Arg = CallExpr->
getArg(0);
283 auto *DRE = dyn_cast<DeclRefExpr>(Arg->IgnoreParenCasts());
286 auto *VD = dyn_cast<VarDecl>(DRE->
getDecl());
289 if (!LambdaOwnerMap.contains(VD))
291 DeclRefExprsToIgnore.insert(DRE);
295 void checkParameters(CallExpr *CE, FunctionDecl *Callee) {
297 bool TreatAllArgsAsNoEscape = shouldTreatAllArgAsNoEscape(Callee);
298 for (
auto *Param :
Callee->parameters()) {
302 if (
auto *L = findLambdaInArg(Arg)) {
303 LambdasToIgnore.insert(L);
304 if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape)
305 Checker->visitLambdaExpr(
306 L, shouldCheckThis() && !hasProtectedThis(L), ClsType);
313 if (
auto *Lambda = dyn_cast_or_null<LambdaExpr>(E))
315 auto *TempExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(E);
321 if (
auto *Lambda = dyn_cast<LambdaExpr>(E))
323 auto *CE = dyn_cast_or_null<CXXConstructExpr>(E);
329 auto *InnerCE = dyn_cast_or_null<CXXConstructExpr>(CtorArg);
330 if (InnerCE && InnerCE->getNumArgs())
331 CtorArg = InnerCE->getArg(0)->IgnoreParenCasts();
332 auto updateIgnoreList = [&] {
333 ConstructToIgnore.insert(CE);
335 ConstructToIgnore.insert(InnerCE);
337 if (
auto *Lambda = dyn_cast<LambdaExpr>(CtorArg)) {
341 if (
auto *TempExpr = dyn_cast<CXXBindTemporaryExpr>(CtorArg)) {
343 if (
auto *Lambda = dyn_cast<LambdaExpr>(E)) {
348 auto *DRE = dyn_cast<DeclRefExpr>(CtorArg);
351 auto *VD = dyn_cast_or_null<VarDecl>(DRE->
getDecl());
357 if (
auto *Lambda = dyn_cast<LambdaExpr>(
Init)) {
358 DeclRefExprsToIgnore.insert(DRE);
365 void checkCalleeLambda(CallExpr *CE) {
370 if (
auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Callee)) {
371 Callee = MTE->getSubExpr();
376 if (
auto *L = dyn_cast<LambdaExpr>(Callee)) {
377 LambdasToIgnore.insert(L);
380 auto *DRE = dyn_cast<DeclRefExpr>(
Callee->IgnoreParenCasts());
383 auto *MD = dyn_cast_or_null<CXXMethodDecl>(DRE->
getDecl());
387 if (
auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) {
388 LambdasToIgnore.insert(L);
391 auto *ArgRef = dyn_cast<DeclRefExpr>(Arg);
394 auto *VD = dyn_cast_or_null<VarDecl>(ArgRef->getDecl());
400 auto *L = dyn_cast_or_null<LambdaExpr>(
Init->IgnoreParenCasts());
403 DeclRefExprsToIgnore.insert(ArgRef);
404 LambdasToIgnore.insert(L);
408 for (
const LambdaCapture &OtherCapture : L->
captures()) {
409 if (!OtherCapture.capturesVariable())
411 if (
auto *ValueDecl = OtherCapture.getCapturedVar()) {
412 if (declProtectsThis(ValueDecl)) {
413 ProtectedThisDecls.insert(ValueDecl);
421 bool declProtectsThis(
const ValueDecl *ValueDecl)
const {
422 auto *VD = dyn_cast<VarDecl>(ValueDecl);
430 if (
auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Arg))
432 if (
auto *CE = dyn_cast<CXXConstructExpr>(Arg)) {
433 auto *Ctor = CE->getConstructor();
437 if (Checker->isPtrType(clsName) && CE->
getNumArgs()) {
442 if (
auto *CXXR =
Type->getPointeeCXXRecordDecl()) {
452 if (
auto *CE = dyn_cast<CallExpr>(Arg)) {
461 if (
auto *OpCE = dyn_cast<CXXOperatorCallExpr>(Arg)) {
462 auto OpCode = OpCE->getOperator();
463 if (OpCode == OO_Star || OpCode == OO_Amp) {
464 auto *
Callee = OpCE->getDirectCallee();
468 if (!Checker->isPtrType(clsName) || !OpCE->getNumArgs())
474 if (
auto *UO = dyn_cast<UnaryOperator>(Arg)) {
475 auto OpCode = UO->getOpcode();
476 if (OpCode == UO_Deref || OpCode == UO_AddrOf) {
483 if (
auto *DRE = dyn_cast<DeclRefExpr>(Arg)) {
485 if (
auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(Decl)) {
486 auto kind = ImplicitParam->getParameterKind();
487 return kind == ImplicitParamKind::ObjCSelf ||
488 kind == ImplicitParamKind::CXXThis;
490 return ProtectedThisDecls.contains(Decl);
496 LocalVisitor visitor(
this);
498 RTC->visitTranslationUnitDecl(TUD);
499 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
502 void visitLambdaExpr(
const LambdaExpr *L,
bool shouldCheckThis,
504 bool ignoreParamVarDecl =
false)
const {
505 if (TFA.isTrivial(L->
getBody()))
507 for (
const LambdaCapture &
C : L->
captures()) {
508 if (
C.capturesVariable()) {
509 ValueDecl *CapturedVar =
C.getCapturedVar();
512 if (
auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(CapturedVar)) {
513 auto kind = ImplicitParam->getParameterKind();
514 if ((
kind == ImplicitParamKind::ObjCSelf ||
515 kind == ImplicitParamKind::CXXThis) &&
519 QualType CapturedVarQualType = CapturedVar->
getType();
520 auto IsUncountedPtr = isUnsafePtr(CapturedVar->
getType());
524 if (IsUncountedPtr && *IsUncountedPtr)
525 reportBug(
C, CapturedVar, CapturedVarQualType, L);
526 }
else if (
C.capturesThis() && shouldCheckThis) {
527 if (ignoreParamVarDecl)
529 reportBugOnThisPtr(
C, T);
534 void reportBug(
const LambdaCapture &
Capture, ValueDecl *CapturedVar,
535 const QualType T,
const LambdaExpr *L)
const {
538 auto Location =
Capture.getLocation();
542 SmallString<100> Buf;
543 llvm::raw_svector_ostream Os(Buf);
548 Os <<
"Implicitly captured ";
551 Os <<
"raw-pointer ";
557 Os <<
" to " << ptrKind(T) <<
" type is unsafe.";
559 PathDiagnosticLocation BSLoc(Location, BR->getSourceManager());
560 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
561 BR->emitReport(std::move(
Report));
564 void reportBugOnThisPtr(
const LambdaCapture &
Capture,
565 const QualType T)
const {
566 SmallString<100> Buf;
567 llvm::raw_svector_ostream Os(Buf);
572 Os <<
"Implicitly captured ";
575 Os <<
"raw-pointer 'this' to " << ptrKind(T) <<
" type is unsafe.";
577 PathDiagnosticLocation BSLoc(
Capture.getLocation(), BR->getSourceManager());
578 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
579 BR->emitReport(std::move(
Report));
583class UncountedLambdaCapturesChecker :
public RawPtrRefLambdaCapturesChecker {
585 UncountedLambdaCapturesChecker()
586 : RawPtrRefLambdaCapturesChecker(
"Lambda capture of uncounted or "
587 "unchecked variable") {}
589 std::optional<bool> isUnsafePtr(QualType QT)
const final {
592 if (result1 && *result1)
594 if (result2 && *result2)
601 virtual bool isPtrType(
const std::string &Name)
const final {
605 const char *ptrKind(QualType QT)
const final {
612class UnretainedLambdaCapturesChecker :
public RawPtrRefLambdaCapturesChecker {
614 UnretainedLambdaCapturesChecker()
615 : RawPtrRefLambdaCapturesChecker(
"Lambda capture of unretained "
617 RTC = RetainTypeChecker();
620 std::optional<bool> isUnsafePtr(QualType QT)
const final {
621 if (QT.hasStrongOrWeakObjCLifetime())
623 return RTC->isUnretained(QT);
626 virtual bool isPtrType(
const std::string &Name)
const final {
630 const char *ptrKind(QualType QT)
const final {
return "unretained"; }
635void ento::registerUncountedLambdaCapturesChecker(
CheckerManager &Mgr) {
639bool ento::shouldRegisterUncountedLambdaCapturesChecker(
644void ento::registerUnretainedLambdaCapturesChecker(
CheckerManager &Mgr) {
648bool ento::shouldRegisterUnretainedLambdaCapturesChecker(
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
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.
bool isMoveConstructor(unsigned &TypeQuals) const
Determine whether this constructor is a move constructor (C++11 [class.copy]p3), which can be used to...
const CXXRecordDecl * getParent() const
Return the parent of this method declaration, which is the class in which this method is defined.
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.
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)
@ 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)