1//=======- UncountedLambdaCapturesChecker.cpp --------------------*- C++ -*-==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "ASTUtils.h"
10#include "DiagOutputUtils.h"
11#include "PtrTypesSemantics.h"
12#include "clang/AST/DynamicRecursiveASTVisitor.h"
13#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
15#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
16#include "clang/StaticAnalyzer/Core/Checker.h"
17#include <optional>
18
19using namespace clang;
20using namespace ento;
21
22namespace {
23class RawPtrRefLambdaCapturesChecker
24 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
25private:
26 BugType Bug;
27 mutable BugReporter *BR = nullptr;
28 TrivialFunctionAnalysis TFA;
29
30protected:
31 mutable std::optional<RetainTypeChecker> RTC;
32
33public:
34 RawPtrRefLambdaCapturesChecker(const char *description)
35 : Bug(this, description, "WebKit coding guidelines") {}
36
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;
40
41 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
42 BugReporter &BRArg) const {
43 BR = &BRArg;
44
45 // The calls to checkAST* from AnalysisConsumer don't
46 // visit template instantiations or lambda classes. We
47 // want to visit those, so we make our own RecursiveASTVisitor.
48 struct LocalVisitor : DynamicRecursiveASTVisitor {
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;
56
57 QualType ClsType;
58
59 explicit LocalVisitor(const RawPtrRefLambdaCapturesChecker *Checker)
60 : Checker(Checker) {
61 assert(Checker);
62 ShouldVisitTemplateInstantiations = true;
63 ShouldVisitImplicitCode = false;
64 }
65
66 bool TraverseCXXMethodDecl(CXXMethodDecl *CXXMD) override {
67 llvm::SaveAndRestore SavedDecl(ClsType);
68 if (CXXMD->isInstance())
69 ClsType = CXXMD->getThisType();
70 return DynamicRecursiveASTVisitor::TraverseCXXMethodDecl(D: CXXMD);
71 }
72
73 bool TraverseObjCMethodDecl(ObjCMethodDecl *OCMD) override {
74 llvm::SaveAndRestore SavedDecl(ClsType);
75 if (OCMD && OCMD->isInstanceMethod()) {
76 if (auto *ImplParamDecl = OCMD->getSelfDecl())
77 ClsType = ImplParamDecl->getType();
78 }
79 return DynamicRecursiveASTVisitor::TraverseObjCMethodDecl(D: OCMD);
80 }
81
82 bool VisitTypedefDecl(TypedefDecl *TD) override {
83 if (Checker->RTC)
84 Checker->RTC->visitTypedef(TD);
85 return true;
86 }
87
88 bool shouldCheckThis() {
89 auto result =
90 !ClsType.isNull() ? Checker->isUnsafePtr(ClsType) : std::nullopt;
91 return result && *result;
92 }
93
94 bool VisitLambdaExpr(LambdaExpr *L) override {
95 if (LambdasToIgnore.contains(V: L))
96 return true;
97 Checker->visitLambdaExpr(L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L),
98 T: ClsType);
99 return true;
100 }
101
102 bool VisitVarDecl(VarDecl *VD) override {
103 auto *Init = VD->getInit();
104 if (!Init)
105 return true;
106 if (auto *L = dyn_cast_or_null<LambdaExpr>(Val: Init->IgnoreParenCasts())) {
107 LambdasToIgnore.insert(V: L); // Evaluate lambdas in VisitDeclRefExpr.
108 return true;
109 }
110 if (!VD->hasLocalStorage())
111 return true;
112 if (auto *E = dyn_cast<ExprWithCleanups>(Val: Init))
113 Init = E->getSubExpr();
114 if (auto *E = dyn_cast<CXXBindTemporaryExpr>(Val: Init))
115 Init = E->getSubExpr();
116 if (auto *CE = dyn_cast<CallExpr>(Val: Init)) {
117 if (auto *Callee = CE->getDirectCallee()) {
118 auto FnName = safeGetName(ASTNode: Callee);
119 unsigned ArgCnt = CE->getNumArgs();
120 if (FnName == "makeScopeExit" && ArgCnt == 1) {
121 auto *Arg = CE->getArg(Arg: 0);
122 if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Val: Arg))
123 Arg = E->getSubExpr();
124 if (auto *L = dyn_cast<LambdaExpr>(Val: Arg)) {
125 LambdaOwnerMap.insert(KV: std::make_pair(x&: VD, y&: L));
126 CallToIgnore.insert(V: CE);
127 LambdasToIgnore.insert(V: L);
128 }
129 } else if (FnName == "makeVisitor") {
130 for (unsigned ArgIndex = 0; ArgIndex < ArgCnt; ++ArgIndex) {
131 auto *Arg = CE->getArg(Arg: ArgIndex);
132 if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Val: Arg))
133 Arg = E->getSubExpr();
134 if (auto *L = dyn_cast<LambdaExpr>(Val: Arg)) {
135 LambdaOwnerMap.insert(KV: std::make_pair(x&: VD, y&: L));
136 CallToIgnore.insert(V: CE);
137 LambdasToIgnore.insert(V: L);
138 }
139 }
140 }
141 }
142 } else if (auto *CE = dyn_cast<CXXConstructExpr>(Val: Init)) {
143 if (auto *Ctor = CE->getConstructor()) {
144 if (auto *Cls = Ctor->getParent()) {
145 auto FnName = safeGetName(ASTNode: Cls);
146 unsigned ArgCnt = CE->getNumArgs();
147 if (FnName == "ScopeExit" && ArgCnt == 1) {
148 auto *Arg = CE->getArg(Arg: 0);
149 if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Val: Arg))
150 Arg = E->getSubExpr();
151 if (auto *L = dyn_cast<LambdaExpr>(Val: Arg)) {
152 LambdaOwnerMap.insert(KV: std::make_pair(x&: VD, y&: L));
153 ConstructToIgnore.insert(V: CE);
154 LambdasToIgnore.insert(V: L);
155 }
156 }
157 }
158 }
159 }
160 return true;
161 }
162
163 bool VisitDeclRefExpr(DeclRefExpr *DRE) override {
164 if (DeclRefExprsToIgnore.contains(V: DRE))
165 return true;
166 auto *VD = dyn_cast_or_null<VarDecl>(Val: DRE->getDecl());
167 if (!VD)
168 return true;
169 if (auto It = LambdaOwnerMap.find(Val: VD); It != LambdaOwnerMap.end()) {
170 auto *L = It->second;
171 Checker->visitLambdaExpr(L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L),
172 T: ClsType);
173 return true;
174 }
175 auto *Init = VD->getInit();
176 if (!Init)
177 return true;
178 auto *L = dyn_cast_or_null<LambdaExpr>(Val: Init->IgnoreParenCasts());
179 if (!L)
180 return true;
181 LambdasToIgnore.insert(V: L);
182 Checker->visitLambdaExpr(L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L),
183 T: ClsType);
184 return true;
185 }
186
187 bool shouldTreatAllArgAsNoEscape(FunctionDecl *FDecl) {
188 std::string PreviousName = safeGetName(ASTNode: FDecl);
189 for (auto *Decl = FDecl->getParent(); Decl; Decl = Decl->getParent()) {
190 if (!isa<NamespaceDecl>(Val: Decl) && !isa<CXXRecordDecl>(Val: Decl))
191 return false;
192 auto Name = safeGetName(ASTNode: Decl);
193 // WTF::switchOn(T, F... f) is a variadic template function and
194 // couldn't be annotated with NOESCAPE. We hard code it here to
195 // workaround that.
196 if (Name == "WTF" && PreviousName == "switchOn")
197 return true;
198 // Treat every argument of functions in std::ranges as noescape.
199 if (Name == "std" && PreviousName == "ranges")
200 return true;
201 PreviousName = Name;
202 }
203 return false;
204 }
205
206 bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {
207 if (ConstructToIgnore.contains(V: CE))
208 return true;
209 if (auto *Callee = CE->getConstructor()) {
210 unsigned ArgIndex = 0;
211 for (auto *Param : Callee->parameters()) {
212 if (ArgIndex >= CE->getNumArgs())
213 return true;
214 auto *Arg = CE->getArg(Arg: ArgIndex)->IgnoreParenCasts();
215 if (auto *L = findLambdaInArg(E: Arg)) {
216 LambdasToIgnore.insert(V: L);
217 if (!Param->hasAttr<NoEscapeAttr>())
218 Checker->visitLambdaExpr(
219 L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L), T: ClsType);
220 }
221 ++ArgIndex;
222 }
223 }
224 return true;
225 }
226
227 bool VisitCallExpr(CallExpr *CE) override {
228 if (CallToIgnore.contains(V: CE))
229 return true;
230 checkCalleeLambda(CE);
231 if (auto *Callee = CE->getDirectCallee()) {
232 if (isVisitFunction(CallExpr: CE, FnDecl: Callee))
233 return true;
234 checkParameters(CE, Callee);
235 } else if (auto *CalleeE = CE->getCallee()) {
236 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: CalleeE->IgnoreParenCasts())) {
237 if (auto *Callee = dyn_cast_or_null<FunctionDecl>(Val: DRE->getDecl()))
238 checkParameters(CE, Callee);
239 }
240 }
241 return true;
242 }
243
244 bool isVisitFunction(CallExpr *CallExpr, FunctionDecl *FnDecl) {
245 bool IsVisitFn = safeGetName(ASTNode: FnDecl) == "visit";
246 if (!IsVisitFn)
247 return false;
248 bool ArgCnt = CallExpr->getNumArgs();
249 if (!ArgCnt)
250 return false;
251 auto *Ns = FnDecl->getParent();
252 if (!Ns)
253 return false;
254 auto NsName = safeGetName(ASTNode: Ns);
255 if (NsName != "WTF" && NsName != "std")
256 return false;
257 auto *Arg = CallExpr->getArg(Arg: 0);
258 if (!Arg)
259 return false;
260 auto *DRE = dyn_cast<DeclRefExpr>(Val: Arg->IgnoreParenCasts());
261 if (!DRE)
262 return false;
263 auto *VD = dyn_cast<VarDecl>(Val: DRE->getDecl());
264 if (!VD)
265 return false;
266 if (!LambdaOwnerMap.contains(Val: VD))
267 return false;
268 DeclRefExprsToIgnore.insert(V: DRE);
269 return true;
270 }
271
272 void checkParameters(CallExpr *CE, FunctionDecl *Callee) {
273 unsigned ArgIndex = isa<CXXOperatorCallExpr>(Val: CE);
274 bool TreatAllArgsAsNoEscape = shouldTreatAllArgAsNoEscape(FDecl: Callee);
275 for (auto *Param : Callee->parameters()) {
276 if (ArgIndex >= CE->getNumArgs())
277 return;
278 auto *Arg = CE->getArg(Arg: ArgIndex)->IgnoreParenCasts();
279 if (auto *L = findLambdaInArg(E: Arg)) {
280 LambdasToIgnore.insert(V: L);
281 if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape)
282 Checker->visitLambdaExpr(
283 L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L), T: ClsType);
284 }
285 ++ArgIndex;
286 }
287 }
288
289 LambdaExpr *findLambdaInArg(Expr *E) {
290 if (auto *Lambda = dyn_cast_or_null<LambdaExpr>(Val: E))
291 return Lambda;
292 auto *TempExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(Val: E);
293 if (!TempExpr)
294 return nullptr;
295 E = TempExpr->getSubExpr()->IgnoreParenCasts();
296 if (!E)
297 return nullptr;
298 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: E))
299 return Lambda;
300 auto *CE = dyn_cast_or_null<CXXConstructExpr>(Val: E);
301 if (!CE || !CE->getNumArgs())
302 return nullptr;
303 auto *CtorArg = CE->getArg(Arg: 0)->IgnoreParenCasts();
304 if (!CtorArg)
305 return nullptr;
306 auto *InnerCE = dyn_cast_or_null<CXXConstructExpr>(Val: CtorArg);
307 if (InnerCE && InnerCE->getNumArgs())
308 CtorArg = InnerCE->getArg(Arg: 0)->IgnoreParenCasts();
309 auto updateIgnoreList = [&] {
310 ConstructToIgnore.insert(V: CE);
311 if (InnerCE)
312 ConstructToIgnore.insert(V: InnerCE);
313 };
314 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: CtorArg)) {
315 updateIgnoreList();
316 return Lambda;
317 }
318 if (auto *TempExpr = dyn_cast<CXXBindTemporaryExpr>(Val: CtorArg)) {
319 E = TempExpr->getSubExpr()->IgnoreParenCasts();
320 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: E)) {
321 updateIgnoreList();
322 return Lambda;
323 }
324 }
325 auto *DRE = dyn_cast<DeclRefExpr>(Val: CtorArg);
326 if (!DRE)
327 return nullptr;
328 auto *VD = dyn_cast_or_null<VarDecl>(Val: DRE->getDecl());
329 if (!VD)
330 return nullptr;
331 auto *Init = VD->getInit();
332 if (!Init)
333 return nullptr;
334 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: Init)) {
335 DeclRefExprsToIgnore.insert(V: DRE);
336 updateIgnoreList();
337 return Lambda;
338 }
339 return nullptr;
340 }
341
342 void checkCalleeLambda(CallExpr *CE) {
343 auto *Callee = CE->getCallee();
344 if (!Callee)
345 return;
346 Callee = Callee->IgnoreParenCasts();
347 if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Val: Callee)) {
348 Callee = MTE->getSubExpr();
349 if (!Callee)
350 return;
351 Callee = Callee->IgnoreParenCasts();
352 }
353 if (auto *L = dyn_cast<LambdaExpr>(Val: Callee)) {
354 LambdasToIgnore.insert(V: L); // Calling a lambda upon creation is safe.
355 return;
356 }
357 auto *DRE = dyn_cast<DeclRefExpr>(Val: Callee->IgnoreParenCasts());
358 if (!DRE)
359 return;
360 auto *MD = dyn_cast_or_null<CXXMethodDecl>(Val: DRE->getDecl());
361 if (!MD || CE->getNumArgs() < 1)
362 return;
363 auto *Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
364 if (auto *L = dyn_cast_or_null<LambdaExpr>(Val: Arg)) {
365 LambdasToIgnore.insert(V: L); // Calling a lambda upon creation is safe.
366 return;
367 }
368 auto *ArgRef = dyn_cast<DeclRefExpr>(Val: Arg);
369 if (!ArgRef)
370 return;
371 auto *VD = dyn_cast_or_null<VarDecl>(Val: ArgRef->getDecl());
372 if (!VD)
373 return;
374 auto *Init = VD->getInit();
375 if (!Init)
376 return;
377 auto *L = dyn_cast_or_null<LambdaExpr>(Val: Init->IgnoreParenCasts());
378 if (!L)
379 return;
380 DeclRefExprsToIgnore.insert(V: ArgRef);
381 LambdasToIgnore.insert(V: L);
382 }
383
384 bool hasProtectedThis(const LambdaExpr *L) {
385 for (const LambdaCapture &OtherCapture : L->captures()) {
386 if (!OtherCapture.capturesVariable())
387 continue;
388 if (auto *ValueDecl = OtherCapture.getCapturedVar()) {
389 if (declProtectsThis(ValueDecl)) {
390 ProtectedThisDecls.insert(V: ValueDecl);
391 return true;
392 }
393 }
394 }
395 return false;
396 }
397
398 bool declProtectsThis(const ValueDecl *ValueDecl) const {
399 auto *VD = dyn_cast<VarDecl>(Val: ValueDecl);
400 if (!VD)
401 return false;
402 auto *Init = VD->getInit();
403 if (!Init)
404 return false;
405 const Expr *Arg = Init->IgnoreParenCasts();
406 do {
407 if (auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Val: Arg))
408 Arg = BTE->getSubExpr()->IgnoreParenCasts();
409 if (auto *CE = dyn_cast<CXXConstructExpr>(Val: Arg)) {
410 auto *Ctor = CE->getConstructor();
411 if (!Ctor)
412 return false;
413 auto clsName = safeGetName(ASTNode: Ctor->getParent());
414 if (Checker->isPtrType(clsName) && CE->getNumArgs()) {
415 Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
416 continue;
417 }
418 if (auto *Type = ClsType.getTypePtrOrNull()) {
419 if (auto *CXXR = Type->getPointeeCXXRecordDecl()) {
420 if (CXXR == Ctor->getParent() && Ctor->isMoveConstructor() &&
421 CE->getNumArgs() == 1) {
422 Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
423 continue;
424 }
425 }
426 }
427 return false;
428 }
429 if (auto *CE = dyn_cast<CallExpr>(Val: Arg)) {
430 if (auto *Callee = CE->getDirectCallee()) {
431 if ((isStdOrWTFMove(F: Callee) || isCtorOfSafePtr(F: Callee)) &&
432 CE->getNumArgs() == 1) {
433 Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
434 continue;
435 }
436 }
437 }
438 if (auto *OpCE = dyn_cast<CXXOperatorCallExpr>(Val: Arg)) {
439 auto OpCode = OpCE->getOperator();
440 if (OpCode == OO_Star || OpCode == OO_Amp) {
441 auto *Callee = OpCE->getDirectCallee();
442 if (!Callee)
443 return false;
444 auto clsName = safeGetName(ASTNode: Callee->getParent());
445 if (!Checker->isPtrType(clsName) || !OpCE->getNumArgs())
446 return false;
447 Arg = OpCE->getArg(Arg: 0)->IgnoreParenCasts();
448 continue;
449 }
450 }
451 if (auto *UO = dyn_cast<UnaryOperator>(Val: Arg)) {
452 auto OpCode = UO->getOpcode();
453 if (OpCode == UO_Deref || OpCode == UO_AddrOf) {
454 Arg = UO->getSubExpr()->IgnoreParenCasts();
455 continue;
456 }
457 }
458 break;
459 } while (Arg);
460 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: Arg)) {
461 auto *Decl = DRE->getDecl();
462 if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(Val: Decl)) {
463 auto kind = ImplicitParam->getParameterKind();
464 return kind == ImplicitParamKind::ObjCSelf ||
465 kind == ImplicitParamKind::CXXThis;
466 }
467 return ProtectedThisDecls.contains(V: Decl);
468 }
469 return isa<CXXThisExpr>(Val: Arg);
470 }
471 };
472
473 LocalVisitor visitor(this);
474 if (RTC)
475 RTC->visitTranslationUnitDecl(TUD);
476 visitor.TraverseDecl(D: const_cast<TranslationUnitDecl *>(TUD));
477 }
478
479 void visitLambdaExpr(const LambdaExpr *L, bool shouldCheckThis,
480 const QualType T,
481 bool ignoreParamVarDecl = false) const {
482 if (TFA.isTrivial(S: L->getBody()))
483 return;
484 for (const LambdaCapture &C : L->captures()) {
485 if (C.capturesVariable()) {
486 ValueDecl *CapturedVar = C.getCapturedVar();
487 if (ignoreParamVarDecl && isa<ParmVarDecl>(Val: CapturedVar))
488 continue;
489 if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(Val: CapturedVar)) {
490 auto kind = ImplicitParam->getParameterKind();
491 if ((kind == ImplicitParamKind::ObjCSelf ||
492 kind == ImplicitParamKind::CXXThis) &&
493 !shouldCheckThis)
494 continue;
495 }
496 QualType CapturedVarQualType = CapturedVar->getType();
497 auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType());
498 if (C.getCaptureKind() == LCK_ByCopy &&
499 CapturedVarQualType->isReferenceType())
500 continue;
501 if (IsUncountedPtr && *IsUncountedPtr)
502 reportBug(Capture: C, CapturedVar, T: CapturedVarQualType, L);
503 } else if (C.capturesThis() && shouldCheckThis) {
504 if (ignoreParamVarDecl) // this is always a parameter to this function.
505 continue;
506 reportBugOnThisPtr(Capture: C, T);
507 }
508 }
509 }
510
511 void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar,
512 const QualType T, const LambdaExpr *L) const {
513 assert(CapturedVar);
514
515 auto Location = Capture.getLocation();
516 if (isa<ImplicitParamDecl>(Val: CapturedVar) && !Location.isValid())
517 Location = L->getBeginLoc();
518
519 SmallString<100> Buf;
520 llvm::raw_svector_ostream Os(Buf);
521
522 if (Capture.isExplicit()) {
523 Os << "Captured ";
524 } else {
525 Os << "Implicitly captured ";
526 }
527 if (isa<PointerType>(Val: T) || isa<ObjCObjectPointerType>(Val: T)) {
528 Os << "raw-pointer ";
529 } else {
530 Os << "reference ";
531 }
532
533 printQuotedQualifiedName(Os, D: CapturedVar);
534 Os << " to " << ptrKind(QT: T) << " type is unsafe.";
535
536 PathDiagnosticLocation BSLoc(Location, BR->getSourceManager());
537 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
538 BR->emitReport(R: std::move(Report));
539 }
540
541 void reportBugOnThisPtr(const LambdaCapture &Capture,
542 const QualType T) const {
543 SmallString<100> Buf;
544 llvm::raw_svector_ostream Os(Buf);
545
546 if (Capture.isExplicit()) {
547 Os << "Captured ";
548 } else {
549 Os << "Implicitly captured ";
550 }
551
552 Os << "raw-pointer 'this' to " << ptrKind(QT: T) << " type is unsafe.";
553
554 PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager());
555 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
556 BR->emitReport(R: std::move(Report));
557 }
558};
559
560class UncountedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
561public:
562 UncountedLambdaCapturesChecker()
563 : RawPtrRefLambdaCapturesChecker("Lambda capture of uncounted or "
564 "unchecked variable") {}
565
566 std::optional<bool> isUnsafePtr(QualType QT) const final {
567 auto result1 = isUncountedPtr(T: QT);
568 auto result2 = isUncheckedPtr(T: QT);
569 if (result1 && *result1)
570 return true;
571 if (result2 && *result2)
572 return true;
573 if (result1)
574 return *result1;
575 return result2;
576 }
577
578 virtual bool isPtrType(const std::string &Name) const final {
579 return isRefType(Name) || isCheckedPtr(Name);
580 }
581
582 const char *ptrKind(QualType QT) const final {
583 if (isUncounted(T: QT))
584 return "uncounted";
585 return "unchecked";
586 }
587};
588
589class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
590public:
591 UnretainedLambdaCapturesChecker()
592 : RawPtrRefLambdaCapturesChecker("Lambda capture of unretained "
593 "variables") {
594 RTC = RetainTypeChecker();
595 }
596
597 std::optional<bool> isUnsafePtr(QualType QT) const final {
598 if (QT.hasStrongOrWeakObjCLifetime())
599 return false;
600 return RTC->isUnretained(QT);
601 }
602
603 virtual bool isPtrType(const std::string &Name) const final {
604 return isRetainPtrOrOSPtr(Name);
605 }
606
607 const char *ptrKind(QualType QT) const final { return "unretained"; }
608};
609
610} // namespace
611
612void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) {
613 Mgr.registerChecker<UncountedLambdaCapturesChecker>();
614}
615
616bool ento::shouldRegisterUncountedLambdaCapturesChecker(
617 const CheckerManager &mgr) {
618 return true;
619}
620
621void ento::registerUnretainedLambdaCapturesChecker(CheckerManager &Mgr) {
622 Mgr.registerChecker<UnretainedLambdaCapturesChecker>();
623}
624
625bool ento::shouldRegisterUnretainedLambdaCapturesChecker(
626 const CheckerManager &mgr) {
627 return true;
628}
629