clang 17.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 {
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 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
48 FileID PrevFID) override;
49
50 void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
51 SrcMgr::CharacteristicKind FileType) override;
52};
53
54/// A callback for emitting header usage information to a file in JSON. Each
55/// line in the file is a JSON object that includes the source file name and
56/// the list of headers directly or indirectly included from it. For example:
57///
58/// {"source":"/tmp/foo.c",
59/// "includes":["/usr/include/stdio.h", "/usr/include/stdlib.h"]}
60///
61/// To reduce the amount of data written to the file, we only record system
62/// headers that are directly included from a file that isn't in the system
63/// directory.
64class HeaderIncludesJSONCallback : public PPCallbacks {
66 raw_ostream *OutputFile;
67 bool OwnsOutputFile;
68 SmallVector<std::string, 16> IncludedHeaders;
69
70public:
71 HeaderIncludesJSONCallback(const Preprocessor *PP, raw_ostream *OutputFile_,
72 bool OwnsOutputFile_)
73 : SM(PP->getSourceManager()), OutputFile(OutputFile_),
74 OwnsOutputFile(OwnsOutputFile_) {}
75
76 ~HeaderIncludesJSONCallback() override {
77 if (OwnsOutputFile)
78 delete OutputFile;
79 }
80
81 void EndOfMainFile() override;
82
83 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
85 FileID PrevFID) override;
86
87 void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
88 SrcMgr::CharacteristicKind FileType) override;
89};
90}
91
92static void PrintHeaderInfo(raw_ostream *OutputFile, StringRef Filename,
93 bool ShowDepth, unsigned CurrentIncludeDepth,
94 bool MSStyle) {
95 // Write to a temporary string to avoid unnecessary flushing on errs().
96 SmallString<512> Pathname(Filename);
97 if (!MSStyle)
98 Lexer::Stringify(Pathname);
99
101 if (MSStyle)
102 Msg += "Note: including file:";
103
104 if (ShowDepth) {
105 // The main source file is at depth 1, so skip one dot.
106 for (unsigned i = 1; i != CurrentIncludeDepth; ++i)
107 Msg += MSStyle ? ' ' : '.';
108
109 if (!MSStyle)
110 Msg += ' ';
111 }
112 Msg += Pathname;
113 Msg += '\n';
114
115 *OutputFile << Msg;
116 OutputFile->flush();
117}
118
120 const DependencyOutputOptions &DepOpts,
121 bool ShowAllHeaders, StringRef OutputPath,
122 bool ShowDepth, bool MSStyle) {
123 raw_ostream *OutputFile = &llvm::errs();
124 bool OwnsOutputFile = false;
125
126 // Choose output stream, when printing in cl.exe /showIncludes style.
127 if (MSStyle) {
128 switch (DepOpts.ShowIncludesDest) {
129 default:
130 llvm_unreachable("Invalid destination for /showIncludes output!");
131 case ShowIncludesDestination::Stderr:
132 OutputFile = &llvm::errs();
133 break;
134 case ShowIncludesDestination::Stdout:
135 OutputFile = &llvm::outs();
136 break;
137 }
138 }
139
140 // Open the output file, if used.
141 if (!OutputPath.empty()) {
142 std::error_code EC;
143 llvm::raw_fd_ostream *OS = new llvm::raw_fd_ostream(
144 OutputPath.str(), EC,
145 llvm::sys::fs::OF_Append | llvm::sys::fs::OF_TextWithCRLF);
146 if (EC) {
147 PP.getDiagnostics().Report(clang::diag::warn_fe_cc_print_header_failure)
148 << EC.message();
149 delete OS;
150 } else {
151 OS->SetUnbuffered();
152 OutputFile = OS;
153 OwnsOutputFile = true;
154 }
155 }
156
157 switch (DepOpts.HeaderIncludeFormat) {
158 case HIFMT_None:
159 llvm_unreachable("unexpected header format kind");
160 case HIFMT_Textual: {
161 assert(DepOpts.HeaderIncludeFiltering == HIFIL_None &&
162 "header filtering is currently always disabled when output format is"
163 "textual");
164 // Print header info for extra headers, pretending they were discovered by
165 // the regular preprocessor. The primary use case is to support proper
166 // generation of Make / Ninja file dependencies for implicit includes, such
167 // as sanitizer ignorelists. It's only important for cl.exe compatibility,
168 // the GNU way to generate rules is -M / -MM / -MD / -MMD.
169 for (const auto &Header : DepOpts.ExtraDeps)
170 PrintHeaderInfo(OutputFile, Header.first, ShowDepth, 2, MSStyle);
171 PP.addPPCallbacks(std::make_unique<HeaderIncludesCallback>(
172 &PP, ShowAllHeaders, OutputFile, DepOpts, OwnsOutputFile, ShowDepth,
173 MSStyle));
174 break;
175 }
176 case HIFMT_JSON: {
178 "only-direct-system is the only option for filtering");
179 PP.addPPCallbacks(std::make_unique<HeaderIncludesJSONCallback>(
180 &PP, OutputFile, OwnsOutputFile));
181 break;
182 }
183 }
184}
185
186void HeaderIncludesCallback::FileChanged(SourceLocation Loc,
187 FileChangeReason Reason,
188 SrcMgr::CharacteristicKind NewFileType,
189 FileID PrevFID) {
190 // Unless we are exiting a #include, make sure to skip ahead to the line the
191 // #include directive was at.
192 PresumedLoc UserLoc = SM.getPresumedLoc(Loc);
193 if (UserLoc.isInvalid())
194 return;
195
196 // Adjust the current include depth.
197 if (Reason == PPCallbacks::EnterFile) {
198 ++CurrentIncludeDepth;
199 } else if (Reason == PPCallbacks::ExitFile) {
200 if (CurrentIncludeDepth)
201 --CurrentIncludeDepth;
202
203 // We track when we are done with the predefines by watching for the first
204 // place where we drop back to a nesting depth of 1.
205 if (CurrentIncludeDepth == 1 && !HasProcessedPredefines) {
206 if (!DepOpts.ShowIncludesPretendHeader.empty()) {
207 PrintHeaderInfo(OutputFile, DepOpts.ShowIncludesPretendHeader,
208 ShowDepth, 2, MSStyle);
209 }
210 HasProcessedPredefines = true;
211 }
212
213 return;
214 } else
215 return;
216
217 // Show the header if we are (a) past the predefines, or (b) showing all
218 // headers and in the predefines at a depth past the initial file and command
219 // line buffers.
220 bool ShowHeader = (HasProcessedPredefines ||
221 (ShowAllHeaders && CurrentIncludeDepth > 2));
222 unsigned IncludeDepth = CurrentIncludeDepth;
223 if (!HasProcessedPredefines)
224 --IncludeDepth; // Ignore indent from <built-in>.
225 else if (!DepOpts.ShowIncludesPretendHeader.empty())
226 ++IncludeDepth; // Pretend inclusion by ShowIncludesPretendHeader.
227
228 if (!DepOpts.IncludeSystemHeaders && isSystem(NewFileType))
229 ShowHeader = false;
230
231 // Dump the header include information we are past the predefines buffer or
232 // are showing all headers and this isn't the magic implicit <command line>
233 // header.
234 // FIXME: Identify headers in a more robust way than comparing their name to
235 // "<command line>" and "<built-in>" in a bunch of places.
236 if (ShowHeader && Reason == PPCallbacks::EnterFile &&
237 UserLoc.getFilename() != StringRef("<command line>")) {
238 PrintHeaderInfo(OutputFile, UserLoc.getFilename(), ShowDepth, IncludeDepth,
239 MSStyle);
240 }
241}
242
243void HeaderIncludesCallback::FileSkipped(const FileEntryRef &SkippedFile, const
244 Token &FilenameTok,
246 if (!DepOpts.ShowSkippedHeaderIncludes)
247 return;
248
249 if (!DepOpts.IncludeSystemHeaders && isSystem(FileType))
250 return;
251
252 PrintHeaderInfo(OutputFile, SkippedFile.getName(), ShowDepth,
253 CurrentIncludeDepth + 1, MSStyle);
254}
255
256void HeaderIncludesJSONCallback::EndOfMainFile() {
257 const FileEntry *FE = SM.getFileEntryForID(SM.getMainFileID());
258 SmallString<256> MainFile(FE->getName());
259 SM.getFileManager().makeAbsolutePath(MainFile);
260
261 std::string Str;
262 llvm::raw_string_ostream OS(Str);
263 llvm::json::OStream JOS(OS);
264 JOS.object([&] {
265 JOS.attribute("source", MainFile.c_str());
266 JOS.attributeArray("includes", [&] {
267 llvm::StringSet<> SeenHeaders;
268 for (const std::string &H : IncludedHeaders)
269 if (SeenHeaders.insert(H).second)
270 JOS.value(H);
271 });
272 });
273 OS << "\n";
274
275 if (OutputFile->get_kind() == raw_ostream::OStreamKind::OK_FDStream) {
276 llvm::raw_fd_ostream *FDS = static_cast<llvm::raw_fd_ostream *>(OutputFile);
277 if (auto L = FDS->lock())
278 *OutputFile << Str;
279 } else
280 *OutputFile << Str;
281}
282
283/// Determine whether the header file should be recorded. The header file should
284/// be recorded only if the header file is a system header and the current file
285/// isn't a system header.
287 SourceLocation PrevLoc, SourceManager &SM) {
288 return SrcMgr::isSystem(NewFileType) && !SM.isInSystemHeader(PrevLoc);
289}
290
291void HeaderIncludesJSONCallback::FileChanged(
292 SourceLocation Loc, FileChangeReason Reason,
293 SrcMgr::CharacteristicKind NewFileType, FileID PrevFID) {
294 if (PrevFID.isInvalid() ||
295 !shouldRecordNewFile(NewFileType, SM.getLocForStartOfFile(PrevFID), SM))
296 return;
297
298 // Unless we are exiting a #include, make sure to skip ahead to the line the
299 // #include directive was at.
300 PresumedLoc UserLoc = SM.getPresumedLoc(Loc);
301 if (UserLoc.isInvalid())
302 return;
303
304 if (Reason == PPCallbacks::EnterFile &&
305 UserLoc.getFilename() != StringRef("<command line>"))
306 IncludedHeaders.push_back(UserLoc.getFilename());
307}
308
309void HeaderIncludesJSONCallback::FileSkipped(
310 const FileEntryRef &SkippedFile, const Token &FilenameTok,
312 if (!shouldRecordNewFile(FileType, FilenameTok.getLocation(), SM))
313 return;
314
315 IncludedHeaders.push_back(SkippedFile.getName().str());
316}
#define SM(sm)
Definition: Cuda.cpp:78
StringRef Filename
Definition: Format.cpp:2772
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.
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 optimizatio...
std::vector< std::pair< std::string, ExtraDepKind > > ExtraDeps
A list of extra dependencies (filename and kind) to be used for every target.
std::string ShowIncludesPretendHeader
In /showIncludes mode, pretend the main TU is a header with this name.
unsigned IncludeSystemHeaders
Include system header dependencies.
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1542
A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...
Definition: FileEntry.h:57
StringRef getName() const
The name of this FileEntry.
Definition: FileEntry.h:61
Cached information about one file (either on disk or in the virtual file system).
Definition: FileEntry.h:356
StringRef getName() const
Definition: FileEntry.h:387
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
bool isInvalid() const
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:261
This interface provides a way to observe the actions of the preprocessor as it does its thing.
Definition: PPCallbacks.h:35
virtual void EndOfMainFile()
Callback invoked when the end of the main file is reached.
Definition: PPCallbacks.h:185
virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID=FileID())
Callback invoked whenever a source file is entered or exited.
Definition: PPCallbacks.h:48
virtual void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok, SrcMgr::CharacteristicKind FileType)
Callback invoked whenever a source file is skipped as the result of header guard optimization.
Definition: PPCallbacks.h:82
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:128
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.
Token - This structure provides full information about a lexed token.
Definition: Token.h:35
SourceLocation getLocation() const
Return a source location identifier for the specified offset in the current file.
Definition: Token.h:126
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
Definition: SourceManager.h:80
bool isSystem(CharacteristicKind CK)
Determine whether a file / directory characteristic is for system code.
Definition: SourceManager.h:89
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
@ HIFIL_None
Definition: HeaderInclude.h:27
@ HIFIL_Only_Direct_System
Definition: HeaderInclude.h:27
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_None
Definition: HeaderInclude.h:22
@ HIFMT_JSON
Definition: HeaderInclude.h:22
@ HIFMT_Textual
Definition: HeaderInclude.h:22
#define false
Definition: stdbool.h:22