@@ -21,15 +21,23 @@ using namespace clang;
2121using namespace ento ;
2222
2323namespace {
24- class UncountedLambdaCapturesChecker
24+ class RawPtrRefLambdaCapturesChecker
2525 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
2626private:
27- BugType Bug{this , " Lambda capture of uncounted variable" ,
28- " WebKit coding guidelines" };
27+ BugType Bug;
2928 mutable BugReporter *BR = nullptr ;
3029 TrivialFunctionAnalysis TFA;
3130
31+ protected:
32+ mutable std::optional<RetainTypeChecker> RTC;
33+
3234public:
35+ RawPtrRefLambdaCapturesChecker (const char *description)
36+ : Bug(this , description, " WebKit coding guidelines" ) {}
37+
38+ virtual std::optional<bool > isUnsafePtr (QualType) const = 0;
39+ virtual const char *ptrKind (QualType QT) const = 0;
40+
3341 void checkASTDecl (const TranslationUnitDecl *TUD, AnalysisManager &MGR,
3442 BugReporter &BRArg) const {
3543 BR = &BRArg;
@@ -38,7 +46,7 @@ class UncountedLambdaCapturesChecker
3846 // visit template instantiations or lambda classes. We
3947 // want to visit those, so we make our own RecursiveASTVisitor.
4048 struct LocalVisitor : public RecursiveASTVisitor <LocalVisitor> {
41- const UncountedLambdaCapturesChecker *Checker;
49+ const RawPtrRefLambdaCapturesChecker *Checker;
4250 llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore;
4351 llvm::DenseSet<const LambdaExpr *> LambdasToIgnore;
4452 llvm::DenseSet<const ValueDecl *> ProtectedThisDecls;
@@ -48,7 +56,7 @@ class UncountedLambdaCapturesChecker
4856
4957 using Base = RecursiveASTVisitor<LocalVisitor>;
5058
51- explicit LocalVisitor (const UncountedLambdaCapturesChecker *Checker)
59+ explicit LocalVisitor (const RawPtrRefLambdaCapturesChecker *Checker)
5260 : Checker(Checker) {
5361 assert (Checker);
5462 }
@@ -63,16 +71,23 @@ class UncountedLambdaCapturesChecker
6371 return Base::TraverseCXXMethodDecl (CXXMD);
6472 }
6573
74+ bool VisitTypedefDecl (TypedefDecl *TD) {
75+ if (Checker->RTC )
76+ Checker->RTC ->visitTypedef (TD);
77+ return true ;
78+ }
79+
6680 bool shouldCheckThis () {
6781 auto result =
68- !ClsType.isNull () ? isUnsafePtr (ClsType, false ) : std::nullopt ;
82+ !ClsType.isNull () ? Checker-> isUnsafePtr (ClsType) : std::nullopt ;
6983 return result && *result;
7084 }
7185
7286 bool VisitLambdaExpr (LambdaExpr *L) {
7387 if (LambdasToIgnore.contains (L))
7488 return true ;
75- Checker->visitLambdaExpr (L, shouldCheckThis () && !hasProtectedThis (L));
89+ Checker->visitLambdaExpr (L, shouldCheckThis () && !hasProtectedThis (L),
90+ ClsType);
7691 return true ;
7792 }
7893
@@ -100,7 +115,8 @@ class UncountedLambdaCapturesChecker
100115 if (!L)
101116 return true ;
102117 LambdasToIgnore.insert (L);
103- Checker->visitLambdaExpr (L, shouldCheckThis () && !hasProtectedThis (L));
118+ Checker->visitLambdaExpr (L, shouldCheckThis () && !hasProtectedThis (L),
119+ ClsType);
104120 return true ;
105121 }
106122
@@ -125,8 +141,8 @@ class UncountedLambdaCapturesChecker
125141 if (auto *L = findLambdaInArg (Arg)) {
126142 LambdasToIgnore.insert (L);
127143 if (!Param->hasAttr <NoEscapeAttr>())
128- Checker->visitLambdaExpr (L, shouldCheckThis () &&
129- !hasProtectedThis (L));
144+ Checker->visitLambdaExpr (
145+ L, shouldCheckThis () && !hasProtectedThis (L), ClsType );
130146 }
131147 ++ArgIndex;
132148 }
@@ -146,8 +162,8 @@ class UncountedLambdaCapturesChecker
146162 if (auto *L = findLambdaInArg (Arg)) {
147163 LambdasToIgnore.insert (L);
148164 if (!Param->hasAttr <NoEscapeAttr>() && !TreatAllArgsAsNoEscape)
149- Checker->visitLambdaExpr (L, shouldCheckThis () &&
150- !hasProtectedThis (L));
165+ Checker->visitLambdaExpr (
166+ L, shouldCheckThis () && !hasProtectedThis (L), ClsType );
151167 }
152168 ++ArgIndex;
153169 }
@@ -172,14 +188,22 @@ class UncountedLambdaCapturesChecker
172188 auto *CtorArg = CE->getArg (0 )->IgnoreParenCasts ();
173189 if (!CtorArg)
174190 return nullptr ;
175- if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg)) {
191+ auto *InnerCE = dyn_cast_or_null<CXXConstructExpr>(CtorArg);
192+ if (InnerCE && InnerCE->getNumArgs ())
193+ CtorArg = InnerCE->getArg (0 )->IgnoreParenCasts ();
194+ auto updateIgnoreList = [&] {
176195 ConstructToIgnore.insert (CE);
196+ if (InnerCE)
197+ ConstructToIgnore.insert (InnerCE);
198+ };
199+ if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg)) {
200+ updateIgnoreList ();
177201 return Lambda;
178202 }
179203 if (auto *TempExpr = dyn_cast<CXXBindTemporaryExpr>(CtorArg)) {
180204 E = TempExpr->getSubExpr ()->IgnoreParenCasts ();
181205 if (auto *Lambda = dyn_cast<LambdaExpr>(E)) {
182- ConstructToIgnore. insert (CE );
206+ updateIgnoreList ( );
183207 return Lambda;
184208 }
185209 }
@@ -192,10 +216,14 @@ class UncountedLambdaCapturesChecker
192216 auto *Init = VD->getInit ();
193217 if (!Init)
194218 return nullptr ;
219+ if (auto *Lambda = dyn_cast<LambdaExpr>(Init)) {
220+ updateIgnoreList ();
221+ return Lambda;
222+ }
195223 TempExpr = dyn_cast<CXXBindTemporaryExpr>(Init->IgnoreParenCasts ());
196224 if (!TempExpr)
197225 return nullptr ;
198- ConstructToIgnore. insert (CE );
226+ updateIgnoreList ( );
199227 return dyn_cast_or_null<LambdaExpr>(TempExpr->getSubExpr ());
200228 }
201229
@@ -229,7 +257,7 @@ class UncountedLambdaCapturesChecker
229257 DeclRefExprsToIgnore.insert (ArgRef);
230258 LambdasToIgnore.insert (L);
231259 Checker->visitLambdaExpr (L, shouldCheckThis () && !hasProtectedThis (L),
232- /* ignoreParamVarDecl */ true );
260+ ClsType, /* ignoreParamVarDecl */ true );
233261 }
234262
235263 bool hasProtectedThis (LambdaExpr *L) {
@@ -296,10 +324,12 @@ class UncountedLambdaCapturesChecker
296324 };
297325
298326 LocalVisitor visitor (this );
327+ if (RTC)
328+ RTC->visitTranslationUnitDecl (TUD);
299329 visitor.TraverseDecl (const_cast <TranslationUnitDecl *>(TUD));
300330 }
301331
302- void visitLambdaExpr (LambdaExpr *L, bool shouldCheckThis,
332+ void visitLambdaExpr (LambdaExpr *L, bool shouldCheckThis, const QualType T,
303333 bool ignoreParamVarDecl = false ) const {
304334 if (TFA.isTrivial (L->getBody ()))
305335 return ;
@@ -309,13 +339,13 @@ class UncountedLambdaCapturesChecker
309339 if (ignoreParamVarDecl && isa<ParmVarDecl>(CapturedVar))
310340 continue ;
311341 QualType CapturedVarQualType = CapturedVar->getType ();
312- auto IsUncountedPtr = isUnsafePtr (CapturedVar->getType (), false );
342+ auto IsUncountedPtr = isUnsafePtr (CapturedVar->getType ());
313343 if (IsUncountedPtr && *IsUncountedPtr)
314344 reportBug (C, CapturedVar, CapturedVarQualType);
315345 } else if (C.capturesThis () && shouldCheckThis) {
316346 if (ignoreParamVarDecl) // this is always a parameter to this function.
317347 continue ;
318- reportBugOnThisPtr (C);
348+ reportBugOnThisPtr (C, T );
319349 }
320350 }
321351 }
@@ -324,6 +354,9 @@ class UncountedLambdaCapturesChecker
324354 const QualType T) const {
325355 assert (CapturedVar);
326356
357+ if (isa<ImplicitParamDecl>(CapturedVar) && !Capture.getLocation ().isValid ())
358+ return ; // Ignore implicit captruing of self.
359+
327360 SmallString<100 > Buf;
328361 llvm::raw_svector_ostream Os (Buf);
329362
@@ -332,22 +365,22 @@ class UncountedLambdaCapturesChecker
332365 } else {
333366 Os << " Implicitly captured " ;
334367 }
335- if (T-> isPointerType ( )) {
368+ if (isa<PointerType>(T) || isa<ObjCObjectPointerType>(T )) {
336369 Os << " raw-pointer " ;
337370 } else {
338- assert (T->isReferenceType ());
339371 Os << " reference " ;
340372 }
341373
342- printQuotedQualifiedName (Os, Capture. getCapturedVar () );
343- Os << " to ref-counted type or CheckedPtr-capable type is unsafe." ;
374+ printQuotedQualifiedName (Os, CapturedVar );
375+ Os << " to " << ptrKind (T) << " type is unsafe." ;
344376
345377 PathDiagnosticLocation BSLoc (Capture.getLocation (), BR->getSourceManager ());
346378 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str (), BSLoc);
347379 BR->emitReport (std::move (Report));
348380 }
349381
350- void reportBugOnThisPtr (const LambdaCapture &Capture) const {
382+ void reportBugOnThisPtr (const LambdaCapture &Capture,
383+ const QualType T) const {
351384 SmallString<100 > Buf;
352385 llvm::raw_svector_ostream Os (Buf);
353386
@@ -357,14 +390,54 @@ class UncountedLambdaCapturesChecker
357390 Os << " Implicitly captured " ;
358391 }
359392
360- Os << " raw-pointer 'this' to ref-counted type or CheckedPtr-capable type "
361- " is unsafe." ;
393+ Os << " raw-pointer 'this' to " << ptrKind (T) << " type is unsafe." ;
362394
363395 PathDiagnosticLocation BSLoc (Capture.getLocation (), BR->getSourceManager ());
364396 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str (), BSLoc);
365397 BR->emitReport (std::move (Report));
366398 }
367399};
400+
401+ class UncountedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
402+ public:
403+ UncountedLambdaCapturesChecker ()
404+ : RawPtrRefLambdaCapturesChecker(" Lambda capture of uncounted or "
405+ " unchecked variable" ) {}
406+
407+ std::optional<bool > isUnsafePtr (QualType QT) const final {
408+ auto result1 = isUncountedPtr (QT);
409+ auto result2 = isUncheckedPtr (QT);
410+ if (result1 && *result1)
411+ return true ;
412+ if (result2 && *result2)
413+ return true ;
414+ if (result1)
415+ return *result1;
416+ return result2;
417+ }
418+
419+ const char *ptrKind (QualType QT) const final {
420+ if (isUncounted (QT))
421+ return " uncounted" ;
422+ return " unchecked" ;
423+ }
424+ };
425+
426+ class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
427+ public:
428+ UnretainedLambdaCapturesChecker ()
429+ : RawPtrRefLambdaCapturesChecker(" Lambda capture of unretained "
430+ " variables" ) {
431+ RTC = RetainTypeChecker ();
432+ }
433+
434+ std::optional<bool > isUnsafePtr (QualType QT) const final {
435+ return RTC->isUnretained (QT);
436+ }
437+
438+ const char *ptrKind (QualType QT) const final { return " unretained" ; }
439+ };
440+
368441} // namespace
369442
370443void ento::registerUncountedLambdaCapturesChecker (CheckerManager &Mgr) {
@@ -375,3 +448,12 @@ bool ento::shouldRegisterUncountedLambdaCapturesChecker(
375448 const CheckerManager &mgr) {
376449 return true ;
377450}
451+
452+ void ento::registerUnretainedLambdaCapturesChecker (CheckerManager &Mgr) {
453+ Mgr.registerChecker <UnretainedLambdaCapturesChecker>();
454+ }
455+
456+ bool ento::shouldRegisterUnretainedLambdaCapturesChecker (
457+ const CheckerManager &mgr) {
458+ return true ;
459+ }
0 commit comments