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