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