13dd7877bSStanislav Gatev #include "TestingSupport.h"
23dd7877bSStanislav Gatev #include "clang/AST/ASTContext.h"
33dd7877bSStanislav Gatev #include "clang/ASTMatchers/ASTMatchFinder.h"
43dd7877bSStanislav Gatev #include "clang/ASTMatchers/ASTMatchers.h"
5*32dcb759SSam Estep #include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
63dd7877bSStanislav Gatev #include "clang/Tooling/Tooling.h"
73dd7877bSStanislav Gatev #include "llvm/Testing/Support/Error.h"
83dd7877bSStanislav Gatev #include "gmock/gmock.h"
93dd7877bSStanislav Gatev #include "gtest/gtest.h"
103dd7877bSStanislav Gatev 
113dd7877bSStanislav Gatev using namespace clang;
123dd7877bSStanislav Gatev using namespace dataflow;
133dd7877bSStanislav Gatev 
143dd7877bSStanislav Gatev namespace {
153dd7877bSStanislav Gatev 
163dd7877bSStanislav Gatev using ::clang::ast_matchers::functionDecl;
173dd7877bSStanislav Gatev using ::clang::ast_matchers::hasName;
183dd7877bSStanislav Gatev using ::clang::ast_matchers::isDefinition;
193dd7877bSStanislav Gatev using ::testing::_;
203dd7877bSStanislav Gatev using ::testing::IsEmpty;
213dd7877bSStanislav Gatev using ::testing::Pair;
223dd7877bSStanislav Gatev using ::testing::UnorderedElementsAre;
233dd7877bSStanislav Gatev 
243dd7877bSStanislav Gatev template <typename T>
findTargetFunc(ASTContext & Context,T FunctionMatcher)253dd7877bSStanislav Gatev const FunctionDecl *findTargetFunc(ASTContext &Context, T FunctionMatcher) {
263dd7877bSStanislav Gatev   auto TargetMatcher =
273dd7877bSStanislav Gatev       functionDecl(FunctionMatcher, isDefinition()).bind("target");
283dd7877bSStanislav Gatev   for (const auto &Node : ast_matchers::match(TargetMatcher, Context)) {
293dd7877bSStanislav Gatev     const auto *Func = Node.template getNodeAs<FunctionDecl>("target");
303dd7877bSStanislav Gatev     if (Func == nullptr)
313dd7877bSStanislav Gatev       continue;
323dd7877bSStanislav Gatev     if (Func->isTemplated())
333dd7877bSStanislav Gatev       continue;
343dd7877bSStanislav Gatev     return Func;
353dd7877bSStanislav Gatev   }
363dd7877bSStanislav Gatev   return nullptr;
373dd7877bSStanislav Gatev }
383dd7877bSStanislav Gatev 
runTest(llvm::StringRef Code,llvm::StringRef TargetName,std::function<void (const llvm::DenseMap<const Stmt *,std::string> &)> RunChecks)391d83a16bSSam Estep void runTest(
401d83a16bSSam Estep     llvm::StringRef Code, llvm::StringRef TargetName,
413dd7877bSStanislav Gatev     std::function<void(const llvm::DenseMap<const Stmt *, std::string> &)>
423dd7877bSStanislav Gatev         RunChecks) {
433dd7877bSStanislav Gatev   llvm::Annotations AnnotatedCode(Code);
443dd7877bSStanislav Gatev   auto Unit = tooling::buildASTFromCodeWithArgs(
453dd7877bSStanislav Gatev       AnnotatedCode.code(), {"-fsyntax-only", "-std=c++17"});
463dd7877bSStanislav Gatev   auto &Context = Unit->getASTContext();
473dd7877bSStanislav Gatev   const FunctionDecl *Func = findTargetFunc(Context, hasName(TargetName));
483dd7877bSStanislav Gatev   ASSERT_NE(Func, nullptr);
493dd7877bSStanislav Gatev 
503dd7877bSStanislav Gatev   llvm::Expected<llvm::DenseMap<const Stmt *, std::string>> Mapping =
513dd7877bSStanislav Gatev       test::buildStatementToAnnotationMapping(Func, AnnotatedCode);
523dd7877bSStanislav Gatev   ASSERT_TRUE(static_cast<bool>(Mapping));
533dd7877bSStanislav Gatev 
543dd7877bSStanislav Gatev   RunChecks(Mapping.get());
553dd7877bSStanislav Gatev }
563dd7877bSStanislav Gatev 
TEST(BuildStatementToAnnotationMappingTest,ReturnStmt)571d83a16bSSam Estep TEST(BuildStatementToAnnotationMappingTest, ReturnStmt) {
583dd7877bSStanislav Gatev   runTest(R"(
593dd7877bSStanislav Gatev     int target() {
603dd7877bSStanislav Gatev       return 42;
613dd7877bSStanislav Gatev       /*[[ok]]*/
623dd7877bSStanislav Gatev     }
633dd7877bSStanislav Gatev   )",
643dd7877bSStanislav Gatev           "target",
653dd7877bSStanislav Gatev           [](const llvm::DenseMap<const Stmt *, std::string> &Annotations) {
663dd7877bSStanislav Gatev             ASSERT_EQ(Annotations.size(), static_cast<unsigned int>(1));
673dd7877bSStanislav Gatev             EXPECT_TRUE(isa<ReturnStmt>(Annotations.begin()->first));
683dd7877bSStanislav Gatev             EXPECT_EQ(Annotations.begin()->second, "ok");
693dd7877bSStanislav Gatev           });
703dd7877bSStanislav Gatev }
713dd7877bSStanislav Gatev 
checkDataflow(llvm::StringRef Code,llvm::StringRef Target,std::function<void (llvm::ArrayRef<std::pair<std::string,DataflowAnalysisState<NoopLattice>>>,ASTContext &)> Expectations)723dd7877bSStanislav Gatev void checkDataflow(
733dd7877bSStanislav Gatev     llvm::StringRef Code, llvm::StringRef Target,
743dd7877bSStanislav Gatev     std::function<void(llvm::ArrayRef<std::pair<
753dd7877bSStanislav Gatev                            std::string, DataflowAnalysisState<NoopLattice>>>,
763dd7877bSStanislav Gatev                        ASTContext &)>
773dd7877bSStanislav Gatev         Expectations) {
783dd7877bSStanislav Gatev   ASSERT_THAT_ERROR(
793dd7877bSStanislav Gatev       test::checkDataflow<NoopAnalysis>(
803dd7877bSStanislav Gatev           Code, Target,
813dd7877bSStanislav Gatev           [](ASTContext &Context, Environment &) {
823dd7877bSStanislav Gatev             return NoopAnalysis(Context, /*ApplyBuiltinTransfer=*/false);
833dd7877bSStanislav Gatev           },
843dd7877bSStanislav Gatev           std::move(Expectations), {"-fsyntax-only", "-std=c++17"}),
853dd7877bSStanislav Gatev       llvm::Succeeded());
863dd7877bSStanislav Gatev }
873dd7877bSStanislav Gatev 
TEST(ProgramPointAnnotations,NoAnnotations)883dd7877bSStanislav Gatev TEST(ProgramPointAnnotations, NoAnnotations) {
893dd7877bSStanislav Gatev   ::testing::MockFunction<void(
903dd7877bSStanislav Gatev       llvm::ArrayRef<
913dd7877bSStanislav Gatev           std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
923dd7877bSStanislav Gatev       ASTContext &)>
933dd7877bSStanislav Gatev       Expectations;
943dd7877bSStanislav Gatev 
953dd7877bSStanislav Gatev   EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);
963dd7877bSStanislav Gatev 
973dd7877bSStanislav Gatev   checkDataflow("void target() {}", "target", Expectations.AsStdFunction());
983dd7877bSStanislav Gatev }
993dd7877bSStanislav Gatev 
TEST(ProgramPointAnnotations,NoAnnotationsDifferentTarget)1003dd7877bSStanislav Gatev TEST(ProgramPointAnnotations, NoAnnotationsDifferentTarget) {
1013dd7877bSStanislav Gatev   ::testing::MockFunction<void(
1023dd7877bSStanislav Gatev       llvm::ArrayRef<
1033dd7877bSStanislav Gatev           std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
1043dd7877bSStanislav Gatev       ASTContext &)>
1053dd7877bSStanislav Gatev       Expectations;
1063dd7877bSStanislav Gatev 
1073dd7877bSStanislav Gatev   EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);
1083dd7877bSStanislav Gatev 
1093dd7877bSStanislav Gatev   checkDataflow("void fun() {}", "fun", Expectations.AsStdFunction());
1103dd7877bSStanislav Gatev }
1113dd7877bSStanislav Gatev 
TEST(ProgramPointAnnotations,WithCodepoint)1123dd7877bSStanislav Gatev TEST(ProgramPointAnnotations, WithCodepoint) {
1133dd7877bSStanislav Gatev   ::testing::MockFunction<void(
1143dd7877bSStanislav Gatev       llvm::ArrayRef<
1153dd7877bSStanislav Gatev           std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
1163dd7877bSStanislav Gatev       ASTContext &)>
1173dd7877bSStanislav Gatev       Expectations;
1183dd7877bSStanislav Gatev 
1193dd7877bSStanislav Gatev   EXPECT_CALL(Expectations,
1203dd7877bSStanislav Gatev               Call(UnorderedElementsAre(Pair("program-point", _)), _))
1213dd7877bSStanislav Gatev       .Times(1);
1223dd7877bSStanislav Gatev 
1233dd7877bSStanislav Gatev   checkDataflow(R"cc(void target() {
1243dd7877bSStanislav Gatev                      int n;
1253dd7877bSStanislav Gatev                      // [[program-point]]
1263dd7877bSStanislav Gatev                    })cc",
1273dd7877bSStanislav Gatev                 "target", Expectations.AsStdFunction());
1283dd7877bSStanislav Gatev }
1293dd7877bSStanislav Gatev 
TEST(ProgramPointAnnotations,MultipleCodepoints)1303dd7877bSStanislav Gatev TEST(ProgramPointAnnotations, MultipleCodepoints) {
1313dd7877bSStanislav Gatev   ::testing::MockFunction<void(
1323dd7877bSStanislav Gatev       llvm::ArrayRef<
1333dd7877bSStanislav Gatev           std::pair<std::string, DataflowAnalysisState<NoopLattice>>>,
1343dd7877bSStanislav Gatev       ASTContext &)>
1353dd7877bSStanislav Gatev       Expectations;
1363dd7877bSStanislav Gatev 
1373dd7877bSStanislav Gatev   EXPECT_CALL(Expectations,
1383dd7877bSStanislav Gatev               Call(UnorderedElementsAre(Pair("program-point-1", _),
1393dd7877bSStanislav Gatev                                         Pair("program-point-2", _)),
1403dd7877bSStanislav Gatev                    _))
1413dd7877bSStanislav Gatev       .Times(1);
1423dd7877bSStanislav Gatev 
1433dd7877bSStanislav Gatev   checkDataflow(R"cc(void target(bool b) {
1443dd7877bSStanislav Gatev                      if (b) {
1453dd7877bSStanislav Gatev                        int n;
1463dd7877bSStanislav Gatev                        // [[program-point-1]]
1473dd7877bSStanislav Gatev                      } else {
1483dd7877bSStanislav Gatev                        int m;
1493dd7877bSStanislav Gatev                        // [[program-point-2]]
1503dd7877bSStanislav Gatev                      }
1513dd7877bSStanislav Gatev                    })cc",
1523dd7877bSStanislav Gatev                 "target", Expectations.AsStdFunction());
1533dd7877bSStanislav Gatev }
1543dd7877bSStanislav Gatev 
1553dd7877bSStanislav Gatev } // namespace
156