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