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