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