xref: /freebsd/contrib/llvm-project/clang/lib/Frontend/HeaderIncludeGen.cpp (revision 37f1f2684f2670b204080ef2d6c303becd28545f)
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  
9  #include "clang/Frontend/DependencyOutputOptions.h"
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    const DependencyOutputOptions &DepOpts;
23    unsigned CurrentIncludeDepth;
24    bool HasProcessedPredefines;
25    bool OwnsOutputFile;
26    bool ShowAllHeaders;
27    bool ShowDepth;
28    bool MSStyle;
29  
30  public:
31    HeaderIncludesCallback(const Preprocessor *PP, bool ShowAllHeaders_,
32                           raw_ostream *OutputFile_,
33                           const DependencyOutputOptions &DepOpts,
34                           bool OwnsOutputFile_, bool ShowDepth_, bool MSStyle_)
35        : SM(PP->getSourceManager()), OutputFile(OutputFile_), DepOpts(DepOpts),
36          CurrentIncludeDepth(0), HasProcessedPredefines(false),
37          OwnsOutputFile(OwnsOutputFile_), ShowAllHeaders(ShowAllHeaders_),
38          ShowDepth(ShowDepth_), MSStyle(MSStyle_) {}
39  
40    ~HeaderIncludesCallback() override {
41      if (OwnsOutputFile)
42        delete OutputFile;
43    }
44  
45    void FileChanged(SourceLocation Loc, FileChangeReason Reason,
46                     SrcMgr::CharacteristicKind FileType,
47                     FileID PrevFID) override;
48  };
49  }
50  
51  static void PrintHeaderInfo(raw_ostream *OutputFile, StringRef Filename,
52                              bool ShowDepth, unsigned CurrentIncludeDepth,
53                              bool MSStyle) {
54    // Write to a temporary string to avoid unnecessary flushing on errs().
55    SmallString<512> Pathname(Filename);
56    if (!MSStyle)
57      Lexer::Stringify(Pathname);
58  
59    SmallString<256> Msg;
60    if (MSStyle)
61      Msg += "Note: including file:";
62  
63    if (ShowDepth) {
64      // The main source file is at depth 1, so skip one dot.
65      for (unsigned i = 1; i != CurrentIncludeDepth; ++i)
66        Msg += MSStyle ? ' ' : '.';
67  
68      if (!MSStyle)
69        Msg += ' ';
70    }
71    Msg += Pathname;
72    Msg += '\n';
73  
74    *OutputFile << Msg;
75    OutputFile->flush();
76  }
77  
78  void clang::AttachHeaderIncludeGen(Preprocessor &PP,
79                                     const DependencyOutputOptions &DepOpts,
80                                     bool ShowAllHeaders, StringRef OutputPath,
81                                     bool ShowDepth, bool MSStyle) {
82    raw_ostream *OutputFile = &llvm::errs();
83    bool OwnsOutputFile = false;
84  
85    // Choose output stream, when printing in cl.exe /showIncludes style.
86    if (MSStyle) {
87      switch (DepOpts.ShowIncludesDest) {
88      default:
89        llvm_unreachable("Invalid destination for /showIncludes output!");
90      case ShowIncludesDestination::Stderr:
91        OutputFile = &llvm::errs();
92        break;
93      case ShowIncludesDestination::Stdout:
94        OutputFile = &llvm::outs();
95        break;
96      }
97    }
98  
99    // Open the output file, if used.
100    if (!OutputPath.empty()) {
101      std::error_code EC;
102      llvm::raw_fd_ostream *OS = new llvm::raw_fd_ostream(
103          OutputPath.str(), EC,
104          llvm::sys::fs::OF_Append | llvm::sys::fs::OF_Text);
105      if (EC) {
106        PP.getDiagnostics().Report(clang::diag::warn_fe_cc_print_header_failure)
107            << EC.message();
108        delete OS;
109      } else {
110        OS->SetUnbuffered();
111        OutputFile = OS;
112        OwnsOutputFile = true;
113      }
114    }
115  
116    // Print header info for extra headers, pretending they were discovered by
117    // the regular preprocessor. The primary use case is to support proper
118    // generation of Make / Ninja file dependencies for implicit includes, such
119    // as sanitizer blacklists. It's only important for cl.exe compatibility,
120    // the GNU way to generate rules is -M / -MM / -MD / -MMD.
121    for (const auto &Header : DepOpts.ExtraDeps)
122      PrintHeaderInfo(OutputFile, Header, ShowDepth, 2, MSStyle);
123    PP.addPPCallbacks(std::make_unique<HeaderIncludesCallback>(
124        &PP, ShowAllHeaders, OutputFile, DepOpts, OwnsOutputFile, ShowDepth,
125        MSStyle));
126  }
127  
128  void HeaderIncludesCallback::FileChanged(SourceLocation Loc,
129                                           FileChangeReason Reason,
130                                         SrcMgr::CharacteristicKind NewFileType,
131                                         FileID PrevFID) {
132    // Unless we are exiting a #include, make sure to skip ahead to the line the
133    // #include directive was at.
134    PresumedLoc UserLoc = SM.getPresumedLoc(Loc);
135    if (UserLoc.isInvalid())
136      return;
137  
138    // Adjust the current include depth.
139    if (Reason == PPCallbacks::EnterFile) {
140      ++CurrentIncludeDepth;
141    } else if (Reason == PPCallbacks::ExitFile) {
142      if (CurrentIncludeDepth)
143        --CurrentIncludeDepth;
144  
145      // We track when we are done with the predefines by watching for the first
146      // place where we drop back to a nesting depth of 1.
147      if (CurrentIncludeDepth == 1 && !HasProcessedPredefines) {
148        if (!DepOpts.ShowIncludesPretendHeader.empty()) {
149          PrintHeaderInfo(OutputFile, DepOpts.ShowIncludesPretendHeader,
150                          ShowDepth, 2, MSStyle);
151        }
152        HasProcessedPredefines = true;
153      }
154  
155      return;
156    } else
157      return;
158  
159    // Show the header if we are (a) past the predefines, or (b) showing all
160    // headers and in the predefines at a depth past the initial file and command
161    // line buffers.
162    bool ShowHeader = (HasProcessedPredefines ||
163                       (ShowAllHeaders && CurrentIncludeDepth > 2));
164    unsigned IncludeDepth = CurrentIncludeDepth;
165    if (!HasProcessedPredefines)
166      --IncludeDepth; // Ignore indent from <built-in>.
167    else if (!DepOpts.ShowIncludesPretendHeader.empty())
168      ++IncludeDepth; // Pretend inclusion by ShowIncludesPretendHeader.
169  
170    // Dump the header include information we are past the predefines buffer or
171    // are showing all headers and this isn't the magic implicit <command line>
172    // header.
173    // FIXME: Identify headers in a more robust way than comparing their name to
174    // "<command line>" and "<built-in>" in a bunch of places.
175    if (ShowHeader && Reason == PPCallbacks::EnterFile &&
176        UserLoc.getFilename() != StringRef("<command line>")) {
177      PrintHeaderInfo(OutputFile, UserLoc.getFilename(), ShowDepth, IncludeDepth,
178                      MSStyle);
179    }
180  }
181