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