1//=======- RawPtrRefLambdaCapturesChecker.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 "RawPtrRefSafetyModel.h"
13#include "clang/AST/DynamicRecursiveASTVisitor.h"
14#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
16#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17#include "clang/StaticAnalyzer/Core/Checker.h"
18#include <optional>
19
20using namespace clang;
21using namespace ento;
22
23namespace {
24class RawPtrRefLambdaCapturesChecker
25 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
26private:
27 BugType Bug;
28 mutable BugReporter *BR = nullptr;
29 TrivialFunctionAnalysis TFA;
30
31protected:
32 const std::unique_ptr<PtrRefSafetyModel> Model;
33
34public:
35 RawPtrRefLambdaCapturesChecker(const char *description,
36 std::unique_ptr<PtrRefSafetyModel> Model)
37 : Bug(this, description, "WebKit coding guidelines"),
38 Model(std::move(Model)) {}
39
40 std::optional<bool> isUnsafePtr(QualType QT) const {
41 return isUnsafePtrForStorage(Model: *Model, T: QT);
42 }
43 bool isPtrType(const std::string &Name) const {
44 return Model->isPtrType(Name);
45 }
46
47 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
48 BugReporter &BRArg) const {
49 BR = &BRArg;
50
51 // The calls to checkAST* from AnalysisConsumer don't
52 // visit template instantiations or lambda classes. We
53 // want to visit those, so we make our own RecursiveASTVisitor.
54 struct LocalVisitor : DynamicRecursiveASTVisitor {
55 const RawPtrRefLambdaCapturesChecker *Checker;
56 llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore;
57 llvm::DenseSet<const LambdaExpr *> LambdasToIgnore;
58 llvm::DenseSet<const ValueDecl *> ProtectedThisDecls;
59 llvm::DenseSet<const CallExpr *> CallToIgnore;
60 llvm::DenseSet<const CXXConstructExpr *> ConstructToIgnore;
61 llvm::DenseMap<const VarDecl *, SmallVector<const LambdaExpr *>>
62 LambdaOwnerMap;
63
64 QualType ClsType;
65
66 explicit LocalVisitor(const RawPtrRefLambdaCapturesChecker *Checker)
67 : Checker(Checker) {
68 assert(Checker);
69 ShouldVisitTemplateInstantiations = true;
70 ShouldVisitImplicitCode = false;
71 }
72
73 bool TraverseCXXConstructorDecl(CXXConstructorDecl *Ctor) override {
74 llvm::SaveAndRestore SavedDecl(ClsType);
75 ClsType = Ctor->getThisType();
76 return DynamicRecursiveASTVisitor::TraverseCXXConstructorDecl(D: Ctor);
77 }
78
79 bool TraverseCXXDestructorDecl(CXXDestructorDecl *Dtor) override {
80 llvm::SaveAndRestore SavedDecl(ClsType);
81 ClsType = Dtor->getThisType();
82 return DynamicRecursiveASTVisitor::TraverseCXXDestructorDecl(D: Dtor);
83 }
84
85 bool TraverseCXXMethodDecl(CXXMethodDecl *CXXMD) override {
86 llvm::SaveAndRestore SavedDecl(ClsType);
87 if (CXXMD->isInstance())
88 ClsType = CXXMD->getThisType();
89 return DynamicRecursiveASTVisitor::TraverseCXXMethodDecl(D: CXXMD);
90 }
91
92 bool TraverseObjCMethodDecl(ObjCMethodDecl *OCMD) override {
93 llvm::SaveAndRestore SavedDecl(ClsType);
94 if (OCMD && OCMD->isInstanceMethod()) {
95 if (auto *ImplParamDecl = OCMD->getSelfDecl())
96 ClsType = ImplParamDecl->getType();
97 }
98 return DynamicRecursiveASTVisitor::TraverseObjCMethodDecl(D: OCMD);
99 }
100
101 bool VisitTypedefDecl(TypedefDecl *TD) override {
102 if (auto *RTC = Checker->Model->retainTypeChecker())
103 RTC->visitTypedef(TD);
104 return true;
105 }
106
107 bool shouldCheckThis() {
108 auto result =
109 !ClsType.isNull() ? Checker->isUnsafePtr(QT: ClsType) : std::nullopt;
110 return result && *result;
111 }
112
113 bool VisitLambdaExpr(LambdaExpr *L) override {
114 if (LambdasToIgnore.contains(V: L))
115 return true;
116 Checker->visitLambdaExpr(L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L),
117 T: ClsType);
118 return true;
119 }
120
121 bool TraverseLambdaExpr(LambdaExpr *L) override {
122 if (!DynamicRecursiveASTVisitor::TraverseLambdaExpr(S: L))
123 return false;
124 // The body of a generic lambda is a template pattern in which calls may
125 // not have been resolved yet, so traverse the instantiations of its
126 // call operator as well. Only the body is traversed so that the lambda
127 // stays associated with the class enclosing it, like the pattern is.
128 if (auto *FTD = L->getLambdaClass()->getDependentLambdaCallOperator()) {
129 for (auto *Spec : FTD->specializations()) {
130 if (auto *Body = Spec->getBody()) {
131 if (!TraverseStmt(S: Body))
132 return false;
133 }
134 }
135 }
136 return true;
137 }
138
139 bool VisitVarDecl(VarDecl *VD) override {
140 auto *Init = VD->getInit();
141 if (!Init)
142 return true;
143 if (auto *L = dyn_cast_or_null<LambdaExpr>(Val: Init->IgnoreParenCasts())) {
144 LambdasToIgnore.insert(V: L); // Evaluate lambdas in VisitDeclRefExpr.
145 return true;
146 }
147 if (!VD->hasLocalStorage())
148 return true;
149 if (auto *E = dyn_cast<ExprWithCleanups>(Val: Init))
150 Init = E->getSubExpr();
151 if (auto *E = dyn_cast<CXXBindTemporaryExpr>(Val: Init))
152 Init = E->getSubExpr();
153 if (auto *CE = dyn_cast<CallExpr>(Val: Init)) {
154 if (auto *Callee = CE->getDirectCallee()) {
155 auto FnName = safeGetName(ASTNode: Callee);
156 unsigned ArgCnt = CE->getNumArgs();
157 if (FnName == "makeScopeExit" && ArgCnt == 1) {
158 auto *Arg = CE->getArg(Arg: 0);
159 if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Val: Arg))
160 Arg = E->getSubExpr();
161 if (auto *L = dyn_cast<LambdaExpr>(Val: Arg))
162 addLambdaOwner(VD, CE, L);
163 } else if (FnName == "makeVisitor") {
164 for (unsigned ArgIndex = 0; ArgIndex < ArgCnt; ++ArgIndex) {
165 auto *Arg = CE->getArg(Arg: ArgIndex);
166 if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Val: Arg))
167 Arg = E->getSubExpr();
168 if (auto *L = dyn_cast<LambdaExpr>(Val: Arg))
169 addLambdaOwner(VD, CE, L);
170 }
171 }
172 }
173 } else if (auto *CE = dyn_cast<CXXConstructExpr>(Val: Init)) {
174 if (auto *Ctor = CE->getConstructor()) {
175 if (auto *Cls = Ctor->getParent()) {
176 auto FnName = safeGetName(ASTNode: Cls);
177 unsigned ArgCnt = CE->getNumArgs();
178 if (FnName == "ScopeExit" && ArgCnt == 1) {
179 auto *Arg = CE->getArg(Arg: 0);
180 if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Val: Arg))
181 Arg = E->getSubExpr();
182 if (auto *L = dyn_cast<LambdaExpr>(Val: Arg))
183 addLambdaOwner(VD, CE, L);
184 }
185 }
186 }
187 }
188 return true;
189 }
190
191 void addLambdaOwner(VarDecl *VD, CallExpr *CE, LambdaExpr *L) {
192 auto result = LambdaOwnerMap.insert(
193 KV: std::make_pair(x&: VD, y: SmallVector<const LambdaExpr *>{L}));
194 if (!result.second)
195 result.first->second.push_back(Elt: L);
196 CallToIgnore.insert(V: CE);
197 LambdasToIgnore.insert(V: L);
198 }
199
200 void addLambdaOwner(VarDecl *VD, CXXConstructExpr *CE, LambdaExpr *L) {
201 auto result = LambdaOwnerMap.insert(
202 KV: std::make_pair(x&: VD, y: SmallVector<const LambdaExpr *>{L}));
203 if (!result.second)
204 result.first->second.push_back(Elt: L);
205 ConstructToIgnore.insert(V: CE);
206 LambdasToIgnore.insert(V: L);
207 }
208
209 bool VisitDeclRefExpr(DeclRefExpr *DRE) override {
210 if (DeclRefExprsToIgnore.contains(V: DRE))
211 return true;
212 auto *VD = dyn_cast_or_null<VarDecl>(Val: DRE->getDecl());
213 if (!VD)
214 return true;
215 if (auto It = LambdaOwnerMap.find(Val: VD); It != LambdaOwnerMap.end()) {
216 for (auto *L : It->second) {
217 Checker->visitLambdaExpr(
218 L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L), T: ClsType);
219 }
220 return true;
221 }
222 auto *Init = VD->getInit();
223 if (!Init)
224 return true;
225 auto *L = dyn_cast_or_null<LambdaExpr>(Val: Init->IgnoreParenCasts());
226 if (!L)
227 return true;
228 LambdasToIgnore.insert(V: L);
229 Checker->visitLambdaExpr(L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L),
230 T: ClsType);
231 return true;
232 }
233
234 bool shouldTreatAllArgAsNoEscape(FunctionDecl *FDecl) {
235 std::string PreviousName = safeGetName(ASTNode: FDecl);
236 for (auto *Decl = FDecl->getParent(); Decl; Decl = Decl->getParent()) {
237 if (!isa<NamespaceDecl>(Val: Decl) && !isa<CXXRecordDecl>(Val: Decl))
238 return false;
239 if (auto *NS = dyn_cast<NamespaceDecl>(Val: Decl); NS && NS->isInline())
240 continue;
241 auto Name = safeGetName(ASTNode: Decl);
242 // WTF::switchOn(T, F... f) is a variadic template function and
243 // couldn't be annotated with NOESCAPE. We hard code it here to
244 // workaround that.
245 if (Name == "WTF" && PreviousName == "switchOn")
246 return true;
247 // Treat every argument of functions in std::ranges as noescape.
248 if (Name == "std" && PreviousName == "ranges")
249 return true;
250 PreviousName = Name;
251 }
252 return false;
253 }
254
255 bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {
256 if (ConstructToIgnore.contains(V: CE))
257 return true;
258 if (auto *Callee = CE->getConstructor()) {
259 unsigned ArgIndex = 0;
260 for (auto *Param : Callee->parameters()) {
261 if (ArgIndex >= CE->getNumArgs())
262 return true;
263 auto *Arg = CE->getArg(Arg: ArgIndex)->IgnoreParenCasts();
264 if (auto *L = findLambdaInArg(E: Arg)) {
265 LambdasToIgnore.insert(V: L);
266 if (!Param->hasAttr<NoEscapeAttr>())
267 Checker->visitLambdaExpr(
268 L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L), T: ClsType);
269 }
270 ++ArgIndex;
271 }
272 }
273 return true;
274 }
275
276 bool VisitCallExpr(CallExpr *CE) override {
277 if (CallToIgnore.contains(V: CE))
278 return true;
279 checkCalleeLambda(CE);
280 if (auto *Callee = CE->getDirectCallee()) {
281 if (isVisitFunction(CallExpr: CE, FnDecl: Callee))
282 return true;
283 checkParameters(CE, Callee);
284 return true;
285 }
286 auto *CalleeE = CE->getCallee();
287 if (!CalleeE)
288 return true;
289 CalleeE = CalleeE->IgnoreParenCasts();
290 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: CalleeE)) {
291 if (auto *Callee = dyn_cast_or_null<FunctionDecl>(Val: DRE->getDecl()))
292 checkParameters(CE, Callee);
293 return true;
294 }
295 // The callee of a call in an uninstantiated template may not have been
296 // resolved yet, in which case whether each lambda argument can escape
297 // isn't known. Wait for the instantiation to check those lambdas.
298 if (isa<OverloadExpr, CXXDependentScopeMemberExpr,
299 DependentScopeDeclRefExpr>(Val: CalleeE))
300 ignoreLambdasInArgs(Args: {CE->getArgs(), CE->getNumArgs()});
301 return true;
302 }
303
304 // Lambdas passed to a constructor which isn't resolved until the
305 // enclosing template is instantiated are checked in the instantiation.
306 bool
307 VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr *CE) override {
308 ignoreLambdasInArgs(Args: {CE->arg_begin(), CE->arg_end()});
309 return true;
310 }
311
312 bool VisitParenListExpr(ParenListExpr *PLE) override {
313 if (PLE->isTypeDependent())
314 ignoreLambdasInArgs(Args: PLE->exprs());
315 return true;
316 }
317
318 bool VisitInitListExpr(InitListExpr *ILE) override {
319 if (ILE->isTypeDependent())
320 ignoreLambdasInArgs(Args: ILE->inits());
321 return true;
322 }
323
324 void ignoreLambdasInArgs(ArrayRef<Expr *> Args) {
325 for (auto *Arg : Args) {
326 if (!Arg)
327 continue;
328 if (auto *L = findLambdaInArg(E: Arg->IgnoreParenCasts()))
329 LambdasToIgnore.insert(V: L);
330 }
331 }
332
333 bool isVisitFunction(CallExpr *CallExpr, FunctionDecl *FnDecl) {
334 bool IsVisitFn = safeGetName(ASTNode: FnDecl) == "visit";
335 if (!IsVisitFn)
336 return false;
337 bool ArgCnt = CallExpr->getNumArgs();
338 if (!ArgCnt)
339 return false;
340 auto *Ns = FnDecl->getParent();
341 if (!Ns)
342 return false;
343 auto NsName = safeGetName(ASTNode: Ns);
344 if (NsName != "WTF" && NsName != "std")
345 return false;
346 auto *Arg = CallExpr->getArg(Arg: 0);
347 if (!Arg)
348 return false;
349 auto *DRE = dyn_cast<DeclRefExpr>(Val: Arg->IgnoreParenCasts());
350 if (!DRE)
351 return false;
352 auto *VD = dyn_cast<VarDecl>(Val: DRE->getDecl());
353 if (!VD)
354 return false;
355 if (!LambdaOwnerMap.contains(Val: VD))
356 return false;
357 DeclRefExprsToIgnore.insert(V: DRE);
358 return true;
359 }
360
361 void checkParameters(CallExpr *CE, FunctionDecl *Callee) {
362 unsigned ArgIndex = isa<CXXOperatorCallExpr>(Val: CE);
363 bool TreatAllArgsAsNoEscape = shouldTreatAllArgAsNoEscape(FDecl: Callee);
364 for (auto *Param : Callee->parameters()) {
365 if (ArgIndex >= CE->getNumArgs())
366 return;
367 auto *Arg = CE->getArg(Arg: ArgIndex)->IgnoreParenCasts();
368 if (auto *L = findLambdaInArg(E: Arg)) {
369 LambdasToIgnore.insert(V: L);
370 if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape)
371 Checker->visitLambdaExpr(
372 L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L), T: ClsType);
373 }
374 ++ArgIndex;
375 }
376 }
377
378 LambdaExpr *findLambdaInArg(Expr *E) {
379 if (auto *Lambda = dyn_cast_or_null<LambdaExpr>(Val: E))
380 return Lambda;
381 auto *TempExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(Val: E);
382 if (!TempExpr)
383 return nullptr;
384 E = TempExpr->getSubExpr()->IgnoreParenCasts();
385 if (!E)
386 return nullptr;
387 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: E))
388 return Lambda;
389 auto *CE = dyn_cast_or_null<CXXConstructExpr>(Val: E);
390 if (!CE || !CE->getNumArgs())
391 return nullptr;
392 auto *CtorArg = CE->getArg(Arg: 0)->IgnoreParenCasts();
393 if (!CtorArg)
394 return nullptr;
395 auto *InnerCE = dyn_cast_or_null<CXXConstructExpr>(Val: CtorArg);
396 if (InnerCE && InnerCE->getNumArgs())
397 CtorArg = InnerCE->getArg(Arg: 0)->IgnoreParenCasts();
398 auto updateIgnoreList = [&] {
399 ConstructToIgnore.insert(V: CE);
400 if (InnerCE)
401 ConstructToIgnore.insert(V: InnerCE);
402 };
403 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: CtorArg)) {
404 updateIgnoreList();
405 return Lambda;
406 }
407 if (auto *TempExpr = dyn_cast<CXXBindTemporaryExpr>(Val: CtorArg)) {
408 E = TempExpr->getSubExpr()->IgnoreParenCasts();
409 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: E)) {
410 updateIgnoreList();
411 return Lambda;
412 }
413 }
414 auto *DRE = dyn_cast<DeclRefExpr>(Val: CtorArg);
415 if (!DRE)
416 return nullptr;
417 auto *VD = dyn_cast_or_null<VarDecl>(Val: DRE->getDecl());
418 if (!VD)
419 return nullptr;
420 auto *Init = VD->getInit();
421 if (!Init)
422 return nullptr;
423 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: Init)) {
424 DeclRefExprsToIgnore.insert(V: DRE);
425 updateIgnoreList();
426 return Lambda;
427 }
428 return nullptr;
429 }
430
431 void checkCalleeLambda(CallExpr *CE) {
432 auto *Callee = CE->getCallee();
433 if (!Callee)
434 return;
435 Callee = Callee->IgnoreParenCasts();
436 if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Val: Callee)) {
437 Callee = MTE->getSubExpr();
438 if (!Callee)
439 return;
440 Callee = Callee->IgnoreParenCasts();
441 }
442 if (auto *L = dyn_cast<LambdaExpr>(Val: Callee)) {
443 LambdasToIgnore.insert(V: L); // Calling a lambda upon creation is safe.
444 return;
445 }
446 auto *DRE = dyn_cast<DeclRefExpr>(Val: Callee->IgnoreParenCasts());
447 if (!DRE)
448 return;
449 auto *MD = dyn_cast_or_null<CXXMethodDecl>(Val: DRE->getDecl());
450 if (!MD || CE->getNumArgs() < 1)
451 return;
452 auto *Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
453 if (auto *L = dyn_cast_or_null<LambdaExpr>(Val: Arg)) {
454 LambdasToIgnore.insert(V: L); // Calling a lambda upon creation is safe.
455 return;
456 }
457 auto *ArgRef = dyn_cast<DeclRefExpr>(Val: Arg);
458 if (!ArgRef)
459 return;
460 auto *VD = dyn_cast_or_null<VarDecl>(Val: ArgRef->getDecl());
461 if (!VD)
462 return;
463 auto *Init = VD->getInit();
464 if (!Init)
465 return;
466 auto *L = dyn_cast_or_null<LambdaExpr>(Val: Init->IgnoreParenCasts());
467 if (!L)
468 return;
469 DeclRefExprsToIgnore.insert(V: ArgRef);
470 LambdasToIgnore.insert(V: L);
471 }
472
473 bool hasProtectedThis(const LambdaExpr *L) {
474 for (const LambdaCapture &OtherCapture : L->captures()) {
475 if (!OtherCapture.capturesVariable())
476 continue;
477 if (auto *ValueDecl = OtherCapture.getCapturedVar()) {
478 if (declProtectsThis(ValueDecl)) {
479 ProtectedThisDecls.insert(V: ValueDecl);
480 return true;
481 }
482 }
483 }
484 return false;
485 }
486
487 bool declProtectsThis(const ValueDecl *ValueDecl) const {
488 auto *VD = dyn_cast<VarDecl>(Val: ValueDecl);
489 if (!VD)
490 return false;
491 auto *Init = VD->getInit();
492 if (!Init)
493 return false;
494 const Expr *Arg = Init->IgnoreParenCasts();
495 do {
496 if (auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Val: Arg))
497 Arg = BTE->getSubExpr()->IgnoreParenCasts();
498 if (auto *CE = dyn_cast<CXXConstructExpr>(Val: Arg)) {
499 auto *Ctor = CE->getConstructor();
500 if (!Ctor)
501 return false;
502 auto ArgClsTy = dyn_cast_or_null<CXXRecordDecl>(Val: Ctor->getParent());
503 if (Checker->Model->isSafePtr(Record: ArgClsTy) && CE->getNumArgs()) {
504 Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
505 continue;
506 }
507 if (auto *Type = ClsType.getTypePtrOrNull()) {
508 if (auto *CXXR = Type->getPointeeCXXRecordDecl()) {
509 if (CXXR == Ctor->getParent() && Ctor->isMoveConstructor() &&
510 CE->getNumArgs() == 1) {
511 Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
512 continue;
513 }
514 }
515 }
516 return false;
517 }
518 if (auto *CE = dyn_cast<CallExpr>(Val: Arg)) {
519 if (auto *Callee = CE->getDirectCallee()) {
520 if ((isStdOrWTFMove(F: Callee) || isCtorOfSafePtr(F: Callee)) &&
521 CE->getNumArgs() == 1) {
522 Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
523 continue;
524 }
525 }
526 }
527 if (auto *OpCE = dyn_cast<CXXOperatorCallExpr>(Val: Arg)) {
528 auto OpCode = OpCE->getOperator();
529 if (OpCode == OO_Star || OpCode == OO_Amp) {
530 auto *Callee = OpCE->getDirectCallee();
531 if (!Callee)
532 return false;
533 auto clsName = safeGetName(ASTNode: Callee->getParent());
534 if (!Checker->isPtrType(Name: clsName) || !OpCE->getNumArgs())
535 return false;
536 Arg = OpCE->getArg(Arg: 0)->IgnoreParenCasts();
537 continue;
538 }
539 }
540 if (auto *UO = dyn_cast<UnaryOperator>(Val: Arg)) {
541 auto OpCode = UO->getOpcode();
542 if (OpCode == UO_Deref || OpCode == UO_AddrOf) {
543 Arg = UO->getSubExpr()->IgnoreParenCasts();
544 continue;
545 }
546 }
547 break;
548 } while (Arg);
549 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: Arg)) {
550 auto *Decl = DRE->getDecl();
551 if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(Val: Decl)) {
552 auto kind = ImplicitParam->getParameterKind();
553 return kind == ImplicitParamKind::ObjCSelf ||
554 kind == ImplicitParamKind::CXXThis;
555 }
556 return ProtectedThisDecls.contains(V: Decl);
557 }
558 return isa<CXXThisExpr>(Val: Arg);
559 }
560 };
561
562 LocalVisitor visitor(this);
563 if (auto *RTC = Model->retainTypeChecker())
564 RTC->visitTranslationUnitDecl(TUD);
565 visitor.TraverseDecl(D: const_cast<TranslationUnitDecl *>(TUD));
566 }
567
568 void visitLambdaExpr(const LambdaExpr *L, bool shouldCheckThis,
569 const QualType T,
570 bool ignoreParamVarDecl = false) const {
571 if (TFA.isTrivial(S: L->getBody()))
572 return;
573 for (const LambdaCapture &C : L->captures()) {
574 if (C.capturesVariable()) {
575 ValueDecl *CapturedVar = C.getCapturedVar();
576 if (ignoreParamVarDecl && isa<ParmVarDecl>(Val: CapturedVar))
577 continue;
578 if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(Val: CapturedVar)) {
579 auto kind = ImplicitParam->getParameterKind();
580 if ((kind == ImplicitParamKind::ObjCSelf ||
581 kind == ImplicitParamKind::CXXThis) &&
582 !shouldCheckThis)
583 continue;
584 }
585 QualType CapturedVarQualType = CapturedVar->getType();
586 auto IsUncountedPtr = isUnsafePtr(QT: CapturedVar->getType());
587 if (C.getCaptureKind() == LCK_ByCopy &&
588 CapturedVarQualType->isReferenceType())
589 continue;
590 if (IsUncountedPtr && *IsUncountedPtr)
591 reportBug(Capture: C, CapturedVar, T: CapturedVarQualType, L);
592 } else if (C.capturesThis() && shouldCheckThis) {
593 if (ignoreParamVarDecl) // this is always a parameter to this function.
594 continue;
595 reportBugOnThisPtr(Capture: C, T);
596 }
597 }
598 }
599
600 void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar,
601 const QualType T, const LambdaExpr *L) const {
602 assert(CapturedVar);
603
604 auto Location = Capture.getLocation();
605 if (isa<ImplicitParamDecl>(Val: CapturedVar) && !Location.isValid())
606 Location = L->getBeginLoc();
607
608 SmallString<100> Buf;
609 llvm::raw_svector_ostream Os(Buf);
610
611 if (Capture.isExplicit())
612 Os << "Captured ";
613 else
614 Os << "Implicitly captured ";
615 Os << "variable ";
616 printQuotedQualifiedName(Os, D: CapturedVar);
617
618 bool IsUnsafePtr = CapturedVar->getType() == T;
619 if (IsUnsafePtr)
620 Os << " is a ";
621 else
622 Os << " contains a ";
623 auto *CapturedType = T.getTypePtrOrNull();
624 printPointer(Os, T: CapturedType);
625
626 PathDiagnosticLocation BSLoc(Location, BR->getSourceManager());
627 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
628 BR->emitReport(R: std::move(Report));
629 }
630
631 void reportBugOnThisPtr(const LambdaCapture &Capture,
632 const QualType T) const {
633 SmallString<100> Buf;
634 llvm::raw_svector_ostream Os(Buf);
635
636 if (Capture.isExplicit()) {
637 Os << "Captured ";
638 } else {
639 Os << "Implicitly captured ";
640 }
641
642 Os << "variable 'this' is a raw pointer to " << Model->typeName();
643 if (auto *RD = T->getPointeeCXXRecordDecl()) {
644 Os << " ";
645 printQuotedQualifiedName(Os, D: RD);
646 }
647
648 PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager());
649 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
650 BR->emitReport(R: std::move(Report));
651 }
652
653 void printPointer(llvm::raw_svector_ostream &Os, const Type *T) const {
654 if (Model->retainTypeChecker()) {
655 // An OS object may be spelled as an id qualified by an OS_-prefixed
656 // protocol; print that protocol name.
657 if (auto *ObjCPtr = dyn_cast<ObjCObjectPointerType>(Val: T)) {
658 for (ObjCProtocolDecl *P : ObjCPtr->quals()) {
659 if (const auto *II = P->getIdentifier()) {
660 auto Name = II->getName();
661 if (Name.starts_with(Prefix: "OS_")) {
662 Os << Model->typeName() << " ";
663 printQuotedQualifiedName(Os, D: P);
664 return;
665 }
666 }
667 }
668 }
669 // Retain/OS types are frequently spelled through a typedef (e.g.
670 // CFXXXRef); print the typedef name rather than desugaring.
671 if (!isa<ObjCObjectPointerType>(Val: T) && T->getAs<TypedefType>()) {
672 auto Typedef = T->getAs<TypedefType>();
673 assert(Typedef);
674 Os << Model->typeName() << " ";
675 printQuotedQualifiedName(Os, D: Typedef->getDecl());
676 return;
677 }
678 }
679 T = T->getUnqualifiedDesugaredType();
680 bool IsPtr = isa<PointerType>(Val: T) || isa<ObjCObjectPointerType>(Val: T);
681 Os << (IsPtr ? "raw pointer" : "raw reference") << " to ";
682 Os << Model->typeName();
683
684 if (auto *RD = T->getPointeeType()->getAsRecordDecl()) {
685 Os << " ";
686 printQuotedQualifiedName(Os, D: RD);
687 } else if (auto *ObjCDecl = getObjCDeclFromObjCPtr(TypePtr: T)) {
688 Os << " ";
689 printQuotedQualifiedName(Os, D: ObjCDecl);
690 }
691 }
692};
693
694class UncountedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
695public:
696 UncountedLambdaCapturesChecker()
697 : RawPtrRefLambdaCapturesChecker("Lambda capture of uncounted variable",
698 makeRefPtrSafetyModel()) {}
699};
700
701class UncheckedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
702public:
703 UncheckedLambdaCapturesChecker()
704 : RawPtrRefLambdaCapturesChecker("Lambda capture of unchecked variable",
705 makeCheckedPtrSafetyModel()) {}
706};
707
708class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
709public:
710 UnretainedLambdaCapturesChecker()
711 : RawPtrRefLambdaCapturesChecker("Lambda capture of unretained "
712 "variables",
713 makeRetainPtrSafetyModel()) {}
714};
715
716} // namespace
717
718void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) {
719 Mgr.registerChecker<UncountedLambdaCapturesChecker>();
720}
721
722bool ento::shouldRegisterUncountedLambdaCapturesChecker(
723 const CheckerManager &mgr) {
724 return true;
725}
726
727void ento::registerUncheckedLambdaCapturesChecker(CheckerManager &Mgr) {
728 Mgr.registerChecker<UncheckedLambdaCapturesChecker>();
729}
730
731bool ento::shouldRegisterUncheckedLambdaCapturesChecker(
732 const CheckerManager &mgr) {
733 return true;
734}
735
736void ento::registerUnretainedLambdaCapturesChecker(CheckerManager &Mgr) {
737 Mgr.registerChecker<UnretainedLambdaCapturesChecker>();
738}
739
740bool ento::shouldRegisterUnretainedLambdaCapturesChecker(
741 const CheckerManager &mgr) {
742 return true;
743}
744