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