Thanks to visit codestin.com
Credit goes to clang.llvm.org

clang 22.0.0git
HeaderIncludeGen.cpp
Go to the documentation of this file.
1//===-- HeaderIncludeGen.cpp - Generate Header Includes -------------------===//
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
14#include "llvm/ADT/SmallString.h"
15#include "llvm/Support/JSON.h"
16#include "llvm/Support/raw_ostream.h"
17using namespace clang;
18
19namespace {
20class HeaderIncludesCallback : public PPCallbacks {
21 SourceManager &SM;
22 raw_ostream *OutputFile;
23 const DependencyOutputOptions &DepOpts;
24 unsigned CurrentIncludeDepth;
25 bool HasProcessedPredefines;
26 bool OwnsOutputFile;
27 bool ShowAllHeaders;
28 bool ShowDepth;
29 bool MSStyle;
30
31public:
32 HeaderIncludesCallback(const Preprocessor *PP, bool ShowAllHeaders_,
33 raw_ostream *OutputFile_,
34 const DependencyOutputOptions &DepOpts,
35 bool OwnsOutputFile_, bool ShowDepth_, bool MSStyle_)
36 : SM(PP->getSourceManager()), OutputFile(OutputFile_), DepOpts(DepOpts),
37 CurrentIncludeDepth(0), HasProcessedPredefines(false),
38 OwnsOutputFile(OwnsOutputFile_), ShowAllHeaders(ShowAllHeaders_),
39 ShowDepth(ShowDepth_), MSStyle(MSStyle_) {}
40
41 ~HeaderIncludesCallback() override {
42 if (OwnsOutputFile)
43 delete OutputFile;
44 }
45
46 HeaderIncludesCallback(const HeaderIncludesCallback &) = delete;
47 HeaderIncludesCallback &operator=(const HeaderIncludesCallback &) = delete;
48
49 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
51 FileID PrevFID) override;
52
53 void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
55
56private:
57 bool ShouldShowHeader(SrcMgr::CharacteristicKind HeaderType) {
58 if (!DepOpts.IncludeSystemHeaders && isSystem(HeaderType))
59 return false;
60
61 // Show the current header if we are (a) past the predefines, or (b) showing
62 // all headers and in the predefines at a depth past the initial file and
63 // command line buffers.
64 return (HasProcessedPredefines ||
65 (ShowAllHeaders && CurrentIncludeDepth > 2));
66 }
67};
68
69/// A callback for emitting header usage information to a file in JSON. Each
70/// line in the file is a JSON object that includes the source file name and
71/// the list of headers directly or indirectly included from it. For example:
72///
73/// {"source":"/tmp/foo.c",
74/// "includes":["/usr/include/stdio.h", "/usr/include/stdlib.h"]}
75///
76/// To reduce the amount of data written to the file, we only record system
77/// headers that are directly included from a file that isn't in the system
78/// directory.
79class HeaderIncludesJSONCallback : public PPCallbacks {
80 SourceManager &SM;
81 raw_ostream *OutputFile;
82 bool OwnsOutputFile;
83 SmallVector<std::string, 16> IncludedHeaders;
84
85public:
86 HeaderIncludesJSONCallback(const Preprocessor *PP, raw_ostream *OutputFile_,
87 bool OwnsOutputFile_)
88 : SM(PP->getSourceManager()), OutputFile(OutputFile_),
89 OwnsOutputFile(OwnsOutputFile_) {}
90
91 ~HeaderIncludesJSONCallback() override {
92 if (OwnsOutputFile)
93 delete OutputFile;
94 }
95
96 HeaderIncludesJSONCallback(const HeaderIncludesJSONCallback &) = delete;
97 HeaderIncludesJSONCallback &
98 operator=(const HeaderIncludesJSONCallback &) = delete;
99
100 void EndOfMainFile() override;
101
102 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
104 FileID PrevFID) override;
105
106 void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
108};
109
110/// A callback for emitting direct header and module usage information to a
111/// file in JSON. The output format is like HeaderIncludesJSONCallback but has
112/// an array of separate entries, one for each non-system source file used in
113/// the compilation showing only the direct includes and imports from that file.
114class HeaderIncludesDirectPerFileCallback : public PPCallbacks {
115 struct HeaderIncludeInfo {
116 SourceLocation Location;
117 FileEntryRef File;
118 const Module *ImportedModule;
119
120 HeaderIncludeInfo(SourceLocation Location, FileEntryRef File,
121 const Module *ImportedModule)
122 : Location(Location), File(File), ImportedModule(ImportedModule) {}
123 };
124
125 SourceManager &SM;
126 HeaderSearch &HSI;
127 raw_ostream *OutputFile;
128 bool OwnsOutputFile;
129 using DependencyMap =
130 llvm::DenseMap<FileEntryRef, SmallVector<HeaderIncludeInfo>>;
131 DependencyMap Dependencies;
132
133public:
134 HeaderIncludesDirectPerFileCallback(const Preprocessor *PP,
135 raw_ostream *OutputFile_,
136 bool OwnsOutputFile_)
137 : SM(PP->getSourceManager()), HSI(PP->getHeaderSearchInfo()),
138 OutputFile(OutputFile_), OwnsOutputFile(OwnsOutputFile_) {}
139
140 ~HeaderIncludesDirectPerFileCallback() override {
141 if (OwnsOutputFile)
142 delete OutputFile;
143 }
144
145 HeaderIncludesDirectPerFileCallback(
146 const HeaderIncludesDirectPerFileCallback &) = delete;
147 HeaderIncludesDirectPerFileCallback &
148 operator=(const HeaderIncludesDirectPerFileCallback &) = delete;
149
150 void EndOfMainFile() override;
151
152 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
153 StringRef FileName, bool IsAngled,
154 CharSourceRange FilenameRange,
155 OptionalFileEntryRef File, StringRef SearchPath,
156 StringRef RelativePath, const Module *SuggestedModule,
157 bool ModuleImported,
159
160 void moduleImport(SourceLocation ImportLoc, ModuleIdPath Path,
161 const Module *Imported) override;
162};
163}
164
165static void PrintHeaderInfo(raw_ostream *OutputFile, StringRef Filename,
166 bool ShowDepth, unsigned CurrentIncludeDepth,
167 bool MSStyle) {
168 // Write to a temporary string to avoid unnecessary flushing on errs().
169 SmallString<512> Pathname(Filename);
170 if (!MSStyle)
171 Lexer::Stringify(Pathname);
172
174 if (MSStyle)
175 Msg += "Note: including file:";
176
177 if (ShowDepth) {
178 // The main source file is at depth 1, so skip one dot.
179 for (unsigned i = 1; i != CurrentIncludeDepth; ++i)
180 Msg += MSStyle ? ' ' : '.';
181
182 if (!MSStyle)
183 Msg += ' ';
184 }
185 Msg += Pathname;
186 Msg += '\n';
187
188 *OutputFile << Msg;
189 OutputFile->flush();
190}
191
193 const DependencyOutputOptions &DepOpts,
194 bool ShowAllHeaders, StringRef OutputPath,
195 bool ShowDepth, bool MSStyle) {
196 raw_ostream *OutputFile = &llvm::errs();
197 bool OwnsOutputFile = false;
198
199 // Choose output stream, when printing in cl.exe /showIncludes style.
200 if (MSStyle) {
201 switch (DepOpts.ShowIncludesDest) {
202 default:
203 llvm_unreachable("Invalid destination for /showIncludes output!");
205 OutputFile = &llvm::errs();
206 break;
208 OutputFile = &llvm::outs();
209 break;
210 }
211 }
212
213 // Open the output file, if used.
214 if (!OutputPath.empty()) {
215 std::error_code EC;
216 llvm::raw_fd_ostream *OS = new llvm::raw_fd_ostream(
217 OutputPath.str(), EC,
218 llvm::sys::fs::OF_Append | llvm::sys::fs::OF_TextWithCRLF);
219 if (EC) {
220 PP.getDiagnostics().Report(clang::diag::warn_fe_cc_print_header_failure)
221 << EC.message();
222 delete OS;
223 } else {
224 OS->SetUnbuffered();
225 OutputFile = OS;
226 OwnsOutputFile = true;
227 }
228 }
229
230 switch (DepOpts.HeaderIncludeFormat) {
231 case HIFMT_None:
232 llvm_unreachable("unexpected header format kind");
233 case HIFMT_Textual: {
234 assert(DepOpts.HeaderIncludeFiltering == HIFIL_None &&
235 "header filtering is currently always disabled when output format is"
236 "textual");
237 // Print header info for extra headers, pretending they were discovered by
238 // the regular preprocessor. The primary use case is to support proper
239 // generation of Make / Ninja file dependencies for implicit includes, such
240 // as sanitizer ignorelists. It's only important for cl.exe compatibility,
241 // the GNU way to generate rules is -M / -MM / -MD / -MMD.
242 for (const auto &Header : DepOpts.ExtraDeps)
243 PrintHeaderInfo(OutputFile, Header.first, ShowDepth, 2, MSStyle);
244 PP.addPPCallbacks(std::make_unique<HeaderIncludesCallback>(
245 &PP, ShowAllHeaders, OutputFile, DepOpts, OwnsOutputFile, ShowDepth,
246 MSStyle));
247 break;
248 }
249 case HIFMT_JSON:
250 switch (DepOpts.HeaderIncludeFiltering) {
251 default:
252 llvm_unreachable("Unknown HeaderIncludeFilteringKind enum");
254 PP.addPPCallbacks(std::make_unique<HeaderIncludesJSONCallback>(
255 &PP, OutputFile, OwnsOutputFile));
256 break;
258 PP.addPPCallbacks(std::make_unique<HeaderIncludesDirectPerFileCallback>(
259 &PP, OutputFile, OwnsOutputFile));
260 break;
261 }
262 break;
263 }
264}
265
266void HeaderIncludesCallback::FileChanged(SourceLocation Loc,
267 FileChangeReason Reason,
268 SrcMgr::CharacteristicKind NewFileType,
269 FileID PrevFID) {
270 // Unless we are exiting a #include, make sure to skip ahead to the line the
271 // #include directive was at.
272 PresumedLoc UserLoc = SM.getPresumedLoc(Loc);
273 if (UserLoc.isInvalid())
274 return;
275
276 // Adjust the current include depth.
277 if (Reason == PPCallbacks::EnterFile) {
278 ++CurrentIncludeDepth;
279 } else if (Reason == PPCallbacks::ExitFile) {
280 if (CurrentIncludeDepth)
281 --CurrentIncludeDepth;
282
283 // We track when we are done with the predefines by watching for the first
284 // place where we drop back to a nesting depth of 1.
285 if (CurrentIncludeDepth == 1 && !HasProcessedPredefines)
286 HasProcessedPredefines = true;
287
288 return;
289 } else {
290 return;
291 }
292
293 if (!ShouldShowHeader(NewFileType))
294 return;
295
296 unsigned IncludeDepth = CurrentIncludeDepth;
297 if (!HasProcessedPredefines)
298 --IncludeDepth; // Ignore indent from <built-in>.
299
300 // FIXME: Identify headers in a more robust way than comparing their name to
301 // "<command line>" and "<built-in>" in a bunch of places.
302 if (Reason == PPCallbacks::EnterFile &&
303 UserLoc.getFilename() != StringRef("<command line>")) {
304 PrintHeaderInfo(OutputFile, UserLoc.getFilename(), ShowDepth, IncludeDepth,
305 MSStyle);
306 }
307}
308
309void HeaderIncludesCallback::FileSkipped(const FileEntryRef &SkippedFile,
310 const Token &FilenameTok,
312 if (!DepOpts.ShowSkippedHeaderIncludes)
313 return;
314
315 if (!ShouldShowHeader(FileType))
316 return;
317
318 PrintHeaderInfo(OutputFile, SkippedFile.getName(), ShowDepth,
319 CurrentIncludeDepth + 1, MSStyle);
320}
321
322void HeaderIncludesJSONCallback::EndOfMainFile() {
323 OptionalFileEntryRef FE = SM.getFileEntryRefForID(SM.getMainFileID());
324 SmallString<256> MainFile;
325 if (FE) {
326 MainFile += FE->getName();
327 SM.getFileManager().makeAbsolutePath(MainFile);
328 }
329
330 std::string Str;
331 llvm::raw_string_ostream OS(Str);
332 llvm::json::OStream JOS(OS);
333 JOS.object([&] {
334 JOS.attribute("source", MainFile.c_str());
335 JOS.attributeArray("includes", [&] {
336 llvm::StringSet<> SeenHeaders;
337 for (const std::string &H : IncludedHeaders)
338 if (SeenHeaders.insert(H).second)
339 JOS.value(H);
340 });
341 });
342 OS << "\n";
343
344 if (OutputFile->get_kind() == raw_ostream::OStreamKind::OK_FDStream) {
345 llvm::raw_fd_ostream *FDS = static_cast<llvm::raw_fd_ostream *>(OutputFile);
346 if (auto L = FDS->lock())
347 *OutputFile << Str;
348 } else
349 *OutputFile << Str;
350}
351
352/// Determine whether the header file should be recorded. The header file should
353/// be recorded only if the header file is a system header and the current file
354/// isn't a system header.
356 SourceLocation PrevLoc, SourceManager &SM) {
357 return SrcMgr::isSystem(NewFileType) && !SM.isInSystemHeader(PrevLoc);
358}
359
360void HeaderIncludesJSONCallback::FileChanged(
361 SourceLocation Loc, FileChangeReason Reason,
362 SrcMgr::CharacteristicKind NewFileType, FileID PrevFID) {
363 if (PrevFID.isInvalid() ||
364 !shouldRecordNewFile(NewFileType, SM.getLocForStartOfFile(PrevFID), SM))
365 return;
366
367 // Unless we are exiting a #include, make sure to skip ahead to the line the
368 // #include directive was at.
369 PresumedLoc UserLoc = SM.getPresumedLoc(Loc);
370 if (UserLoc.isInvalid())
371 return;
372
373 if (Reason == PPCallbacks::EnterFile &&
374 UserLoc.getFilename() != StringRef("<command line>"))
375 IncludedHeaders.push_back(UserLoc.getFilename());
376}
377
378void HeaderIncludesJSONCallback::FileSkipped(
379 const FileEntryRef &SkippedFile, const Token &FilenameTok,
381 if (!shouldRecordNewFile(FileType, FilenameTok.getLocation(), SM))
382 return;
383
384 IncludedHeaders.push_back(SkippedFile.getName().str());
385}
386
387void HeaderIncludesDirectPerFileCallback::EndOfMainFile() {
388 if (Dependencies.empty())
389 return;
390
391 // Sort the files so that the output does not depend on the DenseMap order.
392 SmallVector<FileEntryRef> SourceFiles;
393 for (auto F = Dependencies.begin(), FEnd = Dependencies.end(); F != FEnd;
394 ++F) {
395 SourceFiles.push_back(F->first);
396 }
397 llvm::sort(SourceFiles, [](const FileEntryRef &LHS, const FileEntryRef &RHS) {
398 return LHS.getUID() < RHS.getUID();
399 });
400
401 std::string Str;
402 llvm::raw_string_ostream OS(Str);
403 llvm::json::OStream JOS(OS);
404 JOS.object([&] {
405 JOS.attribute("version", "2.0.0");
406 JOS.attributeArray("dependencies", [&] {
407 for (const auto &S : SourceFiles) {
408 JOS.object([&] {
409 SmallVector<HeaderIncludeInfo> &Deps = Dependencies[S];
410 JOS.attribute("source", S.getName().str());
411 JOS.attributeArray("includes", [&] {
412 for (unsigned I = 0, N = Deps.size(); I != N; ++I) {
413 if (!Deps[I].ImportedModule) {
414 JOS.object([&] {
415 JOS.attribute("location", Deps[I].Location.printToString(SM));
416 JOS.attribute("file", Deps[I].File.getName());
417 });
418 }
419 }
420 });
421 JOS.attributeArray("imports", [&] {
422 for (unsigned I = 0, N = Deps.size(); I != N; ++I) {
423 if (Deps[I].ImportedModule) {
424 JOS.object([&] {
425 JOS.attribute("location", Deps[I].Location.printToString(SM));
426 JOS.attribute(
427 "module",
428 Deps[I].ImportedModule->getTopLevelModuleName());
429 JOS.attribute("file", Deps[I].File.getName());
430 });
431 }
432 }
433 });
434 });
435 }
436 });
437 });
438
439 OS << "\n";
440
441 if (OutputFile->get_kind() == raw_ostream::OStreamKind::OK_FDStream) {
442 llvm::raw_fd_ostream *FDS = static_cast<llvm::raw_fd_ostream *>(OutputFile);
443 if (auto L = FDS->lock())
444 *OutputFile << Str;
445 } else
446 *OutputFile << Str;
447}
448
449void HeaderIncludesDirectPerFileCallback::InclusionDirective(
450 SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
451 bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
452 StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,
453 bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
454 if (!File)
455 return;
456
457 SourceLocation Loc = SM.getExpansionLoc(HashLoc);
458 if (SM.isInSystemHeader(Loc))
459 return;
460 OptionalFileEntryRef FromFile = SM.getFileEntryRefForID(SM.getFileID(Loc));
461 if (!FromFile)
462 return;
463
464 FileEntryRef HeaderOrModuleMapFile = *File;
465 if (ModuleImported && SuggestedModule) {
466 OptionalFileEntryRef ModuleMapFile =
467 HSI.getModuleMap().getModuleMapFileForUniquing(SuggestedModule);
468 if (ModuleMapFile) {
469 HeaderOrModuleMapFile = *ModuleMapFile;
470 }
471 }
472
473 HeaderIncludeInfo DependenciesEntry(
474 Loc, HeaderOrModuleMapFile, (ModuleImported ? SuggestedModule : nullptr));
475 Dependencies[*FromFile].push_back(DependenciesEntry);
476}
477
478void HeaderIncludesDirectPerFileCallback::moduleImport(SourceLocation ImportLoc,
479 ModuleIdPath Path,
480 const Module *Imported) {
481 if (!Imported)
482 return;
483
484 SourceLocation Loc = SM.getExpansionLoc(ImportLoc);
485 if (SM.isInSystemHeader(Loc))
486 return;
487 OptionalFileEntryRef FromFile = SM.getFileEntryRefForID(SM.getFileID(Loc));
488 if (!FromFile)
489 return;
490
491 OptionalFileEntryRef ModuleMapFile =
493 if (!ModuleMapFile)
494 return;
495
496 HeaderIncludeInfo DependenciesEntry(Loc, *ModuleMapFile, Imported);
497 Dependencies[*FromFile].push_back(DependenciesEntry);
498}
static void PrintHeaderInfo(raw_ostream *OutputFile, StringRef Filename, bool ShowDepth, unsigned CurrentIncludeDepth, bool MSStyle)
static bool shouldRecordNewFile(SrcMgr::CharacteristicKind NewFileType, SourceLocation PrevLoc, SourceManager &SM)
Determine whether the header file should be recorded.
llvm::MachO::FileType FileType
Definition MachO.h:46
#define SM(sm)
Defines the clang::Preprocessor interface.
Defines the SourceManager interface.
DependencyOutputOptions - Options for controlling the compiler dependency file generation.
ShowIncludesDestination ShowIncludesDest
Destination of cl.exe style /showIncludes info.
HeaderIncludeFormatKind HeaderIncludeFormat
The format of header information.
HeaderIncludeFilteringKind HeaderIncludeFiltering
Determine whether header information should be filtered.
unsigned ShowSkippedHeaderIncludes
With ShowHeaderIncludes, show also includes that were skipped due to the "include guard optimization...
std::vector< std::pair< std::string, ExtraDepKind > > ExtraDeps
A list of extra dependencies (filename and kind) to be used for every target.
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
StringRef getName() const
The name of this FileEntry.
Definition FileEntry.h:61
unsigned getUID() const
Definition FileEntry.h:352
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
bool isInvalid() const
ModuleMap & getModuleMap()
Retrieve the module map.
static std::string Stringify(StringRef Str, bool Charify=false)
Stringify - Convert the specified string into a C string by i) escaping '\' and " characters and ii) ...
Definition Lexer.cpp:309
OptionalFileEntryRef getModuleMapFileForUniquing(const Module *M) const
This interface provides a way to observe the actions of the preprocessor as it does its thing.
Definition PPCallbacks.h:37
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
void addPPCallbacks(std::unique_ptr< PPCallbacks > C)
DiagnosticsEngine & getDiagnostics() const
Represents an unpacked "presumed" location which can be presented to the user.
const char * getFilename() const
Return the presumed filename of this location.
bool isInvalid() const
Return true if this object is invalid or uninitialized.
Encodes a location in the source.
This class handles loading and caching of source files into memory.
SourceLocation getLocation() const
Return a source location identifier for the specified offset in the current file.
Definition Token.h:134
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
bool isSystem(CharacteristicKind CK)
Determine whether a file / directory characteristic is for system code.
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
@ HeaderSearch
Remove unused header search paths including header maps.
The JSON file list parser is used to communicate input to InstallAPI.
CustomizableOptional< FileEntryRef > OptionalFileEntryRef
Definition FileEntry.h:208
ArrayRef< IdentifierLoc > ModuleIdPath
A sequence of identifier/location pairs used to describe a particular module or submodule,...
@ HIFIL_Direct_Per_File
@ HIFIL_Only_Direct_System
@ Module
Module linkage, which indicates that the entity can be referred to from other translation units withi...
Definition Linkage.h:54
void AttachHeaderIncludeGen(Preprocessor &PP, const DependencyOutputOptions &DepOpts, bool ShowAllHeaders=false, StringRef OutputPath={}, bool ShowDepth=true, bool MSStyle=false)
AttachHeaderIncludeGen - Create a header include list generator, and attach it to the given preproces...
@ HIFMT_Textual
#define false
Definition stdbool.h:26