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 VisitVarDecl(VarDecl *VD) override {
122 auto *Init = VD->getInit();
123 if (!Init)
124 return true;
125 if (auto *L = dyn_cast_or_null<LambdaExpr>(Val: Init->IgnoreParenCasts())) {
126 LambdasToIgnore.insert(V: L); // Evaluate lambdas in VisitDeclRefExpr.
127 return true;
128 }
129 if (!VD->hasLocalStorage())
130 return true;
131 if (auto *E = dyn_cast<ExprWithCleanups>(Val: Init))
132 Init = E->getSubExpr();
133 if (auto *E = dyn_cast<CXXBindTemporaryExpr>(Val: Init))
134 Init = E->getSubExpr();
135 if (auto *CE = dyn_cast<CallExpr>(Val: Init)) {
136 if (auto *Callee = CE->getDirectCallee()) {
137 auto FnName = safeGetName(ASTNode: Callee);
138 unsigned ArgCnt = CE->getNumArgs();
139 if (FnName == "makeScopeExit" && ArgCnt == 1) {
140 auto *Arg = CE->getArg(Arg: 0);
141 if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Val: Arg))
142 Arg = E->getSubExpr();
143 if (auto *L = dyn_cast<LambdaExpr>(Val: Arg))
144 addLambdaOwner(VD, CE, L);
145 } else if (FnName == "makeVisitor") {
146 for (unsigned ArgIndex = 0; ArgIndex < ArgCnt; ++ArgIndex) {
147 auto *Arg = CE->getArg(Arg: ArgIndex);
148 if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Val: Arg))
149 Arg = E->getSubExpr();
150 if (auto *L = dyn_cast<LambdaExpr>(Val: Arg))
151 addLambdaOwner(VD, CE, L);
152 }
153 }
154 }
155 } else if (auto *CE = dyn_cast<CXXConstructExpr>(Val: Init)) {
156 if (auto *Ctor = CE->getConstructor()) {
157 if (auto *Cls = Ctor->getParent()) {
158 auto FnName = safeGetName(ASTNode: Cls);
159 unsigned ArgCnt = CE->getNumArgs();
160 if (FnName == "ScopeExit" && ArgCnt == 1) {
161 auto *Arg = CE->getArg(Arg: 0);
162 if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Val: Arg))
163 Arg = E->getSubExpr();
164 if (auto *L = dyn_cast<LambdaExpr>(Val: Arg))
165 addLambdaOwner(VD, CE, L);
166 }
167 }
168 }
169 }
170 return true;
171 }
172
173 void addLambdaOwner(VarDecl *VD, CallExpr *CE, LambdaExpr *L) {
174 auto result = LambdaOwnerMap.insert(
175 KV: std::make_pair(x&: VD, y: SmallVector<const LambdaExpr *>{L}));
176 if (!result.second)
177 result.first->second.push_back(Elt: L);
178 CallToIgnore.insert(V: CE);
179 LambdasToIgnore.insert(V: L);
180 }
181
182 void addLambdaOwner(VarDecl *VD, CXXConstructExpr *CE, LambdaExpr *L) {
183 auto result = LambdaOwnerMap.insert(
184 KV: std::make_pair(x&: VD, y: SmallVector<const LambdaExpr *>{L}));
185 if (!result.second)
186 result.first->second.push_back(Elt: L);
187 ConstructToIgnore.insert(V: CE);
188 LambdasToIgnore.insert(V: L);
189 }
190
191 bool VisitDeclRefExpr(DeclRefExpr *DRE) override {
192 if (DeclRefExprsToIgnore.contains(V: DRE))
193 return true;
194 auto *VD = dyn_cast_or_null<VarDecl>(Val: DRE->getDecl());
195 if (!VD)
196 return true;
197 if (auto It = LambdaOwnerMap.find(Val: VD); It != LambdaOwnerMap.end()) {
198 for (auto *L : It->second) {
199 Checker->visitLambdaExpr(
200 L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L), T: ClsType);
201 }
202 return true;
203 }
204 auto *Init = VD->getInit();
205 if (!Init)
206 return true;
207 auto *L = dyn_cast_or_null<LambdaExpr>(Val: Init->IgnoreParenCasts());
208 if (!L)
209 return true;
210 LambdasToIgnore.insert(V: L);
211 Checker->visitLambdaExpr(L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L),
212 T: ClsType);
213 return true;
214 }
215
216 bool shouldTreatAllArgAsNoEscape(FunctionDecl *FDecl) {
217 std::string PreviousName = safeGetName(ASTNode: FDecl);
218 for (auto *Decl = FDecl->getParent(); Decl; Decl = Decl->getParent()) {
219 if (!isa<NamespaceDecl>(Val: Decl) && !isa<CXXRecordDecl>(Val: Decl))
220 return false;
221 if (auto *NS = dyn_cast<NamespaceDecl>(Val: Decl); NS && NS->isInline())
222 continue;
223 auto Name = safeGetName(ASTNode: Decl);
224 // WTF::switchOn(T, F... f) is a variadic template function and
225 // couldn't be annotated with NOESCAPE. We hard code it here to
226 // workaround that.
227 if (Name == "WTF" && PreviousName == "switchOn")
228 return true;
229 // Treat every argument of functions in std::ranges as noescape.
230 if (Name == "std" && PreviousName == "ranges")
231 return true;
232 PreviousName = Name;
233 }
234 return false;
235 }
236
237 bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {
238 if (ConstructToIgnore.contains(V: CE))
239 return true;
240 if (auto *Callee = CE->getConstructor()) {
241 unsigned ArgIndex = 0;
242 for (auto *Param : Callee->parameters()) {
243 if (ArgIndex >= CE->getNumArgs())
244 return true;
245 auto *Arg = CE->getArg(Arg: ArgIndex)->IgnoreParenCasts();
246 if (auto *L = findLambdaInArg(E: Arg)) {
247 LambdasToIgnore.insert(V: L);
248 if (!Param->hasAttr<NoEscapeAttr>())
249 Checker->visitLambdaExpr(
250 L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L), T: ClsType);
251 }
252 ++ArgIndex;
253 }
254 }
255 return true;
256 }
257
258 bool VisitCallExpr(CallExpr *CE) override {
259 if (CallToIgnore.contains(V: CE))
260 return true;
261 checkCalleeLambda(CE);
262 if (auto *Callee = CE->getDirectCallee()) {
263 if (isVisitFunction(CallExpr: CE, FnDecl: Callee))
264 return true;
265 checkParameters(CE, Callee);
266 } else if (auto *CalleeE = CE->getCallee()) {
267 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: CalleeE->IgnoreParenCasts())) {
268 if (auto *Callee = dyn_cast_or_null<FunctionDecl>(Val: DRE->getDecl()))
269 checkParameters(CE, Callee);
270 }
271 }
272 return true;
273 }
274
275 bool isVisitFunction(CallExpr *CallExpr, FunctionDecl *FnDecl) {
276 bool IsVisitFn = safeGetName(ASTNode: FnDecl) == "visit";
277 if (!IsVisitFn)
278 return false;
279 bool ArgCnt = CallExpr->getNumArgs();
280 if (!ArgCnt)
281 return false;
282 auto *Ns = FnDecl->getParent();
283 if (!Ns)
284 return false;
285 auto NsName = safeGetName(ASTNode: Ns);
286 if (NsName != "WTF" && NsName != "std")
287 return false;
288 auto *Arg = CallExpr->getArg(Arg: 0);
289 if (!Arg)
290 return false;
291 auto *DRE = dyn_cast<DeclRefExpr>(Val: Arg->IgnoreParenCasts());
292 if (!DRE)
293 return false;
294 auto *VD = dyn_cast<VarDecl>(Val: DRE->getDecl());
295 if (!VD)
296 return false;
297 if (!LambdaOwnerMap.contains(Val: VD))
298 return false;
299 DeclRefExprsToIgnore.insert(V: DRE);
300 return true;
301 }
302
303 void checkParameters(CallExpr *CE, FunctionDecl *Callee) {
304 unsigned ArgIndex = isa<CXXOperatorCallExpr>(Val: CE);
305 bool TreatAllArgsAsNoEscape = shouldTreatAllArgAsNoEscape(FDecl: Callee);
306 for (auto *Param : Callee->parameters()) {
307 if (ArgIndex >= CE->getNumArgs())
308 return;
309 auto *Arg = CE->getArg(Arg: ArgIndex)->IgnoreParenCasts();
310 if (auto *L = findLambdaInArg(E: Arg)) {
311 LambdasToIgnore.insert(V: L);
312 if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape)
313 Checker->visitLambdaExpr(
314 L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L), T: ClsType);
315 }
316 ++ArgIndex;
317 }
318 }
319
320 LambdaExpr *findLambdaInArg(Expr *E) {
321 if (auto *Lambda = dyn_cast_or_null<LambdaExpr>(Val: E))
322 return Lambda;
323 auto *TempExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(Val: E);
324 if (!TempExpr)
325 return nullptr;
326 E = TempExpr->getSubExpr()->IgnoreParenCasts();
327 if (!E)
328 return nullptr;
329 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: E))
330 return Lambda;
331 auto *CE = dyn_cast_or_null<CXXConstructExpr>(Val: E);
332 if (!CE || !CE->getNumArgs())
333 return nullptr;
334 auto *CtorArg = CE->getArg(Arg: 0)->IgnoreParenCasts();
335 if (!CtorArg)
336 return nullptr;
337 auto *InnerCE = dyn_cast_or_null<CXXConstructExpr>(Val: CtorArg);
338 if (InnerCE && InnerCE->getNumArgs())
339 CtorArg = InnerCE->getArg(Arg: 0)->IgnoreParenCasts();
340 auto updateIgnoreList = [&] {
341 ConstructToIgnore.insert(V: CE);
342 if (InnerCE)
343 ConstructToIgnore.insert(V: InnerCE);
344 };
345 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: CtorArg)) {
346 updateIgnoreList();
347 return Lambda;
348 }
349 if (auto *TempExpr = dyn_cast<CXXBindTemporaryExpr>(Val: CtorArg)) {
350 E = TempExpr->getSubExpr()->IgnoreParenCasts();
351 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: E)) {
352 updateIgnoreList();
353 return Lambda;
354 }
355 }
356 auto *DRE = dyn_cast<DeclRefExpr>(Val: CtorArg);
357 if (!DRE)
358 return nullptr;
359 auto *VD = dyn_cast_or_null<VarDecl>(Val: DRE->getDecl());
360 if (!VD)
361 return nullptr;
362 auto *Init = VD->getInit();
363 if (!Init)
364 return nullptr;
365 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: Init)) {
366 DeclRefExprsToIgnore.insert(V: DRE);
367 updateIgnoreList();
368 return Lambda;
369 }
370 return nullptr;
371 }
372
373 void checkCalleeLambda(CallExpr *CE) {
374 auto *Callee = CE->getCallee();
375 if (!Callee)
376 return;
377 Callee = Callee->IgnoreParenCasts();
378 if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Val: Callee)) {
379 Callee = MTE->getSubExpr();
380 if (!Callee)
381 return;
382 Callee = Callee->IgnoreParenCasts();
383 }
384 if (auto *L = dyn_cast<LambdaExpr>(Val: Callee)) {
385 LambdasToIgnore.insert(V: L); // Calling a lambda upon creation is safe.
386 return;
387 }
388 auto *DRE = dyn_cast<DeclRefExpr>(Val: Callee->IgnoreParenCasts());
389 if (!DRE)
390 return;
391 auto *MD = dyn_cast_or_null<CXXMethodDecl>(Val: DRE->getDecl());
392 if (!MD || CE->getNumArgs() < 1)
393 return;
394 auto *Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
395 if (auto *L = dyn_cast_or_null<LambdaExpr>(Val: Arg)) {
396 LambdasToIgnore.insert(V: L); // Calling a lambda upon creation is safe.
397 return;
398 }
399 auto *ArgRef = dyn_cast<DeclRefExpr>(Val: Arg);
400 if (!ArgRef)
401 return;
402 auto *VD = dyn_cast_or_null<VarDecl>(Val: ArgRef->getDecl());
403 if (!VD)
404 return;
405 auto *Init = VD->getInit();
406 if (!Init)
407 return;
408 auto *L = dyn_cast_or_null<LambdaExpr>(Val: Init->IgnoreParenCasts());
409 if (!L)
410 return;
411 DeclRefExprsToIgnore.insert(V: ArgRef);
412 LambdasToIgnore.insert(V: L);
413 }
414
415 bool hasProtectedThis(const LambdaExpr *L) {
416 for (const LambdaCapture &OtherCapture : L->captures()) {
417 if (!OtherCapture.capturesVariable())
418 continue;
419 if (auto *ValueDecl = OtherCapture.getCapturedVar()) {
420 if (declProtectsThis(ValueDecl)) {
421 ProtectedThisDecls.insert(V: ValueDecl);
422 return true;
423 }
424 }
425 }
426 return false;
427 }
428
429 bool declProtectsThis(const ValueDecl *ValueDecl) const {
430 auto *VD = dyn_cast<VarDecl>(Val: ValueDecl);
431 if (!VD)
432 return false;
433 auto *Init = VD->getInit();
434 if (!Init)
435 return false;
436 const Expr *Arg = Init->IgnoreParenCasts();
437 do {
438 if (auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Val: Arg))
439 Arg = BTE->getSubExpr()->IgnoreParenCasts();
440 if (auto *CE = dyn_cast<CXXConstructExpr>(Val: Arg)) {
441 auto *Ctor = CE->getConstructor();
442 if (!Ctor)
443 return false;
444 auto ArgClsTy = dyn_cast_or_null<CXXRecordDecl>(Val: Ctor->getParent());
445 if (Checker->Model->isSafePtr(Record: ArgClsTy) && CE->getNumArgs()) {
446 Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
447 continue;
448 }
449 if (auto *Type = ClsType.getTypePtrOrNull()) {
450 if (auto *CXXR = Type->getPointeeCXXRecordDecl()) {
451 if (CXXR == Ctor->getParent() && Ctor->isMoveConstructor() &&
452 CE->getNumArgs() == 1) {
453 Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
454 continue;
455 }
456 }
457 }
458 return false;
459 }
460 if (auto *CE = dyn_cast<CallExpr>(Val: Arg)) {
461 if (auto *Callee = CE->getDirectCallee()) {
462 if ((isStdOrWTFMove(F: Callee) || isCtorOfSafePtr(F: Callee)) &&
463 CE->getNumArgs() == 1) {
464 Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
465 continue;
466 }
467 }
468 }
469 if (auto *OpCE = dyn_cast<CXXOperatorCallExpr>(Val: Arg)) {
470 auto OpCode = OpCE->getOperator();
471 if (OpCode == OO_Star || OpCode == OO_Amp) {
472 auto *Callee = OpCE->getDirectCallee();
473 if (!Callee)
474 return false;
475 auto clsName = safeGetName(ASTNode: Callee->getParent());
476 if (!Checker->isPtrType(Name: clsName) || !OpCE->getNumArgs())
477 return false;
478 Arg = OpCE->getArg(Arg: 0)->IgnoreParenCasts();
479 continue;
480 }
481 }
482 if (auto *UO = dyn_cast<UnaryOperator>(Val: Arg)) {
483 auto OpCode = UO->getOpcode();
484 if (OpCode == UO_Deref || OpCode == UO_AddrOf) {
485 Arg = UO->getSubExpr()->IgnoreParenCasts();
486 continue;
487 }
488 }
489 break;
490 } while (Arg);
491 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: Arg)) {
492 auto *Decl = DRE->getDecl();
493 if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(Val: Decl)) {
494 auto kind = ImplicitParam->getParameterKind();
495 return kind == ImplicitParamKind::ObjCSelf ||
496 kind == ImplicitParamKind::CXXThis;
497 }
498 return ProtectedThisDecls.contains(V: Decl);
499 }
500 return isa<CXXThisExpr>(Val: Arg);
501 }
502 };
503
504 LocalVisitor visitor(this);
505 if (auto *RTC = Model->retainTypeChecker())
506 RTC->visitTranslationUnitDecl(TUD);
507 visitor.TraverseDecl(D: const_cast<TranslationUnitDecl *>(TUD));
508 }
509
510 void visitLambdaExpr(const LambdaExpr *L, bool shouldCheckThis,
511 const QualType T,
512 bool ignoreParamVarDecl = false) const {
513 if (TFA.isTrivial(S: L->getBody()))
514 return;
515 for (const LambdaCapture &C : L->captures()) {
516 if (C.capturesVariable()) {
517 ValueDecl *CapturedVar = C.getCapturedVar();
518 if (ignoreParamVarDecl && isa<ParmVarDecl>(Val: CapturedVar))
519 continue;
520 if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(Val: CapturedVar)) {
521 auto kind = ImplicitParam->getParameterKind();
522 if ((kind == ImplicitParamKind::ObjCSelf ||
523 kind == ImplicitParamKind::CXXThis) &&
524 !shouldCheckThis)
525 continue;
526 }
527 QualType CapturedVarQualType = CapturedVar->getType();
528 auto IsUncountedPtr = isUnsafePtr(QT: CapturedVar->getType());
529 if (C.getCaptureKind() == LCK_ByCopy &&
530 CapturedVarQualType->isReferenceType())
531 continue;
532 if (IsUncountedPtr && *IsUncountedPtr)
533 reportBug(Capture: C, CapturedVar, T: CapturedVarQualType, L);
534 } else if (C.capturesThis() && shouldCheckThis) {
535 if (ignoreParamVarDecl) // this is always a parameter to this function.
536 continue;
537 reportBugOnThisPtr(Capture: C, T);
538 }
539 }
540 }
541
542 void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar,
543 const QualType T, const LambdaExpr *L) const {
544 assert(CapturedVar);
545
546 auto Location = Capture.getLocation();
547 if (isa<ImplicitParamDecl>(Val: CapturedVar) && !Location.isValid())
548 Location = L->getBeginLoc();
549
550 SmallString<100> Buf;
551 llvm::raw_svector_ostream Os(Buf);
552
553 if (Capture.isExplicit())
554 Os << "Captured ";
555 else
556 Os << "Implicitly captured ";
557 Os << "variable ";
558 printQuotedQualifiedName(Os, D: CapturedVar);
559
560 bool IsUnsafePtr = CapturedVar->getType() == T;
561 if (IsUnsafePtr)
562 Os << " is a ";
563 else
564 Os << " contains a ";
565 auto *CapturedType = T.getTypePtrOrNull();
566 printPointer(Os, T: CapturedType);
567
568 PathDiagnosticLocation BSLoc(Location, BR->getSourceManager());
569 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
570 BR->emitReport(R: std::move(Report));
571 }
572
573 void reportBugOnThisPtr(const LambdaCapture &Capture,
574 const QualType T) const {
575 SmallString<100> Buf;
576 llvm::raw_svector_ostream Os(Buf);
577
578 if (Capture.isExplicit()) {
579 Os << "Captured ";
580 } else {
581 Os << "Implicitly captured ";
582 }
583
584 Os << "variable 'this' is a raw pointer to " << Model->typeName();
585 if (auto *RD = T->getPointeeCXXRecordDecl()) {
586 Os << " ";
587 printQuotedQualifiedName(Os, D: RD);
588 }
589
590 PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager());
591 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
592 BR->emitReport(R: std::move(Report));
593 }
594
595 void printPointer(llvm::raw_svector_ostream &Os, const Type *T) const {
596 if (Model->retainTypeChecker()) {
597 // An OS object may be spelled as an id qualified by an OS_-prefixed
598 // protocol; print that protocol name.
599 if (auto *ObjCPtr = dyn_cast<ObjCObjectPointerType>(Val: T)) {
600 for (ObjCProtocolDecl *P : ObjCPtr->quals()) {
601 if (const auto *II = P->getIdentifier()) {
602 auto Name = II->getName();
603 if (Name.starts_with(Prefix: "OS_")) {
604 Os << Model->typeName() << " ";
605 printQuotedQualifiedName(Os, D: P);
606 return;
607 }
608 }
609 }
610 }
611 // Retain/OS types are frequently spelled through a typedef (e.g.
612 // CFXXXRef); print the typedef name rather than desugaring.
613 if (!isa<ObjCObjectPointerType>(Val: T) && T->getAs<TypedefType>()) {
614 auto Typedef = T->getAs<TypedefType>();
615 assert(Typedef);
616 Os << Model->typeName() << " ";
617 printQuotedQualifiedName(Os, D: Typedef->getDecl());
618 return;
619 }
620 }
621 T = T->getUnqualifiedDesugaredType();
622 bool IsPtr = isa<PointerType>(Val: T) || isa<ObjCObjectPointerType>(Val: T);
623 Os << (IsPtr ? "raw pointer" : "raw reference") << " to ";
624 Os << Model->typeName();
625
626 if (auto *RD = T->getPointeeType()->getAsRecordDecl()) {
627 Os << " ";
628 printQuotedQualifiedName(Os, D: RD);
629 } else if (auto *ObjCDecl = getObjCDeclFromObjCPtr(TypePtr: T)) {
630 Os << " ";
631 printQuotedQualifiedName(Os, D: ObjCDecl);
632 }
633 }
634};
635
636class UncountedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
637public:
638 UncountedLambdaCapturesChecker()
639 : RawPtrRefLambdaCapturesChecker("Lambda capture of uncounted variable",
640 makeRefPtrSafetyModel()) {}
641};
642
643class UncheckedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
644public:
645 UncheckedLambdaCapturesChecker()
646 : RawPtrRefLambdaCapturesChecker("Lambda capture of unchecked variable",
647 makeCheckedPtrSafetyModel()) {}
648};
649
650class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
651public:
652 UnretainedLambdaCapturesChecker()
653 : RawPtrRefLambdaCapturesChecker("Lambda capture of unretained "
654 "variables",
655 makeRetainPtrSafetyModel()) {}
656};
657
658} // namespace
659
660void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) {
661 Mgr.registerChecker<UncountedLambdaCapturesChecker>();
662}
663
664bool ento::shouldRegisterUncountedLambdaCapturesChecker(
665 const CheckerManager &mgr) {
666 return true;
667}
668
669void ento::registerUncheckedLambdaCapturesChecker(CheckerManager &Mgr) {
670 Mgr.registerChecker<UncheckedLambdaCapturesChecker>();
671}
672
673bool ento::shouldRegisterUncheckedLambdaCapturesChecker(
674 const CheckerManager &mgr) {
675 return true;
676}
677
678void ento::registerUnretainedLambdaCapturesChecker(CheckerManager &Mgr) {
679 Mgr.registerChecker<UnretainedLambdaCapturesChecker>();
680}
681
682bool ento::shouldRegisterUnretainedLambdaCapturesChecker(
683 const CheckerManager &mgr) {
684 return true;
685}
686