17330f729Sjoerg //===--- InclusionRewriter.cpp - Rewrite includes into their expansions ---===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This code rewrites include invocations into their expansions. This gives you
107330f729Sjoerg // a file with all included files merged into it.
117330f729Sjoerg //
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg
147330f729Sjoerg #include "clang/Rewrite/Frontend/Rewriters.h"
157330f729Sjoerg #include "clang/Basic/SourceManager.h"
167330f729Sjoerg #include "clang/Frontend/PreprocessorOutputOptions.h"
177330f729Sjoerg #include "clang/Lex/HeaderSearch.h"
187330f729Sjoerg #include "clang/Lex/Pragma.h"
197330f729Sjoerg #include "clang/Lex/Preprocessor.h"
207330f729Sjoerg #include "llvm/ADT/SmallString.h"
217330f729Sjoerg #include "llvm/Support/raw_ostream.h"
227330f729Sjoerg
237330f729Sjoerg using namespace clang;
247330f729Sjoerg using namespace llvm;
257330f729Sjoerg
267330f729Sjoerg namespace {
277330f729Sjoerg
287330f729Sjoerg class InclusionRewriter : public PPCallbacks {
297330f729Sjoerg /// Information about which #includes were actually performed,
307330f729Sjoerg /// created by preprocessor callbacks.
317330f729Sjoerg struct IncludedFile {
327330f729Sjoerg FileID Id;
337330f729Sjoerg SrcMgr::CharacteristicKind FileType;
347330f729Sjoerg const DirectoryLookup *DirLookup;
IncludedFile__anon322bb26d0111::InclusionRewriter::IncludedFile357330f729Sjoerg IncludedFile(FileID Id, SrcMgr::CharacteristicKind FileType,
367330f729Sjoerg const DirectoryLookup *DirLookup)
377330f729Sjoerg : Id(Id), FileType(FileType), DirLookup(DirLookup) {}
387330f729Sjoerg };
397330f729Sjoerg Preprocessor &PP; ///< Used to find inclusion directives.
407330f729Sjoerg SourceManager &SM; ///< Used to read and manage source files.
417330f729Sjoerg raw_ostream &OS; ///< The destination stream for rewritten contents.
427330f729Sjoerg StringRef MainEOL; ///< The line ending marker to use.
43*e038c9c4Sjoerg llvm::MemoryBufferRef PredefinesBuffer; ///< The preprocessor predefines.
447330f729Sjoerg bool ShowLineMarkers; ///< Show #line markers.
457330f729Sjoerg bool UseLineDirectives; ///< Use of line directives or line markers.
467330f729Sjoerg /// Tracks where inclusions that change the file are found.
47*e038c9c4Sjoerg std::map<SourceLocation, IncludedFile> FileIncludes;
487330f729Sjoerg /// Tracks where inclusions that import modules are found.
49*e038c9c4Sjoerg std::map<SourceLocation, const Module *> ModuleIncludes;
507330f729Sjoerg /// Tracks where inclusions that enter modules (in a module build) are found.
51*e038c9c4Sjoerg std::map<SourceLocation, const Module *> ModuleEntryIncludes;
527330f729Sjoerg /// Tracks where #if and #elif directives get evaluated and whether to true.
53*e038c9c4Sjoerg std::map<SourceLocation, bool> IfConditions;
547330f729Sjoerg /// Used transitively for building up the FileIncludes mapping over the
557330f729Sjoerg /// various \c PPCallbacks callbacks.
567330f729Sjoerg SourceLocation LastInclusionLocation;
577330f729Sjoerg public:
587330f729Sjoerg InclusionRewriter(Preprocessor &PP, raw_ostream &OS, bool ShowLineMarkers,
597330f729Sjoerg bool UseLineDirectives);
607330f729Sjoerg void Process(FileID FileId, SrcMgr::CharacteristicKind FileType,
617330f729Sjoerg const DirectoryLookup *DirLookup);
setPredefinesBuffer(const llvm::MemoryBufferRef & Buf)62*e038c9c4Sjoerg void setPredefinesBuffer(const llvm::MemoryBufferRef &Buf) {
637330f729Sjoerg PredefinesBuffer = Buf;
647330f729Sjoerg }
657330f729Sjoerg void detectMainFileEOL();
handleModuleBegin(Token & Tok)667330f729Sjoerg void handleModuleBegin(Token &Tok) {
677330f729Sjoerg assert(Tok.getKind() == tok::annot_module_begin);
68*e038c9c4Sjoerg ModuleEntryIncludes.insert(
69*e038c9c4Sjoerg {Tok.getLocation(), (Module *)Tok.getAnnotationValue()});
707330f729Sjoerg }
717330f729Sjoerg private:
727330f729Sjoerg void FileChanged(SourceLocation Loc, FileChangeReason Reason,
737330f729Sjoerg SrcMgr::CharacteristicKind FileType,
747330f729Sjoerg FileID PrevFID) override;
757330f729Sjoerg void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
767330f729Sjoerg SrcMgr::CharacteristicKind FileType) override;
777330f729Sjoerg void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
787330f729Sjoerg StringRef FileName, bool IsAngled,
797330f729Sjoerg CharSourceRange FilenameRange, const FileEntry *File,
807330f729Sjoerg StringRef SearchPath, StringRef RelativePath,
817330f729Sjoerg const Module *Imported,
827330f729Sjoerg SrcMgr::CharacteristicKind FileType) override;
837330f729Sjoerg void If(SourceLocation Loc, SourceRange ConditionRange,
847330f729Sjoerg ConditionValueKind ConditionValue) override;
857330f729Sjoerg void Elif(SourceLocation Loc, SourceRange ConditionRange,
867330f729Sjoerg ConditionValueKind ConditionValue, SourceLocation IfLoc) override;
877330f729Sjoerg void WriteLineInfo(StringRef Filename, int Line,
887330f729Sjoerg SrcMgr::CharacteristicKind FileType,
897330f729Sjoerg StringRef Extra = StringRef());
907330f729Sjoerg void WriteImplicitModuleImport(const Module *Mod);
91*e038c9c4Sjoerg void OutputContentUpTo(const MemoryBufferRef &FromFile, unsigned &WriteFrom,
92*e038c9c4Sjoerg unsigned WriteTo, StringRef EOL, int &lines,
937330f729Sjoerg bool EnsureNewline);
947330f729Sjoerg void CommentOutDirective(Lexer &DirectivesLex, const Token &StartToken,
95*e038c9c4Sjoerg const MemoryBufferRef &FromFile, StringRef EOL,
967330f729Sjoerg unsigned &NextToWrite, int &Lines);
977330f729Sjoerg const IncludedFile *FindIncludeAtLocation(SourceLocation Loc) const;
987330f729Sjoerg const Module *FindModuleAtLocation(SourceLocation Loc) const;
997330f729Sjoerg const Module *FindEnteredModule(SourceLocation Loc) const;
1007330f729Sjoerg bool IsIfAtLocationTrue(SourceLocation Loc) const;
1017330f729Sjoerg StringRef NextIdentifierName(Lexer &RawLex, Token &RawToken);
1027330f729Sjoerg };
1037330f729Sjoerg
1047330f729Sjoerg } // end anonymous namespace
1057330f729Sjoerg
1067330f729Sjoerg /// Initializes an InclusionRewriter with a \p PP source and \p OS destination.
InclusionRewriter(Preprocessor & PP,raw_ostream & OS,bool ShowLineMarkers,bool UseLineDirectives)1077330f729Sjoerg InclusionRewriter::InclusionRewriter(Preprocessor &PP, raw_ostream &OS,
1087330f729Sjoerg bool ShowLineMarkers,
1097330f729Sjoerg bool UseLineDirectives)
1107330f729Sjoerg : PP(PP), SM(PP.getSourceManager()), OS(OS), MainEOL("\n"),
111*e038c9c4Sjoerg ShowLineMarkers(ShowLineMarkers), UseLineDirectives(UseLineDirectives),
1127330f729Sjoerg LastInclusionLocation(SourceLocation()) {}
1137330f729Sjoerg
1147330f729Sjoerg /// Write appropriate line information as either #line directives or GNU line
1157330f729Sjoerg /// markers depending on what mode we're in, including the \p Filename and
1167330f729Sjoerg /// \p Line we are located at, using the specified \p EOL line separator, and
1177330f729Sjoerg /// any \p Extra context specifiers in GNU line directives.
WriteLineInfo(StringRef Filename,int Line,SrcMgr::CharacteristicKind FileType,StringRef Extra)1187330f729Sjoerg void InclusionRewriter::WriteLineInfo(StringRef Filename, int Line,
1197330f729Sjoerg SrcMgr::CharacteristicKind FileType,
1207330f729Sjoerg StringRef Extra) {
1217330f729Sjoerg if (!ShowLineMarkers)
1227330f729Sjoerg return;
1237330f729Sjoerg if (UseLineDirectives) {
1247330f729Sjoerg OS << "#line" << ' ' << Line << ' ' << '"';
1257330f729Sjoerg OS.write_escaped(Filename);
1267330f729Sjoerg OS << '"';
1277330f729Sjoerg } else {
1287330f729Sjoerg // Use GNU linemarkers as described here:
1297330f729Sjoerg // http://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html
1307330f729Sjoerg OS << '#' << ' ' << Line << ' ' << '"';
1317330f729Sjoerg OS.write_escaped(Filename);
1327330f729Sjoerg OS << '"';
1337330f729Sjoerg if (!Extra.empty())
1347330f729Sjoerg OS << Extra;
1357330f729Sjoerg if (FileType == SrcMgr::C_System)
1367330f729Sjoerg // "`3' This indicates that the following text comes from a system header
1377330f729Sjoerg // file, so certain warnings should be suppressed."
1387330f729Sjoerg OS << " 3";
1397330f729Sjoerg else if (FileType == SrcMgr::C_ExternCSystem)
1407330f729Sjoerg // as above for `3', plus "`4' This indicates that the following text
1417330f729Sjoerg // should be treated as being wrapped in an implicit extern "C" block."
1427330f729Sjoerg OS << " 3 4";
1437330f729Sjoerg }
1447330f729Sjoerg OS << MainEOL;
1457330f729Sjoerg }
1467330f729Sjoerg
WriteImplicitModuleImport(const Module * Mod)1477330f729Sjoerg void InclusionRewriter::WriteImplicitModuleImport(const Module *Mod) {
1487330f729Sjoerg OS << "#pragma clang module import " << Mod->getFullModuleName(true)
1497330f729Sjoerg << " /* clang -frewrite-includes: implicit import */" << MainEOL;
1507330f729Sjoerg }
1517330f729Sjoerg
1527330f729Sjoerg /// FileChanged - Whenever the preprocessor enters or exits a #include file
1537330f729Sjoerg /// it invokes this handler.
FileChanged(SourceLocation Loc,FileChangeReason Reason,SrcMgr::CharacteristicKind NewFileType,FileID)1547330f729Sjoerg void InclusionRewriter::FileChanged(SourceLocation Loc,
1557330f729Sjoerg FileChangeReason Reason,
1567330f729Sjoerg SrcMgr::CharacteristicKind NewFileType,
1577330f729Sjoerg FileID) {
1587330f729Sjoerg if (Reason != EnterFile)
1597330f729Sjoerg return;
1607330f729Sjoerg if (LastInclusionLocation.isInvalid())
1617330f729Sjoerg // we didn't reach this file (eg: the main file) via an inclusion directive
1627330f729Sjoerg return;
1637330f729Sjoerg FileID Id = FullSourceLoc(Loc, SM).getFileID();
1647330f729Sjoerg auto P = FileIncludes.insert(
165*e038c9c4Sjoerg std::make_pair(LastInclusionLocation,
1667330f729Sjoerg IncludedFile(Id, NewFileType, PP.GetCurDirLookup())));
1677330f729Sjoerg (void)P;
1687330f729Sjoerg assert(P.second && "Unexpected revisitation of the same include directive");
1697330f729Sjoerg LastInclusionLocation = SourceLocation();
1707330f729Sjoerg }
1717330f729Sjoerg
1727330f729Sjoerg /// Called whenever an inclusion is skipped due to canonical header protection
1737330f729Sjoerg /// macros.
FileSkipped(const FileEntryRef &,const Token &,SrcMgr::CharacteristicKind)1747330f729Sjoerg void InclusionRewriter::FileSkipped(const FileEntryRef & /*SkippedFile*/,
1757330f729Sjoerg const Token & /*FilenameTok*/,
1767330f729Sjoerg SrcMgr::CharacteristicKind /*FileType*/) {
1777330f729Sjoerg assert(LastInclusionLocation.isValid() &&
1787330f729Sjoerg "A file, that wasn't found via an inclusion directive, was skipped");
1797330f729Sjoerg LastInclusionLocation = SourceLocation();
1807330f729Sjoerg }
1817330f729Sjoerg
1827330f729Sjoerg /// This should be called whenever the preprocessor encounters include
1837330f729Sjoerg /// directives. It does not say whether the file has been included, but it
1847330f729Sjoerg /// provides more information about the directive (hash location instead
1857330f729Sjoerg /// of location inside the included file). It is assumed that the matching
1867330f729Sjoerg /// FileChanged() or FileSkipped() is called after this (or neither is
1877330f729Sjoerg /// called if this #include results in an error or does not textually include
1887330f729Sjoerg /// anything).
InclusionDirective(SourceLocation HashLoc,const Token &,StringRef,bool,CharSourceRange,const FileEntry *,StringRef,StringRef,const Module * Imported,SrcMgr::CharacteristicKind FileType)1897330f729Sjoerg void InclusionRewriter::InclusionDirective(SourceLocation HashLoc,
1907330f729Sjoerg const Token &/*IncludeTok*/,
1917330f729Sjoerg StringRef /*FileName*/,
1927330f729Sjoerg bool /*IsAngled*/,
1937330f729Sjoerg CharSourceRange /*FilenameRange*/,
1947330f729Sjoerg const FileEntry * /*File*/,
1957330f729Sjoerg StringRef /*SearchPath*/,
1967330f729Sjoerg StringRef /*RelativePath*/,
1977330f729Sjoerg const Module *Imported,
1987330f729Sjoerg SrcMgr::CharacteristicKind FileType){
1997330f729Sjoerg if (Imported) {
200*e038c9c4Sjoerg auto P = ModuleIncludes.insert(std::make_pair(HashLoc, Imported));
2017330f729Sjoerg (void)P;
2027330f729Sjoerg assert(P.second && "Unexpected revisitation of the same include directive");
2037330f729Sjoerg } else
2047330f729Sjoerg LastInclusionLocation = HashLoc;
2057330f729Sjoerg }
2067330f729Sjoerg
If(SourceLocation Loc,SourceRange ConditionRange,ConditionValueKind ConditionValue)2077330f729Sjoerg void InclusionRewriter::If(SourceLocation Loc, SourceRange ConditionRange,
2087330f729Sjoerg ConditionValueKind ConditionValue) {
209*e038c9c4Sjoerg auto P = IfConditions.insert(std::make_pair(Loc, ConditionValue == CVK_True));
2107330f729Sjoerg (void)P;
2117330f729Sjoerg assert(P.second && "Unexpected revisitation of the same if directive");
2127330f729Sjoerg }
2137330f729Sjoerg
Elif(SourceLocation Loc,SourceRange ConditionRange,ConditionValueKind ConditionValue,SourceLocation IfLoc)2147330f729Sjoerg void InclusionRewriter::Elif(SourceLocation Loc, SourceRange ConditionRange,
2157330f729Sjoerg ConditionValueKind ConditionValue,
2167330f729Sjoerg SourceLocation IfLoc) {
217*e038c9c4Sjoerg auto P = IfConditions.insert(std::make_pair(Loc, ConditionValue == CVK_True));
2187330f729Sjoerg (void)P;
2197330f729Sjoerg assert(P.second && "Unexpected revisitation of the same elif directive");
2207330f729Sjoerg }
2217330f729Sjoerg
2227330f729Sjoerg /// Simple lookup for a SourceLocation (specifically one denoting the hash in
2237330f729Sjoerg /// an inclusion directive) in the map of inclusion information, FileChanges.
2247330f729Sjoerg const InclusionRewriter::IncludedFile *
FindIncludeAtLocation(SourceLocation Loc) const2257330f729Sjoerg InclusionRewriter::FindIncludeAtLocation(SourceLocation Loc) const {
226*e038c9c4Sjoerg const auto I = FileIncludes.find(Loc);
2277330f729Sjoerg if (I != FileIncludes.end())
2287330f729Sjoerg return &I->second;
2297330f729Sjoerg return nullptr;
2307330f729Sjoerg }
2317330f729Sjoerg
2327330f729Sjoerg /// Simple lookup for a SourceLocation (specifically one denoting the hash in
2337330f729Sjoerg /// an inclusion directive) in the map of module inclusion information.
2347330f729Sjoerg const Module *
FindModuleAtLocation(SourceLocation Loc) const2357330f729Sjoerg InclusionRewriter::FindModuleAtLocation(SourceLocation Loc) const {
236*e038c9c4Sjoerg const auto I = ModuleIncludes.find(Loc);
2377330f729Sjoerg if (I != ModuleIncludes.end())
2387330f729Sjoerg return I->second;
2397330f729Sjoerg return nullptr;
2407330f729Sjoerg }
2417330f729Sjoerg
2427330f729Sjoerg /// Simple lookup for a SourceLocation (specifically one denoting the hash in
2437330f729Sjoerg /// an inclusion directive) in the map of module entry information.
2447330f729Sjoerg const Module *
FindEnteredModule(SourceLocation Loc) const2457330f729Sjoerg InclusionRewriter::FindEnteredModule(SourceLocation Loc) const {
246*e038c9c4Sjoerg const auto I = ModuleEntryIncludes.find(Loc);
2477330f729Sjoerg if (I != ModuleEntryIncludes.end())
2487330f729Sjoerg return I->second;
2497330f729Sjoerg return nullptr;
2507330f729Sjoerg }
2517330f729Sjoerg
IsIfAtLocationTrue(SourceLocation Loc) const2527330f729Sjoerg bool InclusionRewriter::IsIfAtLocationTrue(SourceLocation Loc) const {
253*e038c9c4Sjoerg const auto I = IfConditions.find(Loc);
2547330f729Sjoerg if (I != IfConditions.end())
2557330f729Sjoerg return I->second;
2567330f729Sjoerg return false;
2577330f729Sjoerg }
2587330f729Sjoerg
2597330f729Sjoerg /// Detect the likely line ending style of \p FromFile by examining the first
2607330f729Sjoerg /// newline found within it.
DetectEOL(const MemoryBufferRef & FromFile)261*e038c9c4Sjoerg static StringRef DetectEOL(const MemoryBufferRef &FromFile) {
2627330f729Sjoerg // Detect what line endings the file uses, so that added content does not mix
2637330f729Sjoerg // the style. We need to check for "\r\n" first because "\n\r" will match
2647330f729Sjoerg // "\r\n\r\n".
2657330f729Sjoerg const char *Pos = strchr(FromFile.getBufferStart(), '\n');
2667330f729Sjoerg if (!Pos)
2677330f729Sjoerg return "\n";
2687330f729Sjoerg if (Pos - 1 >= FromFile.getBufferStart() && Pos[-1] == '\r')
2697330f729Sjoerg return "\r\n";
2707330f729Sjoerg if (Pos + 1 < FromFile.getBufferEnd() && Pos[1] == '\r')
2717330f729Sjoerg return "\n\r";
2727330f729Sjoerg return "\n";
2737330f729Sjoerg }
2747330f729Sjoerg
detectMainFileEOL()2757330f729Sjoerg void InclusionRewriter::detectMainFileEOL() {
276*e038c9c4Sjoerg Optional<MemoryBufferRef> FromFile = *SM.getBufferOrNone(SM.getMainFileID());
277*e038c9c4Sjoerg assert(FromFile);
278*e038c9c4Sjoerg if (!FromFile)
2797330f729Sjoerg return; // Should never happen, but whatever.
280*e038c9c4Sjoerg MainEOL = DetectEOL(*FromFile);
2817330f729Sjoerg }
2827330f729Sjoerg
2837330f729Sjoerg /// Writes out bytes from \p FromFile, starting at \p NextToWrite and ending at
2847330f729Sjoerg /// \p WriteTo - 1.
OutputContentUpTo(const MemoryBufferRef & FromFile,unsigned & WriteFrom,unsigned WriteTo,StringRef LocalEOL,int & Line,bool EnsureNewline)285*e038c9c4Sjoerg void InclusionRewriter::OutputContentUpTo(const MemoryBufferRef &FromFile,
2867330f729Sjoerg unsigned &WriteFrom, unsigned WriteTo,
2877330f729Sjoerg StringRef LocalEOL, int &Line,
2887330f729Sjoerg bool EnsureNewline) {
2897330f729Sjoerg if (WriteTo <= WriteFrom)
2907330f729Sjoerg return;
291*e038c9c4Sjoerg if (FromFile == PredefinesBuffer) {
2927330f729Sjoerg // Ignore the #defines of the predefines buffer.
2937330f729Sjoerg WriteFrom = WriteTo;
2947330f729Sjoerg return;
2957330f729Sjoerg }
2967330f729Sjoerg
2977330f729Sjoerg // If we would output half of a line ending, advance one character to output
2987330f729Sjoerg // the whole line ending. All buffers are null terminated, so looking ahead
2997330f729Sjoerg // one byte is safe.
3007330f729Sjoerg if (LocalEOL.size() == 2 &&
3017330f729Sjoerg LocalEOL[0] == (FromFile.getBufferStart() + WriteTo)[-1] &&
3027330f729Sjoerg LocalEOL[1] == (FromFile.getBufferStart() + WriteTo)[0])
3037330f729Sjoerg WriteTo++;
3047330f729Sjoerg
3057330f729Sjoerg StringRef TextToWrite(FromFile.getBufferStart() + WriteFrom,
3067330f729Sjoerg WriteTo - WriteFrom);
3077330f729Sjoerg
3087330f729Sjoerg if (MainEOL == LocalEOL) {
3097330f729Sjoerg OS << TextToWrite;
3107330f729Sjoerg // count lines manually, it's faster than getPresumedLoc()
3117330f729Sjoerg Line += TextToWrite.count(LocalEOL);
3127330f729Sjoerg if (EnsureNewline && !TextToWrite.endswith(LocalEOL))
3137330f729Sjoerg OS << MainEOL;
3147330f729Sjoerg } else {
3157330f729Sjoerg // Output the file one line at a time, rewriting the line endings as we go.
3167330f729Sjoerg StringRef Rest = TextToWrite;
3177330f729Sjoerg while (!Rest.empty()) {
3187330f729Sjoerg StringRef LineText;
3197330f729Sjoerg std::tie(LineText, Rest) = Rest.split(LocalEOL);
3207330f729Sjoerg OS << LineText;
3217330f729Sjoerg Line++;
3227330f729Sjoerg if (!Rest.empty())
3237330f729Sjoerg OS << MainEOL;
3247330f729Sjoerg }
3257330f729Sjoerg if (TextToWrite.endswith(LocalEOL) || EnsureNewline)
3267330f729Sjoerg OS << MainEOL;
3277330f729Sjoerg }
3287330f729Sjoerg WriteFrom = WriteTo;
3297330f729Sjoerg }
3307330f729Sjoerg
3317330f729Sjoerg /// Print characters from \p FromFile starting at \p NextToWrite up until the
3327330f729Sjoerg /// inclusion directive at \p StartToken, then print out the inclusion
3337330f729Sjoerg /// inclusion directive disabled by a #if directive, updating \p NextToWrite
3347330f729Sjoerg /// and \p Line to track the number of source lines visited and the progress
3357330f729Sjoerg /// through the \p FromFile buffer.
CommentOutDirective(Lexer & DirectiveLex,const Token & StartToken,const MemoryBufferRef & FromFile,StringRef LocalEOL,unsigned & NextToWrite,int & Line)3367330f729Sjoerg void InclusionRewriter::CommentOutDirective(Lexer &DirectiveLex,
3377330f729Sjoerg const Token &StartToken,
338*e038c9c4Sjoerg const MemoryBufferRef &FromFile,
3397330f729Sjoerg StringRef LocalEOL,
3407330f729Sjoerg unsigned &NextToWrite, int &Line) {
3417330f729Sjoerg OutputContentUpTo(FromFile, NextToWrite,
3427330f729Sjoerg SM.getFileOffset(StartToken.getLocation()), LocalEOL, Line,
3437330f729Sjoerg false);
3447330f729Sjoerg Token DirectiveToken;
3457330f729Sjoerg do {
3467330f729Sjoerg DirectiveLex.LexFromRawLexer(DirectiveToken);
3477330f729Sjoerg } while (!DirectiveToken.is(tok::eod) && DirectiveToken.isNot(tok::eof));
348*e038c9c4Sjoerg if (FromFile == PredefinesBuffer) {
3497330f729Sjoerg // OutputContentUpTo() would not output anything anyway.
3507330f729Sjoerg return;
3517330f729Sjoerg }
3527330f729Sjoerg OS << "#if 0 /* expanded by -frewrite-includes */" << MainEOL;
3537330f729Sjoerg OutputContentUpTo(FromFile, NextToWrite,
3547330f729Sjoerg SM.getFileOffset(DirectiveToken.getLocation()) +
3557330f729Sjoerg DirectiveToken.getLength(),
3567330f729Sjoerg LocalEOL, Line, true);
3577330f729Sjoerg OS << "#endif /* expanded by -frewrite-includes */" << MainEOL;
3587330f729Sjoerg }
3597330f729Sjoerg
3607330f729Sjoerg /// Find the next identifier in the pragma directive specified by \p RawToken.
NextIdentifierName(Lexer & RawLex,Token & RawToken)3617330f729Sjoerg StringRef InclusionRewriter::NextIdentifierName(Lexer &RawLex,
3627330f729Sjoerg Token &RawToken) {
3637330f729Sjoerg RawLex.LexFromRawLexer(RawToken);
3647330f729Sjoerg if (RawToken.is(tok::raw_identifier))
3657330f729Sjoerg PP.LookUpIdentifierInfo(RawToken);
3667330f729Sjoerg if (RawToken.is(tok::identifier))
3677330f729Sjoerg return RawToken.getIdentifierInfo()->getName();
3687330f729Sjoerg return StringRef();
3697330f729Sjoerg }
3707330f729Sjoerg
3717330f729Sjoerg /// Use a raw lexer to analyze \p FileId, incrementally copying parts of it
3727330f729Sjoerg /// and including content of included files recursively.
Process(FileID FileId,SrcMgr::CharacteristicKind FileType,const DirectoryLookup * DirLookup)3737330f729Sjoerg void InclusionRewriter::Process(FileID FileId,
3747330f729Sjoerg SrcMgr::CharacteristicKind FileType,
3757330f729Sjoerg const DirectoryLookup *DirLookup) {
376*e038c9c4Sjoerg MemoryBufferRef FromFile;
377*e038c9c4Sjoerg {
378*e038c9c4Sjoerg auto B = SM.getBufferOrNone(FileId);
379*e038c9c4Sjoerg assert(B && "Attempting to process invalid inclusion");
380*e038c9c4Sjoerg if (B)
381*e038c9c4Sjoerg FromFile = *B;
382*e038c9c4Sjoerg }
3837330f729Sjoerg StringRef FileName = FromFile.getBufferIdentifier();
384*e038c9c4Sjoerg Lexer RawLex(FileId, FromFile, PP.getSourceManager(), PP.getLangOpts());
3857330f729Sjoerg RawLex.SetCommentRetentionState(false);
3867330f729Sjoerg
3877330f729Sjoerg StringRef LocalEOL = DetectEOL(FromFile);
3887330f729Sjoerg
3897330f729Sjoerg // Per the GNU docs: "1" indicates entering a new file.
3907330f729Sjoerg if (FileId == SM.getMainFileID() || FileId == PP.getPredefinesFileID())
3917330f729Sjoerg WriteLineInfo(FileName, 1, FileType, "");
3927330f729Sjoerg else
3937330f729Sjoerg WriteLineInfo(FileName, 1, FileType, " 1");
3947330f729Sjoerg
3957330f729Sjoerg if (SM.getFileIDSize(FileId) == 0)
3967330f729Sjoerg return;
3977330f729Sjoerg
3987330f729Sjoerg // The next byte to be copied from the source file, which may be non-zero if
3997330f729Sjoerg // the lexer handled a BOM.
4007330f729Sjoerg unsigned NextToWrite = SM.getFileOffset(RawLex.getSourceLocation());
4017330f729Sjoerg assert(SM.getLineNumber(FileId, NextToWrite) == 1);
4027330f729Sjoerg int Line = 1; // The current input file line number.
4037330f729Sjoerg
4047330f729Sjoerg Token RawToken;
4057330f729Sjoerg RawLex.LexFromRawLexer(RawToken);
4067330f729Sjoerg
4077330f729Sjoerg // TODO: Consider adding a switch that strips possibly unimportant content,
4087330f729Sjoerg // such as comments, to reduce the size of repro files.
4097330f729Sjoerg while (RawToken.isNot(tok::eof)) {
4107330f729Sjoerg if (RawToken.is(tok::hash) && RawToken.isAtStartOfLine()) {
4117330f729Sjoerg RawLex.setParsingPreprocessorDirective(true);
4127330f729Sjoerg Token HashToken = RawToken;
4137330f729Sjoerg RawLex.LexFromRawLexer(RawToken);
4147330f729Sjoerg if (RawToken.is(tok::raw_identifier))
4157330f729Sjoerg PP.LookUpIdentifierInfo(RawToken);
4167330f729Sjoerg if (RawToken.getIdentifierInfo() != nullptr) {
4177330f729Sjoerg switch (RawToken.getIdentifierInfo()->getPPKeywordID()) {
4187330f729Sjoerg case tok::pp_include:
4197330f729Sjoerg case tok::pp_include_next:
4207330f729Sjoerg case tok::pp_import: {
4217330f729Sjoerg CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL, NextToWrite,
4227330f729Sjoerg Line);
4237330f729Sjoerg if (FileId != PP.getPredefinesFileID())
4247330f729Sjoerg WriteLineInfo(FileName, Line - 1, FileType, "");
4257330f729Sjoerg StringRef LineInfoExtra;
4267330f729Sjoerg SourceLocation Loc = HashToken.getLocation();
4277330f729Sjoerg if (const Module *Mod = FindModuleAtLocation(Loc))
4287330f729Sjoerg WriteImplicitModuleImport(Mod);
4297330f729Sjoerg else if (const IncludedFile *Inc = FindIncludeAtLocation(Loc)) {
4307330f729Sjoerg const Module *Mod = FindEnteredModule(Loc);
4317330f729Sjoerg if (Mod)
4327330f729Sjoerg OS << "#pragma clang module begin "
4337330f729Sjoerg << Mod->getFullModuleName(true) << "\n";
4347330f729Sjoerg
4357330f729Sjoerg // Include and recursively process the file.
4367330f729Sjoerg Process(Inc->Id, Inc->FileType, Inc->DirLookup);
4377330f729Sjoerg
4387330f729Sjoerg if (Mod)
4397330f729Sjoerg OS << "#pragma clang module end /*"
4407330f729Sjoerg << Mod->getFullModuleName(true) << "*/\n";
4417330f729Sjoerg
4427330f729Sjoerg // Add line marker to indicate we're returning from an included
4437330f729Sjoerg // file.
4447330f729Sjoerg LineInfoExtra = " 2";
4457330f729Sjoerg }
4467330f729Sjoerg // fix up lineinfo (since commented out directive changed line
4477330f729Sjoerg // numbers) for inclusions that were skipped due to header guards
4487330f729Sjoerg WriteLineInfo(FileName, Line, FileType, LineInfoExtra);
4497330f729Sjoerg break;
4507330f729Sjoerg }
4517330f729Sjoerg case tok::pp_pragma: {
4527330f729Sjoerg StringRef Identifier = NextIdentifierName(RawLex, RawToken);
4537330f729Sjoerg if (Identifier == "clang" || Identifier == "GCC") {
4547330f729Sjoerg if (NextIdentifierName(RawLex, RawToken) == "system_header") {
4557330f729Sjoerg // keep the directive in, commented out
4567330f729Sjoerg CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL,
4577330f729Sjoerg NextToWrite, Line);
4587330f729Sjoerg // update our own type
4597330f729Sjoerg FileType = SM.getFileCharacteristic(RawToken.getLocation());
4607330f729Sjoerg WriteLineInfo(FileName, Line, FileType);
4617330f729Sjoerg }
4627330f729Sjoerg } else if (Identifier == "once") {
4637330f729Sjoerg // keep the directive in, commented out
4647330f729Sjoerg CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL,
4657330f729Sjoerg NextToWrite, Line);
4667330f729Sjoerg WriteLineInfo(FileName, Line, FileType);
4677330f729Sjoerg }
4687330f729Sjoerg break;
4697330f729Sjoerg }
4707330f729Sjoerg case tok::pp_if:
4717330f729Sjoerg case tok::pp_elif: {
4727330f729Sjoerg bool elif = (RawToken.getIdentifierInfo()->getPPKeywordID() ==
4737330f729Sjoerg tok::pp_elif);
4747330f729Sjoerg bool isTrue = IsIfAtLocationTrue(RawToken.getLocation());
4757330f729Sjoerg OutputContentUpTo(FromFile, NextToWrite,
4767330f729Sjoerg SM.getFileOffset(HashToken.getLocation()),
4777330f729Sjoerg LocalEOL, Line, /*EnsureNewline=*/true);
4787330f729Sjoerg do {
4797330f729Sjoerg RawLex.LexFromRawLexer(RawToken);
4807330f729Sjoerg } while (!RawToken.is(tok::eod) && RawToken.isNot(tok::eof));
4817330f729Sjoerg // We need to disable the old condition, but that is tricky.
4827330f729Sjoerg // Trying to comment it out can easily lead to comment nesting.
4837330f729Sjoerg // So instead make the condition harmless by making it enclose
4847330f729Sjoerg // and empty block. Moreover, put it itself inside an #if 0 block
4857330f729Sjoerg // to disable it from getting evaluated (e.g. __has_include_next
4867330f729Sjoerg // warns if used from the primary source file).
4877330f729Sjoerg OS << "#if 0 /* disabled by -frewrite-includes */" << MainEOL;
4887330f729Sjoerg if (elif) {
4897330f729Sjoerg OS << "#if 0" << MainEOL;
4907330f729Sjoerg }
4917330f729Sjoerg OutputContentUpTo(FromFile, NextToWrite,
4927330f729Sjoerg SM.getFileOffset(RawToken.getLocation()) +
4937330f729Sjoerg RawToken.getLength(),
4947330f729Sjoerg LocalEOL, Line, /*EnsureNewline=*/true);
4957330f729Sjoerg // Close the empty block and the disabling block.
4967330f729Sjoerg OS << "#endif" << MainEOL;
4977330f729Sjoerg OS << "#endif /* disabled by -frewrite-includes */" << MainEOL;
4987330f729Sjoerg OS << (elif ? "#elif " : "#if ") << (isTrue ? "1" : "0")
4997330f729Sjoerg << " /* evaluated by -frewrite-includes */" << MainEOL;
5007330f729Sjoerg WriteLineInfo(FileName, Line, FileType);
5017330f729Sjoerg break;
5027330f729Sjoerg }
5037330f729Sjoerg case tok::pp_endif:
5047330f729Sjoerg case tok::pp_else: {
5057330f729Sjoerg // We surround every #include by #if 0 to comment it out, but that
5067330f729Sjoerg // changes line numbers. These are fixed up right after that, but
5077330f729Sjoerg // the whole #include could be inside a preprocessor conditional
5087330f729Sjoerg // that is not processed. So it is necessary to fix the line
5097330f729Sjoerg // numbers one the next line after each #else/#endif as well.
5107330f729Sjoerg RawLex.SetKeepWhitespaceMode(true);
5117330f729Sjoerg do {
5127330f729Sjoerg RawLex.LexFromRawLexer(RawToken);
5137330f729Sjoerg } while (RawToken.isNot(tok::eod) && RawToken.isNot(tok::eof));
5147330f729Sjoerg OutputContentUpTo(FromFile, NextToWrite,
5157330f729Sjoerg SM.getFileOffset(RawToken.getLocation()) +
5167330f729Sjoerg RawToken.getLength(),
5177330f729Sjoerg LocalEOL, Line, /*EnsureNewline=*/ true);
5187330f729Sjoerg WriteLineInfo(FileName, Line, FileType);
5197330f729Sjoerg RawLex.SetKeepWhitespaceMode(false);
5207330f729Sjoerg break;
5217330f729Sjoerg }
5227330f729Sjoerg default:
5237330f729Sjoerg break;
5247330f729Sjoerg }
5257330f729Sjoerg }
5267330f729Sjoerg RawLex.setParsingPreprocessorDirective(false);
5277330f729Sjoerg }
5287330f729Sjoerg RawLex.LexFromRawLexer(RawToken);
5297330f729Sjoerg }
5307330f729Sjoerg OutputContentUpTo(FromFile, NextToWrite,
5317330f729Sjoerg SM.getFileOffset(SM.getLocForEndOfFile(FileId)), LocalEOL,
5327330f729Sjoerg Line, /*EnsureNewline=*/true);
5337330f729Sjoerg }
5347330f729Sjoerg
5357330f729Sjoerg /// InclusionRewriterInInput - Implement -frewrite-includes mode.
RewriteIncludesInInput(Preprocessor & PP,raw_ostream * OS,const PreprocessorOutputOptions & Opts)5367330f729Sjoerg void clang::RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS,
5377330f729Sjoerg const PreprocessorOutputOptions &Opts) {
5387330f729Sjoerg SourceManager &SM = PP.getSourceManager();
5397330f729Sjoerg InclusionRewriter *Rewrite = new InclusionRewriter(
5407330f729Sjoerg PP, *OS, Opts.ShowLineMarkers, Opts.UseLineDirectives);
5417330f729Sjoerg Rewrite->detectMainFileEOL();
5427330f729Sjoerg
5437330f729Sjoerg PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Rewrite));
5447330f729Sjoerg PP.IgnorePragmas();
5457330f729Sjoerg
5467330f729Sjoerg // First let the preprocessor process the entire file and call callbacks.
5477330f729Sjoerg // Callbacks will record which #include's were actually performed.
5487330f729Sjoerg PP.EnterMainSourceFile();
5497330f729Sjoerg Token Tok;
5507330f729Sjoerg // Only preprocessor directives matter here, so disable macro expansion
5517330f729Sjoerg // everywhere else as an optimization.
5527330f729Sjoerg // TODO: It would be even faster if the preprocessor could be switched
5537330f729Sjoerg // to a mode where it would parse only preprocessor directives and comments,
5547330f729Sjoerg // nothing else matters for parsing or processing.
5557330f729Sjoerg PP.SetMacroExpansionOnlyInDirectives();
5567330f729Sjoerg do {
5577330f729Sjoerg PP.Lex(Tok);
5587330f729Sjoerg if (Tok.is(tok::annot_module_begin))
5597330f729Sjoerg Rewrite->handleModuleBegin(Tok);
5607330f729Sjoerg } while (Tok.isNot(tok::eof));
561*e038c9c4Sjoerg Rewrite->setPredefinesBuffer(SM.getBufferOrFake(PP.getPredefinesFileID()));
5627330f729Sjoerg Rewrite->Process(PP.getPredefinesFileID(), SrcMgr::C_User, nullptr);
5637330f729Sjoerg Rewrite->Process(SM.getMainFileID(), SrcMgr::C_User, nullptr);
5647330f729Sjoerg OS->flush();
5657330f729Sjoerg }
566