Skip to content

Commit

Permalink
[analyzer][Dominators][NFC] Add unit tests
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D62611

llvm-svn: 365179
  • Loading branch information
Szelethus committed Jul 5, 2019
1 parent 25cf705 commit 2e2db93
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 51 deletions.
69 changes: 69 additions & 0 deletions clang/unittests/Analysis/CFGBuildResult.h
@@ -0,0 +1,69 @@
//===- unittests/Analysis/CFGBuildResult.h - 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 "clang/Analysis/CFG.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Tooling.h"

namespace clang {
namespace analysis {

class BuildResult {
public:
enum Status {
ToolFailed,
ToolRan,
SawFunctionBody,
BuiltCFG,
};

BuildResult(Status S, std::unique_ptr<CFG> Cfg = nullptr)
: S(S), Cfg(std::move(Cfg)) {}

Status getStatus() const { return S; }
CFG *getCFG() const { return Cfg.get(); }

private:
Status S;
std::unique_ptr<CFG> Cfg;
};

class CFGCallback : public ast_matchers::MatchFinder::MatchCallback {
public:
BuildResult TheBuildResult = BuildResult::ToolRan;

void run(const ast_matchers::MatchFinder::MatchResult &Result) override {
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
Stmt *Body = Func->getBody();
if (!Body)
return;
TheBuildResult = BuildResult::SawFunctionBody;
CFG::BuildOptions Options;
Options.AddImplicitDtors = true;
if (std::unique_ptr<CFG> Cfg =
CFG::buildCFG(nullptr, Body, Result.Context, Options))
TheBuildResult = {BuildResult::BuiltCFG, std::move(Cfg)};
}
};

inline BuildResult BuildCFG(const char *Code) {
CFGCallback Callback;

ast_matchers::MatchFinder Finder;
Finder.addMatcher(ast_matchers::functionDecl().bind("func"), &Callback);
std::unique_ptr<tooling::FrontendActionFactory> Factory(
tooling::newFrontendActionFactory(&Finder));
std::vector<std::string> Args = {"-std=c++11",
"-fno-delayed-template-parsing"};
if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args))
return BuildResult::ToolFailed;
return std::move(Callback.TheBuildResult);
}

} // namespace analysis
} // namespace clang
103 changes: 103 additions & 0 deletions clang/unittests/Analysis/CFGDominatorTree.cpp
@@ -0,0 +1,103 @@
//===- unittests/Analysis/CFGDominatorTree.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/Analysis/Analyses/Dominators.h"
#include "gtest/gtest.h"

namespace clang {
namespace analysis {
namespace {

TEST(CFGDominatorTree, DomTree) {
const char *Code = R"(enum Kind {
A
};
void f() {
switch(Kind{}) {
case A:
break;
}
})";
BuildResult Result = BuildCFG(Code);
EXPECT_EQ(BuildResult::BuiltCFG, Result.getStatus());

// [B3 (ENTRY)] -> [B1] -> [B2] -> [B0 (EXIT)]
// switch case A

CFG *cfg = Result.getCFG();

// Sanity checks.
EXPECT_EQ(cfg->size(), 4u);

CFGBlock *ExitBlock = *cfg->begin();
EXPECT_EQ(ExitBlock, &cfg->getExit());

CFGBlock *SwitchBlock = *(cfg->begin() + 1);

CFGBlock *CaseABlock = *(cfg->begin() + 2);

CFGBlock *EntryBlock = *(cfg->begin() + 3);
EXPECT_EQ(EntryBlock, &cfg->getEntry());

// Test the dominator tree.
CFGDomTree Dom;
Dom.buildDominatorTree(cfg);

EXPECT_TRUE(Dom.dominates(ExitBlock, ExitBlock));
EXPECT_FALSE(Dom.properlyDominates(ExitBlock, ExitBlock));
EXPECT_TRUE(Dom.dominates(CaseABlock, ExitBlock));
EXPECT_TRUE(Dom.dominates(SwitchBlock, ExitBlock));
EXPECT_TRUE(Dom.dominates(EntryBlock, ExitBlock));

EXPECT_TRUE(Dom.dominates(CaseABlock, CaseABlock));
EXPECT_FALSE(Dom.properlyDominates(CaseABlock, CaseABlock));
EXPECT_TRUE(Dom.dominates(SwitchBlock, CaseABlock));
EXPECT_TRUE(Dom.dominates(EntryBlock, CaseABlock));

EXPECT_TRUE(Dom.dominates(SwitchBlock, SwitchBlock));
EXPECT_FALSE(Dom.properlyDominates(SwitchBlock, SwitchBlock));
EXPECT_TRUE(Dom.dominates(EntryBlock, SwitchBlock));

EXPECT_TRUE(Dom.dominates(EntryBlock, EntryBlock));
EXPECT_FALSE(Dom.properlyDominates(EntryBlock, EntryBlock));

// Test the post dominator tree.

CFGPostDomTree PostDom;
PostDom.buildDominatorTree(cfg);

EXPECT_TRUE(PostDom.dominates(ExitBlock, EntryBlock));
EXPECT_TRUE(PostDom.dominates(CaseABlock, EntryBlock));
EXPECT_TRUE(PostDom.dominates(SwitchBlock, EntryBlock));
EXPECT_TRUE(PostDom.dominates(EntryBlock, EntryBlock));
EXPECT_FALSE(Dom.properlyDominates(EntryBlock, EntryBlock));

EXPECT_TRUE(PostDom.dominates(ExitBlock, SwitchBlock));
EXPECT_TRUE(PostDom.dominates(CaseABlock, SwitchBlock));
EXPECT_TRUE(PostDom.dominates(SwitchBlock, SwitchBlock));
EXPECT_FALSE(Dom.properlyDominates(SwitchBlock, SwitchBlock));

EXPECT_TRUE(PostDom.dominates(ExitBlock, CaseABlock));
EXPECT_TRUE(PostDom.dominates(CaseABlock, CaseABlock));
EXPECT_FALSE(Dom.properlyDominates(CaseABlock, CaseABlock));

EXPECT_TRUE(PostDom.dominates(ExitBlock, ExitBlock));
EXPECT_FALSE(Dom.properlyDominates(ExitBlock, ExitBlock));

// Tests for the post dominator tree's virtual root.
EXPECT_TRUE(PostDom.dominates(nullptr, EntryBlock));
EXPECT_TRUE(PostDom.dominates(nullptr, SwitchBlock));
EXPECT_TRUE(PostDom.dominates(nullptr, CaseABlock));
EXPECT_TRUE(PostDom.dominates(nullptr, ExitBlock));
}

} // namespace
} // namespace analysis
} // namespace clang
52 changes: 1 addition & 51 deletions clang/unittests/Analysis/CFGTest.cpp
Expand Up @@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//

#include "CFGBuildResult.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Analysis/CFG.h"
#include "clang/Tooling/Tooling.h"
Expand All @@ -17,57 +18,6 @@ namespace clang {
namespace analysis {
namespace {

class BuildResult {
public:
enum Status {
ToolFailed,
ToolRan,
SawFunctionBody,
BuiltCFG,
};

BuildResult(Status S, std::unique_ptr<CFG> Cfg = nullptr)
: S(S), Cfg(std::move(Cfg)) {}

Status getStatus() const { return S; }
CFG *getCFG() const { return Cfg.get(); }

private:
Status S;
std::unique_ptr<CFG> Cfg;
};

class CFGCallback : public ast_matchers::MatchFinder::MatchCallback {
public:
BuildResult TheBuildResult = BuildResult::ToolRan;

void run(const ast_matchers::MatchFinder::MatchResult &Result) override {
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
Stmt *Body = Func->getBody();
if (!Body)
return;
TheBuildResult = BuildResult::SawFunctionBody;
CFG::BuildOptions Options;
Options.AddImplicitDtors = true;
if (std::unique_ptr<CFG> Cfg =
CFG::buildCFG(nullptr, Body, Result.Context, Options))
TheBuildResult = {BuildResult::BuiltCFG, std::move(Cfg)};
}
};

BuildResult BuildCFG(const char *Code) {
CFGCallback Callback;

ast_matchers::MatchFinder Finder;
Finder.addMatcher(ast_matchers::functionDecl().bind("func"), &Callback);
std::unique_ptr<tooling::FrontendActionFactory> Factory(
tooling::newFrontendActionFactory(&Finder));
std::vector<std::string> Args = {"-std=c++11", "-fno-delayed-template-parsing"};
if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args))
return BuildResult::ToolFailed;
return std::move(Callback.TheBuildResult);
}

// Constructing a CFG for a range-based for over a dependent type fails (but
// should not crash).
TEST(CFG, RangeBasedForOverDependentType) {
Expand Down
1 change: 1 addition & 0 deletions clang/unittests/Analysis/CMakeLists.txt
Expand Up @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS
)

add_clang_unittest(ClangAnalysisTests
CFGDominatorTree.cpp
CFGTest.cpp
CloneDetectionTest.cpp
ExprMutationAnalyzerTest.cpp
Expand Down

0 comments on commit 2e2db93

Please sign in to comment.