1//==- ProgramPoint.h - Program Points for Path-Sensitive Analysis --*- C++ -*-// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file defines the interface ProgramPoint, which identifies a 11// distinct location in a function. 12// 13//===----------------------------------------------------------------------===// 14 15#ifndef LLVM_CLANG_ANALYSIS_PROGRAMPOINT_H 16#define LLVM_CLANG_ANALYSIS_PROGRAMPOINT_H 17 18#include "clang/Analysis/AnalysisDeclContext.h" 19#include "clang/Analysis/CFG.h" 20#include "llvm/ADT/DenseMap.h" 21#include "llvm/ADT/FoldingSet.h" 22#include "llvm/ADT/Optional.h" 23#include "llvm/ADT/PointerIntPair.h" 24#include "llvm/ADT/StringRef.h" 25#include "llvm/Support/Casting.h" 26#include "llvm/Support/DataTypes.h" 27#include <cassert> 28#include <string> 29#include <utility> 30 31namespace clang { 32 33class AnalysisDeclContext; 34class FunctionDecl; 35class LocationContext; 36 37/// ProgramPoints can be "tagged" as representing points specific to a given 38/// analysis entity. Tags are abstract annotations, with an associated 39/// description and potentially other information. 40class ProgramPointTag { 41public: 42 ProgramPointTag(void *tagKind = nullptr) : TagKind(tagKind) {} 43 virtual ~ProgramPointTag(); 44 virtual StringRef getTagDescription() const = 0; 45 46protected: 47 /// Used to implement 'isKind' in subclasses. 48 const void *getTagKind() { return TagKind; } 49 50private: 51 const void *TagKind; 52}; 53 54class SimpleProgramPointTag : public ProgramPointTag { 55 std::string Desc; 56public: 57 SimpleProgramPointTag(StringRef MsgProvider, StringRef Msg); 58 StringRef getTagDescription() const override; 59}; 60 61class ProgramPoint { 62public: 63 enum Kind { BlockEdgeKind, 64 BlockEntranceKind, 65 BlockExitKind, 66 PreStmtKind, 67 PreStmtPurgeDeadSymbolsKind, 68 PostStmtPurgeDeadSymbolsKind, 69 PostStmtKind, 70 PreLoadKind, 71 PostLoadKind, 72 PreStoreKind, 73 PostStoreKind, 74 PostConditionKind, 75 PostLValueKind, 76 MinPostStmtKind = PostStmtKind, 77 MaxPostStmtKind = PostLValueKind, 78 PostInitializerKind, 79 CallEnterKind, 80 CallExitBeginKind, 81 CallExitEndKind, 82 PreImplicitCallKind, 83 PostImplicitCallKind, 84 MinImplicitCallKind = PreImplicitCallKind, 85 MaxImplicitCallKind = PostImplicitCallKind, 86 LoopExitKind, 87 EpsilonKind}; 88 89private: 90 const void *Data1; 91 llvm::PointerIntPair<const void *, 2, unsigned> Data2; 92 93 // The LocationContext could be NULL to allow ProgramPoint to be used in 94 // context insensitive analysis. 95 llvm::PointerIntPair<const LocationContext *, 2, unsigned> L; 96 97 llvm::PointerIntPair<const ProgramPointTag *, 2, unsigned> Tag; 98 99protected: 100 ProgramPoint() {} 101 ProgramPoint(const void *P, 102 Kind k, 103 const LocationContext *l, 104 const ProgramPointTag *tag = nullptr) 105 : Data1(P), 106 Data2(nullptr, (((unsigned) k) >> 0) & 0x3), 107 L(l, (((unsigned) k) >> 2) & 0x3), 108 Tag(tag, (((unsigned) k) >> 4) & 0x3) { 109 assert(getKind() == k); 110 assert(getLocationContext() == l); 111 assert(getData1() == P); 112 } 113 114 ProgramPoint(const void *P1, 115 const void *P2, 116 Kind k, 117 const LocationContext *l, 118 const ProgramPointTag *tag = nullptr) 119 : Data1(P1), 120 Data2(P2, (((unsigned) k) >> 0) & 0x3), 121 L(l, (((unsigned) k) >> 2) & 0x3), 122 Tag(tag, (((unsigned) k) >> 4) & 0x3) {} 123 124protected: 125 const void *getData1() const { return Data1; } 126 const void *getData2() const { return Data2.getPointer(); } 127 void setData2(const void *d) { Data2.setPointer(d); } 128 129public: 130 /// Create a new ProgramPoint object that is the same as the original 131 /// except for using the specified tag value. 132 ProgramPoint withTag(const ProgramPointTag *tag) const { 133 return ProgramPoint(getData1(), getData2(), getKind(), 134 getLocationContext(), tag); 135 } 136 137 /// \brief Convert to the specified ProgramPoint type, asserting that this 138 /// ProgramPoint is of the desired type. 139 template<typename T> 140 T castAs() const { 141 assert(T::isKind(*this)); 142 T t; 143 ProgramPoint& PP = t; 144 PP = *this; 145 return t; 146 } 147 148 /// \brief Convert to the specified ProgramPoint type, returning None if this 149 /// ProgramPoint is not of the desired type. 150 template<typename T> 151 Optional<T> getAs() const { 152 if (!T::isKind(*this)) 153 return None; 154 T t; 155 ProgramPoint& PP = t; 156 PP = *this; 157 return t; 158 } 159 160 Kind getKind() const { 161 unsigned x = Tag.getInt(); 162 x <<= 2; 163 x |= L.getInt(); 164 x <<= 2; 165 x |= Data2.getInt(); 166 return (Kind) x; 167 } 168 169 /// \brief Is this a program point corresponding to purge/removal of dead 170 /// symbols and bindings. 171 bool isPurgeKind() { 172 Kind K = getKind(); 173 return (K == PostStmtPurgeDeadSymbolsKind || 174 K == PreStmtPurgeDeadSymbolsKind); 175 } 176 177 const ProgramPointTag *getTag() const { return Tag.getPointer(); } 178 179 const LocationContext *getLocationContext() const { 180 return L.getPointer(); 181 } 182 183 // For use with DenseMap. This hash is probably slow. 184 unsigned getHashValue() const { 185 llvm::FoldingSetNodeID ID; 186 Profile(ID); 187 return ID.ComputeHash(); 188 } 189 190 bool operator==(const ProgramPoint & RHS) const { 191 return Data1 == RHS.Data1 && 192 Data2 == RHS.Data2 && 193 L == RHS.L && 194 Tag == RHS.Tag; 195 } 196 197 bool operator!=(const ProgramPoint &RHS) const { 198 return Data1 != RHS.Data1 || 199 Data2 != RHS.Data2 || 200 L != RHS.L || 201 Tag != RHS.Tag; 202 } 203 204 void Profile(llvm::FoldingSetNodeID& ID) const { 205 ID.AddInteger((unsigned) getKind()); 206 ID.AddPointer(getData1()); 207 ID.AddPointer(getData2()); 208 ID.AddPointer(getLocationContext()); 209 ID.AddPointer(getTag()); 210 } 211 212 static ProgramPoint getProgramPoint(const Stmt *S, ProgramPoint::Kind K, 213 const LocationContext *LC, 214 const ProgramPointTag *tag); 215}; 216 217class BlockEntrance : public ProgramPoint { 218public: 219 BlockEntrance(const CFGBlock *B, const LocationContext *L, 220 const ProgramPointTag *tag = nullptr) 221 : ProgramPoint(B, BlockEntranceKind, L, tag) { 222 assert(B && "BlockEntrance requires non-null block"); 223 } 224 225 const CFGBlock *getBlock() const { 226 return reinterpret_cast<const CFGBlock*>(getData1()); 227 } 228 229 Optional<CFGElement> getFirstElement() const { 230 const CFGBlock *B = getBlock(); 231 return B->empty() ? Optional<CFGElement>() : B->front(); 232 } 233 234private: 235 friend class ProgramPoint; 236 BlockEntrance() {} 237 static bool isKind(const ProgramPoint &Location) { 238 return Location.getKind() == BlockEntranceKind; 239 } 240}; 241 242class BlockExit : public ProgramPoint { 243public: 244 BlockExit(const CFGBlock *B, const LocationContext *L) 245 : ProgramPoint(B, BlockExitKind, L) {} 246 247 const CFGBlock *getBlock() const { 248 return reinterpret_cast<const CFGBlock*>(getData1()); 249 } 250 251 const Stmt *getTerminator() const { 252 return getBlock()->getTerminator(); 253 } 254 255private: 256 friend class ProgramPoint; 257 BlockExit() {} 258 static bool isKind(const ProgramPoint &Location) { 259 return Location.getKind() == BlockExitKind; 260 } 261}; 262 263class StmtPoint : public ProgramPoint { 264public: 265 StmtPoint(const Stmt *S, const void *p2, Kind k, const LocationContext *L, 266 const ProgramPointTag *tag) 267 : ProgramPoint(S, p2, k, L, tag) { 268 assert(S); 269 } 270 271 const Stmt *getStmt() const { return (const Stmt*) getData1(); } 272 273 template <typename T> 274 const T* getStmtAs() const { return dyn_cast<T>(getStmt()); } 275 276protected: 277 StmtPoint() {} 278private: 279 friend class ProgramPoint; 280 static bool isKind(const ProgramPoint &Location) { 281 unsigned k = Location.getKind(); 282 return k >= PreStmtKind && k <= MaxPostStmtKind; 283 } 284}; 285 286 287class PreStmt : public StmtPoint { 288public: 289 PreStmt(const Stmt *S, const LocationContext *L, const ProgramPointTag *tag, 290 const Stmt *SubStmt = nullptr) 291 : StmtPoint(S, SubStmt, PreStmtKind, L, tag) {} 292 293 const Stmt *getSubStmt() const { return (const Stmt*) getData2(); } 294 295private: 296 friend class ProgramPoint; 297 PreStmt() {} 298 static bool isKind(const ProgramPoint &Location) { 299 return Location.getKind() == PreStmtKind; 300 } 301}; 302 303class PostStmt : public StmtPoint { 304protected: 305 PostStmt() {} 306 PostStmt(const Stmt *S, const void *data, Kind k, const LocationContext *L, 307 const ProgramPointTag *tag = nullptr) 308 : StmtPoint(S, data, k, L, tag) {} 309 310public: 311 explicit PostStmt(const Stmt *S, Kind k, const LocationContext *L, 312 const ProgramPointTag *tag = nullptr) 313 : StmtPoint(S, nullptr, k, L, tag) {} 314 315 explicit PostStmt(const Stmt *S, const LocationContext *L, 316 const ProgramPointTag *tag = nullptr) 317 : StmtPoint(S, nullptr, PostStmtKind, L, tag) {} 318 319private: 320 friend class ProgramPoint; 321 static bool isKind(const ProgramPoint &Location) { 322 unsigned k = Location.getKind(); 323 return k >= MinPostStmtKind && k <= MaxPostStmtKind; 324 } 325}; 326 327// PostCondition represents the post program point of a branch condition. 328class PostCondition : public PostStmt { 329public: 330 PostCondition(const Stmt *S, const LocationContext *L, 331 const ProgramPointTag *tag = nullptr) 332 : PostStmt(S, PostConditionKind, L, tag) {} 333 334private: 335 friend class ProgramPoint; 336 PostCondition() {} 337 static bool isKind(const ProgramPoint &Location) { 338 return Location.getKind() == PostConditionKind; 339 } 340}; 341 342class LocationCheck : public StmtPoint { 343protected: 344 LocationCheck() {} 345 LocationCheck(const Stmt *S, const LocationContext *L, 346 ProgramPoint::Kind K, const ProgramPointTag *tag) 347 : StmtPoint(S, nullptr, K, L, tag) {} 348 349private: 350 friend class ProgramPoint; 351 static bool isKind(const ProgramPoint &location) { 352 unsigned k = location.getKind(); 353 return k == PreLoadKind || k == PreStoreKind; 354 } 355}; 356 357class PreLoad : public LocationCheck { 358public: 359 PreLoad(const Stmt *S, const LocationContext *L, 360 const ProgramPointTag *tag = nullptr) 361 : LocationCheck(S, L, PreLoadKind, tag) {} 362 363private: 364 friend class ProgramPoint; 365 PreLoad() {} 366 static bool isKind(const ProgramPoint &location) { 367 return location.getKind() == PreLoadKind; 368 } 369}; 370 371class PreStore : public LocationCheck { 372public: 373 PreStore(const Stmt *S, const LocationContext *L, 374 const ProgramPointTag *tag = nullptr) 375 : LocationCheck(S, L, PreStoreKind, tag) {} 376 377private: 378 friend class ProgramPoint; 379 PreStore() {} 380 static bool isKind(const ProgramPoint &location) { 381 return location.getKind() == PreStoreKind; 382 } 383}; 384 385class PostLoad : public PostStmt { 386public: 387 PostLoad(const Stmt *S, const LocationContext *L, 388 const ProgramPointTag *tag = nullptr) 389 : PostStmt(S, PostLoadKind, L, tag) {} 390 391private: 392 friend class ProgramPoint; 393 PostLoad() {} 394 static bool isKind(const ProgramPoint &Location) { 395 return Location.getKind() == PostLoadKind; 396 } 397}; 398 399/// \brief Represents a program point after a store evaluation. 400class PostStore : public PostStmt { 401public: 402 /// Construct the post store point. 403 /// \param Loc can be used to store the information about the location 404 /// used in the form it was uttered in the code. 405 PostStore(const Stmt *S, const LocationContext *L, const void *Loc, 406 const ProgramPointTag *tag = nullptr) 407 : PostStmt(S, PostStoreKind, L, tag) { 408 assert(getData2() == nullptr); 409 setData2(Loc); 410 } 411 412 /// \brief Returns the information about the location used in the store, 413 /// how it was uttered in the code. 414 const void *getLocationValue() const { 415 return getData2(); 416 } 417 418private: 419 friend class ProgramPoint; 420 PostStore() {} 421 static bool isKind(const ProgramPoint &Location) { 422 return Location.getKind() == PostStoreKind; 423 } 424}; 425 426class PostLValue : public PostStmt { 427public: 428 PostLValue(const Stmt *S, const LocationContext *L, 429 const ProgramPointTag *tag = nullptr) 430 : PostStmt(S, PostLValueKind, L, tag) {} 431 432private: 433 friend class ProgramPoint; 434 PostLValue() {} 435 static bool isKind(const ProgramPoint &Location) { 436 return Location.getKind() == PostLValueKind; 437 } 438}; 439 440/// Represents a point after we ran remove dead bindings BEFORE 441/// processing the given statement. 442class PreStmtPurgeDeadSymbols : public StmtPoint { 443public: 444 PreStmtPurgeDeadSymbols(const Stmt *S, const LocationContext *L, 445 const ProgramPointTag *tag = nullptr) 446 : StmtPoint(S, nullptr, PreStmtPurgeDeadSymbolsKind, L, tag) { } 447 448private: 449 friend class ProgramPoint; 450 PreStmtPurgeDeadSymbols() {} 451 static bool isKind(const ProgramPoint &Location) { 452 return Location.getKind() == PreStmtPurgeDeadSymbolsKind; 453 } 454}; 455 456/// Represents a point after we ran remove dead bindings AFTER 457/// processing the given statement. 458class PostStmtPurgeDeadSymbols : public StmtPoint { 459public: 460 PostStmtPurgeDeadSymbols(const Stmt *S, const LocationContext *L, 461 const ProgramPointTag *tag = nullptr) 462 : StmtPoint(S, nullptr, PostStmtPurgeDeadSymbolsKind, L, tag) { } 463 464private: 465 friend class ProgramPoint; 466 PostStmtPurgeDeadSymbols() {} 467 static bool isKind(const ProgramPoint &Location) { 468 return Location.getKind() == PostStmtPurgeDeadSymbolsKind; 469 } 470}; 471 472class BlockEdge : public ProgramPoint { 473public: 474 BlockEdge(const CFGBlock *B1, const CFGBlock *B2, const LocationContext *L) 475 : ProgramPoint(B1, B2, BlockEdgeKind, L) { 476 assert(B1 && "BlockEdge: source block must be non-null"); 477 assert(B2 && "BlockEdge: destination block must be non-null"); 478 } 479 480 const CFGBlock *getSrc() const { 481 return static_cast<const CFGBlock*>(getData1()); 482 } 483 484 const CFGBlock *getDst() const { 485 return static_cast<const CFGBlock*>(getData2()); 486 } 487 488private: 489 friend class ProgramPoint; 490 BlockEdge() {} 491 static bool isKind(const ProgramPoint &Location) { 492 return Location.getKind() == BlockEdgeKind; 493 } 494}; 495 496class PostInitializer : public ProgramPoint { 497public: 498 /// \brief Construct a PostInitializer point that represents a location after 499 /// CXXCtorInitializer expression evaluation. 500 /// 501 /// \param I The initializer. 502 /// \param Loc The location of the field being initialized. 503 PostInitializer(const CXXCtorInitializer *I, 504 const void *Loc, 505 const LocationContext *L) 506 : ProgramPoint(I, Loc, PostInitializerKind, L) {} 507 508 const CXXCtorInitializer *getInitializer() const { 509 return static_cast<const CXXCtorInitializer *>(getData1()); 510 } 511 512 /// \brief Returns the location of the field. 513 const void *getLocationValue() const { 514 return getData2(); 515 } 516 517private: 518 friend class ProgramPoint; 519 PostInitializer() {} 520 static bool isKind(const ProgramPoint &Location) { 521 return Location.getKind() == PostInitializerKind; 522 } 523}; 524 525/// Represents an implicit call event. 526/// 527/// The nearest statement is provided for diagnostic purposes. 528class ImplicitCallPoint : public ProgramPoint { 529public: 530 ImplicitCallPoint(const Decl *D, SourceLocation Loc, Kind K, 531 const LocationContext *L, const ProgramPointTag *Tag) 532 : ProgramPoint(Loc.getPtrEncoding(), D, K, L, Tag) {} 533 534 const Decl *getDecl() const { return static_cast<const Decl *>(getData2()); } 535 SourceLocation getLocation() const { 536 return SourceLocation::getFromPtrEncoding(getData1()); 537 } 538 539protected: 540 ImplicitCallPoint() {} 541private: 542 friend class ProgramPoint; 543 static bool isKind(const ProgramPoint &Location) { 544 return Location.getKind() >= MinImplicitCallKind && 545 Location.getKind() <= MaxImplicitCallKind; 546 } 547}; 548 549/// Represents a program point just before an implicit call event. 550/// 551/// Explicit calls will appear as PreStmt program points. 552class PreImplicitCall : public ImplicitCallPoint { 553public: 554 PreImplicitCall(const Decl *D, SourceLocation Loc, const LocationContext *L, 555 const ProgramPointTag *Tag = nullptr) 556 : ImplicitCallPoint(D, Loc, PreImplicitCallKind, L, Tag) {} 557 558private: 559 friend class ProgramPoint; 560 PreImplicitCall() {} 561 static bool isKind(const ProgramPoint &Location) { 562 return Location.getKind() == PreImplicitCallKind; 563 } 564}; 565 566/// Represents a program point just after an implicit call event. 567/// 568/// Explicit calls will appear as PostStmt program points. 569class PostImplicitCall : public ImplicitCallPoint { 570public: 571 PostImplicitCall(const Decl *D, SourceLocation Loc, const LocationContext *L, 572 const ProgramPointTag *Tag = nullptr) 573 : ImplicitCallPoint(D, Loc, PostImplicitCallKind, L, Tag) {} 574 575private: 576 friend class ProgramPoint; 577 PostImplicitCall() {} 578 static bool isKind(const ProgramPoint &Location) { 579 return Location.getKind() == PostImplicitCallKind; 580 } 581}; 582 583/// Represents a point when we begin processing an inlined call. 584/// CallEnter uses the caller's location context. 585class CallEnter : public ProgramPoint { 586public: 587 CallEnter(const Stmt *stmt, const StackFrameContext *calleeCtx, 588 const LocationContext *callerCtx) 589 : ProgramPoint(stmt, calleeCtx, CallEnterKind, callerCtx, nullptr) {} 590 591 const Stmt *getCallExpr() const { 592 return static_cast<const Stmt *>(getData1()); 593 } 594 595 const StackFrameContext *getCalleeContext() const { 596 return static_cast<const StackFrameContext *>(getData2()); 597 } 598 599 /// Returns the entry block in the CFG for the entered function. 600 const CFGBlock *getEntry() const { 601 const StackFrameContext *CalleeCtx = getCalleeContext(); 602 const CFG *CalleeCFG = CalleeCtx->getCFG(); 603 return &(CalleeCFG->getEntry()); 604 } 605 606private: 607 friend class ProgramPoint; 608 CallEnter() {} 609 static bool isKind(const ProgramPoint &Location) { 610 return Location.getKind() == CallEnterKind; 611 } 612}; 613 614/// Represents a point when we start the call exit sequence (for inlined call). 615/// 616/// The call exit is simulated with a sequence of nodes, which occur between 617/// CallExitBegin and CallExitEnd. The following operations occur between the 618/// two program points: 619/// - CallExitBegin 620/// - Bind the return value 621/// - Run Remove dead bindings (to clean up the dead symbols from the callee). 622/// - CallExitEnd 623class CallExitBegin : public ProgramPoint { 624public: 625 // CallExitBegin uses the callee's location context. 626 CallExitBegin(const StackFrameContext *L, const ReturnStmt *RS) 627 : ProgramPoint(RS, CallExitBeginKind, L, nullptr) { } 628 629private: 630 friend class ProgramPoint; 631 CallExitBegin() {} 632 static bool isKind(const ProgramPoint &Location) { 633 return Location.getKind() == CallExitBeginKind; 634 } 635}; 636 637/// Represents a point when we finish the call exit sequence (for inlined call). 638/// \sa CallExitBegin 639class CallExitEnd : public ProgramPoint { 640public: 641 // CallExitEnd uses the caller's location context. 642 CallExitEnd(const StackFrameContext *CalleeCtx, 643 const LocationContext *CallerCtx) 644 : ProgramPoint(CalleeCtx, CallExitEndKind, CallerCtx, nullptr) {} 645 646 const StackFrameContext *getCalleeContext() const { 647 return static_cast<const StackFrameContext *>(getData1()); 648 } 649 650private: 651 friend class ProgramPoint; 652 CallExitEnd() {} 653 static bool isKind(const ProgramPoint &Location) { 654 return Location.getKind() == CallExitEndKind; 655 } 656}; 657 658/// Represents a point when we exit a loop. 659/// When this ProgramPoint is encountered we can be sure that the symbolic 660/// execution of the corresponding LoopStmt is finished on the given path. 661/// Note: It is possible to encounter a LoopExit element when we haven't even 662/// encountered the loop itself. At the current state not all loop exits will 663/// result in a LoopExit program point. 664class LoopExit : public ProgramPoint { 665public: 666 LoopExit(const Stmt *LoopStmt, const LocationContext *LC) 667 : ProgramPoint(LoopStmt, nullptr, LoopExitKind, LC) {} 668 669 const Stmt *getLoopStmt() const { 670 return static_cast<const Stmt *>(getData1()); 671 } 672 673private: 674 friend class ProgramPoint; 675 LoopExit() {} 676 static bool isKind(const ProgramPoint &Location) { 677 return Location.getKind() == LoopExitKind; 678 } 679}; 680 681/// This is a meta program point, which should be skipped by all the diagnostic 682/// reasoning etc. 683class EpsilonPoint : public ProgramPoint { 684public: 685 EpsilonPoint(const LocationContext *L, const void *Data1, 686 const void *Data2 = nullptr, 687 const ProgramPointTag *tag = nullptr) 688 : ProgramPoint(Data1, Data2, EpsilonKind, L, tag) {} 689 690 const void *getData() const { return getData1(); } 691 692private: 693 friend class ProgramPoint; 694 EpsilonPoint() {} 695 static bool isKind(const ProgramPoint &Location) { 696 return Location.getKind() == EpsilonKind; 697 } 698}; 699 700} // end namespace clang 701 702 703namespace llvm { // Traits specialization for DenseMap 704 705template <> struct DenseMapInfo<clang::ProgramPoint> { 706 707static inline clang::ProgramPoint getEmptyKey() { 708 uintptr_t x = 709 reinterpret_cast<uintptr_t>(DenseMapInfo<void*>::getEmptyKey()) & ~0x7; 710 return clang::BlockEntrance(reinterpret_cast<clang::CFGBlock*>(x), nullptr); 711} 712 713static inline clang::ProgramPoint getTombstoneKey() { 714 uintptr_t x = 715 reinterpret_cast<uintptr_t>(DenseMapInfo<void*>::getTombstoneKey()) & ~0x7; 716 return clang::BlockEntrance(reinterpret_cast<clang::CFGBlock*>(x), nullptr); 717} 718 719static unsigned getHashValue(const clang::ProgramPoint &Loc) { 720 return Loc.getHashValue(); 721} 722 723static bool isEqual(const clang::ProgramPoint &L, 724 const clang::ProgramPoint &R) { 725 return L == R; 726} 727 728}; 729 730template <> 731struct isPodLike<clang::ProgramPoint> { static const bool value = true; }; 732 733} // end namespace llvm 734 735#endif 736