1e5dd7070Spatrick //===- Environment.cpp - Map from Stmt* to Locations/Values ---------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This file defined the Environment and EnvironmentManager classes.
10e5dd7070Spatrick //
11e5dd7070Spatrick //===----------------------------------------------------------------------===//
12e5dd7070Spatrick
13e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/Environment.h"
14e5dd7070Spatrick #include "clang/AST/Expr.h"
15e5dd7070Spatrick #include "clang/AST/ExprCXX.h"
16e5dd7070Spatrick #include "clang/AST/PrettyPrinter.h"
17e5dd7070Spatrick #include "clang/AST/Stmt.h"
18a9ac8606Spatrick #include "clang/AST/StmtObjC.h"
19e5dd7070Spatrick #include "clang/Analysis/AnalysisDeclContext.h"
20e5dd7070Spatrick #include "clang/Basic/LLVM.h"
21e5dd7070Spatrick #include "clang/Basic/LangOptions.h"
22e5dd7070Spatrick #include "clang/Basic/JsonSupport.h"
23e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
24e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
25e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
26e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
27e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
28e5dd7070Spatrick #include "llvm/ADT/ImmutableMap.h"
29e5dd7070Spatrick #include "llvm/ADT/SmallPtrSet.h"
30e5dd7070Spatrick #include "llvm/Support/Casting.h"
31e5dd7070Spatrick #include "llvm/Support/ErrorHandling.h"
32e5dd7070Spatrick #include "llvm/Support/raw_ostream.h"
33e5dd7070Spatrick #include <cassert>
34e5dd7070Spatrick
35e5dd7070Spatrick using namespace clang;
36e5dd7070Spatrick using namespace ento;
37e5dd7070Spatrick
ignoreTransparentExprs(const Expr * E)38e5dd7070Spatrick static const Expr *ignoreTransparentExprs(const Expr *E) {
39e5dd7070Spatrick E = E->IgnoreParens();
40e5dd7070Spatrick
41e5dd7070Spatrick switch (E->getStmtClass()) {
42e5dd7070Spatrick case Stmt::OpaqueValueExprClass:
43e5dd7070Spatrick E = cast<OpaqueValueExpr>(E)->getSourceExpr();
44e5dd7070Spatrick break;
45e5dd7070Spatrick case Stmt::ExprWithCleanupsClass:
46e5dd7070Spatrick E = cast<ExprWithCleanups>(E)->getSubExpr();
47e5dd7070Spatrick break;
48e5dd7070Spatrick case Stmt::ConstantExprClass:
49e5dd7070Spatrick E = cast<ConstantExpr>(E)->getSubExpr();
50e5dd7070Spatrick break;
51e5dd7070Spatrick case Stmt::CXXBindTemporaryExprClass:
52e5dd7070Spatrick E = cast<CXXBindTemporaryExpr>(E)->getSubExpr();
53e5dd7070Spatrick break;
54e5dd7070Spatrick case Stmt::SubstNonTypeTemplateParmExprClass:
55e5dd7070Spatrick E = cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement();
56e5dd7070Spatrick break;
57e5dd7070Spatrick default:
58e5dd7070Spatrick // This is the base case: we can't look through more than we already have.
59e5dd7070Spatrick return E;
60e5dd7070Spatrick }
61e5dd7070Spatrick
62e5dd7070Spatrick return ignoreTransparentExprs(E);
63e5dd7070Spatrick }
64e5dd7070Spatrick
ignoreTransparentExprs(const Stmt * S)65e5dd7070Spatrick static const Stmt *ignoreTransparentExprs(const Stmt *S) {
66e5dd7070Spatrick if (const auto *E = dyn_cast<Expr>(S))
67e5dd7070Spatrick return ignoreTransparentExprs(E);
68e5dd7070Spatrick return S;
69e5dd7070Spatrick }
70e5dd7070Spatrick
EnvironmentEntry(const Stmt * S,const LocationContext * L)71e5dd7070Spatrick EnvironmentEntry::EnvironmentEntry(const Stmt *S, const LocationContext *L)
72e5dd7070Spatrick : std::pair<const Stmt *,
73e5dd7070Spatrick const StackFrameContext *>(ignoreTransparentExprs(S),
74e5dd7070Spatrick L ? L->getStackFrame()
75e5dd7070Spatrick : nullptr) {}
76e5dd7070Spatrick
lookupExpr(const EnvironmentEntry & E) const77e5dd7070Spatrick SVal Environment::lookupExpr(const EnvironmentEntry &E) const {
78e5dd7070Spatrick const SVal* X = ExprBindings.lookup(E);
79e5dd7070Spatrick if (X) {
80e5dd7070Spatrick SVal V = *X;
81e5dd7070Spatrick return V;
82e5dd7070Spatrick }
83e5dd7070Spatrick return UnknownVal();
84e5dd7070Spatrick }
85e5dd7070Spatrick
getSVal(const EnvironmentEntry & Entry,SValBuilder & svalBuilder) const86e5dd7070Spatrick SVal Environment::getSVal(const EnvironmentEntry &Entry,
87e5dd7070Spatrick SValBuilder& svalBuilder) const {
88e5dd7070Spatrick const Stmt *S = Entry.getStmt();
89a9ac8606Spatrick assert(!isa<ObjCForCollectionStmt>(S) &&
90a9ac8606Spatrick "Use ExprEngine::hasMoreIteration()!");
91*12c85518Srobert assert((isa<Expr, ReturnStmt>(S)) &&
92a9ac8606Spatrick "Environment can only argue about Exprs, since only they express "
93a9ac8606Spatrick "a value! Any non-expression statement stored in Environment is a "
94a9ac8606Spatrick "result of a hack!");
95e5dd7070Spatrick const LocationContext *LCtx = Entry.getLocationContext();
96e5dd7070Spatrick
97e5dd7070Spatrick switch (S->getStmtClass()) {
98e5dd7070Spatrick case Stmt::CXXBindTemporaryExprClass:
99e5dd7070Spatrick case Stmt::ExprWithCleanupsClass:
100e5dd7070Spatrick case Stmt::GenericSelectionExprClass:
101e5dd7070Spatrick case Stmt::OpaqueValueExprClass:
102e5dd7070Spatrick case Stmt::ConstantExprClass:
103e5dd7070Spatrick case Stmt::ParenExprClass:
104e5dd7070Spatrick case Stmt::SubstNonTypeTemplateParmExprClass:
105e5dd7070Spatrick llvm_unreachable("Should have been handled by ignoreTransparentExprs");
106e5dd7070Spatrick
107e5dd7070Spatrick case Stmt::AddrLabelExprClass:
108e5dd7070Spatrick case Stmt::CharacterLiteralClass:
109e5dd7070Spatrick case Stmt::CXXBoolLiteralExprClass:
110e5dd7070Spatrick case Stmt::CXXScalarValueInitExprClass:
111e5dd7070Spatrick case Stmt::ImplicitValueInitExprClass:
112e5dd7070Spatrick case Stmt::IntegerLiteralClass:
113e5dd7070Spatrick case Stmt::ObjCBoolLiteralExprClass:
114e5dd7070Spatrick case Stmt::CXXNullPtrLiteralExprClass:
115e5dd7070Spatrick case Stmt::ObjCStringLiteralClass:
116e5dd7070Spatrick case Stmt::StringLiteralClass:
117e5dd7070Spatrick case Stmt::TypeTraitExprClass:
118e5dd7070Spatrick case Stmt::SizeOfPackExprClass:
119a9ac8606Spatrick case Stmt::PredefinedExprClass:
120e5dd7070Spatrick // Known constants; defer to SValBuilder.
121*12c85518Srobert return *svalBuilder.getConstantVal(cast<Expr>(S));
122e5dd7070Spatrick
123e5dd7070Spatrick case Stmt::ReturnStmtClass: {
124e5dd7070Spatrick const auto *RS = cast<ReturnStmt>(S);
125e5dd7070Spatrick if (const Expr *RE = RS->getRetValue())
126e5dd7070Spatrick return getSVal(EnvironmentEntry(RE, LCtx), svalBuilder);
127e5dd7070Spatrick return UndefinedVal();
128e5dd7070Spatrick }
129e5dd7070Spatrick
130e5dd7070Spatrick // Handle all other Stmt* using a lookup.
131e5dd7070Spatrick default:
132e5dd7070Spatrick return lookupExpr(EnvironmentEntry(S, LCtx));
133e5dd7070Spatrick }
134e5dd7070Spatrick }
135e5dd7070Spatrick
bindExpr(Environment Env,const EnvironmentEntry & E,SVal V,bool Invalidate)136e5dd7070Spatrick Environment EnvironmentManager::bindExpr(Environment Env,
137e5dd7070Spatrick const EnvironmentEntry &E,
138e5dd7070Spatrick SVal V,
139e5dd7070Spatrick bool Invalidate) {
140e5dd7070Spatrick if (V.isUnknown()) {
141e5dd7070Spatrick if (Invalidate)
142e5dd7070Spatrick return Environment(F.remove(Env.ExprBindings, E));
143e5dd7070Spatrick else
144e5dd7070Spatrick return Env;
145e5dd7070Spatrick }
146e5dd7070Spatrick return Environment(F.add(Env.ExprBindings, E, V));
147e5dd7070Spatrick }
148e5dd7070Spatrick
149e5dd7070Spatrick namespace {
150e5dd7070Spatrick
151e5dd7070Spatrick class MarkLiveCallback final : public SymbolVisitor {
152e5dd7070Spatrick SymbolReaper &SymReaper;
153e5dd7070Spatrick
154e5dd7070Spatrick public:
MarkLiveCallback(SymbolReaper & symreaper)155e5dd7070Spatrick MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {}
156e5dd7070Spatrick
VisitSymbol(SymbolRef sym)157e5dd7070Spatrick bool VisitSymbol(SymbolRef sym) override {
158e5dd7070Spatrick SymReaper.markLive(sym);
159e5dd7070Spatrick return true;
160e5dd7070Spatrick }
161e5dd7070Spatrick
VisitMemRegion(const MemRegion * R)162e5dd7070Spatrick bool VisitMemRegion(const MemRegion *R) override {
163e5dd7070Spatrick SymReaper.markLive(R);
164e5dd7070Spatrick return true;
165e5dd7070Spatrick }
166e5dd7070Spatrick };
167e5dd7070Spatrick
168e5dd7070Spatrick } // namespace
169e5dd7070Spatrick
170e5dd7070Spatrick // removeDeadBindings:
171e5dd7070Spatrick // - Remove subexpression bindings.
172e5dd7070Spatrick // - Remove dead block expression bindings.
173e5dd7070Spatrick // - Keep live block expression bindings:
174e5dd7070Spatrick // - Mark their reachable symbols live in SymbolReaper,
175e5dd7070Spatrick // see ScanReachableSymbols.
176e5dd7070Spatrick // - Mark the region in DRoots if the binding is a loc::MemRegionVal.
177e5dd7070Spatrick Environment
removeDeadBindings(Environment Env,SymbolReaper & SymReaper,ProgramStateRef ST)178e5dd7070Spatrick EnvironmentManager::removeDeadBindings(Environment Env,
179e5dd7070Spatrick SymbolReaper &SymReaper,
180e5dd7070Spatrick ProgramStateRef ST) {
181e5dd7070Spatrick // We construct a new Environment object entirely, as this is cheaper than
182e5dd7070Spatrick // individually removing all the subexpression bindings (which will greatly
183e5dd7070Spatrick // outnumber block-level expression bindings).
184e5dd7070Spatrick Environment NewEnv = getInitialEnvironment();
185e5dd7070Spatrick
186e5dd7070Spatrick MarkLiveCallback CB(SymReaper);
187e5dd7070Spatrick ScanReachableSymbols RSScaner(ST, CB);
188e5dd7070Spatrick
189e5dd7070Spatrick llvm::ImmutableMapRef<EnvironmentEntry, SVal>
190e5dd7070Spatrick EBMapRef(NewEnv.ExprBindings.getRootWithoutRetain(),
191e5dd7070Spatrick F.getTreeFactory());
192e5dd7070Spatrick
193e5dd7070Spatrick // Iterate over the block-expr bindings.
194a9ac8606Spatrick for (Environment::iterator I = Env.begin(), End = Env.end(); I != End; ++I) {
195e5dd7070Spatrick const EnvironmentEntry &BlkExpr = I.getKey();
196e5dd7070Spatrick const SVal &X = I.getData();
197e5dd7070Spatrick
198a9ac8606Spatrick const Expr *E = dyn_cast<Expr>(BlkExpr.getStmt());
199a9ac8606Spatrick if (!E)
200a9ac8606Spatrick continue;
201ec727ea7Spatrick
202a9ac8606Spatrick if (SymReaper.isLive(E, BlkExpr.getLocationContext())) {
203e5dd7070Spatrick // Copy the binding to the new map.
204e5dd7070Spatrick EBMapRef = EBMapRef.add(BlkExpr, X);
205e5dd7070Spatrick
206e5dd7070Spatrick // Mark all symbols in the block expr's value live.
207e5dd7070Spatrick RSScaner.scan(X);
208e5dd7070Spatrick }
209e5dd7070Spatrick }
210e5dd7070Spatrick
211e5dd7070Spatrick NewEnv.ExprBindings = EBMapRef.asImmutableMap();
212e5dd7070Spatrick return NewEnv;
213e5dd7070Spatrick }
214e5dd7070Spatrick
printJson(raw_ostream & Out,const ASTContext & Ctx,const LocationContext * LCtx,const char * NL,unsigned int Space,bool IsDot) const215e5dd7070Spatrick void Environment::printJson(raw_ostream &Out, const ASTContext &Ctx,
216e5dd7070Spatrick const LocationContext *LCtx, const char *NL,
217e5dd7070Spatrick unsigned int Space, bool IsDot) const {
218e5dd7070Spatrick Indent(Out, Space, IsDot) << "\"environment\": ";
219e5dd7070Spatrick
220e5dd7070Spatrick if (ExprBindings.isEmpty()) {
221e5dd7070Spatrick Out << "null," << NL;
222e5dd7070Spatrick return;
223e5dd7070Spatrick }
224e5dd7070Spatrick
225e5dd7070Spatrick ++Space;
226e5dd7070Spatrick if (!LCtx) {
227e5dd7070Spatrick // Find the freshest location context.
228e5dd7070Spatrick llvm::SmallPtrSet<const LocationContext *, 16> FoundContexts;
229e5dd7070Spatrick for (const auto &I : *this) {
230e5dd7070Spatrick const LocationContext *LC = I.first.getLocationContext();
231e5dd7070Spatrick if (FoundContexts.count(LC) == 0) {
232e5dd7070Spatrick // This context is fresher than all other contexts so far.
233e5dd7070Spatrick LCtx = LC;
234e5dd7070Spatrick for (const LocationContext *LCI = LC; LCI; LCI = LCI->getParent())
235e5dd7070Spatrick FoundContexts.insert(LCI);
236e5dd7070Spatrick }
237e5dd7070Spatrick }
238e5dd7070Spatrick }
239e5dd7070Spatrick
240e5dd7070Spatrick assert(LCtx);
241e5dd7070Spatrick
242e5dd7070Spatrick Out << "{ \"pointer\": \"" << (const void *)LCtx->getStackFrame()
243e5dd7070Spatrick << "\", \"items\": [" << NL;
244e5dd7070Spatrick PrintingPolicy PP = Ctx.getPrintingPolicy();
245e5dd7070Spatrick
246e5dd7070Spatrick LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) {
247e5dd7070Spatrick // LCtx items begin
248e5dd7070Spatrick bool HasItem = false;
249e5dd7070Spatrick unsigned int InnerSpace = Space + 1;
250e5dd7070Spatrick
251e5dd7070Spatrick // Store the last ExprBinding which we will print.
252e5dd7070Spatrick BindingsTy::iterator LastI = ExprBindings.end();
253e5dd7070Spatrick for (BindingsTy::iterator I = ExprBindings.begin(); I != ExprBindings.end();
254e5dd7070Spatrick ++I) {
255e5dd7070Spatrick if (I->first.getLocationContext() != LC)
256e5dd7070Spatrick continue;
257e5dd7070Spatrick
258e5dd7070Spatrick if (!HasItem) {
259e5dd7070Spatrick HasItem = true;
260e5dd7070Spatrick Out << '[' << NL;
261e5dd7070Spatrick }
262e5dd7070Spatrick
263e5dd7070Spatrick const Stmt *S = I->first.getStmt();
264e5dd7070Spatrick (void)S;
265e5dd7070Spatrick assert(S != nullptr && "Expected non-null Stmt");
266e5dd7070Spatrick
267e5dd7070Spatrick LastI = I;
268e5dd7070Spatrick }
269e5dd7070Spatrick
270e5dd7070Spatrick for (BindingsTy::iterator I = ExprBindings.begin(); I != ExprBindings.end();
271e5dd7070Spatrick ++I) {
272e5dd7070Spatrick if (I->first.getLocationContext() != LC)
273e5dd7070Spatrick continue;
274e5dd7070Spatrick
275e5dd7070Spatrick const Stmt *S = I->first.getStmt();
276e5dd7070Spatrick Indent(Out, InnerSpace, IsDot)
277*12c85518Srobert << "{ \"stmt_id\": " << S->getID(Ctx) << ", \"kind\": \""
278*12c85518Srobert << S->getStmtClassName() << "\", \"pretty\": ";
279e5dd7070Spatrick S->printJson(Out, nullptr, PP, /*AddQuotes=*/true);
280e5dd7070Spatrick
281e5dd7070Spatrick Out << ", \"value\": ";
282e5dd7070Spatrick I->second.printJson(Out, /*AddQuotes=*/true);
283e5dd7070Spatrick
284e5dd7070Spatrick Out << " }";
285e5dd7070Spatrick
286e5dd7070Spatrick if (I != LastI)
287e5dd7070Spatrick Out << ',';
288e5dd7070Spatrick Out << NL;
289e5dd7070Spatrick }
290e5dd7070Spatrick
291e5dd7070Spatrick if (HasItem)
292e5dd7070Spatrick Indent(Out, --InnerSpace, IsDot) << ']';
293e5dd7070Spatrick else
294e5dd7070Spatrick Out << "null ";
295e5dd7070Spatrick });
296e5dd7070Spatrick
297e5dd7070Spatrick Indent(Out, --Space, IsDot) << "]}," << NL;
298e5dd7070Spatrick }
299