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