/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- Environment.cpp - Map from Stmt* to Locations/Values ---------------===// |
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 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // This file defined the Environment and EnvironmentManager classes. |
10 | | // |
11 | | //===----------------------------------------------------------------------===// |
12 | | |
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" |
33 | | #include <cassert> |
34 | | |
35 | | using namespace clang; |
36 | | using namespace ento; |
37 | | |
38 | 5.14M | static const Expr *ignoreTransparentExprs(const Expr *E) { |
39 | 5.14M | E = E->IgnoreParens(); |
40 | | |
41 | 5.14M | switch (E->getStmtClass()) { |
42 | 4.31k | case Stmt::OpaqueValueExprClass: |
43 | 4.31k | E = cast<OpaqueValueExpr>(E)->getSourceExpr(); |
44 | 4.31k | break; |
45 | 14.3k | case Stmt::ExprWithCleanupsClass: |
46 | 14.3k | E = cast<ExprWithCleanups>(E)->getSubExpr(); |
47 | 14.3k | break; |
48 | 42 | case Stmt::ConstantExprClass: |
49 | 42 | E = cast<ConstantExpr>(E)->getSubExpr(); |
50 | 42 | break; |
51 | 2.06k | case Stmt::CXXBindTemporaryExprClass: |
52 | 2.06k | E = cast<CXXBindTemporaryExpr>(E)->getSubExpr(); |
53 | 2.06k | break; |
54 | 118 | case Stmt::SubstNonTypeTemplateParmExprClass: |
55 | 118 | E = cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(); |
56 | 118 | break; |
57 | 5.12M | default: |
58 | | // This is the base case: we can't look through more than we already have. |
59 | 5.12M | return E; |
60 | 5.14M | } |
61 | | |
62 | 20.8k | return ignoreTransparentExprs(E); |
63 | 5.14M | } |
64 | | |
65 | 5.13M | static const Stmt *ignoreTransparentExprs(const Stmt *S) { |
66 | 5.13M | if (const auto *E = dyn_cast<Expr>(S)) |
67 | 5.12M | return ignoreTransparentExprs(E); |
68 | 17.5k | return S; |
69 | 5.13M | } |
70 | | |
71 | | EnvironmentEntry::EnvironmentEntry(const Stmt *S, const LocationContext *L) |
72 | | : std::pair<const Stmt *, |
73 | | const StackFrameContext *>(ignoreTransparentExprs(S), |
74 | | L ? L->getStackFrame() |
75 | 5.13M | : nullptr) {} |
76 | | |
77 | 1.92M | SVal Environment::lookupExpr(const EnvironmentEntry &E) const { |
78 | 1.92M | const SVal* X = ExprBindings.lookup(E); |
79 | 1.92M | if (X) { |
80 | 1.83M | SVal V = *X; |
81 | 1.83M | return V; |
82 | 1.83M | } |
83 | 86.1k | return UnknownVal(); |
84 | 1.92M | } |
85 | | |
86 | | SVal Environment::getSVal(const EnvironmentEntry &Entry, |
87 | 2.05M | SValBuilder& svalBuilder) const { |
88 | 2.05M | const Stmt *S = Entry.getStmt(); |
89 | 2.05M | assert(!isa<ObjCForCollectionStmt>(S) && |
90 | 2.05M | "Use ExprEngine::hasMoreIteration()!"); |
91 | 2.05M | assert((isa<Expr, ReturnStmt>(S)) && |
92 | 2.05M | "Environment can only argue about Exprs, since only they express " |
93 | 2.05M | "a value! Any non-expression statement stored in Environment is a " |
94 | 2.05M | "result of a hack!"); |
95 | 2.05M | const LocationContext *LCtx = Entry.getLocationContext(); |
96 | | |
97 | 2.05M | switch (S->getStmtClass()) { |
98 | 0 | case Stmt::CXXBindTemporaryExprClass: |
99 | 0 | case Stmt::ExprWithCleanupsClass: |
100 | 0 | case Stmt::GenericSelectionExprClass: |
101 | 0 | case Stmt::OpaqueValueExprClass: |
102 | 0 | case Stmt::ConstantExprClass: |
103 | 0 | case Stmt::ParenExprClass: |
104 | 0 | case Stmt::SubstNonTypeTemplateParmExprClass: |
105 | 0 | llvm_unreachable("Should have been handled by ignoreTransparentExprs"); |
106 | |
|
107 | 164 | case Stmt::AddrLabelExprClass: |
108 | 1.40k | case Stmt::CharacterLiteralClass: |
109 | 3.72k | case Stmt::CXXBoolLiteralExprClass: |
110 | 3.83k | case Stmt::CXXScalarValueInitExprClass: |
111 | 4.05k | case Stmt::ImplicitValueInitExprClass: |
112 | 98.8k | case Stmt::IntegerLiteralClass: |
113 | 99.0k | case Stmt::ObjCBoolLiteralExprClass: |
114 | 99.3k | case Stmt::CXXNullPtrLiteralExprClass: |
115 | 100k | case Stmt::ObjCStringLiteralClass: |
116 | 110k | case Stmt::StringLiteralClass: |
117 | 110k | case Stmt::TypeTraitExprClass: |
118 | 110k | case Stmt::SizeOfPackExprClass: |
119 | 111k | case Stmt::PredefinedExprClass: |
120 | | // Known constants; defer to SValBuilder. |
121 | 111k | return *svalBuilder.getConstantVal(cast<Expr>(S)); |
122 | | |
123 | 17.5k | case Stmt::ReturnStmtClass: { |
124 | 17.5k | const auto *RS = cast<ReturnStmt>(S); |
125 | 17.5k | if (const Expr *RE = RS->getRetValue()) |
126 | 15.4k | return getSVal(EnvironmentEntry(RE, LCtx), svalBuilder); |
127 | 2.12k | return UndefinedVal(); |
128 | 17.5k | } |
129 | | |
130 | | // Handle all other Stmt* using a lookup. |
131 | 1.92M | default: |
132 | 1.92M | return lookupExpr(EnvironmentEntry(S, LCtx)); |
133 | 2.05M | } |
134 | 2.05M | } |
135 | | |
136 | | Environment EnvironmentManager::bindExpr(Environment Env, |
137 | | const EnvironmentEntry &E, |
138 | | SVal V, |
139 | 1.16M | bool Invalidate) { |
140 | 1.16M | if (V.isUnknown()) { |
141 | 36.3k | if (Invalidate) |
142 | 36.3k | return Environment(F.remove(Env.ExprBindings, E)); |
143 | 0 | else |
144 | 0 | return Env; |
145 | 36.3k | } |
146 | 1.12M | return Environment(F.add(Env.ExprBindings, E, V)); |
147 | 1.16M | } |
148 | | |
149 | | namespace { |
150 | | |
151 | | class MarkLiveCallback final : public SymbolVisitor { |
152 | | SymbolReaper &SymReaper; |
153 | | |
154 | | public: |
155 | 426k | MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {} |
156 | | |
157 | 650k | bool VisitSymbol(SymbolRef sym) override { |
158 | 650k | SymReaper.markLive(sym); |
159 | 650k | return true; |
160 | 650k | } |
161 | | |
162 | 923k | bool VisitMemRegion(const MemRegion *R) override { |
163 | 923k | SymReaper.markLive(R); |
164 | 923k | return true; |
165 | 923k | } |
166 | | }; |
167 | | |
168 | | } // namespace |
169 | | |
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. |
177 | | Environment |
178 | | EnvironmentManager::removeDeadBindings(Environment Env, |
179 | | SymbolReaper &SymReaper, |
180 | 426k | 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 | 426k | Environment NewEnv = getInitialEnvironment(); |
185 | | |
186 | 426k | MarkLiveCallback CB(SymReaper); |
187 | 426k | ScanReachableSymbols RSScaner(ST, CB); |
188 | | |
189 | 426k | llvm::ImmutableMapRef<EnvironmentEntry, SVal> |
190 | 426k | EBMapRef(NewEnv.ExprBindings.getRootWithoutRetain(), |
191 | 426k | F.getTreeFactory()); |
192 | | |
193 | | // Iterate over the block-expr bindings. |
194 | 2.88M | for (Environment::iterator I = Env.begin(), End = Env.end(); I != End; ++I2.46M ) { |
195 | 2.46M | const EnvironmentEntry &BlkExpr = I.getKey(); |
196 | 2.46M | const SVal &X = I.getData(); |
197 | | |
198 | 2.46M | const Expr *E = dyn_cast<Expr>(BlkExpr.getStmt()); |
199 | 2.46M | if (!E) |
200 | 0 | continue; |
201 | | |
202 | 2.46M | if (SymReaper.isLive(E, BlkExpr.getLocationContext())) { |
203 | | // Copy the binding to the new map. |
204 | 1.23M | EBMapRef = EBMapRef.add(BlkExpr, X); |
205 | | |
206 | | // Mark all symbols in the block expr's value live. |
207 | 1.23M | RSScaner.scan(X); |
208 | 1.23M | } |
209 | 2.46M | } |
210 | | |
211 | 426k | NewEnv.ExprBindings = EBMapRef.asImmutableMap(); |
212 | 426k | return NewEnv; |
213 | 426k | } |
214 | | |
215 | | void Environment::printJson(raw_ostream &Out, const ASTContext &Ctx, |
216 | | const LocationContext *LCtx, const char *NL, |
217 | 158 | unsigned int Space, bool IsDot) const { |
218 | 158 | Indent(Out, Space, IsDot) << "\"environment\": "; |
219 | | |
220 | 158 | if (ExprBindings.isEmpty()) { |
221 | 36 | Out << "null," << NL; |
222 | 36 | return; |
223 | 36 | } |
224 | | |
225 | 122 | ++Space; |
226 | 122 | if (!LCtx) { |
227 | | // Find the freshest location context. |
228 | 58 | llvm::SmallPtrSet<const LocationContext *, 16> FoundContexts; |
229 | 58 | for (const auto &I : *this) { |
230 | 58 | const LocationContext *LC = I.first.getLocationContext(); |
231 | 58 | if (FoundContexts.count(LC) == 0) { |
232 | | // This context is fresher than all other contexts so far. |
233 | 58 | LCtx = LC; |
234 | 116 | for (const LocationContext *LCI = LC; LCI; LCI = LCI->getParent()58 ) |
235 | 58 | FoundContexts.insert(LCI); |
236 | 58 | } |
237 | 58 | } |
238 | 58 | } |
239 | | |
240 | 122 | assert(LCtx); |
241 | | |
242 | 122 | Out << "{ \"pointer\": \"" << (const void *)LCtx->getStackFrame() |
243 | 122 | << "\", \"items\": [" << NL; |
244 | 122 | PrintingPolicy PP = Ctx.getPrintingPolicy(); |
245 | | |
246 | 131 | LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { |
247 | | // LCtx items begin |
248 | 131 | bool HasItem = false; |
249 | 131 | unsigned int InnerSpace = Space + 1; |
250 | | |
251 | | // Store the last ExprBinding which we will print. |
252 | 131 | BindingsTy::iterator LastI = ExprBindings.end(); |
253 | 292 | for (BindingsTy::iterator I = ExprBindings.begin(); I != ExprBindings.end(); |
254 | 161 | ++I) { |
255 | 161 | if (I->first.getLocationContext() != LC) |
256 | 9 | continue; |
257 | | |
258 | 152 | if (!HasItem) { |
259 | 122 | HasItem = true; |
260 | 122 | Out << '[' << NL; |
261 | 122 | } |
262 | | |
263 | 152 | const Stmt *S = I->first.getStmt(); |
264 | 152 | (void)S; |
265 | 152 | assert(S != nullptr && "Expected non-null Stmt"); |
266 | | |
267 | 152 | LastI = I; |
268 | 152 | } |
269 | | |
270 | 292 | for (BindingsTy::iterator I = ExprBindings.begin(); 131 I != ExprBindings.end(); |
271 | 161 | ++I) { |
272 | 161 | if (I->first.getLocationContext() != LC) |
273 | 9 | continue; |
274 | | |
275 | 152 | const Stmt *S = I->first.getStmt(); |
276 | 152 | Indent(Out, InnerSpace, IsDot) |
277 | 152 | << "{ \"stmt_id\": " << S->getID(Ctx) << ", \"kind\": \"" |
278 | 152 | << S->getStmtClassName() << "\", \"pretty\": "; |
279 | 152 | S->printJson(Out, nullptr, PP, /*AddQuotes=*/true); |
280 | | |
281 | 152 | Out << ", \"value\": "; |
282 | 152 | I->second.printJson(Out, /*AddQuotes=*/true); |
283 | | |
284 | 152 | Out << " }"; |
285 | | |
286 | 152 | if (I != LastI) |
287 | 30 | Out << ','; |
288 | 152 | Out << NL; |
289 | 152 | } |
290 | | |
291 | 131 | if (HasItem) |
292 | 122 | Indent(Out, --InnerSpace, IsDot) << ']'; |
293 | 9 | else |
294 | 9 | Out << "null "; |
295 | 131 | }); |
296 | | |
297 | 122 | Indent(Out, --Space, IsDot) << "]}," << NL; |
298 | 122 | } |