Changeset View
Changeset View
Standalone View
Standalone View
include/clang/Analysis/CFG.h
Show First 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | |||||
/// CFGElement - Represents a top-level expression in a basic block. | /// CFGElement - Represents a top-level expression in a basic block. | ||||
class CFGElement { | class CFGElement { | ||||
public: | public: | ||||
enum Kind { | enum Kind { | ||||
// main kind | // main kind | ||||
Statement, | Statement, | ||||
Initializer, | Initializer, | ||||
ScopeBegin, | |||||
ScopeEnd, | |||||
NewAllocator, | NewAllocator, | ||||
LifetimeEnds, | LifetimeEnds, | ||||
LoopExit, | LoopExit, | ||||
// dtor kind | // dtor kind | ||||
AutomaticObjectDtor, | AutomaticObjectDtor, | ||||
DeleteDtor, | DeleteDtor, | ||||
BaseDtor, | BaseDtor, | ||||
MemberDtor, | MemberDtor, | ||||
▲ Show 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | explicit CFGNewAllocator(const CXXNewExpr *S) | ||||
: CFGElement(NewAllocator, S) {} | : CFGElement(NewAllocator, S) {} | ||||
// Get the new expression. | // Get the new expression. | ||||
const CXXNewExpr *getAllocatorExpr() const { | const CXXNewExpr *getAllocatorExpr() const { | ||||
return static_cast<CXXNewExpr *>(Data1.getPointer()); | return static_cast<CXXNewExpr *>(Data1.getPointer()); | ||||
} | } | ||||
private: | private: | ||||
friend class CFGElement; | friend class CFGElement; | ||||
dcoughlin: Even though a lot of code doesn't follow the guidelines, LLVM style discourages duplicating the… | |||||
CFGNewAllocator() = default; | CFGNewAllocator() = default; | ||||
static bool isKind(const CFGElement &elem) { | static bool isKind(const CFGElement &elem) { | ||||
return elem.getKind() == NewAllocator; | return elem.getKind() == NewAllocator; | ||||
} | } | ||||
}; | }; | ||||
/// Represents the point where a loop ends. | /// Represents the point where a loop ends. | ||||
/// This element is is only produced when building the CFG for the static | /// This element is is only produced when building the CFG for the static | ||||
/// analyzer and hidden behind the 'cfg-loopexit' analyzer config flag. | /// analyzer and hidden behind the 'cfg-loopexit' analyzer config flag. | ||||
/// | /// | ||||
/// Note: a loop exit element can be reached even when the loop body was never | /// Note: a loop exit element can be reached even when the loop body was never | ||||
/// entered. | /// entered. | ||||
class CFGLoopExit : public CFGElement { | class CFGLoopExit : public CFGElement { | ||||
public: | public: | ||||
explicit CFGLoopExit(const Stmt *stmt) : CFGElement(LoopExit, stmt) {} | explicit CFGLoopExit(const Stmt *stmt) : CFGElement(LoopExit, stmt) {} | ||||
const Stmt *getLoopStmt() const { | const Stmt *getLoopStmt() const { | ||||
return static_cast<Stmt *>(Data1.getPointer()); | return static_cast<Stmt *>(Data1.getPointer()); | ||||
} | } | ||||
Same doc comment style issue here. dcoughlin: Same doc comment style issue here. | |||||
private: | private: | ||||
friend class CFGElement; | friend class CFGElement; | ||||
CFGLoopExit() = default; | CFGLoopExit() = default; | ||||
static bool isKind(const CFGElement &elem) { | static bool isKind(const CFGElement &elem) { | ||||
return elem.getKind() == LoopExit; | return elem.getKind() == LoopExit; | ||||
Show All 19 Lines | private: | ||||
CFGLifetimeEnds() = default; | CFGLifetimeEnds() = default; | ||||
static bool isKind(const CFGElement &elem) { | static bool isKind(const CFGElement &elem) { | ||||
return elem.getKind() == LifetimeEnds; | return elem.getKind() == LifetimeEnds; | ||||
} | } | ||||
}; | }; | ||||
/// Represents beginning of a scope implicitly generated | |||||
/// by the compiler on encountering a CompoundStmt | |||||
class CFGScopeBegin : public CFGElement { | |||||
public: | |||||
CFGScopeBegin() {} | |||||
CFGScopeBegin(const VarDecl *VD, const Stmt *S) | |||||
: CFGElement(ScopeBegin, VD, S) {} | |||||
// Get statement that triggered a new scope. | |||||
const Stmt *getTriggerStmt() const { | |||||
return static_cast<Stmt*>(Data2.getPointer()); | |||||
} | |||||
// Get VD that triggered a new scope. | |||||
const VarDecl *getVarDecl() const { | |||||
return static_cast<VarDecl *>(Data1.getPointer()); | |||||
} | |||||
private: | |||||
friend class CFGElement; | |||||
static bool isKind(const CFGElement &E) { | |||||
Kind kind = E.getKind(); | |||||
return kind == ScopeBegin; | |||||
} | |||||
}; | |||||
/// Represents end of a scope implicitly generated by | |||||
/// the compiler after the last Stmt in a CompoundStmt's body | |||||
class CFGScopeEnd : public CFGElement { | |||||
public: | |||||
CFGScopeEnd() {} | |||||
CFGScopeEnd(const VarDecl *VD, const Stmt *S) : CFGElement(ScopeEnd, VD, S) {} | |||||
const VarDecl *getVarDecl() const { | |||||
return static_cast<VarDecl *>(Data1.getPointer()); | |||||
} | |||||
const Stmt *getTriggerStmt() const { | |||||
return static_cast<Stmt *>(Data2.getPointer()); | |||||
} | |||||
private: | |||||
friend class CFGElement; | |||||
static bool isKind(const CFGElement &E) { | |||||
Kind kind = E.getKind(); | |||||
return kind == ScopeEnd; | |||||
} | |||||
}; | |||||
/// CFGImplicitDtor - Represents C++ object destructor implicitly generated | /// CFGImplicitDtor - Represents C++ object destructor implicitly generated | ||||
/// by compiler on various occasions. | /// by compiler on various occasions. | ||||
class CFGImplicitDtor : public CFGElement { | class CFGImplicitDtor : public CFGElement { | ||||
protected: | protected: | ||||
CFGImplicitDtor() = default; | CFGImplicitDtor() = default; | ||||
CFGImplicitDtor(Kind kind, const void *data1, const void *data2 = nullptr) | CFGImplicitDtor(Kind kind, const void *data1, const void *data2 = nullptr) | ||||
: CFGElement(kind, data1, data2) { | : CFGElement(kind, data1, data2) { | ||||
▲ Show 20 Lines • Show All 497 Lines • ▼ Show 20 Lines | public: | ||||
CFG *getParent() const { return Parent; } | CFG *getParent() const { return Parent; } | ||||
void dump() const; | void dump() const; | ||||
void dump(const CFG *cfg, const LangOptions &LO, bool ShowColors = false) const; | void dump(const CFG *cfg, const LangOptions &LO, bool ShowColors = false) const; | ||||
void print(raw_ostream &OS, const CFG* cfg, const LangOptions &LO, | void print(raw_ostream &OS, const CFG* cfg, const LangOptions &LO, | ||||
bool ShowColors) const; | bool ShowColors) const; | ||||
void printTerminator(raw_ostream &OS, const LangOptions &LO) const; | void printTerminator(raw_ostream &OS, const LangOptions &LO) const; | ||||
void printAsOperand(raw_ostream &OS, bool /*PrintType*/) { | void printAsOperand(raw_ostream &OS, bool /*PrintType*/) { | ||||
I would suggest calling this "AddScopes" to match the pluralization of the other options and propagate that name throughout the rest of the patch (e.g. "IncludeScopesInCFG", etc.). dcoughlin: I would suggest calling this "AddScopes" to match the pluralization of the other options and… | |||||
OS << "BB#" << getBlockID(); | OS << "BB#" << getBlockID(); | ||||
} | } | ||||
/// Adds a (potentially unreachable) successor block to the current block. | /// Adds a (potentially unreachable) successor block to the current block. | ||||
void addSuccessor(AdjacentBlock Succ, BumpVectorContext &C); | void addSuccessor(AdjacentBlock Succ, BumpVectorContext &C); | ||||
void appendStmt(Stmt *statement, BumpVectorContext &C) { | void appendStmt(Stmt *statement, BumpVectorContext &C) { | ||||
Elements.push_back(CFGStmt(statement), C); | Elements.push_back(CFGStmt(statement), C); | ||||
} | } | ||||
void appendInitializer(CXXCtorInitializer *initializer, | void appendInitializer(CXXCtorInitializer *initializer, | ||||
BumpVectorContext &C) { | BumpVectorContext &C) { | ||||
Elements.push_back(CFGInitializer(initializer), C); | Elements.push_back(CFGInitializer(initializer), C); | ||||
} | } | ||||
void appendNewAllocator(CXXNewExpr *NE, | void appendNewAllocator(CXXNewExpr *NE, | ||||
BumpVectorContext &C) { | BumpVectorContext &C) { | ||||
Elements.push_back(CFGNewAllocator(NE), C); | Elements.push_back(CFGNewAllocator(NE), C); | ||||
} | } | ||||
void appendScopeBegin(const VarDecl *VD, const Stmt *S, | |||||
BumpVectorContext &C) { | |||||
Elements.push_back(CFGScopeBegin(VD, S), C); | |||||
} | |||||
void prependScopeBegin(const VarDecl *VD, const Stmt *S, | |||||
BumpVectorContext &C) { | |||||
Elements.insert(Elements.rbegin(), 1, CFGScopeBegin(VD, S), C); | |||||
} | |||||
void appendScopeEnd(const VarDecl *VD, const Stmt *S, BumpVectorContext &C) { | |||||
Elements.push_back(CFGScopeEnd(VD, S), C); | |||||
} | |||||
void prependScopeEnd(const VarDecl *VD, const Stmt *S, BumpVectorContext &C) { | |||||
Elements.insert(Elements.rbegin(), 1, CFGScopeEnd(VD, S), C); | |||||
} | |||||
void appendBaseDtor(const CXXBaseSpecifier *BS, BumpVectorContext &C) { | void appendBaseDtor(const CXXBaseSpecifier *BS, BumpVectorContext &C) { | ||||
Elements.push_back(CFGBaseDtor(BS), C); | Elements.push_back(CFGBaseDtor(BS), C); | ||||
} | } | ||||
void appendMemberDtor(FieldDecl *FD, BumpVectorContext &C) { | void appendMemberDtor(FieldDecl *FD, BumpVectorContext &C) { | ||||
Elements.push_back(CFGMemberDtor(FD), C); | Elements.push_back(CFGMemberDtor(FD), C); | ||||
} | } | ||||
Show All 12 Lines | public: | ||||
void appendLoopExit(const Stmt *LoopStmt, BumpVectorContext &C) { | void appendLoopExit(const Stmt *LoopStmt, BumpVectorContext &C) { | ||||
Elements.push_back(CFGLoopExit(LoopStmt), C); | Elements.push_back(CFGLoopExit(LoopStmt), C); | ||||
} | } | ||||
void appendDeleteDtor(CXXRecordDecl *RD, CXXDeleteExpr *DE, BumpVectorContext &C) { | void appendDeleteDtor(CXXRecordDecl *RD, CXXDeleteExpr *DE, BumpVectorContext &C) { | ||||
Elements.push_back(CFGDeleteDtor(RD, DE), C); | Elements.push_back(CFGDeleteDtor(RD, DE), C); | ||||
} | } | ||||
void removeAllSuccessors() { Succs.clear(); } | |||||
// Destructors must be inserted in reversed order. So insertion is in two | // Destructors must be inserted in reversed order. So insertion is in two | ||||
// steps. First we prepare space for some number of elements, then we insert | // steps. First we prepare space for some number of elements, then we insert | ||||
// the elements beginning at the last position in prepared space. | // the elements beginning at the last position in prepared space. | ||||
iterator beginAutomaticObjDtorsInsert(iterator I, size_t Cnt, | iterator beginAutomaticObjDtorsInsert(iterator I, size_t Cnt, | ||||
BumpVectorContext &C) { | BumpVectorContext &C) { | ||||
return iterator(Elements.insert(I.base(), Cnt, | return iterator(Elements.insert(I.base(), Cnt, | ||||
CFGAutomaticObjDtor(nullptr, nullptr), C)); | CFGAutomaticObjDtor(nullptr, nullptr), C)); | ||||
} | } | ||||
Show All 9 Lines | iterator beginLifetimeEndsInsert(iterator I, size_t Cnt, | ||||
BumpVectorContext &C) { | BumpVectorContext &C) { | ||||
return iterator( | return iterator( | ||||
Elements.insert(I.base(), Cnt, CFGLifetimeEnds(nullptr, nullptr), C)); | Elements.insert(I.base(), Cnt, CFGLifetimeEnds(nullptr, nullptr), C)); | ||||
} | } | ||||
iterator insertLifetimeEnds(iterator I, VarDecl *VD, Stmt *S) { | iterator insertLifetimeEnds(iterator I, VarDecl *VD, Stmt *S) { | ||||
*I = CFGLifetimeEnds(VD, S); | *I = CFGLifetimeEnds(VD, S); | ||||
return ++I; | return ++I; | ||||
} | } | ||||
// Scope leaving must be performed in reversed order. So insertion is in two | |||||
// steps. First we prepare space for some number of elements, then we insert | |||||
// the elements beginning at the last position in prepared space. | |||||
iterator beginScopeEndInsert(iterator I, size_t Cnt, | |||||
BumpVectorContext &C) { | |||||
return iterator( | |||||
Elements.insert(I.base(), Cnt, CFGScopeEnd(nullptr, nullptr), C)); | |||||
} | |||||
iterator insertScopeEnd(iterator I, VarDecl *VD, Stmt *S) { | |||||
*I = CFGScopeEnd(VD, S); | |||||
return ++I; | |||||
} | |||||
}; | }; | ||||
/// \brief CFGCallback defines methods that should be called when a logical | /// \brief CFGCallback defines methods that should be called when a logical | ||||
/// operator error is found when building the CFG. | /// operator error is found when building the CFG. | ||||
class CFGCallback { | class CFGCallback { | ||||
public: | public: | ||||
CFGCallback() = default; | CFGCallback() = default; | ||||
virtual ~CFGCallback() = default; | virtual ~CFGCallback() = default; | ||||
Show All 26 Lines | public: | ||||
CFGCallback *Observer = nullptr; | CFGCallback *Observer = nullptr; | ||||
bool PruneTriviallyFalseEdges = true; | bool PruneTriviallyFalseEdges = true; | ||||
bool AddEHEdges = false; | bool AddEHEdges = false; | ||||
bool AddInitializers = false; | bool AddInitializers = false; | ||||
bool AddImplicitDtors = false; | bool AddImplicitDtors = false; | ||||
bool AddLifetime = false; | bool AddLifetime = false; | ||||
bool AddLoopExit = false; | bool AddLoopExit = false; | ||||
bool AddTemporaryDtors = false; | bool AddTemporaryDtors = false; | ||||
bool AddScopes = false; | |||||
bool AddStaticInitBranches = false; | bool AddStaticInitBranches = false; | ||||
bool AddCXXNewAllocator = false; | bool AddCXXNewAllocator = false; | ||||
bool AddCXXDefaultInitExprInCtors = false; | bool AddCXXDefaultInitExprInCtors = false; | ||||
BuildOptions() = default; | BuildOptions() = default; | ||||
bool alwaysAdd(const Stmt *stmt) const { | bool alwaysAdd(const Stmt *stmt) const { | ||||
return alwaysAddMask[stmt->getStmtClass()]; | return alwaysAddMask[stmt->getStmtClass()]; | ||||
▲ Show 20 Lines • Show All 306 Lines • Show Last 20 Lines |
Even though a lot of code doesn't follow the guidelines, LLVM style discourages duplicating the function or class name at the beginning of the comment. (See http://llvm.org/docs/CodingStandards.html#doxygen-use-in-documentation-comments)