1 //===- Environment.cpp - Map from Stmt* to Locations/Values ---------------===//
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 //===----------------------------------------------------------------------===//
9 // This file defined the Environment and EnvironmentManager classes.
11 //===----------------------------------------------------------------------===//
13 #include "clang/StaticAnalyzer/Core/PathSensitive/Environment.h"
14 #include "clang/AST/Expr.h"
15 #include "clang/AST/ExprCXX.h"
16 #include "clang/AST/PrettyPrinter.h"
17 #include "clang/AST/Stmt.h"
18 #include "clang/AST/StmtObjC.h"
19 #include "clang/Analysis/AnalysisDeclContext.h"
20 #include "clang/Basic/JsonSupport.h"
21 #include "clang/Basic/LLVM.h"
22 #include "clang/Basic/LangOptions.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
25 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
27 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
28 #include "llvm/ADT/ImmutableMap.h"
29 #include "llvm/ADT/SmallPtrSet.h"
30 #include "llvm/Support/Casting.h"
31 #include "llvm/Support/ErrorHandling.h"
32 #include "llvm/Support/raw_ostream.h"
35 using namespace clang
;
38 static const Expr
*ignoreTransparentExprs(const Expr
*E
) {
39 E
= E
->IgnoreParens();
41 switch (E
->getStmtClass()) {
42 case Stmt::OpaqueValueExprClass
:
43 E
= cast
<OpaqueValueExpr
>(E
)->getSourceExpr();
45 case Stmt::ExprWithCleanupsClass
:
46 E
= cast
<ExprWithCleanups
>(E
)->getSubExpr();
48 case Stmt::ConstantExprClass
:
49 E
= cast
<ConstantExpr
>(E
)->getSubExpr();
51 case Stmt::CXXBindTemporaryExprClass
:
52 E
= cast
<CXXBindTemporaryExpr
>(E
)->getSubExpr();
54 case Stmt::SubstNonTypeTemplateParmExprClass
:
55 E
= cast
<SubstNonTypeTemplateParmExpr
>(E
)->getReplacement();
58 // This is the base case: we can't look through more than we already have.
62 return ignoreTransparentExprs(E
);
65 static const Stmt
*ignoreTransparentExprs(const Stmt
*S
) {
66 if (const auto *E
= dyn_cast
<Expr
>(S
))
67 return ignoreTransparentExprs(E
);
71 EnvironmentEntry::EnvironmentEntry(const Stmt
*S
, const LocationContext
*L
)
72 : std::pair
<const Stmt
*,
73 const StackFrameContext
*>(ignoreTransparentExprs(S
),
74 L
? L
->getStackFrame()
77 SVal
Environment::lookupExpr(const EnvironmentEntry
&E
) const {
78 const SVal
* X
= ExprBindings
.lookup(E
);
86 SVal
Environment::getSVal(const EnvironmentEntry
&Entry
,
87 SValBuilder
& svalBuilder
) const {
88 const Stmt
*S
= Entry
.getStmt();
89 assert(!isa
<ObjCForCollectionStmt
>(S
) &&
90 "Use ExprEngine::hasMoreIteration()!");
91 assert((isa
<Expr
, ReturnStmt
>(S
)) &&
92 "Environment can only argue about Exprs, since only they express "
93 "a value! Any non-expression statement stored in Environment is a "
95 const LocationContext
*LCtx
= Entry
.getLocationContext();
97 switch (S
->getStmtClass()) {
98 case Stmt::CXXBindTemporaryExprClass
:
99 case Stmt::ExprWithCleanupsClass
:
100 case Stmt::GenericSelectionExprClass
:
101 case Stmt::OpaqueValueExprClass
:
102 case Stmt::ConstantExprClass
:
103 case Stmt::ParenExprClass
:
104 case Stmt::SubstNonTypeTemplateParmExprClass
:
105 llvm_unreachable("Should have been handled by ignoreTransparentExprs");
107 case Stmt::AddrLabelExprClass
:
108 case Stmt::CharacterLiteralClass
:
109 case Stmt::CXXBoolLiteralExprClass
:
110 case Stmt::CXXScalarValueInitExprClass
:
111 case Stmt::ImplicitValueInitExprClass
:
112 case Stmt::IntegerLiteralClass
:
113 case Stmt::ObjCBoolLiteralExprClass
:
114 case Stmt::CXXNullPtrLiteralExprClass
:
115 case Stmt::ObjCStringLiteralClass
:
116 case Stmt::StringLiteralClass
:
117 case Stmt::TypeTraitExprClass
:
118 case Stmt::SizeOfPackExprClass
:
119 case Stmt::PredefinedExprClass
:
120 // Known constants; defer to SValBuilder.
121 return *svalBuilder
.getConstantVal(cast
<Expr
>(S
));
123 case Stmt::ReturnStmtClass
: {
124 const auto *RS
= cast
<ReturnStmt
>(S
);
125 if (const Expr
*RE
= RS
->getRetValue())
126 return getSVal(EnvironmentEntry(RE
, LCtx
), svalBuilder
);
127 return UndefinedVal();
130 // Handle all other Stmt* using a lookup.
132 return lookupExpr(EnvironmentEntry(S
, LCtx
));
136 Environment
EnvironmentManager::bindExpr(Environment Env
,
137 const EnvironmentEntry
&E
,
142 return Environment(F
.remove(Env
.ExprBindings
, E
));
146 return Environment(F
.add(Env
.ExprBindings
, E
, V
));
151 class MarkLiveCallback final
: public SymbolVisitor
{
152 SymbolReaper
&SymReaper
;
155 MarkLiveCallback(SymbolReaper
&symreaper
) : SymReaper(symreaper
) {}
157 bool VisitSymbol(SymbolRef sym
) override
{
158 SymReaper
.markLive(sym
);
162 bool VisitMemRegion(const MemRegion
*R
) override
{
163 SymReaper
.markLive(R
);
170 // removeDeadBindings:
171 // - Remove subexpression bindings.
172 // - Remove dead block expression bindings.
173 // - Keep live block expression bindings:
174 // - Mark their reachable symbols live in SymbolReaper,
175 // see ScanReachableSymbols.
176 // - Mark the region in DRoots if the binding is a loc::MemRegionVal.
178 EnvironmentManager::removeDeadBindings(Environment Env
,
179 SymbolReaper
&SymReaper
,
180 ProgramStateRef ST
) {
181 // We construct a new Environment object entirely, as this is cheaper than
182 // individually removing all the subexpression bindings (which will greatly
183 // outnumber block-level expression bindings).
184 Environment NewEnv
= getInitialEnvironment();
186 MarkLiveCallback
CB(SymReaper
);
187 ScanReachableSymbols
RSScaner(ST
, CB
);
189 llvm::ImmutableMapRef
<EnvironmentEntry
, SVal
>
190 EBMapRef(NewEnv
.ExprBindings
.getRootWithoutRetain(),
193 // Iterate over the block-expr bindings.
194 for (Environment::iterator I
= Env
.begin(), End
= Env
.end(); I
!= End
; ++I
) {
195 const EnvironmentEntry
&BlkExpr
= I
.getKey();
196 const SVal
&X
= I
.getData();
198 const Expr
*E
= dyn_cast
<Expr
>(BlkExpr
.getStmt());
202 if (SymReaper
.isLive(E
, BlkExpr
.getLocationContext())) {
203 // Copy the binding to the new map.
204 EBMapRef
= EBMapRef
.add(BlkExpr
, X
);
206 // Mark all symbols in the block expr's value live.
211 NewEnv
.ExprBindings
= EBMapRef
.asImmutableMap();
215 void Environment::printJson(raw_ostream
&Out
, const ASTContext
&Ctx
,
216 const LocationContext
*LCtx
, const char *NL
,
217 unsigned int Space
, bool IsDot
) const {
218 Indent(Out
, Space
, IsDot
) << "\"environment\": ";
220 if (ExprBindings
.isEmpty()) {
221 Out
<< "null," << NL
;
227 // Find the freshest location context.
228 llvm::SmallPtrSet
<const LocationContext
*, 16> FoundContexts
;
229 for (const auto &I
: *this) {
230 const LocationContext
*LC
= I
.first
.getLocationContext();
231 if (FoundContexts
.count(LC
) == 0) {
232 // This context is fresher than all other contexts so far.
234 for (const LocationContext
*LCI
= LC
; LCI
; LCI
= LCI
->getParent())
235 FoundContexts
.insert(LCI
);
242 Out
<< "{ \"pointer\": \"" << (const void *)LCtx
->getStackFrame()
243 << "\", \"items\": [" << NL
;
244 PrintingPolicy PP
= Ctx
.getPrintingPolicy();
246 LCtx
->printJson(Out
, NL
, Space
, IsDot
, [&](const LocationContext
*LC
) {
248 bool HasItem
= false;
249 unsigned int InnerSpace
= Space
+ 1;
251 // Store the last ExprBinding which we will print.
252 BindingsTy::iterator LastI
= ExprBindings
.end();
253 for (BindingsTy::iterator I
= ExprBindings
.begin(); I
!= ExprBindings
.end();
255 if (I
->first
.getLocationContext() != LC
)
263 const Stmt
*S
= I
->first
.getStmt();
265 assert(S
!= nullptr && "Expected non-null Stmt");
270 for (BindingsTy::iterator I
= ExprBindings
.begin(); I
!= ExprBindings
.end();
272 if (I
->first
.getLocationContext() != LC
)
275 const Stmt
*S
= I
->first
.getStmt();
276 Indent(Out
, InnerSpace
, IsDot
)
277 << "{ \"stmt_id\": " << S
->getID(Ctx
) << ", \"kind\": \""
278 << S
->getStmtClassName() << "\", \"pretty\": ";
279 S
->printJson(Out
, nullptr, PP
, /*AddQuotes=*/true);
281 Out
<< ", \"value\": ";
282 I
->second
.printJson(Out
, /*AddQuotes=*/true);
292 Indent(Out
, --InnerSpace
, IsDot
) << ']';
297 Indent(Out
, --Space
, IsDot
) << "]}," << NL
;