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;
53 InclusionRewriter(
Preprocessor &PP, raw_ostream &OS,
bool ShowLineMarkers,
54 bool UseLineDirectives);
56 void setPredefinesBuffer(
const llvm::MemoryBuffer *Buf) {
57 PredefinesBuffer = Buf;
59 void detectMainFileEOL();
64 void FileSkipped(
const FileEntry &SkippedFile,
const Token &FilenameTok,
69 StringRef SearchPath, StringRef RelativePath,
70 const Module *Imported)
override;
73 StringRef Extra = StringRef());
74 void WriteImplicitModuleImport(
const Module *Mod);
75 void OutputContentUpTo(
const MemoryBuffer &FromFile,
76 unsigned &WriteFrom,
unsigned WriteTo,
77 StringRef EOL,
int &lines,
79 void CommentOutDirective(
Lexer &DirectivesLex,
const Token &StartToken,
80 const MemoryBuffer &FromFile, StringRef EOL,
81 unsigned &NextToWrite,
int &Lines);
85 const IncludedFile *FindIncludeAtLocation(
SourceLocation Loc)
const;
87 StringRef NextIdentifierName(
Lexer &RawLex,
Token &RawToken);
93 InclusionRewriter::InclusionRewriter(
Preprocessor &PP, raw_ostream &OS,
95 bool UseLineDirectives)
96 : PP(PP),
SM(PP.getSourceManager()), OS(OS), MainEOL(
"\n"),
97 PredefinesBuffer(nullptr), ShowLineMarkers(ShowLineMarkers),
98 UseLineDirectives(UseLineDirectives),
105 void InclusionRewriter::WriteLineInfo(
const char *
Filename,
int Line,
108 if (!ShowLineMarkers)
110 if (UseLineDirectives) {
111 OS <<
"#line" <<
' ' << Line <<
' ' <<
'"';
112 OS.write_escaped(Filename);
117 OS <<
'#' <<
' ' << Line <<
' ' <<
'"';
118 OS.write_escaped(Filename);
134 void InclusionRewriter::WriteImplicitModuleImport(
const Module *Mod) {
136 <<
" /* clang -frewrite-includes: implicit import */" << MainEOL;
142 FileChangeReason Reason,
145 if (Reason != EnterFile)
147 if (LastInclusionLocation.isInvalid())
151 auto P = FileIncludes.insert(std::make_pair(
152 LastInclusionLocation.getRawEncoding(), IncludedFile(Id, NewFileType)));
154 assert(
P.second &&
"Unexpected revisitation of the same include directive");
160 void InclusionRewriter::FileSkipped(
const FileEntry &,
163 assert(LastInclusionLocation.isValid() &&
164 "A file, that wasn't found via an inclusion directive, was skipped");
173 void InclusionRewriter::InclusionDirective(
SourceLocation HashLoc,
182 assert(LastInclusionLocation.isInvalid() &&
183 "Another inclusion directive was found before the previous one "
186 auto P = ModuleIncludes.insert(
189 assert(
P.second &&
"Unexpected revisitation of the same include directive");
191 LastInclusionLocation = HashLoc;
196 const InclusionRewriter::IncludedFile *
197 InclusionRewriter::FindIncludeAtLocation(
SourceLocation Loc)
const {
199 if (
I != FileIncludes.end())
207 InclusionRewriter::FindModuleAtLocation(
SourceLocation Loc)
const {
209 if (
I != ModuleIncludes.end())
216 static StringRef
DetectEOL(
const MemoryBuffer &FromFile) {
220 const char *Pos = strchr(FromFile.getBufferStart(),
'\n');
223 if (Pos - 1 >= FromFile.getBufferStart() && Pos[-1] ==
'\r')
225 if (Pos + 1 < FromFile.getBufferEnd() && Pos[1] ==
'\r')
230 void InclusionRewriter::detectMainFileEOL() {
241 void InclusionRewriter::OutputContentUpTo(
const MemoryBuffer &FromFile,
242 unsigned &WriteFrom,
unsigned WriteTo,
243 StringRef LocalEOL,
int &Line,
244 bool EnsureNewline) {
245 if (WriteTo <= WriteFrom)
247 if (&FromFile == PredefinesBuffer) {
256 if (LocalEOL.size() == 2 &&
257 LocalEOL[0] == (FromFile.getBufferStart() + WriteTo)[-1] &&
258 LocalEOL[1] == (FromFile.getBufferStart() + WriteTo)[0])
261 StringRef TextToWrite(FromFile.getBufferStart() + WriteFrom,
262 WriteTo - WriteFrom);
264 if (MainEOL == LocalEOL) {
267 Line += TextToWrite.count(LocalEOL);
268 if (EnsureNewline && !TextToWrite.endswith(LocalEOL))
272 StringRef Rest = TextToWrite;
273 while (!Rest.empty()) {
275 std::tie(LineText, Rest) = Rest.split(LocalEOL);
281 if (TextToWrite.endswith(LocalEOL) || EnsureNewline)
292 void InclusionRewriter::CommentOutDirective(
Lexer &DirectiveLex,
293 const Token &StartToken,
294 const MemoryBuffer &FromFile,
296 unsigned &NextToWrite,
int &Line) {
297 OutputContentUpTo(FromFile, NextToWrite,
300 Token DirectiveToken;
303 }
while (!DirectiveToken.
is(tok::eod) && DirectiveToken.
isNot(
tok::eof));
304 if (&FromFile == PredefinesBuffer) {
308 OS <<
"#if 0 /* expanded by -frewrite-includes */" << MainEOL;
309 OutputContentUpTo(FromFile, NextToWrite,
312 LocalEOL,
Line,
true);
313 OS <<
"#endif /* expanded by -frewrite-includes */" << MainEOL;
317 StringRef InclusionRewriter::NextIdentifierName(
Lexer &RawLex,
320 if (RawToken.
is(tok::raw_identifier))
321 PP.LookUpIdentifierInfo(RawToken);
322 if (RawToken.
is(tok::identifier))
329 bool InclusionRewriter::HandleHasInclude(
334 if (Tok.
isNot(tok::l_paren))
344 if (Tok.
is(tok::less)) {
347 FilenameBuffer +=
'<';
349 if (Tok.
is(tok::eod))
352 if (Tok.
is(tok::raw_identifier))
353 PP.LookUpIdentifierInfo(Tok);
357 bool Invalid =
false;
358 StringRef TmpName = PP.getSpelling(Tok, TmpBuffer, &Invalid);
362 FilenameBuffer += TmpName;
365 }
while (Tok.
isNot(tok::greater));
367 FilenameBuffer +=
'>';
368 Filename = FilenameBuffer;
370 if (Tok.
isNot(tok::string_literal))
373 bool Invalid =
false;
374 Filename = PP.getSpelling(Tok, FilenameBuffer, &Invalid);
381 if (Tok.
isNot(tok::r_paren))
388 const FileEntry *FileEnt = PP.getSourceManager().getFileEntryForID(FileId);
391 Includers.push_back(std::make_pair(FileEnt, FileEnt->
getDir()));
393 const FileEntry *File = PP.getHeaderSearchInfo().LookupFile(
394 Filename,
SourceLocation(), isAngled,
nullptr, CurDir, Includers,
nullptr,
395 nullptr,
nullptr,
nullptr,
false);
397 FileExists = File !=
nullptr;
403 bool InclusionRewriter::Process(
FileID FileId,
407 const MemoryBuffer &FromFile = *
SM.
getBuffer(FileId, &Invalid);
408 assert(!Invalid &&
"Attempting to process invalid inclusion");
409 const char *
FileName = FromFile.getBufferIdentifier();
410 Lexer RawLex(FileId, &FromFile, PP.getSourceManager(), PP.getLangOpts());
413 StringRef LocalEOL =
DetectEOL(FromFile);
416 if (FileId ==
SM.
getMainFileID() || FileId == PP.getPredefinesFileID())
417 WriteLineInfo(FileName, 1, FileType,
"");
419 WriteLineInfo(FileName, 1, FileType,
" 1");
438 Token HashToken = RawToken;
440 if (RawToken.
is(tok::raw_identifier))
441 PP.LookUpIdentifierInfo(RawToken);
444 case tok::pp_include:
445 case tok::pp_include_next:
446 case tok::pp_import: {
447 CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL, NextToWrite,
449 if (FileId != PP.getPredefinesFileID())
450 WriteLineInfo(FileName, Line - 1, FileType,
"");
451 StringRef LineInfoExtra;
453 if (
const Module *Mod = PP.getLangOpts().ObjC2
454 ? FindModuleAtLocation(Loc)
456 WriteImplicitModuleImport(Mod);
457 else if (
const IncludedFile *Inc = FindIncludeAtLocation(Loc)) {
459 if (Process(Inc->Id, Inc->FileType)) {
464 LineInfoExtra =
" 2";
469 WriteLineInfo(FileName, Line, FileType, LineInfoExtra);
472 case tok::pp_pragma: {
473 StringRef Identifier = NextIdentifierName(RawLex, RawToken);
474 if (Identifier ==
"clang" || Identifier ==
"GCC") {
475 if (NextIdentifierName(RawLex, RawToken) ==
"system_header") {
477 CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL,
481 WriteLineInfo(FileName, Line, FileType);
483 }
else if (Identifier ==
"once") {
485 CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL,
487 WriteLineInfo(FileName, Line, FileType);
499 if (RawToken.
is(tok::raw_identifier))
500 PP.LookUpIdentifierInfo(RawToken);
502 if (RawToken.
is(tok::identifier)) {
508 if (!HandleHasInclude(FileId, RawLex,
nullptr, RawToken,
513 "__has_include_next")) {
518 if (!HandleHasInclude(FileId, RawLex, Lookup, RawToken,
527 LocalEOL,
Line,
false);
528 OS <<
'(' << (int) HasFile <<
")/*";
529 OutputContentUpTo(FromFile, NextToWrite,
532 LocalEOL,
Line,
false);
535 }
while (RawToken.
isNot(tok::eod));
537 OutputContentUpTo(FromFile, NextToWrite,
540 LocalEOL,
Line,
true);
541 WriteLineInfo(FileName, Line, FileType);
556 OutputContentUpTo(FromFile, NextToWrite,
559 LocalEOL,
Line,
true);
560 WriteLineInfo(FileName, Line, FileType);
571 OutputContentUpTo(FromFile, NextToWrite,
581 InclusionRewriter *Rewrite =
new InclusionRewriter(
583 Rewrite->detectMainFileEOL();
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() 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. ...
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...
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.