1 #include "TestingSupport.h" 2 #include "NoopAnalysis.h" 3 #include "clang/AST/ASTContext.h" 4 #include "clang/ASTMatchers/ASTMatchFinder.h" 5 #include "clang/ASTMatchers/ASTMatchers.h" 6 #include "clang/Tooling/Tooling.h" 7 #include "gmock/gmock.h" 8 #include "gtest/gtest.h" 9 10 using namespace clang; 11 using namespace dataflow; 12 13 namespace { 14 15 using ::clang::ast_matchers::functionDecl; 16 using ::clang::ast_matchers::hasName; 17 using ::clang::ast_matchers::isDefinition; 18 using ::testing::_; 19 using ::testing::IsEmpty; 20 using ::testing::Pair; 21 using ::testing::UnorderedElementsAre; 22 23 template <typename T> 24 const FunctionDecl *findTargetFunc(ASTContext &Context, T FunctionMatcher) { 25 auto TargetMatcher = 26 functionDecl(FunctionMatcher, isDefinition()).bind("target"); 27 for (const auto &Node : ast_matchers::match(TargetMatcher, Context)) { 28 const auto *Func = Node.template getNodeAs<FunctionDecl>("target"); 29 if (Func == nullptr) 30 continue; 31 if (Func->isTemplated()) 32 continue; 33 return Func; 34 } 35 return nullptr; 36 } 37 38 class BuildStatementToAnnotationMappingTest : public ::testing::Test { 39 public: 40 void 41 runTest(llvm::StringRef Code, llvm::StringRef TargetName, 42 std::function<void(const llvm::DenseMap<const Stmt *, std::string> &)> 43 RunChecks) { 44 llvm::Annotations AnnotatedCode(Code); 45 auto Unit = tooling::buildASTFromCodeWithArgs( 46 AnnotatedCode.code(), {"-fsyntax-only", "-std=c++17"}); 47 auto &Context = Unit->getASTContext(); 48 const FunctionDecl *Func = findTargetFunc(Context, hasName(TargetName)); 49 ASSERT_NE(Func, nullptr); 50 51 llvm::Expected<llvm::DenseMap<const Stmt *, std::string>> Mapping = 52 test::buildStatementToAnnotationMapping(Func, AnnotatedCode); 53 ASSERT_TRUE(static_cast<bool>(Mapping)); 54 55 RunChecks(Mapping.get()); 56 } 57 }; 58 59 TEST_F(BuildStatementToAnnotationMappingTest, ReturnStmt) { 60 runTest(R"( 61 int target() { 62 return 42; 63 /*[[ok]]*/ 64 } 65 )", 66 "target", 67 [](const llvm::DenseMap<const Stmt *, std::string> &Annotations) { 68 ASSERT_EQ(Annotations.size(), static_cast<unsigned int>(1)); 69 EXPECT_TRUE(isa<ReturnStmt>(Annotations.begin()->first)); 70 EXPECT_EQ(Annotations.begin()->second, "ok"); 71 }); 72 } 73 74 void checkDataflow( 75 llvm::StringRef Code, llvm::StringRef Target, 76 std::function<void(llvm::ArrayRef<std::pair< 77 std::string, DataflowAnalysisState<NoopLattice>>>, 78 ASTContext &)> 79 Expectations) { 80 test::checkDataflow<NoopAnalysis>( 81 Code, Target, 82 [](ASTContext &Context, Environment &) { return NoopAnalysis(Context); }, 83 std::move(Expectations), {"-fsyntax-only", "-std=c++17"}); 84 } 85 86 TEST(ProgramPointAnnotations, NoAnnotations) { 87 ::testing::MockFunction<void( 88 llvm::ArrayRef< 89 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>, 90 ASTContext &)> 91 Expectations; 92 93 EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1); 94 95 checkDataflow("void target() {}", "target", Expectations.AsStdFunction()); 96 } 97 98 TEST(ProgramPointAnnotations, NoAnnotationsDifferentTarget) { 99 ::testing::MockFunction<void( 100 llvm::ArrayRef< 101 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>, 102 ASTContext &)> 103 Expectations; 104 105 EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1); 106 107 checkDataflow("void fun() {}", "fun", Expectations.AsStdFunction()); 108 } 109 110 TEST(ProgramPointAnnotations, WithCodepoint) { 111 ::testing::MockFunction<void( 112 llvm::ArrayRef< 113 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>, 114 ASTContext &)> 115 Expectations; 116 117 EXPECT_CALL(Expectations, 118 Call(UnorderedElementsAre(Pair("program-point", _)), _)) 119 .Times(1); 120 121 checkDataflow(R"cc(void target() { 122 int n; 123 // [[program-point]] 124 })cc", 125 "target", Expectations.AsStdFunction()); 126 } 127 128 TEST(ProgramPointAnnotations, MultipleCodepoints) { 129 ::testing::MockFunction<void( 130 llvm::ArrayRef< 131 std::pair<std::string, DataflowAnalysisState<NoopLattice>>>, 132 ASTContext &)> 133 Expectations; 134 135 EXPECT_CALL(Expectations, 136 Call(UnorderedElementsAre(Pair("program-point-1", _), 137 Pair("program-point-2", _)), 138 _)) 139 .Times(1); 140 141 checkDataflow(R"cc(void target(bool b) { 142 if (b) { 143 int n; 144 // [[program-point-1]] 145 } else { 146 int m; 147 // [[program-point-2]] 148 } 149 })cc", 150 "target", Expectations.AsStdFunction()); 151 } 152 153 } // namespace 154