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,
67 StringRef FileName,
bool IsAngled,
69 StringRef SearchPath, StringRef RelativePath,
70 const Module *Imported)
override;
71 void WriteLineInfo(
const char *Filename,
int Line,
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.isInvalid() &&
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))
386 bool isAngled = PP.GetIncludeFilenameSpelling(Tok.
getLocation(), Filename);
388 const FileEntry *FileEnt = PP.getSourceManager().getFileEntryForID(FileId);
391 Includers.push_back(std::make_pair(FileEnt, FileEnt->
getDir()));
392 const FileEntry *File = PP.getHeaderSearchInfo().LookupFile(
393 Filename,
SourceLocation(), isAngled,
nullptr, CurDir, Includers,
nullptr,
394 nullptr,
nullptr,
false);
396 FileExists = File !=
nullptr;
402 bool InclusionRewriter::Process(
FileID FileId,
406 const MemoryBuffer &FromFile = *
SM.
getBuffer(FileId, &Invalid);
407 assert(!Invalid &&
"Attempting to process invalid inclusion");
408 const char *FileName = FromFile.getBufferIdentifier();
409 Lexer RawLex(FileId, &FromFile, PP.getSourceManager(), PP.getLangOpts());
412 StringRef LocalEOL =
DetectEOL(FromFile);
415 if (FileId ==
SM.
getMainFileID() || FileId == PP.getPredefinesFileID())
416 WriteLineInfo(FileName, 1, FileType,
"");
418 WriteLineInfo(FileName, 1, FileType,
" 1");
437 Token HashToken = RawToken;
439 if (RawToken.
is(tok::raw_identifier))
440 PP.LookUpIdentifierInfo(RawToken);
443 case tok::pp_include:
444 case tok::pp_include_next:
445 case tok::pp_import: {
446 CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL, NextToWrite,
448 if (FileId != PP.getPredefinesFileID())
449 WriteLineInfo(FileName, Line - 1, FileType,
"");
450 StringRef LineInfoExtra;
452 if (
const Module *Mod = FindModuleAtLocation(Loc))
453 WriteImplicitModuleImport(Mod);
454 else if (
const IncludedFile *Inc = FindIncludeAtLocation(Loc)) {
456 if (Process(Inc->Id, Inc->FileType)) {
461 LineInfoExtra =
" 2";
466 WriteLineInfo(FileName, Line, FileType, LineInfoExtra);
469 case tok::pp_pragma: {
470 StringRef Identifier = NextIdentifierName(RawLex, RawToken);
471 if (Identifier ==
"clang" || Identifier ==
"GCC") {
472 if (NextIdentifierName(RawLex, RawToken) ==
"system_header") {
474 CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL,
478 WriteLineInfo(FileName, Line, FileType);
480 }
else if (Identifier ==
"once") {
482 CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL,
484 WriteLineInfo(FileName, Line, FileType);
496 if (RawToken.
is(tok::raw_identifier))
497 PP.LookUpIdentifierInfo(RawToken);
499 if (RawToken.
is(tok::identifier)) {
505 if (!HandleHasInclude(FileId, RawLex,
nullptr, RawToken,
510 "__has_include_next")) {
515 if (!HandleHasInclude(FileId, RawLex, Lookup, RawToken,
524 LocalEOL,
Line,
false);
525 OS <<
'(' << (int) HasFile <<
")/*";
526 OutputContentUpTo(FromFile, NextToWrite,
529 LocalEOL,
Line,
false);
532 }
while (RawToken.
isNot(tok::eod));
534 OutputContentUpTo(FromFile, NextToWrite,
537 LocalEOL,
Line,
true);
538 WriteLineInfo(FileName, Line, FileType);
553 OutputContentUpTo(FromFile, NextToWrite,
556 LocalEOL,
Line,
true);
557 WriteLineInfo(FileName, Line, FileType);
568 OutputContentUpTo(FromFile, NextToWrite,
578 InclusionRewriter *Rewrite =
new InclusionRewriter(
580 Rewrite->detectMainFileEOL();
bool isAtStartOfLine() const
SourceManager & getSourceManager() const
bool LexFromRawLexer(Token &Result)
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.
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. ...
static StringRef DetectEOL(const MemoryBuffer &FromFile)
SourceLocation getLocForEndOfFile(FileID FID) const
Return the source location corresponding to the last byte of the specified file.
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.
Encodes a location in the source. The SourceManager can decode this to get at the full include stack...
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
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
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)
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)
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.