clang 20.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 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 {
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
111static void PrintHeaderInfo(raw_ostream *OutputFile, StringRef Filename,
112 bool ShowDepth, unsigned CurrentIncludeDepth,
113 bool MSStyle) {
114 // Write to a temporary string to avoid unnecessary flushing on errs().
115 SmallString<512> Pathname(Filename);
116 if (!MSStyle)
117 Lexer::Stringify(Pathname);
118
120 if (MSStyle)
121 Msg += "Note: including file:";
122
123 if (ShowDepth) {
124 // The main source file is at depth 1, so skip one dot.
125 for (unsigned i = 1; i != CurrentIncludeDepth; ++i)
126 Msg += MSStyle ? ' ' : '.';
127
128 if (!MSStyle)
129 Msg += ' ';
130 }
131 Msg += Pathname;
132 Msg += '\n';
133
134 *OutputFile << Msg;
135 OutputFile->flush();
136}
137
139 const DependencyOutputOptions &DepOpts,
140 bool ShowAllHeaders, StringRef OutputPath,
141 bool ShowDepth, bool MSStyle) {
142 raw_ostream *OutputFile = &llvm::errs();
143 bool OwnsOutputFile = false;
144
145 // Choose output stream, when printing in cl.exe /showIncludes style.
146 if (MSStyle) {
147 switch (DepOpts.ShowIncludesDest) {
148 default:
149 llvm_unreachable("Invalid destination for /showIncludes output!");
150 case ShowIncludesDestination::Stderr:
151 OutputFile = &llvm::errs();
152 break;
153 case ShowIncludesDestination::Stdout:
154 OutputFile = &llvm::outs();
155 break;
156 }
157 }
158
159 // Open the output file, if used.
160 if (!OutputPath.empty()) {
161 std::error_code EC;
162 llvm::raw_fd_ostream *OS = new llvm::raw_fd_ostream(
163 OutputPath.str(), EC,
164 llvm::sys::fs::OF_Append | llvm::sys::fs::OF_TextWithCRLF);
165 if (EC) {
166 PP.getDiagnostics().Report(clang::diag::warn_fe_cc_print_header_failure)
167 << EC.message();
168 delete OS;
169 } else {
170 OS->SetUnbuffered();
171 OutputFile = OS;
172 OwnsOutputFile = true;
173 }
174 }
175
176 switch (DepOpts.HeaderIncludeFormat) {
177 case HIFMT_None:
178 llvm_unreachable("unexpected header format kind");
179 case HIFMT_Textual: {
180 assert(DepOpts.HeaderIncludeFiltering == HIFIL_None &&
181 "header filtering is currently always disabled when output format is"
182 "textual");
183 // Print header info for extra headers, pretending they were discovered by
184 // the regular preprocessor. The primary use case is to support proper
185 // generation of Make / Ninja file dependencies for implicit includes, such
186 // as sanitizer ignorelists. It's only important for cl.exe compatibility,
187 // the GNU way to generate rules is -M / -MM / -MD / -MMD.
188 for (const auto &Header : DepOpts.ExtraDeps)
189 PrintHeaderInfo(OutputFile, Header.first, ShowDepth, 2, MSStyle);
190 PP.addPPCallbacks(std::make_unique<HeaderIncludesCallback>(
191 &PP, ShowAllHeaders, OutputFile, DepOpts, OwnsOutputFile, ShowDepth,
192 MSStyle));
193 break;
194 }
195 case HIFMT_JSON: {
197 "only-direct-system is the only option for filtering");
198 PP.addPPCallbacks(std::make_unique<HeaderIncludesJSONCallback>(
199 &PP, OutputFile, OwnsOutputFile));
200 break;
201 }
202 }
203}
204
205void HeaderIncludesCallback::FileChanged(SourceLocation Loc,
206 FileChangeReason Reason,
207 SrcMgr::CharacteristicKind NewFileType,
208 FileID PrevFID) {
209 // Unless we are exiting a #include, make sure to skip ahead to the line the
210 // #include directive was at.
211 PresumedLoc UserLoc = SM.getPresumedLoc(Loc);
212 if (UserLoc.isInvalid())
213 return;
214
215 // Adjust the current include depth.
216 if (Reason == PPCallbacks::EnterFile) {
217 ++CurrentIncludeDepth;
218 } else if (Reason == PPCallbacks::ExitFile) {
219 if (CurrentIncludeDepth)
220 --CurrentIncludeDepth;
221
222 // We track when we are done with the predefines by watching for the first
223 // place where we drop back to a nesting depth of 1.
224 if (CurrentIncludeDepth == 1 && !HasProcessedPredefines)
225 HasProcessedPredefines = true;
226
227 return;
228 } else {
229 return;
230 }
231
232 if (!ShouldShowHeader(NewFileType))
233 return;
234
235 unsigned IncludeDepth = CurrentIncludeDepth;
236 if (!HasProcessedPredefines)
237 --IncludeDepth; // Ignore indent from <built-in>.
238
239 // FIXME: Identify headers in a more robust way than comparing their name to
240 // "<command line>" and "<built-in>" in a bunch of places.
241 if (Reason == PPCallbacks::EnterFile &&
242 UserLoc.getFilename() != StringRef("<command line>")) {
243 PrintHeaderInfo(OutputFile, UserLoc.getFilename(), ShowDepth, IncludeDepth,
244 MSStyle);
245 }
246}
247
248void HeaderIncludesCallback::FileSkipped(const FileEntryRef &SkippedFile, const
249 Token &FilenameTok,
251 if (!DepOpts.ShowSkippedHeaderIncludes)
252 return;
253
254 if (!ShouldShowHeader(FileType))
255 return;
256
257 PrintHeaderInfo(OutputFile, SkippedFile.getName(), ShowDepth,
258 CurrentIncludeDepth + 1, MSStyle);
259}
260
261void HeaderIncludesJSONCallback::EndOfMainFile() {
262 OptionalFileEntryRef FE = SM.getFileEntryRefForID(SM.getMainFileID());
263 SmallString<256> MainFile(FE->getName());
264 SM.getFileManager().makeAbsolutePath(MainFile);
265
266 std::string Str;
267 llvm::raw_string_ostream OS(Str);
268 llvm::json::OStream JOS(OS);
269 JOS.object([&] {
270 JOS.attribute("source", MainFile.c_str());
271 JOS.attributeArray("includes", [&] {
272 llvm::StringSet<> SeenHeaders;
273 for (const std::string &H : IncludedHeaders)
274 if (SeenHeaders.insert(H).second)
275 JOS.value(H);
276 });
277 });
278 OS << "\n";
279
280 if (OutputFile->get_kind() == raw_ostream::OStreamKind::OK_FDStream) {
281 llvm::raw_fd_ostream *FDS = static_cast<llvm::raw_fd_ostream *>(OutputFile);
282 if (auto L = FDS->lock())
283 *OutputFile << Str;
284 } else
285 *OutputFile << Str;
286}
287
288/// Determine whether the header file should be recorded. The header file should
289/// be recorded only if the header file is a system header and the current file
290/// isn't a system header.
292 SourceLocation PrevLoc, SourceManager &SM) {
293 return SrcMgr::isSystem(NewFileType) && !SM.isInSystemHeader(PrevLoc);
294}
295
296void HeaderIncludesJSONCallback::FileChanged(
297 SourceLocation Loc, FileChangeReason Reason,
298 SrcMgr::CharacteristicKind NewFileType, FileID PrevFID) {
299 if (PrevFID.isInvalid() ||
300 !shouldRecordNewFile(NewFileType, SM.getLocForStartOfFile(PrevFID), SM))
301 return;
302
303 // Unless we are exiting a #include, make sure to skip ahead to the line the
304 // #include directive was at.
305 PresumedLoc UserLoc = SM.getPresumedLoc(Loc);
306 if (UserLoc.isInvalid())
307 return;
308
309 if (Reason == PPCallbacks::EnterFile &&
310 UserLoc.getFilename() != StringRef("<command line>"))
311 IncludedHeaders.push_back(UserLoc.getFilename());
312}
313
314void HeaderIncludesJSONCallback::FileSkipped(
315 const FileEntryRef &SkippedFile, const Token &FilenameTok,
317 if (!shouldRecordNewFile(FileType, FilenameTok.getLocation(), SM))
318 return;
319
320 IncludedHeaders.push_back(SkippedFile.getName().str());
321}
#define SM(sm)
Definition: Cuda.cpp:83
StringRef Filename
Definition: Format.cpp:3001
llvm::json::OStream & JOS
Definition: HTMLLogger.cpp:147
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
Defines the clang::Preprocessor interface.
SourceLocation Loc
Definition: SemaObjC.cpp:759
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.
unsigned IncludeSystemHeaders
Include system header dependencies.
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1547
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
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:310
This interface provides a way to observe the actions of the preprocessor as it does its thing.
Definition: PPCallbacks.h:36
virtual void EndOfMainFile()
Callback invoked when the end of the main file is reached.
Definition: PPCallbacks.h:217
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:49
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:83
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:137
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:36
SourceLocation getLocation() const
Return a source location identifier for the specified offset in the current file.
Definition: Token.h:132
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
Definition: SourceManager.h:81
bool isSystem(CharacteristicKind CK)
Determine whether a file / directory characteristic is for system code.
Definition: SourceManager.h:90
The JSON file list parser is used to communicate input to InstallAPI.
@ 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:26