clang 24.0.0git
RawPtrRefLocalVarsChecker.cpp
Go to the documentation of this file.
1//=======- UncountedLocalVarsChecker.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"
14#include "clang/AST/Decl.h"
15#include "clang/AST/DeclCXX.h"
24#include <optional>
25
26using namespace clang;
27using namespace ento;
28
29namespace {
30
31// FIXME: should be defined by anotations in the future
32bool isRefcountedStringsHack(const VarDecl *V) {
33 assert(V);
34 auto safeClass = [](const std::string &className) {
35 return className == "String" || className == "AtomString" ||
36 className == "UniquedString" || className == "Identifier";
37 };
38 QualType QT = V->getType();
39 auto *T = QT.getTypePtr();
40 if (auto *CXXRD = T->getAsCXXRecordDecl()) {
41 if (safeClass(safeGetName(CXXRD)))
42 return true;
43 }
44 if (T->isPointerType() || T->isReferenceType()) {
45 if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
46 if (safeClass(safeGetName(CXXRD)))
47 return true;
48 }
49 }
50 return false;
51}
52
53struct GuardianVisitor : DynamicRecursiveASTVisitor {
54 const VarDecl *Guardian{nullptr};
55
56 explicit GuardianVisitor(const VarDecl *Guardian) : Guardian(Guardian) {
57 assert(Guardian);
58 }
59
60 bool VisitBinaryOperator(BinaryOperator *BO) override {
61 if (BO->isAssignmentOp()) {
62 if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {
63 if (VarRef->getDecl() == Guardian)
64 return false;
65 }
66 }
67 return true;
68 }
69
70 bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {
71 auto *Ctor = CE->getConstructor();
72 if (!Ctor)
73 return false;
74 unsigned ArgIndex = 0;
75 for (auto *Arg : CE->arguments()) {
76 ParmVarDecl *Parm = nullptr;
77 if (ArgIndex < Ctor->getNumParams())
78 Parm = Ctor->getParamDecl(ArgIndex);
79 if (mutatesGuardian(Arg, Parm))
80 return false;
81 ArgIndex++;
82 }
83 return true;
84 }
85
86 bool VisitCallExpr(CallExpr *CE) override {
87 auto *Callee = CE->getDirectCallee();
88 if (!Callee)
89 return false;
90 if (isPtrConversion(Callee))
91 return true;
92 if (auto *Method = dyn_cast<CXXMethodDecl>(Callee)) {
93 if (isGetterOfSafePtr(Method).value_or(false))
94 return true;
95 }
96 unsigned ArgIndex = 0;
97 unsigned ArgOffset = isa<CXXOperatorCallExpr>(CE);
98 for (auto *Arg : CE->arguments()) {
99 ParmVarDecl *Parm = nullptr;
100 if (ArgIndex >= ArgOffset) {
101 unsigned ParmIndex = ArgIndex - ArgOffset;
102 if (ParmIndex < Callee->getNumParams())
103 Parm = Callee->getParamDecl(ParmIndex);
104 }
105 if (mutatesGuardian(Arg, Parm))
106 return false;
107 ArgIndex++;
108 }
109 return true;
110 }
111
112 bool VisitCXXMemberCallExpr(CXXMemberCallExpr *MCE) override {
113 auto *Method = MCE->getMethodDecl();
114 auto ObjType = MCE->getObjectType();
115 if (ObjType.isConstQualified())
116 return true;
117 auto *ThisArg = MCE->getImplicitObjectArgument()->IgnoreParenCasts();
118 if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) {
119 if (!isa<CXXConversionDecl>(Method) && VarRef->getDecl() == Guardian)
120 return false;
121 }
122 return true;
123 }
124
125private:
126 bool mutatesGuardian(const Expr *Arg, const ParmVarDecl *ParmDecl) {
127 Arg = Arg->IgnoreParenCasts();
128 if (auto *VarRef = dyn_cast<DeclRefExpr>(Arg)) {
129 if (VarRef->getDecl() == Guardian) {
130 auto ArgType = ParmDecl ? ParmDecl->getType() : Arg->getType();
131 if (!ArgType.isConstQualified())
132 return true;
133 }
134 }
135 return false;
136 }
137};
138
139bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
140 const VarDecl *MaybeGuardian) {
141 assert(Guarded);
142 assert(MaybeGuardian);
143
144 if (!MaybeGuardian->isLocalVarDecl())
145 return false;
146
147 const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
148
149 ASTContext &ctx = MaybeGuardian->getASTContext();
150
151 for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
152 !guardianAncestors.empty();
153 guardianAncestors = ctx.getParents(
154 *guardianAncestors
155 .begin()) // FIXME - should we handle all of the parents?
156 ) {
157 for (auto &guardianAncestor : guardianAncestors) {
158 if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
159 guardiansClosestCompStmtAncestor = CStmtParentAncestor;
160 break;
161 }
162 }
163 if (guardiansClosestCompStmtAncestor)
164 break;
165 }
166
167 if (!guardiansClosestCompStmtAncestor)
168 return false;
169
170 // We need to skip the first CompoundStmt to avoid situation when guardian is
171 // defined in the same scope as guarded variable.
172 const CompoundStmt *FirstCompondStmt = nullptr;
173 for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
174 !guardedVarAncestors.empty();
175 guardedVarAncestors = ctx.getParents(
176 *guardedVarAncestors
177 .begin()) // FIXME - should we handle all of the parents?
178 ) {
179 for (auto &guardedVarAncestor : guardedVarAncestors) {
180 if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
181 if (!FirstCompondStmt) {
182 FirstCompondStmt = CStmtAncestor;
183 continue;
184 }
185 if (CStmtAncestor == guardiansClosestCompStmtAncestor) {
186 GuardianVisitor guardianVisitor(MaybeGuardian);
187 auto *GuardedScope = const_cast<CompoundStmt *>(FirstCompondStmt);
188 return guardianVisitor.TraverseCompoundStmt(GuardedScope);
189 }
190 }
191 }
192 }
193
194 return false;
195}
196
197class RawPtrRefLocalVarsChecker
198 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
199 BugType Bug;
200 EnsureFunctionAnalysis EFA;
201
202protected:
203 mutable BugReporter *BR;
204 const std::unique_ptr<PtrRefSafetyModel> Model;
205
206public:
207 RawPtrRefLocalVarsChecker(const char *description,
208 std::unique_ptr<PtrRefSafetyModel> Model)
209 : Bug(this, description, "WebKit coding guidelines"),
210 Model(std::move(Model)) {}
211
212 std::optional<bool> isUnsafePtr(QualType T) const {
213 return isUnsafePtrForStorage(*Model, T);
214 }
215
216 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
217 BugReporter &BRArg) const {
218 BR = &BRArg;
219
220 // The calls to checkAST* from AnalysisConsumer don't
221 // visit template instantiations or lambda classes. We
222 // want to visit those, so we make our own RecursiveASTVisitor.
223 struct LocalVisitor : DynamicRecursiveASTVisitor {
224 const RawPtrRefLocalVarsChecker *Checker;
225 Decl *DeclWithIssue{nullptr};
226
227 TrivialFunctionAnalysis TFA;
228
229 explicit LocalVisitor(const RawPtrRefLocalVarsChecker *Checker)
230 : Checker(Checker) {
231 assert(Checker);
232 ShouldVisitTemplateInstantiations = true;
233 ShouldVisitImplicitCode = false;
234 }
235
236 bool TraverseDecl(Decl *D) override {
237 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
238 if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
239 DeclWithIssue = D;
241 }
242
243 bool VisitTypedefDecl(TypedefDecl *TD) override {
244 if (auto *RTC = Checker->Model->retainTypeChecker())
245 RTC->visitTypedef(TD);
246 return true;
247 }
248
249 bool VisitVarDecl(VarDecl *V) override {
250 auto *Init = V->getInit();
251 if (V->isLocalVarDecl())
252 Checker->visitVarDecl(V, Init, DeclWithIssue);
253 return true;
254 }
255
256 bool VisitBinaryOperator(BinaryOperator *BO) override {
257 if (BO->isAssignmentOp()) {
258 if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {
259 if (auto *V = dyn_cast<VarDecl>(VarRef->getDecl()))
260 Checker->visitVarDecl(V, BO->getRHS(), DeclWithIssue);
261 }
262 }
263 return true;
264 }
265
266 bool TraverseIfStmt(IfStmt *IS) override {
267 if (IS->getConditionVariable()) {
268 // This code currently does not explicitly check the "else" statement
269 // since getConditionVariable returns nullptr when there is a
270 // condition defined after ";" as in "if (auto foo = ~; !foo)". If
271 // this semantics change, we should add an explicit check for "else".
272 if (auto *Then = IS->getThen(); !Then || TFA.isTrivial(Then))
273 return true;
274 }
275 if (!TFA.isTrivial(IS))
276 return DynamicRecursiveASTVisitor::TraverseIfStmt(IS);
277 return true;
278 }
279
280 bool TraverseForStmt(ForStmt *FS) override {
281 if (!TFA.isTrivial(FS))
282 return DynamicRecursiveASTVisitor::TraverseForStmt(FS);
283 return true;
284 }
285
286 bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) override {
287 if (!TFA.isTrivial(FRS))
288 return DynamicRecursiveASTVisitor::TraverseCXXForRangeStmt(FRS);
289 return true;
290 }
291
292 bool TraverseWhileStmt(WhileStmt *WS) override {
293 if (!TFA.isTrivial(WS))
294 return DynamicRecursiveASTVisitor::TraverseWhileStmt(WS);
295 return true;
296 }
297
298 bool TraverseCompoundStmt(CompoundStmt *CS) override {
299 if (!TFA.isTrivial(CS))
300 return DynamicRecursiveASTVisitor::TraverseCompoundStmt(CS);
301 return true;
302 }
303
304 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override {
305 if (isSmartPtrClass(safeGetName(Decl)))
306 return true;
307 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);
308 }
309 };
310
311 LocalVisitor visitor(this);
312 if (auto *RTC = Model->retainTypeChecker())
313 RTC->visitTranslationUnitDecl(TUD);
314 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
315 }
316
317 void visitVarDecl(const VarDecl *V, const Expr *Value,
318 const Decl *DeclWithIssue) const {
319 if (shouldSkipVarDecl(V))
320 return;
321
322 if (auto *DD = dyn_cast<DecompositionDecl>(V)) {
323 for (auto *BD : DD->bindings()) {
324 auto *Binding = BD->getBinding();
325 if (!Binding)
326 continue;
327 std::optional<bool> IsUncountedPtr = isUnsafePtr(Binding->getType());
328 if (!IsUncountedPtr || !*IsUncountedPtr)
329 continue;
330 reportBug(V, nullptr, BD, DeclWithIssue);
331 }
332 }
333
334 std::optional<bool> IsUncountedPtr = isUnsafePtr(V->getType());
335 if (IsUncountedPtr && *IsUncountedPtr) {
336 if (Value && isPtrOriginSafe(V, Value, DeclWithIssue))
337 return;
338 reportBug(V, Value, nullptr, DeclWithIssue);
339 }
340 }
341
342 bool isPtrOriginSafe(const VarDecl *V, const Expr *Value,
343 const Decl *DeclWithIssue) const {
344 return tryToFindPtrOrigin(
345 Value, /*StopAtFirstRefCountedObj=*/false,
346 [&](const clang::CXXRecordDecl *Record) {
347 return Model->isSafePtr(Record);
348 },
349 [&](const clang::QualType Type) { return Model->isSafePtrType(Type); },
350 [&](const clang::Decl *D) {
351 return Model->isSafeDecl(D, BR->getSourceManager());
352 },
353 [&](const clang::Expr *InitArgOrigin, bool IsSafe) {
354 if (!InitArgOrigin || IsSafe)
355 return true;
356
357 if (isa<CXXThisExpr>(InitArgOrigin))
358 return true;
359
360 if (isNullPtr(InitArgOrigin))
361 return true;
362
363 if (isa<IntegerLiteral>(InitArgOrigin))
364 return true;
365
366 if (isConstOwnerPtrMemberExpr(InitArgOrigin))
367 return true;
368
369 if (EFA.isACallToEnsureFn(InitArgOrigin))
370 return true;
371
372 if (Model->isSafeExpr(InitArgOrigin))
373 return true;
374
375 if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
376 if (auto *MaybeGuardian =
377 dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
378 const auto *MaybeGuardianArgType =
379 MaybeGuardian->getType().getTypePtr();
380 if (MaybeGuardianArgType) {
381 const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
382 MaybeGuardianArgType->getAsCXXRecordDecl();
383 if (MaybeGuardianArgCXXRecord) {
384 if (MaybeGuardian->isLocalVarDecl() &&
385 (Model->isSafePtr(MaybeGuardianArgCXXRecord) ||
386 isRefcountedStringsHack(MaybeGuardian)) &&
387 isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian))
388 return true;
389 }
390 }
391
392 if (isa<ParmVarDecl>(MaybeGuardian)) {
393 if (auto *FD = dyn_cast<FunctionDecl>(DeclWithIssue)) {
394 if (GuardianVisitor{MaybeGuardian}.TraverseStmt(
395 FD->getBody()))
396 return true;
397 }
398 if (auto *MD = dyn_cast<ObjCMethodDecl>(DeclWithIssue)) {
399 if (GuardianVisitor{MaybeGuardian}.TraverseStmt(
400 MD->getBody()))
401 return true;
402 }
403 }
404 }
405 }
406
407 return false;
408 });
409 }
410
411 bool shouldSkipVarDecl(const VarDecl *V) const {
412 assert(V);
414 return true;
415 return BR->getSourceManager().isInSystemHeader(V->getLocation());
416 }
417
418 void reportBug(const VarDecl *V, const Expr *Value, const Decl *BindingDecl,
419 const Decl *DeclWithIssue) const {
420 assert(V);
421 SmallString<100> Buf;
422 llvm::raw_svector_ostream Os(Buf);
423
424 if (isa<ParmVarDecl>(V)) {
425 Os << "Parameter ";
427 Os << " is a ";
428 printPointerTypeAndType(Os, V->getType());
429
430 SourceLocation ExprLoc = (Value) ? Value->getExprLoc() : V->getLocation();
431 PathDiagnosticLocation BSLoc(ExprLoc, BR->getSourceManager());
432 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
433 if (Value)
434 Report->addRange(Value->getSourceRange());
435 Report->setDeclWithIssue(DeclWithIssue);
436 BR->emitReport(std::move(Report));
437 } else {
438 if (V->hasLocalStorage())
439 Os << "Local variable ";
440 else if (V->isStaticLocal())
441 Os << "Static local variable ";
442 else if (V->hasGlobalStorage())
443 Os << "Global variable ";
444 else
445 Os << "Variable ";
446 if (BindingDecl)
447 Os << "'" << safeGetName(BindingDecl) << "'";
448 else
450 Os << " is a ";
451 printPointerTypeAndType(Os, V->getType());
452
453 PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
454 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
455 Report->addRange(V->getSourceRange());
456 Report->setDeclWithIssue(DeclWithIssue);
457 BR->emitReport(std::move(Report));
458 }
459 }
460
461 void printPointerTypeAndType(llvm::raw_svector_ostream &Os,
462 QualType QT) const {
463 auto *VarType = QT.getTypePtr();
464 auto *RTC = Model->retainTypeChecker();
465 if (RTC && isa<TypedefType>(VarType)) {
466 Os << Model->typeName() << " ";
467 if (auto *Decl = RTC->getCanonicalDecl(QT)) {
468 printQuotedQualifiedName(Os, Decl);
469 } else {
470 auto Typedef = VarType->getAs<TypedefType>();
471 assert(Typedef);
472 printQuotedQualifiedName(Os, Typedef->getDecl());
473 }
474 } else {
475 auto *DesugaredType = VarType->getUnqualifiedDesugaredType();
476 bool IsPtr = isa<PointerType, ObjCObjectPointerType>(DesugaredType);
477 Os << "raw " << (IsPtr ? "pointer" : "reference") << " to ";
478 Os << Model->typeName() << " ";
479 printTypeName(Os, QT);
480 }
481 }
482};
483
484class UncountedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
485public:
486 UncountedLocalVarsChecker()
487 : RawPtrRefLocalVarsChecker("Uncounted raw pointer or reference not "
488 "provably backed by ref-counted variable",
490};
491
492class UncheckedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
493public:
494 UncheckedLocalVarsChecker()
495 : RawPtrRefLocalVarsChecker("Unchecked raw pointer or reference not "
496 "provably backed by checked variable",
498};
499
500class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
501public:
502 UnretainedLocalVarsChecker()
503 : RawPtrRefLocalVarsChecker("Unretained raw pointer or reference not "
504 "provably backed by a RetainPtr",
506};
507
508} // namespace
509
510void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
511 Mgr.registerChecker<UncountedLocalVarsChecker>();
512}
513
514bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
515 return true;
516}
517
518void ento::registerUncheckedLocalVarsChecker(CheckerManager &Mgr) {
519 Mgr.registerChecker<UncheckedLocalVarsChecker>();
520}
521
522bool ento::shouldRegisterUncheckedLocalVarsChecker(const CheckerManager &) {
523 return true;
524}
525
526void ento::registerUnretainedLocalVarsChecker(CheckerManager &Mgr) {
527 Mgr.registerChecker<UnretainedLocalVarsChecker>();
528}
529
530bool ento::shouldRegisterUnretainedLocalVarsChecker(const CheckerManager &) {
531 return true;
532}
#define V(N, I)
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
llvm::MachO::Record Record
Definition MachO.h:31
Defines the clang::SourceLocation class and associated facilities.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:239
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
Expr * getLHS() const
Definition Expr.h:4132
Expr * getRHS() const
Definition Expr.h:4134
static bool isAssignmentOp(Opcode Opc)
Definition Expr.h:4218
arg_range arguments()
Definition ExprCXX.h:1676
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
Definition ExprCXX.h:1615
CXXMethodDecl * getMethodDecl() const
Retrieve the declaration of the called method.
Definition ExprCXX.cpp:774
Expr * getImplicitObjectArgument() const
Retrieve the implicit object argument for the member call.
Definition ExprCXX.cpp:755
QualType getObjectType() const
Retrieve the type of the object argument.
Definition ExprCXX.cpp:767
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition Expr.h:3170
arg_range arguments()
Definition Expr.h:3239
CompoundStmt - This represents a group of statements like { stmt stmt }.
Definition Stmt.h:1752
ASTContext & getASTContext() const LLVM_READONLY
Definition DeclBase.cpp:550
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
virtual bool TraverseDecl(MaybeConst< Decl > *D)
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition Expr.cpp:3128
QualType getType() const
Definition Expr.h:145
Stmt * getThen()
Definition Stmt.h:2360
VarDecl * getConditionVariable()
Retrieve the variable declared in this "if" statement, if any.
Definition Stmt.cpp:1068
A (possibly-)qualified type.
Definition TypeBase.h:938
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition TypeBase.h:8418
bool isTrivial(const Decl *D, const Stmt **OffendingStmt=nullptr) const
QualType getType() const
Definition Decl.h:724
Represents a variable declaration or definition.
Definition Decl.h:933
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
Definition Decl.h:1275
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
Definition Checker.h:565
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
bool isPtrConversion(const FunctionDecl *F)
std::unique_ptr< PtrRefSafetyModel > makeCheckedPtrSafetyModel()
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
std::optional< bool > isUnsafePtrForStorage(const PtrRefSafetyModel &Model, QualType T, bool IgnoreARC=false)
Applies the memory-management exemptions that hold for a variable, member, or lambda capture (but not...
bool tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj, std::function< bool(const clang::CXXRecordDecl *)> isSafePtr, std::function< bool(const clang::QualType)> isSafePtrType, std::function< bool(const clang::Decl *)> isSafeGlobalDecl, std::function< bool(const clang::Expr *, bool)> callback)
This function de-facto defines a set of transformations that we consider safe (in heuristical sense).
Definition ASTUtils.cpp:25
const FunctionProtoType * T
bool isSmartPtrClass(const std::string &Name)
@ Type
The name was classified as a type.
Definition Sema.h:558
std::optional< bool > isGetterOfSafePtr(const CXXMethodDecl *M)
void printTypeName(llvm::raw_ostream &Os, const QualType QT)
std::string safeGetName(const T *ASTNode)
Definition ASTUtils.h:98
bool isNullPtr(const clang::Expr *E)
Definition ASTUtils.cpp:290
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
std::unique_ptr< PtrRefSafetyModel > makeRefPtrSafetyModel()
std::unique_ptr< PtrRefSafetyModel > makeRetainPtrSafetyModel()
bool isConstOwnerPtrMemberExpr(const clang::Expr *E)
Definition ASTUtils.cpp:300