llvm-project/clang/unittests/Analysis/CFGTest.cpp

274 lines
10 KiB
C++

//===- unittests/Analysis/CFGTest.cpp - CFG tests -------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "CFGBuildResult.h"
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"
#include <algorithm>
#include <string>
#include <vector>
namespace clang {
namespace analysis {
namespace {
// Constructing a CFG for a range-based for over a dependent type fails (but
// should not crash).
TEST(CFG, RangeBasedForOverDependentType) {
const char *Code = "class Foo;\n"
"template <typename T>\n"
"void f(const T &Range) {\n"
" for (const Foo *TheFoo : Range) {\n"
" }\n"
"}\n";
EXPECT_EQ(BuildResult::SawFunctionBody, BuildCFG(Code).getStatus());
}
TEST(CFG, StaticInitializerLastCondition) {
const char *Code = "void f() {\n"
" int i = 5 ;\n"
" static int j = 3 ;\n"
"}\n";
CFG::BuildOptions Options;
Options.AddStaticInitBranches = true;
Options.setAllAlwaysAdd();
BuildResult B = BuildCFG(Code, Options);
EXPECT_EQ(BuildResult::BuiltCFG, B.getStatus());
EXPECT_EQ(1u, B.getCFG()->getEntry().succ_size());
CFGBlock *Block = *B.getCFG()->getEntry().succ_begin();
EXPECT_TRUE(isa<DeclStmt>(Block->getTerminatorStmt()));
EXPECT_EQ(nullptr, Block->getLastCondition());
}
// Constructing a CFG containing a delete expression on a dependent type should
// not crash.
TEST(CFG, DeleteExpressionOnDependentType) {
const char *Code = "template<class T>\n"
"void f(T t) {\n"
" delete t;\n"
"}\n";
EXPECT_EQ(BuildResult::BuiltCFG, BuildCFG(Code).getStatus());
}
// Constructing a CFG on a function template with a variable of incomplete type
// should not crash.
TEST(CFG, VariableOfIncompleteType) {
const char *Code = "template<class T> void f() {\n"
" class Undefined;\n"
" Undefined u;\n"
"}\n";
EXPECT_EQ(BuildResult::BuiltCFG, BuildCFG(Code).getStatus());
}
TEST(CFG, IsLinear) {
auto expectLinear = [](bool IsLinear, const char *Code) {
BuildResult B = BuildCFG(Code);
EXPECT_EQ(BuildResult::BuiltCFG, B.getStatus());
EXPECT_EQ(IsLinear, B.getCFG()->isLinear());
};
expectLinear(true, "void foo() {}");
expectLinear(true, "void foo() { if (true) return; }");
expectLinear(true, "void foo() { if constexpr (false); }");
expectLinear(false, "void foo(bool coin) { if (coin) return; }");
expectLinear(false, "void foo() { for(;;); }");
expectLinear(false, "void foo() { do {} while (true); }");
expectLinear(true, "void foo() { do {} while (false); }");
expectLinear(true, "void foo() { foo(); }"); // Recursion is not our problem.
}
TEST(CFG, ElementRefIterator) {
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);
constexpr CFGBlock::ref_iterator::difference_type MainBlockSize = 4;
// The rest of this test looks totally insane, but there iterators are
// templates under the hood, to it's important to instantiate everything for
// proper converage.
// Non-reverse, non-const version
size_t Index = 0;
for (CFGBlock::CFGElementRef ElementRef : MainBlock->refs()) {
EXPECT_EQ(ElementRef.getParent(), MainBlock);
EXPECT_EQ(ElementRef.getIndexInBlock(), Index);
EXPECT_TRUE(ElementRef->getAs<CFGStmt>());
EXPECT_TRUE((*ElementRef).getAs<CFGStmt>());
EXPECT_EQ(ElementRef.getParent(), MainBlock);
++Index;
}
EXPECT_TRUE(*MainBlock->ref_begin() < *(MainBlock->ref_begin() + 1));
EXPECT_TRUE(*MainBlock->ref_begin() == *MainBlock->ref_begin());
EXPECT_FALSE(*MainBlock->ref_begin() != *MainBlock->ref_begin());
EXPECT_TRUE(MainBlock->ref_begin() < MainBlock->ref_begin() + 1);
EXPECT_TRUE(MainBlock->ref_begin() == MainBlock->ref_begin());
EXPECT_FALSE(MainBlock->ref_begin() != MainBlock->ref_begin());
EXPECT_EQ(MainBlock->ref_end() - MainBlock->ref_begin(), MainBlockSize + 1);
EXPECT_EQ(MainBlock->ref_end() - MainBlockSize - 1, MainBlock->ref_begin());
EXPECT_EQ(MainBlock->ref_begin() + MainBlockSize + 1, MainBlock->ref_end());
EXPECT_EQ(MainBlock->ref_begin()++, MainBlock->ref_begin());
EXPECT_EQ(++(MainBlock->ref_begin()), MainBlock->ref_begin() + 1);
// Non-reverse, non-const version
const CFGBlock *CMainBlock = MainBlock;
Index = 0;
for (CFGBlock::ConstCFGElementRef ElementRef : CMainBlock->refs()) {
EXPECT_EQ(ElementRef.getParent(), CMainBlock);
EXPECT_EQ(ElementRef.getIndexInBlock(), Index);
EXPECT_TRUE(ElementRef->getAs<CFGStmt>());
EXPECT_TRUE((*ElementRef).getAs<CFGStmt>());
EXPECT_EQ(ElementRef.getParent(), MainBlock);
++Index;
}
EXPECT_TRUE(*CMainBlock->ref_begin() < *(CMainBlock->ref_begin() + 1));
EXPECT_TRUE(*CMainBlock->ref_begin() == *CMainBlock->ref_begin());
EXPECT_FALSE(*CMainBlock->ref_begin() != *CMainBlock->ref_begin());
EXPECT_TRUE(CMainBlock->ref_begin() < CMainBlock->ref_begin() + 1);
EXPECT_TRUE(CMainBlock->ref_begin() == CMainBlock->ref_begin());
EXPECT_FALSE(CMainBlock->ref_begin() != CMainBlock->ref_begin());
EXPECT_EQ(CMainBlock->ref_end() - CMainBlock->ref_begin(), MainBlockSize + 1);
EXPECT_EQ(CMainBlock->ref_end() - MainBlockSize - 1, CMainBlock->ref_begin());
EXPECT_EQ(CMainBlock->ref_begin() + MainBlockSize + 1, CMainBlock->ref_end());
EXPECT_EQ(CMainBlock->ref_begin()++, CMainBlock->ref_begin());
EXPECT_EQ(++(CMainBlock->ref_begin()), CMainBlock->ref_begin() + 1);
// Reverse, non-const version
Index = MainBlockSize;
for (CFGBlock::CFGElementRef ElementRef : MainBlock->rrefs()) {
llvm::errs() << Index << '\n';
EXPECT_EQ(ElementRef.getParent(), MainBlock);
EXPECT_EQ(ElementRef.getIndexInBlock(), Index);
EXPECT_TRUE(ElementRef->getAs<CFGStmt>());
EXPECT_TRUE((*ElementRef).getAs<CFGStmt>());
EXPECT_EQ(ElementRef.getParent(), MainBlock);
--Index;
}
EXPECT_FALSE(*MainBlock->rref_begin() < *(MainBlock->rref_begin() + 1));
EXPECT_TRUE(*MainBlock->rref_begin() == *MainBlock->rref_begin());
EXPECT_FALSE(*MainBlock->rref_begin() != *MainBlock->rref_begin());
EXPECT_TRUE(MainBlock->rref_begin() < MainBlock->rref_begin() + 1);
EXPECT_TRUE(MainBlock->rref_begin() == MainBlock->rref_begin());
EXPECT_FALSE(MainBlock->rref_begin() != MainBlock->rref_begin());
EXPECT_EQ(MainBlock->rref_end() - MainBlock->rref_begin(), MainBlockSize + 1);
EXPECT_EQ(MainBlock->rref_end() - MainBlockSize - 1, MainBlock->rref_begin());
EXPECT_EQ(MainBlock->rref_begin() + MainBlockSize + 1, MainBlock->rref_end());
EXPECT_EQ(MainBlock->rref_begin()++, MainBlock->rref_begin());
EXPECT_EQ(++(MainBlock->rref_begin()), MainBlock->rref_begin() + 1);
// Reverse, const version
Index = MainBlockSize;
for (CFGBlock::ConstCFGElementRef ElementRef : CMainBlock->rrefs()) {
EXPECT_EQ(ElementRef.getParent(), CMainBlock);
EXPECT_EQ(ElementRef.getIndexInBlock(), Index);
EXPECT_TRUE(ElementRef->getAs<CFGStmt>());
EXPECT_TRUE((*ElementRef).getAs<CFGStmt>());
EXPECT_EQ(ElementRef.getParent(), MainBlock);
--Index;
}
EXPECT_FALSE(*CMainBlock->rref_begin() < *(CMainBlock->rref_begin() + 1));
EXPECT_TRUE(*CMainBlock->rref_begin() == *CMainBlock->rref_begin());
EXPECT_FALSE(*CMainBlock->rref_begin() != *CMainBlock->rref_begin());
EXPECT_TRUE(CMainBlock->rref_begin() < CMainBlock->rref_begin() + 1);
EXPECT_TRUE(CMainBlock->rref_begin() == CMainBlock->rref_begin());
EXPECT_FALSE(CMainBlock->rref_begin() != CMainBlock->rref_begin());
EXPECT_EQ(CMainBlock->rref_end() - CMainBlock->rref_begin(),
MainBlockSize + 1);
EXPECT_EQ(CMainBlock->rref_end() - MainBlockSize - 1,
CMainBlock->rref_begin());
EXPECT_EQ(CMainBlock->rref_begin() + MainBlockSize + 1,
CMainBlock->rref_end());
EXPECT_EQ(CMainBlock->rref_begin()++, CMainBlock->rref_begin());
EXPECT_EQ(++(CMainBlock->rref_begin()), CMainBlock->rref_begin() + 1);
}
TEST(CFG, Worklists) {
const char *Code = "int f(bool cond) {\n"
" int a = 5;\n"
" if (cond)\n"
" a += 1;\n"
" return a;\n"
"}\n";
BuildResult B = BuildCFG(Code);
EXPECT_EQ(BuildResult::BuiltCFG, B.getStatus());
const FunctionDecl *Func = B.getFunc();
AnalysisDeclContext AC(nullptr, Func);
auto *CFG = AC.getCFG();
std::vector<const CFGBlock *> ReferenceOrder;
for (const auto *B : *AC.getAnalysis<PostOrderCFGView>())
ReferenceOrder.push_back(B);
{
ForwardDataflowWorklist ForwardWorklist(*CFG, AC);
for (const auto *B : *CFG)
ForwardWorklist.enqueueBlock(B);
std::vector<const CFGBlock *> ForwardNodes;
while (const CFGBlock *B = ForwardWorklist.dequeue())
ForwardNodes.push_back(B);
EXPECT_EQ(ForwardNodes.size(), ReferenceOrder.size());
EXPECT_TRUE(std::equal(ReferenceOrder.begin(), ReferenceOrder.end(),
ForwardNodes.begin()));
}
std::reverse(ReferenceOrder.begin(), ReferenceOrder.end());
{
BackwardDataflowWorklist BackwardWorklist(*CFG, AC);
for (const auto *B : *CFG)
BackwardWorklist.enqueueBlock(B);
std::vector<const CFGBlock *> BackwardNodes;
while (const CFGBlock *B = BackwardWorklist.dequeue())
BackwardNodes.push_back(B);
EXPECT_EQ(BackwardNodes.size(), ReferenceOrder.size());
EXPECT_TRUE(std::equal(ReferenceOrder.begin(), ReferenceOrder.end(),
BackwardNodes.begin()));
}
}
} // namespace
} // namespace analysis
} // namespace clang