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
12using namespace clang;
13using namespace dataflow;
14
15namespace {
16
17using ::clang::ast_matchers::functionDecl;
18using ::clang::ast_matchers::hasAnyName;
19using ::clang::ast_matchers::hasName;
20using ::clang::ast_matchers::isDefinition;
21using ::clang::dataflow::test::AnalysisInputs;
22using ::clang::dataflow::test::AnalysisOutputs;
23using ::clang::dataflow::test::checkDataflow;
24using ::llvm::IsStringMapEntry;
25using ::testing::_;
26using ::testing::IsEmpty;
27using ::testing::UnorderedElementsAre;
28
29template <typename T>
30const 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
44void 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 Code: AnnotatedCode.code(), Args: {"-fsyntax-only", "-std=c++17"});
51 auto &Context = Unit->getASTContext();
52 const FunctionDecl *Func = findTargetFunc(Context, FunctionMatcher: hasName(Name: 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
62TEST(BuildStatementToAnnotationMappingTest, ReturnStmt) {
63 runTest(Code: R"(
64 int target() {
65 return 42;
66 /*[[ok]]*/
67 }
68 )",
69 TargetName: "target",
70 RunChecks: [](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
77void 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
98TEST(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(n: 1);
105
106 checkDataflow(Code: "void target() {}", TargetFuncMatcher: hasName(Name: "target"),
107 Expectations: Expectations.AsStdFunction());
108}
109
110TEST(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(n: 1);
117
118 checkDataflow(Code: "void target() {}", TargetFuncMatcher: hasName(Name: "target"),
119 Expectations: Expectations.AsStdFunction());
120}
121
122TEST(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(n: 1);
132
133 checkDataflow(Code: R"cc(void target() {
134 int n;
135 // [[program-point]]
136 })cc",
137 TargetFuncMatcher: hasName(Name: "target"), Expectations: Expectations.AsStdFunction());
138}
139
140TEST(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(n: 1);
151
152 checkDataflow(Code: 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 TargetFuncMatcher: hasName(Name: "target"), Expectations: Expectations.AsStdFunction());
162}
163
164TEST(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(n: 1);
175
176 EXPECT_CALL(Expectations, Call(UnorderedElementsAre(
177 IsStringMapEntry("program-point-2a", _),
178 IsStringMapEntry("program-point-2b", _)),
179 _))
180 .Times(n: 1);
181
182 checkDataflow(
183 Code: 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 TargetFuncMatcher: functionDecl(hasAnyName("target1", "target2")),
205 Expectations: Expectations.AsStdFunction());
206}
207
208} // namespace
209

source code of clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp