209 lines · cpp
1#include "TestingSupport.h"2#include "clang/AST/ASTContext.h"3#include "clang/ASTMatchers/ASTMatchFinder.h"4#include "clang/ASTMatchers/ASTMatchers.h"5#include "clang/Analysis/FlowSensitive/NoopAnalysis.h"6#include "clang/Tooling/Tooling.h"7#include "llvm/Testing/ADT/StringMapEntry.h"8#include "llvm/Testing/Support/Error.h"9#include "gmock/gmock.h"10#include "gtest/gtest.h"11 12using namespace clang;13using namespace dataflow;14 15namespace {16 17using ::clang::ast_matchers::functionDecl;18using ::clang::ast_matchers::hasAnyName;19using ::clang::ast_matchers::hasName;20using ::clang::ast_matchers::isDefinition;21using ::clang::dataflow::test::AnalysisInputs;22using ::clang::dataflow::test::AnalysisOutputs;23using ::clang::dataflow::test::checkDataflow;24using ::llvm::IsStringMapEntry;25using ::testing::_;26using ::testing::IsEmpty;27using ::testing::UnorderedElementsAre;28 29template <typename T>30const FunctionDecl *findTargetFunc(ASTContext &Context, T FunctionMatcher) {31 auto TargetMatcher =32 functionDecl(FunctionMatcher, isDefinition()).bind("target");33 for (const auto &Node : ast_matchers::match(TargetMatcher, Context)) {34 const auto *Func = Node.template getNodeAs<FunctionDecl>("target");35 if (Func == nullptr)36 continue;37 if (Func->isTemplated())38 continue;39 return Func;40 }41 return nullptr;42}43 44void runTest(45 llvm::StringRef Code, llvm::StringRef TargetName,46 std::function<void(const llvm::DenseMap<const Stmt *, std::string> &)>47 RunChecks) {48 llvm::Annotations AnnotatedCode(Code);49 auto Unit = tooling::buildASTFromCodeWithArgs(50 AnnotatedCode.code(), {"-fsyntax-only", "-std=c++17"});51 auto &Context = Unit->getASTContext();52 const FunctionDecl *Func = findTargetFunc(Context, hasName(TargetName));53 ASSERT_NE(Func, nullptr);54 55 llvm::Expected<llvm::DenseMap<const Stmt *, std::string>> Mapping =56 test::buildStatementToAnnotationMapping(Func, AnnotatedCode);57 ASSERT_TRUE(static_cast<bool>(Mapping));58 59 RunChecks(Mapping.get());60}61 62TEST(BuildStatementToAnnotationMappingTest, ReturnStmt) {63 runTest(R"(64 int target() {65 return 42;66 /*[[ok]]*/67 }68 )",69 "target",70 [](const llvm::DenseMap<const Stmt *, std::string> &Annotations) {71 ASSERT_EQ(Annotations.size(), static_cast<unsigned int>(1));72 EXPECT_TRUE(isa<ReturnStmt>(Annotations.begin()->first));73 EXPECT_EQ(Annotations.begin()->second, "ok");74 });75}76 77void checkDataflow(78 llvm::StringRef Code,79 ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,80 std::function<81 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,82 const AnalysisOutputs &)>83 Expectations) {84 ASSERT_THAT_ERROR(checkDataflow<NoopAnalysis>(85 AnalysisInputs<NoopAnalysis>(86 Code, std::move(TargetFuncMatcher),87 [](ASTContext &Context, Environment &) {88 return NoopAnalysis(89 Context,90 // Don't apply builtin transfer function.91 DataflowAnalysisOptions{std::nullopt});92 })93 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),94 /*VerifyResults=*/std::move(Expectations)),95 llvm::Succeeded());96}97 98TEST(ProgramPointAnnotations, NoAnnotations) {99 ::testing::MockFunction<void(100 const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,101 const AnalysisOutputs &)>102 Expectations;103 104 EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);105 106 checkDataflow("void target() {}", hasName("target"),107 Expectations.AsStdFunction());108}109 110TEST(ProgramPointAnnotations, NoAnnotationsDifferentTarget) {111 ::testing::MockFunction<void(112 const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,113 const AnalysisOutputs &)>114 Expectations;115 116 EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);117 118 checkDataflow("void target() {}", hasName("target"),119 Expectations.AsStdFunction());120}121 122TEST(ProgramPointAnnotations, WithProgramPoint) {123 ::testing::MockFunction<void(124 const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,125 const AnalysisOutputs &)>126 Expectations;127 128 EXPECT_CALL(129 Expectations,130 Call(UnorderedElementsAre(IsStringMapEntry("program-point", _)), _))131 .Times(1);132 133 checkDataflow(R"cc(void target() {134 int n;135 // [[program-point]]136 })cc",137 hasName("target"), Expectations.AsStdFunction());138}139 140TEST(ProgramPointAnnotations, MultipleProgramPoints) {141 ::testing::MockFunction<void(142 const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,143 const AnalysisOutputs &)>144 Expectations;145 146 EXPECT_CALL(Expectations,147 Call(UnorderedElementsAre(IsStringMapEntry("program-point-1", _),148 IsStringMapEntry("program-point-2", _)),149 _))150 .Times(1);151 152 checkDataflow(R"cc(void target(bool b) {153 if (b) {154 int n;155 // [[program-point-1]]156 } else {157 int m;158 // [[program-point-2]]159 }160 })cc",161 hasName("target"), Expectations.AsStdFunction());162}163 164TEST(ProgramPointAnnotations, MultipleFunctionsMultipleProgramPoints) {165 ::testing::MockFunction<void(166 const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,167 const AnalysisOutputs &)>168 Expectations;169 170 EXPECT_CALL(Expectations, Call(UnorderedElementsAre(171 IsStringMapEntry("program-point-1a", _),172 IsStringMapEntry("program-point-1b", _)),173 _))174 .Times(1);175 176 EXPECT_CALL(Expectations, Call(UnorderedElementsAre(177 IsStringMapEntry("program-point-2a", _),178 IsStringMapEntry("program-point-2b", _)),179 _))180 .Times(1);181 182 checkDataflow(183 R"cc(184 void target1(bool b) {185 if (b) {186 int n;187 // [[program-point-1a]]188 } else {189 int m;190 // [[program-point-1b]]191 }192 }193 194 void target2(bool b) {195 if (b) {196 int n;197 // [[program-point-2a]]198 } else {199 int m;200 // [[program-point-2b]]201 }202 }203 )cc",204 functionDecl(hasAnyName("target1", "target2")),205 Expectations.AsStdFunction());206}207 208} // namespace209