r365177 - [CFG] Add a new function to get the proper condition of a CFGBlock
Kristof Umann via cfe-commits
cfe-commits at lists.llvm.org
Fri Jul 5 02:52:00 PDT 2019
Author: szelethus
Date: Fri Jul 5 02:52:00 2019
New Revision: 365177
URL: http://llvm.org/viewvc/llvm-project?rev=365177&view=rev
Log:
[CFG] Add a new function to get the proper condition of a CFGBlock
getTerminatorCondition() returned a condition that may be outside of the
block, while the new function returns the proper one:
if (A && B && C) {}
Return C instead of A && B && C.
Differential Revision: https://reviews.llvm.org/D63538
Modified:
cfe/trunk/include/clang/Analysis/CFG.h
cfe/trunk/lib/Analysis/CFG.cpp
cfe/trunk/unittests/Analysis/CFGTest.cpp
Modified: cfe/trunk/include/clang/Analysis/CFG.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/CFG.h?rev=365177&r1=365176&r2=365177&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/CFG.h (original)
+++ cfe/trunk/include/clang/Analysis/CFG.h Fri Jul 5 02:52:00 2019
@@ -860,6 +860,14 @@ public:
Stmt *getTerminatorStmt() { return Terminator.getStmt(); }
const Stmt *getTerminatorStmt() const { return Terminator.getStmt(); }
+ /// \returns the last (\c rbegin()) condition, e.g. observe the following code
+ /// snippet:
+ /// if (A && B && C)
+ /// A block would be created for \c A, \c B, and \c C. For the latter,
+ /// \c getTerminatorStmt() would retrieve the entire condition, rather than
+ /// C itself, while this method would only return C.
+ const Expr *getLastCondition() const;
+
Stmt *getTerminatorCondition(bool StripParens = true);
const Stmt *getTerminatorCondition(bool StripParens = true) const {
Modified: cfe/trunk/lib/Analysis/CFG.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFG.cpp?rev=365177&r1=365176&r2=365177&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CFG.cpp (original)
+++ cfe/trunk/lib/Analysis/CFG.cpp Fri Jul 5 02:52:00 2019
@@ -5615,6 +5615,30 @@ void CFGBlock::printTerminatorJson(raw_o
Out << JsonFormat(TempOut.str(), AddQuotes);
}
+const Expr *CFGBlock::getLastCondition() const {
+ // If the terminator is a temporary dtor or a virtual base, etc, we can't
+ // retrieve a meaningful condition, bail out.
+ if (Terminator.getKind() != CFGTerminator::StmtBranch)
+ return nullptr;
+
+ // Also, if this method was called on a block that doesn't have 2 successors,
+ // this block doesn't have retrievable condition.
+ if (succ_size() < 2)
+ return nullptr;
+
+ auto StmtElem = rbegin()->getAs<CFGStmt>();
+ if (!StmtElem)
+ return nullptr;
+
+ const Stmt *Cond = StmtElem->getStmt();
+ if (isa<ObjCForCollectionStmt>(Cond))
+ return nullptr;
+
+ // Only ObjCForCollectionStmt is known not to be a non-Expr terminator, hence
+ // the cast<>.
+ return cast<Expr>(Cond)->IgnoreParens();
+}
+
Stmt *CFGBlock::getTerminatorCondition(bool StripParens) {
Stmt *Terminator = getTerminatorStmt();
if (!Terminator)
Modified: cfe/trunk/unittests/Analysis/CFGTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Analysis/CFGTest.cpp?rev=365177&r1=365176&r2=365177&view=diff
==============================================================================
--- cfe/trunk/unittests/Analysis/CFGTest.cpp (original)
+++ cfe/trunk/unittests/Analysis/CFGTest.cpp Fri Jul 5 02:52:00 2019
@@ -117,6 +117,58 @@ TEST(CFG, IsLinear) {
expectLinear(true, "void foo() { foo(); }"); // Recursion is not our problem.
}
+TEST(CFG, ConditionExpr) {
+ const char *Code = R"(void f(bool A, bool B, bool C) {
+ if (A && B && C)
+ int x;
+ })";
+ BuildResult Result = BuildCFG(Code);
+ EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus());
+
+ // [B5 (ENTRY)] -> [B4] -> [B3] -> [B2] -> [B1] -> [B0 (EXIT)]
+ // \ \ \ /
+ // ------------------------------->
+
+ CFG *cfg = Result.getCFG();
+
+ auto GetBlock = [cfg] (unsigned Index) -> CFGBlock * {
+ assert(Index < cfg->size());
+ return *(cfg->begin() + Index);
+ };
+
+ auto GetExprText = [] (const Expr *E) -> std::string {
+ // It's very awkward trying to recover the actual expression text without
+ // a real source file, so use this as a workaround. We know that the
+ // condition expression looks like this:
+ //
+ // ImplicitCastExpr 0xd07bf8 '_Bool' <LValueToRValue>
+ // `-DeclRefExpr 0xd07bd8 '_Bool' lvalue ParmVar 0xd07960 'C' '_Bool'
+
+ assert(isa<ImplicitCastExpr>(E));
+ assert(++E->child_begin() == E->child_end());
+ const auto *D = dyn_cast<DeclRefExpr>(*E->child_begin());
+ return D->getFoundDecl()->getNameAsString();
+ };
+
+ EXPECT_EQ(GetBlock(1)->getLastCondition(), nullptr);
+ EXPECT_EQ(GetExprText(GetBlock(4)->getLastCondition()), "A");
+ EXPECT_EQ(GetExprText(GetBlock(3)->getLastCondition()), "B");
+ EXPECT_EQ(GetExprText(GetBlock(2)->getLastCondition()), "C");
+
+ //===--------------------------------------------------------------------===//
+
+ Code = R"(void foo(int x, int y) {
+ (void)(x + y);
+ })";
+ Result = BuildCFG(Code);
+ EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus());
+
+ // [B2 (ENTRY)] -> [B1] -> [B0 (EXIT)]
+
+ cfg = Result.getCFG();
+ EXPECT_EQ(GetBlock(1)->getLastCondition(), nullptr);
+}
+
} // namespace
} // namespace analysis
} // namespace clang
More information about the cfe-commits
mailing list