Yitzhak Mandelbaum 93fbaa46c8 Revert "Revert "[clang][dataflow] Add framework for testing analyses.""
This reverts commit 78ff12da1115abcaf4cbf50b605a197011505646 and fixes the initial cause of the revert.
2021-12-11 23:16:59 +00:00

179 lines
5.2 KiB
C++

#include "TestingSupport.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using namespace clang;
using namespace dataflow;
namespace {
using ::clang::ast_matchers::functionDecl;
using ::clang::ast_matchers::hasName;
using ::clang::ast_matchers::isDefinition;
using ::testing::_;
using ::testing::IsEmpty;
using ::testing::Pair;
using ::testing::UnorderedElementsAre;
class NoopLattice {
public:
bool operator==(const NoopLattice &) const { return true; }
LatticeJoinEffect join(const NoopLattice &) {
return LatticeJoinEffect::Unchanged;
}
};
std::ostream &operator<<(std::ostream &OS, const NoopLattice &S) {
OS << "noop";
return OS;
}
class NoopAnalysis : public DataflowAnalysis<NoopAnalysis, NoopLattice> {
public:
NoopAnalysis(ASTContext &Context)
: DataflowAnalysis<NoopAnalysis, NoopLattice>(Context) {}
static NoopLattice initialElement() { return {}; }
NoopLattice transfer(const Stmt *S, const NoopLattice &E, Environment &Env) {
return {};
}
};
template <typename T>
const FunctionDecl *findTargetFunc(ASTContext &Context, T FunctionMatcher) {
auto TargetMatcher =
functionDecl(FunctionMatcher, isDefinition()).bind("target");
for (const auto &Node : ast_matchers::match(TargetMatcher, Context)) {
const auto *Func = Node.template getNodeAs<FunctionDecl>("target");
if (Func == nullptr)
continue;
if (Func->isTemplated())
continue;
return Func;
}
return nullptr;
}
class BuildStatementToAnnotationMappingTest : public ::testing::Test {
public:
void
runTest(llvm::StringRef Code, llvm::StringRef TargetName,
std::function<void(const llvm::DenseMap<const Stmt *, std::string> &)>
RunChecks) {
llvm::Annotations AnnotatedCode(Code);
auto Unit = tooling::buildASTFromCodeWithArgs(
AnnotatedCode.code(), {"-fsyntax-only", "-std=c++17"});
auto &Context = Unit->getASTContext();
const FunctionDecl *Func = findTargetFunc(Context, hasName(TargetName));
ASSERT_NE(Func, nullptr);
llvm::Expected<llvm::DenseMap<const Stmt *, std::string>> Mapping =
test::buildStatementToAnnotationMapping(Func, AnnotatedCode);
ASSERT_TRUE(static_cast<bool>(Mapping));
RunChecks(Mapping.get());
}
};
TEST_F(BuildStatementToAnnotationMappingTest, ReturnStmt) {
runTest(R"(
int target() {
return 42;
/*[[ok]]*/
}
)",
"target",
[](const llvm::DenseMap<const Stmt *, std::string> &Annotations) {
ASSERT_EQ(Annotations.size(), static_cast<unsigned int>(1));
EXPECT_TRUE(isa<ReturnStmt>(Annotations.begin()->first));
EXPECT_EQ(Annotations.begin()->second, "ok");
});
}
void checkDataflow(
llvm::StringRef Code, llvm::StringRef Target,
std::function<void(llvm::ArrayRef<std::pair<
std::string, DataflowAnalysisState<NoopLattice>>>,
ASTContext &)>
Expectations) {
test::checkDataflow<NoopAnalysis>(
Code, Target,
[](ASTContext &Context, Environment &) { return NoopAnalysis(Context); },
std::move(Expectations), {"-fsyntax-only", "-std=c++17"});
}
TEST(ProgramPointAnnotations, NoAnnotations) {
::testing::MockFunction<void(
llvm::ArrayRef<
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
ASTContext &)>
Expectations;
EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);
checkDataflow("void target() {}", "target", Expectations.AsStdFunction());
}
TEST(ProgramPointAnnotations, NoAnnotationsDifferentTarget) {
::testing::MockFunction<void(
llvm::ArrayRef<
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
ASTContext &)>
Expectations;
EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);
checkDataflow("void fun() {}", "fun", Expectations.AsStdFunction());
}
TEST(ProgramPointAnnotations, WithCodepoint) {
::testing::MockFunction<void(
llvm::ArrayRef<
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
ASTContext &)>
Expectations;
EXPECT_CALL(Expectations,
Call(UnorderedElementsAre(Pair("program-point", _)), _))
.Times(1);
checkDataflow(R"cc(void target() {
int n;
// [[program-point]]
})cc",
"target", Expectations.AsStdFunction());
}
TEST(ProgramPointAnnotations, MultipleCodepoints) {
::testing::MockFunction<void(
llvm::ArrayRef<
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
ASTContext &)>
Expectations;
EXPECT_CALL(Expectations,
Call(UnorderedElementsAre(Pair("program-point-1", _),
Pair("program-point-2", _)),
_))
.Times(1);
checkDataflow(R"cc(void target(bool b) {
if (b) {
int n;
// [[program-point-1]]
} else {
int m;
// [[program-point-2]]
}
})cc",
"target", Expectations.AsStdFunction());
}
} // namespace