xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Core/Environment.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
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