@@ -21,15 +21,23 @@ using namespace clang;
21
21
using namespace ento ;
22
22
23
23
namespace {
24
- class UncountedLambdaCapturesChecker
24
+ class RawPtrRefLambdaCapturesChecker
25
25
: public Checker<check::ASTDecl<TranslationUnitDecl>> {
26
26
private:
27
- BugType Bug{this , " Lambda capture of uncounted variable" ,
28
- " WebKit coding guidelines" };
27
+ BugType Bug;
29
28
mutable BugReporter *BR = nullptr ;
30
29
TrivialFunctionAnalysis TFA;
31
30
31
+ protected:
32
+ mutable std::optional<RetainTypeChecker> RTC;
33
+
32
34
public:
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
+
33
41
void checkASTDecl (const TranslationUnitDecl *TUD, AnalysisManager &MGR,
34
42
BugReporter &BRArg) const {
35
43
BR = &BRArg;
@@ -38,15 +46,15 @@ class UncountedLambdaCapturesChecker
38
46
// visit template instantiations or lambda classes. We
39
47
// want to visit those, so we make our own RecursiveASTVisitor.
40
48
struct LocalVisitor : DynamicRecursiveASTVisitor {
41
- const UncountedLambdaCapturesChecker *Checker;
49
+ const RawPtrRefLambdaCapturesChecker *Checker;
42
50
llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore;
43
51
llvm::DenseSet<const LambdaExpr *> LambdasToIgnore;
44
52
llvm::DenseSet<const ValueDecl *> ProtectedThisDecls;
45
53
llvm::DenseSet<const CXXConstructExpr *> ConstructToIgnore;
46
54
47
55
QualType ClsType;
48
56
49
- explicit LocalVisitor (const UncountedLambdaCapturesChecker *Checker)
57
+ explicit LocalVisitor (const RawPtrRefLambdaCapturesChecker *Checker)
50
58
: Checker(Checker) {
51
59
assert (Checker);
52
60
ShouldVisitTemplateInstantiations = true ;
@@ -60,16 +68,23 @@ class UncountedLambdaCapturesChecker
60
68
return DynamicRecursiveASTVisitor::TraverseCXXMethodDecl (CXXMD);
61
69
}
62
70
71
+ bool VisitTypedefDecl (TypedefDecl *TD) override {
72
+ if (Checker->RTC )
73
+ Checker->RTC ->visitTypedef (TD);
74
+ return true ;
75
+ }
76
+
63
77
bool shouldCheckThis () {
64
78
auto result =
65
- !ClsType.isNull () ? isUnsafePtr (ClsType, false ) : std::nullopt;
79
+ !ClsType.isNull () ? Checker-> isUnsafePtr (ClsType) : std::nullopt;
66
80
return result && *result;
67
81
}
68
82
69
83
bool VisitLambdaExpr (LambdaExpr *L) override {
70
84
if (LambdasToIgnore.contains (L))
71
85
return true ;
72
- Checker->visitLambdaExpr (L, shouldCheckThis () && !hasProtectedThis (L));
86
+ Checker->visitLambdaExpr (L, shouldCheckThis () && !hasProtectedThis (L),
87
+ ClsType);
73
88
return true ;
74
89
}
75
90
@@ -97,7 +112,8 @@ class UncountedLambdaCapturesChecker
97
112
if (!L)
98
113
return true ;
99
114
LambdasToIgnore.insert (L);
100
- Checker->visitLambdaExpr (L, shouldCheckThis () && !hasProtectedThis (L));
115
+ Checker->visitLambdaExpr (L, shouldCheckThis () && !hasProtectedThis (L),
116
+ ClsType);
101
117
return true ;
102
118
}
103
119
@@ -122,8 +138,8 @@ class UncountedLambdaCapturesChecker
122
138
if (auto *L = findLambdaInArg (Arg)) {
123
139
LambdasToIgnore.insert (L);
124
140
if (!Param->hasAttr <NoEscapeAttr>())
125
- Checker->visitLambdaExpr (L, shouldCheckThis () &&
126
- !hasProtectedThis (L));
141
+ Checker->visitLambdaExpr (
142
+ L, shouldCheckThis () && !hasProtectedThis (L), ClsType );
127
143
}
128
144
++ArgIndex;
129
145
}
@@ -143,8 +159,8 @@ class UncountedLambdaCapturesChecker
143
159
if (auto *L = findLambdaInArg (Arg)) {
144
160
LambdasToIgnore.insert (L);
145
161
if (!Param->hasAttr <NoEscapeAttr>() && !TreatAllArgsAsNoEscape)
146
- Checker->visitLambdaExpr (L, shouldCheckThis () &&
147
- !hasProtectedThis (L));
162
+ Checker->visitLambdaExpr (
163
+ L, shouldCheckThis () && !hasProtectedThis (L), ClsType );
148
164
}
149
165
++ArgIndex;
150
166
}
@@ -169,14 +185,22 @@ class UncountedLambdaCapturesChecker
169
185
auto *CtorArg = CE->getArg (0 )->IgnoreParenCasts ();
170
186
if (!CtorArg)
171
187
return nullptr ;
172
- if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg)) {
188
+ auto *InnerCE = dyn_cast_or_null<CXXConstructExpr>(CtorArg);
189
+ if (InnerCE && InnerCE->getNumArgs ())
190
+ CtorArg = InnerCE->getArg (0 )->IgnoreParenCasts ();
191
+ auto updateIgnoreList = [&] {
173
192
ConstructToIgnore.insert (CE);
193
+ if (InnerCE)
194
+ ConstructToIgnore.insert (InnerCE);
195
+ };
196
+ if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg)) {
197
+ updateIgnoreList ();
174
198
return Lambda;
175
199
}
176
200
if (auto *TempExpr = dyn_cast<CXXBindTemporaryExpr>(CtorArg)) {
177
201
E = TempExpr->getSubExpr ()->IgnoreParenCasts ();
178
202
if (auto *Lambda = dyn_cast<LambdaExpr>(E)) {
179
- ConstructToIgnore. insert (CE );
203
+ updateIgnoreList ( );
180
204
return Lambda;
181
205
}
182
206
}
@@ -189,10 +213,14 @@ class UncountedLambdaCapturesChecker
189
213
auto *Init = VD->getInit ();
190
214
if (!Init)
191
215
return nullptr ;
216
+ if (auto *Lambda = dyn_cast<LambdaExpr>(Init)) {
217
+ updateIgnoreList ();
218
+ return Lambda;
219
+ }
192
220
TempExpr = dyn_cast<CXXBindTemporaryExpr>(Init->IgnoreParenCasts ());
193
221
if (!TempExpr)
194
222
return nullptr ;
195
- ConstructToIgnore. insert (CE );
223
+ updateIgnoreList ( );
196
224
return dyn_cast_or_null<LambdaExpr>(TempExpr->getSubExpr ());
197
225
}
198
226
@@ -226,7 +254,7 @@ class UncountedLambdaCapturesChecker
226
254
DeclRefExprsToIgnore.insert (ArgRef);
227
255
LambdasToIgnore.insert (L);
228
256
Checker->visitLambdaExpr (L, shouldCheckThis () && !hasProtectedThis (L),
229
- /* ignoreParamVarDecl */ true );
257
+ ClsType, /* ignoreParamVarDecl */ true );
230
258
}
231
259
232
260
bool hasProtectedThis (LambdaExpr *L) {
@@ -293,10 +321,12 @@ class UncountedLambdaCapturesChecker
293
321
};
294
322
295
323
LocalVisitor visitor (this );
324
+ if (RTC)
325
+ RTC->visitTranslationUnitDecl (TUD);
296
326
visitor.TraverseDecl (const_cast <TranslationUnitDecl *>(TUD));
297
327
}
298
328
299
- void visitLambdaExpr (LambdaExpr *L, bool shouldCheckThis,
329
+ void visitLambdaExpr (LambdaExpr *L, bool shouldCheckThis, const QualType T,
300
330
bool ignoreParamVarDecl = false ) const {
301
331
if (TFA.isTrivial (L->getBody ()))
302
332
return ;
@@ -306,13 +336,13 @@ class UncountedLambdaCapturesChecker
306
336
if (ignoreParamVarDecl && isa<ParmVarDecl>(CapturedVar))
307
337
continue ;
308
338
QualType CapturedVarQualType = CapturedVar->getType ();
309
- auto IsUncountedPtr = isUnsafePtr (CapturedVar->getType (), false );
339
+ auto IsUncountedPtr = isUnsafePtr (CapturedVar->getType ());
310
340
if (IsUncountedPtr && *IsUncountedPtr)
311
341
reportBug (C, CapturedVar, CapturedVarQualType);
312
342
} else if (C.capturesThis () && shouldCheckThis) {
313
343
if (ignoreParamVarDecl) // this is always a parameter to this function.
314
344
continue ;
315
- reportBugOnThisPtr (C);
345
+ reportBugOnThisPtr (C, T );
316
346
}
317
347
}
318
348
}
@@ -321,6 +351,9 @@ class UncountedLambdaCapturesChecker
321
351
const QualType T) const {
322
352
assert (CapturedVar);
323
353
354
+ if (isa<ImplicitParamDecl>(CapturedVar) && !Capture.getLocation ().isValid ())
355
+ return ; // Ignore implicit captruing of self.
356
+
324
357
SmallString<100 > Buf;
325
358
llvm::raw_svector_ostream Os (Buf);
326
359
@@ -329,22 +362,22 @@ class UncountedLambdaCapturesChecker
329
362
} else {
330
363
Os << " Implicitly captured " ;
331
364
}
332
- if (T-> isPointerType ( )) {
365
+ if (isa<PointerType>(T) || isa<ObjCObjectPointerType>(T )) {
333
366
Os << " raw-pointer " ;
334
367
} else {
335
- assert (T->isReferenceType ());
336
368
Os << " reference " ;
337
369
}
338
370
339
- printQuotedQualifiedName (Os, Capture. getCapturedVar () );
340
- Os << " to ref-counted type or CheckedPtr-capable type is unsafe." ;
371
+ printQuotedQualifiedName (Os, CapturedVar );
372
+ Os << " to " << ptrKind (T) << " type is unsafe." ;
341
373
342
374
PathDiagnosticLocation BSLoc (Capture.getLocation (), BR->getSourceManager ());
343
375
auto Report = std::make_unique<BasicBugReport>(Bug, Os.str (), BSLoc);
344
376
BR->emitReport (std::move (Report));
345
377
}
346
378
347
- void reportBugOnThisPtr (const LambdaCapture &Capture) const {
379
+ void reportBugOnThisPtr (const LambdaCapture &Capture,
380
+ const QualType T) const {
348
381
SmallString<100 > Buf;
349
382
llvm::raw_svector_ostream Os (Buf);
350
383
@@ -354,14 +387,54 @@ class UncountedLambdaCapturesChecker
354
387
Os << " Implicitly captured " ;
355
388
}
356
389
357
- Os << " raw-pointer 'this' to ref-counted type or CheckedPtr-capable type "
358
- " is unsafe." ;
390
+ Os << " raw-pointer 'this' to " << ptrKind (T) << " type is unsafe." ;
359
391
360
392
PathDiagnosticLocation BSLoc (Capture.getLocation (), BR->getSourceManager ());
361
393
auto Report = std::make_unique<BasicBugReport>(Bug, Os.str (), BSLoc);
362
394
BR->emitReport (std::move (Report));
363
395
}
364
396
};
397
+
398
+ class UncountedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
399
+ public:
400
+ UncountedLambdaCapturesChecker ()
401
+ : RawPtrRefLambdaCapturesChecker(" Lambda capture of uncounted or "
402
+ " unchecked variable" ) {}
403
+
404
+ std::optional<bool > isUnsafePtr (QualType QT) const final {
405
+ auto result1 = isUncountedPtr (QT);
406
+ auto result2 = isUncheckedPtr (QT);
407
+ if (result1 && *result1)
408
+ return true ;
409
+ if (result2 && *result2)
410
+ return true ;
411
+ if (result1)
412
+ return *result1;
413
+ return result2;
414
+ }
415
+
416
+ const char *ptrKind (QualType QT) const final {
417
+ if (isUncounted (QT))
418
+ return " uncounted" ;
419
+ return " unchecked" ;
420
+ }
421
+ };
422
+
423
+ class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
424
+ public:
425
+ UnretainedLambdaCapturesChecker ()
426
+ : RawPtrRefLambdaCapturesChecker(" Lambda capture of unretained "
427
+ " variables" ) {
428
+ RTC = RetainTypeChecker ();
429
+ }
430
+
431
+ std::optional<bool > isUnsafePtr (QualType QT) const final {
432
+ return RTC->isUnretained (QT);
433
+ }
434
+
435
+ const char *ptrKind (QualType QT) const final { return " unretained" ; }
436
+ };
437
+
365
438
} // namespace
366
439
367
440
void ento::registerUncountedLambdaCapturesChecker (CheckerManager &Mgr) {
@@ -372,3 +445,12 @@ bool ento::shouldRegisterUncountedLambdaCapturesChecker(
372
445
const CheckerManager &mgr) {
373
446
return true ;
374
447
}
448
+
449
+ void ento::registerUnretainedLambdaCapturesChecker (CheckerManager &Mgr) {
450
+ Mgr.registerChecker <UnretainedLambdaCapturesChecker>();
451
+ }
452
+
453
+ bool ento::shouldRegisterUnretainedLambdaCapturesChecker (
454
+ const CheckerManager &mgr) {
455
+ return true ;
456
+ }
0 commit comments