1 | //==- ProgramPoint.cpp - Program Points for Path-Sensitive Analysis -*- C++ -*-/ |
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 defines the interface ProgramPoint, which identifies a |
10 | // distinct location in a function. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "clang/Analysis/ProgramPoint.h" |
15 | #include "clang/AST/ASTContext.h" |
16 | #include "clang/Analysis/AnalysisDeclContext.h" |
17 | #include "clang/Basic/JsonSupport.h" |
18 | |
19 | using namespace clang; |
20 | |
21 | ProgramPointTag::~ProgramPointTag() {} |
22 | |
23 | ProgramPoint ProgramPoint::getProgramPoint(const Stmt *S, ProgramPoint::Kind K, |
24 | const LocationContext *LC, |
25 | const ProgramPointTag *tag){ |
26 | switch (K) { |
27 | default: |
28 | llvm_unreachable("Unhandled ProgramPoint kind" ); |
29 | case ProgramPoint::PreStmtKind: |
30 | return PreStmt(S, LC, tag); |
31 | case ProgramPoint::PostStmtKind: |
32 | return PostStmt(S, LC, tag); |
33 | case ProgramPoint::PreLoadKind: |
34 | return PreLoad(S, LC, tag); |
35 | case ProgramPoint::PostLoadKind: |
36 | return PostLoad(S, LC, tag); |
37 | case ProgramPoint::PreStoreKind: |
38 | return PreStore(S, LC, tag); |
39 | case ProgramPoint::PostLValueKind: |
40 | return PostLValue(S, LC, tag); |
41 | case ProgramPoint::PostStmtPurgeDeadSymbolsKind: |
42 | return PostStmtPurgeDeadSymbols(S, LC, tag); |
43 | case ProgramPoint::PreStmtPurgeDeadSymbolsKind: |
44 | return PreStmtPurgeDeadSymbols(S, LC, tag); |
45 | } |
46 | } |
47 | |
48 | LLVM_DUMP_METHOD void ProgramPoint::dump() const { |
49 | return printJson(Out&: llvm::errs()); |
50 | } |
51 | |
52 | StringRef ProgramPoint::getProgramPointKindName(Kind K) { |
53 | switch (K) { |
54 | case BlockEdgeKind: |
55 | return "BlockEdge" ; |
56 | case BlockEntranceKind: |
57 | return "BlockEntrance" ; |
58 | case BlockExitKind: |
59 | return "BlockExit" ; |
60 | case PreStmtKind: |
61 | return "PreStmt" ; |
62 | case PreStmtPurgeDeadSymbolsKind: |
63 | return "PreStmtPurgeDeadSymbols" ; |
64 | case PostStmtPurgeDeadSymbolsKind: |
65 | return "PostStmtPurgeDeadSymbols" ; |
66 | case PostStmtKind: |
67 | return "PostStmt" ; |
68 | case PreLoadKind: |
69 | return "PreLoad" ; |
70 | case PostLoadKind: |
71 | return "PostLoad" ; |
72 | case PreStoreKind: |
73 | return "PreStore" ; |
74 | case PostStoreKind: |
75 | return "PostStore" ; |
76 | case PostConditionKind: |
77 | return "PostCondition" ; |
78 | case PostLValueKind: |
79 | return "PostLValue" ; |
80 | case PostAllocatorCallKind: |
81 | return "PostAllocatorCall" ; |
82 | case PostInitializerKind: |
83 | return "PostInitializer" ; |
84 | case CallEnterKind: |
85 | return "CallEnter" ; |
86 | case CallExitBeginKind: |
87 | return "CallExitBegin" ; |
88 | case CallExitEndKind: |
89 | return "CallExitEnd" ; |
90 | case FunctionExitKind: |
91 | return "FunctionExit" ; |
92 | case PreImplicitCallKind: |
93 | return "PreImplicitCall" ; |
94 | case PostImplicitCallKind: |
95 | return "PostImplicitCall" ; |
96 | case LoopExitKind: |
97 | return "LoopExit" ; |
98 | case EpsilonKind: |
99 | return "Epsilon" ; |
100 | } |
101 | llvm_unreachable("Unknown ProgramPoint kind" ); |
102 | } |
103 | |
104 | std::optional<SourceLocation> ProgramPoint::getSourceLocation() const { |
105 | switch (getKind()) { |
106 | case BlockEdgeKind: |
107 | // If needed, the source and or destination beginning can be used to get |
108 | // source location. |
109 | return std::nullopt; |
110 | case BlockEntranceKind: |
111 | // If needed, first statement of the block can be used. |
112 | return std::nullopt; |
113 | case BlockExitKind: |
114 | if (const auto *B = castAs<BlockExit>().getBlock()) { |
115 | if (const auto *T = B->getTerminatorStmt()) { |
116 | return T->getBeginLoc(); |
117 | } |
118 | } |
119 | return std::nullopt; |
120 | case PreStmtKind: |
121 | case PreStmtPurgeDeadSymbolsKind: |
122 | case PostStmtPurgeDeadSymbolsKind: |
123 | case PostStmtKind: |
124 | case PreLoadKind: |
125 | case PostLoadKind: |
126 | case PreStoreKind: |
127 | case PostStoreKind: |
128 | case PostConditionKind: |
129 | case PostLValueKind: |
130 | case PostAllocatorCallKind: |
131 | if (const Stmt *S = castAs<StmtPoint>().getStmt()) |
132 | return S->getBeginLoc(); |
133 | return std::nullopt; |
134 | case PostInitializerKind: |
135 | if (const auto *Init = castAs<PostInitializer>().getInitializer()) |
136 | return Init->getSourceLocation(); |
137 | return std::nullopt; |
138 | case CallEnterKind: |
139 | if (const Stmt *S = castAs<CallEnter>().getCallExpr()) |
140 | return S->getBeginLoc(); |
141 | return std::nullopt; |
142 | case CallExitBeginKind: |
143 | if (const Stmt *S = castAs<CallExitBegin>().getReturnStmt()) |
144 | return S->getBeginLoc(); |
145 | return std::nullopt; |
146 | case CallExitEndKind: |
147 | return std::nullopt; |
148 | case FunctionExitKind: |
149 | if (const auto *B = castAs<FunctionExitPoint>().getBlock(); |
150 | B && B->getTerminatorStmt()) |
151 | return B->getTerminatorStmt()->getBeginLoc(); |
152 | return std::nullopt; |
153 | case PreImplicitCallKind: |
154 | return castAs<ImplicitCallPoint>().getLocation(); |
155 | case PostImplicitCallKind: |
156 | return castAs<ImplicitCallPoint>().getLocation(); |
157 | case LoopExitKind: |
158 | if (const Stmt *S = castAs<LoopExit>().getLoopStmt()) |
159 | return S->getBeginLoc(); |
160 | return std::nullopt; |
161 | case EpsilonKind: |
162 | return std::nullopt; |
163 | } |
164 | llvm_unreachable("Unknown ProgramPoint kind" ); |
165 | } |
166 | |
167 | void ProgramPoint::printJson(llvm::raw_ostream &Out, const char *NL) const { |
168 | const ASTContext &Context = |
169 | getLocationContext()->getAnalysisDeclContext()->getASTContext(); |
170 | const SourceManager &SM = Context.getSourceManager(); |
171 | const PrintingPolicy &PP = Context.getPrintingPolicy(); |
172 | const bool AddQuotes = true; |
173 | |
174 | Out << "\"kind\": \"" ; |
175 | switch (getKind()) { |
176 | case ProgramPoint::BlockEntranceKind: |
177 | Out << "BlockEntrance\"" |
178 | << ", \"block_id\": " |
179 | << castAs<BlockEntrance>().getBlock()->getBlockID(); |
180 | break; |
181 | |
182 | case ProgramPoint::FunctionExitKind: { |
183 | auto FEP = getAs<FunctionExitPoint>(); |
184 | Out << "FunctionExit\"" |
185 | << ", \"block_id\": " << FEP->getBlock()->getBlockID() |
186 | << ", \"stmt_id\": " ; |
187 | |
188 | if (const ReturnStmt *RS = FEP->getStmt()) { |
189 | Out << RS->getID(Context) << ", \"stmt\": " ; |
190 | RS->printJson(Out, nullptr, PP, AddQuotes); |
191 | } else { |
192 | Out << "null, \"stmt\": null" ; |
193 | } |
194 | break; |
195 | } |
196 | case ProgramPoint::BlockExitKind: |
197 | llvm_unreachable("BlockExitKind" ); |
198 | break; |
199 | case ProgramPoint::CallEnterKind: |
200 | Out << "CallEnter\", \"callee_decl\": \"" ; |
201 | Out << AnalysisDeclContext::getFunctionName( |
202 | D: castAs<CallEnter>().getCalleeContext()->getDecl()) |
203 | << '\"'; |
204 | break; |
205 | case ProgramPoint::CallExitBeginKind: |
206 | Out << "CallExitBegin\"" ; |
207 | break; |
208 | case ProgramPoint::CallExitEndKind: |
209 | Out << "CallExitEnd\"" ; |
210 | break; |
211 | case ProgramPoint::EpsilonKind: |
212 | Out << "EpsilonPoint\"" ; |
213 | break; |
214 | |
215 | case ProgramPoint::LoopExitKind: |
216 | Out << "LoopExit\", \"stmt\": \"" |
217 | << castAs<LoopExit>().getLoopStmt()->getStmtClassName() << '\"'; |
218 | break; |
219 | |
220 | case ProgramPoint::PreImplicitCallKind: { |
221 | ImplicitCallPoint PC = castAs<ImplicitCallPoint>(); |
222 | Out << "PreCall\", \"decl\": \"" |
223 | << PC.getDecl()->getAsFunction()->getQualifiedNameAsString() |
224 | << "\", \"location\": " ; |
225 | printSourceLocationAsJson(Out, Loc: PC.getLocation(), SM); |
226 | break; |
227 | } |
228 | |
229 | case ProgramPoint::PostImplicitCallKind: { |
230 | ImplicitCallPoint PC = castAs<ImplicitCallPoint>(); |
231 | Out << "PostCall\", \"decl\": \"" |
232 | << PC.getDecl()->getAsFunction()->getQualifiedNameAsString() |
233 | << "\", \"location\": " ; |
234 | printSourceLocationAsJson(Out, Loc: PC.getLocation(), SM); |
235 | break; |
236 | } |
237 | |
238 | case ProgramPoint::PostInitializerKind: { |
239 | Out << "PostInitializer\", " ; |
240 | const CXXCtorInitializer *Init = castAs<PostInitializer>().getInitializer(); |
241 | if (const FieldDecl *FD = Init->getAnyMember()) { |
242 | Out << "\"field_decl\": \"" << *FD << '\"'; |
243 | } else { |
244 | Out << "\"type\": \"" ; |
245 | QualType Ty = Init->getTypeSourceInfo()->getType(); |
246 | Ty = Ty.getLocalUnqualifiedType(); |
247 | Ty.print(OS&: Out, Policy: Context.getLangOpts()); |
248 | Out << '\"'; |
249 | } |
250 | break; |
251 | } |
252 | |
253 | case ProgramPoint::BlockEdgeKind: { |
254 | const BlockEdge &E = castAs<BlockEdge>(); |
255 | const Stmt *T = E.getSrc()->getTerminatorStmt(); |
256 | Out << "Edge\", \"src_id\": " << E.getSrc()->getBlockID() |
257 | << ", \"dst_id\": " << E.getDst()->getBlockID() << ", \"terminator\": " ; |
258 | |
259 | if (!T) { |
260 | Out << "null, \"term_kind\": null" ; |
261 | break; |
262 | } |
263 | |
264 | E.getSrc()->printTerminatorJson(Out, LO: Context.getLangOpts(), |
265 | /*AddQuotes=*/true); |
266 | Out << ", \"location\": " ; |
267 | printSourceLocationAsJson(Out, Loc: T->getBeginLoc(), SM); |
268 | |
269 | Out << ", \"term_kind\": \"" ; |
270 | if (isa<SwitchStmt>(Val: T)) { |
271 | Out << "SwitchStmt\", \"case\": " ; |
272 | if (const Stmt *Label = E.getDst()->getLabel()) { |
273 | if (const auto *C = dyn_cast<CaseStmt>(Val: Label)) { |
274 | Out << "{ \"lhs\": " ; |
275 | if (const Stmt *LHS = C->getLHS()) { |
276 | LHS->printJson(Out, Helper: nullptr, Policy: PP, AddQuotes); |
277 | } else { |
278 | Out << "null" ; |
279 | } |
280 | |
281 | Out << ", \"rhs\": " ; |
282 | if (const Stmt *RHS = C->getRHS()) { |
283 | RHS->printJson(Out, Helper: nullptr, Policy: PP, AddQuotes); |
284 | } else { |
285 | Out << "null" ; |
286 | } |
287 | Out << " }" ; |
288 | } else { |
289 | assert(isa<DefaultStmt>(Label)); |
290 | Out << "\"default\"" ; |
291 | } |
292 | } else { |
293 | Out << "\"implicit default\"" ; |
294 | } |
295 | } else if (isa<IndirectGotoStmt>(Val: T)) { |
296 | // FIXME: More info. |
297 | Out << "IndirectGotoStmt\"" ; |
298 | } else { |
299 | Out << "Condition\", \"value\": " |
300 | << (*E.getSrc()->succ_begin() == E.getDst() ? "true" : "false" ); |
301 | } |
302 | break; |
303 | } |
304 | |
305 | default: { |
306 | const Stmt *S = castAs<StmtPoint>().getStmt(); |
307 | assert(S != nullptr && "Expecting non-null Stmt" ); |
308 | |
309 | Out << "Statement\", \"stmt_kind\": \"" << S->getStmtClassName() |
310 | << "\", \"stmt_id\": " << S->getID(Context) |
311 | << ", \"pointer\": \"" << (const void *)S << "\", " ; |
312 | if (const auto *CS = dyn_cast<CastExpr>(Val: S)) |
313 | Out << "\"cast_kind\": \"" << CS->getCastKindName() << "\", " ; |
314 | |
315 | Out << "\"pretty\": " ; |
316 | |
317 | S->printJson(Out, Helper: nullptr, Policy: PP, AddQuotes); |
318 | |
319 | Out << ", \"location\": " ; |
320 | printSourceLocationAsJson(Out, Loc: S->getBeginLoc(), SM); |
321 | |
322 | Out << ", \"stmt_point_kind\": \"" ; |
323 | if (getAs<PreLoad>()) |
324 | Out << "PreLoad" ; |
325 | else if (getAs<PreStore>()) |
326 | Out << "PreStore" ; |
327 | else if (getAs<PostAllocatorCall>()) |
328 | Out << "PostAllocatorCall" ; |
329 | else if (getAs<PostCondition>()) |
330 | Out << "PostCondition" ; |
331 | else if (getAs<PostLoad>()) |
332 | Out << "PostLoad" ; |
333 | else if (getAs<PostLValue>()) |
334 | Out << "PostLValue" ; |
335 | else if (getAs<PostStore>()) |
336 | Out << "PostStore" ; |
337 | else if (getAs<PostStmt>()) |
338 | Out << "PostStmt" ; |
339 | else if (getAs<PostStmtPurgeDeadSymbols>()) |
340 | Out << "PostStmtPurgeDeadSymbols" ; |
341 | else if (getAs<PreStmtPurgeDeadSymbols>()) |
342 | Out << "PreStmtPurgeDeadSymbols" ; |
343 | else if (getAs<PreStmt>()) |
344 | Out << "PreStmt" ; |
345 | else { |
346 | Out << "\nKind: '" << getKind(); |
347 | llvm_unreachable("' is unhandled StmtPoint kind!" ); |
348 | } |
349 | |
350 | Out << '\"'; |
351 | break; |
352 | } |
353 | } |
354 | } |
355 | |
356 | SimpleProgramPointTag::SimpleProgramPointTag(StringRef MsgProvider, |
357 | StringRef Msg) |
358 | : Desc((MsgProvider + " : " + Msg).str()) {} |
359 | |
360 | StringRef SimpleProgramPointTag::getDebugTag() const { return Desc; } |
361 | |