clang 23.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"
13#include "clang/AST/Decl.h"
14#include "clang/AST/DeclCXX.h"
23#include <optional>
24
25using namespace clang;
26using namespace ento;
27
28namespace {
29
30// FIXME: should be defined by anotations in the future
31bool isRefcountedStringsHack(const VarDecl *V) {
32 assert(V);
33 auto safeClass = [](const std::string &className) {
34 return className == "String" || className == "AtomString" ||
35 className == "UniquedString" || className == "Identifier";
36 };
37 QualType QT = V->getType();
38 auto *T = QT.getTypePtr();
39 if (auto *CXXRD = T->getAsCXXRecordDecl()) {
40 if (safeClass(safeGetName(CXXRD)))
41 return true;
42 }
43 if (T->isPointerType() || T->isReferenceType()) {
44 if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
45 if (safeClass(safeGetName(CXXRD)))
46 return true;
47 }
48 }
49 return false;
50}
51
52struct GuardianVisitor : DynamicRecursiveASTVisitor {
53 const VarDecl *Guardian{nullptr};
54
55 explicit GuardianVisitor(const VarDecl *Guardian) : Guardian(Guardian) {
56 assert(Guardian);
57 }
58
59 bool VisitBinaryOperator(BinaryOperator *BO) override {
60 if (BO->isAssignmentOp()) {
61 if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {
62 if (VarRef->getDecl() == Guardian)
63 return false;
64 }
65 }
66 return true;
67 }
68
69 bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {
70 auto *Ctor = CE->getConstructor();
71 if (!Ctor)
72 return false;
73 unsigned ArgIndex = 0;
74 for (auto *Arg : CE->arguments()) {
75 ParmVarDecl *Parm = nullptr;
76 if (ArgIndex < Ctor->getNumParams())
77 Parm = Ctor->getParamDecl(ArgIndex);
78 if (mutatesGuardian(Arg, Parm))
79 return false;
80 ArgIndex++;
81 }
82 return true;
83 }
84
85 bool VisitCallExpr(CallExpr *CE) override {
86 auto *Callee = CE->getDirectCallee();
87 if (!Callee)
88 return false;
89 if (isPtrConversion(Callee))
90 return true;
91 unsigned ArgIndex = 0;
92 unsigned ArgOffset = isa<CXXOperatorCallExpr>(CE);
93 for (auto *Arg : CE->arguments()) {
94 ParmVarDecl *Parm = nullptr;
95 if (ArgIndex >= ArgOffset) {
96 unsigned ParmIndex = ArgIndex - ArgOffset;
97 if (ParmIndex < Callee->getNumParams())
98 Parm = Callee->getParamDecl(ParmIndex);
99 }
100 if (mutatesGuardian(Arg, Parm))
101 return false;
102 ArgIndex++;
103 }
104 return true;
105 }
106
107 bool VisitCXXMemberCallExpr(CXXMemberCallExpr *MCE) override {
108 auto *Method = MCE->getMethodDecl();
109 auto ObjType = MCE->getObjectType();
110 if (ObjType.isConstQualified())
111 return true;
112 auto *ThisArg = MCE->getImplicitObjectArgument()->IgnoreParenCasts();
113 if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) {
114 if (!isa<CXXConversionDecl>(Method) && VarRef->getDecl() == Guardian)
115 return false;
116 }
117 return true;
118 }
119
120private:
121 bool mutatesGuardian(const Expr *Arg, const ParmVarDecl *ParmDecl) {
122 Arg = Arg->IgnoreParenCasts();
123 if (auto *VarRef = dyn_cast<DeclRefExpr>(Arg)) {
124 if (VarRef->getDecl() == Guardian) {
125 auto ArgType = ParmDecl ? ParmDecl->getType() : Arg->getType();
126 if (!ArgType.isConstQualified())
127 return true;
128 }
129 }
130 return false;
131 }
132};
133
134bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
135 const VarDecl *MaybeGuardian) {
136 assert(Guarded);
137 assert(MaybeGuardian);
138
139 if (!MaybeGuardian->isLocalVarDecl())
140 return false;
141
142 const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
143
144 ASTContext &ctx = MaybeGuardian->getASTContext();
145
146 for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
147 !guardianAncestors.empty();
148 guardianAncestors = ctx.getParents(
149 *guardianAncestors
150 .begin()) // FIXME - should we handle all of the parents?
151 ) {
152 for (auto &guardianAncestor : guardianAncestors) {
153 if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
154 guardiansClosestCompStmtAncestor = CStmtParentAncestor;
155 break;
156 }
157 }
158 if (guardiansClosestCompStmtAncestor)
159 break;
160 }
161
162 if (!guardiansClosestCompStmtAncestor)
163 return false;
164
165 // We need to skip the first CompoundStmt to avoid situation when guardian is
166 // defined in the same scope as guarded variable.
167 const CompoundStmt *FirstCompondStmt = nullptr;
168 for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
169 !guardedVarAncestors.empty();
170 guardedVarAncestors = ctx.getParents(
171 *guardedVarAncestors
172 .begin()) // FIXME - should we handle all of the parents?
173 ) {
174 for (auto &guardedVarAncestor : guardedVarAncestors) {
175 if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
176 if (!FirstCompondStmt) {
177 FirstCompondStmt = CStmtAncestor;
178 continue;
179 }
180 if (CStmtAncestor == guardiansClosestCompStmtAncestor) {
181 GuardianVisitor guardianVisitor(MaybeGuardian);
182 auto *GuardedScope = const_cast<CompoundStmt *>(FirstCompondStmt);
183 return guardianVisitor.TraverseCompoundStmt(GuardedScope);
184 }
185 }
186 }
187 }
188
189 return false;
190}
191
192class RawPtrRefLocalVarsChecker
193 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
194 BugType Bug;
195 EnsureFunctionAnalysis EFA;
196
197protected:
198 mutable BugReporter *BR;
199 mutable std::optional<RetainTypeChecker> RTC;
200
201public:
202 RawPtrRefLocalVarsChecker(const char *description)
203 : Bug(this, description, "WebKit coding guidelines") {}
204
205 virtual std::optional<bool> isUnsafePtr(const QualType T) const = 0;
206 virtual bool isSafePtr(const CXXRecordDecl *) const = 0;
207 virtual bool isSafePtrType(const QualType) const = 0;
208 virtual bool isSafeExpr(const Expr *) const { return false; }
209 virtual bool isSafeDecl(const Decl *) const { return false; }
210 virtual const char *ptrKind() const = 0;
211
212 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
213 BugReporter &BRArg) const {
214 BR = &BRArg;
215
216 // The calls to checkAST* from AnalysisConsumer don't
217 // visit template instantiations or lambda classes. We
218 // want to visit those, so we make our own RecursiveASTVisitor.
219 struct LocalVisitor : DynamicRecursiveASTVisitor {
220 const RawPtrRefLocalVarsChecker *Checker;
221 Decl *DeclWithIssue{nullptr};
222
223 TrivialFunctionAnalysis TFA;
224
225 explicit LocalVisitor(const RawPtrRefLocalVarsChecker *Checker)
226 : Checker(Checker) {
227 assert(Checker);
228 ShouldVisitTemplateInstantiations = true;
229 ShouldVisitImplicitCode = false;
230 }
231
232 bool TraverseDecl(Decl *D) override {
233 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
234 if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
235 DeclWithIssue = D;
237 }
238
239 bool VisitTypedefDecl(TypedefDecl *TD) override {
240 if (Checker->RTC)
241 Checker->RTC->visitTypedef(TD);
242 return true;
243 }
244
245 bool VisitVarDecl(VarDecl *V) override {
246 auto *Init = V->getInit();
247 if (V->isLocalVarDecl())
248 Checker->visitVarDecl(V, Init, DeclWithIssue);
249 return true;
250 }
251
252 bool VisitBinaryOperator(BinaryOperator *BO) override {
253 if (BO->isAssignmentOp()) {
254 if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {
255 if (auto *V = dyn_cast<VarDecl>(VarRef->getDecl()))
256 Checker->visitVarDecl(V, BO->getRHS(), DeclWithIssue);
257 }
258 }
259 return true;
260 }
261
262 bool TraverseIfStmt(IfStmt *IS) override {
263 if (IS->getConditionVariable()) {
264 // This code currently does not explicitly check the "else" statement
265 // since getConditionVariable returns nullptr when there is a
266 // condition defined after ";" as in "if (auto foo = ~; !foo)". If
267 // this semantics change, we should add an explicit check for "else".
268 if (auto *Then = IS->getThen(); !Then || TFA.isTrivial(Then))
269 return true;
270 }
271 if (!TFA.isTrivial(IS))
272 return DynamicRecursiveASTVisitor::TraverseIfStmt(IS);
273 return true;
274 }
275
276 bool TraverseForStmt(ForStmt *FS) override {
277 if (!TFA.isTrivial(FS))
278 return DynamicRecursiveASTVisitor::TraverseForStmt(FS);
279 return true;
280 }
281
282 bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) override {
283 if (!TFA.isTrivial(FRS))
284 return DynamicRecursiveASTVisitor::TraverseCXXForRangeStmt(FRS);
285 return true;
286 }
287
288 bool TraverseWhileStmt(WhileStmt *WS) override {
289 if (!TFA.isTrivial(WS))
290 return DynamicRecursiveASTVisitor::TraverseWhileStmt(WS);
291 return true;
292 }
293
294 bool TraverseCompoundStmt(CompoundStmt *CS) override {
295 if (!TFA.isTrivial(CS))
296 return DynamicRecursiveASTVisitor::TraverseCompoundStmt(CS);
297 return true;
298 }
299
300 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override {
301 if (isSmartPtrClass(safeGetName(Decl)))
302 return true;
303 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);
304 }
305 };
306
307 LocalVisitor visitor(this);
308 if (RTC)
309 RTC->visitTranslationUnitDecl(TUD);
310 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
311 }
312
313 void visitVarDecl(const VarDecl *V, const Expr *Value,
314 const Decl *DeclWithIssue) const {
315 if (shouldSkipVarDecl(V))
316 return;
317
318 if (auto *DD = dyn_cast<DecompositionDecl>(V)) {
319 for (auto *BD : DD->bindings()) {
320 auto *Binding = BD->getBinding();
321 if (!Binding)
322 continue;
323 std::optional<bool> IsUncountedPtr = isUnsafePtr(Binding->getType());
324 if (!IsUncountedPtr || !*IsUncountedPtr)
325 continue;
326 reportBug(V, nullptr, BD, DeclWithIssue);
327 }
328 }
329
330 std::optional<bool> IsUncountedPtr = isUnsafePtr(V->getType());
331 if (IsUncountedPtr && *IsUncountedPtr) {
332 if (Value && isPtrOriginSafe(V, Value, DeclWithIssue))
333 return;
334 reportBug(V, Value, nullptr, DeclWithIssue);
335 }
336 }
337
338 bool isPtrOriginSafe(const VarDecl *V, const Expr *Value,
339 const Decl *DeclWithIssue) const {
340 return tryToFindPtrOrigin(
341 Value, /*StopAtFirstRefCountedObj=*/false,
342 [&](const clang::CXXRecordDecl *Record) { return isSafePtr(Record); },
343 [&](const clang::QualType Type) { return isSafePtrType(Type); },
344 [&](const clang::Decl *D) { return isSafeDecl(D); },
345 [&](const clang::Expr *InitArgOrigin, bool IsSafe) {
346 if (!InitArgOrigin || IsSafe)
347 return true;
348
349 if (isa<CXXThisExpr>(InitArgOrigin))
350 return true;
351
352 if (isNullPtr(InitArgOrigin))
353 return true;
354
355 if (isa<IntegerLiteral>(InitArgOrigin))
356 return true;
357
358 if (isConstOwnerPtrMemberExpr(InitArgOrigin))
359 return true;
360
361 if (EFA.isACallToEnsureFn(InitArgOrigin))
362 return true;
363
364 if (isSafeExpr(InitArgOrigin))
365 return true;
366
367 if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
368 if (auto *MaybeGuardian =
369 dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
370 const auto *MaybeGuardianArgType =
371 MaybeGuardian->getType().getTypePtr();
372 if (MaybeGuardianArgType) {
373 const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
374 MaybeGuardianArgType->getAsCXXRecordDecl();
375 if (MaybeGuardianArgCXXRecord) {
376 if (MaybeGuardian->isLocalVarDecl() &&
377 (isSafePtr(MaybeGuardianArgCXXRecord) ||
378 isRefcountedStringsHack(MaybeGuardian)) &&
379 isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian))
380 return true;
381 }
382 }
383
384 if (isa<ParmVarDecl>(MaybeGuardian)) {
385 if (auto *FD = dyn_cast<FunctionDecl>(DeclWithIssue)) {
386 if (GuardianVisitor{MaybeGuardian}.TraverseStmt(
387 FD->getBody()))
388 return true;
389 }
390 if (auto *MD = dyn_cast<ObjCMethodDecl>(DeclWithIssue)) {
391 if (GuardianVisitor{MaybeGuardian}.TraverseStmt(
392 MD->getBody()))
393 return true;
394 }
395 }
396 }
397 }
398
399 return false;
400 });
401 }
402
403 bool shouldSkipVarDecl(const VarDecl *V) const {
404 assert(V);
406 return true;
407 return BR->getSourceManager().isInSystemHeader(V->getLocation());
408 }
409
410 void reportBug(const VarDecl *V, const Expr *Value, const Decl *BindingDecl,
411 const Decl *DeclWithIssue) const {
412 assert(V);
413 SmallString<100> Buf;
414 llvm::raw_svector_ostream Os(Buf);
415
416 if (isa<ParmVarDecl>(V)) {
417 Os << "Assignment to an " << ptrKind() << " parameter ";
419 Os << " is unsafe.";
420
421 PathDiagnosticLocation BSLoc(Value->getExprLoc(), BR->getSourceManager());
422 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
423 Report->addRange(Value->getSourceRange());
424 BR->emitReport(std::move(Report));
425 } else {
426 if (V->hasLocalStorage())
427 Os << "Local variable ";
428 else if (V->isStaticLocal())
429 Os << "Static local variable ";
430 else if (V->hasGlobalStorage())
431 Os << "Global variable ";
432 else
433 Os << "Variable ";
434 if (BindingDecl)
435 Os << "'" << safeGetName(BindingDecl) << "'";
436 else
438 Os << " is " << ptrKind() << " and unsafe.";
439
440 PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
441 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
442 Report->addRange(V->getSourceRange());
443 Report->setDeclWithIssue(DeclWithIssue);
444 BR->emitReport(std::move(Report));
445 }
446 }
447};
448
449class UncountedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
450public:
451 UncountedLocalVarsChecker()
452 : RawPtrRefLocalVarsChecker("Uncounted raw pointer or reference not "
453 "provably backed by ref-counted variable") {}
454 std::optional<bool> isUnsafePtr(const QualType T) const final {
455 return isUncountedPtr(T);
456 }
457 bool isSafePtr(const CXXRecordDecl *Record) const final {
459 }
460 bool isSafePtrType(const QualType type) const final {
462 }
463 const char *ptrKind() const final { return "uncounted"; }
464};
465
466class UncheckedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
467public:
468 UncheckedLocalVarsChecker()
469 : RawPtrRefLocalVarsChecker("Unchecked raw pointer or reference not "
470 "provably backed by checked variable") {}
471 std::optional<bool> isUnsafePtr(const QualType T) const final {
472 return isUncheckedPtr(T);
473 }
474 bool isSafePtr(const CXXRecordDecl *Record) const final {
476 }
477 bool isSafePtrType(const QualType type) const final {
479 }
480 bool isSafeExpr(const Expr *E) const final {
482 }
483 const char *ptrKind() const final { return "unchecked"; }
484};
485
486class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
487public:
488 UnretainedLocalVarsChecker()
489 : RawPtrRefLocalVarsChecker("Unretained raw pointer or reference not "
490 "provably backed by a RetainPtr") {
491 RTC = RetainTypeChecker();
492 }
493 std::optional<bool> isUnsafePtr(const QualType T) const final {
494 if (T.hasStrongOrWeakObjCLifetime())
495 return false;
496 return RTC->isUnretained(T);
497 }
498 bool isSafePtr(const CXXRecordDecl *Record) const final {
500 }
501 bool isSafePtrType(const QualType type) const final {
503 }
504 bool isSafeExpr(const Expr *E) const final {
505 return ento::cocoa::isCocoaObjectRef(E->getType()) &&
507 }
508 bool isSafeDecl(const Decl *D) const final {
509 // Treat NS/CF globals in system header as immortal.
511 }
512 const char *ptrKind() const final { return "unretained"; }
513};
514
515} // namespace
516
517void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
518 Mgr.registerChecker<UncountedLocalVarsChecker>();
519}
520
521bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
522 return true;
523}
524
525void ento::registerUncheckedLocalVarsChecker(CheckerManager &Mgr) {
526 Mgr.registerChecker<UncheckedLocalVarsChecker>();
527}
528
529bool ento::shouldRegisterUncheckedLocalVarsChecker(const CheckerManager &) {
530 return true;
531}
532
533void ento::registerUnretainedLocalVarsChecker(CheckerManager &Mgr) {
534 Mgr.registerChecker<UnretainedLocalVarsChecker>();
535}
536
537bool ento::shouldRegisterUnretainedLocalVarsChecker(const CheckerManager &) {
538 return true;
539}
#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:226
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
Expr * getLHS() const
Definition Expr.h:4091
Expr * getRHS() const
Definition Expr.h:4093
static bool isAssignmentOp(Opcode Opc)
Definition Expr.h:4177
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:748
Expr * getImplicitObjectArgument() const
Retrieve the implicit object argument for the member call.
Definition ExprCXX.cpp:729
QualType getObjectType() const
Retrieve the type of the object argument.
Definition ExprCXX.cpp:741
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition Expr.h:3129
arg_range arguments()
Definition Expr.h:3198
CompoundStmt - This represents a group of statements like { stmt stmt }.
Definition Stmt.h:1750
ASTContext & getASTContext() const LLVM_READONLY
Definition DeclBase.cpp:547
SourceLocation getLocation() const
Definition DeclBase.h:447
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:3102
QualType getType() const
Definition Expr.h:144
Stmt * getThen()
Definition Stmt.h:2358
VarDecl * getConditionVariable()
Retrieve the variable declared in this "if" statement, if any.
Definition Stmt.cpp:1068
A (possibly-)qualified type.
Definition TypeBase.h:937
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition TypeBase.h:8445
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
bool isTrivial(const Decl *D, const Stmt **OffendingStmt=nullptr) const
QualType getType() const
Definition Decl.h:723
Represents a variable declaration or definition.
Definition Decl.h:924
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
Definition Decl.h:1266
const SourceManager & getSourceManager()
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:550
const internal::VariadicAllOfMatcher< Type > type
Matches Types in the clang AST.
bool isCocoaObjectRef(QualType T)
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.
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
bool isExprToGetCheckedPtrCapableMember(const clang::Expr *E)
Definition ASTUtils.cpp:319
bool isPtrConversion(const FunctionDecl *F)
bool isRefOrCheckedPtrType(const clang::QualType T)
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
bool isRetainPtrOrOSPtrType(const clang::QualType T)
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
bool isSmartPtrClass(const std::string &Name)
bool isRefCounted(const CXXRecordDecl *R)
@ Type
The name was classified as a type.
Definition Sema.h:564
bool isRetainPtrOrOSPtr(const std::string &Name)
std::optional< bool > isUncountedPtr(const QualType T)
bool isSafePtr(clang::CXXRecordDecl *Decl)
Definition ASTUtils.cpp:21
std::string safeGetName(const T *ASTNode)
Definition ASTUtils.h:95
bool isNullPtr(const clang::Expr *E)
Definition ASTUtils.cpp:285
bool isCheckedPtr(const std::string &Name)
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
bool isConstOwnerPtrMemberExpr(const clang::Expr *E)
Definition ASTUtils.cpp:295
std::optional< bool > isUncheckedPtr(const QualType T)