• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
IncludedFile__anonbdf3a7190111::InclusionRewriter::IncludedFile35     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   llvm::MemoryBufferRef 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<SourceLocation, IncludedFile> FileIncludes;
48   /// Tracks where inclusions that import modules are found.
49   std::map<SourceLocation, const Module *> ModuleIncludes;
50   /// Tracks where inclusions that enter modules (in a module build) are found.
51   std::map<SourceLocation, const Module *> ModuleEntryIncludes;
52   /// Tracks where #if and #elif directives get evaluated and whether to true.
53   std::map<SourceLocation, 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);
setPredefinesBuffer(const llvm::MemoryBufferRef & Buf)62   void setPredefinesBuffer(const llvm::MemoryBufferRef &Buf) {
63     PredefinesBuffer = Buf;
64   }
65   void detectMainFileEOL();
handleModuleBegin(Token & Tok)66   void handleModuleBegin(Token &Tok) {
67     assert(Tok.getKind() == tok::annot_module_begin);
68     ModuleEntryIncludes.insert(
69         {Tok.getLocation(), (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 MemoryBufferRef &FromFile, unsigned &WriteFrom,
92                          unsigned WriteTo, StringRef EOL, int &lines,
93                          bool EnsureNewline);
94   void CommentOutDirective(Lexer &DirectivesLex, const Token &StartToken,
95                            const MemoryBufferRef &FromFile, StringRef EOL,
96                            unsigned &NextToWrite, int &Lines);
97   const IncludedFile *FindIncludeAtLocation(SourceLocation Loc) 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,
166                      IncludedFile(Id, NewFileType, PP.GetCurDirLookup())));
167   (void)P;
168   assert(P.second && "Unexpected revisitation of the same include directive");
169   LastInclusionLocation = SourceLocation();
170 }
171 
172 /// Called whenever an inclusion is skipped due to canonical header protection
173 /// macros.
FileSkipped(const FileEntryRef &,const Token &,SrcMgr::CharacteristicKind)174 void InclusionRewriter::FileSkipped(const FileEntryRef & /*SkippedFile*/,
175                                     const Token & /*FilenameTok*/,
176                                     SrcMgr::CharacteristicKind /*FileType*/) {
177   assert(LastInclusionLocation.isValid() &&
178          "A file, that wasn't found via an inclusion directive, was skipped");
179   LastInclusionLocation = SourceLocation();
180 }
181 
182 /// This should be called whenever the preprocessor encounters include
183 /// directives. It does not say whether the file has been included, but it
184 /// provides more information about the directive (hash location instead
185 /// of location inside the included file). It is assumed that the matching
186 /// FileChanged() or FileSkipped() is called after this (or neither is
187 /// called if this #include results in an error or does not textually include
188 /// anything).
InclusionDirective(SourceLocation HashLoc,const Token &,StringRef,bool,CharSourceRange,const FileEntry *,StringRef,StringRef,const Module * Imported,SrcMgr::CharacteristicKind FileType)189 void InclusionRewriter::InclusionDirective(SourceLocation HashLoc,
190                                            const Token &/*IncludeTok*/,
191                                            StringRef /*FileName*/,
192                                            bool /*IsAngled*/,
193                                            CharSourceRange /*FilenameRange*/,
194                                            const FileEntry * /*File*/,
195                                            StringRef /*SearchPath*/,
196                                            StringRef /*RelativePath*/,
197                                            const Module *Imported,
198                                            SrcMgr::CharacteristicKind FileType){
199   if (Imported) {
200     auto P = ModuleIncludes.insert(std::make_pair(HashLoc, Imported));
201     (void)P;
202     assert(P.second && "Unexpected revisitation of the same include directive");
203   } else
204     LastInclusionLocation = HashLoc;
205 }
206 
If(SourceLocation Loc,SourceRange ConditionRange,ConditionValueKind ConditionValue)207 void InclusionRewriter::If(SourceLocation Loc, SourceRange ConditionRange,
208                            ConditionValueKind ConditionValue) {
209   auto P = IfConditions.insert(std::make_pair(Loc, ConditionValue == CVK_True));
210   (void)P;
211   assert(P.second && "Unexpected revisitation of the same if directive");
212 }
213 
Elif(SourceLocation Loc,SourceRange ConditionRange,ConditionValueKind ConditionValue,SourceLocation IfLoc)214 void InclusionRewriter::Elif(SourceLocation Loc, SourceRange ConditionRange,
215                              ConditionValueKind ConditionValue,
216                              SourceLocation IfLoc) {
217   auto P = IfConditions.insert(std::make_pair(Loc, ConditionValue == CVK_True));
218   (void)P;
219   assert(P.second && "Unexpected revisitation of the same elif directive");
220 }
221 
222 /// Simple lookup for a SourceLocation (specifically one denoting the hash in
223 /// an inclusion directive) in the map of inclusion information, FileChanges.
224 const InclusionRewriter::IncludedFile *
FindIncludeAtLocation(SourceLocation Loc) const225 InclusionRewriter::FindIncludeAtLocation(SourceLocation Loc) const {
226   const auto I = FileIncludes.find(Loc);
227   if (I != FileIncludes.end())
228     return &I->second;
229   return nullptr;
230 }
231 
232 /// Simple lookup for a SourceLocation (specifically one denoting the hash in
233 /// an inclusion directive) in the map of module inclusion information.
234 const Module *
FindModuleAtLocation(SourceLocation Loc) const235 InclusionRewriter::FindModuleAtLocation(SourceLocation Loc) const {
236   const auto I = ModuleIncludes.find(Loc);
237   if (I != ModuleIncludes.end())
238     return I->second;
239   return nullptr;
240 }
241 
242 /// Simple lookup for a SourceLocation (specifically one denoting the hash in
243 /// an inclusion directive) in the map of module entry information.
244 const Module *
FindEnteredModule(SourceLocation Loc) const245 InclusionRewriter::FindEnteredModule(SourceLocation Loc) const {
246   const auto I = ModuleEntryIncludes.find(Loc);
247   if (I != ModuleEntryIncludes.end())
248     return I->second;
249   return nullptr;
250 }
251 
IsIfAtLocationTrue(SourceLocation Loc) const252 bool InclusionRewriter::IsIfAtLocationTrue(SourceLocation Loc) const {
253   const auto I = IfConditions.find(Loc);
254   if (I != IfConditions.end())
255     return I->second;
256   return false;
257 }
258 
259 /// Detect the likely line ending style of \p FromFile by examining the first
260 /// newline found within it.
DetectEOL(const MemoryBufferRef & FromFile)261 static StringRef DetectEOL(const MemoryBufferRef &FromFile) {
262   // Detect what line endings the file uses, so that added content does not mix
263   // the style. We need to check for "\r\n" first because "\n\r" will match
264   // "\r\n\r\n".
265   const char *Pos = strchr(FromFile.getBufferStart(), '\n');
266   if (!Pos)
267     return "\n";
268   if (Pos - 1 >= FromFile.getBufferStart() && Pos[-1] == '\r')
269     return "\r\n";
270   if (Pos + 1 < FromFile.getBufferEnd() && Pos[1] == '\r')
271     return "\n\r";
272   return "\n";
273 }
274 
detectMainFileEOL()275 void InclusionRewriter::detectMainFileEOL() {
276   Optional<MemoryBufferRef> FromFile = *SM.getBufferOrNone(SM.getMainFileID());
277   assert(FromFile);
278   if (!FromFile)
279     return; // Should never happen, but whatever.
280   MainEOL = DetectEOL(*FromFile);
281 }
282 
283 /// Writes out bytes from \p FromFile, starting at \p NextToWrite and ending at
284 /// \p WriteTo - 1.
OutputContentUpTo(const MemoryBufferRef & FromFile,unsigned & WriteFrom,unsigned WriteTo,StringRef LocalEOL,int & Line,bool EnsureNewline)285 void InclusionRewriter::OutputContentUpTo(const MemoryBufferRef &FromFile,
286                                           unsigned &WriteFrom, unsigned WriteTo,
287                                           StringRef LocalEOL, int &Line,
288                                           bool EnsureNewline) {
289   if (WriteTo <= WriteFrom)
290     return;
291   if (FromFile == PredefinesBuffer) {
292     // Ignore the #defines of the predefines buffer.
293     WriteFrom = WriteTo;
294     return;
295   }
296 
297   // If we would output half of a line ending, advance one character to output
298   // the whole line ending.  All buffers are null terminated, so looking ahead
299   // one byte is safe.
300   if (LocalEOL.size() == 2 &&
301       LocalEOL[0] == (FromFile.getBufferStart() + WriteTo)[-1] &&
302       LocalEOL[1] == (FromFile.getBufferStart() + WriteTo)[0])
303     WriteTo++;
304 
305   StringRef TextToWrite(FromFile.getBufferStart() + WriteFrom,
306                         WriteTo - WriteFrom);
307 
308   if (MainEOL == LocalEOL) {
309     OS << TextToWrite;
310     // count lines manually, it's faster than getPresumedLoc()
311     Line += TextToWrite.count(LocalEOL);
312     if (EnsureNewline && !TextToWrite.endswith(LocalEOL))
313       OS << MainEOL;
314   } else {
315     // Output the file one line at a time, rewriting the line endings as we go.
316     StringRef Rest = TextToWrite;
317     while (!Rest.empty()) {
318       StringRef LineText;
319       std::tie(LineText, Rest) = Rest.split(LocalEOL);
320       OS << LineText;
321       Line++;
322       if (!Rest.empty())
323         OS << MainEOL;
324     }
325     if (TextToWrite.endswith(LocalEOL) || EnsureNewline)
326       OS << MainEOL;
327   }
328   WriteFrom = WriteTo;
329 }
330 
331 /// Print characters from \p FromFile starting at \p NextToWrite up until the
332 /// inclusion directive at \p StartToken, then print out the inclusion
333 /// inclusion directive disabled by a #if directive, updating \p NextToWrite
334 /// and \p Line to track the number of source lines visited and the progress
335 /// through the \p FromFile buffer.
CommentOutDirective(Lexer & DirectiveLex,const Token & StartToken,const MemoryBufferRef & FromFile,StringRef LocalEOL,unsigned & NextToWrite,int & Line)336 void InclusionRewriter::CommentOutDirective(Lexer &DirectiveLex,
337                                             const Token &StartToken,
338                                             const MemoryBufferRef &FromFile,
339                                             StringRef LocalEOL,
340                                             unsigned &NextToWrite, int &Line) {
341   OutputContentUpTo(FromFile, NextToWrite,
342                     SM.getFileOffset(StartToken.getLocation()), LocalEOL, Line,
343                     false);
344   Token DirectiveToken;
345   do {
346     DirectiveLex.LexFromRawLexer(DirectiveToken);
347   } while (!DirectiveToken.is(tok::eod) && DirectiveToken.isNot(tok::eof));
348   if (FromFile == PredefinesBuffer) {
349     // OutputContentUpTo() would not output anything anyway.
350     return;
351   }
352   OS << "#if 0 /* expanded by -frewrite-includes */" << MainEOL;
353   OutputContentUpTo(FromFile, NextToWrite,
354                     SM.getFileOffset(DirectiveToken.getLocation()) +
355                         DirectiveToken.getLength(),
356                     LocalEOL, Line, true);
357   OS << "#endif /* expanded by -frewrite-includes */" << MainEOL;
358 }
359 
360 /// Find the next identifier in the pragma directive specified by \p RawToken.
NextIdentifierName(Lexer & RawLex,Token & RawToken)361 StringRef InclusionRewriter::NextIdentifierName(Lexer &RawLex,
362                                                 Token &RawToken) {
363   RawLex.LexFromRawLexer(RawToken);
364   if (RawToken.is(tok::raw_identifier))
365     PP.LookUpIdentifierInfo(RawToken);
366   if (RawToken.is(tok::identifier))
367     return RawToken.getIdentifierInfo()->getName();
368   return StringRef();
369 }
370 
371 /// Use a raw lexer to analyze \p FileId, incrementally copying parts of it
372 /// and including content of included files recursively.
Process(FileID FileId,SrcMgr::CharacteristicKind FileType,const DirectoryLookup * DirLookup)373 void InclusionRewriter::Process(FileID FileId,
374                                 SrcMgr::CharacteristicKind FileType,
375                                 const DirectoryLookup *DirLookup) {
376   MemoryBufferRef FromFile;
377   {
378     auto B = SM.getBufferOrNone(FileId);
379     assert(B && "Attempting to process invalid inclusion");
380     if (B)
381       FromFile = *B;
382   }
383   StringRef FileName = FromFile.getBufferIdentifier();
384   Lexer RawLex(FileId, FromFile, PP.getSourceManager(), PP.getLangOpts());
385   RawLex.SetCommentRetentionState(false);
386 
387   StringRef LocalEOL = DetectEOL(FromFile);
388 
389   // Per the GNU docs: "1" indicates entering a new file.
390   if (FileId == SM.getMainFileID() || FileId == PP.getPredefinesFileID())
391     WriteLineInfo(FileName, 1, FileType, "");
392   else
393     WriteLineInfo(FileName, 1, FileType, " 1");
394 
395   if (SM.getFileIDSize(FileId) == 0)
396     return;
397 
398   // The next byte to be copied from the source file, which may be non-zero if
399   // the lexer handled a BOM.
400   unsigned NextToWrite = SM.getFileOffset(RawLex.getSourceLocation());
401   assert(SM.getLineNumber(FileId, NextToWrite) == 1);
402   int Line = 1; // The current input file line number.
403 
404   Token RawToken;
405   RawLex.LexFromRawLexer(RawToken);
406 
407   // TODO: Consider adding a switch that strips possibly unimportant content,
408   // such as comments, to reduce the size of repro files.
409   while (RawToken.isNot(tok::eof)) {
410     if (RawToken.is(tok::hash) && RawToken.isAtStartOfLine()) {
411       RawLex.setParsingPreprocessorDirective(true);
412       Token HashToken = RawToken;
413       RawLex.LexFromRawLexer(RawToken);
414       if (RawToken.is(tok::raw_identifier))
415         PP.LookUpIdentifierInfo(RawToken);
416       if (RawToken.getIdentifierInfo() != nullptr) {
417         switch (RawToken.getIdentifierInfo()->getPPKeywordID()) {
418           case tok::pp_include:
419           case tok::pp_include_next:
420           case tok::pp_import: {
421             CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL, NextToWrite,
422               Line);
423             if (FileId != PP.getPredefinesFileID())
424               WriteLineInfo(FileName, Line - 1, FileType, "");
425             StringRef LineInfoExtra;
426             SourceLocation Loc = HashToken.getLocation();
427             if (const Module *Mod = FindModuleAtLocation(Loc))
428               WriteImplicitModuleImport(Mod);
429             else if (const IncludedFile *Inc = FindIncludeAtLocation(Loc)) {
430               const Module *Mod = FindEnteredModule(Loc);
431               if (Mod)
432                 OS << "#pragma clang module begin "
433                    << Mod->getFullModuleName(true) << "\n";
434 
435               // Include and recursively process the file.
436               Process(Inc->Id, Inc->FileType, Inc->DirLookup);
437 
438               if (Mod)
439                 OS << "#pragma clang module end /*"
440                    << Mod->getFullModuleName(true) << "*/\n";
441 
442               // Add line marker to indicate we're returning from an included
443               // file.
444               LineInfoExtra = " 2";
445             }
446             // fix up lineinfo (since commented out directive changed line
447             // numbers) for inclusions that were skipped due to header guards
448             WriteLineInfo(FileName, Line, FileType, LineInfoExtra);
449             break;
450           }
451           case tok::pp_pragma: {
452             StringRef Identifier = NextIdentifierName(RawLex, RawToken);
453             if (Identifier == "clang" || Identifier == "GCC") {
454               if (NextIdentifierName(RawLex, RawToken) == "system_header") {
455                 // keep the directive in, commented out
456                 CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL,
457                   NextToWrite, Line);
458                 // update our own type
459                 FileType = SM.getFileCharacteristic(RawToken.getLocation());
460                 WriteLineInfo(FileName, Line, FileType);
461               }
462             } else if (Identifier == "once") {
463               // keep the directive in, commented out
464               CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL,
465                 NextToWrite, Line);
466               WriteLineInfo(FileName, Line, FileType);
467             }
468             break;
469           }
470           case tok::pp_if:
471           case tok::pp_elif: {
472             bool elif = (RawToken.getIdentifierInfo()->getPPKeywordID() ==
473                          tok::pp_elif);
474             bool isTrue = IsIfAtLocationTrue(RawToken.getLocation());
475             OutputContentUpTo(FromFile, NextToWrite,
476                               SM.getFileOffset(HashToken.getLocation()),
477                               LocalEOL, Line, /*EnsureNewline=*/true);
478             do {
479               RawLex.LexFromRawLexer(RawToken);
480             } while (!RawToken.is(tok::eod) && RawToken.isNot(tok::eof));
481             // We need to disable the old condition, but that is tricky.
482             // Trying to comment it out can easily lead to comment nesting.
483             // So instead make the condition harmless by making it enclose
484             // and empty block. Moreover, put it itself inside an #if 0 block
485             // to disable it from getting evaluated (e.g. __has_include_next
486             // warns if used from the primary source file).
487             OS << "#if 0 /* disabled by -frewrite-includes */" << MainEOL;
488             if (elif) {
489               OS << "#if 0" << MainEOL;
490             }
491             OutputContentUpTo(FromFile, NextToWrite,
492                               SM.getFileOffset(RawToken.getLocation()) +
493                                   RawToken.getLength(),
494                               LocalEOL, Line, /*EnsureNewline=*/true);
495             // Close the empty block and the disabling block.
496             OS << "#endif" << MainEOL;
497             OS << "#endif /* disabled by -frewrite-includes */" << MainEOL;
498             OS << (elif ? "#elif " : "#if ") << (isTrue ? "1" : "0")
499                << " /* evaluated by -frewrite-includes */" << MainEOL;
500             WriteLineInfo(FileName, Line, FileType);
501             break;
502           }
503           case tok::pp_endif:
504           case tok::pp_else: {
505             // We surround every #include by #if 0 to comment it out, but that
506             // changes line numbers. These are fixed up right after that, but
507             // the whole #include could be inside a preprocessor conditional
508             // that is not processed. So it is necessary to fix the line
509             // numbers one the next line after each #else/#endif as well.
510             RawLex.SetKeepWhitespaceMode(true);
511             do {
512               RawLex.LexFromRawLexer(RawToken);
513             } while (RawToken.isNot(tok::eod) && RawToken.isNot(tok::eof));
514             OutputContentUpTo(FromFile, NextToWrite,
515                               SM.getFileOffset(RawToken.getLocation()) +
516                                   RawToken.getLength(),
517                               LocalEOL, Line, /*EnsureNewline=*/ true);
518             WriteLineInfo(FileName, Line, FileType);
519             RawLex.SetKeepWhitespaceMode(false);
520             break;
521           }
522           default:
523             break;
524         }
525       }
526       RawLex.setParsingPreprocessorDirective(false);
527     }
528     RawLex.LexFromRawLexer(RawToken);
529   }
530   OutputContentUpTo(FromFile, NextToWrite,
531                     SM.getFileOffset(SM.getLocForEndOfFile(FileId)), LocalEOL,
532                     Line, /*EnsureNewline=*/true);
533 }
534 
535 /// InclusionRewriterInInput - Implement -frewrite-includes mode.
RewriteIncludesInInput(Preprocessor & PP,raw_ostream * OS,const PreprocessorOutputOptions & Opts)536 void clang::RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS,
537                                    const PreprocessorOutputOptions &Opts) {
538   SourceManager &SM = PP.getSourceManager();
539   InclusionRewriter *Rewrite = new InclusionRewriter(
540       PP, *OS, Opts.ShowLineMarkers, Opts.UseLineDirectives);
541   Rewrite->detectMainFileEOL();
542 
543   PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Rewrite));
544   PP.IgnorePragmas();
545 
546   // First let the preprocessor process the entire file and call callbacks.
547   // Callbacks will record which #include's were actually performed.
548   PP.EnterMainSourceFile();
549   Token Tok;
550   // Only preprocessor directives matter here, so disable macro expansion
551   // everywhere else as an optimization.
552   // TODO: It would be even faster if the preprocessor could be switched
553   // to a mode where it would parse only preprocessor directives and comments,
554   // nothing else matters for parsing or processing.
555   PP.SetMacroExpansionOnlyInDirectives();
556   do {
557     PP.Lex(Tok);
558     if (Tok.is(tok::annot_module_begin))
559       Rewrite->handleModuleBegin(Tok);
560   } while (Tok.isNot(tok::eof));
561   Rewrite->setPredefinesBuffer(SM.getBufferOrFake(PP.getPredefinesFileID()));
562   Rewrite->Process(PP.getPredefinesFileID(), SrcMgr::C_User, nullptr);
563   Rewrite->Process(SM.getMainFileID(), SrcMgr::C_User, nullptr);
564   OS->flush();
565 }
566