1 | //===- CoreEngine.h - Path-Sensitive Dataflow Engine ------------*- 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 a generic engine for intraprocedural, path-sensitive, |
10 | // dataflow analysis via graph reachability. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H |
15 | #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H |
16 | |
17 | #include "clang/AST/Stmt.h" |
18 | #include "clang/Analysis/AnalysisDeclContext.h" |
19 | #include "clang/Analysis/CFG.h" |
20 | #include "clang/Analysis/ProgramPoint.h" |
21 | #include "clang/Basic/LLVM.h" |
22 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
23 | #include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h" |
24 | #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" |
25 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" |
26 | #include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" |
27 | #include "llvm/ADT/SmallVector.h" |
28 | #include "llvm/ADT/iterator_range.h" |
29 | #include "llvm/Support/Casting.h" |
30 | #include <cassert> |
31 | #include <memory> |
32 | #include <utility> |
33 | #include <vector> |
34 | |
35 | namespace clang { |
36 | |
37 | class AnalyzerOptions; |
38 | class CXXBindTemporaryExpr; |
39 | class Expr; |
40 | class LabelDecl; |
41 | |
42 | namespace ento { |
43 | |
44 | class FunctionSummariesTy; |
45 | class ExprEngine; |
46 | |
47 | //===----------------------------------------------------------------------===// |
48 | /// CoreEngine - Implements the core logic of the graph-reachability |
49 | /// analysis. It traverses the CFG and generates the ExplodedGraph. |
50 | /// Program "states" are treated as opaque void pointers. |
51 | /// The template class CoreEngine (which subclasses CoreEngine) |
52 | /// provides the matching component to the engine that knows the actual types |
53 | /// for states. Note that this engine only dispatches to transfer functions |
54 | /// at the statement and block-level. The analyses themselves must implement |
55 | /// any transfer function logic and the sub-expression level (if any). |
56 | class CoreEngine { |
57 | friend class CommonNodeBuilder; |
58 | friend class EndOfFunctionNodeBuilder; |
59 | friend class ExprEngine; |
60 | friend class IndirectGotoNodeBuilder; |
61 | friend class NodeBuilder; |
62 | friend class NodeBuilderContext; |
63 | friend class SwitchNodeBuilder; |
64 | |
65 | public: |
66 | using BlocksExhausted = |
67 | std::vector<std::pair<BlockEdge, const ExplodedNode *>>; |
68 | |
69 | using BlocksAborted = |
70 | std::vector<std::pair<const CFGBlock *, const ExplodedNode *>>; |
71 | |
72 | private: |
73 | ExprEngine &ExprEng; |
74 | |
75 | /// G - The simulation graph. Each node is a (location,state) pair. |
76 | mutable ExplodedGraph G; |
77 | |
78 | /// WList - A set of queued nodes that need to be processed by the |
79 | /// worklist algorithm. It is up to the implementation of WList to decide |
80 | /// the order that nodes are processed. |
81 | std::unique_ptr<WorkList> WList; |
82 | std::unique_ptr<WorkList> CTUWList; |
83 | |
84 | /// BCounterFactory - A factory object for created BlockCounter objects. |
85 | /// These are used to record for key nodes in the ExplodedGraph the |
86 | /// number of times different CFGBlocks have been visited along a path. |
87 | BlockCounter::Factory BCounterFactory; |
88 | |
89 | /// The locations where we stopped doing work because we visited a location |
90 | /// too many times. |
91 | BlocksExhausted blocksExhausted; |
92 | |
93 | /// The locations where we stopped because the engine aborted analysis, |
94 | /// usually because it could not reason about something. |
95 | BlocksAborted blocksAborted; |
96 | |
97 | /// The information about functions shared by the whole translation unit. |
98 | /// (This data is owned by AnalysisConsumer.) |
99 | FunctionSummariesTy *FunctionSummaries; |
100 | |
101 | /// Add path tags with some useful data along the path when we see that |
102 | /// something interesting is happening. This field is the allocator for such |
103 | /// tags. |
104 | DataTag::Factory DataTags; |
105 | |
106 | void setBlockCounter(BlockCounter C); |
107 | |
108 | void generateNode(const ProgramPoint &Loc, |
109 | ProgramStateRef State, |
110 | ExplodedNode *Pred); |
111 | |
112 | void HandleBlockEdge(const BlockEdge &E, ExplodedNode *Pred); |
113 | void HandleBlockEntrance(const BlockEntrance &E, ExplodedNode *Pred); |
114 | void HandleBlockExit(const CFGBlock *B, ExplodedNode *Pred); |
115 | |
116 | void HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred); |
117 | |
118 | void HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, ExplodedNode *Pred); |
119 | |
120 | void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B, |
121 | ExplodedNode *Pred); |
122 | void HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, |
123 | const CFGBlock *B, ExplodedNode *Pred); |
124 | |
125 | /// Handle conditional logic for running static initializers. |
126 | void HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, |
127 | ExplodedNode *Pred); |
128 | |
129 | void HandleVirtualBaseBranch(const CFGBlock *B, ExplodedNode *Pred); |
130 | |
131 | private: |
132 | ExplodedNode *generateCallExitBeginNode(ExplodedNode *N, |
133 | const ReturnStmt *RS); |
134 | |
135 | public: |
136 | /// Construct a CoreEngine object to analyze the provided CFG. |
137 | CoreEngine(ExprEngine &exprengine, |
138 | FunctionSummariesTy *FS, |
139 | AnalyzerOptions &Opts); |
140 | |
141 | CoreEngine(const CoreEngine &) = delete; |
142 | CoreEngine &operator=(const CoreEngine &) = delete; |
143 | |
144 | /// getGraph - Returns the exploded graph. |
145 | ExplodedGraph &getGraph() { return G; } |
146 | |
147 | /// ExecuteWorkList - Run the worklist algorithm for a maximum number of |
148 | /// steps. Returns true if there is still simulation state on the worklist. |
149 | bool ExecuteWorkList(const LocationContext *L, unsigned Steps, |
150 | ProgramStateRef InitState); |
151 | |
152 | /// Dispatch the work list item based on the given location information. |
153 | /// Use Pred parameter as the predecessor state. |
154 | void dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, |
155 | const WorkListUnit& WU); |
156 | |
157 | // Functions for external checking of whether we have unfinished work |
158 | bool wasBlockAborted() const { return !blocksAborted.empty(); } |
159 | bool wasBlocksExhausted() const { return !blocksExhausted.empty(); } |
160 | bool hasWorkRemaining() const { return wasBlocksExhausted() || |
161 | WList->hasWork() || |
162 | wasBlockAborted(); } |
163 | |
164 | /// Inform the CoreEngine that a basic block was aborted because |
165 | /// it could not be completely analyzed. |
166 | void addAbortedBlock(const ExplodedNode *node, const CFGBlock *block) { |
167 | blocksAborted.push_back(x: std::make_pair(x&: block, y&: node)); |
168 | } |
169 | |
170 | WorkList *getWorkList() const { return WList.get(); } |
171 | WorkList *getCTUWorkList() const { return CTUWList.get(); } |
172 | |
173 | auto exhausted_blocks() const { |
174 | return llvm::iterator_range(blocksExhausted); |
175 | } |
176 | |
177 | auto aborted_blocks() const { return llvm::iterator_range(blocksAborted); } |
178 | |
179 | /// Enqueue the given set of nodes onto the work list. |
180 | void enqueue(ExplodedNodeSet &Set); |
181 | |
182 | /// Enqueue nodes that were created as a result of processing |
183 | /// a statement onto the work list. |
184 | void enqueue(ExplodedNodeSet &Set, const CFGBlock *Block, unsigned Idx); |
185 | |
186 | /// enqueue the nodes corresponding to the end of function onto the |
187 | /// end of path / work list. |
188 | void enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS); |
189 | |
190 | /// Enqueue a single node created as a result of statement processing. |
191 | void enqueueStmtNode(ExplodedNode *N, const CFGBlock *Block, unsigned Idx); |
192 | |
193 | DataTag::Factory &getDataTags() { return DataTags; } |
194 | }; |
195 | |
196 | class NodeBuilderContext { |
197 | const CoreEngine &Eng; |
198 | const CFGBlock *Block; |
199 | const LocationContext *LC; |
200 | |
201 | public: |
202 | NodeBuilderContext(const CoreEngine &E, const CFGBlock *B, |
203 | const LocationContext *L) |
204 | : Eng(E), Block(B), LC(L) { |
205 | assert(B); |
206 | } |
207 | |
208 | NodeBuilderContext(const CoreEngine &E, const CFGBlock *B, ExplodedNode *N) |
209 | : NodeBuilderContext(E, B, N->getLocationContext()) {} |
210 | |
211 | /// Return the CoreEngine associated with this builder. |
212 | const CoreEngine &getEngine() const { return Eng; } |
213 | |
214 | /// Return the CFGBlock associated with this builder. |
215 | const CFGBlock *getBlock() const { return Block; } |
216 | |
217 | /// Return the location context associated with this builder. |
218 | const LocationContext *getLocationContext() const { return LC; } |
219 | |
220 | /// Returns the number of times the current basic block has been |
221 | /// visited on the exploded graph path. |
222 | unsigned blockCount() const { |
223 | return Eng.WList->getBlockCounter().getNumVisited( |
224 | CallSite: LC->getStackFrame(), |
225 | BlockID: Block->getBlockID()); |
226 | } |
227 | }; |
228 | |
229 | /// \class NodeBuilder |
230 | /// This is the simplest builder which generates nodes in the |
231 | /// ExplodedGraph. |
232 | /// |
233 | /// The main benefit of the builder is that it automatically tracks the |
234 | /// frontier nodes (or destination set). This is the set of nodes which should |
235 | /// be propagated to the next step / builder. They are the nodes which have been |
236 | /// added to the builder (either as the input node set or as the newly |
237 | /// constructed nodes) but did not have any outgoing transitions added. |
238 | class NodeBuilder { |
239 | virtual void anchor(); |
240 | |
241 | protected: |
242 | const NodeBuilderContext &C; |
243 | |
244 | /// Specifies if the builder results have been finalized. For example, if it |
245 | /// is set to false, autotransitions are yet to be generated. |
246 | bool Finalized; |
247 | |
248 | bool HasGeneratedNodes = false; |
249 | |
250 | /// The frontier set - a set of nodes which need to be propagated after |
251 | /// the builder dies. |
252 | ExplodedNodeSet &Frontier; |
253 | |
254 | /// Checks if the results are ready. |
255 | virtual bool checkResults() { |
256 | return Finalized; |
257 | } |
258 | |
259 | bool hasNoSinksInFrontier() { |
260 | for (const auto I : Frontier) |
261 | if (I->isSink()) |
262 | return false; |
263 | return true; |
264 | } |
265 | |
266 | /// Allow subclasses to finalize results before result_begin() is executed. |
267 | virtual void finalizeResults() {} |
268 | |
269 | ExplodedNode *generateNodeImpl(const ProgramPoint &PP, |
270 | ProgramStateRef State, |
271 | ExplodedNode *Pred, |
272 | bool MarkAsSink = false); |
273 | |
274 | public: |
275 | NodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, |
276 | const NodeBuilderContext &Ctx, bool F = true) |
277 | : C(Ctx), Finalized(F), Frontier(DstSet) { |
278 | Frontier.Add(N: SrcNode); |
279 | } |
280 | |
281 | NodeBuilder(const ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, |
282 | const NodeBuilderContext &Ctx, bool F = true) |
283 | : C(Ctx), Finalized(F), Frontier(DstSet) { |
284 | Frontier.insert(S: SrcSet); |
285 | assert(hasNoSinksInFrontier()); |
286 | } |
287 | |
288 | virtual ~NodeBuilder() = default; |
289 | |
290 | /// Generates a node in the ExplodedGraph. |
291 | ExplodedNode *generateNode(const ProgramPoint &PP, |
292 | ProgramStateRef State, |
293 | ExplodedNode *Pred) { |
294 | return generateNodeImpl( |
295 | PP, State, Pred, |
296 | /*MarkAsSink=*/MarkAsSink: State->isPosteriorlyOverconstrained()); |
297 | } |
298 | |
299 | /// Generates a sink in the ExplodedGraph. |
300 | /// |
301 | /// When a node is marked as sink, the exploration from the node is stopped - |
302 | /// the node becomes the last node on the path and certain kinds of bugs are |
303 | /// suppressed. |
304 | ExplodedNode *generateSink(const ProgramPoint &PP, |
305 | ProgramStateRef State, |
306 | ExplodedNode *Pred) { |
307 | return generateNodeImpl(PP, State, Pred, MarkAsSink: true); |
308 | } |
309 | |
310 | const ExplodedNodeSet &getResults() { |
311 | finalizeResults(); |
312 | assert(checkResults()); |
313 | return Frontier; |
314 | } |
315 | |
316 | using iterator = ExplodedNodeSet::iterator; |
317 | |
318 | /// Iterators through the results frontier. |
319 | iterator begin() { |
320 | finalizeResults(); |
321 | assert(checkResults()); |
322 | return Frontier.begin(); |
323 | } |
324 | |
325 | iterator end() { |
326 | finalizeResults(); |
327 | return Frontier.end(); |
328 | } |
329 | |
330 | const NodeBuilderContext &getContext() { return C; } |
331 | bool hasGeneratedNodes() { return HasGeneratedNodes; } |
332 | |
333 | void takeNodes(const ExplodedNodeSet &S) { |
334 | for (const auto I : S) |
335 | Frontier.erase(N: I); |
336 | } |
337 | |
338 | void takeNodes(ExplodedNode *N) { Frontier.erase(N); } |
339 | void addNodes(const ExplodedNodeSet &S) { Frontier.insert(S); } |
340 | void addNodes(ExplodedNode *N) { Frontier.Add(N); } |
341 | }; |
342 | |
343 | /// \class NodeBuilderWithSinks |
344 | /// This node builder keeps track of the generated sink nodes. |
345 | class NodeBuilderWithSinks: public NodeBuilder { |
346 | void anchor() override; |
347 | |
348 | protected: |
349 | SmallVector<ExplodedNode*, 2> sinksGenerated; |
350 | ProgramPoint &Location; |
351 | |
352 | public: |
353 | NodeBuilderWithSinks(ExplodedNode *Pred, ExplodedNodeSet &DstSet, |
354 | const NodeBuilderContext &Ctx, ProgramPoint &L) |
355 | : NodeBuilder(Pred, DstSet, Ctx), Location(L) {} |
356 | |
357 | ExplodedNode *generateNode(ProgramStateRef State, |
358 | ExplodedNode *Pred, |
359 | const ProgramPointTag *Tag = nullptr) { |
360 | const ProgramPoint &LocalLoc = (Tag ? Location.withTag(tag: Tag) : Location); |
361 | return NodeBuilder::generateNode(PP: LocalLoc, State, Pred); |
362 | } |
363 | |
364 | ExplodedNode *generateSink(ProgramStateRef State, ExplodedNode *Pred, |
365 | const ProgramPointTag *Tag = nullptr) { |
366 | const ProgramPoint &LocalLoc = (Tag ? Location.withTag(tag: Tag) : Location); |
367 | ExplodedNode *N = NodeBuilder::generateSink(PP: LocalLoc, State, Pred); |
368 | if (N && N->isSink()) |
369 | sinksGenerated.push_back(Elt: N); |
370 | return N; |
371 | } |
372 | |
373 | const SmallVectorImpl<ExplodedNode*> &getSinks() const { |
374 | return sinksGenerated; |
375 | } |
376 | }; |
377 | |
378 | /// \class StmtNodeBuilder |
379 | /// This builder class is useful for generating nodes that resulted from |
380 | /// visiting a statement. The main difference from its parent NodeBuilder is |
381 | /// that it creates a statement specific ProgramPoint. |
382 | class StmtNodeBuilder: public NodeBuilder { |
383 | NodeBuilder *EnclosingBldr; |
384 | |
385 | public: |
386 | /// Constructs a StmtNodeBuilder. If the builder is going to process |
387 | /// nodes currently owned by another builder(with larger scope), use |
388 | /// Enclosing builder to transfer ownership. |
389 | StmtNodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, |
390 | const NodeBuilderContext &Ctx, |
391 | NodeBuilder *Enclosing = nullptr) |
392 | : NodeBuilder(SrcNode, DstSet, Ctx), EnclosingBldr(Enclosing) { |
393 | if (EnclosingBldr) |
394 | EnclosingBldr->takeNodes(N: SrcNode); |
395 | } |
396 | |
397 | StmtNodeBuilder(ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, |
398 | const NodeBuilderContext &Ctx, |
399 | NodeBuilder *Enclosing = nullptr) |
400 | : NodeBuilder(SrcSet, DstSet, Ctx), EnclosingBldr(Enclosing) { |
401 | if (EnclosingBldr) |
402 | for (const auto I : SrcSet) |
403 | EnclosingBldr->takeNodes(N: I); |
404 | } |
405 | |
406 | ~StmtNodeBuilder() override; |
407 | |
408 | using NodeBuilder::generateNode; |
409 | using NodeBuilder::generateSink; |
410 | |
411 | ExplodedNode *generateNode(const Stmt *S, |
412 | ExplodedNode *Pred, |
413 | ProgramStateRef St, |
414 | const ProgramPointTag *tag = nullptr, |
415 | ProgramPoint::Kind K = ProgramPoint::PostStmtKind){ |
416 | const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, |
417 | LC: Pred->getLocationContext(), tag); |
418 | return NodeBuilder::generateNode(PP: L, State: St, Pred); |
419 | } |
420 | |
421 | ExplodedNode *generateSink(const Stmt *S, |
422 | ExplodedNode *Pred, |
423 | ProgramStateRef St, |
424 | const ProgramPointTag *tag = nullptr, |
425 | ProgramPoint::Kind K = ProgramPoint::PostStmtKind){ |
426 | const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, |
427 | LC: Pred->getLocationContext(), tag); |
428 | return NodeBuilder::generateSink(PP: L, State: St, Pred); |
429 | } |
430 | }; |
431 | |
432 | /// BranchNodeBuilder is responsible for constructing the nodes |
433 | /// corresponding to the two branches of the if statement - true and false. |
434 | class BranchNodeBuilder: public NodeBuilder { |
435 | const CFGBlock *DstT; |
436 | const CFGBlock *DstF; |
437 | |
438 | bool InFeasibleTrue; |
439 | bool InFeasibleFalse; |
440 | |
441 | void anchor() override; |
442 | |
443 | public: |
444 | BranchNodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet, |
445 | const NodeBuilderContext &C, |
446 | const CFGBlock *dstT, const CFGBlock *dstF) |
447 | : NodeBuilder(SrcNode, DstSet, C), DstT(dstT), DstF(dstF), |
448 | InFeasibleTrue(!DstT), InFeasibleFalse(!DstF) { |
449 | // The branch node builder does not generate autotransitions. |
450 | // If there are no successors it means that both branches are infeasible. |
451 | takeNodes(N: SrcNode); |
452 | } |
453 | |
454 | BranchNodeBuilder(const ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet, |
455 | const NodeBuilderContext &C, |
456 | const CFGBlock *dstT, const CFGBlock *dstF) |
457 | : NodeBuilder(SrcSet, DstSet, C), DstT(dstT), DstF(dstF), |
458 | InFeasibleTrue(!DstT), InFeasibleFalse(!DstF) { |
459 | takeNodes(S: SrcSet); |
460 | } |
461 | |
462 | ExplodedNode *generateNode(ProgramStateRef State, bool branch, |
463 | ExplodedNode *Pred); |
464 | |
465 | const CFGBlock *getTargetBlock(bool branch) const { |
466 | return branch ? DstT : DstF; |
467 | } |
468 | |
469 | void markInfeasible(bool branch) { |
470 | if (branch) |
471 | InFeasibleTrue = true; |
472 | else |
473 | InFeasibleFalse = true; |
474 | } |
475 | |
476 | bool isFeasible(bool branch) { |
477 | return branch ? !InFeasibleTrue : !InFeasibleFalse; |
478 | } |
479 | }; |
480 | |
481 | class IndirectGotoNodeBuilder { |
482 | CoreEngine& Eng; |
483 | const CFGBlock *Src; |
484 | const CFGBlock &DispatchBlock; |
485 | const Expr *E; |
486 | ExplodedNode *Pred; |
487 | |
488 | public: |
489 | IndirectGotoNodeBuilder(ExplodedNode *pred, const CFGBlock *src, |
490 | const Expr *e, const CFGBlock *dispatch, CoreEngine* eng) |
491 | : Eng(*eng), Src(src), DispatchBlock(*dispatch), E(e), Pred(pred) {} |
492 | |
493 | class iterator { |
494 | friend class IndirectGotoNodeBuilder; |
495 | |
496 | CFGBlock::const_succ_iterator I; |
497 | |
498 | iterator(CFGBlock::const_succ_iterator i) : I(i) {} |
499 | |
500 | public: |
501 | // This isn't really a conventional iterator. |
502 | // We just implement the deref as a no-op for now to make range-based for |
503 | // loops work. |
504 | const iterator &operator*() const { return *this; } |
505 | |
506 | iterator &operator++() { ++I; return *this; } |
507 | bool operator!=(const iterator &X) const { return I != X.I; } |
508 | |
509 | const LabelDecl *getLabel() const { |
510 | return cast<LabelStmt>(Val: (*I)->getLabel())->getDecl(); |
511 | } |
512 | |
513 | const CFGBlock *getBlock() const { |
514 | return *I; |
515 | } |
516 | }; |
517 | |
518 | iterator begin() { return iterator(DispatchBlock.succ_begin()); } |
519 | iterator end() { return iterator(DispatchBlock.succ_end()); } |
520 | |
521 | ExplodedNode *generateNode(const iterator &I, |
522 | ProgramStateRef State, |
523 | bool isSink = false); |
524 | |
525 | const Expr *getTarget() const { return E; } |
526 | |
527 | ProgramStateRef getState() const { return Pred->State; } |
528 | |
529 | const LocationContext *getLocationContext() const { |
530 | return Pred->getLocationContext(); |
531 | } |
532 | }; |
533 | |
534 | class SwitchNodeBuilder { |
535 | CoreEngine& Eng; |
536 | const CFGBlock *Src; |
537 | const Expr *Condition; |
538 | ExplodedNode *Pred; |
539 | |
540 | public: |
541 | SwitchNodeBuilder(ExplodedNode *pred, const CFGBlock *src, |
542 | const Expr *condition, CoreEngine* eng) |
543 | : Eng(*eng), Src(src), Condition(condition), Pred(pred) {} |
544 | |
545 | class iterator { |
546 | friend class SwitchNodeBuilder; |
547 | |
548 | CFGBlock::const_succ_reverse_iterator I; |
549 | |
550 | iterator(CFGBlock::const_succ_reverse_iterator i) : I(i) {} |
551 | |
552 | public: |
553 | iterator &operator++() { ++I; return *this; } |
554 | bool operator!=(const iterator &X) const { return I != X.I; } |
555 | bool operator==(const iterator &X) const { return I == X.I; } |
556 | |
557 | const CaseStmt *getCase() const { |
558 | return cast<CaseStmt>(Val: (*I)->getLabel()); |
559 | } |
560 | |
561 | const CFGBlock *getBlock() const { |
562 | return *I; |
563 | } |
564 | }; |
565 | |
566 | iterator begin() { return iterator(Src->succ_rbegin()+1); } |
567 | iterator end() { return iterator(Src->succ_rend()); } |
568 | |
569 | const SwitchStmt *getSwitch() const { |
570 | return cast<SwitchStmt>(Val: Src->getTerminator()); |
571 | } |
572 | |
573 | ExplodedNode *generateCaseStmtNode(const iterator &I, |
574 | ProgramStateRef State); |
575 | |
576 | ExplodedNode *generateDefaultCaseNode(ProgramStateRef State, |
577 | bool isSink = false); |
578 | |
579 | const Expr *getCondition() const { return Condition; } |
580 | |
581 | ProgramStateRef getState() const { return Pred->State; } |
582 | |
583 | const LocationContext *getLocationContext() const { |
584 | return Pred->getLocationContext(); |
585 | } |
586 | }; |
587 | |
588 | } // namespace ento |
589 | |
590 | } // namespace clang |
591 | |
592 | #endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H |
593 | |