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