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/Analysis/FlowSensitive/NoopAnalysis.h"
6 #include "clang/Tooling/Tooling.h"
7 #include "llvm/Testing/ADT/StringMapEntry.h"
8 #include "llvm/Testing/Support/Error.h"
9 #include "gmock/gmock.h"
10 #include "gtest/gtest.h"
11
12 using namespace clang;
13 using namespace dataflow;
14
15 namespace {
16
17 using ::clang::ast_matchers::functionDecl;
18 using ::clang::ast_matchers::hasAnyName;
19 using ::clang::ast_matchers::hasName;
20 using ::clang::ast_matchers::isDefinition;
21 using ::clang::dataflow::test::AnalysisInputs;
22 using ::clang::dataflow::test::AnalysisOutputs;
23 using ::clang::dataflow::test::checkDataflow;
24 using ::llvm::IsStringMapEntry;
25 using ::testing::_;
26 using ::testing::IsEmpty;
27 using ::testing::UnorderedElementsAre;
28
29 template <typename T>
findTargetFunc(ASTContext & Context,T FunctionMatcher)30 const FunctionDecl *findTargetFunc(ASTContext &Context, T FunctionMatcher) {
31 auto TargetMatcher =
32 functionDecl(FunctionMatcher, isDefinition()).bind("target");
33 for (const auto &Node : ast_matchers::match(TargetMatcher, Context)) {
34 const auto *Func = Node.template getNodeAs<FunctionDecl>("target");
35 if (Func == nullptr)
36 continue;
37 if (Func->isTemplated())
38 continue;
39 return Func;
40 }
41 return nullptr;
42 }
43
runTest(llvm::StringRef Code,llvm::StringRef TargetName,std::function<void (const llvm::DenseMap<const Stmt *,std::string> &)> RunChecks)44 void runTest(
45 llvm::StringRef Code, llvm::StringRef TargetName,
46 std::function<void(const llvm::DenseMap<const Stmt *, std::string> &)>
47 RunChecks) {
48 llvm::Annotations AnnotatedCode(Code);
49 auto Unit = tooling::buildASTFromCodeWithArgs(
50 AnnotatedCode.code(), {"-fsyntax-only", "-std=c++17"});
51 auto &Context = Unit->getASTContext();
52 const FunctionDecl *Func = findTargetFunc(Context, hasName(TargetName));
53 ASSERT_NE(Func, nullptr);
54
55 llvm::Expected<llvm::DenseMap<const Stmt *, std::string>> Mapping =
56 test::buildStatementToAnnotationMapping(Func, AnnotatedCode);
57 ASSERT_TRUE(static_cast<bool>(Mapping));
58
59 RunChecks(Mapping.get());
60 }
61
TEST(BuildStatementToAnnotationMappingTest,ReturnStmt)62 TEST(BuildStatementToAnnotationMappingTest, ReturnStmt) {
63 runTest(R"(
64 int target() {
65 return 42;
66 /*[[ok]]*/
67 }
68 )",
69 "target",
70 [](const llvm::DenseMap<const Stmt *, std::string> &Annotations) {
71 ASSERT_EQ(Annotations.size(), static_cast<unsigned int>(1));
72 EXPECT_TRUE(isa<ReturnStmt>(Annotations.begin()->first));
73 EXPECT_EQ(Annotations.begin()->second, "ok");
74 });
75 }
76
checkDataflow(llvm::StringRef Code,ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,std::function<void (const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,const AnalysisOutputs &)> Expectations)77 void checkDataflow(
78 llvm::StringRef Code,
79 ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,
80 std::function<
81 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
82 const AnalysisOutputs &)>
83 Expectations) {
84 ASSERT_THAT_ERROR(checkDataflow<NoopAnalysis>(
85 AnalysisInputs<NoopAnalysis>(
86 Code, std::move(TargetFuncMatcher),
87 [](ASTContext &Context, Environment &) {
88 return NoopAnalysis(
89 Context,
90 // Don't apply builtin transfer function.
91 DataflowAnalysisOptions{std::nullopt});
92 })
93 .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
94 /*VerifyResults=*/std::move(Expectations)),
95 llvm::Succeeded());
96 }
97
TEST(ProgramPointAnnotations,NoAnnotations)98 TEST(ProgramPointAnnotations, NoAnnotations) {
99 ::testing::MockFunction<void(
100 const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
101 const AnalysisOutputs &)>
102 Expectations;
103
104 EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);
105
106 checkDataflow("void target() {}", hasName("target"),
107 Expectations.AsStdFunction());
108 }
109
TEST(ProgramPointAnnotations,NoAnnotationsDifferentTarget)110 TEST(ProgramPointAnnotations, NoAnnotationsDifferentTarget) {
111 ::testing::MockFunction<void(
112 const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
113 const AnalysisOutputs &)>
114 Expectations;
115
116 EXPECT_CALL(Expectations, Call(IsEmpty(), _)).Times(1);
117
118 checkDataflow("void target() {}", hasName("target"),
119 Expectations.AsStdFunction());
120 }
121
TEST(ProgramPointAnnotations,WithProgramPoint)122 TEST(ProgramPointAnnotations, WithProgramPoint) {
123 ::testing::MockFunction<void(
124 const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
125 const AnalysisOutputs &)>
126 Expectations;
127
128 EXPECT_CALL(
129 Expectations,
130 Call(UnorderedElementsAre(IsStringMapEntry("program-point", _)), _))
131 .Times(1);
132
133 checkDataflow(R"cc(void target() {
134 int n;
135 // [[program-point]]
136 })cc",
137 hasName("target"), Expectations.AsStdFunction());
138 }
139
TEST(ProgramPointAnnotations,MultipleProgramPoints)140 TEST(ProgramPointAnnotations, MultipleProgramPoints) {
141 ::testing::MockFunction<void(
142 const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
143 const AnalysisOutputs &)>
144 Expectations;
145
146 EXPECT_CALL(Expectations,
147 Call(UnorderedElementsAre(IsStringMapEntry("program-point-1", _),
148 IsStringMapEntry("program-point-2", _)),
149 _))
150 .Times(1);
151
152 checkDataflow(R"cc(void target(bool b) {
153 if (b) {
154 int n;
155 // [[program-point-1]]
156 } else {
157 int m;
158 // [[program-point-2]]
159 }
160 })cc",
161 hasName("target"), Expectations.AsStdFunction());
162 }
163
TEST(ProgramPointAnnotations,MultipleFunctionsMultipleProgramPoints)164 TEST(ProgramPointAnnotations, MultipleFunctionsMultipleProgramPoints) {
165 ::testing::MockFunction<void(
166 const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
167 const AnalysisOutputs &)>
168 Expectations;
169
170 EXPECT_CALL(Expectations, Call(UnorderedElementsAre(
171 IsStringMapEntry("program-point-1a", _),
172 IsStringMapEntry("program-point-1b", _)),
173 _))
174 .Times(1);
175
176 EXPECT_CALL(Expectations, Call(UnorderedElementsAre(
177 IsStringMapEntry("program-point-2a", _),
178 IsStringMapEntry("program-point-2b", _)),
179 _))
180 .Times(1);
181
182 checkDataflow(
183 R"cc(
184 void target1(bool b) {
185 if (b) {
186 int n;
187 // [[program-point-1a]]
188 } else {
189 int m;
190 // [[program-point-1b]]
191 }
192 }
193
194 void target2(bool b) {
195 if (b) {
196 int n;
197 // [[program-point-2a]]
198 } else {
199 int m;
200 // [[program-point-2b]]
201 }
202 }
203 )cc",
204 functionDecl(hasAnyName("target1", "target2")),
205 Expectations.AsStdFunction());
206 }
207
208 } // namespace
209