xref: /openbsd-src/gnu/llvm/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1*12c85518Srobert //===- ExtractAPI/ExtractAPIConsumer.cpp ------------------------*- C++ -*-===//
2*12c85518Srobert //
3*12c85518Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*12c85518Srobert // See https://llvm.org/LICENSE.txt for license information.
5*12c85518Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*12c85518Srobert //
7*12c85518Srobert //===----------------------------------------------------------------------===//
8*12c85518Srobert ///
9*12c85518Srobert /// \file
10*12c85518Srobert /// This file implements the ExtractAPIAction, and ASTConsumer to collect API
11*12c85518Srobert /// information.
12*12c85518Srobert ///
13*12c85518Srobert //===----------------------------------------------------------------------===//
14*12c85518Srobert 
15*12c85518Srobert #include "clang/AST/ASTConsumer.h"
16*12c85518Srobert #include "clang/AST/ASTContext.h"
17*12c85518Srobert #include "clang/Basic/DiagnosticFrontend.h"
18*12c85518Srobert #include "clang/Basic/SourceLocation.h"
19*12c85518Srobert #include "clang/Basic/SourceManager.h"
20*12c85518Srobert #include "clang/Basic/TargetInfo.h"
21*12c85518Srobert #include "clang/ExtractAPI/API.h"
22*12c85518Srobert #include "clang/ExtractAPI/APIIgnoresList.h"
23*12c85518Srobert #include "clang/ExtractAPI/ExtractAPIVisitor.h"
24*12c85518Srobert #include "clang/ExtractAPI/FrontendActions.h"
25*12c85518Srobert #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
26*12c85518Srobert #include "clang/Frontend/ASTConsumers.h"
27*12c85518Srobert #include "clang/Frontend/CompilerInstance.h"
28*12c85518Srobert #include "clang/Frontend/FrontendOptions.h"
29*12c85518Srobert #include "clang/Lex/MacroInfo.h"
30*12c85518Srobert #include "clang/Lex/PPCallbacks.h"
31*12c85518Srobert #include "clang/Lex/Preprocessor.h"
32*12c85518Srobert #include "clang/Lex/PreprocessorOptions.h"
33*12c85518Srobert #include "llvm/ADT/DenseSet.h"
34*12c85518Srobert #include "llvm/ADT/STLExtras.h"
35*12c85518Srobert #include "llvm/ADT/SmallVector.h"
36*12c85518Srobert #include "llvm/Support/Error.h"
37*12c85518Srobert #include "llvm/Support/FileSystem.h"
38*12c85518Srobert #include "llvm/Support/MemoryBuffer.h"
39*12c85518Srobert #include "llvm/Support/Path.h"
40*12c85518Srobert #include "llvm/Support/Regex.h"
41*12c85518Srobert #include "llvm/Support/raw_ostream.h"
42*12c85518Srobert #include <memory>
43*12c85518Srobert #include <optional>
44*12c85518Srobert #include <utility>
45*12c85518Srobert 
46*12c85518Srobert using namespace clang;
47*12c85518Srobert using namespace extractapi;
48*12c85518Srobert 
49*12c85518Srobert namespace {
50*12c85518Srobert 
getRelativeIncludeName(const CompilerInstance & CI,StringRef File,bool * IsQuoted=nullptr)51*12c85518Srobert std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
52*12c85518Srobert                                                   StringRef File,
53*12c85518Srobert                                                   bool *IsQuoted = nullptr) {
54*12c85518Srobert   assert(CI.hasFileManager() &&
55*12c85518Srobert          "CompilerInstance does not have a FileNamager!");
56*12c85518Srobert 
57*12c85518Srobert   using namespace llvm::sys;
58*12c85518Srobert   // Matches framework include patterns
59*12c85518Srobert   const llvm::Regex Rule("/(.+)\\.framework/(.+)?Headers/(.+)");
60*12c85518Srobert 
61*12c85518Srobert   const auto &FS = CI.getVirtualFileSystem();
62*12c85518Srobert 
63*12c85518Srobert   SmallString<128> FilePath(File.begin(), File.end());
64*12c85518Srobert   FS.makeAbsolute(FilePath);
65*12c85518Srobert   path::remove_dots(FilePath, true);
66*12c85518Srobert   FilePath = path::convert_to_slash(FilePath);
67*12c85518Srobert   File = FilePath;
68*12c85518Srobert 
69*12c85518Srobert   // Checks whether `Dir` is a strict path prefix of `File`. If so returns
70*12c85518Srobert   // the prefix length. Otherwise return 0.
71*12c85518Srobert   auto CheckDir = [&](llvm::StringRef Dir) -> unsigned {
72*12c85518Srobert     llvm::SmallString<32> DirPath(Dir.begin(), Dir.end());
73*12c85518Srobert     FS.makeAbsolute(DirPath);
74*12c85518Srobert     path::remove_dots(DirPath, true);
75*12c85518Srobert     Dir = DirPath;
76*12c85518Srobert     for (auto NI = path::begin(File), NE = path::end(File),
77*12c85518Srobert               DI = path::begin(Dir), DE = path::end(Dir);
78*12c85518Srobert          /*termination condition in loop*/; ++NI, ++DI) {
79*12c85518Srobert       // '.' components in File are ignored.
80*12c85518Srobert       while (NI != NE && *NI == ".")
81*12c85518Srobert         ++NI;
82*12c85518Srobert       if (NI == NE)
83*12c85518Srobert         break;
84*12c85518Srobert 
85*12c85518Srobert       // '.' components in Dir are ignored.
86*12c85518Srobert       while (DI != DE && *DI == ".")
87*12c85518Srobert         ++DI;
88*12c85518Srobert 
89*12c85518Srobert       // Dir is a prefix of File, up to '.' components and choice of path
90*12c85518Srobert       // separators.
91*12c85518Srobert       if (DI == DE)
92*12c85518Srobert         return NI - path::begin(File);
93*12c85518Srobert 
94*12c85518Srobert       // Consider all path separators equal.
95*12c85518Srobert       if (NI->size() == 1 && DI->size() == 1 &&
96*12c85518Srobert           path::is_separator(NI->front()) && path::is_separator(DI->front()))
97*12c85518Srobert         continue;
98*12c85518Srobert 
99*12c85518Srobert       // Special case Apple .sdk folders since the search path is typically a
100*12c85518Srobert       // symlink like `iPhoneSimulator14.5.sdk` while the file is instead
101*12c85518Srobert       // located in `iPhoneSimulator.sdk` (the real folder).
102*12c85518Srobert       if (NI->endswith(".sdk") && DI->endswith(".sdk")) {
103*12c85518Srobert         StringRef NBasename = path::stem(*NI);
104*12c85518Srobert         StringRef DBasename = path::stem(*DI);
105*12c85518Srobert         if (DBasename.startswith(NBasename))
106*12c85518Srobert           continue;
107*12c85518Srobert       }
108*12c85518Srobert 
109*12c85518Srobert       if (*NI != *DI)
110*12c85518Srobert         break;
111*12c85518Srobert     }
112*12c85518Srobert     return 0;
113*12c85518Srobert   };
114*12c85518Srobert 
115*12c85518Srobert   unsigned PrefixLength = 0;
116*12c85518Srobert 
117*12c85518Srobert   // Go through the search paths and find the first one that is a prefix of
118*12c85518Srobert   // the header.
119*12c85518Srobert   for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) {
120*12c85518Srobert     // Note whether the match is found in a quoted entry.
121*12c85518Srobert     if (IsQuoted)
122*12c85518Srobert       *IsQuoted = Entry.Group == frontend::Quoted;
123*12c85518Srobert 
124*12c85518Srobert     if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) {
125*12c85518Srobert       if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) {
126*12c85518Srobert         // If this is a headermap entry, try to reverse lookup the full path
127*12c85518Srobert         // for a spelled name before mapping.
128*12c85518Srobert         StringRef SpelledFilename = HMap->reverseLookupFilename(File);
129*12c85518Srobert         if (!SpelledFilename.empty())
130*12c85518Srobert           return SpelledFilename.str();
131*12c85518Srobert 
132*12c85518Srobert         // No matching mapping in this headermap, try next search entry.
133*12c85518Srobert         continue;
134*12c85518Srobert       }
135*12c85518Srobert     }
136*12c85518Srobert 
137*12c85518Srobert     // Entry is a directory search entry, try to check if it's a prefix of File.
138*12c85518Srobert     PrefixLength = CheckDir(Entry.Path);
139*12c85518Srobert     if (PrefixLength > 0) {
140*12c85518Srobert       // The header is found in a framework path, construct the framework-style
141*12c85518Srobert       // include name `<Framework/Header.h>`
142*12c85518Srobert       if (Entry.IsFramework) {
143*12c85518Srobert         SmallVector<StringRef, 4> Matches;
144*12c85518Srobert         Rule.match(File, &Matches);
145*12c85518Srobert         // Returned matches are always in stable order.
146*12c85518Srobert         if (Matches.size() != 4)
147*12c85518Srobert           return std::nullopt;
148*12c85518Srobert 
149*12c85518Srobert         return path::convert_to_slash(
150*12c85518Srobert             (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" +
151*12c85518Srobert              Matches[3])
152*12c85518Srobert                 .str());
153*12c85518Srobert       }
154*12c85518Srobert 
155*12c85518Srobert       // The header is found in a normal search path, strip the search path
156*12c85518Srobert       // prefix to get an include name.
157*12c85518Srobert       return path::convert_to_slash(File.drop_front(PrefixLength));
158*12c85518Srobert     }
159*12c85518Srobert   }
160*12c85518Srobert 
161*12c85518Srobert   // Couldn't determine a include name, use full path instead.
162*12c85518Srobert   return std::nullopt;
163*12c85518Srobert }
164*12c85518Srobert 
165*12c85518Srobert struct LocationFileChecker {
operator ()__anon1533bdf60111::LocationFileChecker166*12c85518Srobert   bool operator()(SourceLocation Loc) {
167*12c85518Srobert     // If the loc refers to a macro expansion we need to first get the file
168*12c85518Srobert     // location of the expansion.
169*12c85518Srobert     auto &SM = CI.getSourceManager();
170*12c85518Srobert     auto FileLoc = SM.getFileLoc(Loc);
171*12c85518Srobert     FileID FID = SM.getFileID(FileLoc);
172*12c85518Srobert     if (FID.isInvalid())
173*12c85518Srobert       return false;
174*12c85518Srobert 
175*12c85518Srobert     const auto *File = SM.getFileEntryForID(FID);
176*12c85518Srobert     if (!File)
177*12c85518Srobert       return false;
178*12c85518Srobert 
179*12c85518Srobert     if (KnownFileEntries.count(File))
180*12c85518Srobert       return true;
181*12c85518Srobert 
182*12c85518Srobert     if (ExternalFileEntries.count(File))
183*12c85518Srobert       return false;
184*12c85518Srobert 
185*12c85518Srobert     StringRef FileName = File->tryGetRealPathName().empty()
186*12c85518Srobert                              ? File->getName()
187*12c85518Srobert                              : File->tryGetRealPathName();
188*12c85518Srobert 
189*12c85518Srobert     // Try to reduce the include name the same way we tried to include it.
190*12c85518Srobert     bool IsQuoted = false;
191*12c85518Srobert     if (auto IncludeName = getRelativeIncludeName(CI, FileName, &IsQuoted))
192*12c85518Srobert       if (llvm::any_of(KnownFiles,
193*12c85518Srobert                        [&IsQuoted, &IncludeName](const auto &KnownFile) {
194*12c85518Srobert                          return KnownFile.first.equals(*IncludeName) &&
195*12c85518Srobert                                 KnownFile.second == IsQuoted;
196*12c85518Srobert                        })) {
197*12c85518Srobert         KnownFileEntries.insert(File);
198*12c85518Srobert         return true;
199*12c85518Srobert       }
200*12c85518Srobert 
201*12c85518Srobert     // Record that the file was not found to avoid future reverse lookup for
202*12c85518Srobert     // the same file.
203*12c85518Srobert     ExternalFileEntries.insert(File);
204*12c85518Srobert     return false;
205*12c85518Srobert   }
206*12c85518Srobert 
LocationFileChecker__anon1533bdf60111::LocationFileChecker207*12c85518Srobert   LocationFileChecker(const CompilerInstance &CI,
208*12c85518Srobert                       SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles)
209*12c85518Srobert       : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() {
210*12c85518Srobert     for (const auto &KnownFile : KnownFiles)
211*12c85518Srobert       if (auto FileEntry = CI.getFileManager().getFile(KnownFile.first))
212*12c85518Srobert         KnownFileEntries.insert(*FileEntry);
213*12c85518Srobert   }
214*12c85518Srobert 
215*12c85518Srobert private:
216*12c85518Srobert   const CompilerInstance &CI;
217*12c85518Srobert   SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles;
218*12c85518Srobert   llvm::DenseSet<const FileEntry *> KnownFileEntries;
219*12c85518Srobert   llvm::DenseSet<const FileEntry *> ExternalFileEntries;
220*12c85518Srobert };
221*12c85518Srobert 
222*12c85518Srobert class ExtractAPIConsumer : public ASTConsumer {
223*12c85518Srobert public:
ExtractAPIConsumer(ASTContext & Context,std::unique_ptr<LocationFileChecker> LCF,APISet & API)224*12c85518Srobert   ExtractAPIConsumer(ASTContext &Context,
225*12c85518Srobert                      std::unique_ptr<LocationFileChecker> LCF, APISet &API)
226*12c85518Srobert       : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {}
227*12c85518Srobert 
HandleTranslationUnit(ASTContext & Context)228*12c85518Srobert   void HandleTranslationUnit(ASTContext &Context) override {
229*12c85518Srobert     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
230*12c85518Srobert     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
231*12c85518Srobert   }
232*12c85518Srobert 
233*12c85518Srobert private:
234*12c85518Srobert   ExtractAPIVisitor Visitor;
235*12c85518Srobert   std::unique_ptr<LocationFileChecker> LCF;
236*12c85518Srobert };
237*12c85518Srobert 
238*12c85518Srobert class MacroCallback : public PPCallbacks {
239*12c85518Srobert public:
MacroCallback(const SourceManager & SM,LocationFileChecker & LCF,APISet & API,Preprocessor & PP)240*12c85518Srobert   MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API,
241*12c85518Srobert                 Preprocessor &PP)
242*12c85518Srobert       : SM(SM), LCF(LCF), API(API), PP(PP) {}
243*12c85518Srobert 
MacroDefined(const Token & MacroNameToken,const MacroDirective * MD)244*12c85518Srobert   void MacroDefined(const Token &MacroNameToken,
245*12c85518Srobert                     const MacroDirective *MD) override {
246*12c85518Srobert     auto *MacroInfo = MD->getMacroInfo();
247*12c85518Srobert 
248*12c85518Srobert     if (MacroInfo->isBuiltinMacro())
249*12c85518Srobert       return;
250*12c85518Srobert 
251*12c85518Srobert     auto SourceLoc = MacroNameToken.getLocation();
252*12c85518Srobert     if (SM.isWrittenInBuiltinFile(SourceLoc) ||
253*12c85518Srobert         SM.isWrittenInCommandLineFile(SourceLoc))
254*12c85518Srobert       return;
255*12c85518Srobert 
256*12c85518Srobert     PendingMacros.emplace_back(MacroNameToken, MD);
257*12c85518Srobert   }
258*12c85518Srobert 
259*12c85518Srobert   // If a macro gets undefined at some point during preprocessing of the inputs
260*12c85518Srobert   // it means that it isn't an exposed API and we should therefore not add a
261*12c85518Srobert   // macro definition for it.
MacroUndefined(const Token & MacroNameToken,const MacroDefinition & MD,const MacroDirective * Undef)262*12c85518Srobert   void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
263*12c85518Srobert                       const MacroDirective *Undef) override {
264*12c85518Srobert     // If this macro wasn't previously defined we don't need to do anything
265*12c85518Srobert     // here.
266*12c85518Srobert     if (!Undef)
267*12c85518Srobert       return;
268*12c85518Srobert 
269*12c85518Srobert     llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) {
270*12c85518Srobert       return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP,
271*12c85518Srobert                                               /*Syntactically*/ false);
272*12c85518Srobert     });
273*12c85518Srobert   }
274*12c85518Srobert 
EndOfMainFile()275*12c85518Srobert   void EndOfMainFile() override {
276*12c85518Srobert     for (auto &PM : PendingMacros) {
277*12c85518Srobert       // `isUsedForHeaderGuard` is only set when the preprocessor leaves the
278*12c85518Srobert       // file so check for it here.
279*12c85518Srobert       if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
280*12c85518Srobert         continue;
281*12c85518Srobert 
282*12c85518Srobert       if (!LCF(PM.MacroNameToken.getLocation()))
283*12c85518Srobert         continue;
284*12c85518Srobert 
285*12c85518Srobert       StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
286*12c85518Srobert       PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
287*12c85518Srobert       StringRef USR =
288*12c85518Srobert           API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
289*12c85518Srobert 
290*12c85518Srobert       API.addMacroDefinition(
291*12c85518Srobert           Name, USR, Loc,
292*12c85518Srobert           DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
293*12c85518Srobert           DeclarationFragmentsBuilder::getSubHeadingForMacro(Name),
294*12c85518Srobert           SM.isInSystemHeader(PM.MacroNameToken.getLocation()));
295*12c85518Srobert     }
296*12c85518Srobert 
297*12c85518Srobert     PendingMacros.clear();
298*12c85518Srobert   }
299*12c85518Srobert 
300*12c85518Srobert private:
301*12c85518Srobert   struct PendingMacro {
302*12c85518Srobert     Token MacroNameToken;
303*12c85518Srobert     const MacroDirective *MD;
304*12c85518Srobert 
PendingMacro__anon1533bdf60111::MacroCallback::PendingMacro305*12c85518Srobert     PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
306*12c85518Srobert         : MacroNameToken(MacroNameToken), MD(MD) {}
307*12c85518Srobert   };
308*12c85518Srobert 
309*12c85518Srobert   const SourceManager &SM;
310*12c85518Srobert   LocationFileChecker &LCF;
311*12c85518Srobert   APISet &API;
312*12c85518Srobert   Preprocessor &PP;
313*12c85518Srobert   llvm::SmallVector<PendingMacro> PendingMacros;
314*12c85518Srobert };
315*12c85518Srobert 
316*12c85518Srobert } // namespace
317*12c85518Srobert 
318*12c85518Srobert std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)319*12c85518Srobert ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
320*12c85518Srobert   OS = CreateOutputFile(CI, InFile);
321*12c85518Srobert   if (!OS)
322*12c85518Srobert     return nullptr;
323*12c85518Srobert 
324*12c85518Srobert   auto ProductName = CI.getFrontendOpts().ProductName;
325*12c85518Srobert 
326*12c85518Srobert   // Now that we have enough information about the language options and the
327*12c85518Srobert   // target triple, let's create the APISet before anyone uses it.
328*12c85518Srobert   API = std::make_unique<APISet>(
329*12c85518Srobert       CI.getTarget().getTriple(),
330*12c85518Srobert       CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
331*12c85518Srobert 
332*12c85518Srobert   auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
333*12c85518Srobert 
334*12c85518Srobert   CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
335*12c85518Srobert       CI.getSourceManager(), *LCF, *API, CI.getPreprocessor()));
336*12c85518Srobert 
337*12c85518Srobert   // Do not include location in anonymous decls.
338*12c85518Srobert   PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
339*12c85518Srobert   Policy.AnonymousTagLocations = false;
340*12c85518Srobert   CI.getASTContext().setPrintingPolicy(Policy);
341*12c85518Srobert 
342*12c85518Srobert   if (!CI.getFrontendOpts().ExtractAPIIgnoresFile.empty()) {
343*12c85518Srobert     llvm::handleAllErrors(
344*12c85518Srobert         APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFile,
345*12c85518Srobert                                CI.getFileManager())
346*12c85518Srobert             .moveInto(IgnoresList),
347*12c85518Srobert         [&CI](const IgnoresFileNotFound &Err) {
348*12c85518Srobert           CI.getDiagnostics().Report(
349*12c85518Srobert               diag::err_extract_api_ignores_file_not_found)
350*12c85518Srobert               << Err.Path;
351*12c85518Srobert         });
352*12c85518Srobert   }
353*12c85518Srobert 
354*12c85518Srobert   return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
355*12c85518Srobert                                               std::move(LCF), *API);
356*12c85518Srobert }
357*12c85518Srobert 
PrepareToExecuteAction(CompilerInstance & CI)358*12c85518Srobert bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
359*12c85518Srobert   auto &Inputs = CI.getFrontendOpts().Inputs;
360*12c85518Srobert   if (Inputs.empty())
361*12c85518Srobert     return true;
362*12c85518Srobert 
363*12c85518Srobert   if (!CI.hasFileManager())
364*12c85518Srobert     if (!CI.createFileManager())
365*12c85518Srobert       return false;
366*12c85518Srobert 
367*12c85518Srobert   auto Kind = Inputs[0].getKind();
368*12c85518Srobert 
369*12c85518Srobert   // Convert the header file inputs into a single input buffer.
370*12c85518Srobert   SmallString<256> HeaderContents;
371*12c85518Srobert   bool IsQuoted = false;
372*12c85518Srobert   for (const FrontendInputFile &FIF : Inputs) {
373*12c85518Srobert     if (Kind.isObjectiveC())
374*12c85518Srobert       HeaderContents += "#import";
375*12c85518Srobert     else
376*12c85518Srobert       HeaderContents += "#include";
377*12c85518Srobert 
378*12c85518Srobert     StringRef FilePath = FIF.getFile();
379*12c85518Srobert     if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) {
380*12c85518Srobert       if (IsQuoted)
381*12c85518Srobert         HeaderContents += " \"";
382*12c85518Srobert       else
383*12c85518Srobert         HeaderContents += " <";
384*12c85518Srobert 
385*12c85518Srobert       HeaderContents += *RelativeName;
386*12c85518Srobert 
387*12c85518Srobert       if (IsQuoted)
388*12c85518Srobert         HeaderContents += "\"\n";
389*12c85518Srobert       else
390*12c85518Srobert         HeaderContents += ">\n";
391*12c85518Srobert       KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName),
392*12c85518Srobert                                    IsQuoted);
393*12c85518Srobert     } else {
394*12c85518Srobert       HeaderContents += " \"";
395*12c85518Srobert       HeaderContents += FilePath;
396*12c85518Srobert       HeaderContents += "\"\n";
397*12c85518Srobert       KnownInputFiles.emplace_back(FilePath, true);
398*12c85518Srobert     }
399*12c85518Srobert   }
400*12c85518Srobert 
401*12c85518Srobert   if (CI.getHeaderSearchOpts().Verbose)
402*12c85518Srobert     CI.getVerboseOutputStream() << getInputBufferName() << ":\n"
403*12c85518Srobert                                 << HeaderContents << "\n";
404*12c85518Srobert 
405*12c85518Srobert   Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
406*12c85518Srobert                                                 getInputBufferName());
407*12c85518Srobert 
408*12c85518Srobert   // Set that buffer up as our "real" input in the CompilerInstance.
409*12c85518Srobert   Inputs.clear();
410*12c85518Srobert   Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
411*12c85518Srobert 
412*12c85518Srobert   return true;
413*12c85518Srobert }
414*12c85518Srobert 
EndSourceFileAction()415*12c85518Srobert void ExtractAPIAction::EndSourceFileAction() {
416*12c85518Srobert   if (!OS)
417*12c85518Srobert     return;
418*12c85518Srobert 
419*12c85518Srobert   // Setup a SymbolGraphSerializer to write out collected API information in
420*12c85518Srobert   // the Symbol Graph format.
421*12c85518Srobert   // FIXME: Make the kind of APISerializer configurable.
422*12c85518Srobert   SymbolGraphSerializer SGSerializer(*API, IgnoresList);
423*12c85518Srobert   SGSerializer.serialize(*OS);
424*12c85518Srobert   OS.reset();
425*12c85518Srobert }
426*12c85518Srobert 
427*12c85518Srobert std::unique_ptr<raw_pwrite_stream>
CreateOutputFile(CompilerInstance & CI,StringRef InFile)428*12c85518Srobert ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
429*12c85518Srobert   std::unique_ptr<raw_pwrite_stream> OS =
430*12c85518Srobert       CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json",
431*12c85518Srobert                                  /*RemoveFileOnSignal=*/false);
432*12c85518Srobert   if (!OS)
433*12c85518Srobert     return nullptr;
434*12c85518Srobert   return OS;
435*12c85518Srobert }
436