clang 19.0.0git
UncountedLocalVarsChecker.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"
22#include <optional>
23
24using namespace clang;
25using namespace ento;
26
27namespace {
28
29// FIXME: should be defined by anotations in the future
30bool isRefcountedStringsHack(const VarDecl *V) {
31 assert(V);
32 auto safeClass = [](const std::string &className) {
33 return className == "String" || className == "AtomString" ||
34 className == "UniquedString" || className == "Identifier";
35 };
36 QualType QT = V->getType();
37 auto *T = QT.getTypePtr();
38 if (auto *CXXRD = T->getAsCXXRecordDecl()) {
39 if (safeClass(safeGetName(CXXRD)))
40 return true;
41 }
42 if (T->isPointerType() || T->isReferenceType()) {
43 if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
44 if (safeClass(safeGetName(CXXRD)))
45 return true;
46 }
47 }
48 return false;
49}
50
51bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
52 const VarDecl *MaybeGuardian) {
53 assert(Guarded);
54 assert(MaybeGuardian);
55
56 if (!MaybeGuardian->isLocalVarDecl())
57 return false;
58
59 const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
60
61 ASTContext &ctx = MaybeGuardian->getASTContext();
62
63 for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
64 !guardianAncestors.empty();
65 guardianAncestors = ctx.getParents(
66 *guardianAncestors
67 .begin()) // FIXME - should we handle all of the parents?
68 ) {
69 for (auto &guardianAncestor : guardianAncestors) {
70 if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
71 guardiansClosestCompStmtAncestor = CStmtParentAncestor;
72 break;
73 }
74 }
75 if (guardiansClosestCompStmtAncestor)
76 break;
77 }
78
79 if (!guardiansClosestCompStmtAncestor)
80 return false;
81
82 // We need to skip the first CompoundStmt to avoid situation when guardian is
83 // defined in the same scope as guarded variable.
84 bool HaveSkippedFirstCompoundStmt = false;
85 for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
86 !guardedVarAncestors.empty();
87 guardedVarAncestors = ctx.getParents(
88 *guardedVarAncestors
89 .begin()) // FIXME - should we handle all of the parents?
90 ) {
91 for (auto &guardedVarAncestor : guardedVarAncestors) {
92 if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
93 if (!HaveSkippedFirstCompoundStmt) {
94 HaveSkippedFirstCompoundStmt = true;
95 continue;
96 }
97 if (CStmtAncestor == guardiansClosestCompStmtAncestor)
98 return true;
99 }
100 }
101 }
102
103 return false;
104}
105
106class UncountedLocalVarsChecker
107 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
108 BugType Bug{this,
109 "Uncounted raw pointer or reference not provably backed by "
110 "ref-counted variable",
111 "WebKit coding guidelines"};
112 mutable BugReporter *BR;
113
114public:
115 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
116 BugReporter &BRArg) const {
117 BR = &BRArg;
118
119 // The calls to checkAST* from AnalysisConsumer don't
120 // visit template instantiations or lambda classes. We
121 // want to visit those, so we make our own RecursiveASTVisitor.
122 struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
123 const UncountedLocalVarsChecker *Checker;
124
126
128
129 explicit LocalVisitor(const UncountedLocalVarsChecker *Checker)
130 : Checker(Checker) {
131 assert(Checker);
132 }
133
134 bool shouldVisitTemplateInstantiations() const { return true; }
135 bool shouldVisitImplicitCode() const { return false; }
136
137 bool VisitVarDecl(VarDecl *V) {
138 Checker->visitVarDecl(V);
139 return true;
140 }
141
142 bool TraverseIfStmt(IfStmt *IS) {
143 if (!TFA.isTrivial(IS))
144 return Base::TraverseIfStmt(IS);
145 return true;
146 }
147
148 bool TraverseForStmt(ForStmt *FS) {
149 if (!TFA.isTrivial(FS))
150 return Base::TraverseForStmt(FS);
151 return true;
152 }
153
154 bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) {
155 if (!TFA.isTrivial(FRS))
156 return Base::TraverseCXXForRangeStmt(FRS);
157 return true;
158 }
159
160 bool TraverseWhileStmt(WhileStmt *WS) {
161 if (!TFA.isTrivial(WS))
162 return Base::TraverseWhileStmt(WS);
163 return true;
164 }
165
166 bool TraverseCompoundStmt(CompoundStmt *CS) {
167 if (!TFA.isTrivial(CS))
168 return Base::TraverseCompoundStmt(CS);
169 return true;
170 }
171 };
172
173 LocalVisitor visitor(this);
174 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
175 }
176
177 void visitVarDecl(const VarDecl *V) const {
178 if (shouldSkipVarDecl(V))
179 return;
180
181 const auto *ArgType = V->getType().getTypePtr();
182 if (!ArgType)
183 return;
184
185 std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
186 if (IsUncountedPtr && *IsUncountedPtr) {
187 const Expr *const InitExpr = V->getInit();
188 if (!InitExpr)
189 return; // FIXME: later on we might warn on uninitialized vars too
190
191 const clang::Expr *const InitArgOrigin =
192 tryToFindPtrOrigin(InitExpr, /*StopAtFirstRefCountedObj=*/false)
193 .first;
194 if (!InitArgOrigin)
195 return;
196
197 if (isa<CXXThisExpr>(InitArgOrigin))
198 return;
199
200 if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
201 if (auto *MaybeGuardian =
202 dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
203 const auto *MaybeGuardianArgType =
204 MaybeGuardian->getType().getTypePtr();
205 if (MaybeGuardianArgType) {
206 const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
207 MaybeGuardianArgType->getAsCXXRecordDecl();
208 if (MaybeGuardianArgCXXRecord) {
209 if (MaybeGuardian->isLocalVarDecl() &&
210 (isRefCounted(MaybeGuardianArgCXXRecord) ||
211 isRefcountedStringsHack(MaybeGuardian)) &&
212 isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian))
213 return;
214 }
215 }
216
217 // Parameters are guaranteed to be safe for the duration of the call
218 // by another checker.
219 if (isa<ParmVarDecl>(MaybeGuardian))
220 return;
221 }
222 }
223
224 reportBug(V);
225 }
226 }
227
228 bool shouldSkipVarDecl(const VarDecl *V) const {
229 assert(V);
230 if (!V->isLocalVarDecl())
231 return true;
232
233 return false;
234 }
235
236 void reportBug(const VarDecl *V) const {
237 assert(V);
239 llvm::raw_svector_ostream Os(Buf);
240
241 Os << "Local variable ";
243 Os << " is uncounted and unsafe.";
244
245 PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
246 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
247 Report->addRange(V->getSourceRange());
248 BR->emitReport(std::move(Report));
249 }
250};
251} // namespace
252
253void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
254 Mgr.registerChecker<UncountedLocalVarsChecker>();
255}
256
257bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
258 return true;
259}
#define V(N, I)
Definition: ASTContext.h:3284
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
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:182
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
CXXForRangeStmt - This represents C++0x [stmt.ranged]'s ranged for statement, represented as 'for (ra...
Definition: StmtCXX.h:135
Represents a C++ struct/union/class.
Definition: DeclCXX.h:258
CompoundStmt - This represents a group of statements like { stmt stmt }.
Definition: Stmt.h:1606
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:501
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
This represents one expression.
Definition: Expr.h:110
ForStmt - This represents a 'for (init;cond;inc)' stmt.
Definition: Stmt.h:2781
IfStmt - This represents an if/then/else.
Definition: Stmt.h:2138
A (possibly-)qualified type.
Definition: Type.h:940
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition: Type.h:7359
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
The top declaration context.
Definition: Decl.h:84
An inter-procedural analysis facility that detects functions with "trivial" behavior with respect to ...
bool isTrivial(const Decl *D) const
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition: Type.cpp:1870
bool isPointerType() const
Definition: Type.h:7612
bool isReferenceType() const
Definition: Type.h:7624
const CXXRecordDecl * getPointeeCXXRecordDecl() const
If this is a pointer or reference to a RecordType, return the CXXRecordDecl that the type refers to.
Definition: Type.cpp:1855
QualType getType() const
Definition: Decl.h:717
Represents a variable declaration or definition.
Definition: Decl.h:918
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
Definition: Decl.h:1240
WhileStmt - This represents a 'while' stmt.
Definition: Stmt.h:2584
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:585
const SourceManager & getSourceManager()
Definition: BugReporter.h:623
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
The JSON file list parser is used to communicate input to InstallAPI.
std::optional< bool > isUncountedPtr(const Type *T)
std::pair< const Expr *, bool > tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj)
This function de-facto defines a set of transformations that we consider safe (in heuristical sense).
Definition: ASTUtils.cpp:20
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
bool isRefCounted(const CXXRecordDecl *R)
const FunctionProtoType * T
std::string safeGetName(const T *ASTNode)
Definition: ASTUtils.h:65