21 #include "llvm/ADT/SmallString.h"
22 #include "llvm/Support/raw_ostream.h"
24 using namespace clang;
36 : Id(Id), FileType(FileType) {}
42 const llvm::MemoryBuffer *PredefinesBuffer;
44 bool UseLineDirectives;
46 std::map<unsigned, IncludedFile> FileIncludes;
48 std::map<unsigned, const Module *> ModuleIncludes;
50 std::map<unsigned, const Module *> ModuleEntryIncludes;
55 InclusionRewriter(
Preprocessor &PP, raw_ostream &OS,
bool ShowLineMarkers,
56 bool UseLineDirectives);
58 void setPredefinesBuffer(
const llvm::MemoryBuffer *Buf) {
59 PredefinesBuffer = Buf;
61 void detectMainFileEOL();
62 void handleModuleBegin(
Token &Tok) {
63 assert(Tok.
getKind() == tok::annot_module_begin);
71 void FileSkipped(
const FileEntry &SkippedFile,
const Token &FilenameTok,
76 StringRef SearchPath, StringRef RelativePath,
77 const Module *Imported)
override;
80 StringRef Extra = StringRef());
81 void WriteImplicitModuleImport(
const Module *Mod);
82 void OutputContentUpTo(
const MemoryBuffer &FromFile,
83 unsigned &WriteFrom,
unsigned WriteTo,
84 StringRef EOL,
int &lines,
86 void CommentOutDirective(
Lexer &DirectivesLex,
const Token &StartToken,
87 const MemoryBuffer &FromFile, StringRef EOL,
88 unsigned &NextToWrite,
int &Lines);
92 const IncludedFile *FindIncludeAtLocation(
SourceLocation Loc)
const;
95 StringRef NextIdentifierName(
Lexer &RawLex,
Token &RawToken);
101 InclusionRewriter::InclusionRewriter(
Preprocessor &PP, raw_ostream &OS,
102 bool ShowLineMarkers,
103 bool UseLineDirectives)
104 : PP(PP),
SM(PP.getSourceManager()), OS(OS), MainEOL(
"\n"),
105 PredefinesBuffer(nullptr), ShowLineMarkers(ShowLineMarkers),
106 UseLineDirectives(UseLineDirectives),
113 void InclusionRewriter::WriteLineInfo(StringRef
Filename,
int Line,
116 if (!ShowLineMarkers)
118 if (UseLineDirectives) {
119 OS <<
"#line" <<
' ' << Line <<
' ' <<
'"';
120 OS.write_escaped(Filename);
125 OS <<
'#' <<
' ' << Line <<
' ' <<
'"';
126 OS.write_escaped(Filename);
142 void InclusionRewriter::WriteImplicitModuleImport(
const Module *Mod) {
144 <<
" /* clang -frewrite-includes: implicit import */" << MainEOL;
150 FileChangeReason Reason,
153 if (Reason != EnterFile)
155 if (LastInclusionLocation.isInvalid())
159 auto P = FileIncludes.insert(std::make_pair(
160 LastInclusionLocation.getRawEncoding(), IncludedFile(Id, NewFileType)));
162 assert(
P.second &&
"Unexpected revisitation of the same include directive");
168 void InclusionRewriter::FileSkipped(
const FileEntry &,
171 assert(LastInclusionLocation.isValid() &&
172 "A file, that wasn't found via an inclusion directive, was skipped");
183 void InclusionRewriter::InclusionDirective(
SourceLocation HashLoc,
193 auto P = ModuleIncludes.insert(
196 assert(
P.second &&
"Unexpected revisitation of the same include directive");
198 LastInclusionLocation = HashLoc;
203 const InclusionRewriter::IncludedFile *
204 InclusionRewriter::FindIncludeAtLocation(
SourceLocation Loc)
const {
206 if (
I != FileIncludes.end())
214 InclusionRewriter::FindModuleAtLocation(
SourceLocation Loc)
const {
216 if (
I != ModuleIncludes.end())
226 if (
I != ModuleEntryIncludes.end())
233 static StringRef
DetectEOL(
const MemoryBuffer &FromFile) {
237 const char *Pos = strchr(FromFile.getBufferStart(),
'\n');
240 if (Pos - 1 >= FromFile.getBufferStart() && Pos[-1] ==
'\r')
242 if (Pos + 1 < FromFile.getBufferEnd() && Pos[1] ==
'\r')
247 void InclusionRewriter::detectMainFileEOL() {
258 void InclusionRewriter::OutputContentUpTo(
const MemoryBuffer &FromFile,
259 unsigned &WriteFrom,
unsigned WriteTo,
260 StringRef LocalEOL,
int &Line,
261 bool EnsureNewline) {
262 if (WriteTo <= WriteFrom)
264 if (&FromFile == PredefinesBuffer) {
273 if (LocalEOL.size() == 2 &&
274 LocalEOL[0] == (FromFile.getBufferStart() + WriteTo)[-1] &&
275 LocalEOL[1] == (FromFile.getBufferStart() + WriteTo)[0])
278 StringRef TextToWrite(FromFile.getBufferStart() + WriteFrom,
279 WriteTo - WriteFrom);
281 if (MainEOL == LocalEOL) {
284 Line += TextToWrite.count(LocalEOL);
285 if (EnsureNewline && !TextToWrite.endswith(LocalEOL))
289 StringRef Rest = TextToWrite;
290 while (!Rest.empty()) {
292 std::tie(LineText, Rest) = Rest.split(LocalEOL);
298 if (TextToWrite.endswith(LocalEOL) || EnsureNewline)
309 void InclusionRewriter::CommentOutDirective(
Lexer &DirectiveLex,
310 const Token &StartToken,
311 const MemoryBuffer &FromFile,
313 unsigned &NextToWrite,
int &Line) {
314 OutputContentUpTo(FromFile, NextToWrite,
317 Token DirectiveToken;
320 }
while (!DirectiveToken.
is(tok::eod) && DirectiveToken.
isNot(
tok::eof));
321 if (&FromFile == PredefinesBuffer) {
325 OS <<
"#if 0 /* expanded by -frewrite-includes */" << MainEOL;
326 OutputContentUpTo(FromFile, NextToWrite,
329 LocalEOL,
Line,
true);
330 OS <<
"#endif /* expanded by -frewrite-includes */" << MainEOL;
334 StringRef InclusionRewriter::NextIdentifierName(
Lexer &RawLex,
337 if (RawToken.
is(tok::raw_identifier))
338 PP.LookUpIdentifierInfo(RawToken);
339 if (RawToken.
is(tok::identifier))
346 bool InclusionRewriter::HandleHasInclude(
351 if (Tok.
isNot(tok::l_paren))
361 if (Tok.
is(tok::less)) {
364 FilenameBuffer +=
'<';
366 if (Tok.
is(tok::eod))
369 if (Tok.
is(tok::raw_identifier))
370 PP.LookUpIdentifierInfo(Tok);
374 bool Invalid =
false;
375 StringRef TmpName = PP.getSpelling(Tok, TmpBuffer, &Invalid);
379 FilenameBuffer += TmpName;
382 }
while (Tok.
isNot(tok::greater));
384 FilenameBuffer +=
'>';
385 Filename = FilenameBuffer;
387 if (Tok.
isNot(tok::string_literal))
390 bool Invalid =
false;
391 Filename = PP.getSpelling(Tok, FilenameBuffer, &Invalid);
398 if (Tok.
isNot(tok::r_paren))
405 const FileEntry *FileEnt = PP.getSourceManager().getFileEntryForID(FileId);
408 Includers.push_back(std::make_pair(FileEnt, FileEnt->
getDir()));
410 const FileEntry *File = PP.getHeaderSearchInfo().LookupFile(
411 Filename,
SourceLocation(), isAngled,
nullptr, CurDir, Includers,
nullptr,
412 nullptr,
nullptr,
nullptr,
nullptr);
414 FileExists = File !=
nullptr;
420 void InclusionRewriter::Process(
FileID FileId,
423 const MemoryBuffer &FromFile = *
SM.
getBuffer(FileId, &Invalid);
424 assert(!Invalid &&
"Attempting to process invalid inclusion");
425 StringRef
FileName = FromFile.getBufferIdentifier();
426 Lexer RawLex(FileId, &FromFile, PP.getSourceManager(), PP.getLangOpts());
429 StringRef LocalEOL =
DetectEOL(FromFile);
432 if (FileId ==
SM.
getMainFileID() || FileId == PP.getPredefinesFileID())
433 WriteLineInfo(FileName, 1, FileType,
"");
435 WriteLineInfo(FileName, 1, FileType,
" 1");
454 Token HashToken = RawToken;
456 if (RawToken.
is(tok::raw_identifier))
457 PP.LookUpIdentifierInfo(RawToken);
460 case tok::pp_include:
461 case tok::pp_include_next:
462 case tok::pp_import: {
463 CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL, NextToWrite,
465 if (FileId != PP.getPredefinesFileID())
466 WriteLineInfo(FileName, Line - 1, FileType,
"");
467 StringRef LineInfoExtra;
469 if (
const Module *Mod = FindModuleAtLocation(Loc))
470 WriteImplicitModuleImport(Mod);
471 else if (
const IncludedFile *Inc = FindIncludeAtLocation(Loc)) {
472 const Module *Mod = FindEnteredModule(Loc);
474 OS <<
"#pragma clang module begin "
478 Process(Inc->Id, Inc->FileType);
481 OS <<
"#pragma clang module end /*"
486 LineInfoExtra =
" 2";
490 WriteLineInfo(FileName, Line, FileType, LineInfoExtra);
493 case tok::pp_pragma: {
494 StringRef Identifier = NextIdentifierName(RawLex, RawToken);
495 if (Identifier ==
"clang" || Identifier ==
"GCC") {
496 if (NextIdentifierName(RawLex, RawToken) ==
"system_header") {
498 CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL,
502 WriteLineInfo(FileName, Line, FileType);
504 }
else if (Identifier ==
"once") {
506 CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL,
508 WriteLineInfo(FileName, Line, FileType);
520 if (RawToken.
is(tok::raw_identifier))
521 PP.LookUpIdentifierInfo(RawToken);
523 if (RawToken.
is(tok::identifier)) {
529 if (!HandleHasInclude(FileId, RawLex,
nullptr, RawToken,
534 "__has_include_next")) {
539 if (!HandleHasInclude(FileId, RawLex, Lookup, RawToken,
548 LocalEOL,
Line,
false);
549 OS <<
'(' << (int) HasFile <<
")/*";
550 OutputContentUpTo(FromFile, NextToWrite,
553 LocalEOL,
Line,
false);
556 }
while (RawToken.
isNot(tok::eod));
558 OutputContentUpTo(FromFile, NextToWrite,
561 LocalEOL,
Line,
true);
562 WriteLineInfo(FileName, Line, FileType);
577 OutputContentUpTo(FromFile, NextToWrite,
580 LocalEOL,
Line,
true);
581 WriteLineInfo(FileName, Line, FileType);
592 OutputContentUpTo(FromFile, NextToWrite,
601 InclusionRewriter *Rewrite =
new InclusionRewriter(
603 Rewrite->detectMainFileEOL();
620 if (Tok.
is(tok::annot_module_begin))
621 Rewrite->handleModuleBegin(Tok);
bool isAtStartOfLine() const
isAtStartOfLine - Return true if this token is at the start of a line.
SourceManager & getSourceManager() const
Lexer - This provides a simple interface that turns a text buffer into a stream of tokens...
bool LexFromRawLexer(Token &Result)
LexFromRawLexer - Lex a token from a designated raw lexer (one with no associated preprocessor object...
Defines the SourceManager interface.
llvm::MemoryBuffer * getBuffer(FileID FID, SourceLocation Loc, bool *Invalid=nullptr) const
Return the buffer for the specified FileID.
unsigned getRawEncoding() const
When a SourceLocation itself cannot be used, this returns an (opaque) 32-bit integer encoding for it...
void IgnorePragmas()
Install empty handlers for all pragmas (making them ignored).
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
This interface provides a way to observe the actions of the preprocessor as it does its thing...
std::string getFullModuleName(bool AllowStringLiterals=false) const
Retrieve the full name of this module, including the path from its top-level module.
Token - This structure provides full information about a lexed token.
Describes a module or submodule.
unsigned getFileIDSize(FileID FID) const
The size of the SLocEntry that FID represents.
void setParsingPreprocessorDirective(bool f)
Inform the lexer whether or not we are currently lexing a preprocessor directive. ...
tok::TokenKind getKind() const
detail::InMemoryDirectory::const_iterator I
static StringRef DetectEOL(const MemoryBuffer &FromFile)
Detect the likely line ending style of FromFile by examining the first newline found within it...
void * getAnnotationValue() const
SourceLocation getLocForEndOfFile(FileID FID) const
Return the source location corresponding to the last byte of the specified file.
PreprocessorOutputOptions - Options for controlling the C preprocessor output (e.g., -E).
StringRef getName() const
Return the actual identifier string.
Represents a character-granular source range.
void SetMacroExpansionOnlyInDirectives()
Disables macro expansion everywhere except for preprocessor directives.
void EnterMainSourceFile()
Enter the specified FileID as the main source file, which implicitly adds the builtin defines etc...
Defines the clang::Preprocessor interface.
SourceLocation getLocation() const
Return a source location identifier for the specified offset in the current file. ...
bool isNot(tok::TokenKind K) const
Record the location of an inclusion directive, such as an #include or #import statement.
DirectoryLookup - This class represents one entry in the search list that specifies the search order ...
Encodes a location in the source.
Cached information about one file (either on disk or in the virtual file system). ...
void Lex(Token &Result)
Lex the next token for this preprocessor.
unsigned UseLineDirectives
Use #line instead of GCC-style # N.
FileID getMainFileID() const
Returns the FileID of the main source file.
bool is(tok::TokenKind K) const
is/isNot - Predicates to check if this token is a specific kind, as in "if (Tok.is(tok::l_brace)) {...
FileID getPredefinesFileID() const
Returns the FileID for the preprocessor predefines.
bool isStr(const char(&Str)[StrLen]) const
Return true if this is the identifier for the specified string.
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
void RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS, const PreprocessorOutputOptions &Opts)
RewriteIncludesInInput - Implement -frewrite-includes mode.
SrcMgr::CharacteristicKind getFileCharacteristic(SourceLocation Loc) const
Return the file characteristic of the specified source location, indicating whether this is a normal ...
SourceLocation getSourceLocation(const char *Loc, unsigned TokLen=1) const
getSourceLocation - Return a source location identifier for the specified offset in the current file...
unsigned getLineNumber(FileID FID, unsigned FilePos, bool *Invalid=nullptr) const
Given a SourceLocation, return the spelling line number for the position indicated.
unsigned ShowLineMarkers
Show #line markers.
void SetCommentRetentionState(bool Mode)
SetCommentRetentionMode - Change the comment retention mode of the lexer to the specified mode...
A SourceLocation and its associated SourceManager.
unsigned getLength() const
unsigned getFileOffset(SourceLocation SpellingLoc) const
Returns the offset from the start of the file that the specified SourceLocation represents.
const DirectoryEntry * getDir() const
Return the directory the file lives in.
void SetKeepWhitespaceMode(bool Val)
SetKeepWhitespaceMode - This method lets clients enable or disable whitespace retention mode...
void addPPCallbacks(std::unique_ptr< PPCallbacks > C)
This class handles loading and caching of source files into memory.
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
IdentifierInfo * getIdentifierInfo() const
tok::PPKeywordKind getPPKeywordID() const
Return the preprocessor keyword ID for this identifier.