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