1 //=======- UncountedLocalVarsChecker.cpp -------------------------*- C++ -*-==//
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
7 //===----------------------------------------------------------------------===//
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"
25 using namespace clang
;
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
) {
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
>();
52 // FIXME: should be defined by anotations in the future
53 bool isRefcountedStringsHack(const VarDecl
*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
)))
65 if (T
->isPointerType() || T
->isReferenceType()) {
66 if (auto *CXXRD
= T
->getPointeeCXXRecordDecl()) {
67 if (safeClass(safeGetName(CXXRD
)))
74 bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl
*Guarded
,
75 const VarDecl
*MaybeGuardian
) {
77 assert(MaybeGuardian
);
79 if (!MaybeGuardian
->isLocalVarDecl())
82 const CompoundStmt
*guardiansClosestCompStmtAncestor
= nullptr;
84 ASTContext
&ctx
= MaybeGuardian
->getASTContext();
86 for (DynTypedNodeList guardianAncestors
= ctx
.getParents(*MaybeGuardian
);
87 !guardianAncestors
.empty();
88 guardianAncestors
= ctx
.getParents(
90 .begin()) // FIXME - should we handle all of the parents?
92 for (auto &guardianAncestor
: guardianAncestors
) {
93 if (auto *CStmtParentAncestor
= guardianAncestor
.get
<CompoundStmt
>()) {
94 guardiansClosestCompStmtAncestor
= CStmtParentAncestor
;
98 if (guardiansClosestCompStmtAncestor
)
102 if (!guardiansClosestCompStmtAncestor
)
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(
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;
120 if (CStmtAncestor
== guardiansClosestCompStmtAncestor
)
129 class UncountedLocalVarsChecker
130 : public Checker
<check::ASTDecl
<TranslationUnitDecl
>> {
132 "Uncounted raw pointer or reference not provably backed by "
133 "ref-counted variable",
134 "WebKit coding guidelines"};
135 mutable BugReporter
*BR
;
138 void checkASTDecl(const TranslationUnitDecl
*TUD
, AnalysisManager
&MGR
,
139 BugReporter
&BRArg
) const {
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
)
152 bool shouldVisitTemplateInstantiations() const { return true; }
153 bool shouldVisitImplicitCode() const { return false; }
155 bool VisitVarDecl(VarDecl
*V
) {
156 Checker
->visitVarDecl(V
);
161 LocalVisitor
visitor(this);
162 visitor
.TraverseDecl(const_cast<TranslationUnitDecl
*>(TUD
));
165 void visitVarDecl(const VarDecl
*V
) const {
166 if (shouldSkipVarDecl(V
))
169 const auto *ArgType
= V
->getType().getTypePtr();
173 std::optional
<bool> IsUncountedPtr
= isUncountedPtr(ArgType
);
174 if (IsUncountedPtr
&& *IsUncountedPtr
) {
175 const Expr
*const InitExpr
= V
->getInit();
177 return; // FIXME: later on we might warn on uninitialized vars too
179 const clang::Expr
*const InitArgOrigin
=
180 tryToFindPtrOrigin(InitExpr
, /*StopAtFirstRefCountedObj=*/false)
185 if (isa
<CXXThisExpr
>(InitArgOrigin
))
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
)
195 const CXXRecordDecl
*const MaybeGuardianArgCXXRecord
=
196 MaybeGuardianArgType
->getAsCXXRecordDecl();
197 if (!MaybeGuardianArgCXXRecord
)
200 if (MaybeGuardian
->isLocalVarDecl() &&
201 (isRefCounted(MaybeGuardianArgCXXRecord
) ||
202 isRefcountedStringsHack(MaybeGuardian
)) &&
203 isGuardedScopeEmbeddedInGuardianScope(V
, MaybeGuardian
)) {
207 // Parameters are guaranteed to be safe for the duration of the call
208 // by another checker.
209 if (isa
<ParmVarDecl
>(MaybeGuardian
))
218 bool shouldSkipVarDecl(const VarDecl
*V
) const {
220 if (!V
->isLocalVarDecl())
223 if (isDeclaredInForOrIf(V
))
229 void reportBug(const VarDecl
*V
) const {
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
));
246 void ento::registerUncountedLocalVarsChecker(CheckerManager
&Mgr
) {
247 Mgr
.registerChecker
<UncountedLocalVarsChecker
>();
250 bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager
&) {