1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | #include "clang/Lex/Preprocessor.h" |
10 | #include "clang/AST/ASTConsumer.h" |
11 | #include "clang/AST/ASTContext.h" |
12 | #include "clang/Basic/Diagnostic.h" |
13 | #include "clang/Basic/DiagnosticOptions.h" |
14 | #include "clang/Basic/FileManager.h" |
15 | #include "clang/Basic/LangOptions.h" |
16 | #include "clang/Basic/SourceManager.h" |
17 | #include "clang/Basic/TargetInfo.h" |
18 | #include "clang/Basic/TargetOptions.h" |
19 | #include "clang/Lex/HeaderSearch.h" |
20 | #include "clang/Lex/HeaderSearchOptions.h" |
21 | #include "clang/Lex/ModuleLoader.h" |
22 | #include "clang/Lex/PreprocessorOptions.h" |
23 | #include "clang/Parse/Parser.h" |
24 | #include "clang/Sema/Sema.h" |
25 | #include "llvm/ADT/SmallString.h" |
26 | #include "llvm/Support/Path.h" |
27 | #include "gtest/gtest.h" |
28 | |
29 | using namespace clang; |
30 | |
31 | namespace { |
32 | |
33 | |
34 | class InclusionDirectiveCallbacks : public PPCallbacks { |
35 | public: |
36 | void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, |
37 | StringRef FileName, bool IsAngled, |
38 | CharSourceRange FilenameRange, const FileEntry *File, |
39 | StringRef SearchPath, StringRef RelativePath, |
40 | const Module *Imported, |
41 | SrcMgr::CharacteristicKind FileType) override { |
42 | this->HashLoc = HashLoc; |
43 | this->IncludeTok = IncludeTok; |
44 | this->FileName = FileName.str(); |
45 | this->IsAngled = IsAngled; |
46 | this->FilenameRange = FilenameRange; |
47 | this->File = File; |
48 | this->SearchPath = SearchPath.str(); |
49 | this->RelativePath = RelativePath.str(); |
50 | this->Imported = Imported; |
51 | this->FileType = FileType; |
52 | } |
53 | |
54 | SourceLocation HashLoc; |
55 | Token IncludeTok; |
56 | SmallString<16> FileName; |
57 | bool IsAngled; |
58 | CharSourceRange FilenameRange; |
59 | const FileEntry* File; |
60 | SmallString<16> SearchPath; |
61 | SmallString<16> RelativePath; |
62 | const Module* Imported; |
63 | SrcMgr::CharacteristicKind FileType; |
64 | }; |
65 | |
66 | class CondDirectiveCallbacks : public PPCallbacks { |
67 | public: |
68 | struct Result { |
69 | SourceRange ConditionRange; |
70 | ConditionValueKind ConditionValue; |
71 | |
72 | Result(SourceRange R, ConditionValueKind K) |
73 | : ConditionRange(R), ConditionValue(K) {} |
74 | }; |
75 | |
76 | std::vector<Result> Results; |
77 | |
78 | void If(SourceLocation Loc, SourceRange ConditionRange, |
79 | ConditionValueKind ConditionValue) override { |
80 | Results.emplace_back(ConditionRange, ConditionValue); |
81 | } |
82 | |
83 | void Elif(SourceLocation Loc, SourceRange ConditionRange, |
84 | ConditionValueKind ConditionValue, SourceLocation IfLoc) override { |
85 | Results.emplace_back(ConditionRange, ConditionValue); |
86 | } |
87 | }; |
88 | |
89 | |
90 | class PragmaOpenCLExtensionCallbacks : public PPCallbacks { |
91 | public: |
92 | typedef struct { |
93 | SmallString<16> Name; |
94 | unsigned State; |
95 | } CallbackParameters; |
96 | |
97 | PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {} |
98 | |
99 | void PragmaOpenCLExtension(clang::SourceLocation NameLoc, |
100 | const clang::IdentifierInfo *Name, |
101 | clang::SourceLocation StateLoc, |
102 | unsigned State) override { |
103 | this->NameLoc = NameLoc; |
104 | this->Name = Name->getName(); |
105 | this->StateLoc = StateLoc; |
106 | this->State = State; |
107 | } |
108 | |
109 | SourceLocation NameLoc; |
110 | SmallString<16> Name; |
111 | SourceLocation StateLoc; |
112 | unsigned State; |
113 | }; |
114 | |
115 | |
116 | class PPCallbacksTest : public ::testing::Test { |
117 | protected: |
118 | PPCallbacksTest() |
119 | : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem), |
120 | FileMgr(FileSystemOptions(), InMemoryFileSystem), |
121 | DiagID(new DiagnosticIDs()), DiagOpts(new DiagnosticOptions()), |
122 | Diags(DiagID, DiagOpts.get(), new IgnoringDiagConsumer()), |
123 | SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions()) { |
124 | TargetOpts->Triple = "x86_64-apple-darwin11.1.0"; |
125 | Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts); |
126 | } |
127 | |
128 | IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem; |
129 | FileManager FileMgr; |
130 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID; |
131 | IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; |
132 | DiagnosticsEngine Diags; |
133 | SourceManager SourceMgr; |
134 | LangOptions LangOpts; |
135 | std::shared_ptr<TargetOptions> TargetOpts; |
136 | IntrusiveRefCntPtr<TargetInfo> Target; |
137 | |
138 | |
139 | |
140 | void (HeaderSearch &, const char *, |
141 | bool ) { |
142 | |
143 | InMemoryFileSystem->addFile(HeaderPath, 0, |
144 | llvm::MemoryBuffer::getMemBuffer("\n")); |
145 | |
146 | |
147 | StringRef SearchPath = llvm::sys::path::parent_path(HeaderPath); |
148 | const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath); |
149 | DirectoryLookup DL(DE, SrcMgr::C_User, false); |
150 | HeaderInfo.AddSearchPath(DL, IsSystemHeader); |
151 | } |
152 | |
153 | |
154 | StringRef GetSourceString(CharSourceRange Range) { |
155 | const char* B = SourceMgr.getCharacterData(Range.getBegin()); |
156 | const char* E = SourceMgr.getCharacterData(Range.getEnd()); |
157 | |
158 | return StringRef(B, E - B); |
159 | } |
160 | |
161 | StringRef GetSourceStringToEnd(CharSourceRange Range) { |
162 | const char *B = SourceMgr.getCharacterData(Range.getBegin()); |
163 | const char *E = SourceMgr.getCharacterData(Range.getEnd()); |
164 | |
165 | return StringRef( |
166 | B, |
167 | E - B + Lexer::MeasureTokenLength(Range.getEnd(), SourceMgr, LangOpts)); |
168 | } |
169 | |
170 | |
171 | |
172 | CharSourceRange InclusionDirectiveFilenameRange(const char *SourceText, |
173 | const char *, |
174 | bool ) { |
175 | std::unique_ptr<llvm::MemoryBuffer> Buf = |
176 | llvm::MemoryBuffer::getMemBuffer(SourceText); |
177 | SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); |
178 | |
179 | TrivialModuleLoader ModLoader; |
180 | |
181 | HeaderSearch (std::make_shared<HeaderSearchOptions>(), SourceMgr, |
182 | Diags, LangOpts, Target.get()); |
183 | AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader); |
184 | |
185 | Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts, |
186 | SourceMgr, HeaderInfo, ModLoader, |
187 | , |
188 | ); |
189 | return InclusionDirectiveCallback(PP)->FilenameRange; |
190 | } |
191 | |
192 | SrcMgr::CharacteristicKind InclusionDirectiveCharacteristicKind( |
193 | const char *SourceText, const char *, bool ) { |
194 | std::unique_ptr<llvm::MemoryBuffer> Buf = |
195 | llvm::MemoryBuffer::getMemBuffer(SourceText); |
196 | SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); |
197 | |
198 | TrivialModuleLoader ModLoader; |
199 | |
200 | HeaderSearch (std::make_shared<HeaderSearchOptions>(), SourceMgr, |
201 | Diags, LangOpts, Target.get()); |
202 | AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader); |
203 | |
204 | Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts, |
205 | SourceMgr, HeaderInfo, ModLoader, |
206 | , |
207 | ); |
208 | return InclusionDirectiveCallback(PP)->FileType; |
209 | } |
210 | |
211 | InclusionDirectiveCallbacks *InclusionDirectiveCallback(Preprocessor &PP) { |
212 | PP.Initialize(*Target); |
213 | InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks; |
214 | PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks)); |
215 | |
216 | |
217 | PP.EnterMainSourceFile(); |
218 | |
219 | while (true) { |
220 | Token Tok; |
221 | PP.Lex(Tok); |
222 | if (Tok.is(tok::eof)) |
223 | break; |
224 | } |
225 | |
226 | |
227 | return Callbacks; |
228 | } |
229 | |
230 | std::vector<CondDirectiveCallbacks::Result> |
231 | DirectiveExprRange(StringRef SourceText) { |
232 | TrivialModuleLoader ModLoader; |
233 | std::unique_ptr<llvm::MemoryBuffer> Buf = |
234 | llvm::MemoryBuffer::getMemBuffer(SourceText); |
235 | SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); |
236 | HeaderSearch (std::make_shared<HeaderSearchOptions>(), SourceMgr, |
237 | Diags, LangOpts, Target.get()); |
238 | Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts, |
239 | SourceMgr, HeaderInfo, ModLoader, |
240 | , |
241 | ); |
242 | PP.Initialize(*Target); |
243 | auto *Callbacks = new CondDirectiveCallbacks; |
244 | PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks)); |
245 | |
246 | |
247 | PP.EnterMainSourceFile(); |
248 | |
249 | while (true) { |
250 | Token Tok; |
251 | PP.Lex(Tok); |
252 | if (Tok.is(tok::eof)) |
253 | break; |
254 | } |
255 | |
256 | return Callbacks->Results; |
257 | } |
258 | |
259 | PragmaOpenCLExtensionCallbacks::CallbackParameters |
260 | PragmaOpenCLExtensionCall(const char *SourceText) { |
261 | LangOptions OpenCLLangOpts; |
262 | OpenCLLangOpts.OpenCL = 1; |
263 | |
264 | std::unique_ptr<llvm::MemoryBuffer> SourceBuf = |
265 | llvm::MemoryBuffer::getMemBuffer(SourceText, "test.cl"); |
266 | SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf))); |
267 | |
268 | TrivialModuleLoader ModLoader; |
269 | HeaderSearch (std::make_shared<HeaderSearchOptions>(), SourceMgr, |
270 | Diags, OpenCLLangOpts, Target.get()); |
271 | |
272 | Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, |
273 | OpenCLLangOpts, SourceMgr, HeaderInfo, ModLoader, |
274 | , |
275 | ); |
276 | PP.Initialize(*Target); |
277 | |
278 | |
279 | |
280 | |
281 | ASTContext Context(OpenCLLangOpts, SourceMgr, PP.getIdentifierTable(), |
282 | PP.getSelectorTable(), PP.getBuiltinInfo()); |
283 | Context.InitBuiltinTypes(*Target); |
284 | |
285 | ASTConsumer Consumer; |
286 | Sema S(PP, Context, Consumer); |
287 | Parser P(PP, S, false); |
288 | PragmaOpenCLExtensionCallbacks* Callbacks = new PragmaOpenCLExtensionCallbacks; |
289 | PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks)); |
290 | |
291 | |
292 | PP.EnterMainSourceFile(); |
293 | while (true) { |
294 | Token Tok; |
295 | PP.Lex(Tok); |
296 | if (Tok.is(tok::eof)) |
297 | break; |
298 | } |
299 | |
300 | PragmaOpenCLExtensionCallbacks::CallbackParameters RetVal = { |
301 | Callbacks->Name, |
302 | Callbacks->State |
303 | }; |
304 | return RetVal; |
305 | } |
306 | }; |
307 | |
308 | TEST_F(PPCallbacksTest, UserFileCharacteristics) { |
309 | const char *Source = "#include \"quoted.h\"\n"; |
310 | |
311 | SrcMgr::CharacteristicKind Kind = |
312 | InclusionDirectiveCharacteristicKind(Source, "/quoted.h", false); |
313 | |
314 | ASSERT_EQ(SrcMgr::CharacteristicKind::C_User, Kind); |
315 | } |
316 | |
317 | TEST_F(PPCallbacksTest, QuotedFilename) { |
318 | const char* Source = |
319 | "#include \"quoted.h\"\n"; |
320 | |
321 | CharSourceRange Range = |
322 | InclusionDirectiveFilenameRange(Source, "/quoted.h", false); |
323 | |
324 | ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); |
325 | } |
326 | |
327 | TEST_F(PPCallbacksTest, AngledFilename) { |
328 | const char* Source = |
329 | "#include <angled.h>\n"; |
330 | |
331 | CharSourceRange Range = |
332 | InclusionDirectiveFilenameRange(Source, "/angled.h", true); |
333 | |
334 | ASSERT_EQ("<angled.h>", GetSourceString(Range)); |
335 | } |
336 | |
337 | TEST_F(PPCallbacksTest, QuotedInMacro) { |
338 | const char* Source = |
339 | "#define MACRO_QUOTED \"quoted.h\"\n" |
340 | "#include MACRO_QUOTED\n"; |
341 | |
342 | CharSourceRange Range = |
343 | InclusionDirectiveFilenameRange(Source, "/quoted.h", false); |
344 | |
345 | ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); |
346 | } |
347 | |
348 | TEST_F(PPCallbacksTest, AngledInMacro) { |
349 | const char* Source = |
350 | "#define MACRO_ANGLED <angled.h>\n" |
351 | "#include MACRO_ANGLED\n"; |
352 | |
353 | CharSourceRange Range = |
354 | InclusionDirectiveFilenameRange(Source, "/angled.h", true); |
355 | |
356 | ASSERT_EQ("<angled.h>", GetSourceString(Range)); |
357 | } |
358 | |
359 | TEST_F(PPCallbacksTest, StringizedMacroArgument) { |
360 | const char* Source = |
361 | "#define MACRO_STRINGIZED(x) #x\n" |
362 | "#include MACRO_STRINGIZED(quoted.h)\n"; |
363 | |
364 | CharSourceRange Range = |
365 | InclusionDirectiveFilenameRange(Source, "/quoted.h", false); |
366 | |
367 | ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); |
368 | } |
369 | |
370 | TEST_F(PPCallbacksTest, ConcatenatedMacroArgument) { |
371 | const char* Source = |
372 | "#define MACRO_ANGLED <angled.h>\n" |
373 | "#define MACRO_CONCAT(x, y) x ## _ ## y\n" |
374 | "#include MACRO_CONCAT(MACRO, ANGLED)\n"; |
375 | |
376 | CharSourceRange Range = |
377 | InclusionDirectiveFilenameRange(Source, "/angled.h", false); |
378 | |
379 | ASSERT_EQ("<angled.h>", GetSourceString(Range)); |
380 | } |
381 | |
382 | TEST_F(PPCallbacksTest, TrigraphFilename) { |
383 | const char* Source = |
384 | "#include \"tri\?\?-graph.h\"\n"; |
385 | |
386 | CharSourceRange Range = |
387 | InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false); |
388 | |
389 | ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range)); |
390 | } |
391 | |
392 | TEST_F(PPCallbacksTest, TrigraphInMacro) { |
393 | const char* Source = |
394 | "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n" |
395 | "#include MACRO_TRIGRAPH\n"; |
396 | |
397 | CharSourceRange Range = |
398 | InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false); |
399 | |
400 | ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range)); |
401 | } |
402 | |
403 | TEST_F(PPCallbacksTest, OpenCLExtensionPragmaEnabled) { |
404 | const char* Source = |
405 | "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n"; |
406 | |
407 | PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters = |
408 | PragmaOpenCLExtensionCall(Source); |
409 | |
410 | ASSERT_EQ("cl_khr_fp64", Parameters.Name); |
411 | unsigned ExpectedState = 1; |
412 | ASSERT_EQ(ExpectedState, Parameters.State); |
413 | } |
414 | |
415 | TEST_F(PPCallbacksTest, OpenCLExtensionPragmaDisabled) { |
416 | const char* Source = |
417 | "#pragma OPENCL EXTENSION cl_khr_fp16 : disable\n"; |
418 | |
419 | PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters = |
420 | PragmaOpenCLExtensionCall(Source); |
421 | |
422 | ASSERT_EQ("cl_khr_fp16", Parameters.Name); |
423 | unsigned ExpectedState = 0; |
424 | ASSERT_EQ(ExpectedState, Parameters.State); |
425 | } |
426 | |
427 | TEST_F(PPCallbacksTest, DirectiveExprRanges) { |
428 | const auto &Results1 = DirectiveExprRange("#if FLUZZY_FLOOF\n#endif\n"); |
429 | EXPECT_EQ(Results1.size(), 1U); |
430 | EXPECT_EQ( |
431 | GetSourceStringToEnd(CharSourceRange(Results1[0].ConditionRange, false)), |
432 | "FLUZZY_FLOOF"); |
433 | |
434 | const auto &Results2 = DirectiveExprRange("#if 1 + 4 < 7\n#endif\n"); |
435 | EXPECT_EQ(Results2.size(), 1U); |
436 | EXPECT_EQ( |
437 | GetSourceStringToEnd(CharSourceRange(Results2[0].ConditionRange, false)), |
438 | "1 + 4 < 7"); |
439 | |
440 | const auto &Results3 = DirectiveExprRange("#if 1 + \\\n 2\n#endif\n"); |
441 | EXPECT_EQ(Results3.size(), 1U); |
442 | EXPECT_EQ( |
443 | GetSourceStringToEnd(CharSourceRange(Results3[0].ConditionRange, false)), |
444 | "1 + \\\n 2"); |
445 | |
446 | const auto &Results4 = DirectiveExprRange("#if 0\n#elif FLOOFY\n#endif\n"); |
447 | EXPECT_EQ(Results4.size(), 2U); |
448 | EXPECT_EQ( |
449 | GetSourceStringToEnd(CharSourceRange(Results4[0].ConditionRange, false)), |
450 | "0"); |
451 | EXPECT_EQ( |
452 | GetSourceStringToEnd(CharSourceRange(Results4[1].ConditionRange, false)), |
453 | "FLOOFY"); |
454 | |
455 | const auto &Results5 = DirectiveExprRange("#if 1\n#elif FLOOFY\n#endif\n"); |
456 | EXPECT_EQ(Results5.size(), 2U); |
457 | EXPECT_EQ( |
458 | GetSourceStringToEnd(CharSourceRange(Results5[0].ConditionRange, false)), |
459 | "1"); |
460 | EXPECT_EQ( |
461 | GetSourceStringToEnd(CharSourceRange(Results5[1].ConditionRange, false)), |
462 | "FLOOFY"); |
463 | |
464 | const auto &Results6 = |
465 | DirectiveExprRange("#if defined(FLUZZY_FLOOF)\n#endif\n"); |
466 | EXPECT_EQ(Results6.size(), 1U); |
467 | EXPECT_EQ( |
468 | GetSourceStringToEnd(CharSourceRange(Results6[0].ConditionRange, false)), |
469 | "defined(FLUZZY_FLOOF)"); |
470 | |
471 | const auto &Results7 = |
472 | DirectiveExprRange("#if 1\n#elif defined(FLOOFY)\n#endif\n"); |
473 | EXPECT_EQ(Results7.size(), 2U); |
474 | EXPECT_EQ( |
475 | GetSourceStringToEnd(CharSourceRange(Results7[0].ConditionRange, false)), |
476 | "1"); |
477 | EXPECT_EQ( |
478 | GetSourceStringToEnd(CharSourceRange(Results7[1].ConditionRange, false)), |
479 | "defined(FLOOFY)"); |
480 | |
481 | const auto &Results8 = |
482 | DirectiveExprRange("#define FLOOFY 0\n#if __FILE__ > FLOOFY\n#endif\n"); |
483 | EXPECT_EQ(Results8.size(), 1U); |
484 | EXPECT_EQ( |
485 | GetSourceStringToEnd(CharSourceRange(Results8[0].ConditionRange, false)), |
486 | "__FILE__ > FLOOFY"); |
487 | EXPECT_EQ( |
488 | Lexer::getSourceText(CharSourceRange(Results8[0].ConditionRange, false), |
489 | SourceMgr, LangOpts), |
490 | "__FILE__ > FLOOFY"); |
491 | } |
492 | |
493 | } |
494 | |