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