Szelethus created this revision. Szelethus added reviewers: NoQ, xazax.hun, dcoughlin, rnkovacs, Charusso, baloghadamsoftware. Szelethus added a project: clang. Herald added subscribers: cfe-commits, gamesh411, dkrupp.
Seems like we never had these, so here we go! I also did some refactoring as I was chasing a bug unrelated to this revision. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D66715 Files: clang/include/clang/Analysis/CFG.h clang/lib/Analysis/CFG.cpp clang/unittests/Analysis/CFGTest.cpp
Index: clang/unittests/Analysis/CFGTest.cpp =================================================================== --- clang/unittests/Analysis/CFGTest.cpp +++ clang/unittests/Analysis/CFGTest.cpp @@ -67,6 +67,46 @@ expectLinear(true, "void foo() { foo(); }"); // Recursion is not our problem. } +TEST(CFG, ElementDump) { + const char *Code = R"(void f() { + int i; + int j; + i = 5; + i = 6; + j = 7; + })"; + + BuildResult B = BuildCFG(Code); + EXPECT_EQ(BuildResult::BuiltCFG, B.getStatus()); + CFG *Cfg = B.getCFG(); + + // [B2 (ENTRY)] + // Succs (1): B1 + + // [B1] + // 1: int i; + // 2: int j; + // 3: i = 5 + // 4: i = 6 + // 5: j = 7 + // Preds (1): B2 + // Succs (1): B0 + + // [B0 (EXIT)] + // Preds (1): B1 + CFGBlock *MainBlock = *(Cfg->begin() + 1); + + llvm::SmallString<48> DumpBuffer; + llvm::raw_svector_ostream OS(DumpBuffer); + (*MainBlock)[0].dumpToStream(OS); + EXPECT_EQ("int i;\n", DumpBuffer); + + DumpBuffer.clear(); + + (*MainBlock->ref_begin()).dumpToStream(OS); + EXPECT_EQ("1: int i;\n", DumpBuffer); +} + TEST(CFG, ElementRefIterator) { const char *Code = R"(void f() { int i; Index: clang/lib/Analysis/CFG.cpp =================================================================== --- clang/lib/Analysis/CFG.cpp +++ clang/lib/Analysis/CFG.cpp @@ -4965,6 +4965,8 @@ public: StmtPrinterHelper(const CFG* cfg, const LangOptions &LO) : LangOpts(LO) { + if (!cfg) + return; for (CFG::const_iterator I = cfg->begin(), E = cfg->end(); I != E; ++I ) { unsigned j = 1; for (CFGBlock::const_iterator BI = (*I)->begin(), BEnd = (*I)->end() ; @@ -5286,10 +5288,22 @@ } } +static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, + const CFGElement &E); + +void CFGElement::dumpToStream(llvm::raw_ostream &OS) const { + StmtPrinterHelper Helper(nullptr, {}); + print_elem(OS, Helper, *this); +} + static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, const CFGElement &E) { - if (Optional<CFGStmt> CS = E.getAs<CFGStmt>()) { - const Stmt *S = CS->getStmt(); + switch (E.getKind()) { + case CFGElement::Kind::Statement: + case CFGElement::Kind::CXXRecordTypedCall: + case CFGElement::Kind::Constructor: { + CFGStmt CS = E.castAs<CFGStmt>(); + const Stmt *S = CS.getStmt(); assert(S != nullptr && "Expecting non-null Stmt"); // special printing for statement-expressions. @@ -5341,12 +5355,18 @@ // Expressions need a newline. if (isa<Expr>(S)) OS << '\n'; - } else if (Optional<CFGInitializer> IE = E.getAs<CFGInitializer>()) { - print_initializer(OS, Helper, IE->getInitializer()); + + break; + } + + case CFGElement::Kind::Initializer: + print_initializer(OS, Helper, E.castAs<CFGInitializer>().getInitializer()); OS << '\n'; - } else if (Optional<CFGAutomaticObjDtor> DE = - E.getAs<CFGAutomaticObjDtor>()) { - const VarDecl *VD = DE->getVarDecl(); + break; + + case CFGElement::Kind::AutomaticObjectDtor: { + CFGAutomaticObjDtor DE = E.castAs<CFGAutomaticObjDtor>(); + const VarDecl *VD = DE.getVarDecl(); Helper.handleDecl(VD, OS); ASTContext &ACtx = VD->getASTContext(); @@ -5358,53 +5378,76 @@ OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()"; OS << " (Implicit destructor)\n"; - } else if (Optional<CFGLifetimeEnds> DE = E.getAs<CFGLifetimeEnds>()) { - const VarDecl *VD = DE->getVarDecl(); - Helper.handleDecl(VD, OS); + break; + } + + case CFGElement::Kind::LifetimeEnds: + Helper.handleDecl(E.castAs<CFGLifetimeEnds>().getVarDecl(), OS); OS << " (Lifetime ends)\n"; - } else if (Optional<CFGLoopExit> LE = E.getAs<CFGLoopExit>()) { - const Stmt *LoopStmt = LE->getLoopStmt(); - OS << LoopStmt->getStmtClassName() << " (LoopExit)\n"; - } else if (Optional<CFGScopeBegin> SB = E.getAs<CFGScopeBegin>()) { + break; + + case CFGElement::Kind::LoopExit: + OS << E.castAs<CFGLoopExit>().getLoopStmt()->getStmtClassName() << " (LoopExit)\n"; + break; + + case CFGElement::Kind::ScopeBegin: OS << "CFGScopeBegin("; - if (const VarDecl *VD = SB->getVarDecl()) + if (const VarDecl *VD = E.castAs<CFGScopeBegin>().getVarDecl()) OS << VD->getQualifiedNameAsString(); OS << ")\n"; - } else if (Optional<CFGScopeEnd> SE = E.getAs<CFGScopeEnd>()) { + break; + + case CFGElement::Kind::ScopeEnd: OS << "CFGScopeEnd("; - if (const VarDecl *VD = SE->getVarDecl()) + if (const VarDecl *VD = E.castAs<CFGScopeEnd>().getVarDecl()) OS << VD->getQualifiedNameAsString(); OS << ")\n"; - } else if (Optional<CFGNewAllocator> NE = E.getAs<CFGNewAllocator>()) { + break; + + case CFGElement::Kind::NewAllocator: OS << "CFGNewAllocator("; - if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr()) + if (const CXXNewExpr *AllocExpr = E.castAs<CFGNewAllocator>().getAllocatorExpr()) AllocExpr->getType().print(OS, PrintingPolicy(Helper.getLangOpts())); OS << ")\n"; - } else if (Optional<CFGDeleteDtor> DE = E.getAs<CFGDeleteDtor>()) { - const CXXRecordDecl *RD = DE->getCXXRecordDecl(); + break; + + case CFGElement::Kind::DeleteDtor: { + CFGDeleteDtor DE = E.castAs<CFGDeleteDtor>(); + const CXXRecordDecl *RD = DE.getCXXRecordDecl(); if (!RD) return; CXXDeleteExpr *DelExpr = - const_cast<CXXDeleteExpr*>(DE->getDeleteExpr()); + const_cast<CXXDeleteExpr*>(DE.getDeleteExpr()); Helper.handledStmt(cast<Stmt>(DelExpr->getArgument()), OS); OS << "->~" << RD->getName().str() << "()"; OS << " (Implicit destructor)\n"; - } else if (Optional<CFGBaseDtor> BE = E.getAs<CFGBaseDtor>()) { - const CXXBaseSpecifier *BS = BE->getBaseSpecifier(); + break; + } + + case CFGElement::Kind::BaseDtor: { + const CXXBaseSpecifier *BS = E.castAs<CFGBaseDtor>().getBaseSpecifier(); OS << "~" << BS->getType()->getAsCXXRecordDecl()->getName() << "()"; OS << " (Base object destructor)\n"; - } else if (Optional<CFGMemberDtor> ME = E.getAs<CFGMemberDtor>()) { - const FieldDecl *FD = ME->getFieldDecl(); + break; + } + + case CFGElement::Kind::MemberDtor: { + const FieldDecl *FD = E.castAs<CFGMemberDtor>().getFieldDecl(); const Type *T = FD->getType()->getBaseElementTypeUnsafe(); OS << "this->" << FD->getName(); OS << ".~" << T->getAsCXXRecordDecl()->getName() << "()"; OS << " (Member object destructor)\n"; - } else if (Optional<CFGTemporaryDtor> TE = E.getAs<CFGTemporaryDtor>()) { - const CXXBindTemporaryExpr *BT = TE->getBindTemporaryExpr(); + break; + } + + case CFGElement::Kind::TemporaryDtor: { + const CXXBindTemporaryExpr *BT = E.castAs<CFGTemporaryDtor>().getBindTemporaryExpr(); OS << "~"; BT->getType().print(OS, PrintingPolicy(Helper.getLangOpts())); OS << "() (Temporary object destructor)\n"; + break; + } } } Index: clang/include/clang/Analysis/CFG.h =================================================================== --- clang/include/clang/Analysis/CFG.h +++ clang/include/clang/Analysis/CFG.h @@ -121,6 +121,12 @@ x |= Data1.getInt(); return (Kind) x; } + + void dumpToStream(llvm::raw_ostream &OS) const; + + void dump() const { + dumpToStream(llvm::errs()); + } }; class CFGStmt : public CFGElement { @@ -650,8 +656,17 @@ } bool operator!=(ElementRefImpl Other) const { return !(*this == Other); } - CFGElement operator*() { return (*Parent)[Index]; } - CFGElementPtr operator->() { return &*(Parent->begin() + Index); } + CFGElement operator*() const { return (*Parent)[Index]; } + CFGElementPtr operator->() const { return &*(Parent->begin() + Index); } + + void dumpToStream(llvm::raw_ostream &OS) const { + OS << getIndexInBlock() + 1 << ": "; + (*this)->dumpToStream(OS); + } + + void dump() const { + dumpToStream(llvm::errs()); + } }; template <bool IsReverse, bool IsConst> class ElementRefIterator {
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits