Szelethus updated this revision to Diff 207902.
Szelethus added a comment.

- Bail out if the actual terminator isn't a branch
- Bail out if the number of successors is less than 2
- LLVM-ify the code as suggested!
- Add some unit tests (I mean, you can kinda see how it was duct taped 
together, but it's maybe a hair better than nothing?)


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D63538/new/

https://reviews.llvm.org/D63538

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,57 @@
   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));
+    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
Index: clang/lib/Analysis/CFG.cpp
===================================================================
--- clang/lib/Analysis/CFG.cpp
+++ clang/lib/Analysis/CFG.cpp
@@ -5615,6 +5615,30 @@
   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)
Index: clang/include/clang/Analysis/CFG.h
===================================================================
--- clang/include/clang/Analysis/CFG.h
+++ clang/include/clang/Analysis/CFG.h
@@ -860,6 +860,14 @@
   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 {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to