1524b3c18SFangrui Song //===-- HeaderIncludeGen.cpp - Generate Header Includes -------------------===//
227734fdbSDaniel Dunbar //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
627734fdbSDaniel Dunbar //
727734fdbSDaniel Dunbar //===----------------------------------------------------------------------===//
827734fdbSDaniel Dunbar
9f54146c1SNico Weber #include "clang/Frontend/DependencyOutputOptions.h"
1027734fdbSDaniel Dunbar #include "clang/Frontend/Utils.h"
1127734fdbSDaniel Dunbar #include "clang/Basic/SourceManager.h"
129aa47fcbSDaniel Dunbar #include "clang/Frontend/FrontendDiagnostic.h"
1327734fdbSDaniel Dunbar #include "clang/Lex/Preprocessor.h"
144903802fSBenjamin Kramer #include "llvm/ADT/SmallString.h"
159aa47fcbSDaniel Dunbar #include "llvm/Support/raw_ostream.h"
1627734fdbSDaniel Dunbar using namespace clang;
1727734fdbSDaniel Dunbar
1827734fdbSDaniel Dunbar namespace {
1927734fdbSDaniel Dunbar class HeaderIncludesCallback : public PPCallbacks {
2027734fdbSDaniel Dunbar SourceManager &SM;
210e62c1ccSChris Lattner raw_ostream *OutputFile;
22f54146c1SNico Weber const DependencyOutputOptions &DepOpts;
2327734fdbSDaniel Dunbar unsigned CurrentIncludeDepth;
2427734fdbSDaniel Dunbar bool HasProcessedPredefines;
251af1d275SDaniel Dunbar bool OwnsOutputFile;
261af1d275SDaniel Dunbar bool ShowAllHeaders;
27fe908a80SDaniel Dunbar bool ShowDepth;
280fd6207dSHans Wennborg bool MSStyle;
2927734fdbSDaniel Dunbar
3027734fdbSDaniel Dunbar public:
HeaderIncludesCallback(const Preprocessor * PP,bool ShowAllHeaders_,raw_ostream * OutputFile_,const DependencyOutputOptions & DepOpts,bool OwnsOutputFile_,bool ShowDepth_,bool MSStyle_)311af1d275SDaniel Dunbar HeaderIncludesCallback(const Preprocessor *PP, bool ShowAllHeaders_,
324b5aedefSNico Weber raw_ostream *OutputFile_,
33f54146c1SNico Weber const DependencyOutputOptions &DepOpts,
344b5aedefSNico Weber bool OwnsOutputFile_, bool ShowDepth_, bool MSStyle_)
35f54146c1SNico Weber : SM(PP->getSourceManager()), OutputFile(OutputFile_), DepOpts(DepOpts),
36f54146c1SNico Weber CurrentIncludeDepth(0), HasProcessedPredefines(false),
37f54146c1SNico Weber OwnsOutputFile(OwnsOutputFile_), ShowAllHeaders(ShowAllHeaders_),
38f54146c1SNico Weber ShowDepth(ShowDepth_), MSStyle(MSStyle_) {}
391af1d275SDaniel Dunbar
~HeaderIncludesCallback()4034eb2072SAlexander Kornienko ~HeaderIncludesCallback() override {
411af1d275SDaniel Dunbar if (OwnsOutputFile)
429aa47fcbSDaniel Dunbar delete OutputFile;
431af1d275SDaniel Dunbar }
4427734fdbSDaniel Dunbar
45afa7cb3aSCraig Topper void FileChanged(SourceLocation Loc, FileChangeReason Reason,
467a70d2f1SArgyrios Kyrtzidis SrcMgr::CharacteristicKind FileType,
47afa7cb3aSCraig Topper FileID PrevFID) override;
48f29dcbddSHans Wennborg
49f29dcbddSHans Wennborg void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
50f29dcbddSHans Wennborg SrcMgr::CharacteristicKind FileType) override;
5127734fdbSDaniel Dunbar };
52ab9db510SAlexander Kornienko }
5327734fdbSDaniel Dunbar
PrintHeaderInfo(raw_ostream * OutputFile,StringRef Filename,bool ShowDepth,unsigned CurrentIncludeDepth,bool MSStyle)542787e459SBenjamin Kramer static void PrintHeaderInfo(raw_ostream *OutputFile, StringRef Filename,
551193f2cbSIvan Krasin bool ShowDepth, unsigned CurrentIncludeDepth,
561193f2cbSIvan Krasin bool MSStyle) {
571193f2cbSIvan Krasin // Write to a temporary string to avoid unnecessary flushing on errs().
581193f2cbSIvan Krasin SmallString<512> Pathname(Filename);
591193f2cbSIvan Krasin if (!MSStyle)
601193f2cbSIvan Krasin Lexer::Stringify(Pathname);
611193f2cbSIvan Krasin
621193f2cbSIvan Krasin SmallString<256> Msg;
631193f2cbSIvan Krasin if (MSStyle)
641193f2cbSIvan Krasin Msg += "Note: including file:";
651193f2cbSIvan Krasin
661193f2cbSIvan Krasin if (ShowDepth) {
671193f2cbSIvan Krasin // The main source file is at depth 1, so skip one dot.
681193f2cbSIvan Krasin for (unsigned i = 1; i != CurrentIncludeDepth; ++i)
691193f2cbSIvan Krasin Msg += MSStyle ? ' ' : '.';
701193f2cbSIvan Krasin
711193f2cbSIvan Krasin if (!MSStyle)
721193f2cbSIvan Krasin Msg += ' ';
731193f2cbSIvan Krasin }
741193f2cbSIvan Krasin Msg += Pathname;
751193f2cbSIvan Krasin Msg += '\n';
761193f2cbSIvan Krasin
772787e459SBenjamin Kramer *OutputFile << Msg;
781193f2cbSIvan Krasin OutputFile->flush();
791193f2cbSIvan Krasin }
801193f2cbSIvan Krasin
AttachHeaderIncludeGen(Preprocessor & PP,const DependencyOutputOptions & DepOpts,bool ShowAllHeaders,StringRef OutputPath,bool ShowDepth,bool MSStyle)811193f2cbSIvan Krasin void clang::AttachHeaderIncludeGen(Preprocessor &PP,
82f54146c1SNico Weber const DependencyOutputOptions &DepOpts,
83f54146c1SNico Weber bool ShowAllHeaders, StringRef OutputPath,
84f54146c1SNico Weber bool ShowDepth, bool MSStyle) {
85425f48d4SErich Keane raw_ostream *OutputFile = &llvm::errs();
869aa47fcbSDaniel Dunbar bool OwnsOutputFile = false;
871af1d275SDaniel Dunbar
88425f48d4SErich Keane // Choose output stream, when printing in cl.exe /showIncludes style.
89425f48d4SErich Keane if (MSStyle) {
90425f48d4SErich Keane switch (DepOpts.ShowIncludesDest) {
91425f48d4SErich Keane default:
92425f48d4SErich Keane llvm_unreachable("Invalid destination for /showIncludes output!");
93425f48d4SErich Keane case ShowIncludesDestination::Stderr:
94425f48d4SErich Keane OutputFile = &llvm::errs();
95425f48d4SErich Keane break;
96425f48d4SErich Keane case ShowIncludesDestination::Stdout:
97425f48d4SErich Keane OutputFile = &llvm::outs();
98425f48d4SErich Keane break;
99425f48d4SErich Keane }
100425f48d4SErich Keane }
101425f48d4SErich Keane
1021af1d275SDaniel Dunbar // Open the output file, if used.
1039aa47fcbSDaniel Dunbar if (!OutputPath.empty()) {
104dae941a6SRafael Espindola std::error_code EC;
1059aa47fcbSDaniel Dunbar llvm::raw_fd_ostream *OS = new llvm::raw_fd_ostream(
106d9b948b6SFangrui Song OutputPath.str(), EC,
10782b3e28eSAbhina Sreeskantharajan llvm::sys::fs::OF_Append | llvm::sys::fs::OF_TextWithCRLF);
108dae941a6SRafael Espindola if (EC) {
109dae941a6SRafael Espindola PP.getDiagnostics().Report(clang::diag::warn_fe_cc_print_header_failure)
110dae941a6SRafael Espindola << EC.message();
1119aa47fcbSDaniel Dunbar delete OS;
1121af1d275SDaniel Dunbar } else {
1139aa47fcbSDaniel Dunbar OS->SetUnbuffered();
1149aa47fcbSDaniel Dunbar OutputFile = OS;
1151af1d275SDaniel Dunbar OwnsOutputFile = true;
1161af1d275SDaniel Dunbar }
1179aa47fcbSDaniel Dunbar }
1181af1d275SDaniel Dunbar
119f54146c1SNico Weber // Print header info for extra headers, pretending they were discovered by
120f54146c1SNico Weber // the regular preprocessor. The primary use case is to support proper
121f54146c1SNico Weber // generation of Make / Ninja file dependencies for implicit includes, such
122*05f34ffaSZarko Todorovski // as sanitizer ignorelists. It's only important for cl.exe compatibility,
123f54146c1SNico Weber // the GNU way to generate rules is -M / -MM / -MD / -MMD.
1242787e459SBenjamin Kramer for (const auto &Header : DepOpts.ExtraDeps)
12517e5c99dSJan Svoboda PrintHeaderInfo(OutputFile, Header.first, ShowDepth, 2, MSStyle);
1262b3d49b6SJonas Devlieghere PP.addPPCallbacks(std::make_unique<HeaderIncludesCallback>(
127f54146c1SNico Weber &PP, ShowAllHeaders, OutputFile, DepOpts, OwnsOutputFile, ShowDepth,
128b8a70530SCraig Topper MSStyle));
12927734fdbSDaniel Dunbar }
13027734fdbSDaniel Dunbar
FileChanged(SourceLocation Loc,FileChangeReason Reason,SrcMgr::CharacteristicKind NewFileType,FileID PrevFID)13127734fdbSDaniel Dunbar void HeaderIncludesCallback::FileChanged(SourceLocation Loc,
13227734fdbSDaniel Dunbar FileChangeReason Reason,
1337a70d2f1SArgyrios Kyrtzidis SrcMgr::CharacteristicKind NewFileType,
1347a70d2f1SArgyrios Kyrtzidis FileID PrevFID) {
13527734fdbSDaniel Dunbar // Unless we are exiting a #include, make sure to skip ahead to the line the
13627734fdbSDaniel Dunbar // #include directive was at.
13727734fdbSDaniel Dunbar PresumedLoc UserLoc = SM.getPresumedLoc(Loc);
13827734fdbSDaniel Dunbar if (UserLoc.isInvalid())
13927734fdbSDaniel Dunbar return;
14027734fdbSDaniel Dunbar
14127734fdbSDaniel Dunbar // Adjust the current include depth.
14227734fdbSDaniel Dunbar if (Reason == PPCallbacks::EnterFile) {
14327734fdbSDaniel Dunbar ++CurrentIncludeDepth;
1444e52123dSSebastian Redl } else if (Reason == PPCallbacks::ExitFile) {
14527734fdbSDaniel Dunbar if (CurrentIncludeDepth)
14627734fdbSDaniel Dunbar --CurrentIncludeDepth;
14727734fdbSDaniel Dunbar
14827734fdbSDaniel Dunbar // We track when we are done with the predefines by watching for the first
1494e52123dSSebastian Redl // place where we drop back to a nesting depth of 1.
150f54146c1SNico Weber if (CurrentIncludeDepth == 1 && !HasProcessedPredefines) {
151f54146c1SNico Weber if (!DepOpts.ShowIncludesPretendHeader.empty()) {
1522787e459SBenjamin Kramer PrintHeaderInfo(OutputFile, DepOpts.ShowIncludesPretendHeader,
153f54146c1SNico Weber ShowDepth, 2, MSStyle);
154f54146c1SNico Weber }
15527734fdbSDaniel Dunbar HasProcessedPredefines = true;
156f54146c1SNico Weber }
1574e52123dSSebastian Redl
1584e52123dSSebastian Redl return;
1594e52123dSSebastian Redl } else
1604e52123dSSebastian Redl return;
16127734fdbSDaniel Dunbar
162fb244857SDaniel Dunbar // Show the header if we are (a) past the predefines, or (b) showing all
163fb244857SDaniel Dunbar // headers and in the predefines at a depth past the initial file and command
164fb244857SDaniel Dunbar // line buffers.
165fb244857SDaniel Dunbar bool ShowHeader = (HasProcessedPredefines ||
166fb244857SDaniel Dunbar (ShowAllHeaders && CurrentIncludeDepth > 2));
167149d9522SNico Weber unsigned IncludeDepth = CurrentIncludeDepth;
168149d9522SNico Weber if (!HasProcessedPredefines)
169149d9522SNico Weber --IncludeDepth; // Ignore indent from <built-in>.
170f54146c1SNico Weber else if (!DepOpts.ShowIncludesPretendHeader.empty())
171f54146c1SNico Weber ++IncludeDepth; // Pretend inclusion by ShowIncludesPretendHeader.
172bcda1269SNico Weber
173bcda1269SNico Weber if (!DepOpts.IncludeSystemHeaders && isSystem(NewFileType))
174bcda1269SNico Weber ShowHeader = false;
175fb244857SDaniel Dunbar
176fb244857SDaniel Dunbar // Dump the header include information we are past the predefines buffer or
177149d9522SNico Weber // are showing all headers and this isn't the magic implicit <command line>
178149d9522SNico Weber // header.
179149d9522SNico Weber // FIXME: Identify headers in a more robust way than comparing their name to
180149d9522SNico Weber // "<command line>" and "<built-in>" in a bunch of places.
181149d9522SNico Weber if (ShowHeader && Reason == PPCallbacks::EnterFile &&
182149d9522SNico Weber UserLoc.getFilename() != StringRef("<command line>")) {
183149d9522SNico Weber PrintHeaderInfo(OutputFile, UserLoc.getFilename(), ShowDepth, IncludeDepth,
184149d9522SNico Weber MSStyle);
18527734fdbSDaniel Dunbar }
18627734fdbSDaniel Dunbar }
187f29dcbddSHans Wennborg
FileSkipped(const FileEntryRef & SkippedFile,const Token & FilenameTok,SrcMgr::CharacteristicKind FileType)188f29dcbddSHans Wennborg void HeaderIncludesCallback::FileSkipped(const FileEntryRef &SkippedFile, const
189f29dcbddSHans Wennborg Token &FilenameTok,
190f29dcbddSHans Wennborg SrcMgr::CharacteristicKind FileType) {
191f29dcbddSHans Wennborg if (!DepOpts.ShowSkippedHeaderIncludes)
192f29dcbddSHans Wennborg return;
193f29dcbddSHans Wennborg
194f29dcbddSHans Wennborg if (!DepOpts.IncludeSystemHeaders && isSystem(FileType))
195f29dcbddSHans Wennborg return;
196f29dcbddSHans Wennborg
197f29dcbddSHans Wennborg PrintHeaderInfo(OutputFile, SkippedFile.getName(), ShowDepth,
198f29dcbddSHans Wennborg CurrentIncludeDepth + 1, MSStyle);
199f29dcbddSHans Wennborg }
200