1//=======- RetainPtrCtorAdoptChecker.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 "PtrTypesSemantics.h"
11#include "clang/AST/RecursiveASTVisitor.h"
12#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
13#include "clang/Analysis/RetainSummaryManager.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 "llvm/ADT/DenseSet.h"
19#include <optional>
20
21using namespace clang;
22using namespace ento;
23
24namespace {
25
26class RetainPtrCtorAdoptChecker
27 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
28private:
29 BugType Bug;
30 mutable BugReporter *BR = nullptr;
31 mutable std::unique_ptr<RetainSummaryManager> Summaries;
32 mutable llvm::DenseSet<const ValueDecl *> CreateOrCopyOutArguments;
33 mutable llvm::DenseSet<const Expr *> CreateOrCopyFnCall;
34 mutable RetainTypeChecker RTC;
35
36public:
37 RetainPtrCtorAdoptChecker()
38 : Bug(this, "Correct use of RetainPtr, adoptNS, and adoptCF",
39 "WebKit coding guidelines") {}
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 : public RecursiveASTVisitor<LocalVisitor> {
49 const RetainPtrCtorAdoptChecker *Checker;
50 Decl *DeclWithIssue{nullptr};
51
52 using Base = RecursiveASTVisitor<LocalVisitor>;
53
54 explicit LocalVisitor(const RetainPtrCtorAdoptChecker *Checker)
55 : Checker(Checker) {
56 assert(Checker);
57 }
58
59 bool shouldVisitTemplateInstantiations() const { return true; }
60 bool shouldVisitImplicitCode() const { return false; }
61
62 bool TraverseDecl(Decl *D) {
63 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
64 if (D && (isa<FunctionDecl>(Val: D) || isa<ObjCMethodDecl>(Val: D)))
65 DeclWithIssue = D;
66 return Base::TraverseDecl(D);
67 }
68
69 bool TraverseClassTemplateDecl(ClassTemplateDecl *CTD) {
70 if (isRetainPtr(Name: safeGetName(ASTNode: CTD)))
71 return true; // Skip the contents of RetainPtr.
72 return Base::TraverseClassTemplateDecl(D: CTD);
73 }
74
75 bool VisitTypedefDecl(TypedefDecl *TD) {
76 Checker->RTC.visitTypedef(TD);
77 return true;
78 }
79
80 bool VisitCallExpr(const CallExpr *CE) {
81 Checker->visitCallExpr(CE, DeclWithIssue);
82 return true;
83 }
84
85 bool VisitCXXConstructExpr(const CXXConstructExpr *CE) {
86 Checker->visitConstructExpr(CE, DeclWithIssue);
87 return true;
88 }
89
90 bool VisitObjCMessageExpr(const ObjCMessageExpr *ObjCMsgExpr) {
91 Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
92 return true;
93 }
94
95 bool VisitReturnStmt(const ReturnStmt *RS) {
96 Checker->visitReturnStmt(RS, DeclWithIssue);
97 return true;
98 }
99
100 bool VisitVarDecl(const VarDecl *VD) {
101 Checker->visitVarDecl(VD);
102 return true;
103 }
104
105 bool VisitBinaryOperator(const BinaryOperator *BO) {
106 Checker->visitBinaryOperator(BO);
107 return true;
108 }
109 };
110
111 LocalVisitor visitor(this);
112 Summaries = std::make_unique<RetainSummaryManager>(
113 args&: TUD->getASTContext(), args: true /* trackObjCAndCFObjects */,
114 args: false /* trackOSObjects */);
115 RTC.visitTranslationUnitDecl(TUD);
116 visitor.TraverseDecl(D: const_cast<TranslationUnitDecl *>(TUD));
117 }
118
119 bool isAdoptFn(const Decl *FnDecl) const {
120 return isAdoptFnName(Name: safeGetName(ASTNode: FnDecl));
121 }
122
123 bool isAdoptFnName(const std::string &Name) const {
124 return isAdoptNS(Name) || Name == "adoptCF" || Name == "adoptCFArc";
125 }
126
127 bool isAdoptNS(const std::string &Name) const {
128 return Name == "adoptNS" || Name == "adoptNSArc";
129 }
130
131 void visitCallExpr(const CallExpr *CE, const Decl *DeclWithIssue) const {
132 assert(BR && "expected nonnull BugReporter");
133 if (BR->getSourceManager().isInSystemHeader(Loc: CE->getExprLoc()))
134 return;
135
136 std::string FnName;
137 if (auto *F = CE->getDirectCallee()) {
138 FnName = safeGetName(ASTNode: F);
139 if (isAdoptFnName(Name: FnName))
140 checkAdoptCall(CE, FnName, DeclWithIssue);
141 else {
142 checkCreateOrCopyFunction(CE, DeclWithIssue);
143 checkBridgingRelease(CE, Callee: F, DeclWithIssue);
144 }
145 return;
146 }
147
148 auto *CalleeExpr = CE->getCallee();
149 if (!CalleeExpr)
150 return;
151 CalleeExpr = CalleeExpr->IgnoreParenCasts();
152 if (auto *UnresolvedExpr = dyn_cast<UnresolvedLookupExpr>(Val: CalleeExpr)) {
153 auto Name = UnresolvedExpr->getName();
154 if (!Name.isIdentifier())
155 return;
156 FnName = Name.getAsString();
157 if (isAdoptFnName(Name: FnName))
158 checkAdoptCall(CE, FnName, DeclWithIssue);
159 }
160 checkCreateOrCopyFunction(CE, DeclWithIssue);
161 }
162
163 void checkAdoptCall(const CallExpr *CE, const std::string &FnName,
164 const Decl *DeclWithIssue) const {
165 if (!CE->getNumArgs())
166 return;
167
168 auto *Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
169 auto Result = isOwned(E: Arg);
170 if (Result == IsOwnedResult::Unknown)
171 Result = IsOwnedResult::NotOwned;
172
173 const Expr *Inner = nullptr;
174 if (isAllocInit(E: Arg, InnerExpr: &Inner) || isCreateOrCopy(E: Arg)) {
175 if (Inner)
176 CreateOrCopyFnCall.insert(V: Inner);
177 CreateOrCopyFnCall.insert(V: Arg); // Avoid double reporting.
178 return;
179 }
180 if (Result == IsOwnedResult::Owned || Result == IsOwnedResult::Skip) {
181 CreateOrCopyFnCall.insert(V: Arg);
182 return;
183 }
184
185 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: Arg)) {
186 if (CreateOrCopyOutArguments.contains(V: DRE->getDecl()))
187 return;
188 }
189 if (RTC.isARCEnabled() && isAdoptFnName(Name: FnName))
190 reportUseAfterFree(Name: FnName, CE, DeclWithIssue, condition: "when ARC is disabled");
191 else
192 reportUseAfterFree(Name: FnName, CE, DeclWithIssue);
193 }
194
195 void visitObjCMessageExpr(const ObjCMessageExpr *ObjCMsgExpr,
196 const Decl *DeclWithIssue) const {
197 if (BR->getSourceManager().isInSystemHeader(Loc: ObjCMsgExpr->getExprLoc()))
198 return;
199
200 auto Selector = ObjCMsgExpr->getSelector();
201 if (Selector.getAsString() == "autorelease") {
202 auto *Receiver = ObjCMsgExpr->getInstanceReceiver()->IgnoreParenCasts();
203 if (!Receiver)
204 return;
205 ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Val: Receiver);
206 if (!ObjCMsgExpr)
207 return;
208 const Expr *Inner = nullptr;
209 if (!isAllocInit(E: ObjCMsgExpr, InnerExpr: &Inner))
210 return;
211 CreateOrCopyFnCall.insert(V: ObjCMsgExpr);
212 if (Inner)
213 CreateOrCopyFnCall.insert(V: Inner);
214 return;
215 }
216
217 const Expr *Inner = nullptr;
218 if (!isAllocInit(E: ObjCMsgExpr, InnerExpr: &Inner))
219 return;
220 if (RTC.isARCEnabled())
221 return; // ARC never leaks.
222 if (CreateOrCopyFnCall.contains(V: ObjCMsgExpr))
223 return;
224 if (Inner)
225 CreateOrCopyFnCall.insert(V: Inner); // Avoid double reporting.
226 reportLeak(E: ObjCMsgExpr, DeclWithIssue);
227 }
228
229 void checkCreateOrCopyFunction(const CallExpr *CE,
230 const Decl *DeclWithIssue) const {
231 unsigned ArgCount = CE->getNumArgs();
232 auto *CalleeDecl = CE->getCalleeDecl();
233 auto *FnDecl = CalleeDecl ? CalleeDecl->getAsFunction() : nullptr;
234 for (unsigned ArgIndex = 0; ArgIndex < ArgCount; ++ArgIndex) {
235 auto *Arg = CE->getArg(Arg: ArgIndex)->IgnoreParenCasts();
236 auto *Unary = dyn_cast<UnaryOperator>(Val: Arg);
237 if (!Unary)
238 continue;
239 if (Unary->getOpcode() != UO_AddrOf)
240 continue;
241 auto *SubExpr = Unary->getSubExpr();
242 if (!SubExpr)
243 continue;
244 auto *DRE = dyn_cast<DeclRefExpr>(Val: SubExpr->IgnoreParenCasts());
245 if (!DRE)
246 continue;
247 auto *Decl = DRE->getDecl();
248 if (!Decl)
249 continue;
250 if (FnDecl && ArgIndex < FnDecl->getNumParams()) {
251 // Manually check attributes on argumenet since RetainSummaryManager
252 // basically ignores CF_RETRUNS_RETAINED on out arguments.
253 auto *ParamDecl = FnDecl->getParamDecl(i: ArgIndex);
254 if (ParamDecl->hasAttr<CFReturnsRetainedAttr>())
255 CreateOrCopyOutArguments.insert(V: Decl);
256 } else {
257 // No callee or a variadic argument.
258 // Conservatively assume it's an out argument.
259 if (RTC.isUnretained(Decl->getType()))
260 CreateOrCopyOutArguments.insert(V: Decl);
261 }
262 }
263 auto Summary = Summaries->getSummary(C: AnyCall(CE));
264 switch (Summary->getRetEffect().getKind()) {
265 case RetEffect::OwnedSymbol:
266 case RetEffect::OwnedWhenTrackedReceiver:
267 if (!CreateOrCopyFnCall.contains(V: CE))
268 reportLeak(E: CE, DeclWithIssue);
269 break;
270 default:
271 break;
272 }
273 }
274
275 void checkBridgingRelease(const CallExpr *CE, const FunctionDecl *Callee,
276 const Decl *DeclWithIssue) const {
277 if (safeGetName(ASTNode: Callee) != "CFBridgingRelease" || CE->getNumArgs() != 1)
278 return;
279
280 auto *Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
281 auto *InnerCE = dyn_cast<CallExpr>(Val: Arg);
282 if (!InnerCE)
283 return;
284
285 auto *InnerF = InnerCE->getDirectCallee();
286 if (!InnerF || !isCreateOrCopyFunction(FnDecl: InnerF))
287 return;
288
289 CreateOrCopyFnCall.insert(V: InnerCE);
290 }
291
292 void visitConstructExpr(const CXXConstructExpr *CE,
293 const Decl *DeclWithIssue) const {
294 assert(BR && "expected nonnull BugReporter");
295 if (BR->getSourceManager().isInSystemHeader(Loc: CE->getExprLoc()))
296 return;
297
298 auto *Ctor = CE->getConstructor();
299 if (!Ctor)
300 return;
301
302 auto *Cls = Ctor->getParent();
303 if (!Cls)
304 return;
305
306 if (!isRetainPtr(Name: safeGetName(ASTNode: Cls)) || !CE->getNumArgs())
307 return;
308
309 // Ignore RetainPtr construction inside adoptNS, adoptCF, and retainPtr.
310 if (isAdoptFn(FnDecl: DeclWithIssue) || safeGetName(ASTNode: DeclWithIssue) == "retainPtr")
311 return;
312
313 std::string Name = "RetainPtr constructor";
314 auto *Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
315 auto Result = isOwned(E: Arg);
316
317 if (isCreateOrCopy(E: Arg))
318 CreateOrCopyFnCall.insert(V: Arg); // Avoid double reporting.
319
320 const Expr *Inner = nullptr;
321 if (isAllocInit(E: Arg, InnerExpr: &Inner)) {
322 CreateOrCopyFnCall.insert(V: Arg);
323 if (Inner)
324 CreateOrCopyFnCall.insert(V: Inner);
325 }
326
327 if (Result == IsOwnedResult::Skip)
328 return;
329
330 if (Result == IsOwnedResult::Unknown)
331 Result = IsOwnedResult::NotOwned;
332 if (Result == IsOwnedResult::Owned)
333 reportLeak(Name, CE, DeclWithIssue);
334 else if (RTC.isARCEnabled() && isAllocInit(E: Arg))
335 reportLeak(Name, CE, DeclWithIssue, condition: "when ARC is disabled");
336 else if (isCreateOrCopy(E: Arg))
337 reportLeak(Name, CE, DeclWithIssue);
338 }
339
340 void visitVarDecl(const VarDecl *VD) const {
341 auto *Init = VD->getInit();
342 if (!Init || !RTC.isARCEnabled())
343 return;
344 Init = Init->IgnoreParenCasts();
345 const Expr *Inner = nullptr;
346 if (isAllocInit(E: Init, InnerExpr: &Inner)) {
347 CreateOrCopyFnCall.insert(V: Init);
348 if (Inner)
349 CreateOrCopyFnCall.insert(V: Inner);
350 }
351 }
352
353 void visitBinaryOperator(const BinaryOperator *BO) const {
354 if (!BO->isAssignmentOp())
355 return;
356 if (!isa<ObjCIvarRefExpr>(Val: BO->getLHS()))
357 return;
358 auto *RHS = BO->getRHS()->IgnoreParenCasts();
359 const Expr *Inner = nullptr;
360 if (isAllocInit(E: RHS, InnerExpr: &Inner)) {
361 CreateOrCopyFnCall.insert(V: RHS);
362 if (Inner)
363 CreateOrCopyFnCall.insert(V: Inner);
364 }
365 }
366
367 void visitReturnStmt(const ReturnStmt *RS, const Decl *DeclWithIssue) const {
368 if (!DeclWithIssue)
369 return;
370 auto *RetValue = RS->getRetValue();
371 if (!RetValue)
372 return;
373 RetValue = RetValue->IgnoreParenCasts();
374 std::optional<bool> retainsRet;
375 if (auto *FnDecl = dyn_cast<FunctionDecl>(Val: DeclWithIssue))
376 retainsRet = retainsReturnValue(FnDecl);
377 else if (auto *MethodDecl = dyn_cast<ObjCMethodDecl>(Val: DeclWithIssue))
378 retainsRet = retainsReturnValue(FnDecl: MethodDecl);
379 else
380 return;
381 if (!retainsRet || !*retainsRet) {
382 // Under ARC, returning [[X alloc] init] doesn't leak X.
383 if (RTC.isUnretained(RetValue->getType()))
384 return;
385 }
386 if (auto *CE = dyn_cast<CallExpr>(Val: RetValue)) {
387 auto *Callee = CE->getDirectCallee();
388 if (!Callee || !isCreateOrCopyFunction(FnDecl: Callee))
389 return;
390 CreateOrCopyFnCall.insert(V: CE);
391 return;
392 }
393 const Expr *Inner = nullptr;
394 if (isAllocInit(E: RetValue, InnerExpr: &Inner)) {
395 CreateOrCopyFnCall.insert(V: RetValue);
396 if (Inner)
397 CreateOrCopyFnCall.insert(V: Inner);
398 }
399 }
400
401 template <typename CallableType>
402 std::optional<bool> retainsReturnValue(const CallableType *FnDecl) const {
403 auto Summary = Summaries->getSummary(C: AnyCall(FnDecl));
404 auto RetEffect = Summary->getRetEffect();
405 switch (RetEffect.getKind()) {
406 case RetEffect::NoRet:
407 return std::nullopt;
408 case RetEffect::OwnedSymbol:
409 return true;
410 case RetEffect::NotOwnedSymbol:
411 return false;
412 case RetEffect::OwnedWhenTrackedReceiver:
413 return std::nullopt;
414 case RetEffect::NoRetHard:
415 return std::nullopt;
416 }
417 return std::nullopt;
418 }
419
420 bool isAllocInit(const Expr *E, const Expr **InnerExpr = nullptr) const {
421 auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Val: E);
422 if (auto *POE = dyn_cast<PseudoObjectExpr>(Val: E)) {
423 if (unsigned ExprCount = POE->getNumSemanticExprs()) {
424 auto *Expr = POE->getSemanticExpr(index: ExprCount - 1)->IgnoreParenCasts();
425 ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Val: Expr);
426 if (InnerExpr)
427 *InnerExpr = ObjCMsgExpr;
428 }
429 }
430 if (!ObjCMsgExpr)
431 return false;
432 auto Selector = ObjCMsgExpr->getSelector();
433 auto NameForFirstSlot = Selector.getNameForSlot(argIndex: 0);
434 if (NameForFirstSlot == "alloc" || NameForFirstSlot.starts_with(Prefix: "copy") ||
435 NameForFirstSlot.starts_with(Prefix: "mutableCopy"))
436 return true;
437 if (!NameForFirstSlot.starts_with(Prefix: "init") &&
438 !NameForFirstSlot.starts_with(Prefix: "_init"))
439 return false;
440 if (!ObjCMsgExpr->isInstanceMessage())
441 return false;
442 auto *Receiver = ObjCMsgExpr->getInstanceReceiver();
443 if (!Receiver)
444 return false;
445 Receiver = Receiver->IgnoreParenCasts();
446 if (auto *Inner = dyn_cast<ObjCMessageExpr>(Val: Receiver)) {
447 if (InnerExpr)
448 *InnerExpr = Inner;
449 auto InnerSelector = Inner->getSelector();
450 return InnerSelector.getNameForSlot(argIndex: 0) == "alloc";
451 } else if (auto *CE = dyn_cast<CallExpr>(Val: Receiver)) {
452 if (InnerExpr)
453 *InnerExpr = CE;
454 if (auto *Callee = CE->getDirectCallee()) {
455 if (Callee->getDeclName().isIdentifier()) {
456 auto CalleeName = Callee->getName();
457 return CalleeName.starts_with(Prefix: "alloc");
458 }
459 }
460 }
461 return false;
462 }
463
464 bool isCreateOrCopy(const Expr *E) const {
465 auto *CE = dyn_cast<CallExpr>(Val: E);
466 if (!CE)
467 return false;
468 auto *Callee = CE->getDirectCallee();
469 if (!Callee)
470 return false;
471 return isCreateOrCopyFunction(FnDecl: Callee);
472 }
473
474 bool isCreateOrCopyFunction(const FunctionDecl *FnDecl) const {
475 auto CalleeName = safeGetName(ASTNode: FnDecl);
476 return CalleeName.find(s: "Create") != std::string::npos ||
477 CalleeName.find(s: "Copy") != std::string::npos;
478 }
479
480 enum class IsOwnedResult { Unknown, Skip, Owned, NotOwned };
481 IsOwnedResult isOwned(const Expr *E) const {
482 while (1) {
483 if (auto *POE = dyn_cast<PseudoObjectExpr>(Val: E)) {
484 if (unsigned SemanticExprCount = POE->getNumSemanticExprs()) {
485 E = POE->getSemanticExpr(index: SemanticExprCount - 1);
486 continue;
487 }
488 }
489 if (isa<CXXNullPtrLiteralExpr>(Val: E))
490 return IsOwnedResult::NotOwned;
491 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: E)) {
492 auto QT = DRE->getType();
493 if (isRetainPtrType(T: QT))
494 return IsOwnedResult::NotOwned;
495 QT = QT.getCanonicalType();
496 if (RTC.isUnretained(QT, ignoreARC: true /* ignoreARC */))
497 return IsOwnedResult::NotOwned;
498 auto *PointeeType = QT->getPointeeType().getTypePtrOrNull();
499 if (PointeeType && PointeeType->isVoidType())
500 return IsOwnedResult::NotOwned; // Assume reading void* as +0.
501 }
502 if (auto *TE = dyn_cast<CXXBindTemporaryExpr>(Val: E)) {
503 E = TE->getSubExpr();
504 continue;
505 }
506 if (auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Val: E)) {
507 auto Summary = Summaries->getSummary(C: AnyCall(ObjCMsgExpr));
508 auto RetEffect = Summary->getRetEffect();
509 switch (RetEffect.getKind()) {
510 case RetEffect::NoRet:
511 return IsOwnedResult::Unknown;
512 case RetEffect::OwnedSymbol:
513 return IsOwnedResult::Owned;
514 case RetEffect::NotOwnedSymbol:
515 return IsOwnedResult::NotOwned;
516 case RetEffect::OwnedWhenTrackedReceiver:
517 if (auto *Receiver = ObjCMsgExpr->getInstanceReceiver()) {
518 E = Receiver->IgnoreParenCasts();
519 continue;
520 }
521 return IsOwnedResult::Unknown;
522 case RetEffect::NoRetHard:
523 return IsOwnedResult::Unknown;
524 }
525 }
526 if (auto *CXXCE = dyn_cast<CXXMemberCallExpr>(Val: E)) {
527 if (auto *MD = CXXCE->getMethodDecl()) {
528 auto *Cls = MD->getParent();
529 if (auto *CD = dyn_cast<CXXConversionDecl>(Val: MD)) {
530 auto QT = CD->getConversionType().getCanonicalType();
531 auto *ResultType = QT.getTypePtrOrNull();
532 if (isRetainPtr(Name: safeGetName(ASTNode: Cls)) && ResultType &&
533 (ResultType->isPointerType() || ResultType->isReferenceType() ||
534 ResultType->isObjCObjectPointerType()))
535 return IsOwnedResult::NotOwned;
536 }
537 if (safeGetName(ASTNode: MD) == "leakRef" && isRetainPtr(Name: safeGetName(ASTNode: Cls)))
538 return IsOwnedResult::Owned;
539 }
540 }
541 if (auto *CE = dyn_cast<CallExpr>(Val: E)) {
542 if (auto *Callee = CE->getDirectCallee()) {
543 if (isAdoptFn(FnDecl: Callee))
544 return IsOwnedResult::NotOwned;
545 auto Name = safeGetName(ASTNode: Callee);
546 if (Name == "__builtin___CFStringMakeConstantString")
547 return IsOwnedResult::NotOwned;
548 if ((Name == "checked_cf_cast" || Name == "dynamic_cf_cast" ||
549 Name == "checked_objc_cast" || Name == "dynamic_objc_cast") &&
550 CE->getNumArgs() == 1) {
551 E = CE->getArg(Arg: 0)->IgnoreParenCasts();
552 continue;
553 }
554 auto RetType = Callee->getReturnType();
555 if (isRetainPtrType(T: RetType))
556 return IsOwnedResult::NotOwned;
557 if (isCreateOrCopyFunction(FnDecl: Callee)) {
558 CreateOrCopyFnCall.insert(V: CE);
559 return IsOwnedResult::Owned;
560 }
561 } else if (auto *CalleeExpr = CE->getCallee()) {
562 if (isa<CXXDependentScopeMemberExpr>(Val: CalleeExpr))
563 return IsOwnedResult::Skip; // Wait for instantiation.
564 if (isa<UnresolvedLookupExpr>(Val: CalleeExpr))
565 return IsOwnedResult::Skip; // Wait for instantiation.
566 }
567 auto Summary = Summaries->getSummary(C: AnyCall(CE));
568 auto RetEffect = Summary->getRetEffect();
569 switch (RetEffect.getKind()) {
570 case RetEffect::NoRet:
571 return IsOwnedResult::Unknown;
572 case RetEffect::OwnedSymbol:
573 return IsOwnedResult::Owned;
574 case RetEffect::NotOwnedSymbol:
575 return IsOwnedResult::NotOwned;
576 case RetEffect::OwnedWhenTrackedReceiver:
577 return IsOwnedResult::Unknown;
578 case RetEffect::NoRetHard:
579 return IsOwnedResult::Unknown;
580 }
581 }
582 break;
583 }
584 return IsOwnedResult::Unknown;
585 }
586
587 void reportUseAfterFree(const std::string &Name, const CallExpr *CE,
588 const Decl *DeclWithIssue,
589 const char *condition = nullptr) const {
590 SmallString<100> Buf;
591 llvm::raw_svector_ostream Os(Buf);
592
593 Os << "Incorrect use of " << Name
594 << ". The argument is +0 and results in an use-after-free";
595 if (condition)
596 Os << " " << condition;
597 Os << ".";
598
599 assert(BR && "expected nonnull BugReporter");
600 PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
601 BR->getSourceManager());
602 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
603 Report->addRange(R: CE->getSourceRange());
604 Report->setDeclWithIssue(DeclWithIssue);
605 BR->emitReport(R: std::move(Report));
606 }
607
608 void reportLeak(std::string &Name, const CXXConstructExpr *CE,
609 const Decl *DeclWithIssue,
610 const char *condition = nullptr) const {
611 SmallString<100> Buf;
612 llvm::raw_svector_ostream Os(Buf);
613
614 Os << "Incorrect use of " << Name
615 << ". The argument is +1 and results in a memory leak";
616 if (condition)
617 Os << " " << condition;
618 Os << ".";
619
620 assert(BR && "expected nonnull BugReporter");
621 PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
622 BR->getSourceManager());
623 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
624 Report->addRange(R: CE->getSourceRange());
625 Report->setDeclWithIssue(DeclWithIssue);
626 BR->emitReport(R: std::move(Report));
627 }
628
629 template <typename ExprType>
630 void reportLeak(const ExprType *E, const Decl *DeclWithIssue) const {
631 SmallString<100> Buf;
632 llvm::raw_svector_ostream Os(Buf);
633
634 Os << "The return value is +1 and results in a memory leak.";
635
636 PathDiagnosticLocation BSLoc(E->getSourceRange().getBegin(),
637 BR->getSourceManager());
638 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
639 Report->addRange(R: E->getSourceRange());
640 Report->setDeclWithIssue(DeclWithIssue);
641 BR->emitReport(R: std::move(Report));
642 }
643};
644} // namespace
645
646void ento::registerRetainPtrCtorAdoptChecker(CheckerManager &Mgr) {
647 Mgr.registerChecker<RetainPtrCtorAdoptChecker>();
648}
649
650bool ento::shouldRegisterRetainPtrCtorAdoptChecker(const CheckerManager &mgr) {
651 return true;
652}
653