1*700637cbSDimitry Andric //=======- UncountedLocalVarsChecker.cpp -------------------------*- C++ -*-==//
2*700637cbSDimitry Andric //
3*700637cbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*700637cbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*700637cbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*700637cbSDimitry Andric //
7*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
8*700637cbSDimitry Andric
9*700637cbSDimitry Andric #include "ASTUtils.h"
10*700637cbSDimitry Andric #include "DiagOutputUtils.h"
11*700637cbSDimitry Andric #include "PtrTypesSemantics.h"
12*700637cbSDimitry Andric #include "clang/AST/CXXInheritance.h"
13*700637cbSDimitry Andric #include "clang/AST/Decl.h"
14*700637cbSDimitry Andric #include "clang/AST/DeclCXX.h"
15*700637cbSDimitry Andric #include "clang/AST/DynamicRecursiveASTVisitor.h"
16*700637cbSDimitry Andric #include "clang/AST/ParentMapContext.h"
17*700637cbSDimitry Andric #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
18*700637cbSDimitry Andric #include "clang/Basic/SourceLocation.h"
19*700637cbSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
20*700637cbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
21*700637cbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
22*700637cbSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
23*700637cbSDimitry Andric #include <optional>
24*700637cbSDimitry Andric
25*700637cbSDimitry Andric using namespace clang;
26*700637cbSDimitry Andric using namespace ento;
27*700637cbSDimitry Andric
28*700637cbSDimitry Andric namespace {
29*700637cbSDimitry Andric
30*700637cbSDimitry Andric // FIXME: should be defined by anotations in the future
isRefcountedStringsHack(const VarDecl * V)31*700637cbSDimitry Andric bool isRefcountedStringsHack(const VarDecl *V) {
32*700637cbSDimitry Andric assert(V);
33*700637cbSDimitry Andric auto safeClass = [](const std::string &className) {
34*700637cbSDimitry Andric return className == "String" || className == "AtomString" ||
35*700637cbSDimitry Andric className == "UniquedString" || className == "Identifier";
36*700637cbSDimitry Andric };
37*700637cbSDimitry Andric QualType QT = V->getType();
38*700637cbSDimitry Andric auto *T = QT.getTypePtr();
39*700637cbSDimitry Andric if (auto *CXXRD = T->getAsCXXRecordDecl()) {
40*700637cbSDimitry Andric if (safeClass(safeGetName(CXXRD)))
41*700637cbSDimitry Andric return true;
42*700637cbSDimitry Andric }
43*700637cbSDimitry Andric if (T->isPointerType() || T->isReferenceType()) {
44*700637cbSDimitry Andric if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
45*700637cbSDimitry Andric if (safeClass(safeGetName(CXXRD)))
46*700637cbSDimitry Andric return true;
47*700637cbSDimitry Andric }
48*700637cbSDimitry Andric }
49*700637cbSDimitry Andric return false;
50*700637cbSDimitry Andric }
51*700637cbSDimitry Andric
52*700637cbSDimitry Andric struct GuardianVisitor : DynamicRecursiveASTVisitor {
53*700637cbSDimitry Andric const VarDecl *Guardian{nullptr};
54*700637cbSDimitry Andric
GuardianVisitor__anon4bbed29c0111::GuardianVisitor55*700637cbSDimitry Andric explicit GuardianVisitor(const VarDecl *Guardian) : Guardian(Guardian) {
56*700637cbSDimitry Andric assert(Guardian);
57*700637cbSDimitry Andric }
58*700637cbSDimitry Andric
VisitBinaryOperator__anon4bbed29c0111::GuardianVisitor59*700637cbSDimitry Andric bool VisitBinaryOperator(BinaryOperator *BO) override {
60*700637cbSDimitry Andric if (BO->isAssignmentOp()) {
61*700637cbSDimitry Andric if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {
62*700637cbSDimitry Andric if (VarRef->getDecl() == Guardian)
63*700637cbSDimitry Andric return false;
64*700637cbSDimitry Andric }
65*700637cbSDimitry Andric }
66*700637cbSDimitry Andric return true;
67*700637cbSDimitry Andric }
68*700637cbSDimitry Andric
VisitCXXConstructExpr__anon4bbed29c0111::GuardianVisitor69*700637cbSDimitry Andric bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {
70*700637cbSDimitry Andric if (auto *Ctor = CE->getConstructor()) {
71*700637cbSDimitry Andric if (Ctor->isMoveConstructor() && CE->getNumArgs() == 1) {
72*700637cbSDimitry Andric auto *Arg = CE->getArg(0)->IgnoreParenCasts();
73*700637cbSDimitry Andric if (auto *VarRef = dyn_cast<DeclRefExpr>(Arg)) {
74*700637cbSDimitry Andric if (VarRef->getDecl() == Guardian)
75*700637cbSDimitry Andric return false;
76*700637cbSDimitry Andric }
77*700637cbSDimitry Andric }
78*700637cbSDimitry Andric }
79*700637cbSDimitry Andric return true;
80*700637cbSDimitry Andric }
81*700637cbSDimitry Andric
VisitCXXMemberCallExpr__anon4bbed29c0111::GuardianVisitor82*700637cbSDimitry Andric bool VisitCXXMemberCallExpr(CXXMemberCallExpr *MCE) override {
83*700637cbSDimitry Andric auto MethodName = safeGetName(MCE->getMethodDecl());
84*700637cbSDimitry Andric if (MethodName == "swap" || MethodName == "leakRef" ||
85*700637cbSDimitry Andric MethodName == "releaseNonNull" || MethodName == "clear") {
86*700637cbSDimitry Andric auto *ThisArg = MCE->getImplicitObjectArgument()->IgnoreParenCasts();
87*700637cbSDimitry Andric if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) {
88*700637cbSDimitry Andric if (VarRef->getDecl() == Guardian)
89*700637cbSDimitry Andric return false;
90*700637cbSDimitry Andric }
91*700637cbSDimitry Andric }
92*700637cbSDimitry Andric return true;
93*700637cbSDimitry Andric }
94*700637cbSDimitry Andric
VisitCXXOperatorCallExpr__anon4bbed29c0111::GuardianVisitor95*700637cbSDimitry Andric bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *OCE) override {
96*700637cbSDimitry Andric if (OCE->isAssignmentOp()) {
97*700637cbSDimitry Andric assert(OCE->getNumArgs() == 2);
98*700637cbSDimitry Andric auto *ThisArg = OCE->getArg(0)->IgnoreParenCasts();
99*700637cbSDimitry Andric if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) {
100*700637cbSDimitry Andric if (VarRef->getDecl() == Guardian)
101*700637cbSDimitry Andric return false;
102*700637cbSDimitry Andric }
103*700637cbSDimitry Andric }
104*700637cbSDimitry Andric return true;
105*700637cbSDimitry Andric }
106*700637cbSDimitry Andric };
107*700637cbSDimitry Andric
isGuardedScopeEmbeddedInGuardianScope(const VarDecl * Guarded,const VarDecl * MaybeGuardian)108*700637cbSDimitry Andric bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
109*700637cbSDimitry Andric const VarDecl *MaybeGuardian) {
110*700637cbSDimitry Andric assert(Guarded);
111*700637cbSDimitry Andric assert(MaybeGuardian);
112*700637cbSDimitry Andric
113*700637cbSDimitry Andric if (!MaybeGuardian->isLocalVarDecl())
114*700637cbSDimitry Andric return false;
115*700637cbSDimitry Andric
116*700637cbSDimitry Andric const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
117*700637cbSDimitry Andric
118*700637cbSDimitry Andric ASTContext &ctx = MaybeGuardian->getASTContext();
119*700637cbSDimitry Andric
120*700637cbSDimitry Andric for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
121*700637cbSDimitry Andric !guardianAncestors.empty();
122*700637cbSDimitry Andric guardianAncestors = ctx.getParents(
123*700637cbSDimitry Andric *guardianAncestors
124*700637cbSDimitry Andric .begin()) // FIXME - should we handle all of the parents?
125*700637cbSDimitry Andric ) {
126*700637cbSDimitry Andric for (auto &guardianAncestor : guardianAncestors) {
127*700637cbSDimitry Andric if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
128*700637cbSDimitry Andric guardiansClosestCompStmtAncestor = CStmtParentAncestor;
129*700637cbSDimitry Andric break;
130*700637cbSDimitry Andric }
131*700637cbSDimitry Andric }
132*700637cbSDimitry Andric if (guardiansClosestCompStmtAncestor)
133*700637cbSDimitry Andric break;
134*700637cbSDimitry Andric }
135*700637cbSDimitry Andric
136*700637cbSDimitry Andric if (!guardiansClosestCompStmtAncestor)
137*700637cbSDimitry Andric return false;
138*700637cbSDimitry Andric
139*700637cbSDimitry Andric // We need to skip the first CompoundStmt to avoid situation when guardian is
140*700637cbSDimitry Andric // defined in the same scope as guarded variable.
141*700637cbSDimitry Andric const CompoundStmt *FirstCompondStmt = nullptr;
142*700637cbSDimitry Andric for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
143*700637cbSDimitry Andric !guardedVarAncestors.empty();
144*700637cbSDimitry Andric guardedVarAncestors = ctx.getParents(
145*700637cbSDimitry Andric *guardedVarAncestors
146*700637cbSDimitry Andric .begin()) // FIXME - should we handle all of the parents?
147*700637cbSDimitry Andric ) {
148*700637cbSDimitry Andric for (auto &guardedVarAncestor : guardedVarAncestors) {
149*700637cbSDimitry Andric if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
150*700637cbSDimitry Andric if (!FirstCompondStmt) {
151*700637cbSDimitry Andric FirstCompondStmt = CStmtAncestor;
152*700637cbSDimitry Andric continue;
153*700637cbSDimitry Andric }
154*700637cbSDimitry Andric if (CStmtAncestor == guardiansClosestCompStmtAncestor) {
155*700637cbSDimitry Andric GuardianVisitor guardianVisitor(MaybeGuardian);
156*700637cbSDimitry Andric auto *GuardedScope = const_cast<CompoundStmt *>(FirstCompondStmt);
157*700637cbSDimitry Andric return guardianVisitor.TraverseCompoundStmt(GuardedScope);
158*700637cbSDimitry Andric }
159*700637cbSDimitry Andric }
160*700637cbSDimitry Andric }
161*700637cbSDimitry Andric }
162*700637cbSDimitry Andric
163*700637cbSDimitry Andric return false;
164*700637cbSDimitry Andric }
165*700637cbSDimitry Andric
166*700637cbSDimitry Andric class RawPtrRefLocalVarsChecker
167*700637cbSDimitry Andric : public Checker<check::ASTDecl<TranslationUnitDecl>> {
168*700637cbSDimitry Andric BugType Bug;
169*700637cbSDimitry Andric mutable BugReporter *BR;
170*700637cbSDimitry Andric EnsureFunctionAnalysis EFA;
171*700637cbSDimitry Andric
172*700637cbSDimitry Andric protected:
173*700637cbSDimitry Andric mutable std::optional<RetainTypeChecker> RTC;
174*700637cbSDimitry Andric
175*700637cbSDimitry Andric public:
RawPtrRefLocalVarsChecker(const char * description)176*700637cbSDimitry Andric RawPtrRefLocalVarsChecker(const char *description)
177*700637cbSDimitry Andric : Bug(this, description, "WebKit coding guidelines") {}
178*700637cbSDimitry Andric
179*700637cbSDimitry Andric virtual std::optional<bool> isUnsafePtr(const QualType T) const = 0;
180*700637cbSDimitry Andric virtual bool isSafePtr(const CXXRecordDecl *) const = 0;
181*700637cbSDimitry Andric virtual bool isSafePtrType(const QualType) const = 0;
isSafeExpr(const Expr *) const182*700637cbSDimitry Andric virtual bool isSafeExpr(const Expr *) const { return false; }
183*700637cbSDimitry Andric virtual const char *ptrKind() const = 0;
184*700637cbSDimitry Andric
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const185*700637cbSDimitry Andric void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
186*700637cbSDimitry Andric BugReporter &BRArg) const {
187*700637cbSDimitry Andric BR = &BRArg;
188*700637cbSDimitry Andric
189*700637cbSDimitry Andric // The calls to checkAST* from AnalysisConsumer don't
190*700637cbSDimitry Andric // visit template instantiations or lambda classes. We
191*700637cbSDimitry Andric // want to visit those, so we make our own RecursiveASTVisitor.
192*700637cbSDimitry Andric struct LocalVisitor : DynamicRecursiveASTVisitor {
193*700637cbSDimitry Andric const RawPtrRefLocalVarsChecker *Checker;
194*700637cbSDimitry Andric Decl *DeclWithIssue{nullptr};
195*700637cbSDimitry Andric
196*700637cbSDimitry Andric TrivialFunctionAnalysis TFA;
197*700637cbSDimitry Andric
198*700637cbSDimitry Andric explicit LocalVisitor(const RawPtrRefLocalVarsChecker *Checker)
199*700637cbSDimitry Andric : Checker(Checker) {
200*700637cbSDimitry Andric assert(Checker);
201*700637cbSDimitry Andric ShouldVisitTemplateInstantiations = true;
202*700637cbSDimitry Andric ShouldVisitImplicitCode = false;
203*700637cbSDimitry Andric }
204*700637cbSDimitry Andric
205*700637cbSDimitry Andric bool TraverseDecl(Decl *D) override {
206*700637cbSDimitry Andric llvm::SaveAndRestore SavedDecl(DeclWithIssue);
207*700637cbSDimitry Andric if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
208*700637cbSDimitry Andric DeclWithIssue = D;
209*700637cbSDimitry Andric return DynamicRecursiveASTVisitor::TraverseDecl(D);
210*700637cbSDimitry Andric }
211*700637cbSDimitry Andric
212*700637cbSDimitry Andric bool VisitTypedefDecl(TypedefDecl *TD) override {
213*700637cbSDimitry Andric if (Checker->RTC)
214*700637cbSDimitry Andric Checker->RTC->visitTypedef(TD);
215*700637cbSDimitry Andric return true;
216*700637cbSDimitry Andric }
217*700637cbSDimitry Andric
218*700637cbSDimitry Andric bool VisitVarDecl(VarDecl *V) override {
219*700637cbSDimitry Andric auto *Init = V->getInit();
220*700637cbSDimitry Andric if (Init && V->isLocalVarDecl())
221*700637cbSDimitry Andric Checker->visitVarDecl(V, Init, DeclWithIssue);
222*700637cbSDimitry Andric return true;
223*700637cbSDimitry Andric }
224*700637cbSDimitry Andric
225*700637cbSDimitry Andric bool VisitBinaryOperator(BinaryOperator *BO) override {
226*700637cbSDimitry Andric if (BO->isAssignmentOp()) {
227*700637cbSDimitry Andric if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {
228*700637cbSDimitry Andric if (auto *V = dyn_cast<VarDecl>(VarRef->getDecl()))
229*700637cbSDimitry Andric Checker->visitVarDecl(V, BO->getRHS(), DeclWithIssue);
230*700637cbSDimitry Andric }
231*700637cbSDimitry Andric }
232*700637cbSDimitry Andric return true;
233*700637cbSDimitry Andric }
234*700637cbSDimitry Andric
235*700637cbSDimitry Andric bool TraverseIfStmt(IfStmt *IS) override {
236*700637cbSDimitry Andric if (!TFA.isTrivial(IS))
237*700637cbSDimitry Andric return DynamicRecursiveASTVisitor::TraverseIfStmt(IS);
238*700637cbSDimitry Andric return true;
239*700637cbSDimitry Andric }
240*700637cbSDimitry Andric
241*700637cbSDimitry Andric bool TraverseForStmt(ForStmt *FS) override {
242*700637cbSDimitry Andric if (!TFA.isTrivial(FS))
243*700637cbSDimitry Andric return DynamicRecursiveASTVisitor::TraverseForStmt(FS);
244*700637cbSDimitry Andric return true;
245*700637cbSDimitry Andric }
246*700637cbSDimitry Andric
247*700637cbSDimitry Andric bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) override {
248*700637cbSDimitry Andric if (!TFA.isTrivial(FRS))
249*700637cbSDimitry Andric return DynamicRecursiveASTVisitor::TraverseCXXForRangeStmt(FRS);
250*700637cbSDimitry Andric return true;
251*700637cbSDimitry Andric }
252*700637cbSDimitry Andric
253*700637cbSDimitry Andric bool TraverseWhileStmt(WhileStmt *WS) override {
254*700637cbSDimitry Andric if (!TFA.isTrivial(WS))
255*700637cbSDimitry Andric return DynamicRecursiveASTVisitor::TraverseWhileStmt(WS);
256*700637cbSDimitry Andric return true;
257*700637cbSDimitry Andric }
258*700637cbSDimitry Andric
259*700637cbSDimitry Andric bool TraverseCompoundStmt(CompoundStmt *CS) override {
260*700637cbSDimitry Andric if (!TFA.isTrivial(CS))
261*700637cbSDimitry Andric return DynamicRecursiveASTVisitor::TraverseCompoundStmt(CS);
262*700637cbSDimitry Andric return true;
263*700637cbSDimitry Andric }
264*700637cbSDimitry Andric
265*700637cbSDimitry Andric bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override {
266*700637cbSDimitry Andric if (isSmartPtrClass(safeGetName(Decl)))
267*700637cbSDimitry Andric return true;
268*700637cbSDimitry Andric return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);
269*700637cbSDimitry Andric }
270*700637cbSDimitry Andric };
271*700637cbSDimitry Andric
272*700637cbSDimitry Andric LocalVisitor visitor(this);
273*700637cbSDimitry Andric if (RTC)
274*700637cbSDimitry Andric RTC->visitTranslationUnitDecl(TUD);
275*700637cbSDimitry Andric visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
276*700637cbSDimitry Andric }
277*700637cbSDimitry Andric
visitVarDecl(const VarDecl * V,const Expr * Value,const Decl * DeclWithIssue) const278*700637cbSDimitry Andric void visitVarDecl(const VarDecl *V, const Expr *Value,
279*700637cbSDimitry Andric const Decl *DeclWithIssue) const {
280*700637cbSDimitry Andric if (shouldSkipVarDecl(V))
281*700637cbSDimitry Andric return;
282*700637cbSDimitry Andric
283*700637cbSDimitry Andric std::optional<bool> IsUncountedPtr = isUnsafePtr(V->getType());
284*700637cbSDimitry Andric if (IsUncountedPtr && *IsUncountedPtr) {
285*700637cbSDimitry Andric if (tryToFindPtrOrigin(
286*700637cbSDimitry Andric Value, /*StopAtFirstRefCountedObj=*/false,
287*700637cbSDimitry Andric [&](const clang::CXXRecordDecl *Record) {
288*700637cbSDimitry Andric return isSafePtr(Record);
289*700637cbSDimitry Andric },
290*700637cbSDimitry Andric [&](const clang::QualType Type) { return isSafePtrType(Type); },
291*700637cbSDimitry Andric [&](const clang::Expr *InitArgOrigin, bool IsSafe) {
292*700637cbSDimitry Andric if (!InitArgOrigin || IsSafe)
293*700637cbSDimitry Andric return true;
294*700637cbSDimitry Andric
295*700637cbSDimitry Andric if (isa<CXXThisExpr>(InitArgOrigin))
296*700637cbSDimitry Andric return true;
297*700637cbSDimitry Andric
298*700637cbSDimitry Andric if (isa<CXXNullPtrLiteralExpr>(InitArgOrigin))
299*700637cbSDimitry Andric return true;
300*700637cbSDimitry Andric
301*700637cbSDimitry Andric if (isa<IntegerLiteral>(InitArgOrigin))
302*700637cbSDimitry Andric return true;
303*700637cbSDimitry Andric
304*700637cbSDimitry Andric if (isConstOwnerPtrMemberExpr(InitArgOrigin))
305*700637cbSDimitry Andric return true;
306*700637cbSDimitry Andric
307*700637cbSDimitry Andric if (EFA.isACallToEnsureFn(InitArgOrigin))
308*700637cbSDimitry Andric return true;
309*700637cbSDimitry Andric
310*700637cbSDimitry Andric if (isSafeExpr(InitArgOrigin))
311*700637cbSDimitry Andric return true;
312*700637cbSDimitry Andric
313*700637cbSDimitry Andric if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
314*700637cbSDimitry Andric if (auto *MaybeGuardian =
315*700637cbSDimitry Andric dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
316*700637cbSDimitry Andric const auto *MaybeGuardianArgType =
317*700637cbSDimitry Andric MaybeGuardian->getType().getTypePtr();
318*700637cbSDimitry Andric if (MaybeGuardianArgType) {
319*700637cbSDimitry Andric const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
320*700637cbSDimitry Andric MaybeGuardianArgType->getAsCXXRecordDecl();
321*700637cbSDimitry Andric if (MaybeGuardianArgCXXRecord) {
322*700637cbSDimitry Andric if (MaybeGuardian->isLocalVarDecl() &&
323*700637cbSDimitry Andric (isSafePtr(MaybeGuardianArgCXXRecord) ||
324*700637cbSDimitry Andric isRefcountedStringsHack(MaybeGuardian)) &&
325*700637cbSDimitry Andric isGuardedScopeEmbeddedInGuardianScope(
326*700637cbSDimitry Andric V, MaybeGuardian))
327*700637cbSDimitry Andric return true;
328*700637cbSDimitry Andric }
329*700637cbSDimitry Andric }
330*700637cbSDimitry Andric
331*700637cbSDimitry Andric // Parameters are guaranteed to be safe for the duration of
332*700637cbSDimitry Andric // the call by another checker.
333*700637cbSDimitry Andric if (isa<ParmVarDecl>(MaybeGuardian))
334*700637cbSDimitry Andric return true;
335*700637cbSDimitry Andric }
336*700637cbSDimitry Andric }
337*700637cbSDimitry Andric
338*700637cbSDimitry Andric return false;
339*700637cbSDimitry Andric }))
340*700637cbSDimitry Andric return;
341*700637cbSDimitry Andric
342*700637cbSDimitry Andric reportBug(V, Value, DeclWithIssue);
343*700637cbSDimitry Andric }
344*700637cbSDimitry Andric }
345*700637cbSDimitry Andric
shouldSkipVarDecl(const VarDecl * V) const346*700637cbSDimitry Andric bool shouldSkipVarDecl(const VarDecl *V) const {
347*700637cbSDimitry Andric assert(V);
348*700637cbSDimitry Andric if (isa<ImplicitParamDecl>(V))
349*700637cbSDimitry Andric return true;
350*700637cbSDimitry Andric return BR->getSourceManager().isInSystemHeader(V->getLocation());
351*700637cbSDimitry Andric }
352*700637cbSDimitry Andric
reportBug(const VarDecl * V,const Expr * Value,const Decl * DeclWithIssue) const353*700637cbSDimitry Andric void reportBug(const VarDecl *V, const Expr *Value,
354*700637cbSDimitry Andric const Decl *DeclWithIssue) const {
355*700637cbSDimitry Andric assert(V);
356*700637cbSDimitry Andric SmallString<100> Buf;
357*700637cbSDimitry Andric llvm::raw_svector_ostream Os(Buf);
358*700637cbSDimitry Andric
359*700637cbSDimitry Andric if (isa<ParmVarDecl>(V)) {
360*700637cbSDimitry Andric Os << "Assignment to an " << ptrKind() << " parameter ";
361*700637cbSDimitry Andric printQuotedQualifiedName(Os, V);
362*700637cbSDimitry Andric Os << " is unsafe.";
363*700637cbSDimitry Andric
364*700637cbSDimitry Andric PathDiagnosticLocation BSLoc(Value->getExprLoc(), BR->getSourceManager());
365*700637cbSDimitry Andric auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
366*700637cbSDimitry Andric Report->addRange(Value->getSourceRange());
367*700637cbSDimitry Andric BR->emitReport(std::move(Report));
368*700637cbSDimitry Andric } else {
369*700637cbSDimitry Andric if (V->hasLocalStorage())
370*700637cbSDimitry Andric Os << "Local variable ";
371*700637cbSDimitry Andric else if (V->isStaticLocal())
372*700637cbSDimitry Andric Os << "Static local variable ";
373*700637cbSDimitry Andric else if (V->hasGlobalStorage())
374*700637cbSDimitry Andric Os << "Global variable ";
375*700637cbSDimitry Andric else
376*700637cbSDimitry Andric Os << "Variable ";
377*700637cbSDimitry Andric printQuotedQualifiedName(Os, V);
378*700637cbSDimitry Andric Os << " is " << ptrKind() << " and unsafe.";
379*700637cbSDimitry Andric
380*700637cbSDimitry Andric PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
381*700637cbSDimitry Andric auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
382*700637cbSDimitry Andric Report->addRange(V->getSourceRange());
383*700637cbSDimitry Andric Report->setDeclWithIssue(DeclWithIssue);
384*700637cbSDimitry Andric BR->emitReport(std::move(Report));
385*700637cbSDimitry Andric }
386*700637cbSDimitry Andric }
387*700637cbSDimitry Andric };
388*700637cbSDimitry Andric
389*700637cbSDimitry Andric class UncountedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
390*700637cbSDimitry Andric public:
UncountedLocalVarsChecker()391*700637cbSDimitry Andric UncountedLocalVarsChecker()
392*700637cbSDimitry Andric : RawPtrRefLocalVarsChecker("Uncounted raw pointer or reference not "
393*700637cbSDimitry Andric "provably backed by ref-counted variable") {}
isUnsafePtr(const QualType T) const394*700637cbSDimitry Andric std::optional<bool> isUnsafePtr(const QualType T) const final {
395*700637cbSDimitry Andric return isUncountedPtr(T);
396*700637cbSDimitry Andric }
isSafePtr(const CXXRecordDecl * Record) const397*700637cbSDimitry Andric bool isSafePtr(const CXXRecordDecl *Record) const final {
398*700637cbSDimitry Andric return isRefCounted(Record) || isCheckedPtr(Record);
399*700637cbSDimitry Andric }
isSafePtrType(const QualType type) const400*700637cbSDimitry Andric bool isSafePtrType(const QualType type) const final {
401*700637cbSDimitry Andric return isRefOrCheckedPtrType(type);
402*700637cbSDimitry Andric }
ptrKind() const403*700637cbSDimitry Andric const char *ptrKind() const final { return "uncounted"; }
404*700637cbSDimitry Andric };
405*700637cbSDimitry Andric
406*700637cbSDimitry Andric class UncheckedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
407*700637cbSDimitry Andric public:
UncheckedLocalVarsChecker()408*700637cbSDimitry Andric UncheckedLocalVarsChecker()
409*700637cbSDimitry Andric : RawPtrRefLocalVarsChecker("Unchecked raw pointer or reference not "
410*700637cbSDimitry Andric "provably backed by checked variable") {}
isUnsafePtr(const QualType T) const411*700637cbSDimitry Andric std::optional<bool> isUnsafePtr(const QualType T) const final {
412*700637cbSDimitry Andric return isUncheckedPtr(T);
413*700637cbSDimitry Andric }
isSafePtr(const CXXRecordDecl * Record) const414*700637cbSDimitry Andric bool isSafePtr(const CXXRecordDecl *Record) const final {
415*700637cbSDimitry Andric return isRefCounted(Record) || isCheckedPtr(Record);
416*700637cbSDimitry Andric }
isSafePtrType(const QualType type) const417*700637cbSDimitry Andric bool isSafePtrType(const QualType type) const final {
418*700637cbSDimitry Andric return isRefOrCheckedPtrType(type);
419*700637cbSDimitry Andric }
isSafeExpr(const Expr * E) const420*700637cbSDimitry Andric bool isSafeExpr(const Expr *E) const final {
421*700637cbSDimitry Andric return isExprToGetCheckedPtrCapableMember(E);
422*700637cbSDimitry Andric }
ptrKind() const423*700637cbSDimitry Andric const char *ptrKind() const final { return "unchecked"; }
424*700637cbSDimitry Andric };
425*700637cbSDimitry Andric
426*700637cbSDimitry Andric class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
427*700637cbSDimitry Andric public:
UnretainedLocalVarsChecker()428*700637cbSDimitry Andric UnretainedLocalVarsChecker()
429*700637cbSDimitry Andric : RawPtrRefLocalVarsChecker("Unretained raw pointer or reference not "
430*700637cbSDimitry Andric "provably backed by a RetainPtr") {
431*700637cbSDimitry Andric RTC = RetainTypeChecker();
432*700637cbSDimitry Andric }
isUnsafePtr(const QualType T) const433*700637cbSDimitry Andric std::optional<bool> isUnsafePtr(const QualType T) const final {
434*700637cbSDimitry Andric return RTC->isUnretained(T);
435*700637cbSDimitry Andric }
isSafePtr(const CXXRecordDecl * Record) const436*700637cbSDimitry Andric bool isSafePtr(const CXXRecordDecl *Record) const final {
437*700637cbSDimitry Andric return isRetainPtr(Record);
438*700637cbSDimitry Andric }
isSafePtrType(const QualType type) const439*700637cbSDimitry Andric bool isSafePtrType(const QualType type) const final {
440*700637cbSDimitry Andric return isRetainPtrType(type);
441*700637cbSDimitry Andric }
isSafeExpr(const Expr * E) const442*700637cbSDimitry Andric bool isSafeExpr(const Expr *E) const final {
443*700637cbSDimitry Andric return ento::cocoa::isCocoaObjectRef(E->getType()) &&
444*700637cbSDimitry Andric isa<ObjCMessageExpr>(E);
445*700637cbSDimitry Andric }
ptrKind() const446*700637cbSDimitry Andric const char *ptrKind() const final { return "unretained"; }
447*700637cbSDimitry Andric };
448*700637cbSDimitry Andric
449*700637cbSDimitry Andric } // namespace
450*700637cbSDimitry Andric
registerUncountedLocalVarsChecker(CheckerManager & Mgr)451*700637cbSDimitry Andric void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
452*700637cbSDimitry Andric Mgr.registerChecker<UncountedLocalVarsChecker>();
453*700637cbSDimitry Andric }
454*700637cbSDimitry Andric
shouldRegisterUncountedLocalVarsChecker(const CheckerManager &)455*700637cbSDimitry Andric bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
456*700637cbSDimitry Andric return true;
457*700637cbSDimitry Andric }
458*700637cbSDimitry Andric
registerUncheckedLocalVarsChecker(CheckerManager & Mgr)459*700637cbSDimitry Andric void ento::registerUncheckedLocalVarsChecker(CheckerManager &Mgr) {
460*700637cbSDimitry Andric Mgr.registerChecker<UncheckedLocalVarsChecker>();
461*700637cbSDimitry Andric }
462*700637cbSDimitry Andric
shouldRegisterUncheckedLocalVarsChecker(const CheckerManager &)463*700637cbSDimitry Andric bool ento::shouldRegisterUncheckedLocalVarsChecker(const CheckerManager &) {
464*700637cbSDimitry Andric return true;
465*700637cbSDimitry Andric }
466*700637cbSDimitry Andric
registerUnretainedLocalVarsChecker(CheckerManager & Mgr)467*700637cbSDimitry Andric void ento::registerUnretainedLocalVarsChecker(CheckerManager &Mgr) {
468*700637cbSDimitry Andric Mgr.registerChecker<UnretainedLocalVarsChecker>();
469*700637cbSDimitry Andric }
470*700637cbSDimitry Andric
shouldRegisterUnretainedLocalVarsChecker(const CheckerManager &)471*700637cbSDimitry Andric bool ento::shouldRegisterUnretainedLocalVarsChecker(const CheckerManager &) {
472*700637cbSDimitry Andric return true;
473*700637cbSDimitry Andric }
474