InclusionRewriter.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. //===--- InclusionRewriter.cpp - Rewrite includes into their expansions ---===//
  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. //
  9. // This code rewrites include invocations into their expansions. This gives you
  10. // a file with all included files merged into it.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "clang/Rewrite/Frontend/Rewriters.h"
  14. #include "clang/Basic/SourceManager.h"
  15. #include "clang/Frontend/PreprocessorOutputOptions.h"
  16. #include "clang/Lex/HeaderSearch.h"
  17. #include "clang/Lex/Pragma.h"
  18. #include "clang/Lex/Preprocessor.h"
  19. #include "llvm/ADT/SmallString.h"
  20. #include "llvm/Support/raw_ostream.h"
  21. using namespace clang;
  22. using namespace llvm;
  23. namespace {
  24. class InclusionRewriter : public PPCallbacks {
  25. /// Information about which #includes were actually performed,
  26. /// created by preprocessor callbacks.
  27. struct IncludedFile {
  28. FileID Id;
  29. SrcMgr::CharacteristicKind FileType;
  30. const DirectoryLookup *DirLookup;
  31. IncludedFile(FileID Id, SrcMgr::CharacteristicKind FileType,
  32. const DirectoryLookup *DirLookup)
  33. : Id(Id), FileType(FileType), DirLookup(DirLookup) {}
  34. };
  35. Preprocessor &PP; ///< Used to find inclusion directives.
  36. SourceManager &SM; ///< Used to read and manage source files.
  37. raw_ostream &OS; ///< The destination stream for rewritten contents.
  38. StringRef MainEOL; ///< The line ending marker to use.
  39. const llvm::MemoryBuffer *PredefinesBuffer; ///< The preprocessor predefines.
  40. bool ShowLineMarkers; ///< Show #line markers.
  41. bool UseLineDirectives; ///< Use of line directives or line markers.
  42. /// Tracks where inclusions that change the file are found.
  43. std::map<unsigned, IncludedFile> FileIncludes;
  44. /// Tracks where inclusions that import modules are found.
  45. std::map<unsigned, const Module *> ModuleIncludes;
  46. /// Tracks where inclusions that enter modules (in a module build) are found.
  47. std::map<unsigned, const Module *> ModuleEntryIncludes;
  48. /// Tracks where #if and #elif directives get evaluated and whether to true.
  49. std::map<unsigned, bool> IfConditions;
  50. /// Used transitively for building up the FileIncludes mapping over the
  51. /// various \c PPCallbacks callbacks.
  52. SourceLocation LastInclusionLocation;
  53. public:
  54. InclusionRewriter(Preprocessor &PP, raw_ostream &OS, bool ShowLineMarkers,
  55. bool UseLineDirectives);
  56. void Process(FileID FileId, SrcMgr::CharacteristicKind FileType,
  57. const DirectoryLookup *DirLookup);
  58. void setPredefinesBuffer(const llvm::MemoryBuffer *Buf) {
  59. PredefinesBuffer = Buf;
  60. }
  61. void detectMainFileEOL();
  62. void handleModuleBegin(Token &Tok) {
  63. assert(Tok.getKind() == tok::annot_module_begin);
  64. ModuleEntryIncludes.insert({Tok.getLocation().getRawEncoding(),
  65. (Module *)Tok.getAnnotationValue()});
  66. }
  67. private:
  68. void FileChanged(SourceLocation Loc, FileChangeReason Reason,
  69. SrcMgr::CharacteristicKind FileType,
  70. FileID PrevFID) override;
  71. void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
  72. SrcMgr::CharacteristicKind FileType) override;
  73. void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
  74. StringRef FileName, bool IsAngled,
  75. CharSourceRange FilenameRange, const FileEntry *File,
  76. StringRef SearchPath, StringRef RelativePath,
  77. const Module *Imported,
  78. SrcMgr::CharacteristicKind FileType) override;
  79. void If(SourceLocation Loc, SourceRange ConditionRange,
  80. ConditionValueKind ConditionValue) override;
  81. void Elif(SourceLocation Loc, SourceRange ConditionRange,
  82. ConditionValueKind ConditionValue, SourceLocation IfLoc) override;
  83. void WriteLineInfo(StringRef Filename, int Line,
  84. SrcMgr::CharacteristicKind FileType,
  85. StringRef Extra = StringRef());
  86. void WriteImplicitModuleImport(const Module *Mod);
  87. void OutputContentUpTo(const MemoryBuffer &FromFile,
  88. unsigned &WriteFrom, unsigned WriteTo,
  89. StringRef EOL, int &lines,
  90. bool EnsureNewline);
  91. void CommentOutDirective(Lexer &DirectivesLex, const Token &StartToken,
  92. const MemoryBuffer &FromFile, StringRef EOL,
  93. unsigned &NextToWrite, int &Lines);
  94. const IncludedFile *FindIncludeAtLocation(SourceLocation Loc) const;
  95. const Module *FindModuleAtLocation(SourceLocation Loc) const;
  96. const Module *FindEnteredModule(SourceLocation Loc) const;
  97. bool IsIfAtLocationTrue(SourceLocation Loc) const;
  98. StringRef NextIdentifierName(Lexer &RawLex, Token &RawToken);
  99. };
  100. } // end anonymous namespace
  101. /// Initializes an InclusionRewriter with a \p PP source and \p OS destination.
  102. InclusionRewriter::InclusionRewriter(Preprocessor &PP, raw_ostream &OS,
  103. bool ShowLineMarkers,
  104. bool UseLineDirectives)
  105. : PP(PP), SM(PP.getSourceManager()), OS(OS), MainEOL("\n"),
  106. PredefinesBuffer(nullptr), ShowLineMarkers(ShowLineMarkers),
  107. UseLineDirectives(UseLineDirectives),
  108. LastInclusionLocation(SourceLocation()) {}
  109. /// Write appropriate line information as either #line directives or GNU line
  110. /// markers depending on what mode we're in, including the \p Filename and
  111. /// \p Line we are located at, using the specified \p EOL line separator, and
  112. /// any \p Extra context specifiers in GNU line directives.
  113. void InclusionRewriter::WriteLineInfo(StringRef Filename, int Line,
  114. SrcMgr::CharacteristicKind FileType,
  115. StringRef Extra) {
  116. if (!ShowLineMarkers)
  117. return;
  118. if (UseLineDirectives) {
  119. OS << "#line" << ' ' << Line << ' ' << '"';
  120. OS.write_escaped(Filename);
  121. OS << '"';
  122. } else {
  123. // Use GNU linemarkers as described here:
  124. // http://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html
  125. OS << '#' << ' ' << Line << ' ' << '"';
  126. OS.write_escaped(Filename);
  127. OS << '"';
  128. if (!Extra.empty())
  129. OS << Extra;
  130. if (FileType == SrcMgr::C_System)
  131. // "`3' This indicates that the following text comes from a system header
  132. // file, so certain warnings should be suppressed."
  133. OS << " 3";
  134. else if (FileType == SrcMgr::C_ExternCSystem)
  135. // as above for `3', plus "`4' This indicates that the following text
  136. // should be treated as being wrapped in an implicit extern "C" block."
  137. OS << " 3 4";
  138. }
  139. OS << MainEOL;
  140. }
  141. void InclusionRewriter::WriteImplicitModuleImport(const Module *Mod) {
  142. OS << "#pragma clang module import " << Mod->getFullModuleName(true)
  143. << " /* clang -frewrite-includes: implicit import */" << MainEOL;
  144. }
  145. /// FileChanged - Whenever the preprocessor enters or exits a #include file
  146. /// it invokes this handler.
  147. void InclusionRewriter::FileChanged(SourceLocation Loc,
  148. FileChangeReason Reason,
  149. SrcMgr::CharacteristicKind NewFileType,
  150. FileID) {
  151. if (Reason != EnterFile)
  152. return;
  153. if (LastInclusionLocation.isInvalid())
  154. // we didn't reach this file (eg: the main file) via an inclusion directive
  155. return;
  156. FileID Id = FullSourceLoc(Loc, SM).getFileID();
  157. auto P = FileIncludes.insert(
  158. std::make_pair(LastInclusionLocation.getRawEncoding(),
  159. IncludedFile(Id, NewFileType, PP.GetCurDirLookup())));
  160. (void)P;
  161. assert(P.second && "Unexpected revisitation of the same include directive");
  162. LastInclusionLocation = SourceLocation();
  163. }
  164. /// Called whenever an inclusion is skipped due to canonical header protection
  165. /// macros.
  166. void InclusionRewriter::FileSkipped(const FileEntryRef & /*SkippedFile*/,
  167. const Token & /*FilenameTok*/,
  168. SrcMgr::CharacteristicKind /*FileType*/) {
  169. assert(LastInclusionLocation.isValid() &&
  170. "A file, that wasn't found via an inclusion directive, was skipped");
  171. LastInclusionLocation = SourceLocation();
  172. }
  173. /// This should be called whenever the preprocessor encounters include
  174. /// directives. It does not say whether the file has been included, but it
  175. /// provides more information about the directive (hash location instead
  176. /// of location inside the included file). It is assumed that the matching
  177. /// FileChanged() or FileSkipped() is called after this (or neither is
  178. /// called if this #include results in an error or does not textually include
  179. /// anything).
  180. void InclusionRewriter::InclusionDirective(SourceLocation HashLoc,
  181. const Token &/*IncludeTok*/,
  182. StringRef /*FileName*/,
  183. bool /*IsAngled*/,
  184. CharSourceRange /*FilenameRange*/,
  185. const FileEntry * /*File*/,
  186. StringRef /*SearchPath*/,
  187. StringRef /*RelativePath*/,
  188. const Module *Imported,
  189. SrcMgr::CharacteristicKind FileType){
  190. if (Imported) {
  191. auto P = ModuleIncludes.insert(
  192. std::make_pair(HashLoc.getRawEncoding(), Imported));
  193. (void)P;
  194. assert(P.second && "Unexpected revisitation of the same include directive");
  195. } else
  196. LastInclusionLocation = HashLoc;
  197. }
  198. void InclusionRewriter::If(SourceLocation Loc, SourceRange ConditionRange,
  199. ConditionValueKind ConditionValue) {
  200. auto P = IfConditions.insert(
  201. std::make_pair(Loc.getRawEncoding(), ConditionValue == CVK_True));
  202. (void)P;
  203. assert(P.second && "Unexpected revisitation of the same if directive");
  204. }
  205. void InclusionRewriter::Elif(SourceLocation Loc, SourceRange ConditionRange,
  206. ConditionValueKind ConditionValue,
  207. SourceLocation IfLoc) {
  208. auto P = IfConditions.insert(
  209. std::make_pair(Loc.getRawEncoding(), ConditionValue == CVK_True));
  210. (void)P;
  211. assert(P.second && "Unexpected revisitation of the same elif directive");
  212. }
  213. /// Simple lookup for a SourceLocation (specifically one denoting the hash in
  214. /// an inclusion directive) in the map of inclusion information, FileChanges.
  215. const InclusionRewriter::IncludedFile *
  216. InclusionRewriter::FindIncludeAtLocation(SourceLocation Loc) const {
  217. const auto I = FileIncludes.find(Loc.getRawEncoding());
  218. if (I != FileIncludes.end())
  219. return &I->second;
  220. return nullptr;
  221. }
  222. /// Simple lookup for a SourceLocation (specifically one denoting the hash in
  223. /// an inclusion directive) in the map of module inclusion information.
  224. const Module *
  225. InclusionRewriter::FindModuleAtLocation(SourceLocation Loc) const {
  226. const auto I = ModuleIncludes.find(Loc.getRawEncoding());
  227. if (I != ModuleIncludes.end())
  228. return I->second;
  229. return nullptr;
  230. }
  231. /// Simple lookup for a SourceLocation (specifically one denoting the hash in
  232. /// an inclusion directive) in the map of module entry information.
  233. const Module *
  234. InclusionRewriter::FindEnteredModule(SourceLocation Loc) const {
  235. const auto I = ModuleEntryIncludes.find(Loc.getRawEncoding());
  236. if (I != ModuleEntryIncludes.end())
  237. return I->second;
  238. return nullptr;
  239. }
  240. bool InclusionRewriter::IsIfAtLocationTrue(SourceLocation Loc) const {
  241. const auto I = IfConditions.find(Loc.getRawEncoding());
  242. if (I != IfConditions.end())
  243. return I->second;
  244. return false;
  245. }
  246. /// Detect the likely line ending style of \p FromFile by examining the first
  247. /// newline found within it.
  248. static StringRef DetectEOL(const MemoryBuffer &FromFile) {
  249. // Detect what line endings the file uses, so that added content does not mix
  250. // the style. We need to check for "\r\n" first because "\n\r" will match
  251. // "\r\n\r\n".
  252. const char *Pos = strchr(FromFile.getBufferStart(), '\n');
  253. if (!Pos)
  254. return "\n";
  255. if (Pos - 1 >= FromFile.getBufferStart() && Pos[-1] == '\r')
  256. return "\r\n";
  257. if (Pos + 1 < FromFile.getBufferEnd() && Pos[1] == '\r')
  258. return "\n\r";
  259. return "\n";
  260. }
  261. void InclusionRewriter::detectMainFileEOL() {
  262. bool Invalid;
  263. const MemoryBuffer &FromFile = *SM.getBuffer(SM.getMainFileID(), &Invalid);
  264. assert(!Invalid);
  265. if (Invalid)
  266. return; // Should never happen, but whatever.
  267. MainEOL = DetectEOL(FromFile);
  268. }
  269. /// Writes out bytes from \p FromFile, starting at \p NextToWrite and ending at
  270. /// \p WriteTo - 1.
  271. void InclusionRewriter::OutputContentUpTo(const MemoryBuffer &FromFile,
  272. unsigned &WriteFrom, unsigned WriteTo,
  273. StringRef LocalEOL, int &Line,
  274. bool EnsureNewline) {
  275. if (WriteTo <= WriteFrom)
  276. return;
  277. if (&FromFile == PredefinesBuffer) {
  278. // Ignore the #defines of the predefines buffer.
  279. WriteFrom = WriteTo;
  280. return;
  281. }
  282. // If we would output half of a line ending, advance one character to output
  283. // the whole line ending. All buffers are null terminated, so looking ahead
  284. // one byte is safe.
  285. if (LocalEOL.size() == 2 &&
  286. LocalEOL[0] == (FromFile.getBufferStart() + WriteTo)[-1] &&
  287. LocalEOL[1] == (FromFile.getBufferStart() + WriteTo)[0])
  288. WriteTo++;
  289. StringRef TextToWrite(FromFile.getBufferStart() + WriteFrom,
  290. WriteTo - WriteFrom);
  291. if (MainEOL == LocalEOL) {
  292. OS << TextToWrite;
  293. // count lines manually, it's faster than getPresumedLoc()
  294. Line += TextToWrite.count(LocalEOL);
  295. if (EnsureNewline && !TextToWrite.endswith(LocalEOL))
  296. OS << MainEOL;
  297. } else {
  298. // Output the file one line at a time, rewriting the line endings as we go.
  299. StringRef Rest = TextToWrite;
  300. while (!Rest.empty()) {
  301. StringRef LineText;
  302. std::tie(LineText, Rest) = Rest.split(LocalEOL);
  303. OS << LineText;
  304. Line++;
  305. if (!Rest.empty())
  306. OS << MainEOL;
  307. }
  308. if (TextToWrite.endswith(LocalEOL) || EnsureNewline)
  309. OS << MainEOL;
  310. }
  311. WriteFrom = WriteTo;
  312. }
  313. /// Print characters from \p FromFile starting at \p NextToWrite up until the
  314. /// inclusion directive at \p StartToken, then print out the inclusion
  315. /// inclusion directive disabled by a #if directive, updating \p NextToWrite
  316. /// and \p Line to track the number of source lines visited and the progress
  317. /// through the \p FromFile buffer.
  318. void InclusionRewriter::CommentOutDirective(Lexer &DirectiveLex,
  319. const Token &StartToken,
  320. const MemoryBuffer &FromFile,
  321. StringRef LocalEOL,
  322. unsigned &NextToWrite, int &Line) {
  323. OutputContentUpTo(FromFile, NextToWrite,
  324. SM.getFileOffset(StartToken.getLocation()), LocalEOL, Line,
  325. false);
  326. Token DirectiveToken;
  327. do {
  328. DirectiveLex.LexFromRawLexer(DirectiveToken);
  329. } while (!DirectiveToken.is(tok::eod) && DirectiveToken.isNot(tok::eof));
  330. if (&FromFile == PredefinesBuffer) {
  331. // OutputContentUpTo() would not output anything anyway.
  332. return;
  333. }
  334. OS << "#if 0 /* expanded by -frewrite-includes */" << MainEOL;
  335. OutputContentUpTo(FromFile, NextToWrite,
  336. SM.getFileOffset(DirectiveToken.getLocation()) +
  337. DirectiveToken.getLength(),
  338. LocalEOL, Line, true);
  339. OS << "#endif /* expanded by -frewrite-includes */" << MainEOL;
  340. }
  341. /// Find the next identifier in the pragma directive specified by \p RawToken.
  342. StringRef InclusionRewriter::NextIdentifierName(Lexer &RawLex,
  343. Token &RawToken) {
  344. RawLex.LexFromRawLexer(RawToken);
  345. if (RawToken.is(tok::raw_identifier))
  346. PP.LookUpIdentifierInfo(RawToken);
  347. if (RawToken.is(tok::identifier))
  348. return RawToken.getIdentifierInfo()->getName();
  349. return StringRef();
  350. }
  351. /// Use a raw lexer to analyze \p FileId, incrementally copying parts of it
  352. /// and including content of included files recursively.
  353. void InclusionRewriter::Process(FileID FileId,
  354. SrcMgr::CharacteristicKind FileType,
  355. const DirectoryLookup *DirLookup) {
  356. bool Invalid;
  357. const MemoryBuffer &FromFile = *SM.getBuffer(FileId, &Invalid);
  358. assert(!Invalid && "Attempting to process invalid inclusion");
  359. StringRef FileName = FromFile.getBufferIdentifier();
  360. Lexer RawLex(FileId, &FromFile, PP.getSourceManager(), PP.getLangOpts());
  361. RawLex.SetCommentRetentionState(false);
  362. StringRef LocalEOL = DetectEOL(FromFile);
  363. // Per the GNU docs: "1" indicates entering a new file.
  364. if (FileId == SM.getMainFileID() || FileId == PP.getPredefinesFileID())
  365. WriteLineInfo(FileName, 1, FileType, "");
  366. else
  367. WriteLineInfo(FileName, 1, FileType, " 1");
  368. if (SM.getFileIDSize(FileId) == 0)
  369. return;
  370. // The next byte to be copied from the source file, which may be non-zero if
  371. // the lexer handled a BOM.
  372. unsigned NextToWrite = SM.getFileOffset(RawLex.getSourceLocation());
  373. assert(SM.getLineNumber(FileId, NextToWrite) == 1);
  374. int Line = 1; // The current input file line number.
  375. Token RawToken;
  376. RawLex.LexFromRawLexer(RawToken);
  377. // TODO: Consider adding a switch that strips possibly unimportant content,
  378. // such as comments, to reduce the size of repro files.
  379. while (RawToken.isNot(tok::eof)) {
  380. if (RawToken.is(tok::hash) && RawToken.isAtStartOfLine()) {
  381. RawLex.setParsingPreprocessorDirective(true);
  382. Token HashToken = RawToken;
  383. RawLex.LexFromRawLexer(RawToken);
  384. if (RawToken.is(tok::raw_identifier))
  385. PP.LookUpIdentifierInfo(RawToken);
  386. if (RawToken.getIdentifierInfo() != nullptr) {
  387. switch (RawToken.getIdentifierInfo()->getPPKeywordID()) {
  388. case tok::pp_include:
  389. case tok::pp_include_next:
  390. case tok::pp_import: {
  391. CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL, NextToWrite,
  392. Line);
  393. if (FileId != PP.getPredefinesFileID())
  394. WriteLineInfo(FileName, Line - 1, FileType, "");
  395. StringRef LineInfoExtra;
  396. SourceLocation Loc = HashToken.getLocation();
  397. if (const Module *Mod = FindModuleAtLocation(Loc))
  398. WriteImplicitModuleImport(Mod);
  399. else if (const IncludedFile *Inc = FindIncludeAtLocation(Loc)) {
  400. const Module *Mod = FindEnteredModule(Loc);
  401. if (Mod)
  402. OS << "#pragma clang module begin "
  403. << Mod->getFullModuleName(true) << "\n";
  404. // Include and recursively process the file.
  405. Process(Inc->Id, Inc->FileType, Inc->DirLookup);
  406. if (Mod)
  407. OS << "#pragma clang module end /*"
  408. << Mod->getFullModuleName(true) << "*/\n";
  409. // Add line marker to indicate we're returning from an included
  410. // file.
  411. LineInfoExtra = " 2";
  412. }
  413. // fix up lineinfo (since commented out directive changed line
  414. // numbers) for inclusions that were skipped due to header guards
  415. WriteLineInfo(FileName, Line, FileType, LineInfoExtra);
  416. break;
  417. }
  418. case tok::pp_pragma: {
  419. StringRef Identifier = NextIdentifierName(RawLex, RawToken);
  420. if (Identifier == "clang" || Identifier == "GCC") {
  421. if (NextIdentifierName(RawLex, RawToken) == "system_header") {
  422. // keep the directive in, commented out
  423. CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL,
  424. NextToWrite, Line);
  425. // update our own type
  426. FileType = SM.getFileCharacteristic(RawToken.getLocation());
  427. WriteLineInfo(FileName, Line, FileType);
  428. }
  429. } else if (Identifier == "once") {
  430. // keep the directive in, commented out
  431. CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL,
  432. NextToWrite, Line);
  433. WriteLineInfo(FileName, Line, FileType);
  434. }
  435. break;
  436. }
  437. case tok::pp_if:
  438. case tok::pp_elif: {
  439. bool elif = (RawToken.getIdentifierInfo()->getPPKeywordID() ==
  440. tok::pp_elif);
  441. bool isTrue = IsIfAtLocationTrue(RawToken.getLocation());
  442. OutputContentUpTo(FromFile, NextToWrite,
  443. SM.getFileOffset(HashToken.getLocation()),
  444. LocalEOL, Line, /*EnsureNewline=*/true);
  445. do {
  446. RawLex.LexFromRawLexer(RawToken);
  447. } while (!RawToken.is(tok::eod) && RawToken.isNot(tok::eof));
  448. // We need to disable the old condition, but that is tricky.
  449. // Trying to comment it out can easily lead to comment nesting.
  450. // So instead make the condition harmless by making it enclose
  451. // and empty block. Moreover, put it itself inside an #if 0 block
  452. // to disable it from getting evaluated (e.g. __has_include_next
  453. // warns if used from the primary source file).
  454. OS << "#if 0 /* disabled by -frewrite-includes */" << MainEOL;
  455. if (elif) {
  456. OS << "#if 0" << MainEOL;
  457. }
  458. OutputContentUpTo(FromFile, NextToWrite,
  459. SM.getFileOffset(RawToken.getLocation()) +
  460. RawToken.getLength(),
  461. LocalEOL, Line, /*EnsureNewline=*/true);
  462. // Close the empty block and the disabling block.
  463. OS << "#endif" << MainEOL;
  464. OS << "#endif /* disabled by -frewrite-includes */" << MainEOL;
  465. OS << (elif ? "#elif " : "#if ") << (isTrue ? "1" : "0")
  466. << " /* evaluated by -frewrite-includes */" << MainEOL;
  467. WriteLineInfo(FileName, Line, FileType);
  468. break;
  469. }
  470. case tok::pp_endif:
  471. case tok::pp_else: {
  472. // We surround every #include by #if 0 to comment it out, but that
  473. // changes line numbers. These are fixed up right after that, but
  474. // the whole #include could be inside a preprocessor conditional
  475. // that is not processed. So it is necessary to fix the line
  476. // numbers one the next line after each #else/#endif as well.
  477. RawLex.SetKeepWhitespaceMode(true);
  478. do {
  479. RawLex.LexFromRawLexer(RawToken);
  480. } while (RawToken.isNot(tok::eod) && RawToken.isNot(tok::eof));
  481. OutputContentUpTo(FromFile, NextToWrite,
  482. SM.getFileOffset(RawToken.getLocation()) +
  483. RawToken.getLength(),
  484. LocalEOL, Line, /*EnsureNewline=*/ true);
  485. WriteLineInfo(FileName, Line, FileType);
  486. RawLex.SetKeepWhitespaceMode(false);
  487. break;
  488. }
  489. default:
  490. break;
  491. }
  492. }
  493. RawLex.setParsingPreprocessorDirective(false);
  494. }
  495. RawLex.LexFromRawLexer(RawToken);
  496. }
  497. OutputContentUpTo(FromFile, NextToWrite,
  498. SM.getFileOffset(SM.getLocForEndOfFile(FileId)), LocalEOL,
  499. Line, /*EnsureNewline=*/true);
  500. }
  501. /// InclusionRewriterInInput - Implement -frewrite-includes mode.
  502. void clang::RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS,
  503. const PreprocessorOutputOptions &Opts) {
  504. SourceManager &SM = PP.getSourceManager();
  505. InclusionRewriter *Rewrite = new InclusionRewriter(
  506. PP, *OS, Opts.ShowLineMarkers, Opts.UseLineDirectives);
  507. Rewrite->detectMainFileEOL();
  508. PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Rewrite));
  509. PP.IgnorePragmas();
  510. // First let the preprocessor process the entire file and call callbacks.
  511. // Callbacks will record which #include's were actually performed.
  512. PP.EnterMainSourceFile();
  513. Token Tok;
  514. // Only preprocessor directives matter here, so disable macro expansion
  515. // everywhere else as an optimization.
  516. // TODO: It would be even faster if the preprocessor could be switched
  517. // to a mode where it would parse only preprocessor directives and comments,
  518. // nothing else matters for parsing or processing.
  519. PP.SetMacroExpansionOnlyInDirectives();
  520. do {
  521. PP.Lex(Tok);
  522. if (Tok.is(tok::annot_module_begin))
  523. Rewrite->handleModuleBegin(Tok);
  524. } while (Tok.isNot(tok::eof));
  525. Rewrite->setPredefinesBuffer(SM.getBuffer(PP.getPredefinesFileID()));
  526. Rewrite->Process(PP.getPredefinesFileID(), SrcMgr::C_User, nullptr);
  527. Rewrite->Process(SM.getMainFileID(), SrcMgr::C_User, nullptr);
  528. OS->flush();
  529. }