1353358Sdim//===-- HeaderIncludeGen.cpp - Generate Header Includes -------------------===//
2218887Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6218887Sdim//
7218887Sdim//===----------------------------------------------------------------------===//
8218887Sdim
9309124Sdim#include "clang/Frontend/DependencyOutputOptions.h"
10218887Sdim#include "clang/Frontend/Utils.h"
11218887Sdim#include "clang/Basic/SourceManager.h"
12218887Sdim#include "clang/Frontend/FrontendDiagnostic.h"
13218887Sdim#include "clang/Lex/Preprocessor.h"
14234353Sdim#include "llvm/ADT/SmallString.h"
15218887Sdim#include "llvm/Support/raw_ostream.h"
16218887Sdimusing namespace clang;
17218887Sdim
18218887Sdimnamespace {
19218887Sdimclass HeaderIncludesCallback : public PPCallbacks {
20218887Sdim  SourceManager &SM;
21226633Sdim  raw_ostream *OutputFile;
22309124Sdim  const DependencyOutputOptions &DepOpts;
23218887Sdim  unsigned CurrentIncludeDepth;
24218887Sdim  bool HasProcessedPredefines;
25218887Sdim  bool OwnsOutputFile;
26218887Sdim  bool ShowAllHeaders;
27221345Sdim  bool ShowDepth;
28261991Sdim  bool MSStyle;
29218887Sdim
30218887Sdimpublic:
31218887Sdim  HeaderIncludesCallback(const Preprocessor *PP, bool ShowAllHeaders_,
32309124Sdim                         raw_ostream *OutputFile_,
33309124Sdim                         const DependencyOutputOptions &DepOpts,
34309124Sdim                         bool OwnsOutputFile_, bool ShowDepth_, bool MSStyle_)
35309124Sdim      : SM(PP->getSourceManager()), OutputFile(OutputFile_), DepOpts(DepOpts),
36309124Sdim        CurrentIncludeDepth(0), HasProcessedPredefines(false),
37309124Sdim        OwnsOutputFile(OwnsOutputFile_), ShowAllHeaders(ShowAllHeaders_),
38309124Sdim        ShowDepth(ShowDepth_), MSStyle(MSStyle_) {}
39218887Sdim
40288943Sdim  ~HeaderIncludesCallback() override {
41218887Sdim    if (OwnsOutputFile)
42218887Sdim      delete OutputFile;
43218887Sdim  }
44218887Sdim
45276479Sdim  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
46276479Sdim                   SrcMgr::CharacteristicKind FileType,
47276479Sdim                   FileID PrevFID) override;
48218887Sdim};
49218887Sdim}
50218887Sdim
51309124Sdimstatic void PrintHeaderInfo(raw_ostream *OutputFile, StringRef Filename,
52296417Sdim                            bool ShowDepth, unsigned CurrentIncludeDepth,
53296417Sdim                            bool MSStyle) {
54309124Sdim  // Write to a temporary string to avoid unnecessary flushing on errs().
55309124Sdim  SmallString<512> Pathname(Filename);
56309124Sdim  if (!MSStyle)
57309124Sdim    Lexer::Stringify(Pathname);
58296417Sdim
59309124Sdim  SmallString<256> Msg;
60309124Sdim  if (MSStyle)
61309124Sdim    Msg += "Note: including file:";
62296417Sdim
63309124Sdim  if (ShowDepth) {
64309124Sdim    // The main source file is at depth 1, so skip one dot.
65309124Sdim    for (unsigned i = 1; i != CurrentIncludeDepth; ++i)
66309124Sdim      Msg += MSStyle ? ' ' : '.';
67296417Sdim
68309124Sdim    if (!MSStyle)
69309124Sdim      Msg += ' ';
70309124Sdim  }
71309124Sdim  Msg += Pathname;
72309124Sdim  Msg += '\n';
73296417Sdim
74309124Sdim  *OutputFile << Msg;
75309124Sdim  OutputFile->flush();
76296417Sdim}
77296417Sdim
78296417Sdimvoid clang::AttachHeaderIncludeGen(Preprocessor &PP,
79309124Sdim                                   const DependencyOutputOptions &DepOpts,
80309124Sdim                                   bool ShowAllHeaders, StringRef OutputPath,
81309124Sdim                                   bool ShowDepth, bool MSStyle) {
82341825Sdim  raw_ostream *OutputFile = &llvm::errs();
83218887Sdim  bool OwnsOutputFile = false;
84218887Sdim
85341825Sdim  // Choose output stream, when printing in cl.exe /showIncludes style.
86341825Sdim  if (MSStyle) {
87341825Sdim    switch (DepOpts.ShowIncludesDest) {
88341825Sdim    default:
89341825Sdim      llvm_unreachable("Invalid destination for /showIncludes output!");
90341825Sdim    case ShowIncludesDestination::Stderr:
91341825Sdim      OutputFile = &llvm::errs();
92341825Sdim      break;
93341825Sdim    case ShowIncludesDestination::Stdout:
94341825Sdim      OutputFile = &llvm::outs();
95341825Sdim      break;
96341825Sdim    }
97341825Sdim  }
98341825Sdim
99218887Sdim  // Open the output file, if used.
100218887Sdim  if (!OutputPath.empty()) {
101280031Sdim    std::error_code EC;
102218887Sdim    llvm::raw_fd_ostream *OS = new llvm::raw_fd_ostream(
103360784Sdim        OutputPath.str(), EC,
104360784Sdim        llvm::sys::fs::OF_Append | llvm::sys::fs::OF_Text);
105280031Sdim    if (EC) {
106280031Sdim      PP.getDiagnostics().Report(clang::diag::warn_fe_cc_print_header_failure)
107280031Sdim          << EC.message();
108218887Sdim      delete OS;
109218887Sdim    } else {
110218887Sdim      OS->SetUnbuffered();
111218887Sdim      OutputFile = OS;
112218887Sdim      OwnsOutputFile = true;
113218887Sdim    }
114218887Sdim  }
115218887Sdim
116309124Sdim  // Print header info for extra headers, pretending they were discovered by
117309124Sdim  // the regular preprocessor. The primary use case is to support proper
118309124Sdim  // generation of Make / Ninja file dependencies for implicit includes, such
119309124Sdim  // as sanitizer blacklists. It's only important for cl.exe compatibility,
120309124Sdim  // the GNU way to generate rules is -M / -MM / -MD / -MMD.
121309124Sdim  for (const auto &Header : DepOpts.ExtraDeps)
122309124Sdim    PrintHeaderInfo(OutputFile, Header, ShowDepth, 2, MSStyle);
123360784Sdim  PP.addPPCallbacks(std::make_unique<HeaderIncludesCallback>(
124309124Sdim      &PP, ShowAllHeaders, OutputFile, DepOpts, OwnsOutputFile, ShowDepth,
125309124Sdim      MSStyle));
126218887Sdim}
127218887Sdim
128218887Sdimvoid HeaderIncludesCallback::FileChanged(SourceLocation Loc,
129218887Sdim                                         FileChangeReason Reason,
130226633Sdim                                       SrcMgr::CharacteristicKind NewFileType,
131226633Sdim                                       FileID PrevFID) {
132218887Sdim  // Unless we are exiting a #include, make sure to skip ahead to the line the
133218887Sdim  // #include directive was at.
134218887Sdim  PresumedLoc UserLoc = SM.getPresumedLoc(Loc);
135218887Sdim  if (UserLoc.isInvalid())
136218887Sdim    return;
137218887Sdim
138218887Sdim  // Adjust the current include depth.
139218887Sdim  if (Reason == PPCallbacks::EnterFile) {
140218887Sdim    ++CurrentIncludeDepth;
141221345Sdim  } else if (Reason == PPCallbacks::ExitFile) {
142218887Sdim    if (CurrentIncludeDepth)
143218887Sdim      --CurrentIncludeDepth;
144218887Sdim
145218887Sdim    // We track when we are done with the predefines by watching for the first
146221345Sdim    // place where we drop back to a nesting depth of 1.
147309124Sdim    if (CurrentIncludeDepth == 1 && !HasProcessedPredefines) {
148309124Sdim      if (!DepOpts.ShowIncludesPretendHeader.empty()) {
149309124Sdim        PrintHeaderInfo(OutputFile, DepOpts.ShowIncludesPretendHeader,
150309124Sdim                        ShowDepth, 2, MSStyle);
151309124Sdim      }
152218887Sdim      HasProcessedPredefines = true;
153309124Sdim    }
154218887Sdim
155221345Sdim    return;
156221345Sdim  } else
157221345Sdim    return;
158221345Sdim
159218887Sdim  // Show the header if we are (a) past the predefines, or (b) showing all
160218887Sdim  // headers and in the predefines at a depth past the initial file and command
161218887Sdim  // line buffers.
162218887Sdim  bool ShowHeader = (HasProcessedPredefines ||
163218887Sdim                     (ShowAllHeaders && CurrentIncludeDepth > 2));
164309124Sdim  unsigned IncludeDepth = CurrentIncludeDepth;
165309124Sdim  if (!HasProcessedPredefines)
166309124Sdim    --IncludeDepth; // Ignore indent from <built-in>.
167309124Sdim  else if (!DepOpts.ShowIncludesPretendHeader.empty())
168309124Sdim    ++IncludeDepth; // Pretend inclusion by ShowIncludesPretendHeader.
169218887Sdim
170218887Sdim  // Dump the header include information we are past the predefines buffer or
171309124Sdim  // are showing all headers and this isn't the magic implicit <command line>
172309124Sdim  // header.
173309124Sdim  // FIXME: Identify headers in a more robust way than comparing their name to
174309124Sdim  // "<command line>" and "<built-in>" in a bunch of places.
175309124Sdim  if (ShowHeader && Reason == PPCallbacks::EnterFile &&
176309124Sdim      UserLoc.getFilename() != StringRef("<command line>")) {
177309124Sdim    PrintHeaderInfo(OutputFile, UserLoc.getFilename(), ShowDepth, IncludeDepth,
178309124Sdim                    MSStyle);
179218887Sdim  }
180218887Sdim}
181