[DFAJumpThreading] Remove incoming StartBlock from all phis when unfolding select...
[llvm-project.git] / clang / lib / StaticAnalyzer / Checkers / WebKit / UncountedLocalVarsChecker.cpp
blobfa74759349810fb25649784337c4973782fea88e
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 //===----------------------------------------------------------------------===//
9 #include "ASTUtils.h"
10 #include "DiagOutputUtils.h"
11 #include "PtrTypesSemantics.h"
12 #include "clang/AST/CXXInheritance.h"
13 #include "clang/AST/Decl.h"
14 #include "clang/AST/DeclCXX.h"
15 #include "clang/AST/ParentMapContext.h"
16 #include "clang/AST/RecursiveASTVisitor.h"
17 #include "clang/Basic/SourceLocation.h"
18 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
19 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
20 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21 #include "clang/StaticAnalyzer/Core/Checker.h"
22 #include "llvm/ADT/DenseSet.h"
23 #include <optional>
25 using namespace clang;
26 using namespace ento;
28 namespace {
30 // for ( int a = ...) ... true
31 // for ( int a : ...) ... true
32 // if ( int* a = ) ... true
33 // anything else ... false
34 bool isDeclaredInForOrIf(const VarDecl *Var) {
35 assert(Var);
36 auto &ASTCtx = Var->getASTContext();
37 auto parent = ASTCtx.getParents(*Var);
39 if (parent.size() == 1) {
40 if (auto *DS = parent.begin()->get<DeclStmt>()) {
41 DynTypedNodeList grandParent = ASTCtx.getParents(*DS);
42 if (grandParent.size() == 1) {
43 return grandParent.begin()->get<ForStmt>() ||
44 grandParent.begin()->get<IfStmt>() ||
45 grandParent.begin()->get<CXXForRangeStmt>();
49 return false;
52 // FIXME: should be defined by anotations in the future
53 bool isRefcountedStringsHack(const VarDecl *V) {
54 assert(V);
55 auto safeClass = [](const std::string &className) {
56 return className == "String" || className == "AtomString" ||
57 className == "UniquedString" || className == "Identifier";
59 QualType QT = V->getType();
60 auto *T = QT.getTypePtr();
61 if (auto *CXXRD = T->getAsCXXRecordDecl()) {
62 if (safeClass(safeGetName(CXXRD)))
63 return true;
65 if (T->isPointerType() || T->isReferenceType()) {
66 if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
67 if (safeClass(safeGetName(CXXRD)))
68 return true;
71 return false;
74 bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
75 const VarDecl *MaybeGuardian) {
76 assert(Guarded);
77 assert(MaybeGuardian);
79 if (!MaybeGuardian->isLocalVarDecl())
80 return false;
82 const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
84 ASTContext &ctx = MaybeGuardian->getASTContext();
86 for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
87 !guardianAncestors.empty();
88 guardianAncestors = ctx.getParents(
89 *guardianAncestors
90 .begin()) // FIXME - should we handle all of the parents?
91 ) {
92 for (auto &guardianAncestor : guardianAncestors) {
93 if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
94 guardiansClosestCompStmtAncestor = CStmtParentAncestor;
95 break;
98 if (guardiansClosestCompStmtAncestor)
99 break;
102 if (!guardiansClosestCompStmtAncestor)
103 return false;
105 // We need to skip the first CompoundStmt to avoid situation when guardian is
106 // defined in the same scope as guarded variable.
107 bool HaveSkippedFirstCompoundStmt = false;
108 for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
109 !guardedVarAncestors.empty();
110 guardedVarAncestors = ctx.getParents(
111 *guardedVarAncestors
112 .begin()) // FIXME - should we handle all of the parents?
114 for (auto &guardedVarAncestor : guardedVarAncestors) {
115 if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
116 if (!HaveSkippedFirstCompoundStmt) {
117 HaveSkippedFirstCompoundStmt = true;
118 continue;
120 if (CStmtAncestor == guardiansClosestCompStmtAncestor)
121 return true;
126 return false;
129 class UncountedLocalVarsChecker
130 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
131 BugType Bug{this,
132 "Uncounted raw pointer or reference not provably backed by "
133 "ref-counted variable",
134 "WebKit coding guidelines"};
135 mutable BugReporter *BR;
137 public:
138 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
139 BugReporter &BRArg) const {
140 BR = &BRArg;
142 // The calls to checkAST* from AnalysisConsumer don't
143 // visit template instantiations or lambda classes. We
144 // want to visit those, so we make our own RecursiveASTVisitor.
145 struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
146 const UncountedLocalVarsChecker *Checker;
147 explicit LocalVisitor(const UncountedLocalVarsChecker *Checker)
148 : Checker(Checker) {
149 assert(Checker);
152 bool shouldVisitTemplateInstantiations() const { return true; }
153 bool shouldVisitImplicitCode() const { return false; }
155 bool VisitVarDecl(VarDecl *V) {
156 Checker->visitVarDecl(V);
157 return true;
161 LocalVisitor visitor(this);
162 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
165 void visitVarDecl(const VarDecl *V) const {
166 if (shouldSkipVarDecl(V))
167 return;
169 const auto *ArgType = V->getType().getTypePtr();
170 if (!ArgType)
171 return;
173 std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
174 if (IsUncountedPtr && *IsUncountedPtr) {
175 const Expr *const InitExpr = V->getInit();
176 if (!InitExpr)
177 return; // FIXME: later on we might warn on uninitialized vars too
179 const clang::Expr *const InitArgOrigin =
180 tryToFindPtrOrigin(InitExpr, /*StopAtFirstRefCountedObj=*/false)
181 .first;
182 if (!InitArgOrigin)
183 return;
185 if (isa<CXXThisExpr>(InitArgOrigin))
186 return;
188 if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
189 if (auto *MaybeGuardian =
190 dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
191 const auto *MaybeGuardianArgType =
192 MaybeGuardian->getType().getTypePtr();
193 if (!MaybeGuardianArgType)
194 return;
195 const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
196 MaybeGuardianArgType->getAsCXXRecordDecl();
197 if (!MaybeGuardianArgCXXRecord)
198 return;
200 if (MaybeGuardian->isLocalVarDecl() &&
201 (isRefCounted(MaybeGuardianArgCXXRecord) ||
202 isRefcountedStringsHack(MaybeGuardian)) &&
203 isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) {
204 return;
207 // Parameters are guaranteed to be safe for the duration of the call
208 // by another checker.
209 if (isa<ParmVarDecl>(MaybeGuardian))
210 return;
214 reportBug(V);
218 bool shouldSkipVarDecl(const VarDecl *V) const {
219 assert(V);
220 if (!V->isLocalVarDecl())
221 return true;
223 if (isDeclaredInForOrIf(V))
224 return true;
226 return false;
229 void reportBug(const VarDecl *V) const {
230 assert(V);
231 SmallString<100> Buf;
232 llvm::raw_svector_ostream Os(Buf);
234 Os << "Local variable ";
235 printQuotedQualifiedName(Os, V);
236 Os << " is uncounted and unsafe.";
238 PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
239 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
240 Report->addRange(V->getSourceRange());
241 BR->emitReport(std::move(Report));
244 } // namespace
246 void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
247 Mgr.registerChecker<UncountedLocalVarsChecker>();
250 bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
251 return true;