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