xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/Frontend/HeaderIncludeGen.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //===-- HeaderIncludeGen.cpp - Generate Header Includes -------------------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg 
97330f729Sjoerg #include "clang/Frontend/DependencyOutputOptions.h"
107330f729Sjoerg #include "clang/Frontend/Utils.h"
117330f729Sjoerg #include "clang/Basic/SourceManager.h"
127330f729Sjoerg #include "clang/Frontend/FrontendDiagnostic.h"
137330f729Sjoerg #include "clang/Lex/Preprocessor.h"
147330f729Sjoerg #include "llvm/ADT/SmallString.h"
157330f729Sjoerg #include "llvm/Support/raw_ostream.h"
167330f729Sjoerg using namespace clang;
177330f729Sjoerg 
187330f729Sjoerg namespace {
197330f729Sjoerg class HeaderIncludesCallback : public PPCallbacks {
207330f729Sjoerg   SourceManager &SM;
217330f729Sjoerg   raw_ostream *OutputFile;
227330f729Sjoerg   const DependencyOutputOptions &DepOpts;
237330f729Sjoerg   unsigned CurrentIncludeDepth;
247330f729Sjoerg   bool HasProcessedPredefines;
257330f729Sjoerg   bool OwnsOutputFile;
267330f729Sjoerg   bool ShowAllHeaders;
277330f729Sjoerg   bool ShowDepth;
287330f729Sjoerg   bool MSStyle;
297330f729Sjoerg 
307330f729Sjoerg public:
HeaderIncludesCallback(const Preprocessor * PP,bool ShowAllHeaders_,raw_ostream * OutputFile_,const DependencyOutputOptions & DepOpts,bool OwnsOutputFile_,bool ShowDepth_,bool MSStyle_)317330f729Sjoerg   HeaderIncludesCallback(const Preprocessor *PP, bool ShowAllHeaders_,
327330f729Sjoerg                          raw_ostream *OutputFile_,
337330f729Sjoerg                          const DependencyOutputOptions &DepOpts,
347330f729Sjoerg                          bool OwnsOutputFile_, bool ShowDepth_, bool MSStyle_)
357330f729Sjoerg       : SM(PP->getSourceManager()), OutputFile(OutputFile_), DepOpts(DepOpts),
367330f729Sjoerg         CurrentIncludeDepth(0), HasProcessedPredefines(false),
377330f729Sjoerg         OwnsOutputFile(OwnsOutputFile_), ShowAllHeaders(ShowAllHeaders_),
387330f729Sjoerg         ShowDepth(ShowDepth_), MSStyle(MSStyle_) {}
397330f729Sjoerg 
~HeaderIncludesCallback()407330f729Sjoerg   ~HeaderIncludesCallback() override {
417330f729Sjoerg     if (OwnsOutputFile)
427330f729Sjoerg       delete OutputFile;
437330f729Sjoerg   }
447330f729Sjoerg 
457330f729Sjoerg   void FileChanged(SourceLocation Loc, FileChangeReason Reason,
467330f729Sjoerg                    SrcMgr::CharacteristicKind FileType,
477330f729Sjoerg                    FileID PrevFID) override;
48*e038c9c4Sjoerg 
49*e038c9c4Sjoerg   void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
50*e038c9c4Sjoerg                    SrcMgr::CharacteristicKind FileType) override;
517330f729Sjoerg };
527330f729Sjoerg }
537330f729Sjoerg 
PrintHeaderInfo(raw_ostream * OutputFile,StringRef Filename,bool ShowDepth,unsigned CurrentIncludeDepth,bool MSStyle)547330f729Sjoerg static void PrintHeaderInfo(raw_ostream *OutputFile, StringRef Filename,
557330f729Sjoerg                             bool ShowDepth, unsigned CurrentIncludeDepth,
567330f729Sjoerg                             bool MSStyle) {
577330f729Sjoerg   // Write to a temporary string to avoid unnecessary flushing on errs().
587330f729Sjoerg   SmallString<512> Pathname(Filename);
597330f729Sjoerg   if (!MSStyle)
607330f729Sjoerg     Lexer::Stringify(Pathname);
617330f729Sjoerg 
627330f729Sjoerg   SmallString<256> Msg;
637330f729Sjoerg   if (MSStyle)
647330f729Sjoerg     Msg += "Note: including file:";
657330f729Sjoerg 
667330f729Sjoerg   if (ShowDepth) {
677330f729Sjoerg     // The main source file is at depth 1, so skip one dot.
687330f729Sjoerg     for (unsigned i = 1; i != CurrentIncludeDepth; ++i)
697330f729Sjoerg       Msg += MSStyle ? ' ' : '.';
707330f729Sjoerg 
717330f729Sjoerg     if (!MSStyle)
727330f729Sjoerg       Msg += ' ';
737330f729Sjoerg   }
747330f729Sjoerg   Msg += Pathname;
757330f729Sjoerg   Msg += '\n';
767330f729Sjoerg 
777330f729Sjoerg   *OutputFile << Msg;
787330f729Sjoerg   OutputFile->flush();
797330f729Sjoerg }
807330f729Sjoerg 
AttachHeaderIncludeGen(Preprocessor & PP,const DependencyOutputOptions & DepOpts,bool ShowAllHeaders,StringRef OutputPath,bool ShowDepth,bool MSStyle)817330f729Sjoerg void clang::AttachHeaderIncludeGen(Preprocessor &PP,
827330f729Sjoerg                                    const DependencyOutputOptions &DepOpts,
837330f729Sjoerg                                    bool ShowAllHeaders, StringRef OutputPath,
847330f729Sjoerg                                    bool ShowDepth, bool MSStyle) {
857330f729Sjoerg   raw_ostream *OutputFile = &llvm::errs();
867330f729Sjoerg   bool OwnsOutputFile = false;
877330f729Sjoerg 
887330f729Sjoerg   // Choose output stream, when printing in cl.exe /showIncludes style.
897330f729Sjoerg   if (MSStyle) {
907330f729Sjoerg     switch (DepOpts.ShowIncludesDest) {
917330f729Sjoerg     default:
927330f729Sjoerg       llvm_unreachable("Invalid destination for /showIncludes output!");
937330f729Sjoerg     case ShowIncludesDestination::Stderr:
947330f729Sjoerg       OutputFile = &llvm::errs();
957330f729Sjoerg       break;
967330f729Sjoerg     case ShowIncludesDestination::Stdout:
977330f729Sjoerg       OutputFile = &llvm::outs();
987330f729Sjoerg       break;
997330f729Sjoerg     }
1007330f729Sjoerg   }
1017330f729Sjoerg 
1027330f729Sjoerg   // Open the output file, if used.
1037330f729Sjoerg   if (!OutputPath.empty()) {
1047330f729Sjoerg     std::error_code EC;
1057330f729Sjoerg     llvm::raw_fd_ostream *OS = new llvm::raw_fd_ostream(
1067330f729Sjoerg         OutputPath.str(), EC,
107*e038c9c4Sjoerg         llvm::sys::fs::OF_Append | llvm::sys::fs::OF_TextWithCRLF);
1087330f729Sjoerg     if (EC) {
1097330f729Sjoerg       PP.getDiagnostics().Report(clang::diag::warn_fe_cc_print_header_failure)
1107330f729Sjoerg           << EC.message();
1117330f729Sjoerg       delete OS;
1127330f729Sjoerg     } else {
1137330f729Sjoerg       OS->SetUnbuffered();
1147330f729Sjoerg       OutputFile = OS;
1157330f729Sjoerg       OwnsOutputFile = true;
1167330f729Sjoerg     }
1177330f729Sjoerg   }
1187330f729Sjoerg 
1197330f729Sjoerg   // Print header info for extra headers, pretending they were discovered by
1207330f729Sjoerg   // the regular preprocessor. The primary use case is to support proper
1217330f729Sjoerg   // generation of Make / Ninja file dependencies for implicit includes, such
1227330f729Sjoerg   // as sanitizer blacklists. It's only important for cl.exe compatibility,
1237330f729Sjoerg   // the GNU way to generate rules is -M / -MM / -MD / -MMD.
1247330f729Sjoerg   for (const auto &Header : DepOpts.ExtraDeps)
125*e038c9c4Sjoerg     PrintHeaderInfo(OutputFile, Header.first, ShowDepth, 2, MSStyle);
1267330f729Sjoerg   PP.addPPCallbacks(std::make_unique<HeaderIncludesCallback>(
1277330f729Sjoerg       &PP, ShowAllHeaders, OutputFile, DepOpts, OwnsOutputFile, ShowDepth,
1287330f729Sjoerg       MSStyle));
1297330f729Sjoerg }
1307330f729Sjoerg 
FileChanged(SourceLocation Loc,FileChangeReason Reason,SrcMgr::CharacteristicKind NewFileType,FileID PrevFID)1317330f729Sjoerg void HeaderIncludesCallback::FileChanged(SourceLocation Loc,
1327330f729Sjoerg                                          FileChangeReason Reason,
1337330f729Sjoerg                                          SrcMgr::CharacteristicKind NewFileType,
1347330f729Sjoerg                                          FileID PrevFID) {
1357330f729Sjoerg   // Unless we are exiting a #include, make sure to skip ahead to the line the
1367330f729Sjoerg   // #include directive was at.
1377330f729Sjoerg   PresumedLoc UserLoc = SM.getPresumedLoc(Loc);
1387330f729Sjoerg   if (UserLoc.isInvalid())
1397330f729Sjoerg     return;
1407330f729Sjoerg 
1417330f729Sjoerg   // Adjust the current include depth.
1427330f729Sjoerg   if (Reason == PPCallbacks::EnterFile) {
1437330f729Sjoerg     ++CurrentIncludeDepth;
1447330f729Sjoerg   } else if (Reason == PPCallbacks::ExitFile) {
1457330f729Sjoerg     if (CurrentIncludeDepth)
1467330f729Sjoerg       --CurrentIncludeDepth;
1477330f729Sjoerg 
1487330f729Sjoerg     // We track when we are done with the predefines by watching for the first
1497330f729Sjoerg     // place where we drop back to a nesting depth of 1.
1507330f729Sjoerg     if (CurrentIncludeDepth == 1 && !HasProcessedPredefines) {
1517330f729Sjoerg       if (!DepOpts.ShowIncludesPretendHeader.empty()) {
1527330f729Sjoerg         PrintHeaderInfo(OutputFile, DepOpts.ShowIncludesPretendHeader,
1537330f729Sjoerg                         ShowDepth, 2, MSStyle);
1547330f729Sjoerg       }
1557330f729Sjoerg       HasProcessedPredefines = true;
1567330f729Sjoerg     }
1577330f729Sjoerg 
1587330f729Sjoerg     return;
1597330f729Sjoerg   } else
1607330f729Sjoerg     return;
1617330f729Sjoerg 
1627330f729Sjoerg   // Show the header if we are (a) past the predefines, or (b) showing all
1637330f729Sjoerg   // headers and in the predefines at a depth past the initial file and command
1647330f729Sjoerg   // line buffers.
1657330f729Sjoerg   bool ShowHeader = (HasProcessedPredefines ||
1667330f729Sjoerg                      (ShowAllHeaders && CurrentIncludeDepth > 2));
1677330f729Sjoerg   unsigned IncludeDepth = CurrentIncludeDepth;
1687330f729Sjoerg   if (!HasProcessedPredefines)
1697330f729Sjoerg     --IncludeDepth; // Ignore indent from <built-in>.
1707330f729Sjoerg   else if (!DepOpts.ShowIncludesPretendHeader.empty())
1717330f729Sjoerg     ++IncludeDepth; // Pretend inclusion by ShowIncludesPretendHeader.
1727330f729Sjoerg 
173*e038c9c4Sjoerg   if (!DepOpts.IncludeSystemHeaders && isSystem(NewFileType))
174*e038c9c4Sjoerg     ShowHeader = false;
175*e038c9c4Sjoerg 
1767330f729Sjoerg   // Dump the header include information we are past the predefines buffer or
1777330f729Sjoerg   // are showing all headers and this isn't the magic implicit <command line>
1787330f729Sjoerg   // header.
1797330f729Sjoerg   // FIXME: Identify headers in a more robust way than comparing their name to
1807330f729Sjoerg   // "<command line>" and "<built-in>" in a bunch of places.
1817330f729Sjoerg   if (ShowHeader && Reason == PPCallbacks::EnterFile &&
1827330f729Sjoerg       UserLoc.getFilename() != StringRef("<command line>")) {
1837330f729Sjoerg     PrintHeaderInfo(OutputFile, UserLoc.getFilename(), ShowDepth, IncludeDepth,
1847330f729Sjoerg                     MSStyle);
1857330f729Sjoerg   }
1867330f729Sjoerg }
187*e038c9c4Sjoerg 
FileSkipped(const FileEntryRef & SkippedFile,const Token & FilenameTok,SrcMgr::CharacteristicKind FileType)188*e038c9c4Sjoerg void HeaderIncludesCallback::FileSkipped(const FileEntryRef &SkippedFile, const
189*e038c9c4Sjoerg                                          Token &FilenameTok,
190*e038c9c4Sjoerg                                          SrcMgr::CharacteristicKind FileType) {
191*e038c9c4Sjoerg   if (!DepOpts.ShowSkippedHeaderIncludes)
192*e038c9c4Sjoerg     return;
193*e038c9c4Sjoerg 
194*e038c9c4Sjoerg   if (!DepOpts.IncludeSystemHeaders && isSystem(FileType))
195*e038c9c4Sjoerg     return;
196*e038c9c4Sjoerg 
197*e038c9c4Sjoerg   PrintHeaderInfo(OutputFile, SkippedFile.getName(), ShowDepth,
198*e038c9c4Sjoerg                   CurrentIncludeDepth + 1, MSStyle);
199*e038c9c4Sjoerg }
200