PPCallbacksTest.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. //===- unittests/Lex/PPCallbacksTest.cpp - PPCallbacks tests ------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===--------------------------------------------------------------===//
  8. #include "clang/Lex/Preprocessor.h"
  9. #include "clang/AST/ASTConsumer.h"
  10. #include "clang/AST/ASTContext.h"
  11. #include "clang/Basic/Diagnostic.h"
  12. #include "clang/Basic/DiagnosticOptions.h"
  13. #include "clang/Basic/FileManager.h"
  14. #include "clang/Basic/LangOptions.h"
  15. #include "clang/Basic/SourceManager.h"
  16. #include "clang/Basic/TargetInfo.h"
  17. #include "clang/Basic/TargetOptions.h"
  18. #include "clang/Lex/HeaderSearch.h"
  19. #include "clang/Lex/HeaderSearchOptions.h"
  20. #include "clang/Lex/ModuleLoader.h"
  21. #include "clang/Lex/PreprocessorOptions.h"
  22. #include "clang/Parse/Parser.h"
  23. #include "clang/Sema/Sema.h"
  24. #include "llvm/ADT/SmallString.h"
  25. #include "llvm/Support/Path.h"
  26. #include "gtest/gtest.h"
  27. using namespace clang;
  28. namespace {
  29. // Stub to collect data from InclusionDirective callbacks.
  30. class InclusionDirectiveCallbacks : public PPCallbacks {
  31. public:
  32. void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
  33. StringRef FileName, bool IsAngled,
  34. CharSourceRange FilenameRange, const FileEntry *File,
  35. StringRef SearchPath, StringRef RelativePath,
  36. const Module *Imported,
  37. SrcMgr::CharacteristicKind FileType) override {
  38. this->HashLoc = HashLoc;
  39. this->IncludeTok = IncludeTok;
  40. this->FileName = FileName.str();
  41. this->IsAngled = IsAngled;
  42. this->FilenameRange = FilenameRange;
  43. this->File = File;
  44. this->SearchPath = SearchPath.str();
  45. this->RelativePath = RelativePath.str();
  46. this->Imported = Imported;
  47. this->FileType = FileType;
  48. }
  49. SourceLocation HashLoc;
  50. Token IncludeTok;
  51. SmallString<16> FileName;
  52. bool IsAngled;
  53. CharSourceRange FilenameRange;
  54. const FileEntry* File;
  55. SmallString<16> SearchPath;
  56. SmallString<16> RelativePath;
  57. const Module* Imported;
  58. SrcMgr::CharacteristicKind FileType;
  59. };
  60. class CondDirectiveCallbacks : public PPCallbacks {
  61. public:
  62. struct Result {
  63. SourceRange ConditionRange;
  64. ConditionValueKind ConditionValue;
  65. Result(SourceRange R, ConditionValueKind K)
  66. : ConditionRange(R), ConditionValue(K) {}
  67. };
  68. std::vector<Result> Results;
  69. void If(SourceLocation Loc, SourceRange ConditionRange,
  70. ConditionValueKind ConditionValue) override {
  71. Results.emplace_back(ConditionRange, ConditionValue);
  72. }
  73. void Elif(SourceLocation Loc, SourceRange ConditionRange,
  74. ConditionValueKind ConditionValue, SourceLocation IfLoc) override {
  75. Results.emplace_back(ConditionRange, ConditionValue);
  76. }
  77. };
  78. // Stub to collect data from PragmaOpenCLExtension callbacks.
  79. class PragmaOpenCLExtensionCallbacks : public PPCallbacks {
  80. public:
  81. typedef struct {
  82. SmallString<16> Name;
  83. unsigned State;
  84. } CallbackParameters;
  85. PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {}
  86. void PragmaOpenCLExtension(clang::SourceLocation NameLoc,
  87. const clang::IdentifierInfo *Name,
  88. clang::SourceLocation StateLoc,
  89. unsigned State) override {
  90. this->NameLoc = NameLoc;
  91. this->Name = Name->getName();
  92. this->StateLoc = StateLoc;
  93. this->State = State;
  94. }
  95. SourceLocation NameLoc;
  96. SmallString<16> Name;
  97. SourceLocation StateLoc;
  98. unsigned State;
  99. };
  100. // PPCallbacks test fixture.
  101. class PPCallbacksTest : public ::testing::Test {
  102. protected:
  103. PPCallbacksTest()
  104. : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
  105. FileMgr(FileSystemOptions(), InMemoryFileSystem),
  106. DiagID(new DiagnosticIDs()), DiagOpts(new DiagnosticOptions()),
  107. Diags(DiagID, DiagOpts.get(), new IgnoringDiagConsumer()),
  108. SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions()) {
  109. TargetOpts->Triple = "x86_64-apple-darwin11.1.0";
  110. Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
  111. }
  112. IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
  113. FileManager FileMgr;
  114. IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
  115. IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
  116. DiagnosticsEngine Diags;
  117. SourceManager SourceMgr;
  118. LangOptions LangOpts;
  119. std::shared_ptr<TargetOptions> TargetOpts;
  120. IntrusiveRefCntPtr<TargetInfo> Target;
  121. // Register a header path as a known file and add its location
  122. // to search path.
  123. void AddFakeHeader(HeaderSearch &HeaderInfo, const char *HeaderPath,
  124. bool IsSystemHeader) {
  125. // Tell FileMgr about header.
  126. InMemoryFileSystem->addFile(HeaderPath, 0,
  127. llvm::MemoryBuffer::getMemBuffer("\n"));
  128. // Add header's parent path to search path.
  129. StringRef SearchPath = llvm::sys::path::parent_path(HeaderPath);
  130. auto DE = FileMgr.getOptionalDirectoryRef(SearchPath);
  131. DirectoryLookup DL(*DE, SrcMgr::C_User, false);
  132. HeaderInfo.AddSearchPath(DL, IsSystemHeader);
  133. }
  134. // Get the raw source string of the range.
  135. StringRef GetSourceString(CharSourceRange Range) {
  136. const char* B = SourceMgr.getCharacterData(Range.getBegin());
  137. const char* E = SourceMgr.getCharacterData(Range.getEnd());
  138. return StringRef(B, E - B);
  139. }
  140. StringRef GetSourceStringToEnd(CharSourceRange Range) {
  141. const char *B = SourceMgr.getCharacterData(Range.getBegin());
  142. const char *E = SourceMgr.getCharacterData(Range.getEnd());
  143. return StringRef(
  144. B,
  145. E - B + Lexer::MeasureTokenLength(Range.getEnd(), SourceMgr, LangOpts));
  146. }
  147. // Run lexer over SourceText and collect FilenameRange from
  148. // the InclusionDirective callback.
  149. CharSourceRange InclusionDirectiveFilenameRange(const char *SourceText,
  150. const char *HeaderPath,
  151. bool SystemHeader) {
  152. std::unique_ptr<llvm::MemoryBuffer> Buf =
  153. llvm::MemoryBuffer::getMemBuffer(SourceText);
  154. SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
  155. TrivialModuleLoader ModLoader;
  156. HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
  157. Diags, LangOpts, Target.get());
  158. AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader);
  159. Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
  160. SourceMgr, HeaderInfo, ModLoader,
  161. /*IILookup =*/nullptr,
  162. /*OwnsHeaderSearch =*/false);
  163. return InclusionDirectiveCallback(PP)->FilenameRange;
  164. }
  165. SrcMgr::CharacteristicKind InclusionDirectiveCharacteristicKind(
  166. const char *SourceText, const char *HeaderPath, bool SystemHeader) {
  167. std::unique_ptr<llvm::MemoryBuffer> Buf =
  168. llvm::MemoryBuffer::getMemBuffer(SourceText);
  169. SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
  170. TrivialModuleLoader ModLoader;
  171. HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
  172. Diags, LangOpts, Target.get());
  173. AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader);
  174. Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
  175. SourceMgr, HeaderInfo, ModLoader,
  176. /*IILookup =*/nullptr,
  177. /*OwnsHeaderSearch =*/false);
  178. return InclusionDirectiveCallback(PP)->FileType;
  179. }
  180. InclusionDirectiveCallbacks *InclusionDirectiveCallback(Preprocessor &PP) {
  181. PP.Initialize(*Target);
  182. InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks;
  183. PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
  184. // Lex source text.
  185. PP.EnterMainSourceFile();
  186. while (true) {
  187. Token Tok;
  188. PP.Lex(Tok);
  189. if (Tok.is(tok::eof))
  190. break;
  191. }
  192. // Callbacks have been executed at this point -- return filename range.
  193. return Callbacks;
  194. }
  195. std::vector<CondDirectiveCallbacks::Result>
  196. DirectiveExprRange(StringRef SourceText) {
  197. TrivialModuleLoader ModLoader;
  198. std::unique_ptr<llvm::MemoryBuffer> Buf =
  199. llvm::MemoryBuffer::getMemBuffer(SourceText);
  200. SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
  201. HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
  202. Diags, LangOpts, Target.get());
  203. Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
  204. SourceMgr, HeaderInfo, ModLoader,
  205. /*IILookup =*/nullptr,
  206. /*OwnsHeaderSearch =*/false);
  207. PP.Initialize(*Target);
  208. auto *Callbacks = new CondDirectiveCallbacks;
  209. PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
  210. // Lex source text.
  211. PP.EnterMainSourceFile();
  212. while (true) {
  213. Token Tok;
  214. PP.Lex(Tok);
  215. if (Tok.is(tok::eof))
  216. break;
  217. }
  218. return Callbacks->Results;
  219. }
  220. PragmaOpenCLExtensionCallbacks::CallbackParameters
  221. PragmaOpenCLExtensionCall(const char *SourceText) {
  222. LangOptions OpenCLLangOpts;
  223. OpenCLLangOpts.OpenCL = 1;
  224. std::unique_ptr<llvm::MemoryBuffer> SourceBuf =
  225. llvm::MemoryBuffer::getMemBuffer(SourceText, "test.cl");
  226. SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf)));
  227. TrivialModuleLoader ModLoader;
  228. HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
  229. Diags, OpenCLLangOpts, Target.get());
  230. Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags,
  231. OpenCLLangOpts, SourceMgr, HeaderInfo, ModLoader,
  232. /*IILookup =*/nullptr,
  233. /*OwnsHeaderSearch =*/false);
  234. PP.Initialize(*Target);
  235. // parser actually sets correct pragma handlers for preprocessor
  236. // according to LangOptions, so we init Parser to register opencl
  237. // pragma handlers
  238. ASTContext Context(OpenCLLangOpts, SourceMgr, PP.getIdentifierTable(),
  239. PP.getSelectorTable(), PP.getBuiltinInfo());
  240. Context.InitBuiltinTypes(*Target);
  241. ASTConsumer Consumer;
  242. Sema S(PP, Context, Consumer);
  243. Parser P(PP, S, false);
  244. PragmaOpenCLExtensionCallbacks* Callbacks = new PragmaOpenCLExtensionCallbacks;
  245. PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
  246. // Lex source text.
  247. PP.EnterMainSourceFile();
  248. while (true) {
  249. Token Tok;
  250. PP.Lex(Tok);
  251. if (Tok.is(tok::eof))
  252. break;
  253. }
  254. PragmaOpenCLExtensionCallbacks::CallbackParameters RetVal = {
  255. Callbacks->Name,
  256. Callbacks->State
  257. };
  258. return RetVal;
  259. }
  260. };
  261. TEST_F(PPCallbacksTest, UserFileCharacteristics) {
  262. const char *Source = "#include \"quoted.h\"\n";
  263. SrcMgr::CharacteristicKind Kind =
  264. InclusionDirectiveCharacteristicKind(Source, "/quoted.h", false);
  265. ASSERT_EQ(SrcMgr::CharacteristicKind::C_User, Kind);
  266. }
  267. TEST_F(PPCallbacksTest, QuotedFilename) {
  268. const char* Source =
  269. "#include \"quoted.h\"\n";
  270. CharSourceRange Range =
  271. InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
  272. ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
  273. }
  274. TEST_F(PPCallbacksTest, AngledFilename) {
  275. const char* Source =
  276. "#include <angled.h>\n";
  277. CharSourceRange Range =
  278. InclusionDirectiveFilenameRange(Source, "/angled.h", true);
  279. ASSERT_EQ("<angled.h>", GetSourceString(Range));
  280. }
  281. TEST_F(PPCallbacksTest, QuotedInMacro) {
  282. const char* Source =
  283. "#define MACRO_QUOTED \"quoted.h\"\n"
  284. "#include MACRO_QUOTED\n";
  285. CharSourceRange Range =
  286. InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
  287. ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
  288. }
  289. TEST_F(PPCallbacksTest, AngledInMacro) {
  290. const char* Source =
  291. "#define MACRO_ANGLED <angled.h>\n"
  292. "#include MACRO_ANGLED\n";
  293. CharSourceRange Range =
  294. InclusionDirectiveFilenameRange(Source, "/angled.h", true);
  295. ASSERT_EQ("<angled.h>", GetSourceString(Range));
  296. }
  297. TEST_F(PPCallbacksTest, StringizedMacroArgument) {
  298. const char* Source =
  299. "#define MACRO_STRINGIZED(x) #x\n"
  300. "#include MACRO_STRINGIZED(quoted.h)\n";
  301. CharSourceRange Range =
  302. InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
  303. ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
  304. }
  305. TEST_F(PPCallbacksTest, ConcatenatedMacroArgument) {
  306. const char* Source =
  307. "#define MACRO_ANGLED <angled.h>\n"
  308. "#define MACRO_CONCAT(x, y) x ## _ ## y\n"
  309. "#include MACRO_CONCAT(MACRO, ANGLED)\n";
  310. CharSourceRange Range =
  311. InclusionDirectiveFilenameRange(Source, "/angled.h", false);
  312. ASSERT_EQ("<angled.h>", GetSourceString(Range));
  313. }
  314. TEST_F(PPCallbacksTest, TrigraphFilename) {
  315. const char* Source =
  316. "#include \"tri\?\?-graph.h\"\n";
  317. CharSourceRange Range =
  318. InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
  319. ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
  320. }
  321. TEST_F(PPCallbacksTest, TrigraphInMacro) {
  322. const char* Source =
  323. "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n"
  324. "#include MACRO_TRIGRAPH\n";
  325. CharSourceRange Range =
  326. InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
  327. ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
  328. }
  329. TEST_F(PPCallbacksTest, OpenCLExtensionPragmaEnabled) {
  330. const char* Source =
  331. "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n";
  332. PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters =
  333. PragmaOpenCLExtensionCall(Source);
  334. ASSERT_EQ("cl_khr_fp64", Parameters.Name);
  335. unsigned ExpectedState = 1;
  336. ASSERT_EQ(ExpectedState, Parameters.State);
  337. }
  338. TEST_F(PPCallbacksTest, OpenCLExtensionPragmaDisabled) {
  339. const char* Source =
  340. "#pragma OPENCL EXTENSION cl_khr_fp16 : disable\n";
  341. PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters =
  342. PragmaOpenCLExtensionCall(Source);
  343. ASSERT_EQ("cl_khr_fp16", Parameters.Name);
  344. unsigned ExpectedState = 0;
  345. ASSERT_EQ(ExpectedState, Parameters.State);
  346. }
  347. TEST_F(PPCallbacksTest, DirectiveExprRanges) {
  348. const auto &Results1 = DirectiveExprRange("#if FLUZZY_FLOOF\n#endif\n");
  349. EXPECT_EQ(Results1.size(), 1U);
  350. EXPECT_EQ(
  351. GetSourceStringToEnd(CharSourceRange(Results1[0].ConditionRange, false)),
  352. "FLUZZY_FLOOF");
  353. const auto &Results2 = DirectiveExprRange("#if 1 + 4 < 7\n#endif\n");
  354. EXPECT_EQ(Results2.size(), 1U);
  355. EXPECT_EQ(
  356. GetSourceStringToEnd(CharSourceRange(Results2[0].ConditionRange, false)),
  357. "1 + 4 < 7");
  358. const auto &Results3 = DirectiveExprRange("#if 1 + \\\n 2\n#endif\n");
  359. EXPECT_EQ(Results3.size(), 1U);
  360. EXPECT_EQ(
  361. GetSourceStringToEnd(CharSourceRange(Results3[0].ConditionRange, false)),
  362. "1 + \\\n 2");
  363. const auto &Results4 = DirectiveExprRange("#if 0\n#elif FLOOFY\n#endif\n");
  364. EXPECT_EQ(Results4.size(), 2U);
  365. EXPECT_EQ(
  366. GetSourceStringToEnd(CharSourceRange(Results4[0].ConditionRange, false)),
  367. "0");
  368. EXPECT_EQ(
  369. GetSourceStringToEnd(CharSourceRange(Results4[1].ConditionRange, false)),
  370. "FLOOFY");
  371. const auto &Results5 = DirectiveExprRange("#if 1\n#elif FLOOFY\n#endif\n");
  372. EXPECT_EQ(Results5.size(), 2U);
  373. EXPECT_EQ(
  374. GetSourceStringToEnd(CharSourceRange(Results5[0].ConditionRange, false)),
  375. "1");
  376. EXPECT_EQ(
  377. GetSourceStringToEnd(CharSourceRange(Results5[1].ConditionRange, false)),
  378. "FLOOFY");
  379. const auto &Results6 =
  380. DirectiveExprRange("#if defined(FLUZZY_FLOOF)\n#endif\n");
  381. EXPECT_EQ(Results6.size(), 1U);
  382. EXPECT_EQ(
  383. GetSourceStringToEnd(CharSourceRange(Results6[0].ConditionRange, false)),
  384. "defined(FLUZZY_FLOOF)");
  385. const auto &Results7 =
  386. DirectiveExprRange("#if 1\n#elif defined(FLOOFY)\n#endif\n");
  387. EXPECT_EQ(Results7.size(), 2U);
  388. EXPECT_EQ(
  389. GetSourceStringToEnd(CharSourceRange(Results7[0].ConditionRange, false)),
  390. "1");
  391. EXPECT_EQ(
  392. GetSourceStringToEnd(CharSourceRange(Results7[1].ConditionRange, false)),
  393. "defined(FLOOFY)");
  394. const auto &Results8 =
  395. DirectiveExprRange("#define FLOOFY 0\n#if __FILE__ > FLOOFY\n#endif\n");
  396. EXPECT_EQ(Results8.size(), 1U);
  397. EXPECT_EQ(
  398. GetSourceStringToEnd(CharSourceRange(Results8[0].ConditionRange, false)),
  399. "__FILE__ > FLOOFY");
  400. EXPECT_EQ(
  401. Lexer::getSourceText(CharSourceRange(Results8[0].ConditionRange, false),
  402. SourceMgr, LangOpts),
  403. "__FILE__ > FLOOFY");
  404. }
  405. } // namespace