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