[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

llvm-svn: 365177
This commit is contained in:
Kristof Umann 2019-07-05 09:52:00 +00:00
parent bb7e97d783
commit d5c9d9b682
3 changed files with 84 additions and 0 deletions

View File

@ -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 {

View File

@ -5615,6 +5615,30 @@ void CFGBlock::printTerminatorJson(raw_ostream &Out, const LangOptions &LO,
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)

View File

@ -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