Home | History | Annotate | Download | only in Frontend
      1 //===--- HeaderIncludes.cpp - Generate Header Includes --------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "clang/Frontend/Utils.h"
     11 #include "clang/Basic/SourceManager.h"
     12 #include "clang/Frontend/FrontendDiagnostic.h"
     13 #include "clang/Lex/Preprocessor.h"
     14 #include "llvm/ADT/SmallString.h"
     15 #include "llvm/Support/raw_ostream.h"
     16 using namespace clang;
     17 
     18 namespace {
     19 class HeaderIncludesCallback : public PPCallbacks {
     20   SourceManager &SM;
     21   raw_ostream *OutputFile;
     22   unsigned CurrentIncludeDepth;
     23   bool HasProcessedPredefines;
     24   bool OwnsOutputFile;
     25   bool ShowAllHeaders;
     26   bool ShowDepth;
     27   bool MSStyle;
     28 
     29 public:
     30   HeaderIncludesCallback(const Preprocessor *PP, bool ShowAllHeaders_,
     31                          raw_ostream *OutputFile_, bool OwnsOutputFile_,
     32                          bool ShowDepth_, bool MSStyle_)
     33     : SM(PP->getSourceManager()), OutputFile(OutputFile_),
     34       CurrentIncludeDepth(0), HasProcessedPredefines(false),
     35       OwnsOutputFile(OwnsOutputFile_), ShowAllHeaders(ShowAllHeaders_),
     36       ShowDepth(ShowDepth_), MSStyle(MSStyle_) {}
     37 
     38   ~HeaderIncludesCallback() override {
     39     if (OwnsOutputFile)
     40       delete OutputFile;
     41   }
     42 
     43   void FileChanged(SourceLocation Loc, FileChangeReason Reason,
     44                    SrcMgr::CharacteristicKind FileType,
     45                    FileID PrevFID) override;
     46 };
     47 }
     48 
     49 static void PrintHeaderInfo(raw_ostream *OutputFile, const char* Filename,
     50                             bool ShowDepth, unsigned CurrentIncludeDepth,
     51                             bool MSStyle) {
     52     // Write to a temporary string to avoid unnecessary flushing on errs().
     53     SmallString<512> Pathname(Filename);
     54     if (!MSStyle)
     55       Lexer::Stringify(Pathname);
     56 
     57     SmallString<256> Msg;
     58     if (MSStyle)
     59       Msg += "Note: including file:";
     60 
     61     if (ShowDepth) {
     62       // The main source file is at depth 1, so skip one dot.
     63       for (unsigned i = 1; i != CurrentIncludeDepth; ++i)
     64         Msg += MSStyle ? ' ' : '.';
     65 
     66       if (!MSStyle)
     67         Msg += ' ';
     68     }
     69     Msg += Pathname;
     70     Msg += '\n';
     71 
     72     OutputFile->write(Msg.data(), Msg.size());
     73     OutputFile->flush();
     74 }
     75 
     76 void clang::AttachHeaderIncludeGen(Preprocessor &PP,
     77                                    const std::vector<std::string> &ExtraHeaders,
     78                                    bool ShowAllHeaders,
     79                                    StringRef OutputPath, bool ShowDepth,
     80                                    bool MSStyle) {
     81   raw_ostream *OutputFile = MSStyle ? &llvm::outs() : &llvm::errs();
     82   bool OwnsOutputFile = false;
     83 
     84   // Open the output file, if used.
     85   if (!OutputPath.empty()) {
     86     std::error_code EC;
     87     llvm::raw_fd_ostream *OS = new llvm::raw_fd_ostream(
     88         OutputPath.str(), EC, llvm::sys::fs::F_Append | llvm::sys::fs::F_Text);
     89     if (EC) {
     90       PP.getDiagnostics().Report(clang::diag::warn_fe_cc_print_header_failure)
     91           << EC.message();
     92       delete OS;
     93     } else {
     94       OS->SetUnbuffered();
     95       OutputFile = OS;
     96       OwnsOutputFile = true;
     97     }
     98   }
     99 
    100   // Print header info for extra headers, pretending they were discovered
    101   // by the regular preprocessor. The primary use case is to support
    102   // proper generation of Make / Ninja file dependencies for implicit includes,
    103   // such as sanitizer blacklists. It's only important for cl.exe
    104   // compatibility, the GNU way to generate rules is -M / -MM / -MD / -MMD.
    105   for (auto Header : ExtraHeaders) {
    106     PrintHeaderInfo(OutputFile, Header.c_str(), ShowDepth, 2, MSStyle);
    107   }
    108   PP.addPPCallbacks(llvm::make_unique<HeaderIncludesCallback>(&PP,
    109                                                               ShowAllHeaders,
    110                                                               OutputFile,
    111                                                               OwnsOutputFile,
    112                                                               ShowDepth,
    113                                                               MSStyle));
    114 }
    115 
    116 void HeaderIncludesCallback::FileChanged(SourceLocation Loc,
    117                                          FileChangeReason Reason,
    118                                        SrcMgr::CharacteristicKind NewFileType,
    119                                        FileID PrevFID) {
    120   // Unless we are exiting a #include, make sure to skip ahead to the line the
    121   // #include directive was at.
    122   PresumedLoc UserLoc = SM.getPresumedLoc(Loc);
    123   if (UserLoc.isInvalid())
    124     return;
    125 
    126   // Adjust the current include depth.
    127   if (Reason == PPCallbacks::EnterFile) {
    128     ++CurrentIncludeDepth;
    129   } else if (Reason == PPCallbacks::ExitFile) {
    130     if (CurrentIncludeDepth)
    131       --CurrentIncludeDepth;
    132 
    133     // We track when we are done with the predefines by watching for the first
    134     // place where we drop back to a nesting depth of 1.
    135     if (CurrentIncludeDepth == 1 && !HasProcessedPredefines)
    136       HasProcessedPredefines = true;
    137 
    138     return;
    139   } else
    140     return;
    141 
    142   // Show the header if we are (a) past the predefines, or (b) showing all
    143   // headers and in the predefines at a depth past the initial file and command
    144   // line buffers.
    145   bool ShowHeader = (HasProcessedPredefines ||
    146                      (ShowAllHeaders && CurrentIncludeDepth > 2));
    147 
    148   // Dump the header include information we are past the predefines buffer or
    149   // are showing all headers.
    150   if (ShowHeader && Reason == PPCallbacks::EnterFile) {
    151     PrintHeaderInfo(OutputFile, UserLoc.getFilename(),
    152                     ShowDepth, CurrentIncludeDepth, MSStyle);
    153   }
    154 }
    155