1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | #ifndef LLVM_CLANG_UNITTESTS_ASTMATCHERS_ASTMATCHERSTEST_H |
10 | #define LLVM_CLANG_UNITTESTS_ASTMATCHERS_ASTMATCHERSTEST_H |
11 | |
12 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
13 | #include "clang/Frontend/ASTUnit.h" |
14 | #include "clang/Tooling/Tooling.h" |
15 | #include "gtest/gtest.h" |
16 | |
17 | namespace clang { |
18 | namespace ast_matchers { |
19 | |
20 | using clang::tooling::buildASTFromCodeWithArgs; |
21 | using clang::tooling::newFrontendActionFactory; |
22 | using clang::tooling::runToolOnCodeWithArgs; |
23 | using clang::tooling::FrontendActionFactory; |
24 | using clang::tooling::FileContentMappings; |
25 | |
26 | class BoundNodesCallback { |
27 | public: |
28 | virtual ~BoundNodesCallback() {} |
29 | virtual bool run(const BoundNodes *BoundNodes) = 0; |
30 | virtual bool run(const BoundNodes *BoundNodes, ASTContext *Context) = 0; |
31 | virtual void onEndOfTranslationUnit() {} |
32 | }; |
33 | |
34 | |
35 | |
36 | |
37 | class VerifyMatch : public MatchFinder::MatchCallback { |
38 | public: |
39 | VerifyMatch(std::unique_ptr<BoundNodesCallback> FindResultVerifier, bool *Verified) |
40 | : Verified(Verified), FindResultReviewer(std::move(FindResultVerifier)) {} |
41 | |
42 | void run(const MatchFinder::MatchResult &Result) override { |
43 | if (FindResultReviewer != nullptr) { |
44 | *Verified |= FindResultReviewer->run(&Result.Nodes, Result.Context); |
45 | } else { |
46 | *Verified = true; |
47 | } |
48 | } |
49 | |
50 | void onEndOfTranslationUnit() override { |
51 | if (FindResultReviewer) |
52 | FindResultReviewer->onEndOfTranslationUnit(); |
53 | } |
54 | |
55 | private: |
56 | bool *const Verified; |
57 | const std::unique_ptr<BoundNodesCallback> FindResultReviewer; |
58 | }; |
59 | |
60 | template <typename T> |
61 | testing::AssertionResult matchesConditionally( |
62 | const std::string &Code, const T &AMatcher, bool ExpectMatch, |
63 | llvm::ArrayRef<llvm::StringRef> CompileArgs, |
64 | const FileContentMappings &VirtualMappedFiles = FileContentMappings(), |
65 | const std::string &Filename = "input.cc") { |
66 | bool Found = false, DynamicFound = false; |
67 | MatchFinder Finder; |
68 | VerifyMatch VerifyFound(nullptr, &Found); |
69 | Finder.addMatcher(AMatcher, &VerifyFound); |
70 | VerifyMatch VerifyDynamicFound(nullptr, &DynamicFound); |
71 | if (!Finder.addDynamicMatcher(AMatcher, &VerifyDynamicFound)) |
72 | return testing::AssertionFailure() << "Could not add dynamic matcher"; |
73 | std::unique_ptr<FrontendActionFactory> Factory( |
74 | newFrontendActionFactory(&Finder)); |
75 | |
76 | |
77 | |
78 | |
79 | |
80 | |
81 | |
82 | |
83 | std::vector<std::string> Args(CompileArgs.begin(), CompileArgs.end()); |
84 | Args.insert(Args.end(), {"-frtti", "-fexceptions", |
85 | "-target", "i386-unknown-unknown"}); |
86 | if (!runToolOnCodeWithArgs( |
87 | Factory->create(), Code, Args, Filename, "clang-tool", |
88 | std::make_shared<PCHContainerOperations>(), VirtualMappedFiles)) { |
89 | return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; |
90 | } |
91 | if (Found != DynamicFound) { |
92 | return testing::AssertionFailure() << "Dynamic match result (" |
93 | << DynamicFound |
94 | << ") does not match static result (" |
95 | << Found << ")"; |
96 | } |
97 | if (!Found && ExpectMatch) { |
98 | return testing::AssertionFailure() |
99 | << "Could not find match in \"" << Code << "\""; |
100 | } else if (Found && !ExpectMatch) { |
101 | return testing::AssertionFailure() |
102 | << "Found unexpected match in \"" << Code << "\""; |
103 | } |
104 | return testing::AssertionSuccess(); |
105 | } |
106 | |
107 | template <typename T> |
108 | testing::AssertionResult matchesConditionally( |
109 | const std::string &Code, const T &AMatcher, bool ExpectMatch, |
110 | llvm::StringRef CompileArg, |
111 | const FileContentMappings &VirtualMappedFiles = FileContentMappings(), |
112 | const std::string &Filename = "input.cc") { |
113 | return matchesConditionally(Code, AMatcher, ExpectMatch, |
114 | llvm::makeArrayRef(CompileArg), |
115 | VirtualMappedFiles, Filename); |
116 | } |
117 | |
118 | template <typename T> |
119 | testing::AssertionResult matches(const std::string &Code, const T &AMatcher) { |
120 | return matchesConditionally(Code, AMatcher, true, "-std=c++11"); |
121 | } |
122 | |
123 | template <typename T> |
124 | testing::AssertionResult notMatches(const std::string &Code, |
125 | const T &AMatcher) { |
126 | return matchesConditionally(Code, AMatcher, false, "-std=c++11"); |
127 | } |
128 | |
129 | template <typename T> |
130 | testing::AssertionResult matchesObjC(const std::string &Code, const T &AMatcher, |
131 | bool ExpectMatch = true) { |
132 | return matchesConditionally(Code, AMatcher, ExpectMatch, |
133 | {"-fobjc-nonfragile-abi", "-Wno-objc-root-class", |
134 | "-fblocks", "-Wno-incomplete-implementation"}, |
135 | FileContentMappings(), "input.m"); |
136 | } |
137 | |
138 | template <typename T> |
139 | testing::AssertionResult matchesC(const std::string &Code, const T &AMatcher) { |
140 | return matchesConditionally(Code, AMatcher, true, "", FileContentMappings(), |
141 | "input.c"); |
142 | } |
143 | |
144 | template <typename T> |
145 | testing::AssertionResult matchesC99(const std::string &Code, |
146 | const T &AMatcher) { |
147 | return matchesConditionally(Code, AMatcher, true, "-std=c99", |
148 | FileContentMappings(), "input.c"); |
149 | } |
150 | |
151 | template <typename T> |
152 | testing::AssertionResult notMatchesC(const std::string &Code, |
153 | const T &AMatcher) { |
154 | return matchesConditionally(Code, AMatcher, false, "", FileContentMappings(), |
155 | "input.c"); |
156 | } |
157 | |
158 | template <typename T> |
159 | testing::AssertionResult notMatchesObjC(const std::string &Code, |
160 | const T &AMatcher) { |
161 | return matchesObjC(Code, AMatcher, false); |
162 | } |
163 | |
164 | |
165 | |
166 | |
167 | template <typename T> |
168 | testing::AssertionResult matchesConditionallyWithCuda( |
169 | const std::string &Code, const T &AMatcher, bool ExpectMatch, |
170 | llvm::StringRef CompileArg) { |
171 | const std::string = |
172 | "typedef unsigned int size_t;\n" |
173 | "#define __constant__ __attribute__((constant))\n" |
174 | "#define __device__ __attribute__((device))\n" |
175 | "#define __global__ __attribute__((global))\n" |
176 | "#define __host__ __attribute__((host))\n" |
177 | "#define __shared__ __attribute__((shared))\n" |
178 | "struct dim3 {" |
179 | " unsigned x, y, z;" |
180 | " __host__ __device__ dim3(unsigned x, unsigned y = 1, unsigned z = 1)" |
181 | " : x(x), y(y), z(z) {}" |
182 | "};" |
183 | "typedef struct cudaStream *cudaStream_t;" |
184 | "int cudaConfigureCall(dim3 gridSize, dim3 blockSize," |
185 | " size_t sharedSize = 0," |
186 | " cudaStream_t stream = 0);" |
187 | "extern \"C\" unsigned __cudaPushCallConfiguration(" |
188 | " dim3 gridDim, dim3 blockDim, size_t sharedMem = 0, void *stream = 0);"; |
189 | |
190 | bool Found = false, DynamicFound = false; |
191 | MatchFinder Finder; |
192 | VerifyMatch VerifyFound(nullptr, &Found); |
193 | Finder.addMatcher(AMatcher, &VerifyFound); |
194 | VerifyMatch VerifyDynamicFound(nullptr, &DynamicFound); |
195 | if (!Finder.addDynamicMatcher(AMatcher, &VerifyDynamicFound)) |
196 | return testing::AssertionFailure() << "Could not add dynamic matcher"; |
197 | std::unique_ptr<FrontendActionFactory> Factory( |
198 | newFrontendActionFactory(&Finder)); |
199 | |
200 | |
201 | |
202 | std::vector<std::string> Args = { |
203 | "-xcuda", "-fno-ms-extensions", "--cuda-host-only", "-nocudainc", |
204 | "-target", "x86_64-unknown-unknown", CompileArg}; |
205 | if (!runToolOnCodeWithArgs(Factory->create(), |
206 | CudaHeader + Code, Args)) { |
207 | return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; |
208 | } |
209 | if (Found != DynamicFound) { |
210 | return testing::AssertionFailure() << "Dynamic match result (" |
211 | << DynamicFound |
212 | << ") does not match static result (" |
213 | << Found << ")"; |
214 | } |
215 | if (!Found && ExpectMatch) { |
216 | return testing::AssertionFailure() |
217 | << "Could not find match in \"" << Code << "\""; |
218 | } else if (Found && !ExpectMatch) { |
219 | return testing::AssertionFailure() |
220 | << "Found unexpected match in \"" << Code << "\""; |
221 | } |
222 | return testing::AssertionSuccess(); |
223 | } |
224 | |
225 | template <typename T> |
226 | testing::AssertionResult matchesWithCuda(const std::string &Code, |
227 | const T &AMatcher) { |
228 | return matchesConditionallyWithCuda(Code, AMatcher, true, "-std=c++11"); |
229 | } |
230 | |
231 | template <typename T> |
232 | testing::AssertionResult notMatchesWithCuda(const std::string &Code, |
233 | const T &AMatcher) { |
234 | return matchesConditionallyWithCuda(Code, AMatcher, false, "-std=c++11"); |
235 | } |
236 | |
237 | template <typename T> |
238 | testing::AssertionResult matchesWithOpenMP(const std::string &Code, |
239 | const T &AMatcher) { |
240 | return matchesConditionally(Code, AMatcher, true, "-fopenmp=libomp"); |
241 | } |
242 | |
243 | template <typename T> |
244 | testing::AssertionResult notMatchesWithOpenMP(const std::string &Code, |
245 | const T &AMatcher) { |
246 | return matchesConditionally(Code, AMatcher, false, "-fopenmp=libomp"); |
247 | } |
248 | |
249 | template <typename T> |
250 | testing::AssertionResult |
251 | matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher, |
252 | std::unique_ptr<BoundNodesCallback> FindResultVerifier, |
253 | bool ExpectResult) { |
254 | bool VerifiedResult = false; |
255 | MatchFinder Finder; |
256 | VerifyMatch VerifyVerifiedResult(std::move(FindResultVerifier), &VerifiedResult); |
257 | Finder.addMatcher(AMatcher, &VerifyVerifiedResult); |
258 | std::unique_ptr<FrontendActionFactory> Factory( |
259 | newFrontendActionFactory(&Finder)); |
260 | |
261 | |
262 | |
263 | std::vector<std::string> Args = {"-std=gnu++98", "-target", |
264 | "i386-unknown-unknown"}; |
265 | if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) { |
266 | return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; |
267 | } |
268 | if (!VerifiedResult && ExpectResult) { |
269 | return testing::AssertionFailure() |
270 | << "Could not verify result in \"" << Code << "\""; |
271 | } else if (VerifiedResult && !ExpectResult) { |
272 | return testing::AssertionFailure() |
273 | << "Verified unexpected result in \"" << Code << "\""; |
274 | } |
275 | |
276 | VerifiedResult = false; |
277 | std::unique_ptr<ASTUnit> AST(buildASTFromCodeWithArgs(Code, Args)); |
278 | if (!AST.get()) |
279 | return testing::AssertionFailure() << "Parsing error in \"" << Code |
280 | << "\" while building AST"; |
281 | Finder.matchAST(AST->getASTContext()); |
282 | if (!VerifiedResult && ExpectResult) { |
283 | return testing::AssertionFailure() |
284 | << "Could not verify result in \"" << Code << "\" with AST"; |
285 | } else if (VerifiedResult && !ExpectResult) { |
286 | return testing::AssertionFailure() |
287 | << "Verified unexpected result in \"" << Code << "\" with AST"; |
288 | } |
289 | |
290 | return testing::AssertionSuccess(); |
291 | } |
292 | |
293 | |
294 | |
295 | template <typename T> |
296 | testing::AssertionResult |
297 | matchAndVerifyResultTrue(const std::string &Code, const T &AMatcher, |
298 | std::unique_ptr<BoundNodesCallback> FindResultVerifier) { |
299 | return matchAndVerifyResultConditionally( |
300 | Code, AMatcher, std::move(FindResultVerifier), true); |
301 | } |
302 | |
303 | template <typename T> |
304 | testing::AssertionResult |
305 | matchAndVerifyResultFalse(const std::string &Code, const T &AMatcher, |
306 | std::unique_ptr<BoundNodesCallback> FindResultVerifier) { |
307 | return matchAndVerifyResultConditionally( |
308 | Code, AMatcher, std::move(FindResultVerifier), false); |
309 | } |
310 | |
311 | |
312 | |
313 | |
314 | template <typename T> |
315 | class VerifyIdIsBoundTo : public BoundNodesCallback { |
316 | public: |
317 | |
318 | |
319 | explicit VerifyIdIsBoundTo(llvm::StringRef Id) |
320 | : Id(Id), ExpectedCount(-1), Count(0) {} |
321 | |
322 | |
323 | |
324 | VerifyIdIsBoundTo(llvm::StringRef Id, int ExpectedCount) |
325 | : Id(Id), ExpectedCount(ExpectedCount), Count(0) {} |
326 | |
327 | |
328 | |
329 | |
330 | VerifyIdIsBoundTo(llvm::StringRef Id, llvm::StringRef ExpectedName, |
331 | int ExpectedCount = 1) |
332 | : Id(Id), ExpectedCount(ExpectedCount), Count(0), |
333 | ExpectedName(ExpectedName) {} |
334 | |
335 | void onEndOfTranslationUnit() override { |
336 | if (ExpectedCount != -1) { |
337 | EXPECT_EQ(ExpectedCount, Count); |
338 | } |
339 | if (!ExpectedName.empty()) { |
340 | EXPECT_EQ(ExpectedName, Name); |
341 | } |
342 | Count = 0; |
343 | Name.clear(); |
344 | } |
345 | |
346 | ~VerifyIdIsBoundTo() override { |
347 | EXPECT_EQ(0, Count); |
348 | EXPECT_EQ("", Name); |
349 | } |
350 | |
351 | bool run(const BoundNodes *Nodes) override { |
352 | const BoundNodes::IDToNodeMap &M = Nodes->getMap(); |
353 | if (Nodes->getNodeAs<T>(Id)) { |
354 | ++Count; |
355 | if (const NamedDecl *Named = Nodes->getNodeAs<NamedDecl>(Id)) { |
356 | Name = Named->getNameAsString(); |
357 | } else if (const NestedNameSpecifier *NNS = |
358 | Nodes->getNodeAs<NestedNameSpecifier>(Id)) { |
359 | llvm::raw_string_ostream OS(Name); |
360 | NNS->print(OS, PrintingPolicy(LangOptions())); |
361 | } |
362 | BoundNodes::IDToNodeMap::const_iterator I = M.find(Id); |
363 | EXPECT_NE(M.end(), I); |
364 | if (I != M.end()) { |
365 | EXPECT_EQ(Nodes->getNodeAs<T>(Id), I->second.get<T>()); |
366 | } |
367 | return true; |
368 | } |
369 | EXPECT_TRUE(M.count(Id) == 0 || |
370 | M.find(Id)->second.template get<T>() == nullptr); |
371 | return false; |
372 | } |
373 | |
374 | bool run(const BoundNodes *Nodes, ASTContext *Context) override { |
375 | return run(Nodes); |
376 | } |
377 | |
378 | private: |
379 | const std::string Id; |
380 | const int ExpectedCount; |
381 | int Count; |
382 | const std::string ExpectedName; |
383 | std::string Name; |
384 | }; |
385 | |
386 | } |
387 | } |
388 | |
389 | #endif |
390 | |